6.LEDを光らせてみる


ここでは、PICに書き込んだプログラムを基板にセットし、実際にLEDを光らせてみます。

LEDを光らせるプログラム
LEDの点滅
カラーLEDの点滅
指定した色で指定した回数光らせる関数を作る

ここで用いるCPU(PIC16F690)の特徴
■20ピンとちょっとI/Oポートが多いPICマイコンです。
■ADコンバータも内蔵して、4kワード,256バイトRAMと容量も大きく、価格も安い(140円)
■仕様
・最大周波数:20MHz
・電源電圧:2.0V(MAX8MHz)~5.5V(MAX20MHz)
・プログラムメモリ:4096ワード
・RAM:256バイト
・EEPROM:256バイト
・I/Oポート:最大18本
・ADコンバータ:10ビット12ch
・コンパレータ:2ch
・タイマー:8ビットx2,16ビットx1
・周辺機能:SSP,ECCP+,EUSART

LEDの点滅

MPLABの使い方で使用した「LEDの点滅」プログラムです。
「LEDの点滅」プログラム
// ファイル名 led.c
// LEDの点滅
#include <htc.h>

__CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS);

#define _XTAL_FREQ 20000000
main()
{
  unsigned int i;
  PORTA = 0;                 // PORTAを0にする
  TRISA = 0;                 // PORTAを出力に設定する
  ANSEL = 0;                 // アナログ入力をOFF
  while(1) {
    PORTA =  ~PORTA;         // PORTAを反転
    for(i=0; i<100; i++)        
       __delay_ms(10);       // 1秒ディレイ
  }
}

はじめに、// ファイル名 led.cとありますが、頭に//が付く行はコメント文になります。ここでは、先頭に何をするプログラムか分かるようにコメントしています。 コメント文は読み飛ばされるので、プログラムには影響しません。プログラムを作る際、特に長いプログラムではこまめにコメントを記入しておくと便利です。 また、/* ~コメント~ */のように/**/で囲むと複数行のコメントを記述することか可能です。

#includeは指定されたファイルの読み込みを行ないます。2行目ではhtc.hというファイルを 読み込むよう指定してます。C言語では使用する関数はすべてそのプログラムの中でで定義しなければなりませんが、その指定されたファイルにはよく使う関数などがあらかじめ定義されているので、これを読み込むことによりプログラム中に関数の定義をしなくても、使用することができるようになります。ここでは、Hitech-CでのPICの定義を読み込んでいます。

4行目にある__CONFIG はPICのハードウエア的な動作のモードを設定します。これは毎回プログラムと一緒に書き込む必要があります。

PWRTEN
パワーアップタイマーを使用する。パワーアップタイマーとは、周辺回路を安定しさせてるためのウェイト機能。
HS
外部クロック発振モードを一番高いゲインで設定。
WDTDIS
ウォッチドッグタイマーをOFFに設定。
UNPROTECT
書き込んだコードにプロテクトをかけない。
MCLRDIS
リセットピン(RA3)はI/Oピンとして使用する。
BORDIS
ブラウンアウトリセットを無効にする。これは電圧が下がり動かなくなる前にプログラムのリセットを行ない中断する機能。電圧が安定している時は無効にしておく。
IESODIS
Internal Extarnal Switchoverモードを無効。クロックの2段階(内外部)スタートアップ機能を無効にする。
FCMDIS
Fail-Safe Clock Monitorは外部のクロックが動作しないとき、内部クロックさせる機能だが、これを無効にする。

#defineでは定数を定義しています。またはマクロ定義するともいいます。
形式は次のようになります。

#define 定数名 数値

このように記述すると数値の値が定数名に置き換えられます。
上記のプログラムでは_XTAL_FREQ20000000に置き換えられ、プログラム上に_XTAL_FREQがある場合は20000000に置換されます。
この_XTAL_FREQはあとに出てくる__delay_ms( )マクロを使用する為に必要で、PICの外部クロック20MHzの値を示しています。

次はmain関数の定義です。はじめのうちはこのmain関数の{ }中に処理するプログラムを記述していきます。
C言語のプログラムでは複数の関数を組み合わせて作っていきますが、このmain関数は必ず必要でここから処理が実行されていきます。

main()
{
    関数の中身(処理)
}

ここからはmain関数の中身をみていきます。unsigned int i ; はプログラム中で使用する変数の宣言をおこなっています。
変数宣言の形式は次のようになります。

変数の型 変数名 ;

