本篇目标:从搭建工程开始,一步步在 STM32F407ZGT6 上移植 ucos_ii 操作系统
材料准备:
- stm32库文件:stm32f4官方库函数文件(stm32f4官网库函数资料)
- ucos_ii源文件:ucos_ii操作系统官方源代码文件(ucos_ii官网资料)
- 移植工程:stm32f4移植ucos_II最终工程(stm32f4移植ucos_ii工程)
搭建stm32f4工程
建立如下文件夹,方便整理和拷贝:
解压stm32库函数包,进行文件的拷贝:
(1)将 STM32F4xx_DSP_StdPeriph_Lib_V1.7.1\Libraries\STM32F4xx_
StdPeriph_Driver 文件夹下的两个文件夹拷贝到fwlib文件夹;
(2)将 Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates\arm 文件夹下的 startup_stm32f40_41xxx.s 拷贝到startup文件夹;
(3)将 Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates 文件夹下的 system_stm32f4xx.c 拷贝到misis文件夹;
(4)将 Libraries\CMSIS\Device\ST\STM32F4xx\Include 文件夹下的两个文件拷贝到misis文件夹;
(5)将 Libraries\CMSIS\Include 文件夹下的 core_cm4.h 、core_cmFunc.h 、core_cmInstr.h 、core_cmSimd.h 拷贝到misis文件夹;
(6)在 Project\STM32F4xx_StdPeriph_Examples 文件夹下随便找一个工程例子,将其中的 main.c 、stm32f4xx_conf.h 、stm32f4xx_it.c 、stm32f4xx_it.h 拷贝到user文件夹;建立keil工程如图:
keil工程修改:
(1)删除main.c所有内容,只写一个main函数;
(2)删除stm32f4xx.h中的包含头文件#inlcude”main.h”;
(3)打开配置工程页面;在output中勾上creat hex file;在C/C++中Define处输入 STM32F40_41xxx,USE_STDPERIPH_DRIVER ;在C/C++中inlucde paths处添加所有头文件的路径,如图;
编译,通过即可;
移植ucos_ii操作系统
在ucos_II文件下建立Ports和Source文件夹
解压ucos_ii源代码文件:
(1)将 Micrium\Software\uCOS-II\Source 文件夹下的所有文件拷贝到Source文件夹;
(2)将 Micrium\Software\uCOS-II\Ports\ARM-Cortex-M4\Generic\
RealView 文件夹下的所有文件拷贝到Ports文件夹;
(3)将 Micrium\Examples\ST\STM3240G-EVAL\OS2 文件夹下的app_cfg.h 、os_cfg.h拷贝到user文件夹;
(4)在user文件夹下创建sys_cfg.c 、sys_cfg.h新文件;将文件添加进keil工程里面如图:
接下来开始修改ucos_ii源码
注释 app_cfg.h中的 #include “cpu.h”和 void App_SerPrintf (CPU_CHAR *format, …);(136-137行)
注释 os_cpu.h 中的 void OS_CPU_SysTickHandler (void); 和 void OS_CPU_
SysTickInit (INT32U cnts);(185-186行)打开文件 os_cfg.h ,修改 OS_APP_HOOKS_EN 的定义值为0(30行);修改 OS_TICKS_PER_SEC 的定义值为 1000(51行);修改 OS_MEM_EN 的定义值为1(98行);修改 OS_TMR_EN 的定义值为 1(139行);
打开文件os_cpu_c.c ,注释 #include “lib_def.h”(53行);注释 #if (OS_CPU_ARM_FP_EN == DEF_ENABLED) 与 #endif之间的所有内容(317-354行);注释 OS_CPU_SysTickHandler 整个函数内容(461-473行);注释 OS_CPU_SysTickInit 整个函数内容(488-507行);
打开文件startup_stm32f40_41xxx.s 文件,将所有的 PendSV_Handler 替换成 OS_CPU_PendSVHandler (共3处,替换前需要将文件的只读属性去掉才能够修改文件);
打开stm32f4xx_it.c文件,添加头文件 #include “ucos_ii.h”;找到 SysTick_Handler 函数(146行),添加以下程序:
void SysTick_Handler(void)
{
OSIntEnter();
OSTimeTick();
OSIntExit();
}
- 打开 sys_cfg.c 和 sys_cfg.h,编写一些函数:
sys_cfg.c:
#include "stm32f4xx.h"
#include "os_cfg.h"
#include "sys_cfg.h"
/***
* 函数名称 : OS_CPU_SysTickInit();
*
* 函数描述 : 滴答定时器设定;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
void OS_CPU_SysTickInit(void)
{
RCC_ClocksTypeDef rcc_clocks;
/* 获取系统时钟 */
RCC_GetClocksFreq(&rcc_clocks);
/* 设置滴答定时器溢出计数值 */
SysTick_Config(rcc_clocks.HCLK_Frequency / OS_TICKS_PER_SEC);
}
void Sys_Config(void)
{
}
sys_cfg.h:
#ifndef SYS_CFG_H
#define SYS_CFG_H
void OS_CPU_SysTickInit(void);
void Sys_Config(void);
#endif
至此为止,ucos_ii的源码修改结束,接下来就是ucos_ii初始化和任务的创建;
ucos_ii初始化
- 修改main.c文件,添加相关头文件,添加任务相关宏定义,修改main函数如下:
#include "app_cfg.h"
#include "sys_cfg.h"
#include "ucos_ii.h"
#include "task_start.h"
#define APP_TASK_START_PRIO 10u
#define APP_TASK_START_STK_SIZE 128u
static OS_STK App_TaskStartStk[APP_TASK_START_STK_SIZE];
int main(void)
{
/* stm32系统初始化配置 */
Sys_Config();
/* ucos_ii初始化 */
OSInit();
/* 创建ucos_ii初始化任务 TASK_START */
OSTaskCreateExt(APP_TASK_START,
(void *)0,
(OS_STK *)&App_TaskStartStk
[APP_TASK_START_STK_SIZE - 1],
(INT8U )APP_TASK_START_PRIO,
(INT16U )APP_TASK_START_PRIO,
(OS_STK *)&App_TaskStartStk[0],
(INT32U )APP_TASK_START_STK_SIZE,
(void *)0,
(INT16U)(OS_TASK_OPT_STK_CHK |
OS_TASK_OPT_STK_CLR));
/* 开始 ucos_ii */
OSStart();
return 0;
}
- 在task文件夹下创建文件 task_start.c 和 task_start.h,并添加进keil工程,打开文件添加头文件,宏定义,以及初始任务:
task_start.c:
#include "app_cfg.h"
#include "sys_cfg.h"
#include "ucos_ii.h"
#include "stm32f4xx.h"
#include "task_led.h"
#define APP_TASK_USER_GREEN_PRIO 6u
#define APP_TASK_USER_RED_PRIO 8u
#define APP_TASK_USER_STK_SIZE 128u
static OS_STK App_TaskGreenStk[APP_TASK_USER_STK_SIZE];
static OS_STK App_TaskRedStk[APP_TASK_USER_STK_SIZE];
static void APP_TASK_GREEN (void *p_arg);
static void APP_TASK_RED (void *p_arg);
/***
* 函数名称 : APP_TASK_START();
*
* 函数描述 : ucos_ii 初始化任务;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
void APP_TASK_START(void *p_arg)
{
OS_CPU_SR cpu_sr;
(void)p_arg;
/* 配置滴答定时器,此函数调用位置为第一个创建任务开头 */
OS_CPU_SysTickInit();
/* 关中断 */
OS_ENTER_CRITICAL();
/* 创建绿灯任务 */
OSTaskCreateExt(APP_TASK_GREEN,
(void *)0,
(OS_STK *)&App_TaskGreenStk
[APP_TASK_USER_STK_SIZE - 1],
(INT8U )APP_TASK_USER_GREEN_PRIO,
(INT16U )APP_TASK_USER_GREEN_PRIO,
(OS_STK *)&App_TaskGreenStk[0],
(INT32U )APP_TASK_USER_STK_SIZE,
(void *)0,
(INT16U )(OS_TASK_OPT_STK_CHK |
OS_TASK_OPT_STK_CLR));
/* 创建红灯任务 */
OSTaskCreateExt(APP_TASK_RED,
(void *)0,
(OS_STK *)&App_TaskRedStk
[APP_TASK_USER_STK_SIZE - 1],
(INT8U )APP_TASK_USER_RED_PRIO,
(INT16U )APP_TASK_USER_RED_PRIO,
(OS_STK *)&App_TaskRedStk[0],
(INT32U )APP_TASK_USER_STK_SIZE,
(void *)0,
(INT16U )(OS_TASK_OPT_STK_CHK |
OS_TASK_OPT_STK_CLR));
/* 删除任务自身 */
OSTaskDel(OS_PRIO_SELF);
/* 关中断 */
OS_EXIT_CRITICAL();
}
task_start.h:
#ifndef TASK_START_H
#define TASK_START_H
void APP_TASK_START(void *p_arg);
#endif
ucos_ii建立用户任务
- 首先需要初始化stm32的GPIO口,在sys_cfg.c中修改和添加下列函数:
/***
* 函数名称 : GPIO_Configuration();
*
* 函数描述 : GPIO初始化配置;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
static void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_NOPULL;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_Init(GPIOG, &GPIO_InitStructure);
/* 关闭所有灯 */
GPIO_ResetBits(GPIOD,GPIO_Pin_7);
GPIO_ResetBits(GPIOG,GPIO_Pin_9);
}
/***
* 函数名称 : Sys_Config();
*
* 函数描述 : 系统初始化配置;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
void Sys_Config(void)
{
/* 配置GPIO口 */
GPIO_Configuration();
}
- 在task文件夹下创建文件 task_led.c 和 task_led.h,并添加进keil工程,打开文件添加led任务函数:
task_led.c:
#include "stm32f4xx.h"
#include "ucos_ii.h"
#define LED_RED_OFF GPIO_ResetBits(GPIOD,GPIO_Pin_7);
#define LED_RED_ON GPIO_SetBits(GPIOD,GPIO_Pin_7);
#define LED_GREEN_OFF GPIO_ResetBits(GPIOG,GPIO_Pin_9);
#define LED_GREEN_ON GPIO_SetBits(GPIOG,GPIO_Pin_9);
/***
* 函数名称 : APP_TASK_GREEN();
*
* 函数描述 : 绿灯任务;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
void APP_TASK_GREEN(void *p_arg)
{
for (;;)
{
/* 红灯亮 */
LED_GREEN_ON;
/* 延时0.5s,并任务切换 */
OSTimeDly(500);
/* 红灯灭 */
LED_GREEN_OFF;
/* 延时0.5s,并任务切换 */
OSTimeDly(500);
}
}
/***
* 函数名称 : APP_TASK_RED();
*
* 函数描述 : 红灯任务;
*
* 传递值 : 无;
*
* 返回值 : 无;
*
**/
void APP_TASK_RED(void *p_arg)
{
for (;;)
{
/* 红灯亮 */
LED_RED_ON;
/* 延时0.25s,并任务切换 */
OSTimeDly(250);
/* 红灯灭 */
LED_RED_OFF;
/* 延时0.25s,并任务切换 */
OSTimeDly(250);
}
}
task_led.h:
#ifndef TASK_LED_H
#define TASK_LED_H
void APP_TASK_GREEN (void *p_arg);
void APP_TASK_RED (void *p_arg);
#endif
至此,移植工作全部完成,编译,下载;成功后会发现两个led闪烁,就相当于两个led灯的任务相互交替进行,互不影响;
小结:每个裸板移植 ucos_ii 方法都是想通的,整理觉得移植ucos_ii比较重要的几个点:
- 滴答定时器的配置,包括滴答定时器的初始化配置其计数值,以及滴答定时器溢出中断中函数的调用;
- 任务调度,在每个创建的任务中必须使用延时函数、挂起函数或者删除函数,才能使多个任务进行相互切换,调用的以上函数最终都会调用用汇编写的任务切换函数 OSCtxSw() ,所以任务切换相关代码也是重要的一部分;
总结:从移植 ucos_ii 到移植 lwip 经过了很漫长的过程,终于也算移植成功了,所以先写了对 ucos_ii 的移植,之后再在此基础上再写 lwip 的移植,共勉