本帖最后由 7788281 于 2012-10-27 22:44 编辑
11.串行I2C总线E2PROM AT24CXXX的应用3接上一篇
unsigned char code BMP1[]={ //字节倒序
//-- 调入一幅图像:E:!Program!BmpSample12864.bmp
//宽度x高度=1264
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0xE0,0xF0,0xFC,0xFE,0xFE,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0x9F,0xCF,0xDF,0x9F,
0x9E,0x3E,0xFF,0xFE,0xFE,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFA,0xFC,0xF8,0xE0,0xC0,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0xC0,0xF0,0xF8,0xFC,0xFE,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF,0xEF,
0xCF,0xDF,0x9F,0x0F,0x1F,0x7F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFE,0xFC,0xF0,0xC0,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xC0,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xFB,0xFF,
0xFF,0xFF,0xFF,0xFE,0xF8,0xFE,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF,0x3F,
0x37,0x37,0x37,0x27,0x63,0x43,0x03,0x03,
0x03,0x03,0x03,0x07,0x67,0x27,0x0F,0x0F,
0x1F,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x80,0x80,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x10,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x7F,0x7F,
0x7F,0x7F,0x3F,0x3F,0x3F,0x3F,0x3F,0x3F,
0x3F,0x3F,0x3F,0x3F,0x3F,0x7F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x3F,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x80,0xE0,0xFC,0xFE,0xFE,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xDF,0x0F,0x07,0x07,0x03,
0x03,0x03,0x03,0x02,0xC0,0xAC,0xBF,0xA0,
0x80,0x00,0x00,0x00,0x02,0x02,0x06,0x06,
0x04,0x00,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFE,0xFC,0xF0,0xE0,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x01,0x03,0x07,0x0F,0x1F,0x3F,0x3F,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xBF,0x3F,0x3F,0x3D,
0x7D,0xF8,0xF0,0xF0,0xC0,0x00,0x00,0x00,
0x08,0x8C,0xFC,0xFE,0xEE,0xE6,0xC2,0xC0,
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
0x1F,0x1F,0x87,0x0D,0x7D,0x70,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,
0xF0,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFC,0xE0,0x00,
0x00,0x00,0x02,0xE7,0xE7,0xE7,0xE7,0xC3,
0xC3,0xC1,0x82,0x87,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xE3,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFC,0xF8,0xE0,0xC0,0x80,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,
0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xDF,0x1F,0x1F,0x1F,0x1B,0xF9,
0xF9,0xFF,0xFF,0xFF,0xFF,0x80,0x00,0x00,
0x00,0x01,0x01,0x03,0x03,0x83,0x83,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x01,0x03,0x00,0x00,0x00,0x00,
0xF8,0xFC,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFE,0xFC,0xF8,0xFE,0x8F,0x8F,0x0E,0x06,
0x0E,0x0C,0x0C,0x00,0x01,0x00,0x00,0x80,
0xC0,0xF0,0xFC,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFC,
0xF8,0xF0,0xF0,0xE0,0x00,0x00,0x00,0x00,
0x00,0x03,0x07,0x0F,0x1F,0x1F,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFE,0xFC,0xFC,0xFE,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xDF,0xCF,0xDE,
0xD4,0xC2,0x82,0x80,0x80,0x83,0xC7,0xC7,
0x0F,0x04,0x00,0x00,0x00,0x00,0x00,0x00,
0xC0,0xF4,0xFC,0xFC,0xFC,0xFC,0xF8,0xF8,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x7F,0x3F,0x1F,0x1F,0xDF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0x1E,
0x1E,0x0E,0x0C,0x04,0x02,0x06,0x1F,0xFF,
0xFF,0xCF,0x0F,0x0F,0x0F,0x1F,0x3F,0x7F,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x7F,0x1F,0x1F,0x03,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x80,0xC1,0xE7,
0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFB,
0xF3,0xF9,0x71,0x31,0x00,0x00,0x00,0x01,
0x00,0x00,0x00,0x00,0x00,0x80,0xE0,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x03,0x07,0x0F,0x1F,0x1F,0x3F,0x3F,0x1F,
0x1F,0x1F,0x0F,0x4F,0x67,0x27,0x33,0x31,
0x38,0x38,0x78,0x7C,0x7E,0x7F,0x7F,0x7F,
0x7F,0x7F,0x7F,0x7F,0x3F,0x3F,0x7F,0x7F,
0x7F,0x7F,0x7D,0x79,0x79,0x70,0x70,0x70,
0x70,0x70,0x70,0x78,0x78,0x3C,0x5E,0x6F,
0x3F,0x77,0x0F,0x0C,0x7C,0x78,0x78,0x40,
0x00,0x01,0x01,0x03,0x07,0x0F,0x1F,0x1F,
0x1F,0x1F,0x1F,0x1F,0x0F,0x07,0x03,0x00,
0x00,0x00,0x00,0x40,0x60,0x70,0x70,0x78,
0x78,0x7C,0x7E,0x7F,0x7F,0x7F,0x7F,0x7F,
0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,
0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7F,0x7C,
0x7C,0x7C,0x78,0x78,0x70,0x58,0x00,0x00,
0x40,0x70,0x78,0x7C,0x7F,0x7F,0x7F,0x7F,
0x7F,0x7F,0x07,0x07,0x0F,0x1F,0x3F,0x7F
};
uchar code ASC_5x7[]={
0x00, 0x00, 0x00, 0x00, 0x00, //
0x00, 0x00, 0x4F, 0x00, 0x00, //
0x00, 0x07, 0x00, 0x07, 0x00, //
0x14, 0x7F, 0x14, 0x7F, 0x14, //
0x24, 0x2A, 0x7F, 0x2A, 0x12, //
0x23, 0x13, 0x08, 0x64, 0x62, //
0x36, 0x49, 0x55, 0x22, 0x50, //
0x00, 0x05, 0x03, 0x00, 0x00, //
0x00, 0x1C, 0x22, 0x41, 0x00, //
0x00, 0x41, 0x22, 0x1C, 0x00, //
0x14, 0x08, 0x3E, 0x08, 0x14, //
0x08, 0x08, 0x3E, 0x08, 0x08, //
0x00, 0x50, 0x30, 0x00, 0x00, //
0x08, 0x08, 0x08, 0x08, 0x00, //
0x00, 0x60, 0x60, 0x00, 0x00, //
0x20, 0x10, 0x08, 0x04, 0x02, //
0x3E, 0x51, 0x49, 0x45, 0x3E, //
0x00, 0x42, 0x7F, 0x40, 0x00, //
0x42, 0x61, 0x51, 0x49, 0x46, //
0x21, 0x41, 0x45, 0x4B, 0x31, //
0x18, 0x14, 0x12, 0x7F, 0x10, //
0x27, 0x45, 0x45, 0x45, 0x39, //
0x3C, 0x4A, 0x49, 0x49, 0x30, //
0x01, 0x01, 0x79, 0x05, 0x03, //
0x36, 0x49, 0x49, 0x49, 0x36, //
0x06, 0x49, 0x49, 0x29, 0x1E, //
0x00, 0x36, 0x36, 0x00, 0x00, //
0x00, 0x56, 0x36, 0x00, 0x00, //
0x08, 0x14, 0x22, 0x41, 0x00, //
0x14, 0x14, 0x14, 0x14, 0x14, //
0x00, 0x41, 0x22, 0x14, 0x08, //
0x02, 0x01, 0x51, 0x09, 0x06, //
0x32, 0x49, 0x79, 0x41, 0x3E, //
0x7E, 0x11, 0x11, 0x11, 0x7E, //
0x41, 0x7F, 0x49, 0x49, 0x36, //
0x3E, 0x41, 0x41, 0x41, 0x22, //
0x41, 0x7F, 0x41, 0x41, 0x3E, //
0x7F, 0x49, 0x49, 0x49, 0x49, //
0x7F, 0x09, 0x09, 0x09, 0x01, //
0x3E, 0x41, 0x41, 0x49, 0x7A, //
0x7F, 0x08, 0x08, 0x08, 0x7F, //
0x00, 0x41, 0x7F, 0x41, 0x00, //
0x20, 0x40, 0x41, 0x3F, 0x01, //
0x7F, 0x08, 0x14, 0x22, 0x41, //
0x7F, 0x40, 0x40, 0x40, 0x40, //
0x7F, 0x02, 0x0C, 0x02, 0x7F, //
0x7F, 0x06, 0x08, 0x30, 0x7F, //
0x3E, 0x41, 0x41, 0x41, 0x3E, //
0x7F, 0x09, 0x09, 0x09, 0x06, //
0x3E, 0x41, 0x51, 0x21, 0x5E, //
0x7F, 0x09, 0x19, 0x29, 0x46, //
0x26, 0x49, 0x49, 0x49, 0x32, //
0x01, 0x01, 0x7F, 0x01, 0x01, //
0x3F, 0x40, 0x40, 0x40, 0x3F, //
0x1F, 0x20, 0x40, 0x20, 0x1F, //
0x7F, 0x20, 0x18, 0x20, 0x7F, //
0x63, 0x14, 0x08, 0x14, 0x63, //
0x07, 0x08, 0x70, 0x08, 0x07, //
0x61, 0x51, 0x49, 0x45, 0x43, //
0x00, 0x7F, 0x41, 0x41, 0x00, //
0x02, 0x04, 0x08, 0x10, 0x20, //
0x00, 0x41, 0x41, 0x7F, 0x00, //
0x04, 0x02, 0x01, 0x02, 0x04, //
0x40, 0x40, 0x00, 0x40, 0x40, //
0x01, 0x02, 0x04, 0x00, 0x00, //
0x20, 0x54, 0x54, 0x54, 0x78, //
0x7F, 0x48, 0x44, 0x44, 0x38, //
0x38, 0x44, 0x44, 0x44, 0x28, //
0x38, 0x44, 0x44, 0x48, 0x7F, //
0x38, 0x54, 0x54, 0x54, 0x18, //
0x00, 0x08, 0x7E, 0x09, 0x02, //
0x0C, 0x52, 0x52, 0x4C, 0x3E, //
0x7F, 0x08, 0x04, 0x04, 0x78, //
0x00, 0x44, 0x7D, 0x40, 0x00, //
0x20, 0x40, 0x44, 0x3D, 0x00, //
0x00, 0x7F, 0x10, 0x28, 0x44, //
0x00, 0x41, 0x7F, 0x40, 0x00, //
0x7C, 0x04, 0x78, 0x04, 0x78, //
0x7C, 0x08, 0x04, 0x04, 0x78, //
0x38, 0x44, 0x44, 0x44, 0x38, //
0x7E, 0x0C, 0x12, 0x12, 0x0C, //
0x0C, 0x12, 0x12, 0x0C, 0x7E, //
0x7C, 0x08, 0x04, 0x04, 0x08, //
0x58, 0x54, 0x54, 0x54, 0x64, //
0x04, 0x3F, 0x44, 0x40, 0x20, //
0x3C, 0x40, 0x40, 0x3C, 0x40, //
0x1C, 0x20, 0x40, 0x20, 0x1C, //
0x3C, 0x40, 0x30, 0x40, 0x3C, //
0x44, 0x28, 0x10, 0x28, 0x44, //
0x1C, 0xA0, 0xA0, 0x90, 0x7C, //
0x44, 0x64, 0x54, 0x4C, 0x44, //
0x00, 0x08, 0x36, 0x41, 0x00, //
0x00, 0x00, 0x77, 0x00, 0x00, //
0x00, 0x41, 0x36, 0x08, 0x00, //
0x02, 0x01, 0x02, 0x04, 0x02, //
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, //
};
unsigned char code HZK_12[]={ //字节颠倒
//-- 文字: 鹰 --*
//- 宋体11; 此字体下对应的点阵为:宽x高=11x
0x42,0x00,0x22,0x00,0x12,0x00,0xFA,0x07,0x4E,0x01,0x4B,0x01,0x4A,0x01,0x4A,0x05,
0xFA,0x07,0x02,0x00,0x02,0x00,0x00,0x00,/*"有",{0}*/
0x42,0x04,0xAE,0x04,0x93,0x04,0x9A,0x02,0xA6,0x01,0xC0,0x00,0xA6,0x04,0x9A,0x04,
0x8A,0x04,0x96,0x03,0x20,0x00,0x00,0x00,/*"努",{1}*/
0x00,0x04,0x08,0x04,0x08,0x02,0x08,0x01,0xC8,0x00,0x3F,0x00,0x08,0x04,0x08,0x04,
0x08,0x04,0xFC,0x03,0x08,0x00,0x00,0x00,/*"力",{2}*/
0x02,0x02,0xFA,0x01,0x4B,0x04,0xCA,0x07,0x7A,0x01,0x02,0x04,0x08,0x03,0xFF,0x00,
0xC8,0x07,0x0E,0x04,0x08,0x07,0x00,0x00,/*"就",{3}*/
0x42,0x00,0x22,0x00,0x12,0x00,0xFA,0x07,0x4E,0x01,0x4B,0x01,0x4A,0x01,0x4A,0x05,
0xFA,0x07,0x02,0x00,0x02,0x00,0x00,0x00,/*"有",{4}*/
0x00,0x04,0xFC,0x03,0x24,0x01,0x24,0x02,0xE4,0x01,0x04,0x04,0x7F,0x02,0x84,0x01,
0x45,0x02,0x36,0x04,0x04,0x07,0x00,0x00,/*"成",{5}*/
0x02,0x01,0x02,0x01,0xFE,0x00,0x82,0x04,0x82,0x04,0x08,0x02,0x88,0x01,0x7F,0x04,
0x08,0x04,0x08,0x04,0xF8,0x03,0x00,0x00,/*"功",{6}*/
0x00,0x00,0x1C,0x00,0x7E,0x03,0x7E,0x03,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00/*"!",{7}*/
};
上面代码的各个函数部分我就不说了,只说说AT24C16的调试,也就是主函数部分:
void Main( void )
{
uchar x,i;
uchar *ascii; //先定义一个字符型指针变量
uchar abcd[]="iic test"; //定义一个字符数组内容为“iic test”
LcmInit();
LcmClear();
ascii=abcd; //将字符数组的首地址交给指针变量
write_nbyte(0,0,ascii,8); //向AT24C16写进以ascii为首地址的八个字节数据。
//read_nbyte(0,0,ascii,8); //读AT24C16语句补注释掉了
while(1)
{
LcmClear();
LcmPutBMP(BMP1);
Delay(3000);
LcmClear();
//LcmReverseBMP();
Delay(1000);
//LcmClear();
/*
x=0;
for(i=0;i<8;i++)
{
LcmPutHZ_12(x,i*7,i);
x=x+16;
}
*/
LcmPutstr( 3,40,ascii ); //向LCD12864显示所定义的字符数组(这时还不是从AT2416内读取的)
LcmPutstr( 5,40,"AT24C16!" );
Delay(5000);
}
}
对代码进行编译成HEX文件写入S51后运行:
好!数据已写入AT24C16内,下面修改程序,看看读取出的内容是否正确。
void Main( void )
{
uchar x,i;
uchar *ascii;
uchar abcd[9]; //定义一个字符数组,内容为空
LcmInit();
LcmClear();
ascii=abcd; //将字符数组的首地址交给指针变量
//write_nbyte(0,0,ascii,8); //写AT24C16语句被注释掉
read_nbyte(0,0,ascii,8); //从AT24C16读取8个字节的数据存入以ascii为首地址的abcd变量内
while(1)
{
LcmClear();
LcmPutBMP(BMP1);
Delay(3000);
LcmClear();
//LcmReverseBMP();
Delay(1000);
//LcmClear();
/*
x=0;
for(i=0;i<8;i++)
{
LcmPutHZ_12(x,i*7,i);
x=x+16;
}
*/
LcmPutstr( 3,40,ascii ); //显示从AT24C16内读取的内容
LcmPutstr( 5,40,"AT24C16!" );
Delay(5000);
}
}
重新编译后写入S51芯片运行:
第一幅:
第二幅:
OK,成功!这次"iic test"这行字符是从AT24C16内读取的。你可以试着拔掉那两根SDA,SCL的数据线,再按下复位键重新运行,你会发现那行“iic test”没有显示了。说明没有读取到数据。再装上数据线,重按复位键运行,又正常了。后面就可以将存储在S51代码段的字模,图形数据等,都写到AT24C16里了。
在网上找到一个写的不错的读写AT24CXXX的通用程序函数,也很好用,程序见下面:
#include
#include
#define uchar unsigned char
#define uint unsigned int
#define ERRORCOUNT 10
***it SDA=P1^6;
***it SCL=P1^7;
enum eepromtype {M2401,M2402,M2404,M2408,M2416,M2432,M2464,M24128,M24256};
enum eepromtype mType;
bit RW24XX(unsigned char *DataBuff,unsigned char ByteQuantity,unsigned int Address,
unsigned char ControlByte,enum eepromtype mType);
//DataBuff为读写数据输入/输出缓冲区的首址
//ByteQuantity 为要读写数据的字节数量
//Address 为EEPROM的片内地址
//ControlByte 为EEPROM的控制字节,具体形式为(1)(0)(1)(0)(A2)(A1)(A0)(R/W),其中R/W=1,
//表示读操作,R/W=0为写操作,A2,A1,A0为EEPROM的页选或片选地址;
//EepromType为枚举变量,需为M2401至M24256中的一种,分别对应24C01至24C256;
//函数返回值为一个位变量,若返回1表示此次操作失效,0表示操作成功;
//ERRORCOUNT为允许最大次数,若出现ERRORCOUNT次操作失效后,则函数中止操作,并返回1
//SDA和SCL由用户自定义,这里暂定义为P0^0和P0^1;
//其余的用户不用管,只要把只子程序放在你的程序中并调用它就可以了;
/***********************************************************************************/
bit RW24XX(unsigned char *DataBuff,unsigned char ByteQuantity,unsigned int Address,
unsigned char ControlByte,enum eepromtype mType)
{
void Delay1(uchar DelayCount);
void IICStart(void);
void IICStop(void);
bit IICRecAck(void);
void IICNoAck(void);
void IICAck(void);
unsigned char IICReceiveByte(void);
void IICSendByte(unsigned char sendbyte);
unsigned char data j,i=ERRORCOUNT;
bit errorflag=1;
while(i--)
{
IICStart();
IICSendByte(ControlByte&0xfe);
if(IICRecAck())
continue;
if(mType>M2416)
{
IICSendByte((unsigned char)(Address>>8));
if(IICRecAck())
continue;
}
IICSendByte((unsigned char)Address);
if(IICRecAck())
continue;
if(!(ControlByte&0x01))
{
j=ByteQuantity;
errorflag=0; //********clr errorflag
while(j--)
{
IICSendByte(*DataBuff++);
if(!IICRecAck())
continue;
errorflag=1;
break;
}
if(errorflag==1)
continue;
break;
}
else
{
IICStart();
IICSendByte(ControlByte);
if(IICRecAck())
continue;
while(--ByteQuantity)
{
*DataBuff++=IICReceiveByte();
IICAck();
}
*DataBuff=IICReceiveByte(); //read last byte data
IICNoAck();
errorflag=0;
break;
}
}
IICStop();
if(!(ControlByte&0x01))
{
Delay1(255);
Delay1(255);
Delay1(255);
Delay1(255);
}
return(errorflag);
}
/*****************以下是对IIC总线的操作子程序***/
/*****************启动总线**********************/
void IICStart(void)
{
SCL=0; //
SDA=1;
SCL=1;
_nop_();
_nop_();
_nop_();
SDA=0;
_nop_();
_nop_();
_nop_();
_nop_();
SCL=0;
SDA=1; //
}
/*****************停止IIC总线****************/
void IICStop(void)
{
SCL=0;
SDA=0;
SCL=1;
_nop_();
_nop_();
_nop_();
SDA=1;
_nop_();
_nop_();
_nop_();
SCL=0;
}
/**************检查应答位*******************/
bit IICRecAck(void)
{
SCL=0;
SDA=1;
SCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
CY=SDA; //因为返回值总是放在CY中的
SCL=0;
return(CY);
}
/***************对IIC总线产生应答*******************/
void IICACK(void)
{
SDA=0;
SCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
SCL=0;
_nop_();
SDA=1;
}
/*****************不对IIC总线产生应答***************/
void IICNoAck(void)
{
SDA=1;
SCL=1;
_nop_();
_nop_();
_nop_();
_nop_();
SCL=0;
}
/*******************向IIC总线写数据*********************/
void IICSendByte(unsigned char sendbyte)
{
unsigned char data j=8;
for(;j>0;j--)
{
SCL=0;
sendbyte<<=1; //无论C51怎样实现这个操作,始终会使CY=sendbyte^7;
SDA=CY;
SCL=1;
}
SCL=0;
}
/**********************从IIC总线上读数据子程序**********/
unsigned char IICReceiveByte(void)
{
register receivebyte,i=8;
SCL=0;
while(i--)
{
SCL=1;
receivebyte=(receivebyte<<1)|SDA;
SCL=0;
}
return(receivebyte);
}
/***************一个简单延时程序************************/
void Delay1(uchar DelayCount)
{
while(--DelayCount);
}
但实际使用中发现它有个严重的错误。就是没有考虑到不同的I2C器件在写页操作时,是有一页数据限制的,AT24C01是8个字节,AT24C02-16是16个字节,AT24C33-64是32个字节。AT24C128-256是64个字节。AT24C512是128个字节。AT24C1024是256个字节。
这是器件说明书上的资料,我手上只有AT24C02、AT24C16和AT24C256,实际使用了一下,AT24C02的页限制是8个字节。另两个是和说明书上一致的。当我写的数据量超出页限制时,地址指针就会回到起始点覆盖原来的数据。例如我以页方式写10个字节的数据到AT24C02时。前8个字节的数据正常写入,第9、第10个数据就回头覆盖掉了第一、第二个数据。
你如果把265个字节的数据往AT24C02进行写页操作,它也不报错,就一个劲地反复覆盖你8个字节的空间,再比如你往AT24C256里输入一个字节的地址,或往AT24C02里输入两个字节的地址,它也不报错,就这么胡乱地把地址当数据或把数据当成地址往里一阵乱写。当你读取数据时才发现里面混乱不堪。这使得上面那个读写AT24CXXX器件的程序怎么运行都不会出错。我觉得这是AT24CXX器件设计上比较弱智的地方。
所以上面的程序得修改。
12.板子上的最后一个部件RS232串口
电路图:
检查无短路现象后接上电源。
在电脑上将程序写好,编译后用ISP写入S51。程序很简单,就是不停地向PC传送“hello World!”。程序如下:
#include
#include
void main(void)
{
SCON=0x50; //串口方式1,允许接收
TMOD=0x20; //定时器1定时方式2
PCON=0x80; //设定SMOD为1
TCON=0x40; //设定时器1开始计数
TH1=0xfd; //设定波特率为19200
TL1=0xfd;
TI=1;
TR1=1; //启动定时器
while(1)
{
printf("hello World!n");
}
}
在PC上启动串口调试器,将波特率调为19200,打开串口。然后按一下单片机上的复位钮。如下图:
电路运行正常。后面就是要把电路焊到板子上了。
13、板外的第一个扩展—DS18B20温度测量(1)理论知识DS18B20 DS18B20与前产品DS1820的不同:
DS18B20继承了DS1820的全部优点,并做了如下改进 1.供电范围扩大为3.0--5.5V。2.温度分辨力可编程。3.转换速率有很大提高.4.内部存储器映射关系发生变化。5.具有电源反接保护电路。5.体积减小一半。 对我们使用来说最大的不同就是DS18B20可以程序设定9~12位的分辨率数字值,而DS1820为固定的9位数字值,且温度转换时的延时时间由2s减为750ms。。
电路的接法:
DS18B20说明书上介绍了几种电路的接法,但我这里就说最常用的一种接法见图:
先介绍一下DS18B20内部的结构:
常规的内部逻辑图我就不说了,只说说跟我们使用直接相关的内容。
DS18B20的内部存储资源分为8个字节的ROM、9个字节的RAM、3个字节的EEPROM如下图:
一、ROM:
在DS18B20内部光刻了一个长度为64bit的ROM,这个编码是器件的身份识别标志。如下图:
64位光刻ROM的排列是:开始(最低)8位是产品类型标号,对于DS18B20来说就是(28H),接着的48位是该DS18B20自身的序列号,最后8位是前面56位的循环冗余校验码(CRC=X8+X5+X4+1)。光刻ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
二、RAM:
高速暂存存储器(RAM)由9个字节组成,包含了8个连续字节,前两个字节是测得的温度信息,第一个字节的内容是温度温度的低八位,第二个字节是温度的高八位。第三个和第四个字节是温度高限TH、温度低限TL暂存区,第五个字节是配置寄存器暂存区,第6、7、8字节是系统保留用,就相当于DS18B20的运算内存,第九个字节是冗余检验字节。其分配如下表所示。
①、第0和第1字节:
当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后。对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制;当S=1时,先将补码变为原码,再计算十进制值。
这是12位转化后得到的12位数据,存储在18B20的两个8比特的RAM中,二进制中的前面5位是符号位,如果测得的温度大于0,这5位为0,只要将测到的数值乘于0.0625即可得到实际温度;如果温度小于0,这5位为1,测到的数值需要取反加1再乘于0.0625即可得到实际温度。看下图
例如+125℃的数字输出为07D0H,十进制是2000,乘以0.0625就等于125℃。同样+25.0625℃的数字输出为0191H,十进制为401,乘以0.0625就得出25.0625℃了。-55℃的数字输出为FC90H,因为符号位为1,先将1111110010010000取反,得1101101111,再加一得1101110000,十进制为880,乘以0.0625就得55,为负值,即-55℃。
② 第2第3字节:
RAM的第2、3、4字节和EEPROM的三个字节是对应的,内容是相同的,只是RAM因为是暂存器,失电后数据就丢失了。而EEPROM是电擦除只读存储器,失电后数据不会丢失。在工作时得到复位命令后就从EEPROM复制一份数据到RAM的第2、3、4字节内,作为我们进行报警搜索、改写报警值和改写器件设置用,我们从外部只能对RAM进行操作,EEPROM只能从RAM复制而得到要保存的数据。
第2字节为报警值高限TH,第3字节为报警值低限。DS18B20完成一次温度转换后,就拿温度值和存储在TH和TL中的值进行比较,因为这些寄存器是8位的,所以小数位被忽略不计。TH或TL的最高有效位直接对应16位温度寄存器的符号位。如果测得的温度高于TH或低于TL,器件内部就会置位一个报警标识。每进行一次测温就对这个标识进行一次更新。当报警标识置位时,DS18B20会对报警搜索命令有反应。这样就允许许多DS18B20并联在一起同时测温,如果某个地方温度超过了限定值。报警的器件就会被立即识别出来并读取。而不用读未报警的器件。
③ 第4字节 配置寄存器:
第4字节的配置寄存器是用来设置DS18B20的工作模式和测量精度的,其内容如下图:
低五位一直都是"1",TM是测试模式位,用于设置DS18B20在工作模式还是在测试模式。在DS18B20出厂时该位被设置为0,用户不要去改动。R1和R0用来设置分辨率,如下图所示:(DS18B20出厂时被设置为12位)
我们使用时可以跟据实际需要通过修改RAM第4字节的R0和R1的值来DS18B20的温度测量精度。需要保存这种设置时,还要用一条复制命令将RAM内的数据复制到EEPROM内。
④ 第5、6、7、8字节:
前面我们已经说过。RAM的第5、6、7字节是器件的保留字节,就相当于器件内部转换运算时所用的内存。第8字节是循环冗余校验字节。它是前面8个字节的CRC值。起着对前面字节的校验作用。
三、EEPROM:
EEPROM只有三个字节,和RAM的第2、3、4字节的内容相对应,它的作用就是存储RAM第2、3、4字节的内容,以使这些数据在掉电后不丢失。可能通过几条命令将RAM的该3个字节内容复制到EEPROM或从EEPROM将该3个字节内容复制到RAM的第2、3、4字节去。因为我们从外部想改写报警值和器件的设置都是只对RAM进行操作的。要保存这些设置后的数据就还要用相应的命令将RAM的数据复制到EEPROM去。
好了,下面说说对DS18B20的操作都有哪些命令:
对DS18B20的操作分为对ROM的操作和对RAM的操作。列表见下图:
实际操作的具体实现:
DS18B20是单总线器件,通讯协议包括几种单线信号类型:复位脉冲、存在脉冲、写0、写1、读0、读1。所有这些信号,除存在脉冲外,其余都是由总线控制器(单片机)发出的。根据DS18B20的通讯协议,主机(单片机)控制DS18B20完成一次操作经过三个步骤:①要对DS18B20进行复位操作,②复位成功后发送一条ROM指令,③最后发送RAM指令,这样才能对DS18B20进行预定的操作。
① 对DS18B20复位操作:
主机(单片机)和DS18B20间的任何通讯都需要以初始化序列开始,初始化序列就是主机发出一个复位脉冲跟着检测一个DS18B20的存在脉冲,表明DS18B20已经准备好发送和接收数据。初始化序列见下图:
主机首先发出一个480-960微秒的低电平脉冲,然后释放总线变为高电平,并在随后的480微秒时间内对总线进行检测,如果有低电平出现说明总线上有器件已做出应答。若无低电平出现一直都是高电平说明总线上无器件应答。
做为从器件的DS18B20在一上电后就一直在检测总线上是否有480-960微秒的低电平出现,如果有,在总线转为高电平后等待15-60微秒后将总线电平拉低60-240微秒做出响应存在脉冲,告诉主机本器件已做好准备。若没有检测到就一直在检测等待。
② 对DS18B20的写和读操作:
接下来就是主机发出各种操作命令,但各种操作命令都是向DS18B20写0和写1组成的命令字节,接收数据时也是从DS18B20读取0或1的过程。因此首先要搞清主机是如何进行写0、写1、读0和读1的。
写周期最少为60微秒,最长不超过120微秒。写周期一开始做为主机先把总线拉低1微秒表示写周期开始。随后若主机想写0,则继续拉低电平最少60微秒直至写周期结束,然后释放总线为高电平。若主机想写1,在一开始拉低总线电平1微秒后就释放总线为高电平,一直到写周期结束。而做为从机的DS18B20则在检测到总线被拉底后等待15微秒然后从15us到45us开始对总线采样,在采样期内总线为高电平则为1,若采样期内总线为低电平则为0。
对于读数据操作时序也分为读0时序和读1时序两个过程。读时隙是从主机把单总线拉低之后,在1微秒之后就得释放单总线为高电平,以让DS18B20把数据传输到单总线上。DS18B20在检测到总线被拉低1微秒后,便开始送出数据,若是要送出0就把总线拉为低电平直到读周期结束。若要送出1则释放总线为高电平。主机在一开始拉低总线1微秒后释放总线,然后在包括前面的拉低总线电平1微秒在内的15微秒时间内完成对总线进行采样检测,采样期内总线为低电平则确认为0。采样期内总线为高电平则确认为1。完成一个读时序过程,至少需要60us才能完成。(为什么不可以像写时序那样将采样时间放在读周期开始后的第15微秒到45微秒之间呢。虽然目前这样也不是不可以,但总觉得不安全。有点悬啊!)
DS18B20的说明书上也说,由于主机拉低总线电平时间Tint、释放总线时的恢复时间TRC与采样时间Tsample之和必须小于15微秒。如下图13。为了使读出数据更可靠,说明书上建议Tint和TRC保持时间尽可能小,把控制器采样时间放到15微秒周期的最后。如下图14。
(要是像写周期那样不就从容了,何必搞得紧紧张张的,唉!)
好!弄清了如何复位,如何写1写0和读1读0,我们现在就要看看在总线上如何进行实际的运用。
例如,我们做两个操作,第一个是让DS18B20进行一次温度的转换。第二是读取RAM内的温度。
① 让DS18B20进行一次温度的转换。前面已经讲过每一个对DS18B20的操作都要有三个步骤。一是复位操作。二是对ROM的操作。三是对RAM的操作。现在我们要做的是让DS18B20进行一次温度的转换,那具体的操作就是:1、主机先作个复位操作,2、主机再写跳过ROM的操作(CCH)命令,3、然后主机接着写个转换温度的操作命令,后面释放总线至少一秒,让DS18B20完成转换的操作。在这里要注意的是每个命令字节在写的时候都是低字节先写,例如CCH的二进制为11001100,在写到总线上时要从低位开始写,写的顺序是“零、零、壹、壹、零、零、壹、壹”。整个操作的总线状态如下图。
② 读取RAM内的温度数据。同样,这个操作也要接照三个步骤。1、主机发出复位操作并接收DS18B20的应答(存在)脉冲。2、主机发出跳过对ROM操作的命令(CCH)。3、主机发出读取RAM的命令(BEH),随后主机依次读取DS18B20发出的从第0一第8,共九个字节的数据。如果只想读取温度数据,那在读完第0和第1个数据后就不再理会后面DS18B20发出的数据即可。同样读取数据也是低位在前的。整个操作的总线状态如下图:
在这里得说明一下,第二步跳过对ROM操作的命令是在总线上只有一个器件时,为节省时间而简化的操作,若总线上不止一个器件,那么跳过ROM操作命令将会使几器件同时响应,这样就会出现数据冲突。
13、板外的第一个扩展——DS18B20温度测量(2)实际制做这个扩展,电路很简单,板子就不用做了吧!把电路焊好就行了。
这个是原理图:
实际接线图如下:
接好的实物图如下:
接下来就是写程序了,我们还是一步步地来完成:
① 向总线发出复位信号:
***it TMDAT=P1^1; //设P1.1为TMDAT
void tmreset(void)
{
uint i;
TMDAT=0; //将总线拉低
i=103;
while(i>0) i--; //延时700微秒
TMDAT=1; //释放总线
i=4;
while(i>0) i--; //延时40微秒
}
② 检测总线上是否有器件应答(是否有存在信号):
void tmpre(void)
{
uint i;
while(TMDAT); //检测低电平的存在。否则一直循环。
while(~TMDAT); //检测高电平的存在。否则一直循环。
i=4;
while(i>0) i--; //延时
}
这段程序就是检测一个先低后高的脉冲的存在。说明有器件应答了。
③ 从DS18B20上读一个bit
bit tmrbit(void)
{
uint i;
bit dat;
TMDAT=0; //先将总线拉低
i++; //延时一微秒
TMDAT=1; //释放总线
i++;
i++; //延时两微秒
dat=TMDAT; //读取总线
i=8;
while(i>0) i--; //延时
return(dat);
}
④ 向总线写一个bit
void tmwbit(bit testb)
{
if(testb) //如果是1
{ TMDAT=0; //先拉低总线
i++; i++; //延时2微秒
TMDAT=1; //释放总线
i=8;
while(i>0) i--; //延时40微秒
}
else //如果是0
{ TMDAT=0; //先拉低总线
i=8;
while(i>0) i--; //延时40微秒
TMDAT=1; //释放总线
i++; i++; //延时2微秒
}
}
我这么写大家能看懂吧!
与单片机连接图:
好!现在上完整的程序:(要慢慢的读了)
//LCD12864
//**********************************************************
//连线表: CPU=89C51 SysClock=12MHz *
//RS=P2.0 R/W=P2.1 E=P2.2 CS1=P2.3 CS2=P2.4 *
//DB0-DB7=P3.0-P3.7 /Reset=InBoard *
//**********************************************************
//DS18B20
//**********************************************************
//连线表: CPU=89C51 SysClock=12MHz *
//单总线: TMDAT=P1.1
//
//**********************************************************
#include
#include
#include
#include
#include
#define uchar unsigned char
#define uint unsigned int
/********************LCD引脚定义********************/
#define DataPort P3 //LCD128*64 I/O 信号管脚
***it RS =P2^0; //数据指令
***it RW =P2^1; //读写
***it E =P2^2; //使能
***it CSL =P2^3; //左片选
***it CSR =P2^4; //右片选
uchar Page; //页 地址
uchar Col; //列 地址
uchar code ASC_5x7[]; //5×7阵点字模
uchar str[4]; //char的值转换成字符串
/********************DS18B20引脚定义********************/
***it TMDAT=P1^1;
/********************DS18B20函数定义*******************/
void dmsec(uint count);//延时(count)毫秒
void tmreset(void); //产生复位信号
void tmpre(void); //检测器件应答信号
bit tmrbit(void); //从总线读一个bit
uchar tmrbyte(void); //从总线读一个字节
void mwbyte(uchar dat);//向总线写一个字节
void tmstart(void); //启动一次温度转换
uchar tmrtemp(void); //读取温度数据
/********************LCD函数定义*******************/
void BusyL(void); //左屏检测忙
void BusyR(void); //右屏检测忙
void CheckBusy(void); //读取忙信号
void Delay(uint MS); //延时
void Locatexy(void); //将屏幕横向0-12纵向0-7转换成左、右屏的的X、Y
void WriteCommandL( uchar CommandByte ); //向左屏写入指令
void WriteCommandR( uchar CommandByte ); //向右屏写入指令
uchar ReadData( void ); //读数据
void WriteData( uchar DataByte ); //写数据
void LcmClear( void ); //清屏
void LcmInit( void ); //初始化
uchar * uchartostr(unsigned char unm); //将值转成字符串
void LcmPutAsc( uchar asc ); //显示一个5×7的ASC字符
void LcmPutstr( uchar row,uchar y,uchar * str ); //在设定位置显示字符串
/*****************DS18B20函数体定义****************/
void dmsec(uint count)
{
uint i;
while(count--)
{ for(i=0;i<125;i++){}
}
}
void tmreset(void)
{
uint i;
TMDAT=0;
i=103;
while(i>0) i--;
TMDAT=1;
i=4;
while(i>0) i--;
}
void tmpre(void)
{
uint i;
while(TMDAT);
while(~TMDAT);
i=4;
while(i>0) i--;
}
bit tmrbit(void)
{
uint i;
bit dat;
TMDAT=0;
i++;
TMDAT=1;
i++;
i++;
dat=TMDAT;
i=8;
while(i>0) i--;
return(dat);
}
uchar tmrbyte(void)
{
uchar i,j,dat;
dat=0;
for(i=1;i<=8;i++)
{ j=tmrbit();
dat=(j<<7)|(dat>>1);
}
return(dat);
}
void tmwbyte(uchar dat)
{
uint i;
uchar j;
bit testb;
for(j=1;j<=8;j++)
{ testb=dat & 0x01;
dat=dat>>1;
if(testb)
{ TMDAT=0;
i++; i++;
TMDAT=1;
i=8;
while(i>0) i--;
}
else
{ TMDAT=0;
i=8;
while(i>0) i--;
TMDAT=1;
i++; i++;
}
}
}
void tmstart(void)
{
tmreset();
tmpre();
dmsec(1);
tmwbyte(0xcc);
tmwbyte(0x44);
}
uchar tmrtemp(void)
{
uchar a,b,y1,y2,y3;
tmreset();
tmpre();
dmsec(1);
tmwbyte(0xcc);
tmwbyte(0xbe);
a=tmrbyte();
b=tmrbyte();
y1=a>>4;
y2=b<<4;
y3=y1|y2;
return(y3);
}
/************LCD12864函数体***************/
/***************************/
/*检查Busy */
/***************************/
void BusyL(void)
{
CSL= 1;
CSR= 0;
CheckBusy();
}
void BusyR(void)
{
CSL= 0;
CSR= 1;
CheckBusy();
}
void CheckBusy(void)
{
RS = 0; //指令
RW = 1;
DataPort= 0xFF; //输出0xff以便读取正确
E = 1;
_nop_();
while(0);//DataPort & 0x80); //Status Read Bit7 = BUSY
E = 0;
_nop_();
}
/********************************************************/
/*根据设定的坐标数据,定位LCM上的下一个操作单元位置 */
/********************************************************/
void Locatexy(void)
{
uchar x,y;
switch (Col&0xc0) /* col.and.0xC0 */
{ /*条件分支执行 */
case 0: {BusyL();break;}/*左区 */
case 0x40: {BusyR();break;}/*右区 */
}
x = Col&0x3F|0x40; /* col.and.0x3f.or.Set Y Address*/
y = Page&0x07|0xB8; /* row.and.0x07.or.set Page */
CheckBusy(); /* waitting for enable */
RS = 0; //指令
RW = 0; //写
DataPort = y; //设置页面地址
E = 1;
_nop_();
E = 0;
_nop_();
CheckBusy(); /* waitting for enable */
RS = 0;
RW = 0;
DataPort = x; //设置列地址
E = 1;
_nop_();
E = 0;
_nop_();
}
/***************************/
/*写指令 */
/***************************/
void WriteCommandL( uchar CommandByte )
{
BusyL();
DataPort = CommandByte;
RS = 0; //指令
RW = 0;
E = 1;
_nop_();
E = 0;
_nop_();
}
void WriteCommandR( uchar CommandByte )
{
BusyR();
DataPort = CommandByte;
RS = 0; //指令
RW = 0;
E = 1;
_nop_();
E = 0;
_nop_();
}
/***************************/
/*读数据 */
/***************************/
uchar ReadData( void )
{
uchar DataByte;
Locatexy(); /*坐标定位,返回时保留分区状态不变 */
RS = 1; /*数据输出*/
RW = 1; /*读入 */
DataPort = 0xFF; //输出0xff以便读取正确
E = 1; /*读入到LCM*/
_nop_();
DataByte = DataPort; /*数据读出到数据口P1 */
E = 0;
_nop_();
return DataByte;
}
/***************************/
/*写数据 */
/***************************/
void WriteData( uchar DataByte )
{
Locatexy(); /*坐标定位,返回时保留分区状态不变 */
RS = 1; /*数据输出*/
RW = 0; /*写输出 */
DataPort = DataByte; /*数据输出到数据口 */
E = 1; /*写入到LCM*/
_nop_();
E = 0;
_nop_();
}
void LcmClear( void )
{
Page = 0;
Col = 0;
for(Page=0;Page<8;Page++)
for(Col=0;Col<128;Col++)
WriteData(0);
}
void LcmInit( void )
{
Delay(200); //等待复位
WriteCommandL(0x3f); //开显示
WriteCommandR(0x3f);
WriteCommandL(0xc0); //设置起始地址=0
WriteCommandR(0xc0);
WriteCommandL(0x3f); //开显示
WriteCommandR(0x3f);
LcmClear();
Col = 0;
Page= 0;
Locatexy();
}
uchar * uchartostr(uchar unm)
{
uchar x00,xx,x0,x,n;
x00=unm/100;
xx=unm%100;
x0=xx/10;
x=xx%10;
n=0;
if(x00!=0)
{ str[n]=x00+48; //值加48即为字符
n++;
}
if(!(x00==0&x0==0))
{ str[n]=x0+48;
n++;
}
str[n]=x+48;
n++;
str[n]=' |