分享到plurk 分享到twitter 分享到facebook

版本 926d29ddabb8fd6d6e862c3d3570d5f0edc1cf16

General-purpose Input/Output (GPIO)

Introduction

General Purpose Input/Output (GPIO) is a generic pin on a chip whose behavior (including whether it is an input or output pin) can be controlled (programmed) by the user at run time.

通常我們program使用的data都是放在memory比較多,而GPIO也提供類似的操作方法給programmer(讓我們去更改記憶體內容就可以去控制pin,並影響周遭設備的運行),但真正的設備的位置並非真正落在記憶體上(如:LED、Button、…等),故GPIO的核心是記憶體操作與設備之間的一些電路特性。

Main Feature

GPIO雖然建立起記憶體與設備之間的橋梁,但也並非我們就可以隨意使用,我們必須要經過設定之後才能讓我們想要的設備正常工作。

一個pin通常可被設定成input、output、alternate function或analog,input會有兩種狀態表現(floating, pull-up/down),output也有兩種狀態表現(push-pull with pull-up/pull-down or open drain with pull-up/down)。

  • input/output方向解說 : input是指記憶體方接收來自設備的訊號源,output是指記憶體傳送訊號給設備。

當pin被設定成input時,非analog的設定下,我們可以利用GPIO的input data register(GPIOx_IDR) 或是memory中提供給目標設備的data register (當設成alternate function的時候)去接收data。

當pin被設定成output時,非analog的設定下,GPIO本身有提供output data register (GPIOx_ODR)來對目標設備做控制,但要是pin不是使用原本預先定義好的功能時(非預先定義的功能都算是alternate function的類別),此時要用memory中,另外規劃給目標設備用的register。

如果pin被設成analog的話,無論input or output都會由adc/dac那邊做處理。

Functional Description

input

  • floating vs. pull-up/pull-down

當input pin被處在高阻抗的模式下,若沒有外部訊號源進來的話,此時是無法確定pin的狀態(不能確定現在處在高電位或低電位),除非有外部訊號來驅動電路。換句話說,input floating,這個input電位狀態完全是由外部訊號來決定,沒有訊號驅動的話,就會呈現高阻抗狀態。

剛剛提到floating在沒有外部訊號驅動的情況下是呈現高阻抗狀態(無法確定電位狀態=>不能明確表示現在值是0或1),如果我們需要這個pin有一個明確的預設狀態時,必須借助pull-up(pull-down)resistor來做調整,在pull-up resistor(pull-up外接高電壓,pull-down通常會接地)加入之下,讓pin的維持在明確的高電壓狀態(pull-down則是讓pin維持在低電壓狀態)。舉例來說,如果我們定電壓在3-4 V之間是1的狀態,0-1之間是0的狀態,高阻抗的時候,電壓是不明確的,有可能電壓值會落在1-3之間的不明確地帶,甚至是沒有在任何一個狀態維持一段時間,此時的狀態是未定的,但如果我們加入pull-up resistor的話,這個pin接受來自pull-up另一端的電壓供應,讓pin至少維持在3v以上時,我們就可以確定在沒有外部訊號驅動時,pin是維持在高電位狀態。

output

  • push-pull vs open-drain mode

output處在push-pull模式下時,當output registers為“0”時輸出低電位,“1”時輸出高電位。而在open-drain模式下,“0”輸出低電位,“1”時為高阻抗(Hi-Z)狀態,電位無法確定。此兩種模式下,pin皆可pull-up/pull-down。

當有多個輸出pin在互相連通時(例如一個bus),若有兩個輸出呈不同狀態,電路上的電位便無法確定。因此,除了正在輸出的pin以外,其他pin應在高阻抗態以免干擾。以bus的例子來說,若令所有pin皆在open-drain mode pull-up,並使非輸出端pin為“1”(hi-Z),當輸出pin為“1”時,bus為高電位(因為pull-up);當輸出pin為“0”時,bus為低電位。

analog

前面所述的input/output跟現在要談的類比模式是不一樣的類型,前者的資料型態主要是高低電位的數位型態(0/1的分別),而類比訊號是普遍自然界的訊號型態,故當我們設定成類比輸入的模式時,進來GPIO pin的原始訊號源在還沒經過施密特觸發器(Schmitt trigger)會有另一個線路將訊號做導向(通常是要導到ADC去),另一方面,當我們用了類比輸出模式後,GPIO的內部將會有一條線路接收DAC處理完的類比訊號,在經過此pin傳遞到外部去。

  • 施密特觸發器(Schmitt trigger) : 將類比訊號的波形整成數位電路所能處理的方波波形(處理完只能分辨出高低電位的差別)。

