【CW32饭盒派开发板试用体验】2. 使用扩展板串口UART3以及scanf重定向到串口
本来打算移植freeRTOS来试试的,但是移植了大半天没搞定,而且板载资源有限(8K SRAM和64K Flash),就算成功移植了OS,资源也所剩无几了,所以就决定自己写一个调用器来用,为后续的作品任务调度做准备。
任务调用使用了BTIM1(其实使用systick也是可以,只要有定时功能都OK)。实现了2ms,5ms,10ms,100ms,500ms,1000ms的任务周期性调度功能。
本文实现了基于调度器的LED周期性闪烁功能。
BTIM是基础定时器。就不做过多介绍了,可以参考用户手册。我们在使用的时候直接调用官方提供的库函数即可。
下面说一下LED接线,连接到了:
并且对应引脚拉低,则LED会被点亮。
调度器依靠定时器提供时基,设置了定时器时基为1ms,每1ms进入一次定时器中断,在中断中计数:
调度器原理还是很简单,代码实现如下。
其中:
调度器使用了堆空间,对分配的任务进行存储,属于动态分配。
/*
@hehung
2023-5-28
email: 1398660197@qq.com
wechat: hehung95
reproduced and please indicate the source @hehung
*/
// This is a simple OS, and base a timer
#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);
// === User function declaration
static void BTIM_init(void);
// === End user function declaration
// Scheduler Initialization
void Schr_Init(void)
{
// === User Code
// === End user code
}
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;
}
}
// Start timer to shceduler
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,初始化代码以及中断服务程序如下:
// === User code: timer initialization and interrupt handler
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)
{
/* USER CODE BEGIN */
if(BTIM_GetITStatus(CW_BTIM1, BTIM_IT_OV))
{
BTIM_ClearITPendingBit(CW_BTIM1, BTIM_IT_OV);
Schr_SchedulerRunning();
}
/* USER CODE END */
}
LED驱动逻辑如下,写了一个比较通用的逻辑,方便在不同的板子之间移植,所以逻辑相对复杂了一点点:
app_led.c
/*
@hehung
2023-5-28
email: 1398660197@qq.com
wechat: hehung95
reproduced and please indicate the source @hehung
*/
#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);
// LED Initialization
void Led_Init(void)
{
#if ((APP_LED_MODE == APP_LED_IO) || (APP_LED_MODE == APP_LED_BOTH))
// === User code: LED GPIO intialization code
Led_GpioConfiguration();
// === End user code
#endif
#if ((APP_LED_MODE == APP_LED_PWM) || (APP_LED_MODE == APP_LED_BOTH))
// === User code: LED PWM intialization code
// === End user code
#endif
}
// GPIO Control LED
void Led_IoOutput(uint8_t led_num, uint8_t led_level)
{
led_gpio_t led_pin[APP_IO_LED_TOTAL_NUM] =
{
// === User code: LED output PIN
{CW_GPIOA, GPIO_PIN_7},
{CW_GPIOA, GPIO_PIN_8},
{CW_GPIOC, GPIO_PIN_13}
// === End user code
};
// === User code: LED output control
GPIO_WritePin(led_pin[led_num].gpio, led_pin[led_num].pin, (GPIO_PinState)led_level);
// === End user code
}
// === User code
static void Led_GpioConfiguration(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
__RCC_GPIOA_CLK_ENABLE();
__RCC_GPIOC_CLK_ENABLE();
GPIO_InitStruct.IT = GPIO_IT_NONE; //LED2
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; //LED4
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);
}
// === End user code
app_led.h
/*
@hehung
2023-5-28
email: 1398660197@qq.com
wechat: hehung95
reproduced and please indicate the source @hehung
*/
#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)
// LED working mode: GPIO output or PWM output
#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
// Total number of leds
#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
下面是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;
}
代码中使用到的app_common.h函数逻辑如下:
#ifndef APP_COMMON_H__
#define APP_COMMON_H__
// includes processor header files
#include <stdint.h>
#include "main.h"
#endif
主函数逻辑:
/*
@hehung
2023-5-28
email: 1398660197@qq.com
wechat: hehung95
reproduced and please indicate the source @hehung
*/
#include "main.h"
#include "app_uart.h"
#include "app_scheduler.h"
#include "app_led.h"
void RCC_Configuration(void);
int main()
{
RCC_Configuration(); // 64MHz clock
Led_Init();
Schr_CreateTask(Led_Running, SCHR_PERIOD_500MS);
Schr_StartScheduler();
while(1)
{
}
}
void RCC_Configuration(void)
{
/* 0. HSI使能并校准 */
RCC_HSI_Enable(RCC_HSIOSC_DIV6);
/* 1. 设置HCLK和PCLK的分频系数 */
RCC_HCLKPRS_Config(RCC_HCLK_DIV1);
RCC_PCLKPRS_Config(RCC_PCLK_DIV1);
/* 2. 使能PLL,通过PLL倍频到64MHz */
RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, 8); // HSI 默认输出频率8MHz
// RCC_PLL_OUT(); //PC13脚输出PLL时钟
///< 当使用的时钟源HCLK大于24M,小于等于48MHz:设置FLASH 读等待周期为2 cycle
///< 当使用的时钟源HCLK大于48MHz:设置FLASH 读等待周期为3 cycle
__RCC_FLASH_CLK_ENABLE();
FLASH_SetLatency(FLASH_Latency_3);
/* 3. 时钟切换到PLL */
RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL);
RCC_SystemCoreClockUpdate(64000000);
}
见如下视频,LED每500ms闪烁一次,表示调度器使用成功,后续就可以在调度器基础上加上一些其他功能了。
更多回帖