基于uCOSII的MCU串口传输处理.docx

上传人:b****7 文档编号:9253587 上传时间:2023-02-03 格式:DOCX 页数:30 大小:1.42MB
下载 相关 举报
基于uCOSII的MCU串口传输处理.docx_第1页
第1页 / 共30页
基于uCOSII的MCU串口传输处理.docx_第2页
第2页 / 共30页
基于uCOSII的MCU串口传输处理.docx_第3页
第3页 / 共30页
基于uCOSII的MCU串口传输处理.docx_第4页
第4页 / 共30页
基于uCOSII的MCU串口传输处理.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

基于uCOSII的MCU串口传输处理.docx

《基于uCOSII的MCU串口传输处理.docx》由会员分享,可在线阅读,更多相关《基于uCOSII的MCU串口传输处理.docx(30页珍藏版)》请在冰豆网上搜索。

基于uCOSII的MCU串口传输处理.docx

基于uCOSII的MCU串口传输处理

 

基于uCOSII的MCU串口传输处理

目录

1.综述3

1.1.我们需要什么样的代码3

1.2.理论基础3

1.3.如何改进我们的代码3

2.发送5

2.1.旧发送方式5

2.1.1.基本流程5

2.1.2.基本发送接口5

2.1.3.协议发送接口7

2.1.4.中断中的处理8

2.2.新发送方式9

2.2.1.基本流程9

2.2.2.基本发送接口9

2.2.3.协议发送接口11

2.2.4.中断中的处理11

2.3.优劣分析12

3.接收12

3.1.旧接收处理方式13

3.1.1.基本流程13

3.1.2.中断中的处理13

3.1.3.应用中处理14

3.2.新接收处理方式16

3.2.1.软硬件基础16

3.2.2.基本流程16

3.2.2.1.中断部分流程17

3.2.2.2.应用处理部分流程17

3.2.3.中断中的处理18

3.2.4.应用中处理19

3.2.5.消息处理典型模式流程20

3.3.优劣分析21

4.分层原则22

4.1.发送和业务分离23

4.2.接收和处理分离23

4.3.协议和应用分离23

5.其他23

5.1.带响应的命令发送24

5.2.打印分级26

5.3.通讯协议的制订27

5.4.ucosii中的BUG28

5.4.1.BUG_128

5.4.2.BUG_228

串口通讯是我们用的很多的一种通讯方式,也是MCU的工作重点之一,改进串口通讯的工作方式和流程可以极大程度上改进我们项目代码质量。

随着MCU性能的逐渐增强和对代码要求的提高,使用RTOS已经慢慢成为MCU主流编程模式,RTOS中一般会提供大量的工具,会给我们代码的编排带来了很大的便利。

本文就基于uCOSII的MCU串口传输展开讨论并提出一种和现在不同的处理方式供大家参考讨论。

1.综述

本文所讨论的内容均只针对MCU的串口通讯。

本文不针对任何人,所列代码均不含编写者名字,就事论事,纯技术探讨。

本文资料来源公司现有资料,仅限公司内部参考。

限于作者水平本文可能存在一些错误的地方,非常欢迎指正。

1.1.我们需要什么样的代码

实时,高效,易懂,易移植。

实时就是说,通讯中响应速度要尽可能快,包含发送和接收。

比如接收过程中,至少在短时间内,接收方不应该要求发送方发送数据时有最小发送间隔。

见过一些代码,串口处理时,要求发送方发送两帧之间至少有5毫秒、10毫秒甚至更大的时间间隔,这样的系统是不能满足高实时性要求的,此处间隔必须是0毫秒(指短时间内)。

因为系统中常常会有短时突发大数据(例如上电掉电的时刻)。

所谓高效,举个栗子,两段代码执行同样的功能,A代码的CPU使用率为10%,B代码的CPU使用率为20%,那么谁都知道A代码效率更高。

