2: 2009-08-03 (月) 22:10:44 takaboo ソース 現: 2013-10-07 (月) 19:00:15 eid7gud ソース
Line 1: Line 1:
 +TITLE:ATmega32 Sample Code
 +#norelated
 +#contents
*ダウンロード[#l493db9b] *ダウンロード[#l493db9b]
 +#ref(http://www.besttechnology.co.jp/dow​nload/ATMEGA32SMPL1.6.ZIP)
 +|CENTER:BGCOLOR(red): :idea:|最新のGCCにおいてATmega32Aをターゲットとした場合のDDRのビット定義名が変わってしまったためソース互換性が保てなくなりました。そのため、PORT・DDR・PINの各レジスタのビット定義名は、PORT7~PORT0・DD7~DD0・PIN7~PIN0に変更しました。|
 +
*一覧 [#j8c69fab] *一覧 [#j8c69fab]
-**smpl1(USART).c [#y20358e6] +**USART1.c [#y20358e6] 
-マイコンのUSARTを使ったシリアル通信を行う。シリアル通信はGCC Developer Liteで提供される専用のライブラリを直接使用し、文字及び文字列の送受信や書式化文字列の送信を行う。~+マイコンのUSARTを使ったシリアル通信を行う。シリアル通信はGCC Developer Liteで提供される専用のライブラリを直接使用し、文字及び文字列の送受信や書式化文字列の送信を行う。 
シリアル通信ライブラリのプロトタイプ宣言はrs.hとして提供され、ソースにインクルードする事で参照される。~ シリアル通信ライブラリのプロトタイプ宣言はrs.hとして提供され、ソースにインクルードする事で参照される。~
 #include <rs.h>            // USART通信ライブラリ  #include <rs.h>            // USART通信ライブラリ
Line 10: Line 17:
 // 通常の使用においては数バイト程度で十分です。またバッファはグローバル変数としてください。  // 通常の使用においては数バイト程度で十分です。またバッファはグローバル変数としてください。
 static char txb[10], rxb[10];  static char txb[10], rxb[10];
 + 
 // 通信ポートを初期化 ボーレートは115200[bps]  // 通信ポートを初期化 ボーレートは115200[bps]
 rs_init (br115200, txb, sizeof(txb), rxb, sizeof(rxb));  rs_init (br115200, txb, sizeof(txb), rxb, sizeof(rxb));
Line 21: Line 28:
 ・文字列送信  ・文字列送信
  void  rs_puts (char *);   void  rs_puts (char *);
- ・バイナリ列送信+ ・指定サイズのバイナリバイト列送信
  void  rs_putb (char *, short);   void  rs_putb (char *, short);
 ・受信バッファデータサイズ取得  ・受信バッファデータサイズ取得
Line 27: Line 34:
 ・受信バッファクリア  ・受信バッファクリア
  void  rs_rx_purge (void);   void  rs_rx_purge (void);
- ・1文字受信+ ・1文字受信(受信されるまで待つ)
  char rs_getc (void);   char rs_getc (void);
- ・文字列受信+ ・文字列受信(入力時のエコーバックあり,CRで入力完了)
  short rs_gets (char *, short);   short rs_gets (char *, short);
 ・簡易書式化文字列送信  ・簡易書式化文字列送信
  short rs_printf (const char *, ...);   short rs_printf (const char *, ...);
-**smpl2(USART).c [#jbe6dac4] +**USART2.c [#jbe6dac4] 
-マイコンのUSARTを使ったシリアル通信を行う。avr-libcで提供される標準I/Oルーチンを中継し、シリアル通信はGCC Developer Liteで提供される専用のライブラリを間接的に使用する。~ +マイコンのUSARTを使ったシリアル通信を行う。avr-libcで提供される標準I/Oルーチンを中継し、シリアル通信はGCC Developer Liteで提供される専用のライブラリを間接的に使用する。 
-初期化まではsmpl1とほぼ同様だが、fdevopenで初期化された以後はC言語でよく使われる標準I/Oルーチン(putcharやprintf等)を使用する事ができる。 + 
-  // 通信ポートを初期化 ボーレートは115200[bps] +初期化まではUSART1.cとほぼ同様だが、fdevopenで初期化された以後はC言語でよく使われる標準I/Oルーチン(putcharやprintf等)を使用する事ができる。 
-  rs_init (br115200, txb, sizeof(txb), rxb, sizeof(rxb)); + // 通信ポートを初期化 ボーレートは115200[bps] 
-  // 標準入出力をlibm32.a内のルーチンに割り当て + rs_init (br115200, txb, sizeof(txb), rxb, sizeof(rxb)); 
-  fdevopen(rs_putchar, rs_getchar);+ // 標準入出力をlibm32.a内のルーチンに割り当て 
 + fdevopen(rs_putchar, rs_getchar);
標準I/Oルーチンではまかないきれない機能は、シリアル通信ライブラリで提供されるサブルーチンで補填出来る。 標準I/Oルーチンではまかないきれない機能は、シリアル通信ライブラリで提供されるサブルーチンで補填出来る。
-**smpl3(LED).c [#d0e3aab7]+**LED1.c [#d0e3aab7]
PC4にソース接続されたLEDを点滅させる。~ PC4にソース接続されたLEDを点滅させる。~
#ref(led1234.png) #ref(led1234.png)
-AVRの内部レジスタは avr/io.h をインクルードする事でデータシートに記載されている名称で参照できる。~+AVRの内部レジスタは avr/io.h をインクルードする事でデータシートに記載されている名称で参照出来る。なお、io.hは複数のデバイスのヘッダを取りまとめており、ATmega32の場合は avr/iom32.h が実体である。 
 + #include <avr/io.h>
PC4を出力ポートに設定するにはDDRCの4ビット目を1に設定する。 PC4を出力ポートに設定するにはDDRCの4ビット目を1に設定する。
- DDRC |= _BV(DDC4);    // PC4を出力に+ DDRC |= _BV(DD4);    // PC4を出力に
PC4へ1を書くとLEDは点灯、0を書くと消灯する。ちなみに、_BV() は引数で指定した数値をビットに変換するavr-libcで提供されるマクロである。~ PC4へ1を書くとLEDは点灯、0を書くと消灯する。ちなみに、_BV() は引数で指定した数値をビットに変換するavr-libcで提供されるマクロである。~
- PORTC &= ~_BV(PC4); // 消灯 + PORTC &= ~_BV(PORT4); // 消灯 
- PORTC |= _BV(PC4);  // 点灯+ PORTC |= _BV(PORT4);  // 点灯
点滅の周期は util/delay.h で宣言される _delay_ms 関数を使用し、ミリ秒単位で設定している。 点滅の周期は util/delay.h で宣言される _delay_ms 関数を使用し、ミリ秒単位で設定している。
 _delay_ms (500);    // 500ms待つ  _delay_ms (500);    // 500ms待つ
-**smpl4(4bitLED).c [#k0566312] +**LED2.c [#k0566312] 
-PC4~PC7へそれぞれLEDをソース接続し点滅させる。~+PC4~PC7へそれぞれLEDをソース接続し点滅させる。 
出力端子の初期化は以下の通り。 出力端子の初期化は以下の通り。
-  DDRC |= _BV(DDC4) | _BV(DDC5) | _BV(DDC6) | _BV(DDC7);  // PC4..7を出力に+ DDRC |= _BV(DD4) | _BV(DD5) | _BV(DD6) | _BV(DD7);  // PC4..7を出力に
点灯させるビットが増えると一つ一つビットを指定するのが面倒なので、4ビットまとめて点灯ないし消灯させる関数を用意している。 点灯させるビットが増えると一つ一つビットを指定するのが面倒なので、4ビットまとめて点灯ないし消灯させる関数を用意している。
 void LED (int d) {  void LED (int d) {
Line 64: Line 74:
 }  }
-**smpl5(ALLPIO).c [#i281618a]+**ALLPIO.c [#i281618a]
PC2~3にDIPスイッチ、PD2~PD3にプッシュボタンを接続し、逐次それらの状態を取り込んでLED1~4に反映させる。~ PC2~3にDIPスイッチ、PD2~PD3にプッシュボタンを接続し、逐次それらの状態を取り込んでLED1~4に反映させる。~
#ref(switch.png) #ref(switch.png)
入力端子の初期化は以下の通り。 入力端子の初期化は以下の通り。
 // DIP1(PC2,3)を構成  // DIP1(PC2,3)を構成
- DDRC &= ~(_BV(DDC2) | _BV(DDC3)); // PC2,3を入力に + DDRC &= ~(_BV(DD2) | _BV(DD3)); // PC2,3を入力に 
- PORTC |= (_BV(PC2) | _BV(PC3));  // PC2,3の内蔵プルアップ抵抗をON + PORTC |= (_BV(PORT2) | _BV(PORT3));  // PC2,3の内蔵プルアップ抵抗をON 
 + 
 // PB1~PB2(PD2,3)を構成  // PB1~PB2(PD2,3)を構成
- DDRD &= ~(_BV(DDD2) | _BV(DDD3)); // PD2,3を入力に + DDRD &= ~(_BV(DD2) | _BV(DD3)); // PD2,3を入力に 
- PORTD |= (_BV(PD2) | _BV(PD3));  // PD2,3の内蔵プルアップ抵抗をON+ PORTD |= (_BV(PORT2) | _BV(PORT3));  // PD2,3の内蔵プルアップ抵抗をON
DIPスイッチは2ビットまとめて数値として取り込む関数を用意している。 DIPスイッチは2ビットまとめて数値として取り込む関数を用意している。
 // DIP1(ディップスイッチ)の状態取り込み  // DIP1(ディップスイッチ)の状態取り込み
Line 83: Line 93:
 // プッシュボタン1の状態取り込み  // プッシュボタン1の状態取り込み
 unsigned char PB1_STAT (void) {  unsigned char PB1_STAT (void) {
-   return bit_is_clear (PIND, PIND2);+   return bit_is_clear (PIND, PIN2);
 }  }
 + 
 // プッシュボタン2の状態取り込み  // プッシュボタン2の状態取り込み
 unsigned char PB2_STAT (void) {  unsigned char PB2_STAT (void) {
-   return bit_is_clear (PIND, PIND3);+   return bit_is_clear (PIND, PIN3);
 }  }
-**smpl6(PIOINT).c [#g253e09b] +**PIOINT.c [#g253e09b] 
-外部割り込みを使ってプッシュボタンの押下を検出し、各々のボタンで異なる機能を持たせる。さらに、割り込みが発生しない間はマイコンをスリープモードにし、低消費電力モードに移行させる。~+外部割り込みを使ってプッシュボタンの押下を検出し、各々のボタンで異なる機能を持たせる。さらに、割り込みが発生しない間はマイコンをスリープモードにし、低消費電力モードに移行させる。 
プッシュボタン1,2はPD2(INT0)とPD3(INT1)に直結しており、押下されて信号が0になった瞬間に割り込みを励起するよう設定する。 プッシュボタン1,2はPD2(INT0)とPD3(INT1)に直結しており、押下されて信号が0になった瞬間に割り込みを励起するよう設定する。
 // INT0/1の割り込み条件を立下りエッジに  // INT0/1の割り込み条件を立下りエッジに
 MCUCR |= _BV(ISC11) | _BV(ISC01);    // ISC11とISC01を1に  MCUCR |= _BV(ISC11) | _BV(ISC01);    // ISC11とISC01を1に
 MCUCR &= ~(_BV(ISC10) | _BV(ISC00));  // ISC10とISC00を0に  MCUCR &= ~(_BV(ISC10) | _BV(ISC00));  // ISC10とISC00を0に
 + 
 // INT0/1割り込みを許可  // INT0/1割り込みを許可
 GICR |= _BV(INT1) | _BV (INT0);  GICR |= _BV(INT1) | _BV (INT0);
-マイコンの低消費電力モードを設定する。今回はINT0/1でスリープから復帰できるモードを選択する。+  
 + // 全割り込み許可 
 + sei (); 
 + 
 +マイコンの低消費電力モードはINT0/1でスリープから復帰できるモードを選択する。
 set_sleep_mode (SLEEP_MODE_IDLE);  set_sleep_mode (SLEEP_MODE_IDLE);
mainの中では sleep_mode () をコールする以外に特に何もしないが、スリープから復帰した事を明示的にわかるようにLED4を点滅する様に仕掛けている。 mainの中では sleep_mode () をコールする以外に特に何もしないが、スリープから復帰した事を明示的にわかるようにLED4を点滅する様に仕掛けている。
Line 106: Line 121:
 while (1) {  while (1) {
   sleep_mode ();    sleep_mode ();
-   PORTC ^= _BV(PC7);  // 起きる度にLED4をブリンク+   PORTC ^= _BV(PORT7);  // 起きる度にLED4をブリンク
 }  }
INT0に対応した割り込みルーチン名は INT0_vect としてavr-libcにて決められているので、その様に記述。処理はcnt変数の値をインクリメントし、その値をLED1~3に表示する。 INT0に対応した割り込みルーチン名は INT0_vect としてavr-libcにて決められているので、その様に記述。処理はcnt変数の値をインクリメントし、その値をLED1~3に表示する。
Line 112: Line 127:
   LED (++cnt);    LED (++cnt);
 }  }
-INT1に対応した割り込みルーチン名は INT1_vect としてavr-libcにて決められているので、その様に記述。処理はcnt変数の値をインクリメントし、その値をLED1~3に表示する。+INT1に対応した割り込みルーチン名は INT1_vect としてavr-libcにて決められているので、その様に記述。処理はcnt変数の値をデクリメントし、その値をLED1~3に表示する。
 ISR(INT1_vect) {  ISR(INT1_vect) {
   LED (--cnt);    LED (--cnt);
 }  }
-**smpl7(ADC).c [#h9ecd904] + 
-PA0~PA7の端子に入力されるDC0~5Vの電圧を計測し、シリアル通信で送信する。~+**ADC.c [#h9ecd904] 
 +PA0~PA7の端子に入力されるDC0~5Vの電圧を計測し、シリアル通信で送信する。 
ここではA/D変換するにあたり基準となる電圧(DC5V)をAVCC端子に接続した。 ここではA/D変換するにあたり基準となる電圧(DC5V)をAVCC端子に接続した。
#ref(cn2_adc.png) #ref(cn2_adc.png)
Line 139: Line 156:
 ・PushButton2を押している間  ・PushButton2を押している間
  ADC=[ 0.57V,0.32V,1.21V,1.18V,1.55V,1.93V,1.82​V,1.78V ]   ADC=[ 0.57V,0.32V,1.21V,1.18V,1.55V,1.93V,1.82​V,1.78V ]
 +
 +**TIMERINT.c [#abc72a43]
 +タイマ/カウンタ0のオーバーフローを使用して一定間隔で割り込みを励起する。
 +
 +タイマ/カウンタ0のオーバーフロー割り込みを許可するのは以下の通り。
 + // タイマ/カウンタ0割り込みマスク構成
 + TIMSK |= _BV(TOIE0);  // タイマカウンタ0オーバーフロー割り込み許可
 +タイマ/カウンタ0をCLK/1024(16MHz/1024=15.625kHz)で標準動作をさせるには以下の通り。クロックを選択した瞬間からタイマとして動作を開始する。
 + TCCR0 = _BV(CS02) | _BV(CS00);  // 標準動作,CLK/1024
 +15.625kHz毎にTCNT0の値がインクリメントされ、TOP値の255を超えて0にリセットされるとオーバーフロー割り込みが発生(15.625kHz/256=約61Hz)する。タイマ/カウンタ0のオーバフロー割り込みルーチン名は TIMER0_OVF_vect としてavr-libcにて決められているので、その様に記述。ここでは割り込みが発生したらさらにソフト的に分周し、約0.5秒毎にLED1を点滅させている。
 + ISR (TIMER0_OVF_vect) {
 +   static int cnt = 0;
 +   // 1/16MHz * 1024 * 256 秒毎に割り込み発生
 +   if (cnt++ > 31) { // 約0.5秒毎にLEDを反転
 +     cnt = 0;
 +     rbi (PORTC, PORT4);
 +   }
 + }
 +mainは諸々の初期化を行った後は例のごとくしつこく省電力モードに移行し続ける以外の処理は行っていない。
 + while (1) {  // 無限ループ
 +   sleep_mode ();
 + }
 +
 +**2chPWM.c [#p286a3ce]
 +タイマ/カウンタ1の高速PWMモードにてPD4(OC1B)端子とPD5(OC1A)端子から個別のデューティー比でPWM信号を出力できる様に構成し、PWMの1周期(コンペアマッチ)毎に割り込みを発生させてLED1の点滅と周期の更新を行う。~
 +PWM信号を五感で把握するにはPD4とPD5端子にオシロスコープ等を接続し、オシロスコープのディスプレイ上に表示される波形にて確認する。
 +
 +LED1が接続されたPC4とPWM信号を出力するPD4とPD5を出力に設定する。
 + // LED1(PC4)を構成
 + DDRC |= _BV(DD4);    // PC4を出力に
 + PORTC &= ~_BV(PORT4);    // LEDをOFF PC4=0
 + 
 + // PWM出力端子を構成
 + DDRD |= _BV(DD4) | _BV(DD5);  // PD5,PD4を出力に
 +タイマ/カウンタ1を高速PWMモードに設定する。ICR1がPWMの周期、OCR1AがPD5(OC1A)のデューティー、OCR1BがPD4(OC1B)のデューティーとなり、ここでは周期を8ms(=125Hz=CLK/256 /500)としたので以下の通りに設定する。
 + // タイマ/カウンタ1を高速PWMモードに設定 (ICR1でTOP)
 + TCNT1  = 0;
 + ICR1    = 500;    // TOP値 62.5kHz/500=8ms周期
 + OCR1A  = cnt[0]; // PD5(OC1A)のデューティー
 + OCR1B  = cnt[1]; // PD4(OC1B)のデューティー
 + TCCR1A  = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11);
 + TCCR1B  = _BV(CS12) | _BV(WGM13) | _BV(WGM12); // CLK/256=62.5kHz
 +TCNT1の値がICR1と一致したらコンペアマッチA割り込みを励起させるには、TIMSKのOCIE1Aビットを1にする。
 + // タイマ/カウンタ割り込みマスク設定
 + TIMSK |= _BV(OCIE1A);
 +タイマ/カウンタ1のコンペアマッチAオーバフロー割り込みルーチン名は TIMER1_COMPA_vect としてavr-libcにて決められているので、その様に記述。割り込み発生毎に各々のPWMのデューティ値をインクリメントし、500以上になったら0にリセットしている。
 + ISR (TIMER1_COMPA_vect) {
 +   int i;
 + 
 +   // 1周期毎に比較レジスタ更新
 +   for (i = 0; i < 2; i++) if (cnt[i]++ > 500) cnt[i] = 0;
 +   OCR1A = cnt[0];
 +   OCR1B = cnt[1];
 + 
 +   rbi (PORTC, PORT4); // LEDビット反転
 + }
 +
 +**EEPROM.c [#v4acb9af]
 +マイコンに内蔵されたEEPROMの読み書きを行う。操作及び状況の確認はシリアル通信にて行う。
 +
 +内蔵EEPROM関連のサブルーチン等は avr/eeprom.h に定義される。
 + #include <avr/eeprom.h>    // EEPROM関連
 +EEPROMへのアクセスを行う際はそれ以前の内部的なEEPROMへの処理が完了している必要があるため eeprom_busy_wait 関数にて処理完了を待ち、さらにアクセス中は他の処理に影響が及ぶとの事で割り込み等は禁止にする必要がある。
 + cli ();
 + eeprom_busy_wait ();
 + eeprom_write_byte ((uint8_t *)i, c);
 + sei ();
 +**CONSTMEM.c [#gc65cc96]
 +avr-libcで提供される特殊なメモリアクセス方法の紹介。
 +
 +AVRはハーバードアーキテクチャーであるため、プログラムコード領域(フラッシュROM)とワーク領域(SRAM)が分離されている。つまり連続・不連続に関わらず同一のメモリマップ上には配置されないため、一般的なC言語の記述ではフラッシュROM上に置かれたデータ類へのアクセスが出来ない。~
 +例えば
 + const char hoge[] = "TEST DATA\n";
 +といった宣言を行うとフラッシュROM内にhogeのデータが確保され、プログラムの実行が開始される際にhogeをSRAM上にコピーしたものがアクセス対象となる。つまり、ある程度サイズに余裕のあるフラッシュROM上に不変の固定データを置いたつもりでも、それと同等のサイズのデータがSRAM上に別途確保されるので、非常に小さいSRAMを圧迫する結果となる。~
 +avr-libcにはSRAMにコピーせずにフラッシュROM上に確保したデータへアクセスするために、宣言時と利用時に特殊な命令が用意されている。
 + const char gyao[27000] PROGMEM = {0,0,0,0,0,0,};
 +**BUZZER.c [#e8471f5c]
 +PD7(OC2)端子に接続されたトランジスタを介して他励式のブザーを駆動する。
 +#ref(bz.png)
 +この回路ではPD7端子がHIGHになるとトランジスタがONしてブザーの+/-両端に電圧が加わる。LOWになるとトランジスタがOFFしてブザーの-端子がオープンになる。これをゆっくり繰り返すとブザーがプチプチとなるだけだが、数百~数kHzの周期で繰り返すとぴ~ぷ~といった音として聞こえる様になる。
 +
 +周波数はPA7(ADC7)端子に接続したポテンショメータで分圧された電圧を計測する事で設定するものとしている。
 +#ref(vr.png)
 +
 +PD7はカウンタタイマ2のOC2端子が接続されており、コンペアマッチでトグル動作する様に設定すればOCR2に設定できる範囲の値で周波数が設定出来る。また同時にPD7を出力端子に設定する必要もある。
 + void ToneOn (int tone) {
 +   OCR2 = UD_ToneTable[tone];
 +   TCCR2 = _BV(WGM21) | _BV(COM20) | _BV(CS22) | _BV(CS20);
 +   // PD7(OC2)を出力に
 +   DDRD |= _BV(DD7);
 + }
 +大抵ブザーの類は常時一定の電圧を加えておく事は推奨されていないので、音を出さないときはOFFしておく事で劣化を防ぎつつ無駄に電力を消費する事もなくなる。
 + void ToneOff (void) {
 +   // PD7(OC2)を入力(外部のプルダウン抵抗でLOWに)
 +   DDRD &= ~_BV(DD7);
 +   TCCR2 = 0;
 + }
 +
 +**SPI_BRIDGE.c [#j47c03d0]
 +SPIバスで接続されたATA6831(3ch ハーフブリッジドライバ)と通信を行い、任意のパターンで各チャネルのトランジスタをON/OFFさせる。
 +#ref(spi.png)
 +SPIは簡単に言えばクロック同期式の双方向シリアル通信で、MISO・MOSI・SCKとチップセレクトの4本の信号でコミュニケーションする。対象によってタイミング等細かい違いはあるが、AVRの場合は大抵のデバイスとと接続する事ができる。
 +
 +ATA6831は3chのハーフブリッジを内蔵した主にリレーやランプを駆動するドライバICである。ここのうちハーフブリッジを2つ使えばモータの正/逆転を行うのに十分なHブリッジが構成できるので、小型モータの駆動程度であれば十分に使える。~
 +また3chのハーフブリッジに対してモータをうまくつなげば、動作モードの制限はあるものの2台を個別に駆動する事ができない事も無い。
 +
 +**RCSERVO.c [#l9555243]
 +1chのPWM信号をマルチプレクサを介して見かけ上8chに分配する。出力端子を変更する毎にPWMのデューティを更新しなくてはならないので少し面倒。
 +
 +**ARRAYFUNC.c [#e780cbb7]
 +C言語の分岐命令等で条件別に関数を呼ぶのではなく、初期化済み配列に関数自体を登録しておき簡便に呼び出すプログラム。


トップ   差分 リロード印刷に適した表示   全ページ一覧 単語検索 最新ページの一覧   最新ページのRSS 1.0 最新ページのRSS 2.0 最新ページのRSS Atom