Arduino

2019年4月15日 (月)

Arduino PB5(デジタル入出力D13)に注意を

2019年3月22日:Arduinoのアナログ基準電圧入力の絡みで、Arduinoのブートローダーを眺めていて注意というか覚え書きをちょいとまとめておきます。

●PB5ポート
ATmega328の19番ピン。
Arduinoでいうところのデジタル入出力「D13」ポート。
SPI(Serial Peripheral Interface)での「SCK」。

●タイマー1
16bitタイマー

この2点、Arduinoのブートローダーで使われていますので、競合に注意が必要です。
タイマー1に関しては、オーバーフローをチェックしているだけで割り込みなどは使っていませんし、ユーザープログラムはリセット起動しますので問題は発生しません。
ブートローダー内でRUNしているという認識だけで良いかと。

※この他にも、TXD、RXDの通信ポート、ウォッチドッグタイマーが使われます。

ここで問題なのがPB5ポート
Arduinoのサンプルプログラムで基板に乗ったLEDを点滅させるのがこのポートです。

PB5ポート、ブートローダー起動で出力ポートに設定されます。
そして、起動確認のためにパルスを出力してLEDを点滅します。
つまり、ユーザープログラムが走る前に「出力ポート」に設定されて、なおかつ「H/L」のパルスが出力されるのです。
その後、ユーザープログラムに切り替わるといったんリセットされ、入力ポートになってからsetupが実行されます。
この起動待ちのおよそ1.5秒、「PB5は出力ポートになる」ということを記憶にとどめておいてください。
  ※これ、ネットを探しても注意書きが見つかりません。

具体的にどんなかオシロで見てみました。
入力と出力、そして内蔵プルアップ状態を見るため、PB5に47kΩの抵抗を2本つなぎ、それぞれ+5VとGNDにつなぎます。
すると・・・
  ・出力ポートなら0Vあるいは5Vに張り付く。
  ・入力ポートなら0V・5Vの中間レベル、2.5Vに。
  ・入力で内蔵プルアップ有りならHレベルが
   少し持ち上がり3Vくらいに。
これでPB5の状態を確認できます。

こんなスケッチを書き込みます。

void setup() { // デジタル13番ピン=PB5を
 pinMode(13, INPUT_PULLUP); // pullup有りの入力に
}

void loop() { // なにもしない
}

結果、こんな波形が観測されます。

Ard21

リセットスイッチ押し下げで、
(1)リセット状態なので中間レベル。
(2)ブートローダー開始で出力ポートになり
  H/Lのパルスを出力(LED点滅)。
    ※単発でHになるArduino UNOもありました
(3)LレベルのままPCからの通信待ち。
(4)通信無しが続くとリセットしてユーザー
  プログラム起動。
  いったん入力モードに。 中間レベルの2.5V。
(5)ユーザープログラムのsetupが走り始めて内蔵
  プルアップ有効で3Vほどに。

この(2)と(3)区間が問題なのです。
(a)PB5を出力として使った場合
  ブートローダー起動中に不要なパルスがユーザー回路
  に加わる。
  これが問題なければ気にする必要はないが、何かの
  出力を直接制御していると具合が悪いことがある。
    (リレーを駆動しているとか)

(b)PB5を入力として使った場合
  入力信号とPB5が衝突する。
  ユーザープログラムが走り始めれば問題ないが、
  ブートローダー起動中、PB5は出力になるのでこの期間、
  信号衝突の危険性がある。

(c)PB5=入力で、GNDに引っ張るスイッチやオープンコレクタ信号
  この場合も、Hパルスが出る区間(2)があるのでだめ。
  PB5がHの時、スイッチがオンしていたりOC信号が
  L駆動だったら短絡発生の可能性。

・対策方法:
<1> PB5は入出力として使わない。
    逃げの一手で!

<2>出力として使いたい時は、不要パルスが加わっても
  大丈夫な出力で使う。
   例:LED表示用出力やLCDのデータ線、7seg LEDの駆動出力
    ピピちゃん温度計     LCDのD7ライン
    バレーボール用得点表示器  7seg LED駆動出力

