当前位置: 首页 > news >正文

基于 STM32CubeMX 实现按键改变 FreeRTOS 多任务的状态( STM32F103ZET6)

1. 实验目的  

        本文通过使用 STM32CubeMX 搭建基于 FreeRTOS 的实时操作系统工程,创建并运行五个独立任务,分别为:LED0 闪烁任务、LED1 闪烁任务、按键扫描任务(含 4 个按键)、LCD 显示任务以及串口监控任务

在实验中,通过外部按键实现对任务状态的动态控制:

  • 使用 KEY0 、KEY1 按键实现 LED0 任务的挂起与恢复

  • 使用 KEY2、KEY_UP 按键实现 LED1 任务的删除与重新创建

系统运行过程中,通过 串口输出LCD 屏幕显示 实时观察各任务的状态变化,从而验证 FreeRTOS 任务管理机制(包括任务的创建、删除、挂起、恢复等)的实际运行效果与调度特性。

2 按键扫描任务

2.1 外部中断配置

2.1.1 硬件连线

本文使用了PE4 PE3 PE2 PA0四个引脚作为外部中断按键引脚

2.1.2 引脚配置

因为PE4对应的按键与低电平串联,所以在设置中断的时候,启用上拉电阻,下降沿触发。

同理,PA0的按键与高电平串联,所以在设置中断的时候,启用下拉电阻,上升沿触发。

2.1.3 使能中断

        也可以在中断设置中使能中断,并且调整中断优先级,属于同一中断向量的共享中断只能统一设置优先级,独立中断可独立设置优先级。

2.2 外部中断执行步骤

2.1.1 按键触发电平变化

当外部按键产生电平变化时(可配置为上升沿下降沿触发),EXTI 硬件线路检测到该变化并准备产生中断请求。
电平有效方式取决于硬件连接方式和配置参数:

  • 若按键与 低电平 串联,应启用 上拉电阻,配置为 下降沿触发

  • 若按键与 高电平 串联,应启用 下拉电阻,配置为 上升沿触发

此时,EXTI 线路检测到电平变化信号,进入中断请求阶段。

2.1.2 中断服务函数调用

当 EXTI 检测到设定的触发沿后,会向 NVIC 发出中断请求(IRQ)。
根据引脚编号,系统调用对应的中断服务函数(IRQHandler):

  • 独立中断
    EXTI Line0–Line4 各自拥有独立的中断向量,可单独配置优先级。并在中断向量入口中独立调用中断处理函数。
    例如:

    void EXTI0_IRQHandler(void) {HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
    }
    
  • 共享中断
    EXTI Line5–Line9 共用 EXTI9_5_IRQHandler()
    EXTI Line10–Line15 共用 EXTI15_10_IRQHandler()
    多个引脚共用同一中断向量,因此在中断服务函数中需要依次调用对应的 HAL 中断处理函数。共享向量的所有引脚只能统一设置 同一个 NVIC 中断优先级,无法对单个引脚单独设置。
    HAL 库推荐的标准写法如下:

    void EXTI9_5_IRQHandler(void) {HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_5);HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_6);
    }
    

        HAL 库内部会自动判断中断来源并清除相应标志位。无论是独立还是共享中断,用户无需手动区分中断引脚,业务逻辑可在回调函数中根据 GPIO_Pin 参数分流处理。

若使用 STM32CubeMX 配置外部中断,系统将自动生成上述中断服务代码,
并由 HAL 层自动完成:

  • 中断来源判断

  • EXTI 挂起标志位清除

  • 调用用户回调函数 HAL_GPIO_EXTI_Callback(GPIO_PIN_x)

中断函数与对应硬件引脚对应关系如下:

EXTI 线路(Line)对应引脚(GPIO)对应中断向量(IRQ Handler)调用的 HAL 函数最终用户回调函数
EXTI0Px0(如 PA0、PB0、PC0…)EXTI0_IRQHandler()HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0)HAL_GPIO_EXTI_Callback(GPIO_PIN_0)
EXTI1Px1EXTI1_IRQHandler()HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1)HAL_GPIO_EXTI_Callback(GPIO_PIN_1)
EXTI2Px2EXTI2_IRQHandler()HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2)HAL_GPIO_EXTI_Callback(GPIO_PIN_2)
EXTI3Px3EXTI3_IRQHandler()HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3)HAL_GPIO_EXTI_Callback(GPIO_PIN_3)
EXTI4Px4EXTI4_IRQHandler()HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4)HAL_GPIO_EXTI_Callback(GPIO_PIN_4)
EXTI5~9Px5 ~ Px9EXTI9_5_IRQHandler()对应的 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x)HAL_GPIO_EXTI_Callback(GPIO_PIN_x)
EXTI10~15Px10 ~ Px15EXTI15_10_IRQHandler()对应的 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x)HAL_GPIO_EXTI_Callback(GPIO_PIN_x)

