Prism_tomor_night

2018年8月24日 星期五

[8051] 以UART串列介面實現LCD顯示終端機輸入字元

目標

  
以8051收終端機輸入的字元並發送至液晶顯示器。


材料


  1. LCM1602 x1 (在此使用LCD1602整合PCF8574T模組、I2C介面)
  2. USB Hub x1 (與電腦UART通訊)
  3. Putty (終端機,免費軟體)


應用


  1. I2C(IIC)介面通訊使用
  2. Printf & Scanf debug介面、直接由終端機輸出/輸入觀察變數


功能說明


  由上位終端機(在此使用Putty)鍵入字元,透過UART介面傳送至8051,再由8051透過I2C介面傳送給LCD1602顯示鍵入字元。
  輸入第1-16字元顯示在第一列、第17-31字元顯示在第二列,最多同時顯示31字元,若超過時所有字元會往前移一位擠出第1位字元,且在第31字元顯示新輸入字元。


結果





設定


  依控制台 – 裝置管理員中的COM位址,並設定Baudrate(此以COM4 19200bps為例)


程式碼


//LCM1602 with PCF8574T IIC interface
//realize that LCD shows what PC inputs
//Baudrate 19200

#include <reg51.h>
#include <stdio.h>

sbit sda = P2^0;
sbit scl = P2^1;

#define SLAVE 0x4E //slave addr.

void delay(unsigned int dl)
{
 while (dl>0)
  dl--;
}

void start()
{
 scl = 1;
 delay(5);
 sda = 1;
 delay(5);
 sda = 0;
 delay(4);
}

void send_8bits(unsigned char strg)
{
 unsigned char sf;
 
 for (sf=0; sf<8; sf++)
 {
  scl = 0;
  sda =(bit)(strg & (0x80>>sf));
  delay(5);
  scl = 1;
  delay(4);
 }
 scl = 0;
 delay(5);
}

void ack(void)
{
 sda = 1; //read ack on sda
 
 if(sda == 0)
 {  
  scl = 1;
  delay(4);
  scl = 0;
  delay(5);
 }
}

void stop(void)
{
 sda = 0;
 scl = 1;
 delay(5);
 sda = 1;
}

void WriteInst4bits(unsigned char inst_4b)
{
 send_8bits(0x08);   //RS=0, RW=0
 ack();
 send_8bits(0x0C);   //EN=1
 ack();
 send_8bits((inst_4b&0xF0)+0x0C); //inst 4bits
 ack();
 send_8bits((inst_4b&0xF0)+0x08); //EN=0, read this 4bits
 ack();
}

void WriteInst(unsigned char inst)
{ 
 send_8bits(0x08);   //RS=0, RW=0
 ack();
 send_8bits(0x0C);   //EN=1
 ack();
 send_8bits((inst&0xF0)+0x0C); //MSB
 ack();
 send_8bits((inst&0xF0)+0x08); //EN=0, read MSB
 ack();
 
 send_8bits(0x0C);   //EN=1
 ack();
 send_8bits((inst<<4)+0x0C); //LSB
 ack();
 send_8bits((inst<<4)+0x08); //EN=0, read LSB
 ack();
} 

void WriteData(unsigned char data_)
{
 send_8bits(0x09);   //RS=1, RW=0
 ack();
 send_8bits(0x0D);   //EN=1
 ack();
 send_8bits((data_&0xF0)+0x0D); //MSB
 ack();
 send_8bits((data_&0xF0)+0x09); //EN=0, read MSB
 ack();
 
 send_8bits(0x0D);   //EN=1
 ack();
 send_8bits((data_<<4)+0x0D); //LSB
 ack();
 send_8bits((data_<<4)+0x09); //EN=0, read LSB
 ack();
} 

