hệ thống nhúng nó dành cho những công việc phức tạp lúc đó viết trên hệ điều hành của nó sẽ đơn giản hơn là mình lập trình thông thường. mình lấy ví dụ đơn giản như khi lập trình giao diện đồ họa trên C sẽ khó khăn hơn là lập trình giao diện đồ hoạ trên C# vì nó hỗ trợ nhiều hơn lập trình nhúng cũng như vậy
Trang 1BÙI QUỐC BẢO
LẬP TRÌNH HỆ THỐNG
NHÚNG
Concurrency (Tác vụ chạy ñồng
thời)
Một hệ thống có thể có 2 hay nhiều tác vụ chạy ñộc
lập nhau.
VD: Một hệ thống dùng 8051 ñể:
Bật/Tắt 1 LED gắn vào P1.0 sau chu kỳ 500 ms.
Cho 3 LED vàng-xanh-ñỏ lần lượt sáng-tắt sau chu
kỳ 500 ms
Chia 2 tác vụ trên thành 2 máy trạng thái SM
ñồng bộ với chu kỳ 500 ms
Trang 2BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 3
LEDON LEDOFF
GREEN_ON YELLOW_ON RED_ON
Blink LED Period: 500 ms
Rotate LED Period: 500 ms
LED SHOW
enum BLState_t {OFF_STATE,ON_STATE} BLState;
enum RTState_t {YELLOW_STATE, GREEN_STATE,
RED_STATE} RTState;
unsigned char timerFlag = 0;
timerFlag = 1;
}
…
}
void RT_Update() {
…
}
Trang 3BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 5
{
outputInit();
timerInit();
BLStateInit();
STStateInit();
while (1)
{
while (!timerFlag);
BL_Update();
RT_Update();
timerFlag = 0;
}
}
Shared variable (biến dùng chung)
Hoạt ñộng của một hệ thống có thể ñược
chia thành nhiều tác vụ ñộc lập mặc dù
chúng có liên quan tới nhau.
VD:
Một hệ thống phát hiện chuyển ñộng sử dụng 1 sensor kết nối vào
chân P1.0 Một chuyển ñộng ñược phát hiện khi sensor bằng 1 trong
2 lần lâý mẫu liên tiếp cách nhau 200ms
LED1 (nôí vào P1.1) ñược nhấp nháy sau với chu kỳ 200ms trong
thời gian phát hiện chuyển ñộng
LED2 (nôí vào P1.2) ñược bật khi phát hiện chuyển ñộng và tắt 10s
sau khi chuyển ñộng không còn bị phát hiện
Trang 4BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 7
Shared variable (biến dùng chung)
!P1.0 !P1.0
P1.0 P1.0
P1.0
!P1.0
mnt = 0 mnt = 1
!mnt
mnt
mnt
!mnt
cnt = 0
!mnt && (cnt < 50)
cnt++
!mnt && !(cnt < 50)
mnt
Shared variable (biến dùng chung)
Chú ý:
Chỉ có 1 tác vụ ghi vào biến chung Nhiều
tác vụ có thể cùng ñọc một biến chung.
VD
Vẽ máy trạng thái cho hệ thống làm việc sau:
Bật lò ñốt bằng cách set P1.0 khi nhiệt ñộ ño ñược (ñọc
vào từ P2) nhỏ hơn 100.
Trong khi bật lò, chớp nháy LED nối vào P1.1 với chu kỳ
500ms ñể báo cho người dùng biết là lò ñang bật.
Trang 5BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 9
Máy trạng thái có chu kỳ khác nhau
Các tác vụ với chu kỳ khác nhau có thể ñược
chạy bằng cách sử dụng một biến ñể ñếm số
lần cờ timerFlag ñược bật (Timer tick) và thực
thi khi biến ñó ñạt ñến giá trị mong muốn
VD:
Viết chương trình ñể 8051 chớp LED gắn vào
P1.0 với chu kỳ 600 ms, và cho 3 LED xanh,
vàng, ñỏ lần lượt sáng sau khoảng thời gian 1s.
enum blState_t {LEDON, LEDOFF} BLState;
static unsigned char timerFlag = 0;
void timerISR(void)
{
timerFlag = 1;
}
{
…
}
{
…
}
Trang 6BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 11
void main(void)
{
unsigned char BLTickCount = 0;
unsigned char RTTickCount = 0;
unsigned char BLTickPeriod = 3;
unsigned char RTTickCount = 5;
timerPeriodSet(200);
BLInit();
RTInit();
runTimer();
while (1) { While (!timerFlag);
BLTickCount++;
RTTickCount++;
If (BLTickCount == BLTickPeriod ) {
BLUpdate();
BLTickCount = 0;
}
If (RTTickCount == RTTickCount ) {
RTUpdate();
RTTickCount = 0;
} } }
Task Scheduler
Trong chương trình có nhiều tác vụ chạy song
song, ñoạn chương trình quyết ñịnh xem tác vụ
nào sẽ ñược chạy gọi là 1 scheduler.
Một scheduler gồm:
Hàm khởi tạo.
Hàm cập nhật các thông số của các tác vụ,
thực thi trong ngắt timer.
Một hàm thực thi tác vụ ñến thời ñiểm ñược
chạy (dispatcher).
Một hàm thêm tác vụ vào scheduler
Trang 7BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 13
Task Structure
Mỗi tác vụ (task) ñều có các thông số sau: (ví dụ tác vụ chớp led)
Tên tác vụ (BLUpdate)
Hằng số chứa thời gian giữa 2 lần thực thi (BLTickPeriod )
Biến ñếm số lần timer tick
Một cờ báo tác vụ ñuợc cho phép chạy
typedef data struct
{
void (code * ptask) (void);
unsigned char period ;
unsigned char count ;
unsigned char runMe;
} taskStructType;
Simple scheduler
void main (void)
{
schedulerInit(); //khởi tạo scheduler
/*Thêm tác vụBLLED vào scheduler, tác vụnày ñảo LED
sau 1000 lần timer tick*/
schedulerAddTask(BLLed, 1000, 0);
schedulerStart();
while (1)
{
schedulerDispatch();
}
}
void schedulerUpdate(void) interrupt 1 using 0
{
//update tasks
}
Trang 8BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 15
Scheduler.h
#ifndef SCHEDULER_H
#define SCHEDULER_H
#include <reg51.h>
#ifndef SCHEDULER_GLOBAL
#else
#define SCHEDULER_GLOBAL
#endif
Scheduler.h
typedef data struct
{
void (code * ptask) (void);
unsigned char period ;
unsigned char count ;
} taskStructType;
void schedulerGotoSleep(void);
void schedulerInit(void);
void schedulerStart(void);
unsigned char schedulerAddTask(void (code * pfunction)() ,
const unsigned char period, const unsigned char count );
#endif
Trang 9BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 17
Scheduler.c
#define SCHEDULER_GLOBAL
#include "scheduler.h"
unsigned char errorCode;
Khởi tạo scheduler
void schedulerInit()
{
unsigned char i;
for (i=0;i<SCHEDULER_MAX_TASK; i++)
{
schedulerTask[i].ptask = 0x0000;
schedulerTask[i].period = 0;
schedulerTask[i].count = 0;
schedulerTask[i].runMe = 0;
}
TMOD &= 0xF0;
TMOD |= 0x01;
TH0 = ((65536 - 50000) >> 8);
TL0 = ((65536 - 50000) &0xFF);
ET0 = 1;
TR0 = 1;
}
Trang 10BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 19
Cập nhật trạng thái tác vụ
void schedulerUpdate(void) interrupt 1 using 0 {
unsigned char i;
TR0 = 0;
TF0 = 0;
TH0 = ((65536 - 50000) >> 8);
TL0 = ((65536 - 50000) &0xFF);
TR0 = 1;
for (i=0; i< SCHEDULER_MAX_TASK; i++)
if (schedulerTask[i].ptask)
{
if ( schedulerTask[i].count == 0)
{ schedulerTask[i].runMe += 1;
schedulerTask[i].count = schedulerTask[i].period;
} }
}
Thêm tác vụ vào scheduler
unsigned char schedulerAddTask(void (code * pfunction)() ,
const unsigned char period, const unsigned char count ) {
unsigned char i;
while ((schedulerTask[i].ptask != 0) && (i < SCHEDULER_MAX_TASK))
i++;
if (i == SCHEDULER_MAX_TASK)
{
errorCode = ERROR_TOO_MANY_TASK;
return SCHEDULER_MAX_TASK;
}
schedulerTask[i].ptask = pfunction;
schedulerTask[i].period = period;
schedulerTask[i].count = count + 1;
schedulerTask[i].runMe = 0;
return i;
}
Trang 11BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 21
Dispatcher
void schedulerDispatch()
{
unsigned char i;
for (i=0;i<SCHEDULER_MAX_TASK;i++)
{
if (schedulerTask[i].runMe > 0) {
(*schedulerTask[i].ptask)();
schedulerTask[i].runMe -= 1;
} }
schedulerGotoSleep();
}
void schedulerStart()
{
EA = 1;
}
void schedulerGotoSleep()
{
PCON |= 0x01;
}
Trang 12BM Kỹ Thuật điện Tử - đH Bách Khoa TP.HCM 23
main.c
#include "scheduler.h"
void BlinkLED()
{
P1 ^= 0x01;
}
void main(void)
{
schedulerInit();
schedulerAddTask(BlinkLED,20,0);
schedulerStart();
while (1)
{
schedulerDispatch();
}
}
Triggered task
đôi khi, một tác vụ ựược cho phép bởi 1 tác
ựộng từ bên ngoài
VD: đèn giao thông có thể ựược ựiều khiển bằng
tay dùng các nút bấm.
Tác vụ có thể ựược thiết kế bằng cách lấy mẫu
tắn hiệu vào mỗi khi ựược gọi.
để tăng tốc ựộ ựáp ứng hệ thống, tác vụ có thể
ựược cho phép chạy bằng cách dùng ngắt (trong
trường hợp này là ngắt ngoài).
Trang 13BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 25
Triggered task
void extISR0(void) interrupt 0 using 0
{
schedulerTask[0].runMe = 1;
schedulerTask[0].count = schedulerTask[0].period;
}
Non-preemptive kernel
Những hệ ñiều hành giống như trên gọi là
non-preemptive kernel.
Tác vụ phải tự trả quyền ñiều khiển CPU cho hệ
ñiều hành.
Các “event” bất ñồng bộ ñược ñiều khiển bởi
ISR.
Một ISR có thể làm 1 tác vụ có ñộ ưu tiên cao
sẵn sàng chạy nhưng chỉ khi nào tác vụ hiện
hành chấm dứt, tác vụ này mới ñược chạy.
Trang 14BM Kỹ Thuật ðiện Tử - ðH Bách Khoa TP.HCM 27
Non-preemptive kernel
Question
trợ mức ñộ ưu tiên cho tác vụ?