カブのブレーキワイヤーを調整する

カブに乗り始めてしばらくたちましたが、ブレーキワイヤーのあそびが大きくなってきたので
調整してみました。

ブレーキワイヤーの調整は、オイル交換等と比べてもかなり簡単なので、誰でもメンテ可能です(オイル交換も慣れれば結構簡単ですが…)。



ブレーキワイヤー調整方法(前輪)


前輪のブレーキは右手のハンドルですが、ブレーキの適正な”あそび”の幅は、1~2cm程度です。ハンドルを引いて、抵抗を感じるまでの幅が2cmを超える場合は、調整が必要です。


前輪が傾いていると、ブレーキワイヤーの引っ張られ具合が変わるので、まずタイヤをまっすぐにします。
その後、アジャスタを回して調整します。
アジャスタは、前輪左側にあります。(写真の赤枠にあります)


アジャスタ部の拡大です。



この調整ですが、特別な工具は必要なく手回しで十分です。
上にある金具を後ろ側に押すとアジャスタの部分に隙間が出来るので、もう一方の手でアジャスタを回せばOKです。
アジャスタのナットは緩まないようにアールが付いているので、増し締めなども不要です。


調整が終わったら、アジャスタを前方向に引っ張って隙間が出来る事を確認します。
隙間が出来ないぐらいまで締めてしまうと、あそびが少なくなり過ぎです。

その後、ブレーキレバーのあそびを再確認してください。


ブレーキワイヤー調整方法(後輪)


後輪側の適正なあそび量は前輪よりちょっと多くて、2~3cm程度です。
後輪は足で操作するので、普段乗っている時にはあそび幅が掴み難いですが、ブレーキペダルを手で押してみると分かりやすいです。

調整方法は、前輪の時と同じです。
後輪右側のマフラー横にあるので、こちらも手で回して調整します。





ブレーキワイヤーの調整部付近には、中芯のケーブルを保護するためにゴム(ラバーブーツ)が付いています。
ワイヤー調整をした時、ついでに掃除をしたくなる場合も有りますが、この箇所(中のケーブル)に直接水をかけてしまうとサビの原因になるので注意してください(ヨゴレを布で拭く程度で留めておくのがベターです)。



また、ワイヤーの調整時に、ついでにブレーキのシューが減っていない事を確認しておくとよいです。
ブレーキをいっぱいに掛けた状態で、アームにある印と、ブレーキパネルの印(書き写真の赤枠)が一致していない事を確認します。一致するところまで行く場合はシューが限界まで減っているので交換が必要となります(前後輪とも同じです)。



PICkit3のファームを手動でアップデートする

MPLABでPICkit3を使用している場合、通常は初回接続時に自動でファームアップデート処理が走ります。

正常にプログラムが書き込めない等、PICkit3の動作が安定しない場合は、手動でファームを更新させると上手くいく場合があります。



ファームの手動アップデートは、以下の手順で行う事が出来ます。

メニューバーのProgrammer->Settingsを選択します。


Configrationタブを選択し、Manual Downloadボタンをクリックします。


※MPLABをデフォルトの場所にインストールした場合、PICkit3のファームは以下の場所にあります。
C:\Program Files\Microchip\MPLAB IDE\PICkit 3\PK3FW_012715.jam

[パタヘネ:読書メモ]第5章 容量と速度の両立:記憶階層の利用 その2

5.3 キャッシュの性能の測定と改善


キャッシュの性能の測定を行うためには、CPUの使用時間の式を再定義する必要がある。
メモリアクセスを考慮したCPU使用時間は、以下の式で表すことができる。

CPU時間 = (CPU実行クロック数 + メモリストールクロック数 ) * クロックサイクル数
 
メモリストールクロック数
        = 読出メモリストールクロック数 + 書込メモリストールクロック数
 
読出メモリストールクロック数
        = 読出件数 * 読出ミス率 * 読出ミスペナルティ
 
書込メモリストールクロック数
        = 書込件数 * 書込ミス率 * 書込ミスペナルティ + 書込バッファストール



メモリストールクロック数は、主にキャッシュミスが原因で発生する。

書込バッファストールは、書き込みを行おうとしたときライトバッファが満杯だったときに発生する。
だが、書込バッファストールに関しては、通常はストールが発生しないようなバッファサイズを設けるので、影響度は低い。

なので、メモリストールクロック数は、以下の式に簡略化できる。

メモリストールクロック数
        = メモリアクセス件数 * ミス率 * ミスペナルティ



主記憶の速度を変更せず、CPUのクロックだけ向上させた場合ミスペナルティは大きくなる。
(速度が増すのが理由)




次は、キャッシュの性能の改善について。

キャッシュ使用時のパフォーマンス向上には2つのアプローチがある

1. 異なるメモリブロックが、同一のキャッシュ領域を取り合うことを避ける
2. 記憶の階層を増やして、キャッシュミスペナルティを減らす


後者はマルチレベルキャッシュといい、IntelのCPUだとL1キャッシュやL2キャッシュ等と呼ばれるモノ。


まずは、”1. 異なるメモリブロックが、同一のキャッシュ領域を取り合うことを避ける”について考える。

前節ではダイレクトマップ方式で説明したけど、他にもキャッシュの配置方法は考えられる。

ダイレクトマップ方式
    メモリブロックをは、キャッシュ状の固定場所に置かれる
    実装が簡単
 
フルアソシアティブ方式
    メモリブロックをキャッシュの任意の場所に置くことができる
    キャッシュのミス率を下げることができる。
    どこにあるか分からないので、対象データを検索する必要があるが、これはハードウェア的に
    実装が面倒なので、キャッシュサイズが数が多いと大変になる
    理想的ではあるけど、ちょっと現実的ではないイメージ??
 
セットアソシアティブ方式
    各ブロックをキャッシュできる場所が複数(n個所)存在する
    Nウェイセットアソシアティブ方式とも呼ばれる
 
    要は、基本ダイレクトマップ方式だけど、マップ先のブロックが2個とかもう少し多めに
    用意されているイメージ



ダイレクトマップ方式の場合、キャッシュ位置は元データのアドレス下位Nビットを見るだけでよかったけど、たとえば、2ウェイセットアソシアティブ方式の場合は、
アドレス下位Nビット * 2と、アドレス下位Nビット * 2 + 1 がキャッシュ位置になり、どっちに入っているかは都度、両方見なければいけない。


セットアソシアティブのメリットはキャッシュヒット率が上がることで、デメリットはヒット時間が増大する(セット内を探索するので)事となる。
なので、Nウェイの”N”を際限なく大きくすれば良いというわけではない。



セットアソシアティブ方式の場合は、キャッシュデータが存在するかを”探索する”と書いたけど、実際は電子回路上で行われることなので、順次調べていくわけではない。
N個のデータを同時にコンパレータで比較し、ヒットしたデータをMUXで抽出するイメージになる。
このようなアクセス方法のメモリを連想メモリと呼ぶ


また、Nウェイセットアソシアティブ方式の場合は、新たにキャッシュすべきデータが発生したときにどのデータを破棄すべきかはLRUに従うことがあ多い。
2ウェイだったら、どっちが新しいかを1bitのメモリで管理できるけど、多重度が上がると難しくなる(詳細は後の節で検討する)



キャッシュメモリの階層を増やす

キャッシュをL1キャッシュと、L2キャッシュの2階層(マルチレベルキャッシュ)にできると、採用できる戦略が増える。具体的にはL1キャッシュはヒット時間の最小化、L2キャッシュはキャッシュミス率の最小化を目指すことが多い。


[PIC]TMRモジュールを使用する(タイマー割り込み)

PICにはTimer0モジュールというものがあり、このモジュールはカウンタやタイマー処理として使用できます。
今回はPI15F84におけるタイマー処理について説明します。

Timer0モジュールの仕組み

