jl7gmnのblog

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

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プログラムでのディレーのタイミング関連部を確認しようと思うわけの分らないビックリマークから一応開放されたので今日はスッキリと寝れそうだ。

PICその16

LCDのディスプレーの表示の位置の設定方法を変えてるサンプルがあったので、早速取り入れてみた。
http://nanoappli.com/blog/archives/5363 thanks!
先のプログラムでの液晶の位置設定は、lcd_goto(0x00)とlcd_goto(0x40)でしたが、LCDの表示部のアドレス指定なので、1行目は良いが、2行目はアドレスが1行目からの連続でないので分りにくい。このためインデックス的に表す方法があったので、取り上げてプログラムに追加することにしたわけです。以下私が理解した内容を示します。
LCDの1行目の先頭アドレスは0x00、2行目の先頭アドレスは0x40です。ここでコマンドとして書き込む場合の処理があるので、実際の1行目のコマンド書き込みでのデータでは、
lcd_write(0x80);
2行目では
lcd_write(0xC0);
が行われています。つまり先まで使っていた位置指定関数 lcd_goto(0x00)はlcd_write(0x80)を行うための関数なのです。同様に2行目の指定 lcd_goto(0x40)はlcd_write(0xC0)を行っているということです。
以下lcd.c内の部分を抜粋(posが0x00,0x40ということです。)
---------- lcd.c ---------------
/*
 * Go to the specified position
 */
void
lcd_goto(unsigned char pos)
{
LCD_RS = 0;
lcd_write(0x80+pos);
}
------------------------------
先のコマンド書き込みのための関数lcd_locate(x,y)は次のように決められてます。
x:0 or 1 //1行目が0、2行目が1
y:0 to 15  //1行目の先頭インデックスが0、次が1,2,3.....15と16文字分の位置インデックスが対応します。

インデックスとアドレスとの対応が以下の関数内の演算で行われています。
--------------------------------------------------------
実際に位置指定の数字をいれて計算すると1行目の先頭(0,0)では 
lcd_locate(0,0)のときの計算
writeData =(0x40*x)+yは 
(0x40*0)+0 = 0x00 です。
あとはビットの8に1を立てて送るためのビット演算orが入ります
writeData |= 0x80;が該当します。
実際の(0,0)では0x00より00000000がデータですが、ビットの8とのor演算で
00000000
10000000
  ↓
10000000
(0x80)が送出データとなります。lcd_write(0x80)と言う事です。
--------------------------------------------------------
同様に(1,0)では
writeData = (0x40*1)+0
(x40*1)+0 = 0x40です。
writeData |= 0x80;が該当します。
同様にビットの8に1をたてて送るため上記のビット演算orでは
01000000
10000000
  ↓
11000000
(0xC0)が送出データとなります。lcd_write(0xC0)ということです。
--------------------------------------------------------
以下が説明した位置指定の関数です。
lcd_locate( unsigned char x , unsigned char y )
{
unsigned char writeData;
writeData = (0x40*x)+y;
writeData |= 0x80;
lcd_write(writeData);
}
--------------------------------------------------------
上記関数をlcd.cコードに追加します。またヘッダーlcd.hにこの関数の宣言を追加します。
先のlcd_goto(unsigned char pos);は使わなくなるので、コメントアウトもしくは、削除してもいいのですが、そのままにしてあります。
-------lcd.h内に追加--------------------------------------
extern void lcd_locate(unsigned char x,unsigned char y);
--------------------------------------------------------
このことから入力データ(0x00)を入れると(0x80)が出力データ書き込まれ、また入力データ(0x40)を入れると(0xC0)が出力データで書き込まれる関数が以下の仕様で動作することになります。
つまり、入力が連続でないデータアドレス(0x00)(0x40)なのが、対応を座標的に考えてLCDの1行目の先頭ロケーションを(0,0)と決め、また2行目は(1,0)とデータ値がアドレスなのとは無関係にインデックスで簡素化されたということになります。
lcd_locate(0,0);~lcd_locate(0,15);
1行目は(0,0)から(0,15)という指定方法が仕様となります。
lcd_locate(1,0);~lcd_locate(1,15);
2行目は(1,0)から(1,15)という指定方法が仕様となります。
これで分りにくかったLCDへの表示位置指定方法は相対的なx,yの座標指定方法に変わったのでした。
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計:

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