0.96寸OLED(SSD1306)屏幕显示
最近,我突发奇想去翻阅了一些我本科期间所做的一些小项目,发现都挺有意思的!当年做这些项目的时候可走了很多弯路,所以想着可以将它们上传到网络上,并通过我的讲解文章可以帮助你们少走一些弯路!
今天,我要分享的是一个0.96寸OLED(SSD1306)屏幕显示的学习笔记,因为我想要讲的详细一点,所以打算做成一个系列,手把手地教你学习和认识屏幕显示。由于我知识有限,如果出现一些错误,希望大家可以帮助我指出来,我们一起学习进步!!!
一、内置水平滚动方法
我们通过查询SSD1306的用户手册得知,芯片内部提供了水平滚动的方法,如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/9c8b5327abbc4e13425b30a03c862daa.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/f7085084f4ec9f2747558b12bfe72e26.png)
官方手册里给出的命令表清晰明了,并不难理解,下面我会给出详细代码,但在此之前我想要强调两点:
1)一定要在滚动前关闭滚动;
2)需要在发送命令D[2:0]后,发送两个Dummy byte;
// 每个滚动步骤之间的时间间隔,以帧为单位,越大越慢
typedef enum
{
FRAME_2 = 0x07,
FRAME_3 = 0x04,
FRAME_4 = 0x05,
FRAME_5 = 0x06,
FRAME_6 = 0x00,
FRAME_32 = 0x01,
FRAME_64 = 0x02,
FRAME_128 = 0x03,
}Roll_Frame;
/**
* @brief OLED_Inside_Hor_Sroll,内部设置水平滚动
* @param start_page,end_page :起始滚动页与终止滚动页(start_page:0~7, end_page:0~7);
* frame : 每个滚动步骤之间的时间间隔,以帧为单位,越大越慢(FRAME_2 ~ FRAME_128)
* dir : 1 向右水平滚动,0 向左水平滚动
* @retval 无
*/
void OLED_Inside_Hor_Sroll(uint8_t start_page,uint8_t end_page,Roll_Frame frame,uint8_t dir)
{
// 先必须关闭滚动
OLED_Write_Cmd(0x2E);
// 1是向右水平滚动,0是向左水平滚动
OLED_Write_Cmd(dir ? 0x26 : 0x27);
// 发送一个虚拟字节
OLED_Write_Cmd(0x00);
OLED_Write_Cmd(start_page & 0x07); //起始页 0
OLED_Write_Cmd(frame & 0x07); //滚动时间间隔
OLED_Write_Cmd(end_page & 0x07); //终止页 7
// 发送两个虚拟字节
OLED_Write_Cmd(0x00);
OLED_Write_Cmd(0xFF);
//开启滚动
OLED_Write_Cmd(0x2F);
}
二、自定义水平滚动方法
通过上个文章,我们知道0.96寸OLED屏幕内部是靠SSD1306驱动芯片驱动,而GDDRAM大小为 128x64 位,一列最多显示8个16x16像素点的汉字,除了缩小字体的办法外,并不能在一行内显示更多的中文字符串。
而在内置水平滚动方法,从上节可以看到,只能使得8个字符串在有限的空间重复滚动,不能在一列滚动超过8个字符串;于是我便想到自己编写函数来实现,最终在我的努力下将这个功能呈现了出来,大家可以先看看效果如何!!!
![](https://i-blog.csdnimg.cn/blog_migrate/2a6fb0cadba6e9e9373d622dcfb3ee4a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/4fe5e9b7f683d374606f5ef398c368db.gif)
严格来说,通过我写的长文字水平滚动方法,可以实现任意长度的文字显示,只需要更改程序中宏定义 LONG_CN_LEN 的值,以下是详细的代码:
// 宏定义最多显示的中文字符串长度(可以直接更改以显示更多字符串)
#define LONG_CN_LEN 20
/***************************点阵字体取模方式:共阴——列行式——逆向输出*********/
const typFNT_GB16 cfont16[] =
{
" ",0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,/*" ",0*/
"我",0x20,0x24,0x24,0x24,0xFE,0x23,0x22,0x20,0x20,0xFF,0x20,0x22,0x2C,0xA0,0x20,0x00,0x00,0x08,0x48,0x84,0x7F,0x02,0x41,0x40,0x20,0x13,0x0C,0x14,0x22,0x41,0xF8,0x00,/*"我",0*/
"是",0x00,0x00,0x00,0x7F,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x7F,0x00,0x00,0x00,0x00,0x81,0x41,0x21,0x1D,0x21,0x41,0x81,0xFF,0x89,0x89,0x89,0x89,0x89,0x81,0x81,0x00,/*"是",1*/
"羌",0x00,0x08,0x48,0x48,0x49,0x4E,0xC8,0x78,0x48,0x4C,0x4B,0x48,0x48,0x08,0x00,0x00,0x82,0x82,0x42,0x22,0x12,0x0E,0x03,0x02,0x7E,0x82,0x82,0x82,0x82,0x82,0xE2,0x00,/*"羌",3*/
"族",0x10,0x10,0xF1,0x96,0x90,0x90,0x10,0x08,0xF7,0x44,0xC4,0x44,0x44,0x44,0x04,0x00,0x80,0x60,0x1F,0x40,0x80,0x7F,0x04,0x85,0x44,0x34,0x0F,0x34,0x44,0x84,0x84,0x00,/*"族",4*/
"小",0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x20,0x40,0x80,0x00,0x00,0x08,0x04,0x03,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x00,/*"小",5*/
"煞",0x10,0x28,0x27,0x24,0x34,0x2C,0xE0,0x20,0x10,0x6C,0x8B,0x08,0x88,0x78,0x08,0x00,0x88,0x69,0x09,0x09,0x29,0xC9,0x0F,0x00,0x30,0xC8,0x05,0x02,0x25,0xC8,0x10,0x00,/*"煞",6*/
};
// 测定字符串长度
int my_strlen(uint8_t* str)
{
int count = 0; //计数
while(*str != '\0')
{
count++;
str++;
}
return count;
}
/**
* @brief OLED_Show_LongRoll_CN显示长中文字符串并向左滚动(16x16字符串0.96寸最多显示8个)
* @param x : 中文字符串显示的起始位置x(0-20*16)
y : 中文字符串显示的起始位置y(0-7)
*str : 中文字符串的指针
mode : 1 - 水平滚动模式1(滚动一圈后原位置开始继续滚动)
2 - 水平滚动模式2(滚动一圈后屏幕末尾位置开始继续滚动)
* @retval 无
*/
void OLED_Show_LongRoll_CN(uint16_t x,uint8_t y,uint8_t *str,uint8_t mode)
{
uint16_t CN_Last_Pixel = (my_strlen(str)/2)*16;
/* 将数据直接放入长数组内(最大长度依据LONG_CN_LEN的值,默认为20) */
// 字符串宽度是16
uint8_t hsize=16;
// 移动的像素点
uint16_t move_pixel = 0;
// 模式2的标志位
uint8_t mode2_flag = 0;
// 对长数组进行赋值
y = y*8;
while(*str!='\0')
{
if(hsize == 16)
{
GUI_ShowFont16(x,y,str,2);
}
else
{
return;
}
x+=hsize;
str+=2;
}
// 死循环进行向左移动阅读
while(1)
{
for(uint8_t i=0;i<8;i++)
{
// 起始点开始全部刷新
OLED_Write_Cmd(0xb0+i); //设置页地址(0~7)
OLED_Write_Cmd(0x00); //设置显示位置—列低地址
OLED_Write_Cmd(0x10); //设置显示位置—列高地址
for(uint16_t n=move_pixel;n<128 + move_pixel;n++) //写一PAGE的GDDRAM数据
{
if(mode == 1 || mode2_flag == 0)
{
// 设置起始点坐标
OLED_Write_Data(OLED_LONG_GRAM[n][i]);
}
else if(mode == 2)
{
if(n<128)
{
OLED_Write_Data(0x00);
}
else
{
// 设置起始点坐标
OLED_Write_Data(OLED_LONG_GRAM[n-128][i]);
}
}
}
}
// 每次移动的像素点为1
move_pixel++;
// 这里变小可以更改移动速度
SysTick_Delay_ms(30);
// 字符串长度走完正好留一段空白(8个字符)
if(move_pixel > CN_Last_Pixel)
{
move_pixel = 0;
if(mode == 2)
{
mode2_flag = 1;
}
}
// 模式2分为两个阶段才能重复显示,因此需要一个标志位
if(mode2_flag == 1 && move_pixel > 128)
{
move_pixel = 0;
mode2_flag = 0;
}
}
}
总结
本系列所用单片机型号为STM32F103VE,后续我会把本节所涉及的工程及相关资料放在评论区,大家自取!请大家多多点赞、多多收藏、感谢支持!!!我会继续输出优质内容的!!!加油!加油!🌸🌸🌸