基本データの型にはint型、float型、double型、char型、void型などがあります。各型の意味は以下の表で確認してください。

データ型
意味
大きさ
扱える数値範囲
int
符号付き整数型 4バイト -2,147,483,648 ~ 2,147,483,647
float
浮動小数点数 4バイト 6桁分の精度
double
倍精度浮動小数点数 8バイト 10桁分の精度
char
文字 1バイト -128 ~ 127
void
値なし    


int i ; と記述するとi 符号付整数型の変数であるといえます。したがって、int型はそのまま使うと正数も負数も扱えますが、
今回のように「unsigned」という型修飾子といわれるものを頭に付けると負数を扱えないようにすることができます。
unsignedを使うことによって、符号を記憶しておく必要がなくなるため、より大きい正数が扱えるようになります。

データ型
大きさ
扱える数値範囲
unsigned int
4バイト 0~4,294,967,295

PORTAは6ビット(RA0~RA5)からなるI/O(入出力)ポートです。このポートの入出力設定はTRISAで行ないます。TRISAを'1'にするとPORTAは入力ポートになり、'0'にすると出力ポートになります。

次に繰り返し文について説明します。プログラムの中で同じ処理を何回も行なう場合は繰り返し文をつかいます。
この繰り返しのことをループともいいます。今回はwhile文とfor文を使っています。

while文の書式形式は次のようになります。

while(条件式)
{
    繰り返す処理
}

C言語では条件式の値が0ならば偽(flase)、0以外ならば真(true)となります。while文ではこの条件式が真(true)の時、繰り返し処理が実行されます。通常は条件を満たしている間は繰り返し処理が行なわれ、繰り返し処理の中に「条件式」の条件を変化させていくよなプログラムを書きますが、今回は条件式が(1)となってます。これは条件が変化することな状態で、永久に終わらない無限ループの意味になります。

for文の書式形式は次のようになります。

for(変数の初期化式;条件式;変数の増減)
{
    繰り返す処理
}

変数の初期値から条件式を満たすまで繰り返し処理が行なわれます。今回は変数i が0から100になるまで処理が繰り返されます。
変数の増減のi++i=i+1の意味と同じです。処理が1回終わるごとにi の値には+1され、0、1、2、3、・・・と変化し100になるまで繰り返されます。

カラーLEDの点滅

「LEDの点滅」と同様に新規にプロジェクトを作成し、ソースプログラムは次の「赤LEDの点滅」をコピーして作成し、コンパイルをします。
PICに書き込み、赤LEDを点滅しているか確認してください。

以下のプログラムをコピーして、貼り付けて使用してください。
カラーLEDはPORTAに接続されていて、赤はbit0、青はbit1、緑はbit2に割り当てられています。 赤のPOTRAのbit0はRA0と記述します。同様に青はRA1、緑はRA2になります。
プログラム中のRA0 = ~RA0;の~は否定(ビット反転)を意味しています。
bit2bit1bit0
RA2RA1RA0

「赤LEDの点滅」プログラム
// ファイル名 LED1.c
// 赤LEDの点滅
#include <htc.h>
__CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS);

#define _XTAL_FREQ 20000000
main()
{
  unsigned int i, LED=0;
  PORTA = 0;                 // PORTAを0にする
  TRISA = 0;                 // PORTAを出力に設定する
  ANSEL = 0;                 // アナログ入力をOFF
  while(1) {
    RA0 = ~RA0;              // カラーLED赤
    for(i=0; i<100; i++)        
       __delay_ms(10);       // 1秒ディレイ
  }
}


PORTA = 0;
PORTAを0にする

は電源を投入した直後にポートの値は不定なので全てゼロを出力(LED消灯)に設定しています。PORTAは下の図で説明されています。RA0からRA5までRA3を除いて入出力が可能なポートです。ポートは港という意味で、船とか飛行機が出たり入ったりする場所を模擬しています。ここで用いるRA0は港Aの0番埠頭、あるいは飛行場Aの0番ゲートだと思ってください。埠頭(ゲート)では同時に船を出港/入港させることはできません。船が衝突して事故になります。従って、出港の場合か入港の場合かを指定しておかないと大事故になります。PORTAのこれを設定するのがTRISAレジスタです。各ビットを0に指定すると出力1に設定されます。ここで注意ですが、出力に設定させた場合、そのbitに出力する装置をつなげてはいけないことです。先ほどの船の話で、船は出ていく設定なのに入港してくるので事故になります。具体的にはCPUのそのポートが壊れるか、繋げた装置の出力部分が壊れます。

