'STM32, AVR'에 해당되는 글 2건

  1. 2022.02.28 STM32H7 DMA 사용 시 주의점
  2. 2020.04.06 STM32 디버깅하면서 생겼던 문제들 10
posted by 샛별의꿈 2022. 2. 28. 11:03

삽질을 안하면 하루도 편히 잠들 수 없는(아니 있는...)건지, 이번에도 또 삽질임.

 

STM32F1이나 F4, F7 계열로 쓸 때 겪어보지 못했던 문제였는데, 알고나서 생각해보니 F7에서도 충분히 발생할 만한 문제였던 것 같다.

 

UART RX를 DMA 로 사용하고, NDTR 레지스터 읽어가면서 버퍼가 채워지는 경우 처리를 했는데...

STM32H7로 했더니 갑자기 NDTR값은 바뀌는데 실제 버퍼가 채워지지 않거나 이상한 값이 나타난 것.

 

원인을 찾아보니 아래와 같은 내용 발견

https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices

 

Service Not Available

Your browser doesn’t support some features on this site. For the best experience, update your browser to the latest version, or switch to another browser.

community.st.com

내용에 따르면, D-Cache 를 켜는 순간 D-Cache가 SRAM에 내용을 쓰지 않고, Cache 에 데이터를 가지고 있는 문제로 인하여 SRAM에 할당된 UART 버퍼가 업데이트되지 않았던 것.

 

이를 해결하기 위해 몇 가지 방법이 있는데,

 

1. D-Cache를 사용하지 않고 모든 메모리 할당을 D1 도메인에 배치한다.

2. 캐싱이 필요한 변수 / 캐싱하지 않을 변수를 별도로 분리하고 분리배치한다. MPU에서 캐싱을 비활성화시킨다.

3. 캐시 유지관리 기능을 사용한다.

 

일단, 위와 같은 방법 중에서 1번은 손해볼 일이 많다.

실험을 위해 연산이 많으나 빨리 처리해야 하는 부분의 처리속도 측정 결과, 캐시를 사용하였을 때와 사용하지 않았을 때의 처리속도 차이가 5~6배 가량 발생하는 것을 확인하였다.

이런 속도차이를 포기하고 캐시를 비활성화시키는 건 그닥 올바른 방향은 아닌 것 같다.

 

그래서 2, 3번이 남는데 일단 3번은 메모리 할당 시 32바이트 단위로밖에 설정할 수가 없는 것 같고, 괜히 잘못 건드렸다가 Hardfault 에 빠질 수 있다는 내용이 있어 뒤로 미뤄두고(...)

 

2번을 테스트해 보기로 함.

 

이를 위해 위 문서에 자세히 설명이 되어 있지만 정리하면,

 

 1) 버퍼를 D2 Domain 에 할당한다. (DMA1/DMA2 채널이 모두 D2 Domain 에 가장 가까이 연결되어 있다)
   이를 위해, 링커스크립트 파일(*.ld) 에 아래 내용을 넣는다. 위치는 중요하지 않은 것 같지만 .bss 밑에 넣었다.
   STM32CubeIDE에서는 STM32xxxx_FLASH.ld 파일과 STM32xxxx_RAM.ld 파일 두개가 생성되는데, 처음엔 RAM쪽에만 넣어 보았으나 할당이 안되어서 FLASH 쪽에도 넣어보니 할당이 되었다. 왜 그런지는 아직 모르겠다.

   STM32CubeIDE에서 프로젝트를 기본 생성하면 링커 스크립트를 STM32xxxx_FLASH.ld 파일로 로드한다. (프로젝트 설정 -> Tool Settings -> MCU GCC Linker -> General 참고) 환경에 따라 바꿔주면 됨.

  .dma_buffer : /*Space before ':' is critical */
  {
  	*(.dma_buffer)
  } >RAM_D2

  다음으로, 버퍼로 사용할 변수를 .dma_buffer 영역에 할당하면 되는데 이 또한 위의 문서에 기록되어 있고 아래와 같이 하면 된다.

#if defined( __ICCARM__ )
  #define DMA_BUFFER \
      _Pragma("location=\".dma_buffer\"")
#else
  #define DMA_BUFFER \
      __attribute__((section(".dma_buffer")))
#endif

DMA_BUFFER uint8_t	U3_rxbuf[U3_DMA_RX_BUFSIZE];

 

 2) MPU를 설정하여 D2 Domain의 Cache 사용을 비활성화한다.

  MPU설정이 조금 헷갈리고, 아직 모르겠는 부분은 있지만 아래와 같이 하였다.

  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.BaseAddress = RAM_D2_ADDR;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_NOT_SHAREABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL0;
  MPU_InitStruct.SubRegionDisable = 0x00;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_ENABLE;

  HAL_MPU_ConfigRegion(&MPU_InitStruct);
  
  HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);

  여기서 중요 포인트는 BaseAddress 속성을 D2주소값으로 설정, IsCacheable 속성을 MPU_ACCESS_NOT_CACHEABLE 로 설정, 그리고 Size를 정해줘야 하는 부분인데 D2가 288KB인데 설정가능한 크기가 32Byte 부터 배수 값으로밖에 안돼서 그냥 좀더 작은 256KB 로 선택함. Number 속성은 AN4838 문서에 구체적이진 않지만 나와있는걸 참고하면 내부메모리 영역이라서 Region 0 으로 설정하면 되는 것으로 보인다.

