7-0 BASCOMやAVRの「くせ」(マニュアルやネットに載っていない問題点や新規事項など)

2013年5月:(6)の項を修正。BASCOMの問題ではなく、AVRの問題。
2013年11月:(12)項、ソフトウエアI2Cのバグを追加。
2016年2月:(14)項のSPIコマンドとインタラプトについてを追加。
2017年:(15)、(16)項を追加。

 BASCOM-AVRを使う際に、ハードが悪いのか、BASCOMの問題点か、悩むことが幾つかあったので、
以下に記しておく。この「くせ」はマニュアルやネットには、ほとんど載っていない。
なお、MCS-Electronics(BASCOMの製造元)のUser-forumには幾つかヒントになることが載っていた。

以下、この章の内容

(1)UARTに割り当てられているポートを普通のI/Oポートとして使う際の注意。(BASCOM)
(2)Timer定義の問題 (BASCOM)
(3)省電力化に対する対応 (BASCOM)
(3-2)低い周波数でのタイマー割り込みにおける多重インタラプト(AVR)
(4)複数行に渡る命令文の記述 (BASCOM)
(5)XMega CPUに対する対応 (BASCOM)
(6)Config SPI 命令におけるAVR側のクセ(BASCOMのバグと見間違うAVR側のクセ)
(7)赤外リモコンを使うRC5命令におけるMega1284のバグ
(8)Mega128等のXRAMに関するバグ(BASCOM ver 2.xx、2.0.7.6まで)
(9)Mega1284に対するPortAのデジタル出力指定のバグ
(10)ver.2.0.7.5で "On Interrupt"命令に追加されたAllsave オプション
(11)TinyシリーズでConfig SPI = Hardをコンパイルするとエラーとなる
(12)ソフトウエア I2C におけるバグ
(13)ロータリーエンコーダーの回転が正しく認識されない場合
(14)複数のSPIコマンドを実行している際のインタラプトについて
(15)JTAGのdisabelについて
(16)' 文字でコメント化する際の注意


(1)UARTに割り当てられているポートを普通のI/Oポートとして使う際の注意。(BASCOM)
 普通、市販のAVRボードを買うとUARTによるシリアル通信を行うRXDやTXDは既に使われており、
それらはユーザーのI/Oポートとして使うことがないので、問題にならない。
(例えば、本ホームページで多用しているデジットのBoot-Loaderボード(ABL168, ABL128))
ところが、AVRのチップを買って、自分でボードを作り、シリアルイーンターフェース(XBeeなど)を
使わずに、RXDやTXDピンを普通のI/Oとして使用する際には、以下のことに注意する必要がある。
例えば、Mega168のPortd.0, Portd.1はRXD, TXDにもなり得るので、それらをI/Oポートとして
使う際などの場合。

1. ポート全体(8-bit)を出力か入力として定義する場合は、問題は起きていなかった。
例:Mega168で、 Config Portd = output

2. 同一ポート内で入出力を個別に定義する場合に、問題が発生する。
例:Mega168で、 Config Portd.0 = output, Config Portd.1 = input などとする場合。
 上記のようにした場合、Mega168ではBASCOMはPortd.0はRXD, Portd.1はTXDに割り振ってしまう。
そこで、どのような症状が出るかというと、(以下、Mega168の場合)
「1」Portd.0は出力を出すが、ドライブ電流がとれず、ちょとした負荷でも0にドライブできなくなる。
また、無負荷ではポート出力が0になる時間が極端に遅くなる。(数十us以上かかる)
その原因は、どうやら、BSACOMがRXDピンのプルアップ抵抗をON/OFFしているだけで、
1/0を出力しているらしい。
「2」Portd.1はRXD出力として、シリアル出力が出ない場合の1状態のままで、
まったくゼロResetが効かなくなる。
もし、ここをInputとして定義して使っていれば、ゼロ入力の場合、
おそらく、バッファー出力をショートして過大な電流が流れている。
(強引にGNDに落とせば、ゼロのInput読み込みはこの場合でもできるがICは劣化するかも?
また、頻繁に行うと、AVRのFlush-memory(Program ROM)が消えることがあった。)

 この不具合を解決するには以下のコマンドをBASCOMのプログラム中に置き、以下の対応をする。
[1]  UARTを使うような命令をプログラム中には1個も使用しない。
例えば、BAUD, $BAUD,ECHO, PRINT, INKEYなど。
これらの命令はBASCOMに「UARTを使うんだ!」というコンパイラフラグを与えてしまう。
なお、MCS-ElectronicのBASCOM FORUMではDisable Interruptsも問題になると書いてあった。
その場合は、その命令文の後に、以下の[2]で述べるコマンドを記述しておくこと。
[2] RXDとTXDを含むポート全体を入出力指定しない場合は、
BASCOMはRXD, TXDに相当するピンを 自動的にUART使用に割り振ってしまうので、
以下のようにそれを強制的に解除してやる。
個別のポートの入出力方向をConfig文で指定した後などに、以下の命令を置く。

 Reset UCR.3
 Reset UCR.4

