2025.9.18知识笔记

Sep 18 2025 每日笔记

怎么使用verilog进行按键消抖和新建STM32F4的工程模板文件

1、按键消抖

key_fliter.v

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
module key_fliter #(
parameter CNT_20MS=20'd1000000
)(
input clk,
input rst,
input key_in,
output reg key_flag
);

reg [19:0] counter;

always@(posedge clk or negedge rst) begin
if(!rst)
counter<=1'b0;
else if(key_in==1'b1) //检测到高电平,立即清0
counter<=1'b0;
else if(counter==CNT_20MS) //到检测到按键持续低电平20ms的时候,保持这个counter,因为不需要再增加了,等到按键结束拉高,一次完整按键结束,counter自动清0
counter<=CNT_20MS;
else
counter<=counter+1'b1;
end

always@(posedge clk or negedge rst) begin
if(!rst)
key_flag<=1'b0;
else if(counter==CNT_20MS-1)//检测到20ms稳定低电平,证明是一次完整的按键动作
key_flag<=1'b1;
else
key_flag<=1'b0;
end

endmodule

key.v

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
module led(
input clk,
input rst,
input key_in,
output reg led
);

wire key_flag;

key_fliter #(
.CNT_20MS(20'd1000000)
)led_inst
(
.clk(clk),
.rst(rst),
.key_in(key_in),
.key_flag(key_flag)
);

always@(posedge clk or negedge rst) begin
if(!rst)
led<=1'b0;
else if(key_flag==1'b1)
led<=~led;
else
led<=led;
end

endmodule

2、梅花代码小疑问

1
2
#include <stdbool.h>
#include <stdint.h>

​​#include <stdbool.h>​​:提供布尔类型支持,定义 bool、true、false,用于逻辑判断和状态标志,提升代码可读性。

​​#include <stdint.h>​​:提供精确位宽整数类型(如 uint8_t、int32_t),确保数据跨平台一致性,适用于硬件寄存器和协议数据处理。

初始化结构体,我们可以使用下列操作:
GPIO_InitTypeDef GPIO_InitStructure={0};
​​作用​​:将结构体所有成员​​显式清零​​(数值型为0,指针为NULL)。

我们也可以使用另外一种方法:
memset(&GPIO_InitStructure, 0, sizeof(GPIO_InitTypeDef));
但是要注意要引入头文件 #include<string.h>

memset是 C 语言标准库中的内存操作函数,其核心作用为 ​​对指定内存区域进行快速填充​​。用一句话概括:
​memset将一段连续内存的每个字节强制设置为指定值​​,常用于初始化内存块或数据清零。
memset函数举例子:

1
2
3
4
5
6
7
// 1. 清零结构体(防御未初始化风险)
GPIO_InitTypeDef config;
memset(&config, 0, sizeof(config)); // 所有字节置0

// 2. 填充特定模式(如0xFF)
uint8_t buffer[100];
memset(buffer, 0xFF, sizeof(buffer)); // 全填充为0xFF

static const led_desc_t led1 =
{
.clk_source = RCC_AHB1Periph_GPIOE,
.port = GPIOE,
.pin = GPIO_Pin_6,
.on_lvl = Bit_RESET,
.off_lvl = Bit_SET,
};
static用于修饰结构体变量 led1(而非结构体类型 led_desc_t)
核心作用​​:
​​将 led1变量的作用域限制为当前源文件​​,其他文件无法通过 extern引用该变量,实现以下目标:
​​避免命名冲突​​:多个文件可定义同名的 led1变量而不会引发链接错误。

const的作用:
硬件配置的确定性​​:
LED的引脚、时钟源等参数在硬件设计阶段就已固定,运行时修改无意义且可能导致故障。
示例(尝试修改会触发编译错误):

1
led1.pin = GPIO_Pin_7;  // 编译报错!const禁止修改'

const修饰的变量会被编译器特殊处理,其存储位置和访问方式与普通变量不同。具体到您的代码:

1
static const led_desc_t led1 = { ... };  // const变量

存储位置:Flash(而非RAM)​​
​​普通变量​​:存储在RAM中,可读可写,但RAM空间有限(如STM32F4仅有192KB RAM)。

​​const变量​​:

编译器会将其放入 ​​Flash(只读存储器)​​ 中(例如STM32F4的1MB Flash),因为它的值在运行时不需要修改。

​​意义​​:节省宝贵的RAM空间,尤其对资源紧张的嵌入式设备至关重要。

我们在这里再来详细得讲解一下static这个关键字的作用吧
①修饰局部变量(函数内部)​​
​​作用​​:

✅ ​​延长生命周期​​

普通局部变量在函数调用结束后销毁,而static局部变量​​在程序整个运行期间存在​​(存储在静态存储区)。

1
2
3
4
5
void counter() {
static int count = 0; // 只初始化一次,函数调用间保持值
count++;
printf("%d\n", count);
}

②在C语言中,​​不加static的全局变量或函数​​可以被整个程序中的所有源文件访问,而​​加了static的全局变量或函数​​则只能被​​当前所在的源文件​​访问,其他文件无法使用它。

通俗解释​​
​​不加 static(默认情况)​​:

全局变量或函数是“公开的”,其他文件只要用 extern声明就能访问。

比如:

1
2
3
4
5
6
7
8
9
// file1.c
int global_var = 10; // 全局变量,其他文件可用
void public_func() { // 全局函数,其他文件可用
printf("Hello");
}

// file2.c
extern int global_var; // 声明后即可使用
extern void public_func(); // 声明后即可调用

加 static​​:

全局变量或函数变成“私有的”,只能在当前文件内使用,其他文件无法访问。

比如:

1
2
3
4
5
6
7
8
9
// file1.c
static int private_var = 20; // 只能在本文件使用
static void private_func() { // 只能在本文件调用
printf("Secret");
}

// file2.c
extern int private_var; // 错误!无法访问
extern void private_func(); // 错误!无法调用

​​C语言标准规定​​:

static​​不能用于修饰结构体类型定义​​,它只能修饰变量或函数。这样的代码在 C 语言中 ​​没有实际作用​​,编译器可能会忽略 static或给出警告。

1
2
3
4
5
// ❌ 无意义:static 修饰结构体类型(C语言不支持)
static struct Point { // 编译器可能忽略或警告
int x;
int y;
};

static可以用于修饰结构体变量

此时结构体类型​​仅在当前文件可用

1
2
3
4
5
6
7
// ✅ file1.c(结构体定义不暴露给其他文件)
struct SecretData { // 仅当前文件可用
int id;
char name[20];
};

static struct SecretData data; // 变量也限制为文件内可见

3、常规方法点亮LED

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
63
64
65
66
67
68
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "stm32f4xx.h"


static void led_delay(void)
{
for (uint32_t a = 0; a < 1000; a++)
{
for (uint32_t b = 0;b < 1000; b++)
{
__NOP();
__NOP();
__NOP();
__NOP();
}
}
}

static void led_init(void)
{
// 打开外设时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

// 配置GPIO
GPIO_InitTypeDef gpio_init;
memset(&gpio_init, 0, sizeof(GPIO_InitTypeDef));

gpio_init.GPIO_Pin = GPIO_Pin_5;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Medium_Speed;
GPIO_Init(GPIOE, &gpio_init);

gpio_init.GPIO_Pin = GPIO_Pin_6;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Medium_Speed;
GPIO_Init(GPIOE, &gpio_init);

gpio_init.GPIO_Pin = GPIO_Pin_13;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Medium_Speed;
GPIO_Init(GPIOC, &gpio_init);

// 启动GPIO
}

int main(void)
{
led_init();

while(1)
{
GPIO_WriteBit(GPIOE, GPIO_Pin_5, Bit_RESET);
GPIO_WriteBit(GPIOE, GPIO_Pin_6, Bit_RESET);
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_RESET);
led_delay();
GPIO_WriteBit(GPIOE, GPIO_Pin_5, Bit_SET);
GPIO_WriteBit(GPIOE, GPIO_Pin_6, Bit_SET);
GPIO_WriteBit(GPIOC, GPIO_Pin_13, Bit_SET);
led_delay();
}
}

4、使用对象方法点亮LED

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "stm32f4xx.h"


typedef struct led_desc
{
uint32_t clk_source;
GPIO_TypeDef *port;
uint16_t pin;
BitAction on_lvl;
BitAction off_lvl;
} led_desc_t;


static const led_desc_t led0 =
{
.clk_source = RCC_AHB1Periph_GPIOE,
.port = GPIOE,
.pin = GPIO_Pin_5,
.on_lvl = Bit_RESET,
.off_lvl = Bit_SET,
};
static const led_desc_t led1 =
{
.clk_source = RCC_AHB1Periph_GPIOE,
.port = GPIOE,
.pin = GPIO_Pin_6,
.on_lvl = Bit_RESET,
.off_lvl = Bit_SET,
};
static const led_desc_t led2 =
{
.clk_source = RCC_AHB1Periph_GPIOC,
.port = GPIOC,
.pin = GPIO_Pin_13,
.on_lvl = Bit_RESET,
.off_lvl = Bit_SET,
};


static void led_delay(void)
{
for (uint32_t a = 0; a < 1000; a++)
{
for (uint32_t b = 0;b < 1000; b++)
{
__NOP();
__NOP();
__NOP();
__NOP();
}
}
}

static void led_init(const led_desc_t *desc)
{
RCC_AHB1PeriphClockCmd(desc->clk_source, ENABLE);

GPIO_InitTypeDef ginit;
memset(&ginit, 0, sizeof(GPIO_InitTypeDef));
ginit.GPIO_Pin = desc->pin;
ginit.GPIO_Mode = GPIO_Mode_OUT;
ginit.GPIO_OType = GPIO_OType_PP;
ginit.GPIO_Speed = GPIO_Medium_Speed;
GPIO_Init(desc->port, &ginit);

GPIO_WriteBit(desc->port, desc->pin, desc->off_lvl);
}

static void led_deinit(const led_desc_t *desc)
{
GPIO_WriteBit(desc->port, desc->pin, desc->off_lvl);
}

static void led_on(const led_desc_t *desc)
{
GPIO_WriteBit(desc->port, desc->pin, desc->on_lvl);
}

static void led_off(const led_desc_t *desc)
{
GPIO_WriteBit(desc->port, desc->pin, desc->off_lvl);
}

int main(void)
{
led_init(&led0);
led_init(&led1);
led_init(&led2);

while(1)
{
led_on(&led0);
led_on(&led1);
led_on(&led2);
led_delay();

led_off(&led0);
led_off(&led1);
led_off(&led2);
led_delay();
}
}