1. 搭建最小实时系统框架
任务定义
本案例基于STM32F103系列MCU与FreeRTOS,搭建一个最小实时系统:创建两个独立任务——LED闪烁任务与按键扫描任务。LED任务以固定周期翻转GPIO电平,按键任务轮询按键输入并在按下时翻转另一路LED状态。通过观察两路LED的独立运行节奏,验证FreeRTOS的任务调度与延时机制。
硬件连接
软件实现
使用STM32标准外设库与FreeRTOS V10.4.6。系统时钟配置为72MHz,创建两个任务:vLEDTask每500ms翻转PB0电平;vKeyTask每20ms轮询PA0,检测到下降沿(按键按下)时翻转PB1电平。按键采用软件消抖(连续两次采样均为低电平才判定有效)。
/* FreeRTOS 头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f10x.h"
/* 任务函数声明 */
void vLEDTask(void *pvParameters);
void vKeyTask(void *pvParameters);
/* 按键消抖计数 */
static uint8_t key_debounce = 0;
/* GPIO 初始化 */
static void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能 GPIOB 和 GPIOA 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOA, ENABLE);
/* PB0, PB1 推挽输出,驱动LED */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* PA0 上拉输入,接按键到GND */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 初始状态:LED1灭,LED2灭 */
GPIO_ResetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1);
}
/* LED闪烁任务:每500ms翻转一次PB0 */
void vLEDTask(void *pvParameters)
{
(void)pvParameters;
for (;;)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_0,
(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0)));
vTaskDelay(pdMS_TO_TICKS(500));
}
}
/* 按键扫描任务:每20ms轮询PA0,下降沿翻转PB1 */
void vKeyTask(void *pvParameters)
{
(void)pvParameters;
uint8_t key_state = 0; /* 当前按键状态:0=未按下,1=按下 */
uint8_t last_key = 1; /* 上一次采样值:1=高(未按下) */
for (;;)
{
uint8_t sample = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);
/* 软件消抖:连续两次采样一致才更新状态 */
if (sample == last_key)
{
key_debounce++;
if (key_debounce >= 2)
{
key_state = sample;
key_debounce = 0;
}
}
else
{
key_debounce = 0;
last_key = sample;
}
/* 检测下降沿:之前为高,现在为低 → 按键按下 */
if (key_state == 0 && last_key == 0 && sample == 0)
{
GPIO_WriteBit(GPIOB, GPIO_Pin_1,
(BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_1)));
/* 等待释放,避免重复触发 */
while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
vTaskDelay(pdMS_TO_TICKS(10));
}
last_key = 1;
key_state = 1;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}
int main(void)
{
/* 系统时钟配置为72MHz */
SystemInit();
/* GPIO初始化 */
GPIO_Config();
/* 创建两个任务 */
xTaskCreate(vLEDTask, "LED", 128, NULL, 1, NULL);
xTaskCreate(vKeyTask, "KEY", 128, NULL, 1, NULL);
/* 启动调度器 */
vTaskStartScheduler();
/* 正常不会执行到这里 */
for (;;);
}
工程配置说明
将上述代码保存为main.c,替换STM32标准工程模板中的同名文件。需在工程中添加FreeRTOS内核源码(tasks.c、list.c、queue.c、port.c、heap_4.c),并在FreeRTOSConfig.h中配置:configTICK_RATE_HZ设为1000,configMINIMAL_STACK_SIZE设为128。编译下载至STM32F103最小系统板。
验证方法
将编译生成的hex文件烧录至开发板,观察以下现象:
- LED1(PB0):以约1Hz频率(亮500ms灭500ms)持续闪烁,不受按键操作影响。
- LED2(PB1):每次按下KEY按键(PA0接地)时翻转一次状态,松开后不会重复翻转。
- 两路LED工作相互独立,LED1的闪烁节奏稳定,证明FreeRTOS调度器正常分配CPU时间片,
vTaskDelay延时功能准确。
判定标准:LED1以稳定周期闪烁且LED2仅在每次按键按下时翻转一次,即视为任务调度与延时功能验证通过。若LED1闪烁频率明显异常或按键响应失灵,检查系统时钟配置与FreeRTOSConfig.h中的时钟节拍设置。