STM32——GPIO

目录

一、基础知识

           (一)基本特性

                        数字和模拟信号

                        IO端口基本结构

           (二)引脚配置

       多路复用——基于stm32f103

       引脚电气特性

二、工作原理

           (一)逻辑状态

           (二)输入模式与输出模式的区别

           (三)基于中断的GPIO输入

三、控制方法

           (一) 硬件端口的配置

                        寄存器配置(8种工作模式)

      端口初始化

           (二)使用编程语言控制GPIO

       C语言基础操作

       使用库函数(HAL库)

            (三)示例代码讲解

        点亮LED

四、应用实例(PWM)——基于stm32f407

           (一) LED闪烁

           (二)LED流水灯

           (三)控制电机和蜂鸣器

五、调试与故障排除


一、基础知识

(一)基本特性

  • 数字和模拟信号
    • 数字信号

           基本概念:两种逻辑状态:逻辑高(1,接近VDD)和逻辑低(0,接近GND)。

           处理机制:GPIO操作、通信接口、中断驱动

    • 模拟信号

           处理机制:ADC、DAC、Op-Amps

  • IO端口基本结构
    • 端口
    • 引脚:包括GPIO(通用输入/输出)和其他引脚。
    • 寄存器

(二)引脚配置

  • 多路复用——基于stm32f103
    • // 以配置USART1为例  
      __HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟  
      __HAL_RCC_USART1_CLK_ENABLE(); // 使能USART1时钟
      //使用HAL库的GPIO初始化函数,设置GPIO引脚的模式、输出类型以及复用功能。
      
      GPIO_InitTypeDef GPIO_InitStruct = {0};  
      
      // 配置PA9为USART1_TX  
      GPIO_InitStruct.Pin = GPIO_PIN_9;  
      GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; // 设置为复用推挽输出  
      GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉或下拉  
      GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 设置为高速  
      HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);  
      
      // 配置PA10为USART1_RX,如果需要  
      GPIO_InitStruct.Pin = GPIO_PIN_10;  
      // 其余配置与USART1_TX相同  
      HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

      对STM32F1系列来说,复用功能由特定的引脚与外设映射定义,因此选择特定复用功能的步骤主要通过正确选择引脚和外设关联来实现,并不需要像STM32F4系列那样设置AFR寄存器。

  • 引脚电气特性
    • 引脚输出类型

      • 推挽(Push-Pull):可主动输出高/低电平,适合大多数输出场景。
      • 开漏(Open-Drain):只能输出低电平,高电平依赖外部上拉,适用于总线通信。
    • 驱动能力与速度

      • 低功耗:低电流驱动,适合功耗敏感应用。
      • 中速:平衡电流和速度,适用于通用目的。
      • 高速:高电流驱动,适用于高速信号或大负载。
    • 上拉/下拉配置

      • 上拉(Pull-up):高电平稳定,避免悬空。
      • 下拉(Pull-down):低电平稳定,避免悬空。
    • 模拟模式

      • ADC/DAC转换,减少数字噪音干扰。

二、工作原理

(一)逻辑状态

  • 低电平(逻辑0)

  • 高电平(逻辑1)

  • 中断触发(ISR)

(二)输入模式与输出模式的区别

  • 方向性

  • 电阻状态

  • 应用目的

(三)基于中断的GPIO输入

  1. 使能GPIO端口时钟:确保GPIO端口的时钟使能,以供后续配置。

  2. 配置GPIO引脚为输入模式:通过GPIO初始化结构体设置引脚为输入模式,并定义上拉、下拉等电气特性。

  3. 配置中断触发条件:根据需要选择适当的触发条件,如上升沿、下降沿或双边沿触发。

  4. 使能中断和设置优先级:在NVIC(Nested Vectored Interrupt Controller)中使能GPIO中断,并设置其优先级。

  5. 实现中断服务程序(ISR):编写ISR以处理当中断发生时需要执行的任务,例如读取传感器数据、更改系统状态等。

        

三、控制方法

(一) 硬件端口的配置

  • 寄存器配置(8种工作模式)
  • 端口初始化
    • 启用外设时钟
      • __HAL_RCC_GPIOA_CLK_ENABLE(); // 启用GPIOA时钟
        __HAL_RCC_TIM1_CLK_ENABLE();   // 启用定时器1时钟
        
    • 配置GPIO引脚
      • GPIO_InitTypeDef gpioInitStruct = {0};
        gpioInitStruct.Pin = GPIO_PIN_0; // 选择引脚PA0
        gpioInitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
        gpioInitStruct.Pull = GPIO_NOPULL; // 无上下拉
        gpioInitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
        
        HAL_GPIO_Init(GPIOA, &gpioInitStruct); // 初始化GPIO
        
    • 配置定时器和PWM
      • TIM_HandleTypeDef htim1;
        
        void MX_TIM1_Init(void) {
            TIM_OC_InitTypeDef ocInitStruct = {0};
            
            htim1.Instance = TIM1; // 选择定时器1
            htim1.Init.Prescaler = 84 - 1; // 设置预分频器
            htim1.Init.Period = 100 - 1; // 设置PWM周期
            htim1.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
            htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
        
            HAL_TIM_PWM_Init(&htim1); // 初始化定时器
        
            // 配置PWM参数
            ocInitStruct.OCMode = TIM_OCMODE_PWM1;
            ocInitStruct.Pulse = 0; // 初始占空比为0
            ocInitStruct.OCPolarity = TIM_OCPOLARITY_HIGH;
            ocInitStruct.OCFastMode = TIM_OCFAST_DISABLE;
        
            HAL_TIM_PWM_ConfigChannel(&htim1, &ocInitStruct, TIM_CHANNEL_1); // 配置PWM通道
        }
        

