STM32 软件IIC 控制OLED 显示屏

1. 硬件IIC 实在是太难用了,各种卡死,各种发不出来数据,没那么多时间折腾了,还是用软件IIC 先吧,初始化

复制代码
void OLED_Software_IIC_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    RCC_AHBPeriphClockCmd(OLED_SOFTWARE_IIC_PORT_B | OLED_SOFTWARE_IIC_AF_CLOCK, ENABLE);

    GPIO_InitStruct.GPIO_Pin = OLED_IIC_SDA_PIN_B11 | OLED_IIC_SCL_PIN_B10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_40MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct); // PB11 - SDA, PB10 - SCL

    GPIO_SetBits(GPIOB, OLED_IIC_SDA_PIN_B11 | OLED_IIC_SCL_PIN_B10);

    OLED_IIC_Stop();
}

2. 一些宏定义,带FreeRTOS 实时操作系统,系统节拍设置为1ms,系统主频设置为32Mhz

复制代码
#define OLED_USE_SOFTWARE_IIC

#define OLED_SOFTWARE_IIC_PORT_B    RCC_AHBPeriph_GPIOB // SCL - PB10, SDA -PB11
#define OLED_SOFTWARE_IIC_AF_CLOCK    RCC_APB1Periph_I2C2

#define IIC_TIMEOUT_COUNTER    0x2000 // iic transmit timeout

#define OLED_IIC_SCL_PIN_B10    GPIO_Pin_10
#define OLED_IIC_SDA_PIN_B11    GPIO_Pin_11
#define OLED_SCL_H()    GPIO_SetBits(GPIOB, OLED_IIC_SCL_PIN_B10)
#define OLED_SCL_L()    GPIO_ResetBits(GPIOB, OLED_IIC_SCL_PIN_B10)

#define OLED_ADDRESS    0x78 // 0x78: device address + write, 0x79 - device address + read
#define IIC_CMD_LEN    2

#define OLED_SDA_H()    GPIO_SetBits(GPIOB, OLED_IIC_SDA_PIN_B11)
#define OLED_SDA_L()    GPIO_ResetBits(GPIOB, OLED_IIC_SDA_PIN_B11)

#define OLED_SDA_READ()    GPIO_ReadInputDataBit(GPIOB, OLED_IIC_SDA_PIN_B11)

3. 延时函数,CPU 阻塞,空转

复制代码
static void IIC_Delay(void)
{
   uint8_t temp;

   for (temp = 0; temp < 10; temp++)
   {
       // do nothing
   }
}

4. 起始信号

复制代码
static void OLED_IIC_Start(void)
{
    OLED_SDA_H();
    OLED_SCL_H();

    IIC_Delay();
    OLED_SDA_L();
    IIC_Delay();
    OLED_SCL_L();
    IIC_Delay();
}

5. 停止信号

复制代码
static void OLED_IIC_Stop(void)
{
   OLED_SDA_L();
   OLED_SCL_H();
   IIC_Delay();
   OLED_SDA_H();
   IIC_Delay();
}

6. 发送一个字节的数据

复制代码
static void OLED_IIC_SendByte(uint8_t data)
{
   uint8_t index;
   GPIO_InitTypeDef GPIO_InitStruct;

   for (index = 0; index < 8; index++) // send one byte data, first send MSB (bit[7]), send LSB at the last (bit[0])
   {
       if (data & 0x80)
       {
           OLED_SDA_H(); // send 1
       }
       else
       {
           OLED_SDA_L(); // send 0
       }
       IIC_Delay();
       OLED_SCL_H();
       IIC_Delay();
       OLED_SCL_L();
       data <<= 1; // send next bit (MSB -> LSB)
       IIC_Delay();
   }
//    OLED_SDA_H();
   GPIO_InitStruct.GPIO_Pin = OLED_IIC_SDA_PIN_B11;
   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
   GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;

   GPIO_Init(GPIOB, &GPIO_InitStruct);

   IIC_Delay();
}

7. 发送一个字节数据后等待从机回复一个ACK 信号

复制代码
uint8_t OLED_IIC_WaitAck(void)
{
   uint8_t ret;

   OLED_SCL_H();
   if (OLED_SDA_READ())
   {
       ret = 1; // not received ACK signal, wait fail
   }
   else
   {
       ret = 0; // received ACK signal, wait success
   }
   IIC_Delay();
   OLED_SCL_L();
   IIC_Delay();

   return ret;
}

8. GPIO 重新初始化

复制代码
static void OLED_SoftwareIIC_SDA_Resume(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;

    GPIO_InitStruct.GPIO_Pin = OLED_IIC_SDA_PIN_B11;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_40MHz;
    GPIO_Init(GPIOB, &GPIO_InitStruct); // PB11 - SDA
}

9. 模仿HAL库写的一个自己的函数,实现功能,发送设备地址+读写控制,再发送0x00,表示发的是命令,再发一个字节数据,具体的内容,数据,共发了三个字节的数据,加了超时退出机制

复制代码
/*
 * imitate the stm32 HAL library, achieve myself's software i2c send function
 * deviceAddr: 0x78 - device address and write operation, 0x79 - device address and read operation
 */
