2. 外设——GPIO
2.1 GPIO简介
GPIO(General Purpose Input Output)通用输入输出口 可配置8种输入输出模式 引脚电平:0V~3.3V,部分引脚可容忍5V(在STM32F103C8T在6引脚定义中,带FT可容忍5V) 在输出模式下,端口输出的高低电平可以控制LED、控制蜂鸣器,模拟通信协议输出顺序等 读取按键输入、外部模块电平信号输入、ADC模拟通信协议接收数据等
2.2 GPIO基本结构
在STM32中,所有的GPIO都是挂在在APB2外设总线,其中GPIO根据外设的名称GPIOA、GPIOB、GPIOC等等,这样命名,每个GPIO总共有16个引脚(0-15)
寄存器只负责存储数据,如果要进行点灯操作,还需要驱动器增强驱动能力
2.3 GPIO位结构
**施密特触发器的作用:**对于输入电压进行整形手术,其执行逻辑是,如果输入电影大于某个阈值,输出将立即升高到高电平,如果输入电压小于某个阈值,输出将立即降低到低电平
2.3.1 GPIO模式
通过配置GPIO端口配有寄存器,可配置以下8种模式
浮空输入 | 数字输入 | 可读取引脚电平。如果引脚悬空,则电平不确定 |
数字输入 | ||
下拉输入 | 数字输入 | 可读取引脚电平,内部连接下拉电阻,悬挂时默认低电平 |
模拟输入 | 模拟输入 | GPIO无效,引脚直接进入内部ADC |
数字输出 | ||
数字输出 | ||
复用开漏输出 | 数字输出 | 高电平为高阻态,低电平由片上外设控制VSS |
复用推挽输出 | 数字输出 | 由片上外设控制,高电平接VDD,低电平接 |
2.3.2 浮空/上拉/下拉输入
2.3.3 模拟输入
2.3.4 泄漏/推挽输出
2.3.5 复用开漏/推挽输出
2.4 GPIO的输出
LED闪烁&LED流水灯&蜂鸣器
2.4.1 LED闪烁
拿出一个LED,长脚正极接正极,短脚负极接正极PA0端口(低电平点亮)
新建LED点亮工程(可赋值上述工程模板并进行更改)
我们可以在项目目录中建立一个新的文件夹,称为System,存储系统资源。
#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
#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);
}
}
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置使能时钟
GPIO_InitTypeDef GPIO_InitStructure;//结构体给参数
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//GPIO_SetBits(GPIOA,GPIO_Pin_0);//设置高电平 ,灭
//GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//设置高电平 ,灭
//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//设置低电平,点亮
//GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//设置低电平,点亮
while(1)
{
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_RESET);//设置低电平,点亮
Delay_ms(500);
GPIO_WriteBit(GPIOA,GPIO_Pin_0,Bit_SET);//设置高电平 ,灭
Delay_ms(500);
}
}
2.4.2 LED流水灯
如果要控制多个端口可以进行按位或来进行操作
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 等等
这里我们也可以使用
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All 表示全部选择
可以同时控制16个端口
GPIO_Write(GPIOx,指定写到输出数据寄存器的值);//GPIOx->ODR = 指定写到输出数据寄存器的值
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_All;//选择A的所有端口
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
while(1)
{
//低电平点亮,第一个亮,其他灯灭
GPIO_Write(GPIOA,~0x0001);//0000 0000 0000 0001
Delay_ms(500);
GPIO_Write(GPIOA,~0x0002);//0000 0000 0000 0010
Delay_ms(500);
GPIO_Write(GPIOA,~0x0004);//0000 0000 0000 0100
Delay_ms(500);
GPIO_Write(GPIOA,~0x0008);//0000 0000 0000 1000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0010);//0000 0000 0001 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0020);//0000 0000 0010 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0040);//0000 0000 0100 0000
Delay_ms(500);
GPIO_Write(GPIOA,~0x0080);//0000 0000 1000 0000
Delay_ms(500);
}
}
2.4.3 蜂鸣器
使用的PB12口
#include "stm32f10x.h" // Device header
#include "Delay.h"
int main()
{
// RCC->APB2ENR=0x00000010;
// GPIOC->CRH =0x00300000;
// GPIOC->ODR =0x00002000;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,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);
while(1)
{
GPIO_SetBits(GPIOB,GPIO_Pin_12);//设置高电平
Delay_ms(500);
GPIO_ResetBits(GPIOB,GPIO_Pin_12);//设置低电平
Delay_ms(500);
}
}
2.5 GPIO的输入
按键控制LED&光敏传感器控制蜂鸣器
介绍c语言的相关知识点
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 |
关键字:#define
用途:用一个字符串代替一个数字,便于理解,防止出错;提取程序中经常出现的参数,便于快速修改
定义宏定义:
#define ABC 12345
引用宏定义:
int a = ABC; //等效于int a = 12345;
关键字:typedef
用途:将一个比较长的变量类型名换个名字,便于使用
定义typedef:
typedef unsigned char uint8_t;
引用typedef:
uint8_t a; //等效于unsigned char a;
C语言结构体
关键字:struct
用途:数据打包,不同类型变量的集合
定义结构体变量:
struct{char x; int y; float z;} StructName;
因为结构体变量类型较长,所以通常用typedef更改变量类型名
引用结构体成员:
StructName.x = 'A';
StructName.y = 66;
StructName.z = 1.23;
或 pStructName->x = 'A'; //pStructName为结构体的地址 pStructName->y = 66;
pStructName->z = 1.23;
C语言枚举
关键字:enum
用途:定义一个取值受限制的整型变量,用于限制变量取值范围;宏定义的集合
定义枚举变量:
enum{FALSE = 0, TRUE = 1} EnumName;
因为枚举变量类型较长,所以通常用typedef更改变量类型名
引用枚举成员:
EnumName = FALSE;
EnumName = TRUE;
2.5.1 按键控制LED
首先,模块化编程,封装驱动代码,简化主函数
我们在工程目录新建一个文件夹,Hardware,用来存放硬件驱动,在再keil新建一个组也叫Hardware,并添加到头文件路径列表中
LED.c(LED闪烁)
#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_Pin=GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//这里注意,GPIO配置好了之后,默认是低电平
GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2);//设置高电平,
}
void LED1_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//设置低电平
}
void LED1_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);//设置高电平
}
void LED2_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//设置低电平
}
void LED2_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);//设置高电平
}
LED.h(LED闪烁)
#ifndef __LED_H
#define __LED_H
void LED_Init(void);//声明该函数可以被外部调用
void LED1_ON(void);
void LED1_OFF(void);
void LED2_ON(void);
void LED2_OFF(void);
#endif
mian.c(LED闪烁)
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h" //加上LED.h,就可以调用函数了
int main()
{
LED_Init();//可能会爆红,编译一下就好了
while(1)
{
void LED1_ON(void);
Delay_ms(500);
void LED2_OFF(void);
Delay_ms(500);
void LED1_OFF(void);
Delay_ms(500);
void LED2_ON(void);
Delay_ms(500);
}
}
按键的一端
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_IPU;//上拉输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
uint8_t Key_GetNum(void)
{
uint8_t KeyNum=0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
{
Delay_ms(20);//消抖
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0);//检测是否松手
Delay_ms(20);//消抖
KeyNum=1;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0)
{
Delay_ms(20);//消抖
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)==0);//检测是否松手
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.c(完整)
#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_Pin=GPIO_Pin_1 | GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//这里注意,GPIO配置好了之后,默认是低电平
GPIO_SetBits(GPIOA,GPIO_Pin_1 | GPIO_Pin_2);//设置高电平,
}
void LED1_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//设置低电平
}
void LED1_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);//设置高电平
}
void LED2_ON(void)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//设置低电平
}
void LED2_OFF(void)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);//设置高电平
}
//PA1端口的电平翻转
void LED1_Turn(void)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_1)==0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_1);//设置高电平
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);//设置低电平
}
}
//PA2端口的电平翻转
void LED2_Turn(void)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)==0)
{
GPIO_SetBits(GPIOA,GPIO_Pin_2);//设置高电平
}
else
{
GPIO_ResetBits(GPIOA,GPIO_Pin_2);//设置低电平
}
}
LED.h(完整)
#ifndef __LED_H
#define __LED_H
void LED_Init(void);//声明该函数可以被外部调用
void LED1_ON(void);
void LED1_OFF(void);
void LED2_ON(void);
void LED2_OFF(void);
void LED1_Turn(void);
void LED2_Turn(void);
#endif
main.c(完整)
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "LED.h" //加上LED.h,就可以调用函数了
#include "Key.h"
uint8_t KeyNum=0;//接收返回值
int main()
{
LED_Init();//可能会爆红,编译一下就好了
Key_Init();
while(1)
{
KeyNum=Key_GetNum();//不断读取按键返回值
if(KeyNum==1)
{
LED1_Turn();
}else if(KeyNum==2)
{
LED2_Turn();
}
}
}
2.5.2 光敏传感器控制蜂鸣器
接线图
依旧是在Hardware文件夹内添加
封装蜂鸣器功能(PB12)
Buzzer.c
#include "stm32f10x.h" // Device header 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配置好了之后,默认是低电平 GPIO_SetBits(GPIOB,GPIO_Pin_12);//设置高电平, } void Buzzer_ON(void) { GPIO_ResetBits(GPIOB,GPIO_Pin_12);//设置低电平 } void Buzzer_OFF(void) { GPIO_SetBits(GPIOB,GPIO_Pin_12);//设置高电平 } //PB12端口的电平翻转 void Buzzer_Turn(void) { if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)==0 标签:
快恢复肖特基基二极管