如何写一个健壮的MCU串口程序?

这里说的MCU是没有带SDK的那种,完完全全就是裸的,所有驱动得自己开发。串口驱动得自己从0开始写。下面总结几条写MCU串口程序需要注意的。

1、  全双工与半双工

首先要考虑的最基本的问题就是,这款芯片串口是全双工还是半双工的?

如果是半双工,收和发不能同时进行,收的时候不能发,发的时候不能收,因为发送器和接收器是同一个,只能单向用,一般收数据是中断触发在中断里1byte 1byte地收。

那么怎么判断当前能不能发数据呢?当收到数据过一定时间(可以用timer实现)还没有再收到数据认为串口空闲没有数据要收了,此时可以发数据。

半双工相当于是用户自己去实现产生了串口空闲中断。

如果是全双工,想发就发想收就收,是不是省事多了?

发数据是由用户先把某个发送中断寄存器置1,接着会自动产生写中断,用户在中断服务函数里把要发的数据装到数据寄存器并把中断寄存器清零,等刚刚装载的那1byte数据发送成功,硬件又会自动产生发送中断,用户再次把数据装载进去并把中断寄存器清零。

如此循环,当某次产生中断,用户不再装载数据,只是把中断寄存器清零那么不再有发送中断产生,发送数据过程就结束了。

全双工相当于是MCU自己产生了串口空闲中断,用户不用操心。

2、  驱动层和应用层分开 

驱动层设计串口读和写串口的接口,提供给应用层调用,驱动层读和写的数据分开两个环形fifo保存。

当应用层要发数据,只需要把要发的数据传给驱动层的发数据的fifo就好,数据存在fifo里,至于什么时候真正写串口是驱动层的事。

当驱动层收到串口数据,把收到的数据存在收数据的fifo里,当应用层调用读串口接口时,再把收到的数据传给应用层指针。

3、  应用层循环队列处理机制

这是一定要做的一个机制,串口通信的双方一般一个主机一个从机,当对方发一条串口命令过来,理想情况是这条命令数据全都一次被对端收到,有时候的情况是先收到前半段,接着收到后半段,谁也没法保证数据全都一次收全。

驱动层收到数据只是把数据存到他的缓存,当应用层调用读串口接口时,他收到多少数据就把多少数据传给应用层缓存指针。

 

所以要考虑以下情况:

1)  一条完成的命令一次收到

2)  两条及以上的命令一次收到

3)  收到一条完整命令+下一条命令前半段

4)  收到命令后半段+一条完整命令

5)  收到多条命令+下一条命令前半段

6)  收到命令后半段+一条完整命令

 

所以应用层可以将收到的数据存在一个环形buffer里(就是fifo,先进先出缓存),数据从头装进去,读也是从头部开始去读。每写一个数据,写指针往后移一步,每读一个数据读指针往后移一步。

首先对数据进行帧结构检查,当检查到一条完整的符合协议格式的数据帧时对数据进行处理,

一般串口协议,一包完整的数据帧至少包含:

固定的头码 + 命令码+数据长度+数据区数据+校验码

代码大致如下:

//定义一个帧控制结构体如下
typedef struct
{
   uint8_t frame_status;      帧状态,如目前收到头了、收到命令码了、收到长度了、收到完整数据了
  uint32_t tx_index;    //fifo 收数据指针,当从驱动层读了新的数据,tx_index往后移
  uint32_t rx_index;    //fifo读数据指针,当进行帧结构检查时,每读1byte,rx_index往后
  uint32_t header_index;  //当收到头时,header_index记下数据帧头码位置
  uint32_t frame_len;   //有效数据帧数据长度  
}frame_ctrl;


//代码大致如下
frame_ctrl_t   frameCtrl;
While(tx_index != rx_index)     //当数据没处理完,一直循环
{
  //从环形buffer 读1byte数据
   rx_index++;  
  switch(frameCtrl. framestatus)
  {
  // 数据帧检查 状态机
    //根据当前状态进行处理
    case(现在还没收到头)
    {
       if(data == 头码)
       frameCtrl. Framestatus = 收到头了
      记下头码位置赋给header_index
    } 
    case(收到头了)
    {
     if(到了命令码的位置)
     frameCtrl. Framestatus = 收到命令码了 
    }
    ……
  }
  if(frameCtrl. Framestatus == 收到完整数据帧了)
  {
    //根据头码位置,进行命令解析处理
    frameCtrl. Framestatus =现在还没收到头;  //帧状态复位
  }
}

4、  重发机制

对于重要的不能丢的命令一定要有重发机制,但重发也有一定次数限制一般3次,不能无限制一直重发。

1.用于MCU,基于FreeRTOS的micro(轻量级)ROS!

2.RT-Thread团队回应社区关切:鸿蒙OS带来的影响-合作远远大于竞争

3.从嵌入式编程中感悟「栈」为何方神圣?

4.bug粉碎机之volatile的那些坑

5.118亿晶体管!苹果A14首秀,这颗全球首发的5nm芯片有多香?

6.嵌入式C语言源代码优化方案(非编译器优化)

免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值