8ピンなどの小さなマイコンでは使用するピン数が少ないUARTやI2Cなどのシリアル接続が便利です。

液晶キャラクタディスプレイも自作のモジュールでUART接続I2C接続にしてきましたが,どうせなら両方に対応できるモジュールが便利かと思い,試しに作ってみました。



サンライク社の液晶キャラクタディスプレイSC1602BSLB(パラレル接続)に作ったモジュールを組み込み,Raspberry Pi PicoからUARTとI2Cの両方の接続でデータを送り表示しているところです。一応動いています(^^)。






・モジュールの構成
 モジュールはUART接続として作っていたものにI2Cパートのハードとソフトを追加しました。ほぼATtiny404に配線してコネクタをつけた感じです(^^;;;;;;。

 MPU:ATtiny404,3.3V動作
 UART:Rxのみ使用,9600baud
 UPDI:プログラミング用
 I2C :SDA,SCL,Groveコネクタで接続
 抵抗:I2C PULL UP 4.7KΩ,バックライト可変抵抗 20kΩ
 コンデンサ:バイパスコンデンサ 104





・配線の概略とコマンドなど
 データバスは4bitモードで使用しています。
モジュールの機能は「どこに何を書くか」に絞っています(^^)。





無理やりI2Cパートを追加したので,配線はより分かり難くなりました(^^;;;;,。




幾つか同じ構成で作っておこうかと思うのですが,,,ディスプレイの裏側はもっと余裕があるのでもう少し大きめの基板にしないといけませんね(^^)。



・プログラムのメモ
 以下に1) モジュール と 2) 送信側のPicoのプログラム をメモしておきます。

1) モジュールのプログラム
 特に割り込みなどは使わず,普通にデータを待っています。

/** LCD1602 Controll test Pro., ATtiny404 UART & I2C **/
// UART: 9600baud, Rx_Pin PB3
// I2C : Slave Address 5, SCL_Pin PB0, SDA_Pin PB1
// 4Bit Bus: A4-A7, Enable A2, Resistor Select A1
// LCD : HD44780 compatible, number of lines 1-4

#include <Wire.h>

#define DB7 A7
#define DB6 A6
#define DB5 A5
#define DB4 A4
#define _E  A2
#define _RS A1

void setup() {              /* Set Up */

 pinMode(DB7, OUTPUT );    // Set Pins as output for 4bit Data Bus
 pinMode(DB6, OUTPUT );
 pinMode(DB5, OUTPUT );
 pinMode(DB4, OUTPUT );
 pinMode(_E, OUTPUT );     // Data Enable (Latch), High -> Low
 pinMode(_RS, OUTPUT );    // Rgistor Select, 1:data / 0:command

 delay (40);             // Init. LCD (Power on Reset)
 sendU4bit(0, 0x30);     // Function Set 8bit mode #1
 delayMicroseconds(37);
 sendU4bit(0, 0x30);     // Function Set 8bit mode #2
 delayMicroseconds(37);
 sendU4bit(0, 0x30);     // Function Set 8bit mode #3
 delayMicroseconds(37);
 sendU4bit(0, 0x20);     // Function Set 4bit mode
 delayMicroseconds(37);
 sendCommand(0x28);      // 4bit mode & 2line=1, font=0
 sendCommand(0x06);      // Cursor move increment, Shift off
 sendCommand(0x01);      // Clear Display
 sendCommand(0x0C);      // Display On, Cursor off, Blink  off

 Serial.begin(9600);     // Start UART 9600baud

 Wire.begin(5);          // Start I2C, Slave_Address=5

} // setup end


void loop() {                       /* main loop */

 byte recieveData1,recieveData2;

 recieveData1=getOnebyte();        // Recieve One Byte

 if (recieveData1<0x04){              // if data < 4, it is Row_Number
   recieveData2=getOnebyte();         // get next 1byte, Column Number
   locate(recieveData1,recieveData2); // Set Locate
 }else if (recieveData1>0x1F && recieveData1<0x80){  // if data is " >0x1F & <0x80"
   sendDisplay(recieveData1);                        // Disp. Ascii
 }else if (recieveData1==0x80){      // if data is 0x80
   recieveData2=getOnebyte();        // get next 1byte(command)
   sendCommand(recieveData2);        // send command
 }else if (recieveData1==0xC0){      // if data is 0xC0
   recieveData2=getOnebyte();        // get next 1byte(charactor code)
   sendDisplay(recieveData2);        // Disp. charactor
 }

} //loop end


