AT24C02数据存储+普中51单片机+江科大自化协
1 系统原理图
2 现象
当按键Key1被按下时,LCD1602显示的数值加1,每按下一次,自增1;当按键Key2被按下时,LCD1602显示的数值自减1;当按键Key3被按下时,单片机将LCD1602显示的数值保存在AT24C02中,掉电不丢失;当按键Key4被按下时,单片机从AT24C02中读取数据,并显示在LCD1602。
3 参考程序
(1)主函数
#include <REGX52.H> #include "delayms.h" #include "key.h" #include "lcd1602.h" #include "at24c02.h" unsigned char KeyNum; unsigned int Num; void main() { LCD1602_Init(); LCD1602_ShowNum(1,1,Num,5); while(1) { KeyNum=key(); if(KeyNum==1) //K1按键,Num自增 { Num++; LCD1602_ShowNum(1,1,Num,5); } if(KeyNum==2) //K2按键,Num自减 { Num--; LCD1602_ShowNum(1,1,Num,5); } if(KeyNum==3) //K3按键,向AT24C02写入数据 { AT24C02_WriteByte(0,Num%256); delayms(5); AT24C02_WriteByte(1,Num/256); delayms(5); LCD1602_ShowString(2,1,"Write is OK"); delayms(1000); LCD1602_ShowString(2,1," "); } if(KeyNum==4) //K4按键,从AT24C02读取数据 { Num=AT24C02_ReadByte(0); Num|=AT24C02_ReadByte(1)<<8; LCD1602_ShowNum(1,1,Num,5); LCD1602_ShowString(2,1,"Read is OK"); delayms(1000); LCD1602_ShowString(2,1," "); } } }
(2)I2C函数
#ifndef _i2c_h_ #define _i2c_h_ void I2C_Start(void); void I2C_Stop(void); void I2C_SendByte(unsigned char Byte); unsigned char I2C_ReceiveByte(void); void I2C_SendAck(unsigned char AckBit); unsigned char I2C_ReceiveAck(void); #endif
#include <REGX52.H> sbit I2C_SCL=P2^1; sbit I2C_SDA=P2^0; /** * @brief I2C通信开始 * @param 无 * @retval 无 */ void I2C_Start(void) { I2C_SCL=1; //空闲状态 I2C_SDA=1; //空闲状态 I2C_SDA=0; I2C_SCL=0; } /** * @brief I2C通信结束 * @param 无 * @retval 无 */ void I2C_Stop(void) { I2C_SDA=0; I2C_SCL=1; //回到空闲状态 I2C_SDA=1; //回到空闲状态 } /** * @brief I2C主机向从机发送一个字节,SCL为同步信号,低电平写数据 * @param Byte 要发送的字节 * @retval 无 */ void I2C_SendByte(unsigned char Byte) { unsigned char i; for(i=0;i<8;i++) //一个字节,8bit { I2C_SDA=Byte&(0x80>>i); //SCL为低电平,主机为发送器,写数据 I2C_SCL=1; //SCL为高电平,从机为接收器,读数据 I2C_SCL=0; //时序要求,51单片机速度比较慢 } } /** * @brief I2C主机接收从机一个字节,SCL为同步信号,高电平读数据 * @param 无 * @retval 接收到的一个字节数据 */ unsigned char I2C_ReceiveByte(void) { unsigned char i,Byte=0x00; I2C_SDA=1; //主机释放数据线SDA for(i=0;i<8;i++) { I2C_SCL=1; //主机作为接收器 if(I2C_SDA) Byte|=(0x80>>i); //读数据 I2C_SCL=0; //从机作为发送器,写数据 } return Byte; } /** * @brief I2C主机发送应答 * @param AckBit 应答位,0为应答,1为非应答 * @retval 无 */ void I2C_SendAck(unsigned char AckBit) { I2C_SDA=AckBit; I2C_SCL=1; I2C_SCL=0; } /** * @brief I2C主机接收应答位 * @param 无 * @retval 接收到的应答位,0为应答,1为非应答 */ unsigned char I2C_ReceiveAck(void) { unsigned char AckBit; I2C_SDA=1; I2C_SCL=1; AckBit=I2C_SDA; I2C_SCL=0; return AckBit; }
(3)AT24C02函数
#ifndef _at24c02_h_ #define _at24c02_h_ void AT24C02_WriteByte(unsigned char WordAddress,Data); unsigned char AT24C02_ReadByte(unsigned char WordAddress); #endif
#include <REGX52.H> #include "i2c.h" #define AT24C02_ADDRESS 0xA0 /** * @brief AT24C02写入一个字节 * @param WordAddress 要写入字节的地址 * @param Data 要写入的数据 * @retval 无 */ void AT24C02_WriteByte(unsigned char WordAddress,Data) { I2C_Start(); I2C_SendByte(AT24C02_ADDRESS); I2C_ReceiveAck(); I2C_SendByte(WordAddress); I2C_ReceiveAck(); I2C_SendByte(Data); I2C_ReceiveAck(); I2C_Stop(); } /** * @brief AT24C02读取一个字节 * @param WordAddress 要读出字节的地址 * @retval 读出的数据 */ unsigned char AT24C02_ReadByte(unsigned char WordAddress) { unsigned char Data; I2C_Start(); I2C_SendByte(AT24C02_ADDRESS); I2C_ReceiveAck(); I2C_SendByte(WordAddress); I2C_ReceiveAck(); I2C_Start(); I2C_SendByte(AT24C02_ADDRESS|0x01); I2C_ReceiveAck(); Data=I2C_ReceiveByte(); I2C_SendAck(1); I2C_Stop(); return Data; }
(4)LCD1602函数
#ifndef __LCD1602_H__ #define __LCD1602_H__ void LCD1602_WriteCommand(unsigned char Command); void LCD1602_WriteData(unsigned char Data); void LCD1602_Init(void); void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char); void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String); void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length); #endif
#include <REGX52.H> #include "intrins.h" //引脚配置,硬件原理图,普中A2 sbit LCD1602_RS = P2^6; sbit LCD1602_RW = P2^5; sbit LCD1602_E = P2^7; #define LCD1602_DATAPORT P0 /** * @brief LCD1602延时函数 * @param xms=1, 延时1ms * @retval 无 */ void LCD1602_Delayms(unsigned char xms) //@11.0592MHz { unsigned char i, j,k; for(k=0;k<xms;k++) { _nop_(); i = 2; j = 199; do { while (--j); } while (--i); } } /** * @brief LCD1602写命令,时序图 * @param Command 要写入的命令 * @retval 无 */ void LCD1602_WriteCommand(unsigned char Command) { LCD1602_RS = 0; LCD1602_RW = 0; LCD1602_DATAPORT = Command; LCD1602_E = 1; LCD1602_Delayms(1); //延时1ms LCD1602_E = 0; LCD1602_Delayms(1); //延时1ms } /** * @brief LCD1602写数据,时序图 * @param Data 要写入的数据 * @retval 无 */ void LCD1602_WriteData(unsigned char Data) { LCD1602_RS = 1; LCD1602_RW = 0; LCD1602_DATAPORT = Data; LCD1602_E = 1; LCD1602_Delayms(1); //延时1ms LCD1602_E = 0; LCD1602_Delayms(1); //延时1ms } /** * @brief LCD1602初始化函数 * @param 无 * @retval 无 */ void LCD1602_Init(void) { LCD1602_WriteCommand(0x38); //0011_1000 功能设置,设置16x2显示,5x7点阵,8位数据接口 LCD1602_WriteCommand(0x0C); //0000_1100 显示开关控制,显示开,光标关,闪烁关 LCD1602_WriteCommand(0x06); //0000_0110 输入方式设置,数据写后,AC自动加一,画面不动 LCD1602_WriteCommand(0x01); //0000_0001 清屏,数据指针清零、所有显示清零 } /** * @brief LCD1602设置光标位置 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @retval 无 */ void LCD1602_SetCursor(unsigned char Line,unsigned char Column) { if(Line == 1) { LCD1602_WriteCommand(0x80|(Column-1)); //设置要存入数据的DDROM地址,第一行地址 } else { LCD1602_WriteCommand(0x80|(Column-1)+0x40); //设置要存入数据的DDROM地,第二行地址 } } /** * @brief 在LCD1602指定位置上显示一个字符 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符 * @retval 无 */ void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char) { LCD1602_SetCursor(Line,Column); LCD1602_WriteData(Char); } /** * @brief 在LCD1602指定位置上显示所给字符串 * @param Line 行位置,范围:1~2 * @param Column 列位置,范围:1~16 * @param Char 要显示的字符串 * @retval 无 */ void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String) //指针 { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=0;String[i]!='\0';i++) //字符串结束标志'\0' { LCD1602_WriteData(String[i]); } } /** * @brief 返回值=X的Y次方,指数运算 */ int LCD_Power(int X,int Y) { unsigned char i; int result = 1; for(i=0;i<Y;i++) { result = result * X; } return result; } /** * @brief 在LCD1602指定位置开始显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~65535 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number/LCD_Power(10,i-1)%10); // '0'=0x30,转换为字符显示; /** * 789, 789/100%10, 取出百位 * 789, 789/10%10, 取出十位 * 789, 789/1%10, 取出个位 */ } } /** * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:-32768~32767 * @param Length 要显示数字的长度,范围:1~5 * @retval 无 */ void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length) { unsigned char i; unsigned int Number1; LCD1602_SetCursor(Line,Column); if(Number >= 0) { LCD1602_WriteData('+'); Number1 = Number; } else { LCD1602_WriteData('-'); Number1 = -Number; //-32768 } for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number1/LCD_Power(10,i-1)%10); } } /** * @brief 在LCD1602指定位置开始以十六进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~0xffff * @param Length 要显示数字的长度,范围:1~4 * @retval 无 */ void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; unsigned char SingleNumber; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { SingleNumber = Number/LCD_Power(16,i-1)%16; //从高位到低位,取出每一个十六进制数字 if(SingleNumber < 10) LCD1602_WriteData('0'+SingleNumber); else LCD1602_WriteData('A'+SingleNumber-10); //SingleNumber已含有10+x } } /** * @brief 在LCD1602指定位置开始以二进制显示所给数字 * @param Line 起始行位置,范围:1~2 * @param Column 起始列位置,范围:1~16 * @param Number 要显示的数字,范围:0~1111_1111_1111_1111 * @param Length 要显示数字的长度,范围:1~16 * @retval 无 */ void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length) { unsigned char i; LCD1602_SetCursor(Line,Column); for(i=Length;i>0;i--) { LCD1602_WriteData('0'+Number/LCD_Power(2,i-1)%2); //从高位到低位,取出每一个二进制数字 } }
(5)独立按键函数
#ifndef _key_h_ #define _key_h_ unsigned char key(); #endif
#include <reg52.h> #include "delayms.h" sbit key1 = P3^1; sbit key2 = P3^0; sbit key3 = P3^2; sbit key4 = P3^3; /** * @brief 获取独立按键键码 * @param 无 * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0 */ unsigned char key() { unsigned char KeyNumber = 0; if(key1==0){delayms(20);while(key1==0);delayms(20);KeyNumber=1;} if(key2==0){delayms(20);while(key2==0);delayms(20);KeyNumber=2;} if(key3==0){delayms(20);while(key3==0);delayms(20);KeyNumber=3;} if(key4==0){delayms(20);while(key4==0);delayms(20);KeyNumber=4;} return KeyNumber; }
(6)延时函数
#ifndef _delayms_h_ #define _delayms_h_ delayms(unsigned int xms); #endif
#include <intrins.h> void delayms(unsigned int xms) //@11.0592MHz { unsigned char i, j; while(xms--) { _nop_(); i = 2; j = 199; do { while (--j); } while (--i); } }