目标
利用FreeRTOS运行两个任务,分别为点灯和OLED屏的显示。
利用STM32CubeMX生成Keil工程和相关初始化代码
知识回顾
之前已经利用STM32CubeMX生成过Keil工程和相关初始化代码了,可以去回顾一下,详情见:https://blog.csdn.net/wenhao_ir/article/details/146909781
选择芯片
打开STM32CubeMX,然后按下面的截图依次操作:
STM32F103C8T6:
双击上图红线的部分启动创建工程:
System Core配置
配置RCC(时钟源选择)
配置SYS(调试接口选择)
配置GPIO(配置PC组的第13个引脚为输出引脚)
接下来对GPIO的PC组的第13个引脚进行选择配置。
首先切换到“Pinout&Configuration项”:
然后按下面的操作找到引脚:
设置成输出引脚:
这样对GPIO引脚的配置就完了。
配置I2C1控制器
参数那里用默认的配置就行了。
Clock Configuration(时钟配置)
输入完后点击回车键:
计算完成后的截图如下:
配置FreeRTOS
FreeRTOS的版本选择
FreeRTOS的核心参数配置
各界面尺寸调整下,把“Config parameters”调整到方便查看的大小:
看下FreeRTOS的关键参数对不对:
FreeRTOS的任务管理配置
使用 STM32CubeMX,可以手工添加任务、队列、信号量、互斥锁、定时器等等。但是本
课程不想严重依赖 STM32CubeMX,所以不会使用 STM32CubeMX 来添加这些对象,而是手写代
码来使用这些对象。
使用 STM32CubeMX 时,有一个默认任务,此任务无法删除,只能修改其名称和函数类型,
如下图所示:
工程项的配置
K003_FreeRTOS_my1
这里说明一下为什么Firmware Package Name and Version
那里要改为V1.8.5,因为V1.8.6与之前在博文 https://blog.csdn.net/wenhao_ir/article/details/146674379 中安装的STM32F1xx_DFP不兼容。
生成工程
以上内容配置完成后,就可以点击生成工程了。
用Keil打开生成的工程并配置好Debugger
用Keil打开生成的工程
配置好Debugger
添加LED的driver代码并进行测试
添加LED的相关C文件到工程
在工程目录E:\Keil_project\K003_FreeRTOS_my1\Drivers
中新建目录my_driver
下载并解压:01_freertos_template.zip
把下面这4个文件复制到目录E:\Keil_project\K003_FreeRTOS_my1\Drivers\my_driver
说明一下,其实这里我们是想用driver_led.c
,但是driver_led.c
用到了driver_timer.c
,所以driver_timer.c
也要一并复制过来。
工程中增另一个group来添加以上文件:
把目录E:\Keil_project\K003_FreeRTOS_my1\Drivers\my_driver
添加到工程 的include目录中:
主函数中添加相关代码
打开文件E:\Keil_project\K003_FreeRTOS_my1\Core\Src\freertos.c
然后加入下面的代码:
# include "driver_led.h"
......
Led_Test();
编译工程
烧写测试
然后发现绿灯一闪一炒了~~~~测试成功。
添加OLED的driver代码并进行测试
先把OLED模块插到开发板上
添加OLED的相关C文件到工程
注意:以下的操作是在上一个目录的基础上进行的。
复制下面4个文件到目录E:\Keil_project\K003_FreeRTOS_my1\Drivers\my_driver
然后工程中添加上文件driver_lcd.c
和driver_oled.c
:
说明一下:driver_lcd.c
是对driver_oled.c
进行调用,也就是说driver_lcd.c
是更抽象的一层。
复制字库文件到相关目录
上面这个文件复制到:E:\Keil_project\K003_FreeRTOS_my1\Drivers\my_driver
主函数中添加相关代码
# include "driver_lcd.h"
......
LCD_Test();
编译工程
上面的警告不用管。
烧写测试
此时发现OLED屏有显示了。
编写程序实现两个测试程序同时运行
代码修改和编写
因为我们在工程中引入了FreeRTOS,所以虽然两个测试程序都是while循环,所以还是可以同时运行:
具体的源码分析见视频的第2分44秒开始看…
修改E:\Keil_project\K003_FreeRTOS_my1\Core\Src\freertos.c
的函数StartDefaultTask
,函数StartDefaultTask
是第一个任务的回调函数:
依照已有的 defaultTaskHandle
再创建一个名叫LcdTask
的任务:
freertos.c修改后的完整源代码
修改后的E:\Keil_project\K003_FreeRTOS_my1\Core\Src\freertos.c
完整源代码如下:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : freertos.c
* Description : Code for freertos applications
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
# include "driver_led.h"
# include "driver_lcd.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
osThreadId_t LcdTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
const osThreadAttr_t LcdTask_attributes = {
.name = "LcdTask",
.stack_size = 128 * 4,
.priority = (osPriority_t) osPriorityNormal,
};
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
void LcdTask(void *argument);
/* USER CODE END FunctionPrototypes */
void StartDefaultTask(void *argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/**
* @brief FreeRTOS initialization
* @param None
* @retval None
*/
void MX_FREERTOS_Init(void) {
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
LcdTaskHandle = osThreadNew(LcdTask, NULL, &LcdTask_attributes);
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
}
/* USER CODE BEGIN Header_StartDefaultTask */
/**
* @brief Function implementing the defaultTask thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
Led_Test();
/* USER CODE END StartDefaultTask */
}
void LcdTask(void *argument)
{
LCD_Test();
}
/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
/* USER CODE END Application */
再修改下driver_oled.c
然后我们把E:\Keil_project\K003_FreeRTOS_my1\Drivers\my_driver\driver_oled.c
中的函数OLED_Test
也修改一下,使最终我们的OLED屏上能不断计数。
void OLED_Test(void)
{
int cnt = 0;
OLED_Init();
// 清屏
OLED_Clear();
while (1)
{
// 在(0, 0)打印'A'
OLED_PutChar(0, 0, 'A');
// 在(1, 0)打印'Y'
OLED_PutChar(1, 0, 'Y');
// 在第0列第2页打印一个字符串"Hello World!"
OLED_PrintString(0, 2, "Hello World!");
OLED_PrintSignedVal(0, 4, cnt++);
}
}
编译运行
下载测试
然后我们发绿色的LED在闪,同时OLED屏上也不断在计数…
附完整工程文件
https://pan.baidu.com/s/17m8R7hpnigDqcmNST8Pd_w?pwd=h9rx