Padaukマイコン内蔵の11bitPWM機能を利用して、任意のデューティのPWM波形を生成することができました。
内蔵PWM
8bitと11bit
PFS123には、Timer2とTimer3を使用した2つの8bit PWM生成回路と、専用の3ch 11bit PWM生成回路が内蔵されています。
Timerを使用したPWMの場合、PWMの周波数は設定したクロック源の周波数の1/256となります。
PWM専用の11bitPWM生成回路の場合は、カウントの最大値を設定できるため、任意の周波数に設定することができます。
今回は、100kHzの周波数のPWMを生成したいので、11bitPWM生成回路でPWMを生成してみまたいと思います。
接続
11bit PWMのブロック図は上の図のようになっています。PFS123には3chの11bitPWM生成回路が内蔵されていて、それぞれ出力できるピンが異なります。
今回はPA3にPWMを出力したいので、3ch目の11bitPWM生成回路を使用します。
まずは左上のクロック源の設定です。IHRCは一番速いクロック源となります。PFS123の場合は16MHzです。今回はIHRCを使用します。
3ch目のPWM生成回路には、分周機能やコンパレータ出力でANDする機能、PWM波形を反転する機能があります(図の中央下から右下)。これらの機能は、今回は使用しません。
最後に一番右下の部分で、生成されたPWM波形をどこのピンに出力するかを設定することができます。これをPA3にします。
プログラム
最低限のプログラム
#include <pdk/device.h> #include <stdint.h> #include "auto_sysclock.h" #include "delay.h" #define PWM_OUT 3 #define PWM_MAX 320 void main() { PDK_USE_FACTORY_IHRCR_16MHZ(); PDK_USE_FACTORY_BGTR(); AUTO_INIT_SYSCLOCK(); CLKMD &= ~CLKMD_ENABLE_WATCHDOG; // Disenable WDT PAC |= ( 1 << PWM_OUT); PWMGCUBL = (uint8_t)(PWM_MAX << 5); PWMGCUBH = (uint8_t)(PWM_MAX >> 3); PWMG2DTL = 0; PWMG2DTH = 0; PWMGCLK = (uint8_t)(PWMGCLK_PWMG_ENABLE | PWMGCLK_CLK_IHRC); PWMG2C = (uint8_t)(PWMG2C_OUT_PA3); ROP = ROP_PWM_16MHZ; while(1) { for( int16_t pwm = 0; pwm<PWM_MAX; pwm++) { PWMG2DTL = (uint8_t)(pwm << 5); PWMG2DTH = (uint8_t)(pwm >> 3); _delay_ms(10); } } }
上記が、最低限の11bitPWM生成プログラムです。
14行目でPA3ピンを出力に設定します。
15,16行目でカウンタの最大値を設定します。PWMの周波数はクロック源×2/カウンタの最大値となります。クロック源は今回はIHRC=16MHz、カウンタの最大値を320とすると、PWMの周波数は100kHzとなります。
17,18行目でPWMの閾値を設定します。11bitなので2つのレジスタに値を書き込むのですが、Lowバイトから先に書き込み、次にHighバイトを書き込みます。この順番で書き込むと、Highバイトを書き込んが瞬間に、LowバイトとHighバイトが同時に比較器にセットされます。
19行目はクロック源を設定します。PWMGCLKレジスタはライトオンリーのレジスタなので、1バイト分をまとめて書き込む必要があります。PWMGCLK_PWMG_ENABLE を書き込んでから、PWMGCLK_CLK_IHRCをORするといったことはできません。
20行目はどこのピンに出力するかを設定します。今回はPA3です。
以上の設定をすればPWM波形が生成されます。
これはPA3の波形です。デューティが0から100%まで変化する波形が出力されます。
スイッチでデューティを設定できるようにする
続いて、スイッチを操作して任意のデューティのPWMを生成できるようにします。現在の閾値がわかるように、先日作った5bitバイナリ表示器を接続します。
表示器に関して詳しくはこちらをご覧ください。

