实验程序已经发布到百度网盘,本文末有链接可以自取
IIC协议查看这篇博客IIC协议
数据手册查看SSD1306Z
字模链接 链接:https://pan.baidu.com/s/1k_ay03xnAdGChirlTpTIVA
提取码:OLED
OLED即有机发光管(Organic Light-Emitting Diode,OLED)。OLED显示技术具有自发光、广视角、几乎无穷高的对比度、较低功耗、极高反应速度、可用于绕曲性面板、使用温度范围广、构造及制程简单等有点,被认为是下一代的平面显示屏新兴应用技术。
OLED显示和传统的LCD显示不同,其可以自发光,所以不需要背光灯,这使得OLED显示屏相对于LCD显示屏尺寸更薄,同时显示效果更优。
常用的OLED屏幕有蓝色、黄色、白色等几种。屏的大小为0.96寸,像素点为128*64,所以我们称为0.96oled屏或者12864屏。

这类屏幕大部分是由SSD1306来驱动的,查看数据手册在这:SSD1306Z
| 标号 | PIN | 引脚说明 |
|---|---|---|
| 1 | GND | OLED显示模块电源地 |
| 2 | VCC | OLED显示模块电源正 |
| 3 | SCL | OLED显示模块I2C总线时钟信号 |
| 4 | SDA | OLED显示模块I2C总线数据信号 |
屏幕有128列(Column ),64行(Row),共128*64像素点,每8行为1页(PAGE),共有8页

STM32内部建立一个缓存(共128*8个字节),每次修改的时候,只是修改STM32上的缓存(静态随机存储器 SRAM),修改完后一次性把STM32上的缓存数据写入到OLED的GRAM。
这个方法也有坏处,对于SRAM很小的单片机(51系列)就比较麻烦。
这个写的很详细,建议看看
https://wlink.blog.csdn.net/article/details/81329770
OLED点阵的点亮方式,举个显示”P”的例子,最左边位最高位P7。
这个图画错了从下到上应该是8个点,他画了9个点。从下至上第2个点开始。

可以看出,要想显示”P”,首先写入0x1f,则显示一个竖杠,之后控制器自动水平移动到下一列。
再写入0X05,则出现两个小横杆,这个两个横杆就是0X05中0000 0101中两个1所处的位置,写完第二列后,控制器自动跳到第三列.
再写入0X07,第四列写入0X00后,P就显示出来了。这也说明,即使你只想再一列的最上端显示一个小点,即写入0X01。
即你不能一次性控制一个点阵,只能一次性控制八位点阵,即一列点阵。这也决定了字模选择的取模方式为“列行式”。
链接:https://pan.baidu.com/s/1k_ay03xnAdGChirlTpTIVA
提取码:OLED


GND接GND
VCC接3.3
OLED的SCL接stm32的B8(模拟IIC)
OLED的SDA接stm32的B9(模拟IIC)
硬件IIC请查看IIC协议
配置通讯使用的目标引脚为开漏模式;
使能 I2C 外设的时钟;
配置 I2C 外设的模式、地址、速率等参数并使能 I2C 外设;
编写向OLED写入函数;
编写OLED初始化指令



