以前在雷兒電子網發表的文章,重貼在這裏。目前有使用在商品上,穩定性還不錯。分成幾篇來貼。
這是為了對付8051這種沒有多大堆疊的MCU發開出來的。
我懶的用組合語言寫,實際上是說我對8051組合語言不太熟,所以用純粹C寫。因為簡單,所以拿來教學應是不錯。
整個系統可以說是為了對付LCM這個對時間有點敏感的裝置設計出來的。
LCM的規格上每次下指令或資料的時間間隔為73us。說大不大,又說小不小,拿來做單純delay不做事也很浪費。所以就想出這種多工方法。
因有對時間有點敏感的Task存在,故採用固定輪詢法(Polling)來保障單一Task微量延時的時間。
基本程式如下:
#include "c8051f340.h" // SFR declarations
#include < stdio.h >
#define LCM_DB P2 /* LCM Data Bus */
#include "F340Init.h"
sbit LED1 = 0xB4;
sbit LED2 = 0xB5;
sbit LED3 = 0xB6;
sbit LED4 = 0xB7;
// task start function
void LED_Light(void);
void LED_Black(void);
// measure skip time
void TestSkip0(void);
void TestSkip1(void);
// O.S function declaim
#define TASK_NUM 4
void Dummy(void);
idata unsigned char FuncID = 0;
idata unsigned char Task_Delay_Count[TASK_NUM]={0,0,0,0};
unsigned char Task_Skip_Count[TASK_NUM]={0,0,0,0};
idata void (*TaskFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
void (*TaskResumeFunc[TASK_NUM])(void) = {Dummy,Dummy,TestSkip0,LED_Light};
// O.S functions
void Dummy(void)
{
return;
}
void Task_Set_Next(void (*p_func)(void))
{
TaskFunc[FuncID]=p_func;
}
void Task_Delay_Next(void (*p_func)(void))
{
TaskResumeFunc[FuncID]=p_func;
}
void Task_Delay_Ms(unsigned char n)
{
if (! n ) n = 1;
TaskFunc[FuncID] = Dummy; // to sleep
Task_Delay_Count[FuncID] = n;
}
void Task_Skip_Next(void (*p_func)(void))
{
TaskResumeFunc[FuncID]=p_func;
}
void Task_Skip(void)
{
if ( Task_Skip_Count[FuncID] ) {
Task_Skip_Count[FuncID]--;
if (! Task_Skip_Count[FuncID]) TaskFunc[FuncID]=TaskResumeFunc[FuncID];
}
}
void Task_Set_Skip(unsigned char n)
{
if (! n ) n = 1;
TaskFunc[FuncID] = Task_Skip; // to sleep
Task_Skip_Count[FuncID] = n;
}
#define TASK_DELAY_MS_NEXT(n,s) Task_Delay_Next(s);Task_Delay_Ms(n);
#define TASK_SKIP_N_NEXT(n,s) Task_Skip_Next(s);Task_Set_Skip(n);
// O.S functions end
// main loop
void main(void)
{
PCA0MD &= ~0x40; // Disable Watchdog timer
Init_Device();
while (1)
{
register void (*Current_Func)(void);
EA = 0;
Current_Func = TaskFunc[FuncID]; // may break with interrupt
EA = 1;
Current_Func();
FuncID = (++FuncID) % TASK_NUM;
}
}
void Timer0_ISR (void) interrupt INTERRUPT_TIMER0
{
unsigned char i;
TL0 = 0x80;
TH0 = 0x44; // set delay for 1 ms
for ( i=0; i < TASK_NUM ; i++ )
{
if ( Task_Delay_Count[i] )
{
Task_Delay_Count[i]--;
if (! Task_Delay_Count[i]) TaskFunc[i]=TaskResumeFunc[i]; // wake up function
}
}
}
// Begin User Tasks
// Task 0
// Task 1
// Task 2 : measure skip time
void TestSkip0(void)
{
LED4 = 0;
TASK_SKIP_N_NEXT(10,TestSkip1);
}
void TestSkip1(void)
{
LED4 = 1;
TASK_SKIP_N_NEXT(20,TestSkip0);
}
// Task 3 : function for LED
void LED_Light(void)
{
LED1 = 0;
TASK_DELAY_MS_NEXT(250,LED_Black);
}
void LED_Black(void)
{
LED1 = 1;
TASK_DELAY_MS_NEXT(250,LED_Light);
}
請教一下
回覆刪除問題1:
因為定義TASK_NUM 是4
#define TASK_NUM 4
但因為FuncID%4,所以只會有0-3,但TASK宣告時,為5個,請問這是故意的嗎?
FuncID = (++FuncID) % TASK_NUM;
問題2:
為什麼要再宣告一個指向函數的指標
register void (*Current_Func)(void);
Current_Func();
不直接使用定義好的指向函數的指標就好?
(*TaskFunc[FuncID])( );
謝謝
[版主回覆11/21/2010 22:29:59]問題在於EA=0及EA=1二行中間。
直接寫沒有EA=0及EA=1,Task會死掉。被中斷撞死。
直接加的話也不對,變成在EA=0時就跳進函式。
所以只好用另一個暫存器變數來取。
@@ 我看錯了,我把LED_Light 看成是 LED 和 Light , 所以我以為有5個Task
回覆刪除所以才會有剛剛的問題1,抱歉