我们在stm32f103c8t6单片机上验证RTOS中任务的创建与删除,利用stm32cube进行RTOS的配置。在选择TIM2当做RTOS的时钟,裸机的时钟源默认是 SysTick,但是开启 FreeRTOS 后,FreeRTOS会占用 SysTick (用来生成1ms 定时,用于任务调度),所以需要需要为其他总线提供另外的时钟源。
验证的功能比较简单,选择V1 版本的内核完全够用。
一、验证的思路以及需要使用的函数
1.验证思路
创建 4 个任务:taskLED1,taskLED2,taskKEY1,taskKEY2。
任务要求如下:
taskLED1:间隔 500ms 闪烁 LED1;
taskLED2:间隔 1000ms 闪烁 LED2;
taskKEY1:如果 taskLED1 存在,则按下 KEY1 后删除 taskLED1 ,否则创建 taskLED1 ; taskKEY2:如果 taskLED2 正常运行,则按下 KEY2 后挂起 taskLED2 ,否则恢复 taskLED2。
2.需要用到的函数
xTaskCreate();动态方式创建任务
vTaskDelete();删除任务
osThreadSuspend(TaskLED2Handle);挂起函数, 指定任务进行挂起,挂起后这个任务将不被执行
osThreadResume(TaskLED2Handle);恢复函数, 可以将这个任务从挂起态恢复
xTaskCreate 函数原型
pvTaskCode:指向任务函数的指针,任务必须实现为永不返回(即连续循环);
pcName:任务的名字,主要是用来调试,默认情况下最大长度是16;
pvParameters:指定的任务栈的大小;
pvParameters:传递任务函数的参数;
uxPriority:任务优先级,数值越大,优先级越大;
pxCreatedTask:用于返回已创建任务的句柄可以被引用。
vTaskDelete 函数原型
void vTaskDelete(TaskHandle_t xTaskToDelete);
只需将待删除的任务句柄传入该函数,即可将该任务删除。当传入的参数为NULL,则代表删除任务自身(当前正在运行的任务)。
二、stm32cube的配置
SYS
RCC
GPIO
PA0对应按键1,PA1对应按键2;PB8对应LED1,PB9对应LED2
RTOS
在Tasks and Queues中配置我们的四个任务
其实就是相当于stm32cube帮我们调用了并封装了xTaskCreate()函数。
四个任务的名字分别是
TaskLED1.TaskLED2,TaskKEY1,TaskKEY2;
四个任务的入口函数名字分别是
StartLED1,StartLED2,StartKEY1,StartKEY2;
其余配置相同,如下图
三、代码部分
usart.c
加入
#include "stdio.h"
int fputc(int ch, FILE *f)
{?????
?????? unsigned char temp[1]={ch};
?????? HAL_UART_Transmit(&huart1,temp,1,0xffff);?
?????? return ch;
}
同时打开“魔术棒”,勾选Use MicroLIB,点击OK。这样就可以进行串口打印了。
freertos.c
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
/* USER CODE END Variables */
osThreadId TaskLED1Handle;
osThreadId TaskLED2Handle;
osThreadId TaskKEY1Handle;
osThreadId TaskKEY2Handle;
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
/* USER CODE END FunctionPrototypes */
void StartLED1(void const * argument);
void StartLED2(void const * argument);
void StartKEY1(void const * argument);
void StartKEY2(void const * argument);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
/* GetIdleTaskMemory prototype (linked to static allocation support) */
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize );
/* USER CODE BEGIN GET_IDLE_TASK_MEMORY */
static StaticTask_t xIdleTaskTCBBuffer;
static StackType_t xIdleStack[configMINIMAL_STACK_SIZE];
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
? *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
? *ppxIdleTaskStackBuffer = &xIdleStack[0];
? *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
? /* place for user code */
}
/* USER CODE END GET_IDLE_TASK_MEMORY */
/**
? * @brief? FreeRTOS initialization
? * @param? None
? * @retval None
? */
void MX_FREERTOS_Init(void) {
? /* USER CODE BEGIN Init */
? /* USER CODE END Init */
? /* USER CODE BEGIN RTOS_MUTEX */
? /* add mutexes, ... */
? /* USER CODE END RTOS_MUTEX */
? /* USER CODE BEGIN RTOS_SEMAPHORES */
? /* add semaphores, ... */
? /* USER CODE END RTOS_SEMAPHORES */
? /* USER CODE BEGIN RTOS_TIMERS */
? /* start timers, add new ones, ... */
? /* USER CODE END RTOS_TIMERS */
? /* USER CODE BEGIN RTOS_QUEUES */
? /* add queues, ... */
? /* USER CODE END RTOS_QUEUES */
? /* Create the thread(s) */
? /* definition and creation of TaskLED1 */
? osThreadDef(TaskLED1, StartLED1, osPriorityNormal, 0, 128);
? TaskLED1Handle = osThreadCreate(osThread(TaskLED1), NULL);
? /* definition and creation of TaskLED2 */
? osThreadDef(TaskLED2, StartLED2, osPriorityNormal, 0, 128);
? TaskLED2Handle = osThreadCreate(osThread(TaskLED2), NULL);
? /* definition and creation of TaskKEY1 */
? osThreadDef(TaskKEY1, StartKEY1, osPriorityNormal, 0, 128);
? TaskKEY1Handle = osThreadCreate(osThread(TaskKEY1), NULL);
? /* definition and creation of TaskKEY2 */
? osThreadDef(TaskKEY2, StartKEY2, osPriorityNormal, 0, 128);
? TaskKEY2Handle = osThreadCreate(osThread(TaskKEY2), NULL);
? /* USER CODE BEGIN RTOS_THREADS */
? /* add threads, ... */
? /* USER CODE END RTOS_THREADS */
}
/* USER CODE BEGIN Header_StartLED1 */
/**
? * @brief? Function implementing the TaskLED1 thread.
? * @param? argument: Not used
? * @retval None
? */
/* USER CODE END Header_StartLED1 */
void StartLED1(void const * argument)
{
? /* USER CODE BEGIN StartLED1 */
? /* Infinite loop */
? for(;;)
? {
????????????? HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
??? osDelay(1000);
? }
? /* USER CODE END StartLED1 */
}
/* USER CODE BEGIN Header_StartLED2 */
/**
* @brief Function implementing the TaskLED2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartLED2 */
void StartLED2(void const * argument)
{
? /* USER CODE BEGIN StartLED2 */
? /* Infinite loop */
? for(;;)
? {
??? HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);
??? osDelay(500);
? }
? /* USER CODE END StartLED2 */
}
/* USER CODE BEGIN Header_StartKEY1 */
/**
* @brief Function implementing the TaskKEY1 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartKEY1 */
void StartKEY1(void const * argument)
{
? /* USER CODE BEGIN StartKEY1 */
? /* Infinite loop */
? for(;;)
? {
????????????? if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
????????????? {
???????????????????? osDelay(20);
???????????????????? if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
???????????????????? {
???????????????????? printf("KEY1按下\r\n");
??????????????????????????? if(TaskLED1Handle==NULL)
??????????????????????????? {
?????????????????????????????????? printf("任务1不存在,创建任务1\r\n");
?????????????????????????????????? ?osThreadDef(TaskLED1, StartLED1, osPriorityNormal, 0, 128);
?????????? TaskLED1Handle = osThreadCreate(osThread(TaskLED1), NULL);
????????????????????????????????????????? if(TaskLED1Handle!=NULL)
???????????????????????????????????????????????? printf("任务1创建完成\r\n");
??????????????????????????? }
??????????????????????????? else
??????????????????????????? {
?????????????????????????????????? printf("删除任务1\r\n");
?????????????????????????????????? osThreadTerminate(TaskLED1Handle);
?????????????????????????????????? TaskLED1Handle=NULL;
??????????????????????????? }
???????????????????? }
?????? ????????????? while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET);
????????????? }
??? osDelay(10);
? }
? /* USER CODE END StartKEY1 */
}
/* USER CODE BEGIN Header_StartKEY2 */
/**
* @brief Function implementing the TaskKEY2 thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_StartKEY2 */
void StartKEY2(void const * argument)
{
? /* USER CODE BEGIN StartKEY2 */
?????? static int flag=0;
? /* Infinite loop */
? for(;;)
? {
?? if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)==GPIO_PIN_RESET)
????????????? {
???????????????????? osDelay(20);
???????????????????? if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)==GPIO_PIN_RESET)
???????????????????? {
???????????????????? printf("KEY2按下\r\n");
??????????????????????????? if(flag==0)
??????????????????????????? {
?????????????????????????????????? osThreadSuspend(TaskLED2Handle);
?????????????????????????????????? printf("任务2挂起\r\n");
??????????????????????????? ?????? flag=1;
??????????????????????????? }
?????????????????????????????????? else
?????????????????????????????????? {
????????????????????????????????????????? osThreadResume(TaskLED2Handle);
????????????????????????????????????????? printf("任务2已恢复\r\n");
????????????????????????????????????????? flag=0;
?????????????????????????????????? }
???????????????????? }
???????????????????? while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_1)==GPIO_PIN_RESET);
????????????? }
??? osDelay(10);
? }
? /* USER CODE END StartKEY2 */
}
要说明的是
osThreadDef(TaskLED1, StartLED1, osPriorityNormal, 0, 128);
TaskLED1Handle = osThreadCreate(osThread(TaskLED1), NULL);
这两个函数的使用是stm32cube把任务动态创建函数xTaskCreate()封装后的变形,这两个函数的出现就代表TaskLED1的函数创建完成了,这里面的参数分别对应了任务名,入口函数,优先级,参数,堆栈大小,返回已创建任务的句柄,其余的3个任务同理。
接着看
void StartLED1(void const * argument)
{
? /* USER CODE BEGIN StartLED1 */
? /* Infinite loop */
? for(;;)
? {
????????????? HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
??? osDelay(1000);
? }
? /* USER CODE END StartLED1 */
}
我们在stm32cube配置任务时,Entry function(入口函数)的名字是StartLED1。
意思就是说,我们的任务要运行的时候,需要我们进入“入口函数”才可以执行相应的命令。这里实现的是LED1每隔1000ms电平变化一次。
再看
void StartKEY1(void const * argument)
{
? /* USER CODE BEGIN StartKEY1 */
? /* Infinite loop */
? for(;;)
? {
????????????? if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
????????????? {
???????????????????? osDelay(20);//软件消除按键抖动
???????????????????? if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET)
???????????????????? {
???????????????????? printf("KEY1按下\r\n");
??????????????????????????? if(TaskLED1Handle==NULL)
??????????????????????????? {
?????????????????????????????????? printf("任务1不存在,创建任务1\r\n");
?????????????????????????????????? ?osThreadDef(TaskLED1, StartLED1, osPriorityNormal, 0, 128);
?????????? TaskLED1Handle = osThreadCreate(osThread(TaskLED1), NULL);
????????????????????????????????????????? if(TaskLED1Handle!=NULL)
???????????????????????????????????????????????? printf("任务1创建完成\r\n");
??????????????????????????? }
??????????????????????????? else
??????????????????????????? {
?????????????????????????????????? printf("删除任务1\r\n");
?????????????????????????????????? osThreadTerminate(TaskLED1Handle);
?????????????????????????????????? TaskLED1Handle=NULL;
??????????????????????????? }
???????????????????? }
???????????????????? while(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_RESET);
????????????? }
??? osDelay(10);
? }
TaskLED1Handle是我们生成TaskLED1任务时的最后一个参数,句柄。如果我们要删除该任务,就用删除任务的函数,它的参数就是该任务的句柄osThreadTerminate(TaskLED1Handle);
任务被删除后就句柄TaskLED1Handle就被赋值NULL;如果没有被删除,句柄TaskLED1Handle的值就不会为NULL。这也是为什么我们按下KEY1就可以不断地通过判断句柄的状态来实现任务的创建于删除,KEY2的操作同理。下面是程序烧录完毕后,连接串口助手按下KEY1,KEY2的情况。