この命令はUCRレジスターの制御ビットを書き換えて、
AVRにRXD, TXDの割り付けをUARTではなく、通常のI/Oポート制御レジスターの制御下に
渡すという指定である。これにより、ちゃんとSet, Reset, ToggleなどのI/O命令の支配下に置かれる。


(2)Timer定義の問題 (BASCOM)

 BASCOMではTimer定義のConfig TIMERnを複数使った場合、最後のTimer定義のみが有効になる。すなわち、Prescaleの値を1とか1024とかに切り替えたい場合、プログラム中の必要な場所で異なったprescaleの値を定義したConfig文を使えば良いように単純には考えられるが、それだと、プログラム中の一番最後の方に置かれたConfig TIMER文のみが有効となり、他の設定は無視されてしまう。その理由として、BASCOMではStart TIMERnを実行した時のみTimerの初期化が最後のConfig定義を用いて行われることが挙げられる。
 上記を解決するには、各Config文の最後に「, Configuration = label」(labelは任意の名前)を指定し、タイマーをスタートする時に、Start TIMERn, label として、どのConfig TIMERnの設定を使うかを指定してやる。(BASCOMのマニュアルにそれらしい解説があるが、説明不足なのでわかりにくい。)これにより、異なるPrescale値などを同じタイマーでケースバイケースで使用できるようになる。

 もう一つの問題として、PWMモードの形態を詳細指定するWGM13ビットがStart TIMERn 時に設定されないことである。従って、Start TIMERn を行った後、すぐにWGM13ビットをSet命令で再度セットする必要がある。(ゼロにしておく分には指定しなくても良い。)例えば、Set TCCR1B.WGM13 とする。WGM13ビットをセットしない場合は、例えば、PWMの TOP値が0x3FFなどでスタートし、TOP=OCRAnなどにはなっていない。(最近のAVRではTIMER3のあるものもあり、その場合は上記のWGM13をWGM33と読み替えて、TCCR3Bなどに設定する。)TIMER0などについては実験していないが、同様のケースもあるかもしれない。

 上記のプログラム例は回路ページの6-7-2章の1にある。

 

(3)省電力化に対する対応 (BASCOM)

 多くのAVRはPowerSaveモードなどの省電力機能を持っているが、PowerSaveモードなどにおいては、そのモードに入る前にADCやTimerのクロック供給を止めて省電力化を促進する。その場合、STOP ADC等のコマンドを置く。ところが、BASCOMではSTOP UARTという命令は無い。(UARTも電力を消費する)
したがって、AVRのレジスターを直接BASCOMから指定して、UARTを止める必要がある。
(UARTを持たないAVRではこの必要は無い。)
ところが、UARTを持っているAVR (Tiny2313, Mega168など)では、(1)に述べたように
BASCOMが自動的にUARTを生かしてしまっている。なお、TWIもSTOP命令は無いようなので、
これも省電力制御のレジスターをダイレクトに指定して止めるしかない。
Mega168の場合は、そのレジスター名はPRR(Power Reduction Register)。

 AVRのクロック周波数を下げるほどActiveモード時には低消費電力になる。その場合、BASCOMはUARTに9600-baudを仮定してコンパイルを行うので、エラーとなることがある。
