電視頻道記憶功能,交通燈倒計(jì)時(shí)時(shí)間的設(shè)定,戶外 LED 廣告的記憶功能,都有可能用到 EEPROM 這類存儲(chǔ)器件。這類器件的優(yōu)勢(shì)是存儲(chǔ)的數(shù)據(jù)不僅可以改變,而且掉電后數(shù)據(jù)保存不丟失,因此大量應(yīng)用在各種電子產(chǎn)品上。
我們這節(jié)課的例程,有點(diǎn)類似廣告屏。上電后,1602 的第一行顯示 EEPROM 從 0x20 地址開始的 16 個(gè)字符,第二行顯示 EERPOM 從 0x40 開始的 16 個(gè)字符。我們可以通過 UART串口通信來改變 EEPROM 內(nèi)部的這個(gè)數(shù)據(jù),并且同時(shí)也改變了 1602 顯示的內(nèi)容,下次上電的時(shí)候,直接會(huì)顯示我們更新過的內(nèi)容。
這個(gè)程序所有的相關(guān)內(nèi)容,前面都已經(jīng)講過了。但是這個(gè)程序體現(xiàn)在了一個(gè)綜合應(yīng)用能力上。這個(gè)程序用到了 1602 液晶、UART 串口通信、EEPROM 讀寫操作等多個(gè)功能的綜合應(yīng)用。寫個(gè)點(diǎn)亮小燈好簡(jiǎn)單,但是我們想真正學(xué)好單片機(jī),必須得學(xué)會(huì)這種綜合程序的應(yīng)用,實(shí)現(xiàn)多個(gè)模塊同時(shí)參與工作。因此同學(xué)們,要認(rèn)認(rèn)真真的把工程建立起來,一行一行的把程序編寫起來,最終鞏固下來。
#include
unsigned char T0RH = 0; //T0 重載值的高字節(jié)
unsigned char T0RL = 0; //T0 重載值的低字節(jié)
void InitShowStr();
void ConfigTimer0(unsigned int ms);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void E2Read(unsigned char *buf, unsigned char addr, unsigned char len);
extern void E2Write(unsigned char *buf, unsigned char addr, unsigned char len);
extern void UartDriver();
extern void ConfigUART(unsigned int baud);
extern void UartRxMonitor(unsigned char ms);
extern void UartWrite(unsigned char *buf, unsigned char len);
void main(){
EA = 1; //開總中斷
ConfigTimer0(1); //配置 T0 定時(shí) 1ms
ConfigUART(9600); //配置波特率為 9600
InitLcd1602(); //初始化液晶
InitShowStr(); //初始顯示內(nèi)容
while (1){
UartDriver(); //調(diào)用串口驅(qū)動(dòng)
}
}
/* 處理液晶屏初始顯示內(nèi)容 */
void InitShowStr(){
unsigned char str[17];
str[16] = ‘’;//在最后添加字符串結(jié)束符,確保字符串可以結(jié)束
E2Read(str, 0x20, 16); //讀取第一行字符串,其 E2 起始地址為 0x20
LcdShowStr(0, 0, str); //顯示到液晶屏
E2Read(str, 0x40, 16); //讀取第二行字符串,其 E2 起始地址為 0x40
LcdShowStr(0, 1, str); //顯示到液晶屏
}
/* 內(nèi)存比較函數(shù),比較兩個(gè)指針?biāo)赶虻膬?nèi)存數(shù)據(jù)是否相同,
ptr1-待比較指針 1,ptr2-待比較指針 2,len-待比較長(zhǎng)度
返回值-兩段內(nèi)存數(shù)據(jù)完全相同時(shí)返回 1,不同返回 0 */
bit CmpMemory(unsigned char *ptr1, unsigned char *ptr2, unsigned char len){
while (len--){
if (*ptr1++ != *ptr2++){ //遇到不相等數(shù)據(jù)時(shí)即刻返回 0
return 0;
}
}
return 1; //比較完全部長(zhǎng)度數(shù)據(jù)都相等則返回 1
}
/* 將一字符串整理成 16 字節(jié)的固定長(zhǎng)度字符串,不足部分補(bǔ)空格
out-整理后的字符串輸出指針,in-待整理字符串指針 */
void TrimString16(unsigned char *out, unsigned char *in){
unsigned char i = 0;
while (*in != ‘’){ //拷貝字符串直到輸入字符串結(jié)束
*out++ = *in++;
i++;
if (i 》= 16){ //當(dāng)拷貝長(zhǎng)度已達(dá)到 16 字節(jié)時(shí),強(qiáng)制跳出循環(huán)
break;
}
}
for ( ; i《16; i++){ //如不足 16 個(gè)字節(jié)則用空格補(bǔ)齊
*out++ = ‘ ’;
}
*out = ‘’; //最后添加結(jié)束符
}
/* 串口動(dòng)作函數(shù),根據(jù)接收到的命令幀執(zhí)行響應(yīng)的動(dòng)作
buf-接收到的命令幀指針,len-命令幀長(zhǎng)度 */
void UartAction(unsigned char *buf, unsigned char len){
unsigned char i;
unsigned char str[17];
unsigned char code cmd0[] = “showstr1 ”; //第一行字符顯示命令
unsigned char code cmd1[] = “showstr2 ”; //第二行字符顯示命令
unsigned char code cmdLen[] = { //命令長(zhǎng)度匯總表
sizeof(cmd0)-1, sizeof(cmd1)-1,
};
unsigned char code *cmdPtr[] = { //命令指針匯總表
&cmd0[0], &cmd1[0],
};
for (i=0; i
if (len 》= cmdLen[i]){ //首先接收到的數(shù)據(jù)長(zhǎng)度要不小于命令長(zhǎng)度
if (CmpMemory(buf, cmdPtr[i], cmdLen[i])){ //比較相同時(shí)退出循環(huán)
break;
}
}
}
switch (i){ //根據(jù)比較結(jié)果執(zhí)行相應(yīng)命令
case 0:
buf[len] = ‘’; //為接收到的字符串添加結(jié)束符
TrimString16(str, buf+cmdLen[0]); //整理成 16 字節(jié)固定長(zhǎng)度字符串
LcdShowStr(0, 0, str); //顯示字符串 1
E2Write(str, 0x20, sizeof(str)); //保存字符串 1,起始地址為 0x20
break;
case 1:
buf[len] = ‘’; //為接收到的字符串添加結(jié)束符
TrimString16(str, buf+cmdLen[1]); //整理成 16 字節(jié)固定長(zhǎng)度字符串
LcdShowStr(0, 1, str); //顯示字符串 1
E2Write(str, 0x40, sizeof(str)); //保存字符串 2,起始地址為 0x40
break;
default: //未找到相符命令時(shí),給上機(jī)發(fā)送“錯(cuò)誤命令”的提示
UartWrite(“bad command.rn”, sizeof(“bad command.rn”)-1);
return;
}
buf[len++] = ‘r’; //有效命令被執(zhí)行后,在原命令幀之后添加
buf[len++] = ‘n’; //回車換行符后返回給上位機(jī),表示已執(zhí)行
UartWrite(buf, len);
}
/* 配置并啟動(dòng) T0,ms-T0 定時(shí)時(shí)間 */
void ConfigTimer0(unsigned int ms){
unsigned long tmp; //臨時(shí)變量
tmp = 11059200 / 12; //定時(shí)器計(jì)數(shù)頻率
tmp = (tmp * ms) / 1000; //計(jì)算所需的計(jì)數(shù)值
tmp = 65536 - tmp; //計(jì)算定時(shí)器重載值
tmp = tmp + 33; //補(bǔ)償中斷響應(yīng)延時(shí)造成的誤差
T0RH = (unsigned char)(tmp》》8); //定時(shí)器重載值拆分為高低字節(jié)
T0RL = (unsigned char)tmp;
TMOD &= 0xF0; //清零 T0 的控制位
TMOD |= 0x01; //配置 T0 為模式 1
TH0 = T0RH; //加載 T0 重載值
TL0 = T0RL;
ET0 = 1; //使能 T0 中斷
TR0 = 1; //啟動(dòng) T0
}
/* T0 中斷服務(wù)函數(shù),執(zhí)行串口接收監(jiān)控和蜂鳴器驅(qū)動(dòng) */
void InterruptTimer0() interrupt 1{
TH0 = T0RH; //重新加載重載值
TL0 = T0RL;
UartRxMonitor(1); //串口接收監(jiān)控
}
我們?cè)趯W(xué)習(xí) UART 通信的時(shí)候,剛開始也是用的IO口去模擬UART通信過程,最終實(shí)現(xiàn)和電腦的通信,而后因?yàn)?STC89C52 內(nèi)部具備 UART 硬件通信模塊,所以我們直接可以通過配置寄存器就可以很輕松的實(shí)現(xiàn)單片機(jī)的 UART 通信。同樣的道理,這個(gè) I2C 通信,如果單片機(jī)內(nèi)部有硬件模塊的話,單片機(jī)可以直接自動(dòng)實(shí)現(xiàn) I2C 通信了,就不需要我們?cè)龠M(jìn)行 IO口模擬起始、模擬發(fā)送、模擬結(jié)束,配置好寄存器,單片機(jī)就會(huì)把這些工作全部做了。
不過我們的STC89C52單片機(jī)內(nèi)部不具備I2C的硬件模塊,所以我們使用STC89C52進(jìn)行I2C通信的話必須用IO口來模擬。使用IO口模擬I2C實(shí)際上更有利于我們徹底理解透徹I2C通信的實(shí)質(zhì)。當(dāng)然了,通過學(xué)習(xí)IO口模擬通信,今后如果遇到內(nèi)部帶 I2C 模塊的單片機(jī),也應(yīng)該很輕松的搞定,使用內(nèi)部的硬件模塊,可以提高程序的執(zhí)行效率。
來源;21ic
評(píng)論