このドキュメントは http://edu.net.c.dendai.ac.jp/ 上で公開されています。
近年の組み込み系のプログラミングは生産効率を上げるためや、習熟期間の短 縮などのために C 言語での開発が主流になってきています。 但し、これから行うマイコンのプログラミングにおける C 言語のプログラミ ングは、プログラミングの初心者が習う C 言語のプログラミングと異なり、 かなりハードウェアの構造を意識したプログラミングを行うことになります。
通常の C 言語とマイコンの C 言語の扱いの違いとして次があります。
なお、 公式に出されている ATtiny2313 などに対する C 言語の手引きにはつ ぎのものがあります。
C言語を扱う上で、通常のプログラミングテクニックはそのまま使うことがで きます。 しかし、通常の計算して結果を出力して終了するプログラムと違う点は、前 処理の後に、無限ループを作る点です。
また、入出力は I/O ポートを使用することになりますが、これはグローバル
変数が用意されています。
この変数を使うのに、 arv/io.h というヘッダファイルを読み込みます。
なお、このヘッダファイルは
C:\Program Files (x86)\Atmel\Atmel Toolchain\AVR8
GCC\Native\3.4.1056\avr8-gnu-toolchain\avr\include\avr
にあり
ます。
例5-1と同じ問題を C言語でやってみましょう。
あらかじめ与えられた特定のパターン(例えば 2 進数で与える)の値を PORTB に出力し、 LED を光らせたままにするプログラムを作りなさい。 (ヒント: プログラムを止めるには sleep ではなく、単なる無限ループを作ります。 )
これを行うには、次を行う必要がありました。
C言語で唯一アセンブラと違って不便なのは二進数を記述することができない ことです。 そのため、通常は16進数を使用します。 つまり、8bit すべてを ON にするには 0b11111111 の代わりに 0xff を使用 します。 avr/io.h ファイルをインクルードすると、 DDRB, PORTB ともアセンブラの時 と同様に、今度はラベルの代わりに変数として使用することができます。
作成したプログラムは下記のようになります。
/************
* 例9-1 *
************/
#include <avr/io.h>
int main(void){
DDRB = 0xff;
PORTB = 0x55;
while(1){
}
}
次に、特定のスイッチ操作に反応するプログラムを作ります。
switch プログラムの移植を考えます。 つまり、次のような処理を考えます。
以下を繰り返す
PORTD の 4bit 目が ON だったら PORTB に 0 を入れ、そうでなければ PORTB に0xff を入れる
このようにして組んだプログラムを次に示します。
/************
* 例9-2 *
* switch *
************/
#include <avr/io.h>
int main(void){
DDRB = 0xff;
DDRD = 0;
while(1){
if(PIND & (1<<4)){
PORTB = 0;
}else{
PORTB = 0xff;
}
}
}
次に、ボタンを押す度に表示の On, Off を繰り返すプログラムの演習をしま した。 これを実現するには、前回のボタンの値を覚えておく必要がありました。 そのため、 C 言語では変数を宣言して、値を記憶させることを考えます。 但し、AVR用のC言語はデフォルトで変数宣言をすると 16bit の領域を確保することに なりますので、8bitマイコンを扱う上で、ビット長を考える必要があります。
文献[3]の表3-1を下記に引用します。
Data type | Size | |
---|---|---|
signed char / unsigned char | int8_t / uint8_t | 8-bit |
signed int / unsigned int | int16_t / uint16_t | 16-bit |
signed long / unsigned long | int32_t / uint32_t | 32-bit |
signed long long / unsigned long long | int64_t / uint64_t | 64-bit |
ボタンを押す度に表示を反転させるため、まず次の 8bit の変数を宣言します。
PORTD の 4bit 目が ON だったら PORTB に 0 を入れ、そうでなければ PORTB に0xff を入れる
このようにして組んだプログラムを次に示します。
/************
* 例9-3 *
************/
#include <avr/io.h>
int main(void){
int8_t current = 0;
int8_t last = 0;
int8_t pattern = 0xff;
DDRB = 0xff;
DDRD = 0;
while(1){
current = PIND;
if((!(last & (1<<4)) && (current & (1<<4)))){
pattern = ~ pattern;
}
PORTB = pattern;
last = current;
}
}
次に、 flash の移植を考えましょう。 flash はビジーウェイトを用いて、ほぼ1秒毎に表示を点滅させてました。 ここでは int32_t を使って、 一重ループで反転するようにしましょう。
C言語の変数において、プログラムの流れ的には意味が無かったり、値が変わ らないようでも、実際は必ず定義されて無いといけないような変数を使うとき、 volatile 宣言をします。 これをすると、最適化の際に変数その物を消されたりすることがなくなります。 今回はビジーウェイトという、プログラム上はなくしても計算の結果には影響 せず、計算時間を遅くするだけの変数に対して、volatile 宣言をしています。
/************
* 例9-4 *
* flash *
************/
#include <avr/io.h>
void wait(void){
volatile int32_t i;
for(i=0;i<35000;i++){
}
}
int main(void){
DDRB = 0xff;
while(1){
PORTB = 0xff;
wait();
PORTB = 0;
wait();
}
}
volatile 宣言を取ってコンパイルすると、最適化によりビジーウェイトその 物が取り除かれることが分かると思います。
次に、特定のパターンを順番に表示させてみましょう。 パターンを作成、保持するのに、配列変数を使います。
なお、配列変数に入っている要素数は、(配列変数の全バイト数)/(配列変数の
要素のバイト数)ですので sizeof(配列変数)/sizeof(配列変数[0])
で計算できます(ポインタではできないので要注意)。
/************
* 例9-5 *
************/
#include <avr/io.h>
void wait(void){
volatile int32_t i;
for(i=0;i<35000;i++){
}
}
int main(void){
const uint8_t pattern[]={0,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff};
int8_t i;
DDRB = 0xff;
while(1){
for(i=0;i<sizeof(pattern)/sizeof(pattern[0]);i++){
PORTB = pattern[i];
wait();
}
}
}
PORTB につながっている LED が 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F を順番に表示するプログラムを作りなさい。
PD4 に接続した sw を押す度に PORT B に つながっている LED が 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F を順番に表示するプログラムを作りなさい。
C言語で割り込みを行う場合、 avr/interrupt.h をインクルードします。 そして、次のような手順で割り込みのプログラムを記述します。
ISR(割り込みベ
クタ名)
という名前で関数を定義すると、その割り込みでその関数
が呼ばれるようになる。
CPU を sleep させるには avr/sleep.h をインクルードします。 そして、次のように使用します。
例9-5と同様に一定時間毎に別のパターンを表示するプログラムを、割り込み と sleep を使用して書いたのが以下のプログラムです。
/************
* 例9-6 *
************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
const unsigned char pattern[]={0x00,0x01,0x03,0x07,0x0f,0x1f,0x3f,0x7f,0xff};
volatile int8_t counter;
void inccounter(){
counter++;
if(counter>=sizeof(pattern)/sizeof(pattern[0])){
counter=0;
}
}
void dispcounter(){
PORTB = pattern[counter];
}
ISR( TIMER0_OVF_vect){
inccounter();
dispcounter();
}
int main(void){
DDRB = 0xff;
set_sleep_mode(SLEEP_MODE_IDLE);
TCCR0A = 0x00;
TCCR0B = 0x05;
TIMSK = 0x02;
counter =0;
dispcounter();
sei(); // 全体の割込を許可
while(1){
sleep_cpu();
}
}
演習9-1同様に PORTB につながっている LED が 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F を順番に表示するプログラムを割り込みと、sleepを使って作りなさい。
演習9-2同様に PD4 に接続した sw を押す度に PORT B に つながっている LED が 0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F を順番に表示するプログラムを作りなさい。 但し、割り込みを使用して、小数点が 0.5 秒毎に点滅をするようにしなさい。
スイッチを押す度に、小数点が暗くなっていくプログラムを作りなさい。
スイッチを押す度に、LED の数字が増えていき、小数点が暗くなっていくプロ グラムを作りなさい。