PR

PICマイコン(PIC16F1827)でRGB LEDを操作する

組み込みエンジニア
本記事はプロモーションが含まれています。

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

PIC16F1827のDOとNOPを使用することでマイコン内蔵のRGB LEDの操作して色のパターンを生成して点灯させることができます。DO出力をNOPで遅延させながらタイミング波形を生成し点灯のパターンを切り替えて動作確認を行いました。

RGB LEDはマイコン内蔵RGB LED 5mm PL9823-F5(秋月電子で購入)を使用しています。PIC16F1827で動作確認したことについてリンクをまとめています。

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

マイコン内蔵RGB LEDを操作する

引用:PL9823のデータシート Timing waveform
引用:PL9823のデータシート Timing waveform

RGB LEDはユニポーラゼロ調整コードを使用します。一色あたり8ビットの構成になるため256パターンの調整ができます。RGB LEDは赤・緑・青の3色の調整を行うためプロトコル(Timing waveformの集合体)は24ビット構成になります。Timing waveformはHighでスタートしHighの長さとLowの長さによって1の指定か0の指定かを判定します。

PL9823のデータシートによると1 codeとして認識させるためには標準でT1H(1 code, high level time)が1.36usとT1L(1 code, low level time)が0.35us必要です。

0 codeとして認識させる場合は標準でT0H(0 code, high level time)が0.35usとT0L(1 code, low level time)が1.36usが必要です。誤差を含めると標準±150nsの間で制御する必要があります。

PL9823を制御するためにPIC16F1827のシステムクロックを32MHzに設定した状態でDOを操作しNOPで待機させてタイミング波形を生成します。

NOPは1命令分(動作クロック1パルス)処理せず待機させるものです。例えば動作クロックが1MHz(1us)の場合は1us間何も処理せず待機する動作になります。

広告
PR:わからないを放置せず、あなたにあったスキルを身に着けるコツを教える テックジム 「書けるが先で、理解が後」を体験しよう!

タイミング波形を生成する

タイミング波形を生成の説明
タイミング波形を生成の説明

PIC16F1827はSystem Clockが4パルス分で1つの命令を処理します。System Clockが32MHzの場合は動作クロックが8MHzになるため1命令当たり125nsが最小の動作の分解能になります。RGB LEDの1bit分に1 codeを指定する例について説明します。

最初にT1Hを生成します。codeは必ずHighから始まるのでDOをHighにします。DOをHighにする処理で2命令(Program Memoryを確認すると2ステップ)となるので250nsになりますが、T1Hは標準で1.36us必要なのでNOPによってDOのHighの区間を調整します。

NOPは1命令の待機なのでNOP1つあたり125ns経過します。NOPを9回連続で入れると0.125×9=1.125us経過します。DOの操作とNOPによる遅延を合わせると約1.38usになりT1Hに相当します。

次にT1Lを生成します。DOをLowにする処理で2命令となるので250nsになりますが、T0Lは標準で0.35us必要なのでNOPによってDOのLowの区間を調整します。

NOPを入れると0.125usになります。DOの操作とNOPによる遅延を合わせると約0.38usになりT1Lに相当します。

RGB LEDを操作の2ビット目以降も同様にタイミング波形を生成することでLEDの点灯パターンを作ることができます。

NOPを使ったタイミング波形の生成例

生成例ではMCCが生成したIO_RA0_SetHigh()とIO_RA0_SetLow()を使用しています。IO_RA0_SetHigh()はRA0をHighにするものでIO_RA0_SetLow()はLowにするものですがDOとして説明します。

void Set1code(void){
    //1 codeの例
    IO_RA0_SetHigh();//DOをHIGH
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    IO_RA0_SetLow();//DOをLOW
    NOP();
}

1 codeの生成の例を説明します。T1Hを生成するためDOをHighにします。その後NOP()を連続で10個置いてDOのHighを遅延します。次にT1Lを生成するためDOをLowにします。その後NOP()を連続で2個置いてDOのLowを遅延します。

void Set0code(void){
    //0 codeの例
    IO_RA0_SetHigh();//DOをHIGH
    NOP();
    IO_RA0_SetLow();//DOをLOW
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();   
}

0 codeの生成の例を説明します。T0Hを生成するためDOをHighにします。その後NOP()を連続で2個置いてDOのHighを遅延します。次にT0Lを生成するためDOをLowにします。その後NOP()を連続で10個置いてDOのLowを遅延します。

NOPが複数回繰り返しのためにfor()文を使用すると条件判断の処理が入るため処理間隔が長くなってしまうため注意が必要です。

1 codeと0 codeのパターンを関数化してタイミング波形を生成していますが、関数のコールにも命令による遅延が発生するため注意が必要です。

スポンサーリンク

MCCの設定

PIC16F1827の設定はMCCを使って行っています。MCCの使い方については下記記事にまとめています。

MPLAB Code Configurator(MCC)の追加と使い方

今回はMCCを使用して動作クロックとTMR0の設定を行っています。

System Moduleの設定

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

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

内部クロックを使用するためINTOSCを選択しています。NOPの分解能を上げるためシステムクロックを最大にするためPLLを使用します。System Clock SelectをFOSCを指定しIntemal Clockを8MHz_HFを指定します。PLL Enabledにチェックを入れるとPLLが動作しSystem Clockが32MHzになります。

