TM4C123GH6PM单片机裸机任务调度程序

本文代码原作者为:裸机环境实现多任务调度(例程及源码)_Z大侠的博客-CSDN博客无RTOS(裸机环境)下实现分时调度法,非常适用于单片机环境下的一般应用场景。RTOS优点不用多说了,但对于一些简单的业务需求,移植一个操作系统又显得非常麻烦,且非常占用系统资源。通常的做法是在一个大while下执行各个业务函数,通过多个定时器去控制各个函数的执行周期,利用几个全局变量关联各个模块功能。但这样的做法在多轮开发下,会造成 代码非常臃肿,模块关系混乱,不利于阅读。为了解决这个问题,手动码了一个简单的多任务调度,封装成接口供大家使用!实现方式:创建任务时,设定好任务的调度周期和https://blog.csdn.net/qq_37830757/article/details/114590077

 原作者已将代码上传至github,有需要的读者请移步原创博文阅读。

 如果对原作者造成困扰,本人深感抱歉,请联系本人立即删除。


 本例程将原作者的调度代码稍作调整,移植到了TM4C123GH6PM单片机,并且已在EK-TM4C123GXL评估板上验证通过。

 头文件:

 main.h

#ifndef INC_MAIN_H_
#define INC_MAIN_H_

// #define TIMER_BASE (16000)   // 16MHz, 1ms
#define TIMER_BASE (80000)      // 80MHz, 1ms

#endif /* INC_MAIN_H_ */

os.h 

#ifndef INC_OS_H_
#define INC_OS_H_

#define UART_TXBUF_SIZE 64

extern char g_ui8TxBuf[];
extern uint8_t g_ui8TxDoneFlag;
extern uint8_t g_ui8TxBeginFlag;
extern uint8_t g_ui81msFlag;
extern uint32_t g_ui32TimerCount;
extern uint32_t g_ui32uDMAErrCount;

#endif /* INC_OS_H_ */

int.h

#ifndef INC_INT_H_
#define INC_INT_H_

// 暂时没用上这个头文件,但为了规范起见,仍然保留

#endif /* INC_INT_H_ */

 schedule.h

#ifndef INC_SCHEDULE_H_
#define INC_SCHEDULE_H_

#ifndef TRUE
#define TRUE                                (1)
#define FALSE                               (0)
#endif

#define _OFFSETOF(TYPE, MEMBER)                             ((long)(&(((TYPE *)0)->MEMBER)))
#define ARRAY_LEN(ARRAY_NAME)                               (sizeof(ARRAY_NAME) / sizeof(ARRAY_NAME[0]))//求数组成员个数
#define GET_ARRAY_TAIL_ADDR(ARRAY_NAME)                     ((void*)((char*)(&(ARRAY_NAME) + 1 ) - 1))//获取数组结尾地址,  注意类型
#define GET_ARRAY_LAST_ELEMENT_ADDR(ARRAY_NAME,_TYPE_)      (((_TYPE_*)(&(ARRAY_NAME) + 1 ) - 1))//获取数组最后一个元素地址

#define SET_BIT(_Val_,_Bit_)                ((_Val_) |= (1 << (_Bit_)))
#define CLEAN_BIT(_Val_,_Bit_)              ((_Val_) &= (~(1 << (_Bit_))))
#define GET_BIT(_Val_,_Bit_)                (((_Val_) >> (_Bit_)) & 0x0001)

#define BigLittleSwap16(_16t_)              ((((uint16_t)(_16t_) & 0xff00) >> 8) | \
                                            (((uint16_t)(_16t_) & 0x00ff) << 8))

#define BigLittleSwap32(_32t_)              ((((uint32_t)(_32t_) & 0xff000000) >> 24) | \
                                            (((uint32_t)(_32t_) & 0x00ff0000) >> 8) | \
                                            (((uint32_t)(_32t_) & 0x0000ff00) << 8) | \
                                            (((uint32_t)(_32t_) & 0x000000ff) << 24))

#define TASK_AMOUNT_MAX                     (10U)      // 最大任务数量
#define TASK_NAME_LEN_MAX                   (20U)      // 任务名最大长度

#define TEST_TASK_PERIOD                    (10U)
#define TEST_TASK_DELAY_MS(_ms_)            (((_ms_) > TEST_TASK_PERIOD) ? ((_ms_) / TEST_TASK_PERIOD -1) : 0)

