1、GPIO各输入函数
- GPIO_ReadInputDataBit:读某个引脚的输入电平,返回 0 或 1
- GPIO_ReadInputData:一次性读整个 GPIO 端口所有引脚的输入值,返回 16 位数字
- GPIO_ReadOutputDataBit:读某个引脚上输出寄存器的值(你程序输出的 0/1,不是引脚真实外部电平)
- GPIO_ReadOutputData:一次性读整个端口输出寄存器的 16 位值
简化来讲就是
- Input 系列:看引脚外面真实电压(别人传给单片机)
- Output 系列:看单片机内部打算输出的值(单片机计划发给别人)
- Bit 结尾:只读单独 1 根引脚
- 不带 Bit:一次性读整个端口 16 根
2、按键控制LED
//你好 #include "stm32f10x.h" //STM32标准库头文件 #include "Delay.h" //延时函数头文件 /** * @brief 按键扫描函数,检测PB1、PB11两个按键,松开后返回按键编号 * @retval 0:无按键按下;1:PB1按键;2:PB11按键 */ uint8_t Key_GetNumber(void) { uint8_t KeyNumber = 0; //定义按键编号变量,默认值0,代表没有按键按下 //检测PB1按键 if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0) //读取PB1引脚输入电平,等于0说明按键按下 { Delay_ms(20); //延时20ms,软件消抖,跳过按下瞬间机械抖动造成的电平乱跳 while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0); //原地循环等待,直到手指松开按键(引脚变为1才退出) Delay_ms(20); //松开按键再次消抖,防止松开瞬间抖动误触发 KeyNumber=1; //标记:本次按下的是PB1按键,编号1 } //检测PB11按键 if (GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0) //读取PB11引脚输入电平,等于0说明按键按下 { Delay_ms(20); //按下消抖 while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0); //原地等待,直到松开PB11按键 Delay_ms(20); //松开消抖 KeyNumber=2; //标记:本次按下的是PB11按键,编号2 } return KeyNumber; //返回按键编号,0无按键,1=PB1,2=PB11 } /** * @brief LED1状态翻转函数(PA4引脚) * @note 读取内部输出寄存器的值,当前亮就灭,当前灭就亮 */ void LED1_Turn(void) { //GPIO_ReadOutputDataBit:读取单片机内部输出寄存器设置的值,不是外部引脚真实电压 if (GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_4)==0) //如果PA4内部输出是0(LED亮) { GPIO_SetBits(GPIOA,GPIO_Pin_4); //PA4置高电平,熄灭LED1 } else { GPIO_ResetBits(GPIOA,GPIO_Pin_4); //PA4置低电平,点亮LED1 } } /** * @brief LED2状态翻转函数(PA5引脚) * @note 读取内部输出寄存器的值,当前亮就灭,当前灭就亮 */ void LED2_Turn(void) { if (GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_5)==0) //读取PA5内部输出值,如果是0(LED亮) { GPIO_SetBits(GPIOA,GPIO_Pin_5); //PA5置高电平,熄灭LED2 } else { GPIO_ResetBits(GPIOA,GPIO_Pin_5); //PA5置低电平,点亮LED2 } } int main() { //开启GPIOA外设时钟,GPIO使用前必须打开时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //定义GPIO初始化结构体,用来配置PA4、PA5 GPIO_InitTypeDef GPIO_InitStructure_0; GPIO_InitStructure_0.GPIO_Mode=GPIO_Mode_Out_PP; //模式:推挽输出,用来驱动LED GPIO_InitStructure_0.GPIO_Pin=GPIO_Pin_4 | GPIO_Pin_5; //选中PA4、PA5两个引脚 GPIO_InitStructure_0.GPIO_Speed=GPIO_Speed_50MHz; //IO口翻转速度,输出模式才生效 GPIO_Init(GPIOA,&GPIO_InitStructure_0); //把上面配置写入GPIOA寄存器,生效 //开启GPIOB外设时钟,按键在PB引脚,需要开启GPIOB时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //定义GPIOB初始化结构体,配置PB1 PB11按键引脚 GPIO_InitTypeDef GPIO_InitStructure_1; GPIO_InitStructure_1.GPIO_Mode=GPIO_Mode_IPU; //模式:内部上拉输入,适合按键(松开为高电平1,按下为低电平0) GPIO_InitStructure_1.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_11; //选中PB1、PB11两个按键引脚 GPIO_InitStructure_1.GPIO_Speed=GPIO_Speed_50MHz; //输入模式下,这个参数无效,可以随便写 GPIO_Init(GPIOB,&GPIO_InitStructure_1); //配置写入GPIOB寄存器,生效 while(1) //死循环,程序一直在这里循环跑 { uint8_t Key = Key_GetNumber(); //调用按键函数,获取按键编号,0无按键,1/2对应两个按键 if (Key==1) //如果拿到的编号等于1,代表PB1按键按下并松开 { LED1_Turn(); //翻转LED1状态,亮变灭、灭变亮 } if (Key==2) //如果拿到的编号等于2,代表PB11按键按下并松开 { LED2_Turn(); //翻转LED2状态 } } }3、光敏电阻控制蜂鸣器
//你好 #include "stm32f10x.h" // STM32标准外设库头文件,提供GPIO、时钟等底层函数 #include "Delay.h" // 延时函数头文件,提供Delay_ms毫秒延时 /** * @brief 读取光敏传感器DO引脚电平 * @retval 返回PA9引脚读到的电平,1=高电平,0=低电平 */ uint8_t Photosen_Get(void) { // GPIO_ReadInputDataBit:读取引脚外部实际输入电平(数字输入) return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_9); } /** * @brief 打开蜂鸣器(江科大有源低电平触发蜂鸣器) */ void Buzzer_On(void) { // GPIO_ResetBits:将PA8引脚置低电平(0V),蜂鸣器响 GPIO_ResetBits(GPIOA,GPIO_Pin_8); } /** * @brief 关闭蜂鸣器 */ void Buzzer_Off(void) { // GPIO_SetBits:将PA8引脚置高电平(3.3V),蜂鸣器关闭 GPIO_SetBits(GPIOA,GPIO_Pin_8); } int main() { // 开启GPIOA外设时钟,所有PA口引脚使用前,必须开启时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 定义GPIO初始化结构体,用来配置蜂鸣器PA8引脚 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; // 推挽输出模式,用来驱动蜂鸣器 GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8 ; // 选择PA8引脚 GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; // IO翻转速度,仅输出模式生效 GPIO_Init(GPIOA,&GPIO_InitStructure); // 将配置写入寄存器,使PA8配置生效 // 配置光敏传感器PA9引脚 // 再次开启GPIOA时钟(GPIOA时钟只需开启一次,重复写也没问题,不会报错) RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 定义光敏引脚初始化结构体 GPIO_InitTypeDef GPIO_PhotosenStructure; GPIO_PhotosenStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; // 浮空输入,光敏模块DO自带上下拉,单片机用浮空输入读取 GPIO_PhotosenStructure.GPIO_Pin=GPIO_Pin_9 ; // 选择PA9引脚,接光敏DO数字输出 GPIO_PhotosenStructure.GPIO_Speed=GPIO_Speed_50MHz; // 输入模式下该参数无效,可保留 GPIO_Init(GPIOA,&GPIO_PhotosenStructure); // 光敏引脚配置生效 // 主循环,程序会一直循环执行这里 while(1) { // 判断光敏传感器读到的电平是否等于0 if (Photosen_Get() == 0) { Buzzer_On(); // 读到0(遮光),打开蜂鸣器 } else { Buzzer_Off(); // 读到1(有光),关闭蜂鸣器 } Delay_ms(100); // 延时100ms,降低扫描频率,减少频繁读取IO的压力 } }4、C语言
关键字 | 位数 | 表示范围 | 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 |
以后要用stdint的关键字,含义没变就是变了个名字而已。
宏定义:#define
用途:用一个字符串代替一个数字,便于理解,防止出错;提取程序中经常出现的参数,便于快速修改
定义宏定义: #define ABC 12345
引用宏定义: int a = ABC; //等效于int a = 12345
typedef
用途:将一个比较长的变量类型名换个名字,便于使用
定义typedef: typedef unsigned char uint8_t;(uint8_t是新名字)
引用typedef: uint8_t a; //等效于unsigned char a;
(typedef与宏定义的区别:两个都是用来改名的,宏定义是全局无脑改名,typedef是专门改变量的名字并且能检查错误)
结构体struct
用途:数据打包,不同类型变量的集合(数组eg.char[10] /int[10]只能集合一种类型,需要集合不同类型就用struct)
定义结构体变量: struct{char x; int y; float z;} StructName; 因为结构体变量类型较长,所以通常用typedef更改变量类型名
怎么使用?StructName . x=1 就是结构体名字 . 里面子变量名字然后赋值
怎么用typedef重命名struct?
typedef struct{ int x; char y;} struct_a; int main() { struct_a.x=1; struct_a.y=2; }(在库函数里也可以看见GPIO初始化的结构体就是这样重命名的)
枚举:enum
用途:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合(就比如说我定义一个枚举是week,只能有七天,如果我后面将week赋值为星期八,就会显示错误)
定义枚举变量: enum{FALSE = 0, TRUE = 1} EnumName; 因为枚举变量类型较长,所以通常用typedef更改变量类型名(跟结构体类似定义方法)
引用枚举成员: EnumName = FALSE; EnumName = TRUE;