(아니 그렇다면 문서에 따르면 SDRAM이면 FMC 라서 Region 2여야 맞는데 예제 보면 Region 3으로 설정하던데...뭐가 맞는지 아직 모르겠다)

그외에는 우선 기본값인데 아직 문제가 발생하지는 않았다.

 

아무튼, 위와 같은 방법으로 설정하니 UART DMA RX 의 버퍼가 정상적으로 채워지는 것을 확인할 수 있었다.

 

시스템 인터럽트 부담 줄여서 마음이 편하다(이게 왜 편한지 모르겠지만)

 


2022-08-23 덧붙임.

 

위와 같이 Cache를 비활성화한 영역에 할당된 변수를 접근하려고 하니, 

Unaligned Access 에 의한 Hardfault 가 발생하였다.

처음엔 해당 변수를 사용하려는 코드의 문제라고 생각하였으나, 결론은 MPU 설정 문제.

 

MPU_RASR Register 설정 시, 각 속성들을 별도로 설정할 수 있으면 좋겠지만 원하는 기능에 따라 이미 정해진 Table 에서 설정값을 골라야 하는 것으로 보인다. 

AN4838 문서의 Table 4. Cache properties and shareability 를 참고하면,

Non-cacheable 로 설정하기 위해서는 TEX 001, Cacheable 0, Bufferable 0, Shareable(don't care) 

의 설정이 필요하다.

CubeMX 로 생성된 코드는 아래와 같다.

 

  MPU_InitStruct.Enable = MPU_REGION_ENABLE;
  MPU_InitStruct.Number = MPU_REGION_NUMBER0;
  MPU_InitStruct.BaseAddress = 0x30000000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;
  MPU_InitStruct.SubRegionDisable = 0x0;
  MPU_InitStruct.TypeExtField = MPU_TEX_LEVEL1;
  MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
  MPU_InitStruct.DisableExec = MPU_INSTRUCTION_ACCESS_DISABLE;
  MPU_InitStruct.IsShareable = MPU_ACCESS_SHAREABLE;
  MPU_InitStruct.IsCacheable = MPU_ACCESS_NOT_CACHEABLE;
  MPU_InitStruct.IsBufferable = MPU_ACCESS_NOT_BUFFERABLE;

 

위와 같이 설정하니 Hardfault 에 빠지지 않고 정상적으로 Access 되는 것을 확인함.

언제나 그랬듯 나중에 또 같은 실수를 반복할테니 그때 빠른 수습을 위해 기록한다.

posted by 샛별의꿈 2020. 4. 6. 10:46

1. USB Host 기능이 이상동작한다

1) Stack / Heap 메모리 용량 늘려줘봐

 

2. HardFault Exception 으로 빠져들어간다

1) Stack / Heap 메모리 용량 늘려줘봐

2) Enable 되지 않은 Peripheral 에서 값을 읽어오는 경우 발생할 수 있다. 이러한 Logic 이 존재하는지 확인해본다.

3) Timer 여러개를 켰을때 발생할 수 있는데, 아마도 주기를 짧게 설정해놓고 콜백함수 안에서 많은 일을 수행해서 콜백함수가 끝나지 않았는데 중복호출되면서 발생하는 것으로 보임. 그리고 짧은 주기의 타이머의 인터럽트 Priority를 더 높게 잡아도 생길 수 있음. 경우가 다양하니 상황에 맞게 대처해야겠다.

4) HAL_Delay() 를 쓰니 갑자기 HardFault 로 빠지는 경우가 발생했는데, 어이없게도 해결은 다른 PC 에서 빌드하니까 그냥 됨. 환경도 CubeIDE 1.5.1 로 동일한 상황이었음. 아직 명확한 원인은 모르겠고 기회가 되면 더 찾아보겠음. 그냥 나중에 또 겪으면 고민하기전에 다른 PC에서 먼저 해보자.

 

3. SPI Comm 이 되지 않는다

1) IT 함수를 사용하면, NSS핀을 Software GPIO 로 제어하는 것이 매우 어려워진다. 실제로 NSS 핀은 1->0->1 로 원복되었는데, IT함수때문에 늦어져서 통신을 안하고 있다가 NSS가 원복된 후에서야 통신 시도를 하는 경우가 발생했다. 이건 DMA 함수라도 NSS를 Soft 로 제어할 경우는 마찬가지로 예상된다.
2) Polarity 같은 설정이 잘못되었거나, 통신속도가 Slave가 감당할 수 없을만큼 빠른 경우

3) Hardware NSS 로 설정하면, SPI를 Disable 할 때까지 NSS 가 1로 돌아오지 않는다. (원래 그런건지 버그인지 모르겠다)

