引言
STM32F103系列微控制器是STMicroelectronics推出的一款高性能、低功耗的32位ARM Cortex-M3处理器。本文将介绍如何基于STM32F103的LL库(Low-Level库)来进行I2C通信的操作,通过配置寄存器实现I2C主设备的数据读写。
I2C基础原理
I2C(Inter-Integrated Circuit)是一种串行通信协议,用于连接微控制器与外围设备,例如传感器、存储器等。I2C通信包括两根线路:时钟线(SCL)和数据线(SDA)。通信的设备分为主设备(Master)和从设备(Slave),主设备负责发起通信,而从设备负责响应。I2C通信按字节传输,每个字节由8个位组成,其中7个位用于数据,最后一个位是应答位(ACK)。
STM32F103 LL库的I2C配置
首先,我们需要在STM32CubeMX中进行引脚配置和LL库的初始化。
- 打开STM32CubeMX,选择相应的STM32F103型号。
- 在 “Pinout & Configuration” 选项卡中,配置I2C的SCL和SDA引脚。
- 在 “Configuration” 选项卡中,启用 “I2C1” 外设,选择 “I2C1 Mode” 为 “I2C”.
- 配置 I2C1 的时钟频率和模式。
- 生成代码并打开STM32CubeIDE。
STM32F103 LL库的I2C编程
以下是一个简单的例子,演示如何在STM32F103上使用LL库实现I2C通信。
#include "stm32f1xx_ll_bus.h"
#include "stm32f1xx_ll_gpio.h"
#include "stm32f1xx_ll_i2c.h"
#include "stm32f1xx_ll_rcc.h"
#include "stm32f1xx_ll_usart.h"
#define I2C_SCL_PIN LL_GPIO_PIN_6
#define I2C_SDA_PIN LL_GPIO_PIN_7
I2C_HandleTypeDef hi2c1;
void I2C_Init() {
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能I2C1和GPIOB时钟
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
// 配置SCL和SDA引脚
GPIO_InitStruct.Pin = I2C_SCL_PIN | I2C_SDA_PIN;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 配置I2C1外设
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 (LL_I2C_Init(&hi2c1) != HAL_OK) {
// 处理初始化失败的情况
}
}
void I2C_WriteData(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
while (LL_I2C_IsActiveFlag_BUSY(I2C1))
;
// 发送起始信号
LL_I2C_GenerateStartCondition(I2C1);
while (!LL_I2C_IsActiveFlag_SB(I2C1))
;
// 发送设备地址和写标志
LL_I2C_TransmitData8(I2C1, devAddr << 1);
while (!LL_I2C_IsActiveFlag_ADDR(I2C1))
;
LL_I2C_ClearFlag_ADDR(I2C1);
// 发送寄存器地址
LL_I2C_TransmitData8(I2C1, regAddr);
while (!LL_I2C_IsActiveFlag_TXE(I2C1))
;
// 发送数据
LL_I2C_TransmitData8(I2C1, data);
while (!LL_I2C_IsActiveFlag_TXE(I2C1))
;
// 发送停止信号
LL_I2C_GenerateStopCondition(I2C1);
}
uint8_t I2C_ReadData(uint8_t devAddr, uint8_t regAddr) {
uint8_t data;
while (LL_I2C_IsActiveFlag_BUSY(I2C1))
;
// 发送起始信号
LL_I2C_GenerateStartCondition(I2C1);
while (!LL_I2C_IsActiveFlag_SB(I2C1))
;
// 发送设备地址和写标志
LL_I2C_TransmitData8(I2C1, devAddr << 1);
while (!LL_I2C_IsActiveFlag_ADDR(I2C1))
;
LL_I2C_ClearFlag_ADDR(I2C1);
// 发送寄存器地址
LL_I2C_TransmitData8(I2C1, regAddr);
while (!LL_I2C_IsActiveFlag_TXE(I2C1))
;
// 重新启动
LL_I2C_GenerateStartCondition(I2C1);
while (!LL_I2C_IsActiveFlag_SB(I2C1))
;
// 发送设备地址和读标志
LL_I2C_TransmitData8(I2C1, (devAddr << 1) | 1);
while (!LL_I2C_IsActiveFlag_ADDR(I2C1))
;
LL_I2C_ClearFlag_ADDR(I2C1);
// 开启接收模式
LL_I2C_AcknowledgeNextData(I2C1, LL_I2C_ACK);
// 发送停止信号
LL_I2C_GenerateStopCondition(I2C1);
// 等待数据接收
while (!LL_I2C_IsActiveFlag_RXNE(I2C1))
;
data = LL_I2C_ReceiveData8(I2C1);
return data;
}
int main(void) {
I2C_Init();
uint8_t deviceAddress = 0x50;
uint8_t registerAddress = 0x00;
uint8_t dataToWrite = 0xAA;
// 写入数据
I2C_WriteData(deviceAddress, registerAddress, dataToWrite);
// 读取数据
uint8_t readData = I2C_ReadData(deviceAddress, registerAddress);
// 此处可进行处理读取到的数据
while (1) {
}
}
代码解释
I2C_Init()
: 初始化I2C外设和引脚。I2C_WriteData(uint8_t devAddr, uint8_t regAddr, uint8_t data)
: 向指定设备的指定寄存器写入数据。I2C_ReadData(uint8_t devAddr, uint8_t regAddr)
: 从指定设备的指定寄存器读取数据。
结论
通过以上配置和代码,我们演示了如何基于STM32F103的LL库实现I2C通信。LL库提供了对底层硬件的直接控制,使得我们可以更加灵活地进行配置和操作。在实际应用中,可以根据需要扩展和改进这个基础框架,以适应更复杂的I2C通信场景。