(二)使用编程语言控制GPIO

  • C语言基础操作
  • 使用库函数(HAL库)
    1. 启用GPIO时钟

      __HAL_RCC_GPIOC_CLK_ENABLE(); // 启用GPIOC时钟
      
    2. 配置GPIOC引脚

      GPIO_InitTypeDef gpioInitStruct = {0};
      gpioInitStruct.Pin = GPIO_PIN_0; // 选择引脚PC0
      gpioInitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
      gpioInitStruct.Pull = GPIO_NOPULL; // 无上下拉
      gpioInitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
      
      HAL_GPIO_Init(GPIOC, &gpioInitStruct); // 初始化PC0
      
    3. 设置引脚状态

      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET); // 将PC0设置为高
      HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET); // 将PC0设置为低
      
    4. 读取引脚状态

      GPIO_PinState pinState = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_0); // 读取PC0状态
      

(三)示例代码讲解

  • 点亮LED
    • //Arduino
      
      #define LED_PIN 13 // LED引脚
      
      void setup() {
          pinMode(LED_PIN, OUTPUT); // 设置引脚为输出
      }
      
      void loop() {
          digitalWrite(LED_PIN, HIGH); // 点亮LED
          delay(1000);                  // 延时1秒
          digitalWrite(LED_PIN, LOW);  // 熄灭LED
          delay(1000);                  // 延时1秒
      }
      

四、应用实例(PWM)——基于stm32f407

(一)LED闪烁

  • #include "main.h"
    
    // 定义LED引脚
    const uint16_t ledPin = GPIO_PIN_0; // 选择PA0作为LED引脚
    
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    
    int main(void) {
        HAL_Init();
        SystemClock_Config();
        MX_GPIO_Init();
    
        while (1) {
            HAL_GPIO_WritePin(GPIOA, ledPin, GPIO_PIN_SET);   // 点亮LED
            HAL_Delay(500);                                    // 延时500毫秒
            HAL_GPIO_WritePin(GPIOA, ledPin, GPIO_PIN_RESET); // 熄灭LED
            HAL_Delay(500);                                    // 延时500毫秒
        }
    }
    
    static void MX_GPIO_Init(void) {
        __HAL_RCC_GPIOA_CLK_ENABLE();
    
        GPIO_InitTypeDef gpioInitStruct = {0};
        gpioInitStruct.Pin = ledPin;                          // 设置引脚
        gpioInitStruct.Mode = GPIO_MODE_OUTPUT_PP;            // 推挽输出模式
        gpioInitStruct.Pull = GPIO_NOPULL;                    // 不使用上拉或下拉
        gpioInitStruct.Speed = GPIO_SPEED_FREQ_LOW;           // 低速
    
        HAL_GPIO_Init(GPIOA, &gpioInitStruct);               // 初始化GPIO引脚
    }
    

(二)LED流水灯

  • #include "main.h"
    
    // 定义LED引脚数组
    const uint16_t ledPins[] = { GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3 };
    const uint8_t ledCount = sizeof(ledPins) / sizeof(ledPins[0]);
    
    void SystemClock_Config(void);
    static void MX_GPIO_Init(void);
    
    int main(void) {
        HAL_Init();
        SystemClock_Config();
        MX_GPIO_Init();
    
        while (1) {
            for (uint8_t i = 0; i < ledCount; i++) {
                HAL_GPIO_WritePin(GPIOA, ledPins[i], GPIO_PIN_SET);   // 点亮LED
                HAL_Delay(250);                                       // 延时
                HAL_GPIO_WritePin(GPIOA, ledPins[i], GPIO_PIN_RESET); // 熄灭LED
            }
        }
    }
    
    static void MX_GPIO_Init(void) {
        __HAL_RCC_GPIOA_CLK_ENABLE();
    
        GPIO_InitTypeDef gpioInitStruct = {0};
        gpioInitStruct.Mode = GPIO_MODE_OUTPUT_PP;
        gpioInitStruct.Pull = GPIO_NOPULL;
        gpioInitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    
        // 为所有LED引脚初始化
        for (uint8_t i = 0; i < ledCount; i++) {
            gpioInitStruct.Pin = ledPins[i];
            HAL_GPIO_Init(GPIOA, &gpioInitStruct);
        }
    }
    

