STM32用SPI方式控制OLED模块

一、OLED

1. OLED模块的外观

外观

2. OLED模块的电路图

电路图

3. OLED模块参数

项目说明
接口特性3.3V(串电阻后,可与 5V 系统连接)
通信接口4 线 SPI
屏幕分辨率128*64
屏幕尺寸0.96 寸
工作温度-40℃~70℃

颜色 纯蓝色、黄蓝双色
模块尺寸| 27mm*26mm

二、GPIO模拟SPI

1. 硬件连接

通过引脚和模块电路图可以分析出SPI的电路连接

OLED                    STM32
GND      <---------->   GND
VCC      <---------->   3.3V
D0       <---------->   PA4(CLK)
D1       <---------->   PA3(MOSI)
RES      <---------->   PA2(RET复位)
DC       <---------->   PA1(命令|数据dc)
CS       <---------->   GND

2. 软件驱动

SPI

由OLED模块数据手册上SPI的操作时序图写出由GPIO口模拟的SPI驱动代码

模块只支持向模块写数据不能读数据

所以只需要写SPI发送即可

2.1 “SPI.h”

#define OLED_CMD 0   //命令声明
#define OLED_DATA 1 //数据声明

#define OLED_CLK 	PAout(4)	// CLK时钟 	d0
#define OLED_MOSI 	PAout(3)	// MOSI		d1
#define OLED_RST 	PAout(2)	// RET复位	ret
#define OLED_DC  	PAout(1)	// 命令|数据	dc	(0表传输命令1表传输数据)

void OLED_SPI_Init(void); //配置MCU的SPI
void SPI_WriteByte(uint8_t addr,uint8_t data); //向寄存器地址写一个byte的数据
void WriteCmd(unsigned char cmd); //写命令
void WriteDat(unsigned char data); //写数据

2.2 “SPI.c”


/*************************************************************************/
/*函数功能: GPIO模拟SPI端口初始化                                         */
/*************************************************************************/
void OLED_SPI_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//使能PA端口时钟
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //端口配置
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO口速度为50MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOA
}

/*************************************************************************/
/*函数功能: 通过SPIO软件模拟SPI通信协议,向模块(SSD1306)写入一个字节                                  */
/*入口参数:																															 */	
/*						data:要写入的数据/命令																		 */				
/*						cmd :数据/命令标志 0,表示命令;1,表示数据;                   */
/*************************************************************************/
void SPI_WriteByte(unsigned char data,unsigned char cmd)
{
	unsigned char i=0;
	OLED_DC =cmd;
	OLED_CLK=0;
	for(i=0;i<8;i++)
	{
		OLED_CLK=0;
		if(data&0x80)OLED_MOSI=1; //从高位到低位
		else OLED_MOSI=0;
		OLED_CLK=1;
		data<<=1;
	}
	OLED_CLK=1;
	OLED_DC=1;
}
/*************************************************************************/
/*函数功能: 写命令                                                        */
/*************************************************************************/
void WriteCmd(unsigned char cmd)
{
	SPI_WriteByte(cmd,OLED_CMD);
}
/*************************************************************************/
/*函数功能: 写数据                                                        */
/*************************************************************************/
void WriteData(unsigned char data)
{
	SPI_WriteByte(data,OLED_DATA);
}

三、STM32对OLED模块的控制

3.1 指令集

序号HEX指令说明
081设置对比度值越大,屏幕越亮(默认 0X7F); 0X000XFF(1255)
1AE/AF设置显示开关AE,关闭显示;AF,开启显示
28D电荷泵设置0x10,关闭电荷泵;0x14开启电荷泵
3B0~B7设置页地址0~7 对应页 0~7
400~0F设置列地址(低四位)设置 8 位起始列地址的低四位
510~1F设置列地址(高四位)设置 8 位起始列地址的高四位

更多请查询SSD1306 的data sheet

3.2 “OLED.h"

void OLED_Init(void);//初始化OLED
void OLED_ON(void);//唤醒OLED
void OLED_OFF(void);//OLED休眠
void OLED_Refresh_Gram(void);//更新显存到OLED
void OLED_Clear(void);//清屏
void OLED_DrawPoint(u8 x,u8 y,u8 t);//画点
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot);//填充
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode);//显示字符
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);//显示2个数字
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size);//显示字符串

3.3 “OLED.c”

//OLED的显存
//存放格式如下.
//[0]0 1 2 3 ... 127	
//[1]0 1 2 3 ... 127	
//[2]0 1 2 3 ... 127	
//[3]0 1 2 3 ... 127	
//[4]0 1 2 3 ... 127	
//[5]0 1 2 3 ... 127	
//[6]0 1 2 3 ... 127	
//[7]0 1 2 3 ... 127 		   
u8 OLED_GRAM[128][8];	

