一、I2C簡(jiǎn)介
I2C(Inter-Integrated Circuit ,內(nèi)部集成電路) 總線是一種由飛利浦 Philip 公司開(kāi)發(fā)的串行總線葛作。是兩條串行的總線闲延,它由一根數(shù)據(jù)線(SDA)和一根 時(shí)鐘線(SCL)組成姆涩。I2C 總線上可以接多個(gè) I2C 設(shè)備,每個(gè)器件都有一個(gè)唯一的地址識(shí)別志膀。同一時(shí)間只能有一個(gè)主設(shè)備,其他為從設(shè)備鳖擒。通常 MCU 作為主設(shè)備控制溉浙,外設(shè)作為從設(shè)備。
STM32 的 I2C 外設(shè)可用作通訊的主機(jī)及從機(jī)蒋荚,支持 100Kbit/s 和 400Kbit/s 的速率戳稽,支持 7 位、10 位設(shè)備地址期升,支持 DMA 數(shù)據(jù)傳輸惊奇,并具有數(shù)據(jù)校驗(yàn)功能。它的 I2C 外設(shè)還支持 SMBus2.0 協(xié)議播赁,SMBus 協(xié)議與 I2C 類似颂郎,主要應(yīng)用于筆記本電腦的電池管理中。
二容为、引腳分布
STM32 芯片有多個(gè) I2C 外設(shè)乓序,它們的 I2C 通訊信號(hào)引出到不同的 GPIO 引腳上,使用時(shí)必須配置到這些指定的引腳坎背。PB8 PB9 為重映射替劈。
三、EEPROM芯片
開(kāi)發(fā)板中的 EEPROM 芯片型號(hào):AT24C02得滤。AT24C 系列為美國(guó) ATMEL 公司推出的串行 COMS 型 EEPROM陨献。芯片型號(hào)后兩位表示芯片容量,例如 ATC24C02 為 2K耿戚。引腳圖中 A0湿故、A1、A2 為器件地址引腳膜蛔,GND為地坛猪,VCC為正電源,WP為寫保護(hù)皂股,SCL為串行時(shí)鐘線墅茉,SDA為串行數(shù)據(jù)線。
EEPROM 芯片中 WP 引腳具有寫保護(hù)功能,當(dāng)該引腳電平為高時(shí)就斤,禁止寫入數(shù)據(jù)悍募,當(dāng)引腳為低電平時(shí),可寫入數(shù)據(jù)洋机,我們直接接地坠宴,不使用寫保護(hù)功能。
AT24Cxx 設(shè)備地址為如下绷旗,前四位固定為
1010
喜鼓,A2~A0為由管腳電平?jīng)Q定。AT24Cxx EEPROM Board模塊中默認(rèn)為接地衔肢。A2~A0 為 000
庄岖,最后一位 R/W 表示讀寫操作。所以由于 I2C 通訊時(shí)常常是地址跟讀寫方向連在一起構(gòu)成一個(gè) 8 位數(shù)角骤,且當(dāng) R/W 位為 0
時(shí)隅忿,表示寫方向,所以加上 7 位地址邦尊,其值為 0xA0
背桐,常稱該值為 I2C 設(shè)備的“寫地址”;當(dāng) R/W 位為 1
時(shí)胳赌,表示讀方向牢撼,加上 7 位地址,其值為 0xA1
疑苫,常稱該值為“讀地址”熏版。四、新建工程
1. 打開(kāi) STM32CubeMX 軟件捍掺,點(diǎn)擊“新建工程”
2. 選擇 MCU 和封裝
3. 配置時(shí)鐘
RCC 設(shè)置撼短,選擇 HSE(外部高速時(shí)鐘) 為 Crystal/Ceramic Resonator(晶振/陶瓷諧振器)
選擇 Clock Configuration,配置系統(tǒng)時(shí)鐘 SYSCLK 為 72MHz
修改 HCLK 的值為 72 后挺勿,輸入回車曲横,軟件會(huì)自動(dòng)修改所有配置
4. 配置調(diào)試模式
非常重要的一步,否則會(huì)造成第一次燒錄程序后續(xù)無(wú)法識(shí)別調(diào)試器
SYS 設(shè)置不瓶,選擇 Debug 為 Serial Wire
五禾嫉、I2C1
5.1 參數(shù)配置
在 Connectivity
中選擇 I2C1
設(shè)置,并選擇 I2C
內(nèi)部集成電路
I2C 為默認(rèn)設(shè)置不作修改蚊丐。只需注意一下熙参,I2C 為標(biāo)準(zhǔn)模式,I2C 傳輸速率 (I2C Clock Speed) 為
100KHz
麦备。5.2 生成代碼
輸入項(xiàng)目名和項(xiàng)目路徑
選擇應(yīng)用的 IDE 開(kāi)發(fā)環(huán)境 MDK-ARM V5
每個(gè)外設(shè)生成獨(dú)立的
’.c/.h’
文件不勾:所有初始化代碼都生成在 main.c
勾選:初始化代碼生成在對(duì)應(yīng)的外設(shè)文件孽椰。 如 GPIO 初始化代碼生成在 gpio.c 中昭娩。
點(diǎn)擊 GENERATE CODE 生成代碼
5.3 添加全局變量
在 main.c 頭部添加寫地址 0xA0
,讀地址 0xA1
黍匾,寫緩存區(qū) WriteBuffer
栏渺,讀緩存區(qū) ReadBuffer
。
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart1;
/* USER CODE BEGIN PV */
#define ADDR_24LCxx_Write 0xA0
#define ADDR_24LCxx_Read 0xA1
#define BufferSize 256
uint8_t WriteBuffer[BufferSize] = {0};
uint8_t ReadBuffer[BufferSize] = {0};
/* USER CODE END PV */
5.4 添加寫入和讀取函數(shù)
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_USART1_UART_Init();
MX_I2C1_Init();
/* USER CODE BEGIN 2 */
printf("\r\n***************I2C Example*******************************\r\n");
uint32_t i;
uint8_t j;
for(i = 0; i < 256; i++)
{
WriteBuffer[i] = i; /* WriteBuffer init */
printf("0x%02X ", WriteBuffer[i]);
if(i % 16 == 15)
{
printf("\n\r");
}
}
/* wrinte date to EEPROM */
for (j = 0; j < 32; j++)
{
if(HAL_I2C_Mem_Write(&hi2c1, ADDR_24LCxx_Write, 8*j, I2C_MEMADD_SIZE_8BIT, WriteBuffer+8*j, 8, 100) == HAL_OK)
{
printf("\r\n EEPROM 24C02 Write Test OK \r\n");
}
else
{
printf("\r\n EEPROM 24C02 Write Test False \r\n");
}
}
/* read date from EEPROM */
HAL_I2C_Mem_Read(&hi2c1, ADDR_24LCxx_Read, 0, I2C_MEMADD_SIZE_8BIT, ReadBuffer, BufferSize, 1000);
for(i = 0; i < 256; i++)
{
printf("0x%02X ",ReadBuffer[i]);
if(i%16 == 15)
{
printf("\n\r");
}
}
if(memcmp(WriteBuffer,ReadBuffer,BufferSize) == 0 ) /* check date */
{
printf("\r\n EEPROM 24C02 Read Test OK\r\n");
}
else
{
printf("\r\n EEPROM 24C02 Read Test False\r\n");
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
程序中先初始化寫數(shù)據(jù)緩存锐涯。然后調(diào)用 HAL_I2C_Mem_Write()
函數(shù)將數(shù)據(jù)寫入 EEPROM 中磕诊。根據(jù)函數(shù)返回值判斷寫操作是否正確。在 I2C 中可以找到內(nèi)存寫函數(shù)說(shuō)明纹腌。
- 第一個(gè)參數(shù)為 I2C 操作句柄秀仲。
- 第二個(gè)參數(shù)為 EEPROM 的寫操作設(shè)備地址。
- 第三個(gè)參數(shù)為內(nèi)存地址壶笼。
- 第四個(gè)參數(shù)為內(nèi)存地址長(zhǎng)度,EEPROM 內(nèi)存長(zhǎng)度為 8bit雁刷。
- 第五個(gè)參數(shù)為數(shù)據(jù)緩存的起始地址覆劈。
- 第六個(gè)參數(shù)為傳輸數(shù)據(jù)的大小。
AT24C02 型號(hào)的芯片頁(yè)寫入時(shí)序最多可以一次 發(fā)送 8 個(gè)數(shù)據(jù)(即 n = 8 )沛励,該值也稱為頁(yè)大小责语,某些型號(hào)的芯片每個(gè)頁(yè)寫入時(shí)序最多可傳輸 16 個(gè)數(shù)據(jù)。
- 第七個(gè)參數(shù)為操作超時(shí)時(shí)間目派。
/**
* @brief Write an amount of data in blocking mode to a specific memory address
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address: The device 7 bits address value
* in datasheet must be shifted to the left before calling the interface
* @param MemAddress Internal memory address
* @param MemAddSize Size of internal memory address
* @param pData Pointer to data buffer
* @param Size Amount of data to be sent
* @param Timeout Timeout duration
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize,
uint8_t *pData, uint16_t Size, uint32_t Timeout)
調(diào)用
HAL_I2C_Mem_Read()
函數(shù)讀取 EEPROM 中剛才寫入的數(shù)據(jù)坤候。HAL_I2C_Mem_Read()
函數(shù)描述如下。
- 第一個(gè)參數(shù)為 I2C 操作句柄企蹭。
- 第二個(gè)參數(shù)為 EEPROM 的讀操作設(shè)備地址白筹。
- 第三個(gè)參數(shù)為內(nèi)存地址。
- 第四個(gè)參數(shù)為內(nèi)存地址長(zhǎng)度谅摄。
- 第五個(gè)參數(shù)為讀取數(shù)據(jù)存儲(chǔ)的起始地址徒河。
- 第六個(gè)參數(shù)為傳輸數(shù)據(jù)的大小。
- 第七個(gè)參數(shù)為操作超時(shí)時(shí)間送漠。
/**
* @brief Read an amount of data in blocking mode from a specific memory address
* @param hi2c Pointer to a I2C_HandleTypeDef structure that contains
* the configuration information for the specified I2C.
* @param DevAddress Target device address: The device 7 bits address value
* in datasheet must be shifted to the left before calling the interface
* @param MemAddress Internal memory address
* @param MemAddSize Size of internal memory address
* @param pData Pointer to data buffer
* @param Size Amount of data to be sent
* @param Timeout Timeout duration
* @retval HAL status
*/
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize,
uint8_t *pData, uint16_t Size, uint32_t Timeout)
程序最后調(diào)用 memcmp()
函數(shù)判斷讀寫的兩個(gè)緩存的數(shù)據(jù)是否一致顽照。memcmp()
是比較內(nèi)存區(qū)域是否相等,標(biāo)準(zhǔn)庫(kù)里面的函數(shù)闽寡,在 main.c
前面添加 string.h
頭文件代兵。
5.5 查看打印
串口打印功能查看 STM32CubeMX學(xué)習(xí)筆記(6)——USART串口使用
5.6 HAL庫(kù)與標(biāo)準(zhǔn)庫(kù)代碼比較
STM32CubeMX 使用 HAL 庫(kù)生成的代碼:
/**
* @brief I2C1 Initialization Function
* @param None
* @retval None
*/
static void MX_I2C1_Init(void)
{
/* USER CODE BEGIN I2C1_Init 0 */
/* USER CODE END I2C1_Init 0 */
/* USER CODE BEGIN I2C1_Init 1 */
/* USER CODE END I2C1_Init 1 */
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();
}
/* USER CODE BEGIN I2C1_Init 2 */
/* USER CODE END I2C1_Init 2 */
}
HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout);
使用 STM32 標(biāo)準(zhǔn)庫(kù)的代碼:
/**
* @brief I2C I/O配置
* @param 無(wú)
* @retval 無(wú)
*/
static void I2C_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能與 I2C 有關(guān)的時(shí)鐘 */
EEPROM_I2C_APBxClock_FUN ( EEPROM_I2C_CLK, ENABLE );
EEPROM_I2C_GPIO_APBxClock_FUN ( EEPROM_I2C_GPIO_CLK, ENABLE );
/* I2C_SCL、I2C_SDA*/
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SCL_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 開(kāi)漏輸出
GPIO_Init(EEPROM_I2C_SCL_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = EEPROM_I2C_SDA_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; // 開(kāi)漏輸出
GPIO_Init(EEPROM_I2C_SDA_PORT, &GPIO_InitStructure);
}
/**
* @brief I2C 工作模式配置
* @param 無(wú)
* @retval 無(wú)
*/
static void I2C_Mode_Config(void)
{
I2C_InitTypeDef I2C_InitStructure;
/* I2C 配置 */
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
/* 高電平數(shù)據(jù)穩(wěn)定爷狈,低電平數(shù)據(jù)變化 SCL 時(shí)鐘線的占空比 */
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 =I2Cx_OWN_ADDRESS7;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
/* I2C的尋址模式 */
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
/* 通信速率 */
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
/* I2C 初始化 */
I2C_Init(EEPROM_I2Cx, &I2C_InitStructure);
/* 使能 I2C */
I2C_Cmd(EEPROM_I2Cx, ENABLE);
}
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);
void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);
六植影、注意事項(xiàng)
用戶代碼要加在 USER CODE BEGIN N
和 USER CODE END N
之間,否則下次使用 STM32CubeMX 重新生成代碼后淆院,會(huì)被刪除何乎。
? 由 Leung 寫于 2021 年 1 月 26 日
? 參考:STM32CubeMX系列教程9:內(nèi)部集成電路(I2C)
【STM32Cube_13】使用硬件I2C讀寫EEPROM(AT24C02)