.. image:: /embedded/Schmitt_trigger.jpg (施密特觸發器具有整流效果)

basic structure (P.266, Figure 25. Basic structure of a five-volt tolerant I/O port bit in Reference manual)

.. image:: /embedded/GPIO_basic_src.PNG - VDD:晶片內部的工作電壓 - VDD_FT : I/O port專用電壓,與VDD不同 - VSS:接地點 - protection diode : 防止輸入電壓超過一定範圍。當電壓低於VSS時,電流通過protection diode流入pin;當電壓高於VDD_FT時,電流流出pin。

Configuration

Input configuration (P.273, 8.3.9 Input configuration in Reference manual)

.. image:: /GPIO_ Input_configurations.png When the I/O port is programmed as Input:

  • the output buffer is disabled
  • the Schmitt trigger input is activated
    • 這種模式處理的數位訊號只在意高低電位的差別(開關控制)。
  • the pull-up and pull-down resistors are activated depending on the value in the GPIOx_PUPDR register
    • 想要讓pin的state變成一個確定的狀態,可以設定pull-up/pull-down的使用。
  • The data present on the I/O pin are sampled into the input data register every AHB1 clock cycle
    • input data的更新主要就是以AHB1本身的更新週期做決定,每一個cycle抵達時,data register就會根據當時Schmitt trigger整流完的狀態做更新。
  • A read access to the input data register provides the I/O State
    • 對data register的理解,我覺得用’狀態’比’數值’的敘述來的更好(大部分都是開關,就是外部訊號源是否有狀態改變,如 : button的按下與放開),而此處寫I/O state的意思是,即使我們現在是設成output(如 : LED控制),但我們仍然可用input data register來檢查LED現在的狀態。

Output configuration (P.274, 8.3.10 Output configuration in Reference manual)

.. image:: /GPIO_ Output_configurations.png When the I/O port is programmed as output:

.. TODO: Output Control

  • The output buffer is enabled:
    • Open drain mode: A “0” in the Output register activates the N-MOS whereas a “1” in the Output register leaves the port in Hi-Z (the P-MOS is never activated)
    • Push-pull mode: A “0” in the Output register activates the N-MOS whereas a “1” in the Output register activates the P-MOS
    • 如前所述,設定Open drain / Push-pull的使用。
  • The Schmitt trigger input is activated
  • The weak pull-up and pull-down resistors are activated or not depending on the value in the GPIOx_PUPDR register
  • The data present on the I/O pin are sampled into the input data register every AHB1 clock cycle
  • A read access to the input data register gets the I/O state
  • A read access to the output data register gets the last written value
    • 不一定與當下pin的狀態相同

Alternate function configuration (P.275, 8.3.11 Alternate function configuration in Reference manual)

.. image:: /GPIO_Alternate_function_configuration.PNG When the I/O port is programmed as alternate function:

  • The output buffer can be configured as open-drain or push-pull
  • The output buffer is driven by the signal coming from the peripheral (transmitter enable and data)
  • The Schmitt trigger input is activated
  • The weak pull-up and pull-down resistors are activated or not depending on the value in the GPIOx_PUPDR register
  • The data present on the I/O pin are sampled into the input data register every AHB1 clock cycle
  • A read access to the input data register gets the I/O state

Remark:

GPIOx_AFRL[31:0] and GPIOx_AFRH[31:0] provide ways to select alternation functions. However, different alternate functions maps to different bits of ports. For more information, please refer to Table 12. Alternate function mapping from P.73 in STM32F429xx Datasheet.

Analog configuration (P.276, 8.3.12 Analog configuration in Reference manual)

.. image:: /GPIO_Analog_configurations.PNG When the I/O port is programmed as analog configuration:

  • The output buffer is disabled
  • The Schmitt trigger input is deactivated, providing zero consumption for every analog value of the I/O pin. The output of the Schmitt trigger is forced to a constant value (0).
  • The weak pull-up and pull-down resistors are disabled
  • Read access to the input data register gets the value “0”