TRISA = 0;
PORTAを出力に設定する

このTRISAは電源投入時には全て1に初期化されるので入力になっています。従って、LEDを点灯させるにはその3つのbitをゼロにしなくてはいけません。ここのサンプルプログラムでは手抜きで、全ポートを出力に設定していますが、今回は問題ありません。現実問題PA4,5はポートとしては用いず、20MHzのセラロックを使用する設定になっており、RA3は出力には設定できないからです。 従って、PORTAは出力としては3bitしか使えないことになっています。
下の図で、R/W-xは読み書きできて、電源投入時では値は不定という意味です。

ANSEL = 0;
アナログ入力をOFF

は電源投入時にPORTAはデジタル入出力ではなくA/Dコンバーター入力に設定されるのでこれをoffにしてデジタル入出力が出来るように設定しなければいけません。

以下のプログラムをコピーして、貼り付けて使用してください。

「カラーLEDの点滅」プログラム
// ファイル名 LED2.c
// カラーLEDの点滅
#include <htc.h>
__CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS);

#define _XTAL_FREQ 20000000
main()
{
  unsigned int i, LED=0;
  PORTA = 0;                 // PORTAを0にする
  TRISA = 0;                 // PORTAを出力に設定する
  ANSEL = 0;                 // アナログ入力をOFF
  while(1) {
    PORTA =  LED++ % 7 + 1;  // カラーLED
    for(i=0; i<100; i++)        
       __delay_ms(10);       // 1秒ディレイ
  }
}

指定した色で指定した回数光らせる関数を作る

こちらも同様に次のプログラムも動かしてみます。

前までのプログラムとは違い、main関数の他にLED関数、Delay50関数、Delay100関数が定義されています。

以下のプログラムをコピーして、貼り付けてください。

「指定した色で指定した回数光らせる」プログラム
// ファイル名 LED3.c
// 指定した色で指定した回数光らせる関数を作る
#include <htc.h>
__CONFIG(PWRTEN&HS&WDTDIS&UNPROTECT&MCLRDIS&BORDIS&IESODIS&FCMDIS);

#define _XTAL_FREQ 20000000

typedef unsigned char byte;

void LED(byte n, byte times);
void Delay50();
void Delay100();
main()
{
  unsigned int i;
  PORTA = 0;                  // PORTAを0にする
  TRISA = 0;                  // PORTAを出力に設定する
  ANSEL = 0;                  // アナログ入力をOFF
  while(1) {
    LED(1, 5);                // 赤LED
    for(i=0; i<100; i++)        
       __delay_ms(10);        // 1秒ディレイ
  }
}

void LED(byte n, byte times)
{
  byte i;
  for(i=0; i<times; i++) {
    PORTA =  n;
    Delay50();          // 50msディレイ
    PORTA =  0;
    Delay50();          // 50msディレイ
  }
}

void Delay50()          // 50m秒ディレイ
{
  int i;
  for(i=0; i<5; i++)        
    __delay_ms(10); 
}
void Delay100()          // 100m秒ディレイ
{
  int i;
  for(i=0; i<10; i++)        
    __delay_ms(10); 
}

関数にはあらかじめC言語で用意されたものもありますが、自分で関数を作ることもできます。main関数の中に長々と処理を書き込むより、まとまった処理を関数として定義するとプログラムもコンパクトになり、見やすくなります。また同じ処理を何度も行なう場合も、1度関数で定義しておけばその関数を呼び出すだけで処理できとても便利です。

関数の定義形式は次のようになります。

戻り値の型 関数名(引数型 仮引数名) { 処理内容 }

関数の定義についてLED関数を例に説明します。
void LED(byte n ,byte times)
{
  byte i;

  for(i=0; i<times; i++) {
    PORTA =  n;
    Delay50();          // 50msディレイ
    PORTA =  0;
    Delay50();          // 50msディレイ
  }
}

プログラムはmain()関数の上から順に実行されていきます。LED( )関数のところに来たらLED関数の処理へ飛びます。このとき、データとして1と5を渡します。これを実引数といいます。
LED()関数ではこの1と5をnとtimesにそれぞれ代入され処理を行ないます。このnとtimesは仮引数といいます。処理後、main( )関数に戻す値が今回はありません。
そのため、このLED関数の戻り値の型はvoid型(値なし)になっています。

目次へ戻る