本帖最后由 Lp19800618 于 2019-10-21 19:06 编辑
使用“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];
}
}
|