<3> どうしても入力として使いたいときは、衝突による
  大電流を避けるため1kΩ程度の抵抗を直列に挿入し
  ておく。
  すると、5V-0V間の衝突でも5mA程度の電流に押さえられる。
  内蔵プルアップを有効にした場合でも、十分ローレベル
  に引っ張れる。
    (内蔵プルアップ抵抗は20~50kΩ)

具体的には、
・ナダ電子製「プリンタシールド」応用例 Arduino UNOでチャートレコーダを実現

ここで紹介している回路の「PB5」を見てください。
スイッチにつないでいるんで、起動時にオンしている(GNDに落ちる)とPB5が短絡してしまいます。
それを防ぐため1kΩの抵抗を直列に入れています。


※ブートローダーに関連するファイル

■pin_defs.h
/* Onboard LED is connected to pin PB5 in Arduino NG, Diecimila, and Duemilanove */
#define LED_DDR DDRB
#define LED_PORT PORTB
#define LED_PIN PINB
#define LED PINB5

■optiboot.c
#if LED_START_FLASHES > 0
// Set up Timer 1 for timeout counter
TCCR1B = _BV(CS12) | _BV(CS10); // div 1024  ←タイマー1起動
#endif

/* Set LED pin as output */
LED_DDR |= _BV(LED);  ←PB5を出力ポートに

#if LED_START_FLASHES > 0
void flash_led(uint8_t count) {  ←LED点滅処理
do {
TCNT1 = -(F_CPU/(1024*16));
TIFR1 = _BV(TOV1);
while(!(TIFR1 & _BV(TOV1)));
#ifdef __AVR_ATmega8__
LED_PORT ^= _BV(LED);
#else
LED_PIN |= _BV(LED);
#endif
watchdogReset();
} while (--count);
}
#endif


※ブートローダーからユーザープログラムの実行
ユーザープログラムの実行はウオッチドッグタイマーでリセットさせているようです。
MCUSRを見て、「通常リセットならブートローダーの処理へ」、「通常リセット以外はユーザープログラムを実行」という処理が入っています。
だもんで、ユーザープログラムはリセット状態からの実行になり、マイコン内のレジスタはリセット状態に初期化されます。
ブートローダーで使っているタイマー1は無視できますが、PB5は外に現れる現象ですんで無視できません。



| | コメント (2)

2019年4月12日 (金)

昔に実験したダイソー製LED懐中電灯の劣化調査

工作フォルダーを眺めていたら、「あれ、このデータ、紹介したかな?」っというのを見つけました。
ダイソーのLED懐中電灯に使われていたLEDの長期点灯実験です。

使われているLEDそのものは、
・2015年02月23日:LEDライト
・2015年03月02日:LEDライトのLED輝度

で、電流値と輝度変化を測定しています。
  (ダイソーのとシルクのを比較)

その後なんですが、ダイソーのLEDを長期点灯させて劣化具合を計っていました。
そのグラフを公開してなかったようなのです。
・2015年08月15日:LED劣化実験:ダイソーのUSB LEDライト

この実験の続き(ひどい結果だった)で、LED懐中電灯を調べていました。
  ※長期実験ですんで、一度の調べられない。。。

対象はこれ↓に使われている白色LED。

21_5

22_1

100mA流した時と50mA流した時の違いを見ました。
   (100mAのが情けなかったんで)
グラフの線、4本有るうちの2本は比較対象用のです。
1日に一回、2秒間だけ点灯して、測定系に異常が無いかを見ています。
ですんで、こちらの方は劣化はほぼ無いので一直線。
初期の輝度(A/D値)に違いがあるのは、LEDの傾きなどの影響で照度センサーへ光りの当たり具合が違っているからです。

さて、結果。  (クリックで拡大↓)

23

100mA流すと1ヶ月で・・・。 →赤線
50mAでも3ヶ月。 →青線
品質は良くありません。
4年前の実験ですんで、今のはどうでしょう。

・100mA-2、50mA-2というのが比較用のLED。
・50mAのほうが明るくなっているのはLEDとフォトタランジスタの距離の差。

●LEDの劣化まとめ


