终于完成从裸机到RTOS的转变

从裸机到RTOS的进化

最近终于在裸机工程中移植了RTThread这个实时操作系统,这是迈向嵌入式的第一步,在这里要感谢野火的《RT-Thread 内核实现与应用开发实战指南》,这本书简单细致的向我们介绍了RTThread的内核,以及对裸机程序如何移植到rtthread的操作环境中。在大型一点的项目中,RTOS给了我们希望,让我们不必再让阻塞延时来降低我们的工作效率,同时模块化任务的管理,信号量,互斥量的处理可以让我们减少bug。消息队列,邮箱,双向链表是我们对数据处理的有效手段,内存管理解决了RAM空间不足,可能产生碎片等问题。中断管理解决了中断对线程上下文所产生的影响。

RTThread的裸机移植方法

方法有两种:

  1. 安装 RT-Thread Package
    使用keil时添加rtthread的3.0.3版本
    终于完成从裸机到RTOS的转变_第1张图片
  2. 添加 RT-Thread Package源码到裸机工程根目录
    RT-dhread 文件夹内容组成
    在这里插入图片描述
    终于完成从裸机到RTOS的转变_第2张图片
    2.2.1 拷贝 rtconfig.h 文件到 user 文件夹
    将 RT-Thread/3.0.3/bsp 文件夹下面的 rtconfig.h 配套文件拷贝到工程根目录下面的 user
    文件夹, 等下我们需要对这个文件进行修改。
    用户可以通过修改这个 RT-Thread 内核的配置头文件来裁剪 RT-Thread 的功能,所以
    我们把它拷贝一份放在 user 这个文件夹下面。 user,见名之义我们就可以知道里面存放的
    文件都是用户自己编写的。
    2.2.1 拷贝 board.c 文件到 user 文件夹
    将 RT-Thread/3.0.3/bsp 文件夹下面的 board.c 配套文件拷贝到工程根目录下面的 user 文
    件夹, 等下我们需要对这个 board.c 进行修改。

用户在使用 RT-Thread 的时候,用户只需要修改 board.c 和 rtconfig.h 这两个文件的内容即可,其它
文件我们不需要改动。 如果为了减小工程的大小, bsp 文件夹下面除了 board.c 和 rtconfig.h
这两个文件要保留外,其它的统统可以删除。

  1. 新建 rtt/source 和 rtt/ports 组
    接下来我们在开发环境里面新建 rtt/source 和 rtt/ports 两个组文件夹,其中 rtt/source 用
    于存放 src 文件夹的内容, rtt/ports 用于存放 libcpu/arm/cortex-m? 文件夹的内容,“?”表
    示 3、 4 或者 7,具体选择哪个得看你使用的是STM32的哪个型号的主控。
  2. 源码添加完毕之后,具体见图
    终于完成从裸机到RTOS的转变_第3张图片
  3. 接下来就是进行魔法棒的配置了。
  4. 修改 rtconfig.h
    rtconfig.h 是直接从 RT-Thread/3.0.3/bsp 文件夹下面拷贝过来的,该头文件对裁剪整个
    RT-Thread 所需的功能的宏均做了定义,有些宏定义被使能,有些宏定义被失能,一开始
    我们只需要配置最简单的功能即可。 要想随心所欲的配置 RT-Thread 的功能,我们必须对
    这些宏定义的功能有所掌握,下面我们先简单的介绍下这些宏定义的含义,然后再对这些
    宏定义进行修改。
/* RT-Thread config file */

#ifndef __RTTHREAD_CFG_H__
#define __RTTHREAD_CFG_H__


#define RT_THREAD_PRIORITY_MAX  32 //RT_THREAD_PRIORITY_MAX 这个宏表示 RT-Thread 支持多少个优先级,取值范围为 8~256,默认为 32。

#define RT_TICK_PER_SECOND	1000 //RT_TICK_PER_SECOND 表示操作系统每秒钟有多少个 tick,tick 即是操作系统的时钟周期,默认为 1000,即操作系统的时钟周期 tick 等于 1ms。

#define RT_ALIGN_SIZE   4 //RT_ALIGN_SIZE 这个宏表示 CPU 处理的数据需要多少个字节对齐,默认为 4 个字节。

