文章目录
前言
本文介绍了基于STM32F405ZGT× HAL库的uCOSIII移植的详细过程,创建了三个任务,实现了LED闪烁、浮点数计算和串口打印等功能
一、uCOS源码获取
方法一:可以通过官网下载源码,官网链接:
https://www.silabs.com/developers/micrium
提示:在官网下载资源时需要注册账号。我注册的时候总是提示邮箱有误,不知道怎么解决,有了解的小伙伴欢迎把方法发在评论区
方法二:热心网友的分享:
https://download.csdn.net/download/fhdghfuiahfsdifbs/18909773
源码目录如下:
补充几个UCOS源码下载的相关博客链接:
- https://blog.csdn.net/DZRYWYBL/article/details/123153091
- https://blog.csdn.net/weixin_44388614/article/details/121076842
二、HAL工程建立
1.配置SYS,选择Serial Wire
2.配置RCC,选择高速外部时钟
3.配置GPIO(LED)和USART1,通过实现相关功能来测试uCOS移植效果
4.配置完成后,点击“GENERATE CODE”,生成工程代码。生成的keil工程如下
三、移植uCOS
3.1 工程文件夹的文件移植
1.在工程目录下新建文件夹UCOSIII,将源码内的uC-CPU、uC-LIB和uCOS-III这三个文件夹复制粘贴到UCOSIII文件夹下_
2.在UCOSIII目录下创建一个新的文件夹,命名为uCOS-CONFIG。接着,将源代码目录中的八个指定文件复制并粘贴到新创建的uCOS-CONFIG文件夹中。
3.在UCOSIII目录下创建一个新的文件夹,命名为uCOS-BSP,并新建两个空白文件bsp.c和bsp.h
3.2 keil工程目录文件添加
1.向keil工程中添加如下分组
2.向分组中添加文件
3.添加头文件路径
3.3 文件修改
1.在bsp.h中添加如下内容
#ifndef __BSP_H__
#define __BSP_H__
#include "stm32f4xx_hal.h"
void BSP_Init(void);
#endif
2.在bsp.c中添加如下内容
#include "includes.h"
#define BSP_REG_DEM_CR (*(CPU_REG32 *)0xE000EDFC) //DEMCR寄存器
#define BSP_REG_DWT_CR (*(CPU_REG32 *)0xE0001000) //DWT控制寄存器
#define BSP_REG_DWT_CYCCNT (*(CPU_REG32 *)0xE0001004) //DWT时钟计数寄存器
#define BSP_REG_DBGMCU_CR (*(CPU_REG32 *)0xE0042004)
//DEMCR寄存器的第24位,如果要使用DWT ETM ITM和TPIU的话DEMCR寄存器的第24位置1
#define BSP_BIT_DEM_CR_TRCENA DEF_BIT_24
//DWTCR寄存器的第0位,当为1的时候使能CYCCNT计数器,使用CYCCNT之前应当先初始化
#define BSP_BIT_DWT_CR_CYCCNTENA DEF_BIT_00
/*
*********************************************************************************************************
* BSP_CPU_ClkFreq()
* Description : Read CPU registers to determine the CPU clock frequency of the chip.
* Argument(s) : none.
* Return(s) : The CPU clock frequency, in Hz.
* Caller(s) : Application.
* Note(s) : none.
*********************************************************************************************************
*/
CPU_INT32U BSP_CPU_ClkFreq (void)
{
return HAL_RCC_GetHCLKFreq();//返回HCLK时钟频率
}
/*
*********************************************************************************************************
* BSP_Tick_Init()
* Description : BSP_Tick_Init.
* Argument(s) : none.
* Return(s) : none.
* Note(s) : none.
*********************************************************************************************************
*/
void BSP_Tick_Init(void)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
cpu_clk_freq = BSP_CPU_ClkFreq();
#if(OS_VERSION>=3000u)
cnts = cpu_clk_freq/(CPU_INT32U)OSCfg_TickRate_Hz;
#else
cnts = cpu_clk_freq/(CPU_INT32U)OS_TICKS_PER_SEC;
#endif
OS_CPU_SysTickInit(cnts);
}
void BSP_Init(void)
{
BSP_Tick_Init();//此函数会初始化OS系统时钟,如果移植了正点原子的delay文件,则与主函数中的delay_init(168)只需要调用一个即可
}
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void CPU_TS_TmrInit (void)
{
CPU_INT32U cpu_clk_freq_hz;
BSP_REG_DEM_CR |= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA; //使用DWT /* Enable Cortex-M4's DWT CYCCNT reg.*/
BSP_REG_DWT_CYCCNT = (CPU_INT32U)0u; //初始化CYCCNT寄存器
BSP_REG_DWT_CR |= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA; //开启CYCCNT
cpu_clk_freq_hz = BSP_CPU_ClkFreq();
CPU_TS_TmrFreqSet(cpu_clk_freq_hz);
}
#endif
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
CPU_TS_TMR CPU_TS_TmrRd (void)
{
return ((CPU_TS_TMR)BSP_REG_DWT_CYCCNT);
}
#endif
#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U CPU_TS32_to_uSec (CPU_TS32 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U CPU_TS64_to_uSec (CPU_TS64 ts_cnts)
{
CPU_INT64U ts_us;
CPU_INT64U fclk_freq;
fclk_freq = BSP_CPU_ClkFreq();
ts_us = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);
return (ts_us);
}
#endif
3.修改启动文件startup_stm32f405xx.s
将
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
修改为
DCD OS_CPU_PendSVHandler ; PendSV Handler
DCD OS_CPU_SysTickHandler ; SysTick Handler
将
PendSV_Handler PROC
EXPORT PendSV_Handler [WEAK]
B .
ENDP
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
修改为
OS_CPU_PendSVHandler PROC
EXPORT OS_CPU_PendSVHandler [WEAK]
B .
ENDP
OS_CPU_SysTickHandler PROC
EXPORT OS_CPU_SysTickHandler [WEAK]
B .
ENDP
4.支持浮点运算
在startup_stm32f405xx.s 中添加如下汇编代码
IF {FPU} != "SoftVFP"
; Enable Floating Point Support at reset for FPU
LDR.W R0, =0xE000ED88 ; Load address of CPACR register
LDR R1, [R0] ; Read value at CPACR
ORR R1, R1, #(0xF <<20); Set bits 20-23 to enable CP10 and CP11 coprocessors
; Write back the modified CPACR value
STR R1, [R0] ; Wait for store to complete
DSB
; Disable automatic FP register content
; Disable lazy context switch
LDR.W R0, =0xE000EF34 ; Load address to FPCCR register
LDR R1, [R0]
AND R1, R1, #(0x3FFFFFFF) ; Clear the LSPEN and ASPEN bits
STR R1, [R0]
ISB ; Reset pipeline now the FPU is enabled
ENDIF
添加完成后如图所示:
3.4 编译测试
完成上述步骤后,点击编译按钮,若提示无报错,则移植成功
四、功能实现
任务概述
- 创建一个名为start_task的初始化任务,负责执行系统启动过程以及创建后续任务。
- 创建一个名为led_task的任务,其功能是控制LED的开关状态。
- 创建一个名为float_task的任务,用于执行浮点数运算测试,并将结果通过串口输出
4.1 串口重定向
修改usart.c,添加如下代码
//******************其它代码************************//
/* USER CODE BEGIN 0 */
#include "stdio.h"
/* USER CODE END 0 */
//******************其它代码************************//
/* USER CODE BEGIN 1 */
/**
* 函数功能: 重定向c库函数printf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
return ch;
}
/**
* 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
* 输入参数: 无
* 返 回 值: 无
* 说 明:无
*/
int fgetc(FILE *f)
{
uint8_t ch = 0;
HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
return ch;
}
/* USER CODE END 1 */
//******************其它代码************************//
4.2 创建任务
修改main.c,添加如下代码
//******************其它代码************************//
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include <includes.h>
/* USER CODE END Includes */
//******************其它代码************************//
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* UCOSIII中以下优先级用户程序不能使用:
* 将这些优先级分配给了UCOSIII的5个系统内部任务
* 优先级0:中断服务服务管理任务 OS_IntQTask()
* 优先级1:时钟节拍任务 OS_TickTask()
* 优先级2:定时任务 OS_TmrTask()
* 优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask()
* 优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask()
*/
/* 任务优先级 */
#define START_TASK_PRIO 3
#define LED_TASK_PRIO 4
#define FLOAT_TASK_PRIO 5
/* 任务堆栈大小 */
#define START_STK_SIZE 512
#define LED_STK_SIZE 128
#define FLOAT_STK_SIZE 128
/* USER CODE END PD */
//******************其它代码************************//
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* 任务控制块 */
OS_TCB StartTaskTCB;
OS_TCB LedTaskTCB;
OS_TCB FloatTaskTCB;
/* 任务栈 */
CPU_STK START_TASK_STK[START_STK_SIZE];
CPU_STK LED_TASK_STK[LED_STK_SIZE];
__align(8) CPU_STK FLOAT_TASK_STK[FLOAT_STK_SIZE];
/* USER CODE END PV */
//******************其它代码************************//
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* 任务函数定义 */
void start_task(void *p_arg);
void led_task(void *p_arg);
void float_task(void *p_arg);
/* USER CODE END 0 */
//******************其它代码************************//
int main(void)
{
/* USER CODE BEGIN 1 */
OS_ERR err;
CPU_SR_ALLOC();
/* USER CODE END 1 */
//******************其它代码************************//
/* USER CODE BEGIN 2 */
//delay_init(168); //时钟初始化 如果移植了正点原子的delay文件且SYSTEM_SUPPORT_OS被定义,此函数会初始化OS系统时钟,与start_task任务中的BSP_Init()只需要调用一个即可
OSInit(&err); //初始化UCOSIII
OS_CRITICAL_ENTER();//进入临界区
//创建开始任务
OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块
(CPU_CHAR* )"start task", //任务名字
(OS_TASK_PTR)start_task, //任务函数
(void * )0, //传递给任务函数的参数
(OS_PRIO )START_TASK_PRIO, //任务优先级
(CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址
(CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位
(CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY)0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
(OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度,
(void * )0, //用户补充的存储区
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项
(OS_ERR * )&err); //存放该函数错误时的返回值
OS_CRITICAL_EXIT(); //退出临界区
OSStart(&err); //启动多任务系统,控制权交给uC/OS-III
/* USER CODE END 2 */
//******************其它代码************************//
}
/* USER CODE BEGIN 4 */
//开始任务函数
void start_task(void *p_arg)
{
OS_ERR err;
CPU_SR_ALLOC();
p_arg = p_arg;
BSP_Init(); /* Initialize BSP functions */
//CPU_Init();
//Mem_Init(); /* Initialize Memory Management Module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); //统计任务
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间
CPU_IntDisMeasMaxCurReset();
#endif
#if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候
//使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms
OSSchedRoundRobinCfg(DEF_ENABLED,1,&err);
#endif
OS_CRITICAL_ENTER(); //进入临界区
/* 创建LED任务 */
OSTaskCreate((OS_TCB * )&LedTaskTCB,
(CPU_CHAR * )"led task",
(OS_TASK_PTR )led_task,
(void * )0,
(OS_PRIO )LED_TASK_PRIO,
(CPU_STK * )&LED_TASK_STK[0],
(CPU_STK_SIZE)LED_STK_SIZE/10,
(CPU_STK_SIZE)LED_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
/* 创建浮点测试任务 */
OSTaskCreate((OS_TCB * )&FloatTaskTCB,
(CPU_CHAR * )"float test task",
(OS_TASK_PTR )float_task,
(void * )0,
(OS_PRIO )FLOAT_TASK_PRIO,
(CPU_STK * )&FLOAT_TASK_STK[0],
(CPU_STK_SIZE)FLOAT_STK_SIZE/10,
(CPU_STK_SIZE)FLOAT_STK_SIZE,
(OS_MSG_QTY )0,
(OS_TICK )0,
(void * )0,
(OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
(OS_ERR * )&err);
OS_TaskSuspend((OS_TCB*)&StartTaskTCB,&err); //挂起开始任务
OS_CRITICAL_EXIT(); //进入临界区
}
/* led任务函数 */
void led_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
while(1)
{
HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
OSTimeDly(500, OS_OPT_TIME_DLY, &err);
}
}
/* 浮点测试任务 */
void float_task(void *p_arg)
{
OS_ERR err;
p_arg = p_arg;
CPU_SR_ALLOC();
static float float_num=0.01;
while(1)
{
float_num+=0.01f;
OS_CRITICAL_ENTER(); //进入临界区
printf("float_num的值为:%.4f\r\n",float_num);
OS_CRITICAL_EXIT();
OSTimeDly(500, OS_OPT_TIME_DLY, &err);
}
}
/* USER CODE END 4 */
//******************其它代码************************//
五、编译测试
编译程序,将程序烧录到开发板上,可以观察到LED灯闪烁,串口打印浮点数据
在浮点计算处设置断点,全速运行,程序到达断点后查看汇编窗口,如下图所示:
可以看到有VLDR、VADD.F32等FPU指令,这说明STM32F405内部的FPU进行浮点运算成功
总结
本文章介绍了基于STM32F405ZGT× HAL库的uCOSIII移植的详细过程,实现了LED闪烁、浮点数计算和串口打印等功能,给出了实验验证。