目次
前
次
RTOS(Real Time Operating System)実装
自作のRTOSのUSO(Unvoiced Shadow Operating system)を
実装します。
USOはコンフィグレータで、システムコール、
タスクID、タスク関数の雛形を生成するので
何をやりたいのかだけを考えます。
ADcU7026で、USOを動かせるのかを考え、タスクを
3個用意して、次の動作をさせてみます。
- タスク0 1秒周期で基板上のLED制御
- タスク1 2秒周期で外部にある8個のLEDを点滅
- タスク2 3秒周期で外部にある8個のLEDを点滅
USOのシステムコールは、以下の6種類だけ
なので、これらを組合わせてタスク動作を
決定します。
- cre_tsk タスク関数をシステムに登録
- sta_tsk タスク初期状態を設定
- rsm_tsk タスクIDを指定して、他タスクをREADY状態に設定
- sus_tsk タスクIDを指定して、他タスクをSUSPEND状態に設定
- slp_tsk 自タスクをSUSPEND状態に設定
- wai_tsk 自タスクを指定時間だけWAIT状態に設定
USOでは、システムコールwai_tskで周期処理を
10msのN倍(N=1-->65535)に設定できます。
システムコールwai_tskを利用して、周期処理します。
void tsk0_proc(void)
{
/* judge and impress */
GP4DAT ^= (1 << 23) ;
/* cycle 1000ms */
wai_tsk( TSK0_INTERVAL );
}
マイコンを使うので、LEDをどこかのピンに
接続しなければなりません。今回はポート4
を利用します。
基板上のポート4の7ビットにLEDがあるので
ポート2、ポート3に各8個のLEDを接続。
各タスクは、別の周期で動作すればよいので
起動時に、状態としてREADYを与えます。
全ソースコードです。
#include <ADuC7026.h>
#define OFF 0
#define ON OFF+1
#define NO 0
#define YES NO+1
#define TSK0_INTERVAL 50
#define TSK1_INTERVAL 100
#define TSK2_INTERVAL 150
#define MASKFF 0xff
/* data definitions */
typedef unsigned char UBYTE ;
typedef signed char SBYTE ;
typedef unsigned short UWORD ;
typedef signed short SWORD ;
typedef unsigned long ULONG ;
typedef signed long SLONG ;
typedef struct {
void (*tsk)(void);
UWORD wcount ;
} TCBP ;
#define TSK_ID_MAX 3
#define TSK_ID0 0
#define TSK_ID1 1
#define TSK_ID2 2
TCBP tcb[TSK_ID_MAX];
#define TTS_SUSPEND 0
#define TTS_WAIT TTS_SUSPEND+1
#define TTS_READY TTS_SUSPEND+2
volatile UWORD ready ;
volatile UWORD suspend;
volatile UWORD waitq ;
volatile UBYTE run_tsk;
volatile UBYTE tflag ;
/*------------------------*/
/* task function protoype */
/*------------------------*/
void tsk0_proc(void);
void tsk1_proc(void);
void tsk2_proc(void);
/*-----------------------*/
/* system call prototype */
/*-----------------------*/
void init_os(void);
void cre_tsk(UBYTE tid,void (*tsk)(void));
void sta_tsk(UBYTE tid,UBYTE sta);
void rsm_tsk(UBYTE tid);
void sus_tsk(UBYTE tid);
void slp_tsk(void);
void wai_tsk(UWORD x);
UBYTE is_tsk_ready(UBYTE x);
void timer_handler(void);
/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void user_initialize(void);
/* global variables */
volatile UBYTE cnt0 ;
volatile UBYTE cnt1 ;
volatile UBYTE cnt2 ;
/*-------------------*/
/* interrupt handler */
/*-------------------*/
void IRQ_Handler(void) __irq;
int main(void)
{
TCBP pcur_tsk ;
/* initialize user */
user_initialize();
/* initialize RTOS */
init_os();
/* set task */
cre_tsk(TSK_ID0,tsk0_proc);
cre_tsk(TSK_ID1,tsk1_proc);
cre_tsk(TSK_ID2,tsk2_proc);
/* set task state */
sta_tsk(TSK_ID0,TTS_READY);
sta_tsk(TSK_ID1,TTS_READY);
sta_tsk(TSK_ID2,TTS_READY);
/* endless loop */
run_tsk = TSK_ID0 ;
while(ON)
{
/* get target task state */
pcur_tsk = tcb[run_tsk] ;
/* perform task */
if ( is_tsk_ready( run_tsk ) == YES ) {
(*(pcur_tsk.tsk))();
}
/* change next task */
run_tsk++;
if ( run_tsk == TSK_ID_MAX ) {
run_tsk = TSK_ID0 ;
}
/* timer handler */
if ( tflag == ON ) {
tflag = OFF ;
timer_handler();
}
}
/* dummy return */
return (0);
}
/*------------*/
/* initialize */
/*------------*/
void user_initialize(void)
{
/* select clock 41.78MHz
initialize in start up routine
*/
/* power control */
POWKEY1 = 0x01 ;
POWCON = 0x00 ; /* Active 41.78MHz */
POWKEY2 = 0xF4 ;
/* initialize PORT2 GPIO */
{
/* initialize PORT2 output and clear */
GP2DAT = 0xffff0000 ;
}
/* initialize PORT3 GPIO */
{
/* initialize PORT3 output and clear */
GP3DAT = 0xffff0000 ;
}
/* initialize PORT4 GPIO */
{
/* initialize PORT4 output and clear */
GP4DAT = 0xffff0000 ;
}
/* clear */
cnt0 = 0 ;
cnt1 = 0 ;
cnt2 = 0 ;
/* initialize timer 0 */
{
T0LD = 1632 ; /* (41.78MHz / 256) / 100 */
T0CON = 0xc4 ; /* enable , cyclic , 1/256 */
}
/* enable timer 0 */
IRQEN = RTOS_TIMER_BIT ;
}
/*----------------*/
/* task functions */
/*----------------*/
void tsk0_proc(void)
{
/* judge and impress */
GP4DAT ^= (1 << 23) ;
/* cycle 1000ms */
wai_tsk( TSK0_INTERVAL );
}
void tsk1_proc(void)
{
/* impress */
GP2DAT ^= (1 << (cnt1+16)) ;
/* increment */
cnt1++ ;
if ( cnt1 == 8 ) { cnt1 = 0 ; }
/* cycle 2000ms */
wai_tsk( TSK1_INTERVAL );
}
void tsk2_proc(void)
{
/* impress */
GP3DAT ^= (1 << (23-cnt2)) ;
/* increment */
cnt2++ ;
if ( cnt2 == 8 ) { cnt2 = 0 ; }
/* cycle 3000ms */
wai_tsk( TSK2_INTERVAL );
}
/*------------------*/
/* system call body */
/*------------------*/
void init_os(void)
{
ready = suspend = waitq = 0 ;
}
void cre_tsk(UBYTE tid,void (*tsk)(void))
{
tcb[tid].tsk = tsk ;
tcb[tid].wcount = 0;
}
void sta_tsk(UBYTE tid,UBYTE sta)
{
UWORD tmp ;
tmp = (1 << tid);
if ( sta == TTS_READY ) { ready |= tmp; }
if ( sta == TTS_SUSPEND ) { suspend |= tmp; }
if ( sta == TTS_WAIT ) { waitq |= tmp; }
}
void rsm_tsk(UBYTE tid)
{
UWORD tmp ;
tmp = (1 << tid);
ready |= tmp;
suspend &= ~tmp;
waitq &= ~tmp;
}
void sus_tsk(UBYTE tid)
{
UWORD tmp ;
tmp = (1 << tid);
ready &= ~tmp;
suspend |= tmp;
waitq &= ~tmp;
}
void slp_tsk(void)
{
sus_tsk(run_tsk);
}
void wai_tsk(UWORD x)
{
UWORD tmp ;
tmp = (1 << run_tsk);
ready &= ~tmp;
suspend &= ~tmp;
waitq |= tmp;
tcb[run_tsk].wcount = x ;
}
UBYTE is_tsk_ready(UBYTE x)
{
return( (ready >> x) & ON ) ;
}
void timer_handler(void)
{
UWORD xtmp;
UBYTE loop;
/* call timer handling */
xtmp = waitq ;
for ( loop = 0 ; loop < TSK_ID_MAX ; loop++ ) {
if ( xtmp & ON ) {
tcb[loop].wcount-- ;
if ( tcb[loop].wcount == 0 ) { rsm_tsk(loop); }
}
xtmp >>= 1 ;
}
}
/*********************/
/* */
/* Interrupt Handler */
/* */
/*********************/
void IRQ_Handler(void) __irq
{
/* judge timer0 interruption */
if ( (IRQSTA & RTOS_TIMER_BIT) == RTOS_TIMER_BIT ) {
/* clear timer0 interrupt flag */
T0CLRI = 0xff ;
/* USO timer handler */
tflag = ON ;
}
}
内部PLLを利用して、CPUの動作周波数を41.78MHz
にしています。
ADuC7026では、CPUの動作クロックとパワー制御を
PLLCON、POWCONへのパラメータで設定しますが
シーケンスが決まっています。
シーケンスに気づかず、指定したクロックでの動作
にならずに、原因がわかるまで時間がかかりました。
シーケンスは、以下です。
- PLLKEY1に 0xAA 設定
- PLLCON にパラメータ設定
- PLLKEY2に 0x55 設定
- POWKEY1に 0x01 設定
- POWCON にパラメータ設定
- POWKEY2に 0xF4 設定
全ポートは、GPIOとして利用するのでGPxCONの
対応ビットに00(2進数)を設定しています。
また、出力で利用するので、GPxDATの4バイト
のうちの最上位バイトは0xFFにします。
初期化では、上から2バイト目は0x00に設定。
タスク0では、1ビットだけの変化でよい
ので排他的論理和を利用して1ビットのみ
変化させるコードに。
タスク1、2では、GPxDATの上から2バイト目
だけを入れ変える操作をしています。
8ビットのカウンタを用意して、カウンタの値を
GPxDATの上から2バイト目に当てはめます。
外部基板のLEDは、負論理動作なので、排他的論理和
を利用して、反転してから、値を格納します。
このRTOSを使って動かした写真は、以下です。
赤色LEDと緑色の点灯個数のちがいで、点滅周期が
異なるのを見分けられます。
目次
前
次