2.1.3 用户回调函数执行

当中断请求被 HAL 处理后,系统会自动调用用户回调函数:

/* USER CODE BEGIN PV */
volatile uint8_t key0_flag = 0;// 定义全局按键标志位,供 main 文件访问
volatile uint8_t key1_flag = 0;
volatile uint8_t key2_flag = 0;
volatile uint8_t keyup_flag = 0;
/* USER CODE END PV *//* USER CODE BEGIN 1 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{// === KEY0 中断 ===if (GPIO_Pin == KEY0_Pin) {if (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET)key0_flag = 1;elsekey0_flag = 0;}// === KEY1 中断 ===if (GPIO_Pin == KEY1_Pin) {if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET)key1_flag = 1;elsekey1_flag = 0;}// === KEY2 中断 ===if (GPIO_Pin == KEY2_Pin) {if (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET)key2_flag = 1;elsekey2_flag = 0;}// === KEY_UP 中断 ===if (GPIO_Pin == KEY_UP_Pin) {if (HAL_GPIO_ReadPin(KEY_UP_GPIO_Port, KEY_UP_Pin) == GPIO_PIN_SET)keyup_flag = 1;elsekeyup_flag = 0;}
}/* USER CODE END 1 */

此函数在 HAL 框架中是统一入口,无论中断来源是独立还是共享,均通过此函数进入用户逻辑层。
典型处理步骤如下:

  1. 根据 GPIO_Pin 参数判断是哪一个按键触发(例如 KEY1、KEY2、KEY3)。

  2. 执行按键消抖逻辑(可在此函数中进行短延时,或在任务中完成软件消抖)。

  3. 设置相应的按键标志位(如 key1_flag = 1;key2_flag = 1;)。

  4. 退出回调函数,返回中断服务层。

⚠️ 注意:
在中断回调中不应执行复杂或耗时的操作,例如任务切换或 LCD 输出。
推荐仅设置标志位或发送信号量,业务处理应在任务中完成。


2.1.4 按键扫描与业务处理

在主循环或单独创建的 “按键扫描任务” 中,定期检测按键标志位,并执行相应业务逻辑:

  1. 定期扫描标志位状态(例如 key1_flagkey2_flag)。

  2. 若检测到标志位被置位:

    • 执行对应的操作(如挂起/恢复任务、删除/创建任务);

    • 执行完毕后清除标志位,防止重复触发。

  3. 返回循环,等待下一次中断触发。

这种“中断触发 + 任务轮询响应”的设计方式,兼顾了实时性与系统稳定性,
既能快速响应外部事件,又避免在中断中执行耗时逻辑。

