【STM32CubeMX】教程二_IIC驱动0.96oled屏幕(SSD1306)

【STM32CubeMX】教程二_IIC驱动0.96oled屏幕(SSD1306)


前言

本篇博客涉及以下内容

  • IIC通讯原理讲解、代码编写
  • SSD1306驱动芯片datasheet讲解、驱动代码编写、0.96oled模块原理图说明
  • STM32CubeMX配置IIC

使用硬件:STM32F103C8T6
使用软件:STM32CubeMX、keil5(MDK)、VSCode(搭载keil assistant插件)


提示:以下是本篇文章正文内容

一、IIC通讯

  在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设。STM32HAL 库则是在寄存器与用户代码之间的软件层。
  对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。

1.IIC物理层

  IIC(Inter-Integrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器以及其外围设备。它是由数据线 SDA 和时钟线 SCL 构成的串行总线,可发送和接收数据,在 CPU 与被控 IC 之间、IC 与 IC 之间进行双向传送。

  SDA(Serial data)是数据线,D代表Data也就是数据,Send Data 也就是用来传输数据的

  SCL(Serial clock line)是时钟线,C代表Clock 也就是时钟 也就是控制数据发送的时序的

  IIC作为一种低数半双工总线,通信速率比SPI低,常见的半双工还有485,SPI和USART都是全双工。

  全双工:同一时间可以双向通信,即可以同时接收,发送数据。
  半双工:同一时间只能单向通信,即同一时间只能接收或者发送数据。

  常见的IIC通讯接线方式如下图:
在这里插入图片描述
IIC设备分为主设备和从设备,基本上谁控制时钟线(即控制SCL的电平高低变换)谁就是主设备

它的物理层有如下特点:

(1) 它是一个支持设备的总线。“总线”指多个设备共用的信号线。在一个 I2C 通讯总线中,可连接多个 IIC 通讯设备,支持多个通讯主机及多个通讯从机。

  主机从机概念:
  主机就是负责整个系统的任务协调与分配.发布主要命令的称为主机。
  从机一般是通过接收主机的指令从而完成某些特定的任务,主机和从机之间通过总线连接,进行数据通讯。

(2) 一个 IIC 总线只使用两条总线线路,一条双向串行数据线 (SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。

(3) 每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。

(4) 总线通过上拉电阻接到电源。当 IIC 设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。

(5) 多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。

(6) 具有三种传输模式:标准模式传输速率为 100kbit/s ,快速模式为 400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多 IIC 设备尚不支持高速模式。

(7) 连接到相同总线的 IC 数量受到总线的最大电容 400pF 限制。

2.IIC协议层

 学习IIC协议之前,我们先看一下它的总线时序图
在这里插入图片描述
 IIC 的协议定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。

  • ① 起始信号
      当 SCL 为高电平期间,SDA 由高到低的跳变。起始信号是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在起始信号产生后,总线就处于被占用状态,准备数据传输。

代码如下:

void iic_start(void)
{
    IIC_SDA(1);     /* SDA置高 */
    IIC_SCL(1);     /* SCL置高 */
    iic_delay();
    IIC_SDA(0);     /* START信号: 当SCL为高时, SDA从高变成低, 表示起始信号 */
    iic_delay();
    IIC_SCL(0);     /* 钳住I2C总线,准备发送或接收数据 */
    iic_delay();
}
  • ② 停止信号
      当 SCL 为高电平期间,SDA 由低到高的跳变。停止信号也是一种电平跳变时序信号,而不是一个电平信号。该信号由主机发出,在停止信号发出后,总线就处于空闲状态。

代码如下:

void iic_stop(void)
{
    IIC_SDA(0);     /* STOP信号: 当SCL为高时, SDA从低变成高, 表示停止信号 */
    iic_delay();
    IIC_SCL(1);
    iic_delay();
    IIC_SDA(1);     /* 发送I2C总线结束信号 */
    iic_delay();
}
  • ③ 数据传输
      在 I2C 总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在 SCL 串行时钟的配合下,在 SDA 上逐位地串行传送每一位数据。数据位的传输是边沿触发。

  • 数据有效性
      IIC 使用 SDA 信号线来传输数据,使用 SCL 信号线进行数据同步。IIC 总线进行数据传送时,SCL 为高电平的时候 SDA 表示的数据有效,即此时的 SDA 为高电平时表示数据“1”,为低电平时表示数据“0”。当 SCL 为低电平时,SDA 的数据无效,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化.数据在 SCL 的上升沿到来之前就需准备好。并在下降沿到来之前必须稳定。
    在这里插入图片描述
      每次数据传输都以字节为单位,每次传输的字节数不受限制。

  • ⑤ 应答信号
      发送方每发送一个字节(一个字节8位),由接收方反馈一个应答信号(在第9个时钟信号期间),包括“应答 (ACK)”和“非应答 (NACK)”两种信号。应答信号为低电平时,规定为有效应答位(ACK 简称应答位),表示接收方已经成功地接收了该字节,发送方会继续发送下一个数据;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功,发送方接收到该信号后会产生一个停止信号,结束信号传输。
    在这里插入图片描述
      传输时主机产生时钟,在第 9 个时钟时,数据发送端会释放 SDA 的控制权,由数据接收端控制SDA,若 SDA 为高电平,表示非应答信号 (NACK),低电平表示应答信号 (ACK)。

代码如下:

void iic_ack(void)
{
    IIC_SDA(0);     /* SCL 0 -> 1 时 SDA = 0,表示应答 */
    iic_delay();
    IIC_SCL(1);     /* 产生一个时钟 */
    iic_delay();
    IIC_SCL(0);
    iic_delay();
    IIC_SDA(1);     /* 释放SDA线 */
    iic_delay();
}
  • ⑥ 空闲状态
      IIC 总线的 SDA 和 SCL 两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。

  • ⑦ 读写通讯过程
      I2C 总线上的每个设备都有自己的独立地址,主机发起通讯时,通过 SDA 信号线发送设备地址(SLAVE_ADDRESS) 来查找从机。I2C 协议规定设备地址可以是 7 位或 10 位,实际中 7 位的地址应用比较广泛。紧跟设备地址的一个数据位用来表示数据传输方向,它是数据方向位 (R/w),第 8位或第 11 位。数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机写数据。
    在这里插入图片描述
      IIC 的基本的读写通讯过程,包括主机写数据到从机即写操作,主机到从机读取数据即读操作。下面先看一下写操作通讯过程图
    在这里插入图片描述
      主机首先在 IIC 总线上发送起始信号,那么这时总线上的从机都会等待接收由主机发出的
    数据。主机接着发送从机地址+0(写操作)组成的 8/11bit 数据,所有从机接收到该 8/11bit 数据后,自行检验是否是自己的设备的地址,假如是自己的设备地址,那么从机就会发出应答信号。主机在总线上接收到有应答信号后,才能继续向从机发送数据。
      注意:IIC 总线上传送的数据信号是广义的,既包括地址信号,又包括真正的数据信号。

   写数据
  若配置的方向传输位为“写数据”方向。如下图:广播完地址,接收到应答信号后,主机开始正式向从机传输数据 (DATA),数据包的大小为 8 位,主机每发送完一个字节数据,都要等待从机的应答信号 (ACK),重复这个过程,可以向从机传输 N 个数据,这个 N 没有大小限制。当数据传输结束时,主机向从机发送一个停止传输信号 §,表示不再传输数据。
在这里插入图片描述
代码如下:

uint8_t iic_read_byte(uint8_t ack)
{
    uint8_t i, receive = 0;

    for (i = 0; i < 8; i++ )    /* 接收1个字节数据 */
    {
        receive <<= 1;  /* 高位先输出,所以先收到的数据位要左移 */
        IIC_SCL(1);
        iic_delay();

        if (IIC_READ_SDA)/* 接收到高电平‘1’ */
        {
            receive++;
        }
        
        IIC_SCL(0);
        iic_delay();
    }

    if (!ack)
    {
        iic_noack();     /* 发送nACK */
    }
    else
    {
        iic_ack();      /* 发送ACK */
    }

    return receive;
}

   读数据
  若配置的方向传输位为“读数据”方向。如下图:广播完地址,接收到应答信号后,从机开始向主机返回数据 (DATA),数据包大小也为 8 位,从机每发送完一个数据,都会等待主机的应答信号 (ACK),重复这个过程,可以返回 N 个数据,这个 N 也没有大小限制。当主机希望停止接收数据时,就向从机返回一个非应答信号 (NACK),则从机自动停止数据传输。
在这里插入图片描述


void iic_send_byte(uint8_t data)
{
    uint8_t t;
    
    for (t = 0; t < 8; t++)/* 一个字节8位,发送一个字节需要八个时钟周期 */
    {
        IIC_SDA((data & 0x80) >> 7);    /* 高位先发送 */
        iic_delay();
        IIC_SCL(1);
        iic_delay();
        IIC_SCL(0);
        data <<= 1;     /* 左移1位,用于下一次发送 */
    }
    IIC_SDA(1);         /* 发送完成, 主机释放SDA线,检测接收端应答信号 */
}

   读写数据
  除了基本的读写,I2C 通讯更常用的是复合格式。如下图该传输过程有两次起始信号 (S)。一般在第一次传输中,主机通过 SLAVE_ADDRESS 寻找到从设备后,发送一段“数据”,这段数据通常用于表示从设备内部的寄存器或存储器地址 (注意区分它与 SLAVE_ADDRESS 的区别);在第二次的传输中,对该地址的内容进行读或写。也就是说,第一次通讯是告诉从机读写地址,第二次则是读写的实际内容。以上通讯流程中包含的各个信号分解如下:
在这里插入图片描述

3.STM32的 II 特性及架构

3.1 软件IIC

  如果我们直接控制 STM32 的两个 GPIO 引脚,分别用作 SCL 及 SDA,按照上述信号的时序要求,直接像控制 LED 灯那样控制引脚的输出 (若是接收数据时则读取 SDA 电平),就可以实现 I2C 通讯。由于直接控制 GPIO 引脚电平产生通讯时序时,需要由 CPU 控制每个时刻的引脚状态,所以称之为“软件模拟协议”方式。优点就是不管是 ST 生产的控制器还是 ATMEL 生产的存储器,都能按通讯标准交互。

3.2 硬件IIC

STM32 的 I2C 片上外设专门负责实现 I2C 通讯协议,只要配置好该外设,它就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU 只要检测该外设的状态和访问数据寄存器,就能完成数据收发,称之为“硬件协议”方式。优点就是由硬件外设处理 I2C 协议的方式减轻了CPU 的工作,且使软件设计更加简单。

4.软件IIC代码

  本代码适用于HAL库与标准库,两者切换仅需更改iic_init()与头文件。
  软件IIC代码的编写应当具有高度可移植性,方便用于不同款的MCU,上出已近列写部分代码,此处仅列出STM2初始化GPIO代码和.h文件代码。完整代码链接在文章末。

4.1 IIC.c

void iic_init(void)
{
/******************************************************************************************/
    /*HAL库版本*/
    GPIO_InitTypeDef gpio_init_struct;

    IIC_SCL_GPIO_CLK_ENABLE();  /* SCL引脚时钟使能 */
    IIC_SDA_GPIO_CLK_ENABLE();  /* SDA引脚时钟使能 */

    gpio_init_struct.Pin = IIC_SCL_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;        /* 推挽输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                /* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; /* 快速 */
    HAL_GPIO_Init(IIC_SCL_GPIO_PORT, &gpio_init_struct);/* SCL */

    gpio_init_struct.Pin = IIC_SDA_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;        /* 开漏输出 */
    HAL_GPIO_Init(IIC_SDA_GPIO_PORT, &gpio_init_struct);/* SDA */

    /* SDA引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */

/******************************************************************************************/
    /*标准库版本*/
/*
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//ʹÄÜGPIOBʱÖÓ

    //GPIOB7,B8初始化
    GPIO_InitStructure.GPIO_Pin = IIC_SCL_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;       //输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;      //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  //100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉
    GPIO_Init(IIC_SCL_GPIO_PORT, &GPIO_InitStructure);  //SCL

    GPIO_InitStructure.GPIO_Pin = IIC_SDA_GPIO_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;       //输出模式
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;      //开漏输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;  //100MHz
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;        //上拉
    GPIO_Init(IIC_SDA_GPIO_PORT, &GPIO_InitStructure);  //SDA
*/
/******************************************************************************************/

    IIC_SCL(1);     /* 总线空闲状态 */
    IIC_SDA(1);
}

4.2 IIC.h

/*
 ****************************************************************************************************
 * @file        IIC.h
 * @author      橙子太
 * @version     V1.0
 * @date        2023-1-8
 * @brief       软件IIC 驱动代码
 ****************************************************************************************************
 * @attention
 *
 * 实验平台:STM32F103C8T6
 *
 * 修改说明
 * V1.0 2023-1-8
 * 第一次编写
 *
 * 本代码仅用于个人学习使用
 * 
 ****************************************************************************************************
 *@引脚接线
 * PB6---->SCL
 * PB7---->SDL
 * 
 ****************************************************************************************************
 */

#ifndef __IIC_H
#define __IIC_H

#include "stm32f1xx_hal.h"
//#include "stm32f1xx.h" 
/******************************************************************************************/

#ifndef uint32_t
typedef unsigned           char uint8_t;
typedef unsigned short     int  uint16_t;
typedef unsigned           int  uint32_t;
#endif

/******************************************************************************************/
/* 引脚 定义 */

#define IIC_SCL_GPIO_PORT               GPIOB
#define IIC_SCL_GPIO_PIN                GPIO_PIN_6
#define IIC_SCL_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */

#define IIC_SDA_GPIO_PORT               GPIOB
#define IIC_SDA_GPIO_PIN                GPIO_PIN_7
#define IIC_SDA_GPIO_CLK_ENABLE()       do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)   /* PB口时钟使能 */

/******************************************************************************************/

/* IO操作 */
#define IIC_SCL(x)        do{ x ? \
                              HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(IIC_SCL_GPIO_PORT, IIC_SCL_GPIO_PIN, GPIO_PIN_RESET); \
                          }while(0)       /* SCL */

#define IIC_SDA(x)        do{ x ? \
                              HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_SET) : \
                              HAL_GPIO_WritePin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN, GPIO_PIN_RESET); \
                          }while(0)       /* SDA */

