PWM
TivaCローンチパッドのTM4C123GH6PMには、16ch PWM出力がついているようですが、stellarisローンチパッドのLM4F120には、PWMモジュールがついていません。代わりにタイマをPWMモードで使うことができるので試してみます。
まず、タイマをPWMモードで使う場合、Timer0~Timer5までの6つのタイマが使用でき、さらに1つのタイマを2つの16bitタイマに分けて使うので、最大12個のPWMを生成することができます。16bit精度になりますが、8ビットのプリスケーラを設定できるため、タイマカウントとしては24bitとることができ、システムクロック上限の80MHz設定の場合でも、1/80MHz << 24= 約200ms周期まで作ることができます。
サンプルとして、RCサーボ用のPWMを生成するプログラムを作ってみました。一般的なRCサーボは、20ms周期、1ms~2msパルスのPWMを使って制御します。3秒おきにパルス幅を1.0ms->1.5ms->2.0msで可変させると、RCサーボは、0°->90°->180°で動作します。
#include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "inc/hw_timer.h" #include "driverlib/gpio.h" #include "driverlib/pin_map.h" #include "driverlib/sysctl.h" #include "driverlib/timer.h" void main(void) { unsigned long cycle, // 周期用カウンタ duty; // パルス用カウンタ unsigned char cycleScale, // 周期用プリスケーラカウンタ dutyScale; // パルス用プリスケーラカウンタ // システムクロック 80MHz SysCtlClockSet( SYSCTL_SYSDIV_2_5|SYSCTL_USE_PLL|SYSCTL_XTAL_16MHZ|SYSCTL_OSC_MAIN); // モジュール有効化 SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); // タイマ設定 // Timer0を 16bitタイマ(Timer0-A、Timer0-B)として使う // Timer0-Aを PWMモードで使う TimerConfigure(TIMER0_BASE, TIMER_CFG_SPLIT_PAIR | TIMER_CFG_A_PWM); // Timer0-AをLow-Activeに設定 TimerControlLevel(TIMER0_BASE, TIMER_A, true); // GPIO設定 // Timer0-AをポートB6番ピンに出力 GPIOPinConfigure(GPIO_PB6_T0CCP0); GPIOPinTypeTimer(GPIO_PORTB_BASE, GPIO_PIN_6); // PWM周期の設定 cycle = SysCtlClockGet() / 1000 * 20; // 20ms周期のトータルカウント値取得 cycleScale = cycle >> 16; // 16bitより上位ビットを // プリスケーラカウント値として取得 cycle &= 0xFFFF; // 16bit以下を残りのカウンタ値として取得 TimerPrescaleSet(TIMER0_BASE, TIMER_A, cycleScale); TimerLoadSet(TIMER0_BASE, TIMER_A, cycle); // PWMパルスの設定 duty = SysCtlClockGet() / 1000 * 1; // 1ms周期のトータルカウント値取得 dutyScale = duty >> 16; // 16bitより上位ビットを // プリスケーラカウント値として取得 duty &= 0xFFFF; // 16bit以下を残りのカウンタ値として取得 TimerPrescaleMatchSet(TIMER0_BASE, TIMER_A, dutyScale); TimerMatchSet(TIMER0_BASE, TIMER_A, duty); // タイマスタート TimerEnable(TIMER0_BASE, TIMER_A); while (1) { SysCtlDelay(SysCtlClockGet()); // 3s待機 duty = SysCtlClockGet() / 1000 * 1.5; // パルス幅を1.5msに更新 dutyScale = duty >> 16; duty &= 0xFFFF; TimerPrescaleMatchSet(TIMER0_BASE, TIMER_A, dutyScale); TimerMatchSet(TIMER0_BASE, TIMER_A, duty); SysCtlDelay(SysCtlClockGet()); // 3s待機 duty = SysCtlClockGet() / 1000 * 2.0; // パルス幅を2.0msに更新 dutyScale = duty >> 16; duty &= 0xFFFF; TimerPrescaleMatchSet(TIMER0_BASE, TIMER_A, dutyScale); TimerMatchSet(TIMER0_BASE, TIMER_A, duty); SysCtlDelay(SysCtlClockGet()); // 3s待機 duty = SysCtlClockGet() / 1000 * 1.0; // パルス幅を1.0msに更新 dutyScale = duty >> 16; duty &= 0xFFFF; TimerPrescaleMatchSet(TIMER0_BASE, TIMER_A, dutyScale); TimerMatchSet(TIMER0_BASE, TIMER_A, duty); } }
RCサーボを制御する場合は、5V駆動が多いので、+3.3->+5Vレベルシフトが必要になる点は注意が必要です。