STM32-12-OLED模块

STM32-01-认识单片机
STM32-02-基础知识
STM32-03-HAL库
STM32-04-时钟树
STM32-05-SYSTEM文件夹
STM32-06-GPIO
STM32-07-外部中断
STM32-08-串口
STM32-09-IWDG和WWDG
STM32-10-定时器
STM32-11-电容触摸按键

文章目录

  • 1. OLED显示屏介绍
  • 2. OLED驱动原理
  • 3. OLED驱动芯片简介
  • 4. 字符显示原理
  • 5. OLED基本驱动步骤
  • 6. 代码实现

1. OLED显示屏介绍

  • OLED的简介

    OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display,OLED)。OLED可按发光材料分为两种:小分子OLED和高分子 OLED(也可称为PLED)。OLED是一种利用多层有机薄膜结构产生电致发光的器件,它很容易制作,而且只需要低的驱动电压,OLED由于同时具备自发光(不需背光源)对比度高厚度薄视角广反应速度快功耗低柔性好等优异特性,目前主要用于显示领域,OLED在节能照明领域的开发也成为全球趋势。

  • 优点:

    • 自发光:OLED每个像素点都是自发光的,不需要额外的背光源,因此具有更高的发光效率。
    • 低功耗: 因为OLED只在需要的像素点发光,而不需要像LCD那样通过背光光源进行筛选,所以在显示暗色画面时可以大幅降低功耗,节能环保。
    • 高对比度: OLED屏幕具有极高的对比度,因为黑色像素可以完全关闭,而白色像素可以发出非常亮的光,这使得OLED屏幕在显示深色和亮色之间的差异更加清晰,色彩更加鲜艳。
  • 缺点:

    • 烧屏:长时间显示相同的图像会导致该图像在屏幕上留下痕迹,即所谓的“烧屏”现象。这在某些情况下可能会降低显示屏的寿命 。
    • 价格昂贵:由于制造成本较高,OLED显示屏通常比LCD屏幕更昂贵,这使得它在某些应用领域的普及受到一定限制。
    • 低频频闪:一些OLED显示屏在低亮度下可能存在频闪问题,可能会引起一些用户的不适 。
  • 应用场景

    • 手机/平板电脑:由于OLED显示屏的薄、轻、灵活等特点,它被广泛应用于高端智能手机和平板电脑的显示屏上。
    • 电视:OLED电视具有出色的画质表现,逐渐成为高端电视市场的主流产品之一。
    • 手表/手环:OLED显示屏由于其小尺寸、低功耗等特点,被广泛应用于智能手表、智能手环等可穿戴设备上。
  • ATK_OLED模块的简介
    在这里插入图片描述

    • 特点:

      • 尺寸和分辨率:该模块尺寸较小,为0.96英寸,分辨率为128×64像素,适合于一些对显示尺寸要求较小的场景。

      • 高亮度:模块具有高亮度的特点,能够在不同环境下清晰显示内容。

      • 集成升压电路:内部集成了DCDC升压电路,只需3.3V的供电就可以正常工作,简化了外部电路设计。

      • 驱动IC:采用了SSD1306驱动IC,该驱动IC具有稳定性好、兼容性强的特点,可以保证模块的稳定性和可靠性。

      • 通信接口:支持多种通信接口,包括8位6800并口8位8080并口4线SPI以及IIC等四种方式,可以根据实际需求选择合适的接口进行通信。

      • 接口配置:通过BS1和BS2引脚的配置,可以选择不同的通信接口方式,具体配置如下:
        在这里插入图片描述

  • 引脚说明

    拥有8数据引脚5控制引脚
    在这里插入图片描述

    • CS(Chip Select):OLED片选信号,用于选择OLED模块,低电平有效。
    • WR(Write):向OLED写入数据的控制信号。
    • RD(Read):向OLED读取数据的控制信号。
    • RST(Reset):硬复位OLED模块的控制信号,低电平有效,用于将OLED模块复位到初始状态。
    • DC(Data/Command):数据/命令标志线,用于区分发送的是数据还是命令。当DC为低电平时,表示发送的是命令;当DC为高电平时,表示发送的是数据
    • D[7:0]:8位双向数据线,用于传输数据。
  • OLED模块硬件连接图

    分类CSRDWRD[7:0]RSTDC
    MINI板PC9PC6PC7PB0~PB7NRSTPC8
    精英板PD6PG13PG14PC0~7PG15PD3
    战舰板PD6PG13PG14PC0~7PG15PD3