(三)控制电机和蜂鸣器

  • //PWM初始化与配置
    
    static void MX_TIM_Init(void) {
        __HAL_RCC_TIM1_CLK_ENABLE(); // 启用定时器1时钟
    
        TIM_OC_InitTypeDef ocInitStruct = {0};
        htim1.Instance = TIM1; // 选择定时器1
        htim1.Init.Prescaler = 84 - 1; // 设置预分频器(假设时钟频率为84MHz,分频到1MHz)
        htim1.Init.Period = 100 - 1; // 设置PWM周期(100us)
        htim1.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
        htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
        
        HAL_TIM_PWM_Init(&htim1); // 初始化定时器
        
        // 设置PWM参数
        ocInitStruct.OCMode = TIM_OCMODE_PWM1;
        ocInitStruct.Pulse = 0; // 初始占空比为0
        ocInitStruct.OCPolarity = TIM_OCPOLARITY_HIGH;
        ocInitStruct.OCFastMode = TIM_OCFAST_DISABLE;
    
        HAL_TIM_PWM_ConfigChannel(&htim1, &ocInitStruct, TIM_CHANNEL_1); // 配置PWM通道
    }
    
    //控制电机和蜂鸣器
    
    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); // 启动PWM
    
    // 控制电机转动
    for (uint32_t dutyCycle = 0; dutyCycle <= 100; dutyCycle += 10) {
        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, dutyCycle); // 设置PWM占空比
        HAL_Delay(500); // 延时500毫秒
    }
    
    // 控制蜂鸣器发声
    for (uint32_t freq = 500; freq <= 2000; freq += 500) {
        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 50); // 设置占空比
        HAL_TIM_Base_Start(&htim1); // 启动定时器
        HAL_Delay(500); // 延时500毫秒
        __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, 0); // 关闭蜂鸣器
        HAL_Delay(500); // 延时500毫秒
    }
    

五、调试与故障排除

  • USB Type-C接口自身并不直接支持传统的硬件级串口通信
### STM32按键中断的实现方法及常见问题解决方案 #### 一、STM32按键中断的基础概念 在STM32微控制器中,按键中断是一种通过外部设备触发CPU响应的方式。当中断发生时,处理器会暂停当前的任务并跳转到相应的中断服务程序(Interrupt Service Routine, ISR)[^1]。 为了确保按键操作能够被准确识别而不受机械抖动的影响,通常需要加入消抖机制。可以通过硬件或软件的方式来完成这一目标[^3]。 #### 二、按键中断的具体实现流程 以下是基于STM32的按键中断实现的一个典型例子: ##### 1. 初始化GPIO端口 首先需配置用于检测按键状态的GPIO引脚为输入模式,并启用上拉/下拉电阻以稳定电平信号。 ```c void GPIO_Init(void){ __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发中断 GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` ##### 2. 配置NVIC (Nested Vectored Interrupt Controller) 使能对应外设的中断线并向量表映射地址设置好之后才能接收到来自该源发出的通知请求。 ```c void NVIC_Configuration(void){ HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); } ``` ##### 3. 编写中断服务程序(ISR) 当指定条件满足时即会产生一个异常事件进而激活下面定义好的函数体部分来进行相应处理逻辑编码工作。 ```c void EXTI0_IRQHandler(void){ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){ if(GPIO_Pin == GPIO_PIN_0){ Key_Short_Pro(); // 调用短按处理函数 } } ``` 以上代码片段展示了如何初始化GPIO以及配置相关的中断向量控制单元(NVIC),同时还给出了简单的ISR框架结构供参考使用[^1]。 #### 三、解决按键中断中的常见问题 尽管上述步骤可以成功建立基本的功能模块,但在实际应用过程中可能会遇到一些挑战或者错误情况的发生。这里列举几个常见的案例及其可能的原因分析与对策建议: - **问题**: 当按下某个按钮后系统进入死循环无法正常退出. - 可能原因: 如果存在未清除标志位的情况,则每次都会重复触发相同的中断直到堆栈溢出为止。 - 解决方案: 确保每一次进入ISRs之前都将关联的状态变量重置回初始值;另外也可以考虑适当增加延时语句以便给予足够的反应时间给外围器件恢复平静再继续下一步骤的操作[^2]. - **问题**: 使用定时器配合按键扫描发现长时间按住某键会出现多次计数的现象。 - 可能原因: 定时期间如果持续监测到低电平时就会不断累加计数值造成误判。 - 解决方案: 引入额外的状态标记区分首次下降沿捕捉与其他后续动作的区别对待即可有效避免此类状况出现[^4]. - **问题**: 在调用了`HAL_Delay()`期间似乎忽略了来自外部的紧急事务通知而导致延迟响应甚至丢失某些重要数据包传输机会等问题的存在。 - 可能原因: 默认情况下SysTick timer 的抢占级别较低不足以打断正在进行中的阻塞型APIs如 `HAL_Delay()`. - 解决方案: 提高SysTick interrupt priority level使之高于其他常规任务从而允许即时介入调整调度顺序达成实时性的需求标准[^5]. #### 结论 通过对STM32按键中断的学习我们可以了解到其背后涉及到的知识点广泛而且深入细致之处颇多值得我们去探索研究。只有掌握了这些核心原理才能够灵活运用它们构建更加复杂可靠的电子控制系统出来服务于各行各业的实际应用场景之中。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值