PWM  pulse width modulation

 onとoffを繰り返し、1周期のonの割合(デューティ比)を変えて平均電力を変更する方法です。  LEDの発光を弱くしたり、直流モータの回転制御を電力の無駄なくすることができます。

2 ソフトウエア制御による8LEDの点滅(Tiny2313) 09.08.09
1 Tiny2313の簡単な実験 09.08.08






ソフトウエア制御による8LEDの点滅
ハードウエアのPWMでは、タイマ0のAとB、タイマ1のAとBの4個のPWMを使えると思うのですが(検証していません)PORTBに接続した8個のLEDを蛍のように次第に明るく次第に暗く点滅し、かつ8個の位相が少しずつずれたものを考えました。

このプログラムは以前に考えたもので、今見直してもすぐには理解できないほど忘れ去っています。作ったときのままリストを挙げておきます。
回路は単純にポートBに電流制限抵抗を付けたLEDを接続しているだけです。
/**********************************************************************************
  spwm1.c     ソフトウエアによるPWMコントロール  050811 im

ATtiny2313の8つのポートをソフトウエアでpwm制御する。クロックは内蔵RC8MHz。

タイマで約0.03ミリ秒毎に割込をかける(TCNT0=252の時)。
割込が発生したらフラグを立てメインルーチンに伝える。これは割込ルーチンで処理を続ける
には多くのグローバル変数が必要だから、メインで処理することにしたため。

この割込により変数cntで100回の割込処理を繰り返す。この100回で1クールの発光をする。
デューティ1%の時は1回発光し残りの99回は発光を止める。デューティ90%なら90回発光10回消灯となる。
1クールの時間は 0.03ms*100回=3ms(333Hz)である。(TCNT0=252の時)

ポートBに8個のLEDをつなぎ、Hで発光させる。
8個のポートに対応した変数を準備し、この変数を割込100回につき1ずつ増加している。この変数に
でポイントされる配列変数の値がカウンタが等しくなったときにデューティの終わりで消灯する。

配列変数は1〜99〜1・・・と変化するから次第に明るくなり最大の明るさからは徐々に暗くなる。

8個のポートにはオフセットがかかっているので明るい部分が流れるように見える。

*********************************************************************************** */

#include <avr/io.h>
#include <avr/interrupt.h>

volatile uint8_t f=0;

void wait(unsigned int time){                  /* 8MHzで100μsのウエイトルーチン */
  register unsigned char lpcnt;
  __asm__ __volatile__("\n"
    "a00%=:\n\t"      //"CPU_wait_entry:\n\t"
    "ldi %0,200\n"
    "CPU_wait_lp%=:\n\t"
    "nop\n\t"
    "dec %0\n\t"
    "brne CPU_wait_lp%=\n\t"
    "sbiw %1,1\n\t"
    "brne a00%=\n\t"     //"brne CPU_wait_entry\n\t"
    :"=&a"(lpcnt)
    :"w"(time)
  );
  return;
} 



ISR(TIMER0_OVF_vect){
  TCNT0=240;                        /* タイマカウンタ再設定 255に近づけると周期が短くなる */
  f=1;                              /* 割込ルーチンではフラグを立てているだけ */
}

/*   メインルーチン   */
int main (void) {
  uint8_t cnt=0,cnt2=0,a0=0,a1=5,a2=10,a3=15,a4=20,a5=25,a6=30,a7=35,a8=40;
  uint8_t d[49]={1,1,1,2,3,3,4,4,5,6,7,9,11,13,15,18,22,27,32,38,46,55,66,79,95,
                  79,66,55,46,38,32,27,22,18,15,13,11,9,7,6,5,4,4,3,3,2,1,1,1};
  
  DDRD=0xff;                                     // PORTD出力モード  
  DDRB=0xff;                                     // PORTB出力モード  
  TIMSK=_BV(TOIE0);                              //タイマー割込許可
  TCCR0B=3;                                      //分周比1/64
  TCNT0=206;                                     //タイマカウンタ初期値 一瞬だから何でもよい
  
  sei(); /* 割り込みを有効にする */
  
  for(;;) {
//            wait(10);

    if(f){
      f=0;                                        /* フラグをリセット */
      if(PORTD&_BV(6)){PORTD&=~_BV(6);}else{PORTD|=_BV(6);}    /* 割込時間測定用端子 割込毎に反転 */
      cnt++;
      if(cnt==100){                               /* 割込100回で1クール */
        cnt=0;
        PORTB=0xff;                               /* 全LEDを点灯して        */
        cnt2++; if(cnt2==3){cnt2=0;}              /* 2を3,4・・・と増加すると点滅の周期が長くなる */
        if(cnt2==0){                              /* しかし、ちらつきが目立つようになる        */
          a8++;
          a0++;if(a0==49){a0=0;}
          a1++;if(a1==49){a1=0;}
          a2++;if(a2==49){a2=0;}
          a3++;if(a3==49){a3=0;}
          a4++;if(a4==49){a4=0;}
          a5++;if(a5==49){a5=0;}
          a6++;if(a6==49){a6=0;}
          a7++;if(a7==49){a7=0;}
        }
      }
      if(cnt==d[a0]){PORTB&=0b11111110;}   /* デューティに応じて消灯 */
      if(cnt==d[a1]){PORTB&=0b11111101;}   /*  */
      if(cnt==d[a2]){PORTB&=0b11111011;}   /*  */
      if(cnt==d[a3]){PORTB&=0b11110111;}   /*  */
      if(cnt==d[a4]){PORTB&=0b11101111;}   /*  */
      if(cnt==d[a5]){PORTB&=0b11011111;}   /*  */
      if(cnt==d[a6]){PORTB&=0b10111111;}   /*  */
      if(cnt==d[a7]){PORTB&=0b01111111;}   /*  */
    }
  }/* forの終わり */
}
/*   メインルーチン終わり   */

                                             
 