※ごめん!
よく見てみたら、このデータ、「まとめ」にまとめていなかっただけで、
・2015年12月21日:ダイソーのLEDライト 寿命テスト
で、すでに紹介していました。
お騒がせです。


※さらに!
このデータは出してなかったかと。
一度に4種のLEDを測定できるようにした回路。
これで、オプトサプライ社のLED2種とアバゴと東芝のを一つずつ、計4つのLEDを光らせました。
回路の外観。
40

照度センサーとLEDは塩ビ管のキャップに装着して、塩ビパイプの両端に装着。

41

42

試したLEDのスペック
・オプトサプライ OSW57LZ161D
  50mA(max)  3.1V(30mA) 10000cd(30mA) 60deg
・オプトサプライ OSW4XME3C1S
  800mA(max) 3.3V(350mA) 200lm(700mA) 120deg
・アバゴ  ASMT-MW22-NLN00
  700mA(max) 3.2V(350mA) 85lm(350mA) 110deg
・東芝  TL1F2-DW0,L
  550mA(max) 2.85v(350mA) 135lm(350mA) 120deg

OSW57LZ161Dだけ30mAで、あとの3つは350mAで定電流駆動。

その結果!
Cap025

4ヶ月を超えてもほぼ変化無し。
自動計測できるんで、もっとほったらかしにしておけば良かったかと・・・
  ※実験回路の解説とLED劣化のまとめをトラ技へ
   投稿しようとしてたんです。
      LED電球の明滅の話も含めて
   でも、記事はボツ。
   で、実験を終了したという次第。



・2019年2月17日:ダイソーの400円LED電球、1年目

ですんでねぇ。(むちゃ優秀)
  ↑現在も継続中です。


※参考  制御回路図

Ledtest2

Arduino-UNOのチップで動かしてます。


| | コメント (0)

2019年4月 1日 (月)

ArduinoのEEPROMアクセス、「EEMEM」について

ArduinoのEEPROMを使う上での注意点。
EEPROMに置いたデータを16進ダンプした時、「むむ?」な事態に遭遇しました。

Arduino、EEPROMに不揮発性データを置くことができます。
その時に使うのが「EEMEM」。
たとえば
word EEMEM data0;
word EEMEM data1;
    :
word EEMEM data9;
こんな具合に、順に記していけばdata0~9までEEPROM内のアドレスが得られます。

↓のように、EEPROMアドレスの増加を自分に勘定しなくてもよいわけです。
#define data0 0
#define data1 2   ←wordだから2進む
  :
#define data9 18
1バイトデータだけでなく多バイトデータでも、うまく配置してくれますので数えミスがありません。

※参考
  「eeprom.h」の中で
    #define EEMEM __attribute__((section(".eeprom")))
  「.eeprom」というAVRマイコンのメモリー領域に割り当て
  よっと指示されるわけです。


で、このEEMEMを使って配置したEEPROM内のデータ、これ、どういう
わけか並べた順にならないのです。
普通だと、列記した順に先頭からアドレスが割り振られるんじゃないか
と考えます。
ところが・・・実際は逆順に

このあたりを確かめてみました。
一つはEEMEMを使ってのアドレス割り付け。
もう一つがstructを使ってのアドレス確保です。
offsetof()を使って、構造体先頭からのアドレス増加分を得て、これを
EEPROMのアドレスとします。

~~~~~~~~~~~~~~~~~~~~~~~~~~
// EEMEMのテスト
// EEPROMの読み書きで必要なのはアドレス情報
// EEMEMで順に列記
word EEMEM ee0; // 0~3まで
word EEMEM ee1;
word EEMEM ee2;
word EEMEM ee3;
// structでデータを並べる (実体は無い)
struct eeprom{
 word ss0; // 0~3まで
 word ss1;
 word ss2;
 word ss3;
};
// eepromのwordデータをアクセスするマクロ(アドレスを返す)
#define EPADRSW(eep_word) (word *)offsetof(eeprom, eep_word)

// 書式付シリアル出力
void txprintf(const char *s, ...)
{
va_list vp;
char bff[80]; // バッファを確保
  va_start(vp,s);
  vsnprintf(bff,sizeof(bff),s,vp);
  Serial.print(bff);
  va_end(vp);
}