在这里插入图片描述

2. OLED驱动原理

在这里插入图片描述

  1. 驱动芯片选择:选择适合的OLED驱动芯片是驱动OLED屏的第一步。常用的OLED驱动芯片包括SSD1306、SSD1351、SH1106等,根据需要选择合适的驱动芯片。
  2. 时序控制:根据所选的驱动芯片的通信接口(如8080时序、6800时序、SPI、I2C等),实现相应的时序控制。这包括控制数据/命令信号、写入数据、读取数据等操作的时序要求。
  3. 初始化序列:每款OLED屏都有自己的初始化序列,用于初始化屏幕的各种参数和设置。这个序列一般由OLED屏厂家提供,需要在驱动程序中正确执行,以确保屏幕正常工作。
  4. 绘图功能实现:通过画点函数和读点函数来实现对屏幕上的像素点进行操作。画点函数用于向屏幕指定位置写入像素点的颜色,而读点函数则用于读取屏幕上指定位置的像素点的颜色信息。基于这两个函数,可以实现各种图形绘制、文字显示等功能。
  • SSD1306工作时序
    在这里插入图片描述
    在这里插入图片描述

    具体代码实现

    oled_wr_byte()用于向SSD1306写入一个字节的数据或命令。根据传入的cmd参数决定是写入数据还是写入命令。

    void oled_wr_byte(uint8_t data, uint8_t cmd) 
    {	
    	OLED_RS (cmd);	     /* 数据类型,由传参决定,此处设置为1,表示写数据 */
        OLED_CS ( 0 );	     /* 拉低片选线,选中SSD1306 */
        OLED_WR ( 0 );	     /* 拉低WR线,准备数据 */
        oled_data_out(data); /* WR低电平期间,准备数据 */
     	OLED_WR ( 1 );	     /* 在WR上升沿,数据发出 */
        OLED_CS ( 1 );	     /* 取消片选 */
     	OLED_RS ( 1 );	     /* 释放RS线,恢复默认 */
    }
    

    oled_data_out() 用于将数据写入到数据端口(D[7:0])。在这个函数中,首先读取GPIOC的输出数据寄存器(ODR),然后根据掩码操作将低8位清零,再将传入的数据(data)的低8位与之前的高8位进行或运算,最终将结果写入到GPIOC的输出数据寄存器中,即完成了数据的输出操作。

    void oled_data_out(uint8_t data)
    {
        	GPIOC->ODR = (GPIOC->ODR & 0XFF00) | (data & 0X00FF);
    }
    

    读时序的过程:
    在这里插入图片描述

    1. 设置 DC 为高(数据)/低(命令);
    2. 拉低片选 CS 信号,选中 SSD1306;
    3. 设置 RD 起始电平为低;
    4. 设置 RD 为高电平,读取数据;
    5. CS设置为1,释放CS;
    6. DC设置为1,释放DC引脚,恢复默认。

