2015年3月25日水曜日

JUMA TX-136/500用マルチモード&GPS対応ファームウエア20150324版

先日の試験運用でTX-136から自動時刻同期送信したWSPR2が無事他局のグラバーでデコードされたのですが、00秒スタートのためDTが常にマイナスで余裕が無く、他のグラバーではデコードされなかったと見て、本来の送信スタートタイミングである01秒時に開始するようにコードを変更しました。

自宅の受信システムでは長時間自動送信でもDTの大きな変動無くデコード100%でした。WSPR15ではDTがまだマイナスですが、一応このままとしました。

上がWSPR15、下がWSPR2受信ウインドウ

また、いずれ直すつもりですがWSPR自動送信待機中(AutoOnce、RPT1/4~1/1)にCW UPボタンを押すと送信タイミング設定が変わってしまうので、OPERボタン長押しで一旦解除してからタイミング設定を変更するようにしてください。

そろそろフォーラムにも投稿しますが、とりあえずファームウエアと簡単な説明書は
こちらから。

2015年3月23日月曜日

フリマと新ローディングコイルテスト運用

昨日こちらのエリアは天気も穏やかで上着が要らないくらいの陽気でした。

Google+のストリームにジャンクのフリーマーケットが開催されるとの情報をみたところ、比較的近くだったので車で行ってみました。場所は小田急線読売ランド前駅の近くにあるコンビニの隣のビル前で、東京マイクロウエーブクラブ主催のおもにマイクロ波関連のジャンクフリーマーケットでした。



いただいたパンフレットを見ると年に5回行われる予定だそうです。他にも行事予定がたくさん。とてもアクティブなクラブですね。

しかし、長波やってるのになぜにマイクロ波なのでしょうか???

さすがに導波管などは必要ありませんが、OCXOやRbなどの基準発信器や測定器関係など長波と共通している部分も多いのでそういったものがないだろかと探してみました。


でもって上の写真は今回手に入れた品々です。

前々から作ろうかと悩んでいて作ってなかった40dBの方向性結合器とステップアッテネーターのようなもの(笑)、それからダミーロード型の減衰器です。

ステップアッテネーターはそのままで使い物になるかどうかはわかりませんが、少なくともガワは使えるのでとりあえずは中を開けてチェックですね。あとは方向性結合器とダミーロードアッテネータをAPB-3につなげて減衰レベルや周波数特性を測定(といっても50MHzまでですが(汗))して問題なければこれでやっと送信機のスプリアス特性など何とかまともに測定できそうです。

閑話休題。

1時間ほどフリマで過ごした後、天気も良かったので新しいローディングコイル(VARIOMETER 3号)とJUMAに装着したGPSの調子を見るために筑波山近くまで車で移動しました。

いつものようにお店広げて準備中 新しいローディングコイルが大きい!

おなじみ12mグラスファイバーポールに2条傘型エレメントにアースマット10枚

GPSは正しいGLを示していました
最初mini-VNAで測定するとアンテナ入力が140Ωと高くちょっとがっかりしましたが、どうやら傘エレメントの片方の端が立ち木の枝に重なってしまっていたため高く出たものと判断し、離して再測定したところ87Ωまで下がりました。今回はテストなので深追いせず、旧コイルとの比較のため旧コイルに挿げ替えると107Ωほどと新コイルより20Ω高い値でした。

立ち木の枝の影響は予想以上に大きいです

立ち木からエレメントを十分離したときアンテナ入力は半分近くになりました

先日のJD1交信チャレンジのときにアースを海に投げ込んだときのアンテナ入力が33Ωであったことを考えると、単純ではありますが新コイルの場合13Ωと推測されます。JH1GVY局が水路にアースを落としたときの接地抵抗が2Ωほどという記事を思い出すと、新ローディングコイルの高周波抵抗は約10Ωほどということになり、ネットワークアナライザなどで測定した値とほぼ一致します。旧コイルとの直接比較で20Ω低下していたのでまずまずの成果だったと思います。

アンテナ設置から測定を済ませ、TX-136からPWR LOW設定(送信中の表示で4-8W程度)でDFCW30に続いてWSPR2を4回送信しました。

上が長点、下が短点を表しています

自宅のmini-Whip NRD535受信システムできれいに映っていました(約80km)
他局のグラバーにもくっきり映っておりましたが、WSPRは自宅のPCの時刻同期がなされていないためかデコードされませんでした。DTがマイナスなのでもう少しプラス方向に調整するコードを追加しようと思います。

なにしろ突発的に移動敢行したので告知しませんでしたが、次回は本格的に移動を考えますので136kHzの各局よろしくお願いします。

最後に寄り添う新旧コイルたち(笑)

2015年3月22日日曜日

スイッチ類のソフト的チャタリング対策覚書

先日Keyer Mini-V2を使っていただいている方から動作不具合に関するご報告がありました。メモリ再生ボタンを押すとたまに長点か短点1つだけ送出して終了してしまうことがあるということでした。手持ちのKeyerではなかなか再現されませんでしたが、Google+の無線とPICコミュニティにこの現象について投稿したところ、超短押しで再現できると報告をいただきました。自分でも確認したところ時々同じ現象が再現されました。

通常使用にはあまり支障は無さそうでしたが、話を総合するとどうやらスイッチのチャタリングが絡んでいるものとみて、この現象をソフトウエア的に回避でいないものか考えてみました。

Keyer Mini-V2のスイッチはすべて1ミリ秒のタイマー割り込みでポートを監視し、1ミリ秒前と現在のポート状態でスイッチ押下を判断していました。(具体的には立下りエッジ(1から0への遷移)を検出してスイッチオンフラグを立てて各処理にまわす)

スイッチが押して離した状態について下の図といっしょに解説します。
 上段は立下りを検出してスイッチオンフラグを立てていますが、スイッチを押したときのチャタリングで立下りを誤検出しています。しかしチャタリング持続時間はフラグオンの後の処理時間内にだいたい収まりため実害はほとんど無いと思われます。一方スイッチを離したときのチャタリングはもし立下りと判断された場合フラグを立ててしまう可能性が出てきます(実動作ではほとんど発生しません)。超短押しの場合にはチャタリングの発生している時間が延びて、処理が済んでリセットされたフラグが再立ち上がりしたため誤動作したものと推察されます。

対策としては、立下りを検出した段階で数ミリ秒のタイマーを作動させ、数ミリ秒後のポート状態(1ミリ秒前と現在の状態) で判定しフラグを立たせるというようなコードに書き換えました。押してから数ミリ秒の遅れが出ますが、スイッチを離したときや超短押しのチャタリングによる誤動作はほぼ完全に抑えられると思います。

というわけで報告いただいた現象は回避できたのですが、ロータリーエンコーダについても以前から動作にちょっと不満が残っていたので何とかならないだろうかとついでに考えてみました。

ロータリーエンコーダでは2ミリ秒前、1ミリ秒前、現在のポート状態を監視して2つのポートのうちどちらが先に立ち下がるかで回転方向を判定していました。

しかし、採用したロータリーエンコーダはメカニカル接点なのでスイッチと同じようにチャタリングは避けられません。3つの時相監視をもってしてもチャタリングは完全に取り除くことはできず、つまみを回した瞬間にメニューを1つ飛ばしたりまたはなぜか戻ってみたりという現象が時折起りあまりよろしくありませんでした。

ロータリーエンコーダは回す速度によって立下りのパルス幅が変化するため、スイッチのような一定時間後に再チェックする方法では回転速度によって取りこぼしが出る可能性が考えられたため下図のような方法で対応してみました。
まず対策前と同じように立下りを検出したときにキープフラグを立て(up_keep、down_keep)、そのフラグが真であることを条件にして次の立ち上がりを検出したところでスイッチオンフラグを立て(UP_flag, down_flag)キープフラグをリセットします。

これによってクリックありのエンコーダーのときは、1クリックごとにほぼ確実に読み取ることが可能になると思います。

高速回転の場合はやはり光学式のロータリーエンコーダが必要になりますが、周波数調整などの低速回転の用途なら秋月の安価なエンコーダでもこの方法で十分使えそうです^^

なお、パドルスイッチのポートにはチャタリング対策を施していません。なぜでしょう?

2015年3月19日木曜日

475.5kHz帯免許申請準備

JUMAのファームウエア改造がひと段落したところで、475.5kHz帯の申請に取り掛かります。

移動しない局ではTX-500で臨局検査を受ける予定ですが、申請から臨局検査・免許がおりるまで現状2ヶ月以上かかるようです。一方移動する局は今年再免許の時期に来ているため、臨局検査を受ける時間が厳しいことから軽微な変更による申請をすることにしました。

当初既存の50WのHF機をベースにと考えていましたが勘違いしていて、50W機で付加装置をつけても軽微な変更にはみなされないと指摘されましたので、5W出力のKD1JV TriBanderか10W出力のGenesisRadio G11にトランスバーターを付加という形にしようかと考えました。場合によってはFT-817を追加申請後トランスバーター付加して変更申請という形もアリかと。

トランスバーターは手持ちのTCXOを基準発信器としてDDSで局発を生成し混合、MOSFETによる電力増幅で20W出力にしようと思います。

2015年3月17日火曜日

JUMA TX-136/500用マルチモード対応ファームウエア一応完成

1ミリ秒のタイマー割り込みを利用して組んだ内部時計が意外に誤差が大きく出て手間取りましたが、とりあえず修正が効くようになったので最新ファームウエアをFinalboxにアップロードし公開します。

こちらからダウンロードしてください。

GPSモジュールはNMEA0183フォーマット準拠でありデフォルトでGGAセンテンスが1秒間隔で流れているものであれば、1PPS出力がなくても使えると思います。(ただし、時刻のフォーマットで秒小数点以下3桁で出てくる必要があります。2桁しか出ないものですとセンテンスから抽出する位置がずれてしまって位置情報が得られません...がこれもセンテンス先頭からの番号で抽出する方法を、区切りで使用しているカンマ『,』の順番で数えて抽出するなどの方法で回避可能です)

また、WSPRの文字列をシンボル変換する前にGPS情報から算出したGLと送信電力を自動検出して上書きしてシンボル変換するとまた便利になりますが、まだこのバージョンでは導入していません。

ハードの外観は最終的にこんな感じになりました。

上から 角餅が乗っかっています(笑)
後ろから オリジナルのRS232ポートは同時に使える...と思います
 ふと考えたのですが、GPSから受け取ったセンテンスをそのままオリジナルのRS232ポートに流せるようにするとまた便利に使えるかもしれませんね(PCの時刻同期などに)。

さて暖かくなってきたので、そろそろ今年の移動再開しようと思います。新しいローディングコイルも試してみたいことですし。

2015年3月16日月曜日

JUMA TX136/500にGPSユニット装着

秋月のGPSユニットをJUMA送信機に装着して、自動WSPR時刻同期送信をようやく実現するところまでたどり着けました。

以前購入したGT-723FはVenusチップを採用しているモデルです。サイズも20mm角以下と小型なのですが、今回は同じCanMore製の新しい GMS-CR6 GMS6-CR6というSiRF-IVチップを採用したモジュールにしました。

GMS-CR6 GMS6-CR6の通信速度はデフォルトで4800pbsと遅いものの(コマンドで変更可能らしい)、事前にターミナルソフトで情報を確認したところコールドスタートが速く安定しているため、TX-136にはこちらのモジュールを採用としました。

それにしても、GT-723と比較してふた周りも大きなサイズ...^^;

同じCanMore製でも中身は違います RS232/TTL両出力は便利
GPSモジュールは当初TX-136のリアパネルに装着するつもりでしたが、GMS-CR6 GMS6-CR6の思いもよらない大きさに装着する場所が見当たらなかったためプラスチックケースに入れて天板に貼り付ける格好にしました。いっそのこと本体の中に入れたい気分でしたが、ケースでシールドされており内部ノイズも相まって感度が下がるため筐体外にくっつけました。

パネルにコネクタをつけて着脱可能なほうが良かったのかお悩み中
存在感バッチリですね(苦笑)

ケースにGPSモジュール本体を入れて固定。厚さ0.3mmのFFCケーブルと変換基板をaitendoで購入してモジュールと本体への接続線としました。

ケースはTAKACHIのTW5-2-5 真ん中が1.0mmピッチのFFCと変換基板右隣がFFCケーブルコネクタ
FFCコネクタをSMDユニバーサル基板にハンダ付けして固定基板とモジュールは両面テープで固定
コネクタとモジュールをつなげてFFCケーブルを接続 熱収縮チューブで覆う
変換基板は強力両面テープで天板内側に接着 J1とISCPヘッダ6番ピン(5V電源ライン)に接続
 FFCケーブルはそのまま筐体内に引き込んでも良かったのですが、リアパネルに当たる部分を熱収縮チューブで覆って保護しました。少し厚くなるのでリアパネルをちょっとだけ削ってスペースを作って圧迫されないようにしました。

ソフトウエアは、前回までは第2のUARTを使ってGPSから送られてくる情報を抽出して時刻と位置情報から計算したグリッドロケーターを表示するところまででした。

まず、1ミリ秒割り込みを使って内部時計を作りGPS時刻と1回同期させてから内部時計を参照して2分ごと、15分ごとにフラグを立ててそのときに送信を開始するというものです。

操作はCWスピードのUPボタンで送信タイミング(Manual(非同期), AutoOnce(同期スタート),Repeat1/4(4回に1回送信), 1/3, 1/2, 1/1(連続送信))を、DOWNボタンでWSPR2とWSPR15を切り替え、あとはOPERボタン長押しでスタート、ストップという感じです。

現状では内部時計とGPSの時刻同期はGPSに接続して最初に衛星捕捉したときのみ行います。内部時計については1m秒割り込みを使っていますが、どうもだんだんとずれていくようです。修正用のコードを入れてみましたがうまく働いていないようでコード見直す必要がありそうです。同期スタートボタンを押したときと一定時間ごとに内部時計再同期が必要かも知れません。

とりあえず現在のファームウエアスナップショットをここに公開します。

GPSモジュールとの通信速度の調整はサービスモードメニュー(電源ボタンを数秒長押し)に入れました。ファームウエアを適用するとユーザーで変更または登録したEEPROMの内容は自動的にすべて消され初期状態にもどりますので再設定してください。あとは自己責任でどうぞ。(READMEは準備中なのでファームウエアのみの公開です)

2015年3月12日木曜日

JUMA送信機のWSPRモードGPS時刻同期送信機能

ようやくJUMA送信機単体でGPSから得た時刻情報から送信開始タイミングを同期させることができました。

WSPR送信タイミングは、手動、同期スタート1回のみ、同期スタート繰り返し4回に1回、3回に1回、2回に1回、連続が選べます。これくらい選択できるようにすれば十分でしょう。

WSPR15時刻同期モード送信待機中
時刻同期1回送信モード
時刻同期スタート成功
 GPSモジュールに1PPS出力端子が無いためまだ誤差が多少ありますが(DT-1.6)、1秒以下の時刻カウンター補正を入れたので調整によりDT±1に収められるようになると思います。
連続送信 DTは変わらず-1.0前後(未補正)
2回に1回送信(1/2モード) 少し補正してDTは-0.6になった
GPSと内部時計との同期は衛星捕捉時とGPS再接続時のみですが、内部時計の誤差が意外にみられるので(6秒に1ミリ秒程度(6千分の1)か)一定時間で再同期させるか内部時計補正を加えるかその両方が必要です。

あとは、同期送信待機中の表示やGPS通信速度の設定など作りこみまとめていこうと思います。スナップショット公開はもう少しお待ちください。



2015年3月8日日曜日

JUMA送信機にGPSモジュール接続するテスト

今日は天気がおもわしくないので家に篭ってプログラム作成を進めました。

PICで作ったGPS情報表示グリッドロケータ計算表示プログラムをJUMAファームウエア用に書きなおして早速移植してみました。

JUMA送信機のMCUであるdsPICには2つの独立したUARTモジュールがあって、実際オリジナルのシリアルポートとは別にコントロール基板に第2のUARTのヘッダがたっていることは前の投稿のとおりです。この第2のUARTにGPSモジュールを接続してGPSから時刻情報と緯度経度情報を取得してグリッドロケータを計算するようにしました。

JUMA TX-500コントロールボード上のJ1ヘッダピンの入力ピンとGNDピンから線を引き出す
引き出した線をGPSモジュールのRS232C出力ピンとGNDにつなげる
プログラムはPICのと同様にUART受信で割り込みをかけて受信バッファに送り、GGAセンテンスの内容を抽出して時刻表示と緯度経度から算出したグリッドロケータ(6桁) を表示用バッファにコピーして表示器に表示させます。

設定メニューをひとつ増やしてGPS情報を表示する
あとはWSPR送信タイミングの自動化機能組み込みへ進みますが、WSPR送信中はUSRT割り込みを中断することで割り込み処理によるWSPR送信時間のずれを防ぐ必要があるかどうかなど、確認事項がいくつかあります。

OPERAモードについてはエンコードソフトがVisualBasicで書かれているようで、仕組みを理解するには少し時間がかかりそうです。でもいずれ組み込みたいと思います。

秋月旧GPSモジュール(GT-723F)をPICに接続してみました

JUMA送信機にGPSを内蔵させる計画の一歩として、GPSモジュールをPICに接続して必要な情報を表示させる実験を行いました。

試用したGPSモジュールは以前秋月で購入していたCanMore製のGT-723Fというアンテナつきで2cm角厚さ8mm程度の小型モジュールです。