/* USER CODE BEGIN Variables */
extern volatile uint8_t key0_flag;
extern volatile uint8_t key1_flag;
extern volatile uint8_t key2_flag;
extern volatile uint8_t keyup_flag;
extern TaskHandle_t led0_taskHandle;
extern TaskHandle_t led1_taskHandle;
/* USER CODE END Variables *//* USER CODE BEGIN Header_key_task_handler */
/**
* @brief Function implementing the key_task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_key_task_handler */
void key_task_handler(void *argument)
{/* USER CODE BEGIN key_task_handler *//* Infinite loop */for(;;)for(;;){// 扫描 KEY0 按键(挂起 LED0)if(key0_flag) {key0_flag = 0; // 清除标志位// 简单消抖osDelay(10);if(HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET) {if(led0_taskHandle != NULL) {vTaskSuspend(led0_taskHandle);printf("LED0 task suspended\r\n");}}}// 扫描 KEY1 按键(恢复 LED0)if(key1_flag) {key1_flag = 0;osDelay(10);if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET) {if(led0_taskHandle != NULL) {vTaskResume(led0_taskHandle);printf("LED0 task resumed\r\n");}}}// 扫描 KEY2 按键(删除 LED1)if(key2_flag) {key2_flag = 0;osDelay(10);if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_RESET) {if(led1_taskHandle != NULL) {vTaskDelete(led1_taskHandle);led1_taskHandle = NULL;printf("LED1 task deleted\r\n");}}}// 扫描 KEY_UP 按键(重新创建 LED1)if(keyup_flag) {keyup_flag = 0;osDelay(10);if(HAL_GPIO_ReadPin(KEY_UP_GPIO_Port, KEY_UP_Pin) == GPIO_PIN_RESET) {if(led1_taskHandle == NULL) {// 创建 LED1 任务xTaskCreate(led1_task_handler, "LED1_Task", 128, NULL, 8, &led1_taskHandle);printf("LED1 task created\r\n");}}}osDelay(5); // 扫描间隔}/* USER CODE END key_task_handler */
}

2.1.5 独立与共享中断的区分

在共享中断场景下(如 EXTI9_5_IRQHandler),
多个引脚共用同一中断入口,但 HAL 会在内部自动判断中断来源并调用对应回调函数。
因此,用户无需在中断服务函数中再手动使用 __HAL_GPIO_EXTI_GET_IT() 判断标志位。

所有引脚的实际区分都由 HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) 的参数完成,
从而确保多个按键共享中断向量时也能互不干扰。

特性独立中断(Line0–Line4)共享中断(Line5–Line9 / Line10–Line15)
中断向量每个引脚独立中断向量(EXTI0_IRQHandler ~ EXTI4_IRQHandler)多个引脚共用一个中断向量(EXTI9_5_IRQHandler / EXTI15_10_IRQHandler)
中断优先级可以为每个引脚单独设置 NVIC 优先级所有共享引脚只能统一设置同一个 NVIC 中断优先级
HAL 处理函数在独立 IRQHandler 中直接调用 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x)在共享 IRQHandler 中依次调用多个 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_x)
中断标志清除HAL 内部自动清除对应 EXTI 挂起标志位HAL 内部自动清除对应 EXTI 挂起标志位,无需手动判断
回调函数统一调用 HAL_GPIO_EXTI_Callback(GPIO_Pin),通过 GPIO_Pin 参数区分引脚同样统一调用 HAL_GPIO_EXTI_Callback(GPIO_Pin),通过 GPIO_Pin 参数区分引脚
任务响应设计可独立对每个按键响应和任务操作,优先级更灵活多按键共享优先级,可能影响实时性,需要注意任务优先级分配和标志位扫描

3 LCD显示任务

3.1 硬件连线

3.2 配置截图

3.3 驱动文件