例えば、32768Hzというリアルタイム用のクリスタルを使う場合、エラーとなる。
対応法はプログラム中に$Baud = 300 などとして、仮想的にボーレートを下げてやる。
ここで、$Baudを使ったので、(1)に述べたようにUARTは活性化しているので、
必要に応じて(1)の対応や、(2)の対応を行うこと。なお、あまりに周波数を低くするとSPI書き込み機で書けなくなる。(Fuse-bitのみは書き込める。)SPIは5kHzまでで、その4倍は必要らしいので、20kHzまでとなる。このようにクロック周波数を下げてしまうと、AVRの処理速度が低下するので、複雑な処理だと間に合わないことも起こりうる。

 これに対し、MegaシリーズのAVRは、TOSC1とTOSC2というピンを持っており、そこに32768Hzのクリスタルをつなぎ、Timer2のクロック入力に非同期(Async)を設定すると、32768Hzリアルタイムクロックの発振と同時に、AVR内部ではCPUや他のI/Oのクロックとして内部RC発振器の8MHz, 1MHz, 128kHz(Fuse指定)を使えるようになっている。この場合、AVRのスリープにはPowersaveを使い、Timer2のみを32768Hzクロックで動作させて1秒ごとにインタラプトをかけてスリープから起床させるといった低消費電力化が可能となる。注:32768Hzのクリスタルの両端にはコンデンサーは不要。AVRの発振指定のヒューズビットをLow-Power Crystalに指定した場合、AVR内部でAVRのドキュメントに記載されている値のCが付加される(例:6pFと16pF)。したがって、回路では32768Hz水晶の両端のCは必要ない。

 これらの対応をしたプログラムの例を添付する。液晶への電流供給を除くと、以下のシステムでMega168Aのスリープ中の消費電流は7uAとなった(電源電圧=3.3V)。添付したプログラムではシステムとして外部の32768Hz発振器を仮定している。この場合、Mega168はTimer2への非同期クロック入力としてTOSC1ピンを使う。ここに低消費電流型の32168Hz水晶発振器(下記参照)を接続すると、Mega168をPowersaveモードで使用することにより、10uA以下の低消費電流化が可能となる。または、32768Hzの水晶発振子をTOSC1とTOSC2の間につないでMega168に発振させてやる手もある。(その場合は添付したプログラム中のASSR=&H60を削除されたい。)水晶発振子をAVRで発振させたケースでは、実験したところ消費電流は17uAとなった。なお、Powersave中はCPUクロックやI/Oクロックが止まるので、8MHzのCPU クロックでも128kHzのCPUクロックでも、まったく消費電流は変わらない。
 Megaシリーズでは、PowersaveによるSleepの場合、Timer2のインタラプトでAVRは起床できる。すなわち、添付したプログラムにあるようにTimer2のオーバーフロー・インタラプト(1秒ごと)により、Mega168は起床し、必要な処理を行う。その際は10uAオーダーよりも多くの電流が流れるが、瞬間的であり、トータルとして低消費電流を保てる。32168Hz発振器はRS-componentsにあるOV-7604-C7を使うと、0.5uA以下の消費電流である。(上記の7uAはこの条件下での値。)なお、前記したようにMega168のTOSC1とTOSC2端子の間に32768Hz水晶発振子を接続してAVRで発振させてやっても、低消費電流化は可能であり、下記の液晶モジュールの消費電流を考えた場合はこれでもよい。注:(Timer2の割り込みの利用に関しては、次節=3-2節にある注意も参照されたい。)

 ここで、液晶表示器も低消費電流化しないと総合電流は下がらない。特に、コントラスト調整ボリュームが必要な液晶を使用すると、そのボリューム(例:10k ohm)のみで数百uAの電流を定常的に消費してしまうので注意が必要である。また、通常の液晶では仕様書を見ると数mAの保持電流を必要とするとある。これに対し、DOGM162Eというのが通販されていて(RS-コンポーネンツ)、これだと調整用ボリュームは不要で250uA以下の消費電流である。これを使用すれば、AVRの1秒おきの起床で、単3アルカリ電池x2 の3V電源で1年程度は持つ勘定になり、AVRで実用的な時計が作れることになる。(普通に時計を作ると、AC電源でないと実用的なものはできなくて、電池だと1ヶ月も保たなかったりする。)注:添付したプログラムはDOGM162用ではなく、一般的な液晶用である。DOGM162では別の初期化シーケンスを追加する必要性がある(試作例は回路のページの6-14章に示してある)。
 低消費電流の液晶には、その他にストロベリー・リナックスのI2Cインターフェースの液晶表示器があるが、AVRをスリープさせた後のI2C回線の立ち上がりに不安定性があり(Mega168の問題か?、液晶表示関係のページで2-1-2章参照)、TWIインターフェースをdisableやパワーダウンさせないように設定する必要があるようである。また、TWI回線がスリープから復帰するまでのディレイを入れる必要があるようで、したがって、その間(Activeモードになる)は電流が増えるし、スリープ設定もIdleモードを使うので電流消費は増す。この場合は、単3アルカリ電池x2で3ヶ月程度となる。(なお、単1なら1年は行けそう。)
 市販の液晶表示時計の電池が長持ちするのは、液晶の表示が決められた数字やAM/PMの決められた文字のみでよいため、液晶の表示セル数(ドット数)が少ないためである。これに対し、一般的な文字表示液晶では1文字あたりに8x8ドット以上のセルが必要であり、その保持電流も大きくなってしまう。もし、時計専用の液晶を入手できれば、上記のシステムで、より長時間の電池寿命を確保できると思われる。

 以上の考察を行って、実際に設計した実用例はこちらを参照されたい。そこでは、 上記に述べていないテクニックも使われており、真に低消費電力化が図られている。また、「グラフィック液晶の制御法と回路」の7章も参考になる(トップページからリンクが有る)。そこではグラフィック液晶を使いながらもシステム全体の消費電流を10uA程度に抑えており、小型Li-Polymer電池の使用下でも1年程度の無充電動作を実現している。

 

(3-2)低い周波数でのタイマー割り込みにおける多重インタラプト(AVR)

 (3)に述べたような、Timer2で低い周波数を扱うタイマー割り込み処理ルーチンで気をつける点を以下に記す。
Timer2で32768Hzのリアルタイムクロック用水晶振動子を指定して,以下のようにしたプログラムは正しく動作しない。

$regfile = "m168def.dat"
$crystal = 8000000  ' 8MHz Internal RC oscillator clock

...

Config Timer2 = Timer , Async = On , Prescale = 128 ' 32768HzXtal = Timer2 overflows every 1sec.
On Timer2 T2_intsub
Enable Timer2
Start Timer2
Enable Interrupts

......

T2_intsub:
Incr I
If I = 5 Then  ' 5-sec. duration
 I = 0
 Toggle Led
End If
Return