效率更高,我们就可以在同样的芯片里实现更多的功能,或者说用更便宜的芯片实现同样的功能,这也就降低了我们产品的成本。

易懂,要求我们的代码尽量符合人类自然语言的逻辑,只有这样,我们理解起来才更加容易,当然,最好是有足够的注释。

最后一条是易移植,抽象中发送或处理过程中的无差别部分组成共用代码,差异部分单独编写。

1.2.理论基础

限于水平以及篇幅,不展开讨论,只说一点点要点。

1,串口通讯是点对点的收发,也就是说,一方是发送方,一方是接收方。

我们把数据的发送称之为数据的生产,数据的接收处理称之为数据的消费。

一个正常的系统一定是消费能力大于生产能力的,不然铁定产生数据丢失。

而提高数据消费能力则成为串口通讯设计中的重点。

2,代码不仅要满足实际“无错误”,还应该尽量满足理论上“无错误”,要符合一些基本性的常识要求。

例如,假设一帧数据由10个字节组成,发送方一次发送了9个,另一个数据在24小时后才发送,这样同样完成了一帧数据的发送,那么作为接收方,该判定这帧数据时错误的还是正确的呢?

3,不管是什么样的通讯协议,软件处理时都应该满足这样一个原则——数据校验后才能使用。

4,在不影响软件移植性的原则下,尽可能的发挥硬件的特长或者说一些特有的属性来简化代码,例如新唐M0串口的硬件buffer。

1.3.如何改进我们的代码

抽象出公共部分代码;

合理利用软件工具提供的功能(如RTOS提供的一些功能);

合理的利用MCU硬件提供的功能(比如某些MCU的串口部分会提供硬件buffer,那这个的速度当然比软件要快和好使)。

尽可能的完善通讯协议,在协议层面减小漏洞的产生。

2.发送

串口通讯过程中发送应使用中断或DMA的方式,不推荐使用死等的方式进行,因为DMA并不是所有MCU或者说MCU的串口模块都有的,所以现以中断方式为例说明。

2.1.旧发送方式

2.1.1.基本流程

N

Y

2.1.2.基本发送接口

此部分最大的问题是用死等的方式进行数据发送,在数据量稍大的时候,往往会严重影响系统的实时性。

图1直接调用的接口

图2最终调用的接口注意,本处有个等待的动作,耗时就在此处

2.1.3.协议发送接口

此处的协议发送接口有多个,但是都是类似的,只举两个进行说明。

协议发送大致两种方式,一种是逐个发送,另一种是组好包后一并发送。

因为不需要额外开辟缓存,前者较省内存,但是效率比较低下;后者效率较高,但是对内存的消耗也会大一些。

在早前两种方式可以说各有各的好处,但是随着MCU性能的逐渐增加,前者的省内存优势很多时候已经微乎其微了。

图3算上打印,此函数耗时较大

图4此图于上图类似

2.1.4.中断中的处理

旧的发送方式由于未使用中断的发送方式,所以中断中无动作。

2.2.新发送方式

2.2.1.基本流程

N

Y

N

Y

N

Y

2.2.2.基本发送接口

发送接口就是上述流程图的代码化。

需要特别说明一下的是,发送接口应统一为三个参数,分别为串口编号索引,要发送的源指针,要发送的数据长度。

还有一个就是,本处并非实际发送数据,而是把数据送入发送缓存中,后续在中断中再将数据从发送缓存中取出实际发送。

本种发送方式依赖串口发送完成中断,现在的MCU一般都提供这种中断。

本处有个环节需要注意一下,就是说假如要发送的数据长度len超过buffer剩余空间大小后该如何处理,常见的处理方法三种,

1,此轮要发送的数据全丢;

2,只发送能放入buffer部分的数据,超出部分丢弃;

3,等待至发送完成。

其实,代码设计本身应尽量避免出现要发送的数据长度超过buffer剩余空间大小这种情况的出现,假如不幸还是发生了,本处推荐使用第三种处理方法,因为前两种均会造成数据丢失,很多时候系统是不允许数据丢失。

