PICマイコン(PIC16F1827)でPWMを実装する

組み込みエンジニア

こんにちは、ENGかぴです。

PIC16F1827のPWM機能を持っているため振動波形を模擬してブザーを鳴らしたりデューティー比を調整したりすることで電圧を調整しながらモーターを動かしたりすることができます。MCCでPWMを実装し動作確認を行いました。

PWMを実装する

PWMとはpulse width modulationの略でありパルス幅変調です。 一定のキャリア周波数に対する波形のデューティ比(HighとLowの比率)によって電圧を調整することができます。

PWMのイメージ
PWMのイメージ

PWM制御によってトランジスタなどの素子がON/OFFを繰り返すことからOFF時には電流が流れないことから消費電流を抑えることができます。

PIC16F1827ではPWM機能としてCCP及びECCPが実装されています。PWM機能はタイマ機能と連動しているためPWM及びTMRの双方の設定が必要です。

System Moduleの設定

最初に共通事項であるSystem Moduleの設定を行います。MPLAB X IDEを起動しMCCのアイコンをクリックしてMCCを有効にします。

System Moduleの設定(MCC)
System Moduleの設定(MCC)

内部クロックを使用するためINTOSCを選択しています。クロック周波数は任意でもよいですが4MHz_Hzを選択しています。クロックを高速にするほど消費電流が増えてしまいます。他にも設定項目はありますがクロックの設定のみとしています。

CCP3(PWM)及びTMR2の設定

TMR2をDevice Resources内のPeripherals欄のTMR2を選択して追加します。

TMR2の設定(MCC)
TMR2の設定(MCC)

PWMはタイマと連動しておりPWMに指定できるタイマはTMR2・TMR4・TMR6です。PWMはCCP3(CCP4)の機能に含まれています。 Resources内のPeripherals欄のCCP3を選択して追加します。

CCP3の設定(MCC)
CCP3の設定(MCC)

Select TimerではTMR2を使用するためTimer2を選択します。TMR2の設定が終わっている場合はPWM Parametersの欄にPWMの情報が表示されます。Duty Cycleにデューティー比を入力します。

CCPR Valueはタイマのプリスケーラやクロックの条件から10ビットのタイマベースを作る仕様に基づくものです。この値を基にPWM Resolutionのビット値に換算された値がレジスタ値に設定されます。

引用:PIC16F1827のデータシート(PWMのデューティーの計算)
引用:PIC16F1827のデータシート(PWMのデューティーの計算)

例ではデューティーを50%に設定したときのCCPR Value(データシートの式よりCCPRxLを求める)は249になりますが、10ビットでの計算値となるため8ビット値に換算した値が値がレジスタ値となります。

10ビットの値を8ビット値に換算するため2回右にシフト(4で割る)した値である0x3EがCCPR3Lに設定されます。Registersでレジスタ値の詳細を確認すると一致することが分かります。

ECCP1(EPWM)及びTMR4の設定

Resources内のPeripherals欄のTMR4を選択して追加します。

TMR4の設定(MCC)
TMR4の設定(MCC)

TMR4はECCP1と連動して使用します。TMR4の割り込みを有効にして20ms毎にデューティー比を変更できるようにします。後述のAD変換の値を使用してデューティー比を変更します。 Resources内のPeripherals欄のECCP1を選択して追加します。

ECCP1(EPWM)の設定(MCC)
ECCP1(EPWM)の設定(MCC)

PMWの拡張機能としてEnhanced PWM機能があります。ブリッジ回路などでインバータ起動する際に使用する用途でPWMの出力を最大4ピンで制御する機能です。

Timer SelectにTimer4を選択します。PWM Duty Cycleは初期値として50%にしています。CCPR ValueはCCP3と同じ考え方です。Enhance PWM modeは2ピンで制御を行うためhalfbridgeを選択しています。

P1AとP1Bはお互いに反対の波形を生成しますがタイミングに遅延を持たせるためPWM parametersのPWM Delay countsに0xA(10)を入力して遅延を持たせています。

void IntTmr4(void){
    uint16_t value;
    
    value = dutyValue >> 1;
    //PWMのデューティー100%でも500となるため
    //CCPR Value相当に変換
    if( value > DUTY_MAX){
        value = DUTY_MAX;
    }
    value = value >> 2; //8ビット値に換算
    CCPR1L = (uint8_t)value;
}

IntTmr4()関数はTMR4割り込みが複数回(20ms)経過したときに処理されるコールバック関数です。AD変換で取得した値をCCPR Value相当に変換した後で8ビット値に換算しています。換算の条件はTMR4のプリスケーラやクロックの条件によって変ります。

8ビット値に換算してCCPR1Lレジスタに値を書き込むとデューティー比が変更できます。

ADCとFVR及びTMR0の設定

ADCの設定(MCC)
ADCの設定(MCC)

AD変換の値からEPWMのデューティーを変更するためADCとFVRを実装します。 Resources内のPeripherals欄のADCを選択して追加します。

AD変換値を右寄せで取得するためにResult Alignmentをrightに設定します。AD変換の基準電圧にFVRを使用するためPositive ReferenceをFVRに設定します。AD変換完了時にAD変換値を取得するため割り込みを使用します。

