GPIO

作者: 简小黑 | 来源:发表于2019-05-30 20:19 被阅读0次

    基础知识

    STM32F103RCT6有(16*3+3)个IO口,每个IO口有以下工作模式:
    (1) GPIO_Mode_AIN 模拟输入(开关1、2均打开,开关3打开)
    (2) GPIO_Mode_IN_FLOATING 浮空输入(开关1、2均打开,开关3关闭)(悬空时不确定)
    (3) GPIO_Mode_IPD 下拉输入(开关1打开,开关2闭合,开关3闭合)(悬空时保持高电平)
    (4) GPIO_Mode_IPU 上拉输入(开关1闭合,开关2打开,开关3闭合)(悬空时保持低电平)
    (5) GPIO_Mode_Out_OD 开漏输出(P-MOS一直处于关闭状态,当输出高电平,N-MOS管关闭,输出由外部决定,当输出低电平时,N-MOS管闭合,输出为低电平。)
    (6) GPIO_Mode_Out_PP 推挽输出(当输出为高电平时,P-MOS处于开启状态,N-MOS处于关闭状态,输出高电平。当输出为低电平时,P-MOS处于关闭状态,N-MOS处于开启状态,输出低电平。)
    (7) GPIO_Mode_AF_OD 复用开漏输出(与开漏输出模式类似。输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。)
    (8) GPIO_Mode_AF_PP 复用推挽输出(与推挽输出类似,输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的。)


    GPIO端口结构.PNG

    开漏输出:只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极。适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内);
    推挽输出:可以输出强高、低电平,连接数字器件。

    寄存器

    STM32 的每个 IO 端口都有 7 个寄存器来控制。他们分别是:配置模式的 2 个 32 位的端口配置寄存器 CRL 和 CRH;2 个 32 位的数据寄存器 IDR 和 ODR;1 个 32 位的置位/复位寄存器BSRR;一个 16 位的复位寄存器 BRR;1 个 32 位的锁存寄存器 LCKR。
    端口配置寄存器每位rw。


    输出模式位.PNG
    端口配置表.PNG
    CRH.PNG
    CRL.PNG

    数据寄存器只有低16位有效。


    ODR.PNG
    IDR.PNG
    置位/复位寄存器BSRR 写入0无效 BR写入1清除对应ODR位为0,BS写入1设置对应的ODR位为1
    BSRR.PNG
    复位寄存器BRR写入0无效写入1清除对应ODR位为0(作用与BSRR的BR相同)
    BRR.PNG
    锁存寄存器 LCKR,用于锁定配置寄存器 CRL和CRH的值。一旦被锁住,只有再次“复位GPIO口”才能解锁。锁键的写入序列:写1 -> 写0 -> 写1 -> 读0 -> 读1。在操作锁键的写入序列时,不能改变LCK[15:0]的值。
    LCKR.PNG

    为了优化 64 脚或 100 脚封装的外设数目,可以把一些复用功能重新映射到其他引脚上。设置 复用重映射和调试I/O配置寄存器(AFIO_MAPR)实现引脚的重新映射。这时,复用功能不再映射到它们的原始分配上。即AFIO寄存器。(先不学习)

    开发板代码分析

    通过代码控制 ALIENTEK MiniSTM32 开发板上的两个 LED:DS0 和 DS1 交替闪烁,实现类似跑马灯的效果。DS0 接 PA8,DS1 接 PD2。

    寄存器

    配置GPIO前要先配置所在的时钟。


    APB2ENR.PNG
    RCC->APB2ENR|=1<<2; //使能 PORTA 时钟
    RCC->APB2ENR|=1<<5; //使能 PORTD 时钟
    

    PA8 PD2输出高低电平,推挽输出,CNFMODE配置:0011(50MHz)(11二进制中为3)

    GPIOA->CRH&=0XFFFFFFF0;
    GPIOA->CRH|=0X00000003;//PA8 推挽输出
    GPIOA->ODR|=1<<8; //PA8 输出高
    GPIOD->CRL&=0XFFFFF0FF;
    GPIOD->CRL|=0X00000300;//PD.2 推挽输出
    GPIOD->ODR|=1<<2; //PD.2 输出高
    

    设置输出高低电平,开发板中附带了sys.h内含有位操作定义,可直接使用

    LED0=0; LED1=1;(#define LED0 PAout(8) // DS0 #define LED1 PDout(2) // DS1)
    

    若无位操作,可以通过ODR寄存器来控制输出高低电平。

    #define LED0 (1<<8) //led0 PA8
    #define LED1 (1<<2) //led1 PD2
    #define LED0_SET(x) GPIOA->ODR=(GPIOA->ODR&~LED0)|(x ? LED0:0)
    #define LED1_SET(x) GPIOD->ODR=(GPIOD->ODR&~LED1)|(x ? LED1:0)
    

    库函数

    库函数的使用需要添加相应的库函数文件。
    时钟使能

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|
    RCC_APB2Periph_GPIOD, ENABLE); //使能 GPIOA,GPIOD 端口时钟
    

    PA8 PD2输出高低电平,推挽输出。初始化配置函数,输入配置参数。

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //LED0-->PA8 端口配置
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO 口速度为 50MHz
    GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化 PA8
    GPIO_SetBits(GPIOA,GPIO_Pin_8); //PA8 输出高
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //LED1-->PD2 推挽输出
    GPIO_Init(GPIOE, &GPIO_InitStructure); //初始化 PD2
    GPIO_SetBits(GPIOD,GPIO_Pin_2); //PD2 输出高
    

    设置输出高低电平

    GPIO_SetBits(GPIOA, GPIO_Pin_8); //GPIOA->BRR=GPIO_Pin_8;
    GPIO_ResetBits (GPIOA, GPIO_Pin_8); //GPIOA->BSRR=GPIO_Pin_8;
    

    实践

    MDK工程框架搭建

    1. 新建工程时,选择CORE DSP GPIO Startup文件(与开发板实例中的不同)
      CORE为Cortex-M3内核支持库,DSP为信号处理算法库、GPIO为通用目的输入、输出口驱动库、Startup为芯片启动代码库。
    2. 分组管理添加工程,以及路径配置
    3. 配置输出和仿真设置
      main 函数为入口 运行时main函数里用到的函数需要声明,这些声明在头文件里。

    寄存器工程实例

    includes.h 包含了所有需要的头文件。

    #include "includes.h"
    void LEDInit(void)
    {
        RCC->APB2ENR |=(1<<2)|(1<<5);
        GPIOA->CRH&=0XFFFFFFF0;
        GPIOA->CRH|=0X00000003;//PA8 推挽输出
        GPIOA->ODR|=1<<8; //PA8 输出高
        GPIOD->CRL&=0XFFFFF0FF;
        GPIOD->CRL|=0X00000300;//PD.2 推挽输出
        GPIOD->ODR|=1<<2; //PD.2 输出高
     } 
     void LED(Int08U w, LEDState s)
     {
        switch(w)
        {
            case 0:
                if(s==LED_ON)
                GPIOA->BRR=(1<<8);
                else
                GPIOA->BSRR=(1<<8);
                break;
            case 1:
                if(s==LED_ON)
                GPIOD->BRR=(1<<2);
                else
                GPIOD->BSRR=(1<<2);
                break;
         }
     }
    
    #include "includes.h"
    void Delay(Int32U);
    int main(void)
    {
        LEDInit();
        for(;;)
        {
            LED(0,LED_ON);
            LED(1,LED_OFF);
            Delay(500);
            LED(0,LED_OFF);
            LED(1,LED_ON);
            Delay(500);
        }
     } 
     void Delay(Int32U u)
     {
        Int32U i,j;
        for(i=0;i<u;i++)
         for(j=0;j<12000;j++);
     }
    

    变量类型重命名

    #ifndef _VARTYPES_H
    #define _VARTYPES_H
    
    typedef unsigned char  Int08U;
    typedef signed char    Int08S;
    typedef unsigned short Int16U;
    typedef signed char    Int16S;
    typedef unsigned int   Int32U;
    typedef signed int     Int32S;
    typedef float          float32;
    typedef enum{LED_ON,LED_OFF} LEDState;
    #endif
    

    库函数工程实例

    库函数和寄存器不同的是,项目管理处需要添加库函数需要的文件,本例程添加“gpio”和“rcc”即可。程序部分只需要修改“led.c”部分。
    库函数下载地址 https://download.csdn.net/download/alanzjl/8638291
    libraries 中 inc存放.h文件 src存放.c文件,可以从别的文件夹如例程中拷贝stm32f10x_conf.h文件。
    在C/C++选项卡中添加两个全局宏定义常量STM32F10X_HD和USE_STDPERIPH_DRIVER。
    需要修改的led.c文件:

    #include "includes.h"
    void LEDInit(void)
    {
        GPIO_InitTypeDef g;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE);
        g.GPIO_Pin=GPIO_Pin_8;
        g.GPIO_Mode=GPIO_Mode_Out_PP;
        g.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &g);
        
        g.GPIO_Pin=GPIO_Pin_2;
        g.GPIO_Mode=GPIO_Mode_Out_PP;
        g.GPIO_Speed=GPIO_Speed_50MHz;
        GPIO_Init(GPIOD, &g);
     } 
     void LED(Int08U w, LEDState s)
     {
        switch(w)
        {
            case 0:
                if(s==LED_ON)
                GPIO_ResetBits(GPIOA,GPIO_Pin_8);
                else
                GPIO_SetBits(GPIOA,GPIO_Pin_8);
                break;
            case 1:
                if(s==LED_ON)
                GPIO_ResetBits(GPIOD,GPIO_Pin_2);
                else
                GPIO_SetBits(GPIOD,GPIO_Pin_2);
                break;
         }
     }
    

    加入库函数时,可以使用库函数提供的API函数,也可以直接使用寄存器方法。

    STM32Cube应用

    STM32Cube使用比较简单,外设的配置可以自动生成。而且可以生成报表。只需要填写主函数的逻辑关系即可。填写时可通过hal_gpio.h查看函数名称。

    while (1)
      {
        /* USER CODE END WHILE */
          HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET);
          HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
          HAL_Delay(1000);
          HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET);
          HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET);
          HAL_Delay(1000);
    
        /* USER CODE BEGIN 3 */
      }
    

    总结

    1. 目标确定,分析要用到哪些外设;
    2. 写主函数,分析需要调用哪些子函数;
    3. 写子函数,分析用哪种方式配置外设(包括所在时钟);
    4. 添加头文件等整理项目。

    相关文章

      网友评论

          本文标题:GPIO

          本文链接:https://www.haomeiwen.com/subject/myymtctx.html