700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > STM32F10x_模拟I2C读写EEPROM(2)(切换SDA方向 + 读ACK位 + 完整代码)

STM32F10x_模拟I2C读写EEPROM(2)(切换SDA方向 + 读ACK位 + 完整代码)

时间:2020-08-24 19:27:49

相关推荐

STM32F10x_模拟I2C读写EEPROM(2)(切换SDA方向 + 读ACK位 + 完整代码)

文章目录

前言一、宏定义二、I2C延时函数1. 注意三、起始 / 停止信号1. 时序图2. 起始信号3. 停止信号四、切换SDA方向1. SDA配置为输入模式2. SDA配置为输出模式五、应答位信息1. 主机(MCU)读取 应答位2. 主机(MCU)产生 应答位六、I2C读 / 写 一个字节1. 注意2. I2C写一个字节3. I2C读一个字节七、E2 页写 / 连续读1. E2 页写2. E2 连续读八、小结

前言

关于此文一些名词术语不太理解的,可以去看我这篇博文

→ 《STM32F10x_模拟I2C读写EEPROM(1)》

读写E2函数(带备份区+校验和判断),可以去看我这篇博文

→ 《STM32F10x_模拟I2C读写EEPROM(3)(读写E2备份区 + 校验位 + 完整代码 + 应用实例)》

E2的中文资料可以到我博客资源里下载,没有积分下载的,可以评论Ding我o( ̄▽ ̄)ブ

一、宏定义

// I2C引脚#define PORT_I2C_SCL GPIOx#define PORT_I2C_SDA GPIOx#define PIN_I2C_SCLGPIO_Pin_x#define PIN_I2C_SDAGPIO_Pin_x// 控制 SDA / SCL 高低电平#define I2C_SCL_LOW(PORT_I2C_SCL->BRR = PIN_I2C_SCL)#define I2C_SCL_HIGH (PORT_I2C_SCL->BSRR = PIN_I2C_SCL)#define I2C_SDA_LOW(PORT_I2C_SDA->BRR = PIN_I2C_SDA)#define I2C_SDA_HIGH (PORT_I2C_SDA->BSRR = PIN_I2C_SDA)// 读 SDA 电平状态#define I2C_SDA_READ (PORT_I2C_SDA->IDR & PIN_I2C_SDA)// 应答位信息#define I2C_ACK 0 //应答#define I2C_NOACK 1 //非应答/*1、"地址长度"根据芯片型号不同略有不同8位: AT24C01、AT24C0216位: AT24C04、AT24C08、AT24C16、AT24C32、AT24C64、AT24C128、AT24C256、AT24C5122、"页长度"根据芯片型号不同略有不同8字节: AT24C01、AT24C0216字节: AT24C04、AT24C08、AT24C1632字节: AT24C32、AT24C6464字节: AT24C128、AT24C256128字节: AT24C512*/// 此文的E2型号 - AT24C512#define EEPROM_WORD_ADDR_SIZE 16//地址长度#define EEPROM_PAGE_SIZE 128//页长度#define EEPROM_DEV_ADDR0xA0//地址(设备地址:与A2、A1、A0有关)#define EEPROM_WR0x00//写#define EEPROM_RD0x01//读

二、I2C延时函数

1. 注意

此函数实现的是非标准延时,请根据MCU速度 调节大小

/************************************************函数名称 : I2C_Delay功 能 : I2C延时(非标准延时,请根据MCU速度 调节大小)参 数 : 无返 回 值 : 无*************************************************/static void I2C_Delay(void){uint16_t cnt = 100;while(cnt--);}

三、起始 / 停止信号

1. 时序图

2. 起始信号

时钟线SCL保持高电平期间 数据线SDA电平从高到低的跳变作为I2C总线的起始信号

