【C言語】PICマイコンのLED点滅プログラム(Lチカ)を1から書いてみる【PIC16F1938】

Pocket

config部分

config部分は自動で書いてくれる

前回、PICのプログラムの基本構成を学びました。今回は、実際に書いていきます。




まず一番難しそうなconfig部分ですが、ほぼクリックとコピペだけで行けます。

  1. MPLAB X IDEを開き、上のProductionタブの中のSet Configuration Bitsを選択
  2. すると、下のような画面が画面右下に出ます。これはこのPICのコンフィグ一覧です。Optionの列をポチポチクリックしながら設定していきます。
  3. 一番上の項目であるFOSC(クロックをどうするか)の設定をします。クロックは非常に短い周期でオンオフをしてくれる部品です。一回オンオフをすると、時計の秒針のように次の位置(PICでは命令)へと進んでくれると思えばいいです。ですので、クロックがないとコンピュータは動きません。このPICは内臓のクロック発振子があるので、それを使おうと思います。この内部発振子を表すINTOSCを選び、クリックします。
  4. 他の項目は全てOFFにします。
  5. 画面下にあるGenerate Source Code to Outputボタンをクリックすると、このようなコードが自動出力されます。
  6. これをctrl + Aで全選択(もしくはマウスをドラッグして選択)し、ctrl + Cでコピーします。
  7. コピーしたものを上のソースコード画面へctrl + Vで貼り付けます。すると、このようにほぼ自動でconfig部分を書くことができました!簡単ですね!

PICの初期設定

内部クロック周波数の設定(OSCCON)

内部クロックはここで設定します。設定しない場合は、デフォルトで31kHzとなっています。データシート上でctrl + Fを押して検索欄を開き、OSCCONと入力してください。110ページにこのようなページが見つかります。

OSCCONレジスタは、8bitで構成されるレジスタであることが分かります。スマホでWiFiをオンオフするように、それぞれ1か0を代入すればいいわけです。

まずビット7から。PLLENとは、クロックを4倍にする機能です。これは、先程configでOFFにしたので、PLLEN=0の場合に該当します。そのまま無効にしたいので、ビット7は0。


      OSCCON = 0b0   //途中経過。0bは右の数字が2進数であることを示します。
    

次に内部発振子のクロック数を設定します。IRCFと書かれた6~3bit目を使うみたいですね。この周波数が高いほど処理スピードが速いと言いましたが、速ければ消費電力も多くなります。今回は、8MHzくらいにしておきましょう。このデータシートより、1110とすれば8MHzになることが分かります。


      OSCCON = 0b01110
    

ビット2は何も割り当てられていないので、0です。

最後にビット0-1ですが、ここではどの発振子を使うかを設定します。(実はPIC外部に発振子を繋げることもできるのです。むしろ昔はそれしかなかった)CONFIGで設定したINTOSC(内部発振子を使用)にしたいので、これらのbitはどちらも0です。

上記すべてをまとめると、2進数表示である0bを付けて、


      OSCCON = 0b01110000;
    

となります。レジスタはこのように設定していきます。

アナログ入力の設定(ANSEL)

PICにはアナログ信号の強度を調べる機能が付いているピンがあります。例えばRA1ピンに3Vが入力されたら、電源が5Vの場合では3/5に当たる数値がどこかのレジスタに勝手に代入→それを適当な変数に代入して温度や湿度に変換する という使い方をします。また、ANSELの後にはAやB等のアルファベットが付きます。これは、ポート名と呼ばれるものです。データシートを見てみましょう。

RA~やRB~,RC~がありますね。このアルファベットは、ピンをある程度まとめる役割をしています。ピンをまとめるレジスタが8bitなため、全てAではまとめ切れません。このひとまとめのことをポートと呼んでいます。AだったらポートAです。レジスタの最後にAとついていたら、ポートAのピンの何かの機能を設定するんだなと思えばOKです。

ポートAにはアナログに使えるピンが6つあるので、bit5まで設定可能です。今回はアナログ入力機能は使用しないので、


      ANSELA = 0b00000000;
      ANSELB = 0b00000000;
    

とします。ANSELBも同様です。

ピン入出力の設定(TRIS)

TRISレジスタは、ポートのピンを出力にするか入力にするかを決めるレジスタです。例えばTRISA0であれば、RA0に対応しています。今回はLEDに信号を送ってチカチカさせるので、出力ですね。出力の時は、そのピンのTRISにを入れます。また、入力にしていると間違えてそのピンに電圧がかかった時、誤動作をする可能性があります。ですので、入力に使わないピンは0(出力)にしておきましょう


      TRISA = 0b00000000;
      TRISB = 0b00000000;
      TRISC = 0b00000000;
    

ピンのオンオフ設定(PORT)

TRISレジスタではそのピンを出力にするかどうかを設定しただけで、実際にそのピンからHIGH(5V)が出力されるわけではありません。これを設定するのがPORTレジスタです。例えばRA2に繋がっているLEDを付けたいときは、


      PORTA = 0b00000100;
    

とします(RA0~RA7まであるので3bit目)。

今回は初期化を目的としているので、まず全てのポートのピンはオフにしておきましょう。オフはそのまま0です。


      PORTA = 0b00000000;    //2進数で書いた場合
      PORTB = 0x00;          //16進数で書いた場合
      PORTC = 0;            //10進数で書いた場合
    