Timer0モジュールは、カウンタ又はタイマーのどちらか一方として使用することが出来ます。
どちらのモードとして使うかはオプションレジスタ(0x81:OPTION_REG)の5ビット目にあるT0CSビットで指定することが可能で、このビットは以下の意味を持ちます。

OPTION_REG.T0CS(bit5)
 
1:RA4/T0CKI   -> カウンタモード
0:内部クロック  -> タイマーモード




タイマーモードとして使う場合は、通常はTMR0レジスタの値が1命令実行するたびにインクリメントされていきます。プレスケーラを使用すると、1命令単位ではなくN命令実行するたびにインクリメントさせる様に変更できるので、より長い周期でのタイマーを定義できます。


ただし、TMR0レジスタに値が書き込まれた時、直後の2サイクル分は値が加算されないことに注意が必要です(と、データシートに書かれていたのですが、シミュレータで実行する限り、直ぐに加算されているようです… 要確認。)


一方、カウンターモードの場合、TMR0レジスタの値は自動で変わりません。
その代わり、PIC15F84Aの場合、RA4の値が変わったら(エッジを検出したら)TMR0レジスタの値が変わります。

エッジには立ち上がりと立ち下りがありますが、どっちのエッジを取りたいかはOPTION_REGのT0SEビットで指定できます。

OPTION_REG.T0SE(bit4)
 
1:立ち下がりの検出を行う
0:立ち上がりの検出を行う



ちなみにOPTION_REGは81hなので、このレジスタに値をセットするときはBank1に切り替える必要がある事に注意してください。

タイマー処理を有効にする


タイマー処理の有効化は、具体的には以下のコードで行うことが出来ます。

    ;タイマ割込みを有効にする
    MOVLW       0F0h    ;タイマーの初期値を指定する
    MOVWF       TMR0
 
    BSF         STATUS, RP0 ; BANK1に切り替え
    BCF         OPTION_REG, T0CS    ; TMR0をタイマモードとして実行
    BCF         STATUS, RP0 ; BANK0に戻す



上記のコードを実行させると、OPTION_REGの値を設定した直後から、1命令実行するたびにTMR0レジスタの値が加算されていきます。
これでタイマーとしての実行は完了ですが、タイマーが期待した値になったかを定期的に監視(ポーリング)するのは面倒なので、通常はタイマーが特定の値になった時に処理を行います。

この為、上記に加えてタイマー割り込みの登録を行います。
割り込み関係の定義は、INTCONレジスタを操作する事で設定できます。

    BSF         INTCON, T0IE ; TMR0 interruptをenableにする
    BSF         INTCON, GIE  ; Grobal interruptをenableにする


PICには複数の割り込みがあるので、T0IEビットで”タイマー割り込みを有効”にした上で、GIEビットで”割り込みの仕組み自体を有効にする”という2段階での設定が必要となります。

この設定を行うことで、TMR0レジスタの値がFFから00に代わるタイミングで、割り込み処理が行われます。


割り込みを処理を記述する

PICでは割り込みが発生すると、PCが0x04番地に移ります。
ですので、割り込みの処理は必ず0x04番地から記述されている必要があります。

また、割り込みが行われた時、PICのハード側では割り込み前のPCだけが保存されます。
その他のレジスタを使用する場合は、割り込み処理の最初に自分でレジスタ情報を退避し、割り込み処理の最後に戻す必要があります。
特にWレジスタやSTATUSレジスタは何らかの命令を実行すると書き換えられてしまうので、退避・復元処理が必須です。

この退避・復元は一般的に行われる作業なので、下記のようなテンプレートを使用する事が多いです。

W_TEMP      EQU 0x??
STATUS_TEMP EQU 0x??
 
    ;レジスタの退避
    MOVWF   W_TEMP      ; Wレジスタを W_TEMPに退避
 
    SWAPF   STATUS, W   ; STAUSレジスタをSTATUS_TEMPに退避(上位4ビットと下位4ビットが反転される)
    MOVWF   STATUS_TEMP
 
 
    ;割り込み処理メイン
    ...
 
    ; 割り込みの再設定
    BCF INTCON, T0IF
 
    ;レジスタの復元
    SWAPF   STATUS_TEMP, W  ; STATUS_TEMP
    MOVWF   STATUS
    SWAPF   W_TEMP, F
    SWAPF   W_TEMP, W
 
    ; 割り込みから戻る
    RETFIE


[gcc]値を2進数で表記する

C言語のプログラムでは”0x”の接頭語で16進数の値を指定可能ですが、gccでは”0b”の接頭語を付けることで2進表記を行うことができます。

プログラム

printf( "%d\n", 0x6f );
printf( "%d\n", 0b01101111 );



実行結果

111
111




ただしこの表記はgccで使用可能なもので、ANSI C標準では規格化されて無いです。
ですので、gcc以外のコンパイラを使用した場合、正しくコンパイルされない場合があり、ポータビリティが損なわれるので注意が必要です。

この機能は、gnu.orgにあるgccのオンラインドキュメント(http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html)にもC言語の拡張機能として、以下のように説明されています。

6.60 Binary constants using the `0b' prefix
 
Integer constants can be written as binary constants, consisting of a sequence of `0' and `1' digits, 
prefixed by `0b' or `0B'. This is particularly useful in environments that operate a lot on 
the bit-level (like microcontrollers).
 
The following statements are identical:
 
     i =       42;
     i =     0x2a;
     i =      052;
     i = 0b101010;
 
The type of these constants follows the same rules as for octal or hexadecimal integer constants, 
so suffixes like `L' or `UL' can be applied.



ざっくり日本語で意訳すると以下のような感じになります。


6.60 “0b”の接頭語を使用した二進数の定数

“0b”の接頭語の後に0,1の値を羅列する事で、整数の定数値を二進表記で記述することが出来ます。
この記法は、マイコンのようなビット単位で操作するような環境で便利です。

以下の4つは同じ意味になります。
i = 42;
i = 0x2a;
i = 052;
i = 0b101010;

これらの定数は8進や16進表記の定数と同じルールなので、接尾語に’L’や’UL’を付与する事でlong型やunsined long型である事を明記することが出来ます。

vimで複数ファイルを編集する

vimでは、複数のファイルを同時に編集することが出来ます。
vimではないviでも複数ファイル編集は出来るけど、ウィンドウ分割させて複数のファイルを”同時に”表示させる事は出来ません。

まずは1つファイルを開く

vi test1.c





vi上で以下のコマンドを打つと、2つ目のファイルが開く。
viでは複数ファイルを開いている場合、オープンしている各ファイルのことをバッファと呼びます。

:e test2.c





オープンしている全バッファを確認する

:ls



:lsの出力結果は以下のような感じになる。:filesでも同じだけどlsのほうが文字数が少ないのでおすすめ。

:ls
  1 #    "test1.c"                      行 1
  2 %a   "test2.c"                      行 1
続けるにはENTERを押すかコマンドを入力してください





さっきまで編集していたファイルに戻る

ctrl-^





バッファ番号を指定して、2つ目のファイルを画面に表示させる

:buffer 2



“:buffer 2″だと長いので”:b2″と省略してもよい



開いているバッファを閉じる

:bdelete


“:bd”で省略可。”:bd!”で強制クローズ(:q!と同じ意味で)



両方のファイルを並べて表示(ウィンドウ分割表示)

:split
もしくは
:vsplit


これも:sp, :vsで省略可能



並べたウィンドウ間を移動する

Ctrl-w h
Ctrl-w j
Ctrl-w k
Ctrl-w l


それぞれの方向に移動できる。



並べて開いた画面を1つだけ閉じる

:close




4774147958
Vimテクニックバイブル ~作業効率をカイゼンする150の技

[パタヘネ:読書メモ]第5章 容量と速度の両立:記憶階層の利用

