jl7gmnのblog

yahooブログから移行してきました。アマチュア無線を中心としたブログです。

2013年06月

PICその21

PIC16F88での時計の表示の為のポート設定を検討した。なにかと言うとLCD表示をするときのポートの選択方法なのだ。LCD側とPICの接続は大概(RA0~RA3、RB0~RB3、RB4~RB7のどれかRA4,RA5,RA6,RA7はRA5が入力ポートなので連続使用不可)連続4ポートとLCD側4ポート(D7,D6,D5,D4、4ビット接続時)の接続例がほとんどだ。上位4ビット、または、下位4ビットでの連続ポートである。新たに作成する回路都合で問題が無い場合は良いが、いろいろと動作済みのものをマージ(合体)したい場合は、LCD接続用で全部が連続でない空きポートを使う必要がでてくる。今回は時計のSWポートをそのまま使い、LCDへ表示する為の第一段階として先の標準の3例のポート接続ができない場合(RA3ポートが時計のSW入力で使われてしまっている場合)のポート(RA0,RA1,RA2,RA6)対応を行ってみる。他のコンパイラではLCD専用関数でのコンフィグ設定で使用できるものがあるようだが、XC8での対応例はインターネット上では一切見つからなかった。皆さんは連続ポートでの使い方だけなのかな~?

通常時のLCD接続PICポート----->空きポート都合でのLCD接続PICポート
RA0,RA1,RA2,RA3----------->RA0,RA1,RA2,RA6