Note: In the analog configuration, the I/O pins cannot be 5 Volt tolerant. - analog operating voltage (VDDA) max at 3.6V

Demo

工具介紹

.. image:: /embedded/BNC line.JPG

  • BNC對鱷魚夾線

.. image:: /embedded/oscilloscope.JPG

  • 示波器(oscilloscope) 畫面很差…

示波器驗證

.. image:: /embedded/stable.JPG

  • 示波器我們調整的scale是1 volt為單位。
  • 我們接線是使用BNC對鱷魚夾線,黑接Groud,紅接要觀察的Pin。此圖為我們還沒做任何操作呈現的狀態。

.. image:: /embedded/button_pressed.JPG

  • 以button做為觀察GPIO input的部份
  • USER button對應的腳位為PA0
  • 按下button馬上放開後會產生一個小方波,由此可知,按下button是產生高電位訊號。(看’按下’多久高電位就維持多久)

.. image:: /embedded/LEDOn_2.8v.JPG

  • 用LED4來觀察GPIO output的部份
  • LED4對應的腳位是PD12,當LED4亮的時候,我們可以看到波形產生變化(從0v左右的位置升到高電位2.8v左右)

.. image:: /embedded/PD12_PD14CNT.JPG

  • 我們在接Pin的過程的時候,發現有些燈會亮起來,圖中紅色鱷魚夾線把PD12跟PD14同時夾起來,LED5在LED4亮起來的時候也會亮,對照PD14的位置是LED5。
  • 我們觀察示波器用的Code只有對button跟LED4做設定,我們推測原因是LED4亮起來的時候,PD12處於高電位狀態,而鱷魚夾線連通PD14讓此腳位也處於高電位使LED5也亮起來了。

Code Section

sample code download :

.. code-block:: c

git clone https://github.com/shengwen1997/stm32_GPIO_demo.git
cd stm32_GPIO_demo/

test for code :

.. code-block:: c

make
make flash

測試方法 :

  • input使用button作為範例,output是LED控制。

stm32f4_discovery library的預先定義(in libstm/Utilities/STM32F4-Discovery/stm32f4_discovery.h) :

.. code-block:: c

#define LEDn                             4

#define LED4_PIN                         GPIO_Pin_12
#define LED4_GPIO_PORT                   GPIOD
#define LED4_GPIO_CLK                    RCC_AHB1Periph_GPIOD  

#define LED3_PIN                         GPIO_Pin_13
#define LED3_GPIO_PORT                   GPIOD
#define LED3_GPIO_CLK                    RCC_AHB1Periph_GPIOD  

#define LED5_PIN                         GPIO_Pin_14
#define LED5_GPIO_PORT                   GPIOD
#define LED5_GPIO_CLK                    RCC_AHB1Periph_GPIOD  

#define LED6_PIN                         GPIO_Pin_15
#define LED6_GPIO_PORT                   GPIOD
#define LED6_GPIO_CLK                    RCC_AHB1Periph_GPIOD

#define BUTTONn                          1  

#define USER_BUTTON_PIN                GPIO_Pin_0
#define USER_BUTTON_GPIO_PORT          GPIOA
#define USER_BUTTON_GPIO_CLK           RCC_AHB1Periph_GPIOA
#define USER_BUTTON_EXTI_LINE          EXTI_Line0
#define USER_BUTTON_EXTI_PORT_SOURCE   EXTI_PortSourceGPIOA
#define USER_BUTTON_EXTI_PIN_SOURCE    EXTI_PinSource0
#define USER_BUTTON_EXTI_IRQn          EXTI0_IRQn 
  • LED是屬於GPIO port D,第12-15個pin分別是控制,LED4、LED3、LED5、LED6。
  • Button是屬於GPIO port A,第0個pin是可以用來檢查button狀態的腳位。在文件中,有時會看到Wake-up button這一名稱,其實就是在說User button,兩者是指同一個東西。

input 設定 :

  • button initialization(in libstm/Utilities/STM32F4-Discovery/stm32f4_discovery.c) :

.. code-block:: c

