C言語(WINAVR)メモ    (項目のうしろの6桁数字は追記した年月日です。)        TOPへ
AVRにはフリーのすばらしいコンパイラ'WINAVR'が用意されています("whenever"と発音するそうです)。 アセンブリ言語で始めたAVR ですが、Webページの先輩のファイルにCが多いので勉強することにしました。デバイスが変わっても変更がしやすく、また、デバイスの命令を 覚えていなくても使えるのがうれしいところです。ここに、自分が忘れないように気のついたことをメモすることにしました。少しずつ増やして いくつもりです。
コンパイルしない指定 #if 0 #endif 080515 
SFR(Special function registers)の設定 070310 
SRAMを消費しないメッセージ 061008 
SRAMを消費しない定数(フラッシュに置く) 110423 
一次元配列(変数の) 060610 
makefileの変更場所の工夫 050823 
ポートの1ビットを反転する 050810 
make.exeを実行するバッチファイル 050312 
WinAVR-20050214の注意 050301 sbi() cbi() マクロ
AVRstudioでシミュレーション 050227 050213 
数値の表記法 050213 
waitルーチン(インラインアセンブラによる) 050108 
waitルーチン(_delay_ms による) 090508 
switch :case文 041229 
グローバル変数をコンパイラに省略させない volatile 
整数を文字列に変換する itoa()
ヘッダファイルの取り込み:
データ型: 変数の型
変数の宣言:
メイン関数(メインルーチン):
リストの改行:
関数(サブルーチン)を呼び出す:
関数(サブルーチン)の書き方:
ポートの入出力の決定:
ポートへの出力:
1ポートだけの出力:
入力ポートのプルアップ:
ポートからの入力:
演算式:
if文:
for文: ループ処理
コメント:
複文:
一般的なプログラムの書き方:
割込:
キャスト:
ビットのセット: _BV


コンパイルしない指定 #if 0 #endif 080515 
一時的にコンパイルしたくない部分ができてきます。コメント /* */ で隠しても目的は達せられますが、ネストができません。コメントにするよりもコンパイルしない指令を入れておく方が本来の書き方です。
 #if 0 /* コンパイルしたくない理由を書く */
 :  /* コメントがあっても大丈夫 */
 #endif
コンパイルしたいときは 0 を 1 に書き換えます。senshuさんに教えていただきました。


SFR(Special function registers)の設定 070310  機能を設定するために8bitのSFRに8bitの定数を書き込む場合が多くあります。例えば、tiny2313で USARTの初期化を行うとき
 UCSRB= (1<<RXEN)|(1<<TXEN);    /* 送受信許可*/
 UCSRC = (1<<USBS)|(3<<UCSZ0);   /* フレーム形式設定(8ビット,2ストップビット) */
これはまずUCSRBレジスタを見ると
 7ビット〜0ビットに RXCIE TXCIE UDRIE RXEN TXEN UCSZ2 RXB8 TXB8 という名前が付けられていて、また、これらの名前に左から順に 7,6,5,4,3,2,1,0の定数が割り当てられています。したがって、
 (1<<RXEN)は(1<<4)の意味であり、二進数で書けば 0b00010000 となります。同様に、
 (1<<TXEN)は(1<<3)の意味であり、二進数で書けば 0b00001000 となります。 これらのORを取っていますから 0b00010000 or 0b00001000 = 0b00011000 が得られることになります。4,3ビットが1ですから、送信・受信が許可されているのが わかります。レジスタとにらめっこで、「送受信許可」にするには 4,3bitを1にすればよいから 0b00011000 が得られるので UCSRB=0b00011000 とプログラムでは (私はよく)書きます。RXEN、TXENの意味がよくわかれば最初の方法が優れているとわかります(でも私は覚え切れません)。
次の行の (3<<UCSZ0) は、
UCSRC: --- UMSEL UPM1 UPM0 USBS UCSZ1 UCSZ0 UCPOL から bit4,3 の二進数2桁の指定ができますから十進数では3〜0を書くことができます。
下の桁に3を書くとは、上に1,下に1を書くことになり(UCSZ2とあわせて)8bitデータを指定することになります。ここでも、0b0001110 と書いても 同じです。
(1<<namae) は namaeビットを立てる、と考えるといいでしょう(シフトを考えると煩わしい)。2桁以上になるときは十進数で表すと見やすいということ になるでしょう。


