前言
-
前两篇已经 通过 STM32CubeMX 搭建了
NUCLEO-L476RG
的 STM32L476RG 的 裸机工程,下载了 uC-OS2 V2.93 的源码,并把 uC-OS2 的源文件加入 Keil MDK5 工程 -
本篇适配 uC-OS2 的 系统定时器(Systick)与
PendSV_Handler
,创建 用户任务,让 uC-OS2 启动并运行起来
开发环境
-
win10 64位
-
Keil uVision5,MDK V5.36
-
uC-OS2 V2.93
-
开发板:NUCLEO-L476RG ,MCU 为 STM32L476RG
-
STM32CubeMX 6.9.1,用于生成 STM32的裸机工程
创建用户任务
- 这里创建一个 led 闪烁的任务,在 main.c 中实现
#include "main.h"
#include "led.h"
#include "app_cfg.h"
#include "os.h"
UART_HandleTypeDef huart2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
#define TASK_LED_PRIO 5
#define TASK_LED_STACK_SIZE 128
static OS_STK task_led_stack[TASK_LED_STACK_SIZE];
static void task_led_entry(void *p_arg)
{
while (1)
{
led_grn_ctrl(1);
OSTimeDly(1000);
led_grn_ctrl(0);
OSTimeDly(1000);
}
}
void led_task_init(void)
{
OSTaskCreate(task_led_entry,(void *)0, &task_led_stack[TASK_LED_STACK_SIZE-1], TASK_LED_PRIO);
}
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
return HAL_OK;
}
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
OSInit();
led_task_init();
OS_CPU_SysTickInitFreq(80000000);
OSStart();
return 0;
}
适配 Systick
-
工程编译下载后,发现 任务没有正常调度起来,原因是没有适配 uC-OS2
-
首先需要开启 uC-OS2 的系统定时器,也就是系统的心跳
-
uC-OS2 V2.93 有 Systick 的初始化函数,
uC-OS2\Ports\ARM-Cortex-M\ARMv7-M\os_cpu_c.c
,OS_CPU_SysTickInitFreq
-
备注: STM32的 HAL库,默认在
HAL_Init
中通过HAL_InitTick
初始化了 1ms 一次的 Systick -
这里首先重新 STM32 HAL 库的
HAL_InitTick
,默认是个 weak 函数,可以重新,这里重写为空,也就是不通过 STM32 HAL 库初始化 Systick
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
return HAL_OK;
}
-
通过
OS_CPU_SysTickInitFreq(80000000);
初始化 Systick -
初始化了 Systick,就要处理一下 Systick 的中断函数:
SysTick_Handler
,需要调用 uC-OS2 的OS_CPU_SysTickHandler
,修改文件stm32l476_ucosii_led\Core\Src\stm32l4xx_it.c
#include "app_cfg.h"
#include "os_cpu.h"
void SysTick_Handler(void)
{
OS_CPU_SysTickHandler();
HAL_IncTick();
}
-
以上的修改, Systick 初始化,Systick 的中断可以周期性的触发
-
如果发现 Systick 没有按1毫秒触发一次中断,如延时1秒发现时间是 10秒,需要修改
uC-OS2\Cfg\Template\os_cfg.h
,修改OS_TICKS_PER_SEC
, 改为#define OS_TICKS_PER_SEC 1000u
,也就是 1毫秒触发一次Systick 中断
适配 PendSV_Handler
- 像 STM32 系列的芯片,默认通过
PendSV_Handler
中断进行任务的切换,修改文件stm32l476_ucosii_led\Core\Src\stm32l4xx_it.c
中的PendSV_Handler
void PendSV_Handler(void)
{
OS_CPU_PendSVHandler();
}
- 注意
stm32l476_ucosii_led\Core\Src\stm32l4xx_it.c
中 包含头文件
#include "app_cfg.h"
#include "os_cpu.h"
编译烧写
-
编译烧写后,发现开发板的 LED 正常的闪烁, uC-OS2 系统移植初步完成,任务切换、系统延时等正常工作了
-
可以点击 Keil MDK5 的【Debug】调试按钮,进入 Debug 调试模式,加入【断点】,单步或者连续运行,观察 uC-OS2 的任务调度、切换,用户例程的执行等
调试说明
- 如果调试发现【断点】不起作用,需要调整 Keil MDK5 的编译优化等级,改为 O0 试试
- 通过软件调试,可以熟悉整个系统的初始化、启动、运行流程
小结
-
本篇通过适配 uC-OS2 的两个关键的适配: 系统滴答定时器与
PendSV
系统异常,实现 uC-OS2 的正常启动与运行 -
uC-OS2 移植起来相对简单,后面继续研究 uC-OS2,如增加串口输出、移植 Shell 等