天天看點

STM32硬體I2C的一點心得(AT24C32C和AT24C64C)前言CubeMX設定I2C初始化代碼從裝置讀寫

前言

網上有些人說STM32的硬體I2C使用起來有問題,我用起來一點問題都沒有,下面大緻說一下最近做這個的心得

CubeMX設定

軟體采用最新的CubeMX和SDK生成,晶片型号STM32F107RC

首先時鐘使用正常的72M,這部配置設定置很多晶片都一樣

STM32硬體I2C的一點心得(AT24C32C和AT24C64C)前言CubeMX設定I2C初始化代碼從裝置讀寫

I2C的配置也是普普通通,保持預設就好,不使用DMA傳輸

STM32硬體I2C的一點心得(AT24C32C和AT24C64C)前言CubeMX設定I2C初始化代碼從裝置讀寫

I2C初始化代碼

以下代碼由CubeMX自動生成,在使用者代碼區(USER CODE)添加了解除寫保護WR的代碼,除此之外沒有其它修改

void MX_I2C1_Init(void)
{

  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }

}

void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(i2cHandle->Instance==I2C1)
  {
  /* USER CODE BEGIN I2C1_MspInit 0 */

  /* USER CODE END I2C1_MspInit 0 */
  
    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**I2C1 GPIO Configuration    
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* I2C1 clock enable */
    __HAL_RCC_I2C1_CLK_ENABLE();

    /* I2C1 interrupt Init */
    HAL_NVIC_SetPriority(I2C1_EV_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    HAL_NVIC_SetPriority(I2C1_ER_IRQn, 5, 0);
    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
  /* USER CODE BEGIN I2C1_MspInit 1 */
	GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);	//DISABLE wr
  /* USER CODE END I2C1_MspInit 1 */
  }
}
           

從裝置讀寫

一般的EEPROM,像AT24C02這種小容量的裝置,位址都隻需要8位,頁大小一般是16位元組一個頁

而像AT24C32C、AT24C64C這種32K、64K位元組的大容量EEPROM,8位位址是不夠的,使用了16位位址,頁大小在這兩個器件中也變成了32位元組

正是由于容量的不同,導緻代碼上需要做差異化處理,才能正确讀取EEPROM晶片

以下代碼可以參考,寫的時候無法跨頁,是以寫大量資料的時候,隻能一頁一頁的寫,兩次寫之間保證5MS的間隔

讀沒有跨頁的影響,可以一次性把全部資料讀出來,但是要注意,讀和寫之間,是要有5MS的間隔的,否則讀不到資料。也就是說每次寫完,延遲5MS,就能保證後續的程式沒有問題。

//#define I2C_MEMADD_SIZE		I2C_MEMADD_SIZE_8BIT			//小容量EEPROM晶片8位位址用此參數
#define I2C_MEMADD_SIZE		I2C_MEMADD_SIZE_16BIT		//大容量EEPROM晶片16位位址用此參數
#define ADDR_AT24C02_Write 0xA0		//EEPROM I2C寫位址
#define ADDR_AT24C02_Read 0xA0+1	//EEPROM I2C讀位址

typedef enum SYS_PARA_ENUM {
	LOCAL_IP = 0,
	UDP_LOCAL_PORT,
	UDP_PC_PORT,
	NETMASK,
	GATEWAY,
	SERVER_IP,
	SERVER_PORT,
	VERSION,
	SN_NUM,	//16位元組
	SENSOR_TYPE = SN_NUM + 4,
	SENSOR_DATA_TYPE,
	SENSOR_INTERVAL,
	SYS_PARA_MAX,		//end
}sys_para_e;

//請注意,為了友善flash讀寫操作,此處的每一項均設為uint32_t類型
//如果不為uint32_t類型,則flash_write函數将出現錯誤
typedef struct SYS_PARA_TYPE {
	uint32_t local_ip;			//本機IP,大端模式
	uint32_t udp_local_port;	//本機端口号
	uint32_t udp_pc_port;		//PC端口号
	uint32_t netmask;			//本機子網路遮罩,大端模式
	uint32_t gateway;			//本機網關,大端模式
	uint32_t server_ip;			//伺服器IP,大端模式
	uint32_t server_port;		//伺服器端口号
	uint32_t version;			//stm32軟體版本号
	uint32_t sn_num[4];			//SN号	5 6 7 8
	uint32_t sensor_type;		//傳感器類型 9
	uint32_t sensor_data_type;	//傳感器資料類型
	uint32_t sensor_interval;	//傳感器采集時間間隔
}sys_para_t;

