stm32学习记录之0.96寸OLED显示屏配置

您所在的位置:网站首页 液晶显示屏和oled stm32学习记录之0.96寸OLED显示屏配置

stm32学习记录之0.96寸OLED显示屏配置

2024-07-08 13:38:59| 来源: 网络整理| 查看: 265

目录 0.96寸oled屏概述工程实现GPIO初始化SSD1306 初始化启动与停止函数各个功能函数

0.96寸oled屏概述

本次实验所用oled显示屏为黄蓝屏,即屏上1/4 部分为黄光,下3/4 为蓝;而且是固定区域显示固定颜色,颜色和显示区域均不能修改。分辨率为128*64,采用IIC 接口方式进行通讯(默认地址0x78)。

IIC 接口模块 接口定义

GND:电源地VCC:电源正(3.3~5V)SCL:OLED 的D0 脚,在IIC 通信中为时钟管脚SDA:OLED 的D1 脚,在IIC 通信中为数据管脚

0.96 寸OLED 驱动IC 0.96’OLED(4Pin)模块采用SSD1306 为驱动芯片,模块带有稳压芯片,支持软件模拟IIC 通讯与硬件IIC 通讯,上电自动复位,功耗低,自发光自由视角。

SSD1306 有3 中寻址模式:页寻址模式、水平寻址模式、垂直寻址模式。寻址方式决定了写入数据的方式。

工程实现

本实验程序基于普中stm32f103zet6核心板: 在这里插入图片描述

SCL时钟管脚 接 PD6 SDA数据管脚 接 PD7 VCC 接 3.3v电源 GND 电源地 GPIO初始化

为了实际应用,对IO口进行了进一步封装。需要改变管脚的时候,修改程序更方便。 文件oled.h

/* 若要改变管脚 直接在此配置 */ #define GPIO GPIOD #define RCC_IO RCC_APB2Periph_GPIOD //gpio时钟 #define SCL GPIO_Pin_6 //时钟管脚 #define SDA GPIO_Pin_7 //数据管脚

文件oled.c

