3.3 GPIO输入
一、硬件电路
1.1 按键
简介:常见的输入设备,按下导通,松手断开。

抖动问题:由于按键内部使用的是机械式弹簧片来进行通断的,按下和松手瞬间,信号在高低电平转换时会多次抖动,抖动时间通常在 5 - 10ms。虽人眼难以察觉,但对高速运行的单片机可能导致误判。常用解决方法是在程序中添加延时,避开抖动时间。

硬件电路接法:
一共有四种接法,分上接和下接两种方式,下接方式更为常用。
下接:

- 左侧电路
GPIO 配置为上拉输入模式。按键按下时,PA0 被下拉至地,读取电压为低电平;按键松开时,PA0 为上拉状态,呈高电平。 - 右侧电路
GPIO 可配置为上拉输入或浮空模式。按键按下时,PA0 被下拉至地,读取电压为低电平;按键松开时,PA0 为上拉或浮空状态,均呈高电平。
- 左侧电路
上接:

1.2 传感器模块
模块概述:以下为光敏电阻、热敏电阻、对射式红外和反射式红外四种传感器模块。它们电路结构和工作原理相似,均利用传感器元件(如光敏电阻、热敏电阻、红外接收管等),其电阻随外界模拟量变化而改变,如光线越强,光敏电阻阻值越小。

电路原理:
由于电阻变化不易观察,通常将传感器元件与定值电阻串联分压,获取模拟电压输出。还可以通过电压比较器对模拟电压进行二值化处理,得到数字电压输出。
见下方图片中间部分,N1 代表随外界环境变化的可变电阻,R1 是用于分压的定值电阻,AO 端输出模拟电压,C2 为滤波电容。根据分压原理不难看出,当 N1 阻值增大时,AO 端电压减小,反之增大。
见下方图片左侧部分,数字电压输出是通过 LM393 电压比较器实现的,IN+ 电压大于 IN- 时,输出端 DO 接 VCC,反之接 GND。

二、C 语言基础知识
2.1 数据类型
基本数据类型:C 语言基本数据类型包括 char、short、int、long、float、double 等。在不同单片机系统中,数据类型所占位数有差异。如在 51 单片机中,int 占 16 位,在 STM32 中占 32 位。若需使用 16 位数据,在 STM32 中用 short 表示。此外,还有无符号类型,如 unsigned char、unsigned short 等,取值范围与有符号类型不同。
数据类型重命名:由于 C 语言部分数据类型名字较长,且 int 位数因系统而异,名字有时不能准确反映含义,C 语言和标准库提供了新名字。引入 stdint.h 头文件,可使用新类型定义,如 int8_t、uint8_t、int16_t、uint16_t 等,更直观体现数据位数和有无符号属性。
注意
标准库中曾使用 u8、u16 等定义,虽新版本库函数仍支持,但从兼容性和规范性考虑,建议使用新的官方定义,即 stdint 关键字。
| 关键字 | 位数 | 表示范围 | stdint关键字 | ST关键字 |
|---|---|---|---|---|
char | 8 | -128~127 | int8_t | s8 |
unsigned char | 8 | 0~255 | uint8_t | u8 |
short | 16 | -32768~32767 | int16_t | s16 |
unsigned short | 16 | 0~65535 | uint16_t | u16 |
int | 32 | -2147483648~2147483647 | int32_t | s32 |
unsigned int | 32 | 0~4294967295 | uint32_t | u32 |
long | 32 | -2147483648~2147483647 | ||
unsigned long | 32 | 0~4294967295 | ||
long long | 64 | -(2^64)/2~(2^64)/2-1 | int64_t | |
unsigned long long | 64 | 0~(2^64)-1 | uint64_t | |
float | 32 | -3.4e38~3.4e38 | ||
double | 64 | -1.7e308~1.7e308 |
2.2 宏定义
用途:宏定义关键字为 #define,主要有两个用途。一是用有意义的字符串代替数字,增强代码可读性,防止出错,如用 HIGH_LEVEL 代表高电平 1,LOW_LEVEL 代表低电平 0。二是提取程序中频繁出现的参数,便于集中修改,若程序多处使用同一参数,通过宏定义只需在一处修改。
定义与引用:
在 STM32 程序中,宏定义常用于表示 GPIO 口编号,使代码更简洁清晰。
#define ABC 12345 //宏定义,表示用 ABC 替代 12345
int a = ABC; //等效于int a = 12345;2.3 typedef
用途:typedef 关键字用于将较长的变量类型重命名,简化代码书写。如:
typedef unsigned char uint8_t; //定义typedef
uint8_t a; //等效于unsigned char a;与宏定义的区别:宏定义新名字在左边,无需分号,可对任意名字替换;typedef 新名字在右边,后面必须加分号,只能用于变量类型重命名。因此,对变量类型重命名时,typedef 更安全,会对命名进行检查,确保合法。
2.4 结构体
用途:结构体是 C 语言的组合数据类型,用于组合不同类型的数据。在复杂程序中,结构体有助于数据管理与传递,增强代码逻辑可读性。如描述学生信息时,可用结构体将姓名、年龄、成绩等整合,方便统一管理。
定义与引用:定义结构体时,使用 struct 关键字,后跟结构体名称和花括号,花括号内列出成员变量及其类型,如:
struct Student {
char name[20];
int age;
float score;
};引用结构体成员时,可用结构体变量名加点运算符,如:
struct Student stu;
stu.age = 20;由于结构体在函数间数据传递通常采用地址传递,还可使用结构体指针加 -> 运算符引用成员,如:
stu.name = 'May';
stu.age = 20;
stu.score = 3.67;
pstu.name->x = 'May'; //pstu为结构体的地址
pstu.age->y = 20;
pstu.score->z = 1.23;2.5 枚举
用途:枚举关键字为 enum,是一种数据类型,用于定义取值受限的整型变量,限制变量取值范围。枚举是宏定义的集合。
定义与引用:
定义枚举时,用 enum 关键字,后跟枚举类型名和花括号,花括号内列出取值。引用枚举变量时,只能赋枚举中定义的值。
因为枚举变量类型较长,所以通常用typedef更改变量类型名,如定义枚举变量表示一周的天数:
typedef enum {
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7
} Week_t;enum Week_t week;
week = Monday; //等同于 week = 1;
week = Tuesday; //等同于 week = 2;
week = (Week_t)3; //如果想要赋具体的值,需要强制类型转换若赋未定义的值,编译器会发出警告。
枚举也有类似于宏定义的功能,可以随意定义变量,把枚举值赋给变量,如:
int day = Monday; //等同于 int day = 1;