まず基本原則として、コスト的な制約により記憶域には”少量の高速な記憶域”か”大量の低速な記憶域”のどちらかしかない。これは、たとえ十分な資金を用意できたとしても、”より大量”の情報をアーカイブしておく保存領域が欲しくなるので、結局上記の状況になる。

一方、プログラム側としては、大量の記憶域へ高速へアクセスしたいという要望が出てくる。
その結果として、データのキャッシュを行う必要があるけど、キャッシュがなぜ有効かというと、それは以下の原則によっている。

局所性の法則

時間的局所性(temporal locality)
    最近使ったデータは、近いうちに再度使われる可能性が高い
 
空間的局所性
    最近使ったデータの近くにあるデータは,近いうちに使われる可能性が高い
    (配列をループ処理するときの事を考えるとイメージしやすい))



なので、複数の速度・容量を持つメモリを記憶階層を組み合わせて情報を管理する事になり、この階層を記憶階層と呼ぶ。

階層はCPUに近い方を上位レベルと呼び、遠い側を買いレベルと呼ぶ。
また、各階層間で受け渡すデータの最小単位をブロック(もしくはライン)と呼ぶ。

5.2 キャッシュの基礎


まずはデータ要求サイズとデータサイズ共に1ワードである、シンプルなキャッシュシステムを考える。

キャッシュ構造を作るためには、実際のキャッシュデータ格納領域に加えて、以下の2つの情報が必要となる。

あるデータがキャッシュされているかのフラグ
キャッシュデータがある場所の検索方法


前者は純粋な有効フラグで、CPUの起動時は全て無効化されており、キャッシュが進むにつれて有効となっていく。

後者の検索方法には複数の戦略があるが、最もシンプルなのはダイレクトマップ方式になる。

— ダイレクトマップ方式 —
あるデータのキャッシュが存在する場所(アドレス)が一意とする実装方式。
当然の前提条件として、元データの場所のサイズよりはキャッシュ領域のサイズの方が小さいので、あるデータをキャッシュする場合には、キャッシュ場所を求めるための計算式が必要となる。
これは、元データの場所がある場所の下位Nビットを、キャッシュの保存場所のキーとすることが多い。

ダイレクトマップ方式でデータの検索方法を決めたとして、次はデータがキャッシュされているか否かを判定出来る必要がある。
これには、キャッシュアドレスの数分、付加情報を持たせればよい。
付加情報には、以下の情報が含まれる。

有効フラグ: キャッシュデータが有効かのFlg
タグ      : どのデータをキャッシュしているかのアドレス情報



前者は1bit有れば事が足りる。後者に関しては前述した”下位Nビットを、キャッシュの保存場所のキー”としている場合だったら、N+1ビット目以上のデータを管理すればよくなる。
このN+1ビット目以上のデータのことを、タグと呼ぶ。

一般的にキャッシュサイズというのはキャッシュデータのサイズ分だけを指し、有効フラグやタグ情報の格納領域のことはカウントに入れない。



キャッシュに関しては、WriteよりReadについてから考えるほうが用意となる。
これはWriteがキャッシュデータと実データの一貫性についてを検討する必要があるのに対して、Readは単に読むこと(とキャッシュミスの検討)だけだから。

というわけで、まず読み込みを考えるが、CPUがLOADしたかったデータがキャッシュに存在するかの判定は以下のロジックになる。

if ( 有効フラグ == false ) {
    キャッシュミス
}
 
検索キー = Load元アドレスの下位Nビット
if ( キャッシュ[検索キー].タグ != Load元アドレスのN+1ビット以降 ) {
    キャッシュミス
}
 
キャッシュヒット



というわけで、このロジックは値の同値比較とビット演算(AND)のみで実装できるので、論理回路として実装するのは比較的容易になる。

ここまでの説明でざっくりと説明したけど、MIPSの場合は32bit単位でデータのアクセスを行うので、アクセスするアドレスのうち下位2bitは検索キーとして使用する必要が無く、これによって記憶領域の節約が出来る。

ここまで、1度のLOADで1ワードだけキャッシュすることを考えたけど、周辺の複数ワード分のデータをキャッシュできれば(=ブロックサイズを大きくすれば)キャッシュのヒット率は向上する。
これは、最初に考えたデータの空間的局所性に起因する性質となる。

それではブロックサイズを際限なく大きくすればよいか?という質問になるけど、大きくしすぎると関係ないところのデータまでキャッシュされ、本来必要だったデータがキャッシュから追い出される頻度が上がってくるので却ってヒット率が下がる事もありうる。
また、ブロックサイズが大きすぎると、キャッシュミスが起きたときに、キャッシュへロードする量が増えるのでキャッシュミスペナルティが大きくなる。


複数ブロックをまとめてキャッシュする場合、全データをロードしてから処理を継続させると、転送処理を待つ必要が出るので処理が遅くなる。
これを避けるために、必要データが手に入ったタイミングで処理を続行し、キャッシュデータのロードを非同期で続けるという考え方も有る。この方法を早期実行再開(early restart)と呼ぶ。

さらに、必要データを一番最初にロードしてしまえば、早く処理を継続できる。これを要求データ先送り(requested word first)と呼ぶ。




キャッシュへの書き込みには、前述したようにデータの一貫性の管理についての問題が発生する。
キャッシュへ書くと同時に、下のレイヤの記憶装置にもデータを書けば(ライトスルー)、一貫性は保たれるが、処理が遅くなる。
キャッシュだけにデータを記録すれば(ライトバック)、処理が早くなるが、実データとキャッシュの間でデータの不整合が発生する。
ライトバック方式の場合、データが書き込まれるのは該当のキャッシュが追い出されるタイミングになる。

読み込みと同様、書き込みにもキャッシュミスが発生しうる。
書き込みのキャッシュミスが有った場合、下位の階層への書き込みには時間が掛かるので、ライトバッファを設けて非同期処理を行う場合もある。


[PIC]内蔵EEPROMへアクセスする

PICには電源を切ってもデータを保存して置ける領域としてEEPROM(Electrically Erasable Programmable ROM)が用意されています。

PIC16F84Aの場合EEPROMのサイズは、データ長8bitのものが64個で計64byteの領域が存在します。


このEEPROMですが、その特性上書き込み回数の上限値が決まっており、仕様上は1000万回までの書き換えが保証されています。一見すると1000万回なんて到達しなさそうですが、PICの動作速度が速い為、頻繁に書き換えると、意外とすぐに上限に達してしまいます。
計算してみると、1回の書き込みは10msec程度で終わるので、プログラムのロジックが悪い(?)と毎秒100回ぐらいは書き込むことができてしまいます。そうすると1日で100*3600*24=864万回ぐらい書けてしまうので、1日強で書き込み上限に達することができます。

また書き込んだデータの保持期間については,40年までは保障されています。
ただしこれは設計値を守って使用した場合なので、温度が高いところで使用したり供給する電圧が微妙に高すぎるなどの設計値を逸脱した環境で使用した場合はもっと短くなります。


アセンブラによるEEPROM領域へのアクセス方法についてですが、レジスタと異なりMOV命令では取得することはできません。その代わりに、以下のSFR(Special Function Register)を操作することで読み書きを行います。

EEDATA  書き込み(読み出し)データ指定用レジスタ
EEADR   書き込み(読み出し)先のアドレス指定用レジスタ
EECON1  EEPROM操作のコントロール用レジスタ
EECON2  EEPROM操作のコントロール用レジスタ



それでは、EEPROMへの具体的なアクセス方法を確認していきます。

EEPROMへのデータ書き込み


EEPROMへの書き込み手順は以下の通りで行います

1. EEADRレジスタに書き込むべき場所(アドレス)を指定する
 
2. EEDATAレジスタに書き込むデータを設定する
 
3. EECON1の2ビット目(WREN)を1にする(書き込み有効にする)
 
