본문 바로가기
  • SpokeHouse
STM32/STM32F

STM32F103 - UART DMA로 문자열 한번에 받기

by SpokeHouse 2023. 11. 25.
728x90
728x90

 

안녕하세요 오토입니다.

 

통신 모듈이나 기타 인터페이스에서 문자열이 정해진 프로토콜이 아닌 길이가 변경되서 들어오면 

 

일반 HAL 드라이버에서 리시브로는 잘안되고 너무 복잡스런 상황이 생깁니다.

 

하나씩 받아서 링퍼버 만든거에 넣고 링버퍼 확인해서 꺼내오고 초기화 해주고 

 

해봤는데... 항상 느끼지만 복잡하면 실수와 버그가 늘고.. 하다가 머리털 다빠질것같아 여기저기 뒤적거리다가 괜찮을것

 

같은 소스 땡겨다가 수정좀 했더니 잘돌아가서 메모 합니다

 

 

일단 UART2 DMA로 사용할거고 세팅 해줍니다.

 

통신 모듈이 BAUD RATE가 9600이라 맞춰주고

DMA 챕터? 카테고리? 에서 ADD 해줍니다.

 

인터럽트 켜주시고 저장하고 코드 제네레이션 해줍니다.

 

메인에서 UART2 이니셜 되었는지 확인해주고 다음으로 넘어갑니다.

 

 

UART-DMA로 분리된 헤더 및 소스파일 만들어주시고

 

변수필요한거부터 만들어보겠습니다. 저도 여기저기서 계속 수정한거라 안쓰는 변수 발견하시면 지워서 쓰시면 되고 없으면 만들어서 쓰시고...

 

uint8_t RxBuffer[MAX_RX_BUF + 16];
uint8_t RxBuf2[MAX_RX_BUF + 16];
uint8_t message[MAX_RX_BUF];
uint16_t wrPtr = 0;
uint16_t rdPtr = 0;
uint16_t rcvdLen = 0;
uint8_t rcvFlag = 0;
uint8_t RxBuf3[MAX_RX_BUF + 16];
uint32_t totalSentBytes = 0;
uint32_t totalRcvdBytes = 0;
uint16_t sentbytes = 0;
uint16_t _HT_Count = 0;
uint16_t _TC_Count = 0;
uint16_t _IDLE_Count = 0;
uint8_t count = 0;
uint8_t dma_buf[MAX_RX_BUF + 16];
uint16_t ms_count = 0;
uint8_t onesecondElapsed = 0;

헤더에는 2개정도 있네요

#define MAX_RX_BUF 	512
#define ARRAY_LEN(x)	(sizeof(x) / sizeof((x)[0]))

 

void uart2DMAinit()
{

	__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);
	HAL_UART_Receive_DMA(&huart2, RxBuffer, MAX_RX_BUF + 16);
	printf("Uart2_DMA_init_ok\r\n");
}

init 함수 만들어줍니다. 

printf 함수 세팅은 지난 포스팅 보시면 나와있으니까 세팅하셔서 편히 쓰시구요

2023.11.04 - [M's Work] - stm32IDE - uart를 printf로 세팅하는법

 

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	_TC_Count++;
	rcvFlag = 1;
}
//void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart)
//{
//	_HT_Count++;
//	rcvFlag = 1;
//}


//void UART_IDLECallback(UART_HandleTypeDef *huart)
//{
//	_IDLE_Count++;
//	rcvFlag = 1;
//}

이건 IDLE 이나 HALF 쓸때 쓰시면됨.

 

콜백 함수 불러다가 flag 맥여주시구요.

 

오랜만에 core에 인터럽트 소스파일 열어주셔서

 

void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
	  if(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_IDLE) != RESET)
	  {
		  __HAL_UART_CLEAR_IDLEFLAG(&huart2);
//		  UART_IDLECallback(&huart2);
		  HAL_UART_RxCpltCallback(&huart2);
	  }
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

콜백함수 만들어논거 이거가지고 노세요.. 해주고

 

리시프 플래그로  전체 수신 func 만들어줍니다. 메모리도 초기화 해주고 

 

