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

发布者:明石轩最新更新时间:2017-09-18 来源: eefocus关键字:STM32  串口  接收消息 手机看文章 扫描二维码
随时随地手机看文章

在 《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校验,进一步保证接收数据的可靠性。

其通信数据格式如下:

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

红色部分代表起始帧 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 0x06typedef 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检测等,数据可靠性增加,当然软件通信可靠性增强一般是通过增加冗余来实现,此也不例外。

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

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

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


关键字:STM32  串口  接收消息 引用地址:STM32串口如何代码实现更稳定的接收消息

上一篇:STM32F2xx的tcp_echoserver例程解说
下一篇:STM32F207核心版的LwIP例程的心得

推荐阅读最新更新时间:2024-03-16 15:37

(STM32代码生成器)
一、STM32CubeMX 介绍 STM32CubeMX 是 ST 意法半导体近几年来大力推荐的STM32 芯片图形化配置工具,通过自己对硬件的需要,进行选择,而后可以快速生成代码,减少开发人员的开发难度,时间和花销。STM32 覆盖整个STM32系列。 在我看来有如下优点: 大量的芯片(资源,价格介绍),方便对我们进行芯片选型 除了拥有芯片本身的外设选择外,还拥有一系列的中间件,如 RTOS, USB, TCP/IP等 对芯片的整体资源,以及时钟树有更深刻的认识 快速代码生成(对外设进行初始化),方便我们对我们的想法进行验证与开发 工程也可以进行更新(当增加外设时) 下面我们详细介绍一下以上优点:
[单片机]
(<font color='red'>STM32</font>代码生成器)
STM32学习笔记——系统定时器SysTick的使用
//Cortex系统定时器SysTick提供1个24位、降序、零约束、写清除的计数器,具有灵活的控制机制 #include stm32f10x_lib.h GPIO_InitTypeDef GPIO_InitStructure; //定义用于初始化配置GPIO的结构体变量 static vu32 TimingDelay; //定义为volatile类型 ErrorStatus HSEStartUpStatus; //定义枚举类型的错误状态 void TimingDelay_Decrement(void); void RCC_Configuration(void); void
[单片机]
STM32学习笔记4:外部中断
NVIC: STM32F40xx/STM32F41xx的92个中断里面,包括10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级,而我们常用的就是这82个可屏蔽中断。 那么我们如何管理82个外部中断呢? 首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。分组配置在SCB- AIRCR寄存器,如下表: 抢占优先级 & 响应优先级区别1.高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。 2.抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。 3.抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。 4.如果两个中断的
[单片机]
<font color='red'>STM32</font>学习笔记4:外部中断
stm32学习之四
systick(滴答定时器): 系统的滴答定时器可以测试的例子是,将开发板上的LED等轮流点亮即可(我选择的是1s轮流点亮三个LED等)。 首先,系统滴答定时器的特点是,设置开启定时器的话,会自动计数,这个时候,计数到0的时候,会触发中断。 可以设置一个静态的变量,全局进行计数,从而延时。 步骤: 1、建立一个SysTick.h文件,代码如下: #ifndef _SYSTICK_H #define _SYSTICK_H #include stm32f10x.h void SysTick_Init(void); //void timeDecrement(void); void delay(__IO ui
[单片机]
SP2328串口扩展专用芯片及其与单片机的接口电路
摘 要: 本文介绍一种新型的单片机串口扩展芯片的功能特性以及与单片机接口的应用。 关键词: 单片机; 多串口通信 在设计由多个单片机组成的数据采集电路时,一般要用多个串口在各个单片机之间进行数据通信。为了解决单片机扩展多个串口的问题,以前大多采用多片AT89C2051来实现多串口通信。每个AT89C2051用并口与上位机连接,再通过AT89C2051的串口与下位机串口连接。这种电路设计,单片机编程比较复杂,整个电路的调试也比较麻烦,可靠性不是很高。一种新开发的SP2328串口扩展芯片很好的解决了上述问题。 SP2328是成都视普科技公司的串行口扩展专用芯片,能将普通单片机(如:AT89C2051、AT89C51等)的
[单片机]
详解STM32之SD卡
一、SD卡概述   1、定义   2、容量等级   3、SD卡框图   4、SD卡与TF卡的区别 二、 SD卡内部结构   1、 SD卡内部结构简图   2、 存储阵列结构图   3、Buffer   4、“存储阵列Block”--最小的存储单元   5、SD卡的特殊功能寄存器 三、SDIO接口 四、SD卡协议的核心--数据读、写、擦除   1、SD卡写数据块   2、SD卡读数据块   3、擦除SD卡 五、SD卡物理层协议   1、接口   2、命令格式   3、响应格式   4、SD卡的工作状态   5、SD卡的两种状态信息 六、STM32与SD卡相配的外设--SDIO适配器   1、SDIO adapter 结构图
[单片机]
详解<font color='red'>STM32</font>之SD卡
STM32HAL库ADC实验(二)——连续采样模式打印电压值
int main(void) { /* USER CODE BEGIN 1 */ uint16_t adcData; float voltage; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /*
[单片机]
STM32HAL库ADC实验(二)——连续采样模式打印电压值
嵌入式系统的USB虚拟串口设计
   引 言:   现代嵌入式系统中,异步串行通信接口往往作为标准外设出现在单片机和嵌入式系统中。但是随着个人计算机通用外围设备越来越少地使用串口,串口正在逐渐从个人计算机特别是便携式电脑上消失。于是嵌入式开发人员常常发现自己新买来的计算机上没有串口,或者出现调试现场用户的计算机没有串口的尴尬局面。相反,现在的个人计算机普遍拥有4个以上的USB接口,能不能使用USB接口代替串口,完成PC机和嵌入式系统的通信呢?   1、 USB虚拟串口代替物理串口的可行性   首先,越来越多带USB接口的器件涌现出来,如带USB接口的单片机,或独立的USB接口器件,而且这些器件的成本已经很接近于使用RS232电平转换芯片所带来的成本。 ?
[嵌入式]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved