|
函數(shù)目的:對GPIO進行初始化。
如果對寄存器操作GPIO有一定了解的話,對下面理解起來就比較簡單。
如果將GPIO口設(shè)置為輸出模式,要設(shè)置兩個寄存器,CRL與ODR。
CRL:規(guī)定了低8位GPIO的輸出輸入狀態(tài)模式。
ODR:只用[15:0]確定GPIO端口的輸出值。
如下重點要理解CRL的工作方式。
CRL設(shè)置原理如上:
例如設(shè)置端口PD7,那么就需要設(shè)置[31:28]四位,首先確定輸入低二位輸入輸出狀態(tài)及輸出模式下的速度,高二位設(shè)置GPIO端口的工作方式。如果不理解,多看看兩幅原理圖。
編寫GPIO_Init()的原理(以CRL為例):
1.首先對GPIO_Mode,GPIO_Pin,GPIO_Speed進行宏定義。與CRL中使用略有差異,對GPIO_Mode中的定義,可以看到輸出模式下的定義的高四位均為0x1.而輸入模式下設(shè)置為0/2/4,此舉的目的是為了便于計算機進行識別處理。進行完第一步后,能夠的得到4位的GPIO的狀態(tài)的數(shù)據(jù)。
2.管腳及管腳的輸出值如何確定。這是GPIO_Init()的第二個難點。
首先確定GPIO_Pin是哪個管腳,然后確定后,將CRL寄存器的4*Pin的位置上的數(shù)據(jù)值為零,然后將第一步的取得值賦予CRL。
3.CRH和CRL的原理相同,通過 if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)來判斷是設(shè)置GPIO的低8位和高8位。
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
//GPIO_Init()函數(shù)定義
{
uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;
uint32_t tmpreg = 0x00, pinmask = 0x00;
//定義變量用作GPIO中CRL、CRH、ODR的判定
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));
//檢查實參是否符合要求
currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);
//將GPIO_Mode的值與0x0f相與,只取Mode的低四位將值賦予currentmode。
if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)
//說明是輸出。需要對速度進行配置。
// 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
由此可以看出當(dāng)上式判斷為真時,即GPIO設(shè)定為輸出模式。則進行下步判斷。
{
assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
//判斷GPIO_Speed的速度值是否符合要求
currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
//若為輸出,將速度配置到最后兩位。
//注意:因為GPIO_Mode設(shè)置的值得低二位均為零,所以將GPIO_Speed賦予currentmode。
}
if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)
//判斷是否是設(shè)置CRL。
{
tmpreg = GPIOx->CRL;
//首先將GPIOx_CRL的值賦予tmpreg。
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
//通過循環(huán)比較,確定管腳
{
pos = ((uint32_t)0x01) << pinpos;
//位移操作,簡單好用
currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
// 將定義的GPIO_Pin與pos相與,如同下句,對管腳定位
if (currentpin == pos)
//如果管腳確定為第pos個管腳。
{
pos = pinpos << 2;
//那么就將pinpos的向左位移兩位。即理解為位置乘以4.這需要看下CRL寄存器原理
pinmask = ((uint32_t)0x0F) << pos;
//將0x0f(1111)向左位移pos位(剛剛經(jīng)過pinpos向左位移過兩位)。即將CRL要處理的位置進行處理。
tmpreg &= ~pinmask;
//將tmpreg的相應(yīng)位置置零!
tmpreg |= (currentmode << pos);
//將剛剛設(shè)置好的currentmode放置到指定的位置(原理參照CRL)。
if (GPIO_InitStruct->GPIO _Mode == GPIO_Mode_IPD)
{
GPIOx->BRR = (((uint32_t)0x01) << pinpos);
}
else
{
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
GPIOx->BSRR = (((uint32_t)0x01) << pinpos);
}
//若為上下拉輸入。如上設(shè)置,較為簡單。
}
}
}
GPIOx->CRL = tmpreg;
}
if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
{
tmpreg = GPIOx->CRH;
for (pinpos = 0x00; pinpos < 0x08; pinpos++)
{
pos = (((uint32_t)0x01) << (pinpos + 0x08));
currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
if (currentpin == pos)
{
pos = pinpos << 2;
pinmask = ((uint32_t)0x0F) << pos;
tmpreg &= ~pinmask;
tmpreg |= (currentmode << pos);
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)
{
GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
{
GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));
}
}
}
GPIOx->CRH = tmpreg;
}
}
|
|