관련자료는 https://community.st.com/thread/39153-spi-master-nss-always-low-in-stm32f4 참고

 

4. Timer 가 동작하지 않는다.

1) STM32CubeMX 자동생성 코드에서는 Timer Start 함수까지 다 실행시켜주진 않는다. 이건 사용자가 실행해야 할 부분.

 

5. SPI 통신시 NSS 지연시간 관련

1) SPI에서 NSS 를 컨트롤하는데 있어서 HAL 드라이버로 Software NSS 를 구현하면 NSS 제어 이후 SPI 통신을 수행하였을 때 많은 시간낭비를 초래함. (3Mbps 시 통신 전후로 약 1 datacycle(8bit) 수준의 시간낭비 발생)

  - 해결책으로, SPI HAL driver 의 통신단에서 통신시작 직전/직후에 NSS를 제어하면 상당량의 시간낭비 감축이 가능

  - GPIO HAL driver 는 거의 Lowlevel 과 다를 바 없어 그대로 써도 무방할 듯.

 

6. I2C가 안된다

1) 연결 잘 했냐?

2) 동작 순서는 잘 되었냐? 조금이라도 삐끗하면 무한루프로 가버렷!

3) Master Receiver 입장이 된다면, ACK를 무시하면 안된다. 간혹 이걸 빠뜨리면 SDA로 전송된 데이터의 마지막 비트와 같은 값이 ACK로 보내지는 경우가 생겨서, 그게 하필 NAK으로 가면 위의 말처럼 무한루프로 가버리는 지름길.(Slave Transmitter 입장에서는 보낼거 3바이트쯤 되는데 1바이트 받고선 Master가 NAK 보내고나서 Stop condition 도 안 주면 뭘 할수가 없어지게 되겠지?)

 

7. (TrueStudio 확인) 브레이크포인트 안잡았는데 엄한데에 자꾸 걸리고 진행이 안된다.

- 니가 이전에 브레이크포인트를 너무 많이 잡아서 그럼.

- 케이블 다 빼고 전원 다 빼고 다시 전원넣는다. TrueStudio 는 재시작할 필요는 없었음.

- 경험으로는 SystemClock_Config() 에서 걸리기도 했고, I2C의 Digital Filter 설정하는 함수에서도 걸린 적 있었다.

 

8. USB Mass Storage 로 hex 파일 복붙하여 F/W 다운로딩이 안된다.

- 원래 hex 지원 안함. hex2bin 으로 bin 으로 변경하여 넣자. IDE에는 bin생성 옵션 있으니 그걸로 생성해주면 더 편하다.

 

9. 코드를 작성했는데 전혀 해당 코드를 타지 않는다

- 컴파일 옵션을 내려보고, 그렇게 되면 최적화에 의한 결과이므로 해당 사용변수를 volatile 로 선언한다. 예전에 겪었던 경험이 있는데 게시판에서 같은 경험 한 사람을 보며 나도 나중에 똑같은 실수 또 할까봐 적음..

 

10. Timer 를 잘 수행중인데, HAL_Delay() 가 들어간 구문을 쓰니 갑자기 Systick 에서 무한루프 돌고 있다.

- Timer Interrupt 의 Priority 가 너무 높아서 그렇다. Systick 이 0이니까 TImer는 그보다 아랫단계로 낮추자.

(이건 System Timer 을 일반 TIM으로 바꿔주면 Priority 에 관계없이 될지도 모르겠다?)

 

11. Float 타입 변수값을 printf 했더니 값이 하나도 안 나온다.

- 링커 기본설정값에 floating point 는 사용안함으로 되어 있어 그렇다. 

프로젝트 옵션 -> C/C++ Build -> Settings -> Tool Settings -> C Linker 의 Command 에 -u _printf_float  을 넣어주면 해결...

타겟보드 FLASH가 너무 작으면 이거 하나 켰다고 플래시 오버플로우 될 수 있다. 최적화 옵션을 조금씩 올려가면서 오버플로우 안되게 맞춰봐.

 

12. EXTI 가 어쩌다 한번 발생하고 더 안된다.

- 32L476Disco 보드에서 발생한 문제였는데, 스위치 GPIO를 EXTI로 변경하고, Rising Edge Triggering 을 했다. 그랬더니 EXTI가 한번쯤 발생은 하는데 더이상 발생이 안되는 것이다. 확인해보니 하드웨어 회로적으로는 각 입력핀마다 직렬저항 하나 물려있고 병렬로 C가 하나 붙어 있고, COMMON 핀에 풀업이 걸려있어서 누르면 H가 뜨도록 되어있다. 그런데 죄다 핀이 Floating 되게 생겨먹었으니, 핀 내부에서 풀다운 걸어줘야 한다. 안그러면 이상현상 발생함. 이 글 쓰고나서 CubeMX 에서 설정된 기본값 보니 풀다운 걸려있네. 어휴 바보.

 

13. STM32CubeIDE (Eclipse 환경) 에서 한글을 적었더니 다음 실행시 작성된 한글 다 깨짐

