【虹星宝典】记录单片机学习之旅——目录
I2C协议,也称iic协议。常用于短距离、低速设备间的数据传输,是有飞利浦(NXP)开发的同步串行通信协议,仅需两根线(SDA:数据线;SCL:时钟线)即可进行多设备的半双工互联通信。
基本的通讯时序分为:起始信号(IIC_Start)、结束信号(IIC_Stop)、发送ACK应答信号(IIC_SendACK)、发送NACK应答信号(IIC_SendNoACK)、接收ACK/NACK应答信号(IIC_WaitACK)和读/写1字节数据,并可以衍生出读/写N字节数据等。
本篇作为IIC协议入门实验的器件是AT24C128(实验箱上为AT24C02)。
一、IIC部分
【时刻周期】
以SCL的脉冲信号为标准(这个可以决定IIC的通讯速率)。
当SCL=0时,SDA的电平无效,此时可以随意改变SDA的电平,即从蓝色到黄色这段时间。
当SCL=1时,SDA的电平需要保持稳定,持续到再一次SCL=0的瞬间(及SCL维持信号稳定的一小段时间),此时数据有效,将被接收方读取,
即从黄色到红色这段时间。一个SCL周期,传输1bit数据。
- void IIC_Wait(void)
- {
- Delay2us();
- }
复制代码
【起始信号】和【结束信号】
起始信号:先拉低SDA总线,再拉低SCL总线
- void IIC_Start(void)
- {
- IIC_SCL = 1;
- IIC_SDA = 1;
- IIC_Wait();
- IIC_SDA = 0;
- IIC_Wait();
- IIC_SCL = 0;
- IIC_Wait();
- }
复制代码
结束信号:先拉高SCL总线,再拉高SDA总线
- void IIC_Stop(void)
- {
- IIC_SCL = 0;
- IIC_SDA = 0;
- IIC_Wait();
- IIC_SCL = 1;
- IIC_Wait();
- IIC_SDA = 1;
- IIC_Wait();
- }
复制代码
【发送ACK应答信号】和【发送NACK应答信号】
发送ACK信号:1位数据,SDA=0
- void IIC_SendACK(void)
- {
- IIC_SDA = 0;
- IIC_Wait();
- IIC_SCL = 1;
- IIC_Wait();
- IIC_SCL = 0;
- IIC_Wait();
- }
复制代码
发送NACK信号:1位数据,SDA=1
- void IIC_SendNoACK(void)
- {
- IIC_SDA = 1;
- IIC_Wait();
- IIC_SCL = 1;
- IIC_Wait();
- IIC_SCL = 0;
- IIC_Wait();
- }
复制代码
【接收ACK/NACK应答信号】
接收和发送原理一致。区别是作为接收方。
- u8 IIC_WaitACK(void)
- {
- u8 ack = 0;
- IIC_SDA = 1;
- IIC_Wait();
- IIC_SCL = 1;
- IIC_Wait();
- ack = IIC_SDA; // 0:ACK 1:NACK
- IIC_Wait();
- IIC_SCL = 0;
- IIC_Wait();
- return !ack; // 取反后 1:有应答 0:无应答
- }
复制代码
【读/写1字节数据】
一般8bit数据+1bitACK信号作为一包数据。
- u8 IIC_R_Byte(void)
- {
- u8 i = 8, dat = 0;
- IIC_SDA = 1;
- IIC_Wait();
- do
- {
- IIC_SCL = 1;
- IIC_Wait();
- dat <<= 1;
- if (IIC_SDA)
- dat |= 1;
- IIC_SCL = 0;
- IIC_Wait();
- } while (--i);
-
- return dat;
- }
复制代码
- void IIC_W_Byte(u8 dat)
- {
- u8 i = 8;
- do
- {
- if (dat & 0x80)
- IIC_SDA = 1;
- else
- IIC_SDA = 0;
- IIC_Wait();
- dat <<= 1;
- IIC_SCL = 1;
- IIC_Wait();
- IIC_SCL = 0;
- IIC_Wait();
- } while (--i);
- }
复制代码
二、AT24Cxx部分
主要用途
AT24CXX是一种EEPROM存储器,主要用于失去电源时,仍可以储存处理器的重要数据。使用I2C协议进行数据通信,具有宽电压(约1.7V to 5.5V)
从机地址
AT24CXX最多有3个从机地址配置引脚(A0/A1/A2),即最多挂载8个从机。
01 = A0/A1/A2 *08-byte Page Write mode*
02 = A0/A1/A2 *08-byte Page Write mode*
04 = NC/A1/A2 *16-byte Page Write Mode*
08 = NC/NC/A2 *16-byte Page Write Mode*
16 = NC/NC/NC *16-byte Page Write Mode*
32 = A0/A1/A2 *32-byte Page Write Mode*
64 = A0/A1/A2 *32-byte Page Write Mode*
128 = A0/A1/A2 *64-byte Page Write Mode*
256 = A0/A1/A2 *64-byte Page Write Mode*
512 = A0/A1/A2 *128-byte Page Write Mode*
1024 = NC/A1/NC
命名含义
其中XX可代表存储的容量大小,一般为XX*128byte:
01 = 128 Byte = 1K bit
02 = 256 Byte = 2K bit
04 = 512 Byte = 4K bit
08 = 1024 Byte = 8K bit
16 = 2048 Byte = 16K bit
32 = 4096 Byte = 32K bit
64 = 8192 Byte = 64K bit
128 = 16384 Byte = 128K bit
256 = 32768 Byte = 256K bit
512 = 65536 Byte = 512K bit
1024 = 131072 Byte = 1M bit
数据读写
每个数据地址可以保存1Byte数据(即char/u8类型)。
一般读写的协议格式为:(具体请看对应的手册)
对于01/02/04/08/16为【从机地址】【数据地址】【数据】....
对于32/64/128/256.等为【从机地址】【数据地址高位】【数据地址低位】【数据】....
由于1字节(8bit)最多描述2^8=256个,所以01(128)/02(256)有3个地址配置引脚可用。
04(512),需要9bit,所有只有2个地址配置引脚,而将【从机地址】中对应的NC位作为第9bit
08(1024),需要10bit,同理2个NC位作为第9/10bit
16(2048),需要11bit,3个地址配置引脚均为NC。
而从32(4096)开始,【数据地址】使用2Byte(16bit)表示,最多可以描述2^16=65536个,因此不需要向地址配置引脚借位。1024(131072)需要借1位,当手册中有2个NC位,这个用的少不深入讨论了。
注:AT24Cxx每次连续写入,最多写1页数据,便要等AT24Cxx完成1次擦写周期,一般为5-10ms。
- u8 AT24Cxx_R_Byte(u32 AT24Cxx_Type, u8 AT24Cxx_id, u16 AT24Cxx_addr)
- {
- u8 R_Byte;
- if (AT24Cxx_addr > AT24Cxx_Type)
- return 0xFF;
-
- // 1字节地址长度
- if (AT24Cxx_Type <= AT24C16)
- {
- IIC_R_NByte(AT24Cxx_id | (u8)((AT24Cxx_addr >> 8) << 1), AT24Cxx_addr, IIC_REG_TYPE_1Byte, &R_Byte, 1);
- }
- // 2字节地址长度
- else
- {
- IIC_R_NByte(AT24Cxx_id, AT24Cxx_addr, IIC_REG_TYPE_2Byte, &R_Byte, 1);
- }
- return R_Byte;
- }
-
- void AT24Cxx_R_NByte(u32 AT24Cxx_Type, u8 AT24Cxx_id, u16 AT24Cxx_addr, u8 *R_buff, u32 num)
- {
- if ((AT24Cxx_addr + num - 1) > AT24Cxx_Type)
- return;
-
- // 1字节地址长度
- if (AT24Cxx_Type <= AT24C16)
- {
- IIC_R_NByte(AT24Cxx_id | (u8)((AT24Cxx_addr >> 8) << 1), AT24Cxx_addr, IIC_REG_TYPE_1Byte, R_buff, num);
- }
- // 2字节地址长度
- else
- {
- IIC_R_NByte(AT24Cxx_id, AT24Cxx_addr, IIC_REG_TYPE_2Byte, R_buff, num);
- }
- }
复制代码
- #define Write_Cycle_Time 400 // 400us
-
- void AT24Cxx_W_Byte(u32 AT24Cxx_Type, u8 AT24Cxx_id, u16 AT24Cxx_addr, u8 W_Byte)
- {
- if (AT24Cxx_addr > AT24Cxx_Type)
- return ;
-
- // 1字节地址长度
- if (AT24Cxx_Type <= AT24C16)
- {
- IIC_W_NByte(AT24Cxx_id | (u8)((AT24Cxx_addr >> 8) << 1), AT24Cxx_addr, IIC_REG_TYPE_1Byte, &W_Byte, 1);
- }
- // 2字节地址长度
- else
- {
- IIC_W_NByte(AT24Cxx_id, AT24Cxx_addr, IIC_REG_TYPE_2Byte, &W_Byte, 1);
- }
- delay_us(Write_Cycle_Time); // 等待一段写周期 一般为5-10ms 测试最短为400-500us
- }
-
- void AT24Cxx_W_NByte(u32 AT24Cxx_Type, u8 AT24Cxx_id, u16 AT24Cxx_addr, u8 *W_buff, u32 num)
- {
- u16 addr_offset = 0; // 地址偏移量
- u16 page = 0; // 计算要写多少页
- u8 Remainder = 0; // 不满1页
-
- if ((AT24Cxx_addr + num - 1) > AT24Cxx_Type)
- return;
-
- switch (AT24Cxx_Type)
- {
- // 08-byte Page Write mode
- case AT24C01:
- case AT24C02:
- for (page = 0; page < (num / 8); page++)
- {
- addr_offset = AT24Cxx_addr + page * 8;
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_1Byte, &W_buff[page * 8], 8);
- delay_us(Write_Cycle_Time);
- }
- addr_offset = AT24Cxx_addr + page * 8;
- Remainder = num % 8;
- // 如果有空余数据
- if (Remainder)
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_1Byte, &W_buff[page * 8], Remainder);
- break;
- // 16-byte Page Write mode
- case AT24C04:
- case AT24C08:
- case AT24C16:
- for (page = 0; page < (num / 16); page++)
- {
- addr_offset = AT24Cxx_addr + page * 16;
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_1Byte, &W_buff[page * 16], 16);
- delay_us(Write_Cycle_Time);
- }
- addr_offset = AT24Cxx_addr + page * 16;
- Remainder = num % 16;
- // 如果有空余数据
- if (Remainder)
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_1Byte, &W_buff[page * 16], Remainder);
- break;
- // 32-byte Page Write mode
- case AT24C32:
- case AT24C64:
- for (page = 0; page < (num / 32); page++)
- {
- addr_offset = AT24Cxx_addr + page * 32;
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_2Byte, &W_buff[page * 32], 32);
- delay_us(Write_Cycle_Time);
- }
- addr_offset = AT24Cxx_addr + page * 32;
- Remainder = num % 32;
- // 如果有空余数据
- if (Remainder)
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_2Byte, &W_buff[page * 32], Remainder);
- break;
- // 64-byte Page Write mode
- case AT24C128:
- case AT24C256:
- for (page = 0; page < (num / 64); page++)
- {
- addr_offset = AT24Cxx_addr + page * 64;
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_2Byte, &W_buff[page * 64], 64);
- delay_us(Write_Cycle_Time);
- }
- addr_offset = AT24Cxx_addr + page * 64;
- Remainder = num % 64;
- // 如果有空余数据
- if (Remainder)
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_2Byte, &W_buff[page * 64], Remainder);
- break;
- // 128-byte Page Write mode
- case AT24C512:
- for (page = 0; page < (num / 128); page++)
- {
- addr_offset = AT24Cxx_addr + page * 128;
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_2Byte, &W_buff[page * 128], 128);
- delay_us(Write_Cycle_Time);
- }
- addr_offset = AT24Cxx_addr + page * 128;
- Remainder = num % 128;
- // 如果有空余数据
- if (Remainder)
- IIC_W_NByte(AT24Cxx_id | (u8)((addr_offset >> 8) << 1), addr_offset, IIC_REG_TYPE_2Byte, &W_buff[page * 128], Remainder);
- break;
- default:
- break;
- }
- delay_us(Write_Cycle_Time);
- }
复制代码
三、实验结果
简单搞了一个串口读写和擦除的指令。
W(写指令):往地址0-254写入0-254,对于AT24C02(ai8051u实验箱)的地址255是校验码。
R(读指令):把地址0-255的数据读出来
E(擦除指令):往地址0-254的数据写0
- // 写 W(0x57)
- if (RX1_Buffer[0] == 'W')
- {
- // at24c02 实验箱
- // Uart1_printf("开始写入\r\n");
- // for ( i = 0; i < 128; i++)
- // {
- // TX1_Buffer[i] = i;
- // }
- // AT24Cxx_W_NByte(AT24C02, AT24C02_ID1, 0, TX1_Buffer, 128);
- // for ( i = 128; i < 255; i++)
- // {
- // TX1_Buffer[i-128] = i;
- // }
- // AT24Cxx_W_NByte(AT24C02, AT24C02_ID1, 128, TX1_Buffer, 127);
- // Uart1_printf("写入完成\r\n");
-
- // at24128
- Uart1_printf("开始写入\r\n");
- for ( i = 0; i < 128; i++)
- {
- TX1_Buffer[i] = i;
- }
- AT24Cxx_W_NByte(AT24C128, AT24C128_ID1, 0, TX1_Buffer, 128);
- for ( i = 128; i < 255; i++)
- {
- TX1_Buffer[i-128] = i;
- }
- AT24Cxx_W_NByte(AT24C128, AT24C128_ID1, 128, TX1_Buffer, 127);
- Uart1_printf("写入完成\r\n");
- }
- // 读 R(0x52)
- else if (RX1_Buffer[0] == 'R')
- {
- // at24c02 实验箱
- // for ( i = 0; i < 96; i++)
- // {
- // TX1_Buffer[i] = AT24Cxx_R_Byte(AT24C02, AT24C02_ID1, i);
- // }
- // Uart1_Send_NByte(TX1_Buffer, 96);
- // for ( i = 0; i < 96; i++)
- // {
- // TX1_Buffer[i] = AT24Cxx_R_Byte(AT24C02, AT24C02_ID1, 96 + i);
- // }
- // Uart1_Send_NByte(TX1_Buffer, 96);
- // for ( i = 0; i < 64; i++)
- // {
- // TX1_Buffer[i] = AT24Cxx_R_Byte(AT24C02, AT24C02_ID1, 192 + i);
- // }
- // Uart1_Send_NByte(TX1_Buffer, 64);
-
- // at24c128
- for ( i = 0; i < 96; i++)
- {
- TX1_Buffer[i] = AT24Cxx_R_Byte(AT24C128, AT24C128_ID1, i);
- }
- Uart1_Send_NByte(TX1_Buffer, 96);
- for ( i = 0; i < 96; i++)
- {
- TX1_Buffer[i] = AT24Cxx_R_Byte(AT24C128, AT24C128_ID1, 96 + i);
- }
- Uart1_Send_NByte(TX1_Buffer, 96);
- for ( i = 0; i < 64; i++)
- {
- TX1_Buffer[i] = AT24Cxx_R_Byte(AT24C128, AT24C128_ID1, 192 + i);
- }
- Uart1_Send_NByte(TX1_Buffer, 64);
- }
- // 擦除 E(0x45)
- else if (RX1_Buffer[0] == 'E')
- {
- // at24c02 实验箱
- // Uart1_printf("开始擦除\xfd\r\n");
- // for ( i = 0; i < 0xff; i++)
- // {
- // AT24Cxx_W_Byte(AT24C02, AT24C02_ID1, i, 0);
- // delay_us(500);
- // }
- // Uart1_printf("擦除\xfd完成\r\n");
-
- // at24c128
- Uart1_printf("开始擦除\xfd\r\n");
- for ( i = 0; i < 0xff; i++)
- {
- AT24Cxx_W_Byte(AT24C128, AT24C128_ID1, i, 0);
- delay_us(500);
- }
- Uart1_printf("擦除\xfd完成\r\n");
- }
复制代码
结果如下:
先读一下,数据全为0。
再执行写命令
再读一下,数据正常
擦除指令
再读一下,数据已全部擦除
番外-有趣的实验
实验箱中测试2.5M速率和50K速率。从发送读取指令,到第1次接收串口反馈的时间差。约读96字节数据
2.5M:约20ms
50K:约90ms
好奇又测了几组:
1M,约20ms
500K,约20ms
400K,约20ms
250K,约30ms
125K,约40ms
100K,约50-60ms
50K,约90-100ms
结果表明,400K大约就是最大速率了,再高也会因为从机跟不上而限速了。
如何使用?
config.h里选择软硬件模式
iic.h里修改引脚定义,注意修改GPIO初始化
主函数初始化GPIO和IIC即可。
具体任务在task_uart1_polling.c中
13_IIC-AT24C128.zip
(442.34 KB, 下载次数: 3)
IIC总线协议手册.pdf
(1.29 MB, 下载次数: 1)
AT24C01_02C.pdf
(954.67 KB, 下载次数: 0)
AT24C04_08C.pdf
(879.92 KB, 下载次数: 0)
AT24C16C.pdf
(992.86 KB, 下载次数: 0)
AT24C32_64C.pdf
(812.67 KB, 下载次数: 0)
AT24C128_256C.pdf
(1.18 MB, 下载次数: 0)
AT24C256C.pdf
(900.91 KB, 下载次数: 1)
AT24C512C.pdf
(954.76 KB, 下载次数: 1)
AT24C1024.pdf
(212.73 KB, 下载次数: 2)
|