このプログラムでは5秒おきにLEDが点滅を繰り返すはずであるが、実行するとLEDの状態は変わらない。また、If文の外にToggle LEDを移しても、ダメ。非常に不思議な現象であったが、LEDの端子にオシロをつなぐと、約50usほどの間のみ、LEDはToggleしている。

 その理由:32768Hzの基本クロックでTimer2を使うと、オーバーフロー状態から抜けたと認識できるまでに1/32768 = 30.5usほどかかる。(注:prescalerの値が128でもこのようになる。32768Hzでサンプリングしているようである。)上記のインタラプト処理ルーチンは簡単なので、30us経つ前にメインルーチンにリターンしている。その場合、オーバーフロー状態が変化していないままなので、再度インタラプトがかかり、T2_intsubルーチンを2度実行することになる。したがって、一旦ToggleしたLEDを再度Toggleしてしまい、まったく変化していないないように見せる。この場合、If I = 5という奇数回の判定も不具合を招いている。If I > 5なら動くが、5秒ではなく、約2.5秒おきにToggleしてしまう。

 以上のように、遅いクロックをソースにしたTimerの割り込みでは多重インタラプトがかかることがあるので注意されたい。上記のインタラプト処理ルーチンでは、その内部に例えば、waitus 31と入れると正常に動作するようになる。処理時間に余裕があるのなら、念のため,waitus 50 程度を入れておくのも良い。(もし、処理ルーチンが条件によって30us以下で終わる場合も、これで対応できる。すなわち、もし、秒の更新が条件によって30usたたずに早く終わる場合に対して、2秒加算されてしまうことを防げる。)

注:この現象は、インタラプト処理ルーチン中でSet TOV2としてやって、インタラプトフラグをクリアしてやればよいように思われるが、そのようにやっても、30us以前に処理ルーチンから戻ると、再度、インタラプトがかかってしまう。どうやら、戻ったとたんに再度TOV2が立ってしまうようである。やはり、Waitus を入れて、Returnするのを遅らせるしかない。

 

 

(4)複数行に渡る命令文の記述 (BASCOM)

 Ver. 2.0.6以降、_ 文字を行末に付けると、命令行の継続を行えるようになった。
例えば、Config 文などの長々と1行で書かなければならなかった命令文も
_ 文字を行末に付けて複数の行に書けるようになった。
なお、_は , 等の区切り文字の後に入れなければならない。

 

(5)XMega CPUに対する対応 (BASCOM)

 現在は、まだ未対応な部分があり、以下のバグが最悪である。
 XMega-AにはMega128等に見られるような外部RAMインターフェースが備わっており、
さらに4-bit幅のSDRAM接続も可能となっている
(EBI: External Bus Interfaceと呼ばれるインターフェース自身はMega128とは大きく異なり、
その拡張型となっている)。
 ここで注意点としては、BASCOM ver.2.0.7.3ではバグがあり、
マニュアルどおりにやってもSRAMを正常にアクセスできない点である。
(非常にまずいバグで、そのマニュアルから予測される設定でプログラムを行うと、
CPUがデッドロックする。
BASCOMではメモリーが定義されるとそれをゼロに初期クリアするようで、
もし、メモリーがアクセスできないとそこでデッドロックに陥ると思われる。)
したがって、XMegaの制御レジスターにBASCOMから直接、制御ビットを設定するはめになる。
対応法は別のページを参照。

 

(6)Config SPI 命令におけるAVR側のクセ(BASCOMのバグと見間違うAVR側のクセ)

(旧題:Mega1284に対するConfig SPI 命令におけるバグ(BASCOM ver 2.xx))

 旧題ではBASCOMのConfig SPI 命令におけるバグとなっていたが、詳細に検討したところ、そうではない事が分かった。

 BASCOMのバグかと思ったのは、以下の現象が出たため。


現象1. BASCOMのバージョンを2.0以上にバージョンアップしたところ、ver. 1.9.xxでは現れなかった以下のようなバグが有り、原因の究明に非常に苦労した。以前、Mega1284ボードを作成した際にBASCOM ver.1.9.xxでコンパイルして正常に動作していたSPI インターフェースを利用したプログラムをちょっと変更して、ver.2.0.xxでコンパイルしたところ、SPI ラインから何も出力されないという現象が現れた。すなわち、SPIOUT命令を行っても、SCK信号線からは何もパルスが出ないし、MOSIラインも何もデーターが出なくなった。
 SPIインターフェースの定義は以下のようである。
