完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
STM32-IIC 配置解说(原创)STM32 - I2C 简介 :I2C 总线接口连接微控制器和串行 I2C 总线。它提供多主机功能,控制所有 I2C总线特定的时序、协议、仲裁和定时。支持标准和快速两种模式,另外 STM32的 I2C 可以使用 DMA 方式操作。本文主要以一个实例来介绍 STM32-I2C 的配置方式和具体在工程中通过调用哪些库函数来实现 I2C 器件的通信。实例:写入数据到器件 AT24C02 并将存入的数据读出好,我们先来讲讲 STM32 I2C 模块的端口基本配置,由 STM32 中文参考手册可以查到在使用 I2C 时对应的引脚要配置成哪种模式。 SCL 和 SDA 引脚都配置成开漏复用输出
本人用的是 STM32F103VET6,它有 2 个 I2C 接口。 I/O 口定义为 PB6-I2C_SCL, PCB7-I2C1_SDA; PB10-I2C_SCL, PB11-I2C_SDA,由手册可以查出对应的端口。 图文如下: 调用库函数将 I2C 端口配置好(本文使用的是 PB6、 PB7 端口): 程序代码如下: /*I2C-IO 口配置*/ void I2C_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; //GPIO 结构体定义 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//使能 I2C 的 IO 口 /* PB6-I2C1_SCL、 PB7-I2C1_SDA*/ GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 开漏输出 GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化结构体配置 } /* I2C 工作模式配置 */ void I2C_Mode_config(void) { /* 使能与 I2C1 有关的时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); /*定义 I2C 结构体*/ I2C_InitTypeDef I2C_InitStructure; /*配置为 I2C 模式*/ I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; /*该参数只有在 I2C 工作在快速模式(时钟工作频率高于 100KHz)下才有意义。 */ I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; /*设置第一个设备自身地址*/ I2C_InitStructure.I2C_OwnAddress1 =0x0A; /*使能应答*/ I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ; /*AT24C02 地址为 7 位所以设置 7 位就行了*/ I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; /*时钟速率,以 HZ 为单位的,最高为 400khz*/ I2C_InitStructure.I2C_ClockSpeed = 400000; /* 使能 I2C1 */ I2C_Cmd(I2C1, ENABLE); /* I2C1 初始化 */ I2C_Init(I2C1, &I2C_InitStructure); } 好了, STM32 内部的 I2C 模块工作模式就这样被设好了,接下来需要完成与外部器件 AT24C02( EEPROM)进行通信。将分两部分进行代码解析,第一部分是:对 AT24C02 进 行写操作,第二部分:对 AT24C02 进行读操作。 第一部分(写): 备注: I2C_PageSize 为宏定义 #define I2C_PageSize 8 ; /* * 函数名: I2C_EE_BufferWrite * 描述 :将缓冲区中的数据写到 I2C EEPROM 中 * 输入 : -pBuffer 缓冲区指针 * -WriteAddr 接收数据的 EEPROM 的地址 * -NumByteToWrite 要写入 EEPROM 的字节数 * 输出 :无 * 返回 :无 * 调用 :外部调用 */ void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite) { u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0; Addr = WriteAddr % I2C_PageSize;//查看输入的地址是不是 8 的整数倍 count = I2C_PageSize - Addr;//表示距离下一页页首地址的距离(步伐数) NumOfPage = NumByteToWrite / I2C_PageSize;//算出一共有多少页 NumOfSingle = NumByteToWrite % I2C_PageSize;//算出不够一页的数据的余数 if(Addr == 0) //如果输入的地址是首页地址 { if(NumOfPage == 0) //如果不足一页数据 { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不 够一页的余数作为实参 I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作 } /* If NumByteToWrite > I2C_PageSize */ else //如果数据有一页以上 { while(NumOfPage--)//用一个 while 循环,执行页写循环操作,有多少页就写多少次 { I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); //调用写函数,将 I2C_PageSize 变量作为实 参执行页写 I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作 WriteAddr += I2C_PageSize;//每执行完一次页写对应的地址也需要移 8 个位 pBuffer += I2C_PageSize;//数据指针移 8 个位 } if(NumOfSingle!=0)//如果有不足一页的数据余数则执行 { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不够一页的余数作为实参 I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作 } } } else //输入的地址不是首页地址 { if(NumOfPage== 0) //如果不足一页 { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不 够一页的余数作为实参 I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作 } else//如果有一页或一页以上 { NumByteToWrite -= count;//将地址后续的缺省位置补上数据,数据的多少就是 count 的值, NumByteToWrite 变量的值就是补上数据之后 还剩下未发送的数量 NumOfPage = NumByteToWrite / I2C_PageSize;//剩余的页数 NumOfSingle = NumByteToWrite % I2C_PageSize;//不足一页的数据数量 if(count != 0)//将地址后续的缺省位置补上数据 { I2C_EE_PageWrite(pBuffer, WriteAddr, count);//调用写函数,以 count 为实参,将地 址缺省下来的部分地址给填充 上数据 I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作 WriteAddr += count;//加上 count 后,地址就移位到下一页的首地址 pBuffer += count;//数据指针移 count 个位 } while(NumOfPage--)//将剩余的页数数据写入 EEPROM { I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);//调用写函数,将 I2C_PageSize 变量作为实 参执行页写 I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作 WriteAddr += I2C_PageSize;//将地址移 8 个位 pBuffer += I2C_PageSize; //将数据指针移 8 个位 } if(NumOfSingle != 0)//将不足一页的数据写入 EEPROM { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//调用写函数, NumOfSingle 不够一页的余数作为实参 I2C_EE_WaitEepromStandbyState();//等待 EEPROM 器件完成内部操作 } } } } /*END OF FUNCtiON*/ 在以上写操作里面我们拿经常被调用的 I2C_EE_PageWrite 函数还有 I2C_EE_WaitEepromStandbyState 函数并结合 STM32 中文参考手册图文进行对照分析 请读者在读 I2C_EE_PageWrite 函数时请结合上述时序图和下述代码联系一起看! 注: EEPROM_ADDRESS 为器件的地址,大家按照自己具体器件地址写入即可, 例: #define EEPROM_ADDRESS 0xA0 /* * 函数名: I2C_EE_PageWrite * 描述 :在 EEPROM 的一个写循环中可以写多个字节,但一次写入的字节数 * 不能超过 EEPROM 页的大小。 AT24C02 每页有 8 个字节。 * 输入 : -pBuffer 缓冲区指针 * -WriteAddr 接收数据的 EEPROM 的地址 * -NumByteToWrite 要写入 EEPROM 的字节数 * 输出 :无 * 返回 :无 * 调用 :外部调用 */ void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite) { I2C_GenerateSTART(I2C1, ENABLE);//产生起始位 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); //清除 EV5 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//发送器件地 址 while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //ADDR=1,清除 EV6 I2C_SendData(I2C1, WriteAddr); //EEPROM 的具体存储地址位置 while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//移位寄 存器非空,数据寄存器已经空,产生 EV8,发送数据到 DR 既可清除该事件 while(NumByteToWrite--) //利用 while 循环 发送数据 { I2C_SendData(I2C1, *pBuffer); //发送数据 pBuffer++; //数据指针移位 while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除 EV8 } I2C_GenerateSTOP(I2C1, ENABLE);//产生停止信号 } /*END OF FUNCTION*/ I2C_EE_WaitEepromStandbyState 这个函数,在每调用完写操作函数后都调用这个函数,这 个函数是用来检测 EEPROM 器件是否已经完成内部写的操作,判断器件完成操作后在进行 下一步的操作!代码如下: /* * 函数名: I2C_EE_WaitEepromStandbyState * 描述 : Wait for EEPROM Standby state * 输入 :无 * 输出 :无 * 返回 :无 * 调用 : */ void I2C_EE_WaitEepromStandbyState(void) { vu16 SR1_Tmp = 0; do { I2C_GenerateSTART(I2C1, ENABLE);//产生起始信号 SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);//读 SR1 寄存器 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//发送器件 地址清除事 件 }while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));//如果接收不到从机的应 答( NACK)则说明 EEPROM 器件还在工作,直到完成操作跳出循环体! I2C_ClearFlag(I2C1, I2C_FLAG_AF);//清除 AF 标志位 I2C_GenerateSTOP(I2C1, ENABLE); //产生停止信号 } 第二部分(读): 由以上 AT24C02 读时序图可以知道:读部分需要产生两次起始信号 另外:主设备在从从设备接收到最后一个字节后发送一个 NACK 。接收到 NACK 后,从设备 释放对 SCL 和 SDA 线的控制;主设备就可以发送一个停止/ 重起始条件。 ● 为了在收到最后一个字节后产生一个 NACK 脉冲,在读倒数第二个数据字节之后(在倒 数第二个 RxNE 事件之后)必须清除 ACK 位。 ● 为了产生一个停止/ 重起始条件,软件必须在读倒数第二个数据字节之后(在倒数第二 个 RxNE 事件之后)设置 STOP/START 位。 ● 只接收一个字节时,刚好在 EV6 之后(EV6_1 时,清除 ADDR 之后)要关闭应答和停止条 件的产生位。 请读者将代码和图结合在一起看! /* * 函数名: I2C_EE_BufferRead * 描述 :从 EEPROM 里面读取一块数据。 * 输入 : -pBuffer 存放从 EEPROM 读取的数据的缓冲区指针。 * -WriteAddr 接收数据的 EEPROM 的地址。 * -NumByteToWrite 要从 EEPROM 读取的字节数。 * 输出 :无 * 返回 :无 * 调用 :外部调用 */ void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)//需要两个起 始信号 { while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); //调用库函数检测 I2C 器件是否处 于 BUSY 状态 I2C_GenerateSTART(I2C1, ENABLE);//开启信号 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//清除 EV5 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//写入器 件地址 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//清 除 EV6 I2C_SendData(I2C1, ReadAddr); //发送读的地址 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//清除 EV8 I2C_GenerateSTART(I2C1, ENABLE);//开启信号 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));//清除 EV5 I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);//将器件地址 传出,主机为读 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//清除 EV6 while(NumByteToRead) { if(NumByteToRead == 1)//只剩下最后一个数据时进入 if 语句 { I2C_AcknowledgeConfig(I2C1, DISABLE);//最后有一个数据时关闭应答位 I2C_GenerateSTOP(I2C1, ENABLE);//最后一个数据时使能停止位 } if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) //读取数据 { *pBuffer = I2C_ReceiveData(I2C1);//调用库函数将数据取出到 pBuffer pBuffer++; //指针移位 NumByteToRead--;//字节数减 1 } } I2C_AcknowledgeConfig(I2C1, ENABLE);//将应答位使能回去,等待下次通信 } STM32-IIC 配置解说到此告一段落! 如果有不正确的地方也请各位多多指教,本人及时纠正;欢 迎大家来和我相互交流学习,谢谢大家。 |
|
相关推荐
|
|
顶一下。。来 看看
|
|
|
|
|
|
DINGYIXIA
|
|
|
|
支持、支持
|
|
|
|
好哈哈哦
|
|
|
|
cWEGJNQWHOPUEGQNHWOG
|
|
|
|
SAGAWOYGIBASPNUEIDNFPWIUREGQWE
|
|
|
|
REGO4UHG90728WRHFNQIUWEGNMPQRWGQRWGQ34G
|
|
|
|
2201 浏览 1 评论
AD7686芯片不传输数据给STM32,但是手按住就会有数据。
2027 浏览 3 评论
4633 浏览 0 评论
如何解决MPU-9250与STM32通讯时,出现HAL_ERROR = 0x01U
2171 浏览 1 评论
hal库中i2c卡死在HAL_I2C_Master_Transmit
2705 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-26 08:31 , Processed in 0.801331 second(s), Total 96, Slave 79 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号