PIC16F877A(PICマイコン)
TEditorMX(テキストエディタ)をご活用頂ければと思いまして、PIC16F877A(PICマイコン)です。
Z80ボード同様にI/O入出力とシリアル通信を行おうと思います。
PICマイコンはI/OやA/Dコンバータ、シリアル通信、タイマなどが内蔵されているため、
その範囲内で使用する分には比較的簡単な回路構成で済んでしまいます。
私(管理人)はPICを使うのは今回が全くの初めてでしてRISC CPU(PIC)のアセンブラでプログラムするのも初めてです。
PICでプログラムを作成するにはどうするのが早道かと考えて、
できればC言語でサクッと終わらせたかったのですがそれではPICのことを全く理解できずに何もできない...、
というわけで仕方なくアセンブリ言語を理解することから始めることにしました。
PICはRISC CPUなので命令が簡単(単純)なものしかなく、ニーモニックもちょっと分かりづらかったので「どうしよう...」
と思いながらPIC16F877Aのニーモニック表を見ていたらちょっとした規則に気付きまして「これなら簡単に理解できそう」
ということになりました。で、その規則ですが、非常に簡単で
ニーモニックの先頭に機能が付いてその後に操作対象を示す記号が付くことが多いということです。
例えば、
MOV | 移動 |
ADD | 加算 |
SUB | 減算 |
などの後に続くのは、
LW | 定数 → ファイルレジスタ |
WF | W → ファイルレジスタ |
F | ファイルレジスタ → W or F(ファイルレジスタ) |
の3種類です。
そう分かると少ない命令が更に少なくなって、ニーモニックも比較的簡単に理解できそうです。
...というわけですので頑張って少しだけ覚えてくださいね。
今回作成するプログラムで使用する命令は、
移動命令 | MOVxx |
分岐命令 | BTFSS, BTFSC |
ジャンプ命令 | GOTO |
ビットセット、クリア命令 | BSF, BCF |
クリア命令 | CLRx |
スワップ命令 | SWAPF |
サブルーチン命令 | CALL, RETURN |
割り込み復帰命令 | RETFIE |
疑似命令 | ORG, EQU, BANKSEL, END |
くらいです。
その他の疑似命令もありますがとりあえず「おまじない」とお考え頂ければ大丈夫だと思います。
もう1つ理解しなければならないことがあります。
それはメモリ構成です。
PIC16F877Aにはプログラムを書き込むEEPROM(8KW), RAM&I/O(総称としてレジスタまたはファイルレジスタと呼ばれているようです)(4バンク), スタック(8段)
です。プログラムを書き込むためのEEPROMは8KWありますが、2KW×4ページという構成になっています。
小さいプログラム(2KW以下)であれば「ページ」を気にする必要はありません
(ちなみに2KWの「KW」は「キロワード」の略でPIC16F877Aの命令長は14ビットで1ワードです)。
RAM&I/Oはファイルレジスタと呼ばれているようで全部で255バイト×4バンクしかありません。
スタックについては8段しかなく、それ以上サブルーチン(割り込みも含む)を呼び出してしまうと正常に動作しません。
ファイルレジスタの詳細については解説書かマニュアルをご参照ください。
マニュアル(英語)はインターネットで検索すると出てきます。
今回はアセンブリ言語でプログラムを作成しますので下記の2つのどちらかをダウンロードしてインストールしてください
(インターネットで検索すればすぐに出てくると思いますし、インストール方法も掲載しているホームページがあると思います。
ちなみに私は両方ともデフォルトの設定でインストールしてしまいました)。
(1)MPLAB XIDE (2019年6月現在、v5.20が無償で利用できます)に含まれているmpasmx.exeとインクルードファイル
(2)gputils (GNU PIC Utilities)
インストールしたら(1)の場合にはmpasmx.exeのあるフォルダにパスを通します。
mpasmx.exeは私(管理人)の場合には下記のフォルダにインストールされていました。
C:\Program Files (x86)\Microchip\MPLABX\v5.20\mpasmx
パスを通せばコマンドプロンプト(cmd.exe)でmpasmxと入力すると起動できます。
このページのサンプルプログラムをアセンブルするためのコマンドは、
mpasmx main.asm /p16f877a /q
とすればmain.hexというファイル(インテルヘキサファイル)が作成されてPICに焼くことができます。
インストールしたら(2)の場合にはgpasm.exeにパスが通っていると思いますのでコマンドプロンプト(cmd.exe)でgpasmと入力すれば起動すると思います。
このページのサンプルプログラムをアセンブルするためのコマンドは、
gpasm main.asm
とすればmain.hexというファイル(インテルヘキサファイル)が作成されてPICに焼くことができます。
以上でアセンブラに関してはOKです。
続いてハードウェア(自作する基板)です。
必要な(主な)部品は以下のようになります。
品名 | 型番など | 数量 | 購入先 |
---|---|---|---|
ユニバーサル基板 | CPU-133 | 1 | アマゾン |
CPU | PIC16F877A | 1 | 秋月電子通商 |
ICソケット | 40極 | 1 | 秋月電子通商 |
RS232CインターフェースIC | ICL3232CPZ | 1 | 秋月電子通商 |
トランジスタアレイ | TD62083APG | 1 | 秋月電子通商 |
セラミック発振子 | 10MHz | 1 | 秋月電子通商 |
LED | 9 | 秋月電子通商 | |
抵抗(1/4W) | 10KΩ | 9 | 秋月電子通商 |
抵抗(1/4W) | 100KΩ | 1 | 秋月電子通商 |
抵抗(1/4W) | 4.7KΩ | 1 | 秋月電子通商 |
抵抗(1/4W) | 470Ω | 9 | 秋月電子通商 |
積層セラミックコンデンサ | 0.1μF | 5 | 秋月電子通商 |
積層セラミックコンデンサ | 1μF | 1 | 秋月電子通商 |
ダイオード | 1S1588相当品 | 1 | 秋月電子通商 |
コネクタ | XG4M-1030-T | 1 | 楽天 |
コネクタ | DSUB 9pin メス | 1 | アマゾン |
PCと接続するためのRS-232Cクロスケーブル 1本
ケーブルは購入しても良いですが、自作してしまっても良いのではないかと思います。
今回使用するクロスケーブルの結線図は下図の通りです(Z80で使用したものと同じものです)。
その他必要なものは手持ちであったものを使用しました。
(1)スイッチの入力とLEDへの出力
まずは簡単なディップスイッチの入力とLEDへの出力です。
回路は下図のようになります。
制作した基板(I/Oプログラム動作中)は下図のようになりました。
(下図には次に使用するRS-232CインターフェースICも取り付けてあります)。
PORTDでスイッチの入力を受けてPORTBへ出力します。
アセンブリ言語プログラムは以下のようになりました(io.zip)。
LIST p=16f877a #INCLUDE p16f877a.inc ; __CONFIG _HS_OSC & _CP_OFF & _PWRTE_OFF & _WDT_OFF & _LVP_OFF __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_OFF & _BOREN_OFF & _LVP_OFF & _CPD_OFF & _WRT_OFF & _CP_OFF ;------------------------------------------------------------- ;初期化 ;------------------------------------------------------------- ; BANK0選択 BCF STATUS, RP0 BCF STATUS, RP1 ;念のためI/Oポート出力を0に CLRF PORTA CLRF PORTB CLRF PORTC CLRF PORTD CLRF PORTE ;BANK1を選択 BSF STATUS, RP0 ;PORTDを入力に設定 MOVLW 0xff MOVWF TRISD ;PORTD以外をすべて出力に設定 CLRF TRISA CLRF TRISB CLRF TRISC CLRF TRISE ;BANK0を選択 BCF STATUS, RP0 ;------------------------------------------------------------- ;メインループ ;------------------------------------------------------------- main: ;PORTDを読み込み、そのままPORTBに出力 MOVF PORTD, w MOVWF PORTB GOTO main END
上記プログラムでは使用していないポートはすべて出力に設定していて、出力はLowレベル(0)にしています。
これは未使用の入力ポートをオープンにしたままだとCPU内部で大電流が流れる可能性があって最悪の場合、
PICが壊れてしまうことがあるために行っています。
そのため、テスターやプローブなどを接続するときは気を付けて下さいね。
基板に電源を入れ、ディップスイッチをON/OFFするとそれに合わせてLEDが消灯/点灯します。
(2)シリアル通信
次はシリアル通信プログラムです。
受信には割り込みを使用します。
送信にも割り込みを使用するつもりでいたのですが、
(たぶんハードバグではないかと思いますが)上手く動作させることができませんでしたので割り込みは使用していません。
まぁPIC16F877Aの場合、RAMの容量も少ないので長い文の通信はしない方が良いかと思いますので良しとします。
今回のプログラムは割り込みで受信した文字をオウム返しで送信するだけのものです。
多分このままでは機能的に不十分だと思いますので改造してご利用下さいね。
回路図は下図のようになります。
制作した基板は(1)スイッチの入力とLEDへの出力で使用した基板とまったく同じものです。
(下図はI/Oプログラム動作中です)
アセンブリ言語プログラムは以下のようになりました(serial.zip)。
;------------------------------------------------------------- ; PIC16F877A 10MHz用 ; RS-232C 送受信サンプルプログラム ; 受信した文字をオウム返しで送信する ; 受信のみ割り込みを使用 ; ; アセンブル方法 ; mpasmx main.asm /p16f877a /q ; gpasm main.asm ; ; 確認している不具合が1点あります。 ; (1)電源投入時にゴミデータが1バイトくらい相手側に送信されてしまいます。 ; PIC16F887Aの問題(ハードバグ)なのかもしれません。 ; 回避方法として簡単なのは「起動メッセージ」を出力してそれ以降のデータを有効とする方法が考えられます。 ; ; その他 ; 送信側も割込みを使用することを試みたのですが、上手くいきませんでした。 ; 多分ハードバグが原因だと考えています。 ;------------------------------------------------------------- ; ERRORLEVEL -302 ;mpasmx.exe で Message 302 を出力しないように指定 ERRORLEVEL -1302 ;gpasm.exe で Message 1302 を出力しないように指定 LIST p=16f877a #INCLUDE p16f877a.inc ; __CONFIG _HS_OSC & _CP_OFF & _PWRTE_OFF & _WDT_OFF & _LVP_OFF __CONFIG _FOSC_HS & _WDTE_OFF & _PWRTE_ON & _BOREN_ON & _LVP_OFF & _CPD_OFF & _WRT_OFF & _CP_OFF ;レジスタ(バンク切り替えに影響しないレジスタへ割り当て) RXTMP EQU 0x7c PCLATH_TEMP EQU 0x7d W_TEMP EQU 0x7e STATUS_TEMP EQU 0x7f ORG 0x0 GOTO init ;初期化部へジャンプ ;------------------------------------------------------------- ; 割り込みルーチン ; PIC16F877A(だけじゃないかも)は0x04番地が唯一の割り込み開始 ; アドレスです ;------------------------------------------------------------- ORG 0x4 ;割り込み開始アドレス ; BCF INTCON, GIE ; di MOVWF W_TEMP ; WやSTATUSレジスタ等を退避 SWAPF STATUS, W ; CLRF STATUS ; MOVWF STATUS_TEMP ; MOVF PCLATH, W ; MOVWF PCLATH_TEMP ; CLRF PCLATH ; ;割り込み要因チェック BTFSS PIR1, RCIF GOTO INT_NEXT1 ; BCF PIR1, RCIF ;割り込み要因フラグクリア ;1バイト受信 MOVF RCREG,W ;受信した文字を取り込み ; MOVWF PORTB ;そのままPORTBへ出力 MOVWF RXTMP ;バッファへ保存 GOTO INT_NEXT1 INT_NEXT1: INT_END: ; 割り込みルーチン終了処理 ; BSF INTCON, GIE ; ei MOVF PCLATH_TEMP, W ; WやSTATUSレジスタ等を復帰 MOVWF PCLATH ; SWAPF STATUS_TEMP, W ; MOVWF STATUS ; SWAPF W_TEMP, F ; SWAPF W_TEMP, W ; RETFIE ; 割り込みルーチンからリターン ;------------------------------------------------------------- ; 初期化 ;------------------------------------------------------------- init: ; BANK0選択 BANKSEL PORTA ;念のためI/Oポート出力を0に CLRF PORTA CLRF PORTB CLRF PORTC CLRF PORTD CLRF PORTE ;BANK1を選択 BANKSEL TRISD ;PORTDを入力に設定 MOVLW 0xff MOVWF TRISD ;PORTC,PORTD以外をすべて出力に設定 CLRF TRISA CLRF TRISB CLRF TRISE ;RS232C設定 ;RCSTA BANKSEL RCSTA MOVLW B'10010000' MOVWF RCSTA ;TXSTA BANKSEL TXSTA MOVLW B'00100110' ; MOVLW B'00100100' MOVWF TXSTA ;SPBRG BANKSEL SPBRG MOVLW D'64' MOVWF SPBRG ;BANK1を選択 BANKSEL TRISC ;PORTCのbit7は入力、それ以外は出力に設定 MOVLW B'10000000' MOVWF TRISC ;割り込み設定 ;PIE1 BANKSEL PIE1 MOVLW B'00100000' ;とりあえず受信割り込みだけ設定 ; MOVLW B'00010000' ;送信割り込みだけ設定 ; MOVLW B'00110000' ;送受信割り込みはこっち MOVWF PIE1 ;INTCON BANKSEL INTCON MOVLW B'11000000' ;割り込み許可(bit7は全体の割り込み許可) MOVWF INTCON ;BANK0を選択 BANKSEL PORTA ;PORTA は BANK0 ;------------------------------------------------------------- ;メインループfdsfgdsggds ;------------------------------------------------------------- main: MOVF RXTMP, W BTFSC STATUS, Z ;RXTMPが0のときはmainに戻る GOTO main CLRF RXTMP ;受信データを0に(重複送信を防ぐため) CALL txchar ;Wの値(文字)を送信 GOTO main ;------------------------------------------------------------- ; 1文字送信 ;------------------------------------------------------------- txchar: ; MOVWF PORTB ;そのままPORTBへ出力 BTFSS PIR1, TXIF GOTO $-1 MOVWF TXREG RETURN END
プログラムについて少しだけ解説します。
シリアル通信を行うための設定は、
TXSTA | 送信とボーレート関連の設定 |
RCSTA | 受信他の設定 |
SPBRG | ボーレート設定 |
を設定すればOKです。
また割り込みを使用する場合には更に、
PIE1 | 個別に割り込み許可 |
INTCON | 全体的な割り込み許可 |
を設定すればOKです。
本プログラムでの通信仕様は、
ボーレート | 9600bps |
ビット数 | 8 |
ストップビット | 1 |
パリティ | なし |
同期/非同期 | 非同期 |
フロー制御 | なし |
です。
割り込みについてですが、
PIC16F877Aの割り込み開始アドレスは0x4固定ですべての割り込み共通です。
そのため割り込みルーチン内で何の割り込みで呼び出されたのかをチェックする必要があります。
割り込みルーチンでは、使用するレジスタを退避・復帰する必要がありますが、そのやり方は下記のようになります。
PCLATH_TEMP EQU 0x7d W_TEMP EQU 0x7e STATUS_TEMP EQU 0x7f ・ ・ ・ ORG 0x4 ; 割り込みルーチン開始アドレス ;退避処理 MOVWF W_TEMP ; WやSTATUSレジスタ等を退避 SWAPF STATUS, W ; CLRF STATUS ; MOVWF STATUS_TEMP ; MOVF PCLATH, W ; MOVWF PCLATH_TEMP ; CLRF PCLATH ; ; ; ここに割り込み処理を記述します ; ;退避処理 MOVF PCLATH_TEMP, W ; WやSTATUSレジスタ等を復帰 MOVWF PCLATH ; SWAPF STATUS_TEMP, W ; MOVWF STATUS ; SWAPF W_TEMP, F ; SWAPF W_TEMP, W ; RETFIE ; 割り込みルーチンからリターン
スタック用のメモリがたくさんあって退避・復帰が簡単なCPUでしたらもっとスマートなやり方ができるのですがRISC CPUだからなのでしょうか?...
いや多分PICだからなのでしょうが、ちょっとスマートではないような気がします...が、このやり方はマニュアルに記述されていますので正しいやり方です。
受信割り込み処理では受信したデータをRXTMP(ファイルレジスタ)に保存するだけで終わりです。
メインループ側ではRXTMPに0以外のデータ(値)が入っているときにはその文字を送信します。
送信したら(正確には送信データとしてWレジスタにコピーしたら...ですが)RXTMPに0を代入して次のデータを待ちます。
このプログラムでは正常な受信データには0は含まれていないものとしています。
通常はデバッグ作業を容易にするためバイナリデータはテキストデータに変換して送受信するのが一般的です。
そのためこれは「仕様」ということになります。
基板が出来上がって、シリアル通信プログラムをPIC16F877Aに焼いて基板に取り付けたらPCと接続します。
あとはPC上でターミナルソフトを起動し、基板の電源を入れればキーボードから入力した文字がターミナルソフト上に表示されると思います。
ちょっとした不具合があるのですが、基板に電源を入れたときにゴミデータが1バイトくらい出力されてしまうようです。
ゴミデータが出力されないようにいろいろ対策を試みたのですがどうしようもない感じなのです。
なので、実際に使用する場合には起動メッセージなどを出力するようにしてそれ以前に出力されたデータはゴミデータとして処理しないようにするなどの工夫が必要になると思われます。
そうそう、書き忘れましたが基板に供給する電源はDC5Vにして下さい。
今回、私(管理人)が使用したターミナルソフトはフリーソフトの Multi Port Terminal ver1.00 (金澤ソフト設計さん)を利用させて頂きました。
Vectorさんよりダウンロードさせて頂きました。
最後に、このページからダウンロードできるソースプログラムに関してはフリーウェアとします。
よろしければご自由にお使い下さい。