STM32的GPIO使用的函數(shù)剖析
一、GPIO_Init函數(shù)解析1
本文引用地址:http://m.butianyuan.cn/article/201611/317503.htm1、參數(shù)GPIO_TypeDef1
2、參數(shù)GPIO_InitStruct2
3、函數(shù)代碼詳解4
4、備注6
一、GPIO_Init函數(shù)解析
首先來看一下GPIO_Init函數(shù)的原型voidGPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_InitStruct)。這個(gè)函數(shù)的實(shí)現(xiàn)是在Stm32f10x_gpio.c文件中,若要使用該函數(shù)在相應(yīng)的應(yīng)用程序的前面包含Stm32f10x_gpio.h頭文件。
1、參數(shù)GPIO_TypeDef
該函數(shù)的第一個(gè)參數(shù)為GPIO_TypeDef,它是一個(gè)結(jié)構(gòu)體類型,該類型在Stm32f10x.h中被定義。定義的原型為:
typedefstruct
{
__IOuint32_tCRL;
__IOuint32_tCRH;
__IOuint32_tIDR;
__IOuint32_tODR;
__IOuint32_tBSRR;
__IOuint32_tBRR;
__IOuint32_tLCKR;
}GPIO_TypeDef;
在這個(gè)結(jié)構(gòu)體類型當(dāng)中有7個(gè)32(8字節(jié))位的變量,這些變量在存儲(chǔ)空間的地址是相鄰的。打開STM32數(shù)據(jù)手冊不難看出,每個(gè)端口對應(yīng)有16的引腳,由7個(gè)寄存器控制GPIO行為,并且這7個(gè)寄存器的順序也是連續(xù)的。各個(gè)端口都有相同的結(jié)構(gòu)。STM32的固件庫就將這種結(jié)構(gòu)抽象出一個(gè)類型GPIO_TypeDef。在操作寄存器之前你一定要有一個(gè)寄存器映射的操作,否則無法訪問指定的寄存器,在這里我們只需要映射一次而不需要映射7此。這樣做是不是很方便,也提高了代碼的可讀性,使代碼規(guī)范化。
既然GPIO_Init的第一個(gè)參數(shù)GPIO_TypeDef的指針變量,這個(gè)指針變量存放的就是某一個(gè)端口的首地址。某一個(gè)程序的調(diào)用語句是這樣的GPIO_Init(GPIOD,&GPIO_InitStructure);//初始化GPIOD
GPID是固件庫中定義的一個(gè)宏,在編譯的時(shí)候會(huì)宏展開,先列出與GPIOD端口地址映射有關(guān)的宏定義如下:
#defineGPIOD((GPIO_TypeDef*)GPIOD_BASE)
#defineGPIOD_BASE(APB2PERIPH_BASE+0x1400)
#defineAPB2PERIPH_BASE(PERIPH_BASE+0x10000)
#definePERIPH_BASE((uint32_t)0x40000000)
看到了0x40000000這個(gè)數(shù)字是不是非常熟悉,它是外設(shè)的首地址。在STM32芯片的內(nèi)部STM32有兩個(gè),一個(gè)叫APB1,一個(gè)叫APB2。每一個(gè)APB橋都會(huì)管理很多外設(shè)。STM32F10x把這兩個(gè)APB的外設(shè)寄存器訪問地址放在了不同的存儲(chǔ)空間。0x10000就是APB2外設(shè)的存儲(chǔ)空間首地址相對于整個(gè)外設(shè)的偏移。而0x1400是GPIOD端口外設(shè)首地址相對于APB2外設(shè)的存儲(chǔ)空間首地址的偏移。這樣就找到了GPIOD外設(shè)的基地址了!而((GPIO_TypeDef*)GPIOD_BASE)可以同時(shí)實(shí)現(xiàn)所有控制GPIOD端口的7個(gè)寄存器的映射。若訪問某一個(gè)寄存器只需要通過指向GPIO_TypeDef變量的指針。
2、參數(shù)GPIO_InitStruct
第二個(gè)參數(shù)的為GPIO_InitTypeDef*GPIO_InitStruct。就是一個(gè)指向GPIO_InitTypeDef的地址。第一個(gè)參數(shù)只找到配置的目標(biāo)寄存器,第二個(gè)參數(shù)就是對相應(yīng)端口如何配置的數(shù)據(jù)參數(shù)。這些參數(shù)存儲(chǔ)在指向GPIO_InitTypeDef變量的首地址處。先列處該參數(shù)由來的一斷代碼
GPIO_InitTypeDefGPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitTypeDef是一個(gè)結(jié)構(gòu)體的變量,該變量在Stm32f10x_gpio.h頭文件中被定義,定義的原型如下:
typedefstruct
{
uint16_tGPIO_Pin;
GPIOSpeed_TypeDefGPIO_Speed;
GPIOMode_TypeDefGPIO_Mode;
}GPIO_InitTypeDef;
GPIO_InitTypeDef的第一個(gè)變量為GPIO_Pin是一個(gè)16為的無符號數(shù),該數(shù)只有16位,每一位代表一個(gè)引腳,若要配置某一個(gè)端口的某一個(gè)引腳只需要把相應(yīng)的位設(shè)置為1就可以了。在STM32的固件庫中有如下引腳號定義:
#defineGPIO_Pin_0((uint16_t)0x0001)/*! #defineGPIO_Pin_1((uint16_t)0x0002)/*! #defineGPIO_Pin_2((uint16_t)0x0004)/*! #defineGPIO_Pin_3((uint16_t)0x0008)/*! #defineGPIO_Pin_4((uint16_t)0x0010)/*! #defineGPIO_Pin_5((uint16_t)0x0020)/*! #defineGPIO_Pin_6((uint16_t)0x0040)/*! #defineGPIO_Pin_7((uint16_t)0x0080)/*! #defineGPIO_Pin_8((uint16_t)0x0100)/*! #defineGPIO_Pin_9((uint16_t)0x0200)/*! #defineGPIO_Pin_10((uint16_t)0x0400)/*! #defineGPIO_Pin_11((uint16_t)0x0800)/*! #defineGPIO_Pin_12((uint16_t)0x1000)/*! #defineGPIO_Pin_13((uint16_t)0x2000)/*! #defineGPIO_Pin_14((uint16_t)0x4000)/*! #defineGPIO_Pin_15((uint16_t)0x8000)/*! #defineGPIO_Pin_All((uint16_t)0xFFFF)/*! 使用這些定義好的宏就方便多了,要配置某幾個(gè)引腳只需要把相應(yīng)的引腳相或就可以了。若你要多某一個(gè)端口的所有為進(jìn)行配置,那么只需要使用一個(gè)宏GPIO_Pin_All。簡單吧!哈哈! GPIOSpeed_TypeDef是一個(gè)枚舉變量,它用于存儲(chǔ)GPIO速度的參數(shù),它的定義如下: typedefenum { GPIO_Speed_10MHz=1, GPIO_Speed_2MHz, GPIO_Speed_50MHz }GPIOSpeed_TypeDef; 通過定義可以知道,GPIOSpeed_TypeDef的變量有三種取值,那么GPIO的速度有三種, 枚舉變量的值 對應(yīng)的速度 1 10MHZ 2 2MHZ 3 50MHZ GPIOMode_TypeDef也是一個(gè)枚舉變量,它用于存儲(chǔ)GPIO工作的模式,它的定義如下: typedefenum {GPIO_Mode_AIN=0x0, GPIO_Mode_IN_FLOATING=0x04, GPIO_Mode_IPD=0x28, GPIO_Mode_IPU=0x48, GPIO_Mode_Out_OD=0x14, GPIO_Mode_Out_PP=0x10, GPIO_Mode_AF_OD=0x1C, GPIO_Mode_AF_PP=0x18 }GPIOMode_TypeDef; 設(shè)計(jì)這個(gè)枚舉變量的可取值有一定的意義。在第四位當(dāng)中只用到了其中的高兩位,這兩位數(shù)據(jù)用來存儲(chǔ)到某一個(gè)引腳的模式控制位MODEx[1:0],而高四位用來標(biāo)志某一些標(biāo)志。 高四位的取值 意義 0 輸入模式 1 輸出模式 2 下拉輸入 4 上拉輸入 上面是GPIO_Init函數(shù)參數(shù)的解釋。我在我們就可以進(jìn)入GPIO_Init函數(shù)的內(nèi)部看看了。 先把函數(shù)的代碼列出,對代碼的解釋都放在了注釋當(dāng)中,如下: voidGPIO_Init(GPIO_TypeDef*GPIOx,GPIO_InitTypeDef*GPIO_InitStruct) { uint32_tcurrentmode=0x00,currentpin=0x00,pinpos=0x00,pos=0x00; uint32_ttmpreg=0x00,pinmask=0x00; /*Checktheparameters*/ assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode)); assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); /*----------------------------GPIOModeConfiguration-----------------------*/ currentmode=((uint32_t)GPIO_InitStruct->GPIO_Mode)&((uint32_t)0x0F); if((((uint32_t)GPIO_InitStruct->GPIO_Mode)&((uint32_t)0x10))!=0x00)//若為輸出上拉就會(huì)配置GPIO的速度 { /*Checktheparameters*/ assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed)); /*Outputmode*/ currentmode|=(uint32_t)GPIO_InitStruct->GPIO_Speed; } /*----------------------------GPIOCRLConfiguration------------------------*/ /*Configuretheeightlowportpins*/ if(((uint32_t)GPIO_InitStruct->GPIO_Pin&((uint32_t)0x00FF))!=0x00)//若對第八個(gè)引腳進(jìn)行配置,GPIO_Pin的值某一位為1就會(huì)對該引腳配置 { tmpreg=GPIOx->CRL;//暫存GPIO控制寄存器原來的值 for(pinpos=0x00;pinpos<0x08;pinpos++)//掃描8次決定,查看哪一引腳需要配置,若//需要配置則進(jìn)行配置 { pos=((uint32_t)0x01)< /*Gettheportpinsposition*/ currentpin=(GPIO_InitStruct->GPIO_Pin)&pos;//currentpin的值為0或者為pos if(currentpin==pos)//若為pos說明該位需要配置 { pos=pinpos<<2;//pinpos的值乘以4得到某一引腳配置位的最低位號:0,4,8......28 /*Clearthecorrespondinglowcontrolregisterbits*///用于屏蔽某一個(gè)引腳的配置位,使這4位為0 pinmask=((uint32_t)0x0F)< tmpreg&=~pinmask; /*Writethemodeconfigurationinthecorrespondingbits*/ tmpreg|=(currentmode< /*ResetthecorrespondingODRbit*/ if(GPIO_InitStruct->GPIO_Mode==GPIO_Mode_IPD)//若為輸入下拉,需要打開相應(yīng)的開關(guān) { GPIOx->BRR=(((uint32_t)0x01)< } else { /*SetthecorrespondingODRbit*/ if(GPIO_InitStruct->GPIO_Mode==GPIO_Mode_IPU)//若為輸入下拉,需要打開相應(yīng)的開關(guān) { GPIOx->BSRR=(((uint32_t)0x01)< } } } } GPIOx->CRL=tmpreg;//對低8個(gè)引腳配置寄存器賦值 } /*----------------------------GPIOCRHConfiguration------------------------*/ /*Configuretheeighthighportpins*/ if(GPIO_InitStruct->GPIO_Pin>0x00FF) { tmpreg=GPIOx->CRH; for(pinpos=0x00;pinpos<0x08;pinpos++) { pos=(((uint32_t)0x01)<<(pinpos+0x08)); /*Gettheportpinsposition*/ currentpin=((GPIO_InitStruct->GPIO_Pin)&pos); if(currentpin==pos) { pos=pinpos<<2; /*Clearthecorrespondinghighcontrolregisterbits*/ pinmask=((uint32_t)0x0F)< tmpreg&=~pinmask; /*Writethemodeconfigurationinthecorrespondingbits*/ tmpreg|=(currentmode< /*ResetthecorrespondingODRbit*/ if(GPIO_InitStruct->GPIO_Mode==GPIO_Mode_IPD) { GPIOx->BRR=(((uint32_t)0x01)<<(pinpos+0x08)); } /*SetthecorrespondingODRbit*/ if(GPIO_InitStruct->GPIO_Mode==GPIO_Mode_IPU) { GPIOx->BSRR=(((uint32_t)0x01)<<(pinpos+0x08)); } } } GPIOx->CRH=tmpreg; } } assert_param函數(shù)是對參數(shù)的檢測。參數(shù)要么是邏輯0或者1。IS_GPIO_ALL_PERIPH也是一個(gè)宏,宏定義為: #defineIS_GPIO_ALL_PERIPH(PERIPH)(((PERIPH)==GPIOA)|| ((PERIPH)==GPIOB)|| ((PERIPH)==GPIOC)|| ((PERIPH)==GPIOD)|| ((PERIPH)==GPIOE)|| ((PERIPH)==GPIOF)|| ((PERIPH)==GPIOG)) 其他的參數(shù)檢測函數(shù)當(dāng)中使用的宏都是相似的,具體可以查看相應(yīng)的宏定義,在此不一一列出。 對低8位的配置和對高8位的配置原理是一樣的。所以在此只對低8引腳配置進(jìn)行說明。3、函數(shù)代碼詳解
4、備注
評論