void UART2_receive_func()
{

	  if(onesecondElapsed)
	  {
//		  onesecondElapsed = 0;
//		  count++;	// increment count
//		  HAL_GPIO_TogglePin(LED_GREEN_1_GPIO_Port, LED_GREEN_1_Pin);
//		  memset(dma_buf, 0, MAX_RX_BUF + 16);
//		  printf("message: [%d] bytes, %s\r\n", strlen(message), message);
//		  sprintf((char *)dma_buf, "UART2: %s\r\n", message);
//		  printf("dma_buf: %s\r\n", dma_buf);
//		  sentbytes = strlen((const char*)dma_buf);
//		  HAL_UART_Transmit_DMA(&huart1, dma_buf, sentbytes);
//		  printf("UART2 HAL_UART_Transmit_DMA sent: %d bytes\r\n", sentbytes);
//		  totalSentBytes += sentbytes;
	  }
	  if(rcvFlag)
	  {
		  rcvFlag = 0;
		  memset(dma_buf, 0, MAX_RX_BUF + 16);
		  UART_Data_Process(&huart2);
		  totalRcvdBytes += rcvdLen;
//		  printf("RxBuf2: [%d] bytes, %s\r\n", strlen(RxBuf3), RxBuf3);
		  printf("%s",RxBuf3);
//		  printf("_HT_Count: %d, _TC_Count: %d, _IDLE_Count: %d, rdPtr: %d, wrPtr: %d, rcvdLen: %d, totalSentBytes: %d, totalRcvdBytes: %d\r\n", _HT_Count, _TC_Count, _IDLE_Count, rdPtr, wrPtr, rcvdLen, totalSentBytes, totalRcvdBytes);

	  }
}

정보 uart로 출력문 많은데 주석 풀어서 보시면 되구요 

 

이제 받은데이터 버퍼 만들어 논곳에 Copy하는 func 만들어 주시구요

 

void UART_Data_Process(UART_HandleTypeDef *huart)
{
	wrPtr = ARRAY_LEN(RxBuffer) - huart->hdmarx->Instance->CNDTR;
	if(wrPtr != rdPtr)
	{
		memset(RxBuf3, 0, MAX_RX_BUF + 16);

		if (wrPtr > rdPtr)
		{
			rcvdLen = wrPtr - rdPtr;
			memcpy(RxBuf3, RxBuffer + rdPtr, rcvdLen);
		}else
		{
			rcvdLen = ARRAY_LEN(RxBuffer) - rdPtr;
			memcpy(RxBuf3, RxBuffer + rdPtr, rcvdLen);
			if(wrPtr > 0)
			{
				rcvdLen += wrPtr;
				memcpy(RxBuf3 + rcvdLen, RxBuffer, wrPtr);
			}
		}
		rdPtr = wrPtr;
	}
	receive_uart_command();
}

여기까지만 해주셔도 rcvFlag =1 일때 printf문 세팅해논 uart1로 RxBuf3에 카피 해놓은 수신 데이터들 쭉쭉 밷습니다.

 

근데 확인만 해서는 좀 그렇고 비교해서 동작 시키는것도 필요하니까 같이 메모 해놓죠

 

char turn_on[] = "MAST0"; //비교를 위한 string Data
char turn_off[] = "MASTX"; //비교를 위한 string Data

void receive_uart_command()
{

	char *ptr1_a = strstr(RxBuf3, turn_on);//short string
	char *ptr2_a = strstr(RxBuf3, turn_off);//short string
    
 if(ptr1_a !=NULL )//light_turn_on
	{
		printf("OK Turn_on the light------------------------\r\n");

		HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //출력 pwm start
		HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //출력 pwm start
		htim1.Instance->CCR1 = 800; //출력 pwm duty 변경
		htim2.Instance->CCR1 = 800;//출력 pwm duty 변경

	}
	else if(ptr2_a != NULL)//light_turn_off
	{
		printf("OK Turn_OFF the light------------------------\r\n");
		htim1.Instance->CCR1 = 0;//출력 pwm duty 변경
		htim2.Instance->CCR1 = 0;//출력 pwm duty 변경
		HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);//출력 pwm stop
		HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1);//출력 pwm stop
   }

	memset(RxBuf3, 0, MAX_RX_BUF);

 }

strstr로 문자열 비교해서 if 문에 해당하면 pwm 하고 duty 변경하는게 필요해서 이렇게 쓰고있구요

 

warning이 문자열 비교할때 뭐 타입이 안맞다는거같은데 일단 동작은 잘해서 그냥 쓰고있는데 아시는분은 알려주시면 대단히 감사드리겠습니다.

 

뭐 이렇게 하면 전체적인 uart DMA 수신은 잘되는걸로 확인됩니다.

 

그럼 오늘도 대단히 맛있는거 많이 드세요

 

728x90
728x90

댓글