- 기본 Encoding 값이 뭔지는 모르겠지만(비어있다), 
Window - Preferences - General - Content Types - Text 까지 클릭 후에 아래쪽 Default encoding 란에 EUC-KR 로 변경하고 저장. 기존 소스코드에 작성된 한글(이미 깨지거나 한)이 있다면 문서에 EUC-KR로 표시할수 없는 문자셋이 있다고 오류 뱉는데 깨진건 어쩔 수 없고 새로 작성해야지 뭐. CubeMX 의 자동 소스코드 생성기 녀석이 자동으로 돌리면서 깨지는 거 같다.

 

14. STM32F1 에서 UART 로(외부는 RS-422) 전송시 반응이 이상하다

- 일단, STM32의 문제라기보다는 RS-422를 연결하면서 GND 를 안 붙였을 때 문제가 되는데, USB-RS422 컨버터와 개발 보드간에 연결 선이 짧을 경우 별 문제가 없다. 그래서 괜찮은 줄 알고 막 코딩 다 하고 잘 되네 했는데...

장비에 붙이니까 안되기 시작. 오잉? 원래 붙어있던 기존보드는 통신이 잘 된다. 그럼 내 보드가 문제인건 맞으니까, 열심히 디버깅을 한다.

그런데 답이 안나와. 분명 PC->Board 로 특정 헤더가 전송되면 이에 따라 프레임 1개를 응답하도록 코드를 짰는데, 특정 헤더가 1번 왔는데 두세번씩 막 응답을 한다.

대체 펌웨어를 그렇게 안짰는데 왜 왜 왜 하다가 나온 결론은 USB-RS422 컨버터와 타겟장비 간에 GND를 연결을 안해서 노이즈때문에 발생하는 문제였다.

이상한 것은, 노이즈더라도 특정 헤더(내 경우는 3byte) 가 동일하지 않은데 왜 응답을 하느냐는 것인데, 이건 좀더 생각해봐야 할 것 같고, 이 디버깅을 하면서 발견한 이상한점은,

UART를 HAL 드라이버로 작성했는데 UART_ErrorHandler 함수가 발생해서 Error 내용을 확인해보면 값이 0(No Error) 이라는 점이다. 로직애널라이저로 보면 전송 전후로 노이즈가 많이 끼는 것을 확인하였고, 이 노이즈에 의해서 ErrorHandler가 호출되는 것으로 보인다. 아래 추가 내용과 같이 컨버터를 변경하고 나니 ErrorHandler 가 한번도 호출되지 않았다.

추가 - Coms USB to 485/422 컨버터가 노이즈에 매우 취약하다. 아니 노이즈를 만들어내는거같은?그정도의 상황. 동일한 칩셋을 쓴 Realsys CNV485 로 바꿨더니 문제 다 사라짐. 다시는 Coms 컨버터 안 쓰리라..

보드 쪽 칩은 SN65C1168E 였는데, 이게 노이즈에 좀 더 취약한 것은 아닐까도 확인해봐야겠다. 저 Coms 제품으로도 DS34C86 / DS34C87 과 인터페이스 하는데는 그닥 문제가 없었으니까.

 

15. STM32L1xx 에서 USB CDC를 포함 USB기능이 작동을 안함

분명 회로 구성할때 찾아보니 D+ D- 신호를 추가 회로 없이 USB커넥터로 바로 연결해도 된다고 했다(아마 ST정식 문서였을 것이다). 그리고 나중에 찾다 보니 STM32 내부에 D+ 라인에 풀업을 걸어주는 기능을 넣어 외부에 아무것도 연결하지 않아도 되도록 만든 것으로 보인다.

그런데, CubeMX(던 CubeIDE 내에 들어가 있는 것이든)에서 자동생성해주는 코드에 이 풀업 기능을 활성화하도록 하는 코드 생성이 빠져 있는 것이 문제였다.

CubeMX 에서 뭘 잘못 설정했으면 오류라도 떴어야 하는데 그런것도 없었고, USB 설정에서 GPIO 에 풀업 설정조차 n/a 로 나타나고 있기에 이 부분은 전혀 예상을 못 하고 있다가 

https://community.st.com/s/question/0D50X0000B5IXCVSQ4/usb-cdc-not-working-with-stm32cubemx521-and-stm32l151

위 링크에서 답을 찾았다. 

결론은 설정이 잘 되었다면, main 함수 시작되는 부분에 어디든

__HAL_SYSCFG_USBPULLUP_ENABLE();

이 매크로함수 하나만 넣어주면 끝.

LL드라이버에도 같은 기능 하는 함수가 있으니 골라쓰면 되겠다.

 

16. STLINK GDB Server 실행시 에러가 발생하고 디버깅모드로 진입되지 않는다. Error MI Command 등의 에러가 발생한다.

