【CW32饭盒派开发板试用体验】1. 开箱啦
【CW32饭盒派开发板试用体验】2. 使用扩展板串口UART3以及scanf重定向到串口
前言
本来打算移植freeRTOS来试试的,但是移植了大半天没搞定,而且板载资源有限(8K SRAM和64K Flash),就算成功移植了OS,资源也所剩无几了,所以就决定自己写一个调用器来用,为后续的作品任务调度做准备。
任务调用使用了BTIM1(其实使用systick也是可以,只要有定时功能都OK)。实现了2ms,5ms,10ms,100ms,500ms,1000ms的任务周期性调度功能。
本文实现了基于调度器的LED周期性闪烁功能。
1 硬件资源
BTIM是基础定时器。就不做过多介绍了,可以参考用户手册。我们在使用的时候直接调用官方提供的库函数即可。
下面说一下LED接线,连接到了:
- LED2 - PA7
- LED3 - PA8
- LED4 - PC11
并且对应引脚拉低,则LED会被点亮。
2 软件编写
2.1 调度器原理介绍
调度器依靠定时器提供时基,设置了定时器时基为1ms,每1ms进入一次定时器中断,在中断中计数:
- 计满2个:进入2ms任务中指定所有注册到2ms中的函数
- 计满5个:进入5ms任务中指定所有注册到5ms中的函数
- 计满10个:进入10ms任务中指定所有注册到10ms中的函数
- 计满100个:进入100ms任务中指定所有注册到100ms中的函数
- 计满500个:进入500ms任务中指定所有注册到500ms中的函数
- 计满1000个:进入1000ms任务中指定所有注册到1000ms中的函数
调度器原理还是很简单,代码实现如下。
其中:
- Schr_SchedulerRunning是调度器函数,用于周期性调用任务函数集
- Schr_StartScheduler是启动调度器的函数,其实就是初始化定时器
- Schr_CreateTask是注册任务的函数,有两个参数:
- 第一个参数是任务函数地址
- 第二个参数是任务的执行周期
调度器使用了堆空间,对分配的任务进行存储,属于动态分配。
#include "app_scheduler.h"
#include "app_common.h"
#include <stdlib.h>
static uint32_t time_escape = 1U;
static schr_task_func_t *task_2ms_list;
static uint8_t task_2ms_cnt = 0;
static schr_task_func_t *task_5ms_list;
static uint8_t task_5ms_cnt = 0;
static schr_task_func_t *task_10ms_list;
static uint8_t task_10ms_cnt = 0;
static schr_task_func_t *task_100ms_list;
static uint8_t task_100ms_cnt = 0;
static schr_task_func_t *task_500ms_list;
static uint8_t task_500ms_cnt = 0;
static schr_task_func_t *task_1000ms_list;
static uint8_t task_1000ms_cnt = 0;
static void Schr_SchedulerRunning(void);
static void BTIM_init(void);
void Schr_Init(void)
{
}
static void Schr_SchedulerRunning(void)
{
uint8_t task_cnt;
#if (SCHR_PERIOD_2MS != 0U)
if ((time_escape % SCHR_PERIOD_2MS) == 0U)
{
for (task_cnt = 0; task_cnt < task_2ms_cnt; task_cnt++)
{
task_2ms_list[task_cnt]();
}
}
#endif
#if (SCHR_PERIOD_5MS != 0U)
if ((time_escape % SCHR_PERIOD_5MS) == 0U)
{
for (task_cnt = 0; task_cnt < task_5ms_cnt; task_cnt++)
{
task_5ms_list[task_cnt]();
}
}
#endif
#if (SCHR_PERIOD_10MS != 0U)
if ((time_escape % SCHR_PERIOD_10MS) == 0U)
{
for (task_cnt = 0; task_cnt < task_10ms_cnt; task_cnt++)
{
task_10ms_list[task_cnt]();
}
}
#endif
#if (SCHR_PERIOD_100MS != 0U)
if ((time_escape % SCHR_PERIOD_100MS) == 0U)
{
for (task_cnt = 0; task_cnt < task_100ms_cnt; task_cnt++)
{
task_100ms_list[task_cnt]();
}
}
#endif
#if (SCHR_PERIOD_500MS != 0U)
if ((time_escape % SCHR_PERIOD_500MS) == 0U)
{
for (task_cnt = 0; task_cnt < task_500ms_cnt; task_cnt++)
{
task_500ms_list[task_cnt]();
}
}
#endif
#if (SCHR_PERIOD_1000MS != 0U)
if ((time_escape % SCHR_PERIOD_1000MS) == 0U)
{
for (task_cnt = 0; task_cnt < task_1000ms_cnt; task_cnt++)
{
task_1000ms_list[task_cnt]();
}
}
#endif
time_escape ++;
if (time_escape > SCHR_PERIOD_1000MS)
{
time_escape = 1;
}
}
void Schr_StartScheduler(void)
{
BTIM_init();
}
void Schr_CreateTask(const schr_task_func_t task_func, uint32_t task_period)
{
switch (task_period)
{
case SCHR_PERIOD_2MS:
{
task_2ms_cnt++;
task_2ms_list = (schr_task_func_t*)realloc(task_2ms_list, task_2ms_cnt * sizeof(schr_task_func_t));
task_2ms_list[task_2ms_cnt-1] = task_func;
break;
}
case SCHR_PERIOD_5MS:
{
task_5ms_cnt++;
task_5ms_list = (schr_task_func_t*)realloc(task_5ms_list, task_5ms_cnt * sizeof(schr_task_func_t));
task_5ms_list[task_5ms_cnt-1] = task_func;
break;
}
case SCHR_PERIOD_10MS:
{
task_10ms_cnt++;
task_10ms_list = (schr_task_func_t*)realloc(task_10ms_list, task_10ms_cnt * sizeof(schr_task_func_t));
task_10ms_list[task_10ms_cnt-1] = task_func;
break;
}
case SCHR_PERIOD_100MS:
{
task_100ms_cnt++;
task_100ms_list = (schr_task_func_t*)realloc(task_100ms_list, task_100ms_cnt * sizeof(schr_task_func_t));
task_100ms_list[task_100ms_cnt-1] = task_func;
break;
}
case SCHR_PERIOD_500MS:
{
task_500ms_cnt++;
task_500ms_list = (schr_task_func_t*)realloc(task_500ms_list, task_500ms_cnt * sizeof(schr_task_func_t));
task_500ms_list[task_500ms_cnt-1] = task_func;
break;
}
case SCHR_PERIOD_1000MS:
{
task_1000ms_cnt++;
task_1000ms_list = (schr_task_func_t*)realloc(task_1000ms_list, task_1000ms_cnt * sizeof(schr_task_func_t));
task_1000ms_list[task_1000ms_cnt-1] = task_func;
break;
}
default:
{
break;
}
}
}
调度器的实现主要依靠了BTIM1,初始化代码以及中断服务程序如下:
static void BTIM_init(void)
{
BTIM_TimeBaseInitTypeDef BTIM_InitStruct;
__RCC_BTIM_CLK_ENABLE();
__disable_irq();
NVIC_EnableIRQ(BTIM1_IRQn);
__enable_irq();
BTIM_InitStruct.BTIM_Mode = BTIM_Mode_TIMER;
BTIM_InitStruct.BTIM_OPMode = BTIM_OPMode_Repetitive;
BTIM_InitStruct.BTIM_Period = 8000;
BTIM_InitStruct.BTIM_Prescaler = BTIM_PRS_DIV8;
BTIM_TimeBaseInit(CW_BTIM1, &BTIM_InitStruct);
BTIM_ITConfig(CW_BTIM1, BTIM_IT_OV, ENABLE);
BTIM_Cmd(CW_BTIM1, ENABLE);
}
void BTIM1_IRQHandler(void)
{
if(BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV))
{
BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV);
Schr_SchedulerRunning();
}
}
2.2 LED控制逻辑
LED驱动逻辑如下,写了一个比较通用的逻辑,方便在不同的板子之间移植,所以逻辑相对复杂了一点点:
app_led.c
#include "app_common.h"
#include "app_led.h"
#define APP_LED_DEBUG
#undef APP_LED_DEBUG
#ifdef APP_LED_DEBUG
#include <stdio.h>
#endif
typedef struct
{
GPIO_TypeDef *gpio;
uint16_t pin;
} led_gpio_t;
static void Led_GpioConfiguration(void);
void Led_Init(void)
{
#if ((APP_LED_MODE == APP_LED_IO) || (APP_LED_MODE == APP_LED_BOTH))
Led_GpioConfiguration();
#endif
#if ((APP_LED_MODE == APP_LED_PWM) || (APP_LED_MODE == APP_LED_BOTH))
#endif
}
void Led_IoOutput(uint8_t led_num, uint8_t led_level)
{
led_gpio_t led_pin[APP_IO_LED_TOTAL_NUM] =
{
{CW_GPIOA, GPIO_PIN_7},
{CW_GPIOA, GPIO_PIN_8},
{CW_GPIOC, GPIO_PIN_13}
};
GPIO_WritePin(led_pin[led_num].gpio, led_pin[led_num].pin, (GPIO_PinState)led_level);
}
static void Led_GpioConfiguration(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
__RCC_GPIOA_CLK_ENABLE();
__RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.IT = GPIO_IT_NONE;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pins = GPIO_PIN_7 | GPIO_PIN_8;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pins = GPIO_PIN_13;
GPIO_Init(CW_GPIOC,&GPIO_InitStruct);
GPIO_WritePin(CW_GPIOA,GPIO_PIN_7,GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOA,GPIO_PIN_8,GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOC,GPIO_PIN_13,GPIO_Pin_RESET);
}
app_led.h
#ifndef APP_LED_H__
#define APP_LED_H__
#include "app_common.h"
#define APP_LED_IO (0U)
#define APP_LED_PWM (1U)
#define APP_LED_BOTH (2U)
#define APP_LED_MODE (APP_LED_IO)
#if (APP_LED_MODE == APP_LED_IO)
#define APP_LED_ON (GPIO_Pin_RESET)
#define APP_LED_OFF (GPIO_Pin_SET)
#endif
#if ((APP_LED_MODE == APP_LED_IO) || (APP_LED_MODE == APP_LED_BOTH))
#define APP_IO_LED_TOTAL_NUM (3U)
#endif
#if ((APP_LED_MODE == APP_LED_PWM) || (APP_LED_MODE == APP_LED_BOTH))
#define APP_PWM_LED_TOTAL_NUM (2U)
#endif
extern void Led_Init(void);
extern void Led_IoOutput(uint8_t led_num, uint8_t led_level);
#endif
2.3 LED任务实现
下面是LED任务实现函数Led_Running():
void Led_Running(void)
{
static uint8_t aa = 0;
if (aa == 0)
{
Led_IoOutput(0, APP_LED_ON);
Led_IoOutput(1, APP_LED_ON);
Led_IoOutput(2, APP_LED_ON);
}
else
{
Led_IoOutput(0, APP_LED_OFF);
Led_IoOutput(1, APP_LED_OFF);
Led_IoOutput(2, APP_LED_OFF);
}
aa ^= 1;
}
2.3 主函数实现
代码中使用到的app_common.h函数逻辑如下:
#ifndef APP_COMMON_H__
#define APP_COMMON_H__
#include <stdint.h>
#include "main.h"
#endif
主函数逻辑:
- 先调用Led_Init()初始化LED相关GPIO
- 然后调用Schr_CreateTask(Led_Running, SCHR_PERIOD_500MS)将Led_Running()放到500ms任务中运行
- 最后调用Schr_StartScheduler()开始启动调度器
#include "main.h"
#include "app_uart.h"
#include "app_scheduler.h"
#include "app_led.h"
void RCC_Configuration(void);
int main()
{
RCC_Configuration();
Led_Init();
Schr_CreateTask(Led_Running, SCHR_PERIOD_500MS);
Schr_StartScheduler();
while(1)
{
}
}
void RCC_Configuration(void)
{
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8);
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_3);
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(64000000);
}
3 实现效果
见如下视频,LED每500ms闪烁一次,表示调度器使用成功,后续就可以在调度器基础上加上一些其他功能了。