文档章节

STM32串口如何代码实现更稳定的接收消息

OceanStack
 OceanStack
发布于 2014/11/06 13:17
字数 1444
阅读 3872
收藏 39

在 《STM32串口向世界问好》介绍过如何发送消息,那么又如何接收消息呢?

也很简单,只需要配置好串口接收,配置好中断,并在串口中断函数里面进行数据接收就可以了。通用配置代码如下:

/**
  * @brief  初始化IO 串口1
  * @param  bound:波特率
  * @retval None
  */
void USART1_Debug_Init(u32 bound)
{
    //GPIO端口设置
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    assert_param(bound >0 && bound <= 256000);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    USART_DeInit(USART1);  //复位串口1
    //USART1_TX   PA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
    GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
    //USART1_RX	  PA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);  //初始化PA10

    //USART 初始化设置
    USART_InitStructure.USART_BaudRate = bound;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;	//收发模式
    USART_Init(USART1, &USART_InitStructure); //初始化串口

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2 ; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;		
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			
    NVIC_Init(&NVIC_InitStructure);	

    USART_ClearFlag(USART1, USART_FLAG_TC);//防止第一个数据被覆盖
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断

    USART_Cmd(USART1, ENABLE);                    //使能串口
}

中断处理接收函数为:

 void USART1_IRQHandler(void)
{
    u8 res;
    
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 有数据为 1 SET
    {
        res = (u8)USART_ReceiveData(USART1);
        res = res ;
    }
}

如果此时需要判断当接收的数据为1时点亮LED1,当接收数据为2时熄灭LED1则可在中断里作如下处理:

 void USART1_IRQHandler(void)
{
    u8 res;
    
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 有数据为 1 SET
    {
        res = (u8)USART_ReceiveData(USART1); 

        if(0x01 == res)
        {
            LED1 = ON ; 

        }
        if(0x02 == res)
        {
            LED1 = OFF ; 

        }

    }
}

但这种接收控制方法是不够稳定与灵活的,比如在传输的过程中受到干扰,0x01 变成 0x02,则就会出现错误的控制。又比如我要接收一串数据并进行处理,这样就不好控制了。

这时我们就要想着制定一套通信协议来方便通信。

在此介绍一种简单通信协议,是我在设计一款无人机数据链通信时用到的一开源协议:MAVLink,另外加上CRC校验,进一步保证接收数据的可靠性。

其通信数据格式如下:

红色部分代表起始帧 STX 为 0xFE ; LEN表示要发送的数据长度(PAYLOAD长度);SEQ表示数据的序列号,循环从0至255发送(可以检测是否丢包,并可能过此来判断信号强度);SYS是用来表示区分同一网络中不同飞行器号的,即系统ID;COMP代表组件ID,表示飞行器上各个组成部分,如飞控单元,GPS等;MSG则代表消息ID,即要发送不同控制命令ID;PAYLOAD表示此命令的内容;最后两字节是自动生的的CRC校验码 。

从上图也可以看出PAYLOAD有效长度可为0至255字节(因为LEN来表示,它们都是无符号8位数据类型),所以一条消息长度最小为8字节,最大为263字节。

至此一简单通信协议就介绍过了,说的有点多。下面就是如何对其解析,话不多说直接代码说明:

#define MavlinkLenMin  8
#define MavlinkLenMax  263
#define STX      0xFE//MAVLINK HEAD
#define Add_STX  0x00
#define Add_LEN  0x01
#define Add_SEQ  0x02
#define Add_SYS  0x03
#define Add_COMP 0x04
#define Add_MSG  0x05
#define Add_PAYLOAD  0x06//PAYLOAD start from 0x06


typedef enum {BEEN_RECEIVED = 0, RECEIVING = !BEEN_RECEIVED} Receive_Status;

typedef struct
{
    boolean Get ; 
    u16 Len ; 
    u8 Cache[MavlinkLenMax];
}MAVLink_Data_Struct , * MAVLink_Data_Struct_p ; 

MAVLink_Data_Struct Msg_Rev ; 

