標(biāo)題: GPIO_Init()函數(shù)詳解 [打印本頁(yè)]

作者: jiajialin    時(shí)間: 2015-5-20 02:28
標(biāo)題: GPIO_Init()函數(shù)詳解
函數(shù)目的:對(duì)GPIO進(jìn)行初始化。
如果對(duì)寄存器操作GPIO有一定了解的話,對(duì)下面理解起來(lái)就比較簡(jiǎn)單。
如果將GPIO口設(shè)置為輸出模式,要設(shè)置兩個(gè)寄存器,CRL與ODR。
CRL:規(guī)定了低8位GPIO的輸出輸入狀態(tài)模式。
ODR:只用[15:0]確定GPIO端口的輸出值。
如下重點(diǎn)要理解CRL的工作方式。

CRL設(shè)置原理如上:
例如設(shè)置端口PD7,那么就需要設(shè)置[31:28]四位,首先確定輸入低二位輸入輸出狀態(tài)及輸出模式下的速度,高二位設(shè)置GPIO端口的工作方式。如果不理解,多看看兩幅原理圖。
編寫GPIO_Init()的原理(以CRL為例):
1.首先對(duì)GPIO_Mode,GPIO_Pin,GPIO_Speed進(jìn)行宏定義。與CRL中使用略有差異,對(duì)GPIO_Mode中的定義,可以看到輸出模式下的定義的高四位均為0x1.而輸入模式下設(shè)置為0/2/4,此舉的目的是為了便于計(jì)算機(jī)進(jìn)行識(shí)別處理。進(jìn)行完第一步后,能夠的得到4位的GPIO的狀態(tài)的數(shù)據(jù)。
2.管腳及管腳的輸出值如何確定。這是GPIO_Init()的第二個(gè)難點(diǎn)。
首先確定GPIO_Pin是哪個(gè)管腳,然后確定后,將CRL寄存器的4*Pin的位置上的數(shù)據(jù)值為零,然后將第一步的取得值賦予CRL。
3.CRH和CRL的原理相同,通過(guò) if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)來(lái)判斷是設(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));  
  //檢查實(shí)參是否符合要求

  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)
//說(shuō)明是輸出。需要對(duì)速度進(jìn)行配置。
// 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)上式判斷為真時(shí),即GPIO設(shè)定為輸出模式。則進(jìn)行下步判斷。
  {

assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
//判斷GPIO_Speed的速度值是否符合要求

currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;
//若為輸出,將速度配置到最后兩位。
//注意:因?yàn)镚PIO_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++)
//通過(guò)循環(huán)比較,確定管腳
    {
      pos = ((uint32_t)0x01) << pinpos;
//位移操作,簡(jiǎn)單好用

      currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;
//   將定義的GPIO_Pin與pos相與,如同下句,對(duì)管腳定位
      if (currentpin == pos)
//如果管腳確定為第pos個(gè)管腳。
      {
        pos = pinpos << 2;
//那么就將pinpos的向左位移兩位。即理解為位置乘以4.這需要看下CRL寄存器原理

        pinmask = ((uint32_t)0x0F) << pos;
//將0x0f(1111)向左位移pos位(剛剛經(jīng)過(guò)pinpos向左位移過(guò)兩位)。即將CRL要處理的位置進(jìn)行處理。
        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è)置,較為簡(jiǎn)單。
        }
      }
    }
    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;
  }
}


作者: katv0718    時(shí)間: 2022-9-6 21:48
好,分析得很詳細(xì),謝謝!
作者: 我,菜雞    時(shí)間: 2022-12-17 11:49
樓主辛苦了,剛好想做一個(gè)基于寄存器的IO口初始化函數(shù),便于不同IO口的快速操作。,
作者: lml_user    時(shí)間: 2023-10-11 11:54
很詳細(xì)




歡迎光臨 (http://www.torrancerestoration.com/bbs/) Powered by Discuz! X3.1