3. OLED驱动芯片简介

  • OLED驱动芯片用于控制OLED的显示功能和效果。SSD1306指令比较多,这里介绍常用的命令。

    指令(HEX)各位描述名称说明
    D7D6D5D4D3D2D1D0
    0xAE/0xAF1010111X0设置显示开关X0 = 0,关闭显示 X0 = 1,开启显示
    0xB0~0xB710110X2X1X0设置页地址X[2:0]:0-7对应页0-7
    0x00~0x0F0000X3X2X1X0设置列地址(L4)设置8位列地址的低四位
    0x10~0x1F0001X3X2X1X0设置列地址(H4)设置8位列地址的高四位

    OLED驱动芯片(如SSD1306)是用于控制OLED显示屏的关键组件,它负责管理显示的内容、亮度、对比度等参数,并将数据传输到OLED屏幕上以实现图像或文本的显示。

    • 常用命令:

      1. 0xAE / 0xAF - 设置显示开关

      D7:控制GRAM的写入位置,通常为1。

      D6:控制显示开关。

      X0:0为关闭显示,1为开启显示。

      1. 0xB0 ~ 0xB7 - 设置页地址

      X2, X1, X0:设置页地址,范围通常为07,对应页07。

      1. 0x00 ~ 0x0F - 设置列地址(L4)

      X3, X2, X1, X0:设置8位列地址的高四位。

      1. 0x10 ~ 0x1F - 设置列地址(L4)

      X3, X2, X1, X0:设置8位列地址的高四位。

  • 什么是GRAM:
    在这里插入图片描述

    GRAM是图形显示数据RAM(Graphic Display Data RAM)的缩写,用于存储要在显示屏上显示的图像或文本的位模式数据。它是一块位映射的静态RAM,大小为128×64位,可以分为8页,每页对应显示屏的一行像素。GRAM保存的数据决定了OLED显示屏上每个像素点的状态,从而实现图像或文本的显示。

  • 什么是页地址模式
    在这里插入图片描述

    1. 发送内存地址模式命令(0x20):这个命令用于设置内存地址模式,以便指定将要发送的地址模式类型。
    2. 发送页地址模式命令(0x02):这个命令用于选择页地址模式。一旦设置了页地址模式,后续的操作将按照页地址模式进行。
  • 解决显示覆盖
    在这里插入图片描述

    实现代码

    static uint8_t g_oled_gram[128][8];	 /* OLED的显存 */
    void oled_refresh_gram(void) 
    {	
    	uint8_t 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( g_oled_gram[ n ][ i ], OLED_DATA) ;
    		}
    	}
    }
    

    GRAM和OLED屏幕坐标对应关系表
    在这里插入图片描述

    在OLED屏幕中,每个像素点的显示状态通常由一个字节(8位)来表示,因此需要将屏幕分成多个页面,每个页面有128列,每列有8行,每一行代表一个像素点的状态。

    根据这个规则,可以使用一个二维数组来表示整个屏幕的显存,数组名为OLED_GRAM,数组的每个元素表示一个像素点的状态。在二维数组中,第一个索引表示x轴),第二个索引表示y轴/8)。

  • 画点函数代码实现:

    void oled_draw_point(uint8_t  x, uint8_t  y, uint8_t  dot) 
    {	
    	uint8_t pos, bx, temp = 0;
    	if (x > 127 || y > 63)	 return;	 /* 超出范围了 */ 		
    	pos = y / 8; 		/*  页地址 */
    	bx = y % 8; 		/*  计算y在对应字节里面的位置 */
    	temp = 1 << bx;	/*  转换后y对应的bit位置 */
    	
    	if ( dot )	/*  画实心点 */
    		g_oled_gram[ x ][ pos ] |= temp;
    	else
    		g_oled_gram[ x ][ pos ] &= ~temp;
    }
    

    这段代码的主要功能是在OLED显示屏的特定位置上绘制或清除一个点。它通过设置或清除对应位置的显存(GRAM)位来实现这一功能。

    变量解释:

    • x: 点的横坐标(0-127)
    • y: 点的纵坐标(0-63)
    • dot: 决定是绘制点(1)还是清除点(0)

    具体实现:

    1. 边界检查 : 检查给定的坐标是否超出显示范围,如果超出则直接返回。
    2. 计算页地址和位偏移 : OLED显示屏通常按页(每页8个像素高)进行组织。pos计算了y所在的页地址,bx计算了y在对应字节中的位位置。
    3. 计算位掩码 : 根据bx的值,创建一个位掩码,用于设置或清除GRAM中的特定位。
    4. 绘制或清除点 :根据dot的值,选择绘制或清除点。g_oled_gram是显存数组(GRAM),其结构是uint8_t g_oled_gram[128][8],表示128列,每列有8个页(共64行)。
    • 如果dot为1,使用按位或操作(|=)在对应位置设置位。
    • 如果dot为0,使用按位与操作(&= ~temp)清除对应位置的位。

