STM32CubeMX学习笔记3-串口通信2(不定长数据)

        在上一章中学习到了串口如何接收定长的数据,但我们串口接收到的数据往往是不定长的,利用定长接收,就会导致数据遗漏和丢失,因此本章将学习如何接收不定长的数据。

1、STM32CubeMX配置:

   STM32CubeMX的配置和上一章使用DMA模式的配置一致:

   RCC设置外接HSE,时钟设置为168M

   USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位,其他保持默认

 打开串口中断。

打开DMA。

这里DMA模式选择普通模式,其他默认即可

把串口中断和DMA的中断优先级也改成1.

2、普通中断接收   

        由于数据传输中,数据一般都会以‘\r\n’  为结束符,因此可以以此作为一串数据的结束标志。

      上一章了解到函数HAL_UART_Receive_IT(&huart1,RxMsg,n);是接收到n个数据后触发中断,然后进入中断回调函数。可将n设置为1,那么每次接收到一个数据就会进入中断,在中断回调函数中将数据存放到存储数组中,并判断是否已经接收到结束标志,当接收到结束标志时则判定数据已经接收完毕,也就是接收到一个字节就进入中断处理,直到接收到结束符后再将数据输出,从而实现接收不定长数据。

        在usart.c文件中添加存储数组、编写中断回调函数。

/* USER CODE BEGIN 1 */
extern uint8_t RxMsg[200],temp,RxMsg_len;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
	
  if(huart->Instance == USART1){
		
		RxMsg[RxMsg_len++]=temp;
		if(RxMsg[RxMsg_len-1]==0x0A&&RxMsg[RxMsg_len-2]==0x0D)
			{
				HAL_UART_Transmit(&huart1,RxMsg,RxMsg_len,100);//将接收到的数据发送出去
				RxMsg_len=0;        //数据长度清零
				memset(RxMsg,0,200);//清除接收到的数据,防止影响下一次数据接收
			}
			HAL_UART_Receive_IT(&huart1,&temp,1); //再次开启接收中断
  }
}

main函数中先启动串口中断。

int main(void)
{
///**********
   HAL_UART_Receive_IT(&huart1,&temp,1);//启动串口中断
///**********
  while (1)
 {
    ///**********
  }
}

编译后下载验证

        可以看到利用串口助手给单片机发送不定长的数据,单片机能够将数据完整的返回来。这里利用的是以“\r\n”作为数据的结束标志,也可以换成其他的结束标志。

        这种模式的优点是没有数据量的限制,但缺点就是对CPU的占用较大(),对于CPU占用敏感的场合不建议使用。

3、空闲中断接收:

        单片机在接收完一帧数据时,到开始接收下帧数据之间会存在一个时间间隔,这个时间间隔中串口是空闲的,也就是可以判断串口在超出一定时间没接收到新数据,则可以判断已经接收到一帧数据,在51单片机中可以利用定时器来实现这个时间间隔判断,而stm32在这个时间间隔中可以产生一个空闲中断,因此可以在空闲中断中获取到不定长的数据。其优点是对CPU占用减小,同时可以在没有结束符的情况下接收不定长数据

 在usart.c文件中添加存储数组、编写中断回调函数。

extern uint8_t RxMsg[200];//只需存储数组即可
//空闲中断回调函数
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart,uint16_t Size){
  if(huart == &huart1){       //判断是否是串口一的中断
    __HAL_UNLOCK(huart);      //解锁串口状态
    HAL_UART_Transmit(&huart1,RxMsg,strlen((const char *)RxMsg),100);//将接收到的数据发送出去 
	  memset(RxMsg,0,200);//清除接收到的数据		
    HAL_UARTEx_ReceiveToIdle_IT(&huart1,RxMsg,200-1); //再次开启空闲中断
  }
}

main函数中先启动串口空闲中断。

int main(void)
{
///**********
  HAL_UARTEx_ReceiveToIdle_IT(&huart1,RxMsg,200-1); //开启空闲中断;
///**********
  while (1)
 {
    ///**********
  }
}

编译后下载验证

