初学STM32之简单认识IO口配置(学习笔记)

在使用51单片机的时候基本上不需要额外的配置IO,不过在使用特定的IO的时候需要额外的设计外围电路,比如PO口它是没有内置上拉电阻的。因此若想P0输出高电平,它就需要外接上拉电平。(当然这不是说它输入不需要上拉电阻,主要是它作为输入端的时候接收高电平就是共外围电路的上拉电阻)。

操作目标是控制STM32F103C8T6上某个IO口让其置1或者置0.

GPIO是APB2上的外设资源。

‌**APB2AMBA总线结构中的一种,主要用于低带宽的周边外设之间的连接。**

APB2(Advanced Peripheral Bus 2)是ARM公司提出的AMBA总线结构之一,属于一种片上总线结构。APB2主要用于连接低速外设,如I2CUARTSPI等,这些外设通常具有低带宽和低功耗的需求。

APB2的特点

  1. 低带宽‌:APB2主要用于连接低速外设,适合那些不需要高速数据传输的外设。
  2. 非流水作业‌:APB2的传输至少需要两个时钟周期,且数据在时钟的上升沿变化,不需要等待周期和回应信号。
  3. 控制逻辑简单‌:APB2只有四个控制信号,传输可以采用状态机表示,控制逻辑相对简单。

APB2与其他总线的区别

  • AHB的区别 ‌:AHB是高级高性能总线,主要用于连接高性能模块如CPU、DMA和高速存储器。APB2则是用于连接低速外设。
  • 与APB3和APB4的区别 ‌:APB3增加了PREADYPSLVERR信号,用于反馈读和写的状态;APB4增加了PPROTPSTRB信号,用于提供保护机制和字节选通功能。

在51单片机中如果我们要是某个IO口,比如P1.0端口输出高电平 ,直接赋值即可。但作为输入端就稍微麻烦点需要先赋值P1.0为高电平,再读取P1.0端口的电压。需要两步走。

GPIO作为一种外设资源,在使用它的时候要经过这么几步走,

  • 第一步:假设要是能PC13使其输出高电平,经查得知PC13是GPIOC的编号13的IO口(按照顺序因该是第14位IO,是从0开始编的)所以第一步使能能APB2外设总线时钟寄存器

APB2这个外设使能时钟寄存器名字是RCC_APB2ENR,作为32位的单片机它的特殊功能寄存器一般也是32位,点一下APB2上的外设个数,刚好是16个,GPIOC是其中的一位,然后我们看一下手册上的描述(库函数里有21个)

确实是0-15位可控制,后16位是保留的。从他的复位值可知默认的都是0;详细查看可知

可知位4(是第5位)至1,即可使能GPIOC I/O口。即 RCC->APB2EBR = 0x00000010

= 0B 0000 0000 0000 0000 0000 0000 0001 0000

  • 第二步:GPIO和51单片机的IO口的不同之处是它的输入输出是分开的需要额外的进行功能选择的,而且对应IO口处于什么结构模式也是需要选择的,

在STM32中控制这些功能,每个IO口需要4位,16个IO就是64位,因此这些IO口的功能选择控制寄存器就分成了两个,端口控制高寄存器(CRH)和端口控制低寄存器(CRL),PC13应是由CRH控制,打开手册得知:

先看一下它的复位值:0x4444 4444 = 0b0100 0100 0100 0100 0100 0100 0100 0100

可以看到0100代表的是浮空输入模式,即STM32上电后的复位状态都是浮空输入模式。

浮空输入(GPIO_Mode_IN_FLOATING)
  • 电路结构:无内部上下拉电阻,引脚悬空。
  • 特点
    • 电平由外部电路决定。
    • 未连接外部电路时,电平可能不稳定(易受干扰)。

因此控制这个IO口我们选择0011 (通用推挽输出模式速度50HZ) 即我们赋值寄存器

GPIOC->CRH = 0x0030 0000;当然这个写法是不太对,其它位我们都赋值为0了,

一般来说需要先把该处的控制位清0;即GPIOC->CRH &= 0x1101 1111;

