又花了好几天的时间调I2C,前前后后出现了很多问题,有一些是不仔细的问题,有一些是对于I2C协议不够了解,手册看的不够认真,总之继续学习,这里将遇到的问题记录下,以便日后查阅
在103的中文参考手册p511有关于STOPF的描述如下:
STOPF:停止条件检测位(从模式) (Stop detection (slave mode))
0:没有检测到停止条件;
1:检测到停止条件。
– 在一个应答之后(如果ACK=1),当从设备在总线上检测到停止条件时,硬件将该位置’1’。
– 软件读取SR1寄存器后,对CR1寄存器的写操作将清除该位,或当PE=0时,硬件清除该位。
注:在收到NACK后,STOPF位不被置位。
最关键的是这个注,我们知道主机接收数据时,每接收到一个字节都会发回一个ACK信号(前提是主机配置要使能Ack_Enable),而当发送完最后一个字节时,主机这时发送的NACK。也就是说从机如果使能了AF中断,当监测到总线上有NACK信号时就会进入中断,而之后的Stop信号就算从机检测到了也不会产生置位Stopf不会进入相应的中断。
而且,AF中断是属于I2C1_ER_IRQHandler这个中断通道,在EV_IRQHandler中是检测不到的,并且千万记得要开启I2C_IT_ERR中断以及配置它的NVIC,不然中断也不会响应
STM32F070RB I2C硬件配置
I2C_DeInit(I2C1);
RCC_I2CCLKConfig(RCC_I2C1CLK_SYSCLK);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
/********I2C1 GPIO_Pins configuration***********/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_1);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6 - SCL PB7 - SDA
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB,&GPIO_InitStructure);
/********I2C1 InitStruct configuration***********/
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
I2C_InitStruct.I2C_DigitalFilter = 0x00;
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
/*ÉèÖÃI2Cʱ¼ä¼Ä´æÆ÷Öµ,Ò²ÅäÖÃÁËI2C_Speed_Frequency ²Î¿¼ÅäÖù¤¾ßI2C_Timing_Config_Tool*/
I2C_InitStruct.I2C_Timing = I2C_TIMING;
I2C_InitStruct.I2C_OwnAddress1 = 0x00;
I2C_Init(I2C1,&I2C_InitStruct);
//I2C_ClearFlag(I2C1,I2C_FLAG_BUSY);
I2C_Cmd(I2C1,ENABLE);
I2C主机查询发送数据
/******************************************************************
* Function Name : I2C_Write
* Description : Write more than one byte to the Device with a
* single WRITE cycle
* Input : - DevAddr : Device Address
- RegAddr : Register Address
- pBuffer : pointer to the buffer containing the
data to be written to the Device
- NumByteToWrite : number of bytes to write
* Return : I2C_Status
******************************************************************/
I2C_Status I2C_Write(uint8_t DevAddr, uint8_t* pBuffer, uint8_t NumByteToWrite)
{
uint16_t timeout = I2C_TIMEOUT;
/* while the bus is busy */
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) != RESET)
{
if((timeout--) == 0)
{
return I2C_FAIL;
}
}
I2C_TransferHandling(I2C1,DevAddr,NumByteToWrite ,I2C_AutoEnd_Mode,I2C_Generate_Start_Write);
for(write_Num = 0;write_Num < NumByteToWrite;write_Num++)
{
I2C_SendData(I2C1,pBuffer[write_Num]);
timeout = I2C_TIMEOUT;
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)
{
if((timeout--) == 0)
{
return1++;
return I2C_FAIL;
}
}
}
timeout = I2C_TIMEOUT;
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_STOPF) == RESET)
{
if((timeout--) == 0)
{
return2++;
return I2C_FAIL;
}
}
return I2C_OK;
}
STM32F103的从机中断接收
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
/******** I2C1 GPIO_Pins Configuration***********/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/******** I2C1 NVIC Configuration ***************/
NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn; //I2C Event IRQ
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/********** I2C1 initial Configuration***********/
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_OwnAddress1 = SLAVE_ADDRESS;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_ClockSpeed = 100000;
I2C_Init(I2C1,&I2C_InitStructure);
I2C_ITConfig(I2C1,I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR,ENABLE);
I2C_Cmd(I2C1,ENABLE);
}
void I2C1_EV_IRQHandler(void)
{
switch(I2C_GetLastEvent(I2C1))
{
case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:
break;
/******* Slave receive mode *************/
case I2C_EVENT_SLAVE_BYTE_RECEIVED:
read_buffer[Rx++] = I2C_ReceiveData(I2C1);
break;
case I2C_EVENT_SLAVE_STOP_DETECTED:
Rx = 0;
I2C_Cmd(I2C1,ENABLE); //持续接收改成 I2C_Cmd(I2C1,ENABLE)
break;
}
}
主机中断发送
I2C_Status I2C_INT_Write(uint8_t DevAddr, uint8_t* pBuffer, uint8_t NumByteToWrite)
{
uint16_t timeout = I2C_TIMEOUT;
uint8_t write_Num = 0;
Txptr = pBuffer;
/* while the bus is busy */
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) != RESET)
{
if((timeout--) == 0)
{
return I2C_FAIL;
}
}
I2C_TransferHandling(I2C1,DevAddr,NumByteToWrite,I2C_AutoEnd_Mode,I2C_Generate_Start_Write);
I2C_ITConfig(I2C1,I2C_IT_TXI,ENABLE);
return I2C_OK;
}
主机查询接收
I2C_Status I2C_Read(uint8_t DevAddr,uint8_t* pBuffer, uint8_t NumByteToRead)
{
uint16_t timeout = I2C_TIMEOUT;
/* while the bus is busy */
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) != RESET)
{
if((timeout--) == 0)
return I2C_FAIL;
}
/* Send Device address for read */
I2C_TransferHandling(I2C1,DevAddr,NumByteToRead,I2C_AutoEnd_Mode,I2C_Generate_Start_Read);
for(uint8_t read_Num = 0;read_Num < NumByteToRead;read_Num++)
{
timeout = I2C_TIMEOUT;
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_RXNE) == RESET)
{
if((timeout--) == 0)
return I2C_FAIL;
}
pBuffer[read_Num] = I2C_ReceiveData(I2C1);
}
timeout = I2C_TIMEOUT;
while(I2C_GetFlagStatus(I2C1,I2C_FLAG_STOPF) == RESET)
{
if((timeout--) == 0)
{
return I2C_FAIL;
}
}
return I2C_OK;
}
从机中断发送
void I2C1_EV_IRQHandler(void)
{
switch(I2C_GetLastEvent(I2C1))
{
case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:
case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:
I2C_ClearITPendingBit(I2C1,I2C_IT_ADDR);
break;
/******* Slave transmit mode ************/
case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:
case I2C_EVENT_SLAVE_BYTE_TRANSMITTING:
I2C_SendData(I2C1,write_buffer[Tx++]);
break;
/******* Slave receive mode *************/
case I2C_EVENT_SLAVE_BYTE_RECEIVED:
read_buffer[Rx++] = I2C_ReceiveData(I2C1);
break;
case I2C_EVENT_SLAVE_STOP_DETECTED:
Rx = 0;
I2C_Cmd(I2C1,DISABLE);
break;
}
}
void I2C1_ER_IRQHandler(void)
{
switch(I2C_GetLastEvent(I2C1))
{
case I2C_EVENT_SLAVE_ACK_FAILURE:
Tx = 0;
I2C_ClearITPendingBit(I2C1,I2C_IT_AF);
}
}
1、关于工作模式的切换
硬件I2C默认工作在Slave模式,当其产生一个Start信号时自动切换为Master模式
而当仲裁丢失或者产生一个Stop信号时则自动由Slave模式切换为Master模式
2、数据传输
在Master模式下,数据的传输在产生Start信号后开始,产生Stop信号后结束,而无论是Start信号或是Stop信号,都由软件产生。
Start信号后紧跟的第一个字节(8 bit)包含着Slave的地址和读写位(0 write / 1 read)
3、7 bit地址和10 bit地址
7 bit地址模式下,Start信号后一个字节表示从机地址和读写位 Slave address + W/R );10 bit地址模式下,Start信号后的两个字节表示从机地址和读写位(W/R + 1111 0 + Slave address)
4、关于I2C的几个模式解析:
I2C_AutoEnd_Mode —— 这种模式下,I2C传输完NBytes个字节后会自动产生停止信号以终止信号的传输,所以这个模式下的设备肯定是主机模式
I2C_Reload_Mode —— 自动装载模式,在这种模式下传输完NBytes个字节后,I2C的TXR又会重新装载需要传输的数据,每传输完NBytes个字节后TCR标志位会置位