本文へスキップ

アトメル社のAVRマイコンをつかった電子工作および無線を使った電子工作を紹介します。





ホーム >ソフト室 >AVRマイコンで始めるC言語の使い方 >LEDを点滅させる

AVRマイコンで始めるC言語の使い方

LEDを点滅させる。

今回のプログラムはLEDを約1秒周期で点滅させます。
LEDを点滅させる方法としては以下の方法があります。この3つの方法につして一緒にプログラムをしていきます。
      (1)for繰り返しループを使用する。
      (2)_delay_msを使用する。
      (3)タイマ割込を使用する。

(1)for繰り返しループを使用する。

1.for繰り返しループの使い方
for( A; B; C)で表現します。
  A:カウンタの初期値を入れます。
  B:カウンタの上限値を入れます。
  C:カウンタの増分を入れます。
for繰り返しループでプログラムをループさせて約0.5秒間待ちます。
例)for( i = 0; i < 10; i++){処理};
変数iが0(初期値)から10(上限値)になるまで処理を繰り返し行います。iは繰り返し毎に1ずつ増えていきます。

2.変数の型宣言
for繰り返しループの中で変数を使用するときは変数の型を宣言しなければなりません。変数の型宣言のやり方は関数の最初で「int i;」というように宣言します。(宣言は何処でもできるのですが関数の最初でしておく方が後からプログラムが見やすいです。)これは、int型としてiを使用しますという意味です。
変数型 サイズ 値の範囲
char 1バイト 8ビット -128 ~ +127
unsigned char 1バイト 8ビット 0 ~ 255
int 2バイト 16ビット -32768 ~ +32767
unsigned int 2バイト 16ビット 0 ~ 65535
このほかにlong等がありますが、これらは後々必要になったときに勉強していきましょう。

3.フローチャート

フローチャート

ポートの初期化を行います。
この後はwhileループ内にプログラムを書き無限ループとします。
LEDを点灯させた後に約0.5秒待ちます。
次にLEDを消灯させた後に約0.5秒待ちます。
これを繰り返すことにより約1秒周期でLEDを点滅させます
4.プログラムの作成
0.5秒間for繰り返しループで待ちます。プログラムは1MHzで動作していますから、1クロックは1usです。0.5秒は500,000クロックです。また、ATmeg328Pほとんどの命令を1クロックで1命令実行します。C言語でプログラムを作る場合はアセンブラの様にループで何クロック使うか正確に知ることは難しいですが、今回は4クロックくらいで1ループとあたりを付けてfor繰り返しループを作ってみます。作ったプログラムの点滅の長さをオシロスコープで測定し、繰り返しループを調整します。または、AVR Studio 6のシミュレータを使って繰り返しループの長さを測定します。
/*
 * avr002.c
 * LED点滅させる。 
 * (1)forループを使う
 * Created:
 *  Author:
 */ 


#include <avr/io.h>


int main(void)
{
        unsigned int i, j;
        
        // ポートの初期化を行います。
        // ポートB初期設定
        DDRB = 0xff;    // PB7~PB0を出力に設定します。
        PORTB = 0;              // PB7~PB0に'L'を出力します。
        // ポートC初期設定
        DDRC = 0x3f;    // PC7,PC6は初期(入力)、PC5~PC0を出力に設定します。
        PORTC = 0;              // PC7~PC0に'L'を出力します。
        // ポートD初期設定
        DDRD = 0xf0;    // PD7~PD4は出力、PD3~PD0は入力に設定します。
        PORTD = 0;              // PD7~PD0に'L'を出力します。
        

    while(1)
    {
                PORTD |= 0xf0;  // PD7~PD4に'H'を出力してLEDを点灯させます。

            for ( i = 0; i < 13; i++)
                {
                        for ( j = 0; j < 10000; j++);
                }
                PORTD &= 0x0f;  // PD7~PD4に'L'を出力してLEDを消灯させます。
                                
            for ( i = 0; i < 13; i++)
                {
                        for ( j = 0; j < 10000; j++);
                }                       
    }
}
5.LEDの点滅をオシロスコープで確認

