当前位置:首页 > 嵌入式 > 嵌入式云IOT技术圈
[导读]原版Marlin固件硬件平台基于arduino,采用C++类对串口操作函数函数进行了封装,代码注释中介绍了这些函数的功能。

原版Marlin固件硬件平台基于arduino,采用C++类对串口操作函数函数进行了封装,代码注释中介绍了这些函数的功能。MarlinSerial.h文件中类的定义,此处的类只保留的框架结构,留存的这些函数基本上是要一直到STM32平台要实现的函数。

class MarlinSerial //: public Stream
{
  public:
    MarlinSerial();
    void begin(long); //串口初始化设置,配置串口波特率
    void end();       //禁止串口传输函数
    int peek(void);   //读串口缓存中下一字节的数据(字符型),但不从内部缓存中删除该数据。
    int read(void);   //读取串口数据,一次读一个字符,读完后删除已读数据
    void flush(void); //等待输出数据传送完毕
    int available(void);//返回的是缓冲区准确的可读字节数
    void checkRx(void)
};
extern MarlinSerial MSerial; //外部声明,实例化一个串口对象MSerial

MarlinSerial.cpp文件中定义了具体函数的实现方式,通过实例化的对象便可以操作这些串口函数 。

循环队列简介

该串口操作函数用到了数据结构中循环队列的算法,下面先介绍一下循环队列:

//定义队列 #define MaxSize  50  //定义队列中元素的最大个数 typedef struct
{
    int  data[MaxSize]; //存放队列元素
    int front, rear; //队头指针和队尾指针
}SqQueue

把存储队列元素的表从逻辑上看成一个环,称为循环队列。当队首指针Q.font = MaxSize-1后再前进一个位置就会自动到0,这就可以利用除法取余运算来实现。

具体循环队列的实现请参考数据结构 循环队列部分。(后面整理这一部分)

为什么要在串口接收部分创建环形缓冲区?

(引用)串口数据处理机制是数据接收并原样回发的机制是:成功接收到一个数据,触发进入中断, 在中断函数中将数据读取出来,然后立即处理。这一种数据处理机制是“非缓冲中断方式”,虽然这种数 据处理方式不消耗时间,但是这种数据处理方式严重的缺点是:数据无缓冲区,如果先前接收的的 数据如果尚未发送完成(处理完成),然后串口又接收到新的数据,新接收的数据就会把尚未处理 的数据覆盖,从而导致“数据丢包”。串口接收部分创建环形缓冲区便可以很好的避免因收发速度不 一致产生的数据丢包。

串口缓冲区的实现

接下来具体分析下Marlin串口缓冲区的实现(下面分析的代码为移植到STM32上的实现代码,原理一致。):

.h头文件

#define RX_BUFFER_SIZE 128   //定义串口缓冲区的大小 //定义环形缓冲区结构体
typerdef struct
{
  unsigned char buffer[RX_BUFFER_SIZE];  //存放接收到的字符
  int head;  //队头指针
  int tail;    //队尾指针
}ring_buffer;

注意:这里的头和尾的定义恰与循环队列里面的头和尾定义相反,在理解上将head当作rear,将tail当作front即可

.c文件

ring_buffer rx_buffer  =  { { 0 }, 0, 0 }; //定义结构体类型的接收缓冲区并初始化
void store_char(unsigned char c)  //将接收到的数据存入缓冲区
{
  int i = (unsigned int)(rx_buffer.head + 1) % RX_BUFFER_SIZE;
  //如果我们应该存储的接收到的字符的位置刚好在尾端的前面
  //(意味着头部将要进入尾端的当前位置),这样将会溢出缓冲区,
  //因此我们不该存入这个字符或使这个头前进 if (i != rx_buffer.tail)  //缓冲区没有存满
  {
    rx_buffer.buffer[rx_buffer.head] = c;
    rx_buffer.head = i;
  }
}
unsigned int MSerial_available(void)  //返回串口缓存区中数据的个数
{ return (unsigned int)(RX_BUFFER_SIZE + rx_buffer.head

   - rx_buffer.tail) % RX_BUFFER_SIZE;
  }

uint8_t MSerial_peek(void)
{ if (rx_buffer.head == rx_buffer.tail)
    { return 0;
    } else { return rx_buffer.buffer[rx_buffer.tail];
    }
}

uint8_t Mserial_read(void)  //按存入顺序逐个读取缓冲区的数据
{
  uint8_t c;
   /*如果头不是在尾的前面,将收不到任何字符*/ if (rx_buffer.head == rx_buffer.tail)
 { return 0;
  } else {
    c = rx_buffer.buffer[rx_buffer.tail];
    rx_buffer.tail = (unsigned int)(rx_buffer.tail + 1) % RX_BUFFER_SIZE; return c;
  }
}
void MSerial_flush(void)  //等待串口数据传送完毕
{
  // RX
 //不要颠倒这个否则可能会有一些问题,如果接收中断发生在读
 //取rx_buffer_head之后但在写入rx_buffer_tail之前
 //之前的rx_buffer_head值可能被写到rx_buffer_tail
 //使它呈现缓冲区是满的而非空的状态*/
  rx_buffer.head = rx_buffer.tail;
}

后面还有有什么不太理解,可以检索“循环队列” 、“串口环形缓冲区”等关键字来增进理解。

免责声明:本文内容由21ic获得授权后发布,版权归原作者所有,本平台仅提供信息存储服务。文章仅代表作者个人观点,不代表本平台立场,如有问题,请联系我们,谢谢!

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