芯源半导体CW32
直播中

hehung

8年用户 659经验值
擅长:嵌入式技术
私信 关注
[经验]

【CW32饭盒派开发板试用体验】3. 使用BTIM实现任务调度器

【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会被点亮。
1685516665148.png

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是注册任务的函数,有两个参数:
    • 第一个参数是任务函数地址
    • 第二个参数是任务的执行周期

调度器使用了堆空间,对分配的任务进行存储,属于动态分配。

/*
@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 */
}

2.2 LED控制逻辑

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

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__

// includes processor header files
#include <stdint.h>

#include "main.h"

#endif

主函数逻辑:

  1. 先调用Led_Init()初始化LED相关GPIO
  2. 然后调用Schr_CreateTask(Led_Running, SCHR_PERIOD_500MS)将Led_Running()放到500ms任务中运行
  3. 最后调用Schr_StartScheduler()开始启动调度器
/*
@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);
}

3 实现效果

见如下视频,LED每500ms闪烁一次,表示调度器使用成功,后续就可以在调度器基础上加上一些其他功能了。

87d950fe86b1016eee8cf04e4dd51200

更多回帖

发帖
×
20
完善资料,
赚取积分