STM32f10x学习----通用GPIO 后附具体操作及使用过程中遇到的问题

学习某一个东西,我们首先要了解这个东西的定义是什么,用来干什么的,怎么用,用的过程中有什么注意事项,这些都OK了,那么我们就算是基本掌握他了。

0. 前言

GPIO(英语:General-purpose input/output),通用型输入/输出端口的简称,其接脚可以供使用者由程控自由使用,PIN脚依现实考量可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),如当clk generator, chip select等。

​ GPIO口一是个比较重要的概念,用户可以通过GPIO口和硬件进行数据交互(如UART),控制硬件工作(如LED、蜂鸣器等),读取硬件的工作状态信号(如中断信号)等。GPIO口的使用非常广泛。

1.GPIO(通用输入\输出口)

1.1GPIO功能概述

每个GPIO端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。GPIO端口的每个位可以由软件分别配置成多种模式。下面借用别的博客的图来讲解。

首先来看一下他的结构图

在这里插入图片描述

从图上可以看出GPIO有4种输入模式,4种输出模式。

─ 输入浮空

在这里插入图片描述

此时GPIO不接上下拉电阻,CPU检测到的电平高低就是外围设备的电平情况

─输入上拉

在这里插入图片描述

接上拉电阻,外围设备在没有输入的情况下,CPU接到的仍然是高电平,上拉输入的目的是为了将不稳定的电平嵌套在一个较高的电压上。一般用在外围设备默认设置是高电平的情况

─ 输入下拉

在这里插入图片描述

接下拉电阻,外围设备在没有输入的情况下,CPU接到的仍然是低电平,下拉输入的目的是为了将不稳定的电平嵌套在一个较低的电压上。一般用在外围设备默认设置是低电平的情况

─ 模拟输入

在这里插入图片描述

用在ADC转换上,通过参考电压的设置,让IO引脚输入不同的电压这时仍然是高低电平的跳转,但可以获取一个区间内的电压具体数值

─ 开漏输出

在这里插入图片描述

不主动输出电平,当写入0时,打开N-mos管,外设接到的是低电平;当写入1时,P-mos管不会被激活。一般情况下,当使用开漏输出的时候,会接上拉电阻,这时,外围设备接到的高电平实际是上拉电阻给的

─ 推挽式输出

在这里插入图片描述

主动输出高低电平,当写入0时,打开N-mos管,外设接到的是低电平;当写入1时,打开P-MOS管,外设接到的是高电平

─ 推挽式复用功能

在这里插入图片描述

这个功能和推挽输出很相似,只是主动输出的对象从CPU变成了片上外设,其余的逻辑功能都和推挽输出是一样的

─ 开漏复用功能

在这里插入图片描述

这个功能和开漏输出很相似,只是主动输出的对象从CPU变成了片上外设,其余的逻辑功能都和开漏输出是一样的

作为复用输出的时候,这时GPIO端口不在作为通用IO端口(输出或检测高低电平)来使用,而是作为其他的模块(定时器TIM,串口USART等)来使用,具体的使用要看数据手册

注意:

对于复用的输入功能,端口必须配置成输入模式(浮空、上拉或下拉)且输入引脚必须由外部驱动

对于复用输出功能,端口必须配置成复用功能输出模式(推挽或开漏)。

对于双向复用功能,端口位必须配置复用功能输出模式(推挽或开漏)。这时,输入驱动器被配置成浮空输入模式

每个I/O端口位可以自由编程,然而I/0端口寄存器必须按16位字被访问(不允许半字或字节访问)。GPIOx_BSRR和GPIOx_BRR寄存器允许对任何GPIO寄存器的读/更改的独立访问;这样,在读和更改访问之间产生IRQ时不会发生危险。

GPIO可以用作单独位的设置或清除、用作外部中断\唤醒线、复用功能、软件重映射功能等。

2 使用示例

1 基础配置

GPIO是用户与硬件交互的桥梁,在STM32中使用其他的外设资源也要首先配置GPIO口,下面用官方提供的固件库进行GPIO口的配置。

void GPIO_init(void)
{
		GPIO_InitTypeDef GPIO_InitStructure;
        /*初始化GPIOA组时钟*/
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);
		GPIO_PinRemapConfig(GPIO_Remap_USART1,ENABLE);
  	 	/* Configure USART1  Rx (PB.7) */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
        GPIO_Init(GPIOB, &GPIO_InitStructure);
        /* Configure USART1  Tx (PB.06) */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_Init(GPIOB, &GPIO_InitStructure);

}

对IO口进行操作

GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin); 
GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
例:
    GPIO_SetBits(GPIOA, GPIO_Pin_6); //置1
    GPIO_ResetBits(GPIOA, GPIO_Pin_6);//置0

配置外部中断线

GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource)
例:
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6)

读取IO状态

uint16_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读取输入状态
uint16_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);//读取输出状态
例:
    GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6);
    GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_6);

将IO口映射为外设功能

GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState)
例:
     GPIO_PinRemapConfig(GPIO_Remap1_CAN1,ENABLE);

3 问题总结

1.外设的使用最好先初始化引脚,未初始化引脚就使用外设资源,可能会导致无法预料的问题

2.ODR可以改变引脚的高低电平,BSRR也能改变引脚的高低电平,BRR只能改变引脚为低电平。

但是用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险,ODR改变管脚状态的时候有被打断的风险,BSRR和BRR更能保证原子性操作。

好先初始化引脚,未初始化引脚就使用外设资源,可能会导致无法预料的问题

2.ODR可以改变引脚的高低电平,BSRR也能改变引脚的高低电平,BRR只能改变引脚为低电平。

但是用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险,ODR改变管脚状态的时候有被打断的风险,BSRR和BRR更能保证原子性操作。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现跑马灯程序,可以使用 STM32通用定时器来控制 LED 灯的闪烁频率。下面是一个简单的跑马灯程序的实现步骤: 1. 首先,需要初始化 GPIO 端口,将要控制的 LED 灯连接到 STM32 开发板的 GPIO 引脚。 2. 然后,需要初始化定时器 TIM2。可以选择使用 TIM2 或者其他的可用定时器。在初始化定时器时,需要设置计数模式、分频系数、计数周期等参数。 3. 接下来,需要配置定时器的断,使得每次计数完成时,定时器会触发断。 4. 在断处理函数,可以将 LED 灯的状态取反,从而实现 LED 灯的闪烁效果。 下面是一个完整的示例代码: ```c #include "stm32f10x.h" void GPIO_Configuration(void); void TIM2_Configuration(void); int main(void) { GPIO_Configuration(); TIM2_Configuration(); while(1); } void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); } void TIM2_Configuration(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = 999; TIM_TimeBaseStructure.TIM_Prescaler = 7199; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } void TIM2_IRQHandler(void) { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { GPIO_ToggleBits(GPIOB, GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); TIM_ClearITPendingBit(TIM2, TIM_IT_Update); } } ``` 在上述代码,我们使用了 TIM2 定时器,并将其分频系数设置为 7199,计数周期设置为 999。这意味着定时器将以 10ms 的频率触发断,从而实现 LED 灯的闪烁效果。 在 GPIO 配置函数,我们将 LED 灯连接到了 GPIOB 的引脚 0、1、2、3 上,并将它们初始化为输出模式,初始化为高电平。在断处理函数使用 GPIO_ToggleBits 函数将 LED 灯的状态取反。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值