FVRを使ってAD変換の基準電圧を生成するためResources内のPeripherals欄のFVRを選択して追加します。

FVRの設定(MCC)
FVRの設定(MCC)

AD変換の基準電圧として使用するためFVR_buffer1 Gainに4xを設定します。

TMR0を使ってADC変換のタイミングを生成します。 Resources内のPeripherals欄のTMR0を選択して追加します。

TMR0の設定(MCC)
TMR0の設定(MCC)

TMR0を1msでオーバーフローするようにして10回経過したときコールバックによってADC変換のタイミングを生成します。

#define TIME_ADC_WAIT 10
/* タイマ管理関数(コールバック関数) */
void mainTimer(void){
    
    if( timAdc > TIME_UP ){
        --timAdc;
    }
}
/* メイン処理 */
void mainApp(void){
    
    if( timAdc == TIME_UP){
        timAdc = TIME_ADC_WAIT;
        ADC_SelectChannel(channel_AN0); //AN0を選択
        ADC_StartConversion(); //AD変換開始
    } 
}

mainTimer()が10ms毎にコールバックされてタイマtimAdcを更新します。TIME_ADC_WAITを10にしているため100ms毎にAD変換を開始します。

動作確認

PWMの動作確認用の回路図
PWMの動作確認用の回路図

PIC16F1827のPWM出力にLED・ブザー・DCモータを接続して動作を確認しました。PWM機能の動作の確認としてBZ1のブザーを鳴らします。CCP3とTMR2と連動させキャリア周波数を500us、デューティー比を50%のPWM波形を出力します。

EPWMの動作を確認のためLED1の明るさの調整とDCモーターをPWM制御によって操作します。ECCP1のEPWM機能を使用しP1AとP1BのピンからPWM波形が出力します。

P1AとP1Bは波形のONとOFFが反対の動きとなるためVR1を調整しLED1が明るくなるとP1Bのデューティー比が低下するためトランジスタを介して接続しているモータの回転が遅くなります。逆にLED1が暗くなるようにVR1を調整するとP1Bのデューティー比が高くなりモーターの回転が速くなります。

AD変換ピンとしてAN0を設けていますが可変抵抗(VR1)で電圧することでECCP1(EPWM)のデューティー比が変更できるようにします。

ブザーが鳴り続けていると耳障り(幻聴の原因)になのでボタンを押したらブザーを止める等工夫が必要だと感じました。

ピン設定は以下の通りです。

PIC16F1827のピン設定(MCC)
PIC16F1827のピン設定(MCC)

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。チェックはしておりますがコメントなど細かな部分で間違っている可能性があります。参考としてお使いいただければと思います。

#include "mcc_generated_files/mcc.h"

#define TIME_OFF -1     //タイマーを使用しない場合
#define TIME_UP 0       //タイムアップ
#define TIME_ADC_WAIT 10
#define DUTY_MAX 448
/* 変数宣言*/
int16_t timAdc;
uint16_t dutyValue;
/* プロトタイプ宣言*/
void AdcItr(void);
void mainApp(void);
void mainTimer(void);
void IntTmr4(void);
/* Main application */
void main(void)
{
    SYSTEM_Initialize();
    TMR0_SetInterruptHandler(mainTimer);
    TMR4_SetInterruptHandler(IntTmr4);
    ADC_SetInterruptHandler(AdcItr); //割り込み時AdcIrt関数をコールバックする
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    ADC_SelectChannel(channel_AN0);
    ADC_StartConversion();
    timAdc = TIME_ADC_WAIT;

    while (1)
    {
        mainApp();
    }
}
/* メイン処理 */
void mainApp(void){
    
    if( timAdc == TIME_UP){
        timAdc = TIME_ADC_WAIT;
        ADC_SelectChannel(channel_AN0); //AN0を選択
        ADC_StartConversion(); //AD変換開始
    } 
}
/* タイマ管理関数(コールバック関数) */
void mainTimer(void){
    
    if( timAdc > TIME_UP ){
        --timAdc;
    }
}
void IntTmr4(void){
    uint16_t value;
    
    value = dutyValue >> 1;
    //PWMのデューティー100%でも500となるため
    //CCPR Value相当に変換
    if( value > DUTY_MAX){
        value = DUTY_MAX;
    }
    value = value >> 2; //8ビット値に換算
    CCPR1L = (uint8_t)value;
}
/* AD変換割り込み処理 */
void AdcItr(void){

    dutyValue = ADC_GetConversionResult(); //AD変換値を格納
}
/* End of File */

本ソースコードはMPLAB X IDEにMCCのプラグインをインストールしていないと使用できません。

関連リンク

PICマイコンを使ってマイコンのレジスタの設定やMPLAB X IDEのプラグインであるMCCを使用して動作確認したことについてまとめています。

PICマイコン(PIC12F675)で実現できる機能と解説リンクまとめ

PICマイコン(PIC16F1827)で実現できる機能と解説リンクまとめ

GEEKJOB-未経験からITエンジニアに【オンライン無料体験】

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました