配置MM32微控制器引脚复用功能

配置MM32微控制器引脚复用功能

Introduction

使用过NXP(FSL)微控制器的开发者在配置引脚复用功能,仅在PORT模块中,对应引脚的的PCR寄存器的MUX字段中选定该引脚的复用功能即可。但笔者最近在使用MM32微控制器(MM32F3270)过程中,在配置引脚复用功能上遇到了一些麻烦。MM32微控制器在用户接口的设计上,走的是近似ST的风格,相对于NXP将GPIO(一个通用的控制GPIO的外设)和PORT(同具体芯片相关的复用功能)两个模块分开,分别用于管理仅作为数字IO信号的输入输出,和端口的电气特性,MM32的GPIO外设混合了GPIO和PORT的功能,并把PORT中的引脚异步中断的功能,又分散到了EXTI模块和SYSCFG模块(SYSCFG_EXTICRn)中。

本文整理如何在MM32微控制器上配置引脚复用功能。

现在看来,后续还需要专门整理一下如何使用MM32的引脚中断,要混合GPIO、EXTI、SYSCFG等三个模块才能把引脚中断配起来。

Algorithm

配置MM32的引脚复用功能,需要同时配置GPIO模块的电气特性相关寄存器(CR)和复用功能选择寄存器(AFR),句柄这两个部分的设定需要按照特定搭配起来,才能正常工作。

GPIOx_CR寄存器

这里的x指代A、B、… 、H,每个端口都有。CR寄存器分为高8位(CRH)和低8位(CRL),总共对应一个端口的16个引脚。CRH和CRL合起来的64个bit位,每4个bit对应一个引脚,但在手册中,将这4个bit分成了两个区域(CNFn和MODEn)进行描述。

在这里插入图片描述
本文换一种更清爽的描述方式:

MODEDescription
0b0000模拟输入模式(Analog Input)
0b0100浮空输入模式(Floating Input),可用于模拟信号,也可以用于数字信号
0b1000数字上拉/下拉输入模式(需要写入ODR寄存器向端口输出电平以选择上拉或者下拉)(Pull-Up or Pull-Down)
0b1100未使用
0b0001数字推挽输出模式(Digital Push-Pull Output)
0b0101数字开漏输出模式(Digital Open-Drain Output)
0b1001复用功能推挽输出模式(Mux Push-Pull Output)
0b1101复用功能开漏输出模式(Mux Open-Drain Output)

当选定“数字上拉/下拉输入模式”时,至于是上拉,还是下拉,根据手册上的描述,需要用户通过ODR寄存器向输出端口上写个电平控制信号。GPIO_ODR寄存器是当指定GPIO引脚为GPIO输出时,控制输出引脚的电平。看样子,这里的上下拉配置,实际上是要用GPIO模块自己的输出电路(虽然软件上未使能)拉动自己的输入信号。这让我想起了多年前51单片机端口电路的用法,51单片机的P0上所有的引脚都是开漏的,本身并没有输入输出的配置,并且需要外部上拉电路,默认是输出功能,但当需要使用输入功能时,必须先向引脚写1,然后才能读数,也是用自己的输出电路给自己的输入做了个上拉。

GPIOx_AFR寄存器

GPIOx_AFR寄存器也为高8位(AFRH)和低8位(AFRL),总共64个bit位,每4位对应一个引脚。每个引脚可以配置成16个复用功能中的一个。其中可选的复用功能,就要在datasheet文档中的引脚复用功能表格查阅了。

这里要注意:

  • 如果要使用GPIO功能,必须将对应引脚的AFRn写成0xF,哪怕GPIOx_CR寄存器中配置该引脚是数字输入输出模式也不行,下一节会专门讲这种映射关系的约束情况。
  • 并不是所有引脚的AFRn的默认值都是0xF。例如JTAG(还包含SWD未使用的引脚)、BOOT0和ISP相关功能引脚,上电后的默认值都是对应的功能引脚。因此建议哪怕是使用GPIO功能,也例行将其对应的AFRn值指定为0xF

在这里插入图片描述

GPIOx_CR & GPIOx_AFR寄存器

前文提到,对于某一个引脚,只有其CR[MODE]AFR[AFRn]按照某种特定的配置组合,才能起作用。

