STM32硬件I2C驱动0.96寸OLED屏幕

0.96寸OLED屏幕

0.96寸:屏幕对角线长0.96英寸

驱动:SSD1306驱动IC,驱动芯片内置128*64的SRAM存储器,用于缓存要显示的数据

分辨率:128*64(横128,竖64)

供电:3.3V(驱动内部内置升压电路,会将3.3升到7),需要与stm32共地(GND接在stm32上)

针脚:4脚(GND、VCC、SCL、SDA)

从机地址:0x78

指令操作前缀:

                       先发0x00表示后面的内容是对SSD1306驱动的控制命令;

                       先发0x40表示后面的内容是要显示的数据

显示流程:

        页列显示,每列会以从上到下(上面是低位,下面的高位)的方式显示,显示8bit后自动换到右边的列,重复流程。

        超过第127列后自动从本页开头覆盖显示,不自动跳页。1亮,0暗。

显示操作:对目标列进行显示,需要写命令操作。

 对页操作:命令高五位是10110(0xB0)时,命令低三位则是页(8行)地址。页地址范围是0~8。

 对列操作:  命令高四位为0001(0x10)时,命令低四位则为x坐标的高四位。命令高四位为0000(0x00)时,命令低四位则为x坐标的低四位。

硬件I2C

STM32F106C8T6的I2C资源:I2C1、I2C2。

        I2C1的SCL引脚是GPIOB_Pin_6,I2C1的SDA引脚是GPIOB_Pin_7。

        I2C1的SCL引脚是GPIOB_Pin_10,I2C1的SDA引脚是GPIOB_Pin_11。

引脚配置:开漏输出

输出:SDA、SCL

工作流程:

        启动+等待启动——>7位地址+读写操作(0写1读+)+等待发送完成——>传输数据.+等待传输数据完成....——>停止

数据传输方式:高位先行

工作原理:

                启动条件:SCL高时,下拉SDA

                停止条件:SCL高时,上拉SDA

                发送数据:在完成启动条件后,在SCL高电平时,会发送SDA的数据(0/1);在SCL低电平时,更改SDA上的数据(0/1)。发送方发送完一位数据(8bit)后,需要接收方发送<接收应答>(0/1,0应答,1不应答),然后发送发接收这个应答位来判断发送的数据是否被读取。

                硬件资源特质:硬件I2C的收发不需要人为注意应答位,但需要等待各个阶段操作后的标志位生成,这类似于等待软件的应答位。

细节注意:

                SCL由STM32控制,所以就算是从机要发送应答,也是由STM32改变SCL的电平去加载数据,同时需要STM32去释放对SDA的控制(拉高SDA)

                当STM32是发送方时,STM32在SCL高电平时发送SDA的数据,而从机在这个时候读取SDA的数据;当从机是发送方时,而OLED在SCL高电平时发送的数据,STM32在SCL时接收SDA的数据;

                当STM32是接收方时,STM32发送的应答位(0继续发/1别发了);与从机是接收方是,从机发送的应答位(0收到了/1没收到)的含义不同。

软件模拟和硬件实现的区别:

 软件:由个人自己写函数(具体看STM32软件模拟I2C驱动0.96寸OLED屏幕-优快云博客

               ① 启动条件

               ② 发送从机地址+读写

                ③ 发送数据/指令

                 ④终止条件

 硬件:使用已有的函数,一切等待的格式都是:while( (I2Cx,等待事件的名称) == ERROR )

①判断总线是否忙(while死循环)

函数:

        while (I2C_GetFlagStatus(I2C2,  I2C_FLAG_BUSY)); 

②启动条件+等待启动完成

函数:  

         I2C_GenerateSTART (I2C2, ENABLE);  
         while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

③从机地址+写入+等待发送完成

(0x78是OLED的从机地址,I2C_Direction_Transmitter是发送模式,即写入模式)

函数:

         I2C_Send7bitAddress(I2C2,0X78,I2C_Direction_Transmitter);
        while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);

④指令地址/数据地址+等待发送完成

(根据传参addr来判断  <后面输入的数据data> 是  <对SSD1306操作的指令>   还是   <OLED要显示的内容>)

函数:
        I2C_SendData(I2C2,addr);
        while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR);//等待数据的发送完成

⑤data+等待发送完成

(根据前面addr的操作地址  来确认data   是  <对SSD1306操作的指令>   还是   <OLED要显示的内容>)

函数:
        I2C_SendData(I2C2,data);
        while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);

⑥结束

函数:

            I2C_GenerateSTOP (I2C2,ENABLE);

主要代码     

硬件I2C及其GPIO口的初始化

