zoukankan      html  css  js  c++  java
  • STM32学习笔记——GPIO

    单片机型号STM32F407VET6。

    概述

    GPIO的分类:

    • 可接受5V输入的(FT),绝大多数引脚都是;

    • 只能接受3.3V输入的(TTa),只有PA4PA5,就是DAC输出的两个引脚;

    • 其他,包括BOOT0NRST这两个特殊功能的引脚。

    GPIO不仅可以用作GPIO,每个GPIO都有复用功能(alternate function,AF)和附加功能(additional function),AF用GPIOx_AFR来配置,附加功能用外设中的寄存器。

    一组GPIO为16个,从Px0Px15xAI,有些封装上有些引脚不存在。

    GPIO的功能主要有4类:

    • 输出,推挽(push-pull,PP)或开漏(open-drain,OD),可选上拉(pull-up,PU)或下拉(pull-down,PD),4档速度;

    • AF,细节同上;

    • 输入,可选上拉或下拉;

    • 模拟,用于ADC和DAC。

    HAL

    HAL把外部中断也归到了GPIO中,这里暂且不涉及外部中断。

    初始化这种事情我都交给STM32CubeMX来完成(STM32CubeIDE内置)。我已经初步领略到HAL的设计思想,以后专门开一篇写。

    GPIO有以下函数:

    • HAL_GPIO_Init():初始化一组GPIO中的一个或多个;

    • HAL_GPIO_DeInit():把一组GPIO中的一个或多个还原为复位状态;

    • HAL_GPIO_ReadPin():读引脚电平,返回GPIO_PinState枚举类型,可能值为GPIO_PIN_RESET = 0GPIO_PIN_SET = 1

    • HAL_GPIO_WritePin():写引脚电平,是原子操作,允许中断发生;

    • HAL_GPIO_TogglePin():翻转引脚电平;

    • HAL_GPIO_LockPin():锁定引脚配置,在复位前不可修改,引脚电平还可以写。

    #include "main.h"
    #include <stdbool.h>
    int main(void)
    {
      bool prev = true;
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      while (1)
      {
        if (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin) == GPIO_PIN_RESET)
          HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
        else
          HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);
        bool now = HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET;
        if (!prev && now)
          HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
        prev = now;
        HAL_Delay(1);
      }
    }
    

    (STM32CubeMX生成的代码是两格缩进的,这让我非常不爽!)

    LED0LED1分别连接到PA6PA7,低电平亮;KEY0KEY1分别连接到PE4PE3,上拉。程序的功能为:KEY0按下时LED0亮,松开熄灭;KEY1按下时切换LED1的亮暗状态。

    寄存器

    每一组GPIO都有10个寄存器:

    • GPIOx_MODER,32位,2位MODERy[1:0]一组(y015,下同),设置GPIO模式;

    • GPIOx_OTYPER,16位,1位OTy一组,设置GPIO输出类型;

    • GPIOx_OSPEEDR,32位OSPEEDRy[1:0],设置GPIO输出速度;

    • GPIOx_PUPDR,32位PUPDRy[1:0],设置上拉下拉;

    • GPIOx_IDR,16位IDRy,读取输入电平;

    • GPIOx_ODR,16位ODRy,设置输出电平;

    • GPIOx_BSRR,低16位BSy,写1GPIOx_ODR中对应位置1;高16位BRy,写1GPIOx_ODR中对应位清0;同时写1BSy优先;

    • GPIOx_LCKR:低16为LCKy,第16位LCKK,需要一个特定的写入过程(参考datasheet或HAL_GPIO_LockPin实现),可以锁定GPIOx_MODERGPIOx_OTYPERGPIOx_OSPEEDRGPIOx_PUPDRGPIOx_AFRLGPIOx_AFRH这6个控制寄存器中的对应位;

    • GPIOx_AFRLGPIOx_AFRH,4位AFRHy[3:0]为一组,设置复用输出。

    GPIO的输出级有一个NMOS和一个PMOS:

    • 在推挽输出模式下,ODRx0,NMOS导通;ODRx1,PMOS导通;

    • 在开漏输出模式下,ODRx0,NMOS导通;ODRx1,高阻态;PMOS都不会导通。

    开漏输出的应用有矩阵键盘和I²C等,需要上拉电阻,通常用内置的即可。

    用寄存器重写上面的程序:

    #include "main.h"
    #include <stdbool.h>
    
    #define LED0_Bit 6
    #define LED1_Bit 7
    #define KEY0_Bit 4
    #define KEY1_Bit 3
    
    int main(void)
    {
      bool prev = true;
      HAL_Init();
      SystemClock_Config();
      __HAL_RCC_GPIOA_CLK_ENABLE();
      LED0_GPIO_Port->ODR |= 1 << LED0_Bit;
      LED0_GPIO_Port->MODER |= 0b01 << (LED0_Bit * 2);
      LED1_GPIO_Port->ODR |= 1 << LED1_Bit;
      LED1_GPIO_Port->MODER |= 0b01 << (LED1_Bit * 2);
      __HAL_RCC_GPIOE_CLK_ENABLE();
      KEY0_GPIO_Port->PUPDR |= 0b01 << (KEY0_Bit * 2);
      KEY1_GPIO_Port->PUPDR |= 0b01 << (KEY1_Bit * 2);
      while (1)
      {
        if (!(KEY0_GPIO_Port->IDR & 1 << KEY0_Bit))
          LED0_GPIO_Port->BSRR = 1 << (16 + LED0_Bit);
        else
          LED0_GPIO_Port->BSRR = 1 << LED0_Bit;
        bool now = !(KEY1_GPIO_Port->IDR & 1 << KEY1_Bit);
        if (!prev && now)
          LED1_GPIO_Port->ODR ^= 1 << LED1_Bit;
        prev = now;
        HAL_Delay(1);
      }
    }
    

    只把GPIO相关的改成了寄存器操作,时钟之类的还是用的HAL。

  • 相关阅读:
    直接插入排序
    直接选择排序
    冒泡排序
    归并排序
    进程调度
    进程与线程
    c语言struct和c++struct的区别
    二叉搜索树、AVL平衡二叉搜索树、红黑树、多路查找树

    6-11 先序输出叶结点
  • 原文地址:https://www.cnblogs.com/jerry-fuyi/p/12859848.html
Copyright © 2011-2022 走看看