void setup() {
const char s[]="%04X:%4d\n"; // アドレスとデータ
word i;
 Serial.begin(9600); // シリアル通信
 for(i=0; i<4;i++){ // 4データ EEPROMに書き込み
  eeprom_write_word(2*i, 1001+i); // データは1001から
 }
 txprintf(s, &ee0,eeprom_read_word(&ee0)); // ee0~3のアドレス
 txprintf(s, &ee1,eeprom_read_word(&ee1));
 txprintf(s, &ee2,eeprom_read_word(&ee2));
 txprintf(s, &ee3,eeprom_read_word(&ee3));
 Serial.println(); // 改行
 txprintf(s, EPADRSW(ss0),eeprom_read_word(EPADRSW(ss0))); // ss0~3のアドレス
 txprintf(s, EPADRSW(ss1),eeprom_read_word(EPADRSW(ss1)));
 txprintf(s, EPADRSW(ss2),eeprom_read_word(EPADRSW(ss2)));
 txprintf(s, EPADRSW(ss3),eeprom_read_word(EPADRSW(ss3)));
}

void loop() {
}
~~~~~~~~~~~~~~~~~~~~~~~~~~

シリアルモニターした結果。
 ↓
0006:1004  ←EEMEMで記述した部分
0004:1003
0002:1002
0000:1001

0000:1001  ←struct
0002:1002
0004:1003
0006:1004

最初の4データ、「EEMEM」での書式では列記したのとは逆順にアドレス
が割り振られているのが見えています。
structで実体は無いデータを列記した場合は、書いた順序通りに現れます。

これ、データの名前でアクセスする分には何も問題ありません。
しかし、デバッグ時のときなど、EEPROMデータを16進ダンプして
データを見ようとした時、やはり記述した順に並んでいてほしいもの
です。


もう一つの注意点。
ArduinoのブートローダーはEEPROM内の初期化データを扱うことが
できません。
ですので、EEPROMデータの初期化は自前で行わなければなりません。


※AVRマイコンsectionの種類
・http://d.hatena.ne.jp/seinzumtode/20140719/1405789889

 

| | コメント (0)

2019年3月25日 (月)

割り込みで処理させるwordデータの扱い

Arduinoのアナログ基準電圧入力 に関連して、Arduinoのソースファイルをあれこれ見ていたら、
「そうそう。これこれ。 忘れたらアカンで~」的な内容に遭遇。
「wiring.c」内の、タイマーに関する処理に関して、起動経過時間をミリ秒単位で読み出すmillis()関数はこんな具合に処理されています。

volatile unsigned long timer0_millis = 0; ←Arduino起動からの経過時間(ミリ秒)
TIMER0のオーバーフロー割り込みで固定値が加算される4バイトの値です。
これ、
 ・割り込みで処理される
 ・2バイト以上の数値
というのを忘れちゃいけません。

timer0_millisの値は次の関数で得られます。
unsigned long millis() ←その値を返す関数

timer0_millisが割り込みで変化する値じゃなければ、
こんなふうに書けます。

unsigned long millis()
{
  return timer0_millis; ←4バイト値を読んでリターン
}

ところが、Arduinoで使っているATmega328は、RAM領域を1バイト単位でしか読み書きできません。
4バイトの値では4回のアクセスが実行されます。
そのアクセス中に割り込みが入ると、アクセス途中で値が変わっちゃって、正しい値が得られません。
そこで・・・割り込みで操作される2バイト以上の数値では、アクセス前にいったん割り込み禁止にして、途中で変更されるのを防ぎます。
これを忘れると、正しい値が読み出せません。

millis()ではこんなソースになっています。

unsigned long millis()
{
  unsigned long m;  ←4バイト値を入れるローカル変数
  uint8_t oldSREG = SREG; ←今が割り込み禁止か許可か?
  cli();       ←いったん割り込み禁止にして
  m = timer0_millis; ←4バイト値を読み出す
  SREG = oldSREG;  ←割り込み禁止、許可を元に戻す
  return m; ←ローカル変数に保存した値を持ってリターン
}

