尤溪网站建设,wordpress同步头条,tp5网站文档归档怎么做,最好看的视频免费下载IIC简介
物理层 连接多个devices
它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中#xff0c;可连接多个 I2C 通讯设备#xff0c;支持多个通讯主机及多个通讯从机。
两根线
一个 I2C 总线只使用两条总线线路#xff0c;一条双向串行数…IIC简介
物理层 连接多个devices
它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中可连接多个 I2C 通讯设备支持多个通讯主机及多个通讯从机。
两根线
一个 I2C 总线只使用两条总线线路一条双向串行数据线(SDA) 一条串行时钟线(SCL)。数据线即用来表示数据时钟线用于数据收发同步。
设备地址
每个连接到总线的设备都有一个独立的地址主机可以利用这个地址进行不同设备之间的访问。
上拉电阻和高阻态
总线通过上拉电阻接到电源。当 I2C 设备空闲时会输出高阻态而当所有设备都空闲都输出高阻态时由上拉电阻把总线拉成高电平。
仲裁
多个主机同时使用总线时为了防止数据冲突会利用仲裁方式决定由哪个设备占用总线。
三种速度模式
具有三种传输模式标准模式传输速率为 100kbit/s 快速模式为 400kbit/s 高速模式下可达 3.4Mbit/s但目前大多 I2C 设备尚不支持高速模式。
最大devices限制
连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制 。
协议层
I2C 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。
I2C 基本读写过程 主机写数据到从机 主机由从机中读数据 I2C 通讯复合格式 数据由主机传输至从机 数据由从机传输至主机 传输开始信号 传输方向选择位1 为读0 为写 应答(ACK)或非应答(NACK)信号 停止传输信号
1.S是起始信号由主机产生挂载在总线上的设备都会收到准备接收主机下一个数据 2.设备聆听地址信息选择从机 起始信号之后主机会广播从机地址在 I2C 总线上每个设备的地址都是唯一的当主机广播的地址与某个设备地址相同时这个设备就被选中了没被选中的设备将会忽略之后的数据信号。根据 I2C 协议这个从机地址可以是 7 位或 10 位。 3.传输方向主机广播传输方向0是主机到从机主机往从机写1为从机到主机从机往主机写 4.从机应答回复ack与nack只有收到ack之后主机继续发送或者接收数据
写数据 主机首先在 IIC 总线上发送起始信号那么这时总线上的从机都会等待接收由主机发出的数据。主机接着发送从机地0(写操作)组成的 8bit 数据所有从机接收到该 8bit 数据后自行检验是否是自己的设备的地址假如是自己的设备地址那么从机就会发出应答信号。主机在总线上接收到有应答信号后才能继续向从机发送数据。注意IIC 总线上传送的数据信号是广义的既包括地址信号又包括真正的数据信号。
读数据 若配置的方向传输位为“读数据”方向即第二幅图的情况广播完地址接收到应答信号后从机开始向主机返回数据(DATA)数据包大小也为 8 位从机每发送完一个数据都会等待主机的应答信号(ACK)重复这个过程可以返回 N 个数据这个 N 也没有大小限制。当主机希望停止接收数据时就向从机返回一个非应答信号(NACK)则从机自动停止数据传输。
信号电平
起始 停止信号 当 SCL 线是高电平时 SDA 线从高电平向低电平切换这个情况表示通讯的起始。当 SCL 是高电平时 SDA 线由低电平向高电平切换表示通讯的停止。起始和停止信号一般由主机产生。
数据有效性 SCL的高电平 SCL为高电平的时候 SDA表示的数据有效SCL为低电平时数据变换
地址及数据方向
I2C 总线上的每个设备都有自己的独立地址主机发起通讯时通过 SDA 信号线发送设备地址(SLAVE_ADDRESS)来查找从机。I2C 协议规定设备地址可以是 7 位或 10 位实际中 7 位的地址应用比较广泛。紧跟设备地址的一个数据位用来表示数据传输方向。 读数据方向时主机会释放对 SDA 信号线的控制由从机控制 SDA 信号线主机接收信号写数据方向时SDA 由主机控制从机接收信号。 大端高有效位存在低地址波形高位先行
ACK NACK
主站发送起始信号地址读写信号之后释放SDA控制权从站开始自动控制SDA信号发送ACK NACK
整体控制逻辑
整体控制逻辑负责协调整个 I2C 外设控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变。在外设工作时控制逻辑会根据外设的工作状态修改“状态寄存器(SR1 和 SR2)”我们只要读取这些寄存器相关的寄存器位就可以了解 I2C的工作状态。除此之外控制逻辑还根据要求负责控制产生 I2C 中断信号、DMA 请求及各种 I2C 的通讯信号(起始、停止、响应信号等)。
IIC使用 读写EEPROM为例
要点
(1) 配置通讯使用的目标引脚为开漏模式 (2) 使能 I2C 外设的时钟 (3) 配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设 (4) 编写基本 I2C 按字节收发的函数 (5) 编写读写 EEPROM 存储内容的函数
硬件IIC
配置IIC复用的GPIO
#配置 GPIO 复用
GPIO_InitTypeDef GPIO_InitStructure;/* 使能与 I2C 有关的时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB)/* 配置GPIO为开漏输出模式 */
//配置Gpio初始化结构体略
GPIO_Init(GPIOB, GPIO_InitStructure);1.配置IIC模式
I2C_InitTypeDef I2C_InitStructure;/* I2C 配置 */
I2C_InitStructure.I2C_Mode I2C_Mode_I2C;/*! 指定工作模式可选 I2C 模式及 SMBUS 模式 *//* 高电平数据稳定低电平数据变化 SCL 时钟线的占空比 */
I2C_InitStructure.I2C_DutyCycle I2C_DutyCycle_2; /*指定时钟占空比可选 low/high 2:1 及 16:9 模式*//*! 指定自身的 I2C 设备地址 */
I2C_InitStructure.I2C_OwnAddress1 I2Cx_OWN_ADDRESS7;/*! 指定自身的 I2C 设备地址 */I2C_InitStructure.I2C_Ack I2C_Ack_Enable ; /*! 使能或关闭响应(一般都要使能) *//* I2C 的寻址模式 */
I2C_InitStructure.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit; /*! 指定地址的长度可为 7 位及 10 位 */I2C_InitStructure.I2C_ClockSpeed I2C_Speed; /*! 设置 SCL 时钟频率此值要低于 400000*/I2C_Init(I2C1, I2C_InitStructure);
I2C_Cmd(I2C1, ENABLE);1.1作为主机端进行数据发送
即作为 I2C 通讯的主机端时对外部发送数据的过程
以写一个E2ROM为例 (1) 控制产生起始信号(S)当发生起始信号后它产生事件“EV5”并会对 SR1 寄存器的“SB”位置 1表示起始信号已经发送
(2) 紧接着发送设备地址并等待应答信号若有从机应答则产生事件“EV6”及“EV8”这时 SR1 寄存器的“ADDR”位及“TXE”位被置 1ADDR 为 1 表示地址已经发送TXE 为 1 表示数据寄存器为空
(3) 以上步骤正常执行并对 ADDR 位清零后我们往 I2C 的“数据寄存器 DR”写入要发送的数据这时TXE位会被重置0表示数据寄存器非空I2C外设通过SDA信号线一位位把数据发送出去后又会产生“EV8”事件即 TXE 位被置 1重复这个过程就可以发送多个字节数据了
(4) 当我们发送数据完成后控制 I2C 设备产生一个停止信号§这个时候会产生EV8_2 事件SR1 的 TXE 位及 BTF 位都被置 1表示通讯结束。
假如我们使能了 I2C 中断以上所有事件产生时都会产生 I2C 中断信号进入同一个中断服务函数到 I2C 中断服务程序后再通过检查寄存器位来判断是哪一个事件。
IIC STM32固件库函数链接: IIC STM32固件库函数链接
1.1.1发送单字节数据 AT24C02的单字节时序规定向它写入数据的时候第一个字节为内存地址第二个字节是要写入的数据内容。在发送device地址之后需要发送eeprom的内部存储地址和需要存的数据
#define EEPROM_I2Cx I2C1/* 具体修改的寄存器位置查看固件库函数*/uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr){/* 产生 I2C 起始信号 *///I2Cx-CR1 | I2C_CR1_START; 操作I2C中的CR1寄存器I2C_GenerateSTART(I2C1, ENABLE);/*设置超时等待时间*/I2CTimeout I2CT_FLAG_TIMEOUT;//I2CTimeout 常数/* 检测 EV5 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){/* 超时报错*/if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(0);}/* 发送 EEPROM 设备地址 */I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS,I2C_Direction_Transmitter);/* 检测 EV6 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(1);}/* 发送要写入的 EEPROM 内部地址(即 EEPROM 内部存储器的地址) *//* EEPROM 内部存储器读写的规则是第一个数据为要读写的地址第二个数据是具体读写的数据 */I2C_SendData(EEPROM_I2Cx, WriteAddr);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV8 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(2);}/* 发送一字节要写入的数据 */I2C_SendData(EEPROM_I2Cx, *pBuffer);I2CTimeout I2CT_FLAG_TIMEOUT;I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV8 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(2);}/* 发送停止信号 */I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);}1.1.2发送多字节数据
等待存储设备准备好
这个函数主要实现是向 EEPROM 发送它设备地址检测 EEPROM 的响应若EEPROM 接收到地址后返回应答信号则表示 EEPROM 已经准备好可以开始下一次通讯。函数中检测响应是通过读取 STM32 的 SR1 寄存器的 ADDR 位及 AF 位来实现的当I2C 设备响应了地址的时候ADDR 会置 1若应答失败AF 位会置 1。 //等待 EEPROM 到准备状态void I2C_EE_WaitEepromStandbyState(void){vu16 SR1_Tmp 0;do {/* 发送起始信号 */I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);/* 读 I2C1 SR1 寄存器 */SR1_Tmp I2C_ReadRegister(EEPROM_I2Cx, I2C_Register_SR1);/* 发送 EEPROM 地址 写方向 */I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS,I2C_Direction_Transmitter);}// SR1 位 1 ADDR1 表示地址发送成功0 表示地址发送没有结束// 等待地址发送成功while (!(I2C_ReadRegister(EEPROM_I2Cx, I2C_Register_SR1) 0x0002));/* 清除 AF 位 */I2C_ClearFlag(EEPROM_I2Cx, I2C_FLAG_AF);/* 发送停止信号 */I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
}循环单字节发送
uint8_t I2C_EE_ByetsWrite(uint8_t* pBuffer,uint8_t WriteAddr,uint16_t NumByteToWrite)
{uint16_t i;uint8_t res;/*每写一个字节调用一次 I2C_EE_ByteWrite 函数前文的单字节写入*/for (i0; iNumByteToWrite; i){/*等待 EEPROM 准备完毕*/I2C_EE_WaitEepromStandbyState();/*按字节写入数据*/res I2C_EE_ByteWrite(pBuffer,WriteAddr);}return res;
}
1.1.3 EEPROM 的页写入
EEPROM 定义了一种页写入时序只要告诉 EEPROM 第一个内存地址 address1后面的数 据按次序写入到 address2、address3… 这样可以节省通讯的时间加快速度。 根据页写入时序第一个数据被解释为要写入的内存地址 address1后续可连续发送 n个数据这些数据会依次写入到内存中。其中 AT24C02 型号的芯片页写入时序最多可以一次发送 8 个数据(即 n 8 )该值也称为页大小某些型号的芯片每个页写入时序最多可传输 16 个数据。
但是这种方式还是比较慢拘泥于页的大小。
//在 EEPROM 的一个写循环中可以写多个字节但一次写入的字节数不能超过 EEPROM 页的大小AT24C02 //每页有 8 个字节
//NumByteToWrite:要写的字节数要求 NumByToWrite 小于页大小uint8_t I2C_EE_PageWrite(uint8_t* pBuffer, uint8_t WriteAddr,uint8_t NumByteToWrite)
{ I2CTimeout I2CT_LONG_TIMEOUT;while (I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_BUSY)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(4);}/* 产生 I2C 起始信号 */I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV5 事件并清除标志 */while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(5);}/* 发送 EEPROM 设备地址 */I2C_Send7bitAddress(EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV6 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(6);}/* 发送要写入的 EEPROM 内部地址(即 EEPROM 内部存储器的地址) */I2C_SendData(EEPROM_I2Cx, WriteAddr);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV8 事件并清除标志*/while (! I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(7);}/* 循环发送 NumByteToWrite 个数据 */while (NumByteToWrite--){/* 发送缓冲区中的数据 */I2C_SendData(EEPROM_I2Cx, *pBuffer);/* 指向缓冲区中的下一个数据 */pBuffer;I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV8 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(8);}}/* 发送停止信号 */I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
}1.1.4 EEPROM 的页写入plus
利用 EEPROM 的页写入方式可以改进前面的“多字节写入”函数加快传输速度 // AT24C01/02 每页有 8 个字节#define I2C_PageSize 8void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr,u16 NumByteToWrite){u8 NumOfPage0,NumOfSingle0,Addr 0,count0,temp 0;/*mod 运算求余若 writeAddr 是 I2C_PageSize 整数倍运算结果 Addr 值为 0*//*判断目标地址(起始地址)是否为一页的开始*/Addr WriteAddr % I2C_PageSize;/*差 count 个数据值刚好可以对齐到页地址*/count I2C_PageSize - Addr;/*计算出要写多少整数页*/NumOfPage NumByteToWrite / I2C_PageSize;/*mod 运算求余计算出剩余不满一页的字节数*/NumOfSingle NumByteToWrite % I2C_PageSize;// Addr0,则 WriteAddr 刚好按页对齐 aligned也就是从这页的第一个byte开始// 这样就很简单了直接写就可以写完整页后 把剩下的不满一页的写完即可if (Addr 0) {//从某页的第一位写起/* 如果 NumByteToWrite I2C_PageSize *//* 总字数还不满一页 */if (NumOfPage 0) {//总页数不满一页I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);I2C_EE_WaitEepromStandbyState();}/* 如果 NumByteToWrite I2C_PageSize */else {/*先把整数页都写了*/while (NumOfPage--) {I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);//写一页I2C_EE_WaitEepromStandbyState();//等待rom准备好WriteAddr I2C_PageSize; //写完一页之后下一页的地址pBuffer I2C_PageSize; //写完一页之后要传输的源地址也要加}/*若有多余的不满一页的数据把它写完*/if (NumOfSingle!0) {I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);I2C_EE_WaitEepromStandbyState();}}}从某页的第一位写起// 如果 WriteAddr 不是按 I2C_PageSize 对齐// 那就算出对齐到页地址还需要多少个数据先把这几个数据写完剩下开始的地址就已经对齐else{//不是从某页的第一位写起/* 如果 NumByteToWrite I2C_PageSize */if (NumOfPage 0){//总数不满一页if (NumOfSingle count) {//count:还需要写多少个字节才能页面对齐//NumOfSingle:当前页面空闲的字节数// temp 的数据要写到写一页temp NumOfSingle - count;I2C_EE_PageWrite(pBuffer, WriteAddr, count);//写count数目的到当前页I2C_EE_WaitEepromStandbyState();//等待准备完毕WriteAddr count;//目标地址移动pBuffer count;I2C_EE_PageWrite(pBuffer, WriteAddr, temp);//在下一页写完剩下的I2C_EE_WaitEepromStandbyState();}else{ /*若 count 比 NumOfSingle 大*/I2C_EE_PageWrite(pBuffer, WriteAddr, NumByteToWrite);I2C_EE_WaitEepromStandbyState();}}/* 如果 NumByteToWrite I2C_PageSize *//* 如果 不止写一页 */else { /* 如果 不止写一页 *//*地址不对齐多出的 count 分开处理不加入这个运算*/NumByteToWrite - count;NumOfPage NumByteToWrite / I2C_PageSize;NumOfSingle NumByteToWrite % I2C_PageSize;/*先把 WriteAddr 所在页的剩余字节写了*/if (count ! 0) {I2C_EE_PageWrite(pBuffer, WriteAddr, count);I2C_EE_WaitEepromStandbyState();/*WriteAddr 加上 count 后地址就对齐到页了*/WriteAddr count;pBuffer count;}/*把整数页都写了*/while (NumOfPage--) {I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);I2C_EE_WaitEepromStandbyState();WriteAddr I2C_PageSize;pBuffer I2C_PageSize;}/*若有多余的不满一页的数据把它写完*/if (NumOfSingle ! 0) {I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);I2C_EE_WaitEepromStandbyState();}}/*把整数页都写了*/}/* 如果 不止写一页 */ }1.2作为主机端进行数据接收
即作为 I2C 通讯的主机端时从外部接收数据的过程 (1) 同主发送流程起始信号(S)是由主机端产生的控制发生起始信号后它产生事件“EV5”并会对 SR1 寄存器的“SB”位置 1表示起始信号已经发送 (2) 紧接着发送设备地址并等待应答信号若有从机应答则产生事件“EV6”这时SR1 寄存器的“ADDR”位被置 1表示地址已经发送。 (3) 从机端接收到地址后开始向主机端发送数据。当主机接收到这些数据后会产生“EV7”事件SR1 寄存器的 RXNE被置 1表示接收数据寄存器非空我们读取该寄存器后可对数据寄存器清空以便接收下一次数据。此时我们可以控制I2C 发送应答信号(ACK)或非应答信号(NACK)若应答则重复以上步骤接收数据若非应答则停止传输 (4) 发送非应答信号后产生停止信号P结束传输。 uint8_t I2C_EE_BufferRead(uint8_t* pBuffer, uint8_t ReadAddr,u16 NumByteToRead){I2CTimeout I2CT_LONG_TIMEOUT;while (I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_BUSY)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(9);}/* 产生 I2C 起始信号 */I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV5 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(10);}/* 发送 EEPROM 设备地址 */I2C_Send7bitAddress(EEPROM_I2Cx,EEPROM_ADDRESS,I2C_Direction_Transmitter);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV6 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(11);} /*通过重新设置 PE 位清除 EV6 事件 */I2C_Cmd(EEPROM_I2Cx, ENABLE);/* 发送要读取的 EEPROM 内部地址(即 EEPROM 内部存储器的地址) *//* EEPROM 内部存储器读写的规则是第一个数据为要读写的地址第二个数据是具体读写的数据 */I2C_SendData(EEPROM_I2Cx, ReadAddr);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV8 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(12);}/* 产生第二次 I2C 起始信号 */I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);I2CTimeout I2CT_FLAG_TIMEOUT; /* 检测 EV5 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(13);}/* 发送 EEPROM 设备地址 */I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Receiver);I2CTimeout I2CT_FLAG_TIMEOUT;/* 检测 EV6 事件并清除标志*/while (!I2C_CheckEvent(EEPROM_I2Cx,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(14);}/* 读取 NumByteToRead 个数据*/while (NumByteToRead){/*若 NumByteToRead1表示已经接收到最后一个数据了发送非应答信号结束传输*/if (NumByteToRead 1){/* 发送非应答信号 */I2C_AcknowledgeConfig(EEPROM_I2Cx, DISABLE);/* 发送停止信号 */I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);}I2CTimeout I2CT_LONG_TIMEOUT;while (I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)0){if ((I2CTimeout--) 0) return I2C_TIMEOUT_UserCallback(3);}{/*通过 I2C从设备中读取一个字节的数据 */*pBuffer I2C_ReceiveData(EEPROM_I2Cx);/* 存储数据的指针指向下一个地址 */pBuffer;/* 接收数据自减 */NumByteToRead--;} } /* 读取 NumByteToRead 个数据*//* 使能应答方便下一次 I2C 传输 */I2C_AcknowledgeConfig(EEPROM_I2Cx, ENABLE);}1.3作为从机端进行数据接收 1.4作为从机端进行数据接收 GPIO模拟IIC
我之前使用这套方法读取一个外置的RTC模块参考正点原子的例程这边就不赘述了。或者之后有机会把例程贴出来哈
1.配置GPIO
2.编写微秒级软延时
3.根据时序编写各个函数
DMA 与IIC
可以使用DMA对IIC的DR中要传输或者接收的数据进行搬运DMA请求(当被使能时)仅用于数据传输。发送时数据寄存器变空或接收时数据寄存器变满则产生DMA请求。DMA请求必须在当前字节传输结束之前被响应。当为相应DMA通道设置的数据传输量已经完成时DMA控制器发送传输结束信号ETO到I2C接口并且在中断允许时产生一个传输完成中断。
主发送器在EOT中断服务程序中需禁止DMA请求然后在等到BTF事件后设置停止条件。
主接收器当要接收的数据数目大于或等于2时DMA控制器发送一个硬件信号EOT_1它对应DMA传输(字节数1)。如果在I2C_CR2寄存器中设置了LAST位硬件在发送完EOT_1后的下一个字节将自动发送NACK。在中断允许的情况下用户可以在DMA传输完成的中断服务程序中产生一个停止条件。
意思是说当DMA产生EOT标志后如果开启了EOT相关中断就进中断程序没有开启就进行软件查询做后续处理关闭DMA请求然后等待BTF事件之后执行STOP操作。 这里的BTF事件就是I2C数据收发过程中的数据字节是否传输完成的的事件。 IIC通讯过程 标志位
配置DMA
对DMA进行配置
在IIC的配置中开启DMA
通过设置I2C_CR2寄存器中的DMAEN位可以激活DMA模式。只要TxE位被置位数据将由DMA从预置的存储区装载进I2C_DR寄存器。为I2C分配一个DMA通道须执行以下步骤(x是通道号)。
利用DMA发送 在DMA_CPARx寄存器中设置I2C_DR寄存器地址。数据将在每个TxE事件后从存储器传 送至这个地址。 在DMA_CMARx寄存器中设置存储器地址。数据在每个TxE事件后从这个存储区传送至 I2C_DR。 在DMA_CNDTRx寄存器中设置所需的传输字节数。在每个TxE事件后此值将被递减。 利用DMA_CCRx寄存器中的PL[0:1]位配置通道优先级。 设置DMA_CCRx寄存器中的DIR位。 根据应用要求可以配置在整个传输完成一半或全部完成时发出中断请求。 通过设置DMA_CCTx寄存器上的EN位激活通道。
当DMA控制器中设置的数据传输数目已经完成时DMA控制器给I2C接口发送一个传输结束的EOT/ EOT_1信号。在中断允许的情况下将产生一个DMA中断。 如果使用DMA进行发送时不要设置I2C_CR2寄存器的ITBUFEN位。
利用DMA接收
通过设置I2C_CR2寄存器中的DMAEN位可以激活DMA接收模式。每次接收到数据字节时将由DMA把I2C_DR寄存器的数据传送到设置的存储区(参考DMA说明)。设置DMA通道进行I2C接收须执行以下步骤(x是通道号)
在DMA_CPARx寄存器中设置I2C_DR寄存器的地址。数据将在每次RxNE事件后从此地 址传送到存储区。在DMA_CMARx寄存器中设置存储区地址。数据将在每次RxNE事件后从I2C_DR寄存器 传送到此存储区。在DMA_CNDTRx寄存器中设置所需的传输字节数。在每个RxNE事件后此值将被递 减。用DMA_CCRx寄存器中的PL[0:1]配置通道优先级。清除DMA_CCRx寄存器中的DIR位根据应用要求可以设置在数据传输完成一半或全部 完成时发出中断请求。设置DMA_CCRx寄存器中的EN位激活该通道。
当DMA控制器中设置的数据传输数目已经完成时DMA控制器给I2C接口发送一个传输结束的 EOT/ EOT_1信号。在中断允许的情况下将产生一个DMA中断。 注 如果使用DMA进行接收时不要设置I2C_CR2寄存器的ITBUFEN位