SRAMを消費しないメッセージ 061008 
通信でPCにいろいろなメッセージを送りたいときがあります。これを文字列定数としてプログラムに置くとすべてSRAMにコピーされ多くのSRAMを消費します。 そこで、SRAMには汎用の文字列変数を1つだけ用意して置いて、メッセージはROMから1文字ずつ変数にコピーして使うことにします。これで何種類ものメッセージが あっても文字変数一つだけのSRAM消費ですみます。
#include <avr/pgmspace.h> // 必要です
int main (void) {
  char msg[43];

  strcpy_P(msg,PSTR("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa   ")); string_out(msg); 
  strcpy_P(msg,PSTR("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")); string_out(msg); 
  strcpy_P(msg,PSTR("ccccccccccccccccccccccccccccccccccccccccccc")); string_out(msg); 
}
strcopy_PはROMからSRAMへのコピー、PSTRはROMにある文字列のアドレス取得、strin_out()は通信ルーチン(ここには書いていない)です。 includeファイルに注意が必要です。


SRAMを消費しない定数(フラッシュに置く) 110423 
フラッシュメモリ上にデータを置きたい場合はPROGMEMを使います。
 #include <avr/pgmspace.h> // 必要です
 PROGMEM char Table[] = { 1,2,3,4,5 };
と書きます。 そして、
 pgm_read_byte(&Table[3]);
で取り出します。
pgm_read_byte() : 8ビット幅
pgm_read_word() : 16ビット幅
pgm_read_dword() : 32ビット幅
となります。
PROGMEMを付けない場合、初期値はフラッシュメモリ上に置かれますが、main関数に来る前のスタートアップルーチンでSRAMに展開され、プログラムはSRAMを参照することになります。
(そら。さんの文章が素晴らしいのでそのままいただきました。多謝)


一次元配列(変数の) 060610 
uint8_t suu[10]; と宣言すると、 suu[0]からsuu[9]の10個の符号無し8ビット整数の配列が作られます。[0]から始まることに注意します。


makefileの変更場所の工夫 050823 
makefileは \winAVR\sample\Makefile のデバイスなど必要な場所を変更して使っています。このとき変更場所をわかりやすくするために
変更すべき場所の次の行に "##################・・・#" を 書いています。新しいプログラムを書くときはその前のmakefileをコピーして、これをめやすに変更します。
# MCU name
MCU = attiny2313
##################################################################################################
F_CPU = 8000000
##################################################################################################
TARGET = pwmh1
##################################################################################################
SRC = $(TARGET).c
##################################################################################################
EXTRAINCDIRS =
##################################################################################################
このようにしておくと変更場所がわかりやすく、また、取りこぼしがありません。


ポートの1ビットを反転する 050810 061213
トグル動作をさせるために、出力ポートを繰り返し毎に反転したいときがあります。次のようにします。
if(PORTB&_BV(0)){PORTB&=~_BV(0);}else{PORTB|=_BV(0);} 
これはポートBのビット0を反転しています。ビット3なら _BV(3) とすればよいでしょう。BVのアンダースコアと否定のチルダに注意してください。
jujurouさんから便りをいただきました。
PORTB ^= _BV(0); // XORを使用すればコードサイズを小さくすることができます
ビット3なら _BV(3) とします。


