OLED12864(SSD1306)驱动代码
文章目录
- 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)驱动代码相关推荐
- stm32驱动ssd1306配置_STM32 OLED 屏幕 驱动芯片SSD1306 IIC代码
#include "oled.h" #include "stdlib.h" #include "oledfont.h" #include & ...
- 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 通信 ...
- 【MicroPython ESP32】ssd1306驱动0.96“I2C屏幕cube3D图形显示
[MicroPython ESP32]ssd1306驱动0.96"I2C屏幕cube3D图形显示 cube3D效果 原例程最早在一个Arduino ssd1306 i2c库的示例中可以找到这 ...
- 【MicroPython ESP32】ssd1306驱动0.96“I2C屏幕+mpu6050图形控制
[MicroPython ESP32]ssd1306驱动0.96"I2C屏幕+mpu6050图形控制 效果演示 随着mpu6050模块的移动,oled屏幕矩形线框内的小方块也随对应的方向移动 ...
- 使用ssd1306驱动,来驱动0.96寸中景园oled屏幕
硬件 nucleo-f411RE, 中景园oled屏幕(0.96寸,七线) 驱动文件地址 stm32-ssd1306 驱动文件说明 使用的库: H A L HAL HAL 支持的通信协议: S P I ...
- AMG8833的使用与stm32驱动代码
一.介绍 1.内部框图 可以看到AMG8833的内部有红外点阵测温,并且还带有一个热敏电阻.使用同一个ADC进行采样,然后送给Control. 2.器件参数 测温点数:64(8x8的矩阵) 帧率:10 ...
- Linux创建线程读取双口数据,linux环境下读写一次双口ram尽然要十几个毫秒。(附驱动代码)...
linux环境下读写一次双口ram尽然要十几个毫秒.(附驱动代码) 我用的双口ram是IDT70V28,手册上说的读写时间应该是几个纳秒,我写了个linux驱动,然后做测试,发现读写一次的时间尽然是十 ...
- 三星framebuffer驱动代码分析
一.驱动总体概述 本次的驱动代码是Samsung公司为s5pv210这款SoC编写的framebuffer驱动,对应于s5pv210中的内部外设Display Controller (FIMD)模块. ...
- 树莓派IO口驱动代码的编写、微机总线地址、物理地址、虚拟地址、BCM2835芯片手册
地址总线: 百度百科解释: 地址总线 (Address Bus:又称:位址总线) 属于一种电脑总线 (一部份),是由CPU 或有DMA 能力的单元,用来沟通这些单元想要存取(读取/写入)电脑内存元件/ ...
- 基于框架编写驱动代码
操作驱动的上层代码(pin4test) #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> ...
最新文章
- Docker核心技术之网络管理
- 【神经网络】(6) 卷积神经网络(VGG16),案例:鸟类图片4分类
- 安装Python2.7出现configure: error: no acceptable C compiler found in $PATH错误
- 语音识别数据集处理python进行音频处理
- vba和python哪个好学-python和vba学哪个
- 表的插入、更新、删除、合并操作_7_通过视图插入
- 2020计算机单招的大专,2020专科和单招的区别
- Java初级笔记-第五章
- LeetCode - 7 - Reverse Integer
- python3学习日志Gui编程
- Nodejs教程09:实现一个带接口请求的简单服务器
- php磁力链播放源码,Bt种子转磁力链 PHP源码
- 估计四川长虹的beta系数
- android维持登录状态
- Linux配置定时任务
- Android中MVC,MVP和MVVM的区别
- Tesseract训练中文字体识别(转)
- oracle查表字段长度,oracle中查询某张表中的字段名,字段类型,字段长度等信息...
- Python Animation 画动态图形
- TSGCTF 2021 Welcome to TSG CTF! WP