文章目录

  • 1. 准备工作介绍
  • 2. OLED驱动代码
  • 3. OLED显示字符串代码
    • 3.1 OLED屏幕显示原理
    • 3.2 OLED初始化代码
    • 3.3 操作显示缓冲区的几个重要函数
    • 3.4 绘制任意像素点函数
    • 3.5 在屏幕上显示一个字符
    • 3.6 显示字符串
  • 4. OLED绘图相关代码
    • 4.1 绘制任意线
    • 4.2 绘制四边形
    • 4.3 绘制三角形
    • 4.4 三角形填充
    • 4.5 绘制圆形
    • 4.6 圆形填充

1. 准备工作介绍

我所使用的是 0.96 寸,I2C 接口的 OLED 屏幕。这款屏幕所使用的驱动芯片是 SSD1306 ,关于这款 OLED 驱动芯片的详细介绍可以参考下面这篇文章的介绍。

SSD1306(OLED驱动芯片介绍)

硬件平台测试平台我使用的是 STM32F407ZGT6 芯片的开发板。开发板和 OLED 屏幕的硬件连接引脚如下:

硬件平台连接描述:SSD1306(屏幕)    |STM32F4xx(开发板)    |DESCRIPTION
VCC             |3.3V                |
GND             |GND                 |
SCL             |PB10                |Serial clock line
SDA             |PB11                |Serial data line

关于 STM32F407 芯片 I2C 外设初始化相关的代码,直接使用 CubeMX 生成即可。

下面关于OLED绘图显示的相关代码,是参考了国外一位大佬的github仓库的,链接如下:

https://github.com/MaJerle/stm32f429

2. OLED驱动代码

我所使用的 OLED 屏幕是 I2C 接口的,首先需要把通过 I2C 接口发送数据给 SSD1306 的驱动代码写好,这样才好实现后面的显示功能的代码。

使用 HAL 库驱动 SSD1306 的代码如下:

/** 函数作用    : 通过I2C接口发送一个字节数据给SSD1306* 参数  reg   : 发送数据之前,需要先发送一个字节数据用于区分发送的是命令还是数据。其中先发送0x00,表示发送命令;先发送0x40,表示发送数据* 参数  data  : 要发送的一字节数据* 返回值      : 无*/
static void SSD1306_I2C_Write(uint8_t reg, uint8_t data)
{uint8_t tmp_array[2] = {reg, data};HAL_I2C_Master_Transmit(&hi2c2, SSD1306_I2C_ADDR, tmp_array, sizeof(tmp_array), 1);
}static void SSD1306_Write_Cmd(uint8_t cmd)
{SSD1306_I2C_Write(0x00, cmd);
}static void SSD1306_Write_Data(uint8_t data)
{SSD1306_I2C_Write(0x40, data);
}static void SSD1306_Write_MultiData(uint8_t *data, uint16_t size)
{uint8_t tmp_array[SSD1306_WIDTH+1] = {0x40, 0};memcpy(&tmp_array[1], data, size);HAL_I2C_Master_Transmit(&hi2c2, SSD1306_I2C_ADDR, tmp_array, sizeof(tmp_array), 10);
}/* 打开显示 */
void SSD1306_ON(void)
{SSD1306_Write_Cmd(0x8D);SSD1306_Write_Cmd(0x14);SSD1306_Write_Cmd(0xAF);
}/* 关闭显示 */
void SSD1306_OFF(void)
{SSD1306_Write_Cmd(0x8D);SSD1306_Write_Cmd(0x10);SSD1306_Write_Cmd(0xAE);
}

3. OLED显示字符串代码

3.1 OLED屏幕显示原理

屏幕是由一个个的像素点构成的,要想让屏幕显示出内容,只要按照规律点亮屏幕像素点即可。

所以,要想让 OLED 显示出文字或者绘图等功能,我们可以通过 I2C 接口不断发送要显示的数据给 SSD1306 驱动芯片即可。这种方法好处是可以节省 MCU 的内存,因为不用在MCU内部使用 RAM 建立一块显存,只是要显示什么内容,立即通过 I2C 接口发送数据过去即可。

