别被野路子带偏了,说说正经嵌入式工程师开发常用的3种架构

别被野路子带偏了,说说正经嵌入式工程师开发常用的3种架构

摘要:对于单片机程序来说,大家都不陌生,但是真正使用架构,考虑架构的恐怕并不多,随着程序开发的不断增多,架构是非常必要的。

71021b6578577dbc17113b0dc6c6d1c3.png

应用程序的架构大致有三种:

1、简单的前后台顺序执行程序,这类写法是大多数人使用的方法,不需用思考程序的具体架构,直接通过执行顺序编写应用程序即可。

2、时间片轮询法,此方法是介于顺序执行与操作系统之间的一种方法。

3、操作系统,此法应该是应用程序编写的最高境界。

///插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以哦~点我头像黑色字体加我地球呺也能领取哦。最近比较闲,带做毕设,带学生参加省级或以上比赛///

正文开始:

一、程序框架设计

1、前后台顺序执行法

这是初学者们常用的程序框架设计方案,不用考虑太多东西,代码简单,或者对系统的整体实时性和并发性要求不高;初始化后通过while(1){}或for(;;){}`循环不断调用自己编写完成的函数,也基本不考虑每个函数执行所需要的时间,大部分情况下函数中或多或少都存在毫秒级别的延时等待。

优点:对于初学者来说,这是最容易也是最直观的程序架构,逻辑简单明了,适用于逻辑简单,复杂度比较低的软件开发。

缺点:实时性低,由于每个函数或多或少存在毫秒级别的延时,即使是1ms,也会造成其他函数间隔执行时间的不同,虽然可通过定时器中断的方式,但是前提是中断执行函数花的时间必须短。当程序逻辑复杂度提升时,会导致后来维护人员的大脑混乱,很难理清楚该程序的运行状态。

以下是在校期间做的寝室防盗系统的部分代码(当时也存在部分BUG,没有解决。现在再看,其实很多问题,而且比较严重,比如中断服务函数内竟然有3000ms延时,这太可怕了,还有串口发送等等;由于实时性要求不算太高,因此主函数中的毫秒级别延时对系统运行没有多大影响,当然除BUG外;若是后期需要维护,那就是一个大工程,还不如推翻重写):

int main(void)
{ 
  u8 temperature;       
  u8 humidity;   
int a;
 delay_init();
 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  I2c_init(); 
 uart2_Init(9600);
 uart_init(9600);//串口初始化为115200  
 TIM3_Int_Init(4999,7199);
 ds1302_init();
while(DHT11_Init())//DHT11初始化
 {
  led2=0;
 }
 a1602_init();  
  Ds1302Init();
 EXTIX_Init();   
 GPIOX_Init();
 lcd12864_INIT();
 LcdInit();
 beep_init();
 RED_Init();
 led1=1;
 beep=0;
while(1)
 {
for(a=0;a<11;a++)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;num[a+3]=At24c02Read(a+2)-208;
&nbsp;&nbsp;&nbsp;delay_us(10);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;}
for(a=0;a<6;a++)
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;shuru[a]=At24c02Read(a+13)-208;
&nbsp;&nbsp;&nbsp;delay_us(10);&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;}&nbsp;&nbsp;
&nbsp;&nbsp;delay_ms(10);&nbsp;&nbsp;
&nbsp;&nbsp;RED_Scan();
&nbsp;&nbsp;Ds1302ReadTime();&nbsp;&nbsp;//读取ds1302的日期时间
&nbsp;&nbsp;shi=At24c02Read(0);&nbsp;//读取闹钟保存的数据
&nbsp;&nbsp;delay_ms(10);
&nbsp;&nbsp;fen=At24c02Read(1);&nbsp;//读取闹钟保存的数据
&nbsp;&nbsp;usart2_scan();&nbsp;&nbsp;&nbsp;//蓝牙数据扫描
&nbsp;&nbsp;usart2_bian();&nbsp;&nbsp;&nbsp;//蓝牙处理数据
&nbsp;&nbsp;usart2_gai();
&nbsp;&nbsp;nao_scan();
&nbsp;&nbsp;k++;
if(k<20)
&nbsp;&nbsp;{
if(k==1)
&nbsp;&nbsp;&nbsp;&nbsp;LcdWriteCom(0x01);//清屏
&nbsp;&nbsp;&nbsp;&nbsp;LcdDisplay();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//显示日期时间
&nbsp;&nbsp;}
if(RED==0)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;RED_Scan();&nbsp;&nbsp;&nbsp;&nbsp;
if(k>=20&&k<30)
&nbsp;&nbsp;{
if(k==20)
&nbsp;&nbsp;&nbsp;&nbsp;LcdWriteCom(0x01);&nbsp;//清屏
&nbsp;&nbsp;&nbsp;Lcddisplay();&nbsp;//显示温湿度
&nbsp;&nbsp;&nbsp;LcdWriteCom(0x80+6);&nbsp;
&nbsp;&nbsp;&nbsp;DHT11_Read_Data(&temperature,&humidity);&nbsp;//读取温湿度值
&nbsp;&nbsp;&nbsp;Temp=temperature;Humi=humidity;
&nbsp;&nbsp;&nbsp;LcdWriteData('0'+temperature/10);
&nbsp;&nbsp;&nbsp;LcdWriteData('0'+temperature%10);
&nbsp;&nbsp;&nbsp;LcdWriteCom(0x80+0X40+6);&nbsp;
&nbsp;&nbsp;&nbsp;LcdWriteData('0'+humidity/10);
&nbsp;&nbsp;&nbsp;LcdWriteData('0'+humidity%10);
&nbsp;&nbsp;}
if(k==30)
&nbsp;&nbsp;&nbsp;k=0;
&nbsp;&nbsp;lcd12864();&nbsp;//显示防盗闹钟状态
&nbsp;}&nbsp;&nbsp;
}

//定时器3中断服务程序
void&nbsp;TIM3_IRQHandler(void)//TIM3中断
{
int&nbsp;i;
if(TIM_GetITStatus(TIM3,&nbsp;TIM_IT_Update)&nbsp;!=&nbsp;RESET)&nbsp;//检查TIM3更新中断发生与否
&nbsp;{
&nbsp;&nbsp;TIM_ClearITPendingBit(TIM3,&nbsp;TIM_IT_Update);//清除TIMx更新中断标志
if(key1==1&&FEN-fen==0&&SHI-shi==0)//时间一到闹钟响起
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;f=1;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;}
if(key1==0||FEN-fen!=0||SHI-shi!=0)
else
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;f=0;
&nbsp;&nbsp;}&nbsp;
if(USART_RX_BUF[0]=='R'&&USART_RX_BUF[1]=='I'&&USART_RX_BUF[2]=='N'&&USART_RX_BUF[3]=='G')
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;key0=1;
for(i=0;i<17;i++)
&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;USART_SendData(USART1,&nbsp;num[i]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
&nbsp;&nbsp;&nbsp;&nbsp;USART_RX_STA=0;&nbsp;
&nbsp;&nbsp;&nbsp;}&nbsp;
&nbsp;&nbsp;&nbsp;delay_ms(3000);
for(i=0;i<3;i++)
&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;USART_SendData(USART1,&nbsp;num1[i]);//向串口1发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
&nbsp;&nbsp;&nbsp;&nbsp;USART_RX_STA=0;&nbsp;
&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;}
&nbsp;}
}

二、时间片论法

介于前后台顺序执行法和操作系统之间的一种程序架构设计方案。该设计方案需能帮助嵌入式软件开发者更上一层楼,在嵌入式软件开发过程中,若遇到以下几点,那么该设计方案可以说是最优选择,适用于程序较复杂的嵌入式系统;

·目前的需求设计需要完全没有必要上操作系统。

5fa092727628b7b503ea6037f4289d5a.png

·任务函数无需时刻执行,存在间隔时间(比如按键,一般情况下,都需要软件防抖,初学者的做法通常是延时10ms左右再去判断,但10ms极大浪费了CPU的资源,在这段时间内CPU完全可以处理很多其他事情)

·实时性有一定的要求。

该设计方案需要使用一个定时器,一般情况下定时1ms即可(定时时间可随意定,但中断过于频繁效率就低,中断太长,实时性差),因此需要考虑到每个任务函数的执行时间,建议不能超过1ms(能通过程序优化缩短执行时间则最好优化,如果不能优化的,则必须保证该任务的执行周期必须远大于任务所执行的耗时时间),同时要求主循环或任务函数中不能存在毫秒级别的延时。

如何确定每个函数的任务周期呢?根据任务的耗时和效果决定、如按键扫描任务周期为&nbsp;10ms(为了提高响应),指示灯控制任务周期为&nbsp;100ms(通常情况下最高100ms的闪烁频率正好,特殊需求除外),LCD/OLED显示周期为&nbsp;100ms(通过这种通过SPI/IIC等接口的方式耗时大约在&nbsp;1~10ms,甚至更长,所以任务周期必须远大于耗时,同时为了满足人眼所能接受的刷屏效果,也不能太长,100ms的任务周期比较合适)等。

以下介绍两种不同的实现方案,分别针对无函数指针概念的朋友和想进一步学习的朋友。

1、无函数指针的设计方式

/**
&nbsp;&nbsp;*&nbsp;@brief&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主函数.
&nbsp;&nbsp;*&nbsp;@param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*&nbsp;@return&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*/

int&nbsp;main(void)
{
&nbsp;&nbsp;&nbsp;&nbsp;System_Init();

while&nbsp;(1)
&nbsp;&nbsp;&nbsp;&nbsp;{
if&nbsp;(TIM_1msFlag)//&nbsp;1ms
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CAN_CommTask();&nbsp;//&nbsp;CAN发送/接收通信任务

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TIM_1msFlag&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if&nbsp;(TIM_10msFlag)&nbsp;&nbsp;//&nbsp;10ms
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;KEY_ScanTask();&nbsp;//按键扫描处理任务

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TIM_10msFlag&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if&nbsp;(TIM_20msFlag)&nbsp;//&nbsp;20ms
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LOGIC_HandleTask();//逻辑处理任务

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TIM_20msFlag&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if&nbsp;(TIM_100msFlag)&nbsp;//&nbsp;100ms
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LED_CtrlTask();&nbsp;//指示灯控制任务

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TIM_100msFlag&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if&nbsp;(TIM_500msFlag)//&nbsp;500ms
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TIM_500msFlag&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
if&nbsp;(TIM_1secFlag)&nbsp;//&nbsp;1s
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WDog_Task();&nbsp;//喂狗任务
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TIM_1secFlag&nbsp;=&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;}
}

/**
&nbsp;&nbsp;*&nbsp;@brief&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;定时器3中断服务函数.
&nbsp;&nbsp;*&nbsp;@param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*&nbsp;@return&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*/
void&nbsp;TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)&nbsp;==&nbsp;SET)&nbsp;//溢出中断
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_1msTic++;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_1msTic&nbsp;%&nbsp;1&nbsp;==&nbsp;0&nbsp;?&nbsp;TIM_1msFlag&nbsp;=&nbsp;1&nbsp;:&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_1msTic&nbsp;%&nbsp;10&nbsp;==&nbsp;0&nbsp;?&nbsp;TIM_10msFlag&nbsp;=&nbsp;1&nbsp;:&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_1msTic&nbsp;%&nbsp;20&nbsp;==&nbsp;0&nbsp;?&nbsp;TIM_20msFlag&nbsp;=&nbsp;1&nbsp;:&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_1msTic&nbsp;%&nbsp;100&nbsp;==&nbsp;0&nbsp;?&nbsp;TIM_100msFlag&nbsp;=&nbsp;1&nbsp;:&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_1msTic&nbsp;%&nbsp;500&nbsp;==&nbsp;0&nbsp;?&nbsp;TIM_500msFlag&nbsp;=&nbsp;1&nbsp;:&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_1msTic&nbsp;%&nbsp;1000&nbsp;==&nbsp;0&nbsp;?&nbsp;(TIM_1secFlag&nbsp;&nbsp;=&nbsp;1,&nbsp;sg_1msTic&nbsp;=&nbsp;0)&nbsp;:&nbsp;0;
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;TIM_ClearITPendingBit(TIM3,TIM_IT_Update);&nbsp;&nbsp;//清除中断标志位
}

2、含函数指针的设计方式

/**
&nbsp;&nbsp;*&nbsp;@brief任务函数相关信息结构体定义.
&nbsp;&nbsp;*/
typedefstruct{
&nbsp;&nbsp;&nbsp;&nbsp;uint8&nbsp;m_runFlag;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*!<程序运行标记:0-不运行,1运行&nbsp;*/
&nbsp;&nbsp;&nbsp;&nbsp;uint16&nbsp;m_timer;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*!<计时器&nbsp;*/
&nbsp;&nbsp;&nbsp;&nbsp;uint16&nbsp;m_itvTime;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*!<任务运行间隔时间&nbsp;*/
void&nbsp;(*m_pTaskHook)(void);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*!<要运行的任务函数&nbsp;*/
}&nbsp;TASK_InfoType;&nbsp;

#define&nbsp;TASKS_MAX&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//定义任务数目

/**任务函数相关信息&nbsp;*/
static&nbsp;TASK_InfoType&nbsp;sg_tTaskInfo[TASKS_MAX]&nbsp;=&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;{ 0,&nbsp;1,&nbsp;1,&nbsp;CAN_CommTask},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;CAN通信任务
&nbsp;&nbsp;&nbsp;&nbsp;{ 0,&nbsp;10,&nbsp;10,&nbsp;KEY_ScanTask},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//按键扫描任务
&nbsp;&nbsp;&nbsp;&nbsp;{ 0,&nbsp;20,&nbsp;20,&nbsp;LOGIC_HandleTask},&nbsp;&nbsp;&nbsp;//逻辑处理任务
&nbsp;&nbsp;&nbsp;&nbsp;{ 0,&nbsp;100,&nbsp;100,&nbsp;LED_CtrlTask},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//指示灯控制任务
&nbsp;&nbsp;&nbsp;&nbsp;{ 0,&nbsp;1000,&nbsp;1000,&nbsp;WDog_Task},&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//喂狗任务
};


/**
&nbsp;&nbsp;*&nbsp;@brief&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;任务函数运行标志处理.
&nbsp;&nbsp;*&nbsp;@note&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;该函数由1ms定时器中断调用
&nbsp;&nbsp;*&nbsp;@param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*&nbsp;@return&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*/
void&nbsp;TASK_Remarks(void)
{
&nbsp;&nbsp;&nbsp;&nbsp;uint8&nbsp;i;&nbsp;
for&nbsp;(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;{
if&nbsp;(sg_tTaskInfo[i].m_timer)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_tTaskInfo[i].m_timer--;&nbsp;
if&nbsp;(0&nbsp;==&nbsp;sg_tTaskInfo[i].m_timer)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_tTaskInfo[i].m_timer&nbsp;=&nbsp;sg_tTaskInfo[i].m_itvTime;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_tTaskInfo[i].m_runFlag&nbsp;=&nbsp;1;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;}
}

/**
&nbsp;&nbsp;*&nbsp;@brief&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;任务函数运行处理.
&nbsp;&nbsp;*&nbsp;@note&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;该函数由主循环调用
&nbsp;&nbsp;*&nbsp;@param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*&nbsp;@return&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*/
void&nbsp;TASK_Process(void)
{
&nbsp;&nbsp;&nbsp;&nbsp;uint8&nbsp;i;&nbsp;
for&nbsp;(i&nbsp;=&nbsp;0;&nbsp;i&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;{
if&nbsp;(sg_tTaskInfo[i].m_runFlag)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_tTaskInfo[i].m_pTaskHook();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//运行任务
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sg_tTaskInfo[i].m_runFlag&nbsp;=&nbsp;0;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//标志清0
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;
}

/**
&nbsp;&nbsp;*&nbsp;@brief&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主函数.
&nbsp;&nbsp;*&nbsp;@param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*&nbsp;@return&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*/
int&nbsp;main(void)
{
&nbsp;&nbsp;&nbsp;&nbsp;System_Init();&nbsp;&nbsp;&nbsp;&nbsp;
while&nbsp;(1)
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TASK_Process();
&nbsp;&nbsp;&nbsp;&nbsp;}
}

/**
&nbsp;&nbsp;*&nbsp;@brief&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;定时器3中断服务函数.
&nbsp;&nbsp;*&nbsp;@param&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*&nbsp;@return&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;None.
&nbsp;&nbsp;*/
void&nbsp;TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)&nbsp;==&nbsp;SET)&nbsp;//溢出中断
&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TASK_Remarks();
&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;&nbsp;&nbsp;
&nbsp;&nbsp;&nbsp;&nbsp;TIM_ClearITPendingBit(TIM3,TIM_IT_Update);//清除中断标志位
}

三、操作系统

嵌入式操作系统EOS(Embedded OperatingSystem)是一种用途广泛的系统软件,过去它主要应用于工业控制和国防系统领域,而对于单片机来说,比较常用的有UCOS、FreeRTOS、RT-ThreadNano和RTX等多种抢占式操作系统(其他如Linux等操作系统不适用于单片机)

操作系统和“时间片论法”,在任务执行方面来说,操作系统对每个任务的耗时没有过多的要求,需要通过设置每个任务的优先级,在高优先级的任务就绪时,会抢占低优先级的任务;操作系统相对复杂,因此这里没有详细介绍了。

关于如何选择合适的操作系统(uCOS、FreeRTOS、RTThread、RTX等RTOS的对比之特点:

·uCOS:网上资料丰富,非常适合学习,但是在产品上使用则需要收费。

·FreeRTOS:使用免费,因此很多产品都在用。

·RT-Thread:国产物联网操作系统,有着十分丰富的组件,也免费,资料:RT-Thread文档中心。

·RTX:为ARM和Cortex-M设备设计的免版税,确定性的实时操作系统。

借网上一张对比图:

910c99f89152cbfe849b14aecfa4f91e.png

四、总结

从上述的对比中可以看出,时间片轮询法的优势还是比较大的,它既有前后台顺序执行法的优点,也有操作系统的优点。结构清晰,简单,非常容易理解,所以这种是比较常用的单片机设计框架。

END

猜你喜欢

转载自blog.csdn.net/danpianji777/article/details/124447925