当前位置:首页 > 嵌入式 > 嵌入式教程
[导读]一个抢先式“裸奔"系统的设计

摘要:在一些资源比较紧张的嵌入式系统中,使用RTOS有时未必能够较好地满足系统较高的实时性要求。在软件设计时,可以借鉴抢先式RTOS实时调度内核的方法,实现更为高效的任务调度算法,从而实现系统更高的实时性要求。
关键词:嵌入式系统;抢先式调度;实时操作系统;STC12C5410

引言
   
这是2007年笔者在基于STC12C5410的工控系统里采用的软件技术。系统中有两个以主从方式通过I2C总线进行数据通信的节点,作为I2C总线的从机节点,因MCU性能限制了数据传输速率,因而每次通过总线传输30个字节的数据需要持续占用几十ms的时间。由于在进行I2C总线通信的这段时间里,系统将不能响应输入和改变输出(类似系统停顿),这么长的时间延迟对于有较高实时要求的工控系统显得难于容忍。
    为此,最初考虑解决问题的办法有3个:
    ①打断和拆分数据包,采用多次传输的办法。这样做不但需要修改从机的软件,多个数据包的连接又让软件变得复杂起来,所以这不是个很好的办法。
    ②由于I2C总线在进行数据传输中,波特率较低,存在大量短时delay(),可以采用定时中断,在定时中断中只变换一次电平后就返回,从而在后台完成数据发送。但这样就导致中断服务中必须执行一个很庞大的状态机判断,中断服务中大量的判断也非常耗时耗力,且调试也不方便。
    ③采用RTOS技术,但在80C51系统上使用RTOS,再精练的实时调度,每个tick的时间都很难低于1 ms。经测试,I2C总线传输中途遇到1 ms以上的传输中断,会产生总线超时错误,因而在本系统中即使采用RTOS也未必能很好地解决问题。
    通过一段时间对RTOS的分析和研究,最后在80C51的裸奔系统中嵌入特别定制的精练的抢先式调度来完成主要任务和I2C总线任务的并行执行,最终获得了很好的效果。
    下面就来详细地讲述这个定制的抢先式调度的编程技巧。

1 I2C总线通信子程序
   
对I2C总线的时序在此就不作介绍了,下面是部分基于Keil C51模拟主I2C总线的通信子程序代码如下:
   
   
    上面是基于80C51模拟I2C总线的通信程序,其中的HIGH、LOW是1、0的宏定义,idelay()提供时序要求的一段时间的延时。
    不难看出,这和通常的模拟I2C总线的通信子程序完全一样。事实上,我也是直接使用了以前的子程序。[!--empirenews.page--]

2 程序主执行函数main()函数
   
   
   
main()函数也非常简单。首先,调用Sys_init()完成单片机硬件系统的初始化;然后调用I2c_svr(),完成I2C总线通信系统的初始化,并执行数据传输,本函数稍后将作详细的介绍;接下来是一个while(1)主循环,其中的mainfunc()是执行主要任务的函数,完成系统的主要功能,并返回一个bool变量,这个变量用于I2C总线数据传输的请求;
    这里定义了一个bool型变量bi2csvr。作用:由mainfunc()执行结果来置位,系统根据此标志,启动数据通信,并在数据传输完成后清零这个标志。

3 I2C总线通信服务程序
   
通信服务程序I2c_svr()函数代码如下:
   
    这个函数看起来也不复杂,但是需要读者用RTOS任务的概念来理解这个函数。
    首先,关于寄存器组,这里的I2C服务程序I2c_svr()使用了单独的寄存器组(寄存器组1),由于#pragmarb(1)编译指令并不会让编译器自动生成切换寄存器组的指令,所以I2c_svr()中又通过修改PSW特殊寄存器来切换到工作寄存器组1。当然,要切换寄存器组,还需要确认在切换前,本函数没有使用工作寄存器。
    同时,I2c_svr()的初始化部分还执行了特殊功能寄存器压栈保存和切换堆栈指针SP,这些本是实时内核调度器里要完成的任务,在这里的出现相当于建立了新的任务。
    接下来的while(1)表明,这里相当于实时系统里的一个任务了。
    这个任务很简单,i2write()的功能就是通过I2C总线,发送数据缓冲区里所有的数据,在这里就不做详细介绍了。在发送完成后,清零数据发送请求标志位bi2csvr,然后执行延时等待。