static void gpio_init(void) { GPIO_InitTypeDef GPIO_InitStructure; //gpio结构体变量 RCC_APB2PeriphClockCmd(RCC_IO, ENABLE); //使能GPIOD时钟 GPIO_InitStructure.GPIO_Pin = SCL | SDA; //PD6、PD7 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_Init(GPIO, &GPIO_InitStructure); /* 初始化GPIOD */ GPIO_SetBits(GPIO, SCL | SDA); //拉高电平 } SSD1306 初始化

文件oled.c

/******************************************************************************* * 函 数 名 : OLED_Init * 函数功能 : 0.96寸oled初始化函数 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void OLED_Init(void) { gpio_init(); //数据与时钟的gpio管脚初始化 delay_ms(200); //等待oled复位完成 OLED_WR_Byte(0xAE, OLED_CMD); //关闭显示 OLED_WR_Byte(0xD5, OLED_CMD); //设置时钟分频因子,震荡频率 OLED_WR_Byte(0x80, OLED_CMD); //[3:0],分频因子;[7:4],震荡频率 OLED_WR_Byte(0xA8, OLED_CMD); //设置驱动路数 OLED_WR_Byte(0X3F, OLED_CMD); //默认0X3F(1/64) OLED_WR_Byte(0xD3, OLED_CMD); //设置显示偏移 OLED_WR_Byte(0X00, OLED_CMD); //默认为0 OLED_WR_Byte(0x00, OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte(0x10, OLED_CMD); //设置显示位置—列高地址 OLED_WR_Byte(0x40, OLED_CMD); //设置显示开始行 [5:0],行数. OLED_WR_Byte(0x8D, OLED_CMD); //电荷泵设置 OLED_WR_Byte(0x14, OLED_CMD); //bit2,开启/关闭 OLED_WR_Byte(0x20, OLED_CMD); //设置内存地址模式 OLED_WR_Byte(0x02, OLED_CMD); //[1:0],00,列地址模式; 01,行地址模式; 10,页地址模式;默认10; OLED_WR_Byte(0xA1, OLED_CMD); //段重定义设置,bit0: 0,0->0; 1,0->127; OLED_WR_Byte(0xC0, OLED_CMD); //设置COM扫描方向;bit3: 0,普通模式; 1,重定义模式 COM[N-1]->COM0; N:驱动路数 //OLED_WR_Byte(0xC8,OLED_CMD); //设置COM扫描方向 OLED_WR_Byte(0xDA, OLED_CMD); //设置COM硬件引脚配置 OLED_WR_Byte(0x12, OLED_CMD); //[5:4]配置 OLED_WR_Byte(0x81, OLED_CMD); //对比度设置 OLED_WR_Byte(0xEF, OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮) OLED_WR_Byte(0xD9, OLED_CMD); //设置预充电周期 OLED_WR_Byte(0xf1, OLED_CMD); //[3:0],PHASE 1;[7:4],PHASE 2; OLED_WR_Byte(0xDB, OLED_CMD); //设置VCOMH 电压倍率 OLED_WR_Byte(0x30, OLED_CMD); //[6:4] 000,0.65*vcc; 001,0.77*vcc; 011,0.83*vcc; OLED_WR_Byte(0xA4, OLED_CMD); //全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏) OLED_WR_Byte(0xA6, OLED_CMD); //设置显示方式;bit0:1,反相显示;0,正常显示 OLED_WR_Byte(0xAF, OLED_CMD); //开启显示 OLED_Clear(); //清屏函数

以上这些初始化命令我参考了不同淘宝商家和教程给的程序,其中配置方案大致相同,个别参数配置不同:

对比度设置: OLED_WR_Byte (0x81, OLED_CMD); //对比度设置 OLED_WR_Byte (0xEF, OLED_CMD); //1~255;默认0X7F (亮度设置,越大越亮) 这个很好理解,配置的参数不同只影响屏幕亮度。

而有的配置方案中多了这么两个指令: OLED_WR_Byte (0x00, OLED_CMD); //设置显示位置—列低地址 OLED_WR_Byte (0x10, OLED_CMD); //设置显示位置—列高地址

经查看技术手册发现:

页地址模式设置列低半字节的开始地址(00h~0Fh) 这个命令专门为8位列地址的低半字节设置以通过页地址模式显示RAM中的数据。而每一个数据使用后列地址会自动增加。请参考表格9-1的部分以及10.1.3的部分来了解详细情况。

页地址模式设置列高半字节的开始地址(10h~1Fh) 这个命令专门为8位列地址的高半字节设置以通过页地址模式显示RAM中的数据。而每一个数据使用后列地址会自动增加。请参考表格9-1的部分以及10.1.3的部分来了解详细情况。

在这里插入图片描述 在这里插入图片描述

使用X[3:0]作为数据位,将列起始地址寄存器的低位半字节设置为页面寻址模式。复位后,初始显示行寄存器复位至0000b。 此命令仅适用于页面寻址模式。

看到这已经大概明白什么意思了,但是不知道具体有什么用,继续向下挖…

在这里插入图片描述

页面寻址模式(A[1:0]=10xb)。 在页寻址模式下,在读/写显示RAM之后,列地址指针自动加1。如果列地址指针到达列结束地址,则列地址指针重置为列起始地址,页地址指针不变。用户必须设置新的页面和列地址才能访问下一页RAM内容。页面寻址模式的页面和列地址点的移动顺序如图10-1所示。

在正常显示数据RAM读取或写入和页面寻址模式下,需要执行以下步骤。 定义起始RAM访问指针位置: ·通过命令B0h至B7h设置目标显示位置的页面起始地址。 ·通过命令00h~0Fh设置指针的低位起始列地址。 ·通过命令10h~1Fh设置指针的上起始列地址。

看到这儿,已经大概知道页寻址模式下如何向oled模块写入数据。这也是后面的清屏函数与更新显示函数的大概逻辑结构。

在我具体调试过程中,第一次显示字符的时候,字符的形状是上下颠倒的,我就怀疑显示字符函数哪里弄反了,或者是因为ASCII字符点阵数组不匹配。 经过检查和修改,也没有好转。这时候想到之前使用过的TFT显示屏能旋转,于是猜想这个显示屏会不会也有类似的功能。 又对比了几个例程发现有一个COM扫描方向的指令有出入: OLED_WR_Byte (0xC0, OLED_CMD); //设置COM扫描方向;bit3: 0普通模式; 1重定义模式COM[N-1]->COM0; N:驱动路数 查了查技术手册,看不懂(=_=),不管怎样先试试再说。最后字符显示形状正确了,但是字符从左下角变到右上角了,这就说明坐标零点由左下变为左上了。由于不影响使用,也没有过多纠结,当然知道其原因及调试方法更好。 启动与停止函数

oled.h

/* IIC端口定义 */ #define OLED_SCLK_Clr() GPIO_ResetBits(GPIO,SCL) //SDA IIC接口的时钟信号 #define OLED_SCLK_Set() GPIO_SetBits(GPIO,SCL) #define OLED_SDIN_Clr() GPIO_ResetBits(GPIO,SDA) //SCL IIC接口的数据信号 #define OLED_SDIN_Set() GPIO_SetBits(GPIO,SDA) #define OLED_CMD 0 //写命令 #define OLED_DATA 1 //写数据

oled.c

/* 模拟IIC启动信号 (时钟和数据管脚都由高到低)*/ void IIC_Start(void) { OLED_SCLK_Set(); //时钟脚置1 OLED_SDIN_Set(); //数据脚置1 OLED_SDIN_Clr(); //数据脚置0 OLED_SCLK_Clr(); //时钟脚置0 } /* 模拟IIC停止信号 (时钟置高、数据管脚都由低到高)*/ void IIC_Stop(void) { OLED_SCLK_Set(); //时钟脚置1 OLED_SDIN_Clr(); //数据脚置0 OLED_SDIN_Set(); //数据脚置1 }

之所以把这么两个简单的函数单独列出来,是因为我在配置模拟IIC启动信号函数时,由于是第一次写模拟IIC通信协议的函数,没有严格按照时序图来写,启动函数没有起到它应有的作用,导致显示屏没有收到启动信号。

起始条件与停止条件时序图 在这里插入图片描述 最初写启动函数时,先是把SDA时钟线与SCL数据线拉高,然后产生下降沿时,先拉低的是时钟线,再拉低的数据线。而启动条件是通过将SCL数据线从高拉到低,同时SDA时钟线保持高来建立的;停止条件是通过将SDA时钟线从低到高拉入来建立的。刚开始配置的时候没注意这些细节,参照例程比着葫芦画瓢,想着拉高再拉低就行,直到改正过来也不知道为什么是这儿错了,知道后来参照使用手册和时序图才明白过来。(说到这不得不说数据手册真是个好东西)

每次SSD1306 收到数据后都会将SDA 信号线拉低,发送一个应答信号,单片机通过检测应答信号来判断SSD1306 是否有接受到数据。 应答时序图 在这里插入图片描述 时钟线拉高再拉低,不需要真的确定它接收到了本次数据再发送下个数据。

/* 模拟IIC读取从机应答信号 */ static void IIC_Wait_Ack(void) { OLED_SCLK_Set(); OLED_SCLK_Clr(); } 各个功能函数

数据的发送是高位在前,也就是先发送字节的高位。数据的传输是由SDA 信号线与SCL 时钟线通过一定的规范进行传输: 在SCL 时钟线处于高电平期间SDA 的电平必须保持稳定(不允许改变电平状态); 在SCL时钟线处于低电平期间SDA 的电平允许发生改变(可以改变电平状态)。

IIC写入一个字节函数:

/* IIC写入一个字节 */ void Write_IIC_Byte(u8 IIC_Byte) { u8 i = 0; u8 j; for(i = 0; i IIC_Start(); //开始 Write_IIC_Byte(0x78); //写入从机地址,SA0=0 IIC_Wait_Ack(); Write_IIC_Byte(0x00); //写入命令 IIC_Wait_Ack(); Write_IIC_Byte(IIC_Command); //数据 IIC_Wait_Ack(); IIC_Stop(); //停止 } /* IIC写入数据 */ void Write_IIC_Date(u8 IIC_Date) { IIC_Start(); //开始 Write_IIC_Byte(0x78); //写入从机地址,SA0=0 IIC_Wait_Ack(); Write_IIC_Byte(0x40); //写入数据 IIC_Wait_Ack(); Write_IIC_Byte(IIC_Date); //数据 IIC_Wait_Ack(); IIC_Stop(); //停止 } //向SSD1306写入一个字节。 //dat:要写入的数据/命令 //cmd:数据/命令标志 0,表示命令; 1,表示数据; void OLED_WR_Byte(u8 date, u8 cmd) { if(cmd) Write_IIC_Date(date); else Write_IIC_Command(date); }

更新显示函数 & 清屏函数

u8 OLED_GRAM[128][8]; //8页 屏幕大小64*128 //更新图像到LCD void OLED_Refresh_Gram(void) { u8 i, n; for(i = 0; i u8 i, n; for(i = 0; i u8 Y, bx, date = 0; if(x > 127 || y > 63) return; //超出范围 结束函数 Y = 7 - y/8; //判断该点在哪一页 bx = y % 8; //定位该点所在行数 date = 1 tra_x = x1; x1 = x2; x2 = x1; } if(y1 > y2) { tra_y = y1; y1 = y2; y2 = y1; } for(x = x1; x if(size == 12) temp = ascii_1206[chr][t]; //调用1206字体 else if(size == 16) temp = ascii_1608[chr][t]; //调用1608字体 else if(size == 24) temp = ascii_2412[chr][t]; //调用2412字体 else return; //没有的字库 for(t1 = 0; t1 y = y0; x++; break; } } } } //显示一个字符串 //x,y:起点坐标 //size:字体大小 //*p:字符串起始地址 void OLED_ShowString(u8 x, u8 y, const u8 *p, u8 size) { while((*p = ' '))//判断字符是否正确 { if(x>(128-(size/2))) { x = 0; y += size; } if(y > (64-size)) { y = x = 0; OLED_Clear(); } OLED_ShowChar(x, y, *p, size, 1); x += size/2; p++; } }

显示数字函数

//m^n函数 为显示多位数字提供方便 static u32 oled_pow(u8 m,u8 n) { u32 result = 1; while(n--) result *= m; return result; } //显示len个数字 //x,y :起点坐标 //len :数字的位数 //size:字体大小 //num :数值(0~2^32-1); void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 size) { u8 t, temp; u8 enshow = 0; //判断高位是否为0 的标志 for(t = 0; t if(temp == 0) //判断最高位上的数是否有效(若最高位上的数为0 则不显示数字) { OLED_ShowChar(x + (size/2)*t, y,' ', size, 1); continue; //跳出本次循环 且标志位不清除,直到最高位有效 } else enshow = 1; //最高位有效,清除此标志位 } OLED_ShowChar(x + (size/2)*t, y, temp+'0', size, 1); //显示数字 } }

显示汉字函数

//显示汉字 //x,y:起点坐标 //pos:数组位置汉字显示 //size:字体大小 //mode:0,反白显示;1,正常显示 void OLED_ShowFontHZ(u8 x, u8 y, u8 pos, u8 size, u8 mode) { u8 temp, t, t1; u8 y0 = y; u8 csize = (size/8+((size%8)?1:0))*(size);//得到字体一个字符对应点阵集所占的字节数 if(size!=12 && size!=16 && size!=24) return; //不支持的size for(t = 0; t if(temp & 0x80) OLED_DrawPoint(x, y, mode); else OLED_DrawPoint(x, y, !mode); temp RCC_HSE_Config(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // (8/1)*9 = 72M //外部晶振1分频 //PLL锁相环9倍频 SysTick_Init(72); //滴答定时器初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级组合方式2 USART1_Init(9600); //串口1初始化 波特率9600 LED_Init(); //LED0 LED1初始化 OLED_Init(); while(1) { i++; if(i % 50 == 0) { LED2_green = !LED2_green; i=0; } delay_ms(10); OLED_ShowFontHZ(16*0,0,0,16,1); //电子信息 OLED_ShowFontHZ(16*1,0,1,16,1); OLED_ShowFontHZ(16*2,0,2,16,1); OLED_ShowFontHZ(16*3,0,3,16,1); OLED_Refresh_Gram();//更新显示 } }

这是个人在初次学习oled配置过程中的一些理解,如有不妥或错误之处敬请见谅并麻烦各位大佬指正,非常感谢!



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