PIC16F18313の基本動作から応用プログラムまでを学びます。

ホーム
12F1822
16F1455
16F1459
18F14K50
18F26J50
dsPIC
その他
    
16F18313
16F18325
16F18346
16F1619
Curiosity
---
---
シリアル入力LCD
2020−06−14

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