일단 이 상황을 설명하자면, 32L476DISCO 에 포함된 STLINK를 외부 모드로 써서 다른 타겟 보드 디버깅을 하고 있다가, 새로 산 STLINK-V3가 있어 이걸 타겟보드에 연결한 다음 디버깅을 수행했더니 타겟 보드와 연결할 수 없다며 와이어링을 점검해보라는 에러가 떴다.

그래서 다시 32L476DISCO의 STLINK로 연결했더니 제목과 같은 에러가 발생했는데..

결론적으로는 여러 STLINK를 쓰면서 Debug Configuration 이 꼬였던 것으로 보인다.

Configuration 을 제거해버리고 다시 만들어서 했더니 잘 된다.

 

17. STLINK-V3로 STM32L151 타겟보드 디버깅 연결 시 연결 에러 발생 문제

GDB Server Frequency 가 Auto로 되어 있을건데, Auto 로 하면 24MHz 로 연결시도 하는 것 같다. STM32L151이 이걸 못 받아주는거 같다. 21MHz 로 하면 된다. Configuration 가서 강제로 수치를 바꿔주자

 

18. UART LL 드라이버로 개발 시, 전원넣고 첫 부팅때는 잘 동작하다가 시스템 리셋(전원은 계속 공급 중)되는 경우(nRESET 또는 IWDG 등) 시스템이 멈추는(실제로는 수신 인터럽트 처리함수만 무한대로 실행하고 있는) 문제가 발생

조건은, 외부에서 계속 해당 UART 채널로 데이터를 송신하여 이 Peripheral 이 데이터를 수신하고 있는 경우이다. 이 때 리셋이 걸리면, 이후 시스템이 재 초기화되면서 UART채널의 인터럽트를 처리하기 위해 LL_USART_EnableIT_xxxx(); 를 수행하는 순간 USARTx_IRQHandler(); 함수가 미친듯이 호출되고 이를 처리하기 위해 다른것을 아무것도 못하는 경우가 발생한다. [왜 처음부팅엔 괜찮은거야?]

타이머보다 UART채널이 NVIC Priority 가 높게 설정해놔서 그런가 타이머도 안돌고...

 

이를 해결하기 위해서는, LL_USART_EnableIT_xxxx(); 를 수행하기 직전에 인터럽트 플래그를 초기화해줘야하는데, 어떤 특정 플래그인지 몰라서 플래그란 플래그는 다 초기화해줬다.(하나하나씩 빼보면 되겠지만 귀찮...)

아래와 같이...

-----------------------------------------------------------------------------------------

LL_USART_RequestTxDataFlush(USART6);

LL_USART_RequestRxDataFlush(USART6);

LL_USART_ClearFlag_PE(USART6);

LL_USART_ClearFlag_FE(USART6);

LL_USART_ClearFlag_NE(USART6);

LL_USART_ClearFlag_ORE(USART6);

LL_USART_ClearFlag_IDLE(USART6);

LL_USART_ClearFlag_TC(USART6);

LL_USART_ClearFlag_LBD(USART6);

LL_USART_ClearFlag_nCTS(USART6);

LL_USART_ClearFlag_RTO(USART6);

LL_USART_ClearFlag_EOB(USART6);

LL_USART_ClearFlag_CM(USART6);

LL_USART_ClearFlag_WKUP(USART6);

 

LL_USART_EnableIT_RXNE(USART6);

-----------------------------------------------------------------------------------------

이러고 나니 리셋걸려도 다시 잘 처리한다. 

이 분의 내용과 같은 문제 아닐까도 생각된다. 방법은 조금 다른 것 같지만.

https://kjt9109.tistory.com/entry/stm32-uart-interrupt-%EB%A9%88%EC%B6%94%EB%8A%94-%ED%98%84%EC%83%81-RXNEIE-disable-overrun

 

동일한 문제가 HAL Driver 기반에서도 발견되었다. 컴파일러 버그인가?

[이 글을 쓰는 시점은 20/09/11, stm32CubeIDE V1.4.2에 Toolchain 은 GNU 7-2018-q2-update 버전이다. 참고]

아무튼 HAL 에서는 이렇게 하니까 된다.

-----------------------------------------------------------------------------------------

  __HAL_UART_FLUSH_DRREGISTER(&huart1);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_PEF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_FEF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_NEF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_OREF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_IDLEF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_TCF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_LBDF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_CTSF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_CMF);

  __HAL_UART_CLEAR_FLAG(&huart1, UART_CLEAR_RTOF);

  // EOB 플래그와 WKUP 플래그는 없다. 스마트카드 모드가 아닌 상태에선 지원안하는듯

if (HAL_UART_Receive_IT(&huart1,&u1rx,1) != HAL_OK){

  Error_Handler();

}

 

19. 특정 위치에 브레이크 포인터를 걸었는데 콘솔에 "handle_vCont_t, Thread already stopped" 출력되는 경우

   FreeRTOS 올리고 디버깅하다가 발생한 문제인데, 아직 정확히 원인은 모르겠으나 버그같음.

   STM32CubeIDE 환경에서 발생하였고, 이 문제와 잘못짠 코드 문제가 맞물려서 뭐가 문제인지 모르겠는 상황에서, CubeIDE 를 전체 종료하고 컴퓨터도 한번 껐다켰고, 다시 실행 후 프로젝트도 모두 Clean 시키고 재빌드하였음.