4. EECON2に55H,AAHを書き込む
 
5. EECON1の1ビット目(WR)を,1にする(書き込み要求を行う)
 
6. 書き込みが終わるのを待つ




上記の4.での書き込みは、具体的には以下のアセンブリになります。
これは、下記の4命令を正確に、この順に実行する必要があります(データシートでも明記されています)。

MOVLW   H'55'
MOVWF   EECON2
MOVLW   H'AA'
MOVWF   EECON2



6.の完了待ちについては、書き込みが完了するとEECON1の1ビット目が1->0に更新されるので、それを監視します。この更新(書き込み処理)にはかなり(5msec程度)時間がかかるので、一般的にはループ処理を作って完了を待つ事が多いです。。


–書き込みの成功チェック–
上記シーケンスを見ても分かるように、EEPROMへの書き込みは、書き込みが成功したか否かについて、PICデバイス側から応答をもらうことはできません。
このため、書き込みチェックを行うためには、書いたデータが正しいかを再度読み込んでチェックする(ベリファイ処理)必要があります。


EEPROMからのデータ読み出し

EEPROMからのデータ読み出しは、以下の流れになります

1. EEADRに読み出したい場所のアドレスをセットする
2. EECON1の0ビット目(RDビット)を1にする
3. EEDATAからデータを読み取る



これはアセンブラで書くと、以下のような流れになります。

BCF STATUS, RP0 ; バンク0に切り替える
 
MOVLW   0x00        ; 読み込むEEPROMのアドレスを指定する(この例では0番地)
MOVWF   EEADR
 
BSF STATUS, RP0 ; バンク1に切り替える
BSF EECON   RD  ; 読み出し要求を立てる
BSF STATUS, RP0 ; バンク0に戻す
 
MOVF    EEDATA, W   ; WレジスタにEEPROMの内容を読み出す




読み込みは、書き込みと違って1サイクルあればEEPROMからの読み出し準備が完了します。
ですのでRDビットを1にした直後にEEDATAから読み取りを行っても構いません。

※ちなみに,EEPROMへは間接アドレッシングによるアクセスはできないので注意が必要です。


MPLABのシミュレーターで実行時におけるEEPROM関係の制限


MPLABのシミュレータでデバッグ実行する場合、EEPROMの値はメニューバーのView->EEPROMより確認できます。

EECON2は、物理的には存在しないレジスタ(制御用)なので、このレジスタに値を書き込んでもシミュレータ上のレジスタ値は変更しません。


書き込みで、EECON1の1ビット目(WR)の待ちについては、シミュレーター上の時間で4msec程度掛からないと変化しません。
20MHzの設定でデバッグしていると、フラグが落ちるまで20000命令ぐらい待たなければならないので、ステップ実行ではいつまでまでたっても先に進みません。
実際に待たせてみたのが、下のキャプチャ画像です。

書き込み完了をシミュレータで確認したい場合は、ループの終わりにブレークポイントを張って、一度実行させてしまう必要があります。

[PIC16シリーズ]ピン数別の機能・評価一覧

PICはシリーズが多すぎて各チップの機能が分かりづらいのが難点ですが、価格と特徴を上手く整理してくれているものがあったので備忘録としてメモっときます。


PIC16ではなく18シリーズについて知りたい場合は、下記の記事を参考にして下さい。
 [PIC18シリーズ]ピン数別の機能・評価一覧


【Pickit3】PIC専用のスレPart21【速度3倍?】
http://uni.2ch.net/test/read.cgi/denki/1228312753 より

————————————————————-

秋月のPIC16シリーズDIP品限定ピン数別評価


40,28pin PIC


40ピンは ◎16F887 \250
28ピンは ◎16F886 \230で決まり
 
ADC有,eCCP/CCP有,mSSP有,USART有,8Kw,内蔵31K-8MHとフルサポート



20pin PIC


20ピンはF690のバリエーションモデルが多いけど価格差が小さいからF690でOKかな
 
◎16F690  \210     ADC有,eCCP有,eUSART有,SSP有,4Kw,内蔵31K-8MHzとバッチリ
○16F689  \180     F690からeCCP機能削られた
×16F687  \180     さらにF689を2Kwと半分に
△16F677  \170     さらにF687からeUSARTも削られた
○16F685  \190     F690からSSP&eUSART機能が削られた
◎16F785  \180     ADC有,CCP有,2Kw,内蔵31K-8MHz,OPアンプ搭載ってのはいいね



18pin PIC


18ピンは種類は多いけど選択の幅は狭い。F88かいっそ20ピンにするとか
 
×16F84A  \300     機能無,1Kw,いまさら不要
×16F648A \200     CCP有,USART有,4Kw,内蔵4MHz,今はF87があるから
×16F628A \200     F648Aの2Kw版,値段同じじゃ価値無し
△16F627A \160     F648Aの1Kw版,低コストで勝負かな・・・
◎16F88   \230     ADC有,CCP有,SSP有,USART有,4Kw,内蔵31K-8MHzとGood
○16F87   \200     F88にADC無いだけ
×16F819  \250     ADC有,CCP有,SSP有,2Kw,内蔵31K-8MHz,F88に及ばない
○16F818  \200     F819の1Kw版,F87と比べるとROMとUSARTで負け,ADCで勝ち
◎16F716  \110     ADC有,eCCP有,2Kw,内蔵CLK無,EEPROM無だけど値段安い



14pin PIC


14ピンはF688だね
 
◎16F688  \160     ADC有,eUSART有,4Kw,内蔵31K-8MHz,機能的にもマアマア
△16F676  \140     ADC有,1Kw,内蔵4MHz,F675の14ピン版か・・・



8pin PIC


8ピンはF683だと思っていたがF615も\100にしてはなかなか良い
 
◎12F683  \150     ADC有,CCP有,2Kw,内蔵31K-8MHz,8ピンとしては多機能
○12F675  \120/130 ADC有,1Kw,内蔵4MHz ,環境によってはE/P版
△12F629  \100     675のADC無(機能無しってこと)
△12F635  \120/130 機能無し,1Kw,内蔵31K-8MHz,環境によってはE/P版
◎12F615  \100     ADC有,eCCP有,1kw,内蔵4Mor8MHz,EEPROM無し
×12F609  \100     F615からADC,eCCP機能省かれてる



その他


(参考)機能無,EEPROM無,12bitコア プログラム組む上で制限多し
 
△16F57   \100     28ピン,2Kw,    内蔵CLK無,安くて多くのI/O欲しいなら
△16F54    \60     18ピン,0.5Kw,  内蔵CLK無,安いI/O欲しいなら
△10F200   \70     DIP8ピン,1/4Kw,内蔵4MHz,米粒PICのデバッグ用
 
おまけ
◎ATMEGA88 \250    28ピン,ADC,PWMx6,USART,SPI,I2C,8KB,内蔵~8MHz,高性能でお奨め
◎tiny2313 \100    20ピン,PWMx4,USART,2KB,内蔵~8MHz,ADC不要なら安価でいい





一通り見た感じだと、やっぱりPICじゃなくってAVRのATMEGA88が一番かな…

PICkit3を使用時,PICを認識できない時にチェックすべき事(PK3Err0045エラー)

MPLABより、PICkit3を使用してPICに書き込みを行おうとした際、OutputウィンドウにPK3Err0045のエラーが表示する事があります。

PICkit 3 detectedConnecting to PICkit 3...
Running self test...
Self test completed
Firmware Suite Version...... 01.27.15
Firmware type......................Midrange
PICkit 3 Connected.
 
PK3Err0045: You must connect to a target device to use PICkit 3.





これは、PICkit3が書き込み対象となるPICデバイスを検出できなかった事を意味しています。
このエラーが発生する場合は、PIC側の回路に問題がある事が多いです。

以下に、チェックすべきポイントを列挙してみました。