在第二种处理方法中,很可能会造成发送数据不完整,接收方校验就不会通过,数据还是会全部丢失,只不过是放到了接收方来丢失,还浪费了接收方的时间……

至于等待发送完成,有人也许会说,这不是影响实时性了么?

单片机跑的时候能等么?

首先,如果不等,把数据丢了,后续还是需要重发,这个重发难道消耗的时间会更加小么?

其次,我们可以使用一些工具提供的方法,等待耗时会大大降低,比如等待的时候使用RTOS提供的Delay,这样,此处的等待耗时是可以接受的。

图5直接调用的接口

注意,下图的代码(最终调用的接口)在理论上不会产生实际的等待,因为要调用的时候是发送寄存器空的时候。

图6最终调用的接口,此处的while处不会停留

2.2.3.协议发送接口

图7组包统一发送至发送buffer

2.2.4.中断中的处理

中断中的操作是,判断缓存中是否还有未发送的数据,如果有,则取出一个继续发送,如果没有,则关闭发送完成中断(发送寄存器空中断)。

如下图:

图8

2.3.优劣分析

旧的发送方式未使用中断而使用死等的方式来进行发送,耗时巨大,特别是在低波特率的时候,假设使用9600的波特率,发送100字节,耗时超过104毫秒,即便是采用115200的高波特率,耗时也达到了惊人的约8.7毫秒以上(安装2毫秒一个节拍算,此处已经有4~5个节拍了)。

新的发送方式占用发送中断的时间,而发送中断里面所需的时间取决于MCU的进出中断时间和MCU的频率(和波特率的关系倒不大),在115200的波特率下,耗时约3毫秒,在9600波特率下耗时理论上差距不大。

需要特别说明的是后者的3毫秒是分片的3毫秒,程序不会死等3毫秒,而前者的8.7毫秒(或者104毫秒)是死等的,程序在此期间啥都做不了,实时性受到巨大影响。

还有一点需要额外说明,目前我们的调试过程中,常常根据打印信息来判断程序执行到哪里,如果使用中断的方式来进行发送数据,则打印信息相对程序执行位置会产出“滞后”,如继续不更改的使用此调试方法的话,可能会产生误判。

3.接收

接收是串口通讯过程中的重点和难点。

3.1.旧接收处理方式

3.1.1.基本流程

此处的流程大约是中断接收数据(不检查是否溢出),应用中采用状态机机制定时判断是否接收到一帧或多帧数据,如果收到一帧或几帧就开始解析处理。

应用中流程如下(省略校验部分):

 

 

N

Y

 

N

Y

 

 

N

Y

 

3.1.2.中断中的处理

图9此处无溢出判断

注意,此处无溢出判断,代码设计的时候有几个点需要注意,本处在下一节中展开说。

3.1.3.应用中处理

图10本任务会定时被执行

图11

中断出处无溢出判断,这就要求在进行代码设计的时候,接收buffer必须大于处理函数的理论最大间隔中最大能接收的数。

吃个栗子。

最大理论间隔如下:

图12

图13

图14

在不包含其他代码的情况下,最大间隔约40毫秒,在115200波特率下,此时间内最大可能收到460字节以上,考虑到其他代码执行时间以及任务的响应时间,要想实现完全可靠,buffer设置值估计还得大不少。

如果buffer设定不够大,在最极端的情况下往往会旧数据被破坏,新数据又未接受完,两头不对。

还有一个问题就是帧中间间隔问题,假设一帧中间存在很大时间间隔,此种接收处理方式仍判定为正常,这可能会带来隐藏的风险。

3.2.新接收处理方式

3.2.1.软硬件基础

本处主要依赖于两个中断,一个是串口接收中断,一个是串口空闲中断,加入MCU没有串口空闲中断,可以采用一个定时器(软件定时器和硬件定时器都可以)来提供类似的功能。