make.exeを実行するバッチファイル 050312 
make.exeを実行するには、.cファイルがあるディレクトリ(フォルダ)にカレントディレクトリを移動し、WinAVR\utils\bin\にあるmakeを実行する必要があります。
インストールしますとこのWinAVR\utils\bin\にパスが設定されますので、自動的にここのmakeが実行されるのですが、登録されている別のパスに 他のC言語のmake.exeがあるとそのmake.exeが実行されてエラーを起こすことがあります(現に報告がありました)。DOSプロンプトで"PATH"と入力して 確かめてみるのもよいかと思います。
このようなトラブルを避けるために、次のようにmakeをフルパスで書くことを勧めます。
ここでは、WinAVRは C:\ にインストールされ、.c ファイルと makefile は D:\prog\test1\ にあるものとします。
D:
CD D:\prog\test1
REM C:\WinAVR\utils\bin\make clean
C:\WinAVR\utils\bin\make all
1行目:カレントドライブをプログラムリストファイルのある D:ドライブに変更します。
2行目:カレントディレクトリをプログラムリストファイルのあるフォルダに移動します。
3行目:REM文で無効にしてありますが、REM を削除すれば、以前の .o .hex などのファイルをすべて削除します。(残っていると困ることがある)
4行目:普通にmakeします(makeと同じです)。
少し長くなりましたが、新しいプログラムを作るときは、これをコピーして test1 の部分を書き換えるだけです。


WinAVR-20050214の注意 050301 
  旧バージョンで "deprecated" となっていた includeファイルとマクロが使えなくなりました。
io.h interrupt.h などはavrディレクトリ下にあり、avr/io.h のように書くことになります。また、sig-avr.h は avr/signal.h です。一部は新しい  avr/compat ディレクトリにあります。 \winavr\avr\include\ の内容を見ておくと良いでしょう。
なくなったマクロについては、
sbi() → PORTx |= _BV()
cbi() → PORTx &=  ~_BV()
inp() → data = PORTx  or  =PINx
outp() → PORTx = data
と書き換えれば殆ど終わりです。(私のファイルにあったのはこの4種のマクロだけでした。)
書き換えないで新たにマクロを定義するのでしたら、senshuさんのマクロを紹介します。
/* For old avr-libc */
#define _cbi(p, q) ((p) &= ~_BV(q))
#define _sbi(p, q) ((p) |= _BV(q))
#define _outp(p, q) q = p
#define _inp(p) p
おまけ:新バージョンの makefile は少し親切になりました。ROMの使用率%などを示してくれます。
060117追記:上記のsenshuさんのマクロでは実行できない報告がありました。
次のマクロでコンパイルできたとのことです。(GO2さんから)
/* deprecated.h */
#define BV(bit) (1<<(bit))
#define cbi(addr,bit) addr&=~_BV(bit)
#define sbi(addr,bit) addr|=_BV(bit)
#define outp(data,addr) addr=(data)
#define inp(addr) (addr)
#define outb(addr,data) addr=(data)
#define inb(addr) (addr)
#define PRG_RDB(addr) (pgm_read_byte(addr))
出典はAVR-Wikiの「deprecated macroを使う?」のようです。


AVRstudioでシミュレーション 050227 050213 
makefile(C:\WinAVR\sample\にあるものです)の267行目の build: elf hex eep lss sym の最後に extcoff を付けて、
build: elf hex eep lss sym extcoff とします。 これでmakeすると .cof ファイルができます。AVRstudioのv4.11を立てて、.cofファイルを読み込むとシミュレーションできます。教えてもらったばかりで 使いこなしていないので今のところはこれ以上のコメントはできません。(やがて何とかします) AVRstudio v3ではextcofではなくcofを指定するようです。 新しいデバイスが含まれている新バージョンの方がよいでしょう。
WinAVR20050214で(これに含まれるmakefileを使って)同じことをするとソースファイルが読み込めません。.cofではなくデフォルトでできる.elfファイルを AVRstudioのv4.11に読み込むとソースファイルでシミュレートできます。理由はわかりません。


数値の表記法 050213 
別のところへ書きましたが、WINAVRでは、数値の表記に 10進数、16進数、2進数 が使えます。
10進数では数字だけで 46、 16進数では 0x を付けて 0x2E、 2進数では 0b を付けて 0b00101110 と書きます。ポートごとのデータ出力や、 SFR(I/Oレジスタ)の設定に2進数が便利なときがあります。(2進表記は AVR H8 etc. マイコン総合スレ Part3 で学びました)  


