OLED即有机发光管(Organic Light-Emitting Diode,OLED)。OLED显示技术具有自发光、广视角、几乎无穷高的对比度、较低功耗、极高反应速度、可用于绕曲性面板、使用温度范围广、构造及制程简单等有点,被认为是下一代的平面显示屏新兴应用技术。
OLED显示和传统的LCD显示不同,其可以自发光,所以不需要背光灯,这使得OLED显示屏相对于LCD显示屏尺寸更薄,同时显示效果更优。 常用的OLED屏幕有蓝色、黄色、白色等几种。屏的大小为0.96寸,像素点为 128*64,所以我们称为0.96oled屏或者12864屏。
------------------------------------------------------------------------------------
如何让字符显示在OLED屏幕上:
由于我们实际上是在和OLED的驱动IC通信,所以我们要选择驱动的相应时序8080时序。然后利用厂家给的初始化序列进行屏幕初始化。然后再进行画点读点的操作。
接下来介绍8080并行通信的读/写操作过程。
1、设置DC电平,选择写入类型(高电平为读写数据,低电平为读写命令);
2、拉低片选,选中我们的操作设备;
3、设置RD(读)/WD(写)为低电平,选择操作方式,再拉高选择的操作位,使之产生一个上升沿触发信号,会将数据锁存/写入,等于是在这个上升沿之后命令有效。
接下来以写入为例进行示范:根据需求自行选择DC电平,比如说读写数据的时候我们选择高电平;接下来拉低CS选中相应OLED屏设备;然后拉低WR线,准备进行写入数据;接下来在数据线上进行数据准备;然后我们再拉高WR线,产生一个上升沿触发信号,此时我们的数据线上的数据会被读取;然后再分别释放CS和DC线。
接下来进行代码示范:
void oled_wr_byte(uint8_t data, uint_8 cmd){
OLED_RS(cmd); //数据类型,有传参决定
OLED_CS(0);
OLED_WR(0);
oled_data_out(data); //数据线准备
OLED_WR(1);
OLED_CS(0);
OLED_RS(cmd); //释放,恢复默认
}
void oled_data_out(uint8_t data){
GPIOC->ODR = (GPIOC->ODR & 0xFF00) | (data & 0x00FF); //D[7:0]使用的是PC0~7,前一段清空后8位的数据,,后一段传入数据
}
整个OLED屏幕分成了八页,说白了就是把OLED的屏幕把宽平均分成了八份。比如想要在第0列第三行的开头显示一个点那就是按位来配置,0000 0100(0x08)。在对GRAM进行操作的时候,列地址指针自动递增,当列地址指针达到列结束地址的时候,重置开始地址,但页指针不变。用户必须设置新的页和列地址,才能访问下一页GRAM内容。
STM32内部建立一个缓存(共128*8个字节),每次修改的时候,只是修改STM32上的缓存(实际上就是SRAM),修改完后一次性把STM32上的缓存数据写入到OLED的GRAM。
代码实现如下:
static uint8_t g_oled_gram[128][8]; //OLED的显存,前面是列数,后面是页数
void oled_refresh_gram(void){
unit8_t i,n;
for(i=0;i<8;i++){
oled_wr_byte(0xB0 + i, OLED_CMD); //设置页地址
oled_wr_byte(0x00, OLED_CMD); //设置列低地址
oled_wr_byte(0x10, OLED_CMD); //设置列高地址
for(n=0;n<128;n++){
oled_wr_byte(g_oled_gram[n][i], OLED_DATA);
}
}
}
一个通用的置1表达式为OLED_GRAM[x][y/8] |= 1<< y%8;
然后给出标准画点的代码:
void oled_draw_point(uint8_t x, uint8_t y, uint8_t dot){
uint8_t pos, bx, temp = 0;
if(x > 127 || y > 63) return;
pos = y / 8; //页地址
bx = y % 8; //在页中纵坐标的位置
temp = 1 << bx; //转换后y对应的bit位置
if(dot)
g_oled_gram[x][pos] |= temp;
else
g_oled_gram[x][pos] |= ~temp;
}
接下来介绍字符显示原理:
1、显示字符必须有相应的点阵数据,点阵数据的集合叫做字库;
2、单片机根据点阵数据按取模放进行描点还原,从而显示字符;
3、ASCII字符宽度 = 汉字宽度的一半。
画点遵循原则:从上到下,从左到右,高位在前。
如图中的字符,我们对应的数据是
0x00, 0x04, 0x00, 0x3C, 0x03, 0xC4, 0x1C, 0x40。
接下来给出字符显示代码:
void oled_show_chara(){
uint8_t temp, t1, t;
uint8_t y0 = y; //保存y的初值
uint8_t mode = 1;
for(t1=0;t1<16;t1++){
temp = oled_ascii_1608[t];
for(t=0;t<8;t++){
if(temp & 0x80) //用最高位判断,如果这个点有效,则画出来
oled_draw_point(x, y, mode);
else
oled_draw_point(x, y, !mode);
temp <<= 1; //进行移位操作,直接丢弃最高位,以下一位为最高位进行判断
y++; //纵坐标自增
if((y-y0) == 16){ //显示完了一列
y=y0; //纵坐标复位
x++; //横坐标自增
break;
}
}
}
}
接下来用代码实现OLED基本驱动:
先写函数头文件oled.h:
#ifndef __OLED_H
#define __OLED_H
#include "stdlib.h"
#include "./SYSTEM/sys/sys.h"
#define OLED_MODE 1
//引脚定义和时钟开启
#define OLED_SPI_RST_PORT GPIOG
#define OLED_SPI_RST_PIN GPIO_PIN_15
#define OLED_SPI_RST_CLK_ENABLE() do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)
#define OLED_SPI_CS_PORT GPIOD
#define OLED_SPI_CS_PIN GPIO_PIN_6
#define OLED_SPI_CS_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)
#define OLED_SPI_RS_PORT GPIOD
#define OLED_SPI_RS_PIN GPIO_PIN_3
#define OLED_SPI_RS_CLK_ENABLE() do{ __HAL_RCC_GPIOD_CLK_ENABLE(); }while(0)
#define OLED_SPI_SCLK_PORT GPIOC
#define OLED_SPI_SCLK_PIN GPIO_PIN_0
#define OLED_SPI_SCLK_CLK_ENABLE() do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
#define OLED_SPI_SDIN_PORT GPIOC
#define OLED_SPI_SDIN_PIN GPIO_PIN_1
#define OLED_SPI_SDIN_CLK_ENABLE() do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0)
//端口函数启动
#define OLED_RST(x) do{ x ? \
HAL_GPIO_WritePin(OLED_SPI_RST_PORT, OLED_SPI_RST_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(OLED_SPI_RST_PORT, OLED_SPI_RST_PIN, GPIO_PIN_RESET); \
}while(0) /* ÉèÖÃRSTÒý½Å */
#define OLED_CS(x) do{ x ? \
HAL_GPIO_WritePin(OLED_SPI_CS_PORT, OLED_SPI_CS_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(OLED_SPI_CS_PORT, OLED_SPI_CS_PIN, GPIO_PIN_RESET); \
}while(0) /* ÉèÖÃCSÒý½Å */
#define OLED_RS(x) do{ x ? \
HAL_GPIO_WritePin(OLED_SPI_RS_PORT, OLED_SPI_RS_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(OLED_SPI_RS_PORT, OLED_SPI_RS_PIN, GPIO_PIN_RESET); \
}while(0) /* ÉèÖÃRSÒý½Å */
#define OLED_SCLK(x) do{ x ? \
HAL_GPIO_WritePin(OLED_SPI_SCLK_PORT, OLED_SPI_SCLK_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(OLED_SPI_SCLK_PORT, OLED_SPI_SCLK_PIN, GPIO_PIN_RESET); \
}while(0) /* ÉèÖÃSCLKÒý½Å */
#define OLED_SDIN(x) do{ x ? \
HAL_GPIO_WritePin(OLED_SPI_SDIN_PORT, OLED_SPI_SDIN_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(OLED_SPI_SDIN_PORT, OLED_SPI_SDIN_PIN, GPIO_PIN_RESET); \
}while(0) /* ÉèÖÃSDINÒý½Å */
#define OLED_WR(x) do{ x ? \
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_14, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_14, GPIO_PIN_RESET); \
} while (0)
#define OLED_RD(x) do{ x ? \
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_13, GPIO_PIN_RESET); \
}while(0)
#define OLED_CMD 0 //CS脚写命令
#define OLED_DATA 1 //CS脚写数据
static void oled_wr_byte(uint8_t data, uint8_t cmd); //写一个字节到OLED
static uint32_t oled_pow(uint8_t m, uint8_t n); //求平方函数
void oled_init(void);
void oled_clear(void);
void oled_display_on(void);
void oled_display_off(void);
void oled_refresh_gram(void);
void oled_draw_point(uint8_t x, uint8_t y, uint8_t dot);
void oled_fill(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t dot);
void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode);
void oled_show_num(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size);
void oled_show_string(uint8_t x, uint8_t y, const char *p, uint8_t size);
#endif
接下来写函数文件代码:
#include "stdlib.h"
#include "./BSP/OLED/oled.h"
#include "./BSP/OLED/oledfont.h"
#include "./SYSTEM/delay/delay.h"
static uint8_t g_oled_gram[128][8];
void oled_refresh_gram(void)
{
uint8_t i, n;
for (i = 0; i < 8; i++)
{
oled_wr_byte (0xb0 + i, OLED_CMD); //设置页地址
oled_wr_byte (0x00, OLED_CMD); //设置列低地址
oled_wr_byte (0x10, OLED_CMD); //设置列高地址
for (n = 0; n < 128; n++)
{
oled_wr_byte(g_oled_gram[n][i], OLED_DATA);
}
}
}
#if OLED_MODE == 1 //使用8080并口通信驱动OLED
static void oled_data_out(uint8_t data) //数据线准备
{
GPIOC->ODR = (GPIOC->ODR & 0XFF00) | (data & 0X00FF);
}
static void oled_wr_byte(uint8_t data, uint8_t cmd) //OLED读写操作
{
oled_data_out(data);
OLED_RS(cmd);
OLED_CS(0);
OLED_WR(0);
OLED_WR(1);
OLED_CS(1);
OLED_RS(1);
}
#else //使用SPI驱动
static void oled_wr_byte(uint8_t data, uint8_t cmd)
{
uint8_t i;
OLED_RS(cmd); /* дÃüÁî */
OLED_CS(0);
for (i = 0; i < 8; i++)
{
OLED_SCLK(0);
if (data & 0x80)
{
OLED_SDIN(1);
}
else
{
OLED_SDIN(0);
}
OLED_SCLK(1);
data <<= 1;
}
OLED_CS(1);
OLED_RS(1);
}
#endif
//开启OLED显示
void oled_display_on(void)
{
oled_wr_byte(0X8D, OLED_CMD); /* SET DCDCÃüÁî */
oled_wr_byte(0X14, OLED_CMD); /* DCDC ON */
oled_wr_byte(0XAF, OLED_CMD); /* DISPLAY ON */
}
//关闭OLED显示
void oled_display_off(void)
{
oled_wr_byte(0X8D, OLED_CMD); /* SET DCDCÃüÁî */
oled_wr_byte(0X10, OLED_CMD); /* DCDC OFF */
oled_wr_byte(0XAE, OLED_CMD); /* DISPLAY OFF */
}
//清屏
void oled_clear(void)
{
uint8_t i, n;
for (i = 0; i < 8; i++)for (n = 0; n < 128; n++)g_oled_gram[n][i] = 0X00;
oled_refresh_gram();
}
画点
void oled_draw_point(uint8_t x, uint8_t y, uint8_t dot)
{
uint8_t pos, bx, temp = 0;
if (x > 127 || y > 63) return;
pos = y / 8;
bx = y % 8;
temp = 1 << bx;
if (dot)
{
g_oled_gram[x][pos] |= temp;
}
else
{
g_oled_gram[x][pos] &= ~temp;
}
}
//OLED区域填充
void oled_fill(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t dot)
{
uint8_t x, y;
for (x = x1; x <= x2; x++)
{
for (y = y1; y <= y2; y++)oled_draw_point(x, y, dot);
}
oled_refresh_gram();
}
//在指定位置显示字符
void oled_show_char(uint8_t x, uint8_t y, uint8_t chr, uint8_t size, uint8_t mode)
{
uint8_t temp, t, t1;
uint8_t y0 = y;
uint8_t *pfont = 0;
uint8_t csize = (size / 8 + ((size % 8) ? 1 : 0)) * (size / 2); //得到一个字符对应点阵所占字节数
chr = chr - ' '; //得到偏移后的值,因为是从空格开始记录的
if (size == 12) /* 1206字体*/
{
pfont = (uint8_t *)oled_asc2_1206[chr];
}
else if (size == 16) /* 1608字体 */
{
pfont = (uint8_t *)oled_asc2_1608[chr];
}
else if (size == 24) /* 2412字库 */
{
pfont = (uint8_t *)oled_asc2_2412[chr];
}
else /* 不存在相应字库 */
{
return;
}
for (t = 0; t < csize; t++)
{
temp = pfont[t];
for (t1 = 0; t1 < 8; t1++)
{
if (temp & 0x80)oled_draw_point(x, y, mode);
else oled_draw_point(x, y, !mode);
temp <<= 1;
y++;
if ((y - y0) == size)
{
y = y0;
x++;
break;
}
}
}
}
//平方函数
static uint32_t oled_pow(uint8_t m, uint8_t n)
{
uint32_t result = 1;
while (n--)
{
result *= m;
}
return result;
}
/显示len个数字
void oled_show_num(uint8_t x, uint8_t y, uint32_t num, uint8_t len, uint8_t size)
{
uint8_t t, temp;
uint8_t enshow = 0;
for (t = 0; t < len; t++) /* °´×ÜÏÔʾλÊýÑ»· */
{
temp = (num / oled_pow(10, len - t - 1)) % 10; /* »ñÈ¡¶ÔӦλµÄÊý×Ö */
if (enshow == 0 && t < (len - 1)) /* ûÓÐʹÄÜÏÔʾ,ÇÒ»¹ÓÐλҪÏÔʾ */
{
if (temp == 0)
{
oled_show_char(x + (size / 2)*t, y, ' ', size, 1); /* ÏÔʾ¿Õ¸ñ,վλ */
continue; /* ¼ÌÐøϸöһλ */
}
else
{
enshow = 1; /* ʹÄÜÏÔʾ */
}
}
oled_show_char(x + (size / 2)*t, y, temp + '0', size, 1); /* ÏÔʾ×Ö·û */
}
}
//显示字符串
void oled_show_string(uint8_t x, uint8_t y, const char *p, uint8_t size)
{
while ((*p <= '~') && (*p >= ' '))
{
if (x > (128 - (size / 2)))
{
x = 0;
y += size;
}
if (y > (64 - size))
{
y = x = 0;
oled_clear();
}
oled_show_char(x, y, *p, size, 1);
x += size / 2;
p++;
}
}
//初始化OLED
void oled_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
#if OLED_MODE==1 /* 使用8080并口通信 */
/* PC0 ~ 7 设置 */
gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(GPIOC, &gpio_init_struct); /* PC0 ~ 7 设置 */
gpio_init_struct.Pin = GPIO_PIN_3|GPIO_PIN_6; /* PD3, PD6 设置 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(GPIOD, &gpio_init_struct); /* PD3, PD6 设置 */
gpio_init_struct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(GPIOG, &gpio_init_struct); /* WR/RD/RST引脚模式设置 */
OLED_WR(1);
OLED_RD(1);
#else /* SPI串口通信模式 */
gpio_init_struct.Pin = OLED_SPI_RST_PIN; /* RST引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(OLED_SPI_RST_PORT, &gpio_init_struct);
gpio_init_struct.Pin = OLED_SPI_CS_PIN; /* CS引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉*/
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(OLED_SPI_CS_PORT, &gpio_init_struct);
gpio_init_struct.Pin = OLED_SPI_RS_PIN; /* RS引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(OLED_SPI_RS_PORT, &gpio_init_struct);
gpio_init_struct.Pin = OLED_SPI_SCLK_PIN; /* SCLK引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(OLED_SPI_SCLK_PORT, &gpio_init_struct);
gpio_init_struct.Pin = OLED_SPI_SDIN_PIN; /* SDIN引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM; /* 中速 */
HAL_GPIO_Init(OLED_SPI_SDIN_PORT, &gpio_init_struct);
OLED_SDIN(1);
OLED_SCLK(1);
#endif
OLED_CS(1);
OLED_RS(1);
OLED_RST(0);
delay_ms(100);
OLED_RST(1);
oled_wr_byte(0xAE, OLED_CMD);
oled_wr_byte(0xD5, OLED_CMD);
oled_wr_byte(80, OLED_CMD);
oled_wr_byte(0xA8, OLED_CMD);
oled_wr_byte(0X3F, OLED_CMD);
oled_wr_byte(0xD3, OLED_CMD);
oled_wr_byte(0X00, OLED_CMD);
oled_wr_byte(0x40, OLED_CMD);
oled_wr_byte(0x8D, OLED_CMD);
oled_wr_byte(0x14, OLED_CMD);
oled_wr_byte(0x20, OLED_CMD);
oled_wr_byte(0x02, OLED_CMD);
oled_wr_byte(0xA1, OLED_CMD);
oled_wr_byte(0xC8, OLED_CMD);
oled_wr_byte(0xDA, OLED_CMD);
oled_wr_byte(0x12, OLED_CMD);
oled_wr_byte(0x81, OLED_CMD);
oled_wr_byte(0xEF, OLED_CMD);
oled_wr_byte(0xD9, OLED_CMD);
oled_wr_byte(0xf1, OLED_CMD);
oled_wr_byte(0xDB, OLED_CMD);
oled_wr_byte(0x30, OLED_CMD);
oled_wr_byte(0xA4, OLED_CMD);
oled_wr_byte(0xA6, OLED_CMD);
oled_wr_byte(0xAF, OLED_CMD);
oled_clear();
}