2014年2月20日木曜日

SDCC for PIC Tips

PIC18F14K50でUSBデバイスを作るのにSDCCを使ってみた。PIC18F2550系とは互換性がないという事前情報だったけど、レジスタ並びやUSB用RAMのアドレスが違うくらいで、基本的にUSB周りのコードはほぼそのまま動きそう。

むしろハマったのはSDCC周辺。このあたり、やっぱり非公式ツールチェインは辛い。大きくハマったのは次の2点。

  1. bootloaderを使おうとすると色々細工が必要で、信頼できるコードが書けない
  2. USB用RAMのポインタをアドレスから普通に作ると動かない
bootloaderについて、まずtext領域を後ろ(0x800以降)にずらすために専用のリンカスクリプトを用意する必要がある。コンパイラオプションにもそれっぽい--code-locというのがあるが、指定しても動かなかった。また、割り込みベクタはbootloader側がトラップして後ろのアドレスにフォワードしてくれるので、後ろのずれた位置に割り込みベクタを配置する必要がある。こっちはコンパイラオプション--ivt-loc=0x800が正しく動く。また、crtが通常の配置を想定したコード(割り込み周りかな?)を持っているようで、ふとしたタイミングで暴走する。stdio/stdlib使ってなくても、例えばcharをやめてintでforループを回すと暴走、とか悩ましい問題が普通に起きる。海外サイトでcrtを同じフラグでコンパイルし直せば安定する、という話も見かけたけど、どこまで信じていいかわからない状態で実験を続けたくなかったので、bootloaderは諦めた。回路側でバランスをとったら、なんとかICSP指しっぱなしで書き込みしつつ、USBバスも安定ドライブできた。

USB用RAMについてはコンパイラの問題とは思わずに長時間悩んだ。いわゆるBuffer Descriptor Tableを構成するコード。コンパイラの仕様が変わったのか、バグなのか、2550の時のコードからアドレス変えただけでは動かなかった。具体的にはstruct bd* bdt = (struct bd*)0x0200;的な事をやってアドレスを作ってたんだけど、今回はこのポインタで読み書きしても値が変わっていなかった。起動すれば一応USBはしゃべるんだけど。アドレスリセットとパワーセーブ関連の割り込みだけ届いて、その後はだんまり。トランザクションが届かないってことはエンドポイントが正しく初期化できてないのかなぁ・・・というあたりから問題の目星をつけた。結局コンパイル結果を眺めながらの間違い探し。原因が判明してからは、正しい出力を出せそうな表記を勘を頼りに探すお仕事。通らないと承知で間違ったキャストをコンパイラにかけると、内部的な秘密の型がエラーメッセージを通して推測できるのでオススメ。
__at(0x0200) struct bd BD0;static struct bd __near* const bdt = &BD0;
あと、やっぱり配列でアクセスしたくなるUEP0, UEP1, ...なんかもchar *uep = (char*)&UEP0; uep[0] = ...とか書くと正しくアクセスできない。
static __sfr* uep = &UEP0;static __UEP0bits_t* uepbits = &UEP0bits;uep[0] = 0;uepbits[0].EPHSHK = 1;
って感じの__sfr*という特殊な指定が必要。特定用途レジスタの略だと思う。こっちはシステムヘッダを眺めて予想。

2014年2月3日月曜日

HIDじゃないSPX

とりあえず手元のAKI-PICで焼けるPIC18F2550で作ったpic18spx(オリジナルそのまま版)と、それを使ってPIC18F14K50に焼いて作った非HID化改造版pic18spx。
非HID化によりOS Xでも動くようになりました。あと、次にbuildしようとした時にbuildできる環境が作れる気がしないのでhexをウェブ上に置きました。必要になったらここからダウンロードして焼くべし>未来の自分。

あと、昔tcl/tkで書いたhidspx用のGUIが昔の記事にあります。開発中はもっぱらmakeを叩くんだけど、デバイスを確認する時とか、FUSEの値を確認する時、今でも使ってます。

しかし、環境乗り換えの時間的コストは高いな、やっぱり。

2014年2月2日日曜日

pic18spx/mac

HIDクラスを使ってるとOS Xで標準ドライバから制御が奪えない問題。HIDをやめれば解決するかな、と思って試したら解決した。具体的にはUSBデスクリプタのインタフェースクラスをHIDからベンダ固有値0xffに変えるだけ。picmonとか特に修正しなくても、そのまま利用できる。

むしろpic18spxのファームのbuild環境を整えるのに苦戦した。mcc18向けに書かれてるんだけど、現在はmcc18は配布されておらずMPLABXとXC8が標準。いっそXC8かSDCCに移植しようかと思ったけど、そこまで時間かけても仕方ないかな、と思って大昔のディスクを漁ってmcc18をインストール。持ってたのはMPLAB-C18-Academic-v3_30.exeとかいう古めのバージョンだったので、そのままではリンクできない。とりあえず$(TARGET).cofのビルドルールを以下のように変えて凌いだ。