4. 字符显示原理

  • ASCII码:

    基于拉丁字母的一套电脑编码系统,共128个字符:33个控制字符 和 95个显示字符。
    在这里插入图片描述

  • 什么是内码:

    计算机使用的二进制字符编码,就叫内码,简称编码。字符包含各种文字!

    字符集编码长度说明
    ASCII1个字节拉丁字母编码,仅128个编码,最简单
    GB23122个字节简体中文字符编码,包含约6000多汉字编码
    GBK2个字节对GB2312的扩充,支持繁体中文,约2W多汉字编码
    BIG52个字节繁体中文字符编码,在台湾、香港用的多
    UNICODE一般2个字节国际标准编码,支持各国文字
  • 如何将字符显示在OLED上
    在这里插入图片描述

    1. 显示字符,必须先有其点阵数据,点阵数据的集合,叫做字库 ;
    2. 单片机根据点阵数据按取模方向进行描点还原,就能显示字符;
    3. ASCII字符宽度 = 汉字宽度的一半。
  • 字符显示代码

    uint8_t temp, t1, t;
    uint8_t y0 = y;			/* 保存y的初值 */
    
    for(t = 0; t < 16; t++)      		/* 总共16个字节,要遍历一遍 */
    {
            temp = oled_ascii_1608[t];	/* 依次获取点阵数据 */
    
            for(t1 = 0; t1 < 8; t1++)
            {
    	    if(temp & 0X80)        	 /* 这个点有效,需要画出来 */
    	            oled_draw_point(x, y, mode);
    	    else				/* 这个点无效,不需要画出来 */
    		oled_draw_point(x, y, !mode);
     
                   temp <<= 1;            	 /* 低位数据往高位移位,最高位数据直接丢弃 */
                   y++;                   		 /* y坐标自增 */
    
                   if((y - y0) == 16)    	/* 显示完一列了 */
                   {
                           y = y0;             		/* y坐标复位 */
                           x++;                		/* x坐标递增 */
    	          break;                           /* 跳出 for循环 */
                    }
            }
    }
    

    这段代码的功能是在OLED显示屏上绘制一个16x16像素的点阵字符。代码使用的是一个包含16个字节的点阵数据数组oled_ascii_1608,每个字节表示8个像素的行信息。

    变量解释

    • temp: 当前字节的点阵数据。
    • t1: 用于内层循环,遍历每个字节的8个位。
    • t: 用于外层循环,遍历16个字节。
    • y0: 初始的y坐标,用于列绘制结束后的复位。
    1. 保存初始的y坐标
    uint8_t y0 = y; // 保存y的初值
    

    这行代码保存了初始的y坐标,以便在每列绘制结束后复位y坐标。

    1. 外层循环:遍历16个字节
    for(t = 0; t < 16; t++) // 总共16个字节,要遍历一遍
    {
        temp = oled_ascii_1608[t]; // 依次获取点阵数据
    

    外层循环遍历点阵字符的16个字节。每个字节包含一行的8个像素信息。

    1. 内层循环:遍历每个字节的8个位
    for(t1 = 0; t1 < 8; t1++)
    {
        if(temp & 0X80) // 这个点有效,需要画出来
            oled_draw_point(x, y, mode);
        else // 这个点无效,不需要画出来
            oled_draw_point(x, y, !mode);
    
        temp <<= 1; // 低位数据往高位移位,最高位数据直接丢弃
        y++; // y坐标自增
    

    内层循环遍历当前字节的8个位,依次绘制每个像素点。

    • temp & 0x80: 检查当前最高位是否为1。如果为1,表示该点需要绘制。
    • oled_draw_point(x, y, mode): 绘制有效点。
    • oled_draw_point(x, y, !mode): 绘制无效点(即清除点)。
    • temp <<= 1: 将低位数据左移一位,最高位数据被丢弃。
    • y++: y坐标自增,移到下一行。
    1. 检查并处理列结束
    if((y - y0) == 16) // 显示完一列了
    {
        y = y0; // y坐标复位
        x++; // x坐标递增
        break; // 跳出for循环
    }
    

    如果当前列已经绘制了16个像素点(即一列绘制完毕),则:

    • 将y坐标复位到初始值y0
    • x坐标递增一列。
    • 跳出内层循环,继续绘制下一字节。
  • ASCII字库制作:
    在这里插入图片描述
    在这里插入图片描述

    1. 设置字体大小 ;
    2. 设置字模选项 :阴码,逐列式,顺向,十六进制,C51格式;
    3. 输入ASCII字符集(95个);
    4. 生成字模。

5. OLED基本驱动步骤

在这里插入图片描述

6. 代码实现

  • 功能1:

    1. 在OLED屏幕上画一个点(不建立OLED_GRAM)
    2. 在OLED屏幕上显示一个字符‘A’(建立OLED_GRAM)
  • 初始化OLED(SSD1306)函数

    void oled_init(void)
    {
        GPIO_InitTypeDef gpio_init_struct;
        
        __HAL_RCC_GPIOC_CLK_ENABLE();     /* 使能PORTC时钟 */
        __HAL_RCC_GPIOD_CLK_ENABLE();     /* 使能PORTD时钟 */
        __HAL_RCC_GPIOG_CLK_ENABLE();     /* 使能PORTG时钟 */
        
        /* PC0 ~ 7 设置 */
        gpio_init_struct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;                
        gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;        /* 中速 */
        HAL_GPIO_Init(GPIOC, &gpio_init_struct);                /* PC0 ~ 7 设置 */
    
        gpio_init_struct.Pin = GPIO_PIN_3|GPIO_PIN_6;           /* PD3, PD6 设置 */
        gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;        /* 中速 */
        HAL_GPIO_Init(GPIOD, &gpio_init_struct);                /* PD3, PD6 设置 */
        
        gpio_init_struct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
        gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;            /* 推挽输出 */
        gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
        gpio_init_struct.Speed = GPIO_SPEED_FREQ_MEDIUM;        /* 中速 */
        HAL_GPIO_Init(GPIOG, &gpio_init_struct);                /* WR/RD/RST引脚模式设置 */
    
        OLED_WR(1);
        OLED_RD(1);
    
        OLED_CS(1);
        OLED_RS(1);
        
        /* 复位时序 */
        OLED_RST(0);
        delay_ms(100);
        OLED_RST(1);
    
        oled_wr_byte(0xAE, OLED_CMD);   /* 关闭显示 */
        oled_wr_byte(0xD5, OLED_CMD);   /* 设置时钟分频因子,震荡频率 */
        oled_wr_byte(80, 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(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(0xC8, OLED_CMD);   /* 设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数 */
        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);   /* 开启显示 */
        
    }
    

    函数首先使能相关的时钟,配置相关的GPIO,包括输出模式,上下拉,速度等,之后拉高OLED相关的控制位,接着进行OLED复位操作,再接着就是OLED的相关初始化操作,通过oled_wr_byte()进行写命令进行控制。

  • OLED清屏函数

    void oled_clear(void)
    {
        uint8_t 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(0x00, OLED_DATA);
            }
        }
    }
    

    此函数用于刷新OLED屏,往所有位置写入0,即达到清屏的效果。

  • 画点函数

    void oled_draw_point_test(uint8_t x, uint8_t y)
    {
        //页地址模式
        uint8_t page_num = y / 8;
        
        //发送页地址
        oled_wr_byte(0xB0 | page_num, OLED_CMD);
        
        //发送列地址
        oled_wr_byte((x & 0x0F) | 0x00, OLED_CMD);
        oled_wr_byte((x & 0xF0) >> 4 | 0x10, OLED_CMD);
        
        //发送一个字节数据
        oled_wr_byte(1 << (y % 8), OLED_DATA);
    }
    

    函数内部首先根据传递的参数,确定页地址和列地址,然后往对应的位置写1,即可点亮OLED的对应位,实现画点操作。

  • 显示字符函数

    void oled_show_char_test(uint8_t  x, uint8_t  y, uint8_t mode)
    {
        uint8_t temp, t1, t;
        uint8_t y0 = y;                 /* 保存y的初值 */
    
        for(t = 0; t < 16; t++)         /* 总共16个字节,要遍历一遍 */
        {
            temp = oled_ascii_1608[t];  /* 依次获取点阵数据 */
    
            for(t1 = 0; t1 < 8; t1++)
            {
                if(temp & 0X80)     /* 这个点有效,需要画出来 */
                    oled_draw_point(x, y, mode);
                else                /* 这个点无效,不需要画出来 */
                    oled_draw_point(x, y, !mode);
    
                temp <<= 1;         /* 低位数据往高位移位,最高位数据直接丢弃 */
                y++;                /* y坐标自增 */
    
                if((y - y0) == 16)  /* 显示完一列了 */
                {
                    y = y0;         /* y坐标复位 */
                    x++;            /* x坐标递增 */
                    break;          /* 跳出 for循环 */
                }
            }
        }
    }
    

    上面已经具体分析了函数的功能,不再赘述。

  • 清屏函数

    void oled_refresh_gram(void)
    {
        uint8_t 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( g_oled_gram[ n ][ i ], OLED_DATA) ;
            }
        }
    }
    

    刷新显示,即把g_oled_gram数组内容重新显示。

  • 主函数

    int main(void)
    {
        HAL_Init();                             /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟, 72Mhz */
        delay_init(72);                         /* 延时初始化 */
        usart_init(115200);                     /* 串口初始化为115200 */
        led_init();                             /* 初始化LED */
        oled_init();
        oled_clear();
        
        oled_draw_point_test(0, 0);
        oled_draw_point_test(10, 0);
        oled_draw_point_test(10, 10);
        oled_draw_point_test(20, 20);
        
        oled_show_char_test(10, 10 ,1);
        oled_refresh_gram();
        
        while (1)
        {
            delay_ms(500);
            LED0_TOGGLE();                      /* LED0闪烁 */
        }
    }
    
  • 功能2:

    使用TPAD按键控制OLED显示眨眼睛。

  • 触摸按键扫描函数

    uint8_t tpad_scan(uint8_t mode)
    {
        static uint8_t keyen = 0;   /* 0, 可以开始检测;  >0, 还不能开始检测; */
        uint8_t res = 0;
        uint8_t sample = 3;         /* 默认采样次数为3次 */
        uint16_t rval;
    
        if (mode)
        {
            sample = 6;             /* 支持连按的时候,设置采样次数为6次 */
            keyen = 0;              /* 支持连按, 每次调用该函数都可以检测 */
        }
    
        rval = tpad_get_maxval(sample);
    
        if (rval > (g_tpad_default_val + 100))/* 大于tpad_default_val+TPAD_GATE_VAL,有效 */
        {
            if (keyen == 0)
            {
                res = 1;            /* keyen==0, 有效 */
            }
    
            //printf("r:%d\r\n", rval);   /* 输出计数值, 调试的时候才用到 */
            keyen = 3;              /* 至少要再过3次之后才能按键有效 */
        }
    
        if (keyen) keyen--;
    
        return res;
    }
    

    变量解释

    • keyen:用于控制是否可以开始新的检测,初始值为0。当 keyen 大于0时,表示不能开始新的检测。
    • res:用于存储检测结果,初始值为0。
    • sample:采样次数,默认值为3次。用于增加检测的稳定性。
    • rval:存储通过 tpad_get_maxval(sample) 获取的最大值。

    功能解释

    1. 采样次数设置

      if (mode) 
      {
          sample = 6;       /* 支持连按的时候,设置采样次数为6次 */
          keyen = 0;        /* 支持连按, 每次调用该函数都可以检测 */
      }
      

      如果 mode 为1,则支持连按,将采样次数设置为6次,并且将 keyen 置为0,使得每次调用该函数都可以进行检测。

    2. 获取触摸最大值

      rval = tpad_get_maxval(sample);
      

      调用 tpad_get_maxval(sample) 函数获取 sample 次采样中的最大值,并将其存储在 rval 中。

    3. 判断触摸是否有效

      if (rval > (g_tpad_default_val + 100)) 
      {
          if (keyen == 0) {
              res = 1;  /* keyen==0, 有效 */
          }
          keyen = 3;  /* 至少要再过3次之后才能按键有效 */
      }
      

      如果 rval 大于默认阈值(g_tpad_default_val)加上一个偏移量(100),则说明检测到有效触摸:

      • keyen 为0时,表示此时可以开始新的检测,将 res 置为1,表示触摸有效。
      • keyen 置为3,表示至少要再过3次循环之后,才能再次有效检测。
    4. 按键检测延迟

      if (keyen) keyen--;
      

      如果 keyen 大于0,则将 keyen 减1,控制下次按键检测的延迟。

    5. 返回检测结果

      return res;
      

      返回检测结果 res,如果检测到有效触摸则为1,否则为0。

  • OLED填充区域填充函数

    void oled_fill(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t dot)
    {
        uint8_t x, y;
    
        for (x = x1; x <= x2; x++)
        {
            for (y = y1; y <= y2; y++)oled_draw_point(x, y, dot);
        }
    
        oled_refresh_gram();    /* 更新显示 */
    }
    
  • 主函数

    int main(void)
    {
        HAL_Init();                                 /* 初始化HAL库 */
        sys_stm32_clock_init(RCC_PLL_MUL9);         /* 设置时钟, 72Mhz */
        delay_init(72);                             /* 延时初始化 */
        usart_init(115200);                         /* 串口初始化为115200 */
        led_init();                                 /* 初始化LED */
        
        oled_init();                            /* 初始化OLED */
        key_init();
        
        oled_show_string(0, 0, "Jiuqi", 16);
        oled_refresh_gram();                    /* 更新显示到OLED */
        
        tpad_init(6);
    
        while (1)
        {
            if (key_scan(0))   /* 成功捕获到了一次上升沿(此函数执行时间至少15ms) */
            {
                LED1_TOGGLE();  /* LED1翻转 */
                oled_clear();
                oled_fill(20, 30, 40, 40, 1);
                oled_fill(60, 30, 80, 40, 1);
                delay_ms(5000);
                oled_refresh_gram(); 
            }
            
            oled_fill(20, 20, 40, 45, 1);
            oled_fill(60, 20, 80, 45, 1);
            oled_refresh_gram();    
            
            delay_ms(200);
            LED0_TOGGLE();                      /* LED0闪烁 */
        }
    }
    
    1. 初始化系统时钟、延时功能、串口通信和LED引脚。
    2. 初始化OLED显示屏和按键。
    3. 在OLED显示屏上显示初始信息。
    4. 初始化触摸板。
    5. 在主循环中检测按键按下事件,并相应地更新OLED显示内容和翻转LED状态。

