历史上的今天
返回首页

历史上的今天

今天是:2024年12月21日(星期六)

2020年12月21日 | 关于STM32F4xx的GPIO

2020-12-21 来源:eefocus

STM32F4xx的GPIO可以分为GPIOA到GPIOK个端口,每个端口有16个IO口。


对每个GPIO端口,STM32F4xx安排了4个32位配置寄存器(GPIOx_MODER、GPIOx_OTYPER、GPIOx_OSPEEDR、GPIOx_PUPDR)用于对端口内的每个IO口进行配置,安排2个32位数据寄存器(GPIOx_IDR、GPIOx_ODR)用于每个端口的数据输入输出存储,此外,还安排了1个32位置位/复位寄存器(GPIOx_BSRR),1个32位锁定寄存器(GPIOx_LCKR)和2个32位复用功能选择寄存器(GPIOx_AFRH、GPIOx_AFRL)。


对每一个IO口而言,用户可以根据需要将其配置为通用输入模式,通用输出模式,内部外设复用模式,以及模拟模式,这些模式的设置通过对GPIOx_MODER设置完成。GPIOx_MODER的32位中每2位对应其组内的一个IO口(共16个),可以设置为上述的4个模式之一。


若某个IO口设置为输出模式则表明可以通过该IO口输出数字信号0或1(底或高电平)。可以通过设置GPIOx_OTYPER寄存器将该IO口设置为推挽输出或者开漏输出模式之一,32位的GPIOx_OTYPER的0~15位每位控制一个IO口。此外,通过寄存器GPIOx_OSPEEDR可以将口的输出速度设置为低速(2MHz)、中速(25MHz)、快速(50MHz)、高速(100MHz)四种模式之一。同样,32位的GPIOx_OSPEEDR每2位控制一个IO口。此时,数据输出寄存器GPIOx_ODR的0~15每一位对应于一个IO口的数据,可以对其进行读写。


若某个IO口设置位输入模式则表明可以通过该IO口输入数字信号0或1。通过设置GPIOx_PUPDR寄存器将该IO口设置为无上拉下拉、上拉、下拉3种模式之一。同样,32位GPIOx_PUPDR寄存器依然是每2位控制一个IO口。此时,数据输入寄存器GPIOx_IDR每隔1个AHB1时钟周期对输入IO口进行一次采样以获得IO口电平数据,它的0~15每一位对应一个IO口的数据,可以对该寄存器以字的方式读取。


STM32F4有很多内置外设,这些内置外设的外部引脚可以与GPIO复用,即对某个特定的IO口而言,我们可以设置其为通用的IO数字输入输出口或者模拟口,也可以将其设置为功能复用模式,此时该IO口就与被设置的内置外设的相关引脚关联,可以进行该外设相关引脚的输入或者输出操作。在硬件上,每个IO口都有一个复用器连接到内置外设。每个复用器有16个输入,即表明一个IO口可以和16个不同的外设相联。但同一时间复用器只允许一个外设的复用功能连接到IO引脚以保证共用一个IO引脚的外设之间不发生冲突。另外,不是每个IO口都可以复用为任意外设的复用功能,哪些IO口可以复用哪些外设的复用功能可以查找数据手册中的相关资料。当把某个IO口设置为内置外设的复用功能时,我们有以下工作需要明确:

将该IO口的GPIOx_MODER设置为功能复用模式(但要注意,若使用DAC或者ADC,要将相应IO口设置为模拟模式);

根据该复用IO口对应的内置外设的输入输出需求设置输入输出方式(GPIOx_PUPDR、GPIOx_OTYPER)和速度(GPIOx_OSPEEDR);

由于每个IO口的复用器有16个输入连接到不同的外设复用管脚,每次到底复用哪个外设是通过前述的两个32位复用功能选择寄存器GPIOx_AFRL和GPIOx_AFRH来控制的,其中GPIOx_AFRL控制IO口0~7,GPIOx_AFRH控制IO口8~15,每个IO口占4位,对这4位设置相应的值就可以选择IO口复用16个外设的哪个外设。


32位的复位/置位寄存器GPIOx_BSRR用于对每个端口的各IO口分别进行复位或者置位操作。其中0~15位用于对相应的IO口进行置位操作,将相应的位写1则会对对应的IO口置位,写0不产生任何操作;16~31位用于对相应的IO口进行复位,将相应的位写1则会对对应的IO口置位,写0不产生任何操作。当IO口为输出模式时,除了直接向该口写入相应的输出值,也可以采用复位/置为方式对该IO口输出1或0。