$(LD) /l$(MCC18)/lib /k$(MCC18)/lkr rm$(DEVICE).lkr $(OBJS) \
/u_CRUNTIME /z__MPLAB_BUILD=1 /m$(TARGET).map /o$(TARGET).cof

とりあえず、自分のファーム作るときは最初からSDCC考えた方が良さそうだなぁ・・・。

2014年1月29日水曜日

18F2550 -> 18F14K50

本日14K50が届いたので準備の続き。先日ブレッドボードで組んだpic18spxはリセットボタンを付けて基板に載せました。部品点数少なすぎて基板がスカスカ。配線ガシャガシャしててもUSB側は安定してるんでリセットボタンいらなかったかも。書き込み用のピンは手元の機材ですぐに実験できるAVR用の2×3タイプのみ。

右が2550。TINY2313の読み出しを確認した後、左の14K50を使った2台目の書き込みに。書き込みにはpic18spx-2010-0416に付属のpicwriter.exeを利用。hidspxでいけるもんだと思い込んでて-rが成功せずにしばらく悩んだ。

新たにブレッドボードで動作確認。リセットをプルアップしてる以外はクロックと配線のみの超手抜き状態。ですが、安定動作してます。水晶は秋月で28.6円のSG531PAP。水晶のOUTをCLKINに繋げてCLKOUTは開放。設定変更くらい必要かと思ったけど、そのままで動いてる。こっちは基板では2×3と1×6両方付けるつもり。
と、ここまででようやく開発の準備が完了です。
そう言えば、フルスピードになると我が家のオシロでは対応できないので、ハード側でハマると死にそうです。

2014年1月26日日曜日

pic18spx

手持ちのPICライター、AKI-PICではPIC18F14K50に書き込めないっぽいので対策。嬉しい事にさくっと見つけたpic18spxが需要を満たしてくれそう。個人的に嬉しいポイントは

  • Linux / OS Xからも使える
  • PIC18F2550とPIC18F14K50に対応してる
  • ライターもツールもhidspxの上位互換として動く(AVRにも書き込める)
  • PIC18F2550と20MHz水晶だけで作れるので、手持ち在庫とAKI-PICで作れる
ってところ。

回路

ざっくりオフィシャル通りでこんな感じ。
追記:RC2をpull upしてるけど標準hexではBoot Loaderは殺されてるので不要

Linux / OS Xでの利用(ややハマりポイントあり)

picmon/picbootは関してはオフィシャルページのpic18spx-2010-0416.zipを展開すればWindows用のバイナリと一緒にソースが入ってる。picspxは別途配布されているpic18spx-linux.zipを使うこと。
Linuxでは中で呼んでるusb_claim_interface()にroot権限が必要なため、sudoしないと駄目。オフィシャルページ読めば書いてあるけど見落としてた。hidspxではroot権限不要だったと思うんだけど・・・まじめに調べてないけど、単純にhidspxはcontrol転送しか使ってないんで標準HIDドライバが不審に思って掴まなかっただけな気がする。
同じ理由でOS Xでもusb_claim_interface()が失敗。こっちはrootでも駄目。usb_detach_kernel_driver_np()を試しても失敗するのでusb_strerror()を読んでみたら「function not implemented」を返してきた。もう少し検索してみるとこんなスレッド発見。要は標準ドライバ避けに専用kernel driver作ってOSを再起動するしか方法はないらしい。まぁHIDはセキュリティ的にも際どいし、わからなくもない。
よくよく考えるとWindows使わない身としてはHIDのフリをさせるメリットもないので、独自クラスにデスクリプタ書き換えたfirmwareを作ることで解決するつもり。そうしとけば、その気になればUSB API使ってChromeからも書き込めるし。

そのほかハマった点

前エントリの通り、最新版のAKI-PICを試そうとして危うくVM上のWin XPを再起不能にしかけた。しかも試し損。加えてVMのUSBブリッジが不安定。結局、唯一稼働してる実機Windows、Win7/LOOX U50WNを引っ張りだしてPIC18F2550を焼いた。ライターにhidspxを使ってPICに書けるPICspxなんてのあったけど、うちの環境で読み込んでみたら不定値が返ってくる。ライター側のバージョンでAPIが微妙に変わってそうなので、深入りせずに過去に実績のあるAKI-PICに逃げた。これがAKI-PIC最後の出番かも。
追記:USBブリッジの問題はホスト側で/etc/udev/rules.d/40-vmware.rulesみたいなのを以下の内容で作ってやったら解決した。USBデバイスのパーミッションが常に全開放になる点だけは注意。vmware server 2.0/Ubuntu 10.04.4 LTSの組み合わせ(この組み合わせもいい加減古い)。
SUBSYSTEM=="usb", ENV=="usb_device", MODE="0666"
SUBSYSTEM=="usb_device", MODE="0666"