void STM_EVAL_PBInit(Button_TypeDef Button, ButtonMode_TypeDef Button_Mode)
{
   GPIO_InitTypeDef GPIO_InitStructure;
   EXTI_InitTypeDef EXTI_InitStructure;
   NVIC_InitTypeDef NVIC_InitStructure;

   /* 要設定好Clock才能正確更新Button的State,BUTTON_CLK[Button]是指RCC_AHB1Periph_GPIOA */
   RCC_AHB1PeriphClockCmd(BUTTON_CLK[Button], ENABLE);
   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

   /* 設定為input mode,我們不給pin任何預設狀態,所PuPd的部份設成NOPULL */
   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
   GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
   GPIO_InitStructure.GPIO_Pin = BUTTON_PIN[Button];
   GPIO_Init(BUTTON_PORT[Button], &GPIO_InitStructure);

   if (Button_Mode == BUTTON_MODE_EXTI)
   {
     /* 啟用interrupt並建立好與button腳位的連結 */
     SYSCFG_EXTILineConfig(BUTTON_PORT_SOURCE[Button], BUTTON_PIN_SOURCE[Button]);

     /* Configure Button EXTI line */
     EXTI_InitStructure.EXTI_Line = BUTTON_EXTI_LINE[Button];
     EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
     EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;  
     EXTI_InitStructure.EXTI_LineCmd = ENABLE;
     EXTI_Init(&EXTI_InitStructure);

     /* Enable and set Button EXTI Interrupt to the lowest priority */
     NVIC_InitStructure.NVIC_IRQChannel = BUTTON_IRQn[Button];
     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

     NVIC_Init(&NVIC_InitStructure); 
   }
 }
  • Button interrupt handler(in main.c)

.. code-block:: c

void EXTI0_IRQHandler(void) 
{
  /* 當Button按下後觸發interrupt,用自定義變數紀錄Button state的改變。 */
  UserButtonPressed = 0x01;

  /* Clear the EXTI line pending bit */
  EXTI_ClearITPendingBit(USER_BUTTON_EXTI_LINE);
}

output 設定 :

  • LED initialization (in libstm/Utilities/STM32F4-Discovery/stm32f4_discovery.c) :

.. code-block:: c

void STM_EVAL_LEDInit(Led_TypeDef Led)
{
  GPIO_InitTypeDef  GPIO_InitStructure;

  /* LED是屬於Port D,要設定好Clk讓Port D可以正確被更新 */
  RCC_AHB1PeriphClockCmd(GPIO_CLK[Led], ENABLE);

  /* 設定各LED對應的腳位,output type設成push-pull,並使用pull-up電阻,LED初始化完成時,會呈現高電位狀態(亮燈)。 */
  GPIO_InitStructure.GPIO_Pin = GPIO_PIN[Led];
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIO_PORT[Led], &GPIO_InitStructure);
}

main.c :

  • main function :

.. code-block:: c

int main(void)
{
  RCC_ClocksTypeDef RCC_Clocks;

  /* button初始化,並使用interrupt。 */
  STM_EVAL_PBInit(BUTTON_USER, BUTTON_MODE_EXTI); 

  /* 初始化需要用到的LED。 */
  STM_EVAL_LEDInit(LED4);
  STM_EVAL_LEDInit(LED3);
  STM_EVAL_LEDInit(LED5);
  STM_EVAL_LEDInit(LED6);

  /* 初始化有使用到pull-up電阻,預設會亮燈,在這邊把燈關掉。 */
  STM_EVAL_LEDOff(LED4);
  STM_EVAL_LEDOff(LED3);
  STM_EVAL_LEDOff(LED5);
  STM_EVAL_LEDOff(LED6);

  /* 初始化自定義變數 */
  UserButtonPressed = 0x00;

  /* Create a task to flash the LED. */
  xTaskCreate(LED_task,
         (signed portCHAR *) "LED Flash",
         512 /* stack size */, NULL,
         tskIDLE_PRIORITY + 5, NULL);

  /* Create a task to button check. */
  xTaskCreate(button_task,
         (signed portCHAR *) "User Button",
         512 /* stack size */, NULL,
         tskIDLE_PRIORITY + 5, NULL);

  /* Start running the tasks. */
  vTaskStartScheduler(); 

  return 0;
}
  • LED control task

.. code-block:: c