static void HAL_I2C_Master_Transmit(I2C_TypeDef *I2Cx, uint8_t deviceAddr, uint8_t buff[], uint16_t buffLen, uint32_t timeout)
{
    uint32_t temp;
    uint16_t index;

    temp = timeout;

    OLED_IIC_Start();

    IIC_Delay();
    OLED_IIC_SendByte(deviceAddr); // device address and write bit

    while (OLED_IIC_WaitAck())
    {
        temp--;
        if (0 == temp)
        {
            return;
        }
        IIC_Delay();
    }

    OLED_SoftwareIIC_SDA_Resume();
    for (index = 0; index < buffLen; index++)
    {
        OLED_IIC_SendByte(buff[index]);
        IIC_Delay();
        while (OLED_IIC_WaitAck())
        {
            temp--;
            if (0 == temp)
            {
                return;
            }
            IIC_Delay();
        }
        OLED_SoftwareIIC_SDA_Resume();
        IIC_Delay();
    }

    OLED_IIC_Stop();
}

10. 向OLED 发送指令函数

复制代码
static void OLED_SendCmd(uint8_t cmd)
{
    uint8_t sendBuff[2];

    sendBuff[0] = 0x00;
    sendBuff[1] = cmd;

    HAL_I2C_Master_Transmit(I2C2, OLED_ADDRESS, sendBuff, IIC_CMD_LEN, IIC_TIMEOUT_COUNTER);
}

11. OLED 初始化函数,OLED 用的龙科显示公式的产品,控制IC 用的是CH1116G,他们给我发是资料却是SH1106的

复制代码
void OLED_Init(void)
{
    delay_xms(200); // oled startup slowly than stm32l151c8t6
    INFO_LOG("[OLED_Init] init start\r\n");

    OLED_SendCmd(0xAE); // display off

    OLED_SendCmd(0x02); // set colum start address, low 4-bits
    OLED_SendCmd(0x10); // set colum end address, high 4-bits

    OLED_SendCmd(0x40); // set start line (first row)
    OLED_SendCmd(0xB0); // set page address

    delay_xms(5);
    OLED_SendCmd(0x81); // set contrast ratio (对比度)
    delay_xms(5);
    OLED_SendCmd(0xCF); // 128
    delay_xms(5);

    OLED_SendCmd(0xA1); // set segment remapping, from right to left

    OLED_SendCmd(0xA6); // forward display, normal or reverse

    OLED_SendCmd(0xA8); // multiple reuse rate, multiple ratio (多路复用率)
    OLED_SendCmd(0x3F); // duty = 1 / 64

    OLED_SendCmd(0xAD); // set charge pump enable
    OLED_SendCmd(0x8B); // enable DC-DC

    OLED_SendCmd(0x33); // set VPP = 10V

    OLED_SendCmd(0xC8); // set output scan direction, COM[N - 1] to COM[0], COM scan direction

    OLED_SendCmd(0xD3); // set display offset
    OLED_SendCmd(0x00); // 0x00

    OLED_SendCmd(0xD5); // set internal clock frequence, set osc frequency
    OLED_SendCmd(0xC0);

    OLED_SendCmd(0xD9); // set pre-charge period
    OLED_SendCmd(0x1F); // 0x22

    OLED_SendCmd(0xDA); // set COM pins, pin layout
    OLED_SendCmd(0x12);

    OLED_SendCmd(0xDB); // set electrical level, set VCOMH
    OLED_SendCmd(0x40);

    OLED_SendCmd(0xAF); // enable display, display on

    INFO_LOG("[OLED_Init][alfred01] init complete\r\n");
    INFO_LOG("[OLED_Init] init complete\r\n");
}

12. OLED 测试函数

复制代码
void OLED_Test(void)
{
    OLED_SendCmd(0xB0); // page 0
    OLED_SendCmd(0x02); // colume 0, low 4-bits
    OLED_SendCmd(0x10); // colume 0, high 4-bits

    uint8_t sendBuff[] = {0x40, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
    HAL_I2C_Master_Transmit(I2C2, OLED_ADDRESS, sendBuff, sizeof(sendBuff), IIC_TIMEOUT_COUNTER);
}

13. OLED 初步显示效果,表示控制驱动是OK的了,其它的就是慢慢细调了

14. OLED 规格书,控制芯片用的CH1116G,这是硬件发给我,和厂家给的不一致,厂家说是兼容的,那我就直接上手调了

15. 看这三个引脚接的是高电平还是低电平,怎么组合的,就知道用的什么接口,需要看原理图了

需要看原理图了

16. 逻分仪抓的OLED 初始化时序图

相关推荐
国科安芯12 小时前
航天器多路并联大功率电源系统设计与ASP4644均流特性分析
单片机·嵌入式硬件·fpga开发·安全性测试
茯苓gao13 小时前
嵌入式开发笔记:CANopen相关移位运算与通信协议术语详解
网络·嵌入式硬件·学习·信息与通信
梁朝辉16 小时前
STM32H750VBT6中ADCINP与INN什么区别
stm32·单片机·嵌入式硬件
省四收割者17 小时前
一文详解信号完整性(1)
python·嵌入式硬件·数学建模·信息与通信·信号处理·智能硬件
崇山峻岭之间18 小时前
单片机无感FOC驱动之ADC
单片机·嵌入式硬件
Ligocious18 小时前
1.点亮一颗小小的LED
单片机·嵌入式硬件
KaifuZeng1 天前
通信与接口协议面试八、CAN通信
单片机·嵌入式硬件·面试·通信与接口协议
智源单片机设计1 天前
基于单片机的直流电机双闭环调速系统设计
单片机·嵌入式硬件
kebidaixu1 天前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片详解
stm32
kebidaixu1 天前
两轮车换电 BMS 的模拟前端中颖 SH367306 AFE 芯片调试实录:四个关键问题与解决方案
stm32