づづき
この前はATtiny13のコード圧縮化を検討するにあたってレジスタ操作を行ってArduino関数であるdigitalWrite()とanalogRead()を再現してみました。
今回はanalogRead()について掘り下げていこうと思います。
具体的には前回が8bt(0~255)分解能のAD変換であったために10bit(0~1023)分解能のAD変換の実装とAD変換に関するレジスタをちょっと解説しつつ進めていこうかなと思っております。
16bit analogRead()の再現
実装
準備する材料とテスト回路については前回と変わらず可変抵抗入力回路です(変わり映えがなくてすみません、、、)。
では早速16bitのAD変換コードになります。
※効果は不明ですがDIDR0レジスタを操作して指定のADCピンのデジタル入力を禁止しています。省電力化の効果があるようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
//16bitアナログ変換 #include <avr/io.h> #include <BasicSerial3.h> #include <util/delay.h> uint16_t analog; uint16_t Aread(uint8_t ch) { uint16_t ad, ad2; ADCSRA |= 0b10000110;//AD許可,単独変換,AD変換CK/64 ADMUX |= 0b00100000;//Vcc基準,左揃え ADMUX |= ch; //ADC番号を引数とする DIDR0 |= 0b00000001 << (ch + 1);//対応するADCnピンのデジタル入力禁止 ADCSRA |= 0b01000000;//AD変換開始 loop_until_bit_is_set(ADCSRA,ADIF);//変換完了フラグが1になるまで待機 ad = ADCL; //下位8bit取得 ad2 = ADCH; //上位8bit取得 ad2 |= ad2<<2 | ad>>6;//下位を6bit右シフト、上位を2bit左シフトでOR return ad2; } void serOut(const char* str) { while (*str) {TxByte (*str++);} } // 整数を10進数で出力 void OutDEC(uint16_t d) { int8_t n =-1; uint16_t v = 10000; for (uint8_t i=0; i<5; i++) { if (d >= v) { TxByte(d/v + '0'); d %= v; n=i; } else { if (n!=-1||i==4) TxByte ('0'); } v/=10; } } void setup() { } void loop() { _delay_ms(500); analog = Aread(2); OutDEC(analog); serOut("\n\r"); } |
いかがでしょうか、うまく書き込むことが出来きたら10bitのAD変換ができた、、、はずです。
考察と解説
コード的には前回の8bit_AD変換とほとんど変わりませんが10bitアナログ値にするためにアナログ値が格納されているADCL, ADCHのレジスタを組み合わせる処理をしています。イメージ図を下記に示しますね。
※今回は左揃え(ADLAR=1)としています。
- 符号なし16bitの箱を2個用意してADCL, ADCHを格納します。
- adを6bit右にシフトさせて、ad2を2bit左にシフトさせる。
- ビット演算子OR(|)で足し合わせてad2に格納。
今回は左詰めで行いましたが、右詰めの時も考え方は同じです。
まず、右詰めの時はADMUXレジスタの5bit目(ADLAR)を0とします。
演算に関しては上記と同じく符号なし16bit変数を2個用意して、ADCHが入っている方を8bit左にシフトさせてORで足し合わせればOKです。参考に差分のコードを載せますね。
- 上記コードの11行目と入れ替えてください。
1 |
ADMUX |= 0b00000000;//Vcc基準,右揃え |
- 上記のコードの19, 20行目と入れ替えてください。
1 2 |
ad |= ad2<<8;//上位8bit左にシフトさせてOR return ad; |
以上になります!
ちなみに今回は単発でAD変換を行っております。
ちょっとだけおまけで、連続変換でAD変換した時に変換完了割り込みでの連続ADC値取得を最後に行ってみます。
おまけ 連続ADC
という事で最後に連続ADCの値を変換完了割り込みで取り込んでみたいと思います。
割り込みのヘッダファイル<avr/interrupt.h>は以下のサイトを参考にしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//16bitアナログ変換ADC完了割り込み #include <avr/io.h> #include <BasicSerial3.h> #include <avr/interrupt.h> //AD変換完了割り込み ISR(ADC_vect){ uint16_t ad, ad2; ad = ADCL; //下位8bit取得 ad2 = ADCH; //上位8bit取得 ad |= ad2<<8;//上位8bit左にシフトさせてOR } void setup() { ADCSRA |= 0b10101110;//AD許可,連続変換,ADC割り込み許可,AD変換CK/64 ADMUX |= 0b00000001;//Vcc基準,右揃え DIDR0 |= 0b00000001 << 3;//対応するADCnピンのデジタル入力禁止 ADCSRA |= 0b01000000;//AD変換開始 sei(); } void loop() { } |
連続変換を有効にして、ISR(ADC_vect)で割り込みを実行しているつもりですが、、、
シリアルモニタでモニタするにも割り込みが早すぎるのかうまくモニタ出来なく確かめることができなかったす。。。
ちょっとこのやり方でのADCはもう少しお勉強してみます。詳しい方がいらっしゃったらADCの連続変換とADC完了割り込みからのアナログ値取り込みの使い道を教えてくれたら幸せになります。
以上、よろしくお願いいたします。
ではでは~
コメント