0.96寸OLED屏幕

news/2024/11/18 13:58:57/
	(题外话)为什么选择寄存器来实现,对于初学者而言我非常建议从寄存器配置开始,主要是因为搞单片机本来就是一项接近于底层硬件的工作,不要嫌麻烦。了解硬件外设工作原理和配置过程会对以后的调试有很大帮助。更容易理解库函数开发。

1.硬件资源描述

主控STM32F103RC
通讯方式硬件SPI1 +DMA1(DMA可选)
屏幕0.96寸蓝色OLED屏幕

下面是屏幕图片
在这里插入图片描述
在这里插入图片描述
2.OLED屏幕驱动方法说明
屏幕的话可以在那啥宝上买到大概10块钱,现在应该还没涨价吧。至于买IIC协议的还是SPI协议的就看单片机使用习惯了,个人还是喜欢SPI的,相比IIC讲SPI传输速度更快点。至于NSS是接地还是硬件控制,看个人习惯,我选择的是硬件控制。

	屏幕与单片机接线如下:
或者
D0
SCK : PA5
D1
MOSI : PA7
DC
PA1
RES
PA3
NSS
PA4
GND

注意:VDD和GND千万不要搞反了不然10块钱白给(我就烧坏了一个)😂

2.1:怎么和屏幕通讯并配置单片机的通讯

  • 下面这张图是OLED屏幕驱动芯片手册提供的SPI时序List itemCS# :通讯时一定要保持低电平。
    D/C# :就是命令模式选择信号低电平时SDIN(D1)的数据就是命令控制字节。
    SCLK :空闲电平高低无关,从这条时钟线可以看出屏幕是在上升沿锁存数据,下降沿允许数据变化。(这点很重要!)
    SDIN(D1) :8为数据高位在前。

(第三行的时序就是最后两行的组合)

  • 单片机通讯配置
    在这里插入图片描述
    这张图来自STM32F1参考手册
    stm32f1可以将SPI配置成4种通讯时序。对比我们的屏幕,只能选择上升沿采样的两种方式。

如下表:

序号控制位描述
1CPHA=1 CPOL=1空闲时高电平,第二个边沿采样(上升沿)
2CPHA=0 CPOL=0空闲时低电平,第一个边沿采样(上升沿)

下面的例程使用的是上表中序号2。
stm32SPI1寄存器配置如下:

void SPI1_Init(void)
{u16 spitest= 0;RCC->APB2ENR			|= 1<<2;				//IO端口A时钟开启RCC->APB2ENR			|= 1<<0;				//辅助功能IO时钟开启RCC->APB2ENR			|= 1<<12;				//SPI1时钟开启GPIOA->CRL				&= 0x0F00FFFF;			//PA4配置清除(NSS)		PA5配置清除(SCK)		PA7配置清除(MOSI)GPIOA->CRL				|= 0xB0BB0000;			//PA4复用功能推挽输出模式,输出模式,最大速度10MHz		PA5 & PA7复用功能推挽输出模式,输出模式,最大速度50MHzspitest						|= 1<<2;			//配置为主设备spitest						|= 1<<9;			//软件从设备管理spitest						|= 1<<8;			//NSS输出spitest						|= 1<<15;			//选择“单线双向”模式spitest						|= 1<<14;			//输出使能(只发模式)spitest						&=~(1<<1);			//空闲状态时,SCK保持低电平spitest						&=~(1<<0);			//数据采样从第一个时钟边沿开始spitest						&=~(1<<11);			//使用8位数据帧格式进行发送/接收spitest						&= 0xFFFFFFC7;		//波特率36Mbs= 4.5M/Sspitest						&=~(1<<7);			//先发送MSBSPI1->CR1				   = spitest;SPI1->CR2					|= 1<<1;			//TXDMAEN:发送缓冲区DMA使能SPI1->CR1					|=1<<6;				//打开SPI设备
}

2.2:DMA
stm32的DMA可是32的一大特色,优点在于它不需要CPU干预,只要DMA被触发就能直接对数据进行搬运。所以使用它可以节省CPU的工作时间用来处理其他任务,在一些大项目中这点尤为突出。详细的配置介绍请参考STM32F1参考手册,这块我就不多言了。直接上代码。