#define IIC_READ_SDA     HAL_GPIO_ReadPin(IIC_SDA_GPIO_PORT, IIC_SDA_GPIO_PIN) /* 读取SDA */
/*
IIC_SCL(1);     SCL置高
IIC_SCL(0);     SCL置低
IIC_SDA(1);     SDA置高
IIC_SDA(0);     SDA置低
*/
#endif

二、SSD1306

  在此我们简单学习一下SSD1306这款芯片,通过查看datasheet,了解SSD1306的指令、引脚定义、寻址方式、0.96Oled的原理图,学习如何驱动SSD1306。

2.1 通讯协议选择

在这里插入图片描述
  由图可知SSD1306适用于多种通讯协议,IIC,6800,8080,SPI(三线,四线)都可,由BS0;BS1;BS2三位引脚决定。本篇我们只讨论IIC驱动SSD1306。

2.2 引脚定义

  SSD1306的引脚非常多,这里我们仅介绍重要引脚,电气特征引脚不做介绍,详细引脚请看数据手册。

  • RES#
     &emsp;此引脚是复位信号输入。当引脚被拉时,执行芯片的初始化。在正常操作期间保持此引脚较高(即连接到VDD)。
  • CS#
     &emsp;这个引脚是芯片的选择输入.保持低电平即可
  • D/C#
     &emsp;这是数据/命令控制引脚。当它被拉动HIGH(即连接到VDD)时,D[7:0]处的数据被视为数据。当它被LOW时,D[7:0]的数据将被传输到命令寄存器。在II C模式下,该引脚作为从属地址选择的SA0。当选择三线串行接口时,该引脚必须连接到VSS。
    在这里插入图片描述
      DC=0,IIC下地址为0x78;DC=1;IIC下地址为0x7A,下面会介绍
  • R/W#(WR#)
      这是连接到MCU接口的读/写控制输入引脚。1为读,0为写。
  • D[7:0]
      8位双向数据总线。当选择串行接口模式时,D0为串行时钟输入:SCLK;D1为串行数据输入:SDA,SDIN和D2应保持NC。当选择IIC模式时,D2、D1应该绑定在一起,作为SDA。