4 定时中断和延时函数
   
抢先系统的关键部分是定时中断timer1()和延时函数idelay(),代码如下:
   
    首先看tsksw()宏,它的作用是保存堆栈指针并切换堆栈。这等同于RTOS里任务的上下文切换,但这里仅切换一下堆栈指针即可。
    接下来看这个定时中断服务函数timer1(),其中systern_tmr()是个修改定时器TH0的函数,这里不作介绍了。随后,约束判断(后面再作详细介绍)再通过tsksw()函数进行任务间的切换。
    接下来看延时函数idelay(),它提供I2C总线时序里要求的延时函数。注意:我们通常都是使用若干nop或者类似“for(x=LOOP;x>0;x——);”的延时来完成的,但这里一改这类传统的方式,而是通过“任务切换”将CPU控制权交给另外一个任务main来实现的。需要特别指出,idelay()里的关中断很重要,学习过RTOS的读者应该都记得RTOS里面的“临界段代码”的概念。
    最后,介绍上面未详细说明的定时中断服务函数timer1()中任务切换的约束判断。bi2csvr是I2C总线请求标志,如果这个标志为零,则表示不需要I2C总线的通信服务,定时中断里也就不需要做任务切换;此外,bi2cdly也是个控制切换的小技巧,该标志在idelay()中置位,在定时中断服务中判断并清零。也就是在执行idelay()后发生的第一次定时中断里只清除这个标志,而在第二次定时中断中才可能发生任务切换,以此保证idelay()的延时时间一定不少于一个定时器的溢出周期。
[!--empirenews.page--]
5 程序运行流程
   
程序初始化流程图如图1所示。


    首先,main()在完成硬件初始化Sys_init()后,调用I2c_svr()总线通信服务程序。
    I2c_svr()服务程序里,首先完成类似通用RTOS的任务现场保护的过程。再通过切换堆栈指针,完成了新任务堆栈的初始化过程。然后进入I2C总线通信模块主循环(类似创建任务的操作),再通过调用idelay(),将CPU的控制权交还给main()。奥妙就在于idelay()首先保存通信程序的寄存器现场(ACC和PSW),然后转换到main()的堆栈空间,并恢复刚才被I2c_svr()保存的寄存器现场(ACC和PSW)。所以;i2c_svr()里的idelay()函数返回后将不执行其下面的i2write(),而是执行main()里的while(1)。
    i2write()又如何能得到执行呢?它是通过定时中断服务程序timer1()再次获得CPU控制权的。如果在main()的执行中发生timer1()中断,因为timer1()里也进行与idelay()类似的任务切换操作,这时候将切换到I2c_svr()的堆栈和寄存器(现场)。此时timer1()中断返回时,不会返回到main()里,而是执行i2write()。
    另外,函数i2write()内部执行中也会调用idelay(),在I2c_svr()中的每次调用idelay()都会将CPU控制权交给main()的切换。main()和I2c_svr()的切换关系如图2所示。


    当然,timer1()并不总是引起任务的切换,通过判断bi2csvr标志可以避免(在不需要数据传输时)不必要的任务切换。另外,timer1()也可能进行从I2c_svr()到main()的切换。所以即使I2c_svr()里很长时间没有调用idelay(),也不会阻塞main()的执行。
    切换现场一般基于80C51的RTOS,通常要保存所有的CPU寄存器(包括8个工作寄存器、ACC、PSW、B、DPTR等),而这里与它们不同,因为在笔者的通信服务模块I2c_svr()中使用了另外的寄存器组,且未使用B和DPTR,因此不需要保存8个工作寄存器及B和DPTR,仅保存和恢复PSW和ACC这两个寄存器就可以了,大大提高了切换效率。
    本系统里仅有两个“任务”,即main()和I2c_svr(),也没有固定优先级,处于“等待”状态任务的优先级总比当前运行中的任务高,所以相当于同优先级时间片轮转调度方式。但相对于RTOS,这里还缺少操作系统必须管理的与任务相关的状态和数据结构,所以笔者还将其称做“裸奔”系统。