typedef uint32_t                            task_t;//任务控制块

typedef enum
{
    TASK_IDLE = 0,//空闲
    TASK_Resume = 1,//运行中
    TASK_ready = 2,//准备好
    TASK_Suspend = 3,//挂起
}cmTask_status;//任务状态

typedef struct
{
    uint32_t timeOutCnt;//任务执行间隔
    uint32_t timeOut;//任务计数值
    void (*Task_func)(void *arg);//任务函数指针
    void *par ;//任务函数参数
    cmTask_status status;// 任务状态
    char name[TASK_NAME_LEN_MAX];
}ScmTask_info;//单任务信息

typedef struct
{
    uint32_t Task_now;
    ScmTask_info Task_queue[TASK_AMOUNT_MAX];
}Sys_Task;//任务表

// 原作者的代码,没有倒数第二个参数initVal
int Task_create(char* TaskName, task_t *task, void (*start_routine)(void *arg), void *par,
                uint32_t initVal, uint32_t period);
void Task_cancel(task_t* task);
void Task_reckon_time(void);
void Task_scheduling (void);

#endif /* INC_SCHEDULE_H_ */

源文件 

 main.c

#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_uart.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "driverlib/udma.h"
#include "driverlib/timer.h"
#include "string.h"

#include "os.h"
#include "schedule.h"
#include "main.h"

// The error routine that is called if the driver library encounters an error.
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line){}
#endif

#if defined(ewarm)
#pragma data_alignment=1024
static uint8_t ui8ControlTable[1024];
#elif defined(ccs)
#pragma DATA_ALIGN(ui8ControlTable, 1024)
static uint8_t ui8ControlTable[1024];
#else
static uint8_t ui8ControlTable[1024] __attribute__ ((aligned(1024)));
#endif

void LEDInit(){
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
    ROM_GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);
}

void UARTDMAInit(){
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UDMA);

    ROM_uDMAEnable();
    ROM_uDMAControlBaseSet(ui8ControlTable);

    ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
    ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_1);
    ROM_UARTConfigSetExpClk(UART0_BASE, ROM_SysCtlClockGet(), 115200,
                            (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE |
                             UART_CONFIG_PAR_NONE));

//    1. 不使用FIFO,即FIFO深度为1字节。
//    ROM_UARTFIFODisable(UART0_BASE);

//    2. RX使用14个字节的FIFO(不存在UART_FIFO_TX8_8的设置项,最高只能设置14字节)
//    由于UDMA_ARB_1为1字节,所以正常是用不上多余的FIFO的;
//    但是特殊情况下,如果传输速度超过DMA中断的处理能力,
//    FIFO就可以多缓存14个字节,一定程度上能避免数据丢失。
    ROM_UARTFIFOLevelSet(UART0_BASE, UART_FIFO_TX7_8, UART_FIFO_RX7_8);
    ROM_UARTFIFOEnable(UART0_BASE);

    ROM_UARTEnable(UART0_BASE);
    ROM_UARTDMAEnable(UART0_BASE, UART_DMA_TX);

    ROM_IntMasterEnable();
    ROM_IntEnable(INT_UART0);
}

void TimerInit(){
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
    ROM_TimerConfigure(TIMER0_BASE, TIMER_CFG_ONE_SHOT);
    ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, TIMER_BASE);
    ROM_IntEnable(INT_TIMER0A);
    ROM_TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
}

void UARTDMATx(const char *pui8Buffer, uint32_t ui32Count){
    ROM_UARTTxIntModeSet(UART0_BASE, UART_TXINT_MODE_EOT);
    ROM_UARTIntEnable(UART0_BASE, UART_INT_TX);
    ROM_UARTDMAEnable(UART0_BASE, UART_DMA_TX);
    ROM_uDMAChannelAssign(UDMA_CH9_UART0TX);
    ROM_uDMAChannelAttributeDisable(UDMA_CHANNEL_UART0TX,
                                    UDMA_ATTR_ALTSELECT |
                                    UDMA_ATTR_HIGH_PRIORITY |
                                    UDMA_ATTR_REQMASK);
    ROM_uDMAChannelControlSet(UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT,
                              UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE |
                              UDMA_ARB_1);
    ROM_uDMAChannelTransferSet(UDMA_CHANNEL_UART0TX | UDMA_PRI_SELECT,
                               UDMA_MODE_BASIC,
                               (void *)pui8Buffer,
                               (void *)(UART0_BASE + UART_O_DR), ui32Count);
    ROM_uDMAChannelAttributeEnable(UDMA_CHANNEL_UART0TX, UDMA_ATTR_HIGH_PRIORITY);
    ROM_uDMAChannelEnable(UDMA_CH9_UART0TX);
}