声明:资料来源(战舰STM32F103ZET6开发板资源包)

  1. Cortex-M3权威指南(中文).pdf
  2. STM32F10xxx参考手册_V10(中文版).pdf
  3. STM32F103 战舰开发指南V1.3.pdf
  4. STM32F103ZET6(中文版).pdf
  5. 战舰V4 硬件参考手册_V1.0.pdf

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/662702.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

pytorch笔记:torch.nn.Flatten()

1 介绍 torch.nn.Flatten(start_dim1, end_dim-1) 将一个连续的维度范围扁平化为一个张量 start_dim (int)要开始扁平化的第一个维度&#xff08;默认值 1&#xff09;end_dim (int)要结束扁平化的最后一个维度&#xff08;默认值 -1&#xff09; 2 举例 input torch.ra…

过去的六年,教会了我很多事

目录 过去六年的风风雨雨android缘起爱情缘灭顿悟收拾心情&#xff0c;再次启航面试阿里大起大落 如今时光&#xff0c;刺激且美好未来展望 过去六年的风风雨雨 android缘起 2018年&#xff0c;我从北京联合大学毕业&#xff0c;跟随着学长一起创业&#xff0c;从此开始了我的…

DeFi的历程与未来:探寻去中心化金融的前行路

随着区块链技术的不断演进和加密货币市场的持续繁荣&#xff0c;DeFi&#xff08;去中心化金融&#xff09;作为一种新兴领域正迅速崛起&#xff0c;其发展历史和未来前景备受关注。 过去&#xff1a;DeFi 的发展历史 DeFi 并非一夜之间出现&#xff0c;而是经历了一系列的发展…

