12F18313のUARTとI2C通信機能を使用し、非同期のシリアル信号入力を持つLCD表示器を作成します。マイコンからの情報を少ない配線数で表示でき、マイコン側のソフトウエアも単純なプログラムで処理できる表示ユニットです。ASCII文字以外は、画面表示に最低限必要と思われる制御コードを受け付けます。非同期受信は、パリティーなし、9600bpsのロジックレベル信号の入力とします。文字を表示するLCDは、秋月の8文字x2行のI2CLCD表示器を使いましたが、他のI2CLCD表示器の多くも、同じ制御コードを持っているため、同一のプログラムで使用可能です。また、RA0をGNDに接続すると、16 x 2 サイズのLCD表示機も使用できるようプログラムしてあります。
| 制御コード | 動作 |
|---|---|
| 0x00 | 何もしない |
| 0x01, 0x0C (\f) | クリア 全表示をクリアし、カーソルを上段先頭にする |
| 0x02, 0x07 (\a) | カーソルを上段先頭にする |
| 0x03, 0x08 (\b) | カーソルを下段先頭にする |
| 0x0A (\n) | 上段の場合、カーソルを下段先頭にする 下段の場合、上にスクロールしカーソルを下段先頭にする |
| 0x0D (\r) | カーソルを段の先頭にする |
| 0x8X | カーソルを上段 X番目(0-F)にする |
| 0x9X | カーソルを下段 X番目(0-F)にする |
| 0x20-0x7F | ASCII文字(英数字)を表示 |
| 0xA0-0xFF | ASCII文字(カタカナ、特殊文字)を表示 |
<回路図>
I2CのSDA及びSCLラインにはプルアップ抵抗を取り付ける必要があります。この例ではPICの弱プルアップ機能をONにして対応しています。
mainプログラムだけを示します。他のファイルは下のアイコンからダウンロードしてください。
UARTが、9600 BAUDのシリアルデータを受信すると割り込みが発生し、RxBuffer配列にデータを保存します。メインの繰り返しルーティン内で RxBufferを常時モニターしており、データが新たに保存されると一文字毎にRxBufferから取り出して、処理していきます。また、表示できる文字数を越えると、上にスクロールして新たな行にデータを表示します。
シリアル入力LCDソースファイル b06SerialLCD.zip |
/*************************************
* File: UART RX sample
* System ClockはConfigで内部1MHzに設定
* UARTで受信したシリアルデータをLCDに表示する
* SCL:RA4 SDA:RA5
* Serial In : RA2
* Overrun : RA1
* LCD Sel : RA0
* PIC16F18313
* Created on July 2, 2020
**************************************/
// LCD 1行の表示文字数
//#define maxDigit 8
// RA0 Openで 1行 8文字
// RA0 GND で 1行16文字
#include <xc.h>
#include "i2cLCD.h"
#pragma config FEXTOSC = OFF,RSTOSC = HFINT1 // HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF,CSWEN = OFF,FCMEN = OFF
#pragma config MCLRE = ON,PWRTE = OFF,WDTE = OFF,LPBOREN = OFF
#pragma config BOREN = OFF,BORV = LOW,PPS1WAY = OFF,STVREN = ON
#pragma config DEBUG = OFF
#pragma config WRT = OFF,LVP = ON,CP = OFF,CPD = OFF
void dspClr(void);
void mov0_0(void);
void mov1_0(void);
void mov_LF(void);
void mov_CR(void);
void movPos(char);
void dataRx(char);
// ****************** 共通変数 ***********************************
char RxBuffer[16];
char ptr_W = 0;
char ptr_R = 0;
char Xpos=0;
char Ypos=0;
char LineBuff[]=" ";
char maxDigit;
// ******************* main *************************************
void main() {
char RxData;
char PWRUP=1;
TRISA = 0b111101; // RA1出力 他は入力
LATA = 0; // Port 出力 0
ANSELA = 0; // すべてデジタル
WPUA = 0b111111; // RA2を弱プルアップ
RXPPS = 0x02; // RA2をRX入力とする
if(RA0) maxDigit = 8; // RA0 Openで 1行 8文字
else maxDigit = 16; // RA0 GND で 1行16文字
// --------------------------------------------------------
i2c_MasterInit(100000); // I2Cを初期化
LCD_int(); // LCDを初期化
LCD_ROMstr("SerialRx"); // タイトル表示
LCD_posyx(1,0); // 下段にカーソル移動
if(RA0) LCD_ROMstr("9600 2x8");
else LCD_ROMstr("9600 2x16");
//------------ Initialize UART ----------------------------------
RC1STA = 0b10010000; // 8 bit 連続受信
TX1STA = 0b00000100; // 非同期受信 High Baud Rate選択
BAUD1CON = 0b00001000; // 16 bit SPBRG
SP1BRGH = 0; // 9600baud
SP1BRGL = 25;
// ----------- 割込み --------------------------------------------
IDLEN = 1; // Idle(UARTは動作)に指定
RCIE = 1; // UART割込み許可
PEIE = 1; // 周辺割込み許可
GIE = 1; // 割込み許可
// ----------- 繰返し --------------------------------------------
while(1){
if(ptr_R == ptr_W){ // 新しいデータを受信したか
SLEEP(); // 受信していない場合
NOP(); // 受信まで == IDLE ==
continue; // 受信待ちに
}
// 新しいデータを受信した --------------------------------------
RxData = RxBuffer[ptr_R++]; // 受信データ読込
if(ptr_R >= 16)ptr_R = 0; // 読込ポインタを進める
// 電源ON直後なら画面クリア ------------------------------------
if(PWRUP){ // 電源ON直後なら
PWRUP = 0; // 電源ON フラグOFF
LCD_clr(); // 画面クリア
}
// データの処理 -----------------------------------------------
if(RxData == 0x01) dspClr(); // 0x01なら
else if(RxData == 0x02) mov0_0(); // 0x02なら
else if(RxData == 0x03) mov1_0(); // 0x03なら
else if(RxData == 0x07) mov0_0(); // 0x07 \aなら
else if(RxData == 0x08) mov1_0(); // 0x08 \bなら
else if(RxData == 0x0A) mov_LF(); // 0x0A \nなら
else if(RxData == 0x0C) dspClr(); // 0x0C \fなら
else if(RxData == 0x0D) mov_CR(); // 0x0d \rなら
else if((RxData>=0x80)&&(RxData<=0x9F)) // 0x80-9F なら
movPos(RxData);
else if(RxData >= 0x20) dataRx(RxData); // 表示データなら
} // while
} // main
// 画面クリアし表示位置を0-0にする ***********************************
void dspClr(void){
LCD_cmd(0x01); // Clearコマンド出力
Xpos = 0;Ypos = 0; // 表示位置0-0に
for(char i=0;i<maxDigit;i++){ // 下段バッファに
LineBuff[i] = ' '; // スペース
}
}
// 表示位置を0-0にする **********************************************
void mov0_0(void){
LCD_posyx(0,0); // 上行目先頭に移動
Xpos = 0;Ypos = 0; // 表示位置0-0に
}
// 表示位置を1-0にする **********************************************
void mov1_0(void){
LCD_posyx(1,0); // 下行目先頭に移動
Xpos = 0;Ypos = 1; // 表示位置1-0に
}
// 表示位置を次の行の先頭にする ***************************************
void mov_LF(void){
if(Ypos == 1){ // 下段なら
LCD_clr(); // 画面クリア
for(char i=0;i<maxDigit;i++){ // 下段を上段にコピー
LCD_dat(LineBuff[i]); //
LineBuff[i] = ' '; // バッファにスペース
}
}
LCD_posyx(1,0); // 下行先頭に移動
Xpos = 0;Ypos = 1; // 表示位置 1-1に
}
// 表示位置を行の先頭にする *******************************************
void mov_CR(void){
LCD_posyx(Ypos,0); // 行先頭に移動
Xpos = 0;
}
// 表示位置を変更する **********************************************
void movPos(char RxData){
Xpos = RxData<=0x9F; // X表示位置
Ypos = (RxData&0x10)>>4; // X表示位置
LCD_posyx(Ypos,Xpos); // カーソル移動
}
// 文字データを表示する **********************************************
void dataRx(char RxData){
if(Xpos < maxDigit){ // 0-maxDigit文字目なら
LCD_dat(RxData); // 文字の表示
if(Ypos == 1){ // 下行なら
LineBuff[Xpos] = RxData; // バッファに書込む
}
Xpos++; // X位置 +1
}else{ // 9文字目なら
if(Ypos == 1){ // 下段なら
LCD_clr(); // 画面クリア
for(char i=0;i<maxDigit;i++){ // 下段を上段にコピー
LCD_dat(LineBuff[i]); //
LineBuff[i] = ' '; // バッファにスペース
}
}
LCD_posyx(1,0); // 下行先頭に移動
LCD_dat(RxData); // 文字の表示
LineBuff[0] = RxData; // バッファに書込む
Xpos = 1;Ypos = 1; // 表示位置 1-1に
}
}
// ------- Interrupt ----------------------------------
void __interrupt() Rx_isr(void) {
char RxData;
char ptr_N;
if(RCIF){ // UART1 Rxからの割込みを確認
if(RCSTAbits.OERR){ // Overrunエラーなら
RCSTAbits.CREN=0; // エラーをクリア
RCSTAbits.CREN=1; // 再起動
RxData = RCREG; // 空読み
LATA1 = 1; // エラー表示
}else{ // Overrunエラーなければ
RxData = RCREG; // 受信データ取込
ptr_N = ptr_W +1; // 受信ポインタの
if(ptr_N >= 16)ptr_N = 0; // 追い越確認
if(ptr_N == ptr_R){ // 追い越すなら
LATA1 = 1; // エラー表示しデータを捨てる
}else{ // 追い越さないなら
RxBuffer[ptr_W++] = RxData; // 受信データ書込
if(ptr_W >= 16)ptr_W = 0; // 書込ポインタを進める
}
}
} // if(RCIF)
} // __interrupt