コネクタつきケーブルは付属していますが、aitendoでも探すと同様のものが見つかります
いま入手できるのは同じCanMore製のGMS6シリーズで、同じくらいの値段ですがふたまわりほど大きくなってしまいました(しかし両方ともTTLレベルとRS232レベル両方対応しているのが便利、通信速度はデフォルトで4800bpsに変わりました(GT-723は9600bps)

使用したPICは16F1847というメモリ搭載量の多い(プログラムメモリ14KB, RAM 1024B)モデルですが、これは現在秋月に取り扱いがありません。しかし16F1827をターゲットにXC8Freeモードでコンパイルしてもプログラムメモリ34%、RAMも28.1%消費にとどまるので1827でも問題ありません。

UARTはPIC内蔵モジュールを使用しました(そのほうが楽)。表示部は秋月のI2C OLEDで、I2Cはソフトウエアで制御しています。

動作としては、UART受信割り込みを使用し64byteのバッファに順次受信データを格納します。リングバッファを使う方法も考えましたが、せいぜい50byteまで一時的に保持できればよいので64byteを超える場合は以後の受信データを捨てています。GPSから送られてくる1秒ごとのテキストデータは膨大ですが、各NMEA0813センテンスが"$"から始まるため"$"を拾って各センテンスが順次バッファに入れ替わるようにして、メインループで必要なセンテンスを拾って(GGAセンテンス)その後に続く情報をバッファから得て加工表示します。

必要な情報は時刻と緯度経度、GPSが測定可能状態くらいなので、GGAセンテンスのみ抽出できればOKです。

NMEAフォーマットについて詳細は解説しませんが、GGAセンテンスの一部先頭から44byte目までが必要な情報です。

GGA    $GPGGA,hhmmss.sss,ddmm.mmmm,N,dddmm.mmmm,E,1,....
       ↑    ↑          ↑         ↑          ↑ ↑
       0      7          18     28 30        41 43

0:NMEAセンテンスの種類(GGA、RMC、GSAなどなど $GP~)
7:時刻(UTC)
18:緯度(度、分フォーマット)
28:N⇒北緯、S⇒南緯
30:経度(度、分フォーマット)
41:E⇒東経、W⇒西経
43:GPS衛星捕獲状態 1or2⇒衛星捕獲

数字はGGAセンテンスが流れた瞬間に収められたバッファ中の各情報のインデックスです。得られた情報ごとに加工表示していくわけです。

GL(グリッドロケータ)計算についてはJARLのこのページを参考にプログラムしました。ややこしいのは西経、南緯の場合でそのことについてはページでは触れていません(日本に居る限りはあまり必要ないといえばそうかもしれませんが)。経度で180度、緯度で90度のオフセットを与えているので南緯、西経の場合は計算方法が違うのです。

あと、経度緯度ともに小数点4桁まで表示されますが計算では2桁目までが有効なので3桁目4桁目は捨てています。

というわけで、時刻とグリッドロケータ、GPS衛星捕獲状態の表示が出来ました。

SDカードインターフェースつけるとGPSロガーもできそう
ソースファイルはここにあります。XC8でコンパイル可能ですがあくまでも実験用ですのでご承知おきを。

いよいよJUMAに組み込みを
次はJUMA送信機のファームウエアに徐々に組み込んでいきます。

立ちはだかる200mルールと臨局検査

JH1GVY森岡OMの日記によると、475.5kHz帯の免許がおりたそうです。

FT817を親機として自作20Wトランスバーターを付加することによって臨局検査を回避されたようです。その免許状に添付された注意書きに、『今回許可をした設置場所又は運用場所であっても、今後、住宅、事業者などの建物が建設された場合には運用は認められませんので云々...』と記されていることが少々気になりました。

この文言をまともに解釈するならば、もし200m範囲内に住宅やら新設された場合、他の許可された運用場所がなければ475.5kHz帯の免許が取り消されてしまうという意味なのでしょうか。そうでなくても新たに同意書を取る、もしくは新しい運用場所を申請するまでは保留(運用停止)されるということなのでしょうか。いまは局数がごく少ない状況でチェックするのも容易でしょうがこれから免許される局数が多くなる場合にどのようにチェックされるのかまったくわかりません。そもそもどうしてそこまで200m半径にこだわるのでしょうか。

 当局はTX-500の送信機増設で変更申請するので臨局検査を受けなければなりません。前の投稿のとおり高調波スプリアスは基準を満たしているとはいえ、余裕がないので外付けLPFを製作してその特性とLPF付加時の高調波スプリアス特性や空中戦電力、周波数測定など事前の準備が必要になってきます。今月の半ばに7L1RLL若鳥OMが1エリアではじめての臨検が行われる予定です。そこでは送信機本体のスプリアス特性の測定、中波放送受信機への妨害の有無などいろいろ検査をされるようです。

一方136kHz掲示板では連日475.5kHz帯の運用などの話題でもちきりです。当局も早く免許を受けたいと正直焦ってしまいます。別途FT-857に自作トランスバーターでも作成して申請してみようかなとも思いますが...

今後この200mルールがどのように変わっていくのか分かりませんが、これからのこのバンドの運用次第なのかもしれません。臨局検査はさまざまな妨害の有無を調査して状況を把握していこうとする意味合いもあるようです。それならばアマチュアなりにも運用しながらデータを収集し蓄積してしかるべきところに働きかけるなど、少なくともこれは現在このバンドで免許を受けた局のやるべきことであるような気がします。
 

2015年3月3日火曜日

秋月のキャラクタOLED(SO1602A)のお試し

JUMA送信機に使ったOLEDはパラレルインターフェースのモジュールでしたが、以前ためしに秋月で購入しておいたキャラクタOLEDはI2Cインターフェースです。しばらくパーツ箱に眠っていましたが、ようやくテストしてみました。

購入したのは白色タイプで共立エレショップで購入したものより若干お値段安めでした。ピン配置は同じでしたがインターフェースが異なるので、JUMAにはそのままでは使えません。ただし、データライン以外は大部分共通なのでファームウエアにI2Cインターフェースコードを追加変更すれば使えるかもしれません。

とりあえずその辺は置いておいて。

I2Cインターフェースと基本的なコマンドは、同じく秋月で販売されているAQM0802や1602と同じですが拡張コマンドの扱いが異なります。

たとえばコントラスト調整ですが、これは拡張コマンドに属していてもともとのコントラスト調整コマンド(0x81)の前に2つの拡張コマンドのためのコマンドを送る必要があります。そのあと、また拡張コマンドを通常に戻すコマンドを送ります。

後半にコード例を提示しますが、これは秋月製のマニュアルにも書いてありますので使ってみたい方はご一読を。

そのほかは、他のI2CタイプのLCD用コードを流用できるので表示させるだけならば容易だと思います。

というわけで、以前書いたLCD用のプログラムをちょいと直してコンパイルし初点灯。

2本の赤いジャンパ線が信号線になっています
なんだかというよりも水色なのでは?^^;

いえいえ実際目で見ると白色です。そういえばJUMAで使った白色OLEDもカメラで撮ると青っぽかったですね...どうも、LEDなどで作られる白色は青色の発光体をベースにしていて白色に見えるように加えられた波長の光がカメラでは(iPhoneのカメラですが^^;)カットされてしまって水色っぽく見えるのでしょうか。

とりあえず、色です!!(笑)

そういうわけでわりとあっさり表示されたので、次のステップに移ります。

 最後にコード公開しちゃいますが、流用は自己責任でお願いします。PIC内蔵のI2Cモジュールは使っていません。

環境:MPLAB IDE v8.93 + XC8 v1.33

【I2C OLED駆動関連】
//
//    so1602a.c
//    Akiduki's 16x2 character I2C OLED (SO1602A)display handling program
//    by JL1VNQ / HARU 20150301
//

#include <xc.h>

#define SDA    LATBbits.LATB6        // PIN No.12 example for 16F1827/1847, MCU dependent
#define SCL    LATBbits.LATB7        // PIN No.13

#define LCD_AD    0x78            // SO1602A OLED I2C address(SAO = 0)
//#define LCD_AD 0x7A            // (if SAO = 1)

#define uint8_t    unsigned char
#define uint16_t unsigned short


#define _XTAL_FREQ        4000000
 
extern void msec_delay(uint16_t time);


void I2C_init(void){
    SDA = 1;
    SCL = 1;
}

void I2C_send(uint8_t data){
    uint8_t i;
    for(i=0;i<8;i++){
        if(data & 0x80)
            SDA = 1;
//        else
//            SDA = 0;
        SCL = 1;
        SCL = 0;
        SDA = 0;
        data <<= 1;
    }
    SCL = 1;                // for ack
    SCL = 0;
}

void oled_cmd(uint8_t work){
    SDA = 0;
    SCL = 0;
    I2C_send(LCD_AD);
    I2C_send(0x80);                // Co=1, RS=0
    I2C_send(work);
    SCL = 1;
    SDA = 1;
    __delay_us(30);
}

void oled_data(uint8_t work){
    SDA = 0;
    SCL = 0;
    I2C_send(LCD_AD);
    I2C_send(0xC0);                // Co=1, RS=1
    I2C_send(work);
    SCL = 1;
    SDA = 1;
    __delay_us(30);
}

void oled_init(void){
    msec_delay(100);
    oled_cmd(0x01);                // clear diaplay
    msec_delay(20);
    oled_cmd(0x02);                // to home position
    msec_delay(2);
    oled_cmd(0X0C);                // display on
    msec_delay(2);
    oled_cmd(0x01);
    msec_delay(20);
}

void oled_position(uint8_t li, uint8_t col){
    oled_cmd(0x80 | (li << 5) | col);        // line0 columun0 = 0x00, line1 columun0 = 0x20
}

void oled_char_disp(uint8_t li, uint8_t col, uint8_t ascii){
    oled_position(li,col);
    oled_data(ascii);
}

void oled_str_disp(uint8_t li, uint8_t col, const char *string){
    uint8_t i = 0;
    oled_position(li,col);
    while(((col + i) < 16) && string[i]){
        oled_data(string[i]);
        i++;
    }
}

void oled_clear(void){
    oled_cmd(0x01);
    msec_delay(2);
}

void oled_contrast(uint8_t cont){
    oled_cmd(0x2A);                // RE = 1
    oled_cmd(0x79);                // SD = 1
    oled_cmd(0x81);                // contrast control
    oled_cmd(cont);                 // value
    oled_cmd(0x78);                // SD = 0
    oled_cmd(0x28);                // RE = 0
    msec_delay(100);
}


【サンプル表示プログラム】
//
//    oled_test.c
//    Akiduki's 16x2 Character I2C OLED display (SO1602A) tset program for 16F1847/1827
//    by JL1VNQ / HARU 20150303
//

#include <xc.h>

#pragma config CPD = OFF, BOREN = NSLEEP, IESO = OFF, FOSC = INTOSC
#pragma    config FCMEN = OFF, MCLRE = OFF, WDTE = OFF, CP = OFF, PWRTE = ON, CLKOUTEN = OFF
#pragma config PLLEN = OFF, WRT = OFF, STVREN = OFF, BORV = LO, LVP = OFF

#define EEPROM_SIZE     256
#define _XTAL_FREQ        4000000

#define uint8_t unsigned char
#define uint16_t unsigned short
#define uint32_t unsigned long

extern void I2C_init(void);
extern void oled_init(void);
//extern void I2C_send(uint8_t data);
//extern void oled_cmd(uint8_t work);
//extern void oled_data(uint8_t work);
//extern void oled_position(uint8_t li, uint8_t col);
extern void oled_char_disp(uint8_t li, uint8_t col, uint8_t ascii);
extern void oled_str_disp(uint8_t li, uint8_t col, const char *string);
extern void oled_clear(void);
extern void oled_contrast(uint8_t cont);

void msec_delay(uint16_t time);


void main(void){
    OSCCON = 0x6A;                 // 4MHz internal OSC no PLL
    TRISA = 0x00;                    // all ports are output
    TRISB = 0x00;
    ANSELA = 0x00;                  // digital
    ANSELB = 0x00;

    I2C_init();
    oled_init();

   
    while(1){
        oled_str_disp(0,0,"OLED-test!JL1VNQ");
        oled_str_disp(1,0,"ABCDEFGHI0123456");
        msec_delay(1000);
        oled_clear();
        msec_delay(900);
        oled_contrast(0xFF);
        oled_str_disp(0,0,"ContrastLevelMax");
        oled_str_disp(1,0,"ABCDEFGHI0123456");
        msec_delay(1000);
        oled_clear();
        msec_delay(900);
        oled_contrast(0x00);
        oled_str_disp(0,0,"ContrastLevelMin");
        oled_str_disp(1,0,"ABCDEFGHI0123456");
        msec_delay(1000);
        oled_clear();
        msec_delay(900);
        oled_contrast(0x7F);
    }
}

void msec_delay(uint16_t time){
    for(uint16_t i=0;i<time;i++){
        __delay_ms(1);
    }
}


ターゲットはPIC16F1827/1847ですが、ポート割り当てとポート初期化あたりを書き換えれば8ピンPICでも動作可能です。なにせOLEDとPIC間の信号線は2本しかありませんから~

気になる消費電流ですが、コントラストによって大きく変わりました。
コントラスト中央値設定で30mA、最低値で14mA、最大値で45mA、無表示で4~6mAでした。