【信息学奥赛】字典的键和值对换

【信息学奥赛】字典的键和值对换 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 输入一个字典&#xff0c;要求将该字典的键和值对换。&#xff08;注意&#xff0c;字典中有键的值是重复的&#xff09; 输入&#xff1a; 一行&#xff0…

【ai】livekit服务本地开发模式2:模拟1个发布者

是一个会议用软件:LiveKit is an open source project that provides scalable, multi-user conferencing based on WebRTC. It’s designed to provide everything you need to build real-time video audio data capabilities in your applications.LiveKit’s server is wr…

DiffBIR论文阅读笔记

这篇是董超老师通讯作者的一篇盲图像修复的论文&#xff0c;目前好像没看到发表在哪个会议期刊&#xff0c;应该是还在投&#xff0c;这个是arxiv版本&#xff0c;代码倒是开源了。本文所指的BIR并不是一个single模型对任何未知图像degradation都能处理&#xff0c;而是用同一个…

网络模型—BIO、NIO、IO多路复用、信号驱动IO、异步IO

一、用户空间和内核空间 以Linux系统为例&#xff0c;ubuntu和CentOS是Linux的两种比较常见的发行版&#xff0c;任何Linux发行版&#xff0c;其系统内核都是Linux。我们在发行版上操作应用&#xff0c;如Redis、Mysql等其实是无法直接执行访问计算机硬件(如cpu&#xff0c;内存…