然后再通过或运算给控制位赋值:即GPIOC->CRH |= 0x0030 0000;(1个16进制数代表4个2进制数)

第三步: 选择输出高电平还是低电平,在51机中我们直接就是使用赋值语句进行赋值,如:P1.0 = 0;但是在STM32中是在专用的寄存器里操作的而且分输入和输出,端口输出数据寄存器(GPIOx_ODR) (x=A..E),端口输入数据寄存器(GPIOx_IDR) (x=A..E),目前是需要输出一个低电平,

我们是需要在位13处写0,正常情况下还是需要经过与或运算才是正确的赋值方式,这边就直接赋值其它位都置0:

GPIOC->ODR = 0x0000 0000;//输出低电平

GPIOC->ODR = 0x0000 2000//输出高电平

经过这三步我们就成功使PC13端口输出低电平或者高电平(通用推挽输出方式,50MHZ)

这是使用寄存器的方式进行操作。还有使用库函数的,库函数就不叙述了。

笔者的资料来自B站江协科技。


模式 电平特性 驱动能力 适用场景 注意事项
模拟输入 模拟信号 - ADC/DAC 禁用数字功能
浮空输入 由外部决定 - 外部有上下拉的总线 避免悬空
下拉输入 默认低电平 - 检测高电平有效信号 外部信号需强驱动
上拉输入 默认高电平 - 检测低电平有效信号 外部信号需强驱动
推挽输出 主动驱动高 / 低电平 LED、高速信号 避免总线冲突
开漏输出 需外部上拉 I2C、电平转换 必须外接上拉电阻
复用推挽 外设驱动高 / 低电平 定时器 PWM、USART_TX 需配置外设功能
复用开漏 外设驱动 + 外部上拉 I2C、CAN 总线 需配置外设功能

一、输入模式

1. 模拟输入(GPIO_Mode_AIN)
  • 电路结构:引脚直连 ADC/DAC 模块,断开内部上下拉电阻。
  • 特点
    • 无施密特触发器,直接传递模拟信号。
    • 禁止数字信号输入(无法读取高低电平)。
  • 应用
    • ADC 电压采集(如传感器信号)。
    • DAC 模拟输出(某些引脚支持)。
2. 浮空输入(GPIO_Mode_IN_FLOATING)
  • 电路结构:无内部上下拉电阻,引脚悬空。
  • 特点
    • 电平由外部电路决定。
    • 未连接外部电路时,电平可能不稳定(易受干扰)。
  • 应用
    • 需要外部上拉或下拉的场景(如总线通信:I2C 的 SDA/SCL 需外部上拉)。
    • 电平由外部设备驱动(如中断信号线)。
3. 下拉输入(GPIO_Mode_IPD)
  • 电路结构:内部连接下拉电阻(约 20-50kΩ),默认低电平。
  • 特点
    • 引脚悬空时,默认读取低电平。
    • 外部高电平可覆盖下拉。
  • 应用
    • 避免引脚悬空引发误触发(如按键检测低电平有效)。
4. 上拉输入(GPIO_Mode_IPU)
  • 电路结构:内部连接上拉电阻(约 20-50kΩ),默认高电平。
  • 特点
    • 引脚悬空时,默认读取高电平。
    • 外部低电平可覆盖上拉。
  • 应用
    • 避免引脚悬空引发误触发(如按键检测高电平有效)。

二、输出模式

5. 推挽输出(GPIO_Mode_Out_PP)
  • 电路结构:P-MOS 和 N-MOS 组合,可主动输出高 / 低电平。
  • 特点
    • 高电平:P-MOS 导通,输出 VDD。
    • 低电平:N-MOS 导通,输出 GND。
    • 驱动能力强,适合高速切换。
  • 应用
    • 驱动 LED、继电器等负载。
    • 高频信号(如 PWM、SPI 时钟)。