オシロスコープ波形

LEDの点滅の周期を測定したところ、1.03秒でしたので、約1秒周期でLEDを点滅することができました。
オシロスコープがない場合は、時計の秒針を見て、ほぼ1秒周期で点滅していれば良しとします。

(2)_delay_msを使う

1._delay_msの使い方
これを使用するときはdelay.hをインクルードします。
delay.hの前に「F_CPU 1000000UL // 1MHz」を定義しておきます。

2.プログラムの作成
ポートの初期化部分を関数にしています。
/*
 * avr003.c
 * _delay_msを使う
 * Created: 
 *  Author: 
 */ 
#define    F_CPU    1000000UL      // 1MHz

#include <util/delay.h>
#include <avr/io.h>

void InitHard( void);   // ポートの初期化

int main(void)
{
        InitHard();
        
    while(1)
    {
                PORTD |= 0xf0;  // PD7~PD4に'H'を出力してLEDを点灯させます。
                _delay_ms( 500);
                
                PORTD &= 0x0f;  // PD7~PD4に'L'を出力してLEDを消灯させます。
                _delay_ms(500);
    }
}

void InitHard( void) // ポートの初期化
{
        // ポートB初期設定
        DDRB = 0xff;    // PB7~PB0を出力に設定します。
        PORTB = 0;              // PB7~PB0に'L'を出力します。
        // ポートC初期設定
        DDRC = 0x3f;    // PC7,PC6は初期(入力)、PC5~PC0を出力に設定します。
        PORTC = 0;              // PC7~PC0に'L'を出力します。
        // ポートD初期設定
        DDRD = 0xf0;    // PD7~PD4は出力、PD3~PD0は入力に設定します。
        PORTD = 0;              // PD7~PD0に'L'を出力します。
}
3.LEDの点滅をオシロスコープで確認

周期は1秒であることが確認できました。



















4.LEDの点滅プログラムの再考
上記のプログラムではwhileループ内に同じようなプログラム(_delay_ms)を2回書いています。もう少しC言語ぽくしてみましょう。以下のようにすると少しC言語ぽくなったように思いませんか?
whileループ内のみ書いています。
    unsigned char d;

    while(1)
    {
                d = PORTD;                              // PORTDの値を変数dに記憶しておきます。
                PORTD = ~(d & 0xf0) | (d & 0x0f);       // PD7~PD4を反転した値をPORTDに出力します。
                _delay_ms( 500);
    }

(3)タイマー割り込みを使う

1.タイマの使い方
1.1.タイマ設定値
ATmega328のタイマは、タイマ0、タイマ1、タイマ2の3種類のタイマを持っています。タイマ0とタイマ2は8ビットタイマです。タイマ1は16ビットタイマです。以下に設定できる最大タイマ値を示します。

最大設定タイマ値(クロックが1MHzの場合)
分周比 8ビットタイマ[ms] 16ビットタイマ[ms]
0.255 65.535
2.040 524.280
64 16.320 4194.240
256 65.280 16776.960
1024 261.120 67107.840
今回は0.5秒が必要となりますのでタイマ1(16ビットタイマ)を使用します。
タイマの動作モードには標準モード、CTCモード、PWMモードがあります。今回はCTCモードを使用します。CTCモードは設定された値までカウントアップするとカウンタは’0’にクリアされ再度設定された値までカウントアップを行います。以下これを繰り返します。
カウントアップ時間T秒は以下の式で示されます。この式から、ちょうど0.5秒になる分周比は8です。また、OCR1Aの設定値は62499(16進はF423h)です。
T[秒] = fclk[Hz] /( N X ( 1 + OCR1A))
          fclk     : マイコンのクロック
   N        :分周比
         OCR1A:設定値を入れる
1.2.レジスタの設定方法
CTCモード設定
WGM13 WGM12 WGM11 WGM10
0 1 0 0