还有一种方式是,在 MCU 内部建立一块用于显示的缓存,之后我们要显示什么内存,先修改这块显存的数据,然后一次性的通过 I2C 接口把全部显示数据发送到 SSD1306 。这种方式不好的地方就是费内存,但是优势是可以一次性的吧显存数据全部发送到屏幕上显示。

我下面写的代码,就是在 MCU 内部建立一个显存,以后要显示什么内容都操作这块内存了,然后一次性的把显存数据发送到 SSD1306。代码如下:

/* SSD1306 width in pixels */
#define SSD1306_WIDTH            128/* SSD1306 LCD height in pixels */
#define SSD1306_HEIGHT           64/* SSD1306 data buffer */
static uint8_t SSD1306_Buffer[SSD1306_WIDTH * SSD1306_HEIGHT / 8];

0.96寸的OLED一共有 128x64 个像素点(128x64 bit),所以需要建立一块 1Kb 的内存用作显存使用。

3.2 OLED初始化代码

初始化相关代码看厂商提供的手册拿过来用就行。

void SSD1306_Init(void)
{/* A little delay */HAL_Delay(100);/* Init LCD */SSD1306_Write_Cmd(0xAE); //display offSSD1306_Write_Cmd(0x20); //Set Memory Addressing ModeSSD1306_Write_Cmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,InvalidSSD1306_Write_Cmd(0xB0); //Set Page Start Address for Page Addressing Mode,0-7SSD1306_Write_Cmd(0xC8); //Set COM Output Scan DirectionSSD1306_Write_Cmd(0x00); //---set low column addressSSD1306_Write_Cmd(0x10); //---set high column addressSSD1306_Write_Cmd(0x40); //--set start line addressSSD1306_Write_Cmd(0x81); //--set contrast control registerSSD1306_Write_Cmd(0xFF);SSD1306_Write_Cmd(0xA1); //--set segment re-map 0 to 127SSD1306_Write_Cmd(0xA6); //--set normal displaySSD1306_Write_Cmd(0xA8); //--set multiplex ratio(1 to 64)SSD1306_Write_Cmd(0x3F); //SSD1306_Write_Cmd(0xA4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM contentSSD1306_Write_Cmd(0xD3); //-set display offsetSSD1306_Write_Cmd(0x00); //-not offsetSSD1306_Write_Cmd(0xD5); //--set display clock divide ratio/oscillator frequencySSD1306_Write_Cmd(0xF0); //--set divide ratioSSD1306_Write_Cmd(0xD9); //--set pre-charge periodSSD1306_Write_Cmd(0x22); //SSD1306_Write_Cmd(0xDA); //--set com pins hardware configurationSSD1306_Write_Cmd(0x12);SSD1306_Write_Cmd(0xDB); //--set vcomhSSD1306_Write_Cmd(0x20); //0x20,0.77xVccSSD1306_Write_Cmd(0x8D); //--set DC-DC enableSSD1306_Write_Cmd(0x14); //SSD1306_Write_Cmd(0xAF); //--turn on SSD1306 panel/* Clear screen */SSD1306_Fill(SSD1306_COLOR_BLACK);/* Update screen */SSD1306_UpdateScreen();/* Set default values */SSD1306.CurrentX = 0;SSD1306.CurrentY = 0;/* Initialized OK */SSD1306.Initialized = 1;
}

3.3 操作显示缓冲区的几个重要函数

