• Small RTOS51 学习笔记(1)使用 RTOS 的好处


    个人笔记,主要内容均来自原文

    一个例子来说明 RTOS 的好处。

    假设编一个串行通信程序,通信协议为:数据包长度为 NBYTE,起始字节为 STARTBYTE1、STARTBYTE2,最后一个字节为校验和,中间不可能连续出现 STARTBYTE1、STARTBYTE2。

    第一种方法——在中断中处理协议

    原文示例代码:【代码注释为个人理解,仅供参考】

    unsigned char Buf[NBYTE - 2];  // NBYTE 为包的最大字节数,除去包头
    bit GetRight = 0;
    
    // 串行中断
    void comm(void) interrupt 4
    {
    	static unsigned char Sum, Flag = 0, i;
    	unsigned char temp;
    	
    	if(R1 == 1)
    	{
    		R1 = 0;  
    		temp = SBUF;  // 获取串口数据
    		swtich(Flag)
    		{
    			case 0:  // 状态0:开始接收并处理串口协议
    				if(temp == STARTBYTE1)  //接收到的数据为包头第一个字节
    				{
    					Flag = 1;  // 进入下一状态
    				}
    				break;
    			case 1:  // 状态1:接收包头第二个字节
    				if(temp == STARTBYTE2)  // 如果是目标字节
    				{
    					Sum = STARTBYTE1 + STARTBYTE2;  // 更新数据包的和
    					i = 0;  // "清空"串口接收数组
    					Flag = 2;  // 进入下一状态
    					break;
    				}	
    				if(temp == STARTBYTE1)  // 如果依然收到的是包头第一个字节,保持当前的状态
    					break;
    				Flag = 0;  // 如果接收到的数据不是包头数据,直接返回到状态0
    				break;
    			case 2:  // 状态2:开始接收数据(非包头)
    				if(temp == STARTBYTE1)  // 如果接收到包头第一个字节(丢弃之前接收到的数据)
    				{
    					Flag = 3;  // 进入下一状态
    					break;
    				}
    				Sum += temp;  // 更新校验和
    				if(i >= (NBYTE -3) && Sum == 0)  // 如果接收到的是数据包最后一个字节(校验值),同时判断校验和是否为0,
    				{
    					GetRight = 1;  // 表示数据包处理完毕
    					Flag = 0;  // 状态复位
    					break;
    				}
    				Buf[i++] = temp;  // 如果接收到的不是校验值也不是包头数据,则将其存入串口接收数组
    				break;
    			case 3:  // 状态3:在状态2时接收到包头后才会进入此状态
    				if(temp == STARTBYTE2)  // 接收到包头第二个字节
    				{
    					Sum = STARTBYTE1 + STARTBYTE2;  // 计算校验和
    					Flag = 2;  // 进入状态2
    					i = 0;  // "清空"串口接收数组
    					break;
     				}
     				Sum += STARTBYTE1; // 如果上一个数据并不是包头,而是校验值(凑巧相等),则计算校验和
     				if((i >= (NBYTE - 3)) && Sum == 0)  // 如果上一个接收的数据是数据包的最后一个字节,且校验和为0
     				{
     					GetRight=1;  // 表示数据包处理完毕
     					Flag=0;  // 状态复位
     					break;
    				}
    				
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    通过串口中断这种方式处理串口协议,是比较直观和简单的一种处理方式,初学者(比如我)最先想到的就是这种方法了,直接在串口中断完成全部操作。

    第二种方法——使用队列

    原文示例代码(原代码有些地方我不太理解,自己改了一点):【代码注释为个人理解,仅供参考】

    // 串口中断中只完成数据入列
    void comm(void) interrupt 4
    {
    	if(R1 == 1)
    	{
    		R1 = 0;
    		SBUF 入队;
    	}
    }
    
    unsigned char Buf[NBYTE - 2];  // 串口接收数组,协议最大字节为 NBYTE(包括两个包头字节)
    
    //串口处理函数,放在 main() 函数 while 循环的任意位置
    unsigned char ReadSerial(unsigned char *cp)
    {
    	unsigned char i;
    	unsigned char temp, Sum;
    	temp = 队列中数据个数;
    	if(temp < (NBYTE)) // 还没接收完成(一个数据包)
    		return 0;
    	出队 temp;  // 从队列中取出一个字节
    	if(temp != STARTBYTE1)  // 判断是否为队列首字节
    		return 0;
    	出队 temp;  // 从队列中取出一个字节
    	if(temp != STARTBYTE2)  // 判断是否为队列次首字节
    		return 0;
    	出队 temp;
    	Sum = STARTBYTE1 + STARTBYTE2;  // 首字节的校验和
    	for(i = 0; i < NBYTE - 3; i++)  
    	{
    		出队 temp;
    		*cp++= temp;  // 将出队的数据存入串口缓存数组
    		Sum += temp;  // 计算校验和
    	}
    	出队 temp;  // 取出校验值
    	Sum += temp;
    	if(Sum != 0)  // 校验不通过
    		return 0;
    	return 1;  // 成功返回1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    队列的方法可以降低中断执行时间,提高系统实时性。

    第三种方法——使用 Small RTOS51

    原文示例代码:【代码注释为个人理解,仅供参考】

    // 串口中断中只完成数据入列
    void comm(void) interrupt 4
    {
    	OS_INT_ENTER();  // 进入临界区
    	if(R1 == 1)
    	{
    		R1 = 0;
    		OSIntSendSignal(RECIVE_TASK_ID);  // 发送一个信号(表示接收到的串口数据)
    	}
    	OSIntExit();  // 退出临界区
    }
    
    void Receive(void)
    {
    	unsigned char temp, temp1, Sum, i;
    	
    	OSWait(K_SIG, 0);  // 等待串口中断发送信号
    	temp = SBUF;  // 读取串口数据
    	while(1)
    	{
    		while(1)
    		{
    			OSWait(K_SIG, 0)  // 等待串口中断发送信号
    			temp1 = SUBF;  // 读取串口数据
    			if((temp == STARTBYTE1) && (temp1 == STARTBYTE2)) // 判断包头数据
    				break;
    			temp = temp1;
    		}
    		Sum = STARTBYTE1 + STARTBYTE2;  // 计算校验和
    		OSWait(K_SIG, 0);  // 等待串口中断发送信号
    		temp = SBUF;  // 读取串口数据,存入 tmep
    		for(i = 0; i < NBYTE - 3; i++)
    		{
    			OSWait(K_SIG, 0);
    			temp1 = SBUF;  // 读取串口数据,存入 temp1
    			if((temp == STARTBYTE1) && (temp1 == STARTBYTE2))
    			{
    				OSWait(K_SIG, 0)
    				temp = SBUF;
    				i = -1;  // 重新开始循环
    				Sum = STARTBYTE1 + STARTBYTE2;
    				continue;
    			}
    			Buf[i] = temp;  // 将 temp 存入串口接收数组
    			Sum += temp;  // 更新校验和
    			temp = temp1;  // 保存 temp1,下个循环再处理
    		}
    		Sum += temp1;  // 加上校验值,获取校验和
    		if(Sum == 0)
    			OSSendSignal(GET_RIGHT_TASK_ID);  // 串口命令处理完成,发送指定信号。
    	}	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    引入操作系统,那肯定是为了方便任务调度和提高系统实时性,也就是能在处理其他事情的同时,处理串口接收功能。

    三种方法的比较

    以下内容参考原文

    1. 从可读性和编程容易性:第三种最简单易读,第二种次之(要写队列函数),第一种最差。
    2. 从 RAM 占用方面:第二种占用最大(有队列),第三种较小,第一种最小。
    3. 从中断执行时间方面:第三种最长(不太理解,进出临界区要很长时间?),第一种较长,第二种最短。
    4. 从功能方面看:第三种功能最强(还可以升级,实现超时处理)

    如果串口数据发送得太快,第一种方法和第三种方法都会丢弃以前的数据,第二种方法则丢弃后面收到的数据。而且由于第二种方法是先获取整个包,所以在处理一个数据包期间(出现丢包)无法继续处理下一个包,也就是数据处理和接收时串行的关系,而第一种方法和第三种方法数据接收和处理为并行关系。

    RTOS 使得实时应用程序的设计和扩展变得容易,不需要大的改动就可以增加新的功能。通过应用程序吧设计分割为若干独立的任务,RTOS 使得应用程序的设计过程大为简化。

  • 相关阅读:
    猿创征文|瑞吉外卖——移动端_手机端展示
    【TypeScript笔记】04 - 在React中使用TypeScript
    对象 的属性名 在何时使用obj[‘属性名‘]
    vue3 组件v-model绑定props里的值,修改组件的值要触发回调
    算法通关村-----数组中元素出现次数问题
    Transfer principle
    idea安装MyBatisX插件,没有效果
    MySQL日志系统
    Java标识符和关键字
    Java第1章 入门及idea开发工具
  • 原文地址:https://blog.csdn.net/weixin_43772810/article/details/126931015