当前位置:文档之家› 手把手教你学单片机进阶-框架篇(上)

手把手教你学单片机进阶-框架篇(上)

手把手教你学单片机进阶教程
框架篇(上)
本篇里我们聊一聊软件框架,那么什么是框架呢?它又有什么用处呢?
软件框架,说简单一点就是我们组织软件的方式,没有框架的代码,模块是乱添的,写程序的人也不知道要添在哪里,只知道加在这里可以用,放在这里就行。

有框架的代码,模块应该添加到哪里是清楚的。

曾经看过一句话说的挺有道理,“一个好的程序架构,是一个有经验的工程师和一个初学者的分水岭”,我们在单片机软件中用的最多的结构莫过于下面这样的:
1While(1)
2{
3Led_on();
4i = 1000;
5While(i--);
6Led_off();
7}
当我们刚开始学习的时个,程序比较简单,这样单纯的结构还是可以应付的。

但是如果当我们真的要做一个系统级的工程的时候,有很多功能要处理,这样的结构显然就不能适应了,只那一个“while(i--)”就不知道耽误了多少事儿。

到这里有人要说了,工程复杂的时候我可以跑个OS。

可以说RTOS对于功能复杂的嵌入式系统来说是个非常好的解决方案,如比较出名的RT-Thread (我们以后会推出这个的专题),uc/os-ii。

不过,可惜的是,操作系统对于51来说有点太,系统的开销有可能比应用占用的资源的还要多。

那么我们就自己动手来打造一个小资源单片机适用的框架吧。

我们将实现两个版本,其内在思想都是一样的,一个简单点,占用资源更小。

一个稍复杂些,
pzq@/csh@sparkcn
但更灵活。

大家可以根据情况选用。

easy_framework
我们把这个框架暂时命名为easy_framework,这里我们仅提供思想和基本的代码。

完整的代码可以在我们提供代码包(下载地址见文章最后)里找到。

这里测试和实验的硬件环境是Spark51学习板。

大家如果有需要,可以到我们工作室店铺购买或直接通过QQ联系我们首先使用一个Timer来产生一个1m的定时中断。

在51单片机上,这里选用Timer0
1init_sys_timer()
2{
3/*T0 1方式*/
4TMOD |= 0x01;
5/*设定1m的计时*/
6TL0 = (u8)SYS_TIMER_INIT_VALUE;
7TH0= (u8)(SYS_TIMER_INIT_VALUE >> 8);
8/*允许中断*/
9ET0 = 1;
10TR0 = 1;
11}
通过上面这个函数产生一个1m的中断,在其“中断服务函数”里我们对一些时间标志量进行记数
1/*Timer 0中断服务程序*/
2void proc_sys_tick() interrupt 1
3{
4TR0 = 0;
5Cnt5ms++;
pzq@/csh@sparkcn
6Cnt10ms++;
7Cnt20ms++;
8Cnt50ms++;
9Cnt100ms++;
10/*处理1ms 任务*/
11task_1ms_period();
12/*如果需要非常精确的1ms,这里需要做初值的修正*/
13TL0 = (u8)SYS_TIMER_INIT_VALUE;
14TH0= (u8)(SYS_TIMER_INIT_VALUE >> 8);
15TR0 = 1;
16}
每中断一次,这些时间示志量会加1,在主程序里我们会用到这些标志。

主程序如下:
1void main(void)
2{
3/* 要用到1秒的任务,放到100ms里计时*/
4u8 cnt_1000ms = 0;
5/*硬件相关初始化*/
6init_somthing();
7init_sysclk();
8EA = 1;
9while (1)
10{
11/*5 ms 定时到,执行10ms周期任务*/
12if (Cnt5ms >= 5)
13{
14Cnt5ms = 0;
15task_5ms_period();
16}
pzq@/csh@sparkcn
17/*10 ms 定时到,执行10ms周期任务*/
18if (Cnt10ms >= 10)
19{
20Cnt10ms = 0;
21task_10ms_period();
22}
23/*100ms 定时到,执行10ms周期任务*/
24if (Cnt100ms >= 100)
25{
26Cnt100ms = 0;
27task_100ms_period();
28cnt_1000ms++;
29
30/*在100ms的任务里为1000ms的任务计时*/
31if (cnt_1000ms >= 10)
32{
33cnt_1000ms = 0;
34/* 1000ms */
35task_1000ms_period();
36}
37}
38}
39}
到这里,easy_framework已经完全浮出水面了。

我们把系统划分成若干个周期性运行的任务,只需要完成相应的任务函数即可,这种“划分任务,分时运行”的方式也是RTOS的基本思想。

通过这种划分,我们对整个系统的运行情况了解的非常清楚,再也不用发愁新加的模块放到哪里合适了。

而easy_framework的使用的资源仅仅是一个定时器和几个变量而已。

pzq@/csh@sparkcn
pzq@/csh@sparkcn
实验
下面就用这个框架写个简单的流水灯的的例子看一下(完整的例子在代码包里可以找到),我们的实验平台如下:
实验平台: Spark51开发板
编程环境:TKStudio + KEIL 9.0
Spark51学习板上有
8个LED 灯,其连接的原理图如下,也可以参考学习板的整体原理图(学习板的资料里有)
这8个LED 是按共阳级连接的,公共端通过74HC138的一个输出来控制,使公共输出控制端即LEDS6输出低,三极管导通,相应选择端输出低即可点亮对应的LED 灯了。

下面是流水灯的代码
1void led_test()
2{
3static u8 p1_value = 0x01;
4ADDR2 = 1;
5ADDR1 = 1;
6ADDR0 = 0;
7ADDR3 = 1;
8ENLED = 0;
9
10P0 = ~p1_value;
11p1_value <<= 1;
12if (0 == p1_value)
13{
14p1_value = 0x01;
15}
16}
任务代码:
1void task_100ms_period()
2{
3led_test();
4}
是不是见不到讨厌的while(i--)了,把程序编译好下到Spark51学习板里看一下效果。

如果想调整流水灯的频率,把这段代码放到另一个task_XXms_period()里就可以了,是不是很方便。

思考
一个方便、精简的框架搭建好了,我们写程序就方便了。

但我们高兴的同时还需要思考一下几个问题:
pzq@/csh@sparkcn
1这样框架有什么局限性?
a)思路一:怎么实现一个可变流水速度的流水灯
b)思路二:各任务运行的时机效率是否达到最高
2代码中有一句注释“/*如果需要非常精确的1ms,这里需要做初值的修正*/”,应该怎样修正?
欢迎就以上问题与我们交流、讨论,我们的联系方式QQ:pzq@ 或csh@
下篇预告
在下一篇文章我们将和大家一起构建另一个软件框架mini_framework。

比easy_framework灵活、方便。

Are U Ready ?Let’s Go!
作者简介
Spark嵌入式工作室,成立于2010年,致力于嵌入式方面的软、硬件开发和研究,团队成员都是有经验的开发工程师,擅长使用51单片机、STM32。

联系方式QQ:pzq@
csh@
验证码:spark
QQ技术交流群:186232047
代码包下载地址:或直接到群里共享下载
pzq@/csh@sparkcn
参考资料
pzq@/csh@sparkcn。

相关主题