#define FILL_RESPON(respon)                             \
    do{                                                 \
        strncpy(g_ui8TxBuf, (respon), strlen(respon));  \
        g_ui8TxBuf[strlen(respon)] = '\n';              \
        g_ui8TxBuf[strlen(respon)+1] = '\r';            \
        g_ui8TxBuf[strlen(respon)+1] = 0;               \
    }while(0)

void UARTSend(void *pui8Buffer)
{
    if (g_ui8TxBeginFlag == 1) return;
    FILL_RESPON((char *)pui8Buffer);
    UARTDMATx(g_ui8TxBuf, strlen(g_ui8TxBuf));
    g_ui8TxBeginFlag = 1;
}

void UARTDMARestore(void *p){
    if (g_ui8TxDoneFlag == 0 || g_ui32TimerCount == 0) return;

    g_ui32TimerCount = 0;
    g_ui8TxBeginFlag = 0;
    g_ui8TxDoneFlag = 0;
}

void LEDDeal(void *p){
    static uint8_t TurnOnFlag = 0;
    if (TurnOnFlag) {
        TurnOnFlag = 0;
        ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, GPIO_PIN_2);
    } else {
        TurnOnFlag = 1;
        ROM_GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0);
    }
}

static task_t task_id_list[TASK_AMOUNT_MAX];
int main(void)
{
//    外部16MHz的晶振
//    ROM_SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC | SYSCTL_OSC_MAIN |
//                       SYSCTL_XTAL_16MHZ);
//    80MHz时钟
    ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_INT);
    LEDInit();
    UARTDMAInit();
    TimerInit();

    // 对最后两个参数稍作解释:
    // 初次调度时,从998开始计时,每毫秒加1;大于等于999后,任务被调度;
    Task_create("UARTSend", &task_id_list[0], UARTSend, (void *)"Test 1s\0", 998, 999);
    // 初次调度时,从997开始计时,每毫秒加1;大于等于999后,任务被调度;
    Task_create("UARTDMARestore", &task_id_list[1], UARTDMARestore, NULL, 997, 999);
    // 初次调度时,从0开始计时,每毫秒加1;大于等于999后,任务被调度;
    Task_create("LEDDeal", &task_id_list[2], LEDDeal, NULL, 0, 500);

    ROM_TimerEnable(TIMER0_BASE, TIMER_A);
    for (;;)
    {
        Task_reckon_time();
        Task_scheduling();
    }
}

 os.c

#include "stdint.h"
#include "os.h"

char g_ui8TxBuf[UART_TXBUF_SIZE];

uint8_t g_ui8TxDoneFlag = 0;
uint8_t g_ui8TxBeginFlag = 0;
uint8_t g_ui81msFlag = 0;
uint32_t g_ui32TimerCount = 0;
uint32_t g_ui32uDMAErrCount = 0;

int.c

#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "driverlib/rom.h"
#include "driverlib/uart.h"
#include "driverlib/udma.h"
#include "driverlib/timer.h"
#include "os.h"
#include "main.h"
#include "int.h"

void uDMAErrorHandler(void)
{
    uint32_t ui32Status;
    ui32Status = ROM_uDMAErrorStatusGet();
    if(ui32Status)
    {
        ROM_uDMAErrorStatusClear();
        g_ui32uDMAErrCount++;
    }
}

void UARTIntHandler(void)
{
    uint32_t ui32Status;

    ui32Status = ROM_UARTIntStatus(UART0_BASE, true);
    ROM_UARTIntClear(UART0_BASE, ui32Status);

    if( (ui32Status&UART_INT_TX) == UART_INT_TX ){
        ROM_uDMAChannelDisable(UDMA_CH9_UART0TX);
        ROM_UARTDMADisable(UART0_BASE, UART_DMA_TX);
        ROM_UARTIntDisable(UART0_BASE, UART_INT_TX);
        g_ui8TxDoneFlag = 1;
    }
}