이후 문제가 해결됨.

  오래 삽질하지 말고 발생했을 땐 한번 모두 정리작업을 해보자. 원인을 찾으면 추후기재하자.

 

20. running 중 갑자기 hardfault_Handler() 로 빠짐

   여러가지 경우가 있겠지만 이번에 겪었던 일을 기재하자면, 

코딩 스타일이 모듈화를 시키고자 드라이버 코드에 UART Handler 포인터를 선언하고 Init 과정에서 UART 핸들을 넘겨받아서 처리하는 식으로 코딩을 하고 있다. 그래서 여느 때와 다름없이 포인터를 선언해두고 Init 과정에서 핸들을 넘겨받아 처리하고 있었는데, 갑자기 UART TX 채널에서 Hardfault 로 빠져버린 것이다.

   이상해서 추적을 해보니 핸들 포인터에 저장된 핸들이 운용 도중 갑자기 다른 값으로 오염되는 현상이 발생했다.

이전에 동작 확인을 해두고 다른 UART채널을 사용하는 코드를 추가했는데, 다른 채널이 460800bps 의 고속채널이라서 Queue 에 쌓이는 속도가 빨라서 그랬는지 모르겠으나 중간에 갑자기 핸들이 오염되고 오염된 주소값을 가지고 UART 전송을 시키려 하니 Hardfault 로 빠지는 현상이 발생한 것.

   핸들 포인터의 오염 원인은 정확하지는 않으나, 메모리 맵을 보니 바로 앞에 할당되었던 고속의 UART 채널 수신버퍼 처리 관련 변수들을 처리하는 과정에서 바로 뒤쪽 어드레스의 데이터들을 잘못 접근해서 오염시킨 것으로 보인다.

volatile, static, L1 캐시 사용 등 뭔 짓을 다 해봐도 증상은 여전했고, 거의 180Byte 의 완충용 더미변수를 중간에 두어 오염을 막았다. 

컴파일러 차이인가 싶어 MDK 쪽에서 돌려봐도 별 차이가 없고, MDK 에서는 추적도 잘 안되었던 고로 일단 CubeIDE GCC 로 사용하고, 당장은 전역 외부변수로 설정된 핸들을 직접 가져다 하드코딩 하기로 했다.

  이 내용을 쓴 지 2년 쯤 지난 거 같은데, 우연히 디버깅을 하다가 원인을 파악하였다. 

STM32F7 을 사용중이었는데, 여기에서 Data cache 를 활성화 할 경우 나타나는 현상으로 이를 disable 하면 사라지는 것을 확인하였다.

단, 내 경우엔 쓸데없이 D-Cache 를 사용하였기에 제거하여 해결하였지만 LWIP 같이 D-Cache 를 요구하는 middleware 를 사용해야 할 경우 어떻게 해야할지에 대해서는 아직 확인해보진 못하였으니 반쪽짜리 해결책인 셈이다.

 

21. CAN1 / CAN2 동시 사용 시 CAN2 수신 인터럽트 미 발생 문제

  STM32F7 사용 중 발생했고, 문제라기 보다는 잘못 했던 것임.

 각 Peripheral 별 필터뱅크를 따로 잡아야 하는데, 똑같은걸로 해서 문제가 되었었던 것.

 여튼, 아래와 같이 설정하자.

if(id==0){
  sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0;
  sFilterConfig.FilterBank = 0;
  sFilterConfig.SlaveStartFilterBank = 0;//
}
else{
  sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO1;
  sFilterConfig.FilterBank = 1;
  sFilterConfig.SlaveStartFilterBank = 1;//
}

 

if(id==0)
  HAL_CAN_ActivateNotification(p_mtr_hCAN[id], CAN_IT_RX_FIFO0_MSG_PENDING);
else if(id==1)
  HAL_CAN_ActivateNotification(p_mtr_hCAN[id], CAN_IT_RX_FIFO1_MSG_PENDING);

 

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  if(hcan->Instance == CAN1){
    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &can1rxhdr, buffer_can1rx);
    //copy to destination
  }
}

void HAL_CAN_RxFifo1MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
  if(hcan->Instance == CAN2){
    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO1, &can2rxhdr, buffer_can2rx);
    //copy to destination
  }
}

 

22. 사용자 Application에서 STM32 기본 Bootloader 로 Jump하기

장비를 만들다보면 Debug 포트에 접근하기 어려울때가 참 많은데, 

이럴때 쓰라고 ST에서 기본적으로 Bootloader 탑재를 해 놓는다.

물론 Bootloader에 접근하는 방법이 처음에 켤 때 BOOT0 핀을 제어해야 한다는 것이 문제였는데,

 

문득 그냥 Application에서 Bootloader로 점프시키면 되는 거 아닌가? 하고 찾아보니 역시나..

수많은 선구자분들이 계셨던 것이었다.

 

