打印
[STM32F4]

【NUCLEO-F446ZE】在Cube下实现串口不定长数据串接收

[复制链接]
1567|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
串口基本空闲中断和DMA实现不定长数据接收,在实现应用中用得很多,特别是Modbus协议。现在,有了cube,在初始化配置上,确实方便了很多,但从标准外设库转过来,还是需要适应一段时间。在工作中也用到了这一功能,借着二姨家的活动,把这点方法分享给大家,有不对之处,欢迎指正。
1.首先选择单片机,我是直接选择的Nucleo-144开发板。

2.可以看的出来,板上串口使用的是USART3,当然,我们还是要在左侧的功能树中将USART3使能,Asynchronous模式。

3.使能串口全局中断,实际只用到其中的空闲中断。

4.添加DMA,选择USART3_RX。其它保持默认,如图。

5.OK ,保存,生成工程文件。


6.代码。
/* USER CODE BEGIN 0 */
#define RX_BUFFER_SIZE       32         //用于接收到一帧数据的最大长度
uint8_t Rx_Buffer[RX_BUFFER_SIZE];      //保存接收到的一帧数据
uint32_t RxLength;                      //实际接收到的一帧数据的长度
uint8_t RxFlag;                         //接收到一帧数据标记

/*******************************************************************************
串口空闲中断回调函数,在stm32f4xx_it.c 中的 void USART3_IRQHandler(void) 中调用
*******************************************************************************/
void UsartIdle_Callback(void)
{
  uint32_t tmp1 = 0U, tmp2 = 0U;

  tmp1 = __HAL_UART_GET_FLAG(&huart3,UART_FLAG_IDLE);
  if((tmp1 != RESET))
  {
    __HAL_UART_CLEAR_IDLEFLAG(&huart3);

    HAL_UART_DMAStop(&huart3);
    tmp2  = hdma_usart3_rx.Instance->NDTR;            
    RxLength =  RX_BUFFER_SIZE - tmp2;                           
    RxFlag = 1;
  }
}

/*******************************************************************************
解析接收到的一帧数据,根据实际应用修改
*******************************************************************************/
void Rx_Handle(void)
{
  char *pString;
  if(RxFlag)
  {
    RxFlag = 0;
    pString = "unknown\r\n";
    if(RxLength == 3)
    {
      if(strncmp(Rx_Buffer, "MCU", 3) == 0)
      {
        pString = "MCU:STM32F446ZET6\r\n";
      }
    }
    else if(RxLength == 5)
    {
      if(strncmp(Rx_Buffer, "Board", 5) == 0)
      {
        pString = "Board:Nucleo-144\r\n";
      }
      else if(strncmp(Rx_Buffer, "Flash", 5) == 0)
      {
        pString = "Flash:512 KB Flash\r\n";
      }
    }
    else if(RxLength == 4)
    {
      if(strncmp(Rx_Buffer, "SRAM", 4) == 0)
      {
        pString = "SRAM:128+4 KB SRAM\r\n";
      }
    }
    HAL_UART_Transmit(&huart3, pString, strlen(pString), 1000);
    HAL_UART_Receive_DMA(&huart3, Rx_Buffer, RX_BUFFER_SIZE);                   //重新启动串口接收         
  }
}

/* USER CODE END 0 */

int main(void)
{

  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration----------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART3_UART_Init();

  /* USER CODE BEGIN 2 */
  __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);                                  //使能空闲中断
  HAL_UART_Receive_DMA(&huart3, Rx_Buffer, RX_BUFFER_SIZE);                     //启动串口接收                        
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
    Rx_Handle();
  /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */

}


其他函数由Cube生成,我就不复制了,主要增加两个函数,一个是串口空闲中断的处理函数,一个是数据帧的解析函数。


void USART3_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_IRQn 0 */
  UsartIdle_Callback();
  /* USER CODE END USART3_IRQn 0 */
  HAL_UART_IRQHandler(&huart3);
  /* USER CODE BEGIN USART3_IRQn 1 */

  /* USER CODE END USART3_IRQn 1 */
}


7.实际效果


8.上传工程文件
F446Test.zip (2.5 MB)

沙发
Serge_Ding|  楼主 | 2016-4-27 08:21 | 只看该作者
自己顶一下

使用特权

评论回复
板凳
cbb0416| | 2016-4-27 18:36 | 只看该作者
使用Stm32CubeMx生成代码固然很方便,但是我觉得对于不定长度的串口收发效率不是很高,我一般都是把原来的处理给屏蔽掉,然后自己重新写处理函数,下面说说我在应用中的处理方式:
1. 发送
首先建个队列,把要发送的数据按顺序放到队列中,然后开启串口TXE中断,每次中断检查队列是否为空,不为空时发送下一个数据。这样的方式是采用中断的方法,效率较高。当然能结合DMA方式就更好了,但是一直也没时间做。
2. 接收
首先建个队列,开启串口RXNE中断,每次中断读取数据往队列里放,并开启定时器或更新定时器,如果定时器溢出就认为接收完成了,然后处理数据。这样的方式也是采用中断的方法,且中断处理函数中处理内容较少。但是对于接收到的数据不是连续发送过来的,可能就需要增加一些处理逻辑了。

使用特权

评论回复
地板
豆腐块| | 2016-4-27 21:36 | 只看该作者
楼主厉害,支持一下

使用特权

评论回复
5
Serge_Ding|  楼主 | 2016-4-28 16:24 | 只看该作者
cbb0416 发表于 2016-4-27 18:36
使用Stm32CubeMx生成代码固然很方便,但是我觉得对于不定长度的串口收发效率不是很高,我一般都是把原来的 ...

我以前的发送和你的方法类似,建一个环形队列,应用程序只管将要发送的数据往这个队列中填,在发送完成中断中检查这个队列是否为空,直到为空时停止发送。接收和你有点不太一样,我的接收也是一个环形队列,接收中断也只管把接收到的数据往这个队列中填,由应用程序来检查这个队列中是否有新数据,再做解析。
我今天发的这个例子主要针对接收,效率应该比之前的方法高很多,一帧数据的接收,只产生一次中断,而且数据的保存都是由DMA完成的,应用程序只需要检查是否有一帧数据接收完成,再做出相应解析。如果用了RTOS,还可以作为一个事件,来触发串口数据解析任务。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

4

主题

417

帖子

1

粉丝