FreeRTOS使用
一. 源码下载和移植文件提取
1.1 源码下载
在网站https://sourceforge.net/projects/freertos/可以找到freertos最新的源码。
1.2 移植文件提取
根据第一步,我们会得到一个freertos源码文件夹(FreeRTOSvxx.x.x),其下有很多对移植用处不大的文件夹,可以不管,直接打开FreeRTOSvxx.x.x\FreeRTOS\Source即可,我们需要的源码全在这里。
首先是Source下的这几个文件,是整个freertos的核心
然后是include文件夹下的头文件
最后是portable文件夹下的RVDS和MemMang文件夹里的文件
在RDVS文件夹中选择使用的芯片类别,比如我在F103上移植的,那么我就选择ARM_CM3文件夹下的文件。
打开也就是这些文件
MemMang中是与内存相关的文件,根据自己需要选择,比如参考别人的移植工程,我就选择heap_4.c
以上就是需要从源码中提取出来的全部文件,我们可以全部复制出来放到一个方便操作的文件夹,当然就这样也没有任何问题。
二、移植到工程中
2.1 添加源码到工程
这一步跟建立工程时新建分组一样,新建一个freertos的分组再把刚刚的.c文件添加进去,然后把.h文件路径包含到工程就行了
当然,就这样是肯定不行的,我们还要添加一些移植必要的文件和修改原来的工程文件。
2.2 移植配置文件
对于一块从未接触过的芯片,移植操作系统时往往会找网上的例程,但是请注意,freertos可以用STM32CubeMX直接生成啊,那些必要的配置文件直接复制就行了。
我们使用STM32CubeMX选择自己使用的芯片新建一个激活freertos的工程,然后打开工程文件价下的freertos Source文件夹。
发现这和我们刚刚下载的源码几乎一样,是的,确实几乎一样,但是它多了一个CMSIS_RTOS文件夹,这里面的cmsis_os.c和cmsis_os.h是使用大量条件语句编程实现的操作系统API调用文件,调用这两个文件里的函数可以在很大程度上脱离繁琐的freertosAPI调用,具体实现查看源码或者百度CMSIS_RTOS就能懂了,不做过多解释,我们只管快速使用。
然后我们在打开工程文件下的inc文件夹,这有一个至关重要的文件
全部的移植文件都在这里了,我们只要添加进工程就行了,接下来的事情就是把移植后的错误改正。
2.3 移植错误排除
我直接使用的CubeMX生成的配置文件及源码,现在移植到自己建立的标准库工程,会有很多的错误,需要一一修改。
删除FreeRTOSConfig.h中的main.h
删除cmsis_os.c中的cmsis_armcc.h
在cmsis_os.c中添加如下函数
修改cmsis_os.c中的osMailAlloc函数如下
修改cmsis_os.c中的osMailCreate
注释掉stm32f10x_it.c中的两个中断函数
滴答时钟初始化
滴答时钟中断
至此,所有移植任务就完成了,再次编译
三、操作系统的使用
3.1 任务创建
任务句柄
osThreadId led0TaskHandle;
osThreadId led1TaskHandle;
任务函数
void Led0Task(void const * argument);
void Led1Task(void const * argument);
创建任务
osThreadDef(led0Task, Led0Task, osPriorityNormal, 0, 128);
osThreadDef(led1Task, Led1Task, osPriorityNormal, 0, 128);
led0TaskHandle = osThreadCreate(osThread(led0Task), NULL);
led1TaskHandle = osThreadCreate(osThread(led1Task), NULL);
任务代码
void Led0Task(void const * argument)
{
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void Led1Task(void const * argument)
{
while(1)
{
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
3.2 信号量使用
信号量句柄
osSemaphoreId semaHandle;
osSemaphoreDef(se);
信号量任务
void SemRecvTask(void const * argument);
创建信号量
semaHandle = osSemaphoreCreate(osSemaphore(se), 1);
任务代码
void SemRecvTask(void const * argument)
{
osStatus sem;
while(1)
{
sem = (osStatus)osSemaphoreWait(semaHandle, osWaitForever);
if(sem == osOK)
{
/*
**TODO
*/
}
osSemaphoreRelease(semaHandle);
osDelay(500);
}
}
3.3 信号使用
设置信号
void SingalSendTask(void const * argument)
{
uint16_t cnt = 0;
while(1)
{
++cnt;
if(cnt % 9 == 0)
{
printf("task led1 running times: %d\r\n",cnt);
osSignalSet(led0TaskHandle,0x04);
}
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
等待信号
void SingalRecvTask(void const * argument)
{
osEvent event;
while(1)
{
event = osSignalWait(0x04,100);
if(event.status == osEventSignal)
{
if(event.value.signals &0x04)
{
printf("lend0 recv val:%d\r\n",event.value.signals);
}
}
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
3.4 互斥信号量使用
互斥信号量句柄
osMutexId mutexHandle;
osMutexDef(MUTEX);
互斥信号量任务
void Mutex1RecvTask(void const * argument);
void Mutex2RecvTask(void const * argument);
创建互斥信号量
mutexHandle = osMutexCreate(osMutex(MUTEX));
任务代码
void Mutex1RecvTask(void const * argument)
{
osStatus err;
while(1)
{
err = osMutexWait(mutexHandle,osWaitForever);
if(osOK == err)
{
printf("led0 recv mutexsem!\r\n");
}
err = osMutexRelease(mutexHandle);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void Mutex2RecvTask(void const * argument)
{
osStatus err;
while(1)
{
err = osMutexWait(mutexHandle,osWaitForever);
if(osOK == err)
{
printf("led1 recv mutexsem!\r\n");
}
err = osMutexRelease(mutexHandle);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(200);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(200);
}
}
3.5 消息队列使用
消息队列句柄
osMessageQId msgHandle;
osMessageQDef(MSG, 1, uint8_t*); //发送消息为指针(结构体指针也行)
//osMessageQDef(MSG, 1, uint16_t);//发送整形数值
消息队列任务
void MsgQSendTask(void const * argument);
void MsgQRecvTask(void const * argument);
创建消息队列
msgHandle = osMessageCreate(osMessageQ(MSG),NULL);
任务代码
void MsgQSendTask(void const * argument)
{
uint16_t cnt = 0;
osStatus err;
while(1)
{
++cnt;
if(cnt % 9 == 0)
{
err = osMessagePut(msgHandle, (uint32_t)"say", osWaitForever);
if(osOK != err)
{
printf("send error\r\n");
}
else
{
printf("send ok\r\n");
}
//发送整型
//osMessagePut(msgHandle, 5, osWaitForever);
}
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void MsgQRecvTask(void const * argument)
{
osEvent event;
while(1)
{
event = osMessageGet(msgHandle, osWaitForever);
if(event.status == osEventMessage)
{
if(event.def.message_id == msgHandle)
{
printf("lend1 recv val:%s\r\n",(uint8_t *)event.value.p);
}
}
//只有收到消息后才执行
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
3.6 消息邮箱使用
消息邮箱句柄
osMailQId mailHandle;
osMailQDef(MAIL, 1, uint16_t);
消息邮箱任务
void MailSendTask(void const * argument);
void MailRecvTask(void const * argument);
创建消息邮箱
mailHandle = osMailCreate(osMailQ(MAIL),NULL);
任务代码
void MailSendTask(void const * argument)
{
uint16_t cnt = 0;
osStatus err;
while(1)
{
++cnt;
if(cnt % 9 == 0)
{
err = osMailPut(mailHandle, (uint8_t *)"say hi");
if(osOK != err)
{
printf("send error\r\n");
}
else
{
printf("send ok\r\n");
}
}
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void MailRecvTask(void const * argument)
{
osEvent event;
while(1)
{
event = osMailGet(mailHandle, osWaitForever);
if(event.status == osEventMail)
{
if(event.def.mail_id == mailHandle)
{
printf("lend1 recv val:%s\r\n",(uint8_t *)event.value.p);
}
}
//接收到邮箱后才执行
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
3.7 软件定时器使用
软件定时器句柄
osTimerId timHandle;
osTimerDef(TIM, timcCallbackFunction);
回调函数
static void timcCallbackFunction()
{
// printf("i am timer\r\n");
LED1 = !LED1;
}
创建软件定时器
timHandle = osTimerCreate(osTimer(TIM), osTimerPeriodic, NULL);
任务代码
void TimTask(void const * argument)
{
uint16_t cnt = 0;
osTimerStart(timHandle, 100);
while(1)
{
++cnt;
if(cnt == 10)
{
osTimerStop(timHandle);
}
if(cnt == 20)
{
osTimerStart(timHandle, 300);
}
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
四、参考代码
4.1 main.c
#include "sys.h"
#include "usart.h"
#include "led.h"
#include "delay.h"
#include "cmsis_os.h"
#include "FreeRTOS.h"
int main(void)
{
clock_init();
uart_init(115200);
LED_Init();
MX_FREERTOS_Init();
osKernelStart();
}
4.2 freertos.c
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"
#include "stm32f10x.h"
#include "usart.h"
#include "stdio.h"
#include "led.h"
//优先级
osPriority prio;
//程序运行状态
osThreadState taskstate;
//任务句柄
osThreadId defaultTaskHandle;
osThreadId led0TaskHandle;
osThreadId led1TaskHandle;
osThreadId printTaskHandle;
//任务函数
void StartDefaultTask(void const * argument);
void Led0Task(void const * argument);
void Led1Task(void const * argument);
void PrintTask(void const * argument);
//信号量
osSemaphoreId semaHandle;
osSemaphoreDef(se);
//信号量任务
void SemSendTask(void const * argument);
void SemRecvTask(void const * argument);
//信号任务
void SingalSendTask(void const * argument);
void SingalRecvTask(void const * argument);
//消息队列
osMessageQId msgHandle;
osMessageQDef(MSG, 1, uint8_t*); //发送消息为指针(结构体指针也行)
//osMessageQDef(MSG, 1, uint16_t);//发送整形数值
//消息队列函数
void MsgQSendTask(void const * argument);
void MsgQRecvTask(void const * argument);
//消息邮箱
osMailQId mailHandle;
osMailQDef(MAIL, 1, uint16_t);
//消息邮箱函数
void MailSendTask(void const * argument);
void MailRecvTask(void const * argument);
//互斥信号量
osMutexId mutexHandle;
osMutexDef(MUTEX);
//互斥信号量函数
void Mutex1RecvTask(void const * argument);
void Mutex2RecvTask(void const * argument);
//软件定时器
static void timcCallbackFunction();
osTimerId timHandle;
osTimerDef(TIM, timcCallbackFunction);
void TimTask(void const * argument);
void MX_FREERTOS_Init(void) {
//任务初始化
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
osThreadDef(led0Task, Led0Task, osPriorityNormal, 0, 128);
osThreadDef(led1Task, Led1Task, osPriorityNormal, 0, 128);
osThreadDef(printTask, PrintTask, osPriorityNormal, 0, 128);
//创建任务
defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
led0TaskHandle = osThreadCreate(osThread(led0Task), NULL);
led1TaskHandle = osThreadCreate(osThread(led1Task), NULL);
printTaskHandle = osThreadCreate(osThread(printTask), NULL);
//创建信号量
semaHandle = osSemaphoreCreate(osSemaphore(se), 1);
if(NULL != semaHandle)
{
//printf("Sem create success!\r\n");
}
//创建消息队列
msgHandle = osMessageCreate(osMessageQ(MSG),NULL);
//创建消息邮箱
mailHandle = osMailCreate(osMailQ(MAIL),NULL);
//创建互斥信号量
mutexHandle = osMutexCreate(osMutex(MUTEX));
timHandle = osTimerCreate(osTimer(TIM), osTimerPeriodic, NULL);
}
void StartDefaultTask(void const * argument)
{
for(;;)
{
osDelay(11);
}
}
void Led0Task(void const * argument)
{
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void Led1Task(void const * argument)
{
while(1)
{
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
void PrintTask(void const * argument)
{
while(1)
{
osDelay(100);
}
}
/*软件定时器使用
**实现软件精确延时
*/
static void timcCallbackFunction()
{
// printf("i am timer\r\n");
LED1 = !LED1;
}
void TimTask(void const * argument)
{
uint16_t cnt = 0;
osTimerStart(timHandle, 100);
while(1)
{
++cnt;
if(cnt == 10)
{
osTimerStop(timHandle);
}
if(cnt == 20)
{
osTimerStart(timHandle, 300);
}
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
/*互斥信号量
**保证一个资源在一个时刻只能由一个任务访问
**和信号量使用方式一样
*/
void Mutex1RecvTask(void const * argument)
{
osStatus err;
while(1)
{
err = osMutexWait(mutexHandle,osWaitForever);
//err = osSemaphoreWait(semaHandle, osWaitForever);
if(osOK == err)
{
printf("led0 recv mutexsem!\r\n");
}
err = osMutexRelease(mutexHandle);
//err = osSemaphoreRelease(semaHandle);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void Mutex2RecvTask(void const * argument)
{
osStatus err;
while(1)
{
err = osMutexWait(mutexHandle,osWaitForever);
//err = osSemaphoreWait(semaHandle, osWaitForever);
if(osOK == err)
{
printf("led1 recv mutexsem!\r\n");
}
err = osMutexRelease(mutexHandle);
//err = osSemaphoreRelease(semaHandle);
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(200);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(200);
}
}
/*消息邮箱
**和消息队列使用十分类似
***/
void MailSendTask(void const * argument)
{
uint16_t cnt = 0;
osStatus err;
while(1)
{
++cnt;
if(cnt % 9 == 0)
{
err = osMailPut(mailHandle, (uint8_t *)"say hi");
if(osOK != err)
{
printf("send error\r\n");
}
else
{
printf("send ok\r\n");
}
}
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void MailRecvTask(void const * argument)
{
osEvent event;
while(1)
{
event = osMailGet(mailHandle, osWaitForever);
if(event.status == osEventMail)
{
if(event.def.mail_id == mailHandle)
{
printf("lend1 recv val:%s\r\n",(uint8_t *)event.value.p);
}
}
//接收到邮箱后才执行
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
/*消息队列
**主要就是掌握这put和get两个函数
**以及看下这个结构体,如何取出消息来
typedef struct {
osStatus status; ///< status code: event or error information
union {
uint32_t v; ///< message as 32-bit value
void *p; ///< message or mail as void pointer
int32_t signals; ///< signal flags
} value; ///< event value
union {
osMailQId mail_id; ///< mail id obtained by \ref osMailCreate
osMessageQId message_id; ///< message id obtained by \ref osMessageCreate
} def; ///< event definition
} osEvent;
***/
void MsgQSendTask(void const * argument)
{
uint16_t cnt = 0;
osStatus err;
while(1)
{
++cnt;
if(cnt % 9 == 0)
{
err = osMessagePut(msgHandle, (uint32_t)"say", osWaitForever);
if(osOK != err)
{
printf("send error\r\n");
}
else
{
printf("send ok\r\n");
}
//发送整型
//osMessagePut(msgHandle, 5, osWaitForever);
}
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void MsgQRecvTask(void const * argument)
{
osEvent event;
while(1)
{
event = osMessageGet(msgHandle, osWaitForever);
if(event.status == osEventMessage)
{
if(event.def.message_id == msgHandle)
{
printf("lend1 recv val:%s\r\n",(uint8_t *)event.value.p);
}
}
//只有收到消息后才执行
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
/*信号发送和接收
**int32_t osSignalSet (osThreadId thread_id, int32_t signal);
**thread_id:发送到哪个任务
**signal :信号值,用二进制表示
**osEvent osSignalWait (int32_t signals, uint32_t millisec);
**signals:信号值,跟平时写的那种flag一样,不过这个可以接收多个任务发送来的信号按位判断
**millisec:等待延时
*/
void SingalSendTask(void const * argument)
{
osEvent event;
while(1)
{
event = osSignalWait(0x04,100);
if(event.status == osEventSignal)
{
if(event.value.signals &0x04)
{
printf("lend0 recv val:%d\r\n",event.value.signals);
}
}
osSignalSet(led1TaskHandle,0x08);
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
osDelay(200);
}
}
void SingalRecvTask(void const * argument)
{
uint16_t cnt = 0;
osEvent event;
while(1)
{
++cnt;
if(cnt % 9 == 0)
{
printf("task led1 running times: %d\r\n",cnt);
osSignalSet(led0TaskHandle,0x04);
}
event = osSignalWait(0x08,100);
if(event.status == osEventSignal)
{
if(event.value.signals &0x08)
{
printf("led1 recv val:%d\r\n",event.value.signals);
}
}
GPIO_SetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
osDelay(155);
}
}
/*信号量的接收和释放
**osSemaphoreId semaHandle;//创建句柄
**osSemaphoreDef(se); //定义
**semaHandle = osSemaphoreCreate(osSemaphore(se), 1);创建信号量
**osSemaphoreRelease(semaHandle);释放到另一个任务
**osSemaphoreWait(semaHandle, osWaitForever);接收
*/
void SemRecvTask(void const * argument)
{
osStatus sem;
while(1)
{
sem = (osStatus)osSemaphoreWait(semaHandle, osWaitForever);
if(sem == osOK)
{
/*
**TODO
*/
//printf("get it %d\r\n", (uint16_t)semaHandle);
}
osSemaphoreRelease(semaHandle);
osDelay(500);
}
}