millis()実行前が割り込み禁止なら割り込み禁止状態で、
割り込み許可かなら許可状態でリターンするために、SREGをアクセスしています。

割り込み状態の復元が必要ないのであれば(いつも割り込み許可でリターン)、SREGの保存は不要です。
こんな具合に簡略化も可能です。

unsigned long millis()
{
  unsigned long m;  ←4バイト値を入れるローカル変数
  cli();       ←割り込み禁止
  m = timer0_millis; ←4バイト値を読み出す
  sei();       ←割り込み許可
  return m; ←ローカル変数に保存した値を持ってリターン
}

割り込み内で処理される2バイト以上の数値は、この手順を忘れてはなりません。

忘れるとどうなるか、Mstimer2を使って試してみます。

#include <MsTimer2.h> // タイマー割込
#include <wiring_private.h> // sbi,cbi命令用
// テストポイント
#define PB5_H (sbi(PORTB,PORTB5)) // PB5(D13) H/L
#define PB5_L (cbi(PORTB,PORTB5))
#define PB4_H (sbi(PORTB,PORTB4)) // PB4(D12) H/L
#define PB4_L (cbi(PORTB,PORTB4))
#define PB3_H (sbi(PORTB,PORTB3)) // PB3(D11) H/L
#define PB3_L (cbi(PORTB,PORTB3))
// タイマー値
volatile word tm_1ms; // 1ms upカウントタイマー
// 1msタイマー処理
void timer1ms(void)
{
  PB4_H; // テストパルス
  tm_1ms++; // 1msタイマー +1
  PB4_L; // 1,2,3…と+1で変化
}

// セットアップ
void setup() {
  pinMode(13, OUTPUT); // PB5 基板上黄LED
  pinMode(12, OUTPUT); // PB4 タイミング確認
  pinMode(11, OUTPUT); // PB3
  MsTimer2::set(1, timer1ms); // 1ms割り込み
  MsTimer2::start(); // 割り込み処理開始
  Serial.begin(9600); // シリアル出力
}

// ループ
void loop() {
word t1, x;
  cli(); // 割込禁止
  t1 = tm_1ms; // 1ms値をコピー
  sei(); // 割込再開
  while(1){ // 1ms値の変化を見る
    PB5_H; // テストパルス
    x = (tm_1ms - t1); // 経過時間 ★割込処理される数値を読んでいる
    if(x){ // 0以外の時:変化あり
     if(x != 1){ // 差が1msじゃないとき
       PB3_H;
       Serial.print("t1="); // 値を出力
       Serial.print(t1, HEX);
       Serial.print(" x="); // 差
       Serial.println(x);
       PB3_L;
      }
     cli(); // 割込禁止して
     t1 = tm_1ms; // 1ms値をコピー
     sei(); // 割込再開
    }
    PB5_L;
  }
}

シリアルモニターにはこんな結果が出てきます。
 t1=4FF x=256
 t1=BFF x=256
 t1=DFF x=256
 t1=EFF x=256
 t1=10FF x=256
 t1=13FF x=256
 t1=17FF x=256
 t1=19FF x=256
 t1=1DFF x=256
 t1=1FFF x=256
 t1=26FF x=256
 t1=2AFF x=256
 t1=2FFF x=256
 t1=36FF x=256
 t1=3DFF x=256
 t1=42FF x=256
 t1=4AFF x=256
 t1=4FFF x=256
 t1=55FF x=256
 t1=5DFF x=256
 t1=62FF x=256
 t1=65FF x=256
 t1=6CFF x=256
 t1=6DFF x=256
 t1=72FF x=256
 t1=78FF x=256
 t1=7EFF x=256
 t1=86FF x=256
 t1=8AFF x=256
 t1=8FFF x=256
 t1=96FF x=256
 t1=9DFF x=256
 t1=A2FF x=256
 t1=A6FF x=256
 t1=AEFF x=256
 t1=B3FF x=256
 t1=B7FF x=256
 t1=BFFF x=256
 t1=C4FF x=256
 t1=C8FF x=256
 t1=CBFF x=256
 t1=D1FF x=256
 t1=D7FF x=256
 t1=D8FF x=256
 t1=DEFF x=256
 t1=DFFF x=256
 t1=E5FF x=256
 t1=E8FF x=256
 t1=EFFF x=256
 t1=F4FF x=256
 t1=F6FF x=256
 t1=FDFF x=256

