基于MSP430G2553的打铃系统开发手记

前言

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

需求

这次的如下:

基本要求

基本计时和显示功能(用12小时制显示)。

包括上下午标志,时、分的数字显示,秒信号指示。

能设置当前时间(含上、下午,时,分)

能实现基本打铃功能,规定:

上午6:00起床铃;打铃5秒、停2秒、再打铃5秒。

下午10:30熄灯铃;打铃5秒、停2秒、再打铃5秒。

发挥部分

增加整点报时功能,整点时响铃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小时即可。这两个部分解耦之后会发现,我们后面的利用板上按键修改显示格式也变得容易了很多。

通过串口显示也就是需要向指定变量发送字符,将这个功能抽象并封装之后,对于我后续的编程来说,也就是调用一下S_Str(str)的过程。

打铃

打铃是这套系统的重头戏,因为学校方面的资源限制,所以使用指示灯示意的方法来代替打铃。

指示灯的亮灭是通过控制一个变量的值来确定的,于是我只要在正确时候设置正确的值,打铃系统就能按照我期望的方式工作。

修改

修改同样是通过串口进行的。

跟显示有些不同的地方是,通过串口向芯片发送数据需要正确使用串口中断。

综上,这个系统所需要的全部内容就已经实现了。可以看到我做了很多将对硬件的操作抽象化的处理,其实这一点非常重要。因为对于我来说,嵌入式开发最大的难题在于,我不知道里面种种变量的含义,不知道如何操作具体的硬件。将硬件操作抽象化处理之后,我就可以很方便地开展我的后续开发。

问题

实现就不再赘述了,想必读者一定都比我强,下面聊一聊遇到的问题以及debug的经历。

串口配置

串口的收和发其实是分开的,这里用到了两个变量:UCA0TXBUF,UCA0RXBUF。从字面意思上可以看出,前一个用于发送,后一个用于接收(相对于开发者来说)。发送和接收其实就是给这两个值赋值的过程,看起来这两个变量在接受到值之后会将这个值传给别的变量,所以只要不断的将值赋给它就行,我们写了这样的函数:

uart貌似是一个内置的中断函数,用来处理串口的接收,只要将变量UCA0RXBUF的值存储起来即可;后面的S_Str就非常好理解了,将值发送给UCA0TXBUF,从而实现串口的输出。

思路如此清晰,但是测试的时候却遇到了问题,我们的输出是空的,转为16进制显示后,全都是0x00。这个问题调试了很久,拿着原来的代码逐行比对之后发现,出了这样的问题:

1234-UCA0BR0=130;-UCA0BR1=6;+UCA0BR0=104;+UCA0BR1=0;

Google一下才明白,原来UCA0BR0和UCA0BR1是由系统的时钟速度和波特率决定的值,如果设置错误就会导致串口发送失败。具体的值可以参考用户手册,Ctrl+F搜索即可。

串口输出异常

前面提到我们直接使用三个变量保存当前时间,在输出时做进一步处理,转为字符串的过程中,我们进行了这样的操作:

1Time[0]=hour/10+'0';

但有趣的事情是,在初始化之后,我们得到的输出是这样的:0/:0/:0/。随手输出了一下/的ASCII码,发现它刚好比0小一。

难道说,存储器中的默认值不是0吗?Google一下之后发现,还真的不是0。MSP430G2553中的Flash存储器在默认状态下的值全为1,然后写入时只能将1置为0,所以每一次写入数据都需要先清空再写入。那么问题来了,为什么全为1会导致最后输出的结果小1呢?我来简单的阐述一下我的理解:

假设这个存储器只有8位,也就是说,现在的值为11111111,然后我加上一个1,于是我们得到:

12311111111+1100000000

显然,我们最后的结果已经移除了,此时会产生截断,也就是说,存储器现在的数据变成了00000000,也就是0,跟我们期望的结果1刚好相差一。

当然,实际的情况要比我上面的举例要复杂的多,不过我想已经足够我们认识到这个BUG的本质,就不再多说啦。

Flash存储器未清空

在测试中,我们发现每一次烧录程序之后,Flash存储器不会清空,依然会从上一次我们保存的时间开始计时。我觉得这是正确的行为,没有在意,但是我妹纸和她的队友告诉我她们在完成上一个作业的时候每次都是会清空的。我对着这次和上次的代码研究了很久,认为代码里面根本就没有清空Flash存储器的操作,如果有的话,掉电保存这项功能根本无从谈起。我妹纸她们也同意我的分析,但是她们的实践确实证明了每次都会清空Flash存储区。

这个问题也困扰了很久,直到第二天,用别人的电脑重新烧录了一遍程序,发现他们的是会正常清空的。所以说,问题在于CCS的版本:我妹纸使用的CCS版本是6.1,而

他们用的版本是5.1.1,也就是说,不同版本的CCS在烧录程序期间的不同行为导致了这次错误。我们换用了5.1.1之后,成功解决了这个问题。

总结

对嵌入式开发有了初步的了解,向着真·全栈开发工程师又近了一步。

这一次的开发经历遇到了很多因缺思艇的问题,因为嵌入式开发本身比较偏向底层,这次开发甚至还遇到了存储器的存储原理。也有一点将自己看的CSAPP融会贯通的感觉,还是很有意思的。

发布于 2025-07-19
24
目录

    推荐阅读