Config SPI = Hard, INTERRUPT=OFF, DATA_ORDER = MSB, MASTER = YES, POLARITY = LOW , PHASE = 0, CLOCKRATE = 4, NOSS=1
プログラムは正常にコンパイルされ、Mega1284ボードに書き込まれたが、SPI の動作がおかしいので、オシロで調べた結果、以下の現象である事が判明した。すなわち、SPIOUT命令を発行したにもかかわらず、SCKはローレベルのままであり、SCKのパルスが出力されていない。同時に、MOSIラインにも信号は出ていない。
 ハードを調べても正常であり、また、プログラムを変更してConfig SPI とSPIOUT命令のみにしても現象は変わらない。そこで、MCS Electronicsのユーザーフォーラムを調べてみたが、解決法は無かった。唯一、Mega1280でトラブルに会った例が掲っていて、それによると、Mega1280等のCPUではMega168などとは異なり、SPI control registerの名前が異なるので、BASCOMのCPU定義ファイル(M1284pdef.datなど)が訂正されていないため、バグが出るとの事。見つけた記事によると、Mega1280ではConfig SPI 時にNoss = 1ではなく、Noss = 0にしたら、なぜか動いたとの事で、筆者もConfig SPI定義でNoss = 0としてみたところ、動作するようになった。なお、Noss = 0にしても、AVRの/SS ピンはtri-stateのままであり、Noss = 1として期待した動作と同じになっている。
 一旦、Noss = 0としてコンパルしてMega1284に書き込み、正常動作を確かめた後、今度はNoss = 1として再度コンパイル、書き込みをして、リセットスイッチを押すと、最初は正常動作する。ところが、何度もリセットを繰り返すと、異常動作となる。どうやら、レジスターの初期パターンかBASCOMの初期動作パラメーター(Noss=0の時にRAM上にFlashから初期コピーされて有ると思う)に依存するという危ない動作条件に陥ってしまっているようである。したがって、たまたまNoss=1として動作していても、まったく信頼できないシステムとなっている。
 以上より,Mega1284を使う場合は、SPI をハード使用で定義する場合は,例えば、以下のようにする必要がある。
Config SPI = Hard, INTERRUPT=OFF, DATA_ORDER = MSB, MASTER = YES, POLARITY = LOW , PHASE = 0, CLOCKRATE = 4, NOSS=0
最後のNoss=0が以前と異なる。
バグの原因は不明ではあるが、これで一応,動く。

現象2. ATMega168のボードで、
Config SPI = Hard, INTERRUPT=OFF, DATA_ORDER = MSB, MASTER = YES, POLARITY = LOW , PHASE = 0, CLOCKRATE = 4, NOSS=1
とすると、現象1と同様にSPIインターフェース信号が出なくなる。Config SPIとSPIINIT、SPIOUT命令のみのプログラムで実行してみても同様。なお、Noss=0とすると直り、正常に出力が出る。

 

上記の現象が起こる理由:

 理由は以下による。BASCOMのバグではなく、AVRのクセである。

AVRのマニュアルを詳細に読み込むと、SPIの項に以下の記述がある。

When a serial transfer is complete, the SPIF Flag is set.
If SS is an input and is driven low when the SPI is in Master mode, this will also set the SPIF Flag.

前後の文も合わせた意訳としては、
SPIのバイト転送が終わるとSPIFフラグがSPSRレジスターにセットされる。ところが、SPIをマスターモードで使っているとSSピンにLレベル信号が入力される場合でも、SPIFフラグがセットされてしまう。

 それでは、なぜ、BASCOMのConfig SPI命令でNoss=1とするとSPI信号が出なくなるのであろうか。コンパイル済みのプログラムをAVRに書き込んで、詳細に実行ステップを追ってみると、実は最初のSPI転送命令の箇所でデッドロックが起こっている。例えば、Config SPI、SPIINIT、SPIOUTとすると、SPIOUTのところでデッドロックする。
 理由は上記の英文にある。Noss=1とすると、ハード的なSSピンは使わないことを指定する。もし、SSピンに対応するポートピンを何も定義しなければ、それは入力として認識され、かつ、プログラムのスタート時にはLレベルになっている事が多い。これにより、SPIOUT命令実行時に、既にその終了を示すSPIEビットがセットされており、再度SPIの転送データーをセットした場合に永遠にSPIEという終了ビットが立たないため、SPIOUTはデッドロックする。先の現象1で生じた不安定性(時間が経つとSPI信号が出なくなる等)もこれが原因。現象1が出た際はプリント板を作り直していて、ハード的なSS信号に相当する配線がパワーオン時には、たまたまHに弱くプルアップで上がっていたのが、時間が経つとLに落ちてくるようになっていた。
 以上より、Noss=1とした場合は、ハード的にSS信号に対応するピンは、必ず外部抵抗(例:10k ohm)でプルアップしておく必要がある。これは、そのSSピンを出力ピンとして使用する場合も必要である。AVRがリセット後にポートの入出力方向を決める命令を実行するまでは、そのSSピンは一時的にLになるため、前記の不具合が生じる。(このため、SPIInit命令はSSピンが出力定義されてから後で実行する必要がある。)また、このSSピンは入力として使用できない。なぜなら、Lになると上記の現象が起こってしまう。あくまで、出力ピンとしてしか使用できない。なお、Noss=1としてもSSピンはソフト的にアサートするSSピンとして使用可能である。
 なお、Noss=0として、ハード的なSSピンを使用するように指定した場合は、既にSSピンは出力に指定されており、上記の不具合は起こらない。ただし、このようにした場合は複数のSSピンによる複数のSPIデバイスの選択はできなくなる。なぜなら、元々ハード的に使用されるSSピンはSPI転送時には必ずイネーブルされるため、他のデバイスとの間でバスの衝突が起こってしまう。また、ハード的なSSピンのBASCOMにおける制御は、1個のSPI転送命令が起こると、その前後でH->L->Hと変化し、転送終了後にはHに戻ってしまう。したがって、バースト転送などにはうまく対応できない。(バースト転送時には、最初にコマンドやアドレスをSPIデバイスにセットした後、SSはLにしたまま、引き続くバースト転送を行う。したがって、SSがHに戻ってしまうとバーストができない。)したがって、バースト転送を行うにはNoss=1の指定が必要となる。Noss=1としても、もちろんハード的なSS信号ピンを出力ピンとして定義して、それをSet Resetする事ができる。
