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);
}
}