/************************************************函数名称 : I2C_Start功 能 : I2C开始参 数 : 无返 回 值 : 无*************************************************/void I2C_Start(void){I2C_SCL_HIGH;//SCL高I2C_Delay();I2C_SDA_HIGH;//SDA高I2C_Delay();I2C_SDA_LOW;//SDA低I2C_Delay();I2C_SCL_LOW;//SCL低I2C_Delay();}

3. 停止信号

时钟线SCL保持高电平期间 数据线SDA电平从低到高的跳变作为I2C总线的停止信号

/************************************************函数名称 : I2C_Stop功 能 : I2C停止参 数 : 无返 回 值 : 无*************************************************/void I2C_Stop(void){I2C_SDA_LOW;//SDA低I2C_Delay();I2C_SCL_HIGH;//SCL高I2C_Delay();I2C_SDA_HIGH;//SDA高I2C_Delay();}

四、切换SDA方向

1. SDA配置为输入模式

/************************************************函数名称 : I2C_SDA_SetInput功 能 : I2C_SDA设置为输入参 数 : 无返 回 值 : 无*************************************************/void I2C_SDA_SetInput(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;// I2C_SDA设置为 浮空输入GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);}

2. SDA配置为输出模式

/************************************************函数名称 : I2C_SDA_SetOutput功 能 : I2C_SDA设置为输出参 数 : 无返 回 值 : 无*************************************************/void I2C_SDA_SetOutput(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;// I2C_SDA设置为 开漏输出GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);}

五、应答位信息

1. 主机(MCU)读取 应答位

主机(MCU)数据的时候,从机(E2)产生应答,主机读取应答位进行检测;

/************************************************函数名称 : I2C_GetAck功 能 : I2C主机读取应答(或非应答)位参 数 : 无返 回 值 : I2C_ACK ----- 应答I2C_NOACK --- 非应答*************************************************/uint8_t I2C_GetAck(void){uint8_t ack;I2C_SCL_LOW;//SCL低(此时从机可变化SDA电平产生应答位信息)I2C_Delay();I2C_SDA_SetInput();//SDA配置为输入模式(切换SDA方向)I2C_SCL_HIGH;//SCL高(SCL上升沿时,从机发应答位信息到SDA线上)I2C_Delay();if(I2C_SDA_READ)//读取从机应答位信息ack = I2C_NOACK;//非应答elseack = I2C_ACK;//应答I2C_SCL_LOW;//SCL低(防止误操作起始 / 结束信号)I2C_Delay();I2C_SDA_SetOutput();//SDA配置为输出模式(切换SDA方向)return ack;//返回应答位}

2. 主机(MCU)产生 应答位

主机(MCU)数据的时候,主机产生应答,从机(E2)读取应答位进行检测;

/************************************************函数名称 : I2C_PutAck功 能 : I2C主机产生应答(或非应答)位参 数 : I2C_ACK ----- 应答I2C_NOACK --- 非应答返 回 值 : 无*************************************************/void I2C_PutAck(uint8_t Ack){I2C_SCL_LOW;//SCL低(此时主机可变化SDA电平产生应答位信息)I2C_Delay();if(I2C_ACK == Ack)I2C_SDA_LOW;//主机产生 → 应答elseI2C_SDA_HIGH;//主机产生 → 非应答I2C_Delay();I2C_SCL_HIGH;//SCL高 (SCL上升沿时,主机发应答位信息到SDA线上)I2C_Delay();I2C_SCL_LOW;//SCL低(防止误操作起始 / 结束信号)I2C_Delay();}

六、I2C读 / 写 一个字节

1. 注意

I2C读 / 写一字节不是 E2 读 / 写一字节(需要区分开来)。

2. I2C写一个字节

主机每完一个字节后,读取从机返回的应答位:

应答位若为0,表示从机应答,能继续下一步操作;应答位若为1,表示从机非应答,不能进行下一步操作。

/************************************************函数名称 : I2C_WriteByte功 能 : I2C写一字节参 数 : Data -------- 数据返 回 值 : I2C_ACK ----- 应答I2C_NOACK --- 非应答*************************************************/uint8_t I2C_WriteByte(uint8_t Data){uint8_t cnt;// 发送一个字节(8位)for(cnt = 0; cnt < 8; cnt++){I2C_SCL_LOW;//SCL低(SCL为低电平时,主机变化SDA有效,产生SDA数据)I2C_Delay();if(Data & 0x80)I2C_SDA_HIGH;//SDA高elseI2C_SDA_LOW;//SDA低Data <<= 1;I2C_Delay();I2C_SCL_HIGH;//SCL高(SCL上升沿,主机发送数据出去)I2C_Delay();}I2C_SCL_LOW;//SCL低(防止误操作起始 / 结束信号)I2C_Delay();return I2C_GetAck();//主机读取应答位}

3. I2C读一个字节

主机每到一个字节(8位)后,产生应答位给从机检测: 应答位若为0,表示主机应答,主机不继续读取数据了;应答位若为1,表示主机非应答,主机可继续读取数据。

/************************************************函数名称 : I2C_ReadByte功 能 : I2C读一字节参 数 : ack --------- 产生应答(或者非应答)位返 回 值 : data -------- 读取的一字节数据*************************************************/uint8_t I2C_ReadByte(uint8_t ack){uint8_t cnt;uint8_t data;I2C_SCL_LOW;//SCL低(此时从机可变化SDA电平产生数据)I2C_Delay();I2C_SDA_SetInput();//SDA配置为输入模式(切换SDA方向)for(cnt = 0; cnt < 8; cnt++){I2C_SCL_HIGH;//SCL高(SCL上升沿时,从机发数据到SDA线上)I2C_Delay();data <<= 1;if(I2C_SDA_READ)//SDA为高(数据有效)data |= 0x01; I2C_SCL_LOW;//SCL低(防止误操作起始 / 结束信号)I2C_Delay();}I2C_SDA_SetOutput();//SDA配置为输出模式(切换SDA方向)I2C_PutAck(ack);//产生应答(或者非应答)位return data;//返回数据}

七、E2 页写 / 连续读

1. E2 页写

一次页写操作写入的数据字节数最大值为E2的页大小

不同型号的E2对应的页大小可能不同(下面简称页大小的值为P,举例:AT24C512 → P = 128);

E2 页写时序说明:

举例说明:MCU写nn <= P)个字节数据进E2;① 主器件发送起始信号和从器件地址信息( R/W 位置零 )给从器件,主器件检测返回应答位信息;② 主器件发送起始字节地址,主器件检测返回应答位信息;③ 非发送最后一个字节时,主器件每发送完一个字节后,不产生停止信号,主器件检测应答位信息。循环此步骤③ n - 1次,按顺序发送数据;④ 主器件发送最后一个字节后,主器件检测应答位信息,然后主器件产生停止信号;⑤ 从器件接收到停止信号后,从器件开始内部数据的擦写,在擦写过程中,从器件不再应答主器件的任何请求。

注意:如果进行页写操作时,n > p,地址计数器将自动翻转,先前写入的数据会被覆盖,所以要注意每次页写的字节数不能大于E2的页大小P,否则会影响数据的正确性。

/************************************************函数名称 : EEPROM_WritePage功 能 : EEPROM写页参 数 : Addr ------ 地址pData ----- 数据Length -----长度(<=EEPROM_PAGE_SIZE)返 回 值 : I2C_ACK ----应答I2C_NOACK - 非应答*************************************************/uint8_t EEPROM_WritePage(uint16_t Addr, uint8_t *Data, uint16_t Length){uint8_t ack;uint8_t i;/* 1.开始信号 */I2C_Start();/* 2.设备地址/写 */ack = I2C_WriteByte(EEPROM_DEV_ADDR | EEPROM_WR);if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}/* 3.数据地址 */#if(8 == EEPROM_WORD_ADDR_SIZE)ack = I2C_WriteByte((uint8_t)(Addr&0x00FF)); //数据地址(8位)if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}#elseack = I2C_WriteByte((uint8_t)(Addr>>8)); //数据地址(16位)if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}ack = I2C_WriteByte((uint8_t)(Addr&0x00FF));if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}#endif/* 4.写一字节数据(循环) */for(i = 0; i < Length; i++){ack = I2C_WriteByte(*(Data + i));if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}}/* 5.停止信号 */I2C_Stop();return I2C_ACK;}

2. E2 连续读

从E2读取数据是一个复合的I2C时序,它实际上包含一个写过程和一个读过程。E2 连续读时序说明: 举例说明:MCU连续读E2里的nn <= P)个字节数据;① 主器件发送起始信号和从器件地址信息( R/W 位置1 )给从器件,主器件检测返回应答位信息;② 主器件发送数据起始字节地址,主器件检测返回应答位信息;③ 主器件再次发送起始信号和从器件地址信息( R/W 位置0 )给从器件,主器件检测返回应答位信息;③ E2会向主机返回从"数据起始字节地址"开始的数据,一个字节一个字节地传输,主器件每接收完一个字节后,不产生停止信号,主器件响应“应答信号ACK”给E2。只要主器件的响应为"应答信号ACK",E2就会一直传输下去,循环此步骤③ n - 1次;④ 主器件接收最后一个字节后,主器件响应“非应答信号NOACK”给E2,然后主器件产生停止信号;⑤ 从器件接收到停止信号后,从器件停止传输数据,E2 连续读结束。

/************************************************函数名称 : EEPROM_ReadSequential功 能 : EEPROM读一字节参 数 : Addr ------ 地址Data ------ 数据Length -----长度返 回 值 : I2C_ACK --- 应答I2C_NOACK - 非应答*************************************************/uint8_t EEPROM_ReadSequential(uint16_t Addr, uint8_t *Data, uint16_t Length){uint8_t ack;uint8_t i;/* 1.开始信号 */I2C_Start();/* 2.设备地址/写 */ack = I2C_WriteByte(EEPROM_DEV_ADDR | EEPROM_WR);if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}/* 3.数据地址 */#if (8 == EEPROM_WORD_ADDR_SIZE)ack = I2C_WriteByte((uint8_t)(Addr&0x00FF)); //数据地址(8位)if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}#elseack = I2C_WriteByte((uint8_t)(Addr>>8)); //数据地址(16位)if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}ack = I2C_WriteByte((uint8_t)(Addr&0x00FF));if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}#endif/* 4.重新开始 */I2C_Start();/* 5.设备地址/读 */ack = I2C_WriteByte(EEPROM_DEV_ADDR | EEPROM_RD);if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}/* 6.读一字节数据 */for(i = 0; i < Length - 1; i++){*(Data + i) = I2C_ReadByte(I2C_ACK); //只读取1字节(产生应答)}*(Data + i) = I2C_ReadByte(I2C_NOACK);//只读取1字节(产生非应答)/* 7.停止信号 */I2C_Stop();return I2C_ACK;}

八、小结

写的着急,欢迎纠正☆⌒(*^-゜)v THX!!码字不易,记得点小心心 ( •̀ ω •́ )✧

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。