我萌萌哒的妹纸是一个代码苦手,完备无法理解 C 措辞,以是每一次到单片机上机须要交作业的时候都是愁眉苦脸的样子。而我又总是由于自己确实不懂单片机里面的各类奇怪定义(中断,串口,P1.x 之类),以是也一贯没有什么好办法去帮她。这一次的作业对编码能力哀求较高,但是涉及到的硬件比较少,于是决定以此为契机,开始我的嵌入式开拓之旅。
需求
这次的如下:

基本哀求
基本计时和显示功能(用 12 小时制显示)。
包括高下午标志,时、分的数字显示,秒旗子暗记指示。
能设置当前韶光(含上、下午,时,分)
能实现基本打铃功能,规定:
上午 6:00 起床铃;打铃 5 秒、停 2 秒、再打铃 5 秒。
下午 10:30 熄灯铃;打铃 5 秒、停 2 秒、再打铃 5 秒。
铃声可用 LED 灯光显示,如果实验装置没有 LED 发光管,可以用七段显示管的小数点显示,也可以用显示小时的十位数码管的多余段显示。凡是用到铃声功能的均可如此处理。
发挥部分
增加整点报时功能,整点时响铃 5 秒,哀求有掌握启动和关闭功能。
增加调度起床铃、熄灯铃韶光的功能。
增加调度打铃韶光是非和间歇韶光是非的功能。
增设上午 4 节课的上、下课打铃功能,规定:
7:30 上课,8:20 下课;8:30 上课,9:20 下课;9:40 上课,10:30 下课;10:40 上课,11:30 下课;每次铃声 5 秒。
利用板上按键做一个 12 小时/24 小时的显示格式切换
剖析
既然我都出动了,肯定不能知足于只完成基本哀求,决定把所有功能全都完全的实现。
大略的来说,整体需求可以分为三个部分:显示,打铃,修正。
须要用到的东西有:串口,指示灯和一个按键。
显示
遵照大略的前后端分离的思想,我们可以利用三个全局变量 hour , minute , second 来存储当前的韶光,只须要在显示的时候区分高下午和 12 小时/ 24 小时即可。这两个部分解耦之后会创造,我们后面的利用板上按键修正显示格式也变得随意马虎了很多。
通过串口显示也便是须要向指定变量发送字符,将这个功能抽象并封装之后,对付我后续的编程来说,也便是调用一下 Send_Str(str) 的过程。
打铃
打铃是这套系统的重头戏,由于学校方面的资源限定,以是利用指示灯示意的方法来代替打铃。
指示灯的亮灭是通过掌握一个变量的值来确定的,于是我只要在精确时候设置精确的值,打铃系统就能按照我期望的办法事情。
修正
修正同样是通过串口进行的。
在最开始的设计文档中,本来是哀求利用4个按键来进行设计,也便是说跟一个普通的电子表差不多。但是非常因缺思艇的事情是学校的按键不足了,以是老师哀求所有功能都用串口实现。
跟显示有些不同的地方是,通过串口向芯片发送数据须要精确利用串口中断。
综上,这个别系所须要的全部内容就已经实现了。可以看到我做了很多将对硬件的操作抽象化的处理,实在这一点非常主要。由于对付我来说,嵌入式开拓最大的难题在于,我不知道里面各类变量的含义,不知道如何操作详细的硬件。将硬件操作抽象化处理之后,我就可以很方便地开展我的后续开拓。
问题
实现就不再赘述了,想必读者一定都比我强,下面聊一聊碰着的问题以及 debug 的经历。
串口配置
串口的收和发实在是分开的,这里用到了两个变量: UCA0TXBUF , UCA0RXBUF 。从字面意思上可以看出,前一个用于发送,后一个用于吸收(相对付开拓者来说)。发送和吸收实在便是给这两个值赋值的过程,看起来这两个变量在接管到值之后会将这个值传给别的变量,以是只要不断的将值赋给它就行,我们写了这样的函数:
#pragma vector=USCIAB0RX_VECTOR //中断做事函数
__interrupt void uart() {
rec = UCA0RXBUF;
//读取到缓冲区
strtmp[strlen++] = rec;
strtmp[strlen] = '\0';
//切换模式
mode = strtmp[0];
}
//发送字符
void Send_Char(char ch) {
while (!(IFG2 & UCA0TXIFG));
UCA0TXBUF = ch;
}
//发送字符串
void Send_Str(char p) {
unsigned char i;
i = 0;
while ((p + i) != '\0') {
while (!(IFG2 & UCA0TXIFG));
UCA0TXBUF = (p + i);
i++;
}
Send_Char(0x0d);
Send_Char(0x0a);
}
uart 貌似是一个内置的中断函数,用来处理串口的吸收,只要将变量 UCA0RXBUF 的值存储起来即可;后面的 Send_Str 就非常好理解了,将值发送给 UCA0TXBUF ,从而实现串口的输出。
思路如此清晰,但是测试的时候却碰着了问题,我们的输出是空的,转为16进制显示后,全都是0x00。这个问题调试了良久,拿着原来的代码逐行比对之后创造,出了这样的问题:
1234- UCA0BR0 = 130;- UCA0BR1 = 6;+ UCA0BR0 = 104;+ UCA0BR1 = 0;Google 一下才明白,原来 UCA0BR0 和 UCA0BR1 是由系统的时钟速率和波特率决定的值,如果设置缺点就会导致串口发送失落败。详细的值可以参考用户手册, Ctrl+F 搜索 Table 15-4. Commonly Used Baud Rates
即可。
串口输出非常
前面提到我们直策应用三个变量保存当前韶光,在输出时做进一步处理,转为字符串的过程中,我们进行了这样的操作:
1Time[0] = hour / 10 + '0';但有趣的事情是,在初始化之后,我们得到的输出是这样的: 0/:0/:0/
。随手输出了一下/
的 ASCII 码,创造它刚好比 0
小一。
难道说,存储器中的默认值不是 0 吗? Google 一下之后创造,还真的不是 0 。 MSP430G2553 中的 Flash 存储器在默认状态下的值全为 1 ,然后写入时只能将 1 置为 0 ,以是每一次写入数据都须要先清空再写入。那么问题来了,为什么全为 1 会导致末了输出的结果小 1 呢?我来大略的阐述一下我的理解:
假设这个存储器只有 8 位,也便是说,现在的值为 11111111
,然后我加上一个 1
,于是我们得到:
显然,我们末了的结果已经移除了,此时会产生截断,也便是说,存储器现在的数据变成了00000000
,也便是 0
,跟我们期望的结果 1
刚好相差一。
当然,实际的情形要比我上面的举例要繁芜的多,不过我想已经足够我们认识到这个 BUG 的实质,就不再多说啦。
Flash 存储器未清空
在测试中,我们创造每一次烧录程序之后, Flash 存储器不会清空,依然会从上一次我们保存的韶光开始计时。我以为这是精确的行为,没有在意,但是我妹纸和她的队友见告我她们在完成上一个作业的时候每次都是会清空的。我对着这次和上次的代码研究了良久,认为代码里面根本就没有清空 Flash 存储器的操作,如果有的话,掉电保存这项功能根本无从谈起。我妹纸她们也赞许我的剖析,但是她们的实践确实证明了每次都会清空 Flash 存储区。
这个问题也困扰了良久,直到第二天,用别人的电脑重新烧录了一遍程序,创造他们的是会正常清空的。以是说,问题在于 CCS 的版本:我妹纸利用的 CCS 版本是 6.1 ,而
他们用的版本是 5.1.1 ,也便是说,不同版本的 CCS 在烧录程序期间的不同行为导致了这次缺点。我们换用了 5.1.1 之后,成功办理了这个问题。
总结
对嵌入式开拓有了初步的理解,向着真·全栈开拓工程师又近了一步。
这一次的开拓经历碰着了很多因缺思艇的问题,由于嵌入式开拓本身比较倾向底层,这次开拓乃至还碰着了存储器的存储事理。也有一点将自己看的 CSAPP 交融贯通的觉得,还是很故意思的。