byte getOnebyte(){              // get 1byte via UART or I2C
 byte oneByte;
 while (Serial.available() == 0 && Wire.available() == 0){} //waiting for data
 if (Serial.available() > 0){
   oneByte=Serial.read();
 }else{
   oneByte=Wire.read();
 }
 return oneByte;
}

void locate(byte rowNumber, byte columnNumber){
 byte ddramAddress;
 switch (rowNumber) {
   case 0:
     ddramAddress=0;
   break;
   case 1:
     ddramAddress=0x40;
   break;
   case 2:
     ddramAddress=0x14;
   break;
   case 3:
     ddramAddress=0x54;
   break;
 }
 ddramAddress=(ddramAddress+columnNumber) | 0x80;
 sendCommand(ddramAddress);
}

void sendCommand(byte cdData){
 send8bit(0, cdData);      // Resistor Select =0
 if (cdData < 4){          // if command is "clear display" or "Return Home"
   delay(2);               // wait 2ms
 }else{                    // else
   delayMicroseconds(37);  // wait 37us
 }
}

void sendDisplay(byte cdData){
 send8bit(1, cdData);    // Resistor Select =1
 delayMicroseconds(37);  // wait 37us
}

void send8bit(byte RS, byte cdData){
 sendU4bit(RS, cdData);    // send Upper 4bit
 sendU4bit(RS, cdData*16); // send (Lower 4bit *16)
}

void sendU4bit(byte RS, byte upper4bit){
 digitalWrite(_RS, RS);                    // Registor Select
 digitalWrite(_E, HIGH);                   // Set Enable High
 digitalWrite(DB7, bitRead(upper4bit,7));  // Set 4bit data
 digitalWrite(DB6, bitRead(upper4bit,6));
 digitalWrite(DB5, bitRead(upper4bit,5));
 digitalWrite(DB4, bitRead(upper4bit,4));
 digitalWrite(_E, LOW);                    // Set Enable Low (Data Latch)
}





2) 送信側Picoのプログラム
 実際にはUARTとI2Cを同時に接続することはまず無いとは思いますが,I2Cの表示が乱れる事がまれにあります。
 UART処理が終わるのをもう少し待ったほうが安定するのでしょうか,,,。

// Pico Serial test for ATtiny404 UART & I2C module for LCD
// UART: Serial1, 9600baud, Tx_Pin 0 (default)
// I2C : 100kbps, SDA_Pin 4 SCL_Pin 5 (default)


#include <Wire.h>

const byte LCD_Address=5;         // LCD I2c Slave_Address 5

const char* u_str="UART: ";       // character strings
const char* i2c_str="I2C : ";

void Serial1_locate(byte row, byte column){   //  Send cursor location via Serial1
 Serial1.write(row);
 Serial1.write(column);
}

void I2C_write(byte b1){                     //  Send 1byte via I2C
 Wire.beginTransmission(LCD_Address);
   Wire.write(b1);
 Wire.endTransmission();
}

void I2C_locate(byte row, byte column){      // Send cursor location via I2C
 Wire.beginTransmission(LCD_Address);
   Wire.write(row);
   Wire.write(column);
 Wire.endTransmission();
}

void setup(){               /* setup */

 Serial1.begin(9600);      // Start Serial1 9600baud  
 Wire.begin() ;            // Start I2C 100kbps (default)

 delay(100);  // Wait until LCD is ready (Power on Reset)

 Serial1.write(0x80);      // Clear display (command 0x01)
 Serial1.write(0x01);
 delay(2);

 Serial1_locate(0,1);      // disp. characters via Serial1
 Serial1.print(u_str);
 delay(100);

 I2C_locate(1,1);          // disp. characters via I2C
 Wire.beginTransmission(LCD_Address);
   Wire.write(i2c_str);
 Wire.endTransmission();

} //setup end


void loop() {

 // Increase the number from 0 to 100

 for (int d_value=0; d_value<=100; d_value++){

   Serial1_locate(0,7);    // disp. d_value in 3 digits
   uTx_digit3(d_value);    // via Serial1

   I2C_locate(1,7);        // disp. (100-d_value ) in 3 digits
   i2c_digit3(100-d_value);// via I2C

   delay(1000);            // wait 1sec.
 }

} //loop end


void uTx_digit3(int disp_value){    // disp. value in 3 digits
 Serial1.print(disp_value/100);    // via Serial1
 Serial1.print(disp_value%100/10);
 Serial1.print(disp_value%10);
}

void i2c_digit3(int disp_value){    // disp. value in 3 digits
 I2C_write(disp_value/100+0x30);   // via I2C
 I2C_write(disp_value%100/10+0x30);
 I2C_write(disp_value%10+0x30);
}