/* -----------------------------------------------------------------------------
函數名:  	i2c_write
作者:    	glx
日期:    	2020-11-10
功能:    	資料寫入eeprom
輸入參數:	pData:資料指針
傳回值:  	類型:HAL_StatusTypeDef
			HAL_OK:操作成功
			HAL_ERROR:操作失敗
修改記錄:
------------------------------------------------------------------------------*/
HAL_StatusTypeDef i2c_write(void *pData)
{
	HAL_StatusTypeDef ret = HAL_ERROR;
	uint8_t i, page, pageSize;
	uint8_t *p = (uint8_t *)pData;
	if(pData != NULL)
	{
		if(I2C_MEMADD_SIZE == I2C_MEMADD_SIZE_8BIT)
		{
			pageSize = 16;			//一頁16位元組,不能跨頁寫
			page = SYS_PARA_MAX/4;
		}
		else if(I2C_MEMADD_SIZE == I2C_MEMADD_SIZE_16BIT)
		{
			pageSize = 32;			//一頁32位元組,不能跨頁寫
			page = SYS_PARA_MAX/8;
		}
		//寫完整的頁
		for(i = 0; i<page; i++)
		{
			ret = HAL_I2C_Mem_Write(&hi2c1, ADDR_AT24C02_Write, i*pageSize, I2C_MEMADD_SIZE, p, pageSize, 100);
			p += pageSize;
			if(ret != HAL_OK)
			{
				printf("I2C_Write Sys Para write error\r\n");
				return HAL_ERROR;
			}
			HAL_Delay(5);
		}
		//寫殘缺的頁
		if(SYS_PARA_MAX > (page*4))	
		{
			ret = HAL_I2C_Mem_Write(&hi2c1, ADDR_AT24C02_Write, i*pageSize, I2C_MEMADD_SIZE, p, 4*SYS_PARA_MAX-pageSize*page, 100);
			if(ret != HAL_OK)
			{
				printf("I2C_Write Sys Para write error\r\n");
				return HAL_ERROR;
			}
			HAL_Delay(5);
		}
	}
	else
	{
		printf("I2C_Write pData NULL\r\n");
		return HAL_ERROR;
	}
	return HAL_OK;
}

/* -----------------------------------------------------------------------------
函數名:  	i2c_read
作者:    	glx
日期:    	2020-11-10
功能:    	讀取eeprom資料
輸入參數:	pData:資料指針
傳回值:  	類型:HAL_StatusTypeDef
			HAL_OK:操作成功
			HAL_ERROR:操作失敗
修改記錄:
------------------------------------------------------------------------------*/
HAL_StatusTypeDef i2c_read(void *pData)
{
	HAL_StatusTypeDef ret = HAL_ERROR;
	if(pData != NULL)
	{
		ret = HAL_I2C_Mem_Read(&hi2c1, ADDR_AT24C02_Read, 0, I2C_MEMADD_SIZE, (uint8_t *)pData, SYS_PARA_MAX*4, 100);
		if(ret != HAL_OK)
		{
			printf("I2C_Read Sys Para read error\r\n");
			return HAL_ERROR;
		}		
	}
	else
	{
		printf("I2C_Read pData NULL\r\n");
		return HAL_ERROR;
	}
	return HAL_OK;
}
           

讀寫的代碼

//用于儲存系統參數
sys_para_t sys_para = {0};	
//用于儲存系統預設參數
sys_para_t default_para = {
	.local_ip = 107<<24 | 10<<16 | 168<<8 | 192,
	.udp_local_port = 18080,
	.udp_pc_port = 18081,
	.netmask = 0<<24 | 255<<16 | 255<<8 | 255,
	.gateway = 1<<24 | 10<<16 | 168<<8 | 192,
	.server_ip = 9<<24 | 10<<16 | 168<<8 | 192,
	.server_port = 18082,
	.version = 20201124,
	.sn_num[0] = 0xffffffff,
	.sn_num[1] = 0xffffffff,
	.sn_num[2] = 0xffffffff,
	.sn_num[3] = 0xffffffff,
	.sensor_type = SENSOR_DOOR,
	.sensor_data_type = SENSOR_DATA_GPIO,
	.sensor_interval = 1000,
};

i2c_read(&sys_para);
i2c_write(&default_para);

           

繼續閱讀