すなわち、もともとのハード的なSSピンを外部抵抗でプルアップしておけば、Noss=1後に、ソフトによるセット・リセットを行うSSピンとして使用する事もできる。

 以上の結論として、Config SPIにおけるNoss=1の不具合はBASCOMには責任が無く、AVRのクセによるものであり、ハード的なSSピンを10k ohmでプルアップしておく事で防げる。ハード的なSSピンはこの場合、出力ピンとして使うしか無く、入力にはできない。(または、マスターモードでのスレーブからのSS入力ピンとして使用する。)
 マルチSS信号を使う場合は、もちろん、ハード的なSS信号を使えない。なぜなら、Noss=0としてハード的なSS信号を生かしてしまうと、SPI命令(例:SPIIN)を行うと、ハード的なSSが必ずアサートされてしまい、SPIバス上でソフトによってアサートされた別のSSによって活性化される、別のデバイスとの間でバスが衝突してしまう。SPIOUTとやった場合は、2つのSPIデバイスにデーターが書かれてしまう。Noss=0としても、ハード的なSSピンをプルアップしておき、どこにもつながなければ上記の不具合は起こらないが、ハード的なSSピンがNCとなり、一本ポートを損する事になる。

 

(7)赤外リモコンを使うRC5命令におけるMega1284のバグ

 これも(6)のトラブルに近い原因かもしれないが、Mega1284で赤外リモコンを使う際に使用するRC5命令系において、コンパイルエラーが出る。これはBASCOMのユーザーフォーラムをサーチすると出ていて、M1284の定義ファイル(M1284pdef.dat)に問題が有るとのこと。Mega168などとはM1284のレジスター名が異なっており、以下のようにM168などのTimsk 制御ビットをTimsk0と再定義する命令行をプログラム中に入れれば解決する。以下、その行を示す。

$asm
.equ Timsk = TIMSK0
$end asm

これをコンパイルされるプログラム中の最初の部分に入れておけば、正常にコンパイルできるようになる。

(8)Mega128等のXRAMに関するバグ(BASCOM ver 2.xx、2.0.7.6まで)

 こちらを参照。

(9)Mega1284のPortAに対するデジタル入出力指定のバグ(BASCOM ver .2.0.7.6で出ている)

BASCOMではMega1284におけるPortAの入出力指定にバグがあるようです。PortA.1~4をADC入力とし、PortA.5~7をデジタル入力として使用し、PortA.0を出力ポートとして使った場合、PortA.0を出力にできませんでした。この原因は時間がなくて解析できなかったのですが(おそらくBASCOMのポートAの制御レジスター設定がまずい)、PortAをADC入力とデジタル入力だけに使用する場合はうまく動作できるようです。ただし、ハイインピーダンスのデジタル入力は不可で、ADC入力に伴って入力指定したPortAのポートに変なパルスが出力されるという現象がオシロスコープで観測されました(入力インピーダンス=10M ohm)。なお、PortAのデジタル入力を74HC14出力によるローインピーダンス等でドライブした場合は、この現象は無視できました。

(10)ver.2.0.7.5で”On Interrupt"命令に追加されたAllsave オプション

 On Interrupt Label 命令では、デフォールトでは浮動小数演算に使用するレジスター類がインタラプト処理サブルーチン中ではスタックに退避されないため、インタラプト処理ルーチン中で浮動小数演算を行うと、そこから戻った時にメイン側のルーチンがおかしな動作となる場合があった。例えば、メインルーチンで浮動小数点演算を必要とする液晶表示のループをしておき、タイマー割り込みでその中でも浮動小数演算を行うといった場合、メインルーチン側で浮動小数演算に使われるレジスターの値が割り込み処理によって壊されるので、正常な動作が保証されない。これに対しては割り込み処理中でユーザーが関係するレジスターをPush / Popするように書いておく必要があった。(どのレジスターを浮動小数演算が使うかはBASCOMのマニュアルに説明がある。)
 これに対し、ver.2.0.7.5以降では、On Interrupt Label の後に、Allsaveを入れると、全てのレジスターが退避されるようになった。これにより、ユーザーがわざわざ割り込み処理ルーチン中に関連するレジスターのPush / Pop を書かなくてもよくなった。なお、当然、Allsaveをした方が割り込み処理ルーチンの処理速度は落ちるので、浮動小数演算を使わないなら、デフォールトのオプション無しで良い。また、さらに処理速度を上げるには、昔のバージョンからサポートされているNosaveオプションを指定して、必要なレジスターのみユーザーがPush / Popを明示する。

 

