完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
使用“NUCLEO-L073RZ”开发板进行调试。在调试I2C时,遇到了一些问题。
下面介绍一下我们的使用背景以及遇到的问题: - 使用背景 对外通信是通过I2C接口,L073作为I2C从机,供主机读/写数据 - 遇到的问题 注:通过一个自制的USB转I2C通信板,与“NUCLEO-L073RZ”开发板进行I2C通信。 1. 尝试模拟主机向从机的"BYTE ADDRESS"为0x08的地址写入一个字节的数据,假设为0x12。 通过在线仿真,发现数据已经被正确的写入并保存到相关的数组中了。 接下来,尝试模拟主机从从机的"BYTE ADDRESS"为0x08的地址读出一个字节的数据,出现的现象是: 第一次读,可以正确的读出数据为0x12; 第二次读,读出数据变成了0x00; 第三次读,可以正确的读出数据为0x12; 后面都能正确的读出数据为0x12; 2. 尝试模拟主机向从机的"BYTE ADDRESS"为0x08和0x09的地址连续写入两个字节的数据,假设为0x12和0x34。 通过在线仿真,发现数据已经被正确的写入并保存到相关的数组中了。 接下来,尝试模拟主机从从机的"BYTE ADDRESS"为0x08的地址连续读出两个字节的数据,出现的现象是: 第一次读,可以正确的读出数据为0x12和0x34; 第二次读,读出数据变成了0x00和0x12; 第三次读,读出数据变成了0x34和0x12; 后面读出数据都为0x34和0x12; 3. 尝试模拟主机向从机的"BYTE ADDRESS"为0x08、0x09和0x0A的地址连续写入三个字节的数据,假设为0x12、0x34和0x56。 通过在线仿真,发现数据已经被正确的写入并保存到相关的数组中了。 接下来,尝试模拟主机从从机的"BYTE ADDRESS"为0x08的地址连续读出三个字节的数据,出现的现象是: 第一次读,可以正确的读出数据为0x12、0x34和0x56; 第二次读,读出数据变成了0x00、0x12和0x34; 第三次读,读出数据变成了0x56、0x12和0x34; 后面读出数据都为0x56、0x12和0x34; ... ... 另外,我在调试过程中发现一个现象:就是如果Firmware初次运行的时候,如果接收到I2C读一个字节操作的请求,程序会连续进入TXIS中断两次! 但是紧接着再多次发送读一个字节操作的请求,程序就只会进入TXIS一次了!为什么第一次读操作的时候会进入TXIS中断两次? 附I2C初始化及中断部分代码: /* I2C1 init function */ void MX_I2C1_Init(void) { LL_I2C_InitTypeDef I2C_InitStruct = {0}; LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOB); /**I2C1 GPIO Configuration PB8 ------> I2C1_SCL PB9 ------> I2C1_SDA */ GPIO_InitStruct.Pin = I2C1_SCL_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; GPIO_InitStruct.Pull = LL_GPIO_PULL_UP; GPIO_InitStruct.Alternate = LL_GPIO_AF_4; LL_GPIO_Init(I2C1_SCL_GPIO_Port, &GPIO_InitStruct); GPIO_InitStruct.Pin = I2C1_SDA_Pin; GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; GPIO_InitStruct.Pull = LL_GPIO_PULL_UP; GPIO_InitStruct.Alternate = LL_GPIO_AF_4; LL_GPIO_Init(I2C1_SDA_GPIO_Port, &GPIO_InitStruct); /* Peripheral clock enable */ LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); // 使能I2C1的时钟 RCC->CCIPR &= ~RCC_CCIPR_I2C1SEL; // 选择APB时钟作为I2C1的时钟(Cube代码里没有这一行) NVIC_SetPriority(I2C1_IRQn, 0); NVIC_EnableIRQ(I2C1_IRQn); LL_I2C_EnableAutoEndMode(I2C1); LL_I2C_DisableOwnAddress2(I2C1); LL_I2C_DisableGeneralCall(I2C1); LL_I2C_EnableClockStretching(I2C1); I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; I2C_InitStruct.Timing = 0x20302E37; I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; I2C_InitStruct.DigitalFilter = 0; I2C_InitStruct.OwnAddress1 = SLAVE_OWN_ADDRESS; I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK; I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT; LL_I2C_Init(I2C1, &I2C_InitStruct); LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK); LL_I2C_EnableIT_ADDR(I2C1); //LL_I2C_EnableIT_NACK(I2C1); //LL_I2C_EnableIT_ERR(I2C1); LL_I2C_EnableIT_STOP(I2C1); //LL_I2C_EnableIT_TC(I2C1); //LL_I2C_EnableIT_RX(I2C1); } /* USER CODE BEGIN 1 */ void I2C_interrupt_op(void) { uint8_t DoPrefetch = 0; // 为I2C读操作执行预取数操作的标志,如果不为0,则需要预先取数 uint8_t Receiverdata; //---------------------------------------- Check ADDR flag value in ISR register //---------------------------------------- 检查是否为地址匹配中断 if(LL_I2C_IsActiveFlag_ADDR(I2C1)) { LL_I2C_ClearFlag_ADDR(I2C1); // Clear ADDR flag value in ISR register //---------------------------------------- Verify the Address Match with the OWN Slave address //---------------------------------------- 检查地址是否为设置的从机地址 if(LL_I2C_GetAddressMatchCode(I2C1) == SLAVE_OWN_ADDRESS) { //---------------------------------------- Verify the transfer direction, a read direction, Slave enters transmitter mode */ //---------------------------------------- 如果传输方向是读操作 if(LL_I2C_GetTransferDirection(I2C1) == LL_I2C_DIRECTION_READ) { I2CBusy = TRUE; LL_I2C_EnableIT_TX(I2C1); // Enable Transmit Interrupt } //---------------------------------------- 如果传输方向是写操作 else if (LL_I2C_GetTransferDirection(I2C1) == LL_I2C_DIRECTION_WRITE) { LL_I2C_EnableIT_RX(I2C1); // Enable Receive Interrupt I2CRxCount = 0; I2CBusy = TRUE; } else { I2C_error_callback(); // Call Error function } } else { I2C_error_callback(); // Call Error function } } //---------------------------------------- Check TXIS flag value in ISR register //---------------------------------------- 发送中断 else if(LL_I2C_IsActiveFlag_TXIS(I2C1)) { //LL_I2C_DisableIT_TX(I2C1); LL_I2C_TransmitData8(I2C1, I2CTxBufData); // Send the Byte requested by the Master DoPrefetch = 1; //---------------------------------------- 低128字节地址寻址 if(DataAddr == 127) { DataAddr = 0; } //---------------------------------------- 高128字节地址寻址 else if(DataAddr == 255) { DataAddr = 128; } //---------------------------------------- 地址索引增1 else { DataAddr++; } } //---------------------------------------- Check RXNE flag value in ISR register //---------------------------------------- 接收中断 else if(LL_I2C_IsActiveFlag_RXNE(I2C1)) { Receiverdata = LL_I2C_ReceiveData8(I2C1); // 将RXDR中的数据保存到Receiverdata //---------------------------------------- 如果是接收到的Data的第一个字节 if(I2CRxCount == 0) { I2CRxCount++; DataAddr = Receiverdata; // 按照协议规定,Data的第一个字节为MEMORY ADDR DoPrefetch = 2; } else { Page00H[DataAddr] = Receiverdata; //---------------------------------------- 低128字节地址寻址 if(DataAddr == 127) { DataAddr = 0; } //---------------------------------------- 高128字节地址寻址 else if(DataAddr == 255) { DataAddr = 128; } //---------------------------------------- 地址索引增1 else { DataAddr++; } } } //---------------------------------------- Check STOP flag value in ISR register //---------------------------------------- STOPF中断 else if(LL_I2C_IsActiveFlag_STOP(I2C1)) { LL_I2C_ClearFlag_STOP(I2C1); // Clear STOP flag value in ISR register I2CBusy = FALSE; } else { I2CBusy = FALSE; I2C_error_callback(); // Call Error function } //---------------------------------------- 如果预取数标志大于0,则需要预先将数据取出并保存到变量I2CTxBufData中去,等待Host去读取 if (DoPrefetch > 0) { I2CTxBufData = Page00H[DataAddr]; } } |
|
相关推荐
6个回答
|
|
坐等大神解答
|
|
|
|
分别验证您的主机 、从机I2C通讯;不要纠结一起;
先将主机连 某个类似标准 I2C EEPOM[改地址啊],验证主机 I2C读,写,连续读,连续续 OK 再将验证OK 的主机连接从机,在从机上debug【调试】,验证从机的被读,被写是否正确。 可用示波器抓波形,比较细节,包括 ACK有无。。。 |
|
|
|
先将主机连 某个类似标准 I2C EEPOM[改地址啊],验证主机 I2C读,写,连续读,连续写 OK
|
|
|
|
本帖最后由 lm12041204a 于 2019-11-29 10:17 编辑
感觉主机第一次读取从机时,从机发送了0x00,是不是个结束标志? 第一次读好像是从机发送过来的一个数据单位,缓存或者寄存到接收缓冲区(类似进程控制块之类的状态或数据结构,映射外部设备); 第二次(单字节)读取是直接从接收缓冲区中读取,由于第一次读取没有取走全部数据,读取指针停留在读取字节的下一个字节处,所以读取的字节是0x00; 第三次(单字节)读取,如果0x00是读取结束标志的话,读取指针已经复位了,从头读,之后都是正确的。 至于双字节和多字节,一个道理。 第一次读取和后续的读取过程、从机和主机的通信,应该是有些差别的。要看一下从机、读写板和主机上的数据结构、读写逻辑和规则。 第一次应该是有实际的数据传输,之后从映射的数据结构(控制块之、缓冲之类的东西)读取。 另一个要注意的读写指针或Flag、结束标志0x00的问题。 具体情况还要楼主仔细的查看一下开发板和读写板的读写逻辑和规则。 |
|
|
|
参考: 在C语言中没有专门的字符串变量,通常用一个字符数组来存放一个字符串。字符串总是以' |