本文驱动0.96寸IIC接口的OLED为例,向大家说明如何使用LL库配置STM32的IIC。
注意,本文并非教程,只作为使用STM32 IIC 外设的一些补充说明,关于IIC接口和OLED的一些相关知识在此不作说明。
LL库相对于HAL的优点别的不说,就一个字:快!!!,我使用公司的烂电脑编译一个HAL库的工程怎么说也得要两三分钟,使用LL库时可以缩减到30S左右(虽然还是很慢,公司给配的电脑辣鸡没法子)。
使用LL库的前提是需要我们拥有一个LL库的工程(这不是废话吗),最简单的方法是通过STM32CubeMX新建一个。使用STM32CubeMX新建时也没啥要求,配置好时钟就行,至于IIC的相应配置,我们直接通过代码配置就好。就是记得在最后在生成工程的时候把HAL切换为LL。
不懂的可以参考这篇帖子
基于STM32CubeMX的LL库学习记录(二)建立一个工程
下面开始贴代码
IIC引脚初始化
void I2C_GPIO_Init(void)
{
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB); //GPIO Ports Clock Enable
LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_8|LL_GPIO_PIN_9); //设置PB8 PB9 输出1
\
/*引脚功能设置*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_8|LL_GPIO_PIN_9; //引脚
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; //复用模式
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; //输出速度:最高
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; //开漏输出
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP; //上拉使能
LL_GPIO_Init(GPIOB, &GPIO_InitStruct); //写入配置
LL_GPIO_SetAFPin_8_15(GPIOB,LL_GPIO_PIN_8,LL_GPIO_AF_4); //引脚复用设置
LL_GPIO_SetAFPin_8_15(GPIOB,LL_GPIO_PIN_9,LL_GPIO_AF_4);
}
使用的引脚为PB8 和PB9,所以这里是对引脚的初始化设置。LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_8|LL_GPIO_PIN_9);配置前把引脚置1,可以避免配置好后IIC总线被拉低(至于为什么会被拉低,我也不知道),另外我的IIC设备是直接通过杜邦线与stm32连接,没有上拉电阻,因此设置了引脚内部的上拉电阻,如果有外部上拉的话可以取消引脚内部上拉电阻。
IIC初始化
#define OLED_ADDRESS 0x78
void IIC_Init(void)
{
LL_I2C_InitTypeDef I2C_InitStructure = {0};
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); //IIC时钟使能
I2C_InitStructure.PeripheralMode = LL_I2C_MODE_I2C;
I2C_InitStructure.OwnAddress1 = OLED_ADDRESS;
I2C_InitStructure.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
I2C_InitStructure.TypeAcknowledge = LL_I2C_ACK;
I2C_InitStructure.DutyCycle = LL_I2C_DUTYCYCLE_2;
I2C_InitStructure.ClockSpeed = LL_I2C_MAX_SPEED_FAST;
LL_I2C_Init(I2C1,&I2C_InitStructure);
}
这一段配置IIC的主从模式(主机模式),地址(OLED不会对主机有读操作,所以地址就随便设了,这里设置为和OLED的地址一致),寻址模式(7位寻址),应答模式(应答使能),通信速度(400K)等。可以的话最好对着数据手册过一遍。
IIC 写数据
void I2C_WriteByte(unsigned char addr,unsigned char data)
{
while(LL_I2C_IsActiveFlag_BUSY(I2C1)); //判断总线是否忙碌
LL_I2C_GenerateStartCondition(I2C1); //START 1
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //读SR1->SB位,判断起始位是否发送
I2C_Send7bitAddress(I2C1,OLED_ADDRESS,I2C_Direction_Transmitter); //发送从设备地址
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //等待EV6完成;读SR1、SR2
LL_I2C_TransmitData8(I2C1,addr); //从设备的寄存器地址
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
LL_I2C_TransmitData8(I2C1,data); //发送数据
while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
LL_I2C_GenerateStopCondition(I2C1); //停止位
// uint16_t temp = 0;
// while(I2C1->SR2 & 0x0002); //判断总线是否忙碌
// LL_I2C_GenerateStartCondition(I2C1); //START 1
// while(!(I2C1->SR1 & 0x0001)); //读SR1->SB位,判断起始位是否发送
// I2C_Send7bitAddress(I2C1,OLED_ADDRESS,I2C_Direction_Transmitter); //发送从设备地址
// while(!(I2C1->SR1 & 0x0002)); //地址发送结束
// temp = I2C1->SR2; //读SR2
// LL_I2C_TransmitData8(I2C1,addr); //从设备的寄存器地址
// while(!(I2C1->SR1 & 0x0080));
// LL_I2C_TransmitData8(I2C1,data); //发送数据
// while(!(I2C1->SR1 & 0x0080));
// I2C1->CR1 |= 0x0200; //停止位
// LL_I2C_GenerateStopCondition(I2C1); //停止位
}
没注释的是野火开发板的代码,注释掉的是我写的,都能用。使用野火那段的话需要把野火的一些代码拷贝到自己的工程上。代码如下
#define I2C_Direction_Transmitter ((uint8_t)0x00) //发送模式
#define I2C_Direction_Receiver ((uint8_t)0x01) //接收模式
/*IIC 事件*/
/* --EV5 : 起始位已发送...*/
#define I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */
/* --EV6 : 发送/接收完成...*/
#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* 发送 BUSY, MSL, ADDR, TXE and TRA flags */
#define I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ((uint32_t)0x00030002) /* 接收 BUSY, MSL and ADDR flags */
/* Master RECEIVER mode -----------------------------*/
/* --EV7 */
#define I2C_EVENT_MASTER_BYTE_RECEIVED ((uint32_t)0x00030040) /* BUSY, MSL and RXNE flags */
/* Master TRANSMITTER mode --------------------------*/
/* --EV8 */
#define I2C_EVENT_MASTER_BYTE_TRANSMITTING ((uint32_t)0x00070080) /* TRA, BUSY, MSL, TXE flags */
/* --EV8_2 */
#define I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */
/*=========================================================================================*/
void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)
{
/* Test on the direction to set/reset the read/write bit */
if (I2C_Direction != I2C_Direction_Transmitter)
{
/* Set the address bit0 for read */
Address |= I2C_OAR1_ADD0;
}
else
{
/* Reset the address bit0 for write */
Address &= (uint8_t)~((uint8_t)I2C_OAR1_ADD0);
}
/* Send the address */
I2Cx->DR = Address;
}
uint8_t I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t lastevent = 0;
uint32_t flag1 = 0, flag2 = 0;
uint8_t status = 0;
/* Read the I2Cx status register */
flag1 = I2Cx->SR1;
flag2 = I2Cx->SR2;
flag2 = flag2 << 16;
/* Get the last event value from I2C status register */
lastevent = (flag1 | flag2) & ((uint32_t)0x00FFFFFF);
/* Check whether the last event contains the I2C_EVENT */
if ((lastevent & I2C_EVENT) == I2C_EVENT)
{
/* SUCCESS: last event is equal to I2C_EVENT */
status = 1;
}
else
{
/* ERROR: last event is different from I2C_EVENT */
status = 0;
}
/* Return status */
return status;
}
至此本文结束,点击此处可下载相应工程代码,文件中包含野火开发板的工程代码和本文的工程代码