/*************************************************************************/
/*函数功能: 软延时                                                        */
/*************************************************************************/
void OLED_DLY_ms(unsigned int ms)
{                         
  unsigned int a;
  while(ms)
  {
    a=1335;
    while(a--);
    ms--;
  }
}
/*************************************************************************/
/*函数功能: 初始化OLED模块                                                */
/*************************************************************************/
void OLED_Init(void)
{
	OLED_SPI_Init();
	OLED_CLK = 1;
	OLED_RST = 0;
	OLED_DLY_ms(100);
	OLED_RST = 1;

	//从上电到下面开始初始化要有足够的时间,即等待RC复位完毕
	WriteCmd(0xAE);	 		// Display Off (0x00)
	WriteCmd(0xD5);
	WriteCmd(0x80);		    // Set Clock as 100 Frames/Sec
	WriteCmd(0xA8);
	WriteCmd(0x3F);	        // 1/64 Duty (0x0F~0x3F)
	WriteCmd(0xD3);
	WriteCmd(0x00);		    // Shift Mapping RAM Counter (0x00~0x3F)
	WriteCmd(0x40 | 0x00);  // Set Mapping RAM Display Start Line (0x00~0x3F)
	WriteCmd(0x8D);
	WriteCmd(0x10 | 0x04);	// Enable Embedded DC/DC Converter (0x00/0x04)
	WriteCmd(0x20);
	WriteCmd(0x02);		    // Set Page Addressing Mode (0x00/0x01/0x02)
	WriteCmd(0xA0 | 0x01);  // Set SEG/Column Mapping    
	WriteCmd(0xC0);  // Set COM/x Scan Direction   
	WriteCmd(0xDA);
	WriteCmd(0x02 | 0x10);  // Set Sequential Configuration (0x00/0x10)
	WriteCmd(0x81);
	WriteCmd(0xCF);	        // Set SEG Output Current
	WriteCmd(0xD9);
	WriteCmd(0xF1);         // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
	WriteCmd(0xDB);
	WriteCmd(0x40);	        // Set VCOM Deselect Level
	WriteCmd(0xA4 | 0x00);	// Disable Entire Display On (0x00/0x01)
	WriteCmd(0xA6 | 0x00);	// Disable Inverse Display On (0x00/0x01)
	WriteCmd(0xAE | 0x01);  // Display On (0x01)

	OLED_Clear();  //初始清屏	
}
/*************************************************************************/
/*函数功能: 将OLED从休眠中唤醒                                            */
/*************************************************************************/
void OLED_ON(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X14);  //开启电荷泵
	WriteCmd(0XAF);  //OLED唤醒
}
/*************************************************************************/
/*函数功能: 将OLED休眠 -- 休眠模式下,OLED功耗不到10uA                      */
/*************************************************************************/
void OLED_OFF(void)
{
	WriteCmd(0X8D);  //设置电荷泵
	WriteCmd(0X10);  //关闭电荷泵
	WriteCmd(0XAE);  //OLED休眠
}

/*************************************************************************/
/*函数功能: 更新显存到OLED                                                 */
/*************************************************************************/
void OLED_Refresh_Gram(void)
{
	u8 i,n;		    
	for(i=0;i<8;i++)  
	{  
		WriteCmd(0xb0+i);   //设置页地址(0~7)
		WriteCmd(0x00);      //设置显示位置—列低地址
		WriteCmd(0x10);      //设置显示位置—列高地址   
		for(n=0;n<128;n++)WriteData(OLED_GRAM[n][i]); 
	}   
}
/*************************************************************************/
/*函数功能: 清屏                                                          */
/*************************************************************************/
void OLED_Clear(void)  
{  
	u8 i,n;  
	for(i=0;i<8;i++)for(n=0;n<128;n++)OLED_GRAM[n][i]=0X00;  
	OLED_Refresh_Gram();//更新显示
}
/*************************************************************************/
/*函数功能: 画点                                                          */
/*入口参数: 															*/
/*						x:横坐标       0~127              				 */
/*						y:纵坐标	 	 		0~63                     */
/*						dot:0,清空;1,填充	  																			 */				
/*************************************************************************/
void OLED_DrawPoint(u8 x,u8 y,u8 t)
{
	u8 pos,bx,temp=0;
	if(x>127||y>63)return;//超出范围了.
	pos=7-y/8;
	bx=y%8;
	temp=1<<(7-bx);
	if(t)OLED_GRAM[x][pos]|=temp;
	else OLED_GRAM[x][pos]&=~temp;	    
}
/*************************************************************************/
/*函数功能: 填充                                                          */
/*入口参数: 																														 */
/*						x1,y1,x2,y2 填充区域的对角坐标                              */
/*						确保x1<=x2;y1<=y2 0<=x1<=127 0<=y1<=63	 	 								 */
/*						dot:0,清空;1,填充	  																			 */				
/*************************************************************************/
void OLED_Fill(u8 x1,u8 y1,u8 x2,u8 y2,u8 dot)  
{  
	u8 x,y;  
	for(x=x1;x<=x2;x++)
	{
		for(y=y1;y<=y2;y++)OLED_DrawPoint(x,y,dot);
	}													    
	OLED_Refresh_Gram();//更新显示
}

