STM32F103 移植UCOSIII GNU编译实现
1、准备移植文件,从micrium官网下载在UCOSIII移植文件。
当前进入网站首页时,选择STM 法意半导体 芯片生产商查看全部型号。
下图,这里我们选择STM32F107 它使用的是UCOSIII V3.03.01 版本,IDE使用了
Atollic TrueSTUDIO V3.x 是市售增强C / C ++ IDE基于Eclipse ®,CDT ™,GCC和GDB。2017年以来已停止更新,ST已收购。
IAR (EWARM) V6.x 是IARSystems 公司为ARM 微处理器开发的一个集成开发环境。
Keil MDK V3.x 是美国Keil Software公司出品的单片机C语言软件开发系统。
以上三种IED 使用的编译链接器不相同,有GNU 、IAR、 RealView 、后面讲到选择uC-CPU文件夹里面的程序时,需要特别留意
自己IDE环境。
下载完毕后解压
2、整理和拷贝UCOSIII移植文件。
在程序目录下建立自定义文件夹如下:
在自定义的文件夹中,我们将官网下载的Ucosiii源码复制到文件中,详情见以下说明:
UCOSIII
├文件夹1:[UCOSIII_BSP] <-----------------------里面的bsp.c、bsp.h是自定义的程序。
│ ├(1)bsp.c
│ ├(2)bsp.h
│ └█
├文件夹2:[UCOSIII_CONFIG] <------------------MicriumSoftwareEvalBoardsMicriumuC-Eval-STM32F107uCOS-III
│ ├(1)app_cfg.h
│ ├(2)cpu_cfg.h
│ ├(3)includes.h
│ ├(4)lib_cfg.h
│ ├(5)os_app_hooks.c
│ ├(6)os_app_hooks.h
│ ├(7)os_cfg.h
│ ├(8)os_cfg_app.h
│ └█
├文件夹3:[UCOSIII_CORE] <--------------------MicriumSoftwareuCOS-IIISource
│ ├(1)os.h
│ ├(2)os_cfg_app.c
│ ├(3)os_core.c
│ ├(4)os_dbg.c
│ ├(5)os_flag.c
│ ├(6)os_int.c
│ ├(7)os_mem.c
│ ├(8)os_msg.c
│ ├(9)os_mutex.c
│ ├(10)os_pend_multi.c
│ ├(11)os_prio.c
│ ├(12)os_q.c
│ ├(13)os_sem.c
│ ├(14)os_stat.c
│ ├(15)os_task.c
│ ├(16)os_tick.c
│ ├(17)os_time.c
│ ├(18)os_tmr.c
│ ├(19)os_type.h
│ ├(20)os_var.c
│ └█
├文件夹4:[UCOSIII_CPU] <--------------------MicriumSoftwareuC-CPU
│ ├(1)cpu.h
│ ├(2)cpu_a.s
│ ├(3)cpu_c.c
│ ├(4)cpu_core.c
│ ├(5)cpu_core.h
│ ├(6)cpu_def.h
│ └█
├文件夹5:[UCOSIII_LIB] <--------------------MicriumSoftwareuC-LIB
│ ├(1)lib_ascii.c
│ ├(2)lib_ascii.h
│ ├(3)lib_def.h
│ ├(4)lib_math.c
│ ├(5)lib_math.h
│ ├(6)lib_mem.c
│ ├(7)lib_mem.h
│ ├(8)lib_mem_a.s
│ ├(9)lib_str.c
│ ├(10)lib_str.h
│ └█
├文件夹6:[UCOSIII_PORT] <--------------------uCOS-IIIPortsARM-Cortex-M3GenericGNU
│ ├(1)os_cpu.h
│ ├(2)os_cpu_a.s
│ ├(3)os_cpu_c.c
│ └█
└█
这里需要特别说明文件夹6:确认自己的IDE 使用的编译器是GNU 、IAR、 RealView哪一种,对应着拷贝,使用不同的代码,移植会有
一些差异,具体可以参照其它教程,这里我们选择GNU编译的代码,即 ;uCOS-IIIPortsARM-Cortex-M3GenericGNU 的 os_cpu.h 、 os_cpu_a.s 、 os_cpu_c.c 。
拷贝到UCOSIII文件夹到工程后,根据自己IDE设置方法将UCOSIII这个文件夹添加到IDE编译路径,不同的IDE是不同的,比如 我这边使用的是Clion 通过修改CMakelists.txt 增加代码的路径。
2.1修改 UCOSIII_BSP 的代码
官方的Bsp.c文件中,有很多代码是我们不需要用到的,我们只用到DWT的代码,应该参照官网源码,我们做以下修改。
Bsp.h
#ifndef BSP_PRESENT #define BSP_PRESENT #ifdef BSP_MODULE #define BSP_EXT #else #define BSP_EXT extern #endif #include <stdio.h> #include <stdarg.h> #include <cpu.h> #include <cpu_core.h> #include <lib_def.h> #include <lib_ascii.h> //#include <stm32f10x_conf.h> //标准库方式的 #include "stm32f1xx_hal.h" //对应HAL库 主要目的是为了获取系统时钟源 #endif
Bsp.c
#define BSP_MODULE #include <bsp.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) { // RCC_ClocksTypeDef rcc_clocks; // // RCC_GetClocksFreq(&rcc_clocks); //获取各个时钟频率 // // return ((CPU_INT32U)rcc_clocks.HCLK_Frequency); //返回HCLK时钟频率 //RCC_ClkInitTypeDef rcc_clocks ; //HAL_RCC_GetOscConfig(); //HAL_RCC_GetHCLKFreq(); //获取各个时钟频率 return ((CPU_INT32U)HAL_RCC_GetHCLKFreq()); //返回HCLK时钟频率 } /*$PAGE*/ /* ********************************************************************************************************* * CPU_TS_TmrInit() * Description : Initialize & start CPU timestamp timer. * Argument(s) : none. * Return(s) : none. * Caller(s) : CPU_TS_Init(). * This function is an INTERNAL CPU module function & MUST be implemented by application/ * BSP function(s) [see Note #1] but MUST NOT be called by application function(s). * Note(s) : (1) CPU_TS_TmrInit() is an application/BSP function that MUST be defined by the developer * if either of the following CPU features is enabled : * (a) CPU timestamps * (b) CPU interrupts disabled time measurements * See 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #1' * & 'cpu_cfg.h CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION Note #1a'. * (2) (a) Timer count values MUST be returned via word-size-configurable 'CPU_TS_TMR' * data type. * (1) If timer has more bits, truncate timer values' higher-order bits greater * than the configured 'CPU_TS_TMR' timestamp timer data type word size. * (2) Since the timer MUST NOT have less bits than the configured 'CPU_TS_TMR' * timestamp timer data type word size; 'CPU_CFG_TS_TMR_SIZE' MUST be * configured so that ALL bits in 'CPU_TS_TMR' data type are significant. * In other words, if timer size is not a binary-multiple of 8-bit octets * (e.g. 20-bits or even 24-bits), then the next lower, binary-multiple * octet word size SHOULD be configured (e.g. to 16-bits). However, the * minimum supported word size for CPU timestamp timers is 8-bits. * See also 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #2' * & 'cpu_core.h CPU TIMESTAMP DATA TYPES Note #1'. * (b) Timer SHOULD be an 'up' counter whose values increase with each time count. * (c) When applicable, timer period SHOULD be less than the typical measured time * but MUST be less than the maximum measured time; otherwise, timer resolution * inadequate to measure desired times. * See also 'CPU_TS_TmrRd() Note #2'. ********************************************************************************************************* */ #if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) void CPU_TS_TmrInit (void) { CPU_INT32U fclk_freq; fclk_freq = BSP_CPU_ClkFreq(); 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_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq); } #endif /*$PAGE*/ /* ********************************************************************************************************* * CPU_TS_TmrRd() * Description : Get current CPU timestamp timer count value. * Argument(s) : none. * Return(s) : Timestamp timer count (see Notes #2a & #2b). * Caller(s) : CPU_TS_Init(), * CPU_TS_Get32(), * CPU_TS_Get64(), * CPU_IntDisMeasStart(), * CPU_IntDisMeasStop(). * This function is an INTERNAL CPU module function & MUST be implemented by application/ * BSP function(s) [see Note #1] but SHOULD NOT be called by application function(s). * Note(s) : (1) CPU_TS_TmrRd() is an application/BSP function that MUST be defined by the developer * if either of the following CPU features is enabled : * (a) CPU timestamps * (b) CPU interrupts disabled time measurements * See 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #1' * & 'cpu_cfg.h CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION Note #1a'. * (2) (a) Timer count values MUST be returned via word-size-configurable 'CPU_TS_TMR' * data type. * (1) If timer has more bits, truncate timer values' higher-order bits greater * than the configured 'CPU_TS_TMR' timestamp timer data type word size. * (2) Since the timer MUST NOT have less bits than the configured 'CPU_TS_TMR' * timestamp timer data type word size; 'CPU_CFG_TS_TMR_SIZE' MUST be * configured so that ALL bits in 'CPU_TS_TMR' data type are significant. * In other words, if timer size is not a binary-multiple of 8-bit octets * (e.g. 20-bits or even 24-bits), then the next lower, binary-multiple * octet word size SHOULD be configured (e.g. to 16-bits). However, the * minimum supported word size for CPU timestamp timers is 8-bits. * See also 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #2' * & 'cpu_core.h CPU TIMESTAMP DATA TYPES Note #1'. * (b) Timer SHOULD be an 'up' counter whose values increase with each time count. * (1) If timer is a 'down' counter whose values decrease with each time count, * then the returned timer value MUST be ones-complemented. * (c) (1) When applicable, the amount of time measured by CPU timestamps is * calculated by either of the following equations : * (A) Time measured = Number timer counts * Timer period * where * * Number timer counts Number of timer counts measured * Timer period Timer's period in some units of * (fractional) seconds * Time measured Amount of time measured, in same * units of (fractional) seconds * as the Timer period * * Number timer counts * (B) Time measured = --------------------- * Timer frequency * * where * * Number timer counts Number of timer counts measured * Timer frequency Timer's frequency in some units * of counts per second * Time measured Amount of time measured, in seconds * * (2) Timer period SHOULD be less than the typical measured time but MUST be less * than the maximum measured time; otherwise, timer resolution inadequate to * measure desired times. ********************************************************************************************************* */ #if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) CPU_TS_TMR CPU_TS_TmrRd (void) { CPU_TS_TMR ts_tmr_cnts; ts_tmr_cnts = (CPU_TS_TMR)BSP_REG_DWT_CYCCNT; return (ts_tmr_cnts); } #endif /*$PAGE*/ /* ********************************************************************************************************* * CPU_TSxx_to_uSec() * Description : Convert a 32-/64-bit CPU timestamp from timer counts to microseconds. * Argument(s) : ts_cnts CPU timestamp (in timestamp timer counts [see Note #2aA]). * Return(s) : Converted CPU timestamp (in microseconds [see Note #2aD]). * Caller(s) : Application. * This function is an (optional) CPU module application programming interface (API) * function which MAY be implemented by application/BSP function(s) [see Note #1] & * MAY be called by application function(s). * Note(s) : (1) CPU_TS32_to_uSec()/CPU_TS64_to_uSec() are application/BSP functions that MAY be * optionally defined by the developer when either of the following CPU features is * enabled : * (a) CPU timestamps * (b) CPU interrupts disabled time measurements * See 'cpu_cfg.h CPU TIMESTAMP CONFIGURATION Note #1' * & 'cpu_cfg.h CPU INTERRUPTS DISABLED TIME MEASUREMENT CONFIGURATION Note #1a'. * (2) (a) The amount of time measured by CPU timestamps is calculated by either of * the following equations : * * 10^6 microseconds * (1) Time measured = Number timer counts * ------------------- * Timer period * 1 second * * Number timer counts 10^6 microseconds * (2) Time measured = --------------------- * ------------------- * Timer frequency 1 second * * where * * (A) Number timer counts Number of timer counts measured * (B) Timer frequency Timer's frequency in some units * of counts per second * (C) Timer period Timer's period in some units of * (fractional) seconds * (D) Time measured Amount of time measured, * in microseconds * * (b) Timer period SHOULD be less than the typical measured time but MUST be less * than the maximum measured time; otherwise, timer resolution inadequate to * measure desired times. * * (c) Specific implementations may convert any number of CPU_TS32 or CPU_TS64 bits * -- up to 32 or 64, respectively -- into microseconds. ********************************************************************************************************* */ #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、修改UCOSiii关联的代码。
完成第2大点的步骤,UCOSiii的文件算是全部准备好了,下来我们将相关代码来匹配当前的环境。
3.1 UCOSIII/UCOSIII_CONFIG/includes.h 修改
将#include <stm32f10x_lib.h> 注释掉,我们不需要使用。
3.2 UCOSIII/UCOSIII_PORT/os_cpu_a.s 及 Core/Src/stm32f1xx_it.c 修改
os_cpu_a.s
OS_CPU_PendSVHandler 改为 PendSV_Handler
在ucos的源码都定义好了名称是OS_CPU_PendSVHandler,在文件os_cpu_a.s中如3.2上图所示。但是stm32中也定义了异常中断,在stm32的启动文件startup_stm32xxx.s中
可以看到如下图所示,其名称为PendSVHandler(或PendSV_Handler)与前者的名字不同而已。由于stm32是先从启动文件开始运行,中断异常自然是按启动文件中定义的为准,所以后面
ucos在进行任务切换的时候触发了异常中断,而ucos异常中断函数相当于是没用的,所以如果将OS_CPU_PendSVHandler 改为 PendSV_Handler 出现了卡死情况。
stm32f1xx_it.c
将 void PendSV_Handler(void) 注释掉如下图,原因是:stm32f1xx_it.c 和 os_cpu_a.s 中重复定义了 PendSV_Handler 这个函数。
3.3 延时delay.c 程序的修改。
delay.c是用于时间延时控制的,STM32 delay.c的网上有很多教程,这里就不另外展开了。
3.3.1 SysTick_Handler (void) 重名修改。
自定义的Delay.c 延时程序中使用了void SysTick_Handler(void) 函数系统滴答计时器如右图。因为自定义的Delay.c 和 stm32f1xx_it.c 中存在同名冲突, 所以需要屏蔽掉stm32f1xx_it.c 中的函数如左图。
屏蔽掉stm32f1xx_it.c的函数后,HAL的时钟也不启用,其它程序不能使用HAL的Delay() 函数功能。
3.3.2 Delay.c 支持 Ucosiii 功能启用。
自定义的 sys.h 的 SYSTEM_SUPPORT_OS = 1 支持Ucosiii
3、Main.c创建任务运行Ucosiii
#include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "sys.h" // 时钟配置、GPIO位带 【时钟配置不用这里的】 #include "delay.h" #include "includes.h" void SystemClock_Config(void); static void MX_GPIO_Init(void); void flesh_LED(int Delay); void flesh_LEDB8(int Delay); /*----------------------------------------------------------------------------*/ #define START_TASK_PRIO 3 //任务优先级 #define START_STK_SIZE 512 //任务堆栈大小 OS_TCB StartTaskTCB; //任务控制块 CPU_STK START_TASK_STK[START_STK_SIZE]; //任务堆栈 void start_task(void *p_arg); //任务函数 /*----------------------------------------------------------------------------*/ #define LED0_TASK_PRIO 4 //任务优先级 #define LED0_STK_SIZE 128 //任务堆栈大小 OS_TCB Led0TaskTCB; //任务控制块 CPU_STK LED0_TASK_STK[LED0_STK_SIZE]; //任务堆栈 void led0_task(void *p_arg); //任务函数 /*----------------------------------------------------------------------------*/ #define LED1_TASK_PRIO 5 //任务优先级 #define LED1_STK_SIZE 128 //任务堆栈大小 OS_TCB Led1TaskTCB; //任务控制块 CPU_STK LED1_TASK_STK[LED1_STK_SIZE]; //任务堆栈 void led1_task(void *p_arg); //任务函数 /*----------------------------------------------------------------------------*/ /** * @brief The application entry point. * @retval int */ int main(void) { /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. * 重置所有外围设备,初始化Flash接口和Systick*/ HAL_Init(); /* 系统时钟初始化 */ SystemClock_Config(); /* 系统初始化*/ delay_init(72);//初始化延时函数 /*初始化设置外围设备---------------------------------------------------------*/ MX_GPIO_Init(); /*任务开启------------------------------------------------------------------*/ OS_ERR err; CPU_SR_ALLOC(); HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2); //中断分组配置 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); //开启UCOSIII while (1) { } } //开始任务 void start_task(void *p_arg) { OS_ERR err; CPU_SR_ALLOC(); p_arg = p_arg; CPU_Init(); #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(); //进入临界区 //创建LED0任务 OSTaskCreate((OS_TCB * )&Led0TaskTCB, (CPU_CHAR * )"led0 task", (OS_TASK_PTR )led0_task, (void * )0, (OS_PRIO )LED0_TASK_PRIO, (CPU_STK * )&LED0_TASK_STK[0], (CPU_STK_SIZE)LED0_STK_SIZE/10, (CPU_STK_SIZE)LED0_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); //创建LED1任务 OSTaskCreate((OS_TCB * )&Led1TaskTCB, (CPU_CHAR * )"led1 task", (OS_TASK_PTR )led1_task, (void * )0, (OS_PRIO )LED1_TASK_PRIO, (CPU_STK * )&LED1_TASK_STK[0], (CPU_STK_SIZE)LED1_STK_SIZE/10, (CPU_STK_SIZE)LED1_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(); //进入临界区 } /** * @brief 任务0 PB8 LED闪烁 */ void led0_task(void *p_arg) { OS_ERR err; p_arg = p_arg; while(1) { flesh_LEDB8(100); } } /** * @brief 任务1 PC13 LED闪烁 */ void led1_task(void *p_arg) { OS_ERR err; p_arg = p_arg; while(1) { flesh_LED(500); } } /** * @brief LED闪烁PC13 * @param Delay 延时ms * @retval None */ void flesh_LED(int Delay) { //闪烁一个LED PC13 自定义函数 PCout(13) =0; delay_ms(Delay); PCout(13) =1; delay_ms(Delay); } void flesh_LEDB8(int Delay) { //闪烁一个LED PC13 自定义函数 PBout(8) =0; delay_ms(Delay); PBout(8) =1; delay_ms(Delay); }
运行程序后可以发现PC13 和PB8 通过位带操作以不同的频率进行闪烁。
参考链接:https://blog.csdn.net/qq_42660303/article/details/107931915
https://blog.csdn.net/chuancey_cc/article/details/84844740