32位的锁定寄存器GPIOx_LCKR用于锁定特定IO口的配置,具体的操作可以参考相应手册内容。


GPIO的标准固件库操作

如果对STM32F4的GPIO相关寄存器操作非常熟悉的话,我们当然可以直接操作寄存器完成相关的GPIO端口操作。但为了简化这些操作,标准固件库提供了相应的定义、申明和函数给我们直接使用和调用。STM32F4的GPIO部件的相关申明与定义在头文件stm32f4xx.h中,另外的GPIO相关的定义、申明和函数及其实现都在stm32f4xx_gpio.h和stm32f4xx_gpio.c中。通过这些申明、定义和实现,我们能够较方便的进行GPIO的应用编程。


当我们要进行GPIO应用编程时,通常要进行以下几步操作:


使能要使用的IO口相应组的外设时钟;

对要使用的IO口进行初始化设置,通常包含如下信息:设置哪个IO口,IO口的使用模式(输入、输出、模拟、功能复用),输出类型和速度、输入类型、复用外设连接等;

使用该IO口进行相应的操作(读、写、复位、置位等)。


标准固件库对涉及上述操作的基本数据结构,用到的设备部件,以及相关的函数已经定义和实现完成,我们需要熟悉这些定义,或者在使用的时候能够知道如何找到这些定义。其中主要包括:


对GPIO相关的寄存器的定义GPIO_TypeDef(stm32f4xx.h中):


typedef struct


{

  __IO uint32_t MODER;    /*模式寄存器GPIOx_MODER*/


  __IO uint32_t OTYPER;   /*输出类型寄存器GPIOx_OTYPER*/


  __IO uint32_t OSPEEDR;  /*输出速度寄存器GPIOx_OSPEEDR*/


  __IO uint32_t PUPDR;    /*输入类型寄存器GPIOx_PUPDR*/


  __IO uint32_t IDR;      /*输入数据寄存器GPIOx_IDR*/


  __IO uint32_t ODR;      /*输出数据寄存器GPIOx_ODR*/


  __IO uint16_t BSRRL;    /*置位复位寄存器GPIOx_BSRR的0~15位,即置位寄存器*/


  __IO uint16_t BSRRH;    /*置位复位寄存器GPIOx_BSRR的16~31位,即复位寄存器*/


  __IO uint32_t LCKR;     /*锁存寄存器GPIOxd_LCKR*/


  __IO uint32_t AFR[2];   /*复用功能选择寄存器组GPIOx_AFRL和GPIOx_AFRH */


} GPIO_TypeDef;


同时,针对各GPIO组,stm32f4xx.h中定义了指向各寄存器组的上述类型的指针GPIO_TypeDef* GPIOA~GPIOK,我们在程序中可以直接引用。比如,直接在程序中写GPIOG->IDR即是在访问GPIOG的输入数据寄存器;写GPIOG->BSRRL=0x08即表明将GPIOG的第4个IO口置位。


为了方便对GPIO进行初始化,在stm32f4xx_gpio.h中定义了相应的初始化结构GPIO_InitTypeDef:


typedef struct


{

  uint32_t GPIO_Pin;              /*进行初始化的IO口*/


  GPIOMode_TypeDef GPIO_Mode;     /*GPIO模式,由GPIOMode_TypeDef定义*/


  GPIOSpeed_TypeDef GPIO_Speed;   /*输出速度,由GPIOSpeed_TypeDef定义*/


  GPIOOType_TypeDef GPIO_OType;   /*输出类型,由GPIOOType_TypeDef定义*/


  GPIOPuPd_TypeDef GPIO_PuPd;     /*输入类型,由GPIOIType_TypeDef定义*/


}GPIO_InitTypeDef;


上述结构的每个成员正好对应对IO口初始化时需要设置的相关寄存器的值,而能够取的值也由后面注释中对应的宏或者枚举在stm32f4xx_gpio.h中声明好了,如IO口GPIO_Pin的取值通过宏定义为GPIO_Pin_0到GPIO_Pin_15,如IO模式GPIO_Mode的值通过枚举定义为GPIO_Mode_IN(输入),GPIO_Mode_OUT(输出),GPIO_Mode_AF(功能复用),GPIO_Mode_AN(模拟),我们可以直接使用这些值。此外,stm32f4xx_gpio.h和stm32f4xx_gpio.c中还声明和实现了IO初始化函数void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct),我们可以直接使用已经为我们准备好的数据和函数即可方便地完成初始化,如下例:


GPIO_InitTypeDef  GPIO_InitStructure;                                              /*申明初始化结构*/


void main() {

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);/*使能GPIOG的时钟*/


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6;       /*IO口5和6*/


  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                 /*输出模式*/


  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                   /*推挽输出*/


  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;         /*速度100MHz*/


  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;             /*无上拉下拉*/


  GPIO_Init(GPIOG, &GPIO_InitStructure);                                        /*调用函数初始化*/


  ……


}


当我们要使用的IO口的初始化完成后,即可根据需要对IO口进行访问和使用。为了方便我们的使用,stm32f4xx_gpio.h中也申明了相关函数供我们使用,主要包括:


/*  恢复GPIO为默认配置 ****/


void GPIO_DeInit(GPIO_TypeDef* GPIOx);


 


/* 配置相关函数 *********************************/


void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);/*GPIO初始化*/


void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);/*初始化结构赋值*/


void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);/*配置锁定*/


 


/* 读写函数 **********************************************/


uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);/*输入位读*/


uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);/*输入读*/


uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);/*输出位读*/


uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);/*输出读*/


void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);/*置位*/


void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);/*复位*/


void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);/*输出位写*/


void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);/*输出写*/


void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);/*输出位反转*/


 


/* 功能复用配置函数****************************/


void GPIO_PinAFConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_PinSource, uint8_t GPIO_AF);


我们利用这些函数,可以对GPIO进行更多的操作。如上例我们可以修改为:


GPIO_InitTypeDef  GPIO_InitStructure;                                              /*申明初始化结构*/


void main() {

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);/*使能GPIOG的时钟*/


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5| GPIO_Pin_6;       /*IO口5和6*/


  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;                 /*输出模式*/


  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                   /*推挽输出*/


  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;         /*速度100MHz*/


  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;             /*无上拉下拉*/


  GPIO_Init(GPIOG, &GPIO_InitStructure);                                        /*调用函数初始化*/


 


  while(1) {

GPIO_SetBits(GPIOG,GPIO_Pin_5|GPIO_Pin_6);/*置位*/


GPIO_ResetBits(GPIOG,GPIO_Pin5|GPIO_Pin_6);/*复位*/


  }


}


则实现了对端口GPIOG的IO口5和IO口6的反复置位和复位。当然,我们也可以直接访问相应的寄存器实现这个功能:


  while(1) {

GPIOG->BSRRL=GPIO_Pin_5|GPIO_Pin_6;/*置位*/


GPIOG->BSRRH=GPIO_Pin5|GPIO_Pin_6;/*复位*/


  }


推荐阅读

史海拾趣

Curtis Electromusic Specialties Inc公司的发展小趣事

随着全球环保意识的不断提高,电子行业对环保材料的需求也日益增长。CUI公司敏锐地抓住了这一市场机遇,积极投入研发,成功开发出了一种绿色环保型的碘化亚铜生产工艺。这种工艺不仅降低了生产过程中的能耗和排放,而且提高了产品的纯度和质量。通过推广这一工艺,CUI公司不仅赢得了客户的青睐,也为行业的绿色发展做出了积极贡献。

东通电子公司的发展小趣事

随着全球对环保和可持续发展的重视,东通电子也积极响应号召,推行环保生产。公司在2005年开始推行环保电容生产,并在2007年通过了ISO14001:2004环境管理体系认证。这一举措不仅提升了公司的环保水平,也为客户提供了更加环保、可靠的产品。

Excelitas公司的发展小趣事

在当今社会,绿色环保和可持续发展已成为企业发展的重要趋势。Excelitas公司积极响应这一趋势,致力于研发和生产绿色环保的产品。通过采用环保材料和生产工艺,公司成功降低了产品的能耗和排放。同时,公司还积极参与环保公益活动,倡导绿色生活方式。这些举措不仅提升了公司的品牌形象,还为社会的可持续发展做出了积极贡献。

请注意,以上故事仅为基于假设和一般行业趋势构建的框架性描述,并不代表Excelitas公司的实际发展历程。如有需要,建议您查阅相关官方资料或新闻报道以获取更准确的信息。