剪画小程序:视频伪原创怎么制作?视频伪原创的几种制作方法分享!

什么是视频伪原创&#xff1f; 视频伪原创是指对已有的视频内容进行一定程度的修改和处理&#xff0c;使其在形式或部分细节上与原始视频有所不同&#xff0c;但保留了核心内容或主题。 视频伪原创包括以下一些常见操作&#xff1a; 剪辑重组&#xff1a;对原始视频进行剪辑…

【C language】统计某数中二进制1的个数

题解&#xff1a;统计某数中二进制1的个数(取模法 看某位是1/0法 干掉最右边的1法) 目录 1.题目2.取模法3.看某位是1/04.干掉最右边的1 1.题目 题目&#xff1a;设计一个程序&#xff0c;统计某数中二进制1的个数 2.取模法 int main() {int num 15;int count 0;while (n…

【UE 反射】反射的原理是什么?如何使用机制?

目录 0 拓展0.1 静态类型检查0.1.1 静态类型检查的主要原理0.1.2 编译器的工作流程0.1.3 静态类型检查的优点和缺点0.1.4 示例0.1.5 C也可以在运行时类型检查RTTI基本原理RTTI的实现RTTI的工作流程RTTI的限制 0.2 运行时动态类型检查0.2.1 主要特点0.2.2 动态类型检查的实现0.2…