PICkit3のpin4,5(PGC/PGD)に対して…


プルアップ抵抗をかませない

これらのピンには、既にPICkit3内で4.7kのプルダウン抵抗が入っています。
ですので、プルアップ抵抗を入れてしまうと分圧による電圧降下が発生し、正常に動作しなくなります。

コンデンサをかませない

GNDと間にコンデンサを入れたくなるかもしれませんが、データやクロック信号の高速転送を妨げる事になる為、プログラムの書き込みやデバッグが正常に出来なくなります。

ダイオードを挟まない

プログラムの書き込み状況をチェックしたいがためにpin4,5にLEDを接続したくなるかもしれませんがこれは動作しません。書き込み動作であってもPGC/PGDは出力専用ではなく、入出力の双方向通信を行っています。
この為、ダイオードをつけるとPICからの応答情報をPICkit3が取得する事が出来なくなってしまいます。


PICkit3のpin1(MCLR)に対して…


コンデンサをかまさない事

この信号線もPGC/PGDと同様に、高速なON/OFF変化が行われるのですが、コンデンサが入るとこれが妨げられるので正常に動作しなくなります。
替わりに4.7k~10k程度のプルアップ抵抗をかませて下さい。



PICの回路に対して…


PICkit3の信号線が正しく接続されている事

PICkit3には6本の端子が有りますが、通常はpin1-5の5本を使用します。
これらのピンがPIC側の正しい端子に接続されている事を確認してください。

1 MCLR/VPP
2 VDD
3 Ground
4 PDG (ICSPDAT)
5 PGC (ICSPCLK)
6 LVP



参考までに、PIC16F84Aに書き込む場合は以下の配線となります。

PICkit3側           PIC16F84A側
pin1 MCLR/VPP       pin4  MCLR
pin2 VDD            pin14 Vdd
pin3 Ground         pin5  Vss(GND)
pin4 PDG (ICSPDAT)  pin13 RB7
pin5 PGC (ICSPCLK)  pin12 RB6
pin6 LVP            未接続



前回、PIC16F84Aへ書き込みを行ったときの回路です。
作業手順の詳細はこちらの記事を参考にしてください。




外部より電源を供給する事

PICkit3では設定を行う事でターゲットとなるPICに対して電源を供給する機能を持っています。
ですが、この供給能力は非常に低い(30mA程度)なので、対象の回路での消費電力が大きかったり、PC側のUSBポートからの供給が少ないと、正常に動作しなかったり動作が不安定になったりします。
PICkitからPICが正常に認識できない場合は、一度外部電源からPICへの電源供給を行ってみてください。

MPLABでPICkit3を使用してのデバッグが出来ない理由Top10

PICkit3のユーザガイドで、デバッグが上手く行えないときにチェックすべき点が掲載されていました。

日本語の情報が見当たらなかったので、訳したものを置いときます。
(ざっくりな意訳なので、おかしな所があったら指摘してくださると幸いです)



1.オシレータが正しく動作していない。
 オシレータに関するコンフィグレーションビットの定義もチェックしてみてください。

2.デバッグ対象の回路に電源が供給されていない。
 電源ケーブルの接続確認と、テスタで電圧チェックしてください。

3.デバッガが物理的にPC(もしくは回路側)とつながっていない。
 ケーブルの接続を確認してください。

4.PICでコードプロテクトのコンフィグレーションビットがセットされている。
 アセンブラの場合__CONFIGで_CP_OFFの指定が行われている事を確認してください。

5.プログラムがリリースモードでビルドされている。
 プロジェクトツールバーのドロップダウンリストから”Debug”を選択し、リビルドしてください。

6.MPLABのIDEで、デバッガが”Programmer”として実行されている。
 メニューバーのDebugger->Select Tool->PICkit3を選択し,デバッグモードにしますます。

7.デバッガとPCの接続が切断されている。
 MPLAB IDEのデバッガに再接続します(??)

8.デバッグ対象のプログラムが壊れているか、エラーを含んでいる。
 プログラムのリビルドを行い再書き込みと、PICのPower-on Reset機能を使ってみます。

9.他の設定がデバッグを妨げている。
 ???

10.デバッガからアクションの要求が、常に出来ないようになっている。
 例えば、対象となるプログラムが走っている状態だと、デバッガはブレークポイントを設定できません。




他に考えられること

たまたまグリッチ(接触不良などに起因するノイズ)が発生しただけかもしれないので、もう一度チャレンジしてみる。

プログラムの問題に起因しているかも知れないのでLEDの点滅など、もっと簡単なプログラムでデバッグできるか確認してみる。

過電流などの理由でPICが物理的に壊れている可能性があるので、チップを変えて試してみる。

回路上の問題があるかもしれないので、Microchipから出ているデモンストレーションボードを購入してデバッグしてみる。


[MPLAB]エディタのフォントサイズを変更する

PICの開発環境としてスタンダードなMPLABですが、インストール直後の環境だとエディタのフォントサイズが微妙に大きく、ソース全体が見渡し辛いので可読性がちょっと劣ります。

このフォントサイズですが、以下の手順で変更することが可能です。


エディタを表示させ、右クリックし、メニューの一番下にある”Properties…”を選択します。



Textタブにある”Select Font”ボタンをクリックします。


見慣れたWindow標準のフォント選択ダイアログが表示されるので、お好みのフォントに変更してください。



MSゴシックの9ptあたりにすると、全体が見渡せるので楽ちんになります(お勧めです)。



一方、画面をプロジェクタに表示させて複数の人でレビューを行いたい場合は、14ptあたりにすると遠くからでも見やすくなります。

PICアセンブラにおける分岐/ループ処理の組み立て方(基本編)

C言語では、if,switchによる分岐や、for,whileによる繰り返し命令を持っています。
一方、PICのアセンブラはどうかというと、当然ながらそんな便利な制御機能は持っていません。

というか、PICに限らずほぼ全てのCPUは、機械語レベルで上記のような繰り返し処理を持っていません。

それでは、アセンブラレベルでどうやって制御構造を組み立てていくかというと、条件付分岐命令というものを利用していきます。



PICのアセンブリにおいて、条件付き分岐を行える命令は以下の4つです

BTFSC   ; bit test file-reg skip if clear
BTFSS   ; bit test file-reg skip if set
DECFSZ  ; decrement file-reg skip if zero
INCFSZ  ; increment file-reg skip if zero



各命令の意味を日本語で書くと、以下のような感じになります。説明文が”xxxならスキップ”となっていることからも分かるように、全て条件によって処理を分岐させる事が出来ます。

BTFSC   ; レジスタの指定したビットが0なら次の命令をスキップ
BTFSS   ; レジスタの指定したビットが1なら次の命令をスキップ
DECFSZ  ; レジスタ値を1減算し,値が0なら次の命令をスキップ
INCFSZ  ; レジスタ値を1加算し,値が0なら次の命令をスキップ




PICのアセンブリで分岐やループを行いたい場合は、これらの命令がもつ条件付スキップの機能を活用して条件分けを行っていきます。



分岐処理の基本形


例1: Wレジスタの最上位ビットが立っていたら処理を行う


条件に一致するときのみ処理(シフト演算)を行う処理です。

SUBLW   0x80
BTFSC   STATUS, Z   ; ゼロフラグをチェック
RRF     PORTB, F    ; Z = 0の場合の処理 (右シフト)
NOP                 ; Z = 1の場合



これは、Cで書くとこんな感じになります。

if ( wreg - 0x80 == 0 ) {
    portb = portb < 1;
}



例ではWレジスタの値が0x80だったら、PortBの値を左シフトするという処理を行っています。
レジスタの値の判定にはSUB命令を使用し、Wレジスタから0x80(10進で112)を引いて、その値が0だったときのみ後続の処理を行わせています。

