GPIO基本结构
GPIO 挂载在 APB2 上,每个 GPIO 口都有 16 个引脚,每个引脚对应32位输入输出寄存器中的一位,其命名方式为 Pxx。
当某引脚的输出寄存器为1,就代表该引脚输出高电平,而如果输入高电平,则输入寄存器置为1,反之相反。

对于 GPIO 口的端口位结构的电路图,其基本电路结构我们没必要了解,有兴趣可以看看,这里只需要知道有哪些输入输出模式即可,后面能在程序中进行配置,具体配置方式参见:STM32F103参考手册中文.pdf,前面也有提到过。

1、输入模式:
- 浮空输入:逻辑器件与引脚既不接高电平,也不接低电平,电平就不确定了
- 上拉输入:把电位拉高,因此默认高电平
- 下拉输入:把电位拉低,因此默认低电平
- 模拟输入:用于芯片引脚模拟信号输入到内部的模数转换器(ADC)
2、输出模式:
- 开漏输出:此时只控制 N-MOS管 开闭,外部可以接更大的电压,来实现导通和断开,因为 N-MOS管是低电平闭合,所以寄存器输入 0,N-MOS 导通接地,那么外界接高电平,此时就可以实现导通
- 复用开漏输出:由片上外设控制,高电平接高阻态,低电平接入 VSS
- 推挽输出:可以输出高、低电平,连接数字器件
- 复用推挽输出:可以理解为 GPIO 口被用作第二功能时的配置情况
新建项目
这里就不再进行操作了,需要新建3个文件夹,start,Library,User,具体的工程模板可参见前一篇文章:STM32工程创建与点灯实验-CSDN博客(ps:这里可以在 manage project item 中快速建立起 group 和对应文件,比前面自己一点点添加要方便一些)
注意:添加的时候别把启动文件给添加多了,添加多了会报错的😭,本课程需要添加的是startup_stm32f10x_md.s

删除过程文件,快速缩减文件体积的方式在前面的文章:STM32工程创建与点灯实验-CSDN博客,其中已经介绍过了,主要是同添加一个windows批量处理脚本keilkill.bat 来实现的,这里就直接略过了。
GPIO输出实验
这里通过 LED 二级发光管来直观的了解 GPIO 的输出
LED 相关硬件接线图
发光二极管长引脚为正,接入正极,负极接到 PA0 引脚,这时候如果 PA0 引脚输出低电平,那么就会亮,输出高电平就会熄灭。

LED 相关程序与结果
点灯
编写代码的时候一开始要善用函数说明,毕竟初始化配置是根本记不住的。

ps:LED还挺容易坏的,一开始接了一个红色的LED没亮,有一股烧焦的味道,看样子直接烧坏了,这应该就是为什么要加电阻,后面换了个才亮😭。
LED闪烁实验
1、原理:通过在循环中添加延时函数,并交替亮灭来完成闪烁,由于需要间隔时间,所以要用到 delay 函数
该函数的创建步骤如下所示
在自己的工程文件夹内再新建一个 system,这个文件用来存放系统资源,目前需要存放的资源就是延时函数 delay

同样将它们都给添加到 keil 的 group 中。
我把江科大中的延时函数程序源码贴在这里
delay.h 文件
#ifndef __DELAY_H
#define __DELAY_H
void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);
#endif
delay.c 文件
#include "stm32f10x.h"
/**
* @brief 微秒级延时
* @param xus 延时时长,范围:0~233015
* @retval 无
*/
void Delay_us(uint32_t xus)
{
SysTick->LOAD = 72 * xus; //设置定时器重装值
SysTick->VAL = 0x00; //清空当前计数值
SysTick->CTRL = 0x00000005; //设置时钟源为HCLK,启动定时器
while(!(SysTick->CTRL & 0x00010000)); //等待计数到0
SysTick->CTRL = 0x00000004; //关闭定时器
}
/**
* @brief 毫秒级延时
* @param xms 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_ms(uint32_t xms)
{
while(xms--)
{
Delay_us(1000);
}
}
/**
* @brief 秒级延时
* @param xs 延时时长,范围:0~4294967295
* @retval 无
*/
void Delay_s(uint32_t xs)
{
while(xs--)
{
Delay_ms(1000);
}
}
LED闪烁 main 函数代码:
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
Delay_ms(500);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
Delay_ms(500);
}
}
最终效果如下:

LED流水灯
LED 流水灯就需要打开 PA 的所有引脚,然后再通过 for 循环遍历每一个引脚,并添加延时函数即可实现
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
for(int i = 0; i < 8; i++)
{
GPIO_Write(GPIOA,~(0x0001<<i));
Delay_ms(500);
}
}
}
最终效果如下:

蜂鸣器
蜂鸣器就跟前面的流水灯差不多的操作,低电平触发,蜂鸣器声音太响了,这个实验就略过了。
GPIO输入实验
这里需要再新建 Hardware 文件夹来存放驱动程序。

按键控制
按键是通过内部的弹簧片来完成的,那么使用弹簧片就会出现抖动,这里可以直接通过延时来进行消抖,基本上是按照江科大的思路来编写的程序,不过由于面包板部分插口接触不良(排查了半天),所以按键引脚换成了 PB0。
程序编写思路大体如下:
- LED(PA1, PA2) 初始化:这里初始化就仍然用推挽输出模式即可
- LED 相关功能实现:亮灭操作,
- 按键(PB0,PB11)初始化:因为这里我按键接入的是3,3V高电平,所以输入模式需要使用下拉输入,让该引脚始终保持低电平,那么按键接通的时候才会输入高电平,这时候就能使用stm32 中相应的库函数
GPIO_ReadInputDataBit()来读取当前引脚电平的变化,从而知道哪个按键按下。 - 按键功能:按键的定位操作,也就是得到哪个按键被按下。
- 最后在
main.c中调用对应的函数实现即可
源码如下:
key.c
#include "stm32f10x.h"
#include "delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_11;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 1);
Delay_ms(20);
KeyNum = 1;
}
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 1)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 1);
Delay_ms(20);
KeyNum = 2;
}
return KeyNum;
}
key.h
#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
uint8_t Key_GetNum(void);
#endif
led.h
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_WriteBit(GPIOA, GPIO_Pin_1 | GPIO_Pin_2, Bit_RESET);
}
void LED1_ON(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
}
void LED1_OFF(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
}
void LED1_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_1);
}
else
{
GPIO_SetBits(GPIOA, GPIO_Pin_1);
}
}
void LED2_ON(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_RESET);
}
void LED2_OFF(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_2, Bit_SET);
}
void LED2_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 1)
{
GPIO_ResetBits(GPIOA, GPIO_Pin_2);
}
else
{
GPIO_SetBits(GPIOA, GPIO_Pin_2);
}
}
led.h
#ifndef __LED_H
#define __LED_H
void LED_Init(void);
void LED1_Turn(void);
void LED1_OFF(void);
void LED1_ON(void);
void LED2_Turn(void);
void LED2_ON(void);
void LED2_OFF(void);
#endif
最终效果如下:

按下 PB0 处的按键,LED1(对应 PA1引脚) 会熄灭或点亮
按下 PB11 处的按键,LED2(对应PA2引脚)会熄灭或点亮
光敏传感器控制蜂鸣器
光敏电阻如图所示,这里我们使用三根杜邦线来控制光敏传感器

接电后,蓝色的部分用来调节其发光阈值,当光照强度到达一定强度时,光敏电阻就会亮,如动图所示

电路接线实物图如下图所示:

这里用到了 PB12 和 PB13 引脚,PB12引脚接蜂鸣器,PB13引脚接光敏传感器,其余就是接VCC和GND的了
功能:当光照达到一定程度时,蜂鸣器会发出警报,流程图如下图所示

具体代码实现如下:
buzzer.c
#include "stm32f10x.h"
void Buzzer_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_OFF(void)
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_On(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
void Buzzer_Turn(void)
{
if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0)
{
GPIO_SetBits(GPIOB, GPIO_Pin_12);
}
else
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
}
}
buzzer.h
#ifndef __BUZZER_H
#define __BUZZER_H
void Buzzer_Init(void);
void Buzzer_OFF(void);
void Buzzer_On(void);
void Buzzer_Turn(void);
#endif
LightSensor.c
#include "stm32f10x.h"
void LightSensor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
uint8_t LightSensor_Get(void)
{
return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13);
}
LightSensor.h
#ifndef __LIGHTSENSOR_H
#define __LIGHTSENSOR_H
void LightSensor_Init(void);
uint8_t LightSensor_Get(void);
#endif
main.c: main函数部分就是实现了当光照达到一定程度,蜂鸣器会发出警报的功能,通过一个 if 判断语句即可实现
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "buzzer.h"
#include "LightSensor.h"
uint8_t KeyNum;
int main(void)
{
Buzzer_Init();
LightSensor_Init();
while(1)
{
if(LightSensor_Get() == 1)
{
Buzzer_OFF();
}
else
{
Buzzer_On();
}
}
}
参考:

3266

被折叠的 条评论
为什么被折叠?