#define RT_NAME_MAX  10 //RT_NAME_MAX 这个宏表示内核对象名字的最大长度, 取值范围为 2~16, 默认为 8。

#define RT_USING_COMPONENTS_INIT //使用 RT-Thread 组件初始化,默认使能。

#define RT_USING_USER_MAIN //使用用户 main 函数,默认打开。

#define RT_MAIN_THREAD_STACK_SIZE     512 //main 线程栈大小,取值范围为 1~4086,单位为字节,默认为512。

#define RT_DEBUG_INIT 0 //调试配置。包括了内核调试配置,组件调试配置和线程栈溢出检测,目前全部关闭。

#define RT_USING_TIMER_SOFT         0 //软件定时器配置,目前关闭,不使用软件定时器。
#if RT_USING_TIMER_SOFT == 0
#undef RT_USING_TIMER_SOFT
#endif

#define RT_TIMER_THREAD_PRIO		4

#define RT_TIMER_THREAD_STACK_SIZE	512

#define RT_TIMER_TICK_PER_SECOND	100

#define RT_USING_SEMAPHORE //信号量使能

#define RT_USING_MAILBOX //邮箱使能

#define RT_USING_HEAP //堆使能

#define RT_USING_SMALL_MEM //小内存使能

#define RT_USING_CONSOLE //控制台配置,重映射串口使能

#define RT_CONSOLEBUF_SIZE          128 //重映射串口内存大小

#define RT_CONSOLE_DEVICE_NAME      "uart2" //重映射串口号



#if defined(RTE_FINSH_USING_MSH)//FINSH 配置。
#define RT_USING_FINSH
#define FINSH_USING_MSH
#define FINSH_USING_MSH_ONLY

#define __FINSH_THREAD_PRIORITY     5
#define FINSH_THREAD_PRIORITY       (RT_THREAD_PRIORITY_MAX / 8 * __FINSH_THREAD_PRIORITY + 1)

#define FINSH_THREAD_STACK_SIZE     512

#define FINSH_HISTORY_LINES	        1

#define FINSH_USING_SYMTAB

#endif

#if defined(RTE_USING_DEVICE) //设备配置。
#define RT_USING_DEVICE
#endif

#endif

rtconfig.h 头文件对比默认功能的内容修改的不多,具体是: 注释掉头文件 RTE_Components.h、 修
改 了 RT_THREAD_PRIORITY_MAX 、 RT_TICK_PER_SECOND 和
RT_MAIN_THREAD_STACK_SIZE 这三个宏的大小。

  1. 修改 board.c
    board.c 是直接从 RT-Thread/3.0.3/bsp 文件夹下面拷贝过来的, 里面存放的是与硬件相
    关的初始化函数。
/*
 * File      : board.c
 * This file is part of RT-Thread RTOS
 * COPYRIGHT (C) 2006, RT-Thread Development Team
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rt-thread.org/license/LICENSE
 *
 * Change Logs:
 * Date           Author       Notes
 * 2017-07-24     Tanek        the first version
 */
/*初始化硬件相关头文件*/
#include "board.h"
/*初始化rtthread相关头文件*/
#include 
#include 

#if	0
/*========================================================*/
#define _SCB_BASE       (0xE000E010UL)
#define _SYSTICK_CTRL   (*(rt_uint32_t *)(_SCB_BASE + 0x0))
#define _SYSTICK_LOAD   (*(rt_uint32_t *)(_SCB_BASE + 0x4))
#define _SYSTICK_VAL    (*(rt_uint32_t *)(_SCB_BASE + 0x8))
#define _SYSTICK_CALIB  (*(rt_uint32_t *)(_SCB_BASE + 0xC))
#define _SYSTICK_PRI    (*(rt_uint8_t  *)(0xE000ED23UL))

// Updates the variable SystemCoreClock and must be called 
// whenever the core clock is changed during program execution.
extern void SystemCoreClockUpdate(void);

// Holds the system core clock, which is the system clock 
// frequency supplied to the SysTick timer and the processor 
// core clock.
extern uint32_t SystemCoreClock;