広告

TMR0を設定する

TMR0はDevice Resources内のPeripherals欄のTimerを選択しTMR0をクリックして追加します。

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

Timer Clock内でクロックに関する設定を行います。Clock Sourceは内部クロックを使用しているためFOSC(FOSC/4)を指定します。PrescalerはClock Sourceに対してTMR0のカウントアップする分周比を指定します。

Timer PeriodはTMR0がオーバーフローするタイミングを設定します。1ms毎にオーバーフロー割り込みが発生しますがCallback Function Rateを0x0A(10)を指定しているため10ms毎にコールバック関数が呼び出されます。

void mainTimer(void); //タイマー0割り込みのコールバック関数
//コールバック関数の使用例
TMR0_SetInterruptHandler(mainTimer); //コールバック関数を指定

上記例ではmainTimer()が10ms毎にコールされます。

広告
マイベスト3年連続1位を獲得した実績を持つ実践型のプログラミングスクール

MCCが生成した関数の使用例

IO_RA0_SetHigh();//DOをHIGH
IO_RA0_SetLow();//DOをLOW

IO_RA0_SetHigh()関数はRA0のDO出力をHighにします。IO_RA0_SetLow()関数はRA0のDO出力をLowにします。他のポートのDOの場合はRA0の部分が対象のポート名になります。Header Files内のpin_manager.h内で#defineで定義されています。

スポンサーリンク

動作確認

PL9823の動作確認回路
PL9823の動作確認回路

PIC16F1827のRA0とPL9823のDINを接続します。データシートには外付けの部品不要と記載がありますが回路例では抵抗が実装されています。回路例では電流制限する抵抗を実装しています。

電源をONするとタイミング波形の生成が始まりPL9823の操作を開始します。500ms毎に赤色、緑色、青色の順番に色のパターンを変更しながら点灯/消灯させます。RGBの色をすべて0 codeを指定すると消灯します。

動作確認(左:赤、中央:緑、右:青)
動作確認(左:赤、中央:緑、右:青)

PL9823が色を変えながら点灯/消灯していることが確認できました。NOPよるタイミング波形の生成がうまくいっていることが確認できました。PIC16F1827のピン配置は以下の通りです。

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

1kΩの抵抗でも明るい感じがしたので抵抗を上げようとしましたがRGBの輝度に対する電圧が異なるため電流を絞り過ぎると色味が変わってしまうため注意が必要だと感じています。

PR:スキマ時間を有効に!スマホで学べる人気のオンライン資格講座【スタディング】まずは気になる講座を無料で体験しよう!

ソースコード全体

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

#include "mcc_generated_files/mcc.h"

#define TIME_OFF -1     //タイマーを使用しない場合
#define TIME_UP 0       //タイムアップ
#define LED_FL 50

int16_t timLed;
uint8_t ledmd;

/* プロトタイプ宣言*/
void mainApp(void);
void mainTimer(void);
void Set1code(void);
void Set0code(void);

/* Main application  */
void main(void)
{
    SYSTEM_Initialize();
    TMR0_SetInterruptHandler(mainTimer);
    INTERRUPT_GlobalInterruptEnable();
    INTERRUPT_PeripheralInterruptEnable();

    while (1)
    {
        mainApp();
    }
}
/* TMR0オーバーフロー割り込みでの処理  */
/* タイマ管理関数 */
void mainTimer(void){
    
    if( timLed > TIME_UP ){
      timLed--;
    }
}
/* メイン処理 */
void mainApp(void){

    if( timLed == TIME_UP){
        timLed = LED_FL;
        
        switch(ledmd){
            case 0: //redのみすべて1 code
                //red
                Set1code();
                Set1code();
                Set1code();
                Set1code();
                Set1code();
                Set1code();                
                Set1code();
                Set1code();
                //green
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                //blue
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                break;
            case 2: //greenのみすべて1 code
                //red
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                //green
                Set1code();
                Set1code();
                Set1code();
                Set1code();
                Set1code();
                Set1code();                
                Set1code();
                Set1code();
                //blue
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();                
                break;
            case 4: //blueのみすべて1code
                //red
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                //green
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                //blue
                Set1code();
                Set1code();
                Set1code();
                Set1code();
                Set1code();
                Set1code();                
                Set1code();
                Set1code();                             
                break;
            case 1:
            case 3:
            case 5:
                //すべて0 code
                //red
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                //green
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                //blue
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                Set0code();
                break;
        }
        
        if( ++ledmd >= 6){
            ledmd = 0;
        }
    }
}
/* 1 code のタイミング */
void Set1code(void){
    //1 codeの例
    IO_RA0_SetHigh();//DOをHIGH
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    IO_RA0_SetLow();//DOをLOW
    NOP();
}
/* 0 code のタイミング */
void Set0code(void){
    //0 codeの例
    IO_RA0_SetHigh();//DOをHIGH
    NOP();
    IO_RA0_SetLow();//DOをLOW
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
    NOP();
}
/* End of File */

本ソースコードはMPLAB X IDEにMCCのプラグインをインストールしていることが前提となります。MCCをインストールする方法は下記記事を参考にしてください。

MPLAB Code Configurator(MCC)の追加と使い方

関連リンク

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

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

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

広告

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

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