1

AVR – MEGA8のタイマ/カウンタ0割り込み

今日もatmega8。SIG_OVERFLOW0でのタイマ/カウンタ割込みをやってみたよDSC00830.jpg

PB0のLED点滅はタイマー割り込みで行う。同時にmain()の中のループで、UARTで文字列を送信する。→ソースコード(AVR-GCC)動画(2.1MB)

タイマ/カウンタ0は8bit。mega8には他に16bitのタイマ/カウンタ1(PWM付き)と8bitのタイマ/カウンタ2(PWM付き)があり、3つとも別のタイミングで割り込みを発生させる事ができる。

でもまだ1と2は使ってない。

タイマ0の精度として、TCCR0レジスタで分周率を設定する。

HERO’s Downloadさんより

■やり方

タイマ/カウンタ0の設定関数

/* タイマ/カウンタ0オーバーフロー割り込み設定  */
void overflow0_init(){
TCNT0 = 0; // タイマ0初期値
TCCR0 = (0<<CS02)|(0<<CS01)|(1<<CS00); // 分周率設定 前置分周無し
sbi(TIMSK,TOIE0); // タイマ/カウンタ0オーバーフロー割り込み許可
}

これをmain()から呼び出す。

int main(void){
port_init(); // PORT設定
usart_init(MYUBRR); // USART設定
overflow0_init(); // タイマ/カウンタ0設定
sei(); // 割り込み許可
for(;;){
uart_send_str(&qute;hoge\0&qute;);
}
}

sei(); を忘れずに。

んで割り込み部分

volatile unsigned int led0;
/* タイマ/カウンタ0オーバーフロー割り込み */
SIGNAL(SIG_OVERFLOW0){
led0++;
if(led0 > 1500) LED_SET(); // LED点灯
else LED_CLR(); // LED消灯
if(led0 == 3000){
led0 = 0; // カウント初期化
}
nop(1); // これ入れないと固まる
}

よくわからないけど、nop(int count); という自分で用意した関数を呼び出さないと、main()の中の処理を巻き込んで止まる。

多重割り込みとか?

一応nopの中身

/* No Operation */
void nop(int count){
int i;
for(i = 0; i < count*100; i++){
}
}

そういえばDigi-Key、mega8値上がりしてますか?mega48のが安くなってますね。mega88と同じぐらいの値段。次実装するとしたらmega88かな

PWM6個あるし

2

AVR – MEGA8の外部入力割り込み

90S2313での外部入力割り込みに続いて、ATMEGA8でも外部入力割り込み SIG_INTERRUPT0, SIG_INTERRUPT1 を使った。

DSC00816.jpg

INT0、INT1への割り込みをSIG_INTERRUPT0、SIG_INTERRUPT1でそれぞれ受け取り、立ち上がりエッジ/立ち下がりエッジで2つのLEDを点灯/消灯させる。→ソースコード(AVR-GCC)動画(3MB)

90S2313は立ち上がり/立ち下がりのどちらかしか検出できなかったのでプログラムでなんとかしていたが、mega8は論理変換を検出(つまり両エッジ)できるので全然簡単になっている。というかそれ以外全部同じだったので一発で動いた。

データシートの翻訳版より

MCUCR(MCU制御レジスタ)の割り込み発生条件制御ビット表

■やり方

INT0を設定する関数を作った。

/* INT0設定 */
void interrupt0_init(void){
cbi(DDRD,PD2); // INT0入力設定
sbi(GICR,INT0); // INT0割り込み許可
sbi(GIFR,INT0); // INT0割り込み要求フラグON
/* 両エッジ検出 */
sbi(MCUCR,ISC00);
cbi(MCUCR,ISC01);
/* 立ち上がりエッジ検出
sbi(MCUCR,ISC01);
sbi(MCUCR,ISC00);
*/
/* 立ち下がりエッジ検出
sbi(MCUCR,ISC01);
cbi(MCUCR,ISC00);
*/
}

main()で呼び出して、すぐsei();して有効にしておく。

そして割り込み部分。

/* INT0 外部割り込み */
SIGNAL(SIG_INTERRUPT0){
if(bit_is_set(PIND,PD2)){ // 立ち上がりの時
LED1_SET(); // LED1点灯
}
else{ // 立ち下がりの時
LED1_CLR(); // LED1消灯
}
}