waitルーチン(インラインアセンブラによる) 050108
Cでwaitループを書くとコンパイラの最適化によって時間がうまく決まらないことがあります。このときにインラインアセンブラを使いますと 安定した時間が得られます。
まろりさんのページで知りました。 このページの例題のルーチンは次の通りです。(2つのアンダースコアがつながって見えるかもしれません。また、インデントは全角空白でごまかしていますから プログラムに組み込むときは注意してください。)
void wait(unsigned int time){     // 100μS単位のwaitルーチン (クロック8MHzのとき)
  unsigned char lpcnt;            // 引数6で600μSとなる (1MHzなら1で800μs)
  __asm__ __volatile__("\n"
    "CPU_wait_entry%=:\n\t"
    "ldi %0,200\n"          //CK 100us/8MHz
    "CPU_wait_lp%=:\n\t"
    "nop\n\t"
    "dec %0\n\t"
    "brne CPU_wait_lp%=\n\t"
    "sbiw %1,1\n\t"
    "brne CPU_wait_entry%=\n\t"
    :"=&a"(lpcnt)
    :"w"(time)
  );
  return;
}
時間を決めるのは、 nop dec brne の3ステップ(4クロックサイクル)を200回繰り返しているところです。クロックが8MHzとすると
4サイクル×200回=800サイクル、 800/8MHz=0.0001s つまり 0.1ms 100μs が単位時間となります。 1msが必要なら
CPU_wait(10); として呼び出せばよいことになります。クロックに注意が必要です。
(20080123追記)20071221版ではラベル名が%=で終わっていないと最適化によってはエラーとなることがわかりましたので付け加えました。その前の版からのようです。 20060421版では%=がなくても問題はありませんでした。

(20080127追記)引数にF_CPUをかけ算しておくことによりクロックに関係なく引数1で100usとなるように書き換えました。クロックを正しく書く必要はありますが、こちらの方が便利でしょう。25MHzのときは2600(=260ms)まで設定できます。
void wait(unsigned int time)   // 100us wait ルーチン。 クロック設定のこと。
{                                // 25MHzでも 260ms までとれる。
  time*=8;                       // 8MHzのとき。timeにF_CPU(MHz)をかける。重要!!
  unsigned char lpcnt;           
  __asm__ __volatile__("\n"
    "Entry%=: \n\t"
    "ldi %0,24\n"                // 計算値は25。24の方が実測値は良い。
    "Loop%=: \n\t"               // 上の行で 24 のときは100us、249にすれば1msのルーチンになる。
    "nop\n\t"
    "dec %0\n\t"
    "brne Loop%=\n\t"
    "sbiw %1,1\n\t"
    "brne Entry%=\n\t"
    :"=&a"(lpcnt)
    :"w"(time)
  );
  return;
}
    ↑

waitルーチン(_delay_msによる) 050108
WinAVRのルーチンを使ったwaitです。_delay_ms() が大きな値が取れないこと、変数は使えないこと、のために
次のようなルーチンを使います。
makefileで FCPU を正しくセットすることが必要です。 1msよりも少ない時間が必要なときは_delay_ms()の定数を 0.1 や 0.01 とすることもできます。
#define F_CPU 1000000UL  // 1MHzのとき  <util/delay.h>より前に定義してください   
                         //14.7456MHzの時は #define F_CPU 14.7456E6 とも書ける
#include <util/delay.h>
void wait_ms(uint16_t);

void wait_ms(uint16_t t) {        //ウエイトルーチン 引数1で1ms
  while (t--) _delay_ms(1); 
}
(2010.07.04追記)最近のWINAVRでは(2008年以降は間違いなく)大きな引数が取れない制限が無くなりました。
_delay_ms(t); で 1MHzクロックで262.14msまでは正確な値が得られるのは変わりませんが、それを超える値では精度が0.1msになりますが、6553(約6.5秒)まで指定することができます。この拡張で実用上は 100、1000、5000なども使えることになります。
#define F_CPU 1000000UL  // 1MHzのとき。  <util/delay.h>より前に定義してください   
                         //14.7456MHzの時は #define F_CPU 14.7456E6 とも書ける
#include <util/delay.h>
 _delay_ms(100);  // 100ms
 _delay_ms(1000);  // 1秒
 _delay_ms(5000);  // 5秒