关于这些特定的配置组合,千万注意,不能主观从字面上想当然地认为在AFR[AFRn]中选定为复用功能,复用功能的信号就能正常工作了。必须要明确是复用输出功能模式和输入模式:

  • 对复用功能的输出信号,需指定复用推挽输出模式
  • 对复用功能的输入信号,需指定浮动输入模式。注意,这个浮空输入模式,不仅在复用功能时用到,在GPIO功能时也会用到。
  • 对复用功能的输入信号,也可指定为数字上拉/下拉输入模式。这个模式本应该仅支持GPIO功能。

实际上,这也是迷惑我多次并严重影响开发效率的问题点。这种映射关系不具备通用性,仅能通过具体特例展现出来。

TIM
TIM引脚TIM功能(AFR[AFRn]引脚电气特性(CR[MODE])
TIM_CHn输入捕获通道n浮空输入模式
TIM_CHn输出比较通道n复用推挽输出模式
TIM_CHnN互补输出比较通道n复用推挽输出模式
TIM_BKIN刹车输入浮空输入模式
TIM_ETR外部时钟输入浮空输入模式
UART
UART引脚UART功能(AFR[AFRn])引脚电气特性(CR[MODE])
UART_TX串行发送复用推挽输出模式
UART_RX串行接收浮空输入模式(或数字上拉输入模式)
UART_RTS硬件流控复用推挽输出模式
UART_CTS硬件流控浮空输入模式(或数字上拉输入模式)
SPI_MASTER
SPI引脚SPI功能(AFR[AFRn])引脚电气特性(CR[MODE])
SPI_CLK发送时钟复用推挽输出模式
SPI_MOSI串行发送复用推挽输出模式
SPI_MISO串行接收浮空输入模式(或数字上拉输入模式)
SPI_NSS片选复用推挽输出模式
SPI_SLAVE
SPI引脚SPI功能(AFR[AFRn])引脚电气特性(CR[MODE])
SPI_CLK接收时钟浮动输入模式
SPI_MOSI串行接收浮空输入模式(或数字上拉输入模式)
SPI_MISO串行发送复用推挽输出模式
SPI_NSS片选浮空输入模式(或数字上/下拉输入模式)
I2C
I2C引脚I2C功能(AFR[AFRn])引脚电气特性(CR[MODE])
I2C_SCL串行时钟开漏复用输出模式
I2C_SDA串行数据开漏复用输出模式
CAN
CAN引脚CAN功能(AFR[AFRn])引脚电气特性(CR[MODE])
CAN_TXCAN发送(对接收发器)复用推挽输出模式
CAN_RXCAN接收(对接收发器)浮空输入模式(或数字上拉输入模式)
ADC
ADC引脚ADC功能(AFR[AFRn])引脚电气特性(CR[MODE])
ADC_CHnADC输入通道n模拟输入模式
FSMC

所有FSMC信号,无论实际信号的方向,全部设定为“复用推挽输出模式”。

FSMC引脚FSMC功能(AFR[AFRn])引脚电气特性(CR[MODE])
FMC_A[0…25]数据总线信号复用推挽输出模式
FMC_DA[0…15]地址总线信号复用推挽输出模式
FMC_NCS[0…3]输出使能信号复用推挽输出模式
FMC_NBS[0…1]选高低半字(可为16位字,32位字)复用推挽输出模式
FMC_NOE输出读使能信号复用推挽输出模式
FMC_NWE输出写使能信号复用推挽输出模式
FMC_NADV当数据线也能作为地址线时,选择地址有效信号复用推挽输出模式
FMC_NWAIT地址有效信号复用推挽输出模式
QSPI

所有QSPI信号,无论实际信号的方向,全部设定为“复用推挽输出模式”。

QSPI引脚QSPI功能(AFR[AFRn])引脚电气特性(CR[MODE])
QSPI_SCK串行时钟复用推挽输出模式
QSPI_DA[0…3]双向数据总线复用推挽输出模式
QSPI_NSS设备片选线复用推挽输出模式
DAC

DAC输出也使用“模拟输入模式”?!!,没错,是的。实际上这里应该将“模拟输入模式”,理解成为“模拟信号模式”,模拟输入和模拟输出都用这个选项。

DAC引脚DAC功能(AFR[AFRn])引脚电气特性(CR[MODE])
DAC_OUTnDAC输出信号总线模拟输入模式
COMP
COMP引脚DAC功能(AFR[AFRn])引脚电气特性(CR[MODE])
COMP_INPCOMP正极输入端模拟输入模式
COMP_INMCOMP负极输入端模拟输入模式
SDIO

SDIO所有引脚的功能均需配置成复用推挽输出模式。

SDIO引脚SDIO功能(AFR[AFRn])引脚电气特性(CR[MODE])
SDIO_CMDSDIO命令数据线复用推挽输出模式
SDIO_CLKSDIO同步时钟线复用推挽输出模式
SDIO_D[0 … 3]SDIO数据总线引脚复用推挽输出模式
USB

一旦启用USB外设模块,USB相关引脚自动切换为USB功能,此时对应引脚的GPIO配置全部失效。USB功能高于GPIO的配置。例如在MM32F3270芯片上,USB功能的引脚也可以被复用成UART的功能,一旦启用USB功能,原本工作的UART引脚就会失效。

也有常见的做法,是将USB引脚单独拿出来,固定为USB功能,不做复用配置。

Practice

考虑到跟手册尽量保持一致,在SDK的驱动中,对应将复用功能的配置也纳入到gpio驱动中。

typedef enum
{
    GPIO_PinMode_In_Analog      = 0x00u,  /*!< Analog input. */
    GPIO_PinMode_In_Floating    = 0x04u,  /*!< Floating input. */
    GPIO_PinMode_In_PullDown    = 0x28u,  /*!< Pull down input. */
    GPIO_PinMode_In_PullUp      = 0x48u,  /*!< Pull up input. */
    GPIO_PinMode_Out_OpenDrain  = 0x14u,  /*!< Universal open drain output. */
    GPIO_PinMode_Out_PushPull   = 0x10u,  /*!< Universal push-pull output. */
    GPIO_PinMode_AF_OpenDrain   = 0x1Cu,  /*!< Multiplex open drain output. */
    GPIO_PinMode_AF_PushPull    = 0x18u,  /*!< Multiplex push-pull output. */
} GPIO_PinMode_Type;

在SDK的工程结构中,将配置引脚的程序统一放入pin_init.c文件中的Board_InitPins()函数中,通过GPIO_Initf()GPIO_PinAFConf()两个API配置一个引脚的复用功能。现在看来,既然这两部分的功能都放在同一个外设模块中,也可以考虑把这两个功能合并成一个API,相当于这两部分的功能,就对应PORT模块配置引脚复用功能。

void BOARD_InitPins(void)
{
    GPIO_Init_Type gpio_init;

    /* PB6 - UART1_TX. */
    gpio_init.Pins  = GPIO_PIN_6;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio_init);
    GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);

    /* PB7 - UART1_RX. */
    gpio_init.Pins  = GPIO_PIN_7;
    gpio_init.PinMode  = GPIO_PinMode_In_Floating;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpio_init);
    GPIO_PinAFConf(GPIOB, gpio_init.Pins, GPIO_AF_7);

    /* PC12 - SDIO_CLK. */
    gpio_init.Pins  = GPIO_PIN_12;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, GPIO_PIN_12, GPIO_AF_12);

    /* PD2  - SDIO_CMD. */
    gpio_init.Pins  = GPIO_PIN_2;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &gpio_init);
    GPIO_PinAFConf(GPIOD, GPIO_PIN_2, GPIO_AF_12);

    /* PC8  - SDIO_DAT0. */
    gpio_init.Pins  = GPIO_PIN_8;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, GPIO_PIN_8, GPIO_AF_12);

    /* PC9  - SDIO_DAT1. */
    gpio_init.Pins  = GPIO_PIN_9;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, GPIO_PIN_9, GPIO_AF_12);

    /* PC10 - SDIO_DAT2. */
    gpio_init.Pins  = GPIO_PIN_10;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, GPIO_PIN_10, GPIO_AF_12);

    /* PC11 - SDIO_DAT3. */
    gpio_init.Pins  = GPIO_PIN_11;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &gpio_init);
    GPIO_PinAFConf(GPIOC, GPIO_PIN_11, GPIO_AF_12);
}

Conclusion

。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值