static uint32_t _SysTick_Config(rt_uint32_t ticks)
{
    if ((ticks - 1) > 0xFFFFFF)
    {
        return 1;
    }
    
    _SYSTICK_LOAD = ticks - 1; 
    _SYSTICK_PRI = 0xFF;
    _SYSTICK_VAL  = 0;
    _SYSTICK_CTRL = 0x07;  
    
    return 0;
}
/*==========================================================*/
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
#define RT_HEAP_SIZE 1024
static uint32_t rt_heap[RT_HEAP_SIZE];	// heap default size: 4K(1024 * 4)
RT_WEAK void *rt_heap_begin_get(void)
{
    return rt_heap;
}

RT_WEAK void *rt_heap_end_get(void)
{
    return rt_heap + RT_HEAP_SIZE;
}
#endif

/**
 * This function will initial your board.
 */
/**
* @brief 重映射串口 DEBUG_USARTx 到 rt_kprintf()函数
* Note: DEBUG_USARTx 是在 bsp_usart.h 中定义的宏,默认使用串口 1
* @param str:要输出到串口的字符串
* @retval 无
*
* @attention
*
*/
void rt_hw_console_output(const char *str)
{
	/* 进入临界段 */
	rt_enter_critical();

	/* 直到字符串结束 */
	while (*str!='\0')
	{
	/* 换行 */
	if (*str=='\n')
	{
	USART_SendData(DEBUG_USARTx, '\r');
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
	}

	USART_SendData(DEBUG_USARTx, *str++);
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);
	}

	/* 退出临界段 */
	rt_exit_critical();
}
/**
* @brief PVD电压监测配置函数
* Note: None
* @param None
* @retval 无
*
* @attention
*
*/
void PVD_Config(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
	/*使能 PWR 时钟 */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
	
	/* 使能 PVD 中断 */
	NVIC_InitStructure.NVIC_IRQChannel = PVD_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	/* 配置 EXTI16 线(PVD 输出) 来产生上升下降沿中断*/
	EXTI_ClearITPendingBit(EXTI_Line16);
	EXTI_InitStructure.EXTI_Line = EXTI_Line16;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_Init(&EXTI_InitStructure);
	//配置 PVD 级别 PWR_PVDLevel_2V6
	PWR_PVDLevelConfig(PWR_PVDLevel_2V6);
	/* 使能 PVD 输出 */
	PWR_PVDCmd(ENABLE);
}

//硬件初始化
static void HardWareInit(void)
{
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	ADC_DMA_ConfigInit();
	OLED_ConfigInit();
	USB_USART_Config();
	TIMx_NVIC_Configuration();
	Timer_out_ConfigInit(); 
	Digital_GPIO_Config();
	CANPro_Init();
	PVD_Config();
	IWDG_Config(IWDG_Prescaler_64 ,625);//看门狗溢出时间(1s)
}
void rt_hw_board_init()
{	
#if	0
	/* System Clock Update */
	SystemCoreClockUpdate();
	
	/* System Tick Configuration */
	_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
	/*初始化Systick*/
	SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
	
	/* 硬件 BSP 初始化统统放在这里,比如 LED,串口, LCD 等 */
	HardWareInit();
    /* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
    rt_components_board_init();
#endif

#if defined(RT_USING_CONSOLE) && defined(RT_USING_DEVICE)
	rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
#endif
    
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
    rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}

void SysTick_Handler(void)
{
	/* enter interrupt */
	rt_interrupt_enter();

	rt_tick_increase();

	/* leave interrupt */
	rt_interrupt_leave();
}

修改一: 在 user 目录下新建一个 board.h 头文件,用来包含固件库和
BSP 相关的头文件和存放 board.c 里面的函数声明.
修改二:SysTick 相关的寄存器和初始化函数统统屏蔽掉,将由固件库
文件 core_cm3/4/7 里面的替代。
修改三:SysTick 初始化函数由固件库文件 core_cm3/4/7 里面的
SysTick_Config()函数替代。

如果使用的是 HAL 库 ,则必须添加系统时钟初始化函数,这个函数在我们利用 STM32CubeMX 代码生成工具配置工程时会自动给我们生成,我们只需添加到 rt_hw_board_init()函数进行初始化即可。同时只有在使用 HAL 库时才需要添加 core_delay.c 和 core_delay.h 文件。

你可能感兴趣的:(STM32学习笔记,项目经验)