蓝桥杯stm32学习笔记(五):按键
Published in:2024-03-16 | category: 嵌入式
Words: 1.6k | Reading time: 7min | reading:

短按键

电路图

按键按下后接地变为低电平,未按下为高电平;

不用定时器的简单按键:

1
2
3
4
5
6
7
8
9
if(HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_0)==GPIO_PIN_RESET ) {
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_RESET );
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
} else {
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_8,GPIO_PIN_SET );
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}

CubeMX配置

引脚配置

配置PB0-2PA0GPIO_Input
并将GPIO Pull-up/Pull-down配置为Pull-up

定时器配置

选一个定时器(这里选择TIM4)

配置时钟源为内部时钟

选项解释
  1. Prescaler (PSC-16 bits value):

    • Prescaler是用于将输入时钟频率进行分频的参数,以降低计数器的工作频率。16位值表示了分频比,用于控制计数器每次计数之前如何分频输入时钟。较大的分频比会使计数器工作在较低的频率下。例如,如果一个系统的输入时钟频率为10 MHz,而计数器需要以1 kHz的频率进行计数,那么可以通过设置适当的预分频器值来实现这一要求。假设我们设置预分频器的16位值为10000,在这种情况下,输入时钟在进入计数器之前会先进行10,000倍的分频。这样,每10,000个输入时钟周期,计数器才会计数一次,从而使得计数器的工作频率为1 kHz。不同的应用场景会有不同的计数需求。例如,某些应用可能需要高速计数,一种计数器的频率可能需要达到几十MHz甚至更高的频率。在这种情况下,计数器需要以更高的速率进行计数,以满足对计数精度和速度的要求。另一方面,一些应用可能只需要低速计数,比如在一些传感器监测系统中,计数器可以以较低的频率进行计数,以实时监测并记录事件发生的次数。
  2. Counter Mode:

    • 此处设置计数器的工作模式为”Up”,即计数器会从0开始递增计数,计数到最大值后重新从0开始。
  3. Dithering:

    • 在这里设置了禁用抖动(Dithering),抖动是指通过微小的随机变化来减小量化误差的一种技术。在这种情况下,抖动被禁用,即不会在计数器中引入这种微小的变化。
  4. Counter Period (AutoReload Register):

    • 设置计数器的计数周期,通常通过AutoReload Register(自动重装载寄存器)来实现。在这里设置了计数器的周期为0到65535之间的值,当计数器达到最大值时会重新加载初始值。
  5. Internal Clock Division (CKD):

    • 设置内部时钟分频(CKD),在这里选择了”No Division”,表示计数器的时钟不会被分频,以确保计数器运行在输入时钟的最高频率下。
  6. Auto-reload preload:

    • 在这里禁用了自动加载预设值功能,即不会在计数器自动重新加载时预先装载新的值。

这些设置有助于调整计时器/计数器的行为和性能,例如调整计数模式、周期以及时钟分频等,以满足特定的应用需求。通过合理设置这些参数,可以有效地控制计时器/计数器的功能,确保其在系统中正确、稳定地运行。

  1. Prescaler:分频系数,决定定时器的工作频率
    定时器工作频率=外部总线频率(80MHz)/(PSC+1)
    这里设置为80-1,即定时器工作频率=80,000,000/80=1,000,000

  2. Counter Period:
    定时频率=定时器工作频率/counter
    这里设置为10000-1,即定时频率=1,000,000/10,000=100Hz,即定时周期10ms;
    10ms中断一次;

使能中断

生成工程

点击右上角”GENERATE CODE”,生成工程

代码

新建两个文件

保存interrupt.cinterrupt.hbsp文件夹
然后添加interrupt.c到项目bsp文件夹里

写中断回调函数

记不住函数名复制这个:

interrupt.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "main.h"
#include "stdbool.h"
struct keys
{
uchar judge;
bool status;
bool single_flag;

};


#endif

interrupt.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "interrupt.h"

struct keys key[4]= {{0,false,0},{0,false,0},{0,false,0},{0,false,0}};

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

key[0].status=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_0);
key[1].status=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_1);
key[2].status=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_2);
key[3].status=HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_0);

if(htim->Instance==TIM4) {
for(int i=0; i<4; i++)
{
switch(key[i].judge)
{
case 0:
{
if(key[i].status==0)
{
key[i].judge=1;

}
}break;
case 1:
{
if(key[i].status==0)
{
key[i].judge=2;
key[i].single_flag=1;
}else{key[i].judge=0;}

}break;
case 2:
{
if(key[i].status==1)
{
key[i].judge=0;

}

}break;

}
}
}

}

main.c中extern结构体/打开中断

1
extern struct keys key[];

main函数里:

1
HAL_TIM_Base_Start_IT(&htim4 );

一定要放在MX_TIM4_Init();后面!!!!!!!!!!!

启动定时器 TIM4 的基本定时器模式并使能定时器中断.

长按键

interrupt.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef _INTERRUPT_H_
#define _INTERRUPT_H_
#include "main.h"
#include "stdbool.h"
struct keys
{
uchar judge;
bool status;
bool single_flag;
bool long_flag;
uint time;

};


#endif

interrupt.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include "interrupt.h"

struct keys key[4]= {{0,false,0,0,0},{0,false,0,0,0},{0,false,0,0,0},{0,false,0,0,0}};


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {

key[0].status=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_0);
key[1].status=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_1);
key[2].status=HAL_GPIO_ReadPin (GPIOB,GPIO_PIN_2);
key[3].status=HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_0);

if(htim->Instance==TIM4) {
for(int i=0; i<4; i++) {
switch(key[i].judge) {
case 0: {
if(key[i].status==0) {
key[i].judge=1;
key[i].time =0;

}
}
break;
case 1: {
if(key[i].status==0) {
key[i].judge=2;
// key[i].single_flag=1;
} else {
key[i].judge=0;

}

}
break;
case 2: {
if(key[i].status==1) {
key[i].judge=0;
key[i].single_flag=1;
if(key[i].time<200)
{
key[i].long_flag=0;

}
} else {
key[i].time++;
if(key[i].time>200)
{
key[i].long_flag=1;

}
}

}
break;

}
}

}

}

key[i].time>200:
之前配置的定时器一个周期10ms,200也就是2s

函数封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
unsigned char  view=0;
void key_proc();
void disp_proc();

void key_proc()
{
if(key[0].single_flag==1)
{

key[0].single_flag =0;
}
if(key[1].single_flag==1)
{
view++;
if(view>2)
{
view=0;
}

key[1].single_flag =0;
}
if(key[2].single_flag==1)
{

key[2].single_flag =0;
}
if(key[3].single_flag==1)
{

key[3].single_flag =0;
}


}

void disp_proc()
{
if(view==0)
{
char text[30];
sprintf(text," view=0 ");
LCD_DisplayStringLine(Line9, (uint8_t *)text);

}
if(view==1)
{
char text[30];
sprintf(text," view=1 ");
LCD_DisplayStringLine(Line9, (uint8_t *)text);

}
if(view==2)
{
char text[30];
sprintf(text," view=2 ");
LCD_DisplayStringLine(Line9, (uint8_t *)text);

}

}
Prev:
蓝桥杯stm32学习笔记(六):PWM输出
Next:
STM32第二次考核