切记尽量避免使用PB3、PB4,具体看stm32f103c8t6使用PB3和PB4做普通GPIO使用时发现异常
#include "oled.h"
#include "stdlib.h"
#include "oledfont.h"
#include "delay.h"
//起始信号
void I2C_Start(void)
{
OLED_SDIN_Set();
OLED_SCLK_Set();
OLED_SDIN_Clr();
OLED_SCLK_Clr();
}
//结束信号
void I2C_Stop(void)
{
OLED_SCLK_Set();
OLED_SDIN_Clr();
OLED_SDIN_Set();
}
//等待信号响应
void I2C_WaitAck(void) //测数据信号的电平
{
OLED_SCLK_Set();
OLED_SCLK_Clr();
}
//写入一个字节
void Send_Byte(u8 dat)
{
u8 i;
for(i = 0; i < 8; i++)
{
OLED_SCLK_Clr();//将时钟信号设置为低电平
if(dat & 0x80) //将dat的8位从最高位依次写入
{
OLED_SDIN_Set();
}
else
{
OLED_SDIN_Clr();
}
OLED_SCLK_Set();//将时钟信号设置为高电平
OLED_SCLK_Clr();//将时钟信号设置为低电平
dat <<= 1;
}
}
//发送一个字节
//向SSD1306写入一个字节。
//mode:数据/命令标志 0,表示命令;1,表示数据;
void OLED_WR_Byte(u8 dat, u8 mode)
{
I2C_Start();
Send_Byte(0x78);
I2C_WaitAck();
if(mode)
{
Send_Byte(0x40);
}
else
{
Send_Byte(0x00);
}
I2C_WaitAck();
Send_Byte(dat);
I2C_WaitAck();
I2C_Stop();
}
uint8_t CMD_Data[] =
{
0xAE, 0x00, 0x10, 0x40, 0xB0, 0x81, 0xFF, 0xA1, 0xA6, 0xA8, 0x3F,
0xC8, 0xD3, 0x00, 0xD5, 0x80, 0xD8, 0x05, 0xD9, 0xF1, 0xDA, 0x12,
0xD8, 0x30, 0x8D, 0x14, 0xAF
}; //初始化命令
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:15
* @brief: 写入初始化命令
* @return: None
********************************************************************************/
void Write_Cmd(void)
{
int i;
for (i = 0; i < 27; i++)
{
OLED_WR_Byte(CMD_Data[i], OLED_CMD); //SET DCDC命令
}
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:19
* @brief: 向设备写控制命令
* @param: cmd
* @return: None
********************************************************************************/
void OLED_WR_CMD(uint8_t cmd)
{
OLED_WR_Byte(cmd, OLED_CMD); //SET DCDC命令
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:35
* @brief: 写数据
* @param: data
* @return: None
********************************************************************************/
void OLED_WR_DATA(uint8_t data)
{
OLED_WR_Byte(data, OLED_DATA); //SET DCDC命令
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:32
* @brief: 清屏
* @param: None
* @return: None
********************************************************************************/
void OLED_Clear(void)
{
uint8_t i, n;
for (i = 0; i < 27; i++)
{
OLED_WR_CMD(0xb0 + i);
OLED_WR_CMD(0x00);
OLED_WR_CMD(0x10);
for (n = 0; n < 128; n++)
{
OLED_WR_DATA(0);
}
}
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:59
* @brief: 开启OLed显示
* @param: None
* @return: None
********************************************************************************/
void OLED_Display_On(void)
{
OLED_WR_CMD(0x8D); //SET DCDC设置电荷泵
OLED_WR_CMD(0x14); //DCDC ON 开启电荷泵
OLED_WR_CMD(0xAF); //DISPlay ON开启显示
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 16:36
* @brief: 关闭OLED显示
* @param:
* @return:
********************************************************************************/
void OLED_Display_Off(void)
{
OLED_WR_CMD(0x8D); //SET DCDC设置电荷泵
OLED_WR_CMD(0x10); //DCDC OFF 开启电荷泵
OLED_WR_CMD(0xAE); //DISPlay OFF开启显示
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 16:33
* @brief: 设置起始点坐标
* @param: x,y
* @return: None
********************************************************************************/
void OLED_Set_Pos(uint8_t x, uint8_t y)
{
OLED_WR_CMD(0xb0 + y);
OLED_WR_CMD(((x & 0xf0) >> 4) | 0x10);
OLED_WR_CMD(x & 0x0f);
}
void OLED_On(void)
{
uint8_t i, n;
for (i = 0; i < 8; i++)
{
OLED_WR_CMD(0xb0 + i); //设置页地址
OLED_WR_CMD(0x00); //设置显示位置-列低地址
OLED_WR_CMD(0x10); //设置显示位置-列高地址
for (n = 0; n < 128; n++)
{
OLED_WR_DATA(1);
}//刷新显示
}
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-03 15:35
* @brief: n为循环次数,得到值为m的n+1次方
* @param m
* @param n
* @return result
********************************************************************************/
unsigned int oled_pow(uint8_t m, uint8_t n)
{
unsigned int result = 1;
while (n--)result *= m;
return result;
}
//显示2个数字
//x,y :起点坐标
//len :数字的位数
//size:字体大小
//mode:模式 0,填充模式;1,叠加模式
//num:数值(0~4294967295);
void OLED_ShowNum(uint8_t x, uint8_t y, unsigned int num, uint8_t len, uint8_t size2)
{
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_ShowChar(x + (size2 / 2) * t, y, ' ', size2);
continue;
}
else enshow = 1;
}
OLED_ShowChar(x + (size2 / 2) * t, y, temp + '0', size2);
}
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~63
//mode:0,反白显示;1,正常显示
//size:选择字体 16/12
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size)
{
unsigned char c = 0, i = 0;
c = chr - ' ';//得到偏移后的值
if (x > 128 - 1)
{
x = 0;
y = y + 2;
}
if (Char_Size == 16)
{
OLED_Set_Pos(x, y);
for (i = 0; i < 8; i++)
OLED_WR_DATA(F8X16[c * 16 + i]);
OLED_Set_Pos(x, y + 1);
for (i = 0; i < 8; i++)
OLED_WR_DATA(F8X16[c * 16 + i + 8]);
}
else
{
OLED_Set_Pos(x, y);
for (i = 0; i < 6; i++)
OLED_WR_DATA(F6x8[c][i]);
}
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-03 15:02
* @brief: 显示一个字符号串
* @param x
* @param y
* @param chr
* @param Char_Size
* @return: None
********************************************************************************/
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t Char_Size)
{
unsigned char j = 0;
while (chr[j] != '\0')
{
OLED_ShowChar(x, y, chr[j], Char_Size);
x += 8;
//如果--行显示的数字大于16就将行清零,y+2变成下一行
if (x > 120)
{
x = 0;
y += 2;
}
j++;
}
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-03 14:46
* @brief: 显示汉字 hzk 用取模软件得出的数组
* @param x,设置起始点x坐标
* @param y,设置起始点y坐标
* @param no,显示数组的第n个字
* @return:
********************************************************************************/
void OLED_ShowCHinese(uint8_t x, uint8_t y, uint8_t no)
{
uint8_t t, adder = 0;
OLED_Set_Pos(x, y);
//显示上面一部分
for (t = 0; t < 16; t++)
{
//Hzk[第几个数组][第几个值]
OLED_WR_DATA(Hzk[2 * no][t]);
adder += 1;
}
//显示下面一部分
OLED_Set_Pos(x, y + 1);
for (t = 0; t < 16; t++)
{
OLED_WR_DATA(Hzk[2 * no + 1][t]);
adder += 1;
}
}
/********************************************************************************
* @author: Luo Chen
* @date: 2022-07-01 15:38
* @brief: 初始化oled屏幕
* @param: None
* @return: None
********************************************************************************/
void OLED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( OLED_CLK, ENABLE );
GPIO_InitStructure.GPIO_Pin = OLED_SCLK_Pin | OLED_SDIN_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(OLED_PORT, &GPIO_InitStructure);
Delay_ms(100);
Write_Cmd();
OLED_Clear();
}
/**
********************************** Copyright *********************************
*
** (C) Copyright 2022-2022 xxx,China.
** All Rights Reserved.
*
******************************************************************************
**--------------------------------------------------------------------------**
** @FileName : oled.h
** @Description : None
**--------------------------------------------------------------------------**
** @Author : Luo Chen
** @Version : v1.0
** @Creat Date : 2022-07-16
**--------------------------------------------------------------------------**
** @Modfier : None
** @Version : None
** @Modify Date : None
** @Description : None
**--------------------------------------------------------------------------**
** @Function List : None
**--------------------------------------------------------------------------**
** @Attention : None
**--------------------------------------------------------------------------**
******************************************************************************
*
**/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __OLED_H_
#define __OLED_H_
#include "stm32f10x.h"
#include "sys.h"
#include "stdlib.h"
//
//-----------------OLED端口定义----------------
#define OLED_SCLK_Pin GPIO_Pin_8
#define OLED_SDIN_Pin GPIO_Pin_9
#define OLED_CLK RCC_APB2Periph_GPIOB
#define OLED_PORT GPIOB
#define OLED_SCLK_Clr() GPIO_ResetBits(OLED_PORT,OLED_SCLK_Pin)
#define OLED_SCLK_Set() GPIO_SetBits(OLED_PORT,OLED_SCLK_Pin)//SCL
#define OLED_SDIN_Clr() GPIO_ResetBits(OLED_PORT,OLED_SDIN_Pin)//DIN
#define OLED_SDIN_Set() GPIO_SetBits(OLED_PORT,OLED_SDIN_Pin)
#define OLED_CMD 0 //写命令
#define OLED_DATA 1 //写数据
void I2C_Start(void);
void I2C_Stop(void);
void I2C_WaitAck(void);
void Send_Byte(u8 dat);
void OLED_WR_Byte(u8 dat, u8 mode);
void Write_Cmd(void);
void OLED_WR_CMD(uint8_t cmd);
void OLED_WR_DATA(uint8_t data);
void OLED_Init(void);
void OLED_Clear(void);
void OLED_Display_On(void);
void OLED_Display_Off(void);
void OLED_Set_Pos(uint8_t x, uint8_t y);
void OLED_On(void);
void OLED_ShowNum(uint8_t x, uint8_t y, unsigned int num, uint8_t len, uint8_t size2);
void OLED_ShowChar(uint8_t x, uint8_t y, uint8_t chr, uint8_t Char_Size);
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *chr, uint8_t Char_Size);
void OLED_ShowCHinese(uint8_t x, uint8_t y, uint8_t no);
#endif
/******************************** END OF FILE *********************************/
oled.c已经写好(可能会有小问题哦),现在可以给自己一个小难度,自己整合main.c,实现显示
链接:https://pan.baidu.com/s/1rjiJI30Cqo7Bs7Lea0herQ
提取码:OLED
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "stm32f10x.h"
#include "oled.h"
int main()
{
/********************************************************************************
* Delay_init(); //本实验使用的是SysTick时钟
* CPU_TS_TmrInit(); //已经使能宏,不需要初始化
* uart1_init(115200); //串口初始化为115200,需要在usart.h中使能
* uart3_init(115200); //串口初始化为115200
********************************************************************************/
/* 初始化 */
OLED_Init();
OLED_Clear();
OLED_ShowString(30, 0, "OLED", 16); //输出字符
OLED_ShowCHinese(0, 4, 0); //欢
OLED_ShowCHinese(16, 4, 1); //迎
OLED_ShowCHinese(32, 4, 2); //来
OLED_ShowCHinese(48, 4, 3); //到
OLED_ShowCHinese(64, 4, 4); //洛
OLED_ShowCHinese(80, 4, 5); //尘
OLED_ShowCHinese(96, 4, 6); //的
OLED_ShowString (0, 6,"CSDN",16);
while(1)
{
}
}
编译成功

本文选择的是ST_Link烧录工具





链接:https://pan.baidu.com/s/103fBiqMb3REZwn1tGnOgMA 提取码:OLED