16bit=65536回(1分ちょい)でこれだけのミスが発生。
「xxFF」→「yy00」の桁上がりの読み出し時にミスが起こります。
だもんで、エラー発生するかも!のタイミングは256回。
そのうちの1/5ほどを拾ってしまってミスした数値が出ています。
一番先頭のだと・・・
04FF→0500のカウントの時、先に読んだLSBがカウントマップ前で「FF」。 
その間に割り込みが入りカウント値が+1されて「0500」に。
その直後にMSBを読んでカウントアップ後の「05」に。
前回値との差「05FF - 04FF」で「0x0100 = 256」に。
こんな具合です。

オシロ波形では、こんなふうに見えます。
波形の上から、
PB4 1ms割り込み処理タイミング
PB5 毎回ループで出すパルス
PB3 差が1じゃ無い時に出すパルス
   文字変換時間
TXD シリアル出力
  先頭文字「t=0x74」が見えている

Image000

 

Image001

ちなみに、「Z80」の割り込みではこんなバグを体験。
プロセッサ誌1989年3月号に投稿。
『バグタイザー試用レポート Z80のIFF2に関するバグレポート』

  Z80のIFF2に関するトラブル体験談

割り込み禁止か許可なのか、割り込み状態を見るための命令が、命令の実行と割り込みのタイミングがうまく合うと、結果をミスるというチップ内部のバグです。
その解決方法も示しています。
20世紀にあった与太話としてご覧いただければと。

 

| | コメント (0)

2019年3月22日 (金)

Arduinoのアナログ基準電圧入力

Arduinoの
Arduino IDE アップデートしたらMsTimer2が使えなくなった・・・をあれこれ調べていて・・・
こんなページを発見。
  ※MsTimer2とは関係なくって、アナログ入力についてです。
この中の、
参照電圧について (要注意! きちんと守らないと故障の恐れ!)
について、ちょいと私の意見を・・・

Arduinoのアナログ・リファレンス電圧設定についてを追いかけてみます。
※状況
・調べたのはArduino-UNO。(だけ)
  ソースファイルを見たり、現物の信号をオシロで見たり・・・
・ブートローダ起動での状態:
  AREFは入力状態。
  つまり、外部基準電圧を受け入れる状態。
    analogReference(EXTERNAL)と同じ状態。
    INTERNALやDEFAULTではない。
  ブートローダのソースを見ると、A/Dのマルチプレクサ
  はリセット状態のままでリファレンス電圧入力の設定も
  外部基準電圧の状態。
・ユーザープログラムの起動:
  A/Dのクロック設定とADEN(A/D変換許可)を初期化し
  ている。
  しかし、マルチプレクサ切り替えと基準電圧の設定は
  手を付けていない。
・analogReference(INTERNAL)、analogReference(DEFAULT)
 の設定:
  内部基準電圧あるいはAVCCに切り替えても、A/D入力:
  「analogRead() 」を実行するまでは「EXTERNAL」のまま。
  analogReference()の実行だけでは、基準電圧の切り替えは
  行われない。
  analogRead()実行ではじめてanalogReference()の設定が
  有効になる。
・だもんで:
  外部から基準電圧を供給しても問題なし。
  衝突は発生しない。
この中↑の、記述で・・・
■Arduino Reference:analogReference()
Notes and Warnings
 :
Alternatively, you can connect the external reference voltage
to the AREF pin through a 5K resistor, allowing you to switch
between external and internal reference voltages.
Note that the resistor will alter the voltage that gets used
as the reference because there is an internal 32K resistor on
the AREF pin.
The two act as a voltage divider, so, for example, 2.5V
applied through the resistor will yield
  2.5 * 32 / (32 + 5) = ~2.2V at the AREF pin.