/* 更新屏幕显示,当我们把显示缓冲区数据改写了之后,必须调用这个函数才能在屏幕更新显示 */
void SSD1306_UpdateScreen(void)
{uint8_t page;for (page = 0; page < 8; page++) {SSD1306_Write_Cmd(0xB0 + page);SSD1306_Write_Cmd(0x00);SSD1306_Write_Cmd(0x10);/* Write multi data */SSD1306_Write_MultiData(&SSD1306_Buffer[SSD1306_WIDTH * page], SSD1306_WIDTH);}
}/* 反转屏幕显示(就是黑白颜色对调) */
void SSD1306_ToggleInvert(void)
{uint16_t i;/* Toggle invert */SSD1306.Inverted = !SSD1306.Inverted;/* Do memory toggle */for (i = 0; i < sizeof(SSD1306_Buffer); i++) {SSD1306_Buffer[i] = ~SSD1306_Buffer[i];}
}/* 填充显存,比如让屏幕全部点亮或者熄灭 */
void SSD1306_Fill(SSD1306_COLOR_t color)
{/* Set memory */memset(SSD1306_Buffer, (color == SSD1306_COLOR_BLACK) ? 0x00 : 0xFF, sizeof(SSD1306_Buffer));
}

3.4 绘制任意像素点函数

绘制任意点的像素函数,这是后面写的所有绘图函数的基础,后面的显示字符、画线、画圆、填充图案等等绘图函数都是基于这个绘制任意点函数的基础上的。

/*** @brief  SSD1306 color enumeration*/
typedef enum {SSD1306_COLOR_BLACK = 0x00, /*!< Black color, no pixel */SSD1306_COLOR_WHITE = 0x01  /*!< Pixel is set. Color depends on LCD */
} SSD1306_COLOR_t;void SSD1306_DrawPixel(uint16_t x, uint16_t y, SSD1306_COLOR_t color)
{if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) {/* Error */return;}/* Check if pixels are inverted */if (SSD1306.Inverted) {color = (SSD1306_COLOR_t)!color;}/* Set color */if (color == SSD1306_COLOR_WHITE) {SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] |= 1 << (y % 8);} else {SSD1306_Buffer[x + (y / 8) * SSD1306_WIDTH] &= ~(1 << (y % 8));}
}

3.5 在屏幕上显示一个字符

要想在屏幕显示一个字符,需要有字体库。字体库的定义不同,对于显示字符的函数实现也是不一样的,如果用了其他的字体库定义,可能导致该函数并不能完成显示字符的功能。

字体库这里使用一个结构体对字库进行封装起来,包括字库的宽高,还有指向字库数组的指针。

/*** @brief  Font structure used on my LCD libraries*/
typedef struct {uint8_t FontWidth;    /*!< Font width in pixels */uint8_t FontHeight;   /*!< Font height in pixels */const uint16_t* data; /*!< Pointer to data font data array */
} TM_FontDef_t;/** 函数作用 : 在屏幕上显示一个字符* 参数  ch    : 要显示的字符* 参数  Font  : 字体* 参数  color : 颜色* 返回值 : 无*/
char SSD1306_Putc(char ch, TM_FontDef_t* Font, SSD1306_COLOR_t color)
{uint32_t i, b, j;/* Check available space in LCD */if (SSD1306_WIDTH <= (SSD1306.CurrentX + Font->FontWidth) ||SSD1306_HEIGHT <= (SSD1306.CurrentY + Font->FontHeight)) {/* Error */return 0;}/* Go through font */for (i = 0; i < Font->FontHeight; i++) {b = Font->data[(ch - 32) * Font->FontHeight + i];for (j = 0; j < Font->FontWidth; j++) {if ((b << j) & 0x8000) {SSD1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR_t) color);} else {SSD1306_DrawPixel(SSD1306.CurrentX + j, (SSD1306.CurrentY + i), (SSD1306_COLOR_t)!color);}}}/* Increase pointer */SSD1306.CurrentX += Font->FontWidth;/* Return character written */return ch;
}

3.6 显示字符串

有了显示字符的函数,显示字符串就简单多了。

char SSD1306_Puts(char* str, TM_FontDef_t* Font, SSD1306_COLOR_t color)
{/* Write characters */while (*str) {/* Write character by character */if (SSD1306_Putc(*str, Font, color) != *str) {/* Return error */return *str;}/* Increase string pointer */str++;}/* Everything OK, zero should be returned */return *str;
}