上記のように、数値が同じであれば何進数で代入してもOKです。内部では同じ数字ですしね。初期化では基本的に0にすることが多いのでこれでいいですが、見て一発でわかる2進数で書くことをお勧めします。(PICのサンプルプログラム等を公開してくださっている方々は、16進数でレジスタの設定をしていることが多いです。わからなくなったら2進数に直して参照してみましょう。)

これで初期設定は終了です。これを毎回書くのは面倒なので、関数にしてしまいましょう。関数名は適当で構いません。こうすれば、PIC16F1938を使う時はコピペで初期設定ができます。


    void PICinit(){
      OSCCON = 0b01110000;
      ANSELA = 0b00000000;
      ANSELB = 0b00000000;
      TRISA  = 0b00000000;
      TRISB  = 0b00000000;
      TRISC  = 0b00000000;
      PORTA  = 0b00000000;    //2進数で書いた場合
      PORTB  = 0x00;          //16進数で書いた場合
      PORTC  = 0;            //10進数で書いた場合
    }
    

LED点滅部分

RAという表現

いよいよmain部分です。ここで、RA1という表現をまず確認しましょう。これは、PORTAレジスタの1bit目を抜き出した1bitレジスタと同じです。


        PORTA = 0b00000010;    //RA1ピンをHIGH(5V)にする
        RA1   = 1;             //RA1ピンをHIGH(5V)にする
      

つまり、上は同じ意味となります。このように少ないピンを制御するときはRA~やRB~,RC~と書いた方が楽ですね。

__delay_ms()関数

ピンをオンにしても、ある程度時間が経ってくれないと点滅しません。特に8MHzなんて高速にしていたら、


        while(1){
          RA1 = 1;    //RA1をHIGHに(LEDを点灯)
          RA1 = 0;    //RA1をLOWに(LED消灯)
        }
      

こうしても人間の目では点滅しているようには見えません。そこで、点灯した後いくらか待ってくれる関数が必要ですね。それをかなえるのが__delay_ms()関数です。

「そんな関数いったいどこから持ってきたんだ(includeしたんだ)…」と思うでしょう。実はconfig設定の時に下の方でしれっと読み込んでいたのです。


        #include <xc.h>
      

という部分がconfigの下にあると思います。このヘッダファイルにはPICで使える関数が入っています。

__delay_ms()関数は引数×1ms(1ミリ秒)だけ待つ関数です。0.2秒待ってほしい時は


        __delay_ms(200);
      

とします。かなり簡単。ですがこのままビルドすると「_XTAL_FREQが設定されてないよ!」という謎指摘を受けます。実は__delay_ms関数は発振子の発振(クロック)した数がある値を超えるまでループを続けるという処理をしています。つまり、クロック数をどこかで教えてあげないといけないのです。これを、__delay_ms関数は_XTAL_FREQという変数から取ってきています。(XTAL = クリスタル(水晶)、FREQ = frequency(周波数))

「さっきOSCCONでクロック数指定したよね?そこ見てくれよ…」という感想しか浮かびませんが、従いましょう。#includeの下あたりに#define A B(AをBとして扱う)を使い、こんな感じで指定します。


        #include <xc.h>
        #define _XTAL_FREQ 8000000
      

main部分(完成)

先程の超高速ループ内に200msの待機(よく遅延と呼ばれる)を入れたmain関数はこうなります。


          int main(void){
            PICinit();      //PICを初期化
            while(1){
              RA1 = 1;            //RA1のピンをHIGH
              __delay_ms(200);    //200ms遅延
              RA1 = 0;
              __delay_ms(200);
            }


            return 0;
          }
        

完成したLED点滅プログラム

上の3つを合体させると、プログラムの完成です! ビルドしてエラーがないか確かめてください。

PICへの書き込み方法はこちら PICにプログラムを書き込んでみよう


// PIC16F1938 Configuration Bit Settings

// 'C' source line config statements

// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable (All VCAP pin functionality is disabled)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = ON      // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = ON         // Low-Voltage Programming Enable (Low-voltage programming enabled)

// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.

#include <xc.h>
#define _XTAL_FREQ 8000000

void PICinit(){
  OSCCON = 0b01110000;
  ANSELA = 0b00000000;
  ANSELB = 0b00000000;
  TRISA  = 0b00000000;
  TRISB  = 0b00000000;
  TRISC  = 0b00000000;
  PORTA  = 0b00000000;    //2進数で書いた場合
  PORTB  = 0x00;          //16進数で書いた場合
  PORTC  = 0;            //10進数で書いた場合
}

int main(void){
  PICinit();      //PICを初期化
  while(1){
    RA1 = 1;            //RA1のピンをHIGH
    __delay_ms(200);    //200ms遅延
    RA1 = 0;
    __delay_ms(200);
  }


  return 0;
}
        

これで、自分でプログラムが書けるようになりました。次回はLEDで遊んでみて、次々回ぐらいにスピーカーも使おうと思います。それまでは基本設定は同じなので、上記のPICinit関数をコピペで行けます!PICワールドへようこそ!

次回→PICで電子ホタルを作る




Pocket

関連記事   【PICマイコン】RGBフルカラーLEDで電子ホタルを作る【PIC16F1938】

1件のコメント

  1. 具体的で素晴らしいと思います
    書き方もわかりやすいです
    ありがとうございました

返信を残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です