在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
没买板子之前,用protues画过电路图,实现了通过i2c总线向EEPROM中写入和读出数据。 今天,在自己买的板子上面写关于i2c总线的程序,有个地方忘了延时,调程序的时候很蛋疼。下面说说我对I2c总线的理解 i2c总线共有两根线,一根是时钟信号线,一根是数据线。这里我只实现了,指定EEPROM片内地址,并向其中写入一个字节。从EEPROM中指定地址读出一个字节。 写入过程 1.需要发一个启动信号 2.发送EEPROM设备地址 3.发送EEPROM片内地址 4.发送你要写入的数据 5.发送结束信号 读出过程: 1.发送启动信号 2.发送设备地址(注意这里是伪写 和写入过程前两个步骤一样 注意仔细看EEPROM文档) 3.发送EEPROM片内地址 4.发送启动信号(读数据需要启动两次) 5.发送设备地址(这里和读过程不同的是r/w位 前面是低电平 这里是高电平) 6.读取数据 7.发送结束信号 6.发送结束信号 当然单片机向总线写数据完成后,要注意检测应答信号,读取数据后,单片机应向设备发送,非应答信号或者叫应答非信号。 1 /*================================================================================= 2 功能:通过i2c总线向EEPROM中写入数据 并读回数据 验证I2C总线通信 实现记录开机次数 3 电路图:P2.0接SCL 4 P2.1接SDA 5 P1口接led发光二极管 6 P2.3接 数码管位选信号 7 P2.2接段选信号 8 P0口接数码管 9 注意理解设备发出的应答 10 和单片机发出的非应答或者叫应答非 11 两者使得通信完整 只不过方向不一样 12 ====================================================================================*/ 13 #include <reg51.h> 14 #include <intrins.h> 15 #define uchar unsigned char 16 #define uint unsigned int 17 #define TIME_SET 50000 /* 一次定时时间 */ 18 19 sbit scl = P2^0; 20 sbit sda = P2^1; 21 22 bit ack; 23 24 uchar code table[] = { 25 0x3F,0x06,0x5b,0x4f,0x66,0x6d, 26 0x7d,0x07,0x7f,0x6f,0x77,0x7c, 27 0x39,0x5e,0x79,0x71,0x00 28 }; //led亮的数字 29 30 sbit wela = P2^3;//位选信号 31 sbit dula = P2^2;//段选 32 uchar times; 33 34 void start(); /* 开始信号 */ 35 void stop(); /* 结束信号 */ 36 void write(uchar byte); /* 向EEPROM中写入一个字节 */ 37 void delay(uchar time_delay); /* 延时函数 */ 38 void write_address(uchar byte, uchar address); /* 根据地址向EEPROM中写入数据 */ 39 uchar read_address(uchar address); /* 从指定地址读出数据 */ 40 void no_ack(); /* 发送一个非应答信号 */ 41 void display(uchar num_display); /* 显示开机次数 */ 42 void init(); 43 44 void main() 45 { 46 init(); 47 times = read_address(0x50); 48 times += 1; 49 write_address(times, 0x50); 50 display(times); 51 while(1); 52 } 53 /*========================== 54 功能:发送启动信号 55 输入:无 56 输出:启动信号 SDA负跳变 57 ============================*/ 58 void start() 59 { 60 sda = 1; 61 // _nop_(); /* ? */ 62 scl = 1; 63 delay(1); 64 sda = 0; 65 delay(1); 66 scl = 0; /* scl为什么要拉低 */ 67 delay(1); /* ? */ 68 } 69 /*========================== 70 功能:发送结束信号 71 输入:无 72 输出:启动信号 SDA正跳变 73 ============================*/ 74 void stop() 75 { 76 sda = 0; 77 _nop_(); 78 scl = 1; 79 delay(5); 80 sda = 1; 81 delay(5); 82 } 83 /*============================================== 84 功能:延时函数 time_delay = 10 延时大约0.5MS 85 输入:延时时间 86 输出:延时 87 ================================================*/ 88 void delay(uchar time_delay) 89 { 90 uchar i,j; 91 for(i = time_delay; i > 0; i--) 92 for(j = time_delay; j > 0; j --) 93 {} 94 } 95 /*===================================== 96 功能:向数据线上写一个字节 先写高位 97 输入:待写字节 98 输出:写到数据总线上 99 =======================================*/ 100 void write(uchar byte) 101 { 102 uchar i = 0; 103 104 for(; i < 8; i ++) 105 { 106 if((byte << i) & 0x80) 107 sda=1; 108 else 109 sda = 0; 110 delay(1); /* 为啥要延时 */ 111 scl = 1; 112 delay(1); 113 scl = 0; 114 } 115 delay(1); 116 117 sda = 1; 118 delay(1); 119 scl = 1; 120 delay(1); 121 if(sda == 0) 122 ack = 0; 123 else 124 ack = 1; 125 126 scl = 0; 127 } 128 129 /*========================================= 130 功能:向指定地址写入一个字节数据 131 输入:address写入地址,byte写入字节数据 132 输出:无 133 ===========================================*/ 134 void write_address(uchar byte, uchar address) 135 { 136 start(); 137 delay(5); 138 write(0xae); /* 写入设备地址 */ 139 delay(5); 140 write(address); 141 delay(5); 142 write(byte); 143 144 delay(5); 145 146 stop(); 147 delay(200); /* 延时很重要!!??? */ 148 } 149 /*========================== 150 功能:从指定地址读出数据 151 输入:数据地址 152 输出:指定地址的数据 153 ============================*/ 154 uchar read_address(uchar address) 155 { 156 uchar i; 157 uchar result = 0; 158 start(); 159 160 write(0xae); /* 写入设备地址 */ 161 162 write(address); 163 164 start(); /* 第二次设置开始信号 */ 165 166 write(0xaf); 167 168 sda = 1; 169 170 for(i = 0; i < 8; i ++) /* 读出数据 */ 171 { 172 scl = 0; 173 delay(1); 174 scl = 1; 175 delay(1); 176 result <<= 1; /* result <<= 1? */ 177 if(sda == 1) 178 result = result + 1; 179 180 delay(1); 181 } 182 scl = 0; /* 为啥要拉低 */ 183 delay(1); 184 185 no_ack(); /* 发送非应答信号 */ 186 187 stop(); 188 return result; 189 } 190 /*======================= 191 功能:初始化变量 192 输入:无 193 输出:初始化后的变量 194 ========================*/ 195 void init() 196 { 197 ack = 1; 198 scl = 1; 199 sda = 1; 200 201 times = 0; 202 EA = 1; 203 ET0 = 1; 204 TH0 = (65536 - TIME_SET) / 256; 205 TL0 = (65536 - TIME_SET) % 256; 206 TR0 = 1; 207 } 208 /*========================== 209 功能:发送一个非应答信号 210 输入:无 211 输出:非应答信号 212 ============================*/ 213 void no_ack() 214 { 215 sda = 1; 216 delay(5); 217 scl = 1; 218 delay(5); 219 scl= 0; 220 delay(5); 221 } 222 /*====================== 223 功能:定时器中断函数 224 输入:定时器0中断 225 输出:刷新数码管显示 226 ========================*/ 227 void timer0() interrupt 1 228 { 229 TH0 = (65536 - TIME_SET) / 256; 230 TL0 = (65536 - TIME_SET) % 256; 231 display(times); 232 } 233 /*========================== 234 功能:数码管显示开机次数 235 输入:开机次数 236 输出:数码管显示开机次数 237 ============================*/ 238 void display(uchar num_display) 239 { 240 uchar ge,shi;//个位和十位 241 242 ge = num_display % 10; 243 shi = num_display / 10; 244 //消影 245 P0 = 0xff; 246 wela = 1; 247 wela = 0; 248 P0 = 0; 249 dula = 1; 250 dula = 0; 251 252 253 //显示十位数字 254 P0 = 0xfe; 255 wela = 1; 256 wela = 0; 257 P0 = table[shi]; 258 dula = 1; 259 dula = 0; 260 delay(15); 261 262 //消影 263 P0 = 0xff; 264 wela = 1; 265 wela = 0; 266 P0 = 0; 267 dula = 1; 268 dula = 0; 269 270 //显示个位数字 271 P0 = 0xfd; 272 wela = 1; 273 wela = 0; 274 P0 = table[ge]; 275 dula = 1; 276 dula = 0; 277 delay(15); 278 }
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论