目次

PWM制御

 PWM(Pulse Width Modulation)は、DCモータの回転速度を
 可変にするために、使われます。
 CCS社のCコンパイラを使って、PWMの波形生成を考えます。



 PWMは、1周期の時間間隔の中でHがどれだけ続く
 のかを、Duty比というパラメータで表現します。



 Duty比をデジタル値で与えたいので、0〜99の
 整数で指定できるようにしました。

 時間差を利用すれば、周期を生成できます。

 タイマー割込みで、0〜99までを生成し
 与えたデジタルのDuty比と比較し、出力
 する論理値を確定させます。



 タイマー割込みが発生するたびに、カウンタを+1して
 100になると、0に戻します。図では、左から右に大きく
 なる傾斜がカウンタに相当します。

 Duty比は可変にしますが、図では赤線の上下移動が可変になります。

 タイマー割込みの発生をフラグで通知して
 カウンタをインクリメントし、Duty比から
 出力論理値を確定するコードを記述します。

    if ( tflag == ON ) {
      /* clear flag */
      tflag = OFF ;
      /* impress */
      if ( dflag == ON ) {
        if ( pcnt < dcnt ) { a_port |= 0x01  ; }
        else               { a_port &= ~0x01 ; }
      } else {
        a_port &= ~0x01 ;
      }
      /* increment */
      pcnt++ ;
      if ( pcnt == 100 ) {
        /* clear */
        pcnt = 0 ;
        /* update duty ratio */
        dcntx = dcnt ;
      }
    }

 Duty比は、変数dcnt、dcntxで管理します。
 変数を2つ用意するのは、シリアルインタフェースで
 Duty比を設定したときに、1周期の間に回転数が変化
 しないようにするため。

 コマンドを考えます。

 コマンドを決めれば、コマンドインタプリタは
 定義できます。

    if ( sflag == ON ) {
      /* clear flag */
      sflag = OFF ;
      /* new line */
      crlf();
      /* get command */
      cmd = *(sbuf+0) ;
      /* judge */
      if ( cmd == '?' ) {
        puts("? help");
        puts("E enable");
        puts("D disable");
        puts("P set duty");
        puts("p show duty");
      }
      if ( cmd == 'E' ) { dflag = ON ;  }
      if ( cmd == 'D' ) { dflag = OFF ; }
      if ( cmd == 'P' ) {
        dcnt = get_hex( *(sbuf+1) );
        dcnt *= 10 ;
        dcnt += get_hex( *(sbuf+2) );
      }
      if ( cmd == 'p' ) {
        tmp = dcnt ;
        putc( '0' + (tmp / 10) ) ;
        putc( '0' + (tmp % 10) ) ;
        crlf();
      }
    }

 受信割込みは、プラグマを利用し定義します。

#int_rda
void echo_back(void)
{
  UBYTE ch ;
  /* get 1 charactor */
  ch = rcreg ;
  /* store */
  *(sbuf + sindex) = ch ;
  /* increment */
  sindex++ ;
  /* judge */
  if ( ch == CR ) {
    sindex = 0 ;
    sflag  = ON ;
  }
}

 もうひとつの割込みである、タイマー割込みは
 タイマー0を利用して定義します。

#int_rtcc
void rtcc_handler(void)
{
  /* update */
  set_timer0( 6 ) ;
  /* set flag */
  tflag = ON ;
}

 PICは割込みハンドラのエントリーアドレスは
 0x04で固定なので、Cコンパイラが、プラグマ
 を見ながら、指定された割込み処理を並べます。

 タイマー0は、1msごとに割込みを発生させます。
 PWM波形出力は、RA0からとします。

 仕様を決めれば、初期化部分を作成できます。

void user_initialize(void)
{
  /* disable A/D converter */
  setup_adc_ports( NO_ANALOGS );
  /* set directions */
  set_tris_a( 0x00 ) ; /* all outputs */
  set_tris_b( 0xff ) ; /* all inputs  */
  set_tris_c( 0x80 ) ;
  /* initialize port values */
  a_port = 0xff ; /* */
  b_port = 0x00 ; /* */
  c_port = 0xdf ; /* */
  /* initialize pointer */
  sindex = 0 ;
  /* initialize counter */
  pcnt  = 0 ;
  dcnt  = 0 ;
  dcntx = 0 ;
  /* clear flags */
  sflag = OFF ;
  tflag = OFF ;
  dflag = OFF ;
  /* initialize timer 0 */
  setup_counters( RTCC_INTERNAL , RTCC_DIV_8 );
  set_timer0( 6 );
  /* enable interrupt */
  enable_interrupts( INT_RTCC );
  enable_interrupts( INT_RDA );
  enable_interrupts( GLOBAL );
}

 初期化処理の最後に、割込みが発生するように
 設定しておきます。

 ソースコードは、以下。

