本文参考自[野火EmbedFire]《RT-Thread内核实现与应用开发实战——基于STM32》,仅作为个人学习笔记。更详细的内容和步骤请查看原文(可到野火资料下载中心下载)
硬件初始化
本文将创建一个LED闪烁的线程,所以必须先进行LED的硬件配置,我所使用的开发板为正点原子STM32F103ZET6核心板,有两个LED,分别对应PB5和PE5,led的bsp文件如下:
bsp_led.h
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "stm32f10x.h"
#define LED0_GPIO_PIN GPIO_Pin_5
#define LED0_GPIO_PORT GPIOB
#define LED0_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED1_GPIO_PIN GPIO_Pin_5
#define LED1_GPIO_PORT GPIOE
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOE
#define ON 1
#define OFF 0
// \ C语言里面叫续行符,后面不能有任何的东西
#define LED0(a) if(a) \
GPIO_ResetBits(LED0_GPIO_PORT, LED0_GPIO_PIN); \
else GPIO_SetBits(LED0_GPIO_PORT, LED0_GPIO_PIN);
#define LED1(a) if(a) \
GPIO_ResetBits(LED1_GPIO_PORT, LED1_GPIO_PIN); \
else GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
// ^ 异或,C语言的一个二进制的运算符
// 与1异或改变,与0异或不变
#define LED0_TOGGLE {LED0_GPIO_PORT->ODR ^= LED0_GPIO_PIN;}
#define LED1_TOGGLE {LED1_GPIO_PORT->ODR ^= LED1_GPIO_PIN;}
void LED_GPIO_Config(void);
#endif /* __BSP_LED_H */
bsp_led.c
// bsp :board support package 板级支持包
#include "bsp_led.h"
void LED_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(LED0_GPIO_CLK, ENABLE);
RCC_APB2PeriphClockCmd(LED1_GPIO_CLK, ENABLE);
// LED0
GPIO_InitStruct.GPIO_Pin = LED0_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED0_GPIO_PORT, &GPIO_InitStruct);
// 默认拉高
GPIO_SetBits(LED0_GPIO_PORT, LED0_GPIO_PIN);
// LED1
GPIO_InitStruct.GPIO_Pin = LED1_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStruct);
// 默认拉高
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
编写完led的底层驱动后,将LED_GPIO_Config()
添加到board.c
的rt_hw_board_init()
函数里:
void rt_hw_board_init(void)
{
#if 0
/* TODO: OS Tick Configuration */
_SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
#endif
/* 初始化 SysTick */
SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
/* 硬件 BSP 初始化统统放在这里,比如 LED,串口,LCD 等 */
// LED初始化
LED_GPIO_Config();
// 测试
//LED0(1);
//LED1(1);
/* Call components board initial (use INIT_BOARD_EXPORT()) */
#ifdef RT_USING_COMPONENTS_INIT
rt_components_board_init();
#endif
#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)
rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());
#endif
}
定义线程栈
由于本次创建的线程为静态线程,所以有线程自己的栈空间。
// 线程栈需要 RT_ALIGN_SIZE 个字节对齐
ALIGN(RT_ALIGN_SIZE)
// 定义线程栈
static rt_uint8_t rt_led0_thread_stack[1024];
在大多数系统中需要做栈空间地址对齐,ARM体系结构中需要向4字节地址对齐。RT_ALIGN_SIZE是rtconfig.h
中的一个宏。
定义线程控制块
线程控制块是一个结构体,里面存放了线程的全部信息,通常称这个控制块为线程的身份证。
// 定义线程控制块
static struct rt_thread led0_thread;
定义线程函数
线程函数即线程入口函数,是线程的功能代码,由于线程需要不停运行,所以需要加上一个while(1)
,下面这个线程函数实现的功能是LED0以500ms的间隔进行闪烁。
/******************************************************************************
* @ 函数名 : led0_thread_entry
* @ 功 能 : LED0线程入口函数
* @ 参 数 : parameter 外部传入的参数
* @ 返回值 : 无
******************************************************************************/
static void led0_thread_entry(void *parameter)
{
while(1)
{
LED0(1);
rt_thread_delay(500); // 500个tick(500ms)
LED0(0);
rt_thread_delay(500);
}
}
初始化线程
一个静态线程的三要素是线程入口函数,线程栈和线程控制块,我们需要用rt_thread_init()
函数将这三者联系起来,使其变成一个可以工作的线程。
// 初始化一个静态线程
rt_thread_init(&led0_thread, // 线程控制块
"led0", // 线程名字
led0_thread_entry, // 线程入口函数
RT_NULL, // 入口函数参数
&rt_led0_thread_stack[0], // 线程栈起始地址
sizeof(rt_led0_thread_stack), // 线程栈大小
5, // 线程优先级
10); // 线程时间片
关于线程的优先级,一定要填rtconfig.h
中规定的范围内的数字(在昨天移植系统时,我把线程优先级的最大值设置成了8),如果线程初始化时将优先级设置过大,线程将不会工作。
启动线程
线程初始化后,线程只是处于初始态(RT_THREAD_INIT),要想让线程进入就绪态(RT_THREAD_READY),还需使用rt_thread_startup()
函数。
// 开启线程调度
rt_thread_startup(&led0_thread);
实验效果
完整代码
所有静态线程相关函数我都放在main.c,下面代码不包括底层驱动代码。
#include "board.h"
#include "rtthread.h"
// 线程栈需要 RT_ALIGN_SIZE 个字节对齐
ALIGN(RT_ALIGN_SIZE)
// 定义线程栈
static rt_uint8_t rt_led0_thread_stack[1024];
// 定义线程控制块
static struct rt_thread led0_thread;
/******************************************************************************
* @ 函数名 : led0_thread_entry
* @ 功 能 : LED0线程入口函数
* @ 参 数 : parameter 外部传入的参数
* @ 返回值 : 无
******************************************************************************/
static void led0_thread_entry(void *parameter)
{
while(1)
{
LED0(1);
rt_thread_delay(500); // 500个tick(500ms)
LED0(0);
rt_thread_delay(500);
}
}
int main(void)
{
// 硬件初始化和RTT的初始化已经在component.c中的rtthread_startup()完成
// 初始化一个静态线程
rt_thread_init(&led0_thread, // 线程控制块
"led0", // 线程名字
led0_thread_entry, // 线程入口函数
RT_NULL, // 入口函数参数
&rt_led0_thread_stack[0], // 线程栈起始地址
sizeof(rt_led0_thread_stack), // 线程栈大小
5, // 线程优先级
10); // 线程时间片
// 开启线程调度
rt_thread_startup(&led0_thread);
}