4. OLED绘图相关代码

绘图的实现,主要就是操作显存的数据,然后再把数据一次性的更新到屏幕上。这里绘图的代码主要实现了画点、任意线、四边形、三角形、圆形等。

这部分的代码实现,都要依赖于前面实现的绘制任意点函数。

4.1 绘制任意线

/* * 参数:x0, y0是起点坐标*      x1, y1是终点坐标*      c 是绘制的颜色 */
void SSD1306_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, SSD1306_COLOR_t c)
{int16_t dx, dy, sx, sy, err, e2, i, tmp;/* Check for overflow */if (x0 >= SSD1306_WIDTH) {x0 = SSD1306_WIDTH - 1;}if (x1 >= SSD1306_WIDTH) {x1 = SSD1306_WIDTH - 1;}if (y0 >= SSD1306_HEIGHT) {y0 = SSD1306_HEIGHT - 1;}if (y1 >= SSD1306_HEIGHT) {y1 = SSD1306_HEIGHT - 1;}dx = (x0 < x1) ? (x1 - x0) : (x0 - x1);dy = (y0 < y1) ? (y1 - y0) : (y0 - y1);sx = (x0 < x1) ? 1 : -1;sy = (y0 < y1) ? 1 : -1;err = ((dx > dy) ? dx : -dy) / 2;if (dx == 0) {if (y1 < y0) {tmp = y1;y1 = y0;y0 = tmp;}if (x1 < x0) {tmp = x1;x1 = x0;x0 = tmp;}/* Vertical line */for (i = y0; i <= y1; i++) {SSD1306_DrawPixel(x0, i, c);}/* Return from function */return;}if (dy == 0) {if (y1 < y0) {tmp = y1;y1 = y0;y0 = tmp;}if (x1 < x0) {tmp = x1;x1 = x0;x0 = tmp;}/* Horizontal line */for (i = x0; i <= x1; i++) {SSD1306_DrawPixel(i, y0, c);}/* Return from function */return;}while (1) {SSD1306_DrawPixel(x0, y0, c);if (x0 == x1 && y0 == y1) {break;}e2 = err;if (e2 > -dx) {err -= dy;x0 += sx;}if (e2 < dy) {err += dx;y0 += sy;}}
}

4.2 绘制四边形

void SSD1306_DrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, SSD1306_COLOR_t c)
{/* Check input parameters */if (x >= SSD1306_WIDTH || y >= SSD1306_HEIGHT) {/* Return error */return;}/* Check width and height */if ((x + w) >= SSD1306_WIDTH) {w = SSD1306_WIDTH - x;}if ((y + h) >= SSD1306_HEIGHT) {h = SSD1306_HEIGHT - y;}/* Draw 4 lines */SSD1306_DrawLine(x, y, x + w, y, c);         /* Top line */SSD1306_DrawLine(x, y + h, x + w, y + h, c); /* Bottom line */SSD1306_DrawLine(x, y, x, y + h, c);         /* Left line */SSD1306_DrawLine(x + w, y, x + w, y + h, c); /* Right line */
}

4.3 绘制三角形

void SSD1306_DrawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, SSD1306_COLOR_t color)
{/* Draw lines */SSD1306_DrawLine(x1, y1, x2, y2, color);SSD1306_DrawLine(x2, y2, x3, y3, color);SSD1306_DrawLine(x3, y3, x1, y1, color);
}

4.4 三角形填充