#include <pdk/device.h> #include <stdint.h> #include "auto_sysclock.h" #include "delay.h" #define PWM_OUT 3 #define PWM_MAX 320 #define PWM_ADD 4 #define TEST_PIN 4 #define TEST_PIN_OFF() PA &= ~(1 << TEST_PIN) #define TEST_PIN_ON() PA |= (1 << TEST_PIN) #define UP_PIN 7 #define DOWN_PIN 6 #define UP_STATUS() ( ( PB & (1 << UP_PIN) ) != 0 ) #define DOWN_STATUS() ( ( PB & (1 << DOWN_PIN) ) != 0 ) #define PRESS 0 #define UNPRESS 1 void main() { PDK_USE_FACTORY_IHRCR_16MHZ(); PDK_USE_FACTORY_BGTR(); AUTO_INIT_SYSCLOCK(); CLKMD &= ~CLKMD_ENABLE_WATCHDOG; // Disenable WDT //for PWM PAC |= ( 1 << PWM_OUT); PAC |= ( 1 << TEST_PIN); PWMGCUBL = (uint8_t)(PWM_MAX << 5); PWMGCUBH = (uint8_t)(PWM_MAX >> 3); PWMG2DTL = 0; PWMG2DTH = 0; PWMGCLK = (uint8_t)(PWMGCLK_PWMG_ENABLE | PWMGCLK_CLK_IHRC); PWMG2C = (uint8_t)(PWMG2C_OUT_PA3); ROP = ROP_PWM_16MHZ; //for bottom switch input PBC &= ~(1 << UP_PIN); PBPH |= (1 << UP_PIN); PBDIER |= (1 << UP_PIN); PBC &= ~(1 << DOWN_PIN); PBPH |= (1 << DOWN_PIN); PBDIER |= (1 << DOWN_PIN); //for BIN out PBC |= 0x1F; PB &= ~( 0x1F ); int16_t pwm = 0; uint8_t UP_press = UP_STATUS(); uint8_t DOWN_press = DOWN_STATUS(); while(1) { //for UP if( UP_press == UNPRESS & UP_STATUS() == PRESS) { pwm+= PWM_ADD; PWMG2DTL = (uint8_t)(pwm << 5); PWMG2DTH = (uint8_t)(pwm >> 3); if( pwm > PWM_MAX) { pwm = PWM_MAX; } UP_press = PRESS; _delay_ms(30); }else if( UP_press == PRESS & UP_STATUS() == UNPRESS) { UP_press = UNPRESS; _delay_ms(30); } //for DOWN if( DOWN_press == UNPRESS & DOWN_STATUS() == PRESS) { pwm-= PWM_ADD; PWMG2DTL = (uint8_t)(pwm << 5); PWMG2DTH = (uint8_t)(pwm >> 3); if( pwm < 0 ) { pwm = 0; } DOWN_press = PRESS; _delay_ms(30); }else if( DOWN_press == PRESS & DOWN_STATUS() == UNPRESS) { DOWN_press = UNPRESS; _delay_ms(30); } //out BIN PB = ( PB & 0xE0 )|( pwm/PWM_ADD ) & 0x1F; } }
スイッチでPWMの閾値を上下できるようにプログラムを修正しました。スイッチが押されるごとに、PWMの閾値が+4または-4され、最小から最大まで80段階のPWMが生成されます。
35から40行目でスイッチ用にIOを入力に設定しプルアップを有効にしています。
52行目はUPボタンが押されたかを検出しています。押された場合にはpwmの値を+4して30ms待ちます。30ms待つのはスイッチのチャタリング(瞬間的にON,OFFを繰り返す現象)が落ち着くのを待つためです。
63行目はUPボタンが離されたことを検出しています。この場合には何も処理をせず30ms待ちます。
70行目以降はDOWNボタンについて同様な処理をしています。
88行目は表示器にpwmの値を表示されるため、バイナリーを出力します。
上の図は閾値が10と30の時のPWMの波形です。10の場合のデューティは10/80=12.5%です。オシロスコープでの測定とほぼ合っています。30の場合は37.5%です。これもオシロスコープでの測定と合っています。
11bitPWM波形が生成された
Padauk内蔵の11bitPWM生成回路を利用して、100kHzで任意のデューティを出力できるプログラムが完成しました。
2025.1.21 続きはこちら


コメント