(11)TinyシリーズでConfig SPI = Hardをコンパイルするとエラーとなる

 ATtiny861などのTiny AVRでSPIインターフェースを使う際は以下の注意が必要である。Tiny861などのマニュアルにあるピンの機能表示を見ると、SCK, MISO, MOSI などが記されており、あたかもHardware SPIが使えそうに見える。そこでConfig SPI = HardとBASCOMで定義するとエラーが出る。(エラーの際には訳の分からないエラーメッセージが出るので,その原因の解明に非常に手間取った。)この原因は、これらのATTiny AVRではSPIはUSI (Universal Serial Interface)というハードを兼用して対応しているためであり,純粋にSPIハードではないため、エラーとなる。すなわち、USIを使用するSPIに対してはBASCOMはConfig SPI = Hardのサポートをしていない。したがって、SPI = Softとしてプログラムを書く必要がある。なお、USIを利用してハード的にSPIラインを制御する手法もあり、BASCOMのマニュアルのUSIの項に解説がある。(4-18章=220ペジあたりと、そこに紹介されているアプリケーションノート(ANxxx)。およびBASCOMに付属のexampleプログラム。)その場合はレジスター等の直接設定などのBASCOMによる処理が必要となる。もし、SPIの転送速度が遅くてもよい場合は、SPIINとかのSPI関連命令を使えるSPI=Soft宣言を使った方が楽である。

(12)ソフトウエア I2C のバグ

 ATtinyシリーズなどで、I2Cハードウエア(TWI)を持っておらず、それをソフトウエアで制御するsoftware I2C機能をBASCOMは持っている。この場合、SCLとSDAに使うピンをConfig SCL= Portb.3とか、Config SDA=Portb.4 とかやって定義してやると任意のI/O portピンをSCLとSDAに割り当てられる。ソフトウエアI2Cの場合は、Config TWIとはやらないように。Config TWI = xxx はハードウエアI2Cを持っているMegaシリーズなどのみで使用可能であり、これをTinyなどでやるとコンパイラエラーとなる。ソフトウエアI2Cをやる場合は、上記の2つのSCLとSDA割当命令のみを記しておけばソフトウエアI2Cになる。当然、$Lib "I2C_TWI.LBX"も不要。なお、I2Cの転送速度を決めるためにはConfig I2Cdelay = xxx 文を使う。(defaultではxxx = 5。)ソフトウエアI2CであるとBASCOMが判断すると、BASCOMはコンパイル時にI2C.LBXというファイルをライブラリーから自動的に読み込んで、そこのアセンブラーソフトによってI2Cバスの制御を行うようになる。
 さて,本題だが、このI2C.LBXにはバグがある。当方では以下の症状が出た。Mega168でハードウエアI2C (TWIと呼ぶ)を使って制御して完動していたストロベリーリナックス製の低電圧I2C液晶モジュールを、Tiny861でソフトウエアI2Cで動作させたところ、何も表示されなくなった。Mega168のボードに刺して動いているものを、すぐにTiny861ボードに刺してみてもダメ。
 この原因はストロベリーリナックスの低電圧I2C液晶のホームページにあるトラブルシューティング(アプリケーションノート AN004 PDFファイル)に書いてあるのと同様のSCLのゴミパルスが原因であることが、自作のプロトコルアナライザー(自作電子回路のページの6-15章のもの)で観測してみて分かった。(タイミング図はAN004を参照。)すなわち、I2C.LBXライブラリでI2Cのスタートコンディション(I2CStart命令)を行うと、何と,SCLのゴミパルスを一個だけスタートコンディションの前に出力してしまう。このため、液晶側のコントローラーが正しく応答しなくなっていた。
 このバグを回避するには以下のようにI2C.LBXファイルを書き直してやれば良い。BASCOMから「ファイルを開く」で、BASCOMフォルダー内にあるLIBフォルダーを開く。ファイルの種類をall typeにすると、ライブラリファイルがたくさんあるのが見える。そのうちのI2C.LBXファイルを開く。ASCIIテキストなので、そのまま見える。26行目あたりにある_i2c_start:というラベルを見つける。その下には_i2c_rep_start:というラベルもある。その下にある最初のアセンブラ命令である * sbi_scl DDR, _SCL 命令がバグの元凶である。この命令でI2Cstart時にSCLを一時的に L にして、ゴミパルスを発生してしまっている。この命令を次のように書き換えて、SCLが H の状態を保ったままにすると、正常に動作するようになる。
誤:* sbi_scl DDR, _SCL
正:* cbi_scl DDR, _SCL
このように書き換えて、BASCOMからI2C.LBXをセーブし直す。(上書きする。)
 これでソフトウエアI2CにおけるSCLのゴミパルスは無くなって,ストロベリーリナックスの液晶は正常に動作するようになった。以上により、Tinyシリーズなどを使っても正常にI2C通信が行えるようになる。なお、I2Cのクロック速度はMegaにあるようなハードウエアTWIほどには高速にできない。

(13) ロータリーエンコーダーの回転が正しく認識されない場合

 これはBASCOMの問題ではなく、ロータリーエンコーダーのハードの問題なのですが、参考のために記しておきます。
ジャンク的なロータリーエンコーダーを使った場合、スイッチのチャッタリング(接触の不良など)によって左回りや右回りがBASCOMのENCODER文で正常に認識されない事があります。その場合は以下のようにすると認識できるようになる事が有ります。以下のプログラムではPortC.2とPortC.3をロータリーエンコーダーのA、B端子入力として使っている例です。

