【嵌入式协议外设篇】-OLED 显示屏模块(SSD1306 0.96“ 128×64 I²C / SPI)
一、适用场景
适用场景:小型信息显示(传感器数值、状态、菜单)、嵌入式调试界面、简易 UI(图标 + 文本)、时钟/计时器显示、低功耗便携设备显示、课程演示与入门练习(I²C/SPI 与显存管理)。

二、器材清单
0.96OLED 模块(接口:I²C 或 SPI) ×1
STM32F103VET6 ×1
若干杜邦线(母对母/公对公,根据引脚)
5V/3.3V电源
三、工作原理(要点)
OLED 控制器(如 SSD1306)内部维护显存(例如 128×64 → 1024 字节),MCU 更新显存缓冲区后通过 I²C/SPI 将显存写给显示器并触发刷新。
接口:I²C(7-bit)易接线,节省引脚;SPI(4 线)速度更快,适合频繁刷新与动画。·
四、接线示意
VCC → +5V
GND → GND
标准库-IIC
PA6 →SCL
PA7 →SDA
标准库-SPI
PA5 →SCL
PA7 →SDA
PA0 →CS
PA1 →DC
PA2 →RS
五、示例代码
标准库-iic
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"
#include "bsp_SysTick.h"
int n2=0;
void i2c_startup()
{
sda_h;
Delay_us(1);
scl_h;
Delay_us(1);
sda_l;
Delay_us(1);
scl_l;
Delay_us(1);
}
void i2c_stopup()
{
sda_l;
Delay_us(1);
scl_h;
Delay_us(1);
sda_h;
Delay_us(1);
scl_l;
Delay_us(1);
}
void ack_state(int ack_mask)
{
if(ack_mask==0)
{
sda_l;
}
else
sda_h;
Delay_us(1);
scl_h;
Delay_us(1);
scl_l;
Delay_us(1);
}
int get_ack()
{
int ack;
sda_h;
Delay_us(1);
scl_h;
Delay_us(1);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_7) == RESET)
{
ack=0;
}else
ack=1;
scl_l;
Delay_us(1);
return ack;
}
int send_num(u8 num)
{
int i;
char ack;
for(i=0;i<8;i++)
{
if(num&0x80)
{
sda_h;
}else
sda_l;
num<<=1;
scl_h;
Delay_us(1);
scl_l;
Delay_us(1);
}
ack=get_ack();
return ack;
}
u8 get_num(int state)
{
int i;
u8 num=0;
for(i=0;i<8;i++)
{
sda_h;
Delay_us(1);
num <<= 1;
scl_h;
Delay_us(1);
if(GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_3) == SET)
{
num|=0x01;
}
scl_l;
Delay_us(1);
}
ack_state(state);
scl_l;
Delay_us(1);
return num;
}
//写入数据
void Write_Data(unsigned char dat)
{
i2c_startup();
send_num(0x78); //写入oled地址
get_ack();
send_num(0x40); //写入命令地址
get_ack();
send_num(dat); //发送命令
get_ack();
i2c_stopup();
}
void Write_Command(unsigned char dat)
{
i2c_startup();
send_num(0x78); //Slave address,SA0=0
get_ack();
send_num(0x00); //write command
get_ack();
send_num(dat);
get_ack();
i2c_stopup();
}
void OLED_writemode(unsigned dat,unsigned cmd)
{
if(cmd)
Write_Data(dat);
else
Write_Command(dat);
}
//坐标设置
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_writemode(0xb0+y,OLED_CMD);
OLED_writemode((x&0x0f),OLED_CMD); //计算出设置显示
OLED_writemode(((x&0xf0)>>4)|0x10,OLED_CMD); //计算出设置显示位置—每一组中每一列的高地址位置—每一组中每一列的低地址
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
char i,n;
for(i=0;i<8;i++)
{
OLED_writemode(0xb0+i,OLED_CMD); //设置页地址(0~7),默认0页(0组)地址是0xb0
OLED_writemode(0x00,OLED_CMD); //设置显示位置—每一组中每一列的低地址
OLED_writemode(0x10,OLED_CMD); //设置显示位置—每一组中每一列的高地址
for(n=0;n<128;n++)OLED_writemode(0,OLED_DATA); //循环写入128列数据(都是0,相当于清屏)
} //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(char x,char y,char chr)
{
unsigned char c=0,i=0;
c=chr-' '; //得到偏移后的值,该值是根据头文件中符号对应的规律计算,空格是的ascii是32
if(x>Max_Column-1){x=0;y=y+2;} //+2是因为数据显示时每行数据实际上占两行,为了间距看起来正常需要将越界的数据偏移两行
OLED_Set_Pos(x,y); //设置要写入的组号以及列的高低地址
for(i=0;i<8;i++) //循环8次写入数据上半段
OLED_writemode(F8X16[c*16+i],OLED_DATA);
OLED_Set_Pos(x,y+1); //下移一组
for(i=0;i<8;i++) //循环8次写入数据下半段
OLED_writemode(F8X16[c*16+i+8],OLED_DATA);
}
//显示一个字符号串
void OLED_ShowString(char x,char y,char *chr)
{
unsigned char j=0;
while (chr[j]!='\0')
{ OLED_ShowChar(x,y,chr[j]); //写数据
x+=8; //+8是因为在x坐标写一次数据就占8个列,因此要写入下一个数据前必须进行位移
if(x>120){x=0;y+=2;} //+2是因为数据显示时每行数据实际上占两行,为了间距看起来正常需要将越界的数据偏移两行
j++; //数据下标后移
}
}
//初始化SSD1306
void OLED_Init(void)
{
GPIO_InitTypeDef PA;
//使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
PA.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
PA.GPIO_Mode = GPIO_Mode_Out_PP;
PA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&PA);
// GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7);
Delay_us(800000);
OLED_writemode(0xAE,OLED_CMD);//--display off
OLED_writemode(0x00,OLED_CMD);//---set low column address
OLED_writemode(0x10,OLED_CMD);//---set high column address
OLED_writemode(0x40,OLED_CMD);//--set start line address
OLED_writemode(0xB0,OLED_CMD);//--set page address
OLED_writemode(0x81,OLED_CMD); // contract control
OLED_writemode(0xFF,OLED_CMD);//--128
OLED_writemode(0xA1,OLED_CMD);//set segment remap
OLED_writemode(0xA6,OLED_CMD);//--normal / reverse
OLED_writemode(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_writemode(0x3F,OLED_CMD);//--1/32 duty
OLED_writemode(0xC8,OLED_CMD);//Com scan direction
OLED_writemode(0xD3,OLED_CMD);//-set display offset
OLED_writemode(0x00,OLED_CMD);//
OLED_writemode(0xD5,OLED_CMD);//set osc division
OLED_writemode(0x80,OLED_CMD);//
OLED_writemode(0xD8,OLED_CMD);//set area color mode off
OLED_writemode(0x05,OLED_CMD);//
OLED_writemode(0xD9,OLED_CMD);//Set Pre-Charge Period
OLED_writemode(0xF1,OLED_CMD);//
OLED_writemode(0xDA,OLED_CMD);//set com pin configuartion
OLED_writemode(0x12,OLED_CMD);//
OLED_writemode(0xDB,OLED_CMD);//set Vcomh
OLED_writemode(0x30,OLED_CMD);//
OLED_writemode(0x8D,OLED_CMD);//set charge pump enable
OLED_writemode(0x14,OLED_CMD);//
OLED_writemode(0xAF,OLED_CMD);//--turn on oled panel
}
void OLED_Clearport(int a,int b)
{
OLED_writemode(0xb0+a,OLED_CMD); //设置页地址(0~7)
OLED_writemode(0x00,OLED_CMD); //设置显示位置—列低地址
OLED_writemode(0x10,OLED_CMD); //设置显示位置—列高地址
for(n2=0;n2<128;n2++)OLED_writemode(0,OLED_DATA);
OLED_writemode(0xb0+b,OLED_CMD); //设置页地址(0~7)
OLED_writemode(0x00,OLED_CMD); //设置显示位置—列低地址
OLED_writemode(0x10,OLED_CMD); //设置显示位置—列高地址
for(n2=0;n2<128;n2++)OLED_writemode(0,OLED_DATA);
}
#include "stm32f10x.h"
#include "stdio.h"
#include "bsp_SysTick.h"
#include "bsp_usart.h"
#include "oled.h"
int TEMP,NUM;
int cnt=0;
char show[20];
int main()
{
SysTick_Init(); //延时函数初始化
OLED_Init();
OLED_Clear();
while(1)
{
OLED_ShowString(15,0,"open!");
OLED_ShowString(15,3,"hellow world!");
Delay_us(5000000);
OLED_Clear();
OLED_ShowString(15,0,"close!");
OLED_ShowString(15,3,"hellow world!");
Delay_us(5000000);
OLED_Clear();
// OLED_Clearport(3,4);
}
}
标准库-spi(这里面函数名还是iic是忘改了,如果建议直接修改函数名即可)
//标准库-spi
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"
#include "bsp_SysTick.h"
int n2=0;
void i2c_startup()
{
cs_l;
Delay_us(1);
}
void i2c_stopup()
{
cs_h;
Delay_us(1);
}
void send_num(u8 num)
{
int i;
scl_l;
for(i=0;i<8;i++)
{
if(num&0x80)
{
sda_h;
}else
sda_l;
scl_h;
Delay_us(1);
num<<=1;
scl_l;
Delay_us(1);
}
}
void OLED_writemode(unsigned dat,unsigned cmd)
{
if(cmd)
{
dc_h;
}
else
dc_l;
Delay_us(1);
i2c_startup();
send_num(dat);
i2c_stopup();
dc_h;
}
//坐标设置
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_writemode(0xb0+y,OLED_CMD);
OLED_writemode(((x&0xf0)>>4)|0x10,OLED_CMD); //计算出设置显示位置—每一组中每一列的高地址位置—每一组中每一列的低地址
OLED_writemode((x&0x0f),OLED_CMD); //计算出设置显示
}
//清屏函数,清完屏,整个屏幕是黑色的!和没点亮一样!!!
void OLED_Clear(void)
{
char i,n;
for(i=0;i<8;i++)
{
OLED_writemode(0xb0+i,OLED_CMD); //设置页地址(0~7),默认0页(0组)地址是0xb0
OLED_writemode(0x00,OLED_CMD); //设置显示位置—每一组中每一列的低地址
OLED_writemode(0x10,OLED_CMD); //设置显示位置—每一组中每一列的高地址
for(n=0;n<128;n++)OLED_writemode(0,OLED_DATA); //循环写入128列数据(都是0,相当于清屏)
} //更新显示
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(char x,char y,char chr)
{
unsigned char c=0,i=0;
c=chr-' '; //得到偏移后的值,该值是根据头文件中符号对应的规律计算,空格是的ascii是32
if(x>Max_Column-1){x=0;y=y+2;} //+2是因为数据显示时每行数据实际上占两行,为了间距看起来正常需要将越界的数据偏移两行
OLED_Set_Pos(x,y); //设置要写入的组号以及列的高低地址
for(i=0;i<8;i++) //循环8次写入数据上半段
OLED_writemode(F8X16[c*16+i],OLED_DATA);
OLED_Set_Pos(x,y+1); //下移一组
for(i=0;i<8;i++) //循环8次写入数据下半段
OLED_writemode(F8X16[c*16+i+8],OLED_DATA);
}
//显示一个字符号串
void OLED_ShowString(char x,char y,char *chr)
{
unsigned char j=0;
while (chr[j]!='\0')
{ OLED_ShowChar(x,y,chr[j]); //写数据
x+=8; //+8是因为在x坐标写一次数据就占8个列,因此要写入下一个数据前必须进行位移
if(x>120){x=0;y+=2;} //+2是因为数据显示时每行数据实际上占两行,为了间距看起来正常需要将越界的数据偏移两行
j++; //数据下标后移
}
}
//初始化SSD1306
void OLED_Init(void)
{
GPIO_InitTypeDef PA;
//使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
PA.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_5|GPIO_Pin_7;
PA.GPIO_Mode = GPIO_Mode_Out_PP;
PA.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&PA);
rs_h;
Delay_us(100000);
rs_l;
Delay_us(100000);
rs_h;
cs_h;
scl_l;
dc_h;
Delay_us(800000);
OLED_writemode(0xAE,OLED_CMD);//--display off
OLED_writemode(0x00,OLED_CMD);//---set low column address
OLED_writemode(0x10,OLED_CMD);//---set high column address
OLED_writemode(0x40,OLED_CMD);//--set start line address
OLED_writemode(0xB0,OLED_CMD);//--set page address
OLED_writemode(0x81,OLED_CMD); // contract control
OLED_writemode(0xFF,OLED_CMD);//--128
OLED_writemode(0xA1,OLED_CMD);//set segment remap
OLED_writemode(0xA6,OLED_CMD);//--normal / reverse
OLED_writemode(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_writemode(0x3F,OLED_CMD);//--1/32 duty
OLED_writemode(0xC8,OLED_CMD);//Com scan direction
OLED_writemode(0xD3,OLED_CMD);//-set display offset
OLED_writemode(0x00,OLED_CMD);//
OLED_writemode(0xD5,OLED_CMD);//set osc division
OLED_writemode(0x80,OLED_CMD);//
OLED_writemode(0xD8,OLED_CMD);//set area color mode off
OLED_writemode(0x05,OLED_CMD);//
OLED_writemode(0xD9,OLED_CMD);//Set Pre-Charge Period
OLED_writemode(0xF1,OLED_CMD);//
OLED_writemode(0xDA,OLED_CMD);//set com pin configuartion
OLED_writemode(0x12,OLED_CMD);//
OLED_writemode(0xDB,OLED_CMD);//set Vcomh
OLED_writemode(0x30,OLED_CMD);//
OLED_writemode(0x8D,OLED_CMD);//set charge pump enable
OLED_writemode(0x14,OLED_CMD);//
OLED_writemode(0xAF,OLED_CMD);//--turn on oled panel
//
}
void OLED_Clearport(int a,int b)
{
OLED_writemode(0xb0+a,OLED_CMD); //设置页地址(0~7)
OLED_writemode(0x00,OLED_CMD); //设置显示位置—列低地址
OLED_writemode(0x10,OLED_CMD); //设置显示位置—列高地址
for(n2=0;n2<128;n2++)OLED_writemode(0,OLED_DATA);
OLED_writemode(0xb0+b,OLED_CMD); //设置页地址(0~7)
OLED_writemode(0x00,OLED_CMD); //设置显示位置—列低地址
OLED_writemode(0x10,OLED_CMD); //设置显示位置—列高地址
for(n2=0;n2<128;n2++)OLED_writemode(0,OLED_DATA);
}
#include "stm32f10x.h"
#include "stdio.h"
#include "bsp_SysTick.h"
#include "bsp_usart.h"
#include "oled.h"
int main()
{
SysTick_Init(); //延时函数初始化
OLED_Init();
OLED_Clear();
while(1)
{
OLED_ShowString(15,0,"open!");
OLED_ShowString(15,3,"hellow world!");
Delay_us(5000000);
OLED_Clear();
OLED_ShowString(15,0,"close!");
OLED_ShowString(15,3,"hellow world!");
Delay_us(5000000);
OLED_Clear();
// OLED_Clearport(3,4);
}
}
//HAL库-iic和spi
一样只要将标准库工程里面延时修改成HAL_Delay,修改宏定义里面引脚上下拉函数即可
六、讲解视频
https://www.bilibili.com/video/BV13snEzrEV4/?spm_id_from=333.1387.homepage.video_card.click&vd_source=f7dfe1b14f260b9cc3a146d2dbfd0719
https://www.bilibili.com/video/BV1YKnHznEyB/?spm_id_from=333.1387.homepage.video_card.click&vd_source=f7dfe1b14f260b9cc3a146d2dbfd0719
https://www.bilibili.com/video/BV19KnHznEcF/?spm_id_from=333.1387.homepage.video_card.click&vd_source=f7dfe1b14f260b9cc3a146d2dbfd0719
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)