2012年05月28日 01:20
(ブログには書いていないが)今までPICで色々作ってきたが、ライタ(書き込み機)にはずっと自作したRCDライタを使っていた。
RCDライタというのは、詳しくは作者のページ(http://feng3.nobody.jp/rcd/)を見るとよいが、部品代数百円で作れる非常に財布に優しい物である。
しかしこれには欠点があって、最近のPICには書き込めない。
PICのプログラミング(書き込み)時には、プログラミングモードに移行させる指令として高電圧を掛けるのだが、この電圧が昔のものは12V前後なのだが最近の石では9Vになっている。
RCDライタではこの電圧が固定なのでそのまま使うと過電圧で石を壊してしまう。
また、少し前から大抵の石はLVP(Low Voltage Programming; 低電圧書き込み)モードがあるのだが、これもRCDライタは対応していない。
まあ新しい石を使わなければいい話なのでさほど気にせずやってきたのだが、この前衝動買いしたPIC10F322という石が9Vタイプであった。
折角買ったのに使えないのは癪なので、最近の9Vタイプにも書き込めるような書き込み機の情報を探そうかと思ったのだが、どうせなら自分で作った方が楽しいという結論に達した。
というわけで、PICライタを設計してみようかと思う。
作るにあたって、まずは書き込み方法の資料を入手する。
これはデバイスのファミリーごとに「PIC10(L)F320/322 Flash Memory Programming Specification」のような名前で公開されている。
なおデバイスごとに微妙に受け付けるコマンドが違ったりもするが書き込み手順自体は基本的にはみな同じである。
まずプログラミングモードに移行させて(このやり方は数種ある)から、クロックとデータの2本の線でシリアル通信をするという仕組みで、意外と簡単そうである。
今回まずは書き込みの実証程度のプログラムを作ってみた。
使った石は(書き込み側も書き込まれ側も)PIC16F628A。PICを始めるときに18ピンで安いものを選んで買った物だが、無難に色々機能があって使いやすいので以来ずっと愛用している。先日1番ピンが折れたがまだ使える。
何気にLVP(旧式)対応なので今回のテストに最適だ。
LVPなら面倒な高電圧を用意する必要が無いのと、新式のLVPはプログラミングモードに移行させるためにコマンド入力が必要なのだが旧式はピンを1本余計に使う代わりにこの面倒が無い。
事前にRCDライタでLVPをオンに設定して、ついでにプログラムメモリは全消去しておく。
書き込む内容はプログラム中にハードコーディングしておき、プログラムでは書き込みのみを行う。
プログラミングモードに入る手順を勘違い(※)してちょっとはまったりもしたが、まあそれなりにスムーズに成功した。
※手順は、「電源を印加」→「PGMピンをH」→「リセットを解除」なのだが、最後リセットをONにするのだと勘違いしていた。
書き込み風景はこんな感じ。

右が書き込み側、左が書き込まれ側。
上の抵抗2本がPGMと/MCLR、下の赤い線2本がPGD(データ)とPDC(クロック)に繋がっている。
LEDは書き込み信号が出力されているか確かめるために付けたのだが、外さなくても書き込めたのでそのままになっている。
書き込み用のコードはこんな感じ。
あまり人に見せるコードではないが、書き込めて嬉しいので勢いで公開してしまう。
このソースも一応書いておく。全てのI/Oピンから交互にH/Lを出力する。適当なピンとピンの間にLEDを挿してチェックできる。
なんとなく短さに挑戦してみた。同じ条件でこれ以上縮められる気はあまりしない。
これからの予定:
・PCのシリアルポートからデータを受け取って書く
・書き込み用高電圧を昇圧回路で作る
・USBに対応
・既存のライタとインターフェースを合わせる
RCDライタというのは、詳しくは作者のページ(http://feng3.nobody.jp/rcd/)を見るとよいが、部品代数百円で作れる非常に財布に優しい物である。
しかしこれには欠点があって、最近のPICには書き込めない。
PICのプログラミング(書き込み)時には、プログラミングモードに移行させる指令として高電圧を掛けるのだが、この電圧が昔のものは12V前後なのだが最近の石では9Vになっている。
RCDライタではこの電圧が固定なのでそのまま使うと過電圧で石を壊してしまう。
また、少し前から大抵の石はLVP(Low Voltage Programming; 低電圧書き込み)モードがあるのだが、これもRCDライタは対応していない。
まあ新しい石を使わなければいい話なのでさほど気にせずやってきたのだが、この前衝動買いしたPIC10F322という石が9Vタイプであった。
折角買ったのに使えないのは癪なので、最近の9Vタイプにも書き込めるような書き込み機の情報を探そうかと思ったのだが、どうせなら自分で作った方が楽しいという結論に達した。
というわけで、PICライタを設計してみようかと思う。
作るにあたって、まずは書き込み方法の資料を入手する。
これはデバイスのファミリーごとに「PIC10(L)F320/322 Flash Memory Programming Specification」のような名前で公開されている。
なおデバイスごとに微妙に受け付けるコマンドが違ったりもするが書き込み手順自体は基本的にはみな同じである。
まずプログラミングモードに移行させて(このやり方は数種ある)から、クロックとデータの2本の線でシリアル通信をするという仕組みで、意外と簡単そうである。
今回まずは書き込みの実証程度のプログラムを作ってみた。
使った石は(書き込み側も書き込まれ側も)PIC16F628A。PICを始めるときに18ピンで安いものを選んで買った物だが、無難に色々機能があって使いやすいので以来ずっと愛用している。先日1番ピンが折れたがまだ使える。
何気にLVP(旧式)対応なので今回のテストに最適だ。
LVPなら面倒な高電圧を用意する必要が無いのと、新式のLVPはプログラミングモードに移行させるためにコマンド入力が必要なのだが旧式はピンを1本余計に使う代わりにこの面倒が無い。
事前にRCDライタでLVPをオンに設定して、ついでにプログラムメモリは全消去しておく。
書き込む内容はプログラム中にハードコーディングしておき、プログラムでは書き込みのみを行う。
プログラミングモードに入る手順を勘違い(※)してちょっとはまったりもしたが、まあそれなりにスムーズに成功した。
※手順は、「電源を印加」→「PGMピンをH」→「リセットを解除」なのだが、最後リセットをONにするのだと勘違いしていた。
書き込み風景はこんな感じ。

右が書き込み側、左が書き込まれ側。
上の抵抗2本がPGMと/MCLR、下の赤い線2本がPGD(データ)とPDC(クロック)に繋がっている。
LEDは書き込み信号が出力されているか確かめるために付けたのだが、外さなくても書き込めたのでそのままになっている。
書き込み用のコードはこんな感じ。
あまり人に見せるコードではないが、書き込めて嬉しいので勢いで公開してしまう。
list p=16f628a最後の32バイトのデータが書き込んだプログラム。
#include p16f628a.inc
variable env
; RA2+-v-+RA1
; RA3| |RA0
; RA4| |RA7
; RA5| |RA6
; Vss| |Vdd
; RB0| |RB7
; RB1| |RB6
; RB2| |RB5
; RB3+---+RB4
env = _BOREN_OFF
env &= _CP_OFF
env &= _DATA_CP_OFF
env &= _PWRTE_OFF
env &= _WDT_OFF
env &= _LVP_OFF
env &= _MCLRE_OFF
env &= _INTOSC_OSC_NOCLKOUT
__config env
RAMSTART equ 0x20
clk equ 0
dat equ 1
LOAD_CONF equ b'00000000'
LOAD_PROG equ b'00001000'
LOAD_DATA equ b'00001100'
INC_ADDR equ b'00011000'
READ_PROG equ b'00010000'
READ_DATA equ b'00010100'
BEGIN_PROG equ b'00100000'
ERASE_PROG equ b'00100100'
ERASE_DATA equ b'00101100'
cblock RAMSTART
count
count2
pgmcount
iobuf
clkbuf
datbuf
ptr
temp
freeram
endc
;
org 0x00
init
clrf PORTB
bsf STATUS,RP0
clrf TRISB ; PORTB : all output
clrf TRISA ; PORTA : all output
clrf STATUS
decf CMCON,F ; Comparator Off
movlw 0x10
movwf pgmcount
clrf count2
waitloop1 ;なんとなく待つ
call wait500us
call wait500us
call wait500us
call wait500us
decfsz count2
goto waitloop1
clrf iobuf ;PGMピンをHに
bsf iobuf,7
movf iobuf,W
movwf PORTB
nop ;5usでいいがなんとなく余計に待つ
nop
nop
nop
bsf iobuf,6 ;/MCLRピンをHに
movf iobuf,W
movwf PORTB
clrf ptr
pgmloop
;ロード
movlw freeram
movwf FSR
;load for program memoryコマンドをセット
movlw LOAD_PROG
movwf INDF
incf FSR,F
movlw 0xFC
movwf INDF
incf FSR,F
;プログラムデータをセット
call setdata
incf ptr
movlw freeram-1
movwf FSR
movlw d'24'
call transmit
;プログラム
movlw freeram
movwf FSR
;begin programコマンドをセット
movlw BEGIN_PROG
movwf INDF
incf FSR,F
movlw 0xFC
movwf INDF
incf FSR,F
movlw freeram-1
movwf FSR
movlw d'8'
call transmit
;待ち。ループ書くのめんどくさい。
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
call wait500us
;アドレス++
movlw freeram
movwf FSR
;inc addressコマンドをセット
movlw INC_ADDR
movwf INDF
incf FSR,F
movlw 0xFC
movwf INDF
incf FSR,F
movlw freeram-1
movwf FSR
movlw d'8'
call transmit
decfsz pgmcount
goto pgmloop
goto $
transmit
movwf count
transmitloop
movlw 0x07
andwf count,W
btfss STATUS,Z
goto noadvance
incf FSR
movf INDF,W
movwf datbuf
incf FSR
movf INDF,W
movwf clkbuf
noadvance
;clkをLに
bcf iobuf,clk
movf iobuf,W
movwf PORTB
;データ準備
rrf datbuf,F
bcf iobuf,dat
btfsc STATUS,C
bsf iobuf,dat
rrf clkbuf,F
btfsc STATUS,C
bsf iobuf,clk
;出力
movf iobuf,W
movwf PORTB
decfsz count,F
goto transmitloop
;clkをLに
bcf iobuf,clk
movf iobuf,W
movwf PORTB
return
;;;;
setdata
;プログラムデータをセット
movf ptr,W
call readtable
movwf INDF
rlf INDF,F
rlf temp,F ;Cを保存
incf FSR,F
movlw 0xFF
movwf INDF
incf FSR,F
incf ptr
movf ptr,W
call readtable
movwf INDF
rrf temp,F ;Cを取り出し
rlf INDF,F
incf FSR,F
movlw 0xFF
movwf INDF
return
;;;;
;0.5ms待つ
wait500us
movlw d'165'
movwf count
loop500us
decfsz count,F
goto loop500us
return
readtable
addwf PCL
programtable
dt 0x83, 0x16, 0x86, 0x01, 0x85, 0x01, 0x83, 0x01, 0x9F, 0x03, 0x55, 0x30, 0xFF, 0x3A, 0x85, 0x00
dt 0x86, 0x00, 0xA0, 0x01, 0xA1, 0x01, 0xA1, 0x0B, 0x0B, 0x28, 0xA0, 0x0B, 0x0A, 0x28, 0x06, 0x28
dt 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;何か間違ったときのために少し余計にデータを置いておく
dt 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
end
このソースも一応書いておく。全てのI/Oピンから交互にH/Lを出力する。適当なピンとピンの間にLEDを挿してチェックできる。
なんとなく短さに挑戦してみた。同じ条件でこれ以上縮められる気はあまりしない。
(宣言とか略)
org 0x00
init
bsf STATUS,RP0
clrf TRISB ; PORTB : all output
clrf TRISA ; PORTA : all output
clrf STATUS
decf CMCON,F ; Comparator Off
movlw 0x55
loop
xorlw 0xFF
movwf PORTA
movwf PORTB
clrf count1
wait1
clrf count2
wait2
decfsz count2
goto wait2
decfsz count1
goto wait1
goto loop
これからの予定:
・PCのシリアルポートからデータを受け取って書く
・書き込み用高電圧を昇圧回路で作る
・USBに対応
・既存のライタとインターフェースを合わせる
タグ :マイコン
Post time :
2012年05月28日 01:20
│Comments(0)
2012年05月14日 00:08
ふとPICで浮動小数点数を扱いたくなったのでアルゴリズムを作ってみた。
もっとも、浮動小数点数というと語弊があるかもしれない。そんな大層なものではない。
ちょっとテーブルを作りたかったのだが、そのまま作ると1個当たり2バイトの容量になってしまうのでそれを1バイトに圧縮したというだけである。
なので、これで演算をすることは考えていない。また、PIC上でエンコードすることも考えていない。
目標はこんな感じである。
・1バイトの浮動小数点数。
・表す値は2バイト。
・デコードをなるべく単純に。エンコードのことは知らん。
これで作ってみたら以下のようになった。
まず仕様はこんな感じ。
ただしエンコードを軽視したために指数部のビットパターンが少々複雑になった。ビットパターンは次のとおりである。
・上4bitが仮数部 (ケチ表現なので5bitの値の下4bit)
・下4bitは上から順に、指数部の3bit目、1bit目、0bit目、2bit目
なお、普通の順にしてもデコードの途中で1回シフトすればいいだけなのでこんな変なことをすることもなかったかもしれない。まあいいや。
他にもいくつか問題がある。
両端の値が表せない。0, 1, 65535は表したいところである。←0は表せました。
指数部を普通の順にしたとして、指数部と仮数部の順が普通と逆なので、数値が順に並ばない。
仮数部・指数部4bitづつでは普通20bitの範囲が表せるがそれを16bitに制限したため、無駄が多い。
256個の値のうち有効なものは206207個しかない。
直そうと思えば直せるものもあると思うが、今回の目標としてデコードを単純にするというのがあるので、気にしないことにする。
デコード用プログラムは以下。33ワード。最大35サイクル。
全ての有効な値を順に並べると以下のとおり。
見て分かるように順序が分かりづらい。
ここに書かれていない値を入れた場合どうなるかは試していないが、他と全く同じかほぼ同じ値になるはずである。暴走は多分しない。
余談。
このプログラムを作るにあたって1ヶ所はまった所がある。
最後の0~3回シフトする所なのだが、ここで有効なbitが最上位の4bitだった場合にケチ表現の1bitを入れるのを忘れていた。
キャリーに入れればいいのはわりとすぐ気づいたのだが、ここで問題が起こった。
最初のコードはこんな感じだった。
なので今回も相対ジャンプのために何も考えずに使っていたのだが、これではプログラムカウンタに加算するときにキャリーが破壊されてしまうので、今回のようにキャリーを使いたい時に困る。
破壊されるなら別の場所に一時保存しておけばいいかと思ったが、相対ジャンプの直後に使うことになるので不可能である。
これはどうしようもないかなと思ったときに思いついたのが上記のコードである。ジャンプ先のアドレスを先に計算しておけばよかったのだ。
PICを使っているとどうもこういうことが多い。欲しい機能が無くてアルゴリズムが実装できないかと思うと、よく考えてみると他の機能で代替できる。
PICのアーキテクチャはよく考えられているのだなあと思う。
-----
5/15 訂正
0は表せないと思っていたが表せたので各所を訂正。
もっとも、浮動小数点数というと語弊があるかもしれない。そんな大層なものではない。
ちょっとテーブルを作りたかったのだが、そのまま作ると1個当たり2バイトの容量になってしまうのでそれを1バイトに圧縮したというだけである。
なので、これで演算をすることは考えていない。また、PIC上でエンコードすることも考えていない。
目標はこんな感じである。
・1バイトの浮動小数点数。
・表す値は2バイト。
・デコードをなるべく単純に。エンコードのことは知らん。
これで作ってみたら以下のようになった。
まず仕様はこんな感じ。
仮数部 | : | 4bit (ケチ表現で実質5bit) |
指数部 | : | 4bit |
符号 | : | なし |
最小値 | : | |
最大値 | : | 63488 (0xF800) |
ただしエンコードを軽視したために指数部のビットパターンが少々複雑になった。ビットパターンは次のとおりである。
・上4bitが仮数部 (ケチ表現なので5bitの値の下4bit)
・下4bitは上から順に、指数部の3bit目、1bit目、0bit目、2bit目
なお、普通の順にしてもデコードの途中で1回シフトすればいいだけなのでこんな変なことをすることもなかったかもしれない。まあいいや。
他にもいくつか問題がある。
指数部を普通の順にしたとして、指数部と仮数部の順が普通と逆なので、数値が順に並ばない。
仮数部・指数部4bitづつでは普通20bitの範囲が表せるがそれを16bitに制限したため、無駄が多い。
256個の値のうち有効なものは
直そうと思えば直せるものもあると思うが、今回の目標としてデコードを単純にするというのがあるので、気にしないことにする。
デコード用プログラムは以下。33ワード。最大35サイクル。
;使用するレジスタ
;ansh: デコードされた値(上位)
;ansl: 〃(下位)
;exp: 指数
;exp10: 指数の0,1bit目
;使い方
;Wに浮動小数点数を入れてCALL
;<ansh:ansl>にデコードされた値が入る
decodefloat
;各種値をセット
clrf ansh
movwf ansl
andlw 0x0F
movwf exp
andlw 0x06
movwf exp10
movlw 0xF0
andwf ansl,F
;exp: 0000nnnn
;exp10: 00000nn0
;ansh:ansl: 00000000 nnnn0000
;指数がx0xxならば4bitずらす
;同時に、仮数の5bit目を立てる
btfsc exp,0 ;指数<2>
goto shift4end ;if x0xx, then do
swapf ansl,F
bsf ansl,4
btfsc exp,0
shift4end
bsf ansh,0 ;if x1xx
;指数 ansh ansl
;x1xx: 00000001 nnnn0000
;x0xx: 00000000 0001nnnn
;指数が1xxxなら8bitずらす
;同時に、指数が11xxなら仮数の5bit目としてtempフラグを立てる
;tempフラグは空いているexp<7>を流用
btfss exp,3
goto shift8end ;if 1xxx, then do
movf ansl,W
movwf ansh
clrf ansl
btfsc exp,0 ;指数<2>
bsf exp,7 ;if 11xx, then do
shift8end
;11xx: nnnn0000 00000000, temp=1
;10xx: 0001nnnn 00000000, temp=0
;01xx: 00000001 nnnn0000, temp=0
;00xx: 00000000 0001nnnn, temp=0
;指数<1:0>に応じて右シフト
movlw low(jumptarget) ;ジャンプ先アドレスをWにセット
addwf exp10,W ;。
btfsc exp,7 ;tempフラグをCにセット
bsf STATUS,C ;。
movwf PCL ;exp<2:1>の値に応じてジャンプ
;ここからページ境界不可→
jumptarget
rrf ansh,F ;if 0 then 3回シフト
rrf ansl,F
rrf ansh,F ;if 1 then 2回シフト
rrf ansl,F
rrf ansh,F ;if 2 then 1回シフト
rrf ansl,F
return ;if 3 then シフトしない
;→ここまで
全ての有効な値を順に並べると以下のとおり。
見て分かるように順序が分かりづらい。
ここに書かれていない値を入れた場合どうなるかは試していないが、他と全く同じかほぼ同じ値になるはずである。暴走は多分しない。
DT 0x0F, 0x00, 0x80, 0x02, 0x42, 0x82, 0xC2, 0x04 (5/15 0x0F(0に相当)を追加)
DT 0x24, 0x44, 0x64, 0x84, 0xA4, 0xC4, 0xE4, 0x06
DT 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86
DT 0x96, 0xA6, 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x01
DT 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81
DT 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0x03
DT 0x13, 0x23, 0x33, 0x43, 0x53, 0x63, 0x73, 0x83
DT 0x93, 0xA3, 0xB3, 0xC3, 0xD3, 0xE3, 0xF3, 0x05
DT 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85
DT 0x95, 0xA5, 0xB5, 0xC5, 0xD5, 0xE5, 0xF5, 0x07
DT 0x17, 0x27, 0x37, 0x47, 0x57, 0x67, 0x77, 0x87
DT 0x97, 0xA7, 0xB7, 0xC7, 0xD7, 0xE7, 0xF7, 0x08
DT 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, 0x88
DT 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, 0x0A
DT 0x1A, 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A
DT 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, 0xFA, 0x0C
DT 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C
DT 0x9C, 0xAC, 0xBC, 0xCC, 0xDC, 0xEC, 0xFC, 0x0E
DT 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, 0x6E, 0x7E, 0x8E
DT 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE, 0x09
DT 0x19, 0x29, 0x39, 0x49, 0x59, 0x69, 0x79, 0x89
DT 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, 0x0B
DT 0x1B, 0x2B, 0x3B, 0x4B, 0x5B, 0x6B, 0x7B, 0x8B
DT 0x9B, 0xAB, 0xBB, 0xCB, 0xDB, 0xEB, 0xFB, 0x0D
DT 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D
DT 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD
余談。
このプログラムを作るにあたって1ヶ所はまった所がある。
最後の0~3回シフトする所なのだが、ここで有効なbitが最上位の4bitだった場合にケチ表現の1bitを入れるのを忘れていた。
キャリーに入れればいいのはわりとすぐ気づいたのだが、ここで問題が起こった。
最初のコードはこんな感じだった。
movf exp10,W ;指数<1:0>をワーキングレジスタにセットプログラムカウンタに値を加算するのはPIC特有のイディオムで、PICには変数でジャンプする命令が無い代わりにプログラムカウンタに対して演算する設計になっている。
addwf PCL ;プログラムカウンタに加算
rrf ansh,F
rrf ansl,F
rrf ansh,F
rrf ansl,F
rrf ansh,F
rrf ansl,F
return
なので今回も相対ジャンプのために何も考えずに使っていたのだが、これではプログラムカウンタに加算するときにキャリーが破壊されてしまうので、今回のようにキャリーを使いたい時に困る。
破壊されるなら別の場所に一時保存しておけばいいかと思ったが、相対ジャンプの直後に使うことになるので不可能である。
これはどうしようもないかなと思ったときに思いついたのが上記のコードである。ジャンプ先のアドレスを先に計算しておけばよかったのだ。
PICを使っているとどうもこういうことが多い。欲しい機能が無くてアルゴリズムが実装できないかと思うと、よく考えてみると他の機能で代替できる。
PICのアーキテクチャはよく考えられているのだなあと思う。
-----
5/15 訂正
0は表せないと思っていたが表せたので各所を訂正。
Post time :
2012年05月14日 00:08
│Comments(0)