Tiny2313の簡単な実験
OC0Aピン(14番ピン Output Compare T/C0 A)にハードウエア的に出力されるPWMを利用します。
実験回路は、 14番ピン−200Ω−LED−GND を配線して、 電源に0.1uFパスコンを付けて、 5V電源につなぐだけです。回路図は省略します。

TCCR0Aレジスタの COM0A1〜0=10 でカウンタの値がOCR0A設定値と一致するとピン出力がLとなり、カウンタ0でHになります。
カウンタ0から設定値までがHとなりますから、設定値を大きくするとデューティ比が大きくなります。

TCCR0Bレジスタの WGM02=0、TCCR0Aレジスタの WGM01=1、WGM02=1 で カウンタは 0〜255 を繰り返します。

これらの動作は日本語マニュアルp45高速PWM動作に書かれています。

クロックは内蔵RC発振の8MHzとしています。TCCR0Bレジスタの CS02〜0=011 として 64分周しています。これによりタイマのクロックは 8000000/64=125000Hzとなり、PWMの周波数は 125000/256=488Hz となります。

この動作で割り込みを発生することはできますが、割り込みを許可すると作動しませんでした。原因は未解決です。

デューティ比はOCR0Aレジスタの値で決まりますが、一定値では面白くないのでプログラムで変化しています。そのとき、我々の目に入る光の感覚的強度は 対数的に変化するようですので、等差数列的に値を変えず、あえて、等比数列的に変更しています。
LEDの明るさを制御するときはこの値をどのようにきめるかがポイントになります。自動変更の他に他のセンサで変更したり、手動で変更したりします。

プログラムは次のとおりです。
/**********************************************************************************
  pwm_1.c  tiny2313によるPWM基本実験  2009.08.06 im

  回路は  電源に0.1uFのパスコン(電源は5V)
          OC0A端子(14番ピン)に200Ω+赤色LED(Hで点灯)       
  割り込みは使わないこと(理由を解明していないが割り込みを許可すると作動しない)
  
  PWMは  OCR0Aレジスタに0〜255をセットするとデューティ比  n/256 で点灯する。0は1/256か?
  
*********************************************************************************** */
#define F_CPU 8000000UL  // _delay_ms()のために。#includeより前に定義してください

#include <avr/io.h>
#include <util/delay.h>

void wait_ms(uint16_t);

/*   メインルーチン   */
int main (void) {
  DDRB=0xff;                                     // PORTB  
  
  TCCR0A=_BV(COM0A1)|_BV(WGM01)|_BV(WGM00);     //比較一致でL
  TCCR0B=_BV(CS01)|_BV(CS00);                   //クロック64分周
 
  while(1){                                     //無限ループ
    OCR0A=0;        wait_ms(200);
    OCR0A=2;        wait_ms(200);
    OCR0A=4;        wait_ms(200);
    OCR0A=8;        wait_ms(200);
    OCR0A=16;       wait_ms(200);
    OCR0A=32;       wait_ms(200);
    OCR0A=64;       wait_ms(200);
    OCR0A=128;      wait_ms(200);
    OCR0A=255;      wait_ms(200);
  }
}
/*   メインルーチン終わり   */

void wait_ms(uint16_t time){        //ウエイトルーチン 引数1で1ms
  uint16_t i;
  for(i=0;i<time;i++){
    _delay_ms(1);
  }
}








                                             
 


TOPページへもどる




















 



                           


inserted by FC2 system