↑の概略・・・
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
外部基準電圧とAREFピンの間に5kΩの抵抗を入れておけば、
内外の切り替えの間の衝突が避けられる。
ところが、AREF入力内部の入力抵抗は32kΩ。
だもんで、5kΩの抵抗を入れるとドロップが生じて、2.5Vの
基準電圧だと実質はざっと2.2Vになる。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
これ、入力抵抗を32kΩと決め打ちして数値を出していますが、
そんなうまいことはいきません。
というか、Arduino(UNOでしか確かめてませんが)では、
ブートローダーの処理とユーザープログラムの初期化処理を
見る限り、AREFと外部基準電圧出力が衝突する心配はありま
せん。
抵抗を入れると、せっかくの外部基準電圧の精度が落ちてしま
います。
直列抵抗無しでOKというのが、私の結論です。
そして、analogReference()を設定した直後に、ダミーで
analogRead()を1回実行しておくことで、基準電圧の
切り替えがそのタイミングで行われて、以降のA/D変換が
安定するかと考えます。
※補足
アナログ入力を処理しているのが「wiring_analog.c」。
このファイルを見ると・・・
uint8_t analog_reference = DEFAULT;  ←初期値はDEFAULTだけど
                     変数に値を入れているだけで
                     A/Dのハードは操作していない。
void analogReference(uint8_t mode)   ←基準電圧区分を設定する関数
{
// can't actually set the register here because the default setting
// will connect AVCC and the AREF pin, which would cause a short if
// there's something connected to AREF.
  analog_reference = mode;       ←ここでも変数を設定しているだけ。
}
設定された「analog_referenc」をAVRマイコンのA/D変換レジスタ(ADMUX)
に書き込むのは「int analogRead(uint8_t pin)」 関数。
マルチプレクサの入力ch設定とともにADMUXに書き込まれる。
これが実行されるまでAREFピンはリセット状態のまま。
※さらに
実際にA/D変換しているところ・・・
#if defined(__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  ADMUX = (analog_reference << 4) | (pin & 0x07);  ←AREFピンとマルチプレクサ確定
#else
  ADMUX = (analog_reference << 6) | (pin & 0x07);
#endif
#endif
// without a delay, we seem to read from the wrong channel  ←★注目!
// delay(1);
#if defined(ADCSRA) && defined(ADCL)
// start the conversion
  sbi(ADCSRA, ADSC);      ←A/D開始
// ADSC is cleared when the conversion finishes
  while (bit_is_set(ADCSRA, ADSC));  ←変換終了待ち
  :
  low = ADCL;       ←10bitデータ読みだし
  high = ADCH;
  :
こんな流れ。
で、★注目のところに注目。
// delay(1); をコメントアウトしてます。
AREFとマルチプレクサを切り替えてすぐにA/D変換しちゃってる。
ほんとは、切り替え安定待ち時間が欲しい所です。

※記事へのコメント書き込みアドレスをメモ
※追記
古いバージョンのArduino-ソースでどうなっているかを追跡してみました。
ここ → Arduino - OldSoftwareReleases から、まず「1.0」をダウンロード。
関係するファイル。
まずは、ブートローダーですがこれはA/D関連レジスタを操作していません。
で、A/D変換に関係するのは以下の二つのファイル。
・wiring.c
  ADCのクロックを設定するADCSRAを操作しているがADMUXは触らず。
  AREF入力はリセット時のままで外部入力。
・wiring_analog.c
   $Id: wiring.c 248 2007-02-03 15:36:30Z mellis $ という日付。
  ↑で説明したのと同じ処理になっています。
  つまり、外部基準入力時の注意を守る限り「衝突」は発生しません。
そして、もっと古い「0013」を見ても、基本の処理は1.0、つまり現行の
ソフトと同じです。

 

★このトラブルの発端(原因の推測)、
  setup内でanalogReference(EXTERNAL)を設定しないまま、
  analogRead()を実行したのじゃないかと・・・
analog_reference = DEFAULT;が初期値ですんで、
初回のanalogRead()の実行で、A/Dのch指定とともにADMUXの書き込みが
行われた瞬間にAREFピン(AVCCになる)と外部基準電圧ICの出力との短絡が
発生します。
これが「短絡するぞ」の原因じゃないかと考えます。

 

※さらに追記

上の方に記した
■Arduino Reference:analogReference()
  Notes and Warnings
