目次

計測器エミュレータ

 Personal Computer上のアプリケーションソフトを
 開発したときに、対象となる計測器の代用にPICを
 利用しました。シリアルインタフェースで接続して
 使います。



 仕様は、次のように単純です。

 エミュレータのハードウエアブロック図は
 次のようにしました。



 外付けでDIPスイッチを用意し、アナログスイッチで
 0から255の2進数でリードできるようにします。
 これを機械番号とします。

 LEDは、機械番号確認用です。

 コマンドインタプリタから作成していきます。
 ヘルプ処理
    if ( cmd == '?' ) {
      show_help() ;
    }

    利用する関数を定義します。

    void  show_help()
    {
      rs_puts("? help");
      rs_puts("C begin measure");
      rs_puts("c stop measure");
      rs_puts("N show number");
    }

 計測値転送開始
    if ( cmd == 'C' ) {
      eflag = ON ;
    }

    フラグがセットされているときだけ
  計測値を端末に送るようにします。

  計測値出力処理は、後で定義します。

 計測値転送停止
    if ( cmd == 'c' ) {
      eflag = OFF ;
    }

 機械番号表示
    if ( cmd == 'N' ) {
      /* get number from DIP switch */
      xnum = 0 ;
      for ( loop = 0 ; loop < 8 ; loop++ ) {
        /* shift */
        xnum <<= 1 ;
        /* transmitt address */
        PORTC &= ~0x07 ;
        PORTC |= (7-loop) & 0x07 ;
        /* update LSB */
        if ( PORTC.B3 == ON ) { xnum |= ON ; }
      }
      /* show LEDs */
      PORTB = xnum ^ 0xff ; /* inverted */
      /* send number to terminal */
      rs_putchar( (xnum / 100) + '0' ); xnum %= 100 ;
      rs_putchar( (xnum / 10 ) + '0' );
      rs_putchar( (xnum % 10 ) + '0' );
      crlf();
    }

 コマンドインタプリタを考えたので
 シリアル処理で使う関数を定義します。
 1文字出力
  送信バッファにデータがないことを確認後
  レジスタに文字を設定します。

  void  rs_putchar(UBYTE x)
  {
    /* judge */
    while ( !TXSTA.TRMT ) ;
    /* set data */
    TXREG = x ;
  }

 1文字列出力
  送信文字列の先頭アドレスを渡して
  '\0'を検出するまで、1文字ずつ
  送信します。

  void  rs_puts(UBYTE *ptr)
  {
    while ( *ptr ) {
      rs_putchar( *ptr );
      ptr++ ;
    }
    crlf();
  }

 改行
  端末画面で改行できるように、改行
  コードを送信します。

  void  crlf(void)
  {
    rs_putchar('\r');
    rs_putchar('\n');
  }

 文字列受信
  1文字受信すると、受信バッファに保存
  していきます。'\r'を検出するとコマンド
  インタプリタに通知します。

  void interrupt(void)
  {
    UBYTE chx ;
    /* receive interrupt */
    if ( PIR1.RCIF == ON ) {
      /* clear flag */
      PIR1.RCIF = OFF ;
      /* get 1 charactor */
      chx = RCREG ;
      /* store */
      *(sbuf+sindex) = chx ;
      /* increment */
      sindex++ ;
      /* judge */
      if ( chx == '\r' ) {
        sindex = 0 ;
        rflag = ON ;
      }
    }
  }

  受信バッファは、リングバッファで構成するのが
  定石ですが、今回はコマンドが1文字でパラメータ
  がないので、単純な配列にします。
  配列のどこに文字を格納するかを指定する変数も
  用意します。

    typedef unsigned char UBYTE ;

    UBYTE sbuf[4] ;
    UBYTE sindex ;

 計測値は、温度、生値、換算値の3種を1レコードで
 構成しています。温度は、小数第1位までですが10倍
 して整数にします。生値は4けたの整数、換算値は
 浮動小数点つきですが、小数第2位までで全体で5桁
 です。
 (例) 234 1025 30.55

 エミュレーションすればよいので、5レコードを
 文字列で用意して、それを順次送信します。

 フラグがセット中に送信します。

    if ( eflag == ON ) {
      switch ( state ) {
        case  1 : rs_puts("123 1128 31.23") ; break ;
        case  2 : rs_puts("234 1256 32.34") ; break ;
        case  3 : rs_puts("456 1384 34.56") ; break ;
        case  4 : rs_puts("789 1512 37.89") ; break ;
        default : rs_puts("012 1000 30.00") ; break ;
      }
      state++ ;
      if ( state == 5 ) { state = 0 ; }
    }

 時間経過がわからないのでタイマー割込みから
 1秒経過をフラグ通知し、5レコード中の指定
 文字列を表示します。

    if ( tflag == ON ) {
      /* clear flag */
      tflag = OFF ;
      /* update state */
      state++ ;
      if ( state == 5 ) { state = 0 ; }
      /* enable send */
      sflag = ON ;
    }
    if ( eflag == ON && sflag == OFF ) {
      /* clear flag */
      sflag = OFF ;
      /* show */
      switch ( state ) {
        case  1 : rs_puts("123 1128 31.23") ; break ;
        case  2 : rs_puts("234 1256 32.34") ; break ;
        case  3 : rs_puts("456 1384 34.56") ; break ;
        case  4 : rs_puts("789 1512 37.89") ; break ;
        default : rs_puts("012 1000 30.00") ; break ;
      }
    }

 割込みは、タイマー、受信の2種でよいので
 ハンドラとして関数にまとめます。

