本帖最后由 WSGustin 于 2018-3-10 11:42 编辑
第十三章 AT24C32试验本次试验目的是通过W7500EVB的硬件I2C接口实现与AT24C32存储芯片通信,达到数据的存储目的。
本章分为如下几个部分:
13.1 AT24C32 简介
13.2 硬件设计
13.3 软件设计
13.4 下载验证
13.1 AT24C32 简介存储器容量:32Kbit;时钟频率:1MHz;电源电压范围:1.8V 到 3.6V;针脚数:8;存储类型:EEPROM;工作温度最低:-40°C;工作温度最高:+85°C;接口类型:串行接口:I2C;Vcc:5.5V;电源电压最大:5.5V;电源电压最小:1.8V,支持I2C通信。 I2C总线接口采用两线设计串行数据线SDA和串行时钟线SCL,可提供双向的数据传输,并连接到串行存储器RAM和ROM、网络设备、LCD、音频发生器等外部I2C设备上。本芯片支持标准模式(100Kbps)和高速模式(400Kbps)。 I2C的4个工作模式 主机发送从机接收、主机接收从机发送、从机发送主机接收、从机接收主机发送。 数据的有效性 有效的数据在时钟线的高电平期间,SDA 线上的数据必须保持稳定, SDA 线上的数据仅可在时钟 SCL 为低电平时改变。每一位数据传输触发一个时钟脉冲,如图13.1.1所示:
图13.1.1 有效数据范围 应答 所有总线传输都带有所需的应答时钟周期, 该时钟周期由主机产生。 在响应周期内,发送器不能执行下一操作。 START 和 STOP 条件 I2C总线协议定义了开始条件(START Condition) 和结束条件(STOP Condition) 两种状态。当 SCL 为高电平时, SDA 线上由高到低的跳变被定义为开始(START) 条件,由低到高的跳变被定义为结束条件(STOP Condition) ,如图13.1.2所示
图13.1.2 I2C起始和停止时序 如果说要通过I2C进行通信那么就要先发送起始信号,发送起始信号可以用I2C_Start函数来完成。 [mw_shl_code=applescript,true]ErrorStatus I2C_Start(I2C_TypeDef* I2Cx, uint16_t slave_address, I2C_CTR ctr);[/mw_shl_code] I2C_Start函数的第一个传参是选择I2C,第二个传参是从器件地址,第三个传参是I2C的读写状态位。如果要对AT24C32进行写操作可以这样写: [mw_shl_code=applescript,true]I2C_Start(I2C0, 0xa0, I2C_WRITE_SA7);[/mw_shl_code] 重启状态 开始条件(START Condition) 和 结束条件(STOP Condition) 总是由主机产生的。I2C总线在结束条件(STOP Condition) 之后一段时间重新被释放。 如果使用重新开始条件(Repeated START Condition) 条件替代结束条件(STOP Condition) 的话,I2C将会保持占用(忙)状态,如图13.1.3所示:
图13.1.3 I2C重启时序 从机地址 放到 SDA 线上的每个数据字节应该都是 8 位。每个字节之后跟随一个应答位,如图13.1.4所示: 图13.1.4 发送从机地址时序 读写位 7 位地址位之后的第 8 位是数据方向位(R/W) ,‘0’代表写操作, ‘1’ 代表读操作,也可以通过 I2Cx_CTR 寄存器的CTRRWN位来选择读写方向,‘0’代表从写模式,‘1’代表 读模式。 应答(ACK) 和无应答(NACK) 每个字节之后都有一个应答位。 应答位可以使接收者在成功接收到本字节之后,告诉发送者此数据已经被成功接收,可以发送下个字节。主机产生所有的时钟脉冲,包括应答位(第 9 位)的时钟脉冲。 需要进行数据发送时可以调用I2C_SendDataAck函数来完成: [mw_shl_code=applescript,true]int8_t I2C_SendDataAck(I2C_TypeDef* I2Cx,uint16_t Data);[/mw_shl_code]
I2C_SendDataAck函数在传输完数据后还会等待从机发送应答。 需要进行数据接收时可以调用I2C_ReceiveData函数来完成: [mw_shl_code=applescript,true]int I2C_ReceiveData(I2C_TypeDef* I2Cx, int last);[/mw_shl_code] I2C_ReceiveData函数会返回从机发送过来的数据,第二个参数可以选择接收完数据后还发不发送应答给从机。 如果I2C跟AT24C32通信要按在AT24C32规定的时序图来操作。 写一个字节的时序是这个如图13.1.5所示:
图13.1.5 往AT24C32里写一个字节数据 连续写的时序是这个如图13.1.6所示:
图13.1.6 往AT24C32里连续写多个字节的数据 读一个字节的时序是这个如图13.1.7所示:
图13.1.7 从AT24C32里读一个字节数据出来 连续读的时序是这个如图13.1.8所示:
图13.1.8 从AT24C32里读连续多个字节数据出来 13.2 硬件设计本章试验简介:向AT24C32写入8个数据,写入成功后再将数据读出来,最后再将第二个数据加1再次写入AT24C32中,不断更新数据来确保数据的准确性。 本实验用到的硬件资源有: 1) 串口 2) 硬件I2C 3) AT24C32 W7500EVB与AT24C32连线说明 SCL ---> PA_09 SDA ---> PA_10 13.3 软件设计打开 AT24C32实验工程,可以看到工程里有一个文件at24c32.c文件。 [mw_shl_code=applescript,true]#define MAX_SIZE 8
void AT24C32_I2C_Init(void)
{
I2C_ConfigStruct I2C_InitStructure;
I2C_InitStructure.mode = I2C_Master; //选择主机模式
I2C_InitStructure.slave_address = Slave_address; //器件地址
I2C_InitStructure.master.prescale = 0x61; //分频
I2C_InitStructure.master.timeout = 0xFFFF; //等待超时时间
/* Cofigure I2C0 */
I2C_Init(I2C0, I2C_InitStructure); //I2C初始化
}
void I2C_Test(void)
{
static uint8_t TX_Data[MAX_SIZE]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08};
uint8_t i,RX_Data[MAX_SIZE]="\0";
I2C_Burst_Write(I2C0,Slave_address,Memory_address,TX_Data,MAX_SIZE); //连续写函数
I2C_Burst_Read(I2C0,Slave_address,Memory_address,RX_Data,MAX_SIZE); //连续读函数
printf("######## I2C EEPROM TEST #########\rundefined");
for(i=0;i<MAX_SIZE;i+=2)
printf("[%02d]:0x%02x, [%02d]:0x%02x\rundefined",i,RX_Data,i+1,RX_Data[i+1]);
TX_Data[1]+=1;
}
SHAPE \* MERGEFORMAT
int I2C_Burst_Read(I2C_TypeDef* I2Cx, uint16_t address, uint16_t memory_address, uint8_t *rx_data, int length)
{
int recv_cnt;
I2C_Reset(I2Cx); /* I2C复位 */
if(I2C_Start(I2Cx,address,I2C_WRITE_SA7) != ERROR) //起始信号加器件地址写
{
if(I2C_SendDataAck(I2Cx,(memory_address >> 8)) == ERROR) //EEPROM的内存地址高位
{
I2C_Stop(I2Cx);
return -1;
}
if(I2C_SendDataAck(I2Cx,(memory_address & 0xff)) == ERROR) //EEPROM的内存地址低位
{
I2C_Stop(I2Cx);
return -1;
}
I2C_Delay(0x01); //用延时来增加稳定性
if(I2C_Restart_Structure(I2Cx,address,I2C_READ_SA7) == ERROR) //重发起始信号加器件地址读
{
I2C_Stop(I2Cx);
return -1;
}
for(recv_cnt=0;recv_cnt<length;recv_cnt++)
{
rx_data[recv_cnt] = I2C_ReceiveData(I2Cx,0); //连续读取数据
}
I2C_Delay(0x01); //用延时来增加稳定性
I2C_Stop(I2Cx); //发送停止信号
}
I2C_Delay(0x000F0000); //读写之间必须要有延时
return length;
}
int I2C_Burst_Write(I2C_TypeDef* I2Cx, uint16_t address, uint16_t memory_address, uint8_t *tx_data, int length)
{
int cnt;
I2C_Reset(I2Cx); /* I2C复位 */
//只要有一种起始方式能开启它都可以
if(I2C_Start(I2Cx,address,I2C_WRITE_SA7) != ERROR || I2C_Restart_Structure(I2Cx,address,I2C_WRITE_SA7) != ERROR)
SHAPE \* MERGEFORMAT
{
if(I2C_SendDataAck(I2Cx,(memory_address >> 8)) == ERROR) //EEPROM的内存地址高位
{
I2C_Stop(I2Cx);
return -1;
}
if(I2C_SendDataAck(I2Cx,(memory_address & 0xff)) == ERROR) //EEPROM的内存地址低位
{
I2C_Stop(I2Cx);
return -1;
}
for(cnt=0;cnt<length;cnt++)
{
if(I2C_SendDataAck(I2Cx,tx_data[cnt]) == ERROR) //连读发送数据
{
I2C_Stop(I2Cx);
return -1;
}
I2C_Delay(0x01); //用延时来增加稳定性
}
I2C_Stop(I2Cx); //发送停止信号
}
I2C_Delay(0x000F0000); //读写之间必须要有延时
return length;
}[/mw_shl_code] 在at24c32.c文件中写的代码都是按照AT24C32的时序来写的。 13.4 下载验证在代码编译成功之后,下载代码到 W7500EVB 开发板上,可以看到串口打印出来的数据。如图13.4.1所示: 图13.4.1 at24c32的试验结果
|