void Msg_Recv_Data_Analyse_Irq(u8 data)
{
    if(RECEIVING == Msg_Rev.Get){
        Msg_Rev.Cache[Msg_Rev.Len++] = data; 
        if(STX != Msg_Rev.Cache[Add_STX]){
            Msg_Rev.Len = 0 ;
        }

        if(((u16)Msg_Rev.Cache[Add_LEN] + MavlinkLenMin)==Msg_Rev.Len){
            Msg_Rev.Get = BEEN_RECEIVED ;
        }

    }
}

可在串口中断接收函数里调用此函数用作协议数据接收解析

void USART1_IRQHandler(void)
{
    u8 res;

    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断 有数据为 1 SET
    {
        res = (u8)USART_ReceiveData(USART1);
        Msg_Recv_Data_Analyse_Irq(res); 
    }
}

当一条消息接收完成后,Msg_Rev.Get的状态就会被设置成BEEN_RECEIVED ,这时就可在相关函数中对此条消息进行处理。

另外为了消息的更可靠,还可加入CRC校验,如下函数就是一简单通用的CRC16校验码生成函数:

u16 crc_chk_value(u8 *data_value)   
{ 
    u16 crc_value = 0xFFFF;     

    u16 length = (uint16_t)data_value[1] + 6;  
    u16  i;

    while(length--)     
    { 
        crc_value ^= *data_value++;     
        for(i=0;i<8;i++)     
        { 
            if(crc_value & 0x0001)     
                crc_value = (crc_value >>1) ^ 0xa001;     
            else     
                crc_value=crc_value >> 1;     
        }     
    }     
    return(crc_value);     
}

如上述的对LED灯控制,我们可以作如下简单设计,设定发送操控数据的设备SYS ID为1,假定组件串口1的ID为1,消息ID也为1,另外发送的数据长度也为1,则解析控制函数如:

void Msg_Control_Process(void)
{
    u16 checksum;
    if(BEEN_RECEIVED == Msg_Rev.Get){
        checksum = crc_chk_value(Msg_Rev.Cache);
        if( (Msg_Rev.Cache[Msg_Rev.Cache[Add_LEN] + 6 + 1] == (checksum & 0xFF)) &&
                (Msg_Rev.Cache[Msg_Rev.Cache[Add_LEN] + 6 + 2] == ((checksum >> 8) & 0xFF)) ){

            if( (0x01 == Msg_Rev.Cache[Add_SYS]) && (0x01 == Msg_Rev.Cache[Add_COMP]) ){
                if(0x01 == Msg_Rev.Cache[Msg_Rev.Cache[Add_LEN] + 6]){
                    LED1 = ON ; 
                }
                if(0x02 ==  Msg_Rev.Cache[Msg_Rev.Cache[Add_LEN] + 6]){
                    LED1 = OFF ; 
                }
            }

        }
        Msg_Rev.Get = RECEIVING ;
        Msg_Rev.Len = 0;
    }
}

此函数可在主轮询里调用,当中断里正常接收到一串消息后,就可以根据条件判断及加处控制处理,处理完成后再继续接收。因加入了CRC校验及消息和组件ID检测等,数据可靠性增加,当然软件通信可靠性增强一般是通过增加冗余来实现,此也不例外。

稍微复杂的控制用此比较好,上面的例程用此只是作简单原理性说明,有点大材小用的感觉 。

另在此设计中,你会发现,当接收完一条消息,处理完成后才接收下一条。这样,当处理过程较费时间,并且消息在不断的快速发送时,就容易引起丢包现象 ,所以以上设计并不是很好的。那么这个又如何解决呢?

待我研究下,下篇将会作详细介绍 。


© 著作权归作者所有

OceanStack
粉丝 29
博文 21
码字总数 19858
作品 0
武汉
程序员
私信 提问
加载中

评论(10)

橘子O
橘子O
串口使用读取超时中断就好了,非常稳定
OceanStack
OceanStack 博主

引用来自“tianzhihen007”的评论

问一下,LEN 设计成整个包的大小会不会更好,和现在的设计相比有什么 优缺点?13
因为他的消息有定长部分和变长部分(PAYLOAD),他是分开打包、发送的。如果LEN设计成整个包,这样每次都要减掉那个定长部分。
OceanStack
OceanStack 博主

引用来自“tianzhihen007”的评论

