안녕하세요. 오늘은 Serial 통신을 뚫어볼 계획이다. 다른 모듈 개발에 앞서 통신을 먼저 뚫어 놓는 것이 좋다. Nucleo 보드의 경우 Debugging이 가능하지만 나중에 주행 상황에서 체크하고 하려면 필요하다. 책상 앞에서만 볼 수는 없으니까.
본론에 들어가기 앞서 Serial 통신에 대해 설명해야 하는데, Serial 통신에 대한 개념은 이전에 다루었던 블로그 글로 대체하도록 하겠다.
- Serial 통신이란? : https://enginbear.tistory.com/25
[아두이노 기초] Serial 통신
1. 기초이론 개발 경험상 아두이노를 포함해서 임베디드를 개발하는 데 있어 통신 서비스는 매우 중요합니다. 우선 무얼 개발할 지부터 개발 완성에 다다를 때까지 디버깅을 하는데 큰 도움을
enginbear.tistory.com
먼저 통신을 개발하기 앞서 통신 속도를 결정하는 게 시작이다. 학부 때부터 통신 속도는 115200 bps로 설정하는 게 룰이었다. 가끔 몇몇 수업에서는 기본 전송 속도인 9600 bps를 사용했었지만 뭔가 난 더 큰 숫자여서 115200 bps를 쓸 거다. 이에 대해서는 Error Rate랑 해서 계산하는 것까지 설명해야 하는데 아묻따 일단 사용하자.
그리고 두번째로 통신을 사용하는 데 있어 방법 결정이 필요하다. Serial으로 하는 것은 동일하나 그 과정에서 선택할 수 있는 방법이 있다. 예를 들어 DMA? 학부 시절 DMA의 존재를 몰랐었는데 한양대 지능형 모형차 경진대회에 나가면서 알게 되었다. 정말 마법 같은 모듈인데 정말 신세계였다. 아두이노에서는 접할 수 없는 모듈인데, Direct Memory Access라는 이름으로 CPU가 직접 메모리에 데이터를 옮기는 작업을 덜어주는 역할을 하게 된다. 우리가 개발한 Serial 통신에서는 통신 버퍼에 송수신 과정에서 쌓거나 쌓인 데이터를 CPU 혹은 Core가 옮겨주어야 하는데, 이를 DMA가 대신해주고 작업이 끝났다는 결과만 Core에 전달하게 된다. 이러한 동작 특성 덕분에 우리는 Software를 Async 하게 구현이 가능하다.
다시 본론으로 돌아가서 Serial 통신을 개발하기 위해서 IDE에서 통신 속도를 설정하자. 앞서 깔아놓은 대로 115200으로 설정할 계획이며, 그 외 다른 옵션은 건들 필요가 없다.
그 다음에 앞서 설명에서 찬양한 DMA에 대한 설정을 해줘야 한다. 설명은 거창했지만 설정은 별거 없다. Tx/Rx에 대해 두 가지 설정을 열어주면 되며, 나머지는 자동으로 설정된다. Rx의 경우 UART(Peripheral)에서 메모리로 옮겨주는 작업을 하므로 Direction이 아래와 같이 설정되는 것이며, Tx는 반대로 Memory에서 UART(Peripheral)로 옮기기에 저렇게 설정되는 것이다.
DMA까지 설정했으니, 이제 Interrupt를 설정해준다. 우린 통신을 요청한 후에 끝날 때까지 그 자리에서 기다릴 마음의 여유가 없다. 통신을 요청해 둔 뒤 Interrupt를 통해서 완료 알림을 받아보자! DMA를 설정하게 되면, 아래와 같이 자동으로 DMA Interrupt가 걸리게 되는데, 이를 활용해서 Callback 함수만 구현해 주면 된다.
* Callback 함수란?
특정 동작(ex. Interrupt, Function Call)들이 끝난 이후 호출되는 함수로, Event 이후 동작에 대해 기술하는 함수이다.
기본적으로 HAL Library 에서 제공하는 UART RX DMA를 사용하기 위해서는 반복적으로 HAL_UART_Receive_DMA함수를 Call 해서 대기시켜주어야 한다. 매개 변수로는 통신을 하고자 하는 Instance와 수신받을 Buffer 그리고 수신할 Size이다. (해당 Size만큼 수신받아야 Callback함수를 띄운다)
해당 함수를 호출한 시점에서 내가 원하는 만큼의 Data가 들어왔을 경우 DMA Complete Event가 발생하여 HAL_UART_RxCpltCallback함수가 호출되게 된다. 특이하게도 반복적으로 데이터 수신을 원할 경우 Callback 함수가 끝나는 시점에 다시 HAL_UART_Receive_DMA함수를 걸어 수신 DMA Interrupt를 걸어주어야 원활한 수신이 된다.
bool EhalSerialLi_Init(UART_HandleTypeDef *huart){
HAL_StatusTypeDef err;
bool returnValue;
err = HAL_UART_Receive_DMA(huart, &vEhalSerialLi_RxRawData, 4);
if (err == HAL_OK){
returnValue = TRUE;
}
else{
switch(err){
case HAL_ERROR:
break;
case HAL_BUSY:
break;
case HAL_TIMEOUT:
break;
default:
break;
}
returnValue = FALSE;
}
return returnValue;
}
Init의 경우 Rx DMA를 걸고 해당 함수에 대한 정상 수행 여부에 대해 Return 받게 된다. 간단하게 구현한다면 굳이 필요는 없지만 나중에 코드와 시스템이 거대해지게 된다면 해당 에러 처리 부분이 도움이 많이 된다.
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART6){
EhalSerialLi_RxFunc(huart);
}
}
정상적인 Init이 끝난 후 내가 설정한 대로 Rx가 4 Byte 들어온 경우 위와 같은 Callback함수가 뜰 것이다. HAL Library의 경우 채널에 상관 없이 동일한 Callback함수가 뜨기 때문에, 현재 RxCallback을 실행한 Instance가 누군지 확인하고 로직을 구현해야 한다. 우리는 앞서 USART6에 대해 설정했기에 USART6에 대해 if문을 걸어 Reaction을 설정한다.
void EhalSerialLi_RxFunc(UART_HandleTypeDef *huart){
uint8_t i, checksum;
uint8_t tempBuf[4];
for(i = 0;i < 4;i++){
tempBuf[i] = vEhalSerialLi_RxRawData[i];
}
vEhalSerialLi_TestChecksum = SrvCrc_Cal_Crc8(tempBuf, 3);
HAL_UART_Receive_DMA(huart, &vEhalSerialLi_RxRawData, 4);
}
나는 UART Rx를 4Byte씩 받아서 따로 버퍼에 옮겨담은 후 CRC Check를 수행할 계획이다. 다음 글에서 다루겠지만 간단히 말해서 입력받은 데이터 패킷(4 Byte)에 대해 유효성을 검사한다는 뜻이다. 아까 말했듯 Rx에 대한 처리가 끝났으니 다시 DMA를 걸어준다.
이렇게 되면 수신에 대한 구현은 끝났다. 이제 송신하는 부분에 대해 구현하면 되는데, 수신에 비해 송신은 더 간단하다. HAL_UART_Transmit_DMA함수를 Rx와 동일하게 사용하면 되는데, 매개 변수는 전송하고자 하는 채널의 Instance, Buffer, Size이다. Tx도 마찬가지로 Intrrupt 설정을 해두었기에 송신이 완료되었을 경우 Callback함수가 뜨게 된다. 간단한 시스템의 경우 Tx Callback에서 건드릴 부분이 없지만, 내가 구상한 시스템에서는 Tx Callback에서 할 일이 많다. 많은 서비스 들에서 로직이 끝나고 데이터 송수신이 필요한 경우 송신 대기를 걸어두게 되고, UART모듈은 한 패킷씩 계속 송신할 계획이다.
err = HAL_UART_Transmit_DMA(huart, &vEhalSerialLi_TxRawData, 4);
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART6){
}
}
오늘 간략하게 송수신에 대해 설명하였는데, 아직 미구현된 서비스들과 이어 설명하여 자세히 다루지 못하였다. 추후 개발하면서 해당 글과 연관 지어 추가 설명 적어놓도록 하겠다.
Project Github 주소: https://github.com/DongjinJ/Whale_Mk1
GitHub - DongjinJ/Whale_Mk1
Contribute to DongjinJ/Whale_Mk1 development by creating an account on GitHub.
github.com
유튜브 주소: https://youtu.be/7HR-a1uhJZQ
'프로젝트 일지 > Whale Car' 카테고리의 다른 글
[Whale Car] STM32H745ZIQ 보드 연결 (0) | 2024.10.03 |
---|---|
[Whale Car] 개발 보드 변경 (2) | 2024.09.19 |
[Whale Car] Scheduler 만들기 (0) | 2023.06.19 |
[Whale Car] 1ms Timer 만들기 (0) | 2023.05.29 |
[Whale Car] RC카 만드는 이야기 (0) | 2023.05.29 |