基于FREERTOS的STM32多功能手表(软件设计)
目录
前言
程序现象
项目背景
项目介绍
目前版本实现的功能
设计到的freertos知识
使用到的硬件
硬件连线图
实现思路
任务调度流程图
编辑
任务具体操作导图
代码讲解
freertos初始化
按键中断回调函数
显示时间任务
显示菜单任务
其它任务(ShowCalendarTask)
总结
开源链接
前言
Hey,我是Moyiji,一个嵌入式小白,也是一名大三在读生,这是我的第一篇博客,在这里我将向你介绍过去一段时间我开发这个项目的经历和感受。并且通过这篇博客来记录自己的开发过程。
程序现象
my_watch_video
项目背景
在四月份我使用标准库在裸机上复刻了谷歌小恐龙游戏和一个类似于天天酷跑炫飞模式的游戏,并且写了一个多级菜单,在那之后我就决定写一个游戏机项目来集成这些游戏。同时我也在学习韦东山的FREERTOS教程,学习完FREERTOS之后我就感觉这玩意儿用来做这个项目实在太合适了!于是我便决定使用FREERTOS来开发这个项目。不过在五一前夕到五月中旬这段时间里我有足足四门考试要准备, 因此进度便搁置了一段时间。在考完试之后,我便着手开发这个项目(考试中也有缓慢推进)。在写了无数个bug之后,我终于完成了这个项目的软件开发(不过我转换了方向,从做游戏机换成了做多功能手表)。
项目介绍
基于FREERTOS的STM32多功能手表:正如标题所言,这个项目所使用的硬件平台是STM32(stm32f103c8t6),然后使用freerrtos来管理和协同各个任务。
目前版本实现的功能
- 时间显示
- 多级菜单显示
- 万年历(显示2024年份的日历)
- 模拟手电
- 温湿度显示
- 电子闹钟
- 设置(开关系统声音)
这里补充一下,本来我是准备加上血氧监测模块的,因为之前花了十几块大洋买了一个MAX30102一直在搁置,但是当我移植完之后发现这个驱动运行需要占用10K左右的RAM!这你受得了,因为c8t6只有20k RAM, 再加上freertos的占用,就,就没内存了,,,因此只能放弃这个功能了。。。
设计到的freertos知识
- 任务管理(创建,删除,状态转换)
- 软件定时器(创建,启动,停止)
- 队列(创建,写队列,读队列)
- 二值信号量(创建,give,take)
- 中断管理(中断与任务通信)
- 资源管理(主要是堆栈大小的处理)
使用到的硬件
- stm32f103c8t6(20k RAM, 64KROM)
- 0.96寸oled显示屏(黄蓝双色, u8g2库)
- 四个独立按键(中断中写入队列与任务协调)
- 无源蜂鸣器(按键或者电子时钟触发)
- DHT11(显示温湿度信息)
硬件连线图
OLED KEY 蜂鸣器 DHT11 SCL(PB6) key.rdata(PB11) I/O(PA8) DAT(PA1) SDA(PB7) key.ldata(PB10) key.updata(PB1) key.exdata(PB0) 实现思路
在freertos初始化时创建除默认任务之外的七个任务,分别是显示时间task,菜单task以及五个功能task,在默认任务中创建两个软件定时器,分别是时间显示Timer和电子闹钟Timer,在中断中向队列写入数据与各个任务完成通信并响应操作,在显示时间task中挂起其它六个任务,在按键触发任务切换时恢复下一个任务,并且挂起自己。
任务调度流程图
任务具体操作导图
代码讲解
这部分时不太想写的,因为在我写完前文所提到的两个游戏和多级菜单的代码后,回看时真的感觉自己写的代码跟屎一样,毫无命名规范,可阅读性极差,真的就是所谓的屎山代码,虽然在这个项目中有去努力优化代码但还是感觉自己的编程水平很低,毕竟我可是大一C语言考试全班倒数第一的男人(狗头)。好了接下来我向各位讲解部分代码,由于是个人开发,程序前后也没有进行严格的测试和优化,可能部分代码是没有作用的,但是不影响整体程序的运行就没有优化,如果你遇到看不懂的就当作无效跳过就行。
".........................."为省略部分,详情请查看源码
freertos初始化
创建显示时间定时器,和电子闹钟定时器,注意显示时间的周期是1000tick,而电子闹钟的周期是100tick,另外按键音效也是通过软件定时器的。补充:定时器默认优先级很低,你需要到FreeRTOSConfig.h文件中手动提高软件定时器的优先级(高于所以创建的任务)
/* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* time and clock's Timer */ g_Timer = xTimerCreate("Timer1", 1000, pdTRUE, NULL, (TimerCallbackFunction_t)TimerCallBackFun); g_Clock_Timer = xTimerCreate("Timer2", 100, pdTRUE, NULL, (TimerCallbackFunction_t)ClockTimerCallBackFun); /* USER CODE END RTOS_TIMERS */
创建默认task和其它七个task,优先级均为默认优先级,栈大小为128/256,代码中的 Task_default_size也是128,调试的时候设置的就没改。在第三个任务下面有一个ShowWoodenFishTask, 这是一个电子木鱼,按下按键就可以加功德,因为后面又写了日历功能,就把这个给替换掉了,但是文件没有删,你可以将这个功能替换来体验一下
/* Create the thread(s) */ /* creation of defaultTask */ defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* create some tasks */ xTaskCreate(ShowTimeTask, "ShowTimeTask", 128, NULL, osPriorityNormal, &xShowTimeTaskHandle); xTaskCreate(ShowMenuTask, "ShowMenuTask", 256, NULL, osPriorityNormal, &xShowMenuTaskHandle); /******** 5 apps ********/ /*1*/ xTaskCreate(ShowCalendarTask, "ShowCalendarTask", 256, NULL, osPriorityNormal, &xShowCalendarTaskHandle); /*2*/ xTaskCreate(ShowFlashLightTask, "ShowFlashLightTask", Task_default_size, NULL, osPriorityNormal, &xShowFlashLightTaskHandle); /*3*/ xTaskCreate(ShowDHT11Task, "ShowDHT11Task", Task_default_size, NULL, osPriorityNormal, &xShowDHT11TaskHandle); //xTaskCreate(ShowWoodenFishTask, "ShowWoodenFishTask", Task_default_size, NULL, osPriorityNormal, &xShowWoodenFishTaskHandle); /*4*/ xTaskCreate(ShowClockTimeTask, "ShowClockTimeTask", Task_default_size, NULL, osPriorityNormal, &xShowClockTaskHandle); /*5*/ xTaskCreate(ShowSetting_Task, "ShowSetting_Task", 256, NULL, osPriorityNormal, &xShowSettingTaskHandle); /* USER CODE END RTOS_THREADS */
按键中断回调函数
在中断回调函数中将数据写入队列,这里的for循环本意是想进行按键消抖,因为我在开发板上测试的时候,会遇到多次响应的问题就很烦,然后加了这个for循环,发现没啥用,后来自己用小面包板上换了另一种按键搭建了一个按键模块,发现效果好多了,呜呜呜,网上买的按键模块实在是垃圾,电路板明明设计的有电容的焊点但是却没有焊电容,哎浪费我很多时间,也没办法,几块钱还要啥自行车
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { /* key interrupt : send data to queue */ ........................... if(GPIO_Pin == GPIO_PIN_11) { for(int i = 0; i