分周比N
分周比 CS12 CS11 CS10
停止 0 0 0
1(分周比無し) fclk / 1 0 0 1
8分周       fclk / 8 0 1 0
64分周       fclk / 64 0 1 1
256分周       fclk / 256 1 0 0
1024分周              fclk / 1024 1 0 1

CTCモード設定及び分周比を設定するレジスタです。CS12~CS10に"000"以外を設定するとタイマは動作を開始します。従って全ての設定を行った後にCS12~CS10に値を設定してタイマの動作を開始させます。
TCCR1Bレジスタ設定値
ビット名 ICNC1 ICES1 - WGM13 WGM12 CS12 CS11 CS10
設定値 0 0 0 0 1 0 1 0

タイマの周期Tを設定するレジスタです。これはコンペアマッチAレジスタといい、データ長は16ビットです。
OCR1Aレジスタ設定値
ビット 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
設定値 1 1 1 1 0 1 0 0 0 0 1 0 0 0 1 1

タイマ割込を許可するレジスタです。
TIMSK1レジスタ
ビット名 - - ICIE1 - - OCIE1B OCI1A TOIE1
設定値 0 0 0 0 0 0 1 0
ビット名 内容 1 0
ICIE1 インプットキャプチャ割込 許可 禁止
OCIE1B コンペアマッチB割込 許可 禁止
OCIE1A コンペアマッチA割込 許可 禁止
TOIE1 オーバーフロー割込 許可 禁止

2.割込の設定

タイマ割込の設定フロー

タイマの設定を行い、タイマ割込を許可しした後に全体の割込を許可します。

3.プログラムの作成
割り込み処理を行うにはinterrupt.hをインクルードしておきます。
割込処理はSIGNAL(割込イベント)で書くことができます。割り込み処理については「ツールの割込関数SIGNAL」を参照してください。
/*
 * avr005.c
 * タイマ割込を使う
 * Created:
 *  Author:
 */ 


#include <avr/io.h>
#include <avr/interrupt.h>

void InitHard( void);   // ポート及びタイマ設定の初期化

int main(void)
{
        InitHard();                     // ポート及びタイマ設定の初期化
        
        sei();                          // 全体の割込を許可
        
    while(1)
    {
        //TODO:: Please write your application code 
    }
}

void InitHard( void)            // ポート及びタイマ設定の初期化
{
        // ポートB初期設定
        DDRB = 0xff;            // PB7~PB0を出力に設定します。
        PORTB = 0;              // PB7~PB0に'L'を出力します。
        // ポートC初期設定
        DDRC = 0x3f;            // PC7,PC6は初期(入力)、PC5~PC0を出力に設定します。
        PORTC = 0;              // PC7~PC0に'L'を出力します。
        // ポートD初期設定
        DDRD = 0xf0;            // PD7~PD4は出力、PD3~PD0は入力に設定します。
        PORTD = 0;              // PD7~PD0に'L'を出力します。
        
        
        TCCR1B |= (1 << WGM12);         // タイマ1をCTCモードに設定
        OCR1A = 62499;                  // コンペアマッチAレジスタに62499(0xf423)を設定
        TIMSK1 |= (1 << OCIE1A);        // コンペアマッチA一致で割込を許可します。
        TCCR1B |= (1 << CS11);          // 8分周でタイマをスタートさせます。
}

SIGNAL( TIMER1_COMPA_vect)              // コンペアマッチA割込が発生したときの処理を行います。
{
        unsigned char d;
        
        d = PORTD;                                                      // PORTDの値を変数dに記憶しておきます。
        PORTD = ~(d & 0xf0) | (d & 0x0f);       // PD7~PD4を反転した値をPORTDに出力します。
}
4.LEDの点滅をオシロスコープで確認
点滅の周期は1秒でした。

これでLEDの点滅は終了です。
次回は、スイッチの入力について勉強します。
前ページ次ページ