刚开始接触STM8,就被其极高的性价比所吸引,刚好手头有一些STM8S003F3的板子,于是就以此为基础进行研究。
之前使用STM32F103时已经挂载了ucosii,正如周立功先生所说的,一旦使用操作系统来编程,那么就很难再接受以前的前后台编程,
所以在还未确切明了STM8究竟有什么功能的时候,我首先想的是这玩意能使用哪种操作系统呢。ucosii肯定不行,对STM8这样的小身板
而言太大了,扛不住。在网上找啊找,找到了atomtheards,号称专为STM8而设计的。根据atom的官方资料,RAM要求低至1KB,S003刚
好达标,于是找了个基于IAR的例程一番修改,编译成功,也能仿真下载,就是无法正常工作,一直提示堆栈指针超出范围,这还是只有
一个LED显示任务的情况,在加了一个简单的串口任务以后,连编译都无法通过。大概分析了一下,还是RAM太小,跑不动,但是添加任
务就得开辟堆栈,开辟堆栈就需要占用RAM,就那么点资源,三两下就玩完了,所以像S003这种小RAM的片子基本跟OS拜拜了。
但事还没完,OS用不了,那么有没有其他方法来实现呢?想起以前看过的时间片设计方法,就是给每一个任务分配一个时间片(就好
比任务是一辆辆的汽车,有大有小,时间片就是汽油,大车加多点,小车加少点),虽然实时性差点,但资源占用少。不过呢,一个完整
的时间片操作对S003而言还是有点复杂(确切的说是我自己感觉复杂),毕竟在不同的任务跳转时还是需要开辟堆栈进行现场保护的,巧
妇难为无米之炊,RAM太小,要把人逼疯的节奏。RAM太小,不要开辟堆栈不就行了,那跟前后台方式有什么区别?实际上,我这里实现
的方法就是一种前后台的过程,但实现的思想是类似于时间片的多任务方式。
首先实现的是一个定时函数Delay_Ds(u8 ds_num, u32 t);
volatile u32 tick; //系统心跳,每1ms加1,在定时中断中实现
static u32 fac_us = 0; //us延时倍乘数
static u32 ds_read[DS_NUM_MAX]; //读取当前tick
static u32 ds_read_bit[DS_NUM_MAX]; //当前tick已读取标志
static u8 ds_sw[DS_NUM_MAX]; //定时开关
/*****************************************************************************
功能:定时
入参:ds_num, 定时编号
t, 定时时间,单位ms
返回:REACHED, 定时时间到
UNREACHED, 定时时间未到
说明1:当定时函数用于不同的定时任务时,定时编号不能重复
说明2:u32个ms的一个计数周期约49天,不考虑tick跨越多个计数周期的情况(也不用考虑
,因为一个定时最多也就u32个计数)
******************************************************************************/
Timing_Type Delay_Ds(u8 ds_num, u32 t)
{
if(ds_sw[ds_num] == 1) return UNREACHED; //定时器关闭
CLI(); //不允许中断打断
if(ds_read_bit[ds_num]==0) //定时开始,还未读取当前tick值
{
ds_read[ds_num]=tick; //读取当前tick
ds_read_bit[ds_num]=1; //标记为已读
}
SEI();
if(ds_read[ds_num]<tick) //tick还在一个计数周期内
{
if(t<=tick-ds_read[ds_num]) //定时时间到
{
ds_read_bit[ds_num]=0; //重新计数
return REACHED; //返回定时到
}
else return UNREACHED; //否则返回定时未到
}
else if(ds_read[ds_num]>tick) //tick已经在下一个计数周期内
{
if(t<=0xffffffff-ds_read[ds_num]+tick)
{
ds_read_bit[ds_num]=0; //定时时间到,重新计数
return REACHED;
}
else return UNREACHED;
}
else return UNREACHED; //
}
例如,LED以500ms的周期进行闪烁,那么就可以使用Delay_Ds()函数为LED闪烁任务设置一个500ms定时,500ms定时到,则执行一次
LED操作,然后让出CPU去执行其它任务。过程如下:
while(1)
{
while(Delay_Ds(led_num, 500) == REACHED) {LED_Task();}
while(Dealy_Ds(uart_num, 10) == REACHED) {UART_Task();}
……
}
CPU在整个主循环中查询各个任务定时是否到,到则执行一次任务并重新计时,不到则继续查询。
这样实现的好处显而易见,由于每个任务都是执行完成以后才会让出CPU,所以不会出现被打断的情况,所以不用开辟堆栈来进行现场
保护,在S003上运行起来绰绰有余,甚至更小RAM的单片机都可以运行;相比较传统的前后台编程方式,这种方法的逻辑更简单,不同的
任务可以看作是一个独立的处理过程;实现过程是硬件无关的,所以写好的程序可以在任意平台移植。当然,缺点也同样明显,一个任务
的执行时间不能太长,否则会影响其它任务的处理;未涉及任务的优先级,实时性差等等。
现已通过这种方法在S003上实现了一个1HZ的LED闪烁任务,一个2HZ的LED闪烁任务和一个每10ms接收一次串口数据并发送回去的串
口任务,当串口助手以100ms发送一帧数据时 ,S003可以很流畅地运行,执行效果与使用OS的多任务处理一样。
由于本人水平有限,难免有错误以及不足之处,欢迎大家批评指正。我在此也是抛砖引玉,如果各位有更好的方法,还请不吝赐教!
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
一周热门 更多>