この例はC言語でいう”if文”の2分岐処理になります。
C言語での分岐には、他にswitchによる複数の分岐がありますが、上記の処理を書き連ねていくと可読性が悪くなるので、テーブルジャンプという仕組みを使うと分かり易いです。
この辺の詳細はまた改めて説明します。


ループ処理の基本形


例2: 指定された回数だけループ処理を行う

こちらの例では、60回のループ処理を行っています。

CNT1 EQU    0x20    ; ループカウンタの保存先
 
    MOVLW   60      ; ループカウンタに初期値をセット
    MOVWF   CNT1
LOOP
    RRF     PORTB   ; 処理内容
 
    DECFSZ  CNT1    ; ループを抜けるべきか判定する
    GOTO    LOOP
    NOP             ; ループ後の処理



C言語で書くと以下のようなロジックになります。

for ( cnt1 = 60; cnt1 < 0; cnt1-- ) {
    portb = portb < 1;
}




PICでタイマー制御を行いたい場合、基本的な考え方は上記のループを活用します。
具体的には、ループ内でnopを実行し空回しすることで、所定時間の”待ち”を作ることが多いです。

タイマー制御に関して知りたい場合は、以下の記事が参考になります。
PICで一定時間のスリープ(ウェイト)を行う

VMware上でPICkit3は使用できるか?

MPLABをインストールした環境をVMwareで構築しているのですが、
PICkit3を接続してみたところ、問題なく使用することが出来ました。

以下、証拠画面です(クリックで拡大します)。


動作を確認した環境は以下の通りです。

ホストOS
    Windows7 SP1
ゲストOS
    WindowsXP SP3
 
App
    VMware Player 3.1.4



注意すべき事はVMにUSBポートを追加するぐらいで、特にハマリどころもなく動作しました。


USBポートの追加(VMwareのメニューより、仮想マシン->仮想マシンの設定)


MPLABでPICkit3使用時,PICkit3から電源を供給する

MPLABでPICへのプログラム書き込みにPICkit3を使用している場合、PICkit3から回路へ電源を供給することが出来ます。
電源供給はMPLABで設定を行います。

MPLABを起動し、Programmer->Settingをクリックします。


Powerタブをクリック後、”Power target circuit from PICkit3″にチェックを入れるとPICkit3側からPIC回路へ電源を供給することができます。




供給する電圧は、画面よりある程度指定可能です。
スライドバーを操作することで、2.75V~5.5Vを選択可能です。




電流については30mAが上限です。
これは小さい回路を組んだときに、わざわざ外部電源を用意しなくて良いというメリットが有りますが、USBハブ経由でPICkit3を接続している場合等は、PICへ十分な電力が出来ず、正しく動作してくれない場合があります。
大きな回路になると動作しなかったり、動作が不安定になる可能性も有るので注意が必要です。


キホンからはじめるPICマイコン

PICkit3を使ってMPLABからプログラムを焼く方法

前回MPLABを使ってプログラムを作成する方法を確認したので、今回は作成したプログラムを実際にPICへ焼いてみました。
今回の書き込みを行ったのはPIC16F84Aです。


PICへの書き込みにはライタが必要なので、まずは以下のハードを用意します。

用意する道具一覧


PICkit3


今回、ROMの書き込みにはPICkit3を使用しました(PICkit2でも書き込み可能です)。

楽天だと2021年7月現在、Debug Express付きのものが1万円ほどで販売されているようです。
プログラムの書き込みだけならDebug Expressは不要です。PICkit3単体だと4000円程度で入手できます。
マイクロチップ PICkit3 Debug Express DV164131

マイクロチップ PICkit3 Debug Express DV164131
価格:10,920円(税込、送料別)




ブレッドボード


PICの書き込みをするだけなら、以下のように小型で安価なものでも十分です。
小型のものだと400円程度から購入できます。
ブレッドボード(小型)

ブレッドボード(小型)
価格:400円(税込、送料別)


PICにセンサーなどを追加して色々遊んでみたい人は、以下のようにもう少し大き目のものがお勧めです。

大きいほうも持っているのですが、今回は小型のもので動作確認しました。

ブレッドボード用のジャンプワイヤ


ブレッドボードで使用する配線用のワイヤです。
大きめのブレッドボードを購入した場合は、ワイヤも同梱されていることも多いです。
B005AYV154
ブレッドボード用ジャンプワイヤセット



PICへの書き込み方法

まずはPCへPICkit3を差し込みます。接続には必ず付属の赤いUSBケーブルを使用してください。WindowsXPで確認しましたが、特にドライバのインストールは要求されずに認識されました。

デバイスマネージャのUSBヒューマンインターフェースとして、以下のようにPICkit3が認識されていればOKです。


次にMPLABを起動し、書き込みを行いたいプログラムのプロジェクトファイルを開きます。
メニューバーよりProgramer->Select Programmer->PICkit3を選択し、チェックを入れます。


すると、右下にConnectingというメッセージがしばらく表示されます。



この時、PMLABでデバッグを行っていた場合は以下のメッセージが出ます。


Debugger In Use
----------------
A programmer and a debugger cannot be loaded at the same time.
Do you want to unload the debugger and continue loading the 
programmer?


これは、プログラム(デバッグ)機能とPICへの書き込み機能が同時に使用できないという警告です。デバッグを終了して書き込みを行いたい為、OKをクリックします。


その後、OutputウィンドウにPICkit3タブが表示され、デバイスが検出されたというメッセージが表示されます


PICkit3 detected
Connecting to PICkit3... 
Running self test...
Self test completed
RS Version..... 01.08.22
AP Version..... 01.08.22



初回の使用時には、PICkit3とMPLABのバージョンによっては、ファームウェアをアップグレードを促すダイアログが表示されます。通常はファームをアップしないとしないと使用できないので、OKをクリックします。


ファームのダウンロードとアップデートが行われます。
しばらく時間が掛かるので待ちます。



ファームのアップデート後、再度PICkit3を刺しなおすと以下のようになります。
(今回のファームバージョンは01.27.15でした)


PICkit3 detected
Connecting to PICkit3... 
Running self test...
Self test completed
Firmware Suite Version .... 01.27.15
Firmware type ............. Midrange





次に、電圧設定に関する警告が表示されます。


Voltage Caution 
CAUTION: Check that the device selected in MPLAB IDE (PIC14F84A)
is the same one that is physically attached to the debug
tool. Selecting a 5V device when a 3.3V device is connected
can result in damage to the device when the debugger checks
the device ID.


MPLABで選択されたデバイスと同じものが取り付けられているか確認してください。
3.3V駆動のデバイスに対してプロジェクト作成時に5V駆動のデバイスを選択しているとデバイスにダメージを与える原因となります。

メッセージを確認後、問題なければOKをクリックします。




次に接続される側であるPIC側の回路をブレッドボードで組み,PICkitと接続します。
ドキュメントによると以下の接続を行う必要があります。


上記仕様を元に実際にブレッドボードで組んだ回路です。
使用するPICは、前述の通りPIC16F84Aです。
左下の外部に出ている線がPICkit3との接続線で、左側がpin1になります。


こちらはPICkit側です。▲のある側がpin1で、pin6は使用しません。


また、PICkit3は+5V電源の供給も行えるのですが、最初の動作確認は外部から電源を貰ったほうが確実です。



配線を行いPICが認識されると、Target Detectedのメッセージが表示されます。


Target Detected
Device ID Revision = 00000000






ここまで出来たら、次はいよいよプログラムの書き込みです。
メニューバーにPICkit3の操作用アイコンが追加されています。


アイコンは、左から順に以下の操作を行う事ができます。

Program            PICにプログラムを書き込みます
Read               PICからプログラムを読み出します
Verify             PICに正しくプログラムを書き込めたかチェックします
Erase Flash Device PICからプログラムを消去します
Blank Check All    PICのプログラムが空であるかチェックします
Release From Reset 
Hold In Reset      
Power On



