目次

RTOS(Real Time Operating System)実装

 自作のRTOSのUSO(Unvoiced Shadow Operating system)を
 実装します。

 USOはコンフィグレータで、システムコール、
 タスクID、タスク関数の雛形を生成するので
 何をやりたいのかだけを考えます。

 ADcU7026で、USOを動かせるのかを考え、タスクを
 3個用意して、次の動作をさせてみます。




 USOのシステムコールは、以下の6種類だけ
 なので、これらを組合わせてタスク動作を
 決定します。

 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へのパラメータで設定しますが
 シーケンスが決まっています。

 シーケンスに気づかず、指定したクロックでの動作
 にならずに、原因がわかるまで時間がかかりました。

 シーケンスは、以下です。
  1. PLLKEY1に 0xAA 設定
  2. PLLCON にパラメータ設定
  3. PLLKEY2に 0x55 設定
  4. POWKEY1に 0x01 設定
  5. POWCON にパラメータ設定
  6. POWKEY2に 0xF4 設定
 全ポートは、GPIOとして利用するのでGPxCONの  対応ビットに00(2進数)を設定しています。  また、出力で利用するので、GPxDATの4バイト  のうちの最上位バイトは0xFFにします。  初期化では、上から2バイト目は0x00に設定。  タスク0では、1ビットだけの変化でよい  ので排他的論理和を利用して1ビットのみ  変化させるコードに。  タスク1、2では、GPxDATの上から2バイト目  だけを入れ変える操作をしています。  8ビットのカウンタを用意して、カウンタの値を  GPxDATの上から2バイト目に当てはめます。  外部基板のLEDは、負論理動作なので、排他的論理和  を利用して、反転してから、値を格納します。  このRTOSを使って動かした写真は、以下です。  赤色LEDと緑色の点灯個数のちがいで、点滅周期が  異なるのを見分けられます。
目次

inserted by FC2 system