图15

图16

3.2.2.基本流程

本部分流程包含中断部分流程和应用部分流程两部分。

3.2.2.1.中断部分流程

注意,本处所指的首次接收是指在总线在空闲一段时间后新收到数据。

比如,总线空闲10毫秒后,新收到一串数字,0x01、0x02、0x03、0x04、0x05、0x06、0x07、0x08,接收0x01就是我们所指的首次接收。

N

Y

N

Y

N

YNY

3.2.2.2.应用处理部分流程

以下流程省略校验部分。

N

Y

3.2.3.中断中的处理

本处有使用串口空闲中断,如果所使用的MCU没有串口空闲中断,我们可以在接收到第一个数据的时候开启一个定时器(软件定时器和硬件定时器都可),一方面在定时器中断中不断计数,一旦达到阈值,就认为除非了串口的空闲中断,另一方面,在串口的接收中断,每收到一个数就清零一次中断的计数值防止达到阈值。

图17

中断中达到条件会发送一个消息出去,ucos提供了这样的一个接口(OSQPost),这条消息的具体组成可以参考下面:

图18消息取出来的时候需要转换一下

3.2.4.应用中处理

应用中典型处理方式,任务被阻塞在消息队列上,后面switch选择消息类型,分类处理。

图19消息取出来的时候需要转换一下,具体根据自己定义

图20

3.2.5.消息处理典型模式流程

取出数据->校验数据->执行处理。

图21调用具体串口的函数指针

图22具体的命令处理

3.3.优劣分析

两种方式的对比如下

项目

旧方式

新方式

接收处理任务调用方式

不管有无数据,定时唤醒

有确定至少一帧完整数据才唤醒

实时性

取决于最大间隔

接收完数据后瞬时响应

一帧中出现间隔

判断为正常

判断为错误

对缓存要求

取决于任务间隔,波特率

和任务间隔,波特率无关

接收缓存溢出判断

中断耗时

处理流程

复杂

简单

CPU使用率

4.分层原则

4.1.发送和业务分离

直接的发送接口应该和业务分离,具体业务的发送接口应该在直接的发送接口之上新建接口。

4.2.接收和处理分离

接收和处理不要放在一起,这是基本的原则。

4.3.协议和应用分离

应用要基于协议而成,也就是说应用要在协议层之上,换而言之,协议层内不应该直接调用应用层的东西,但是考虑到实际使用的方便,可以采用回调或其他的方式间接调用。

5.其他

5.1.带响应的命令发送

利用ucos自带的软件定时器功能,在发送需要带响应的命令的时候,使能一个软件定时器,定时查看是否收到响应。

ucos的软件定时器,会创建一个独立的任务来执行软件定时器。

图23

图24

图25

图26

图27

图28

图29

5.2.打印分级

软件调试和发布的时候,我们需要的打印信息很有可能会不一样,在调试的时候,我们往往需要较多的打印信息来了解系统的状态信息而软件正式发布后,很多调试打印信息是不需要的,这个时候我们就需要一个比较方便的方式,把这些信息给去掉,建立打印分级机制就是其中的一种方法。

我们可以把打印设置为3个级别,分别为调试级(d),正常级(n),最小级(s),这可以通过在打印函数加上适当的后缀作为区分,我们可以根据需要设定不同的打印级别。

根据我们设定的不同级别,在代码编译的时候,会自动去掉不需要的打印。

图30不同级别的打印信息

图31通过宏定义去掉不需要的打印信息

另外,在系统正常运行的时候,有时候还需要软件可控的打印,我们需要适当的方法,在正常跑得过程中打开或关闭打印功能。

如下图:

图32

5.3.通讯协议的制订

此处请参考下面的文档。

 

5.4.ucosii中的BUG

5.4.1.BUG_1

信号量初值设定毫无用处,任意调用post会任意增加sem的计数值。

图33

5.4.2.BUG_2

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 哲学历史

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1