终于,我们把STM32的入门三板斧的武功讲完了。有了这三板斧,我们就可以完成一些基本的项目了。终于熬出头了,从这一节开始,我们就用这三板斧,来玩不同的项目了。
当然。我们的项目不仅仅是玩,每一个项目都至少包含下面元素:
1.高耦合低内聚,便于移植,便于重用;
2. 不仅仅是好玩,而且有用,在其他项目中常常用得到;
敬请收藏,关注,从下面开始,带你做项目带你飞!
目录
1.3.2 修改main.c中的代码,设置倒计时时间为25分钟;
1.3.5 在main.c中,检查SysTickFlags时间标志,根据分钟标志,实现分钟显示
3.3.2 从gethub查找摩尔斯电码相关代码,对其功能进行测试
3.3.3 分析代码结构,修改GPIO引脚,实现beep发送莫尔斯码。
1. 实战项目1-开发一个番茄钟
1.1项目背景
现代人很容易被各种琐事打扰,一会儿一个微信,一会儿一个电话,一会儿一条手机弹窗,让人们很难真正安静下来,从事写作或者学习。
为了解决这个问题,我们利用手边的STM32开发板,开发一款番茄时钟,具体功能如下:
1. 开机后,自动倒计时25分钟;
2. 倒计时时间,例如:剩余24分钟时,闪烁24下,剩余1分钟时,闪烁1次。闪烁频率为100ms亮,200ms熄灭;
3. 到达倒计时时间后,蜂鸣器会“滴滴滴滴”想起,蜂鸣器想起频率为100ms开900ms关,蜂鸣器响25S后,自动停止;
就是这样简单的一个项目,接下来我们进行需求分析,看看如何快速实现这个项目。
1.2 需求分析
根据前面的三板斧,我们可以知道,这个项目需要这样实现:
1.开启定时器中断,定时器中断间隔设置为10ms;
2. 设置SystemCounter全局结构体变量,用来计算秒和分钟;
3. 采用前面用过的led_check来控制亮灭时间和闪烁次数;
4. 开发类似led_check的函数,来控制蜂鸣器的频率和次数;
5. 从下面这个项目的源码入手修改
每节课都是一个项目 手把手用STM32打造联网气象站-5-STM32基础三件套-采用TIM定时器,实现无阻塞LED任意占空比和次数闪烁_可志嵌入式的博客-优快云博客
1.3 项目开发
1.3.1 修改定时器中断间隔时间
每节课都是一个项目 手把手用STM32打造联网气象站-4-STM32基础三件套-TIM定时器初始化_可志嵌入式的博客-优快云博客
根据这一节中2.2的内容,我们知道,设置10ms中断间隔,我们可以把Period设置为10000-1; Prescaler保持不变。
1.3.2 修改main.c中的代码,设置倒计时时间为25分钟;
宏定义实现倒计时时间的设置,便于led闪烁、蜂鸣器亮灭次数统一
设置好宏定义后,用宏定义作为参数,判定倒计时时间
1.3.3 控制led闪烁时间及闪烁频率
1)在led_check.h,通过修改控制闪烁时间的宏定义,控制闪烁频率为亮100ms,灭200ms。
2)在main.c中,配置set_led_config实现闪烁次数的要求
1.3.4 实现beep蜂鸣一次的响停间隔及蜂鸣频率
1)开发beep_check函数控制蜂鸣器的频率和次数
2)开发set_beep_config函数,用以初始化蜂鸣器的频率和次数。
参考:
每节课都是一个项目 手把手用STM32打造联网气象站-5-STM32基础三件套-采用TIM定时器,实现无阻塞LED任意占空比和次数闪烁_可志嵌入式的博客-优快云博客
思路一样,只是注意bsp_beep.h中,beep开启、关闭与led对应的电平不同。最后要注意在main.c大循环中添加beep_check用以控制蜂鸣器的频率和次数
1.3.5 在main.c中,检查SysTickFlags时间标志,根据分钟标志,实现分钟显示
1)在控制时间的结构体中增加TimeFlag,用以设置单次运行模式,当时间超过25分钟后,通过对标号4,设置TimeFlag为1,便不再执行番茄番茄时钟程序
2)检查SysTickFlags时间标志,实现分钟显示
3)通过对时间标志 按位取反 再与原值位于运算,达到清除时间标志,而不影响其他位的数值。
4)位与位或等运算可参考:
位与&, 位或| ,位异或 ^ 总结 - Lucky& - 博客园 (cnblogs.com)https://www.cnblogs.com/xiaokang01/p/9651366.html
纸上得来终觉浅,绝知此事要躬行!
马上下载测试代码,看看效果。
测试代码链接
https://download.youkuaiyun.com/download/book_drabit/86150743
2. 实战项目2-改进番茄时钟
2.1项目需求
前面的番茄时钟虽然不错,但是还存在两个问题:
1. 没有输入设备,时间始终是固定死的;
2. 没有累计数据,不知道一天到底完成了几个番茄;
3.没有暂停功能,无法在需要时对番茄时钟进行暂停
为了解决这个问题,我们接下来就开发改进版本的番茄时钟;
2.2需求分析
为了实现这个需求,我们需要
1. 在前面工程基础上,移植串口,方便通过串口,设定番茄钟的时间,启动番茄钟,暂停番茄钟。这样,我们就需要开发一下串口解析命令。另外,为了是的代码更加便于维护,我们不仅仅通过串口控制,而且可以通过串口,查询番茄钟的软件版本信息和硬件版本信息;
2. 在前面的SystemConter的结构体全局变量中,我们需要增加一个累计番茄的变量,这样每次完成一个番茄,通过串口输出出来。同时也可以通过串口命令,查询共计完成了几个番茄。
2.3项目开发步骤
在前一节的基础上,我们需要:
2.3.1 移植串口,实现串口环回测试
解析串口代码结构,理解其串口协议,并通过串口发送指令进行验证。
1)带有串口协议的代码如下:
串口收发指令代码,实现用指令获取硬件版本号与软件版本号-C文档类资源-优快云下载
对此串口代码进行移植:
首先,将此代码目录下的串口c文件和h文件文件复制到番茄时钟文件夹usart里面;
其次,在keil工程内将鼠标右键放在USER目录上,右键选择Manage Project Items,在USER目录下添加bsp_usart.c文件,
最后,点击魔法棒->C/C++选项卡->include paths将bsp_usart.h文件目录包含进去
测试。移植好后进行串口环回测试,向单片机发送字符,查看单片机返回的字符是否一致。
向单片机发送GHJ,单片机返回GHJ,发送与返回的字符一致,串口移植成功。
串口环回代码如下:
番茄时钟通过移植增设串口通信,并通过串口环回测试-C文档类资源-优快云下载
2.3.2 串口命令解析,串口指令测试
完成环回之后,我们已经确定串口有效,串口能够接收数据,并且发送出来,在此基础上,我们进一步开发串口命令解析功能。
第一步:it.c中接收完整的串口命令并存入缓存
第二步:缓存中解析命令;
第三步:测试结果,验证功能;
1)在串口中断函数中缓存完整的串口命令
在串口中断服务函数中,urt_recv_tmp数组用于缓存完整的串口命令
在这里要注意一下,CurrentRxStep接收8个字节后,CurrentPoint才会累加
2)解析缓存中的串口指令
首先,代码开始执行时, PrePoint和CurrentPoint初始值都是0。当串口收到8个字节后,CurrentPoint增加1,这时PrePoint!=CurrentPoint,会进行命令复制,将串口收到的命令复制到UARTGetComm[ ]数组内。然后PrePoint自增,使得PrePoint和CurrentPoint再次相等。
其次,不断刷新串口状态,没有收到命令时,uart_status处在URT_IDLE状态,不停的break返回。当串口收到了正确命令后,UrtOrderReceiveFinish这个标志会被设置为SET,这时,uart_status进入URT_RX状态,进行串口命令解析。
最后,通过ReadCom函数提取指令的内容,根据指令的内容执行不同的操作。
其中指令A0 B1 02 00 00 00 00 00为获取固件版本信息。在测试的时候可以进行使用。
至此,串口命令解析到此结束。下面通过测试验证其功能。
3)测试以验证功能
通过串口助手向单片机发送指令A0 B1 02 00 00 00 00 00,查看返回的固件版本是否一致。
对应代码如下:
https://download.youkuaiyun.com/download/book_drabit/86247414
2.3.2 增设串口指令,修改单个番茄时长
在完成对串口指令的解析后,增加通过串口指令修改单个番茄时长。
第一步:定义全局变量,通过在程序内修改全局变量,控制番茄时长
第二步:增设串口指令,通过指令修改控制番茄时长的全局变量
第三步:测试结果,验证功能
1)增设全局变量,控制番茄时长
本节上部分开发番茄时钟时以讲过,可参考本节上部分内容。
2)在ReadCom函数中增设修改番茄时长的指令
首先,将指令第四位,即UARTGETCom[3]的值赋给TomatoTime全局变量。当修改时长后,番茄时钟重新开始计时,设立TomatoRestartFlag标志位用于控制番茄时钟是否清零用于计数的参数。
其次,在主函数中,根据TomatoRestartFlag判断是否复位番茄时钟计数参数。 根据TomatoTime的值对番茄时长进行控制。
至此,通过串口修改番茄时长功能完成开发完成,下一步进行测试。
3)串口修改番茄时间功能测试
首先,在stm32f10x_it.c中修改定制器中断1分钟时长为6秒,缩短测试时间。
其次,通过串口助手向单片机发送16进制指令A0 B1 03 02 00 00 00 00,根据延时进行判断功能是否正常
经测试代码功能符合要求。
串口修改番茄时长代码如下:
(48条消息) 通过串口控制单个番茄时钟时长-C文档类资源-优快云文库
2.3.3 通过串口获取完成的番茄个数
需求:1.一个番茄结束后,自动发送共完成的番茄个数。2.通过串口指令获取完成的番茄个数。
第一步:设置全局变量,用于记录共完成的番茄个数
第二步:当完成一个番茄后,通过串口将记录番茄个数的全局变量数值发给上位机
第三步:增设串口指令,当单片机收到指令后,通过串口将记录番茄个数的全局变量数值发给上位机
第四步:测试以验证功能
1)通过全局变量记录番茄个数
首先,在bsp_TimeBase.c定义一个全局变量(全局变量会自动初始化为0)
其次,在 bsp_TimeBase.h中用extern进行引用
最后,在主函数中包含此头文件即可使用此全局变量
2)当完成一个番茄后,FinishTomatoNum会自动增加 1 ,并通过串口将数值上报
首先,当番茄时间到,全局变量自动增加1
其次,通过串口将全局变量FinishTomatoNum进行打印
再次,通过设置TomatoTimeEndFlag标志位为1;while(1)循环便不在执行此番茄始终程序。
最后,对功能进行测试,通过发送指令,查看单片机的返回值是否与完成次数一致
测试结果,功能正常
3)增加串口指令 0xA0 B1 04 返回完成的番茄次数全局变量值到串口助手
首先,筛选串口数据开头为0xA0 B1 04的指令
其次,设置DealFlag=0;表示完成指令。设置uart_status = URT_IDLE;以便进行下一次数据接收
4)测试功能,验证功能的正确性
首先,向单片机发送开头为0xA0 B1 04的8个字节数据,
其次,对比单片机返回值与实际结果是否一致
单片机返回值与实际结果一致,测试通过。
代码如下:
(48条消息) 通过串口获取完成的番茄个数-C文档类资源-优快云文库
2.3.4 通过串口指令,暂停、开始番茄时钟
需求:1.暂停时,番茄时钟停止计时,秒不再增加,时间停止
2.番茄时间结束后。按暂停功能,番茄时钟重新计时
第一步,增设暂停标志位
第二步,定时器中断检测暂停标志位,停止更新秒变化标志位,达到暂停时间功能
第三步,增设串口暂停指令,控制暂停标志位。
第四步,测试验证功能正确性
1)增设暂停标志位
首先,在bsp_TimBase.h文件中 Tomato结构体中增设TomatTimePauseFlag
其次,在bsp_TimBase.c文件中定义一个Tomato类型的结构体变量
最后,在bsp_TimBase.h文件extern引用此结构体变量,这样其他文件中的函数可以通过include bsp_TimBase.h文件,进而引用此结构体全局变量。
2)定时器中断检测暂停标志位,停止更新秒变化标志位,达到暂停时间功能
首先,进入定时器中断函数,检测TomatClock.TomatTimePauseFlag不为0后,SystemTimer.second停止自增,进而SystemTimer.second == 6 条件不满足,分钟标志位停止更新。
其次,在main.c中,tomat_contrl_led_beep函数,检测不到分钟标志位,便停止更新led和beep蜂鸣器。
3)增设串口暂停指令,控制暂停标志位。
首先,筛选出串口数据表头为0xA0 B1 04的指令
其次,判断若指令UARTGetComm[3] == 1,则设置标志位TomatTimePauseFlag为1,前面说过,若TomatTimePauseFlag为1,则定时器中断函数中,SystemTimer.second停止自增。
再次,判断若指令UARTGetComm[3] == 0,则设置标志位TomatTimePauseFlag为0,这样定时器中断函数中,SystemTimer.second自增,并再次判断,如果番茄时钟时间截至标志位TomatClock.TomatTimeEndFlag 为1,设置番茄时钟重新启动。
最后,设置DealFlag=0,表明指令处理结束,设置uart_status = URT_IDLE,继续进行下一次串口消息接收。
4) 测试验证功能正确性
即通过串口单片机向单片机发指令 0xA0 B1 04 01 00 00 00 00 番茄时钟暂停计时。发指令 0xA0 B1 04 00 00 00 00 00 番茄时钟继续计时
测试通过,功能符合要求。
至此,完成番茄时钟所有需求
1.通过串口指令修改番茄时钟计时时间
2.通过串口指令查看共完成的番茄个数。
3.当一个番茄时间完成后,会自动通过串口发送共完成的番茄个数
4.通过串口指令,暂停、开始番茄时钟
对应代码如下:
(48条消息) 实现番茄时钟暂停功能,设置番茄时钟结构体替代取代控制番茄时钟的全局变量,详情见博客-C文档类资源-优快云文库
3. 实战项目3-摩斯密码翻译器
3.1项目需求
电影中,常常可以看到很多关键情报,都是通过摩斯密码来传递的。然而,翻译摩斯密码并不容易。
摩斯密码采用一系列的点和线,来实现信息的表达和传递。
3.2需求分析
说老实话,真的用代码,从头开始,翻译摩斯密码,还是非常有挑战的。所以这一节,我们将介绍另外一种常用方法:从github找相关思路。
github是程序员的乐园,极客的世界,在github上面,说不定已经有摩斯密码翻译器了,我们只需要把它下载下来,再移植到我们的代码中,即可完成项目功能。
说干就干,我们再github上面,能够找到类似的项目。
3.3开发步骤
第一步,了解摩尔斯电码基本原理
第二步,从gethub查找摩尔斯电码相关代码,对其功能进行测试
第三步,分析代码结构,增加功能,实现用蜂鸣器将消息以摩尔斯电码的方式滴答发送出去
3.3.1摩尔斯电码基本原理
首先,摩尔斯电码通过点(.)和横杠(-)的组合代表不同的字符,字符构成单词,转变为人可以理解的词汇。
's', "..." 'o', "---" 's', "..."
其次,将字符转化为摩尔斯电码,更有利于通过简单的滴答声及延时将信息发送出去。由于不同字符由不同的摩尔斯电码组成,不同的摩尔斯电码对应不同时长的滴答声和滴答声间隔。字符便被区分开来。
********************************************************************************************
* 摩尔斯电码时间间隔包括五种:
*
* 1、点( · ) :1 (读 “滴” dit ,时间占据1t )
* 2、划(—) :111 (读 “嗒” dah ,时间占据3t )
* 3、字符内部的停顿(在点和划之间):0 (时间占据1t )
* 4、字符间停顿 :000 ( 时间占据3t )
* 5、单词间的停顿 :0000000 ( 时间占据7t )
*
**********************************************************************************************
具体请参考一次看懂摩斯电码的密码 - 知乎 (zhihu.com)
3.3.2 从gethub查找摩尔斯电码相关代码,对其功能进行测试
首先,从gethub查找相关代码,github是极客的世界,有大量的优质开源项目。要学会使用github。github的使用大致分为账号注册、创建github仓库、git工具的安装三大步,此处不再赘述,详细使用请参考
还不会使用 GitHub ? GitHub 教程来了!万字图文详解 - 知乎 (zhihu.com)
从gethub下载的摩尔斯电码代码如下:
(40条消息) 摩尔斯电码,实现将字符转为摩尔斯电码的主体功能,能将摩尔斯电码通过串口上位机进行显示-C文档类资源-优快云文库
其次,将代码下载到单片机进行验证,其能实现基本的字符转为摩尔斯电码功能,将摩尔斯电码通过串口进行显示。将摩尔斯电码以led闪烁的形式发送出去
3.3.3 分析代码结构,修改GPIO引脚,实现beep发送莫尔斯码。
第一步,分析代码结构
第二步,修改GPIO引脚,实现beep发送莫尔斯码。
1)进入主函数,查看其对摩尔斯电码函数的调用,分别查看(1)和(2)两个标签对应的函数。
首先, morse_init函数实现对摩尔斯电码GPIO引脚的初始化,后续可以通过GPIO对应的蜂鸣器将信号以声音的方式发送出去。
其次,不定长函数morse_printf实现两个功能(1):将摩尔斯电码通过串口打印显示。(2)将摩尔斯电码通过GPIO外接的led输送给外界。
可见,此摩尔斯电码只是将字母与摩尔斯电码对应起来,部分符号如 ? ( )等符号没有完善。
可见,摩尔斯电码发送时间间隔没有严格按照摩尔斯电码要求进行等待。
最后,对代码进行完善,增加摩尔斯电码对其他字符的支持,增加对莫尔斯码发送时间间隔的支持。在下一节讲述
不定长函数可参考下文:
void morse_printf(char *format, ...) { } 为不定长函数,其对输入参数的个数没有要求。不定长函数的基本调用方式为:
void my_printf(const char *format, ...)
va_list arg; // va_list是一个宏,是指向不定长参数列表的指针
va_start(arg, format); //初始化arg,将format后第一个地址赋值给arg
va_arg(arg, int); //得arg指向参数的值,同时使arg指向下一个参数
vsnprintf((char *)pStr, 100, format, ap);
va_end(arg);
//va_end 在有些实现中可能会把arg改成无效值,
//这里, 是把arg指针指向了 NULL,避 免出现野指针
具体可参考(40条消息) C语言 函数不定长参数 - C语言零基础入门教程_猿说编程的博客-优快云博客_c++不定长参数
2)对功能进行完善,实现用蜂鸣器将消息以摩尔斯电码的方式滴答滴答发送出去
首先,将led改为beep蜂鸣器
在Morse_TX.h修改宏定义,注意,由于电路不同,beep高电平为打开。
其次,在Morse_TX.c修改GPIO引脚为PA8
经测试,beep蜂鸣器正常闪烁。
3.3.4 实现按标准时间间隔发送
第一步,修改beep发送时间间隔,实现按标准时间间隔发送
第二步,增设对其他符号摩尔斯码的支持
第三步,测试以验证功能
- 点( · ):1 (读
“滴”
dit ,时间占据1t ) - 横杠(—):111 (读
“嗒”
dah ,时间占据3t ) - 字符内部的停顿(在点和划之间):0 (时间占据1t )
- 字符间停顿:000 ( 时间占据3t )
- 单词间的停顿:0000000 ( 时间占据7t )
在Morse_TX.c中做如下操作,
1),设定基准等待时间、设定基准时间倍乘系数,这样代码看起来更清晰明了
2),在morse_printf函数内部,设置点、横杠、字符内部、字符间、单词间的时间停顿
3),修改完成后需对其功能进行验证,打开纸飞机串口助手,打开时间戳功能,对时间进行验证
单个时间戳是90ms,.... 共用时10个时间戳,共900ms,红框中时间间隔正确。以此类推对所有时间间隔进行验证,均符合要求。
3.3.5 将全部符号与摩尔斯电码相对应。
首先,在const tp_morse_index st_morse[ ]最下方添加 “ / ” 用于摩尔斯电码单词之间的间隔。
其次,在morse_printf中添加switch语句,使输出字符与摩尔斯码一一对应。
最后,对以上功能进行验证,观察蜂鸣器与串口显示
经验证,功能正常。
代码如下:
(48条消息) 摩尔斯电码,实现将摩尔斯电码通过串口进行打印,通过蜂鸣器进行发送,具体详情见博客-C文档类资源-优快云文库
这一段对话请每一个程序员或者有志于编程的人牢记:
老板:这个代码不是开源的吗?怎么需要这么久?开源的还需要你自己写吗?
程序员:*……&*……&*……&¥……%¥564@#¥@