LCD端子側とのポート対応は以下となる
D7:RA6
D6:RA2
D5:RA1
D4:RA0
このために何を行うか?何となくLCDへデータを送るポート指定関連のプログラムを変えなきゃいけないというのが分ると思う。なので、早速、実際にLCDの制御プログラムであるlcd.cのプログラムを見てみるわけです。
やらなければいけないのはLCDの制御プログラム中のポート設定対応部を対応するポート用に作り直すということです。
まず、ポート出力するそれらしき部分の記載がある関数が2つ程ありました。
----------------------------------------------------------------------------------------
void lcd_data_byteset(unsigned char c){
PORTA = (PORTA & 0b11110000) |  (c & 0b00001111); //Only low 4bit DATA
        LCD_STROBE;
}
void lcd_data_set(unsigned char c){
PORTA = (PORTA & 0b11110000) | ( (c >>4 ) & 0b00001111 ) ; //Hi SIDE DATA       
        LCD_STROBE;
PORTA = (PORTA & 0b11110000) |  (c & 0b00001111 ); //low SIDE DATA    
        LCD_STROBE;
----------------------------------------------------------------------------------------
lcd_data_bytestとlcd_data_setの2関数でのポート指定が連続のPIC4ポート用として書かれています。

まず初期化で使われているlcd_data_bytestについて
実際にパソコンになったつもりで動作を見てみると、PORTA(RA7,RA6,RA5,RA4,RA3,RA2,RA1,RA0)と0xF0とのANDをとり、入力データc0x0FとのANDの結果とのORをとると最終結果PORTAには(RA7,RA6,RA5,RA4,RA3,RA2,RA1,RA0)が対応となる。これだと解りにくい。次のようなことなのだ。
---------------------------------------------
上位
RA7,RA6,RA5,RA4 はポートAの上位がそのまま入る(RA7,RA6,RA5,RA4)
---------------------------------------------
下位
RA3,RA2,RA1,RA0は引数cのデータの下位値が入る(RA3,RA2,RA1,RA0)
---------------------------------------------
またLCDへの出力対応ポートは
RA3,RA2,RA1,RA0なので結局引数cのデータの下位値が出力されるのです。上位はLCDポートにはなんら関係なし。

次にlcd_data_setについて
同様に実際にパソコンになったつもりで動作を見てみると、PORTA(RA7,RA6,RA5,RA4,RA3,RA2,RA1,RA0)と0xF0とのANDをとり、引数cを4ビット右シフトし0x0FとANDをとった結果とのORをとると最終結果PORTAには(RA7,RA6,RA5,RA4,RA7,RA6,RA5,RA4)が対応となる。これだともっと解りにくいかもしれない。次のようなことです。
---------------------------------------------
上位
RA7,RA6,RA5,RA4 はポートAの上位がそのまま入る(RA7,RA6,RA5,RA4)
---------------------------------------------
下位
RA3,RA2,RA1,RA0には引数cのデータの上位(RA7,RA6,RA5,RA4)が入る
またLCDへの出力対応ポートは
RA3,RA2,RA1,RA0なので結局引数cのデータの上位値が出力されるのです。上位はLCDポートにはなんら関係なし。
次のプログラム文はlcd_data_bytestの引数cの下位値出力とまったく同じです。

よって、lcd_data_setでは引数cの上位データを最初に送出し、つぎに引き数cの下位データを送出するというプログラムなのです。紛らわしいったらありゃしない。説明書くのも大変、あ~疲れた!

 プログラム動作内容がわかったので、目的のポート変更への対応を考えてみる。対応する関数はlcd_data_setで他のlcd_data_bytesetは同じなので省略する。
cd_data_setでは要するに引数cの上位値、下位値を順番に(RA6,RA2,RA1,RA0)に出力するようにすればよいわけです。
引数に対しての上位ポート値(X7,X6,X5,X4)下位ポート値(X3,X2,X1,X0)とするとLCDポート(D7,D6,D5,D4)にたいしては次のようになる。
LCDポート:PICポート:引数値-----説明
D7:RA6:X7----- PIC6ポートにX7
D6:RA2:X6----- PIC2ポートにX6
D5:RA1:X5----- PIC1ポートにX5
D4:RA0:X4----- PIC0ポートにX4

上記のようにPIC6ポートにX7なので1ビット右シフトして0x40(0x01000000)とのAND(6ビット目だけ取り出す)をとる。またPIC2,PIC1,PIC0ポートには上位のX6,X5,X4が対応なので、上位X7,X6,X5,X4を4ビット右シフトして0x07(0b00000111)とのANDをとり3ビット分だけ取り出し最初の結果とのORでポート設定する。これを素直にプログラムすると次のようになります。
---------------------------------------------------------------
PORTA = (((c >>4 ) & 0b00000111)|(((c >>1 ) & 0b01000000)));
---------------------------------------------------------------
これで、引数cの上位のデータがPORTA(RA6,RA2,RA1,RA0)に送出できるようになります。

次の下位のデータ送出も同様に
D7:RA6:X3----- PIC6ポートにX3
D6:RA2:X2----- PIC2ポートにX2
D5:RA1:X1----- PIC1ポートにX1
D4:RA0:X0----- PIC0ポートにX0

前と同じ考え方でPIC6ポートにX3なので3ビット左シフトして0x40(0x01000000)とのANDをとる。また必要な下位(X2,X1,X0)は引数cと0x07(0b00000111)との単純なANDで取り出し、最初の結果とのORでポート設定する。プログアムは次のようになります。
---------------------------------------------------------------
PORTA = ((c & 0b00000111)|((c<<3) & 0b01000000));
---------------------------------------------------------------
以上で引数cの下位のデータがPORTA(RA6,RA2,RA1,RA0)に送出できるようになります。

5000文字を超えたようなので次へつづく

PICその20

PIC16F84Aでの時計サンプルをコーディングしていてメモリの不足でコンパイルできない状況が起きた。もともとPIC16F84Aは1024ワードしかプログラムメモリがない。なおかつ私のプルグラムはふんだんに変数もLONGをつかい32ビットをも簡単に消費してしまうためだろう。いろいろとしらべていると、メモリの消費を減らすのはcharを使うのが手のようだ。LONGに比べて8ビットで済むため、1/4ほどで済む。しかしプログラムのアイデアが必要となる。ここが一番難しいのではないかと思う。いろいろとメモリを減らすために定義をなくしたり対応してみたが、さっぱりコンパイルができない状態だ。物理的に私のプログラムでは無理(他の人のは問題の無いものがあるが、私のプログラムの理解ができていないため)なので、PIC16F84Aは一旦保留することにした。ピン互換で使用出来ると言われているPIC16F88に早速差し替えてみた。外部クロックが要らないので、便利なPICだ。新規に作成しなくても途中からの変更が可能だ。Customizeを選び、Configurationのグループ中のDeviceをPIC16F84AからPIC16F88に変更しOKボタンを押すだけでよい。プログラムも余裕でコーディングできる。PIC16F88は4096ワードのプログラムメモリがある。コンパイルも通り、時計が動くようにと期待をこめて小型のブレッドボードにPICを差し込む、そして電源ON!あれっ!何の変化もない。一体どうしたのだろう?出力ポートの使い方がまずかったのか?と思いながら、一応確認の為に、いちいちLEDを出力につないで、点灯するかを確認したり、がしかし特にポートの問題はないようだ。なんで時計の表示がでないんだろう?割り込みタイマーを使っているはずなのだが、動いてなさそうだ。ここに私のプログラムの手順の間違いがありました。結果から言うと、プログラムのCONFIGの設定の方法を便利な機能ということで、PIC Memory ViewsのConfiguration Bitsで間単に作成し使用してしまっていたことが大きな間違いでした。初期の状態での吐き出されるConfigurationコードでは動かない設定となってしまうということです。なので、一つ一つ確認して必要部を書き換えます。ここに来るまでプログラムコードの間違いではないかということで、なんでもないプログラムを無駄に確認してしまってたわけです。あ~あ、またやってしまった。ということで、Configを定義しなおして、無事時計が動いたのでした。時計の秒のドットが無いので空いていたポートRA2を出力として、ディレーと組み合わせて1秒間隔で点灯するように追加改造してみました。やっと時計らしくなりました。後は誤差がどれくらいかということですが、ストップウォッチと比較してみましたが、少し遅いようでしたので、割り込みカウント値を少し減らすようにして調整し、ほとんどずれは感じられない程度までになりました。クロックの周波数補正用にOSCTUNEとOSCCONがありますが、ここは中心値として、補正なしのオリジナルセンター値のままにしてあります。
イメージ 1
左下が小型のブレッドボードに組んだ時計のPIC16F88です。RS-232Cのボード上にLEDセグメントを配置したのでこのようになりました。LEDセグメントは、手持ちのTLG4145という東芝のカソードコモンタイプの4X7セグメントLEDです。古いものなので、インターネットで調べても資料はありませんでした。なので、電源と抵抗で実際にLED点灯させ全ピン配置を確認し、使用しています。
イメージ 2
小型ブレッドボード上には2ポートの動作確認の為のLEDがつないだままにしてあります。はずしてもいいのですが...。接続コードが足りなくなったので、さらに追加で20本ほど作りました。このマップピンのコードは、案外気にいってます。
 時計の動作ではモードSWがオン時(GND)は分と秒表示、時間SWがオン(GND)では時間の桁がカウントアップします。同様に分SWがオン(GND)では分の桁がカウントアップします。PIC16F88での時計はうまくいったので、PIC16F84Aでの1Kワードのサンプルに再挑戦しようかしらん?LCDへの表示もあるしな~。いろいろと調べなきゃ、きゃ!しかしPICは時計もできるしとても、面白いですわ!

PICその19

PIC16F88をつかった小型のブレッドボード上に組んだLCD表示およびRS-232Cの回路を7セグメント表示ハードを組んだボードの方に組みなおしました。理由は、小型のボードで時計を組んでみたくなったからです。既にRS-232Cのレベル変換回路とLCDおよびRS-232C用で手持ちの2個の小型ブレッドボードを使ってしまったので空けれる方の1つを空けた訳です。特に組みなおしで問題はありませんでしたが、LCD用の液晶表示調整用の半固定抵抗をさす場所がボード上に無かった(ピン寸法間隔がボードと異なる)ので、半固定に延長ワイヤを半田付けして自由にさせるように間隔を選べるようにしたぐらいだ。組み上げ後の動作確認では組みなおし前と同じ動作で問題なしでした。ボード上に移した後の写真です。
イメージ 1
この組みなおしの前に7セグメントのハードを組んだ回路上のチェックで使っていたPIC12F675の回路を全てはずすし、小型のボード上のLCD表示およびRS-232CのPIC16F88での出力ポートのプログラムへの変更追加を行っています。PICではほとんど仕様が共通(使い方が一緒)なところがあるので、PICを目的に合わせて簡単な変更で使用できるところがまたいい点です。
-------------PIC12F675の4出力及びシリアル通信ポートのPIC16F88への対応----------
PIC12F675⇔PIC16F88
GP0⇔RB0:BCD出力 2^0(1)
GP1⇔RB1:BCD出力 2^1(2)
GP2⇔RB3:BCD出力 2^2(4)
GP3⇔RB2:RS-232C INPUT AUSART RCV
GP4⇔RB5:RS-232C  OUTPUT AUSART TRANSMIT
GP5⇔RB4:BCD出力 2^3(8)
--------------------------------------------------------------------------
回路を作り終えて、現在は、時計の資料を調査中です。インターネット上にPIC16F84を使う時計サンプルがあるが、直接7セグメントをドライブする回路のようなのでLCD用に変更できないかを解析検討中です。

PICその18

8PINのPIC12F675で7セグメントへの表示やRS-232Cのシリアル通信を確認し、今度はPIC16F88に変えてLEDの点灯確認、LCDへの文字列表示、次にRS-232Cのシリアル通信と順番に確認してみた。とにかくPIC16F88の出力ポートでのLEDの点灯確認は最低限ポートの使い方やポートの入出力設定仕様確認上必要だ。ひとまずインターネットからPIC16F88のデータシートをダウンロードしてみた。ページがかなりあるので、必要な部分をとりあえずプリントアウトした。特に配線で使うPinDiagramと出力ポートの全体像が分るDEVICE BLOCK DIAGRAM,とピンの入出力の設定が分るPINOUT DESCRIPTION,そしてシリアル通信のスピード設定のためのBAUD RATES FOR ASYNCHRONOUS MODE(BRGH=1)がのっているページだ。他の細かな部分はPC上で見るため、手元に紙の上記のデータシートがあると作業時の確認はしやすい。ボード作成は小型のブレッドボードを使ったため、配線がしにくかったが、何とかジャンパーワイヤー等を使うことで作れた。もちろんLEDの位置はバラバラで致し方ない。点けばいいという感じになってしまった。今回もシリアル通信の開始前には電源オンでBCDコードを吐き出す出力4ポートは全点灯にする。まったくPIC12F675でやったときと変わらない仕様です。7セグメントはそのままブレッドボードにあるので、いずれ小型のブレッドボードから7セグメントがあるボードに今回の回路を移動しつなげる予定だ。シリアル通信用のレベル変換は前のブレッドボードのものをそのまま使った。電源(+-)とPC側とPIC側の各送信受信ポート2本をそれぞれつなぐだけだ。回路上はなんら問題はない簡単な回路だ。今回は順番にLCDの表示を先に作成していったため、LCDで使ったポート以外でシリアル通信を行うことになるのだが、順番は逆の方がいいと思った。たまたまPIC16F88のUSARTのポートがLCD作成で未使用であったためできたという状況だからだ。これはPINOUT DESCRIPTIONを見ると分る。つまり使用できるポートがAUSARTでは限定されているのです。受信用がRB2で入力ポート、送信用がRB5で出力ポートという具合です。通信用のポートは除いてLCD表示を作成するというのが常道だろう。
さて一番の問題だったのが、PICの675のときのmain以外にあったプログラムとヘッダー群だ。計算では9600bpsを予定したため、(後述するが)675での通信速度は2400bpsで使えないからだ。今回のPIC16F88ではソフトウェアでのシリアル通信ではなく、ハード上に組み込まれたシリアル通信でやるという違いがある。しかも通信速度を決めるクロックもLCD表示でタイミングも既にFosc=8MHzとなってしまっている。この為まったくPIC12F675のときとは違うハード設定のプログラム方法になる。なのでmain以外のシリアル関連のプログラムとヘッダーは全て削除することになった。つまり、main.c、lcd.c、lcd.h、それと削除した代わりのUSART用のHI-TECH のusart.h、usart.cをそれぞれ名前をpic16f88usart.h,pic16f88usart.cに変えてプロジェクトに追加した。というより新規に作成追加したときのファイル名をpic16f88usartにしたためファイル名がオリジナルと違うようになったのが本当だ。なおUSART情報の参考にしたページのURLです。http://picprograming.blog39.fc2.com/blog-entry-8.html
以下写真の右が作成したブレッドボードPIC16F88の回路です。左はシリアル通信のPC用のレベル変換インターフェース回路(DM3202AN)です。
イメージ 1

電源ON後にPCからのキーで0が押されてLCDに表示されたメッセージです。
イメージ 2

TERA-TERMでのPIC電源ON時のメッセージ(Pewer ON,pic16f88usart.h is usefull!)とその後の
PCでのキー操作です。押した数字キーとPICから返された数字のコードが表示されています。
イメージ 3
動作は上記の通りPIC12F675のときとほとんど同じだ。あえてプログラム上では同じ数字でなくコードを送り返した(変数値を使った)。プログラム検討時に少し悩んだことがあった。通信の速度設定値であるSPBRGがPIC16F88のデータシートには8MHz時の設定データとしては載っていないのです。データがあるのはFosc=20MHz、Fosc=16MHz、Fosc=10MHz、Fosc=4MHz、Fosc=3.6864MHzだけです。この為、概略比例計算でSPBRGの値(ボードレート計算用の数値:X)を出した。9.6Kの速度時で20MHzではSPBRGは129であるので、8MHzでは周波数比0.4倍なので129*0.4=51.6 約52を初期の設定値に仮設定した。この設定値が丁度中心にあるかを確認してみた。単純にコンパイルを何回か行う単純確認作業である。この結果SPBRGの可能設定範囲が49から54が文字化け無しの設定範囲でした。ほぼ概略計算があっているようだった。シリアルデータの送信方法として、1文字ずつの送出用と標準のprintfを使うストリング送出の方法で行っています。
PCへの送出データ”Power on”は各1文字ずつ、スペース、改行コード、復帰コードを送っています。
  Rs232Out('P');
        Rs232Out('o');
        Rs232Out('w');
        Rs232Out('e');
        Rs232Out('r');
        Rs232Out(0x20);
        Rs232Out('O');
        Rs232Out('N');
        Rs232Out('\n');
        Rs232Out('\r');
上記の関数は以下の通りです。
void Rs232Out(char outChar)
{
    while(!TRMT);
    TXREG = outChar;
}
その次にPCに送っているデータ”pic16f88usart.h is usefull!”はprintfでのストリング送出です。
        printf("pic16f88usart.h is usefull!\r\n");
どちらかというとPrintfの方が使い勝手はいいです。なお漢字と全角スペースは文字化けします。
やってみて、XC8の対応するPIC(PIC16)ではふるいのだろうか?USART用のヘッダー、プログラムが無いのが、残念だが、今回のHI-TECKのサンプルはそのままでも問題なかったのでこれからはUSART用として使用できると思う。XC8ではあまり機能の少ないデバイスや古いPICを使うなということなのだろうか(PIC18用のUSARTはインクルードのplib中にある)?と思った。また最初の動画の説明をわすれたので、最後にする。電源を入れて最初のメッセージが出ます。上の”Power ON”と”pic16f88usart.h is usefull!”その後にパソコン側でキーボードの数字キーを0から9,8,7.....とたたいていった時のPCから送られたデータを検出してLCDに表示しているだけです。それからLCDの表示は次のデータを表示する前に画面クリアをしないと前のデータとかさなりぐちゃぐちゃになります。
終わりに先の__delay_us(x)、__delay_ms(x)のタイミングに関して、過去のプログラムでディレーを変更して確認しましたが、タイミング等問題はありませんでした。

PICその17

XC8コンパイラでのディレー関連のコード(__delay_us(x)や__delay_ms(x))がコンパイル時に付くビックリマークがバグといわれているが、やはりコンパイルでのビックリマークが実際でる。何かビックリマークが出る状態だと気持ちが悪いので、ビックリマークが出ないようにといろいろとやって見た。参考にしたのはPICCPRO用のコンパイラーのディレーコード定義部だ。PICCPROでは次のようにinclude<pic.h>を取り込んで_XTAL_FREQを指定するだけで__delay_us(x)や__delay_ms(x)が使用できるとなっている。xc8のxc.hでも同じようにできるとなっているのだが、ビックリマークなのだ。xc8のコンパイラのデバイスドキュメントでは以下のようにp232には問題無いように書かれている。だから皆さんがバグと言っているのだ。以下抜粋
---------------------------------------------------------------------------------------
__DELAY_MS, __DELAY_US
Synopsis
__delay_ms(x) // request a delay in milliseconds
__delay_us(x) // request a delay in microseconds
Description
As it is often more convenient request a delay in time-based terms rather than in cycle 
counts, the macros __delay_ms(x) and __delay_us(x) are provided. These macros simply wrap around _delay(n) and convert the time based request into instruction 
cycles based on the system frequency. In order to achieve this, these macros require 
the prior definition of preprocessor symbol _XTAL_FREQ. This symbol should be 
defined as the oscillator frequency (in Hertz) used by the system.
An error will result if these macros are used without defining oscillator frequency 
symbol or if the delay period requested is too large.
---------------------------------------------------------------------------------------
しかしながらナビで__delay_msや、__delay_usを探しても、どこのインクルードファイル群の定義にもリンクがない状態だ。ならば、pic.hを取り込んでいないxc.hでは新たに追加して定義してやれば良いのではないかということで、コードをpic.hからコピーしてmain.cとlcd.cの宣言部に追加してみた。
以下がコピー元 pic.hの該当オリジナルのディレクティブ関連部だ。
----------------------------------------------------------------------
#ifdef __PICCPRO__
/****************************************************************/
/* Built-in delay routine */
/****************************************************************/
#pragma intrinsic(_delay)
extern void _delay(unsigned long);
// NOTE: To use the macros below, YOU must have previously defined _XTAL_FREQ
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif
----------------------------------------------------------------------
上記の太字青色部分がコピーして貼り付けた部分です。
main.cでは__delay_ms(10);でミリSECでの使用がされていて、ビックリマークのワーニングが出ていた箇所だ。
lcd.cでは__delay_us(40),__delay_ms(2);__delay_ms(15),__delay_ms(5),__delay_us(100)などが使用され同様にビックリマークのワーニングが出ている。
LCD制御のディレーのタイミングがの問題と宣言の2重定義が心配だったが、特にエラーは出なかった。実際コンパイルしてみると、ビックリマークが出てコンパイルが通ったときとは違い、コンパイルがノーエラーでビックリマーク無しでスッキリと終わりLCDの表示も前とまったく変わらずうまく動作しているようだ。LCD表示では問題が無いわけだが、既に動作確認してきた他のPICプログラムでのディレーのタイミング関連部を確認しようと思うわけの分らないビックリマークから一応開放されたので今日はスッキリと寝れそうだ。

アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

QRコード
QRコード
  • ライブドアブログ