同様に _delay_us()も拡張されています。
  _delay_us(900); // 900μs 内部的に _delay_ms(0.9)を呼んでいます。
    ↑

switch :case文 041229 
switch(式){
  case 定数式1: 文11 文12...;break;
  case 定数式2: 文21 文22...;break;
  ・・・
  case 定数式n: 文n1 文n2...;break;
  default: 文1 文2...
}
breakがないと次の行の文を実行してしまいます。特別な意図がない限りbreakを書きます。式には整数型、文字型が使われます。


グローバル変数をコンパイラに省略させない volatile  041226 思いだして書いたので....
グローバル変数が、割り込みルーチン内でのみ更新されるときは、コンパイラの最適化のために変数そのものが無視されます(アセンブリに渡されるときに定数扱いされます)。
これを避けるために、変数宣言で
volatile char int_flag=0; のように volatile を変数名の前に付けておきます。
グローバル変数は宣言時に0に初期化されると読んだ気がしますが、念のために宣言と同時に初期化します。


整数を文字列に変換する 041220
MCUで取り込んだ整数値を、LCDに表示したりパソコンに送るとき文字列に変換したいときがあります。
#include <stdlib.h> //ltoa()が含まれる
int num;        //数値をint型とします。16ビット整数です(確証はありませんが間違いないと思います)。
char strg[7];      //変換後の文字列を入れます。少ないとオーバーフローします。終了記号も含めて十分に。
itoa(num,strg,10);   //これを実行するとstrgに文字列が入ります。10は10進の基数です。2進は2、16進は16と書きます。
lcd_putstr(strg);    //LCDに表示するときはこれを書きます。
32ビット整数(long)の時は ltoa(); を使います。
ここに説明があります。\WinAVR\doc\avr-libc\avr-libc-user-manual\index.htmlのGeneral utilities 



ヘッダファイルの取り込み:.h定義ファイルを読み込んで使うときに書きます。
20050214版から\includeではなく\include\avrにあるものが多くなりました。"avr/"を付けるのが殆どです。
#include<avr/io.h>  avr/io.h を取り込むことを指示します。ポートの入出力に必要です。
#include <avr/interrupt.h>include <avr/signal.h>(20060421で無くなりました)  割り込みを使うときに必要です。
#include "abc.h" 自分で作ったファイルは””で囲みます。


データ型:変数には扱うデータによって、型がきまります。よく使うのは以下のものです。
char c;     変数cには符号付き8ビットデータ(-128〜+127)を入れることができます。
uint8_t n;    変数nには符号なし8ビットデータ(0〜255)を入れることができます。
uint16_t i;    変数iには符号なし16ビットデータ(0〜65536)を入れることができます。
uint32_t bign;  変数bignには符号なし32ビットデータ(0〜約42億)を入れることができます。
C言語には文字列型はありませんが、char str[20]; とすると終了文字\0を含めて20文字の文字列を代入できます。


変数の宣言: 変数の宣言は関数内の初めに書きます。
char suu; uint16_t xx; など。
同じ型の時はカンマで区切って宣言できます。 uint8_t n,nn,p;
宣言と同時に初期化もできます。  uint8_t a=b=0; 


メイン関数(メインルーチン): C言語では、プログラムは int main(void){ ・・・ }関数から始まることになっています。
だからどのプログラムにも必ず int main(void){ }があります(必要です)。
関数は型と引数を持ちます。 型 関数名(引数の型、名) という形になります。
main関数は、通常int型で、引数は「空(から)」又は「ない」という意味の void型とします。


リストの改行: 自由です。見やすいところで改行します。
void wait(uint8_t x){ uint8_t y,z; for(y=1;y<x;y++){z=y*2;}} と書いても、
また,
、 void wait(uint8_t x){ uint8_t y,z; for(y=1;y<x;y++){ z=y*2; } }
と書いても同じです。字下げはプログラムの深さを見やすくします。終わりの「}」は始まりの文の頭とそろえて
見やすくするとともに括弧の数を間違わないようにしています。


