【硬件IIC】stm32单片机利用硬件IIC驱动OLED屏幕

之前操作OLED屏幕都是用GPIO模拟IIC去驱动,最近打算用硬件IIC去驱动,于是写下这个demo,在这个过程中遇到一点小坑,记录一下,本文章非小白教程,所以只突出踩到的坑点,文章中涉及到的OLED也是网上资料写烂的,所以不懂的同学可以万能的百度。话不多说开始。

目标

使用硬件IIC驱动OLED屏,显示英文字符串“I am Rio”
在这里插入图片描述

完整项目工程链接stm32F103C8T6驱动OLED屏显示字符

同时附上一篇网上找的介绍这个OLED屏的文章
0.96寸OLED(SSD1306)屏幕显示(一)——基础功能介绍

硬件

MCU: stm32f103c8t6

屏幕: 0.96寸OLED(SSD1306)

本次使用的是 gpio 的PB6,PB7脚,这两个脚位可以复用硬件I2C1。

驱动程序

代码中已加入详细注释,放心食用

GPIO和IIC初始化配置

这里遇到第一个坑,就是在配置IO的模式时,一开始设置成推挽输出GPIO_MODE_OUTPUT_PP,因为想着就接一个IIC设备,推挽输出或者开漏输出,都影响不大,结果还说遇到坑了,配置成推挽输出之后,程序会出现卡死现象,debug之后发现程序卡死在函数HAL_I2C_Init(&hi2c1)里; 然后进入到错误HardFault_Handler()中,怀疑是HAL_I2C_Init中的某些设置与设置成推挽输出GPIO_MODE_OUTPUT_PP出现冲突导致硬件错误,具体还未深入研究,欢迎大佬补充。

将GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;后不再出现卡死现象。

I2C_HandleTypeDef hi2c1;
uint16_t slaveAddr = 0x78;	//OLED显示屏的IIC设备地址,改地址为写地址
void oled_gpio_init(void)
{
   
   
    GPIO_InitTypeDef GPIO_InitStruct = {
   
   0};
    __HAL_RCC_GPIOB_CLK_ENABLE();	//使能GPIOB口时钟
    /**I2C1 GPIO Configuration
    PB6     ------> I2C1_SCL
    PB7     ------> I2C1_SDA
    */
    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    
    //当时就是这句导致程序卡死,用HAL库实现硬件IIC时还是乖乖用开漏模式好
//    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;  
    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();
    
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000; // 设置I2C时钟速度为100kHz(可以根据需要调整)
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; // 占空比(通常不需要修改)
    hi2c1.Init.OwnAddress1 = 0; // 主设备通常不需要设置自己的地址
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7位地址模式
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 双地址模式禁用
    hi2c1.Init.OwnAddress2 = 0; // 不使用第二个地址
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 通用调用模式禁用
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 时钟延伸模式禁用
    
    HAL_I2C_Init(&hi2c1);

}
IIC的读和写

这里由于没有理解透HAL库的函数踩到第二个坑,用的这一款OLED在写入命令或者数据时,时序是与从机建立IIC通讯开始信号后,就连续写入control byte + data byte; 比如写数据为 0x40 + data; 写命令是0x00 + cmd;

而HAL库提供的IIC读写函数有好几种,如:

HAL_I2C_Master_Receive(&hi2c1, DevAddress, pData, Size, Timeout);
HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, Timeout);

这一组是用来作为主机时,对从机进行读写操作,而我一开始在往从机写数据的时候,使用的就是HAL_I2C_Master_Transmit函数,将control byte + data byte;分开两次来发送,结果就写失败了。原因是分开两次写的话,HAL_I2C_Master_Transmit每次写完一个byte数据之后,就结束该次通讯,这就相当于没有发送完整的control byte + data byte时序;而是

第一次发送control byte结束;第二次发送data byte结束;所以两次没有一次是完整的组合时序。正确的是应该在一次通讯中发送两个byte数据才行;

所以我将要发送的时序进行组合,然后一次发送两个数据即可

uint8_t dataArr[2] = {0x40, data};
I2C_SendData(slaveAddr, dataArr, 2, 1000);

//对HAL_I2C_Master_Transmit函数进行封装
HAL_StatusTypeDef I2C_SendData(uint16_t DevAddress, uint8_t* pData, uint16_t Size, uint32_t Timeout)
{
   
   
    return HAL_I2C_Master_Transmit(&hi2c1, DevAddress, pData, Size, Timeout);
}

//对HAL_I2C_Master_Receive函数进行封装
HAL_StatusTypeDef I2C_ReceiveData(uint16_t DevAddress, uint8_t* pData, uint16_t Size, uint32_t Timeout)
{
   
   
    return HAL_I2C_Master_Receive(&hi2c1, DevAddress, pData, Size, Timeout);
}

//封装函数,实现对往OLED中写入一个字节数据
void oled_write_data(uint8_t data
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值