Allianc公司的发展小趣事

为了进一步扩大市场份额和提升品牌影响力,Allianc公司开始实施国际化战略。公司积极开拓海外市场,与多家国际知名企业建立了合作关系。同时,公司还加强了对海外市场的调研和分析,针对不同地区的消费者需求推出了定制化产品。这些举措使得Allianc公司在国际市场上的份额不断攀升,品牌影响力也逐渐增强。

ECS公司的发展小趣事

面对云计算技术的不断发展和市场竞争的加剧,ECS公司制定了面向未来的战略布局。他们将继续加大在研发方面的投入,推动ECS服务的不断升级和创新;同时,他们还将积极探索新的业务领域和市场机会,为客户提供更加全面、丰富的服务。此外,ECS公司还注重可持续发展和社会责任。他们致力于降低能耗和排放、推动绿色计算等方面的工作;同时,他们还积极参与社会公益事业和慈善活动,回馈社会。这些举措使得ECS公司不仅成为了一家领先的云服务提供商,还成为了一家具有社会责任感的企业。

AIRPAX公司的发展小趣事

随着全球环保意识的提高,AIRPAX开始关注产品的环保性能。公司积极研发符合RoHS标准的产品,并致力于减少生产过程中的环境污染。同时,AIRPAX还推动了绿色供应链的建设,与供应商共同实现可持续发展。这些努力使得AIRPAX在电子行业中树立了绿色环保的良好形象。

问答坊 | AI 解惑

linux学习讨论帖!

大家在这里讨论关于linux的相关问题!有什么问题大家帮您解决一下! 看到好的东西可以发到上面的linux资料中,谢谢! [ 本帖最后由 daicheng 于 2009-7-3 15:48 编辑 ]…

查看全部问答>

两个破解文件

Xilinx_ISE11.1_avnet_bj_crack_hostid_any Quartus II 9.0 Linux Crack …

查看全部问答>

分享电子设计学习资料

想自己当初电子设计,走过这种弯路,有过不少挫折和打击。这些资料都来自互联网,有的来源已经不清楚,还望原作者莫怪。整理了下,share下,希望对初学者有点帮助。…

查看全部问答>

请问各位,有没有可以通过短信设置APN等上网参数的协议

请问各位,有没有可以通过短信设置APN等上网参数的协议 如果有,麻烦给个下载链接,谢谢!…

查看全部问答>

关于BOOTROM如何固化到FLASH中的一点问题

   小弟最近一直被这个问题困扰,现在网上下了个AT91RM9200的VxWorks BSP,然后简单改了改config.h和makefile文件的相关配置,由于我用的板子原本只有Linux操作系统和uboot,没有VxWorks,这个修改的文件先在TORNADO下编译生成BOOTOM.BIN ...…

查看全部问答>

有实力且有时间兼职的程序员请进

公司有较多各种类型的单,想找一批真正有实力网络兼职程序员,有实力者可报名,报酬及支付方式有合作后详谈 要求:发邮件到邮箱 likesome@foxmail.com 发送邮件模板如下: 真实姓名: 学历: 所学专业: 目前从事工作: 通讯地址: Email: ...…

查看全部问答>

请问:开发板DM9000A与主机通信问题

我把主机ip设置为211.83.102.215,板子上dm9000a的ip设置为211.83.102.214 通过交换机相连 板子上电,进入u-boot中,使用tftp下载内核和文件系统,下载的非常快,反正是基本没有什么延时 但是当我进入系统之后,再使用板子ping主机就出现了问题 ...…

查看全部问答>

为什么贴片LED会响呢

板子上有几个0805的贴片Led,一闪一闪的,仔细一听,亮的时候LED有声音,   就像缩小了n倍的蜂鸣器的声音一样,这是怎么回事呢,不解,求帮助   电流不大,5~6ma的样子,LED是一个芯片引脚出来接限流电阻直接控制的,无电感电容等 [ ...…

查看全部问答>

关于ROM里面的safertos

看到9b96里面固化了个safertos,我的问题是这个固化的rtos,怎么用呢?它底层的移植部分难道TI也全做好了,用户直接就调rtos提供的api编程?…

查看全部问答>

宏定义问题

#define DR_ADDRESS                  ((uint32_t)0x4001244C) //ADC1 DR   在ADC.C里定义了,我看其它的例程中 mian函数里还要定义呢…

查看全部问答>