#include <16f873.H>

/* */
#use standard_io(A)
#use standard_io(B)
#use standard_io(C)

/* */
#define CR    0x0d
#define LF    0x0a

/* redefine register name */
#byte a_port = 0x05
#byte b_port = 0x06
#byte c_port = 0x07
#byte rcreg  = 0x1a

#fuses XT,WDT,NOPROTECT,NOLVP

#use delay(clock=8000000)
#use rs232(baud=9600,xmit=PIN_C6,rcv=PIN_C7)

/* redefine data types */
typedef unsigned int  UBYTE ;
typedef unsigned long UWORD ;
typedef   signed int  SBYTE ;

/* global variables */
UBYTE sbuf[8];
UBYTE sindex;
UBYTE sflag ;
UBYTE tflag ;
UBYTE cmd   ;
UBYTE pcnt  ;
UBYTE dcnt  ;
UBYTE dcntx ;
UBYTE dflag ;

#define NO  0
#define YES 1

#define OFF 0
#define ON  1

#define MASK0F 0x0f

/* new line */
void crlf(void)
{
  putc( LF );
  putc( CR );
}

/*--------------------------------*/
/* Insert user functions protoype */
/*--------------------------------*/
void  user_initialize(void);
UBYTE get_hex(UBYTE x);

/* receive interrupt */
#int_rda
void echo_back(void)
{
  UBYTE ch ;
  /* get 1 charactor */
  ch = rcreg ;
  /* store */
  *( sbuf + sindex ) = ch ;
  /* increment */
  sindex++ ;
  /* judge */
  if ( ch == CR ) {
    sindex = 0 ;
    sflag  = ON ;
  }
}

/* timer0 interrupt */
#INT_RTCC
void rtcc_handler(void)
{
  /* update */
  set_timer0( 6 ) ;
  /* set flag */
  tflag = ON ;
}

void main(void)
{
  UBYTE tmp ;
  /* initialize */
  user_initialize();
  puts("Hello !");
  /* loop */
  while ( ON ) {
    /* receive handling */
    if ( sflag == ON ) {
      /* clear flag */
      sflag = OFF ;
      /* new line */
      crlf();
      /* get command */
      cmd = *(sbuf+0) ;
      /* judge */
      if ( cmd == '?' ) {
        puts("? help");
        puts("E enable");
        puts("D disable");
        puts("P set duty");
        puts("p show duty");
      }
      if ( cmd == 'E' ) { dflag = ON ;  }
      if ( cmd == 'D' ) { dflag = OFF ; }
      if ( cmd == 'P' ) {
        dcnt = get_hex( *(sbuf+1) );
        dcnt *= 10 ;
        dcnt += get_hex( *(sbuf+2) );
      }
      if ( cmd == 'p' ) {
        tmp = dcnt ;
        putc( '0' + (tmp / 10) ) ;
        putc( '0' + (tmp % 10) ) ;
        crlf();
      }
    }
    /* timer handling */
    if ( tflag == ON ) {
      /* clear flag */
      tflag = OFF ;
      /* impress */
      if ( dflag == ON ) {
        if ( pcnt < dcnt ) { a_port |= 0x01  ; }
        else               { a_port &= ~0x01 ; }
      } else {
        a_port &= ~0x01 ;
      }
      /* increment */
      pcnt++ ;
      if ( pcnt == 100 ) {
        /* clear */
        pcnt = 0 ;
        /* update duty ratio */
        dcntx = dcnt ;
      }
    } 
  }
}

/*-----------------------*/
/* Insert user functions */
/*-----------------------*/
void user_initialize(void)
{
  /* disable A/D converter */
  setup_adc_ports( NO_ANALOGS );
  /* set directions */
  set_tris_a( 0x00 ) ; /* all outputs */
  set_tris_b( 0xff ) ; /* all inputs  */
  set_tris_c( 0x80 ) ;
  /* initialize port values */
  a_port = 0xff ; /* */
  b_port = 0x00 ; /* */
  c_port = 0xdf ; /* */
  /* initialize pointer */
  sindex = 0 ;
  /* initialize counter */
  pcnt  = 0 ;
  dcnt  = 0 ;
  dcntx = 0 ;
  /* clear flags */
  sflag = OFF ;
  tflag = OFF ;
  dflag = OFF ;
  /* initialize timer 0 */
  setup_counters( RTCC_INTERNAL , RTCC_DIV_8 );
  set_timer0( 6 );
  /* enable interrupt */
  enable_interrupts( INT_RTCC );
  enable_interrupts( INT_RDA  );
  enable_interrupts( GLOBAL );
}

UBYTE  get_hex(UBYTE x)
{
  UBYTE result ;
  result = 0 ;
  if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
  if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
  if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; } 
  return result ;
}

 回路図は、以下。




目次

inserted by FC2 system