KAIMU-UA1
(Kaimunantai - Universal circuit board – AVR - no.1)
ソフトウェア開発説明書
※開発環境のインストールなどはua1-00-avr-start.pdfを参照してください。
※プログラム開発環境の操作については、
Mac
ではua1-crosspackavr-xcode.pdfを、Windows
で はua1-atmelstudio-6.pdfを参照してください。1
LEDを光らせる
(1) LEDをずーっと光らせる
Xcode
やテキストエディタで、次のコードを入力します。なお、
//
で始まる文字列は人間向けの注釈ですから、入力しなくてもプログラムは動作します。
#include <avr/io.h>
int main(void) {
// I/O Port Initialization
DDRD = 0b11111111; // All PORTD ports as Output
while(1){
PORTD |= (1<<PD0); // LED ON }
return 0; }
DDRD
は、入出力ポート(I/O
ポート)の入力/出力を切り替えるレジスタです。0b11111111
は2進数です。AVR-GCC
では、0b
の後に1、0のビットパターンを書くと、 2進数として扱われます。PORTD 7 6 5 4 3 2 1 0 に対応 DDRD = 0b 1 1 1 1 1 1 1 1;
DDRD7
が1
のとき、PORTD7 (PD7)
は出力モードに設定されます。DDRD7
が0
のとき、PORTD7 (PD7)
は入力モードに設定されます。(2) コードを読み取りやすくする工夫
前のコードでは、
LED
を光らせる処理をしていることが今ひとつ伝わりにくかったので、ちょっと変更してみます。
#include <avr/io.h>
static inline void initIo(void) // I/O ポートの初期化
{
DDRD = 0b11111111; // All PORTD ports as Output }
static inline void ledOn(void) // LED を点灯
{
PORTD |= (1<<PD0); }
int main(void) {
initIo(); // I/O ポートの初期化
while(1){
ledOn(); // LED を点灯 }
return 0; }
I/O
ポート初期化の処理やLED
を点灯するコードをわかりやすい(英語ですが)文字同じように
LED
を消灯する処理をledOff()で表し、一定時間だけ待つ処理をwait(待つ時間)で表すコードを書いてみます。
#include <avr/io.h>
static inline void initIo(void) {
DDRD = 0b11110011; // All PORTD ports as Output }
static inline void ledOn(void) // LED light ON
{
PORTD |= (1<<PD0); }
static inline void ledOff(void) // LED light OFF
{
PORTD &= ~(1<<PD0); }
static inline void wait(volatile unsigned long i) // wait for a while
{
while (i-->0); }
int main(void) {
// initialize I/O Ports initIo();
while(1){
ledOn(); // LED ON
wait(30000UL); // wait 30000 counts
ledOff(); // LED OFF
wait(10000UL); // wait 10000 counts }
return 0; }
(3) LEDの点滅
LED
を点滅させるだけならば、出力ポートのビットを反転するだけでよいので、プログラムはもっと簡単になります。
#include <avr/io.h>
static inline void initIo(void) {
DDRD = 0b11110011; // All PORTD ports as Output }
static inline void ledOnOff(void) // LED light ON / OFF
{
PORTD ^= (1<<PD0); }
static inline void wait(volatile unsigned long i) // wait for a while
{
while (i-->0); }
int main(void) {
// initialize I/O Ports initIo();
while(1){
ledOnOff(); // LED ON / OFF
wait(30000UL); // wait 30000 counts }
2
スイッチを押すとLEDが光る
以下のコードは、スイッチを押している間だけ
LED
が点灯するものです。#include <avr/io.h>
static inline void initIo(void) // initialize I/O ports.
{
PORTD = (1<<PD2); // weak pull up resistor enable on PD2 DDRD = 0b11111011; // PD2 as Input, others Output
}
static inline void ledOn(void) // LED light ON
{
PORTD |= (1<<PD0); }
static inline void ledOff(void) // LED light OFF
{
PORTD &= ~(1<<PD0); }
static inline uint8_t isSwitchOn(void) {
return !( PIND & ( 1<<PD2 ) ); }
int main(void) {
// I/O Port Initialization initIo();
while(1){
if ( isSwitchOn() ) { ledOn(); // LED ON } else {
ledOff(); // LED OFF }
}
(1) initIo()関数について
スイッチの
ON/OFF
を判断するには、スイッチを入力ポートに接続する必要があります。本機ではスイッチがポート
PD2
に接続されているので、ポートPD2
を入力モードに設 定します。また、スイッチが押されたときに0
V
、押されていないときに電源電圧となるように、マイコン内の抵抗を有効
(weak pull up)
にします。 その設定方法は以下の通りです。PORTD = (1<<PD2); //まず PD2 の内蔵抵抗を有効にします。 DDRD = 0b11111011; // PD2 を入力モード(0)に設定します。
(2) isSwitchOn()関数について
スイッチが押されているときに
0
以外の値(真、TRUE
)を返し、そうでないときに0
(偽、
FALSE
)を返します。
C
言語のif
文では、条件式の値が「0
でないとき」に「真」となるため、次のように 書くと、スイッチの状態によって処理を切り替えることができます。if ( isSwitchOn() ) {
スイッチが押されたときの処理 } else {
3
音を鳴らす
(1) スイッチが押されたときだけブザーが鳴る
本機では、ブザーが PB0 に接続されていますから、DDRB レジスタの PD2 に対応するビッ トを1に設定して、出力ポートとします。PORTB の他のポートには何も接続されていないた め、簡単にするために全ポートを出力に設定してしまいます。
以下のコードを実行してみてください。
#include <avr/io.h>
static inline void initIo(void) // initialize I/O ports.
{
PORTD = (1<<PD2); // weak pull up resistor enable on PD2 DDRD = 0b11111011; // PD2 as Input, others Output
DDRB = 0b11111111; // PORTB All Output }
static inline void buzOn(void) // Buzzer ON / OFF
{
PORTB ^= (1<<PB0); }
static inline uint8_t isSwitchOn(void) {
return !( PIND & ( 1<<PD2 ) ); }
static inline void wait(volatile unsigned long i) // wait for a while
// Arguments :: i : waiting length {
while ( i-- > 0 ); }
int main(void) {
// I/O Port Initialization initIo();
while(1){
if ( isSwitchOn() ) { buzOn();
wait(10); buzOn();
wait(100); }
}
(2) スイッチが押されると指定した音の高さ(tone)と長さ(length)で音が鳴る。
次のコードは、押しボタンスイッチを押すと、1 秒程度、音が鳴ります。
#include <avr/io.h>
static inline void wait(volatile unsigned long i) // wait for a while
// Arguments :: i : waiting length {
while ( i-- > 0 ); }
static inline void initIo(void) // initialize I/O ports.
{
PORTD = (1<<PD2); // weak pull up resistor enable on PD2 DDRD = 0b11111011; // PD2 as Input, others Output
DDRB = 0b11111111; // PORTB All Output }
static inline void buzOn(void) // Buzzer ON / OFF
{
PORTB ^= (1<<PB0); }
void beepOn(unsigned int tone, unsigned long length) // beep by Buzzer
// Arguments :
// tone : tone height , tone 2 (high) ~ 1000 (low) // length : beep sound time
{
length /= tone; while (length-- > 0) { buzOn();
wait(tone); }
}
static inline uint8_t isSwitchOn(void) {
int main(void) {
// I/O Port Initialization initIo();
while(1){
if ( isSwitchOn() ) { beepOn(100, 50000UL); }
}
return 0; }
(3) beepOn()関数について
次のように音の高さとなっている時間の長さを指定します。
beepOn(音の高さ, 音を鳴らす時間の長さ);
4
タイマ0割り込み
3分間動作して自動的に電源が切れるシステムや1時間後に自動的に起動するシステムを 作る場合、タイマ割り込みという機能を使って動作時間を制御します。
ここでは、ATmega168P のタイマ0(TIMER0)割り込みを使ったコードの例を紹介します。
(1) 割込みの基本コード ~ 1msごとに割込み発生 ~
タイマ0(TIMER0)は、一定間隔で数を数えるカウンターです。
その数が規定値を超えたときに割り込みを発生させることができます。この割り込みを TIMER0割り込みといいます。
次のコードでは、1msごとに LED が点滅しています。(目で見るとずーっと点灯してい るように見えますが)
#include <avr/io.h>
#include <avr/interrupt.h>
static inline void ledOnOff(void) // LED light ON / OFF
{
PORTD ^= (1<<PD0); }
static inline void initIo(void) // initialize I/O ports.
{
DDRD = 0b11111011; // PD2 as Input, others Output }
static inline void initTimer0(void) // Initialize TIMER0
{
// CTC mode , TOP is OCR0A
TCCR0A = (1 << WGM01) | (0 << WGM00) ;
// I/O clock prescaler N = 8 →クロック周波数(1MHz)の 8 倍の周期(8us)でカウントする。
TCCR0B = (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00); // Timer0 on OCR0A compare match interrupt enable
TIMSK0 |= ( 1<< OCIE0A );
// set TOP value . Interrupt every 1 ms OCR0A = 125;
/* OCR0A の値 =
割込み間隔 [s] * クロック周波数(f_clkio [Hz]) / クロックプリスケーラ(I/O clock prescaler) N
ここでは、0.001 * 1000000 / 8 = 125 となる。
TIMER0 が 125 回カウントすると、割込みが発生するため、約 1ms で割込みが発生 することになる。
/*
* Interrupt */
ISR(TIMER0_COMPA_vect)
// Timer0 Timer counter Compair match Interrupt {
ledOnOff(); // TIMER0 割り込みが発生したら LED を点滅させる。 }
/*
* Main */
int main(void) {
initIo(); initTimer0();
sei(); // 割込み待ち受け処理の開始
while (1) {
}
(2) 割込みの実用コード ~ 2秒ごとに割込み発生 ~
先ほどのコードでは、割込みの間隔が早すぎて、人間が使うタイマとしては、実用的では ありませんでした。
そこで、割込みの発生回数を数えるグローバル変数TIMER0_COUNTERを使うことにします。
TIMER0_COUNTERは、TIMER0割込みが発生すると1増加します。この変数の値を 0 にした 後、2000 になるまでの時間は、約2000 ms = 2 s となります。
次のコードは、LED が 2 秒ごとに点滅します。
#include <avr/io.h>
#include <avr/interrupt.h>
static volatile unsigned long TIMER0_COUNTER = 0;
static inline void ledOnOff(void) // LED light ON / OFF
{
PORTD ^= (1<<PD0); }
static inline void initIo(void) // initialize I/O ports.
{
DDRD = 0b11111011; // PD2 as Input, others Output }
static inline void initTimer0(void) // Initialize TIMER0
{
// CTC mode , TOP is OCR0A
TCCR0A = (1 << WGM01) | (0 << WGM00) ;
// I/O clock prescaler N = 8 → クロック周波数(1MHz)の 8 倍の周期(8us)でカウン トする。
TCCR0B = (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00); // Timer0 on OCR0A compare match interrupt enable
TIMSK0 |= ( 1<< OCIE0A );
// set TOP value . Interrupt every 1 ms OCR0A = 125;
/*
OCR0A の値 =
割込み間隔 [s] * クロック周波数(f_clkio [Hz]) / クロックプリスケーラ(I/O clock prescaler) N
ここでは、0.001 * 1000000 / 8 = 125 となる。
TIMER0 が 125 回カウントすると、割込みが発生するため、約 1ms で割込み が発生することになる。
/*
* Interrupt */
ISR(TIMER0_COMPA_vect)
// Timer0 Timer counter Compair match Interrupt {
TIMER0_COUNTER++;
if (TIMER0_COUNTER >= 2000) {
ledOnOff(); // TIMER0 割り込みが 2000 回発生したら LED を点滅させる。 TIMER0_COUNTER = 0; // TIMER0 Counter をリセット
} }
/* * Main */
int main(void) {
initIo(); initTimer0();
sei(); // 割込み待ち受け処理の開始
TIMER0_COUNTER = 0; // TIMER0 Counter をリセット
while (1) {
}
(3) TIMER0割込みの応用コード
この前のコードでは、割込み処理関数( ISR(TIMER0_COMPA_vect) )で、LED の点滅を
制御していましたが、割込み処理関数の中で、複雑な処理をするのは好ましくありません。 他の割込み処理が追いつかなくなる恐れがあります。
そこで、割込み処理関数では、割込み回数のカウントをする処理だけをすることにして、 LED の点滅はmain関数で行うように変更したのが、次のコードです。このコードでは、LED が 点滅する間隔を1秒にしてあります。
#include <avr/io.h>
#include <avr/interrupt.h>
static volatile unsigned long TIMER0_COUNTER = 0;
static inline void ledOnOff(void) // LED light ON / OFF
{
PORTD ^= (1<<PD0); }
static inline void initIo(void) // initialize I/O ports.
{
DDRD = 0b11111011; // PD2 as Input, others Output }
static inline void initTimer0(void) // Initialize TIMER0
{
// CTC mode , TOP is OCR0A
TCCR0A = (1 << WGM01) | (0 << WGM00) ;
// I/O clock prescaler N = 8 → クロック周波数(1MHz)の 8 倍の周期(8us)でカウン トする。
TCCR0B = (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00); // Timer0 on OCR0A compare match interrupt enable
TIMSK0 |= ( 1<< OCIE0A );
// set TOP value . Interrupt every 1 ms OCR0A = 125;
/*
OCR0A の値 =
割込み間隔 [s] * クロック周波数(f_clkio [Hz]) / クロックプリスケーラ(I/O clock prescaler) N
ここでは、0.001 * 1000000 / 8 = 125 となる。
TIMER0 が 125 回カウントすると、割込みが発生するため、約 1ms で割込み が発生することになる。
/*
* Interrupt */
ISR(TIMER0_COMPA_vect)
// Timer0 Timer counter Compair match Interrupt {
TIMER0_COUNTER++; // TIMER0 割込みが起こったら、TIMER0_COUNTER を増やすだけ }
/* * Main */
int main(void) {
initIo(); initTimer0();
sei(); // 割込み待ち受け処理の開始
TIMER0_COUNTER = 0; // TIMER0 Counter をリセット
while (1) {
if (TIMER0_COUNTER >= 1000) {
ledOnOff(); // TIMER0 割り込みが 1000 回発生したら LED を点滅させる。 TIMER0_COUNTER = 0; // TIMER0 Counter をリセット
} }
5
INT0割込み
(1) 割込みを使用しない場合
次のプログラムは、押しボタンスイッチを押すと LED を消灯するつもりで作りました。しか
し、実行すると、ボタンを押すタイミングによって LED が消灯しないことがあります。
#include <avr/io.h>
#include <avr/interrupt.h>
enum { STAGE_LED_ON, STAGE_LED_OFF }; // 現在の状態(STAGE)を表す定数 volatile int STAGE = STAGE_LED_ON; // 現在の状態の初期値は STAGE_LED_ON
static inline void wait(volatile unsigned long i) {
while (i-- > 0); }
static inline void ledOn(void) // LED light ON / OFF
{
PORTD |= (1<<PD0); }
static inline void ledOff(void) // LED light ON / OFF
{
PORTD &= ~(1<<PD0); }
static inline int isSwitchOn(void)
// if Switch-ON -> return TRUE, else return False {
return ( !( PIND & (1<<PD2) ) ); }
static inline void initIo(void) // initialize I/O ports.
{
/* * Main */
int main(void) {
initIo();
while (1) {
switch ( STAGE ) { case STAGE_LED_ON: ledOn();
wait(100000UL); default:
ledOff(); }
if (isSwitchOn()) {
STAGE = STAGE_LED_OFF; ledOff();
} }
(2) INT0割込みの使用
そこで、INT0割込みを利用して、ボタンを押したらすぐに反応するように改良してみまし た。
#include <avr/io.h>
#include <avr/interrupt.h>
enum { STAGE_LED_ON, STAGE_LED_OFF }; // 現在の状態(STAGE)を表す定数 volatile int STAGE = STAGE_LED_ON; // 現在の状態の初期値は STAGE_LED_ON
static inline void wait(volatile unsigned long i) {
while (i-- > 0); }
static inline void ledOn(void) // LED light ON / OFF
{
PORTD |= (1<<PD0); }
static inline void ledOff(void) // LED light ON / OFF
{
PORTD &= ~(1<<PD0); }
static inline void initIo(void) // initialize I/O ports.
{
PORTD |= (1<<PD2); // PD2 weak pull up enable DDRD = 0b11111011; // PD2 as Input, others Output }
static inline void initInt0(void) // INT0 割込みの設定
{
EICRA |= (1<<ISC01) | (1<<ISC11); // INT0 Falling Edge, INT1 Falling edge EIMSK |= (1<<INT0) | (1<<INT1); // Enable Interrupt 0, 1
}
static inline int isSwitchOn(void)
// if Switch-ON -> return TRUE, else return False {
/*
* Interrupt */
ISR(INT0_vect)
// INT0 割込みが発生したとき(押しボタンスイッチを押したとき)の処理 {
switch (STAGE) { case STAGE_LED_ON: ledOff();
STAGE = STAGE_LED_OFF; break;
default:
STAGE = STAGE_LED_ON; }
}
/* * Main */
int main(void) {
initIo();
initInt0(); // INT0 割込みの設定 sei(); // 割込み待ち受け開始
while (1) {
switch ( STAGE ) { case STAGE_LED_ON: ledOn();
wait(100000UL); break;
default: ledOff(); }
}
6
デモンストレーション
次のような動作をするタイマのプログラムです。
LED が点滅→スイッチを押すと→LED が点灯→10 秒たつと→LED が点滅、ブザーが鳴る→
スイッチを押すと→最初に戻る
#include <avr/io.h>
#include <avr/interrupt.h>
static inline void wait(volatile unsigned long i) // wait for a while
// Arguments :: i : waiting length {
while ( i-- > 0 ); }
static inline void buzOn(void) // Buzzer ON / OFF
{
PORTB ^= (1<<PB0); }
void beepOn(unsigned int tone, unsigned long length) // beep by Buzzer
// Arguments :
// tone : tone height , tone 2 (high) ~ 1000 (low) // length : beep sound time
{
length /= tone;
while (length-- > 0) { buzOn();
wait(tone); }
}
static inline void ledOn(void) // LED light ON
{
PORTD |= (1<<PD0); }
static inline void ledOff(void) // LED light OFF
{
{
PORTD ^= (1<<PD0); }
static inline uint8_t isSwitchOn(void) {
return !( PIND & ( 1<<PD2 ) ); }
enum {
STAGE_READY, STAGE_LED, STAGE_ALARM };
static uint8_t STAGE = STAGE_READY;
/*
* initialize */
static inline void initIo(void) // initialize I/O ports.
{
PORTD = (1<<PD2); // weak pull up resistor enable on PD2 DDRD = 0b11111011; // PD2 as Input, others Output
DDRB = 0b11111111; // PORTB All Output }
static inline void initInt0(void) {
EICRA |= (1<<ISC01) | (1<<ISC11); // INT0 Falling Edge, INT1 Falling edge EIMSK |= (1<<INT0) | (1<<INT1); // Enable Interrupt 0, 1
}
static inline void initTimer0(void) // Initialize TIMER0
{
// CTC mode , TOP is OCR0A
TCCR0A = (1 << WGM01) | (0 << WGM00) ; // I/O clock prescaler N = 8
TCCR0B = (0 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00); // Timer0 on OCR0A compare match interrupt enable
TIMSK0 |= ( 1<< OCIE0A );
// set TOP value . Interrupt every 1 ms
OCR0A = 125; // OCR0A = interrupt period [s] * f_clkio [Hz] / I/O clock prescaler N
}
* Interrupt */
volatile unsigned int TIMER0_COUNTER; // Timer0 interrupt counter
// #define TIMER0_10SEC 10000 // #define TIMER0_1SEC 1000 // #define TIMER0_500MSEC 500 #define TIMER0_LIMIT 10000
ISR(TIMER0_COMPA_vect)
// Timer0 Timer counter Compair match Interrupt {
TIMER0_COUNTER++; }
ISR(INT0_vect) // INT0 Interrupt {
beepOn(2,500); switch ( STAGE ) { case STAGE_READY : STAGE = STAGE_LED; TIMER0_COUNTER = 0; break;
case STAGE_LED :
STAGE = STAGE_READY; break;
case STAGE_ALARM : STAGE = STAGE_READY; }
while( isSwitchOn() ); }
/* * main */
int main(void) {
initIo(); initInt0(); initTimer0();
beepOn( 2 , 1000 ); ledOff();
while (1) {
switch ( STAGE) { case STAGE_READY: ledOnOff(); wait(10000); break; case STAGE_LED : ledOn();
if (TIMER0_COUNTER > TIMER0_LIMIT) { STAGE = STAGE_ALARM;
} break;
case STAGE_ALARM : beepOn( 2 , 1000 ); ledOnOff();
wait(3000); break; default: ledOff(); }
}