为您推荐:
精华内容
最热下载
问答
  • 5星
    329KB u010973053 2021-09-08 20:39:57
  • 32.24MB xmingl 2018-05-02 15:11:53
  • 5星
    6.44MB weixin_42666807 2021-10-03 18:36:53
  • 489KB qq_41189541 2018-11-18 09:49:07
  • 298KB kongqingpin2997 2018-05-08 16:23:03
  • 5星
    6.4MB qq_37732833 2018-08-10 13:46:38
  • 68KB qq_36041155 2017-11-25 21:31:06
  • 8KB qq_39492932 2019-03-22 17:33:37
  • 1.61MB wuyuhu20 2018-11-12 13:15:30
  • 8KB qq_45837785 2021-05-02 17:46:43
  • 2.85MB u014701357 2018-01-30 18:33:05
  • 10.61MB a545454669 2021-08-03 11:51:48
  • 5星
    3.34MB qq_36479876 2020-06-03 12:03:08
  • 理解OLED屏显和汉字点阵编码原理,使用STM32F103的SPI或IIC接口实现以下功能: 1.显示自己的学号和姓名; 2.显示AHT20的温度和湿度; 3.上下或左右的滑动显示长字符,比如“Hello,欢迎来到重庆!”或者一段歌词或...

    一、内容

    理解OLED屏显和汉字点阵编码原理,使用STM32F103的SPI或IIC接口实现以下功能:

    1.显示自己的学号和姓名;

    2.显示AHT20的温度和湿度;

    3.上下或左右的滑动显示长字符,比如“Hello,欢迎来到重庆!”或者一段歌词或诗词(最好使用硬件刷屏模式)。

    关于汉字点阵编码的原理可以参考上一篇博客!

    二、关于SPI

    SPI,是英语Serial Peripheral interface的缩写,顾名思义就是串行外围设备接口。是Motorola首先在其MC68HCXX系列处理器上定义的。
    SPI接口主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间
    SPI,是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信协议,比如AT91RM9200。

    AT91RM9200的SPI接口主要由4个引脚构成:SPICLK、MOSI、MISO及 /SS,其中SPICLK是整个SPI总线的公用时钟,MOSI、MISO作为主机,从机的输入输出的标志,MOSI是主机的输出,从机的输入,MISO 是主机的输入,从机的输出。/SS是从机的标志管脚,在互相通信的两个SPI总线的器件,/SS管脚的电平低的是从机,相反/SS管脚的电平高的是主机。在一个SPI通信系统中,必须有主机。SPI总线可以配置成单主单从,单主多从,互为主从。

    SPI的片选可以扩充选择16个外设,这时PCS输出=NPCS,说NPCS0~3 接4-16译码器,这个译码器是需要外接4-16译码器,译码器的输入为NPCS0~3,输出用于16个外设的选择。

    SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。也是所有基于SPI的设备共有的,它们是SDI(数据输入),SDO(数据输出),SCK(时钟),CS(片选)

     - SDO – 主设备数据输出,从设备数据输入对应MOSI master output slave input 
     - SDI – 主设备数据输入,从设备数据输出对应MISO master input slave output 
     - SCLK   – 时钟信号,由主设备产生 
     - CS        – 从设备使能信号,由主设备控制
    

    注意:SCK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备。
    这样传输的优点

    1. 与普通的串行通讯不同,普通的串行通讯一次连续传送至少8位数据,而SPI允许数据一位一位的传送,甚至允许暂停,因为SCK时钟线由主控设备控制,当没有时钟跳变时,从设备不采集或传送数据,也就是说,主设备通过对SCK时钟线的控制可以完成对通讯的控制
    2. SPI还是一个数据交换协议:因为SPI的数据输入和输出线独立,所以允许同时完成数据的输入和输出。不同的SPI设备的实现方式不尽相同,主要是数据改变和采集的时间不同,在时钟信号上沿或下沿采集有不同定义,具体请参考相关器件的文档。
    3. 在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些。

    缺点:
    没有指定的流控制,没有应答机制确认是否接收到数据。

    三、关于OLED

    通常我们所用的OLED屏有白色、蓝色、黄蓝双色等几种;屏的大小为0.96寸,像素点为128*64,所以我们也称之为0.96OLED屏或者12864屏。

    内部驱动IC为SSD1306;通信方式一般为SPI或者I2C

    配置哪种模式主要是根据BS0、BS1和BS2这三个管脚的电平逻辑来的。
    关于OLED的显存分布情况
    我们可以理解为:水平方向分布了128个像素点,垂直方向分布了64个像素点(如图一所示)。而驱动芯片在点亮像素点的时候,是以8个像素点为单位的。官方的例程推荐的是垂直扫描的方式,也就是先画垂直方向的8个像素点(如下图二所示),所以我们在画点的时候Y的取值为0-7,X的取值为0-127,具体请看参考内容。

    四、功能实现

    1)实验准备

    软件:Keil uVision5、烧录软件、串口调试助手
    硬件:PC机、STM32芯片、OLED显示屏、若干杜邦线、USB TO TTL串口

    2)代码准备

    1、显示学号和姓名

    Ⅰ、字模提取
    首先,先进行字模提取,在提取之前,需要进行一些选项设置,具体如下图:
    在这里插入图片描述
    然后在下方输入框中输入想要显示的内容,点击生成字模即可看到对用的字模编码,然后点击保存字模即可:
    在这里插入图片描述
    Ⅱ、代码完善

    ①下载路径:打开该博客,博主分享有完整代码;
    ②下载上述文件后,打开资料包,打开keil工程,如下图所示:
    在这里插入图片描述
    首先,将刚刚生成的字模编码添加到如下文件当中去,如下图所示:
    在这里插入图片描述
    然后打开test.c将以下函数的一部分修改为我们需要显示的内容:
    在这里插入图片描述
    函数说明

    GUI_ShowString() 的参数

    参数一:X 坐标参数二:Y 坐标参数三:字符串(ASCLL码中的)参数四:bit(表示字符显示格式,这里我用的 16,和汉字一样高)参数五:显示样式(1:白字黑底;0:黑字白底)
    GUI_ShowChinese() 的参数

    参数一:X 坐标参数二:Y 坐标参数三:汉字点阵大小(这里使用的是 16×16 的,参数应该是16)参数四:要显示的汉字参数五:显示样式(1:白字黑底;0:黑字白底)

    Ⅲ、主要代码展示

    主函数mian.c:

    int main(void)
    {	
    	delay_init();	    	       //延时函数初始化	  
    	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
    	OLED_Init();			         //初始化OLED  
    	OLED_Clear(0);             //清屏(全黑)
    	while(1) 
    	{	
    		TEST_MainPage();         //主界面显示测试
    	}
    }
    

    OLED相关函数:

    //OLED控制用函数
    void OLED_WR_Byte(unsigned dat,unsigned cmd);     							   		    
    void OLED_Display_On(void);
    void OLED_Display_Off(void);
    void OLED_Set_Pos(unsigned char x, unsigned char y);
    void OLED_Reset(void);
    void OLED_Init_GPIO(void);	   							   		    
    void OLED_Init(void);
    void OLED_Set_Pixel(unsigned char x, unsigned char y,unsigned char color);
    void OLED_Display(void);
    void OLED_Clear(unsigned dat);  
    #endif
    

    2、显示AHT20的温度和湿度

    Ⅰ、工程路径
    打开博客,文章底部有博主分享的完整代码;将文件下载下来,并打开其中的Keil工程,如下图所示:
    在这里插入图片描述
    Ⅱ、主要代码
    温湿度采集函数代码:
    在这里插入图片描述
    主函数main.c

    #include "delay.h"
    #include "usart.h"
    #include "bsp_i2c.h"
    #include "sys.h"
    
    #include "oled.h"
    #include "gui.h"
    #include "test.h"
    
    int main(void)
    {	
    	delay_init();	    	       //延时函数初始化    	  
    	uart_init(115200);	 
    	IIC_Init();
    		  
    	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
    	OLED_Init();			         //初始化OLED  
    	OLED_Clear(0); 
    	while(1)
    	{
    		//printf("温度湿度显示");
    		read_AHT20_once();
    		OLED_Clear(0); 
    		delay_ms(1500);
      }
    }
    
    

    3、滑动显示长字符

    Ⅰ、字模提取
    这里我显示的是热爱可抵岁月漫长,在字模编码生成器当中如第一部分所示生成对应的字模编码并添加:
    在这里插入图片描述

        "热",0x10,0x40,0x10,0x40,0x10,0x40,0xFD,0xF8,0x10,0x48,0x10,0x48,0x1C,0xC8,0x30,0x48,
    	0xD0,0xAA,0x10,0xAA,0x51,0x06,0x22,0x02,0x00,0x00,0x48,0x88,0x44,0x44,0x84,0x44,
        "爱",0x00,0x08,0x01,0xFC,0x7E,0x10,0x22,0x10,0x11,0x20,0x7F,0xFE,0x42,0x02,0x82,0x04,
    	0x7F,0xF8,0x04,0x00,0x07,0xF0,0x0A,0x10,0x11,0x20,0x20,0xC0,0x43,0x30,0x1C,0x0E,
    	"可",0x00,0x00,0x7F,0xFE,0x00,0x10,0x00,0x10,0x1F,0x10,0x11,0x10,0x11,0x10,0x11,0x10,
      0x11,0x10,0x11,0x10,0x1F,0x10,0x11,0x10,0x00,0x10,0x00,0x10,0x00,0x50,0x00,0x20,	
    	"抵",0x10,0x08,0x10,0x3C,0x13,0xE0,0x12,0x20,0xFE,0x20,0x12,0x20,0x12,0x20,0x13,0xFE,
    	0x1A,0x20,0x32,0x10,0xD2,0x10,0x12,0x12,0x12,0x0A,0x12,0x8A,0x53,0x26,0x22,0x12,
    	"岁",0x01,0x00,0x21,0x08,0x21,0x08,0x21,0x08,0x3F,0xF8,0x02,0x00,0x02,0x00,0x07,0xF0,
    	0x08,0x10,0x14,0x10,0x22,0x20,0x01,0x40,0x00,0x80,0x03,0x00,0x1C,0x00,0xE0,0x00,
    	"月",0x00,0x00,0x1F,0xF8,0x10,0x08,0x10,0x08,0x10,0x08,0x1F,0xF8,0x10,0x08,0x10,0x08,
    	0x10,0x08,0x1F,0xF8,0x10,0x08,0x10,0x08,0x20,0x08,0x20,0x08,0x40,0x28,0x80,0x10,
    	"漫",0x03,0xF8,0x22,0x08,0x13,0xF8,0x12,0x08,0x83,0xF8,0x40,0x00,0x47,0xFC,0x14,0xA4,
    	0x14,0xA4,0x27,0xFC,0xE0,0x00,0x23,0xF8,0x21,0x10,0x20,0xE0,0x23,0x18,0x0C,0x06,
    	"长",0x08,0x00,0x08,0x10,0x08,0x20,0x08,0x40,0x08,0x80,0x09,0x00,0x08,0x00,0xFF,0xFE,
    	0x0A,0x00,0x09,0x00,0x08,0x80,0x08,0x40,0x09,0x20,0x0A,0x18,0x0C,0x06,0x08,0x00,
    

    保存该编码文件后,将对应文字编码添加到工程文件oledfont.h当中去。

    Ⅱ、代码完善
    首先,打开第一个板块:显示学号和姓名中的工程;
    然后将如下代码添加到oled.c文件当中去:
    代码如下:

    void OLED_Display_scroll(void)
    {
        OLED_WR_Byte(0x2e,OLED_CMD);//关滚动
        OLED_WR_Byte(0x2a,OLED_CMD);//29向右,2a向左(垂直水平滚动)
        OLED_WR_Byte(0x00,OLED_CMD);//A:空字节
        OLED_WR_Byte(0x00,OLED_CMD);//B:水平起始页
        OLED_WR_Byte(0x00,OLED_CMD);//C:水平滚动速度
        OLED_WR_Byte(0x07,OLED_CMD);//D:水平结束页
        OLED_WR_Byte(0x01,OLED_CMD);//E:每次垂直滚动位移
    	OLED_WR_Byte(0x2f,OLED_CMD);//开滚动
    }
    
    

    再将该函数添加到oled.h文件当中去,如下:
    在这里插入图片描述
    其次,将led.c文件当中文字部分改成我们想要显示的内容:

    void TEST_MainPage(void)
    {	
    	GUI_ShowCHinese(0,20,16,"热爱可抵岁月漫长",1);
    	//GUI_ShowString(40,32,"64X128",16,1);
    	//GUI_ShowString(4,48,"www.lcdwiki.com",16,1);
    	delay_ms(1500);		
    	delay_ms(1500);
    }
    

    三、主要代码
    主函数main.c:

    int main(void)
    {	
    	delay_init();	    	       //延时函数初始化	  
    	NVIC_Configuration(); 	   //设置NVIC中断分组2:2位抢占优先级,2位响应优先级 	
    	OLED_Init();			         //初始化OLED  
    	OLED_Clear(0);             //清屏(全黑)
    	
    	OLED_Display_scroll();  //滚动屏幕
        delay_ms(1000);
        delay_ms(1000);
        delay_ms(1000);
        delay_ms(1000);
        delay_ms(1000);
        delay_ms(1000);
        delay_ms(1000);
    	
    	while(1) 
    	{	
    		TEST_MainPage();         //主界面显示测试
    	}
    }
    

    3)结果展示

    Ⅰ、电路连接
    stm32芯片与USB TO TTL串口的连接方式可以参考我之前的博客

    OLED和STM32芯片的连接方式,可以看工程打开时的函数开头所示,具体如下:
    在这里插入图片描述
    实物图为:
    在这里插入图片描述
    Ⅱ、代码烧录
    打开烧录软件,将以上三个部分生成的HEX文件分别烧录到芯片当中去,烧录成功如下图所示:
    在这里插入图片描述
    Ⅲ、结果展示
    烧录成功后,即可看到电子屏幕上的内容;
    ①显示学号和姓名(这里用的是同类型的内容代替
    在这里插入图片描述
    ②显示温湿度
    在这里插入图片描述

    ③滚动显示长字符
    在这里插入图片描述

    五、参考内容

    SPI协议详解
    关于0.96OLED的显示过程详解(I2C通信方式)
    基于STM32的温湿度采集——OLED显示

    六、总结

    此次实验实现了字符在OLED显示屏上显示,虽然中途遇到了很多问题,但最终通过查询资料和询问同学都得到了解决,详细内容请看参考博客。

    另外,我之前遇到在Keil中无法输入中文的问题,如有相同问题请见解决方法

    展开全文
    boomboomy 2021-11-25 11:32:39
  • 4.在OLED屏幕显示文字 1.GPIO模拟I2C(用C语言描述时序图) 首先认识一下I2C通信协议组成,主要由下面几部分组成。 1)开始与停止 总线在空闲状态时,SCL和SDA都保持着高电平。 启动信号:当SCL为高电...

     

    目录

    1.GPIO模拟I2C(用C语言描述时序q)

     

    2.用I2C驱动屏幕

    3.制作字模

    4.在OLED屏幕上显示文字

     


    1.GPIO模拟I2C(用C语言描述时序图)

    首先认识一下I2C通信协议组成,主要由下面几部分组成。

     

     

    1)开始与停止

    总线在空闲状态时,SCL和SDA都保持着高电平

    启动信号:当SCL为高电平时,SDA由高到低的跳变,表示产生一个起始条件。

    停止信号:当SCL为高电平时,SDA由低到高的跳变,表示产生一个停止条件。

     

    #define I2C_SCL_L() HAL_GPIO_WritePin(GPIOB, I2C_SCL_Pin, GPIO_PIN_RESET)/* I2C接口的时钟信号*/
    #define I2C_SCL_H() HAL_GPIO_WritePin(GPIOB, I2C_SCL_Pin, GPIO_PIN_SET)							
    #define I2C_SDA_L() HAL_GPIO_WritePin(GPIOB, I2C_SDA_Pin, GPIO_PIN_RESET)/* I2C接口的数据信号*/
    #define I2C_SDA_H() HAL_GPIO_WritePin(GPIOB, I2C_SDA_Pin, GPIO_PIN_SET)
    
    void I2C_Start()
    {	
    	SDA_OUT();    												
    	I2C_SCL_H();	
    	Delay_us(1);	
    	I2C_SDA_H();
    	Delay_us(1);
    	I2C_SDA_L();
    	Delay_us(1);
    	
    }
    
    void I2C_Stop()
    {
    	 SDA_OUT();
    	 I2C_SCL_H();
    	 Delay_us(1);
    	 I2C_SDA_L();
    	 Delay_us(1);
    	 I2C_SDA_H();	
    	 Delay_us(1);
    	
    }
    
    /*标准C语言中没有空语句,但在C51中常使用空指令产生延时效果,调用该函数相当于调用汇编中的NOp指令
     *延时一个机器周期的时间8M的晶振也差不多就1us*/
    void Delay_us(u32t us)                	
    {
    	u32t i;
    
            for(i = 0; i < us; i++)
    	{
    		__nop();	                											
    	}
    }
    
    void I2C_SDA_IN(void)
    {
    	GPIO_InitTypeDef GPIO_InitStruct = {0};	
    	
    	/*Configure GPIO pins :  PB9 */
    	GPIO_InitStruct.Pin = I2C_SDA_Pin;
    	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    	GPIO_InitStruct.Pull = GPIO_NOPULL;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
    }
    
    void I2C_SDA_OUT(void)
    {
    	GPIO_InitTypeDef GPIO_InitStruct = {0};
    	
    	/*Configure GPIO pins :  PB9 */
    	GPIO_InitStruct.Pin = I2C_SDA_Pin;
    	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    	GPIO_InitStruct.Pull = GPIO_NOPULL;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    	
    }
    
    void I2C_SCL_OUT(void)
    {
    	GPIO_InitTypeDef GPIO_InitStruct = {0};
    	
    	/*Configure GPIO pins :  PB8 */
    	GPIO_InitStruct.Pin = I2C_SCL_Pin;
    	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    	GPIO_InitStruct.Pull = GPIO_NOPULL;
    	GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    	HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    	
    }

    2)应答与非应答

    应答:SCL为高电平SDA为低电平表示从机产生应答

    非应答:SCL为高电平SDA为高电平表示从机产生非应答

    void I2C_Ack(void)
    {
            SDA_OUT();
    	I2C_SCL_H();
    	Delay_us(2);
    	I2C_SDA_L();
    	Delay_us(4);
    	I2C_SDA_H();
    	Delay_us(2);
    	I2C_SCL_L();
    	Delay_us(2);
    
    }
    
    void I2C_NAck(void)
    {
    	SDA_OUT();
    	I2C_SCL_H();
    	Delay_us(2);
    	I2C_SDA_H();
    	Delay_us(4);
    	I2C_SDA_L();
    	Delay_us(2);
    	I2C_SCL_L();
    	Delay_us(2);
    
    }

    3)写入一个字节

    /*I2C Write one byte*/ 
    void Write_I2C_Byte(unsigned char I2C_Byte){
    	 unsigned char i;
    	 unsigned char m,da;
    	 da=I2C_Byte;
    	 I2C_SCL_L();
     	 for(i=0;i<8;i++)
    	 {
    			m=da;
    			m=m&0x80;
    			if(m==0x80)				//0x80:1000 0000最高位为1 所以该操作是从最高位依次向低位取I2C_Byte中一个位的操作
    				{I2C_SDA_H();}
    			else 
    				I2C_SDA_L();
    			da=da<<1;
    			I2C_SCL_H();
    			Delay_us(5);	
    			I2C_SCL_L();
    			Delay_us(1);	
    	 }
    }


    2.用I2C驱动屏幕

    查看芯片手册使用各种命令实现屏幕的亮灭、清除、初始化

    oled.h

    #ifndef _OLED_H
    #define _OLED_H
    
    #define OLED_CMD  0	//写命令
    #define OLED_DATA 1	//写数据
    
    /*GPIO_WriteBit与GPIO_SetBits区别:
     *对单个IO口置0或1: GPIO_WriteBit(GPIOA,GPIO_Pin_8 , 0);
     *同时对多个IO口置1:GPIO_SetBits(GPIOD,GPIO_Pin_0 | GPIO_Pin_5 | GPIO_Pin_6);
     */
    #define I2C_SCL_L() HAL_GPIO_WritePin(GPIOB, I2C_SCL_Pin, GPIO_PIN_RESET)//SDA I2C接口的时钟信号
    #define I2C_SCL_H() HAL_GPIO_WritePin(GPIOB, I2C_SCL_Pin, GPIO_PIN_SET)							
    #define I2C_SDA_L() HAL_GPIO_WritePin(GPIOB, I2C_SDA_Pin, GPIO_PIN_RESET)//SCL I2C接口的数据信号
    #define I2C_SDA_H() HAL_GPIO_WritePin(GPIOB, I2C_SDA_Pin, GPIO_PIN_SET)
    
    typedef unsigned char   u8t;      ///< range: 0 .. 255
    void I2C_Start(); 
    void OLED_Set_Pos(unsigned char x, unsigned char y);
    void OLED_WR_Byte(unsigned dat,unsigned cmd);
    void OLED_ShowCHinese(u8t x,u8t y,u8t no,u8t font_height,u8t font_width);
    void OLED_Clear(void);
    void OLED_On(void);
    void OLED_Display_On(void);
    
    #endif
    #include "oled.h"
    
    void Write_I2C_Command(unsigned char I2C_Command){
    	 I2C_Start();
    	 Write_I2C_Byte(0x78);           					//从机地址Address(7位+0)------>dec:120 = bin:01111000 = hex:0x78
    	 I2C_Wait_Ack();	
    	 Write_I2C_Byte(0x00);					                //设置命令的显示起始线-------->列低地址
    	 I2C_Wait_Ack();	
    	 Write_I2C_Byte(I2C_Command); 
    	 I2C_Wait_Ack();	
    	 I2C_Stop();
    }
    
    /*I2C Write Data*/ 
    void Write_I2C_Data(unsigned char I2C_Data){
    	 I2C_Start();
    	 Write_I2C_Byte(0x78);							//从机地址
    	 I2C_Wait_Ack();	
    	 Write_I2C_Byte(0x40);							//设置数据的显示起始线
    	 I2C_Wait_Ack();	
    	 Write_I2C_Byte(I2C_Data);
    	 I2C_Wait_Ack();	
    	 I2C_Stop();
    }
    
    /*如果cmd为0表示要往屏幕写入命令
     *如果cmd为1表示要往屏幕写入数据
     */
    void OLED_WR_Byte(unsigned dat,unsigned cmd){
    	 if(cmd)
    	 {
    			Write_I2C_Data(dat);  	//写数据
    	 }
    	 else 
    	 {
    			Write_I2C_Command(dat);	//写命令
    	 }
    }
    
    void  OLED_Init(){	 
    	 Delay_us(200);
    	 OLED_WR_Byte(0xAE,OLED_CMD);							//关闭显示
    	
    	 OLED_WR_Byte(0x40,OLED_CMD);			 				//设置显示开始行 [5:0],行数.
    	 OLED_WR_Byte(0xB0,OLED_CMD);			   	 			//设置页地址
    
    	 OLED_WR_Byte(0xC8,OLED_CMD);							//设置串口扫描方向
    
    	 OLED_WR_Byte(0x81,OLED_CMD);							//设置对比度
    	 OLED_WR_Byte(0xFF,OLED_CMD);							
    
    	 OLED_WR_Byte(0xA1,OLED_CMD);							//段重定向设置
    
    	 OLED_WR_Byte(0xA6,OLED_CMD);							//设置显示方式;bit0:1,反相显示;0,正常显示
    	
    	 OLED_WR_Byte(0xA8,OLED_CMD);							//设置多路传输比率
    	 OLED_WR_Byte(0x1F,OLED_CMD);
    	
    	 OLED_WR_Byte(0xD3,OLED_CMD);							//设置显示偏移量
    	 OLED_WR_Byte(0x00,OLED_CMD);
    	
    	 OLED_WR_Byte(0xD5,OLED_CMD);
    	 OLED_WR_Byte(0xF0,OLED_CMD);
    	
    	 OLED_WR_Byte(0xD9,OLED_CMD);							//设置预充电周期
    	 OLED_WR_Byte(0x22,OLED_CMD);
    	
    	 OLED_WR_Byte(0xDA,OLED_CMD);							//设置COM硬件引脚配置
    	 OLED_WR_Byte(0x02,OLED_CMD);							//[1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
    	
    	 OLED_WR_Byte(0xDB,OLED_CMD);							//设置VCOMH 电压倍率
    	 OLED_WR_Byte(0x49,OLED_CMD);
    	
    	 OLED_WR_Byte(0x8D,OLED_CMD);							//电荷泵设置
    	 OLED_WR_Byte(0x14,OLED_CMD);							
    	 OLED_WR_Byte(0xAF,OLED_CMD);							//开启显示
    	 OLED_Clear();	 
    }
    
    /*清屏*/
    void OLED_Clear(void){  
    	 u8t i,n;		    
    	 for(i=0;i<8;i++)  
    	 {  
    			OLED_WR_Byte (0xb0+i,OLED_CMD);    				//设置页地址(0~7)
    			OLED_WR_Byte (0x00,OLED_CMD);      				//设置显示位置—列低地址
    			OLED_WR_Byte (0x10,OLED_CMD);      				//设置显示位置—列高地址   
    			for(n=0;n<128;n++)
    			OLED_WR_Byte (0,OLED_DATA); 
    	 } 														//更新显示
    }
    
    /*亮屏:效果为8条白线,建议在while循环中观看效果*/
    void OLED_On(void){  
    	 u8t i,n;		    
    	 for(i=0;i<8;i++)  									 //8行(页)
    	 {  
    			/*已经通过实验验证*/
    			OLED_WR_Byte (0xb0+i,OLED_CMD);    				//设置页地址(0~7)0xb0	 b2:b代表页寻址,2是第2页; 
    			OLED_WR_Byte (0x00,OLED_CMD);      				//设置显示位置—列低地址	 03:0代表是列低位,3代表低4位为3;
    			OLED_WR_Byte (0x10,OLED_CMD);      				//设置显示位置—列高地址	 10: 1代表是列高位,0代表高3位为0;
    		
    			for(n=0;n<128;n++)
    			OLED_WR_Byte (1,OLED_DATA);			                //128列已经通过实验验证
    	 } 														//更新显示
    }
    
    void OLED_Display_On(void){
    	 OLED_WR_Byte(0X8D,OLED_CMD);  				  			//SET DCDC命令
    	 OLED_WR_Byte(0X14,OLED_CMD);  				  			//DCDC ON
    	 OLED_WR_Byte(0XAF,OLED_CMD);  				  			//DISPLAY ON
    }


    3.制作字模

     字模制作软件见百度网盘:PCtoLCD2002完美版.zip  提取码:kien

    打开”选项“参考下面这张图来配

    注意:用我这种方法显示字体时必须要满足:点阵大小与字宽大小一样


    4.在OLED屏幕上显示文字

    oledfont.h

    #ifndef __OLEDFONT_H
    #define __OLEDFONT_H 
    	   
    /*用字模生成软件生成的汉字数组这里 宽*高=64*64 */
    char Hzk16[][64]={
    	
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0xF8,0xF8,0xF0,0xF0,0xE0,0xE0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0xF8,0xF8,0xF8,0xF8,0xF0,0xE0,0xE0,0xC0,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x30,0x70,0xF0,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xE0,0xF8,0xFE,0xFF,0xFF,0xFF,0x7F,0x1F,0x0F,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x07,0x0F,0x3F,0x7F,0xFE,0xFE,0xFC,0xF8,0xF0,0xE0,0xC0,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xF0,0xF8,0xFE,0xFF,0x7F,0x3F,0x0F,0x07,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x0F,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xF8,0xE0,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x30,0x38,0x3C,0x3E,0x1F,0x0F,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x1F,0x1F,0x1F,0x1F,0x0F,0x07,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x03,0x03,0x0F,0x1F,0x1F,0x1F,0x0F,0x0F,0x07,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"小",0*/
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0xF0,0xF0,0xE0,0xC0,0xC0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xC0,0xE0,0xF0,0xF0,0xF0},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0xFF,0xFF,0xFF,0xFE,0xFE,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xFE,0xFF,0xFF,0xFF,0xFF,0xFE,0x0C,0x0C,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0xFF,0xFF},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0xFF,0xFF,0xFF,0xFF},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x80,0xFF,0xFF,0xFF,0xFF,0xFF,0x19,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xFF,0xFF,0xFF},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x7F,0x7F,0x3F,0x3F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x0F,0x0F,0x07,0x07,0x00,0x80,0xE0,0xF8,0xFF,0xFF,0xFF,0x7F,0x1F,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF},
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0x7E,0x3F,0x1F,0x0F,0x07,0x03,0x01,0x00,0x00,0x00,0x00,0x00,0x60,0x60,0xE0,0xE0,0xE0,0xE0,0xE0,0xC0,0xC0,0xC0,0xE0,0xFF,0xFF,0xFF,0xFF},/*"明",1*/	
    
    };
    
    #endif

    oled.c

    #include "oledfont.h"
    #include "oled.h"
    #include "gpio.h"
    
    /*设置汉字在屏幕上显示的坐标(位置)*/
    void OLED_Set_Pos(unsigned char x, unsigned char y)
    { 	
    	 OLED_WR_Byte(0xb0+y,OLED_CMD);
    	 OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);
    	 OLED_WR_Byte((x&0x0f),OLED_CMD); 
    } 
    
    /*显示汉字
     *注意:用我这种方法来显示汉字一定要满足用字模生成软件生成的字宽与点阵大小相同才行,否者容易乱码
     *x:表示显示的水平坐标  
     *y: 表示显示的垂直坐标 
     *no: 表示要显示的汉字(模组)在hzk[][]数组中的行号,通过行号来确定在数组中要显示的汉字
     *这里字体的宽font_width的值必须与用字模制作软件生成字模时的点阵值大小一致
     *font_height的值为用字模制作软件生成字模时字体的高,由于我的屏像素为32*128-----0~7共8页,每页4个位
     */
    void OLED_ShowCHinese(u8t x,u8t y,u8t no,u8t font_width,u8t font_height){     			    
    	u8t t, i;
            for(i=0;i<(font_height/8);i++)				/*font_height最大值为32,这张屏只有8个页(行),每页4个位*/
    	 {
    			OLED_Set_Pos(x,y+i);	
    			for(t=0;t<font_width;t++)		/*font_width最大值为128,屏幕只有这么大*/
    			{	
    					OLED_WR_Byte(Hzk16[(font_height/8)*no+i][t],OLED_DATA);
    			}		
    	 }		
    }

    main.c

    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "gpio.h"
    #include "oled.h"
    #include "stdio.h"
    
    /* Private function prototypes */
    void SystemClock_Config(void);
    
    int main(void)
    {
      
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
        OLED_Init();									
    		
      /* Configure the system clock */
        SystemClock_Config();
    
      /* Initialize all configured peripherals */
        MX_GPIO_Init();
      /* USER CODE BEGIN 2 */
        OLED_Display_On();
        OLED_On(); 
        OLED_ShowCHinese(0,0,0,64,64);        /*小*/
        OLED_ShowCHinese(64,0,1,64,64);       /*明*/
        HAL_Delay(6000);
        OLED_Clear(); 
      /* USER CODE BEGIN WHILE */
      while (1)
      {
    	OLED_On(); 
      }
    }
    
    /**
      * @brief System Clock Configuration
      * @retval None
      */
    void SystemClock_Config(void)
    {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};
      RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
      RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    
      /** Initializes the CPU, AHB and APB busses clocks 
       */
      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
      RCC_OscInitStruct.HSEState = RCC_HSE_ON;
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
      RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
      RCC_OscInitStruct.PLL.PLLM = 1;
      RCC_OscInitStruct.PLL.PLLN = 20;
      RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
      RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
      RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
    		Error_Handler();
      }
      /** Initializes the CPU, AHB and APB busses clocks 
       */
      RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                         |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
      RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
      RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
      RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
      RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    
      if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
      {
    		Error_Handler();
      }
      PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1;
      PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
      if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
      {
    		Error_Handler();
      }
      /** Configure the main internal regulator output voltage 
       */
      if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
      {
    		Error_Handler();
      }
    }
    
    void Error_Handler(void)
    {
      /* USER CODE BEGIN Error_Handler_Debug */
      /* User can add his own implementation to report the HAL error return state */
    
      /* USER CODE END Error_Handler_Debug */
    }
    
    #ifdef  USE_FULL_ASSERT
    /**
      * @brief  Reports the name of the source file and the source line number
      *         where the assert_param error has occurred.
      * @param  file: pointer to the source file name
      * @param  line: assert_param error line source number
      * @retval None
      */
    void assert_failed(char *file, uint32_t line)
    { 
      /* USER CODE BEGIN 6 */
      /* User can add his own implementation to report the file name and line number,
         tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
      /* USER CODE END 6 */
    }
    #endif /* USE_FULL_ASSERT */

     

    展开全文
    go_home_look 2019-08-10 19:06:09
  • 对应的引脚 SCL---------PB5 SDA--------PB6 一、汉字取模方式 因为SSD1306没有内部ROM,所以我们只能由STM32帮忙存储了,下面就是相关的取模方式 然后把相关的代码放到oledfont.h文件里面, 接下来就是显示这些汉字...

    对应的引脚
    SCL---------PB5
    SDA--------PB6

    一、汉字取模方式

    因为SSD1306没有内部ROM,所以我们只能由STM32帮忙存储了,下面就是相关的取模方式
    在这里插入图片描述
    然后把相关的代码放到oledfont.h文件里面,
    在这里插入图片描述
    接下来就是显示这些汉字了

    #include "delay.h"
    #include "key.h"
    #include "usart.h"
    #include "timer.h"
    #include "oled.h"
    
    //GND  接电源地
    //VCC  接5V或3.3v电源
    //D0   接(SCL)
    //D1   接(SDA)
    
    int main(void)
    {		
    	 delay_init();	    	 //延时函数初始化	  
    	 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
    	 OLED_Init(); //OLED初始化
    	 OLED_Clear();
    	 
       while(1)
    	 {
    
    				OLED_ShowChinese(0,0,0,16);//"你"
    				OLED_ShowChinese(16,0,1,16);//"号"
    				OLED_ShowChinese(32,0,2,16);//","
    				OLED_ShowChinese(48,0,3,16);//"世"
    				OLED_ShowChinese(64,0,4,16);//"界"
    				OLED_Refresh_Gram();
    	 }
    }
     
    
    
    
    

    在这里插入图片描述

    总结

    这一节属于补充,篇幅有点短,还有不对的地方,希望大家多多包涵,相关的取模的软件和相关的代码会放到最后,失效了可以私信我
    链接:https://pan.baidu.com/s/1iheTy0KGshB_t7ZSSOvWvg
    提取码:ybpb

    展开全文
    qq_33931978 2021-11-02 21:03:48
  •   后来我感觉0.96的OLED屏幕太小了,搞个更大的全彩屏,换成STM32F4系列单片机,跑个ucos,运行NES模拟器,然后我是不是就能拳打任天堂,脚踢PSP了?哈哈哈   打地鼠的游戏,可以显示生命值,...

    屏幕显示英文字符

      有个屏幕,掌机的可玩性可以大大增强,打地鼠玩腻了,可以玩个贪吃蛇,俄罗斯方块,以及其他需要开动想象力的游戏。并且,以前总是玩别人的游戏,现在可以自己写游戏,岂不美哉。
      后来我感觉0.96的OLED屏幕太小了,搞个更大的全彩屏,换成STM32F4系列单片机,跑个ucos,运行NES模拟器,然后我是不是就能拳打任天堂,脚踢PSP了?哈哈哈
    在这里插入图片描述
      打地鼠的游戏,可以显示生命值,得分,关卡或难度。所以要定义几个变量。

    //main.c
    //默认参数
    #define LIFE_NUM  3          // 默认几条命
    
    //全局变量定义
    u8 life = LIFE_NUM;    //生命
    u32 score = 0;          //得分记录
    u8 level = 1;           //当前难度,数字越大难度越高
    

      一般来说,显示英文字符都会有配套的显示函数。我参考的代码也提供了这些函数。直接调用即可。formatScreen用于清屏,就像老师板书之前要擦黑板一样。showString用于在指定的坐标显示英文字符,每个参数的含义可以跳转,看函数说明。

    int main(void)
    {
    	LED_Init();
    	KEY_Init();
    	delay_init();
    	initIIC();
    	initOLED();
    	
    	formatScreen(0x00);
    	showString(0,0,"yoodao",FONT_16_EN);
    	showString(0,2,"life:",FONT_16_EN);
    	showString(0,4,"level:",FONT_16_EN);
    	showString(0,6,"score:",FONT_16_EN);
    	
    	showNumber(56,2,life,DEC,8,FONT_16_EN);
    	showNumber(56,4,level,DEC,8,FONT_16_EN);
    	showNumber(56,6,score,DEC,8,FONT_16_EN);
    	
    	while(1)
    	{
    		score++;
    		showNumber(56,6,score,DEC,8,FONT_16_EN);
    		delay_ms(1000);
    	}
    }
    
    

    屏幕显示汉字

      汉字的显示可能就稍微复杂些,因为我们选用的屏幕没有中文字库,所以要自行取模。在取模之前,我先试了试人家显示汉字的函数showCNString与显示图片的函数showImage’’,成功。
    在此感谢风媒电子。

    //main.c
    	formatScreen(0x00);
    	showImage(0,0,128,8,FM_LOGO_ENUM);
    	delay_ms(1000);
    //	showString(0,0,"yoodao",FONT_16_EN);
    	showCNString(0,0,"风媒电子",FONT_16_CN);
    

      然后把显示的LOGO和汉字改一改。

    	formatScreen(0x00);
    //	showImage(0,0,128,8,Y_LOGO_ENUM);
    //	delay_ms(1000);
     //   formatScreen(0x00);
    //	showString(0,0,"yoodao",FONT_16_EN);
    	showCNString(0,0,"小极客打地鼠掌机",FONT_16_CN);
    

      然后用取模软件,生成“小极客打地鼠掌机”的字模
      取模软件的使用:
    在这里插入图片描述
      取得字模以后,替换原先的汉字数组。

    /************************************16*16 汉字************************************/
    const unsigned char CN1616[8][32] = 
    {
    	{0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x20,0x40,0x80,0x00,0x00,0x08,0x04,0x03,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00,0x01,0x0E,0x00},/*"小",0*/
    
    {0x10,0x10,0xD0,0xFF,0x90,0x10,0x02,0x02,0xFE,0x02,0x02,0x62,0x5A,0xC6,0x00,0x00,0x04,0x03,0x00,0xFF,0x00,0x43,0x30,0x8F,0x80,0x43,0x2C,0x10,0x2C,0x43,0x80,0x00},/*"极",1*/
    
    {0x10,0x0C,0x84,0x44,0xB4,0xA4,0x25,0x26,0x24,0xA4,0x64,0x24,0x04,0x14,0x0C,0x00,0x04,0x04,0x04,0xFA,0x4A,0x4A,0x49,0x49,0x49,0x4A,0x4A,0xFA,0x04,0x04,0x04,0x00},/*"客",2*/
    
    {0x10,0x10,0x10,0xFF,0x10,0x90,0x04,0x04,0x04,0x04,0xFC,0x04,0x04,0x04,0x04,0x00,0x04,0x44,0x82,0x7F,0x01,0x00,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00},/*"打",3*/
    
    {0x20,0x20,0x20,0xFF,0x20,0x20,0x80,0xF8,0x80,0x40,0xFF,0x20,0x10,0xF0,0x00,0x00,0x10,0x30,0x10,0x0F,0x08,0x08,0x00,0x3F,0x40,0x40,0x5F,0x42,0x44,0x43,0x78,0x00},/*"地",4*/
    
    {0x00,0x00,0x7E,0x4A,0x4A,0x49,0x40,0x40,0x40,0x4A,0x4A,0x4A,0x7E,0x00,0x00,0x00,0x00,0x00,0xFF,0x80,0x49,0x12,0x00,0xFF,0x80,0x49,0x12,0x00,0x3F,0x40,0xF0,0x00},/*"鼠",5*/
    
    {0x00,0x10,0x0C,0x05,0x76,0x54,0x54,0x57,0xD4,0xD4,0xF6,0x85,0x14,0x0C,0x00,0x00,0x00,0x10,0x15,0x15,0x15,0x55,0x95,0x7F,0x14,0x14,0x14,0x14,0x14,0x10,0x00,0x00},/*"掌",6*/
    
    {0x10,0x10,0xD0,0xFF,0x90,0x10,0x00,0xFE,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x04,0x03,0x00,0xFF,0x00,0x83,0x60,0x1F,0x00,0x00,0x00,0x3F,0x40,0x40,0x78,0x00},/*"机",7*/
    
    };
    
    unsigned char CN1616_Index[] = "小极客打地鼠掌机"; //16*16中文字库索引表
    

      编译,下载程序,可以看出汉字能够正常显示出来。
      这里有个注意事项,工程中所有文件设置的编码格式必须统一。因为编译器不认识汉字,只认识汉字的编码,同一个汉字,其UTF-8与GB2312的编码是不一样。不同的编码在编译器看来,就是不同的汉字。所以要保证,工程里所有文件的编码方式都一致。我发的源码工程采用UTF-8的编码。
    在这里插入图片描述

    图片的显示

      显示图片与显示汉字的原理是一样的,毕竟,,,,汉字也是被当成图片处理的嘛。话说,外国人眼里的汉字,可能就是图片。
    在这里插入图片描述
    在这里插入图片描述
      我打算显示的图片是我自己的LOGO,像是刻在石碑上的字母Y。也需要借助取模软件把图片变成一个数组。
    在这里插入图片描述
      然后模仿人家的程序,把数组名字和枚举类型修改下。

    //main.c
    showImage(0,0,128,8,Y_LOGO_ENUM);
    //OLED.c
    /**
     * 功能:在制定区域显示图片
     * 参数:
     * 		x:x轴坐标 0-127
     * 		y:y轴坐标 0-7
     * 		x_len:显示区域横坐标长度 0-128
     *		y_len:显示区域纵坐标长度 0-8
     * 		image_index:图片枚举索引
     * 说明:该函数一般用于显示全屏LOGO,另外灵活运用可以显示PPT切换特效
     * 	
     * 返回值:None
     */ 
    void showImage(u8 xpos, u8 ypos,u8 x_len, u8 y_len,IMAGE_INDEX  image_index)
    { 	
    	u16 i,j;
    
    	for(i=0;i<y_len;++i)					//页地址控制
    	{
    		setPos(xpos,ypos++);
    		
    		for(j=i*128+xpos;j<i*128+x_len;++j) //列地址控制
    		{
    			switch(image_index)
    			{
    				case Y_LOGO_ENUM        :writeData(Y_LOGO[j]);        break;
    
    				default                  :                              break;
    			}
    			
    		}
    	}
    } 
    
    

      显示大LOGO一次成功。
    在这里插入图片描述
      接着我设计了几个位图,都是32像素见方的,分别是上下左右,ABCD,圆和圆圈。万一以后要扩展炫舞或者太鼓达人的游戏呢,
    在这里插入图片描述
      代码也相应修改了下。数组里内容太长,就不列出来了。数组和位图都上传了。

    //main.c
    	//显示几个小LOGO
    	formatScreen(0x00);
    	showImage(0,0,32,4,UP_LOGO_ENUM);
    	showImage(32,0,32,4,DOWN_LOGO_ENUM);
    	showImage(64,0,32,4,LEFT_LOGO_ENUM);
    	showImage(96,0,32,4,RIGHT_LOGO_ENUM);
    	showImage(0,4,32,4,A_LOGO_ENUM);
    	showImage(32,4,32,4,B_LOGO_ENUM);
    	showImage(64,4,32,4,C_LOGO_ENUM);
    	showImage(96,4,32,4,D_LOGO_ENUM);
    	delay_ms(1000);
    

      显示图片的函数也做了修改

    void showImage(u8 xpos, u8 ypos,u8 x_len, u8 y_len,IMAGE_INDEX  image_index)
    { 	
    	u16 i,j;
    
    	for(i=0;i<y_len;++i)					//页地址控制
    	{
    		setPos(xpos,ypos++);		
    		for(j=i*128+xpos;j<i*128+x_len;++j) //列地址控制
    		{
    			switch(image_index)
    			{
    				case Y_LOGO_ENUM        :writeData(Y_LOGO[j]);        break;
    				case UP_LOGO_ENUM       :writeData(UP_LOGO[j]);       break;
    				case DOWN_LOGO_ENUM     :writeData(DOWN_LOGO[j]);     break;
    				case LEFT_LOGO_ENUM     :writeData(LEFT_LOGO[j]);     break;
    				case RIGHT_LOGO_ENUM    :writeData(RIGHT_LOGO[j]);    break;
    				case A_LOGO_ENUM        :writeData(A_LOGO[j]);        break;
    				case B_LOGO_ENUM        :writeData(B_LOGO[j]);        break;
    				case C_LOGO_ENUM        :writeData(C_LOGO[j]);        break;
    				case D_LOGO_ENUM        :writeData(D_LOGO[j]);        break;
    				case CIRCLE_LOGO_ENUM   :writeData(CIRCLE_LOGO[j]);   break;
    				case EMPTY_LOGO_ENUM    :writeData(EMPTY_LOGO[j]);    break;
    				default                 :                             break;
    			}//switch			
    		}//for j
    	}//for i
    } 
    

    结果却出现了乱码,这是怎么回事?
    在这里插入图片描述

    解决乱码

      我首先分析了showImage函数。函数的说明中,这句话引起了我的注意:
    说明:该函数一般用于显示全屏LOGO,另外灵活运用可以显示PPT切换特效
      全屏LOGO?全屏是128×64像素 ,我的Y-LOGO显示没有问题。而箭头是32×32像素的图片,显示出来却有问题,这说明,此函数可以显示128×64的图片,不能显示32×32的图片。
      然后我开始分析显示函数的坐标体系。屏幕是黑白的,共有128×64个像素,每个像素有黑白两种状态,正好对应0和1两种状态,所以一个图片需要128×64个bit(二进制位),也就是需要128×8个byte(字节)来表示。x坐标取值范围是0-127,y坐标的取值范围是0-7,所以能猜出来,竖直方向上的8个二进制位,组成了一个字节。这对应了在使用取模软件时的一个细节:取模方式为列行式。先取最左边一列上的8个点,再往右取一列上的8个点。
    在这里插入图片描述
      还有一个需要说明的点:取字方向是低位在前。为了说明位图与数组的关系,我们来分析一下上箭头的图与数组的关系。
    在这里插入图片描述
      取模结果的前16位是
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xC0,0xE0,0xF0,0xF8,0xFC,0xFC,0xFC,
      为了看的更清楚,我把位图与取模得到的数组结合在一起了。已经具体到这种地步了,就不用再说明图片是怎么变成数组了吧?
    在这里插入图片描述
      接下来把数组还原为图片。
      首先要明确一个问题:数组是一维的,图片是二维的。是否可以使用二维数组?可以,但没有必要。用一维的数组储存二维的图片,假设图片的尺寸是x×y,那么图片中个点的坐标系是这样的。

    01x-2
    xx+12x-2
    (y-1)x(y-1)x+1

      最后一个点也可以写为(y-1)x+(x-1),与xy-1在数学上等价。
      储存到数组中是这样的:

    01。。。x-1xx+1。。。(y-1)x(y-1)x+1。。。

      就像大家去操作做广播体操,做操时,排成阵列。然而操场的门很小,只能让一个人通过,因此离开操场时,要排成一队。
      明白这一点以后,来分析代码

    	for(i=0;i<y_len;++i)	//1				//页地址控制
    	{
    		setPos(xpos,ypos++); //2		
    		for(j=i*128+xpos;j<i*128+x_len;++j) //3   //列地址控制
    		{
    			switch(image_index)
    			{
    				case Y_LOGO_ENUM        :writeData(Y_LOGO[j]);        break;//4
    			}//switch			
    		}//for j
    

      代码1,i<y_len 说明一次显示一横行。但由于竖直方向上的8个二进制位,组成了一个字节,为了方便描述,我们假设数值方向上的8个二进制位是一个排,所以i<y_len 实际上显示了一横“排”。
    在这里插入图片描述
      代码2,设置原点,直接使用了传入的坐标系,没毛病
      代码4,把参数j作为数组索引来显示,如果想显示第一个“像素排”,那么j = 0,如果想显示第二排的第一个“像素排”,那么j = 0+x_len。
      那么代码3,为什么i要×128?
      问题就出现在这里了。显示大LOGO的时候,这个函数是可以用的,因为大LOGO的宽度正好是128。显示32×32的小图片的时候,函数就用不了了,因为,图片的宽度是32啊!
      稍作修改,用传入的图片宽度参数替换掉128。大功告成。

    //for(j=i*128+xpos;j<i*128+x_len;++j) //3   //列地址控制
    for(j=i*x_len;j<i*x_len+x_len;++j) //列地址控制
    

    在这里插入图片描述

    代码其实还可以优化

      我发现屏幕刷新的速度太慢了,玩游戏的朋友都知道,FPS太低,画面看起来就卡顿。这怎么能忍,FPS太低会影响我超神的啊!
    在这里插入图片描述
      只显示分数当然没问题,但如果需要玩太鼓达人,或者炫舞之类,依赖屏幕刷新图片的游戏,肯定会卡顿。我分析了一下,可能是IIC总线太慢。代码里IIC操作的延迟都是1us,OLED屏幕最小支持350ns。但是STM32做纳秒级的延时,理论计算并不可靠,需要实测,且提升有限,未做尝试。
      也考虑过使用SPI,理论上快不少。用一个大数组把屏幕上所有点的信息都记下来,数组也就1024个元素就能存储所有的的信息,然后使用定时器+DMA+SPI,每隔一小段时间就刷新一下屏幕,只修改有变化的像素点,理论上速度快很多。暂时先做打地鼠的游戏,有空再折腾吧。
    最终代码在这里。

    展开全文
    geek_monkey 2019-01-18 14:09:28
  • u011383596 2020-07-19 20:26:20
  • weixin_46628481 2021-11-21 15:47:01
  • 2.07MB qq_26975307 2019-10-09 09:36:53
  • Azuresmh 2019-01-25 22:01:40
  • 5星
    2.4MB sqc_hik 2018-06-01 14:30:22
  • 2.93MB weixin_42674361 2021-09-30 12:47:32
  • m0_52869979 2021-11-24 14:35:25
  • 5星
    3.35MB qq_18305599 2015-08-07 19:49:44
  • qq_39492932 2019-02-12 18:14:34
  • 5星
    3MB cqwertyuio 2019-07-29 16:31:28
  • 6.21MB qq_26815959 2019-04-02 10:55:54
  • 3.31MB weixin_42683392 2021-10-01 11:48:20
  • weixin_43704348 2020-12-27 19:30:55
  • 4星
    7.07MB ur_eyes_ 2018-01-10 20:33:04

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,636
精华内容 654
关键字:

stm32oled屏幕显示