6. 开漏输出(GPIO_Mode_Out_OD)
  • 电路结构:仅 N-MOS,需外部上拉电阻输出高电平。
  • 特点
    • 高电平:N-MOS 关闭,电平由外部上拉电阻决定。
    • 低电平:N-MOS 导通,输出 GND。
    • 支持 "线与" 逻辑,但上升沿较慢。
  • 应用
    • I2C、SMBUS 等总线通信。
    • 电平转换(如 5V 与 3.3V 设备通信)。
7. 复用推挽(GPIO_Mode_AF_PP)
  • 电路结构:与普通推挽相同,但输出由外设控制(如 USART、SPI)。
  • 特点
    • 外设自动管理电平输出(如定时器 PWM、串口 TX)。
    • 高驱动能力,适合高频外设。
  • 应用
    • 外设功能引脚(如 TIMx_CHx、USART_TX)。
    • 需要硬件自动控制的场景。
8. 复用开漏(GPIO_Mode_AF_OD)
  • 电路结构:与普通开漏相同,但输出由外设控制。
  • 特点
    • 需外部上拉电阻,支持 "线与" 逻辑。
    • 由外设自动管理电平(如 I2C 的 SDA/SCL)。
  • 应用
    • I2C、CAN 总线等复用功能。
    • 多设备共享总线通信。

这是驱动电路,然后我们调用端口各个功能测试一下他们的区别:

  • GPIO_Mode_IPU 使PC13处于输入状态,并且内部设置上拉电阻,LED熄灭,端口电压3.2V

可以设置端口电压为低电平,这时LED会点亮不过亮度不够端口电压为1V。

  • GPIO_Mode_IPD 使PC13处于输入状态,并且内部设置下拉电阻,LED依然会点亮不过亮度不够

端口电压1V

  • GPIO_Mode_IN_FLOATING LED熄灭端口电压1.3V,推测是是驱动电流不够
  • 当设置IO输出端口的时候如果忘记设置频率将无法正确使IO工作,同理如果忘记设计其它参数IO口也无法正确使能?从测试结果看电压是1.3V即端口状态是之前的浮空输入状态。

使用库函数配置IO口使PC13端口输出低电平

复制代码
#include "stm32f10x.h"                  // Device header
int main(void)
{
	//RCC->APB2ENR = 0x00000010; //APB2 外设时钟使能寄存器
	//GPIOC->CRH = 0x00300000; //端口配置高寄存器
	//GPIOC->ODR = 0X00000000; //端口输出数据寄存器
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOC,&GPIO_InitStructure);
	//GPIO_SetBits(GPIOC,GPIO_Pin_13);//端口设置为1
	GPIO_ResetBits(GPIOC,GPIO_Pin_13); //端口设置为0
	
  while(1)
	{
	
	}
}

GPIOpin的选择上可以用或运算:

复制代码
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_6 | GPIO_Pin_7 ;				//GPIO引脚,赋值为0,2,3,4,6,7

库函数与对应的寄存器列举

下面的程序是PB14外部中断的初始化过程

复制代码
void CountSensor_Init(void)
{
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//GPIOB时钟使能
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//AFIO时钟使能
	
	/*GPIO初始化 */
	GPIO_InitTypeDef  GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;//上拉
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 ;  //14位
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);    //使能GPIOB_14设置
	
	/*AFIO选择引脚*/
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//
	
	/*EXIT初始化*/
    EXTI_InitTypeDef EXIT_InitStructure;
	EXIT_InitStructure.EXTI_Line = EXTI_Line14 ;
	EXIT_InitStructure.EXTI_LineCmd = ENABLE ;
	EXIT_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //选择外部中断
	EXIT_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling ;//下降沿
    EXTI_Init(&EXIT_InitStructure);//使能外部中断设置
	
	/*设置NVIC,中断优先级设置  */
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2 ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2 ;
	NVIC_Init(&NVIC_InitStructure); //使能NVIC中断设置

配置外部中断PB14、上拉电阻、下降沿触发。中断优先级 抢占2 响应2

复制代码
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//GPIOB时钟使能

寄存器 APB2ENR bit 3

复制代码
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//AFIO时钟使能

寄存器 APB2ENR bit 0

复制代码
GPIO_InitTypeDef  GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;//上拉
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 ;  //14位
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);    //使能GPIOB_14设置

