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显示屏介绍
OLED的简介
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display,OLED)。OLED可按发光材料分为两种:小分子OLED和高分子 OLED(也可称为PLED)。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
个控制引脚。
OLED模块硬件连接图
分类 | CS | RD | WR | D[7:0] | RST | DC |
---|---|---|---|---|---|---|
MINI板 | PC9 | PC6 | PC7 | PB0~PB7 | NRST | PC8 |
精英板 | PD6 | PG13 | PG14 | PC0~7 | PG15 | PD3 |
战舰板 | PD6 | PG13 | PG14 | PC0~7 | PG15 | PD3 |
2. OLED驱动原理
- 驱动芯片选择:选择适合的OLED驱动芯片是驱动OLED屏的第一步。常用的OLED驱动芯片包括SSD1306、SSD1351、SH1106等,根据需要选择合适的驱动芯片。
- 时序控制:根据所选的驱动芯片的通信接口(如8080时序、6800时序、SPI、I2C等),实现相应的时序控制。这包括控制数据/命令信号、写入数据、读取数据等操作的时序要求。
- 初始化序列:每款OLED屏都有自己的初始化序列,用于初始化屏幕的各种参数和设置。这个序列一般由OLED屏厂家提供,需要在驱动程序中正确执行,以确保屏幕正常工作。
- 绘图功能实现:通过画点函数和读点函数来实现对屏幕上的像素点进行操作。画点函数用于向屏幕指定位置写入像素点的颜色,而读点函数则用于读取屏幕上指定位置的像素点的颜色信息。基于这两个函数,可以实现各种图形绘制、文字显示等功能。
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); }
读时序的过程:
- 设置 DC 为高(数据)/低(命令);
- 拉低片选 CS 信号,选中 SSD1306;
- 设置 RD 起始电平为低;
- 设置 RD 为高电平,读取数据;
- CS设置为1,释放CS;
- DC设置为1,释放DC引脚,恢复默认。
3. OLED驱动芯片简介
OLED驱动芯片用于控制OLED的显示功能和效果。SSD1306指令比较多,这里介绍常用的命令。
指令(HEX) | 各位描述 | 名称 | 说明 | |||||||
---|---|---|---|---|---|---|---|---|---|---|
D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | |||
0xAE/0xAF | 1 | 0 | 1 | 0 | 1 | 1 | 1 | X0 | 设置显示开关 | X0 = 0,关闭显示 X0 = 1,开启显示 |
0xB0~0xB7 | 1 | 0 | 1 | 1 | 0 | X2 | X1 | X0 | 设置页地址 | X[2:0]:0-7对应页0-7 |
0x00~0x0F | 0 | 0 | 0 | 0 | X3 | X2 | X1 | X0 | 设置列地址(L4) | 设置8位列地址的低四位 |
0x10~0x1F | 0 | 0 | 0 | 1 | X3 | X2 | X1 | X0 | 设置列地址(H4) | 设置8位列地址的高四位 |
OLED驱动芯片(如SSD1306)是用于控制OLED显示屏的关键组件,它负责管理显示的内容、亮度、对比度等参数,并将数据传输到OLED屏幕上以实现图像或文本的显示。
常用命令:
- 0xAE / 0xAF – 设置显示开关:
D7
:控制GRAM的写入位置,通常为1。
D6
:控制显示开关。
X0
:0为关闭显示,1为开启显示。
- 0xB0 ~ 0xB7 – 设置页地址:
X2
, X1
, X0
:设置页地址,范围通常为07,对应页07。
- 0x00 ~ 0x0F – 设置列地址(L4):
X3
, X2
, X1
, X0
:设置8位列地址的高四位。
- 0x10 ~ 0x1F – 设置列地址(L4):
X3
, X2
, X1
, X0
:设置8位列地址的高四位。
什么是GRAM:
GRAM是图形显示数据RAM(Graphic Display Data RAM)的缩写,用于存储要在显示屏上显示的图像或文本的位模式数据。它是一块位映射的静态RAM,大小为128×64位,可以分为8页,每页对应显示屏的一行像素。GRAM保存的数据决定了OLED显示屏上每个像素点的状态,从而实现图像或文本的显示。
什么是页地址模式:
- 发送内存地址模式命令(0x20):这个命令用于设置内存地址模式,以便指定将要发送的地址模式类型。
- 发送页地址模式命令(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)具体实现:
- 边界检查 : 检查给定的坐标是否超出显示范围,如果超出则直接返回。
- 计算页地址和位偏移 : OLED显示屏通常按页(每页8个像素高)进行组织。
pos
计算了y
所在的页地址,bx
计算了y
在对应字节中的位位置。- 计算位掩码 : 根据
bx
的值,创建一个位掩码,用于设置或清除GRAM中的特定位。- 绘制或清除点 :根据
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个显示字符。
什么是内码:
计算机使用的二进制字符编码,就叫内码,简称编码。字符包含各种文字!
字符集 | 编码长度 | 说明 |
---|---|---|
ASCII | 1个字节 | 拉丁字母编码,仅128个编码,最简单 |
GB2312 | 2个字节 | 简体中文字符编码,包含约6000多汉字编码 |
GBK | 2个字节 | 对GB2312的扩充,支持繁体中文,约2W多汉字编码 |
BIG5 | 2个字节 | 繁体中文字符编码,在台湾、香港用的多 |
UNICODE | 一般2个字节 | 国际标准编码,支持各国文字 |
如何将字符显示在OLED上
- 显示字符,必须先有其点阵数据,点阵数据的集合,叫做字库 ;
- 单片机根据点阵数据按取模方向进行描点还原,就能显示字符;
- 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显示屏上绘制一个16×16像素的点阵字符。代码使用的是一个包含16个字节的点阵数据数组
oled_ascii_1608
,每个字节表示8个像素的行信息。变量解释
temp
: 当前字节的点阵数据。t1
: 用于内层循环,遍历每个字节的8个位。t
: 用于外层循环,遍历16个字节。y0
: 初始的y坐标,用于列绘制结束后的复位。
- 保存初始的y坐标
uint8_t y0 = y; // 保存y的初值
这行代码保存了初始的y坐标,以便在每列绘制结束后复位y坐标。
- 外层循环:遍历16个字节
for(t = 0; t < 16; t++) // 总共16个字节,要遍历一遍 { temp = oled_ascii_1608[t]; // 依次获取点阵数据
外层循环遍历点阵字符的16个字节。每个字节包含一行的8个像素信息。
- 内层循环:遍历每个字节的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坐标自增,移到下一行。
- 检查并处理列结束
if((y - y0) == 16) // 显示完一列了 { y = y0; // y坐标复位 x++; // x坐标递增 break; // 跳出for循环 }
如果当前列已经绘制了16个像素点(即一列绘制完毕),则:
将y坐标复位到初始值 y0
。x坐标递增一列。 跳出内层循环,继续绘制下一字节。
ASCII字库制作:
- 设置字体大小 ;
- 设置字模选项 :阴码,逐列式,顺向,十六进制,C51格式;
- 输入ASCII字符集(95个);
- 生成字模。
5. OLED基本驱动步骤
6. 代码实现
功能1:
- 在OLED屏幕上画一个点(不建立OLED_GRAM)
- 在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)
获取的最大值。功能解释
采样次数设置:
if (mode) { sample = 6; /* 支持连按的时候,设置采样次数为6次 */ keyen = 0; /* 支持连按, 每次调用该函数都可以检测 */ }
如果
mode
为1,则支持连按,将采样次数设置为6次,并且将keyen
置为0,使得每次调用该函数都可以进行检测。获取触摸最大值:
rval = tpad_get_maxval(sample);
调用
tpad_get_maxval(sample)
函数获取sample
次采样中的最大值,并将其存储在rval
中。判断触摸是否有效:
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次循环之后,才能再次有效检测。
按键检测延迟:
if (keyen) keyen--;
如果 keyen
大于0,则将 keyen
减1,控制下次按键检测的延迟。
返回检测结果:
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闪烁 */
}
}
- 初始化系统时钟、延时功能、串口通信和LED引脚。
- 初始化OLED显示屏和按键。
- 在OLED显示屏上显示初始信息。
- 初始化触摸板。
- 在主循环中检测按键按下事件,并相应地更新OLED显示内容和翻转LED状态。
声明:资料来源(战舰STM32F103ZET6开发板资源包)
- Cortex-M3权威指南(中文).pdf
- STM32F10xxx参考手册_V10(中文版).pdf
- STM32F103 战舰开发指南V1.3.pdf
- STM32F103ZET6(中文版).pdf
- 战舰V4 硬件参考手册_V1.0.pdf
作者:LittlePrinceRose