void interrupt(void)
{
  UBYTE chx ;
  /* generate 1ms */
  if ( INTCON.TMR0IF == ON ) {
    /* clear flag */
    INTCON.TMR0IF = OFF ;
    /* initialize */
    TMR0 = 6 ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( (timcnt & 0x3ff) == 1000 ) { TFLAG = ON ; }
  }
  /* receive interrupt */
  if ( PIR1.RCIF == ON ) {
    /* clear flag */
    PIR1.RCIF = OFF ;
    /* get 1 charactor */
    chx = RCREG ;
    /* store */
    *(sbuf+sindex) = chx ;
    /* increment */
    sindex++ ;
    /* judge */
    if ( chx == '\r' ) {
      sindex = 0 ;
      RFLAG = ON ;
    }
  }
}

 フラグを利用する場合、1ビットでよいので
 1バイト中の各ビットにフラグを割当てます。

typedef union {
  struct {
    unsigned B7:1;
    unsigned B6:1;
    unsigned B5:1;
    unsigned B4:1;
    unsigned B3:1;
    unsigned B2:1;
    unsigned B1:1;
    unsigned B0:1;
  } BIT ;
  unsigned char DR ;
} FLAGSP ;

volatile FLAGSP xflags ;

#define RFLAG xflags.BIT.B0
#define EFLAG xflags.BIT.B1
#define TFLAG xflags.BIT.B2
#define SFLAG xflags.BIT.B3

 利用するクロックは4MHzとして、通信条件等を指定。

void init_usr(void)
{
  /* disable A/D converter */
  ADCON1 = 0x07 ;
  /* disable Analog comparator */
  CMCON = 0x07 ;
  /* I/O initial state */
  PORTA = 0xff ;
  PORTB = 0x00 ;
  PORTC = (1 << RX_BIT);
  /* I/O direction */
  TRISA = 0 ;
  TRISB = 0 ;
  TRISC = (1 << RX_BIT) | (1 << PM_BIT) ;
  /* initialize serial interface */
  {
    sindex = 0 ;
    /* BAUD rate */
    SPBRG = 25 ; /* 9600bps */
    /* TxD */
    TXSTA.TXEN = ON ;
    TXSTA.SYNC = OFF ;
    TXSTA.BRGH = ON ;
    /* RxD */
    RCSTA.SPEN = ON ;
    RCSTA.CREN = ON ;
    /* enable receive interrupt */
    PIE1.RCIE   = ON ;
  }
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/4 = 250kHz prescaler = 1:4
       all inputs are pull-up .
    */
    OPTION_REG = 0x01 ;
    /* 256 - 250 = 6 */
    TMR0 = 6 ;
    /* enable timer0 overflow interrupt */
    INTCON.TMR0IE = ON ;
  }
  /* enable peripheral interrupt */
  INTCON.PEIE = ON ;
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* clear flags */
  xflags.DR = 0 ;
  /* others */
  timcnt = 0 ;
  state = 0 ;
}

 以上からソースコードを定義します。

/* redefine data type */
typedef unsigned char  UBYTE ;
typedef unsigned int   UWORD ;
typedef unsigned long  ULONG ;

#define OFF 0
#define ON  OFF+1

#define RX_BIT 7
#define TX_BIT 6
#define PM_BIT 3

#define MASKFF 0xff
#define MASK30 0x30
#define MASK0F 0x0f
#define MASK07 0x07
#define MASK03 0x03

#define MASK3FF 0x3ff

typedef union {
  struct {
    unsigned B7:1;
    unsigned B6:1;
    unsigned B5:1;
    unsigned B4:1;
    unsigned B3:1;
    unsigned B2:1;
    unsigned B1:1;
    unsigned B0:1;
  } BIT ;
  unsigned char DR ;
} FLAGSP ;

volatile FLAGSP xflags ;

#define RFLAG xflags.BIT.B0
#define EFLAG xflags.BIT.B1
#define TFLAG xflags.BIT.B2
#define SFLAG xflags.BIT.B3

volatile ULONG timcnt ;
volatile UBYTE sbuf[4] ;
volatile UBYTE sindex ;
volatile UBYTE cmd ;
volatile UBYTE xnum ;
volatile UBYTE loop ;
volatile UBYTE state ;

/* function prototype */
void  init_usr(void);
void  rs_putchar(UBYTE x);
void  rs_puts(UBYTE *ptr);
void  crlf(void);
void  show_help(void);