INT1の方も全く同様に書いてある。

4

AVR – MEGA8のUART送受信

ATMEGA8のUART受信割り込みが出来なかったのが、できた。

DSC00788.jpg

PCのキーボードからoを送るとLEDを点灯、xを送ると消灯する。それ以外を送ると”ERROR”と返してくる。

ソースコード(AVR-GCC)動画(8MB)

UARTの初期化は


#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#define FOSC 8000000 // 8MHz
#define BAUD 9600 // 9600bps
#define MYUBRR FOSC/16/BAUD-1 // UART分周率
volatile char uart_recv_data; // UART受信データ
/* USART設定 */
void usart_init(unsigned int baud){
UBRRH = (unsigned char)(baud>>8); // ボーレート上位
UBRRL = (unsigned char)baud; // ボーレート下位
UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE); // 送受信許可 受信完了割り込み許可
UCSRC = (1<<URSEL)|(3<<UCSZ0)|(0<<USBS)|(0<<UPM0);
// フレーム設定 非同期通信 8ビット 1ストップビット パリティ無し
}


で、 usart_init(MYUBRR); で呼び出す。

受信割り込みは、sei();しておいて

/** UART受信割り込み **/
SIGNAL(SIG_UART_RECV){
if(bit_is_clear(UCSRA,FE)) // フレーミングエラーが無い時
uart_recv_data = UDR; // 受信データをグローバル変数に取得
}

をどこかに書いておけば実行される。

uart_recv_dataはグローバル変数として、volatileで宣言しないとならない。というかここだけ3ヶ月前にやって挫折したコードから直したら動いたので、かなり重要らしい。よくわからないけど。

1

体重計TANITA1630 hack失敗

ネット体重計 – ネット家電研究会を参考にしながら、デザ言Dの課題の一部として体重計(TANITA1630)を改造しようと思ったら失敗した。

DSC00781.jpg

ソースコード(AVR-GCC)

安い体重計。表示部分が7segLEDなのでそこから取る。

DSC00732.jpgDSC00734.jpg



DSC00735.jpg

2カ所のバネを外し、電池ぶたの裏の金具も外すと

DSC00740.jpgDSC00743.jpg

開く。そして基盤裏

DSC00748.jpgDSC00751.jpg

7セグメントLED(BT-M515RE)を取り外す。3桁なのに12ピンしかない。

DSC00756.jpgDSC00758.jpg

データシートの置いてあるchipdocへのログインがめんどくさいので、自分で調べた。普通のアノードコモンの7segが三つ合体しているだけ。アノードが3つあって3桁のどれを光らせるかを選ぶ様になっている。

DSC00786.jpg

この7セグメントLEDへの指示をAVRで横取りして、体重を見る。

ケタ指定はアノードに繋がっていたので体重計のマイコン(NEC uPD789024)からの出力をそのまま入力すればOK。数字はカソードなので

DSC00787.jpg

こんな感じにすると、ケタと数字が読みとれる。

VCC, GNDを体重計から取ってこんな感じになった。

DSC00759.jpgDSC00781.jpg

AT90S4433を久しぶりに使った。

そして失敗した。数字がまざる。

原因は多分クロックか何か。

uPD789024が5MHzで動いていて、すごい勢いで3桁を切り替えている。こちらの4433は8MHzで、桁を監視し、足の状態から数字を推測している。多分これがまずい。

足の状態を取得し終わった後、数字へ変換する様にしてみても駄目だった。

あともっと速くするには

/** 体重計IOまわり **/

#define SEGLED_N_1() bit_is_clear(PINC,PC5) // セグメントLED 数字表示部

#define SEGLED_N_2() bit_is_clear(PINC,PC4)

#define SEGLED_N_3() bit_is_clear(PINC,PC3)

#define SEGLED_N_4() bit_is_clear(PINC,PC2)

#define SEGLED_N_5() bit_is_clear(PINC,PC1)

#define SEGLED_N_6() bit_is_clear(PINC,PC0)

#define SEGLED_N_7() bit_is_clear(PINB,PB2)

#define SEGLED_N_8() bit_is_clear(PINB,PB1)