아무튼, 많은 선구자분들의 글을 탐독하며 갖다 쓰는데,

이상하게 1200bps로밖에 접근이 안되는 거다.

 

왜죠? 왜때문이죠? 하면서 스코프 찍어보니 1200bps 말고는 전부다 Frame Error 가 발생하는 상황..

USB-RS232 컨버터 문제인가 싶어서 PC에서 온갖 설정도 바꿔보았는데 그것도 아니고...

하여 찾다가 아래 글을 발견하였다.

https://www.os4all.com/103

 

STM32F103 DFU BOOTLOADER로 펌웨어 업데이트하기 내용 업데이트

이전에 올린 글 STM32F103 DFU BOOTLOADER로 펌웨어 업데이트하기의 내용을 수정했습니다. STM32CubeMx의 버전이 바뀌면서 STM32CubeMx가 만들어 주는 코드가 달라져서, 이전의 방법대로 만든 프로젝트는 제

www.os4all.com

그리고 위 블로그분의 글에서 타고간 링크

https://stm32f4-discovery.net/2017/04/tutorial-jump-system-memory-software-stm32/

 

Tutorial - Jump to system memory from software on STM32 - STM32F4 Discovery

One of you are already familiar with STM32 feature of embedded bootloader for software download to flash. This memory is called system memory and is normally accessible with BOOT configuration (either pin hardware or option bytes (later OB) in flash soft

stm32f4-discovery.net

 

아...초기화를 잘못하였구나.

클럭이 잘못되어 통신속도를 못 맞추는 모양이었다.

 

아무튼 그래서 아래와 같이 하여 정상적으로 Bootloader로 점프된다.

프로세서 바꾸면 addr 값 정도만 바꿔주면 될 것 같다.

void resetTosystemBoot(void)

{

    void (*SysMemBootJump)(void);

    volatile uint32_t   addr;

 

    addr = 0x1FF00000//For F7 Series

//  addr = 0x1FF09800; //for H7 Series

 

    //HAL De-Initialization

    HAL_DeInit();

 

    //RCC De-Initialization

    HAL_RCC_DeInit();

 

    //Interrupt Clear

    for(int i = 0i<8i++){

        NVIC->ICER[i] = 0xFFFFFFFF;

        NVIC->ICPR[i] = 0xFFFFFFFF;

        __DSB();

        __ISB();

    }

 

    //Systick initialization

    SysTick->CTRL = 0;

    SysTick->LOAD = 0;

    SysTick->VAL = 0;

 

    //Stack Pointer set to Bootloader

    SysMemBootJump = (void (*)(void)) (*((uint32_t *)(addr+4)));

    __set_MSP(*(uint32_t *)addr);

 

    //Jump

    SysMemBootJump();

 

}

 

23. CAN에서 수신인터럽트 안 되는 상황(2)

  에..뭐 항상 내가 잘못이지만, 나중에 또 같은짓을 반복할거같아서 적자면

  CAN쪽 IRQ핸들러 보면 수신시에

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)

위 콜백을 호출하도록 되어 있는데, 

  그냥 별 생각없이 우선 필터나 필터마스크 설정 대충 해도 인터럽트는 뜨고 나서 되든 안되든 할줄 알았건만,

  아예 뜨지 않더라는 게 문제였다. 

  설정에 문제가 없는데 인터럽트가 되지 않고 있다면 마스크나 필터ID 값을 잘 확인해보자.

  

 

24. 변수 오염 문제

 20번의 연장선상인데, 그때는 안되더니 이번엔 되어서 이상하지만 아무튼 기록.

 

UART 쪽에서 빠른 속도로 데이터가 들어오고 있고, 이를 처리하기 위해 RX를 DMA 로 설정하고 버퍼에 쌓아놓으면서 버퍼 처리를 하도록 구현하였는데,

이상하게 버퍼 주변 주소값을 갖는 다른 변수가 종종 비주기적인 형태로 오염이 발생하는 문제였다.

 

나같은 경우엔 UART 핸들을 포인터로 받아서 모듈쪽에서 처리하도록 구현해놨더니 UART 핸들 포인터변수가 오염되면서 엉뚱한 곳을 Access, 그리고 HardFault 로 빠지게 된 것이었다.

 

레지스터를 봐도 CFSR 레지스터를 보면 PRECISERR 플래그가 떴으니 잘못된 Access인건 맞는데,

어디서 오염을 시켰는지 원인은 아직 찾지는 못했다.

 

그러던 와중, MDK-ARM 으로 생성해서 코드를 돌려보니 CubeIDE에서 발생하지 않던 warning 들이 몇군데 발생하였고, (변수타입 캐스팅 등에서 주로 발생했던 것 같음) 

이 warning 들을 해결하고 MDK-ARM에서 빌드해서 돌려보니 문제가 발생하지 않았다.

 

그리고, 이렇게 수정된 코드를 다시 CubeIDE에서 빌드해서 돌려보니 이제 문제가 발생하지 않는다(!?!)