関数(サブルーチン)を呼び出す: 
waitという関数(サブルーチン)を呼び出すときは、wait(); wait(13); wait(a);など関数名に引数を付けて書きます。
引数が無いときは( )だけを書きます。


関数(サブルーチン)の書き方: main関数のあとに関数の形式を守って、関数の型、関数名(引数の型と名){ }と書きます。
次の関数は、符号なし8ビットの数値を引数として受け取って、それに5を足して、ポートAに出力するものです。
関数としての意味はありません。
void hyouji(uint8_t x){
  x=x+5;        // xに5を足したものをxに代入する
  PORTA=x;       // portAに出力 文の最後の「;」を忘れないように。PORTは大文字で定義されています
}
これでよいのですが、コンパイラは上からコンパイルするので、main関数の中のhyouji(a)に出会ったときに、hyouji関数を知らん!と
文句を言います。そのために、あとからでてくるよ!と知らせるためにmain関数の前に、1行目を少し変形した void hyouji(uint8_t x);
置きます。「;」に注意してください。これをプロトタイプ宣言と言います。
main()関数の前にサブルーチンを置くとプロトタイプ宣言は必要ありません。


ポートの入出力の決定: ポートのそれぞれの端子を入力として使うか、出力として使うかをMCUに伝えます。
これはMCUの DDRxレジスタに書き込みます。1(=H)が出力、0(=L)が入力で、bitごとに設定できます。
ポートを使う前に決定しないといけません。たとえば、
ポートAの bit7,6,5,4,3を出力に、bit2,1,0を入力として使うときは、bit7〜0の順(重要)に、11111000を書きます。
2進数の11111000を電卓で10進数に直すと 248 になります。したがって、DDRA=248; と書けばよいわけです。
16進数で書くときは 248=f8ですから、DDRA=0xf8 と書きます。(050203追記→)2進数は 0b で書くことができます。
この場合は 0b11111000 となります。 248=0xf8=0b11111000 です。


ポートへの出力:
ポートの全bitを出力として使うときは簡単です。PORTB=5; と書くと 5=00000101 ですから、PB6〜0に0,0,0,0,1,0,1が出力されます。
PB7は無いから無視されます。
ポートが入出力混在の時は複雑です。入力ポートにも書き込まれるからです。幸いなことにAVRでは入力ポートに1を書けばプルアップ、
0ならプルアップしないというだけで、入力の1,0とは関係ありません。普通はプルアップしますから、出力データ(bitごと)に
入力ポートの1を足して書き出せば良いでしょう。


1ポートだけの出力: 6個または7個あるポートの1つだけを(他はそのままにして置いて)1または0にしたいときが多くあります。
これからは使わないようにとの指示がありますが便利なsbi()とcbi()があります。
ポートAのbit3に1を出力したいとき、sbi(PORTA,PA3) と書きます。推奨されているのは PORTA |= _BV(3) です。
ポートBのbit1に0を出力したいとき、cbi(PORTB,PB1) と書きます。推奨されているのは PORTB &= ~_BV(1) です。
20050214版からsbi()、cbi()が使えなくなったので赤字のとおりに書かねばなりません。


入力ポートのプルアップ: 入力に設定したポートはノイズで誤動作しないようにプルアップするのが普通です。
ポートに1を書けば内部でVCCに抵抗(FET)を通してつながります。PORTBの2,1,0ビットをプルアップするときは
PORTB= (PORTB|7); と書きます。 7=00000111bです。「|」でorを取れば他のビットは変化しません。


ポートからの入力: PINA,PINBレジスタ(アドレス)を読み込みます。
ポートAのPA3から変数sに読み込むときは s=(PINA & _BV(3)) で読み込めます。Lならば0が、Hならばポートによって異なる0以外の数が代入されます。
bit_is_clear(sfr,bit) IOレジスタsfrのビットbitがクリア(0)なら 真(≠0)を返します。
bit_is_set(sfr,bit) IOレジスタsfrのビットbitがセット(1)なら 真(≠0)を返します。