#ifndef __LCD_H
#define __LCD_H#include "fsmc.h"
#include "stm32f1xx_hal.h"  // 方便使用 HAL_Delay、GPIO 等// 宏定义
#define SRAM_BANK1_4 0x6c000000
#define LCD_ADDR_CMD  (uint16_t *)SRAM_BANK1_4       // 写命令指针
#define LCD_ADDR_DATA (uint16_t *)(SRAM_BANK1_4 + (1 << 11)) // 写数据指针// 显示屏的宽高
#define LCD_W 320
#define LCD_H 480
#define CHAR_HEIGHT 16  // 字符高度// RGB565基础颜色
// 基本颜色(RGB565格式)
#define WHITE       0xFFFF  // 白色       (R:31, G:63, B:31)
#define BLACK       0x0000  // 黑色       (R:0,  G:0,  B:0)
#define RED         0xF800  // 红色       (R:31, G:0,  B:0)
#define GREEN       0x07E0  // 绿色       (R:0,  G:63, B:0)
#define BLUE        0x001F  // 蓝色       (R:0,  G:0,  B:31)
#define CYAN        0x07FF  // 青色       (R:0,  G:63, B:31)
#define MAGENTA     0xF81F  // 品红       (R:31, G:0,  B:31)
#define YELLOW      0xFFE0  // 黄色       (R:31, G:63, B:0)
#define GRAY        0x8410  // 灰色       (R:16, G:32, B:16)// 深浅颜色
#define DARK_RED    0x8000  // 深红色     (R:16, G:0,  B:0)
#define LIGHT_BLUE  0x7D7C  // 浅蓝色     (R:15, G:31, B:15)
#define ORANGE      0xFC00  // 橙色       (R:31, G:32, B:0)
#define PINK        0xF81F  // 粉色       (R:31, G:0,  B:31)
#define PURPLE      0x8010  // 紫色       (R:16, G:0,  B:16)// 补充一些常用颜色
#define BROWN       0xA145  // 棕色       (R:20, G:32, B:5)
#define LIGHT_GRAY  0xC618  // 浅灰色     (R:24, G:36, B:24)
#define DARK_GRAY   0x4208  // 深灰色     (R:8,  G:16, B:8)
#define SKY_BLUE    0x867D  // 天蓝色     (R:17, G:39, B:29)
#define ORCHID      0xF15F  // 兰花紫     (R:30, G:12, B:31)
#define GOLD        0xFEA0  // 金色       (R:31, G:53, B:0)// ======================= 基本控制操作 =======================
void LCD_Init(void);
void LCD_Reset(void);
void LCD_BGOn(void);
void LCD_BGOff(void);
void LCD_WriteCmd(uint16_t cmd);
void LCD_WriteData(uint16_t data);
uint16_t LCD_ReadData(void);
uint32_t LCD_ReadID(void);
void LCD_SetArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h);
void LCD_ClearAll(uint16_t color);// ======================= 绘图相关 =========================
void LCD_DrawPixel(uint16_t x, uint16_t y, uint16_t color); // 画点
void LCD_ShowChar(uint16_t x, uint16_t y, char chr, uint16_t color, uint16_t bg); // 显示字符
void LCD_ClearLine(uint16_t line, uint16_t bg);
void LCD_ShowStringOnly(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg);
void LCD_ShowString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg); // 显示字符串
// LCD 显示 8x16 字符
void LCD_ShowChar8x16(uint16_t x, uint16_t y, char chr, uint16_t color, uint16_t bg);
void LCD_ShowString8x16(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg);
#endif
#include "lcd.h"
#include "font.h"
#include "stm32f1xx_hal.h"  // 确保 HAL 函数可用// ======================= 基本控制操作 =======================// LCD 初始化
void LCD_Init(void) {LCD_BGOn(); // 打开背光// 软件复位LCD_WriteCmd(0x01);HAL_Delay(50);// 退出睡眠模式LCD_WriteCmd(0x11);HAL_Delay(120);// 设置像素格式为16位/像素(RGB565)LCD_WriteCmd(0x3A);LCD_WriteData(0x55);// 设置显示方向(BGR位)LCD_WriteCmd(0x36);LCD_WriteData(0x08 | 0x40); // MX=0, MY=1, MV=0, BGR=1// 开启显示LCD_WriteCmd(0x29);
}// LCD 复位(空函数,可根据硬件添加复位操作)
void LCD_Reset(void) {}// 打开/关闭背光
void LCD_BGOn(void) {HAL_GPIO_WritePin(LCD_BL_GPIO_Port, LCD_BL_Pin, GPIO_PIN_SET);
}void LCD_BGOff(void) {HAL_GPIO_WritePin(LCD_BL_GPIO_Port, LCD_BL_Pin, GPIO_PIN_RESET);
}// ======================= 低级操作 =======================// 写命令/数据
void LCD_WriteCmd(uint16_t cmd) {*LCD_ADDR_CMD = cmd;
}void LCD_WriteData(uint16_t data) {*LCD_ADDR_DATA = data;
}uint16_t LCD_ReadData(void) {return *LCD_ADDR_DATA;
}// 读取LCD ID
uint32_t LCD_ReadID(void) {LCD_WriteCmd(0xD3);uint32_t id = 0;LCD_ReadData(); // 第一次读取无效id |= (LCD_ReadData() & 0xFF) << 16;id |= (LCD_ReadData() & 0xFF) << 8;id |= (LCD_ReadData() & 0xFF);return id;
}// 设置绘制区域
void LCD_SetArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h) {LCD_WriteCmd(0x2A);LCD_WriteData((x >> 8) & 0xFF);LCD_WriteData(x & 0xFF);LCD_WriteData(((x + w - 1) >> 8) & 0xFF);LCD_WriteData((x + w - 1) & 0xFF);LCD_WriteCmd(0x2B);LCD_WriteData((y >> 8) & 0xFF);LCD_WriteData(y & 0xFF);LCD_WriteData(((y + h - 1) >> 8) & 0xFF);LCD_WriteData((y + h - 1) & 0xFF);
}// 清屏
void LCD_ClearAll(uint16_t color) {LCD_SetArea(0, 0, LCD_W, LCD_H);LCD_WriteCmd(0x2C);for (uint32_t i = 0; i < LCD_W * LCD_H; i++) {LCD_WriteData(color);}
}// ======================= 绘图相关 =======================// 画点
void LCD_DrawPixel(uint16_t x, uint16_t y, uint16_t color)
{LCD_SetArea(x, y, 1, 1); // 设置1x1区域LCD_WriteCmd(0x2C);       // 写入GRAM命令LCD_WriteData(color);
}// 显示一个 ASCII 字符
void LCD_ShowChar(uint16_t x, uint16_t y, char chr, uint16_t color, uint16_t bg) {uint8_t line, i;if (chr < 0x20 || chr > 0x7E) chr = '?'; // 非法字符显示 '?'const uint8_t *pFont = ASCII_Font6x8[chr - 0x20];for (i = 0; i < 6; i++) { // 每列line = pFont[i];for (uint8_t j = 0; j < 8; j++) { // 每行if (line & 0x01)LCD_DrawPixel(x + i, y + j, color);elseLCD_DrawPixel(x + i, y + j, bg);line >>= 1;}}
}// 显示字符串
void LCD_ShowStringOnly(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg) {while (*str) {LCD_ShowChar(x, y, *str, color, bg);x += 6; // 6像素宽str++;}
}// 清屏指定行(按字符行)
void LCD_ClearLine(uint16_t line, uint16_t bg)
{uint16_t y_start = line * CHAR_HEIGHT;uint16_t y_end   = y_start + CHAR_HEIGHT - 1;if(y_end >= LCD_H) y_end = LCD_H - 1;// 设置绘制区域LCD_SetArea(0, y_start, LCD_W, y_end - y_start + 1);LCD_WriteCmd(0x2C); // Memory Writefor(uint32_t i = 0; i < LCD_W * (y_end - y_start + 1); i++){LCD_WriteData(bg);}
}// 显示字符串(显示前先按背景色清除对应区域)
void LCD_ShowString(uint16_t x, uint16_t y, const char *str, uint16_t color, uint16_t bg)
{// 清除对应背景区域LCD_ClearLine(x,bg);// 逐字符绘制LCD_ShowStringOnly(x,y,str,color,bg);
}
#ifndef __FONT_H__
#define __FONT_H__// 6x8 ASCII 字库 (0x20 - 0x7F),每个字符占6字节
const uint8_t ASCII_Font6x8[][6] = {{0x00,0x00,0x00,0x00,0x00,0x00}, // 20  {0x00,0x00,0x5F,0x00,0x00,0x00}, // 21  !{0x00,0x07,0x00,0x07,0x00,0x00}, // 22  "{0x14,0x7F,0x14,0x7F,0x14,0x00}, // 23  #{0x24,0x2A,0x7F,0x2A,0x12,0x00}, // 24  ${0x23,0x13,0x08,0x64,0x62,0x00}, // 25  %{0x36,0x49,0x55,0x22,0x50,0x00}, // 26  &{0x00,0x05,0x03,0x00,0x00,0x00}, // 27  '{0x00,0x1C,0x22,0x41,0x00,0x00}, // 28  ({0x00,0x41,0x22,0x1C,0x00,0x00}, // 29  ){0x14,0x08,0x3E,0x08,0x14,0x00}, // 2A  *{0x08,0x08,0x3E,0x08,0x08,0x00}, // 2B  +{0x00,0x50,0x30,0x00,0x00,0x00}, // 2C  ,{0x08,0x08,0x08,0x08,0x08,0x00}, // 2D  -{0x00,0x60,0x60,0x00,0x00,0x00}, // 2E  .{0x20,0x10,0x08,0x04,0x02,0x00}, // 2F  /{0x3E,0x51,0x49,0x45,0x3E,0x00}, // 30  0{0x00,0x42,0x7F,0x40,0x00,0x00}, // 31  1{0x42,0x61,0x51,0x49,0x46,0x00}, // 32  2{0x21,0x41,0x45,0x4B,0x31,0x00}, // 33  3{0x18,0x14,0x12,0x7F,0x10,0x00}, // 34  4{0x27,0x45,0x45,0x45,0x39,0x00}, // 35  5{0x3C,0x4A,0x49,0x49,0x30,0x00}, // 36  6{0x01,0x71,0x09,0x05,0x03,0x00}, // 37  7{0x36,0x49,0x49,0x49,0x36,0x00}, // 38  8{0x06,0x49,0x49,0x29,0x1E,0x00}, // 39  9{0x00,0x36,0x36,0x00,0x00,0x00}, // 3A  :{0x00,0x56,0x36,0x00,0x00,0x00}, // 3B  ;{0x08,0x14,0x22,0x41,0x00,0x00}, // 3C  <{0x14,0x14,0x14,0x14,0x14,0x00}, // 3D  ={0x41,0x22,0x14,0x08,0x00,0x00}, // 3E  >{0x02,0x01,0x51,0x09,0x06,0x00}, // 3F  ?{0x32,0x49,0x79,0x41,0x3E,0x00}, // 40  @{0x7E,0x11,0x11,0x11,0x7E,0x00}, // 41  A{0x7F,0x49,0x49,0x49,0x36,0x00}, // 42  B{0x3E,0x41,0x41,0x41,0x22,0x00}, // 43  C{0x7F,0x41,0x41,0x22,0x1C,0x00}, // 44  D{0x7F,0x49,0x49,0x49,0x41,0x00}, // 45  E{0x7F,0x09,0x09,0x09,0x01,0x00}, // 46  F{0x3E,0x41,0x49,0x49,0x7A,0x00}, // 47  G{0x7F,0x08,0x08,0x08,0x7F,0x00}, // 48  H{0x00,0x41,0x7F,0x41,0x00,0x00}, // 49  I{0x20,0x40,0x41,0x3F,0x01,0x00}, // 4A  J{0x7F,0x08,0x14,0x22,0x41,0x00}, // 4B  K{0x7F,0x40,0x40,0x40,0x40,0x00}, // 4C  L{0x7F,0x02,0x0C,0x02,0x7F,0x00}, // 4D  M{0x7F,0x04,0x08,0x10,0x7F,0x00}, // 4E  N{0x3E,0x41,0x41,0x41,0x3E,0x00}, // 4F  O{0x7F,0x09,0x09,0x09,0x06,0x00}, // 50  P{0x3E,0x41,0x51,0x21,0x5E,0x00}, // 51  Q{0x7F,0x09,0x19,0x29,0x46,0x00}, // 52  R{0x46,0x49,0x49,0x49,0x31,0x00}, // 53  S{0x01,0x01,0x7F,0x01,0x01,0x00}, // 54  T{0x3F,0x40,0x40,0x40,0x3F,0x00}, // 55  U{0x1F,0x20,0x40,0x20,0x1F,0x00}, // 56  V{0x7F,0x20,0x18,0x20,0x7F,0x00}, // 57  W{0x63,0x14,0x08,0x14,0x63,0x00}, // 58  X{0x07,0x08,0x70,0x08,0x07,0x00}, // 59  Y{0x61,0x51,0x49,0x45,0x43,0x00}, // 5A  Z{0x00,0x7F,0x41,0x41,0x00,0x00}, // 5B  [{0x02,0x04,0x08,0x10,0x20,0x00}, // 5C  backslash{0x00,0x41,0x41,0x7F,0x00,0x00}, // 5D  ]{0x04,0x02,0x01,0x02,0x04,0x00}, // 5E  ^{0x40,0x40,0x40,0x40,0x40,0x00}, // 5F  _{0x00,0x01,0x02,0x04,0x00,0x00}, // 60  `{0x20,0x54,0x54,0x54,0x78,0x00}, // 61  a{0x7F,0x48,0x44,0x44,0x38,0x00}, // 62  b{0x38,0x44,0x44,0x44,0x20,0x00}, // 63  c{0x38,0x44,0x44,0x48,0x7F,0x00}, // 64  d{0x38,0x54,0x54,0x54,0x18,0x00}, // 65  e{0x08,0x7E,0x09,0x01,0x02,0x00}, // 66  f{0x0C,0x52,0x52,0x52,0x3E,0x00}, // 67  g{0x7F,0x08,0x04,0x04,0x78,0x00}, // 68  h{0x00,0x44,0x7D,0x40,0x00,0x00}, // 69  i{0x20,0x40,0x44,0x3D,0x00,0x00}, // 6A  j{0x7F,0x10,0x28,0x44,0x00,0x00}, // 6B  k{0x00,0x41,0x7F,0x40,0x00,0x00}, // 6C  l{0x7C,0x04,0x18,0x04,0x78,0x00}, // 6D  m{0x7C,0x08,0x04,0x04,0x78,0x00}, // 6E  n{0x38,0x44,0x44,0x44,0x38,0x00}, // 6F  o{0x7C,0x14,0x14,0x14,0x08,0x00}, // 70  p{0x08,0x14,0x14,0x18,0x7C,0x00}, // 71  q{0x7C,0x08,0x04,0x04,0x08,0x00}, // 72  r{0x48,0x54,0x54,0x54,0x20,0x00}, // 73  s{0x04,0x3F,0x44,0x40,0x20,0x00}, // 74  t{0x3C,0x40,0x40,0x20,0x7C,0x00}, // 75  u{0x1C,0x20,0x40,0x20,0x1C,0x00}, // 76  v{0x3C,0x40,0x30,0x40,0x3C,0x00}, // 77  w{0x44,0x28,0x10,0x28,0x44,0x00}, // 78  x{0x0C,0x50,0x50,0x50,0x3C,0x00}, // 79  y{0x44,0x64,0x54,0x4C,0x44,0x00}, // 7A  z{0x00,0x08,0x36,0x41,0x00,0x00}, // 7B  {{0x00,0x00,0x7F,0x00,0x00,0x00}, // 7C  |{0x00,0x41,0x36,0x08,0x00,0x00}, // 7D  }{0x10,0x08,0x08,0x10,0x08,0x00}, // 7E  ~
};#endif /* __FONT_H__ */

3.4 任务代码

/* USER CODE BEGIN Header_lcd_task_handler */
/**
* @brief Function implementing the lcd_task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_lcd_task_handler */
void lcd_task_handler(void *argument)
{/* USER CODE BEGIN lcd_task_handler */char taskListBuf[512];char runTimeStatsBuf[512];char lineBuf[128];char taskName[16], state[4];int prio, stack, num;char runTaskName[16];int runTime;char runUsage[8];uint16_t y;for (;;){// 1️⃣ 清屏LCD_ClearAll(BLACK);// 2️⃣ 打印标题LCD_ShowString(0, 0, "FreeRTOS Monitor", WHITE, BLACK);y = 20; // 内容从第二行�?�?// ----------------- 打印任务列表 -----------------vTaskList(taskListBuf);lineBuf[0] = '\0';char *line = strtok(taskListBuf, "\r\n");// 打印表头LCD_ShowString(0, y, "Task Name    St Prio Stack Num", WHITE, BLACK);y += 16;while (line != NULL){// sscanf 分列if (sscanf(line, "%15s %3s %d %d %d", taskName, state, &prio, &stack, &num) == 5){sprintf(lineBuf, "%-12s %-2s %-4d %-5d %-3d", taskName, state, prio, stack, num);LCD_ShowString(0, y, lineBuf, WHITE, BLACK);y += 16;}line = strtok(NULL, "\r\n");}y += 4; // 表格间距// ----------------- 打印 CPU 利用�? -----------------vTaskGetRunTimeStats(runTimeStatsBuf);line = strtok(runTimeStatsBuf, "\r\n");// 表头LCD_ShowString(0, y, "Task          RunTime      CPU Usage(%)", WHITE, BLACK);y += 16;while (line != NULL){// sscanf 分列if (sscanf(line, "%15s %d %7s", runTaskName, &runTime, runUsage) >= 2){sprintf(lineBuf, "%-12s %-10d %-7s", runTaskName, runTime, runUsage);LCD_ShowString(0, y, lineBuf, WHITE, BLACK);y += 16;}line = strtok(NULL, "\r\n");}y += 4;// ----------------- 系统运行时间 -----------------sprintf(lineBuf, "System uptime: %lu ms (%lu s)", osKernelGetTickCount(), osKernelGetTickCount()/1000);LCD_ShowString(0, y, lineBuf, WHITE, BLACK);osDelay(5000); // 每秒刷新�?�?}/* USER CODE END lcd_task_handler */
}

4. 其他任务

LED0 LED1 以及串口打印任务之前在freeRTOS创建中有讲解,此处不再赘诉。

4.1 其他任务代码

/* USER CODE BEGIN Header_led0_task_handler */
/*** @brief  Function implementing the led0_task thread.* @param  argument: Not used* @retval None*/
/* USER CODE END Header_led0_task_handler */
void led0_task_handler(void *argument)
{/* USER CODE BEGIN led0_task_handler *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);osDelay(1000);}/* USER CODE END led0_task_handler */
}/* USER CODE BEGIN Header_led1_task_handler */
/**
* @brief Function implementing the led1_task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_led1_task_handler */
void led1_task_handler(void *argument)
{/* USER CODE BEGIN led1_task_handler *//* Infinite loop */for(;;){HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);osDelay(300);}/* USER CODE END led1_task_handler */
}/* USER CODE BEGIN Header_monitor_task_handler */
/**
* @brief Function implementing the monitor_task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_monitor_task_handler */
void monitor_task_handler(void *argument)
{/* USER CODE BEGIN monitor_task_handler */char taskListBuf[256];char runTimeStatsBuf[512];/* Infinite loop */for(;;){printf("\r\n========== FreeRTOS Monitor ==========\r\n");// 1️⃣打印任务列表(任务名称�?�状态�?�优先级、剩余栈空间、任务编号)vTaskList(taskListBuf);printf("Task Name   State  Prio  Stack  Num\r\n%s\r\n", taskListBuf);// 2️⃣ 打印 CPU 利用率和运行时间统计vTaskGetRunTimeStats(runTimeStatsBuf);printf("Task          RunTime      CPU Usage(%%)\r\n%s\r\n", runTimeStatsBuf);// 3️⃣ 打印系统启动时间(毫�????????? / 秒)printf("System uptime: %lu ms (%lu s)\r\n", osKernelGetTickCount(), osKernelGetTickCount()/1000);printf("======================================\r\n");osDelay(1000); // 延时 1 秒,循环刷新状�??}/* USER CODE END monitor_task_handler */
}

4.2 其他任务配置

5. 结论 

5.1 开机打印

内容如下所示:

5.2 按下KEY0

led0_task任务被挂起,灯不再闪烁,灯的状态保留最后挂起任务那一瞬间。

5.3 按下KEY1

led0_task任务恢复,灯继续按照任务中的频率进行闪烁。

5.4 按下 KEY2

开机时能看到led1_task,按下KEY2后,任务监控列表中没有这个任务了,灯不再闪烁,灯的状态保留最后删除任务那一瞬间。

5.5 按下KEY_UP

按下KY_UP后,任务又被创建了,灯继续按照任务中的频率闪烁

http://www.dtcms.com/a/497546.html

相关文章:

  • 电影感人文街拍摄影后期Lr调色教程,,手机滤镜PS+Lightroom预设下载!
  • 网站网址ip查询seo优化系统哪个好
  • 个人如何在企业网站做实名认证移动应用开发课程
  • 专业网站设计服务好joomla 做的网站
  • 中山电子商务网站建设珠海响应式网站建设费用
  • DDS的IP核使用
  • 婚纱照网站制作公司网站备案条件
  • 网站建设设计官网近期新闻热点大事件
  • wordpress改企业网站那个网站销售好
  • 做网站的法律网站建设中 怎么办
  • 汇川高压变频器故障代码解析F121 、F123
  • 制作网站的模板免费下载内容营销和传统营销的区别
  • 网站地图html模板蒲城县住房和城乡建设局网站
  • 上传产品网站怎么做的中药材网站开发
  • 莒县网站设计网站建设 代表联系群众
  • 青岛网站建设维护wordpress+整容模板
  • 典型的企业网站国家住房和城乡建设部网站吧
  • 盗用别人的图片做网站犯法注册一个免费的网站
  • 怎么做微信上的网站吗唐山seo优化
  • 湖北网站建设xiduyun企业网站和信息化建设金蝶
  • 重庆网站制作那家好银川网站建站
  • 网站倒计时代码怎样做内网网站
  • 模板网站不可以做seo优化吗电脑做服务器上传网站
  • 织梦网站搭建哪里有竞价推广托管
  • 公司网站建设哪个最好嵌入式培训机构
  • wordpress 开发商城河南网站推广优化
  • 天眼查河南建设网站公司游戏登录器列表更新网站建设
  • 做网站和网页沈阳关键词优化电话
  • 制作手机软件电商运营seo是什么
  • 游戏币网站建设能上国外网站的dns