STM32F030开发日志之I2C

最近调试 一款 IQS333 电容触摸按键芯片IC,调了好几天发现I2C端口上没数据。最后追STM Clock tree 和STM 提供的I2C_Timing_Configuration工具解决了这个问题。现在分享给大家。

1. 首先我们观察Clock tree 查看 I2C clock source的缘由。

STM32F030开发日志之I2C

由 Clock tree可以得出I2C clock source 可以选择使用内部8MHz时钟,还是选用的系统SYSCLK.

本人选用的是STM32F030开发板。开发使用的是8MHz的外部晶振。所以采用外部8MHz时钟为例。

根据下面的 clock source时钟走势进行RCC 模块配置:

STM32F030开发日志之I2C

从走势我们可以看出我们需要确定PREDIV,PLLSRC,PLLMUL,SW和I2C1的源输入选择。

Clock configuration register 2 (RCC_CFGR2)

STM32F030开发日志之I2C

Clock configuration register (RCC_CFGR)

STM32F030开发日志之I2C

最后配置RCC相应的PREDIV,PLLSRC,PLLMUL,SW得到PLLCLK为48MHz.

并且STM32厂商提供得了I2C时钟配置的工具。I2C_Timing_Configuration_V1.0.0.xls

STM32F030开发日志之I2C

Note: The “Reset” button resets the input parameters to their default configuration.
To get the value of the timing register, follow these steps:
1. Select device mode by choosing “Master” or “Slave” in the list box.
2. Configure the speed mode by selecting one of the following modes in the list box:
– Standard mode: maximum frequency is 100 KHz.
– Fast mode: maximum frequency is 400 KHz.
– Fast mode Plus: maximum frequency is 1000 KHz.
3. Set the desired I2C speed frequency (master clock).
4. Set the value of I2C clock source frequency.
5. Specify if analog noise filter is enabled or not.
6. Specify if digital noise filter is used or not by setting the filter coefficient (this coefficient
should be an integer from 0 to 15).
7. Set the value of rise time.
8. Set the value of fall time.
9. Click the RUN button:
a) If the calculation of the timing register is completed, the following message is
displayed:
根据STM32厂商提供的配置I2C时钟说明,根据自己的应用我的配置如下:

STM32F030开发日志之I2C

因此在配置I2C的使用Timing的值如下

STM32F030开发日志之I2C

然后配置相应的I2C管脚,代码如下:

/*******************************************************************************
* Function Name  : I2C_DevInit
* Description    : Initializes peripherals used by the I2C EEPROM driver.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void I2C_DevInit(void)
{	
   	I2C_InitTypeDef I2C_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	/** I2C1 GPIO Configuration			 
	PB8	 ------> I2C1_SCL		 
	PB9	 ------> I2C1_SDA	*/	
	/*Enable or disable the AHB peripheral clock */	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);	/*Configure GPIO pin */
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9;	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;	
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;	
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	/*Configure GPIO pin alternate function */	
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_1);	
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_1);

	RCC_I2CCLKConfig(RCC_I2C1CLK_SYSCLK);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
	
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;	
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_DigitalFilter = 0x01;
	I2C_InitStructure.I2C_OwnAddress1=0x00;
	//I2C_InitStructure.I2C_Timing = 0x40B22536;//100Khz
	I2C_InitStructure.I2C_Timing =0x10950C27;//400Khz
	I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
	
	I2C_Init(I2C1, &I2C_InitStructure);	
	I2C_Cmd(I2C1, ENABLE);
	return;
}

 I2C多字节写实现

/*******************************************************************************
* Function Name  : I2C_Write
* Description    : Writes more than one byte to the Device with a single WRITE
*                  cycle. The number of byte can't exceed the Device page size.
* Input          : - pBuffer : pointer to the buffer containing the data to be 
*                    written to the Device.
*                  - WriteAddr : Device's internal address to write to.
*                  - NumByteToWrite : number of bytes to write to the Device.
* Output         : None
* Return         : None
*******************************************************************************/
BOOLEAN  I2C_Write(INT8U DevAddr, INT8U RegAddr,INT8U* pBuffer, INT8U NumByteToWrite)
{
	INT16U timeout = I2C_TIMEOUT;
	
	/* While the bus is busy */
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}
	
	/* Send Touch address for write */	
	I2C_TransferHandling(I2C1, (DevAddr<<1), 1, I2C_Reload_Mode, I2C_Generate_Start_Write);
	
	timeout = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS)==RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}
	
	I2C_SendData(I2C1, RegAddr);

	timeout = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_TCR) == RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}
	
	I2C_TransferHandling(I2C1, (DevAddr<<1), NumByteToWrite, I2C_AutoEnd_Mode, I2C_No_StartStop);
	
	/* While there is data to be written */
	while(NumByteToWrite--)  
	{
		timeout = I2C_TIMEOUT;
		/* Test on EV8 and clear it */
		while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET)
		{
			if((timeout--) == 0)
			{
				return false;
			}
		}
		/* Send the current byte */
		I2C_SendData(I2C1, *pBuffer); 

		/* Point to the next byte to be written */
		pBuffer++; 		
	}
	/* Send STOP condition */
	timeout = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}
	
	return true;
	
}

 I2C多字节读实现

/*******************************************************************************
* Function Name  : I2C_Read
* Description    : Reads a block of data from the Device.
* Input          : - pBuffer : pointer to the buffer that receives the data read 
*                    from the Device.
*                  - ReadAddr : EEPROM's internal address to read from.
*                  - NumByteToRead : number of bytes to read from the Device.
* Output         : None
* Return         : None
*******************************************************************************/
BOOLEAN  I2C_Read(INT8U DevAddr, INT8U RegAddr, INT8U* pBuffer, INT8U NumByteToRead)
{  
	INT16U timeout = 60;

	/* While the bus is busy */
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY) != RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}

	/* Generate start & wait event detection */
	I2C_TransferHandling(I2C1, (DevAddr<<1), 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);


	timeout = I2C_TIMEOUT;
	while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXIS) == RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}

	I2C_SendData(I2C1, RegAddr);

	timeout = I2C_TIMEOUT;	
	while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TC) == RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}

	/* Send STRAT condition a second time */  
	I2C_TransferHandling(I2C1, (DevAddr<<1), NumByteToRead,  I2C_AutoEnd_Mode, I2C_Generate_Start_Read);
	
	/* While there is data to be read */
	while(NumByteToRead)  
	{	
		timeout = I2C_TIMEOUT;
		
		while(I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == RESET)
		{
			if((timeout--) == 0)
			{
				return false;
			}
		}

		/* Read a byte from the EEPROM */
		*pBuffer = I2C_ReceiveData(I2C1);

		/* Point to the next location where the byte read will be saved */
		pBuffer++; 

		/* Decrement the read bytes counter */
		NumByteToRead--;        
	}
	
	/* Enable Acknowledgement to be ready for another reception */
	timeout = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2C1, I2C_FLAG_STOPF) == RESET)
	{
		if((timeout--) == 0)
		{
			return false;
		}
	}
	return true;
}

上一篇:基于I2C/SPI的温湿度采集与OLED显示


下一篇:OLED滚动显示及温湿度检测