어딘가에서 포인터를 잘못 썼나 싶기도 한데, 수정 전 코드를 홀라당 날렸다(...)

앞으로는 빌드를 양쪽 다 해보고 warning도 격파해야겠다(;;;)

20번 Issue 원인이 파악되어 엄한 땜빵책을 제거함.

 

25. Multiple Definition Error 관련 문제

어느 버전에서부터 발생했는지 모르겠는데 (계속 사용하던 중에 최근부터 겪은 현상이라...),

CubeIDE에서 프로젝트 생성하고 처음엔 빌드가 아무런 문제 없이 되는데 ioc 파일 수정해서 다시 프로젝트 재생성시키고 빌드하면 어느 순간에서부턴가 multiple definition of 'SystemCoreClock' .... 하면서 링커에서 에러가 발생한다.

에러에 써있는 함수가 SystemCoreClock 이 아닐 수도 있지만, 일단 문제는


"system_stm32abxx.c (ab는 사용하는 MCU따라 달라진다) 파일이 프로젝트 안에 두개가 생성되고 빌드된다."

 

그래서 같은 함수가 두개라 그런건데, 

CubeMX로 프로젝트 생성시키면 Project/Core/Src/system_stm32abxx.c 파일이 함께 생성되는데,

엉뚱하게 Project/Drivers/CMSIS/Device/ST/STM32abxx/Source/Templates/ 경로에도 같은 파일이 또 있다.

예전에 생성된 다른 프로젝트 보니 해당 경로에 아무것도 없었음...

따라서, 이 파일을 지워주던가, 해당 폴더를 exclude from build 시키면 되는데

파일을 지워주면 CubeMX로 프로젝트 재생성 시 친절하게 또 살려놓는다.

그러므로 그냥 Templates 폴더 선택하고 alt+Enter 눌러서 Exclude resource from build 를 체크인 하자.

 

 

26. UART RX DMA 설정시 계속 버퍼의 첫 바이트에만 수신되는 문제

우선, 모든 경우에 발생하는 문제인지는 모르겠으나 발생했던 환경은 다음과 같다.

모든 환경에서 발생할 수 있는 문제로 보임. 자세한 내용은 아래에...

 Target :STM32H750 (STM32H750B-DK board)

 개발환경 : STM32CubeIDE 1.10.1

 

CubeIDE 내 Code generation Tool 을 통해 USART 설정을 하고, DMA RX 도 circular 모드로 설정하였다.

이후 코드에서

HAL_UART_Receive_DMA(handle, pbuffer, buflen);

의 코드를 실행하여 buffer 에 데이터가 차는 것을 기대하였으나,

buffer 의 첫번째 바이트에만 계속 데이터가 중복으로 써지는 문제가 있었다.

 

이번에도 역시 뭘 잘못했나 찾다가, 기존에 만들었던 코드에서는 잘 돌아가는데 왜 안되나 하고 차이점을 찾아보니

 

cubeMX 에서 자동으로 생성시켜 준 MX_USART3_UART_Init() 함수 안에서 HAL_UART_Init() 을 해주기에 별 문제 없겠지 하고 믿고 있었으나, 그게 문제였던 것 같다.

 

아주 단순히, 

  HAL_UART_DeInit(&huart3);
  HAL_UART_Init(&huart3);

이 두줄만을 다시 실행했더니, 이후부터는 DMA buffer 에 순차적으로 데이터가 잘 쌓임을 확인하였다.

 

HAL Driver 의 전체적인 버그인지, H7 에서만 나타나는 버그인지는 확인하긴 귀찮아 안해보지만, 혹시 같은 문제가 발생하는 누군가에게 도움이 될까 적어둠

ST에 문의결과 Peripheral 이 DMA 사용하여 수신하기 위해서는 DMA가 먼저 활성화/초기화 된 이후 Peripheral 을 활성화 해야 이후 수신시도 시 peripheral data register에 값이 들어왔을 때 DMA request 를 하고 설정된 destination 주소로 데이터 전송이 가능하다고 함.

 

확인해보니 CubeMX 가 자동생성해준 코드에서  MX_USARTx_UART_Init() 을 먼저 수행하고 MX_DMA_Init()을 나중에  하는 순서로 생성되어 있었고, 이를 코드상에서 바꾸어 주었더니 정상동작 함.

위에서 해결했던 해결책은 이미 MX 에서 생성된 모든 함수가 다 수행되고 나서 다시 UART 를 init 하였기 때문에 순서가 지켜져서 정상 작동한 거였음.

 

CubeMX 에서 코드 자동 생성 시 이 순서를 변경할 수 있게 되어있는데, 문제는 몇몇 경우에 MX_DMA_Init() 과 MX_USARTx_UART_Init() 함수의 순서를 변경하는 버튼이 비활성화 되어 있어 순서를 뒤집을 수 없는 경우가 발생하고 있다는 것.

이 부분 또한 ST에 확인해보니 CubeMX 의 버그로 보여 해당 부서에 수정 요청하겠다는 회신을 받았음.

 

 

발생할때마다 계속 적어나가자.