演算式:
x=y+z;  x←y+z      
x=y-z;  x←y-z
x=y*z;  x←y*z
x=y/z;  x←y/z
x=y%z   x←y/zの余り(剰余)
a && b  A and B
a || b  A or B
!a    not a
a<b  小さいとき
a>b  大きいとき
a==b   等しいとき     
a!=b   等しくないとき
a & 5  bitごとのand
a | 5   bitごとのor
a ^ 5   bitごとのxor


if文:
条件によって処理を変える=条件分岐    等しいときは==(等号を2つ書く)に注意しましょう(自分がよく失敗する)。
if(a==b){ 処理1; } else{ 処理2; }   aとbが等しければ処理1を行い、等しくなければ処理2を行う。
私は次のように行分けしています。
if(a==b){
  x=5;
  sub();
}else{
  x=0;
  sub2();
}
複数の条件があるとき: if((a==b) && (c>d)){ ・・・・ }  //a=bかつc>dの時 

C言語では、≠0 : 真 =0 : 偽 です。if(a!=0) は if(a) と同じ、if(a==0) は if(!a) とおなじです。 「if(a):0でなければ

for文: ループ処理
for(i=1;i<100;i++){ 文1; 文2; ・・・・・ }   // i=1からループを始め、1回ごとにiを+1して、100より小さい間繰り返す。
for(;;){ }   // 条件を書かないと無限ループになる。 ループは終了しない。よく使います。


コメント:
/* と */ で挟まれた文はコメントになります。したがって、コンパイラは無視します。  // のあとは行末までコメントに なります。


複文:
{ }で囲むと、2つ以上の文が単文の扱いになります。for文やif文は単文しか扱えません。普通は2つ以上の文を書くので、{}でくくります。
単文の時でも後からの追加を考えて私は{}を使っています。


一般的なプログラムの書き方:
コメント文で プログラム名、作成者、日付、プログラムの目的、ソフト・ハードの特記事項 など。あとで役に立つ。
インクルードファイルを書く。
プロトタイプ宣言を書く。
グローバル変数があれば宣言する。
mainルーチン{
  変数の宣言;
  ポートの入出力の決定(DDRx);
  入力ポートのプルアップ;
  ポートの値の初期設定;
  変数の初期値設定;
  プログラム本体の処理; //入力チェック、ポートへ出力、計算、条件判断、サブルーチン呼び出し など。無限ループになることが多い。
}
サブルーチン1{ 処理いろいろ; }
サブルーチンn{ 処理いろいろ; } 


割込:
AVRには多くの割込があります。割込の種類はデバイスによって違いますので、データシートで確認します。
割込の種類については \winavr\avr\include\avr\ioxx.h (xxはデバイス名など)の/* Interrupt vectors */に書かれています。 mega48はiomx8.hにあります。
割込にはSIGNAL()関数とINTERRUPT()関数がありますが、割込中の割込を禁止するSIGNAL()が一般的なようです。
20060421版から ISR()関数を使います。(SIGNAL()関数もまだ使えます)
割込は次のような形になります。
  インクルードファイルを書きます。 #include<avr/interrupt.h>
  main()の前に割込ルーチンを書きます。 SIGNAL(割込の種類){ } ISR(割込の種類){ } (割り込みの種類を示す語も変わっています)
  main()のなか、初期設定で割込許可ビットをセットします。 TIMSK など。これを忘れると割込がかかりません。
  main()のなか、全割込を許可します。 sei(); を書きます。 禁止するときは cli(); を書きます。


キャスト:
型が違う変数に代入するときは、代入する変数を一時的に代入される変数の型に変更します。
これをキャストといいます。 (一時変更の型)変数 という形を取ります。
uint16_t型 intaに uint8_t型 uchra を代入するときは  inta = (uint16_t)uchra と書きます。
キャストしなくても自動的にされたり、キャストしないと誤動作したり、よく分からないことがあります(?)。


ビットのセット: _BV
_BV(4) は第4ビットをセットするマクロです。他のビットはクリアされます。ビット番号は他の名称で定義されていることがよくあります。
第4ビットだけクリアする(他はセット)時は、ビット反転のチルダを付けて ~_BV(4) とします。






TOPへ