void SSD1306_DrawFilledTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, SSD1306_COLOR_t color)
{int16_t deltax = 0, deltay = 0, x = 0, y = 0, xinc1 = 0, xinc2 = 0,yinc1 = 0, yinc2 = 0, den = 0, num = 0, numadd = 0, numpixels = 0,curpixel = 0;deltax = ABS(x2 - x1);deltay = ABS(y2 - y1);x = x1;y = y1;if (x2 >= x1) {xinc1 = 1;xinc2 = 1;} else {xinc1 = -1;xinc2 = -1;}if (y2 >= y1) {yinc1 = 1;yinc2 = 1;} else {yinc1 = -1;yinc2 = -1;}if (deltax >= deltay) {xinc1 = 0;yinc2 = 0;den = deltax;num = deltax / 2;numadd = deltay;numpixels = deltax;} else {xinc2 = 0;yinc1 = 0;den = deltay;num = deltay / 2;numadd = deltax;numpixels = deltay;}for (curpixel = 0; curpixel <= numpixels; curpixel++) {SSD1306_DrawLine(x, y, x3, y3, color);num += numadd;if (num >= den) {num -= den;x += xinc1;y += yinc1;}x += xinc2;y += yinc2;}
}

4.5 绘制圆形

void SSD1306_DrawCircle(int16_t x0, int16_t y0, int16_t r, SSD1306_COLOR_t c)
{int16_t f = 1 - r;int16_t ddF_x = 1;int16_t ddF_y = -2 * r;int16_t x = 0;int16_t y = r;SSD1306_DrawPixel(x0, y0 + r, c);SSD1306_DrawPixel(x0, y0 - r, c);SSD1306_DrawPixel(x0 + r, y0, c);SSD1306_DrawPixel(x0 - r, y0, c);while (x < y) {if (f >= 0) {y--;ddF_y += 2;f += ddF_y;}x++;ddF_x += 2;f += ddF_x;SSD1306_DrawPixel(x0 + x, y0 + y, c);SSD1306_DrawPixel(x0 - x, y0 + y, c);SSD1306_DrawPixel(x0 + x, y0 - y, c);SSD1306_DrawPixel(x0 - x, y0 - y, c);SSD1306_DrawPixel(x0 + y, y0 + x, c);SSD1306_DrawPixel(x0 - y, y0 + x, c);SSD1306_DrawPixel(x0 + y, y0 - x, c);SSD1306_DrawPixel(x0 - y, y0 - x, c);}
}

4.6 圆形填充

void SSD1306_DrawFilledCircle(int16_t x0, int16_t y0, int16_t r, SSD1306_COLOR_t c)
{int16_t f = 1 - r;int16_t ddF_x = 1;int16_t ddF_y = -2 * r;int16_t x = 0;int16_t y = r;SSD1306_DrawPixel(x0, y0 + r, c);SSD1306_DrawPixel(x0, y0 - r, c);SSD1306_DrawPixel(x0 + r, y0, c);SSD1306_DrawPixel(x0 - r, y0, c);SSD1306_DrawLine(x0 - r, y0, x0 + r, y0, c);while (x < y) {if (f >= 0) {y--;ddF_y += 2;f += ddF_y;}x++;ddF_x += 2;f += ddF_x;SSD1306_DrawLine(x0 - x, y0 + y, x0 + x, y0 + y, c);SSD1306_DrawLine(x0 + x, y0 - y, x0 - x, y0 - y, c);SSD1306_DrawLine(x0 + y, y0 + x, x0 - y, y0 + x, c);SSD1306_DrawLine(x0 + y, y0 - x, x0 - y, y0 - x, c);}
}

完整的工程源码我已经上传了CSDN,点击下面连接跳转下载:

https://download.csdn.net/download/luobeihai/86501329