/*************************************************************************/
/*函数功能: 在指定位置显示一个字符,包括部分字符                             */
/*入口参数: 																														 */
/*						x:0~12                                                     */
/*						y:0~63                                	 	 								 */
/*						mode:0,反白显示;1,正常显示	  									        		 */	
/*            size:选择字体 16/12                                        */
/*************************************************************************/
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 size,u8 mode)
{      			    
	u8 temp,t,t1;
	u8 y0=y;
	u8 csize=(size/8+((size%8)?1:0))*(size/2);		//得到字体一个字符对应点阵集所占的字节数
	chr=chr-' ';//得到偏移后的值		 
    for(t=0;t<csize;t++)
    {   
		if(size==12)temp=asc2_1206[chr][t]; 	 	//调用1206字体
		else if(size==16)temp=asc2_1608[chr][t];	//调用1608字体
		else if(size==24)temp=asc2_2412[chr][t];	//调用2412字体
		else return;								//没有的字库
        for(t1=0;t1<8;t1++)
		{
			if(temp&0x80)OLED_DrawPoint(x,y,mode);
			else OLED_DrawPoint(x,y,!mode);
			temp<<=1;
			y++;
			if((y-y0)==size)
			{
				y=y0;
				x++;
				break;
			}
		}  	 
    }          
}
//m^n函数
u32 mypow(u8 m,u8 n)
{
	u32 result=1;	 
	while(n--)result*=m;    
	return result;
}				  
/*************************************************************************/
/*函数功能: 显示2个数字                                                   */
/*入口参数: 															*/
/*						x,y :起点坐标                                     */
/*						len :数字的位数                              	 */
/*						size:字体大小	  									 */	
/*            mode:模式	0,填充模式;1,叠加模式                             */
/*            num:数值(0~4294967295)                                     */
/*************************************************************************/		  
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size)
{         	
	u8 t,temp;
	u8 enshow=0;						   
	for(t=0;t<len;t++)
	{
		temp=(num/mypow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==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:起点坐标                                                */
/*						size:字体大小                                	 	 				   */
/*						*p:字符串起始地址	  								         	        		 */	
/*************************************************************************/
void OLED_ShowString(u8 x,u8 y,const u8 *p,u8 size)
{	
    while((*p<='~')&&(*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++;
    }  
	
}	

四、用STM32的SPI资源(可选)

也可以不使用GPIO口模拟,使用STM32的SPI资源

驱动代码如下:

void OLED_SPI_Init(void)
{
	/*
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//使能PA端口时钟
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4; //端口配置
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO口速度为50MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOA
	*/
	GPIO_InitTypeDef GPIO_InitStructure;
	SPI_InitTypeDef SPI_InitStructure;
	RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|
	RCC_APB2Periph_SPI1, ENABLE ); //①GPIO,SPI 时钟使能
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_7); //①初始化 GPIO
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2;//端口配置
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;//IO口速度为50MHz
	GPIO_Init(GPIOA,&GPIO_InitStructure);//根据设定参数初始化GPIOA
	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置 SPI 全双工
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置 SPI 工作模式:设置为主 SPI
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // 8 位帧结构
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//选择了串行时钟的稳态:时钟悬空高
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据捕获于第二个时钟沿
	SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS 信号由硬件管理
	SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //预分频 256
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 位开始
	SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC 值计算的多项式
	SPI_Init(SPI1, &SPI_InitStructure); //②根据指定的参数初始化外设 SPIx 寄存器
	SPI_Cmd(SPI1, ENABLE); //③使能 SPI 外设
	//SPI1_ReadWriteByte(0xff);//④启动传输
}

void OLED_WB(uint8_t data)
{
    /* Loop while DR register in not emplty */
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
    /* Send byte through the SPI2 peripheral */
    SPI_I2S_SendData(SPI1, data);
}

void SPI_WriteByte(unsigned char data,unsigned char cmd)
{
	unsigned char i=0;
	OLED_DC =cmd;
	OLED_WB(data);
}

PS:可以通过添加公众号“山不高海不深”回复关键字“OLED代码“得到完整工程代码
在这里插入图片描述

回复关键字“OLED代码“得到完整工程代码

  • 43
    点赞
  • 251
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
### 回答1: STM32F103是一款常用的ARM Cortex-M3系列的单片机,通过SPI接口来实现与外部设备的通信是常见的应用之一。以下是关于如何使用STM32F103的SPI接口来读写OLED显示屏的简要概述。 首先,确保已经按照需要的电气连接,将OLED显示屏与STM32F103连接起来,其中包括SCK、MOSI、CS(片选)以及DC(数据/命令切换)等信号线。 其次,配置SPI接口。首先,使能SPI时钟,选择合适的SPI通道,配置SPI模式以及时钟分频等参数。可以使用STM32CubeMX工具来简化配置过程。然后,根据OLED显示屏的规格,配置SPI通信的特殊需求,如数据宽度、传输模式、控制信号等。 接下来,编写相关的代码来控制OLED显示屏。首先,确保OLED显示屏处于可用状态,并准备好相应的初始设置和功能配置。然后,使用SPI接口的读写函数将数据发送到OLED显示屏或从中读取数据。SPI接口的读写函数可以通过库或者使用直接读写寄存器的方式实现。 在具体的数据传输过程中,需要根据OLED显示屏使用的协议来设置相应的数据格式和控制信号。例如,发送命令和数据之前,需要将DC信号切换到相应的状态;同时,在SPI通信完成之后需要切换CS信号的状态以结束通信。 最后,记得适时进行相关的错误处理和调试。可以根据实际需要来添加适当的延迟等待SPI数据传输完成以及OLED显示屏的响应。 总之,通过配置STM32F103的SPI接口以及编写相应的代码,可以实现与OLED显示屏的数据读写。具体的实现方式需要根据OLED显示屏的规格和通信协议来确定。 ### 回答2: 在使用STM32F103芯片进行SPI读写OLED时,我们首先需要了解一些基本概念。 SPI(Serial Peripheral Interface)是一种串行通信协议,用于在微控制器或其他数字芯片之间进行全双工的数据通信。在SPI总线上,有一个主设备(通常是微控制器)和一个或多个从设备(如OLED显示屏)。主设备负责控制总线并发送数据,从设备负责接收数据并做出响应。 要在STM32F103芯片上使用SPI读写OLED,我们需要按照以下步骤进行操作: 1. 硬件连接:首先将STM32F103芯片的SPI引脚与OLED显示屏的SPI引脚连接。通常,STM32F103的SPI引脚由标有SCK(时钟线)、MISO(主设备输入,从设备输出)、MOSI(主设备输出,从设备输入)和NSS(片选信号)的引脚组成。 2. 初始化:在代码中,我们需要初始化SPI配置寄存器,设置SPI时钟相位、极性等参数,以及设置NSS引脚的控制方式。还需要初始化OLED显示屏,设置OLED的工作模式和其他参数。 3. 发送数据:通过SPI发送数据到OLED。我们可以使用SPI发送一个字节的数据或一串字节的数据,具体取决于要显示的内容。发送数据的函数通常会等待数据传输完成,然后返回结果。 4. 接收数据(如果需要):如果OLED显示屏返回一些数据,我们可以通过SPI接收数据的功能来读取这些数据。接收数据的函数通常会等待数据传输完成,然后将接收到的数据返回。 通过以上步骤,我们就可以实现STM32F103芯片对OLED显示屏的SPI读写操作。具体的实现方法和代码可以参考STM32F103的相关文档或参考其他开源项目。 ### 回答3: STM32F103是意法半导体生产的一款32位微控制器,具有强大的性能和丰富的外设支持。SPI(Serial Peripheral Interface)是一种串行外设接口协议,用于在微控制器与外部设备之间进行高速数据传输。 要实现STM32F103与OLEDSPI读写,可以按照以下步骤操作: 1. 首先,需要将OLED连接到STM32F103的SPI端口。OLED通常具有SDA(数据线)、SCL(时钟线)、CS(片选线)、RES(复位线)等引脚,需要将它们连接到STM32F103对应的引脚(如PB14、PB13、PB12、PB11)。 2. 在STM32F103的代码中配置SPI外设。可以使用STM32CubeMX进行外设配置,选择SPI模块,并设置相应的引脚和参数(如波特率、数据位宽等)。 3. 编写代码来初始化SPI外设。在代码中,需要初始化SPI控制寄存器的各个参数,如使能SPI、选择主从模式、设置数据传输顺序等。 4. 编写代码来控制OLED的初始化。在初始化时,可以设置OLED的显示参数、清空显示缓存等。 5. 编写代码来实现SPI写入数据到OLED。通过编写SPI发送数据的函数,将要显示的数据发送给OLED。 6. 编写代码来实现SPIOLED读取数据。通过编写SPI接收数据的函数,可以读取OLED的状态信息或其他返回的数据。 7. 在主函数中调用相应的函数,完成SPI读写操作。可以先调用初始化函数,然后通过写入数据和读取数据函数进行SPI读写操作。 综上所述,通过对STM32F103的SPI外设配置和编写相应的代码,可以实现与OLED之间的高速数据读写,可以灵活地控制OLED的显示。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值