void Timer0IntHandler(void)
{
    ROM_TimerDisable(TIMER0_BASE, TIMER_A);
    ROM_TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT);
    ROM_IntMasterDisable();

    g_ui32TimerCount++;
    g_ui81msFlag = 1;

    ROM_IntMasterEnable();
    ROM_TimerLoadSet(TIMER0_BASE, TIMER_A, TIMER_BASE);
    ROM_TimerEnable(TIMER0_BASE, TIMER_A);
}

 schedule.c

#include <stdint.h>
#include <string.h>
#include <os.h>
#include "schedule.h"

volatile static Sys_Task Task_info_all;

/********************************************************
  * @Description:   创建一个任务
  * @Arguments    :
                TaskName        任务名称,指向ASCII字符串的指针
                task            任务控制块指针
                start_routine   任务函数
                par             传递给任务函数的参数
                initVal         初次调度的计时起始值
                period          任务调度周期
  * @Returns    :
                0   创建成功
                -1  创建失败
 *******************************************************/
int Task_create(char *TaskName, task_t *task, void (*start_routine)(void *arg), void *par,
                uint32_t initVal, uint32_t period)
{
    uint32_t i = 0;
    uint8_t Task_full_falg = TRUE;

    if(NULL != task && NULL != start_routine)
    {
        for(i = 0;i < TASK_AMOUNT_MAX;i++)
        {
            if(NULL == Task_info_all.Task_queue[i].Task_func)
            {
                strncpy((char*)&Task_info_all.Task_queue[i].name,TaskName,TASK_NAME_LEN_MAX-1);
                Task_info_all.Task_queue[i].timeOut = initVal;
                Task_info_all.Task_queue[i].timeOutCnt = period;
                Task_info_all.Task_queue[i].Task_func = start_routine;
                Task_info_all.Task_queue[i].par = par;
                Task_info_all.Task_queue[i].status = TASK_IDLE;
                *task = i;
                Task_full_falg = FALSE;
                break;
            }
        }
        if(FALSE == Task_full_falg)
        {
            return 0;
        }
    }

    return -1;
}

/********************************************************
  * @Description:删除一个任务
  * @Arguments:
                task   任务控制块指针
  * @Returns:
 *******************************************************/
void Task_cancel(task_t* task)
{
    if(NULL == task)//结束当前线程
        memset((char*)&Task_info_all.Task_queue[Task_info_all.Task_now],00,sizeof(ScmTask_info));
    else
        memset((char*)&Task_info_all.Task_queue[*(task)],00,sizeof(ScmTask_info));
}

/********************************************************
  * @Description:任务计时
  * @remark     :定时器1mS 执行一次
 *******************************************************/
void Task_reckon_time(void)
{
    if (g_ui81msFlag == 0) return;
    g_ui81msFlag = 0;

    uint32_t i = 0;
    for(i = 0; i < TASK_AMOUNT_MAX; i++)
    {
        if(TASK_IDLE == Task_info_all.Task_queue[i].status && NULL != Task_info_all.Task_queue[i].Task_func)
        {
            Task_info_all.Task_queue[i].timeOut++;
            if(Task_info_all.Task_queue[i].timeOut >= Task_info_all.Task_queue[i].timeOutCnt)
            {
                Task_info_all.Task_queue[i].timeOut = 0;
                Task_info_all.Task_queue[i].status = TASK_ready;
            }
        }
    }
}

void  Task_scheduling (void)
{
    uint32_t i = 0;
    for(i = 0; i < TASK_AMOUNT_MAX; i++)
    {
        if(TASK_ready == Task_info_all.Task_queue[i].status && NULL != Task_info_all.Task_queue[i].Task_func)
        {
            Task_info_all.Task_queue[i].status = TASK_Resume;
            Task_info_all.Task_now = i;
            Task_info_all.Task_queue[i].Task_func(Task_info_all.Task_queue[i].par);
            if(TASK_Suspend != Task_info_all.Task_queue[Task_info_all.Task_now].status)
            {
                Task_info_all.Task_queue[i].status = TASK_IDLE;
            }
        }
    }
}

串口调试助手截图如下: 

由截图可见,1s的任务调度,还是比较可靠的。

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值