OLED12864(SSD1306)驱动代码相关推荐

  1. stm32驱动ssd1306配置_STM32 OLED 屏幕 驱动芯片SSD1306 IIC代码

    #include "oled.h" #include "stdlib.h" #include "oledfont.h" #include & ...

  2. OLED显示屏 IIC/I²C/I2C通信 128*64分辨率 SH1106 / SSD1306 驱动教程

    OLED显示屏 IIC/I²C/I2C通信 128*64分辨率 SH1106 / SSD1306 驱动教程 一.OLED液晶屏 二.SH1106 I2C 驱动 1.SH1106 简介 2.I2C 通信 ...

  3. 【MicroPython ESP32】ssd1306驱动0.96“I2C屏幕cube3D图形显示

    [MicroPython ESP32]ssd1306驱动0.96"I2C屏幕cube3D图形显示 cube3D效果 原例程最早在一个Arduino ssd1306 i2c库的示例中可以找到这 ...

  4. 【MicroPython ESP32】ssd1306驱动0.96“I2C屏幕+mpu6050图形控制

    [MicroPython ESP32]ssd1306驱动0.96"I2C屏幕+mpu6050图形控制 效果演示 随着mpu6050模块的移动,oled屏幕矩形线框内的小方块也随对应的方向移动 ...

  5. 使用ssd1306驱动,来驱动0.96寸中景园oled屏幕

    硬件 nucleo-f411RE, 中景园oled屏幕(0.96寸,七线) 驱动文件地址 stm32-ssd1306 驱动文件说明 使用的库: H A L HAL HAL 支持的通信协议: S P I ...

  6. AMG8833的使用与stm32驱动代码

    一.介绍 1.内部框图 可以看到AMG8833的内部有红外点阵测温,并且还带有一个热敏电阻.使用同一个ADC进行采样,然后送给Control. 2.器件参数 测温点数:64(8x8的矩阵) 帧率:10 ...

  7. Linux创建线程读取双口数据,linux环境下读写一次双口ram尽然要十几个毫秒。(附驱动代码)...

    linux环境下读写一次双口ram尽然要十几个毫秒.(附驱动代码) 我用的双口ram是IDT70V28,手册上说的读写时间应该是几个纳秒,我写了个linux驱动,然后做测试,发现读写一次的时间尽然是十 ...

  8. 三星framebuffer驱动代码分析

    一.驱动总体概述 本次的驱动代码是Samsung公司为s5pv210这款SoC编写的framebuffer驱动,对应于s5pv210中的内部外设Display Controller (FIMD)模块. ...

  9. 树莓派IO口驱动代码的编写、微机总线地址、物理地址、虚拟地址、BCM2835芯片手册

    地址总线: 百度百科解释: 地址总线 (Address Bus:又称:位址总线) 属于一种电脑总线 (一部份),是由CPU 或有DMA 能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/ ...

  10. 基于框架编写驱动代码

    操作驱动的上层代码(pin4test) #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> ...

最新文章

  1. Docker核心技术之网络管理
  2. 【神经网络】(6) 卷积神经网络(VGG16),案例:鸟类图片4分类
  3. 安装Python2.7出现configure: error: no acceptable C compiler found in $PATH错误
  4. 语音识别数据集处理python进行音频处理
  5. vba和python哪个好学-python和vba学哪个
  6. 表的插入、更新、删除、合并操作_7_通过视图插入
  7. 2020计算机单招的大专,2020专科和单招的区别
  8. Java初级笔记-第五章
  9. LeetCode - 7 - Reverse Integer
  10. python3学习日志Gui编程
  11. Nodejs教程09:实现一个带接口请求的简单服务器
  12. php磁力链播放源码,Bt种子转磁力链 PHP源码
  13. 估计四川长虹的beta系数
  14. android维持登录状态
  15. Linux配置定时任务
  16. Android中MVC,MVP和MVVM的区别
  17. Tesseract训练中文字体识别(转)
  18. oracle查表字段长度,oracle中查询某张表中的字段名,字段类型,字段长度等信息...
  19. Python Animation 画动态图形
  20. TSGCTF 2021 Welcome to TSG CTF! WP

热门文章

  1. 银行c语言笔试题库,银行情景模拟面试题及参考答案
  2. 计算机培训教学计划,计算机培训教学计划.doc
  3. Laravel 框架 - 模板继承
  4. WPF界面设计辅助工具--Blend
  5. Nvivo 自动编码语言包
  6. SqlHelper的封装
  7. python可视化计算器_使用Python自带GUI tkinter编写一个期权价格计算器
  8. 使用模板文件流形式导出文件破损或xxx中的部分内容有问题。解决
  9. 世界主要国家地区英文名称,缩写代码
  10. Keil C51大工程建立,模块化编程