【NUCLEO-F446RE试用体验】+8.DMA串口接收不定长数据

  • lygo
  • LV4工程师
  • |      2017-09-14 15:12:13
  • 浏览量 924
  • 回复:3
前面用了串口通信的轮询方式,那么这篇文章就要用DMA来接收数据了。如何接收不定长数据呢?那么这篇帖子就会为大家讲解。其实就是利用STM32单片机中的IDLE中断,所以利用这个中断,可以接收不定长字节的数据IDLE就是串口收到一帧数据后,发生的中断。什么是一帧数据呢?比如说给单片机一次发来1个字节,或者一次发来8个字节,这些一次发来的数据,就称为一帧数据,也可以叫做一包数据。一帧数据结束后,就会产生IDLE中断。这个中断真是太有用了。省去了好多判断的麻烦。详情请见下篇帖子:(不定长数据接收的原理http://blog.csdn.net/hyk0601/article/details/51698489 那么什么是DMA呢?(其实上面帖子也提到过)即就是直接内存存取技术(DMA)方式。所谓直接传送,即在内存与IO设备间传送一个数据块的过程中,不需要CPU的任何中间干涉,只需要CPU在过程开始时向设备发出“传送块数据”的命令,然后通过中断来得知过程是否结束和下次操作是否准备就绪。那么就进入配置吧: (1)DMA的配置: 其余的配置和上面那篇帖子差不多,唯一区别就是要使能DMA,及配置它,如下图: 这里我们只用了DMA的接收所以使能接收就可以了。 (2)、编写用户代码,如下:(1)、主函数main.c代码如下:
void SystemClock_Config(void);

void Error_Handler(void);



extern UART_HandleTypeDef huart2;

extern DMA_HandleTypeDef hdma_usart2_rx;



#define RX_BUFFER_SIZE       32         //用于接收到一帧数据的最大长度

uint8_t Rx_Buffer;      //保存接收到的一帧数据

uint32_t RxLength;                      //实际接收到的一帧数据的长度

uint8_t RxFlag;                         //接收到一帧数据标记

/*******************************************************************************

串口空闲中断回调函数,在stm32f4xx_it.c 中的 void USART3_IRQHandler(void) 中调用

*******************************************************************************/



void UsartIdle_Callback(void)

{

  uint32_t tmp1, tmp2;



  tmp1 = __HAL_UART_GET_FLAG(&huart2,UART_FLAG_IDLE); 

  if((tmp1 != RESET))

  { 

    __HAL_UART_CLEAR_IDLEFLAG(&huart2);



    HAL_UART_DMAStop(&huart2);

    tmp2  = hdma_usart2_rx.Instance->NDTR;   //读取还没有被传送的个数             

    RxLength =  RX_BUFFER_SIZE - tmp2;       //DMA缓存大小减去没有被传送的个数,就等于已经被传送的个数,也就是接收到的个数。                      

    RxFlag = 1;                              //接收完成标志置位

  }

}





/*******************************************************************************

解析接收到的一帧数据,根据实际应用修改

*******************************************************************************/

void Rx_Handle(void)

{

  char *pString;

  if(RxFlag)

  {

    RxFlag = 0;

    pString = "valid input\r\n";

		switch(RxLength)

		{

			case 1:break;//	

			case 4:

						if(strncmp((char *)Rx_Buffer, "lygo", 4) == 0)//判断是不是"lygo",是的话执行事情

						{

							  pString = "success\r\n";

						}break;

			

			case 5:

						if(strncmp((char *)Rx_Buffer, "STM32", 5) == 0)//是STM32的话打印型号出来

						{

								pString = "STM32NUCLEO F446RET6\r\n";

						}break;

			case 6:

				   if(strncmp((char *)Rx_Buffer, "LED-ON", 6) == 0)//是"LED ON"的话点亮LED5

						{

							  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);

								pString = "LED ON\r\n";

						}break;

			case 7:

				   if(strncmp((char *)Rx_Buffer, "LED-OFF", 7) == 0)//是"LED OFF"关闭LED5

						{

							  HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);

								pString = "LED OFF\r\n";

						}break;

			default :break;

		}

		HAL_UART_Transmit(&huart2,(uint8_t *) pString, strlen(pString), 1000);      //轮询方式发送

    HAL_UART_Receive_DMA(&huart2, Rx_Buffer, RX_BUFFER_SIZE);                  //重新使能DMA接收

  }

}





int main(void)

{

  HAL_Init();



  SystemClock_Config();

  MX_GPIO_Init();

  MX_DMA_Init();

  MX_USART2_UART_Init();



  __HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);                     //使能空闲中断

  HAL_UART_Receive_DMA(&huart2, Rx_Buffer, RX_BUFFER_SIZE);        //启动串口接收          

  printf("start....\r\n");

  while (1)

  {

     Rx_Handle();

  }



}
(2)、在it.c添加以下函数: 详情请见这这两篇帖子 http://bbs.21ic.com/icview-1472850-1-1.html?_dsign=53a0296ahttp://www.stmcu.org/module/forum/thread-602761-1-1.html (3)、效果演示: 好的本次实验完成!!!谢谢大家!!!最后上传实验文档如下:
  • 0
  • 收藏
  • 举报
  • 分享
我来回复

登录后可评论,请 登录注册

所有回答 数量:2
黄莨元 2017-09-17
DMA是不是很神奇,哈哈:lol
0   回复
举报
发布
lygo 回复 2017-09-17
还不错诶
0   回复
举报
lygo 2017-09-14
咦咦咦 中间代码居然错位了。:L:L
0   回复
举报
发布
x
收藏成功!点击 我的收藏 查看收藏的全部帖子