2.3 0.96oled屏幕模块

  0.96寸oled显示模块电路图如下,此处使用的模块为IIC通信
在这里插入图片描述

在这里插入图片描述

2.4 SSD1306下的IIC

2.4.1 从机地址

  IIC设备在数据传输之前都必须识别从机地址。SSD1306的从机地址有 0111100b 和 0111101b 两种,通过将SA0(D/C#)脚上拉到高电平可以设置从机地址第七位为 1,将SA0(D/C#)脚下拉到低电平可以设置从机地址第七位为 0。通过SA0(D/C#)脚的上拉和下拉来设置从机地址,从而令总线上可以存在最多2个SSD1306驱动器。由于显示模块只用来写数据,R/W为0,所以从机地址只有01111000 和 01111010 两种
在这里插入图片描述

2.4.2数据读/写

在这里插入图片描述

2.4.3 写入顺序及格式

在这里插入图片描述
  在传输完从属地址后,控制字节或数据字节都可以通过SDA发送。一个控制字节主要由Co和D/C#位组成,后面有六个 “0”。
  如果Co位被设置为逻辑 “0”,则以下信息的传输将只包含数据字节。D/C#位决定了下一个数据字节是作为命令还是数据。
  如果D/C#位被设置为逻辑"0",它定义下一个数据字节为命令。
  如果D/C#位被设置为逻辑"1",则定义下一个数据字节为数据,将被存储在GDDRAM中。

2.5 GDDRAM

  GDDRAM是一个保持要显示的位模式的位映射静态RAM。RAM的大小为128 x 64位,RAM分为8页,从PAGE0到PAGE7,用于单色128 x 64点矩阵显示,就是我们常说的像素点为128x64(横坐标有 128 个像素格,纵坐标有 64 个像素格)。
在这里插入图片描述

  当一个数据字节被写入GDDRAM时,当前列同一页的所有行图像数据都被填充(即列地址指针指向的整个列(8位)被填充)。数据位D0写入上行,数据位D7写入下行。
在这里插入图片描述
  当一个数据字节被写入GDDRAM时,当前列同一页的所有行图像数据都被填充(即列地址指针指向的整个列(8位)被填充)。数据位D0写入上行,数据位D7写入下行。

  简而言之,该显示模块一共64行,可以分为8页(Page0 ~ Page7),每页8行(COM0 ~ COM7,COM8 ~ COM15,…),一共128列(SEG0~SEG127),每次显示时一次性至少显示8位,即一页中的一列,上面的为低位(LSB),下面的为高位(MSB),且低4位为列低地址,高4位为列高地址。一般一个中文字体占 16×16 个像素格,字母以及符号占 8×16或者6x8 个像素格。
在这里插入图片描述

2.6 寻址方式

  通过设置指令,OLED模块可以配置为三种显示模式:页地址模式、水平地址模式、垂直地址模式,其实差别在于显示完本位置后,下一位要在哪显示的问题。

页面寻址模式(A[1:0]=10xb)
在这里插入图片描述
  在页面寻址模式下,读写显示RAM后,列地址指针自动增加1。如果列地址指针到达列结束地址,则列地址指针将被重置为列起始地址,而页面地址指针将不被更改。用户必须设置新的页面和列地址,才能访问下一页的RAM内容。
页面寻址模式设置下低列开始地址(00h~0Fh),页面寻址模式设置高列开始地址(10h~1Fh)

水平地址模式(A[1:0]=00b)
在这里插入图片描述

  在水平寻址模式下,读写显示RAM后,列地址指针自动增加1。如果列地址指针到达列结束地址,则将列地址指针重置为列起始地址,页地址指针增加1。当列和页面地址指针都到达结束地址时,指针将重置为列起始地址和页面起始地址。

垂直地址模式(A[1:0]=01b)
在这里插入图片描述
  在垂直寻址模式下,读写显示RAM后,页面地址指针自动增加1。如果页面地址指针到达页面结束地址,则页面地址指针重置为页面起始地址,列地址指针增加1。当列和页面地址指针都到达结束地址时,指针将重置为列起始地址和页面起始地址。

  需要注意的是,OLED上电复位时的默认状态为页地址模式,而且这种也是最为常用的显示模式,因此可以看到很多OLED的代码中初始化时都没有配置这一项。

2.7 基础指令

  SSD1306的指令非常多,这里我们只介绍几个基础指令,详细说明请看DataSheet。
在这里插入图片描述

2.8 驱动代码编写

2.8.1写数据/指令

  由上文的IIC数据格式可知,当控制字节发送为0x00,代表下一个字节为命令,当控制字节发送为0x40,代表下一个字节为数据,此处我们采用软件IIC方式写入字节,如需使用硬件IIC仅需要在 _Oled_write_Byte()函数中调用STM32硬件IIC函数即可。此处我们从机地址为0x78,这里使用宏定义来设定从机地址,便于阅读和后续修改。

/**
  * @brief  向OLED写入命令
  * @param  iic_command:写入命令
  * @retval 无
*/
void _Oled_Write_Cmd(uint8_t iic_command)
{
    _Oled_Write_Byte(0x00,iic_command);// 0000 0000 ,代表传输的是指令
}

/**
  * @brief  向OLED写入数据
  * @param  iic_data: 写入数据
  * @retval 无
*/
void _Oled_Write_Data(uint8_t iic_data)
{
    _Oled_Write_Byte(0x40,iic_data);// 0100 0000,代表传输的是数据

}

/**
  * @brief  向OLED写入一字节
  * @param  addr:控制字节
  *         data:写入的数据
  * @retval 无
*/
void _Oled_Write_Byte(uint8_t addr,uint8_t data)
{
    _IIc_Start();               //起始信号
    _IIc_Send_Byte(ADDRESS);    //发送从机地址
    _IIc_Wait_Ack();            //等待应答信号
    _IIc_Send_Byte(addr);       //发送控制字节
    _IIc_Wait_Ack();            //等待应答信号
    _IIc_Send_Byte(data);       //发送一个字节
    _IIc_Wait_Ack();            //等待应答
    _IIc_Stop();                //停止信号

}

2.8.2 Oled初始化

  SSD1306指令过多,可以参照下面图发送初始化指令,也可以根据DataSheet自行修改。
在这里插入图片描述
  附上我的初始化代码

uint8_t command[]={
0xAE, 0x20, 0x10, 0xb0, 0xc8, 0x00, 0x10, 0x40, 0x81, 0xff, 0xA1,

0xA6, 0xA8, 0x3F, 0xA4, 0xD3, 0x00, 0xD0, 0xF0, 0xD9, 0x22, 0xDA,

0x12, 0xDB, 0x20, 0x8D, 0x14, 0xAF};

/**
  * @brief  写入初始化指令
  * @param  无
  * @retval 无
*/
void _Oled_Init()
{
    uint8_t i=0;
    HAL_Delay(200);//延时200MS,很重要必须得有
    for(i=0;i<28;i++)
    _Oled_Write_Cmd(command[i]);
    _Oled_Clear();
}

2.8.3功能函数

  成功对Oled屏幕写入字节之后,就可以通过写入指令和数据实现一些简单的功能,比如显示字符,字符串,汉字,图片,绘制点,线、也可以通过发送指令实现滚动显示,屏幕旋转,正反显示等。复杂一点的还可以写OLED菜单;具体实现不过多复述,附上本人的打包文件,代码,文件,教程,数据手册已经打包好了。

  • 字符通常占据6x8、8x16个像素点。
  • 一个汉字大小通常占据16x16个像素点。
  • 显示图片的大小不能超过128x64。

  可以通过pctolcd2002对汉字和图片取模,对于像素过大的图片可以使用imageled缩小。
  具体教程请搜索网络,点击查看imageled教程

2.8.4 演示效果

在这里插入图片描述

3 cubeMX配置硬件IIC

写累了,抽空再写

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值