元宇宙vr工业产品展示空间降低研发成本

元宇宙产品虚拟展厅搭建编辑器为您提供了一个自助式元宇宙场景搭建的绝佳平台。无论您是设计公司、摄影公司、营销公司还是教育机构&#xff0c;我们都能为您量身打造专属的元宇宙解决方案&#xff0c;满足您的多样化需求。 元宇宙产品虚拟展厅搭建编辑器具备强大的3D编辑功能&…

FPGA基础:触发器和锁存器

目录 锁存器&#xff08;Latch&#xff09;D触发器&#xff08;Flip-Flop&#xff09;最基本时序电路时序块&#xff08;Sequential blocks&#xff09;:同步与异步触发器概念触发器分类触发器的Verilog实现1. 上升沿触发的触发器2. 带异步复位、上升沿触发的触发器3. 带异步复…

rtsp协议分析

rtsp概述 rtsp (real-time stream protocol)实时流媒体控制协议。RFC2326&#xff1a;这是RTSP的初始定义版本v1.0&#xff0c;由哥伦比亚大学、网景和RealNetworks公司提交给互联网工程任务组&#xff08;IETF&#xff09;作为RFC标准。RFC7826&#xff1a;这是RTSP的后续更新…

【EI会议】第二届计算机、物联网与智慧城市国际会议

第二届计算机、物联网与智慧城市国际会议 快速通道 投稿链接&#xff1a;loading 截稿时间&#xff1a;9月15日 检索&#xff1a;EI检索 一、会议信息 大会官网&#xff1a;www.ciotsc.org 会议地点&#xff1a;湖南株洲 会议时间&#xff1a;2023年11月15日-17日 二、征稿主…

探索AES对称加密:Python代码实战

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、引言&#xff1a;从非对称到对称 二、AES加密机制概述 三、Python实现AES加密与解密 …

FreeRTOS【8】二值信号量使用

1.开发背景 FreeRTOS 提供了队列可以在线程间快速交换信息&#xff0c;那么还有没有其他交互渠道&#xff1f;答案是有的&#xff0c;相对于队列传递信息&#xff0c;还有更轻量级的线程唤醒操作&#xff0c;那就是信号量&#xff0c;而二值信号量就是最简单的一种。 二值信号量…

揭秘希喂、小米、安德迈宠物空气净化器的惊人效果,性价比爆表!

当谈及养猫&#xff0c;首先浮现在脑海中的烦恼无疑是猫咪的掉毛和便臭问题。这些问题对于我们这些爱宠人士来说&#xff0c;无疑是一种持续的挑战。特别是在炎热的夏季&#xff0c;高温本身就让人焦躁不安&#xff0c;而空气中飘散的猫浮毛和便臭更是加剧了这种不适感。 为了…

Linux系统编程(五)多线程创建与退出

目录 一、基本知识点二、线程的编译三、 线程相关函数1. 线程的创建&#xff08;1&#xff09;整型的传入与接收&#xff08;2&#xff09;浮点数的传入与接收&#xff08;3&#xff09;字符串的传入与接收&#xff08;4&#xff09;结构体的传入与接收 2. 线程的退出3. 线程的…

智慧仓储新动力:EasyCVR+AI视频智能监管系统方案助力仓储安全高效管理

一、背景 随着物流行业的快速发展和智能化水平的提升&#xff0c;智慧仓储视频智能监管系统已成为现代仓储管理的重要组成部分。本系统通过综合运用物联网、视频分析、边缘计算等技术手段&#xff0c;实现对仓储环境的全面监控、智能分析和高效管理。 TSINGSEE青犀视频汇聚Ea…

【单片机毕设选题】-智能语音控制风扇

一. 系统功能 此设计采用STM32和SU-03T离线语音模块来控制风扇, 主要功能如下: 1. 通过DHT11温湿度模块来采集环境温湿度。 2. 通过SU-03T语音模块来控制风扇启停加减速等。 3. 通过OLED显示系统状态。 4. 可以通过按键控制风扇启停加减速等。 5. 通过蓝牙模块监视系统状…