「:」で省略した部分に大事なことが書かれてます。
     ※5kΩや32kΩに目が奪われちゃって・・・

~~~~~~~~~~~~~~~~~~~~~~~~~~
After changing the analog reference, the first
few readings from analogRead() may not be accurate.
~~~~~~~~~~~~~~~~~~~~~~~~~~

「AREFを変えた後の何回かのanalogRead()は不正確かも。」

これ、外部に基準電圧をつなぐとなれば、
analogReference(EXTERNAL)の実行はsetup内で1回だけ。
何度も変えるものじゃありません。

そして、

~~~~~~~~~~~~~~~~~~~~~~~~~~
Don’t use anything less than 0V or more than 5V
for external reference voltage on the AREF pin!
~~~~~~~~~~~~~~~~~~~~~~~~~~

「AREFピンには0V未満あるいは5V越えの電圧を加えるな。」
  ※5VというよりAVCC電圧か。Arduinoなら5Vだけど。

~~~~~~~~~~~~~~~~~~~~~~~~~~
If you’re using an external reference on the
AREF pin, you must set the analog reference to
EXTERNAL before calling analogRead().
~~~~~~~~~~~~~~~~~~~~~~~~~~

「AREFピンに外部基準電圧をつなぐ場合は、analogRead()
 実行前にEXTERNALに設定せよ。」
これが重要なわけです。

~~~~~~~~~~~~~~~~~~~~~~~~~~
Otherwise, you will short together the active
reference voltage (internally generated)and the
AREF pin, possibly damaging the microcontroller on
your Arduino board.
~~~~~~~~~~~~~~~~~~~~~~~~~~

「さもないと、内部生成された基準電圧とAREFピンが
 ショートでArduinoに乗ったマイコンがつぶれるかも。」

そこから短絡防止のための姑息な手段の「5kΩ、32kΩ」の話がくるからややこしい。

 

| | コメント (5)

Arduino IDE アップデートしたらMsTimer2が使えなくなった・・・

   ※私が実行した対策方法はあとで示します。
「Arduino」の開発環境、「アップデートがあるよ」のメッセージが出たんでアップデートしたら、これまで使えていた「MsTimer2」(タイマー割り込み処理)が動かなくなってしまって、「なんで?」状態に。
動かなくなったのは「MEGA-2560」。
「Arduino-UNO」では大丈夫。
調べてみると「MsTimer2.cpp」内のチップ名称指定からMEGA2560が抜けていると。
  ※検索:arduino mega2560 mstimer2
対策として「MsTimer2.cppにパッチを当てよ」とのこと。

アップデート前はちゃんと動いていたんです。
アップデートでMsTimer2.cppが古いのに書き換わったんだろうなの想像。
こんなのイヤ! 

~~~~~~~~~~~~~~~~~~~~~~~~~~
★私の対策方法
MsTimer2を使わず、タイマー2モジュールを直接制御して、
割り込みルーチンをベクトル設定から。

●setup内
 :
// タイマー2 割り込み有効に
TCCR2A = 0b00000010;  // WGM21,20 CTCモード
TCCR2B = 0b00000100;  //  プリスケーラ1/64
OCR2A = 250-1;            // 16MHz / 64 / 250 = 1kHz
TIMSK2 = 0b00000010;  //  OCIE2A コンペアマッチA割り込み有効
 :
●割り込み処理
/***** タイマー値 *****/
volatile byte tm_1ms; // 1msダウンカウントタイマー max 255ms
/***** タイマー2 1mS割り込み *****/
// コンペアマッチA割り込み
ISR(TIMER2_COMPA_vect)
{
  if(tm_1ms) tm_1ms--;  // 1ms値ダウンカウント
}

 

~~~~~~~~~~~~~~~~~~~~~~~~~~
MsTimer2の最小単位が1ms。
チャートレコーダーでは0.5msが欲しかったんで、割り込みを直接記述。

ただし、
AVRマイコンのCコンパイラ 具体的に ※volatileを忘れずに!
のように注意(word以上のデータをアクセスする時の割り込み禁止処理)が必要。

Arduino IDE、そういやこんな変更もあった・・・

 

 

| | コメント (0)