#define SEGLED_D_1() bit_is_set(PINB,PB0) // セグメントLED 桁表示部

#define SEGLED_D_2() bit_is_set(PIND,PD7)

#define SEGLED_D_3() bit_is_set(PIND,PD6)

が原因というか、1PORTの情報まるごと取得する様にすれば良いんだけど4433は8ピンあるPORTが1つも無いので8535か2313などでやり直してみると良いかも知れない。

あと分解したは良いけど元に戻せる気がしない。

1

AVR – 90S2313で外部入力割り込み

AVR-GCCで外部入力割り込みことSIG_INTERRUPT0, SIG_INTERRUPT1を使った。

ピンへの入力電圧が高くなる瞬間/低くなる瞬間を検知(edge detection)して割り込みを起こせる機能で、スイッチや赤外線の解析などに使える。

普通、マイコンのピンへの入力はループの中で毎回いちいち if(bit_is_set(PORT,BIT)) とかやって監視しなければならないが、外部入力割り込みを使えばイベントドリブンなコードが簡単に書ける。地味だけど凄く良い。

(ハードウェアを制御する部分とメインルーチンを分けて書かないと、状態遷移や例外処理で頭がおかしくなってしまう)

DSC00608.jpg

チャタリング防止を施したスイッチをINT0,INT1(90S2313の場合はPD2,PD3)に接続して、入力がある時それぞれPB0,PB2のLEDが光る。→ソースコード映像(5.1MB)

※デジタル入力全般に言える事だけど、使わない入力ピンが存在してはならない。何も接続しないピンはDDRを出力に向けるか、チャタリング防止をつけるかしないと怪しい動きをしてしまう。

■INT0を使う場合の書き方

外部割込みは立ち上がり/立ち下りの片方しか検知できない。

しかし、立ち上がったら立下り検知設定に、立ち下がったら立ち上がり検知設定にすれば両方検知できる。

外部割り込みの設定関数としてmain()より上の行に

/* PORT設定 */

void port_init(void){

DDRB = 0xff;

DDRD = 0xff;

}

/* 外部割り込み INT0設定 */

void interrupt0_init(void){

cbi(DDRD, PD2); // INT0入力

sbi(GIMSK, INT0); // 外部割り込み0許可

sbi(MCUCR, ISC01);

if(bit_is_set(PIND,PD2)) // 既に立ち上がってる時

cbi(MCUCR, ISC00); // INT0立ち下がりエッジ検出設定

else sbi(MCUCR, ISC00); // INT0立ち上がりエッジ検出設定

}

を書く。

初期状態によって立ち上がり/下がりどちらから始めるか分岐する様にした。

割り込み時の処理。

LED1を付けたり消したりする。

/* INT0 外部割り込み */

SIGNAL(SIG_INTERRUPT0){

if(bit_is_set(PIND,PD2)){ // 立ち上がりの時

LED1_SET(); // LED1点灯

cbi(MCUCR,ISC00); // 立ち下がり検出設定に切り替え

}

else{ // 立ち下がりの時

LED1_CLR(); // LED1消灯

sbi(MCUCR,ISC00); // 立ち上がり検出設定に切り替え

}

}

mainはこれだけ

int main(void){

port_init(); // PORT設定

interrupt0_init(); // INT0設定

// interrupt1_init(); // INT1設定(INT0だけ使う)

sei(); // 割り込み許可

for(;;){

}

}

ちなみに読み込むヘッダファイルやマクロの設定はこんな感じ。

LEDのON/OFFもあんまりハードコーディングしたくない。

#include

#include

#include

/** 動作設定 **/

#define TRUE 1

#define FALSE 0

#define NULL ‘\0’

#define sbi(PORT,BIT) PORT|=_BV(BIT) // PORTの指定BITに1をセット

#define cbi(PORT,BIT) PORT&=~_BV(BIT) // PORTの指定BITをクリア

/** LED設定 **/

#define LED1_SET() sbi(PORTB,PB0) // LED1点灯

#define LED1_CLR() cbi(PORTB,PB0) // LED1消灯

#define LED2_SET() sbi(PORTB,PB1) // LED2点灯

#define LED2_CLR() cbi(PORTB,PB1) // LED2消灯

sbi,cbiマクロはこっち