可以看到只要发不超过数据数组长度的数据都可以稳定的接收到,同时数据末尾不要求有结束符,有种问题是如果发送端发送的速度过快可能会导致两帧数据合并在一起,优点是这种方式比普通的中断接收模式更加节省CPU损耗,其中的限制是定义的存储数组RxMsg[]长度是有限的,具体长度需要根据实际情况而定,长度过长也会占据过多的内存。

4、DMA空闲中断接收: 

DMA空闲中断跟空闲中断调用的都是同一中断回调函数,DMA模式下的空闲中断接收相当于第三方进行处理,接收时根本无需CPU参与,因此能够将CPU的使用率降到最低。

 在usart.c文件中添加存储数组、编写中断回调函数。

extern uint8_t RxMsg[200];//只需存储数组即可
//DMA空闲中断回调函数
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart,uint16_t Size){
  if(huart == &huart1){       //判断是否是串口一的中断
    HAL_UART_DMAStop(huart);  //暂停DMA接收
    __HAL_UNLOCK(huart);      //解锁串口状态
		
    HAL_UART_Transmit(&huart1,RxMsg,strlen((const char *)RxMsg),100);//将接收到的数据发送出去 
	  memset(RxMsg,0,200);  //清除接收到的数据
		
    HAL_UARTEx_ReceiveToIdle_DMA(&huart1,RxMsg,200-1); //再次开启DMA空闲中断
  }
}

 main函数中先启动串口空闲中断。

int main(void)
{
///**********
 HAL_UARTEx_ReceiveToIdle_DMA(&huart1,RxMsg,200-1); //开启DMA空闲中断
///**********
  while (1)
 {
    ///**********
  }
}

编译下载验证:

可以看到和空闲中断一样,都可以十分稳定的接收数据

问题1:

在使用DMA模式时,利用串口助手在发送同一个数据,偶尔可能出现字符串中间数据遗漏的问题,具体原因暂时未找到,有哪位大佬知道的可以在评论区留言。

 问题2:

在使用DMA空闲中断时,已经将接收数组的长度增大到2000,但是在单次发送数据超过256个字符时,只能接收前256个数据,后面的数据会丢失,在下一次发送后也不能续接上一次遗失的数据。是否是因为DMA只能接收256个数据?有哪位大佬知道的可以在评论区留言

参考文章:

1、 http://t.csdnimg.cn/CpBcd

2、http://t.csdnimg.cn/ctLqa 

3、http://t.csdnimg.cn/jtRD6

  • 27
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!对于STM32F407和ETH LWIP,我可以为您提供一些关于TCP client客户端的开发方面的信息。 首先,您需要使用STM32CubeMX软件来配置STM32F407的ETH和LWIP库。在STM32CubeMX中,您可以配置以太网接口和选择使用LWIP协议栈。确保在配置期间启用TCP/IP协议。 一旦配置完成并生成代码,您可以在生成的代码中找到LWIP库的API函数以及以太网驱动程序的相关函数。在这里,您将能够设置和管理TCP连接。 以下是一个简单的TCP客户端示例代码,用于向服务器发送数据: ```c #include "lwip/api.h" #define SERVER_IP "192.168.0.100" #define SERVER_PORT 8080 void tcp_client_task(void *arg) { struct netconn *conn; err_t err; // 创建TCP连接 conn = netconn_new(NETCONN_TCP); if (conn != NULL) { ip_addr_t server_addr; // 设置服务器IP地址和端口 IP4_ADDR(&server_addr, 192, 168, 0, 100); // 连接服务器 err = netconn_connect(conn, &server_addr, SERVER_PORT); if (err == ERR_OK) { const char *data = "Hello, server!"; struct netbuf *send_buf; // 创建发送数据包 send_buf = netbuf_new(); if (send_buf != NULL) { // 将数据添加到发送数据包中 netbuf_ref(send_buf, data, strlen(data)); // 发送数据包 err = netconn_send(conn, send_buf); // 释放发送数据包 netbuf_delete(send_buf); } } // 关闭连接 netconn_close(conn); netconn_delete(conn); } vTaskDelete(NULL); } ``` 请注意,此示例代码仅用于演示目的,您可能需要根据您的具体需求进行修改。另外,还要确保正确初始化LWIP协议栈和以太网接口。 希望这些信息对您有所帮助!如果您有任何进一步的问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值