问一下,LEN 设计成整个包的大小会不会更好,和现在的设计相比有什么 优缺点?13
LEN的设计选择其实影响不大,原作者如此设计是为了方便Mavlink消息的打包发送,具体可参考下源码。
t
tianzhihen007
LZ串口解析值得借鉴啊!!79
t
tianzhihen007
#define Add_LEN 0x01
#
define Add_SEQ 0x02
#define Add_SYS 0x03
#
define Add_COMP 0x04
#define Add_MSG 0x05

定义成枚举 会不会更好
t
tianzhihen007
问一下,LEN 设计成整个包的大小会不会更好,和现在的设计相比有什么 优缺点?13
OceanStack
OceanStack 博主

引用来自“jeans19890821”的评论

发送方应该怎么发呢?
如果用此种队列设计发送可以在定时中断里进行 。
j
jeans19890821
发送方应该怎么发呢?
OceanStack
OceanStack 博主

引用来自“Wendal”的评论

有时候觉得stm32这种真够复杂的, arduino就一行...
嗯,arduino又封装了一层,对于快速敏捷开发比较好 。
wendal
wendal
有时候觉得stm32这种真够复杂的, arduino就一行...
STM32串口如何代码实现更高效的接收消息

这段时间一直在研究多旋翼飞行器,以及其它的事情,博客好外没更新,再不坚持怕真荒废了哦。 在上篇简单实现MAVLink协议的解析,并演示按照设计好的命令执行对应的事件处理,以及又加入 CRC校...

OceanStack
2014/12/03
9K
1
STM32串口接收不定长数据原理与源程序

转载:http://blog.csdn.net/u014515202/article/details/73293917 http://www.51hei.com/bbs/dpj-39885-1.html 方法1:串口接受数据,定时器来判断超时是否接受数据完成。 方法2:DMA接受+...

u013184273
2017/12/13
0
0
STM32串口通信乱码详细处理方法

STM32串口通信以及温度采集搞定,其中主要遇到STM32系列单片机时钟树的问题,串口通信遇到串口调试助手能够接收到数据但出现乱码现象,开始一直以为是串口配置和程序代码问题,因为是第一次上...

aizaiyueye
2018/01/13
0
0
STM32学习笔记(7)——USART串口的使用

1、 串口的基本概念 在STM32的参考手册中,串口被描述成通用同步异步收发器(USART),它提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换。USART利用...

葫芦娃的后桌
2018/08/09
0
0
Android实践:做一个可视频交互的智能小车

本文的三位作者正阳、海洋、阿力,是来自不同公司的工程师,在Agora RTC Hack 上海站编程马拉松上,开发了一款可实时视频远程看房的智能小车。本文将从方案设计、硬件开发、Android 端开发、...

Agora
2018/08/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

32位与64位Linux系统下各类型长度对比

64 位的优点:64 位的应用程序可以直接访问 4EB 的内存和文件大小最大达到4 EB(2 的 63 次幂);可以访问大型数据库。本文介绍的是64位下C语言开发程序注意事项。 1. 32 位和 64 位C数据类型...

mskk
30分钟前
6
0
Vue 实现点击空白处隐藏某节点(三种方式:指令、普通、遮罩)

在项目中往往会有这样的需求: 弹出框(或Popover)在 show 后,点击空白处可以将其 hide。 针对此需求,整理了三种实现方式,大家按实际情况选择。 当然,我们做项目肯定会用到 UI 框架,常...

张兴华ZHero
36分钟前
7
0
SpringBoot激活profiles你知道几种方式?

多环境是最常见的配置隔离方式之一,可以根据不同的运行环境提供不同的配置信息来应对不同的业务场景,在SpringBoot内支持了多种配置隔离的方式,可以激活单个或者多个配置文件。 激活Profi...

恒宇少年
38分钟前
8
0
PDF修改文字的方法有哪些?怎么修改PDF文件中的文字

PDF修改文字一直以来都是一个难以解决的问题,很多的办公族在办公的时候会有修改PDF文件中的文字的需要,可是PDF文件一般是不能进行编辑和修改的,难道就没有什么办法解决这个问题了嘛?不要...

趣味办公社
41分钟前
5
0
企业组织中采用服务网格的挑战

作者:Christian Posta 译者:罗广明 原文:https://blog.christianposta.com/challenges-of-adopting-service-mesh-in-enterprise-organizations/ 编者按 本文作者介绍了企业组织采用服务网...

jimmysong
50分钟前
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部