Config Portc.2 = Input ' /A pin
Config Portc.3 = Input ' /B pin

Dim B As Byte

Mloop:

B = Encoder(pinc.2 , Pinc.3 , Rencl , Rencr , 0) ' rotary encoder sense

Goto Mloop ' LOOP

End

Rem Rotary encoder rotates to Left[counter-clockwise]

Rencl:

If B <> &B00 Then Return
' neglect the chattering

' Write program when the rotary encoder rotates left

Return

Rem Rotary encoder to Right[clockwise]

Rencr:

If B <> &B01 Then Return
' neglect the chattering

' Write program when the rotary encoder rotates right

Return

RenclとRencrサブルーチン中の IF文による判定条件によって瞬間的なスイッチのチャッタリングを無視します。
これでうまく行かない場合は、RenclとRencrサブルーチン中の IF文による判定条件を各々入れ替えてみてください。
(RenclではB<>&B01にし、RencrではB<>&B00にする。)

 

(14)複数のSPI入出力コマンドとインタラプト

 これはBASCOMの問題ではなく、接続されているSPIインターフェースデバイスの問題であるが、ここで解説しておく。
SPIインターフェースのLCDやADC, DAC等のデバイスにおいて、BASCOMから複数のSPI命令を与える必要が有るものがある。例えば、SPIOUT A(1), 2 や、SPI OUT B,1 の後にA(1)=SPIMOVE(5) などである。この場合、BASCOMは1回のSPI通信を実行した後、次に続くSPI命令のソフトを実行して行く事になる。ところが、最初のSPI命令を実行した後に、インタラプトが入って、それが長時間中断されると、SPIでつないだデバイスによっては、タイムアップとなったり、データー変換(ADC等)が正常になされない事が起こりうる。SPIの1バイトの転送中にはインタラプトはハード的な1バイト転送の終了まで起こらないが、次のバイト転送のSPI命令の開始までにインタラプトフラグがセットされていると、次のSPI処理がインタラプト処理の終了まで待たされてしまう。これによって、SPI接続されたデバイスの動作やデーター入出力に予期せぬエラーが生じる事が有る。
 これを防ぐには、もし、インタラプト処理と併存している複数バイトのSPI通信ルーチンが合った場合には、そのSPI通信サブルーチンの最初で、Disable Interruptsコマンドを発行してインタラプトを止め、SPIサブルーチンの最後(Return文の前)でEnable Interruptsを発行するようにしておく。これにより、上記の不具合は解消される。

(15)JTAGのdisableについて

M128を使っていた際に、MCUCSRレジスターの<B7>を1にセットしてJTAGに関するI/Oポートを通常のI/Oポートに割付けていたが、Mega1284では、これでは変な動作をしたので、いろいろ調べてみると、なんと、Mega1284ではそのレジスター名がMCUCSRからMCUCRに変わっているではないか!これにより、M128でやっていたMCUCSRレジスターへのJTAG disableが効かなくなっていたようだ。面倒なので、最初からFuseを書き換えて、JTAGをdisableしたところ、それらのI/Oポートは正常なI/Oとして動作するようになった。なお、レジスタ名をMCUCSRから(Mega1284の)MCUCRへの変更は試していない。元々、JTAG関係のポートを通常のI/Oポートとして使う場合は、Fuse-bitを最初から書き換えておく方が安全かも?

注:Mega1284で、実際にMCUCRに変えて試したところ、これでDisable出来る事が確かめられた。Mega1281などでもレジスター名がMCUCRに変わっているので、注意。

実際:プログラムの一番最初(DIM文やConfig文よりも前。$Framesizeの後。)で、
以下の命令をBASCOMソースに書く。

MCUCR.JTD = 1
MCUCR.JTD = 1

2回、連続して書く事。(ハード的に、ある短時間内に次の同じ命令が来ないとセットしないように出来ている。)

もし、Fuse bitでJTAGをdisableしていない場合は、上記を行わないと、JTAGに関係した4ビットのポートは正常に出力動作をしない。

(16)' 文字でコメント化する際の注意

あるプログラムで以下のように書いたら、その行からプログラムの末尾まで、全てコメントになってしまい、コンパイルはエラーになってしまった。

例:

'(This version is revised from v.0.5)

これはBASCOMのマニュアルをよく読むと、原因が見つかる。'(で検索すればヒットする。3.24節や6.368節などに解説がある。
 実は、最近のBASCOMの仕様では(いつからこうなっていたかは不明?)、'(を行の最初に置くと、')がある行まで、ある行ブロックが全てコメントになってしまうのだった。
要は、C言語の仕様にある /* から */の行までのブロックを全てコメントに出来るという機能をBASCOMも '( と ') という方式で持っていたわけだ。

したがって、例に挙げたコメントは

' (This version is revised from v.0.5)

と、
( と ' の間にスペースを入れれば、トラブルは解決した。

追補:何故か、一辺、'(をやらかしてしまうと、変な行からコメント化が復活してしまうことがあった(BUG?)。その場合はAll copyをして、新しいプログラム名を開いて、ペーストすると直った。


回路のTop Pageへ