« 1023 vs 1024:AVRマイコンとPICマイコンのデータシートより | トップページ | 武田コーポレーション VOLCANO NZ単3 (1300mAh) 600cycで終了 »

2025年3月22日 (土)

Arduino、analogWriteは捨てちゃえ。ちゃんとしたPWMの例

analogReadでの電圧値変換問題(1023 vs 1024)だけじゃなく
2020年8月13日:Arduino、analogWriteは捨てちゃえ。ちゃんとしたPWMを使おう
analogWriteにも噛み付いています。

で、まっとうなPWM出力制御の例を出していなかったかと
  (アプリケーション・スケッチの中では
   いっぱい使っているんで探せば出てくるけど)
いうことで、OC2AとOC2Bの2chをPWM出力にして
まっとうなPWMの方法(analogWriteと大きい声で
言っても恥ずかしくないような)を示しておきます。

/*****  タイマー2でまっとうなPWMを *****/
// タイマー2 PWM出力 0~255,256を許容
// 0:duty=0, 128:duty=128/256, 255:duty=255/256
// 256:duty 100%(Hレベル)
/***** PWM出力 OC2A *****/
void pwm2a(short d)
{
if(d < 0) d = 0; // 0未満は0に
if(d < 256){ // 255まで
OCR2A = (byte)(255 - d); // duty 0~255/256 PWM
TCCR2A |= 0b11000011;
// |||| ++--- 8bit 高速PWM動作
// ||++------- OC2B (PWM反転動作)
// ++--------- OC2A PWM反転動作
}
else{ // 256以上はHレベル出力
PORTB |= (1 << PB3); // PB3をHに
TCCR2A &= 0b00110011;
// |||| ++--- 8bit 高速PWM動作
// ||++------- OC2B (PWM反転動作)
// ++--------- OC2A OC2A切断
}
}

/***** PWM出力 OC2B *****/
void pwm2b(short d)
{
if(d < 0) d = 0; // 0未満は0に
if(d < 256){ // 255まで
OCR2B = (byte)(255 - d); // duty 0~255/256 PWM
TCCR2A |= 0b00110011;
// |||| ++--- 8bit 高速PWM動作
// ||++------- OCR2B PWM反転動作
// ++--------- OCR2A (PWM反転動作)
}
else{ // 256以上はHレベル出力
PORTD |= (1 << PD3); // PD3をHに
TCCR2A &= 0b11000011;
// |||| ++--- 8bit 高速PWM動作
// ||++------- OCR2B OC2B切断
// ++--------- OCR2A (PWM反転動作)
}
}

/***** SETUP *****/
void setup() {
// タイマー2 PWM出力に初期化
// OC2A:PB3 , OC2B:PD3出力ポートに
DDRB |= (1 << PB3); // OC2A : PB3
DDRD |= (1 << PD3); // OC2B : PD3
// タイマー2 980Hz PWM出力
TCCR2A = 0b11110011;
// |||| ++--- 8bit 高速PWM動作
// ||++------- OC2B PWM反転動作
// ++--------- OC2A PWM反転動作
TCCR2B = 0b00000100;
// || |+++--- CS 1/64 250kHz → 1/256 976.6Hz(1.024ms)
// || +------ WGM22
// ++--------- FOC2
OCR2A = 255 - 0; // duty 0に
OCR2B = 255 - 0; // duty 0
TIMSK2 = 0b00000000;
// ||+--- TOIE2 割込オフ
// |+---- OCIE2A
// +----- OCIE2B
// タイマー0,1と2の前置分周器をリセット
GTCCR = 0b10000000;
// | |+--- PSRSYNC タイマ0,1
// | +---- PSRASY タイマ2
// +---------- TSM タイマ同期動作
GTCCR = 0b10000011; // リセット操作
TCNT0 = 0;
TCNT2 = 0;
GTCCR = 0b00000000; // 解除
}