见前文的说明不赘述,其中的上下拉模式要特别说明一下

由手册发现上拉/下来输入模式由同一组控制位控制,怎么区分设置呢?

输入模式下的上拉电阻和下拉电阻 寄存器配置 由GPIOx_ODR寄存器控制,为0则为下拉,为1则为上拉。ODR寄存器原先是数据输出寄存器。由上述的两图可以看到当配置为GPIO_Mode_IPU;时ODR14为1,下拉时为0.

复制代码
/*AFIO选择引脚*/
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//

AFIO(Alternate Function Input/Output)复用端口选择寄存器配置

该端口,本例采用的是中断配置因此不设置默认。

端口复用功能配置,本案都是使用主功能因此也不配置。
外部中断配置寄存器 1~4(AFIO_EXTICR1)
外部中断配置寄存器共4个,pb14使用的是第4个
AFIO_EXTICR4

因为是配置PB14因此控制的是8-11 EXTI14[3:0]位共4位控制位,因为是GPIOB 因此这4位是
0001 =0x01

复制代码
EXTI_InitTypeDef EXIT_InitStructure;
	EXIT_InitStructure.EXTI_Line = EXTI_Line14 ;
	EXIT_InitStructure.EXTI_LineCmd = ENABLE ;
	EXIT_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //选择外部中断
	EXIT_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling ;//下降沿
    EXTI_Init(&EXIT_InitStructure);//使能外部中断设置

配置外部中断寄存器:
● 配置 20个中断线的屏蔽位(EXTI_IMR) IMR( Interrupt Mask Register )

复制代码
EXIT_InitStructure.EXTI_Line = EXTI_Line14 ;
	EXIT_InitStructure.EXTI_LineCmd = ENABLE ;
EXIT_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //选择外部中断

默认是全屏蔽的,程序是使能bit14

默认是全屏蔽的即复位值是0x0000 0000. EMR(Event Mask Interrupt)

● 配置所选中断线的触发选择位 (EXTI_RTSR 和 EXTI_FTSR) ;

RTSR(Rising Trigger Selection Register),我们选择的是下降沿因此无需设置即默认。

FTSR(Falling edge Trigger Selection Register) 我们配置是bit14位因此需要设置为1

复制代码
    //EXIT_InitStructure.EXTI_Line = EXTI_Line14 ;
	//EXIT_InitStructure.EXTI_LineCmd = ENABLE ;

EXIT_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling ;//下降沿

软件中断是通过代码或者寄存器操作触发中断,之前代码中是采用GPOI触发是硬件触发中断因此无需设置即默认即可。

PR(Pull Reques),挂起寄存器是操作事件触发的,因此这里也无需设置。至此外部中断EXTI寄存器完毕。

复制代码
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd =ENABLE ;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2 ;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2 ;
	NVIC_Init(&NVIC_InitStructure); //使能NVIC中断设置

至于NVIC是内核中的寄存器KEIL5中有相应的查看界面,但是里面的参数好像有点问题,以本案为
例:
未使能NVIC之前的数据,可以看到优先级分组里的选项就不符合STM32F103C8t6里的分法,使能后的数据
SCB->AIRCR根据手册因是
他的掩码是0x05FA0000,分组是2组即0x500,所以结果是0x05FA0500和这个界面里显示的也不一样,因此这里面的
然后再看一张分组1的NVIC界面是1抢占3响应

记过几次设置分组的比较可以得出以下几个结果: 这里的数据代表分组是第几组(至于组内里面的响应设置变动,不会改变这个值)
这个里面是数据 1是抢占优先级,48 = 16*3 响应优先级是3,即1抢占3响应。176不清楚,这是观察结果可能只适合STM32F103C8T6
硬件事件选择
通过下面的过程,可以配置 20 个线路为事件源
● 配置 20 个事件线的屏蔽位 (EXTI_EMR)
● 配置事件线的触发选择位 (EXTI_RTSR 和 EXTI_FTSR)
软件中断 / 事件的选择
20 个线路可以被配置成软件中断 / 事件线。下面是产生软件中断的过程:
● 配置 20 个中断 / 事件线屏蔽位 (EXTI_IMR, EXTI_EMR)
● 设置软件中断寄存器的请求位 (EXTI_SWIER)
以下来自DEEPSEEK