static void LED_task(void *pvParameters)
{
  RCC_ClocksTypeDef RCC_Clocks;
  uint8_t togglecounter = 0x00;

  while(1)
  {    
      /* Toggle LED5 */
      STM_EVAL_LEDToggle(LED5);
      vTaskDelay(100);
      /* Toggle LED6 */
      STM_EVAL_LEDToggle(LED6);
      vTaskDelay(100);
  }
}
  • Button check task :

.. code-block:: c

static void button_task(void *pvParameters)
{
   	    while (1)
        {
            /* Waiting User Button is pressed */
	        if (UserButtonPressed == 0x01)
	        {
  	            /* Toggle LED4 */
  	            STM_EVAL_LEDToggle(LED4);
  	            vTaskDelay(100);
  	            /* Toggle LED3 */
  	            STM_EVAL_LEDToggle(LED3);
  	            vTaskDelay(100);
  	        }
  	        /* Waiting User Button is Released */
  	        while (STM_EVAL_PBGetState(BUTTON_USER) == Bit_SET);
  	        UserButtonPressed = 0x00;
  	   }
  }

Supplement

  • 一些有預設控制元件的腳位
    • LD3:綠色,連接到PG13
    • LD4:紅色,連接到PG14
    • B1(USER):連接到PA0
    • B2(RESET):連接到NRST,用於重置

Q & A

1. Pull-up與Pull-down的電阻是多少?

(請參考Datasheet p.110,Table 47. I/O static characteristics)

.. image:: /STM32F429_IOstatic.png

實際測量:

  • 將三用電表調到測量電阻的功能
  • 將三用電表的正極接在要測的I/O pin上(設成pull-down),負極接地
  • 觀察電表的數字即可得當前電阻

2. GPIO中取樣頻率的意義?

假設input data是0、1、0、1、……的方波,並以V0的速度變化,此時若GPIO本身的取樣頻率是1/2 V0,則取到的data會全是1或全是0,所以了解GPIO的取樣頻率可以讓使用者設計input data時不以過快的速度做資料的輸入。

取樣頻率可由GPIOx_OSPEEDRy[1:0]設定,各模式下的速度可參考datasheet p134 table 58. I/O AC Characteristics

3. 何謂TTL?

全名Transistor-Transistor Logic,數位IC的種類之一,依製程不同可分為TTL與CMOS(complement metal oxide semiconductor)兩種:

TTL:內部零件多以雙極性電晶體組成。

CMOS:內部零件多以金屬氧化物半導體組成。

(補充)差異性比較:

TTL電路的速度快,傳輸延遲時間短(5-10ns),但是功耗大。

CMOS電路的速度慢,傳輸延遲時間長(25-50ns),但功耗低。

4. STM32 RCC clock

http://www.oliverbehr.de/?option=com_content&view=article&id=52:stm32-clock-configuration&catid=39:technik&Itemid=83

System clock setup

  • Internal High Speed Clock (HSI)

  • External High Speed Clock (HSE)

  • Phase locked Loop (PLL)

Bus clock setup

  • Advanced High Performance Bus (AHB)

  • Low speed Advanced Peripherial Bus (APB1)

  • High speed Advanced Peripherial Bus (APB2)

5. 波形抖動問題

Sampling jitter [from Wikipedia]

http://en.wikipedia.org/wiki/Jitter

In analog to digital and digital to analog conversion of signals, the sampling is normally assumed to be periodic with a fixed period—the time between every two samples is the same. If there is jitter present on the clock signal to the analog-to-digital converter or a digital-to-analog converter, the time between samples varies and instantaneous signal error arises.

Reference

  • General Purpose Input/Output - Wikipedia, the free encyclopedia <http://en.wikipedia.org/wiki/General_Purpose_Input/Output>_
  • STM32F429xx Datasheet<http://www.st.com/web/en/resource/technical/document/datasheet/DM00071990.pdf>_
  • STM32F42xxx Reference Manual<http://www.st.com/web/en/resource/technical/document/reference_manual/DM00031020.pdf>_
  • STM32F429 pin mapping<http://mikrocontroller.bplaced.net/wordpress/wp-content/uploads/2013/10/Pinbelegung_f429_v100.html>_
  • STM32F4-Discovery 中文使用手冊</embedded/STM32-Discovery-Manual-Chinese.pdf>_
  • 稀里糊塗學 STM32 第二講:源源不絕</embedded/learn-stm32-part-2.pdf>_
  • GPIO PPT</embedded/GPIO_v3.ppt>_