原作者已将代码上传至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的任务调度,还是比较可靠的。