/* interrupt handler */
void interrupt(void)
{
  UBYTE chx ;
  /* generate 1ms */
  if ( INTCON.TMR0IF == ON ) {
    /* clear flag */
    INTCON.TMR0IF = OFF ;
    /* initialize */
    TMR0 = 6 ;
    /* increment */
    timcnt++ ;
    /* judge */
    if ( (timcnt & 0x3ff) == 1000 ) { TFLAG = ON ; }
  }
  /* receive interrupt */
  if ( PIR1.RCIF == ON ) {
    /* clear flag */
    PIR1.RCIF = OFF ;
    /* get 1 charactor */
    chx = RCREG ;
    /* store */
    *(sbuf+sindex) = chx ;
    /* increment */
    sindex++ ;
    /* judge */
    if ( chx == '\r' ) {
      sindex = 0 ;
      RFLAG = ON ;
    }
  }
}

void main(void)
{
  /* user initialize */
  init_usr();
  /* endless loop */
  while ( ON ) {
    /* command interpreter */
    if ( RFLAG == ON ) {
      /* clear */
      RFLAG = OFF ;
      /* new line */
      crlf();
      /* get command */
      cmd = *(sbuf+0) ;
      /* help */
      if ( cmd == '?' ) { show_help() ; }
      /* begin */
      if ( cmd == 'C' ) { EFLAG = ON ; }
      /* suspend */
      if ( cmd == 'c' ) { EFLAG = OFF ; }
      /* show machine number */
      if ( cmd == 'N' ) {
        /* get number from DIP switch */
        xnum = 0 ;
        for ( loop = 0 ; loop < 8 ; loop++ ) {
          /* shift */
          xnum <<= 1 ;
          /* transmitt address */
          PORTC &= ~MASK07 ;
          PORTC |= (7-loop) & MASK07 ;
          /* update LSB */
          if ( PORTC.B3 == ON ) { xnum |= ON ; }
        }
        /* show LEDs */
        PORTB = xnum ^ MASKFF ; /* inverted */
        /* send number to terminal */
        rs_putchar( (xnum / 100) + '0' ); xnum %= 100 ;
        rs_putchar( (xnum / 10 ) + '0' );
        rs_putchar( (xnum % 10 ) + '0' );
        crlf();
      }
    }
    /* 1second pass */
    if ( TFLAG == ON ) {
      /* clear flag */
      TFLAG = OFF ;
      /* update state */
      state++ ;
      if ( state == 5 ) { state = 0 ; }
      /* enable send */
      SFLAG = ON ;
    }
    /* show measure recode */
    if ( EFLAG == ON && SFLAG == OFF ) {
      /* clear flag */
      SFLAG = OFF ;
      /* show */
      switch ( state ) {
        case  1 : rs_puts("123 1128 31.23") ; break ;
        case  2 : rs_puts("234 1256 32.34") ; break ;
        case  3 : rs_puts("456 1384 34.56") ; break ;
        case  4 : rs_puts("789 1512 37.89") ; break ;
        default : rs_puts("012 1000 30.00") ; break ;
      }
    }
  }
}

/* define function body */
void init_usr(void)
{
  /* disable A/D converter */
  ADCON1 = 0x07 ;
  /* disable Analog comparator */
  CMCON = 0x07 ;
  /* I/O initial state */
  PORTA = 0xff ;
  PORTB = 0x00 ;
  PORTC = (1 << RX_BIT);
  /* I/O direction */
  TRISA = 0 ;
  TRISB = 0 ;
  TRISC = (1 << RX_BIT) | (1 << PM_BIT) ;
  /* initialize serial interface */
  {
    sindex = 0 ;
    /* BAUD rate */
    SPBRG = 25 ; /* 9600bps */
    /* TxD */
    TXSTA.TXEN = ON ;
    TXSTA.SYNC = OFF ;
    TXSTA.BRGH = ON ;
    /* RxD */
    RCSTA.SPEN = ON ;
    RCSTA.CREN = ON ;
    /* enable receive interrupt */
    PIE1.RCIE   = ON ;
  }
  /* initialize Timer 0 */
  {
    /*
       4MHz/4 = 1MHz -> 1MHz/4 = 250kHz prescaler = 1:4
       all inputs are pull-up .
    */
    OPTION_REG = 0x01 ;
    /* 256 - 250 = 6 */
    TMR0 = 6 ;
    /* enable timer0 overflow interrupt */
    INTCON.TMR0IE = ON ;
  }
  /* enable peripheral interrupt */
  INTCON.PEIE = ON ;
  /* enable general interrupt */
  INTCON.GIE = ON ;
  /* clear flags */
  xflags.DR = 0 ;
  /* others */
  timcnt = 0 ;
  state = 0 ;
}

void  rs_putchar(UBYTE x)
{
  /* judge */
  while ( !TXSTA.TRMT ) ;
  /* set data */
  TXREG = x ;
}

void  rs_puts(UBYTE *ptr)
{
  while ( *ptr ) {
    rs_putchar( *ptr );
    ptr++ ;
  }
  crlf();
}

void  crlf(void)
{
  rs_putchar('\r');
  rs_putchar('\n');
}

void  show_help(void)
{
  rs_puts("? help");
  rs_puts("C begin measure");
  rs_puts("c stop measure");
  rs_puts("N show number");
}

 回路図は、以下。




目次

inserted by FC2 system