目標
以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)