関係ないけど驚いたこと

vimでhexファイル(PICのa.out的なもの)を開いたらレコードごとに色分けして表示してくれた。超便利。これ見ながらpicmonで期待してるデータが読めるか確認。ピンクが長さ、青がアドレス、赤がタイプ、無印がデータ、最後の黄色バックがチェックサムです。

2014年1月25日土曜日

AKIPIC PICPGM6β6.76でハマった

最新版のデバイス対応状況を確認しようとしてカジュアルにアップデートかけたらOSが起動しなくなった。一番悪いのは僕。なにせ使ってたのはXP、アハハ。

問題は単純で、システムのOLEAUT32.DLLを上書きされちゃう。Vista以降でしか動かないバージョンで。エラーメッセージ的にはMSVCRT.DLLにstrcat_sのエントリが見つからない、といってexplorerも何も動かなくなっちゃうんだけど、実際にはエントリがないのがXP版としては正しくて。Vista以降のMSVCRT.DLLに依存を持ったOLEAUT32.DLLがインストールされたのが本質的な原因。

ってことで、対処としては、OSのインストールディスクをドライブXに用意して、コマンドプロンプトを使って以下の処理を行う。explorerは起動しないけど、Ctl+Alt+Delからタスクマネージャは呼べるので、ファイル>新しいタスクの実行(N)…からcmd.exeを実行です。
C:¥WINDOWS¥system32>ren oleaut32.dll oleaut32.bak
C:¥WINDOWS¥system32>x:
X:¥>cd i386
X:¥I386>expand oleaut32.dl_ c:¥windows¥system32
X:¥I386>c:
C:¥WINDOWS¥system32>ren oleaut32.dl_ oleaut32.dll
更新履歴にはVista以降はまだ準対応とか書きながら、実際はVista以降でしか動かないという…かなり攻撃力高いです。っていうか、僕悪く無いじゃん、やっぱり。

2014年1月24日金曜日

昔のサイトをHerokuに詰め込んだ

仲間内の共有サーバに詰め込んでた昔のサイト。マシン乗り換えの話が出たので「えいやっ」とクラウドに載せ替えた。古いサイトなので、基本はほとんど静的なページばかり。リンク先やら取り込んでるコンテンツやらが死亡しまくりなので、動的コンテンツも真面目にサルベージするほどでもない。

ってことで、とりあえずSinatra/Ruby/Herokuの組み合わせで対応してみた。ドメイン的には10サイトほどあるけど、まぁ1Dynoでさばけるでしょう、と。(むしろ、まとめてた方がインスタンスが寝なくて好都合?)

とりあえず静的なコンテンツはSinatraに丸投げしたかったんだけど、VirtualHost対応はApacheやnginxで行い、その下でSinatraが動かすのが一般的だったのかな? Sinatra単体でスマートな方法はなさそうだったんだけど、ここはSinatra単体で行きたかったので小細工で対応しました。動作はRuby 2.0.0 + Sinatra 1.4.4で確認。

require 'sinatra' 
set :add_charset, [] 
before '*/' do
  request.path_info = request.path_info + 'index.html'
end
 
before do
  request.path_info = '/' + request.host + request.path_info
  static! if (request.get? || request.head?)
end

本当はbeforeフィルタでpath_infoを書き換えれば後はよしなに・・・というのを期待したんですが、ファイル存在判定はbeforeフィルタより手前でやってるんですね。なので、path_infoを書き換えた後に、もう一度static!を呼んでファイル再判定させてます。ちょっとしかコード追ってないんですが、ファイルチェックを一番最初にやってbeforeフィルタは常に回して、afterフィルタはファイルの時には回さない、という挙動っぽいけど、何か意味あるのかな?Sinatra詳しいってわけではまったくないので、詳しい人、あるいはもっと良い方法あるよ、という場合は教えて頂けると幸いです。

あと、add_charsetは明示的に空にしてます。これやらないとtext系のContent-Typeにcharset=utf-8を自動的に付けちゃって古いShift_JISのコンテンツに悪影響与えるので。HTTPヘッダのContent-TypeについてるcharsetとHTML側のcharsetのメタ情報が一致しない、Content-Languageとhtmlのlang情報が一致しない・・・ってのはChromeでも良くハマる問題で。文字化けや、間違った翻訳サジェストの原因になってます。頑張って間違い推定するようにしたんだけど、完全にはできないのでサーバ側で気をつけたいところです。

あと、http://toyoshima-house.net/ にあったコンテンツは有用な物だけ当ブログにマージして、元ドメインには転送をしかけました。Google+には更新情報として古いコンテンツも飛んでたみたいですが、コンテンツ自体は古い日付でエントリされてます。加えて、このブログにも独自ドメインを振りました。