電子工作8 - PIC16F877とパソコンとの通信(HyperTerminalを使う)
概要
安価で、機能が一杯詰まったPICの使い方を学んで行く中で、PICとパソコンとの通信法を知ることは必須である。小生が使い続けているBasic コンパイラPICBasic
Proには、PICに内蔵のUSART(Universal Synchronous Asynchronous Receiver Transmitter)を使うためのコマンドや、PICの任意のポートを通信に使うためのコマンドなどが用意されていて、アッセンブラによるブログラム作成に比べ、格段に楽である。
とにかくパソコンのRS232ケーブルには、市販の機器しかつないだことがないので、参考にした文献の配線が、どんな働きをするのか、パソコン(PC)を壊さないか。また、PC側で手軽に使えるとされる、Windowsに長く搭載されてきているHyperTerminalとやらは何なのか、どのように使うのか、最初はまったく不明。それでも、Googleの検索結果から得られた内外の参考資料と悪戦苦闘して、約1ヶ月、PICとPC間でデータの通信が一応可能になった。今回は市販されてから結構時間が経ち、関連情報も豊富な40ピンの16F877という¥500円チップを使うことにした。PCは小生が使っているWindows
XP機、その中に入っているHyperTerminalがPC側のデータの受け渡しを担う。
回路
PCからのRS232ケーブルの末端をSUB-D9と呼ばれる9ピンのコネクタにつなぎ、それからピン2(Rx)とピン3(Tx)を、レベルコンバータMax232(今回は、同等品ADM3202、¥200秋月)を経由して、PIC16F877(4MHzで動かしている)のピン25(Tx)とピン26(Rx)に接続する。さらに、RS232ケーブルのピン5(GND)を取りだし、16F877のVss(GND)につなぐ。ここで大事なことは、RS232ケーブルの末端につないだSUB-D9のピン1、4と6を結線し、また、ピン7と8を結線することである。これで、PC側で入力したデータがそのまま戻ってくるエコーバックという機能が使えるようになる。RS232の結線法の一つということである。これで、16F877に内蔵のUSARTが使えようになる。このSUB-D9コネクタを使う時、小さくふられている番号に従うと、誤配線を防げる。さらに、PICのRA0(ピン2)からの入力電圧を取り込む目的で、内蔵の10-bitアナログ-デジタルコンバータ(ADC)を使えるように設定を行った。変換の基準電圧として16F877への供給電圧(Vdd)
5.0 Vを使うように設定しているので、入力電圧の範囲は0 - 5.0 Vになる。
下の写真は上の回路に基づいて製作した PIC(16F877)ボードです。真中のICが16F877、その右下の小さいICがADM3202、その上部が5V電源、右端にSUB-D9のコネクタがあり、RS232ケーブルでPCにつながっている。このコネクタの末端部分の結線が注意の要るところです。左手のACアダブタと16F877との間にある小さいボードは実験目的の可変式の分圧器で(青色の小さい長方形のものは20回転VRです)、16F877のRA0ピン(ピン2)にADC入力電圧を供給しています。
データ転送手順
PICとPCをRS232ケーブルでつないだ状態で、PICにも電源を入れる。以下のようにしてPCとPICの間に通信状態を成立させる。
1)
PC側のHyperTerminal をクリックして立ち上げる。
2)
表示される[接続の設定]で、キャンセルを選択する。
3)
[プロパテイ]をクリックする。
4)
[接続方法]で、COM2(例えば)を選択する。そして、[モデム構成]で2400bps(例えば)を選ぶ。
5)
HyperTerminal の画面に戻り、Returnキーを押す。これで、PCとPICがつながる。電話器アイコンが沈み、切断アイコンが浮び上がる。
6)
PIC側のResetボタンを押すと、Enter <a.> to get ADC value: と表示される。ここで、”a”
を押すと、以下に示す写真のように3つの値が表示される。得られて値は、ファイルとして保存できる。(*PIC側の電源がすでにONの状態で、Resetボタンを押さずにPC側で”a”を押すとEnter <a.> to get
ADC value: は表示されずにデータだけが表示される)
7)
HyperTerminalを終了するには、切断アイコンを押す。
データ保存手順
1)
PC側のHyperTerminal をクリックして立ち上げる。
2)
表示される[接続の設定]で、ファイル名(例えば. data20)を入れる。
3)
[接続方法]で、COM2(例えば)を選択する。そして、[モデム構成]で2400bps(例えば)を選ぶ。 これで、PCとPIC間が接続状態になる。
4)
PIC側でResetを押すか、電源を入れとEnter <a.> to get ADC value: が表示される。
5)
または、PC側で”a”を押すと、3個のデータが表示される。
6)
HyperTerminal 上に集まったデータを保存するには、[ファイル]−>[上書き保存]を押す。これで、データはファイルdata20に保存される。
7)
ファイルdata20に保存されたデータを見るには、HyperTerminal を立ち上げ、表示される[接続の設定]で、[キャンセル]を選択する。
8)
次いで、[ファイル]−>[開く]で、ファイルdata20を開く。これでHyperTerminal の上部に(要スクロール)データが現れる(コピー可。しかし、テキスト形式ではないようだ)。
下の写真は、実際にPC側のHyperTerminal上から出した指令”a”に応答して、PIC側から送られてきたデータの一例です。
プログラム
まず、内蔵のUSARTを使うための各種の設定をし、さらに10-bitのADCや使用するポートの設定をした後に、メインプログラムが続く。PCとPICをRS232ケーブルでつなぎ、電源を入れ、HyperTerminalを開き通信速度(ここでは、2400
bpsに設定)などを設定した後に、リターンキーを押す。これでPCとPICがつながる。PIC側のリセットキーを押すと、"Enter <a>
to get ADC value"と表示されるので、"a" をキーボードから入力すると、Step No., Volt (V) およびTemp(C)の3つの値が表示される。Step
No.とは、5.0 Vの最大入力値(RA0からの)を1023 (10 bits)と対応させたもので、入力値が2.5 Vなら1023x2.5/5.0=512となる。ADCINというコマンドを実行するとRA0(ピン2)からの入力電圧がstep数の形で取り込まれる。Volt
(V)はそのstep数をvoltageの値に換算したもの。また、Temp(C)の値は、前の“電子工作6”で使用した温度センサLM35DZの出力をアンプ(LMC662CN)で5倍に増幅して、RA0(ピン2)に入力する状況を想定した値である。つまり、LM35DZの出力は10mV/°C。したがって、温度100°Cでは、1.0
Vになり、5倍に増幅すると、5.0 VとADC基準電圧5.0 Vと等しくなる。このことは、温度100°Cではstep数が1023になることを意味している。
'RS16877q.bas 05.6.6
'Exp on communication between PIC16F877 and MicroCode
Studio or
'HyperTerminal of Windows XP via RS232C
'*****************************************************************
'This prog is to read analog data (voltage)
through RA0 (Pin2), send
'them to PC for their display as Step No.,
Voltage value and Temp.
'(running at 2400 baud): Here a Temp data
displayed is when an input voltage
' range is assumed to be 0-5.00 V for 0-1023
(LM35DZ+LMC662CN-Op-amp).
'See my Project Note: 04.8.15
'Connection
*************************************************
'TX-Pin25(RC6), RX-Pin26(RC7), Anal. voltage
input-Pin2 (RA0)
'Defines & settings
*****************************************
DEFINE OSC 4 'Using
4MHz oscillator
DEFINE HSER_RCSTA 90h 'Enable Serial PORT
'Enable continuous receive
DEFINE HSER_TXSTA 20h 'Enable transmit
'Low baud rate (BRGH=0)
DEFINE HSER_SPBRG 25 'Set USART to 2400 baud (when BRGH=0)
DEFINE HSER_CLROERR 1
DEFINE ADC_BITS 10 '10-bit result
DEFINE ADC_SAMPLEUS 30 '30 usec acquisition
(settling) time
ADCVal VAR WORD ‘16-bit variable
Result var word
ResultT var word
ADCON0=%10000000 'ADCS2=0, Fosc/32, CH0(RA0), Vref+=Vdd, Vref-=Vss
ADCON1=%10000000 'Right justify result
'PROGRAM ****************************************************
HSEROUT ["Enter <a> to get ADC
value:",10,13]
'This phrase is
displayed after PIC's Reset Power ON.
Loop:
Loop2:
Pause 100
'Wait 0.1 sec
Hserin 1000, Loop2, [WAIT ("a")]
'Wait the input of
"a" from PC through Loop2. If "a" is
'received, jump to
ADCCAL.
Pause 100
ADCCAL:
ADCIN 0, ADCVal 'Read an anlog value from RA0 (Pin 2) and store
'it as ADCVal
Pause 50
HSEROUT ["Step No.=", DEC ADCVal,10,13]
'Send the
phrase and the decimal value of ADCVal (0 - 1023)
'PC for
display.
Result=ADCVal*/12500 'Trick to convert ADCVal to a Voltage value
Result=(Result+5)/10 'Trick to round a Voltage value (See 04/8/16)
ResultT=Result*2
HSEROUT ["Volt(V)= ", DEC Result Dig
3,".", Dec Result Dig 2,Dec Result Dig 1,10,13]
'Digit
adjustment to display like Volt(V)= 2.55
HSEROUT ["Temp(C)= ", DEC ResultT Dig
3,Dec ResultT Dig 2,".",DEC ResultT Dig 1,10,13]
'Digit
adjustment to display like Temp(C)= 44.3
Goto Loop
End
'Note: The config. values for AKI Programmer are
all default values.
追加プログラム(改良版)
上のプログラムを改良して、データを長時間にわたって、一定の時間間隔でサンプリング出来るようにした。下の写真はHyperTerminal上での、60-min間隔でのサンプリングの例で、 0-min、 60-min、 120-minと3回データをサンプリングしています。プログラムは24時間連続サンプリングするように組んであります。
PIC側でResetを押すと、上記の1行目が現れ、“s”を入力すると、選択項目が現れる。そして、“l”を入力すると、”a”を押すごとに、その時の外部からの入力電圧値が表示される。その他の項目では、選択した時間間隔(1,
5, 15, 30, 45, 60 min間隔)でデータをサンプルし、表示する。この画面の例では、時間間隔60 minが選択され、0 min, 60 min,
120 min のようにサンプリングされていく。ただし、ソフト的に時間間隔をつくり出しているので、精度はあまり良くない(誤差10sec/hour位)。
以下のように、PIC16F877の約半分弱のメモリを使うほどの、結構長いプログラムになってしまった。PC側からの入力文字に応じて、いろんなところにジャンプさせ、結果を表示させるロジックの作成が要点であった。
'RS16877v.bas 2005.7.1
'Modification of RS16877u.bas
'Communication between PIC16F877 and HyperTerminal of Windows XP
'via RS232C
'*****************************************************************
'This prog is to read analog data (voltage) through RA0 (Pin2), send
'them to PC for their display as Voltage value (running at 2400 baud).
'Here a voltage data displayed is when an input voltage
' range is assumed to be 0-5.00 V for 0-1023 steps.
'See my Project Note: 04.8.15
'This time it was designed to be able to choose the measuring interval
of
'ADC data (l,m,n,o,p,q,r = 7 interval modes) on HyperTerminal.
'******Connection *********
'TX-Pin25(RC6), RX-Pin26(RC7), Anal.volt input-Pin2 (RA0)
'******Defines & settings *****
DEFINE OSC 4 'Using 4MHz oscillator
DEFINE HSER_RCSTA 90h
'Enable Serial PORT
'Enable continuous receive
DEFINE HSER_TXSTA 20h
'Enable transmit
'Low baud rate (BRGH=0)
DEFINE HSER_SPBRG 25
'Set USART to 2400 baud (when BRGH=0)
DEFINE HSER_CLROERR 1
DEFINE ADC_BITS 10 '10-bit result
DEFINE ADC_SAMPLEUS 30 '30 usec acquisition (settling) time
TimeIntval var byte 'Variable
ADCVal VAR WORD '16-bit ADC value read by ADCIN
command
Result var word
ResultT var word
CharVal var byte 'Character value
to be read
t var byte
'All shown below are variables.
t1 var byte
t2 var byte
t3 var byte
t4 var byte
t5 var byte
t6 var byte
t7 var byte
t8 var byte
t9 var byte
t10 var byte
ADCON0=%10000000 'ADCS2=0, Fosc/32, CH0(RA0), Vref+=Vdd, Vref-=Vss
ADCON1=%10000000 'Right justify result
'******PROGRAM********
HSEROUT [10,13]
'Line feed and carriage return (1-line space)
HSEROUT ["Enter <s> to start ADC Program",10,13]
Start:
Hserin 1000, Start, [WAIT ("s")] 'Start 'Wait <s> while looping
Pause 50
HSEROUT ["Enter Time Interval value <x> for ADC Data
Collection",10,13]
HSEROUT ["Enter <l> for data collection at any
time",10,13]
HSEROUT ["Enter <m> for 1-min interval",10,13]
HSEROUT ["Enter <n> for 5-min interval",10,13]
HSEROUT ["Enter <o> for 15-min interval",10,13]
HSEROUT ["Enter <p> for 30-min interval",10,13]
HSEROUT ["Enter <q> for 45-min interval",10,13]
HSEROUT ["Enter <r> for 60-min interval",10,13]
Pause 50
Start1:
Hserin 50, Start1, [CharVal] 'Routine to wait the entry of letter <x>
IF CharVal="l" then Jumpl 'while forming a loop (Start1)
IF CharVal="m" then Jumpm
IF CharVal="n" then Jumpn
IF CharVal="o" then Jumpo
IF CharVal="p" then Jumpp
IF CharVal="q" then Jumpq
IF CharVal="r" then Jumpr
goto Start1
Jumpl: TimeIntval=0
HSEROUT [10,13]
'Line feed and carriage return
HSEROUT ["Time Interval=none",10,13] 'Display "TimeIntval=0
min"
goto ADCprocess
Jumpm: TimeIntval=1
HSEROUT [10,13]
HSEROUT ["Time Interval=1 min",10,13]
goto ADCprocess
Jumpn: TimeIntval=5
HSEROUT [10,13]
HSEROUT ["Time Interval=5 min",10,13]
goto ADCprocess
Jumpo: TimeIntval=15
HSEROUT [10,13]
HSEROUT ["Time Interval=15 min",10,13]
goto ADCprocess
Jumpp: TimeIntval=30
HSEROUT [10,13]
HSEROUT ["Time Interval=30 min",10,13]
goto ADCprocess
Jumpq: TimeIntval=45
HSEROUT [10,13]
HSEROUT ["Time Interval=45 min",10,13]
goto ADCprocess
Jumpr: TimeIntval=60
HSEROUT [10,13]
HSEROUT ["Time Interval=60 min",10,13]
goto ADCprocess
ADCprocess:
Pause 50
HSEROUT ["Enter <a> to get ADC values:",10,13]
'This phrase is
displayed after PIC's Reset Power ON.
If TimeIntval=0 then Timer0
If TimeIntval=1 then Timer1
If TimeIntval=5 then Timer5
If TimeIntval=15 then Timer15
If TimeIntval=30 then Timer30
If TimeIntval=45 then Timer45
If TimeIntval=60 then Timer60
goto Start1
Timer0:
LoopT0:
Hserin 1000, LoopT0, [WAIT ("a")] '”a” entry waiting loop
'Wait the input of
"a" from PC through Loop2. If "a" is
'received, jump to
ADCIN0.ADCVal
Pause 100
'Wait 0.1 sec
ADCIN 0, ADCVal
'Read an anlog value from RA0 (Pin 2) and store
'it as ADCVal
Pause 50
Gosub Resultcal
‘Subroutine for calculation
HSEROUT ["Volt(V)=", DEC Result Dig 3,".", Dec
Result Dig 2,Dec Result Dig 1,10,13]
'Digit
adjustment to display like Volt(V)= 2.55
Goto Timer0
Timer1: '1-min
routine
LoopT1
Hserin 1000, LoopT1, [WAIT ("a")]
Pause 100
For t=0 to 60
'Max collection time 0 - 180 min (3 hours)
ADCIN 0, ADCVal
Pause 50
HSEROUT ["Time elapsed=", DEC t, " min",10,13] ‘Display decimal t value.
Gosub Resultcal
HSEROUT ["Volt(V)=", DEC Result Dig 3,".", Dec
Result Dig 2,Dec Result Dig 1,10,13]
Pause 60000 '60-sec timer
Next t
goto completed
Timer5: '5-min
routine
LoopT2:
Hserin 1000, LoopT2, [WAIT ("a")]
Pause 100
For t1=0 to 72
'Max collection time - 6 hours
ADCIN 0, ADCVal
Pause 50
HSEROUT ["Time elapsed=", DEC t1*5, "
min",10,13] '0*5 all right?
Gosub Resultcal
HSEROUT ["Volt(V)=", DEC Result Dig 3,".", Dec
Result Dig 2,Dec Result Dig 1,10,13]
For t2=1 to 5
'5-min timer
Pause 60000
Next t2
Next t1
goto completed
Timer15: '15-minute
routine
LoopT3:
Hserin 1000, LoopT3, [WAIT ("a")]
Pause 100
For t3=0 to 24
'Max collection time - 6 hours
ADCIN 0, ADCVal
Pause 50
HSEROUT ["Time elapsed=", DEC t3*15, "
min",10,13]
Gosub Resultcal
HSEROUT ["Volt(V)=", DEC Result Dig 3,".", Dec
Result Dig 2,Dec Result Dig 1,10,13]
For t4=1 to 15
'15-min timer
Pause 60000
Next t4
Next t3
goto completed
Timer30: '30-minute
routine
LoopT4:
Hserin 1000, LoopT4, [WAIT ("a")]
Pause 100
For t5=0 to 24
'Max collection time - 12 hours
ADCIN 0, ADCVal
Pause 50
HSEROUT ["Time elapsed=", DEC t5*30, "
min",10,13]
Gosub Resultcal
HSEROUT ["Volt(V)=", DEC Result Dig 3,".", Dec
Result Dig 2,Dec Result Dig 1,10,13]
For t6=1 to 30
'30-min timer
Pause 60000
Next t6
Next t5
goto completed
Timer45: '45-minute
routine
LoopT5:
Hserin 1000, LoopT5, [WAIT ("a")]
Pause 100
For t7=0 to 16
'Max collection time - 12 hours
ADCIN 0, ADCVal
Pause 50
HSEROUT ["Time elapsed=", DEC t7*45, "
min",10,13]
Gosub Resultcal
HSEROUT ["Volt(V)=", DEC Result Dig 3,".", Dec
Result Dig 2,Dec Result Dig 1,10,13]
For t8=1 to 45
'45-min timer
Pause 60000
Next t8
Next t7
goto completed
Timer60: '60-minute
routine
LoopT6:
Hserin 1000, LoopT6, [WAIT ("a")]
Pause 100
For t9=0 to 24
'Max collection time - 24 hours
ADCIN 0, ADCVal
Pause 50
HSEROUT ["Time elapsed=", DEC t9*60, "
min",10,13]
Gosub Resultcal
HSEROUT ["Volt(V)=", DEC Result Dig 3,".", Dec
Result Dig 2,Dec Result Dig 1,10,13]
For t10=1 to 60
'60-min timer
Pause 60000
Next t10
Next t9
goto completed
'Subroutine
Resultcal:
Result=ADCVal*/12500
'Trick to convert ADCVal to a Voltage value
Result=(Result+5)/10
'Trick to round a Voltage value (See 04/8/16)
Return
completed:
End
'Note: The config. values for AKI Programmer are all default values.
余談
スタート時に、どうやってPICにResetをかけるか(Pin
1を短時間“Low”に落とす、または、電源を入れる)(PICがPCから離れているところに置かれていると仮定して)や、ソフトウエアタイマーの精度などに、まだ問題がある。しかし、回路が簡単で安く製作でき、しかもWindows搭載のHyperTerminalが使えるので、手っとり早くデータを収集する目的には使えそうです。
通信実験を始めて約2週間、やっとPICからHyperTerminal上に簡単なメッセージ(”The
quick d for jumps”)を送ることに成功した時は、感激!。当たり前だが、知らないことをマスターするのはヤッパリ大変。巷に張り巡らされた様々な巨大なネットワーク網、人間様の英知とは凄いもの、感心するばかりです。