一、定义与核心功能

  1. 事件寄存器

    • 功能‌:用于配置和控制硬件事件的触发条件(如边沿检测)及是否允许事件信号传递到硬件模块(如DMA、ADC等)‌。
    • 典型寄存器 ‌:
      • EMR(事件屏蔽寄存器):控制事件触发是否有效‌。
      • RTSR/FTSR(上升沿/下降沿触发选择寄存器):配置事件的触发条件‌。
  2. 中断寄存器

    • 功能‌:管理中断请求的触发、优先级配置及中断屏蔽状态,直接关联CPU的中断响应流程‌12。
    • 典型寄存器 ‌:
      • IMR(中断屏蔽寄存器):控制中断请求是否被CPU响应‌。
      • EXTICR(外部中断配置寄存器):设置中断线对应的GPIO引脚及触发方式‌。

二、触发机制差异

特性 事件寄存器 中断寄存器
触发目标 直接驱动硬件模块(如DMA、ADC) 触发CPU中断服务程序(ISR)‌13
响应方式 硬件自动处理,无需CPU介入 需CPU保存现场并执行ISR‌12
典型应用 低延迟数据传输、周期性触发操作 异步任务处理、紧急事件响应‌23

三、配置流程对比

  1. 事件寄存器配置示例

    • 通过RTSR设置上升沿触发事件。
    • 通过EMR使能事件触发,信号直接传递至外设模块‌。
  2. 中断寄存器配置示例

    • 通过EXTICR绑定GPIO引脚到中断线。
    • 通过IMR使能中断请求,并设置NVIC优先级‌。

四、关键设计差异

  • 事件寄存器 ‌:
    • 通过硬件链路实现快速响应,适用于实时性要求高且无需复杂逻辑的场景‌。
    • 典型应用:DMA触发、定时器同步‌23。
  • 中断寄存器 ‌:
    • 依赖CPU介入,适合需要执行复杂逻辑或修改程序流程的场景‌。
    • 典型应用:按键检测、通信协议处理‌。

五、总结

  • 事件寄存器 ‌通过硬件链路实现快速响应,‌中断寄存器‌依赖CPU处理异步任务‌。
  • 两者常结合使用,例如:事件触发DMA传输,传输完成后通过中断通知CPU‌23。

● 配置对应到外部中断控制器 (EXTI) 的 NVIC 中断通道的使能和屏蔽位,使得 20 个中断线中的
请求可以被正确地响应。

相关推荐
LS_learner17 分钟前
树莓派(ARM64 架构)Ubuntu 24.04 (Noble) 系统 `apt update` 报错解决方案
嵌入式硬件
来自晴朗的明天1 小时前
16、电压跟随器(缓冲器)电路
单片机·嵌入式硬件·硬件工程
钰珠AIOT1 小时前
在同一块电路板上同时存在 0805 0603 不同的封装有什么利弊?
嵌入式硬件
代码游侠1 小时前
复习——Linux设备驱动开发笔记
linux·arm开发·驱动开发·笔记·嵌入式硬件·架构
代码游侠12 小时前
学习笔记——设备树基础
linux·运维·开发语言·单片机·算法
xuxg200514 小时前
4G 模组 AT 命令解析框架课程正式发布
stm32·嵌入式·at命令解析框架
CODECOLLECT16 小时前
京元 I62D Windows PDA 技术拆解:Windows 10 IoT 兼容 + 硬解码模块,如何降低工业软件迁移成本?
stm32·单片机·嵌入式硬件
BackCatK Chen16 小时前
STM32+FreeRTOS:嵌入式开发的黄金搭档,未来十年就靠它了!
stm32·单片机·嵌入式硬件·freertos·低功耗·rtdbs·工业控制
全栈游侠19 小时前
STM32F103XX 02-电源与备份寄存器
stm32·单片机·嵌入式硬件