(不想使用DMA的话可以将主函数注释掉的部分取消注释,并在屏幕初始化函数子函数内倒数第二行取消调用DMA函数)

inline static void DMA_OLED_Init(void)
{RCC->AHBENR				 |=	1<<0;		//DMA1时钟开启DMA1_Channel3->CPAR = (u32)(&(SPI1->DR));//CPAR:外设数据寄存器的基地址DMA1_Channel3->CMAR = (u32)(OLED_SRAM);	//CMAR:存储器的基地址DMA1_Channel3->CCR |= 0<<14;			//MEM2MEM:非存储器到存储器模式DMA1_Channel3->CCR |= 0<<6;				//MINC:不执行外设地址增量操作DMA1_Channel3->CCR |= 1<<7;				//MINC:存储器地址增量模式DMA1_Channel3->CCR |= 1<<5;				//CIRC:循环模式DMA1_Channel3->CCR |= 1<<4;				//DIR :从存储器读DMA1_Channel3->CNDTR= 0x400;			//1024个字节DMA1_Channel3->CCR |= 1<<0;				//EN  :通道开启}

2.3:OLED屏幕的初始化及设置
相关的控制命令在屏幕手册中都有,下面要注意的一点就是DC控制要注意时间问题。DC是由GPIO控制的,所以反转速度很快,数据发送相比DC要慢得多。倒数第四行的 Wait_us(10); 就是在调试过程中遇到的问题,从代码上看数据已经发送完成了然后再改变DC命令选择,貌似没问题。但是屏幕一直没反应,用逻辑分析仪抓取时序图后发现该问题,下面附图。例程代码已纠正放心复制。
在这里插入图片描述
图中时序自上而下分别是SCK,MISO,DC。明显看出数据还未发送结束DC已经改变。

分析原因:子函数OLED_SendCmd(unsigned char)是判断发送区是否为空,然后条件式的装入。而硬件SPI则需等待上一个数据发送完成才会再处理刚送进来的数据,同样也需要时间。

代码部分如下

void OLED_Init(void) //初始化函数
{#define OLED_DC  	PA1#define OLED_RES 	PA3#define OLED_NSS 	PA4#define OLED_D0  	PA5#define OLED_D1  	PA7GPIOA->CRL				&= 0xFFFF0F0F;	//rs,dcGPIOA->CRL				|= 0x00003030;	//OLED_RES = 0;			 //低电平复位Wait_us(100);OLED_RES = 1;			 //复位结束OLED_DC	 = 0;			 //命令模式Wait_us(100);OLED_SendCmd(0xAE);//关闭显示OLED_SendCmd(0xD5);//设置时钟分频因子,震荡频率OLED_SendCmd(0xF0);//[3:0],分频因子;[7:4],震荡频率OLED_SendCmd(0x81);//设置对比度OLED_SendCmd(0x7F);//128OLED_SendCmd(0x8D);//设置电荷泵开关OLED_SendCmd(0x14);//开OLED_SendCmd(0x20);//设置模式OLED_SendCmd(0x00);//设置为水平地址模式OLED_SendCmd(0xD3);//行偏移命令OLED_SendCmd(0x2A);//校正参数OLED_SendCmd(0x21);//设置列地址的起始和结束的位置OLED_SendCmd(0x00);//0OLED_SendCmd(0x7F);//127   OLED_SendCmd(0x22);//设置页地址的起始和结束的位置OLED_SendCmd(0x00);//0OLED_SendCmd(0x07);//7OLED_SendCmd(0xC8);//0xc9上下反置 0xc8正常OLED_SendCmd(0xA1);//0xa0左右反置 0xa1正常OLED_SendCmd(0xA4);//全局显示开启;0xa4正常,0xa5无视命令点亮全屏OLED_SendCmd(0xA6);//设置显示方式;A7,反相显示;A6,正常显示	OLED_SendCmd(0xAF);//开启显示OLED_SendCmd(0x56);Wait_us(10);//OLED_DC	 = 1;			 //显示数据模式DMA_OLED_Init();Wait_us(10);//}

OLED_SendCmd(0xA4);//全局显示开启;0xa4正常,0xa5无视命令点亮全屏
这行代码在程序调试的时候可以将参数改为0xa5判断通讯是否正常。

这个是命令发送函数
OLED屏幕刷新一帧需要128*8个字节,每个字节的每一位控制屏幕的一个像素点(位的0或1表示亮灭),刚好是128 *64个位。

static void OLED_SendCmd(unsigned char ctrl_data)
{unsigned char t=200;while(! (SPI1->SR & 1<<1) )			//SPI1->SR & 1<<1=1:发送缓冲为空。{t--;if(t<=0)break;}SPI1->DR	= ctrl_data;
}void OLED_Write(unsigned char ASII,unsigned char ye,unsigned char lie)
{char i;for(i=0;i<6;i++){OLED_SRAM[ye][lie+i]=F6X8[(ASII-32)*6+i];}
}

这个是ASII字库,网上能找到太多了。

const unsigned char F6X8[] =
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ,   // sp0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 ,   // !0x00, 0x00, 0x07, 0x00, 0x07, 0x00 ,   // "0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14 ,   // #0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12 ,   // $0x00, 0x62, 0x64, 0x08, 0x13, 0x23 ,   // %0x00, 0x36, 0x49, 0x55, 0x22, 0x50 ,   // &0x00, 0x00, 0x05, 0x03, 0x00, 0x00 ,   // '0x00, 0x00, 0x1c, 0x22, 0x41, 0x00 ,   // (0x00, 0x00, 0x41, 0x22, 0x1c, 0x00 ,   // )0x00, 0x14, 0x08, 0x3E, 0x08, 0x14 ,   // *0x00, 0x08, 0x08, 0x3E, 0x08, 0x08 ,   // +0x00, 0x00, 0x00, 0xA0, 0x60, 0x00 ,   // ,0x00, 0x08, 0x08, 0x08, 0x08, 0x08 ,   // -0x00, 0x00, 0x60, 0x60, 0x00, 0x00 ,   // .0x00, 0x20, 0x10, 0x08, 0x04, 0x02 ,   // /0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E ,   // 0				//30[16]480x00, 0x00, 0x42, 0x7F, 0x40, 0x00 ,   // 10x00, 0x42, 0x61, 0x51, 0x49, 0x46 ,   // 20x00, 0x21, 0x41, 0x45, 0x4B, 0x31 ,   // 30x00, 0x18, 0x14, 0x12, 0x7F, 0x10 ,   // 40x00, 0x27, 0x45, 0x45, 0x45, 0x39 ,   // 50x00, 0x3C, 0x4A, 0x49, 0x49, 0x30 ,   // 60x00, 0x01, 0x71, 0x09, 0x05, 0x03 ,   // 70x00, 0x36, 0x49, 0x49, 0x49, 0x36 ,   // 80x00, 0x06, 0x49, 0x49, 0x29, 0x1E ,   // 90x00, 0x00, 0x36, 0x36, 0x00, 0x00 ,   // :0x00, 0x00, 0x56, 0x36, 0x00, 0x00 ,   // ;0x00, 0x08, 0x14, 0x22, 0x41, 0x00 ,   // <0x00, 0x14, 0x14, 0x14, 0x14, 0x14 ,   // =0x00, 0x00, 0x41, 0x22, 0x14, 0x08 ,   // >0x00, 0x02, 0x01, 0x51, 0x09, 0x06 ,   // ?0x00, 0x32, 0x49, 0x59, 0x51, 0x3E ,   // @0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C ,   // A0x00, 0x7F, 0x49, 0x49, 0x49, 0x36 ,   // B0x00, 0x3E, 0x41, 0x41, 0x41, 0x22 ,   // C0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C ,   // D0x00, 0x7F, 0x49, 0x49, 0x49, 0x41 ,   // E0x00, 0x7F, 0x09, 0x09, 0x09, 0x01 ,   // F0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A ,   // G0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F ,   // H0x00, 0x00, 0x41, 0x7F, 0x41, 0x00 ,   // I0x00, 0x20, 0x40, 0x41, 0x3F, 0x01 ,   // J0x00, 0x7F, 0x08, 0x14, 0x22, 0x41 ,   // K0x00, 0x7F, 0x40, 0x40, 0x40, 0x40 ,   // L0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F ,   // M0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F ,   // N0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E ,   // O0x00, 0x7F, 0x09, 0x09, 0x09, 0x06 ,   // P0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E ,   // Q0x00, 0x7F, 0x09, 0x19, 0x29, 0x46 ,   // R0x00, 0x46, 0x49, 0x49, 0x49, 0x31 ,   // S0x00, 0x01, 0x01, 0x7F, 0x01, 0x01 ,   // T0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F ,   // U0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F ,   // V0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F ,   // W0x00, 0x63, 0x14, 0x08, 0x14, 0x63 ,   // X0x00, 0x07, 0x08, 0x70, 0x08, 0x07 ,   // Y0x00, 0x61, 0x51, 0x49, 0x45, 0x43 ,   // Z0x00, 0x00, 0x7F, 0x41, 0x41, 0x00 ,   // [0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55 ,   // 550x00, 0x00, 0x41, 0x41, 0x7F, 0x00 ,   // ]0x00, 0x04, 0x02, 0x01, 0x02, 0x04 ,   // ^0x00, 0x40, 0x40, 0x40, 0x40, 0x40 ,   // _0x00, 0x00, 0x01, 0x02, 0x04, 0x00 ,   // '0x00, 0x20, 0x54, 0x54, 0x54, 0x78 ,   // a0x00, 0x7F, 0x48, 0x44, 0x44, 0x38 ,   // b0x00, 0x38, 0x44, 0x44, 0x44, 0x20 ,   // c0x00, 0x38, 0x44, 0x44, 0x48, 0x7F ,   // d0x00, 0x38, 0x54, 0x54, 0x54, 0x18 ,   // e0x00, 0x08, 0x7E, 0x09, 0x01, 0x02 ,   // f0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C ,   // g0x00, 0x7F, 0x08, 0x04, 0x04, 0x78 ,   // h0x00, 0x00, 0x44, 0x7D, 0x40, 0x00 ,   // i0x00, 0x40, 0x80, 0x84, 0x7D, 0x00 ,   // j0x00, 0x7F, 0x10, 0x28, 0x44, 0x00 ,   // k0x00, 0x00, 0x41, 0x7F, 0x40, 0x00 ,   // l0x00, 0x7C, 0x04, 0x18, 0x04, 0x78 ,   // m0x00, 0x7C, 0x08, 0x04, 0x04, 0x78 ,   // n0x00, 0x38, 0x44, 0x44, 0x44, 0x38 ,   // o0x00, 0xFC, 0x24, 0x24, 0x24, 0x18 ,   // p0x00, 0x18, 0x24, 0x24, 0x18, 0xFC ,   // q0x00, 0x7C, 0x08, 0x04, 0x04, 0x08 ,   // r0x00, 0x48, 0x54, 0x54, 0x54, 0x20 ,   // s0x00, 0x04, 0x3F, 0x44, 0x40, 0x20 ,   // t0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C ,   // u0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C ,   // v0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C ,   // w0x00, 0x44, 0x28, 0x10, 0x28, 0x44 ,   // x0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C ,   // y0x00, 0x44, 0x64, 0x54, 0x4C, 0x44 ,   // z0x14, 0x14, 0x14, 0x14, 0x14, 0x14     // horiz lines
};

这个是用到的延时初始化函数和ms级延时函数

inline void Wait_Init(void)
{SysTick->CTRL			&= (unsigned int)(~(1<<0));			//关闭SysTick定时器SysTick->CTRL			&= (unsigned int)(~(1<<2));			//9MHzSysTick->CTRL			&= (unsigned int)(~(1<<1));			//不产生下溢中断
}
void Wait_ms(unsigned int t)
{
#define ms_t 9000SysTick->LOAD			 = ms_t;				//1ms定时SysTick->VAL			 = 0;						//当前值清零SysTick->CTRL			|= 1<<0;				//打开SysTick定时器while(t){if(SysTick->CTRL & 1<<16)				//判断下溢{t--;}}
}

下面是主函数
注意:前面用到的子函数请做好声明

unsigned char OLED_SRAM[8][128]; //图像储存
extern const unsigned char F6X8[];int main()
{Wait_Init();//这里的延时函数我用的是SYSTICKSPI1_Init();OLED_Init();OLED_Write('L',0,0);OLED_Write('i',0,6);OLED_Write('a',0,12);OLED_Write('n',0,18);OLED_Write('g',0,24);while(1){
//		for(j=0;j<8;j++)
//		{
//				for(i=0;i<128;i++)
//					OLED_Write(OLED_SRAM[j][i]);
//		}}
}

结果





在这里插入图片描述

如果有问题欢迎指正和讨论


http://www.ppmy.cn/news/119529.html

相关文章

STM32F103系列_OLED屏幕(SSD1306、SSD1315驱动)SPI驱动【DMA】(高刷)

STM32F103系列_OLED屏幕&#xff08;SSD1306、SSD1315驱动&#xff09;SPI驱动【DMA】&#xff08;高刷&#xff09; 一、SSD1306和SSD1315二、电路原理图&#xff08;SPI接法&#xff09;三、STM32_SPI四、STM32_DMA五、代码OLED.cOLED.hOLED_Library.hDelay.h 六、调用方法例…

OLED(SPI)

为了复习OLED和超声波模块&#xff0c;做了OLED显示超声波测得的距离。 下面分别介绍二者如何使用&#xff1a; OLED我们选用7针 SPI作为通信方式&#xff0c;这种型号的OLED价格便宜&#xff0c;也方便使用。 上图淘宝也就是十几的价格。 一&#xff1a; 7针&#xff1a; 1…

STM32F103ZET6 驱动 OLED

STM32F103ZET6 驱动 OLED 目录 前言 OLED模块的基本了解 OLED驱动程序的开发 前言 ​ 大家好&#xff0c;这是我第一次发帖&#xff0c;由于&#xff0c;我的技术并不成熟&#xff0c;程序难免有编写不规范的地方&#xff0c;希望读者能够指正&#xff0c;也希望这篇帖子…

05:OLED模块【MSP430F5529】

目录 实物图 字模取字 字模软件 取模步骤 1.设置软件 2.取模 3.输出数据 代码 type.h oledfont.h oled.h oled.c main.c 实物图 下面图片中&#xff0c;可以看到OLED模块的四个接口&#xff1a;GND,VCC,SCL,SDA GNDVCCSCLSDA接地接电源3.3V/5V接P3.5接P3.6 字模取字 字…

【paddlecls】多机多卡-linux(二:环境搭建)

构建并进入 docker 容器后&#xff0c;我们进入下一步&#xff1a; 1. 退出/进入 docker 容器&#xff1a; 在进入 Docker 容器后&#xff0c;可使用组合键 Ctrl P Q 退出当前容器&#xff0c;同时不关闭该容器&#xff1b; 如需再次进入容器&#xff0c;可使用下述命令&am…

前端随机抽奖效果

功能描述 开始随机、标签收取、重置布局、标签收取后添加标记、删除标记、复原标记、重置布局 可以通过此功能实现随机点名、抽奖功能 效果截图 实现所用技术 vscode编写工具&#xff0c;html、css、jquery 以下为效果代码 <!DOCTYPE html> <html> <head&g…

数据结构与算法08:二分查找和哈希算法

目录 【二分查找】 二分查找的特殊情况 【哈希算法】 应用一&#xff1a;安全加密 应用二&#xff1a;唯一标识 应用三&#xff1a;数据校验 应用四&#xff1a;散列函数 应用五&#xff1a;负载均衡 应用六&#xff1a;数据分片 应用七&#xff1a;分布式存储&…

用 JavaScript 对抗 DDOS 攻击 (下)

抗 v2 之前的那些奇技淫巧&#xff0c;纯属娱乐而已&#xff0c;并不能撑多久。 但简单、好玩&#xff0c;似乎这正是对抗的乐趣。之前从未想过&#xff0c;居然还能把脚本黑科技&#xff0c;用在网络防御上。 于是&#xff0c;又陆陆续续对抗了一段时间。 直到兴致淡却&am…