void WriteString(unsigned char count, unsigned char MSG[])
{
 unsigned char sf;
 
 WriteInst(0x80);   //set DDRAM addr.
 
 if (count <16)
 {
  for (sf=0; sf<count; sf++)
  WriteData(MSG[sf]); 
 }
 else
 {
  for (sf=0; sf<16; sf++)
   WriteData(MSG[sf]); 
 
  WriteInst(0xC0);  //set DDRAM addr.
 
  for (sf=16; sf<count; sf++)
   WriteData(MSG[sf]);
 }
}

void initial(void)
{
 delay(15000);
 
 start();
 send_8bits(SLAVE); //communicate to PCF8574T, request to read
 ack();
 
 WriteInst4bits(0x30); //write 0011 to DB7-4 for initialization
 
 delay(4100);
 
 WriteInst4bits(0x30); //write 0011 to DB7-4 for initialization
 
 delay(100);

 WriteInst4bits(0x30); //write 0011 to DB7-4 for initialization
 WriteInst4bits(0x20); //write 0010 to DB7-4 for initialization
  
 WriteInst(0x28); //function set, DL(DB4)=0(4bits), N(DB3)=1(2 lines), F(DB2)=0(5*7)
 WriteInst(0x08); //display OFF
 WriteInst(0x01); //clear
 WriteInst(0x06); //entry mode
 WriteInst(0x0F); //display ON, D(DB2)=1(disp. ON), C(DB1)=1(cursor ON), B(DB0)=0(blinking ON)
}

void init_uart(void)
{
 SCON = 0x50;  //SM0=0, SM1=1, Mode 1, REN=1
 TMOD = 0x20;  //Timer_1 work in mode 2 (auto_load)
 TCON = 0x40; //TR1=1, turn on timer_1 
 TH1 = 253;
 TI = 1;  //if it is not set to 1, then putchar will wait till 1, then works
 PCON |=0x80; //SMOD=1
}

void main()
{ 
 unsigned char k;
 unsigned char MSG[31];  //MSG contains 31 chars
 unsigned char MSG_count = 0;
 
 init_uart();
 initial();
 
 while(1)
 { 
  if (MSG_count <=30)
  {
   scanf("%c", &MSG[MSG_count]);
   MSG_count++;
  }
  else
  {
   for (k=0; k<30; k++)
    MSG[k] = MSG[k+1];
   
   scanf("%c", &MSG[30]);
  }
  WriteString(MSG_count, MSG);
 }
}

筆記

過程中碰到一些困難點:用scanf(%s,...)來實現此功能時預想以enter送字,但一直無法克服enter帶來的空白字元,改用getchar後沒有太大差別;最後發現用scanf(%c,...)可以更直觀地連續送字且不需要特別清除enter的字元。整理重點如下:
  1. 以getchar實現功能
      i. 在main的while無限迴圈中讀取RI=1後進入串列中斷,再以指令getchar()取得緩衝區內資料
      ii. Putty必須開啟Terminal – Local echo force on,否則無法看到自己輸入的字元
      iii. 必須自己寫清除結束字元(enter鍵、\n、0x0A)的判斷
  2. 以scanf(%c…)實現功能
      i. %c預設只讀入一個字元,故在Putty中不需以enter送出,可連續鍵入字元,鍵入字元後自動會被8051讀取
      ii. 注意,enter本身即為一個字元(0x0A)
 3. 以scanf(%s…)實現功能
      i. %s本身可一次讀取多字元的字串,且會以空白鍵當做字串間隔,意即空白鍵會結束該次scanf
      ii. enter是scanf(%s…)的結束字元,故若使用enter鍵送出字元,必須清除結束字元(\n、0x0A)

[8051] 以I2C介面驅動LCD模組(LCM1602)

主題 透過I2C通訊介面,驅動常見市售的整合式LCD模組LCM1602。 材料 LCM1602顯示器模組(與PCF8574T整合之I2C介面晶片),外觀如下,若擔心相容性,請確認背後標記為Arduino的晶片型號是否與本例相同;本例中使用模組在蝦米拍賣中購得...