ATmega328PBを使う (3) PWMでLEDを点ける


初めに

LED調光にPWMを使いますので、テストプログラムで動作確認してみます。

PWMモード

ATMega328PBタイマーの動作モードには、標準(Normal)、位相補正PWM(PWM,PhaseCorrect)、CTC、高速PWM(Fast PWM)モードがあります。
TC0であれば、動作モードやTOP値などの組み合わせで下表のようなモードを選べます。

LED調光用のため高速PWMでTOP値0xFFのMode 3を設定します。
16bitタイマーのTC1などでは9bit,10bitも選択できますが。すべて8bitにしています。
クロックは8MHzの256分周としました。PWMの周期は約8.2msとなります。

PWMの配置

PWM0~3を追加します。ただし他の用途で使うポートとぶつかるので全ては使えません。
今回PWM3に割り当てたTC3のOCAは他のポート(UART)とバッティングするのでOCBだけPWMとして使います。

コンポーネントの設定


TC3/TC4をPWM出力として使う場合は注意が必要です。
TC3/TC4はOutput Compare Modulatorに接続されています。
そのためTC3/TC4を別々のPWM出力として使えません。またModulatorの設定にPD2を使います。
TC3をPWM出力とするにはPD2を出力とし、’1’を設定します。

コード

7本のPWMを同時に変化させるコードです。
PWMの初期設定後、タイマーで明るさを変化させています。

#define NUMBER_OF_PWM   (7)

uint16_t PwmTbl[] ={
     0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100,
    90, 80, 70, 60, 50, 40, 30, 20, 10
};

int PwmTblSize;
static uint8_t PwmStage[NUMBER_OF_PWM];
static uint16_t PwmTopVal;

void PWM_0AB_init(void)
{
    // Enable pin output
    PWM_0_enable_output_ch0();
    PWM_0_enable_output_ch1();

    // Set channel 0 duty cycle value register value to specified value
    PWM_0_load_duty_cycle_ch0(0x3f);
    PWM_0_load_duty_cycle_ch1(0x3f);

    // Set counter register value
    PWM_0_load_counter(0);
}

void PWM_1AB_init(void)
{
    // Enable pin output
    PWM_1_enable_output_ch0();
    PWM_1_enable_output_ch1();

    // Set channel 0 duty cycle value register value to specified value
    PWM_1_load_duty_cycle_ch0(0x3f);
    PWM_1_load_duty_cycle_ch1(0x3f);

    // Set counter register value
    PWM_1_load_counter(0);
}

void PWM_2AB_init(void)
{
    // Enable pin output
    PWM_2_enable_output_ch0();
    PWM_2_enable_output_ch1();

    // Set channel 0 duty cycle value register value to specified value
    PWM_2_load_duty_cycle_ch0(0x3f);
    PWM_2_load_duty_cycle_ch1(0x3f);

    // Set counter register value
    PWM_2_load_counter(0);
}

void PWM_3AB_init(void)
{
    PWM_3_enable();
    // Enable pin output
    PWM_3_enable_output_ch1();

    // Set channel 0 duty cycle value register value to specified value
    PWM_3_load_duty_cycle_ch1(0x3f);

    // Set counter register value
    PWM_3_load_counter(0);
}


void timerIntr(void)
{
    static unsigned int count = 0;
    uint16_t width[NUMBER_OF_PWM];

    count++;
    if (count >= 400) {
        count = 0;
        for (int i = 0;i < NUMBER_OF_PWM; i++) {
            PwmStage[i]++;
            if (PwmStage[i] >= PwmTblSize) {
                PwmStage[i] = 0;
            }
            width[i] = ((uint16_t)PwmTopVal*(uint16_t)PwmTbl[PwmStage[i]]);
            width[i] /= 100;
        }
        PWM_0_load_duty_cycle_ch0((PWM_0_register_t)width[0]);
        PWM_0_load_duty_cycle_ch1((PWM_0_register_t)width[1]);
        PWM_1_load_duty_cycle_ch0((PWM_1_register_t)width[2]);
        PWM_1_load_duty_cycle_ch1((PWM_1_register_t)width[3]);
        PWM_2_load_duty_cycle_ch0((PWM_2_register_t)width[4]);
        PWM_2_load_duty_cycle_ch1((PWM_2_register_t)width[5]);
        PWM_3_load_duty_cycle_ch1((PWM_3_register_t)width[6]);
    }
}
int main(void)
{
    /* Initializes MCU, drivers and middleware */
    atmel_start_init();
    PwmTblSize = sizeof(PwmTbl)/sizeof(uint16_t);

    PwmTopVal = 0xff;

    PwmStage[0] = 0;
    PwmStage[1] = 0;
    PwmStage[2] = 0;
    PwmStage[3] = 0;
    PwmStage[4] = 0;
    PwmStage[5] = 0;
    PwmStage[6] = 0;

    PWM_0AB_init();
    PWM_1AB_init();
    PWM_2AB_init();
    PWM_3AB_init();

    /* Replace with your application code */
    while (1) {
        ;
    }
}

内容に不備等ありましたら連絡お願いします。