Step1: FreeRTOS的获取
下载完成后,解压后,可以得到以下文件目录列表:
各文件解释:
FreeRTOS文件一览:
portable文件夹里面的东西就是连接软件层面的FreeRTOS操作系统和硬件层面的芯片的桥梁。打开portable文件夹后,可以看到FreeRTOS针对不同的芯片架构和不同编译器提供了不同的移植文件。
主要关注以上三个文件夹,其中Keil文件夹是一个套娃式文件夹,直接转到了RVDS。主要就是提供了这些内核芯片的移植文件。需要注意的是:STM32F103C8T6要移植的就是ARM_CM3。
MemMang文件夹是FreeRTOS提供的内存管理文件。
暂时不做深入讨论,直接移植heap_4.c
Step2: STM32F103C8T6 + Keil + FreeRTOS的配置
1.在平时常规的例程中挑选一个简单的,这里挑选了一个Example_WS2812B的例程。在这个例程的文件夹里新建一个FreeRTOS的文件夹。
2.将FreeRTOS的源代码添加到这个新建的文件夹中。
值得一提的是,由于Portable文件中,我们只需要特定的文件,所以只保留以下几个文件夹。
3.打开例程,添加文件分组。
STARTUP文件组不用更改。(其中的文件具体参考stm32f103c8t6新建一个工程一节)
CMSIS文件组需要检查是否已经添加了stm32f10x.h和stm32f10x.c。(其中的文件具体参考stm32f103c8t6新建一个工程一节)
FWLIB文件组不用更改。(其中的文件具体参考stm32f103c8t6新建一个工程一节)
USER文件组不用更改。(其中的文件具体参考stm32f103c8t6新建一个工程一节)
新建FreeRTOS_CORE文件组。文件组添加的文件为FreeRTOS文件下一级目录的.c文件。
新建FreeRTOS_PORTABLE文件组。文件组添加的文件为ARM_CM3内核文件夹下的port.c文件以及内存管理文件heap_4.c。
4.打开魔法棒,添加编译路径。
6.然后直接编译工程师,基本上都会提示缺失一个”FreeRTOSConfig.h“文件。然后就可以到上述所说的demo文件夹中找到对应平台文件夹内的”FreeRTOSConfig.h“文件。我们copy以下这个文件。
将”FreeRTOSConfig.h“文件放到FreeRTOS文件夹里的include文件夹中。
7.修改工程配置。
修改堆栈大小:
修改中断函数名:将以下三个中断函数注释掉。
增加FreeRTOS中断
至此,所有的移植工作都可以完成啦。下面是例程源码展示。
#include <stm32f10x.h>
#include "stdio.h"
#include "stdlib.h"
#include "WS2812B.h"
#include "FreeRTOS.h"
#include "task.h" //必须添加这个头文件,否则任务创建等函数会报错
//LED_GREEN PB14
#define LED_GREEN_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_GREEN_PORT GPIOB
#define LED_GREEN_PIN GPIO_Pin_14
#define LED_GREEN_ON GPIO_ResetBits(LED_GREEN_PORT, LED_GREEN_PIN);
#define LED_GREEN_OFF GPIO_SetBits(LED_GREEN_PORT, LED_GREEN_PIN);
//LED_RED PB15
#define LED_RED_GPIO_CLK RCC_APB2Periph_GPIOB
#define LED_RED_PORT GPIOB
#define LED_RED_PIN GPIO_Pin_15
#define LED_RED_ON GPIO_ResetBits(LED_RED_PORT, LED_RED_PIN);
#define LED_RED_OFF GPIO_SetBits(LED_RED_PORT, LED_RED_PIN);
#define START_TASK_PRIO 1
#define START_STK_SIZE 128
TaskHandle_t StartTask_Handler;
void start_task(void *pvParameters);
#define LED_GREEN_TASK_PRIO 2
#define LED_GREEN_STK_SIZE 50
TaskHandle_t LedGreenTask_Handler;
void led_green_task(void *pvParameters);
#define LED_RED_TASK_PRIO 3
#define LED_RED_STK_SIZE 50
TaskHandle_t LedRedTask_Handler;
void led_red_task(void *pvParameters);
#define WS2812B_TASK_PRIO 4
#define WS2812B_STK_SIZE 128
TaskHandle_t Ws2812bTask_Handler;
void ws2812b_task(void *pvParameters);
void LEDS_GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(LED_GREEN_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = LED_GREEN_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LED_GREEN_PORT, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(LED_RED_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = LED_RED_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LED_RED_PORT, &GPIO_InitStructure);
LED_GREEN_OFF;
LED_RED_OFF;
}
/*常规配置:将一些下载调试口配置为普通IO口*/
void OS_INIT_Configuration(void)
{
PWR->CR |= 1<<8;
RCC->BDCR &= 0XFFFFFFFE;
BKP->CR &= 0XFFFFFFFE;
PWR->CR &= 0XFFFFFEFF;
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
DBGMCU->CR &= 0XFFFFFFDF;
}
int main(void)
{
OS_INIT_Configuration(); //这里需要特别注意,自己设置的RCC()也就是时钟配置要注释掉,否则会和FreeRTOS的时钟设置冲突
LEDS_GPIO_INIT();
WS2812B_GPIO_INIT();
xTaskCreate((TaskFunction_t )start_task,
(const char* )"start_task",
(uint16_t )START_STK_SIZE,
(void* )NULL,
(UBaseType_t )START_TASK_PRIO,
(TaskHandle_t* )&StartTask_Handler);
vTaskStartScheduler();
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
xTaskCreate((TaskFunction_t )led_green_task,
(const char* )"led_green_task",
(uint16_t )LED_GREEN_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED_GREEN_TASK_PRIO,
(TaskHandle_t* )&LedGreenTask_Handler);
xTaskCreate((TaskFunction_t )led_red_task,
(const char* )"led_red_task",
(uint16_t )LED_RED_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED_RED_TASK_PRIO,
(TaskHandle_t* )&LedRedTask_Handler);
xTaskCreate((TaskFunction_t )ws2812b_task,
(const char* )"ws2812b_task",
(uint16_t )WS2812B_STK_SIZE,
(void* )NULL,
(UBaseType_t )WS2812B_TASK_PRIO,
(TaskHandle_t* )&Ws2812bTask_Handler);
vTaskDelete(StartTask_Handler);
taskEXIT_CRITICAL();
}
unsigned char led_green_on_flag = 0;
unsigned char led_red_on_flag = 0;
unsigned char ws2812b_on_flag = 0;
void led_green_task(void *pvParameters)
{
while(1)
{
if(led_green_on_flag)
{
LED_GREEN_ON;
}else
{
LED_GREEN_OFF;
}
led_green_on_flag = ~led_green_on_flag;
vTaskDelay(500);
}
}
void led_red_task(void *pvParameters)
{
while(1)
{
if(led_red_on_flag)
{
LED_RED_ON;
}else
{
LED_RED_OFF;
}
led_red_on_flag = ~led_red_on_flag;
vTaskDelay(500);
}
}
unsigned char red_num = 0;
unsigned char green_num = 0;
unsigned char blue_num = 0;
void ws2812b_task(void *pvParameters)
{
while(1)
{
if(ws2812b_on_flag)
{
red_num = rand() % 255;
green_num = rand() % 255;
blue_num = rand() % 255;
ws2812b_write_rgb(red_num, green_num, blue_num);
}else
{
ws2812b_write_rgb(0, 0, 0);
}
ws2812b_on_flag = ~ws2812b_on_flag;
vTaskDelay(500);
}
}
软件调试注意点:
Note1:#include "task.h" //必须添加这个头文件,否则任务创建等函数会报错
Note2:OS_INIT_Configuration(); //这里需要特别注意,自己设置的RCC()也就是时钟配置要注释掉,否则会和FreeRTOS的时钟设置冲突
Note3:vTaskDelay(500); 每个任务函数需要有这种可以正常进行系统任务切换的功能函数(如系统延时函数等),否则操作系统无法正常运行。