6 现场保护的补充说明
   
任务切换中的寄存器现场保护代码如下:
   
   
    上面是Keil C51对定时中断服务函数timer1()编译生成的LST文件。编译器在中断服务里自动生成压栈和出栈寄存器的指令,所以在写idelay()函数的寄存器现场切换的时候,必须完全遵守这个寄存器压栈和出栈顺序规则才能正常工作。

结语
   
通过学习和借鉴RTOS的CPU时间抢先调度和分配方法,可以将本系统中总线时序里许多很短的延时都交给主程序使用,最大程度利用CPU时间,实现主程序和通信服务程序的并行执行,从而让主程序和通信服务程序均达到系统要求的实时性能。
    本文为时间紧张的系统设计提供了一个新的解决思路。应该有助于初学操作系统的读者理解操作系统任务切换的工作机理。

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

舍弗勒以"专注驱动技术的科技公司"为主题亮相IAA MOBILITY 2025(B3馆B40展台) 合并纬湃科技后首次亮相IAA MOBILITY,展示拓展后的汽车产品组合 凭借在软件、...

关键字: 电气 软件 驱动技术 BSP

香港2025年 9月12日 /美通社/ -- 全球领先的互联网社区创建者 - 网龙网络控股有限公司 ("网龙"或"本公司",香港交易所股票代码:777)欣然宣布,其子公司My...

关键字: AI 远程控制 控制技术 BSP

深圳2025年9月11日 /美通社/ -- 2025 年 9 月 10 日,第 26 届中国国际光电博览会(简称 "CIOE 中国光博会")在深圳盛大开幕。本届展会吸引力再创新高,全球超3800家优质...

关键字: 自动化 光电 CIO BSP

天津2025年9月11日 /美通社/ -- 国际能源署(IEA)数据显示,2024 年全球数据中心电力消耗达 415 太瓦时,占全球总用电量的 1.5%,预计到 2030 年,这一数字将飙升至 945 太瓦时,近乎翻番,...

关键字: 模型 AI 数据中心 BSP

北京2025年9月11日 /美通社/ -- 国际9月11日上午,2025年中国国际服务贸易交易会(以下简称"服贸会")—体育赛事经济高质量发展大会现场,北京经济技术开发区工委委员、管委会副主...

关键字: 5G BSP GROUP MOTOR

柏林2025年9月9日 /美通社/ -- 2025年9月5日,纳斯达克上市公司优克联集团(NASDAQ: UCL)旗下全球互联品牌GlocalMe,正式亮相柏林国际消费电子展(IFA 2025),重磅推出融合企...

关键字: LOCAL LM BSP 移动网络

深圳2025年9月9日 /美通社/ -- PART 01活动背景 当技术的锋芒刺穿行业壁垒,万物互联的生态正重塑产业疆域。2025年,物联网产业迈入 "破界创造"与"共生进化" 的裂变时代——AI大模型消融感知边界,...

关键字: BSP 模型 微信 AIOT

"出海无界 商机无限"助力企业构建全球竞争力 深圳2025年9月9日 /美通社/ -- 2025年8月28日, 由领先商业管理媒体世界经理人携手环球资源联合主办、深圳•前海出海e站通协办的...

关键字: 解码 供应链 AI BSP

柏林2025年9月9日 /美通社/ -- 柏林当地时间9月6日,在2025德国柏林国际电子消费品展览会(International Funkausstellung...

关键字: 扫地机器人 耳机 PEN BSP

武汉2025年9月9日 /美通社/ -- 7月24日,2025慧聪跨业品牌巡展——湖北•武汉站在武汉中南花园酒店隆重举办!本次巡展由慧聪安防网、慧聪物联网、慧聪音响灯光网、慧聪LED屏网、慧聪教育网联合主办,吸引了安防、...

关键字: AI 希捷 BSP 平板
关闭