// 出力データ
struct st_pwm{
short p0a; // OC0A PD6 (6)
short p0b; // OC0B PD5 (5)
short p2a; // OC2A PB3 (11)
short p2b; // OC2B PD3 (3)
};
// 順に出力
st_pwm pwm_tbl[]={
{ 1, 0, 0, 254 }, // <0>
{ 1, 1, 1, 255 }, // <1>
{ 1, 2, 2, 256 }, // <2>
{ 1, 254, 254, 0 }, // <3>
{ 1, 255, 255, 1 }, // <4>
{ 1, 256, 256, 2 }, // <5>
}; // | | | +--- OC2B PD3 (3)
// | | +-------- OC2A PB3 (11)
// | +------------- OC0B PD5 (5)
// +------------------- OC0A PD6 (6)

/***** LOOP *****/
void loop() {
byte i;
while(1){
for(i = 0; i < 6; i++){
analogWrite(6, pwm_tbl[i].p0a); // OC0A (6)
analogWrite(5, pwm_tbl[i].p0b); // OC0B (5)
pwm2a(pwm_tbl[i].p2a); // OC2A (11)
pwm2b(pwm_tbl[i].p2b); // OC2B (3)
delay(2000); // 2秒待ち
}
}
}
// 2025-03-22 JH3DBO

元のanalogWrite、これの何がおかしいのか気付いたのが
  ・2020年2月10日:ArduinoのanalogWrite 1/255なの?
このあたり。

~~~~~~~~~~~~~~~~~~~~~~~~~~~
タイマー0のanalogWriteに対して、0~255の数値を順に
与えてみると・・・周期1.024msのパルスに対して、
  0ならずっとLOW
  1だと8usのHIGHパルス  ▼1
  2だと12usのHIGHパルス
   :
  253だと8usのLOWパルス
  254だと4usのLOWパルス
  255だとずっとHIGH
が観察されます。
タイマー0に関して、値0と1のところ▼1で、ほんとなら4usステップ
になって欲しいのが8usとなっていて、値1~255とは異なるピッチ
なっていることがわかります。

デューティ50%が欲しい時は127。
普通の8bit D/Aコンバータだと128で半値がでてきます。

8bit D/Aコンバータの代わりになるようなPWMだと、どんな信号に
なるかというと。
  0 → デューティ0%で 全区間L
  1 → デューティ 1/256%
  2 → デューティ 2/256%
  :
 128 → デューティ 128/256% = 50% 方形波
  :
 254 → デューティ 254/256%
 255 → デューティ 255/256%
  ここでおしまい。 全区間Hはなし
~~~~~~~~~~~~~~~~~~~~~~~~~~

「半値」や「1/4値」でチェックすると、「何かおかしい」
と気付くはずなんですが・・・

上に上げた「まっとうなPWM」では、0~255で
0/256~255/256を出力。
そして8bitを越えるけど256でduty 100%となるような
処理を入れ込みました。

オシロで波形を観察するとこんな様子になります。
OC0Aがトリガー用。
OC0BとOC2Aに同じ値を入れてます。
I01b

2020年8月16日:Arduino UNOのPWM出力を(ちょっと精密に)確かめる

analogWriteも
2022年11月12日:『1/1023 vs 1/1024』問題 analogReadを2bitにして確かめてみる
と同じように2bitにしたら分かるかな。
  0はゼロ。
  1で1/4の1.25V。
  2で半値の2.50V。
  3で3.75Vが出てきます。
5.00Vは2bitのD/Aコンバータでは出せません。

  Vout = (Vref * data) / (2^n)
という式で出力が出てきます。

フルスケールの3で5.00Vを出したいとなると、
  0はゼロ。
  1で1.67V。
  2で3.33V。
  3で5.00V。
アナログ的なゲイン調整でこういったことはできますが、
半値がどこかへ行ってしまいます。

|

« 1023 vs 1024:AVRマイコンとPICマイコンのデータシートより | トップページ | 武田コーポレーション VOLCANO NZ単3 (1300mAh) 600cycで終了 »

Arduino」カテゴリの記事

1023 vs 1024」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« 1023 vs 1024:AVRマイコンとPICマイコンのデータシートより | トップページ | 武田コーポレーション VOLCANO NZ単3 (1300mAh) 600cycで終了 »