
一.GPIO是什么GPIO 单片机通用引脚用来输出高低电平、读取外部电平是单片机和硬件交互最基础的通道。二. 输入输出模式2.1、 输入类4 种浮空输入 引脚无上下拉悬空时电平随机仅适合外部自带驱动的信号。上拉输入 内部接上拉电阻默认高电平按键检测最常用。下拉输入 内部接下拉电阻默认低电平。图1 上拉输入/下拉输入(两个差不多只是上电默认电平不同)模拟输入 关闭数字电路专供 ADC 电压采集不能做普通 IO。图2 模拟输入2.2、通用输出类2 种推挽输出高低电平都能驱动驱动 LED、指示灯首选。D:漏极 G:栅极 S:源极N-MOS下管栅极电压 源极电压Source接地即0V时导通。也就是说给栅极高电平(1如3.3V)NMOS就导通。PMOS上管栅极电压源极电压Source接VDD即3.3V时导通。也就是说必须给栅极低电平(0,如0V)PMOS才导通。下面两个分别是推挽模式写入高低电平的电流走向图图3 推挽输出 置1图4 推挽输出 置0为什么电平到输出控制那里变成相反的电平了因为输出控制那个位置串联着一个反相器(非门)所以变成相反的电平了开漏输出只能主动拉低高电平需要外部上拉多用于总线通讯。在开漏输出模式中P-MOS是不工作的N-MOS又只能输出低电平在写入1(高电平)时VGVS,N-MOS管截止这个时候I/O口属于高阻态的状态想要输出高电平必须要自己外接上拉电阻。高阻态High-Z这是引脚内部驱动器的状态。指的是芯片内部的 N-MOS 和 P-MOS 全部截止关闭导致引脚内部与 VDD 和 VSS 完全断开。这是个“电路动作”描述的是“芯片有没有在干活”。为什么非要“外接”而不用内部的因为内部上拉电阻阻值太大40kΩ驱动能力极弱。在 I2C 高速通信400kHz 或更快时这么大的电阻加上线路寄生电容会导致上升沿极其缓慢波形变“钝”造成通信时序错乱。所以外部上拉电阻通常选用 2kΩ ~ 10kΩ阻值越小上升沿越快但功耗越大。这在硬件电路设计时是必须外挂的元器件。下面是开漏输出的写入低电平的电流走向图图5 开漏输出2.3、复用外设输出类2 种复用推挽输出引脚分配给串口、定时器 PWM 等外设推挽驱动。复用推挽、复用开漏的 MOS 驱动电路结构和普通推挽 / 开漏完全一致区别仅在于驱动信号来源不同普通模式由 CPU 写输出寄存器控制复用模式由片上外设直接输出信号控制软件寄存器不再干预引脚电平。下面是复用推挽输出的写入高电平的电流走向图图6 复用推挽输出复用开漏输出外设复用 开漏典型用于 I2C 总线。三、编写驱动程序3.1、介绍部分寄存器(GPIO、RCC)3.1.1、APB2 外设时钟使能寄存器(RCC_APB2ENR)它是挂载在APB2总线上的所有外设GPIO、USART1、ADC1等的“总电闸”——复位后默认全部关闭使用前必须通过该寄存器把对应位置1打开时钟。3.1.2、端口配置低寄存器(GPIOx_CRL) (xA..E)它用 4 个位共 32 位控制一个引脚同时决定了该引脚是输入/输出、输出速度、以及推挽/开漏/复用/模拟等具体模式。这是配置该GPIO的低8位引脚高两位CNFy[1:0],低二位MODEy[1:0]3.1.3、端口配置高寄存器(GPIOx_CRH) (xA..E)它用 4 个位共 32 位控制一个引脚同时决定了该引脚是输入/输出、输出速度、以及推挽/开漏/复用/模拟等具体模式。这是配置该GPIO的高8位引脚高两位CNFy[1:0],低二位MODEy[1:0]3.1.4、端口输入数据寄存器(GPIOx_IDR) (xA..E)这是一个“只读”寄存器它的 16 个有效位Bit0~Bit15直接反映了 GPIO 引脚 Px0~Px15 当前的实时电平状态高/低。 你读到的 1 就是高电平3.3V读到的 0 就是低电平0V。3.1.5、端口输出数据寄存器(GPIOx_ODR) (xA..E)它是一个“可读可写”的寄存器向对应位写入 1 让引脚输出高电平3.3V写入 0 让引脚输出低电平0V。 但写入的效果取决于当前引脚的模式推挽输出时直接驱动电压开漏输出时只能控制拉低而在输入模式下它另有妙用控制上下拉。3.1.6、端口位设置/清除寄存器(GPIOx_BSRR) (xA..E)这是一个 32 位的“只写”寄存器读回值无效低 16 位用来将对应引脚输出高电平高 16 位用来将对应引脚输出低电平。 向任意位写 1 执行动作写 0 则无影响。最核心的优势是一条硬件指令即可完成置位或清零绝对原子永不被打断。3.1.7、端口位清除寄存器(GPIOx_BRR) (xA..E)这是一个 32 位的“只写”寄存器只有低 16 位有效对应 Px0~Px15。向某个位写 1对应引脚就被拉低为 0V写 0 则无任何影响。 它和 BSRR 配合构成了 F10x 下原子操作 GPIO 的“黄金搭档”。3.1.8、端口配置锁定寄存器(GPIOx_LCKR) (xA..E)一旦按特定序列锁定GPIO 引脚的模式配置输入/输出/复用等就会被硬件冻结直到下次复位前都无法再修改。 它像一个保险开关专门用来防止关键引脚的配置被意外篡改。3.2、STM32编程我们今天的任务是使用STM32F103C8T6最小系统板点亮一个led灯让其进行闪烁在原理图上led灯连接的是PC13低电平点亮,所以我们控制PC13一直产生一个高低电平即可3.2.1、寄存器编程先找到外设基地址打开STM32f10x-中文参考手册在储存器映射这里最下面就是外设基地址也是APB1的基地址直接写在代码里面/*片上外设基地址 */ #define PERIPH_BASE ((unsigned int)0x40000000) #define APB1PERIPH_BASE PERIPH_BASE一下是APB2和AHB的基地址AHB原本应该用0x40018000的我们这边为了方便使用0x40020000/*APB2 总线基地址 */ #define APB2PERIPH_BASE (PERIPH_BASE0x10000) /* AHB总线基地址 */ #define AHBPERIPH_BASE (PERIPH_BASE0x20000)找到GPIOC的外设基地址/*GPIOC外设基地址*/ #define GPIOC_BASE (APB2PERIPH_BASE 0x1000)GPIO的寄存器我们只使用到了GPIOC_CRH,GPIOC_ODR,直接用宏定义#define定义用GPIOC的外设基地址加上我们要使用到的寄存器的偏移地址就可以了/* GPIOC寄存器地址,强制转换成指针 */ #define GPIOC_CRH *(unsigned int *)(GPIOC_BASE 0x04) #define GPIOC_ODR *(unsigned int *)(GPIOC_BASE 0x0C)除了以上的几个寄存器还是不够的我们还需要启动该GPIO的时钟因为RCC是在AHB上面所以我们用AHB的基地址加上RCC的基地址然后再加RCC_APB2ENR寄存器的偏移地址就可以了(unsigned int *)强制转换成无符号32位的指针类型*解引用(找到地址里的数据)/*RCC外设基地址*/ #define RCC_BASE (AHBPERIPH_BASE 0x1000) /*RCC的AHB1时钟使能寄存器地址,强制转换成指针*/ #define RCC_APB2ENR *(unsigned int *)(RCC_BASE 0x18)主函数文件夹#include stm32f10x.h void Delay(unsigned int us) { for(volatile unsigned int y 0;y us;y) { ; } } /** * 主函数 */ int main(void) { // 开启GPIOC 端口时钟 RCC_APB2ENR | (14); //先清空控制PC13的端口位 GPIOC_CRH ~( 0x0F (4*5));//~( 0x0F (4*5)) 1111 1111 0000 1111 | 1111 1111 1111 1111 // 然后再配置PC13为通用推挽输出速度为10M GPIOC_CRH | (0x014*5); // PC13 输出 低电平 GPIOC_ODR ~(113); while(1) { // PC13 输出 高电平 GPIOC_ODR | (113); //直接操控ODR来置位/清零可能会被中断打断不安全 //可以用下面这这两个寄存器来做置位/清零 //GPIOC_BSRR (1 13); // 高电平 //GPIOC_BRR (1 13); // 低电平 (F10x 专用) // //延时 Delay(0xffff); // P13 输出 低电平 GPIOC_ODR ~(113); //延时 Delay(0xffff); } } // 函数为空目的是为了骗过编译器不报错 void SystemInit(void) { ; }3.2.2、标准库操作#include stm32f10x.h // Device header //延时 void Delay(unsigned int us) { for(volatile unsigned int y 0;y us;y) { ; } } int main(void) { //启动GPIOC的时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //创建一个GPIO的初始化结构体 GPIO_InitTypeDef GPIO_InitStructure; //使用推挽输出 GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //使用PC13 GPIO_InitStructure.GPIO_Pin GPIO_Pin_13; //速度配置为50MHz GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; //以上程序还没有写入GPIOC的寄存器的 //GPIO_Init将刚刚配置的全部写到寄存器 GPIO_Init(GPIOC, GPIO_InitStructure); while (1) { //PC13置零 GPIO_ResetBits(GPIOC, GPIO_Pin_13); //延时 Delay(0xffff); //PC13置零 GPIO_SetBits(GPIOC, GPIO_Pin_13); //延时 Delay(0xffff); } }上面大部分图片来源为《STM32F10x-中文参考手册》如果你觉得有帮助欢迎点赞、收藏、评论让更多人看到