今回は一番左のProgramをクリックします。



下記のメッセージが表示されれば書き込みは成功です。


Programming...
Programming/Verify complete




今回は、動作確認として以下のプログラムを書き込みました。

    LIST    P=16F84
    INCLUDE P16F84A.INC
    __CONFIG  _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF
 
 
    ORG     0
    BSF     STATUS, RP0
    MOVLW   0xFF    ; PORTAを入力にする
    MOVWF   TRISA
    MOVLW   0x00    ; PORTBを出力にする
    MOVWF   TRISB
    BCF     STATUS, RP0
 
MAIN_LOOP
    ; RA2に入力が無ければPORTBを1,有れば0にする
    MOVLW   B'11111111'
    BTFSS   PORTA, 2
    MOVLW   B'00000000'
    MOVWF   PORTB
 
    GOTO    MAIN_LOOP



動作確認用のスイッチ、LED、オシレータを追加した回路はこちら。


スイッチを操作して、LEDのON/OFFがコントロールできれば動作確認は成功です。

4777516482
ブレッドボードによる電子回路実験 (I・O BOOKS)

[C#,Win32API]ユーザがしばらくPCを使用していなければ自動ログオフさせる

WindowsではユーザがしばらくPCを使用していな時に、スクリーンセーバーを起動させたり、自動デフラグを行わせたりしています。
このように、PCがアイドルの時に限って何らかの処理を行わせるようにしておくと、ユーザに負担を掛けることなく(処理が重くなることなく)タスクを実行できるので便利です。


このような処理を行うためには、ユーザが”何もしていない”時間を計測する必要なのですが、GetLastInputInfo()というWin32API関数を使用すると、この情報を取得可能です。

関数仕様は以下の形で、データの取得に成功すると戻り値に0以外の値が返ってきてくれます。

BOOL GetLastInputInfo( PLASTINPUTINFO plii );



引数で指定される構造体は以下の定義です。

typedef struct tagLASTINPUTINFO {
  UINT  cbSize;			// この構造体のサイズ
  DWORD dwTime;			// 最後にユーザが操作してからの経過時間(tick count)
} LASTINPUTINFO, *PLASTINPUTINFO;




上記関数をC#から使いたい場合は以下のような感じで定義を行うとOKです。

using System.Runtime.InteropServices;
 
[DllImport("User32.dll")]
private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
 
internal struct LASTINPUTINFO {
    public uint cbSize;
    public uint dwTime;
}




構造体のcbSizeメンバは、以下のコードでセットします。

LASTINPUTINFO info = new LASTINPUTINFO();
info.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf( lastInPut );





今のご時勢は節電が意識されているので、ユーザが暫く使っていないと処理を中断させるようなアプリを作ってみるのも有りかも…

PICで一定時間のスリープ(ウェイト)を行う

アセンブラではスリープ処理も,ひと苦労

プログラムの中で一定時間処理を止めたい場合、C言語だとsleep()関数を使用することで秒単位のスリープ処理を行えます。

一方、PICでアセンブラを使って処理を行う場合はどうかというと、残念ながら一定時間処理を待つという命令は有りません。アセンブリのニーモニック一覧を見るとSLEEPというのがあるので、一見するとこれでいけそうな気もしますが、残念ながらPICのSLEEPはMPU自体をスリープモードにする(Windowsでいうサスペンド状態)命令なのでこれは使えません。

このような場合、PICではループを空回しする事でビジーウェイトを作り、所定の時間だけ経過させるという方法を取ります。

何回ループを空回りさせるべき?


ループ空回りで時間をつぶすのは良いとして、ここで問題になってくるのは、ループを何回まわす必要があるかが分からないという事です。これに関しては、PICのクロック周波数から1命令あたりの消費時間を求めて、計算を行うという手間が必要になってきます。


クロック周波数と周期は逆数の関係になるので、例えばPICを20Mhzで駆動させている場合1クロックサイクルは”20M分の1秒”になります。
1Mは100万なので2000万分の1秒ということになります。


ただし、例えばPIC16F84Aのチップを使っている場合、1命令の実行に4クロックサイクル(分岐命令を除く)必要となります。なので、1命令の実行に必要な時間は500万分の1秒です。これは計算すると200ナノ秒(0.2マイクロ秒)に相当します。

先ほど”1命令の実行に4クロックサイクル必要”と書きましたが、自分が使用しているチップが1命令あたり何サイクル必要かは、製造元のMicrochipが公開しているデータシートに載っています。

データシートから引用した上の図を見ると、OSCから入力されたクロック4つで、PC(プログラムカウンタ)が1回分処理される事が分かります。

また、データシート最初に記載されている、機能一覧ページを見ると以下のような記述があり、ここからも上記の計算が合っていることが分かります。

Operation speed: DC -20MHz clock input
                 DC -200ns instruction cycle




というわけで、仮に1マイクロ秒の待ちを発生させたい場合は、5命令分の待ちが必要ですし、1ミリ秒待ちたいなら5000個の命令を実行されれば良いことが分かります。
但し、GOTO命令などの分岐命令は、2サイクル分の時間が必要な事に注意が必要です。

アセンブラでウェイト処理を作る


上記の計算で基礎資料がそろったので、実際にウェイト処理を作ります。

WAIT_USEC			EQU	0x20	;何マイクロ秒ウェイトするか(callerが指定する)
 
; WAIT_USECレジスタで、指定された時間(マイクロ秒)だけ待つ。
WAIT_TIME
	; 1回5cycle掛かる -> 20MHzで1usec消費
	NOP
	NOP
	DECFSZ	WAIT_USEC, F
	GOTO	WAIT_TIME
 
	RETURN


上記の処理を見てみると、WAIT_TIMEラベルで示される処理でループを行っています。
ループはNOPが2,DECFSZが1つ,そしてgotoが1つあり、gotoは命令の実行に2サイクル必要なので、合計2+1+2=5命令分の時間を消費し、丁度1マイクロ秒の待ちになります。
さらにそれをWAIT_USECレジスタに指定された回数分ループさせています。

この処理の呼び出し元は、以下のような記述になります。

	; 200マイクロ秒スリープする
	MOVLW	200
	MOVWF	WAIT_USEC
	CALL	WAIT_TIME



WAIT_USECにスリープさせたい時間を予めセットしてからWAIT_TIMEサブルーチンをコールする事で、任意の時間処理をスリープさせる事が出来る様になりました。

※ループ処理について知りたい場合は、こちらの記事も参考にしてください
PICアセンブラにおける分岐/ループ処理の組み立て方



この方法の欠点は?

上記の方法で、指定した時間だけ処理を待たせる事が可能となりましたが、残念ながらこの方法も万能では有りません。

例えば、何らかの処理を行いながら、10秒ごとにブザーを鳴らしたいという処理があった場合、上記の方法だと10秒間、空ループをひたすら実行し続ける事になるので、他の処理が行えません。
一方、10秒の間に”何らかの処理”のほうを作業させてしまうと、この処理に何クロックサイクル使用するかを確定できない為、こんどは10毎に処理を行うということが出来なくなってしまいます。

上記のような場合は、割り込み機能というものを使って対応する事になるのですが、
これについてはまた後日書くことにします。

[MPLAB] MPASMアセンブラで良く出るエラーメッセージとその対処一覧

PICの開発をMPLABを使用してアセンブラ(MPASM)で開発しているときに、良く表示されがちなエラーメッセージとその対処法の一覧です。

開発を始めたばかりは、特にエラーの意味が分かり辛く、デバッグに手間取る事もあるので参考にしてみてください。

※以降のメッセージと対処法はP16F84Aの場合を元に説明しています。
 他のPICを使用している場合は、一部対処法が異なる場合もあるかもしれません。
 その場合は各自読み替えてください。



よくあるエラーメッセージ一覧


Register in operand not in bank 0. Ensure that bank bits are correct.


メッセージ例


Message[302] TEST.ASM 99 : Register in operand not in bank 0.  Ensure that bank bits are correct.




内容

オペランドで指定されたレジスタはバンク0のものでは有りません。
バンク指定のビットが正しいかを確認してください。


対処方法

P16F84Aの場合、バンク指定のコードは以下のようになります。

BSF		STATUS, RP0		; bank1に切替える
BCF		STATUS, RP0		; bank0に切替える


上記エラーメッセージは、バンク1のレジスタを操作しようとしたときに表示され、正しくバンク切り替えを行っていても表示されるようです。

このメッセージは通常は無視してもかまいませんが、気になる場合はファイルの先頭に以下の1行を追加すると、メッセージが表示されなくなります。

ERRORLEVEL      -302





Expected (END)


メッセージ例


Error[129]  TEST.ASM 99 : Expected (END)




内容

プログラムの最後にEND擬似命令が有りません

対処方法

ファイルの最後にEND命令を追加します



Found label after column 1. (EQU命令で発生)


メッセージ例


Warning[207] C:\HOME\PROJECT\TEST\TEST.ASM 11 : Found label after column 1. (LOOP_CNT)



内容

以下のようにEQU命令を使用した際に、行の頭にスペース(あるいはタブ)が入っています。

	LOOP_CNT	EQU	0x20


MPASMはEQU命令で使用するラベルは1カラム目から始まる事を想定しているため、行頭にスペースがあると上記の警告が表示されます。


対処


以下のように先頭のスペースを取り除きます

LOOP_CNT	EQU	0x20





Found directive in column 1. (xxxx)


メッセージ例


Warning[205] TEST.ASM 99 : Found directive in column 1. (ERRORLEVEL)




内容

以下のようにディレクティブの指定文が、1カラム目から始まっています

ERRORLEVEL      -302



MPASMに対するディレクティブ(指示)は、タブまたはスペースによるインデントがされている事を期待しているため、上記警告が表示されます。


対処

行頭にタブかスペースを入れ、インデントします。

	ERRORLEVEL      -302





Found opcode in column 1. (xxxx)


メッセージ例


Warning[203] TEST.ASM 29 : Found opcode in column 1. (CLRF)




内容

アセンブラの命令(ニーモニック)が,以下のように行頭から始まっています。
(理由の詳細は、前述のWarning[205]も参考にしてください)

CLRF	TRISA




対処

行頭にタブかスペースを入れ、インデントします。

	CLRF	TRISA






良く分からないエラーが出たときにすべき事


初めての開発環境を使用してプログラムを作成していると、分からないエラーメッセージが表示されて、プログラムのコンパイルが正しく行えない場合が良く有ります。

このような場合は、以下の3点に気をつけてチェックを行うと比較的スムーズにエラーを取り除く事が出来ます。


1.OUTPUTウィンドウの内容(コンパイル結果のエラーメッセージ)をよく読む

プログラム初心者の場合、MPLABの開発環境ではエラーメッセージが英語で表示されるため、英語が苦手な人はメッセージを読まずに、エラーの箇所を調査しようとしてしまうことが多いです。

ですが、エラーメッセージは簡単な英語の場合が多いので、落ち着いてメッセージを読んでみると意外と意味が分かる場合も多いです。分からない単語がある場合も、都度google検索などで単語を調べておくとよいです。今後同様のエラーが出た場合に調査が速くなるし、英単語の勉強にもなります。


2.エラーが出ている行を確認する

たいていのコンパイラ(アセンブラ)では、エラーが発生した際エラーメッセージと共に、エラーが発生した行が表示されています。

例えば以下のメッセージの場合、問題が29行目で発生している事がわかります。(MPLABでは親切な事にエラーメッセージをダブルクリックすると、ソースコードウィンドウの該当行にジャンプしてくれます)

Warning[203] TEST.ASM 29 : Found opcode in column 1. (CLRF)



たいていのエラーはここで提示された行か、そのすぐ上の行でエラーが発生している事が多いので、そこを重点的にチェックしてみましょう。


3.エラーメッセージを丸々コピーしてgoogleで検索する

あなたが遭遇したエラーは、大抵の場合他の人も起こした事があるミスです。
先人の方たちは、そのときの事をblogなどに残してくれていたりする事も多いので、エラーメッセージをそのままgoogle検索してみると、解決方法が載っている場合も多いです。

この際に注意するのは、何をキーワードにして検索するかです。
例えば、下記のメッセージが出た場合…

Warning[203] TEST.ASM 29 : Found opcode in column 1. (CLRF)



先頭の”Warning[203]”にある、203は各エラーに対して割り振られた固有のエラーコードである可能性が高いので有力な情報です。
次の”TEST.ASM 29″は、ファイル名と行番号を示しています。ファイル名に何をつけるかは人によって異なりますし、行番号はこれもプログラムによって異なるので、この情報は、google検索を行う上ではあまり役立ちません。
最後の”Found opcode in column 1. “はエラーメッセージなのでこれも有力な情報です。

なので、検索を行う場合は「MPASM Warning[203]」や、「MPASM “Found opcode in column 1. “」、「MPASM “Warning[203]” “Found opcode in column 1. “」あたりをキーワードにしてみると、役に立つ情報に当たる事が多いです。



[PIC]MPLABでデバッグ時に,入力用ポートの値を操作する(Stimulusウィンドウ)

MPLABでは実際のマイコンチップを用意しなくても、IDE上に用意されたシミュレータを使用してプログラムをデバッグする事ができます。

また、PICマイコンでは入出力ピンを使用して外部とのデータをやり取りを行います。

この際に出力ピンについては、PORTAやPORTBなどのレジスタ値を見れば0/1のどちらが出力されているか、確認する事ができます。一方、入力については、実機上はボタンなどを用意すれば入力のON/OFFを変更できますが、シミュレータ上ではボタンが無いのでピンの状態を変更する事が出来ません。

このようなときはStimulus機能を使用することで、ピン状態を変更する事が可能です。
Stimulusというのは、外部からシステムへの入力という意味ですが、辞書的には”刺激”,”誘因”,”動機”といった意味もあるようです。




Stimulus機能の使い方は、以下の通りです。

DebuggerメニューよりStimulus->New Workbookをクリックします。


Stimulusウィンドウが表示されます。
たくさんのタブがありますが、まずはAsynchタブで操作を行いうのが基本です。



Pin/FSRをクリックして、どのピンのON/OFFを操作したいかを指定します。


次にActionを指定します。今回はToggleで確認します。



プログラムをステップ実行し、ピンのON/OFFを操作したいタイミングになったら、Fireの列にある”>”をクリックします(ボタンになっています)。
その後Step Intoで、もう1ステップ進めると入力ピンの状態が変わります。
今回の例では、PortBの最下位ビットが反転されます。



Fireの列の”>”をクリックしてStep Intoを押すたびに、値が交互に切り替わります。

  ↓ ↑ 交互に変わる


これで、Fireのボタンがトグルスイッチとみなされる様になりました。
Fireのボタンを押した後に、Step Intoなどで1命令以上、プログラムを進めることを忘れないようにしてください(これはピンのI/O状態がレジスタに取り込まれるまで、1クロックサイクル必要なためです)。


また、ボタンを押したときの動作はトグル以外にも色々指定できます。

上記の例では、順に”常にOnにする”, “常にOffにする”, “一定時間だけOnにする(10命令分)”, “一定時間だけOnにする(20mSec分)”の定義をしています。
時間指定の場合は、実時間ではなくデバッグ環境下の仮想時間がベースなので、1Secなどを指定したうえで1サイクルづつステップ実行を行ってしまうと、ものすごい時間が掛かってしまうため注意が必要です。