void MyI2C_Init(void)
{
    /*开启时钟*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);        //开启I2C2的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);        //开启GPIOB的时钟
    
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);                    //将PB10和PB11引脚初始化为复用开漏输出
    
    /*I2C初始化*/
    I2C_InitTypeDef I2C_InitStructure;                        //定义结构体变量
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;                //模式,选择为I2C模式
    I2C_InitStructure.I2C_ClockSpeed = 50000;                //时钟速度,选择为50KHz
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;        //时钟占空比,选择Tlow/Thigh = 2
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;                //应答,选择使能
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;    //应答地址,选择7位,从机模式下才有效
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;                //自身地址,从机模式下才有效
    I2C_Init(I2C2, &I2C_InitStructure);                        //将结构体变量交给I2C_Init,配置I2C2
    
    /*I2C使能*/
    I2C_Cmd(I2C2, ENABLE);                                    //使能I2C2,开始运行
}

字节发送
void OLED_Byte_Write(uint8_t addr, uint8_t data)
{

    while (I2C_GetFlagStatus(I2C2,  I2C_FLAG_BUSY));  //检查I2C总线是否繁忙
    
    I2C_GenerateSTART (I2C2, ENABLE);    //产生起始信号
    while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT) == ERROR);
    
    //EV5事件被检测,发送设备地址
    I2C_Send7bitAddress(I2C2,0X78,I2C_Direction_Transmitter);//第几个i2c  i2c通信的从机地址  写/发送模式(模式选择0写/1读)
    while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR);//等待  7bit从机地址+1位读写操作  的指令发送完成
    
    //EV6事件被检测,发送要操作的存储单元地址
    I2C_SendData(I2C2,addr);//根据传参addr来判断  <后面输入的数据data> 是  <对SSD1306操作的指令>   还是   <OLED要显示的内容>
    while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR);//等待数据的发送完成

    //EV8事件被检测,发送要存储的数据
    I2C_SendData(I2C2,data);//根据前面addr的操作地址  来确认data   是  <对SSD1306操作的指令>   还是   <OLED要显示的内容>
    while(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR);//等待数据的发送完成

    //数据传输完成
    I2C_GenerateSTOP (I2C2,ENABLE);//结束发送
}

发送命令/数据

void OLED_WriteCommand(uint8_t     IIC_Command)
{
    OLED_Byte_Write(0x00, IIC_Command);//先发0x00表示后面的内容是对SSD1306驱动的控制命令;
}

void OLED_WriteData(uint8_t     IIC_Data)
{
    OLED_Byte_Write(0x40, IIC_Data);//先发0x40表示后面的内容是要显示的数据

}

指定页列显示

void OLED_SetCursor(uint8_t pag, uint8_t x)
{
    //命令高五位是10110时,命令低三位则是页地址。页地址范围是0~8
    OLED_WriteCommand(0xB0 | pag);//取出数据低四位
    
    //命令高四位为0001时,命令低四位则为x坐标的高四位。
    OLED_WriteCommand(0x10 | (x & 0xF0) >> 4);//取出数据高四位
    //命令高四位为0000时,命令低四位则为x坐标的低四位
    OLED_WriteCommand(0x00 | (x & 0x0F));//取出数据低四位
}

OLED初始化(参考自江科大,但是多次运行后显示起点终点有误,且会自动换页

void OLED_Init()
{
    uint32_t i, j;
    
    for (i = 0; i < 1000; i++)
        for (j = 0; j < 1000; j++);//上电延时
    
    MyI2C_Init();            //GPIO端口初始化
    
    OLED_WriteCommand(0xAE);    //关闭显示
    
    OLED_WriteCommand(0xD5);    //设置显示时钟分频比/振荡器频率
    OLED_WriteCommand(0x80);
    
    OLED_WriteCommand(0xA8);    //设置多路复用率
    OLED_WriteCommand(0x3F);
    
    OLED_WriteCommand(0xD3);    //设置显示偏移
    OLED_WriteCommand(0x00);
    
    OLED_WriteCommand(0x40);    //设置显示开始行
    
    OLED_WriteCommand(0xA1);    //设置左右方向,0xA1正常 0xA0左右反置
    
    OLED_WriteCommand(0xC8);    //设置上下方向,0xC8正常 0xC0上下反置

    OLED_WriteCommand(0xDA);    //设置COM引脚硬件配置
    OLED_WriteCommand(0x12);
    
    OLED_WriteCommand(0x81);    //设置对比度控制
    OLED_WriteCommand(0xCF);

    OLED_WriteCommand(0xD9);    //设置预充电周期
    OLED_WriteCommand(0xF1);

    OLED_WriteCommand(0xDB);    //设置VCOMH取消选择级别
    OLED_WriteCommand(0x30);

    OLED_WriteCommand(0xA4);    //设置整个显示打开/关闭

    OLED_WriteCommand(0xA6);    //设置正常/倒转显示

    OLED_WriteCommand(0x8D);    //设置充电泵
    OLED_WriteCommand(0x14);

    OLED_WriteCommand(0xAF);    //开启显示
    
    for (i = 0; i < 1000; i++)            //上电延时
        for (j = 0; j < 1000; j++);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值