1. 程式人生 > >STM32學習 3:GPIO管腳配置與第一個STM32實驗:LED燈閃爍

STM32學習 3:GPIO管腳配置與第一個STM32實驗:LED燈閃爍

STM32學習 3:GPIO管腳配置與第一個STM32實驗:LED燈閃爍

  鄙人是在淘寶購置了一套STM32開發板,跟著隨帶的光碟學習起來的。碰到不懂的就在CSDN上瀏覽學習高手的部落格,大神們不僅技術很好還樂於分享,蒙其惠澤,未感忘恩,所以自己開始寫部落格慢慢積累。
  如果您是初學者,看到該系列文章後有所幫助,鄙人深感榮幸。如果您是大神,請您指出不足指出。如果沒人看到,權當積累與記錄了。
  鄙人使用普中科技公司的開發板,在淘寶可以找到。晶片是STM32F103ZET6,韌體庫是V3.5,編譯環境是Keil uVision4。

  轉載請註明完整出處。


1,GPIO管腳簡介與配置

1.1GPIO框圖

  GOIO管腳是用來與外設進行訊號傳輸的,所以和時鐘一樣,屬於基本配置。下面是STM32的GPIO框圖。

  除了右邊的二極體和I/O埠,其餘左邊的部分都是在晶片裡面的,也就是開發板最中央的那個小處理器方塊裡面。

1.2GPIO不同模式

  總的輸入輸出模式可以分為以下幾種:

輸入模式:

  • 輸入浮空
  • 輸入上拉
  • 輸入下拉
  • 模擬輸入

輸出模式:

  • 開漏輸出
  • 開漏複用功能
  • 推輓式輸出
  • 推輓式複用功能

  下面結合框圖詳細介紹不同的輸入輸出模式。

輸入浮空

  圖示的藍框和紅框及其左邊部分都是在晶片裡面。電流從I/O埠進入後經過施密特觸發器會變成數字訊號儲存在輸入資料暫存器裡面。晶片內部讀取輸入資料暫存器就知道I/O口輸入了什麼資料。I/O口高電平輸入1,低電平輸入0。

輸入上拉

  相比於輸入浮空,可以看到輸入上拉在1和2之間多連線了一個上拉電阻。當I/O口沒有輸入的時候由於上拉電阻接VDD,故而相當於I/O是高電壓,只有當I/O是低電壓的時候才會輸入0,也就是說I/O口常1。

輸入下拉

  類比輸入上拉,輸入下拉是常0,只有當輸入高電壓的時候才會讀取到1。

模擬輸入

  模擬輸入訊號雖然進入了晶片但是晶片並不能直接將其轉化為數字訊號,而是送入了片上的外設模組,然後經過A/D轉換才變為數字訊號讀入。

開漏輸出

  從圖中可以看到輸出到I/O口之後,還要從輸入電路讀取輸出資料,作為反饋。3處,由於只連線了N-MOS管,所以平時輸出低電平,在外設處加上拉電阻可以輸出高電平,上拉電阻決定功耗和速度,可以方便實現線與。

開漏複用功能

  複用功能可以理解為GPIO口被用作第二功能時的配置情況(即並非作為通用IO口使用),可以在核心板原理圖裡面看到每一個GPIO口旁邊都有一個/符號,表示複用其他功能。

推輓式輸出

  推輓式輸出由於3處有兩個MOS管故可以輸出0可以輸出1。

推輓式複用功能

  訊號源為複用I/O口的輸出,可0,可1。

1.3 GPIO暫存器

  其中使用到的暫存器主要有以下幾個:

  • 兩個32位配置暫存器(GPIOx_CRL ,GPIOx_CRH)
  • 兩個32位資料暫存器 (GPIOx_IDR和GPIOx_ODR)
  • 一個32位置位/ 復位暫存器(GPIOx_BSRR)
  • 一個16位復位暫存器(GPIOx_BRR)
  • 一個32位鎖定暫存器(GPIOx_LCKR)
  • 每個I/O埠位可以自由程式設計,然而I/O埠暫存器必須按32位字被訪問(不允許半字或位元組訪問)

  我們重點關心GPIOx_CRL ,GPIOx_CRH和GPIOx_IDR、GPIOx_ODR。

埠配置低暫存器(GPIOx_CRL)

  鄙人的STM32有ABCDEFG共7組I/O口,每一組有16個I/O口埠。如暫存器所示,為了控制每一個管腳需要用到四位資料,那麼一組埠就需要16*4=64位。這樣就需要兩個32位暫存器去控制,所以就會分GPIOx_CRL與GPIOx_CRH,x表示GPIO管腳,分別控制低8個埠和高8個埠。如上圖所示,每一個埠中,MODEy控制埠y是輸出還是輸入以及輸出速度。可以設定速度是出於耗能與效能的考慮。CNFy控制y埠的輸入輸出模式。

埠輸入資料暫存器(GPIOx_IDR) 埠輸出資料暫存器(GPIOx_ODR)

  埠輸入資料暫存器和埠輸出資料暫存器是1.2中圖示的輸入資料暫存器和輸出資料暫存器,用來儲存輸入輸出資料。

1.4 GPIO庫函式配置

  如果每次使用GPIO口,就要一個一個去想該怎麼設定暫存器,這樣效率是很底的。TI官方有相應的庫函式,直接使用庫函式就可以很方便的對GPIO埠進行初始化。
  這個函式就是GPIO_Init(GPIO_TypeDefGPIOx,GPIO_InitTypeDef GPIO_InitStruct),可以看看《STM32韌體庫使用手冊》中是怎麼說明這個函式的。以下三張圖片選自韌體庫手冊的10.2節,為方便敘述在本小節稱為第一張,第二張和第三張。最好把這三張圖片認真看幾遍。

  從第一個圖片可以看到該函式的輸入有兩個,第一個是A,B,C…用來選擇作用在哪個GPIO上。第二個輸入是一個結構體指標GPIO_InitStruct,該指標指向結構體GPIO_InitTypeDef。
   它有3個成員變數,分別是
   u16 GPIO_Pin;//表示要選中哪個埠進行設定
  GPIOSpeed_TypeDef GPIO_Speed;//表示輸出速率
  GPIOMode_TypeDef GPIO_Mode;//表示GPIO模式

  總而言之在這個結構體裡面儲存了要把第一個輸入的GPIO口設定成什麼樣的資訊。為什麼要用指標而不是整體上把結構體當做第二個引數呢,這是為了嵌入式的效率。具體內容可以參考鄙人C與CPP分類中講解值傳遞、指標傳遞和引用傳遞的內容。

  第二張和第三張圖片介紹了怎麼去賦值這個結構體的成員變數(最後一個表格是GPIO_MODE,不是GPIO_SPEED,手冊在這裡有錯誤)。請注意第一張圖片的一個細節,GPIO_InitTypeDef定義於檔案“stm32f10x_gpio.h”。這就告訴我們庫檔案就是幹這個的,會幫助你提前設定好函式與變數,你只需要知道原理並且怎麼去使用它就行了。如果你深入該庫函式搞明白它是怎麼實現的然後自己搞了一套GPIO的埠配置方法,那樣雖然也可以用,但是並不能被其他人理解。所以用好TI官方給的庫檔案就OK了。

  好了,現在我們給出一個範例,假如我要把GPIO_A的1埠和2埠設定為最高輸出速率為50MHZ的推輓輸出模式,程式碼如下:

//先宣告一個GPIO_InitTypeDef的結構體變數
GPIO_InitTypeDef GPIO_InitStructure; 

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_2; //選擇1埠和2埠,賦值方式在圖2
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP; //設定推輓輸出,賦值方式見圖三
模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //設定傳輸速率,賦值方式見圖4

GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIO埠

2,LED燈閃爍實驗

  現在我們做第一個stm32實驗,讓開發板的LED燈亮滅交替進行。思路是控制連線8個LED燈的GPIO埠,使其交替輸出高低電平。

  先看看開發板原理圖上的硬體接線圖。

  可以看到8個LED燈是接在PC0到PC7共8個埠的。那麼我們要控制的就是C管腳的0到7這8個埠。輸出高電平熄滅,輸出低電平導通。

  開啟Keil uVersion4,將第一篇中建立的工程模板複製一份過來並在Keil中開啟它。在User資料夾下新建一個資料夾LED,在LED中新增Led.c和Led.h,並如下圖所示在Led.h中寫入程式碼。巨集定義是為了使用起來方便,在後面用的時候就明白了。以後每次創立了新的.h檔案都要這樣去寫。

  下面是寫在Led.c中的程式碼。

#include "led.h"

void LED_Init()	  //埠初始化
{
	GPIO_InitTypeDef GPIO_InitStructure; //宣告一個結構體變數,用來初始化GPIO

	SystemInit();	//時鐘初始化

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//配置GPIO的模式和IO口

	GPIO_InitStructure.GPIO_Pin=LED;  //選擇你要設定的IO口
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;	 //設定推輓輸出模式
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;	  //設定傳輸速率
	GPIO_Init(GPIOC,&GPIO_InitStructure); 	   // 初始化GPIO 
}

void delay(u32 i)	  
{
	while(i--);
}

void led_display()
{
	GPIO_SetBits(GPIOC,LED);
	delay(6000000);//延時約為1s
	GPIO_ResetBits(GPIOC,LED);
	delay(6000000);
}

  詳細說說這幾個函式。

  void Led_Init()是編寫的LED初始化函式,裡面將GPIO管腳配置為50MHZ的推輓輸出模式,這個在1.4中已經有了詳細說明。SystemInit();是系統時鐘初始化,具體定義在system_stm32f10x.c中可以找到。它的函式實現過程就不細說了,總體來說就是配置第二篇中講的各種暫存器然後使得系統時鐘為72MHZ,APB1是36MHZ,APB2是72MHZ。RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);用來使能APB2下的GPIOC埠。出於節能的考慮,用哪個埠使能哪個埠,不用的埠是沒有電的。

  delay(u32 i)是做延時用的,while(i–)就是讓CPU空跑。72MHZ下delay(6000000)大概就是1秒。顯然這種延時方式既浪費CPU資源又不夠精確,在下一章會講解定時器的使用,精確計時。

  led_display()是LED閃爍函式,GPIO_SetBits是將指定的GPIO埠置1,GPIO_ResetBits指定的GPIO埠置0。這樣就可以一秒滅一秒亮了。

  按照C語言的語法規則,函式宣告要新增在.h檔案裡面,所以最後要在LED.h裡面寫成如下程式碼。

#ifndef _led_H
#define _led_H
#include "stm32f10x.h"

#define LED GPIO_Pin_All	//管腳巨集定義

void LED_Init(void);
void led_display(void);

#endif 

  在public.h中include剛才寫好的Led.h檔案。這樣在主函式中就可以呼叫LED_Init()和led_display()兩個函數了。

#ifndef _public_H
#define _public_H

#include "stm32f10x.h"
#include "Led.h"

#endif

  在main.c中這樣寫。

#include "public.h"
int main()
{
	LED_Init();		//LED埠及時鐘初始化  
	while(1)
	{
		led_display(); //led顯示			
	}
}

  儲存修改過的檔案後build一下編譯通過,0 error ,0 warning。如果有錯誤,可以仔細看看錯誤在哪裡,多半是單詞拼錯了或者字元間多了空格之類的。有警告是因為沒有將游標打到檔案的最後一行之後。下面說說怎麼把程式送到STM32裡面跑起來。


3 燒錄程式

  Buid通過後會在工程目錄Output資料夾下生成一個與工程名同名的字尾為.hex的檔案,如下圖所示。它就是我們要燒錄的目標檔案。

  燒錄檔案需要燒錄軟體,鄙人使用的是“普中自動下載軟體”。開啟後如圖所示。該軟體的下載連結在這裡。

  https://download.csdn.net/download/huagengpai1994/10843205

  選擇晶片型別為STM32F10XX seri。當把STM32插入電腦後上電串列埠號會自動出來。選擇波特率是19200,波特率和串列埠有關,我們這是通過串列埠的方式把檔案燒錄進stm32中,這些內容以後再說。然後在檔案路徑中點選開啟檔案,找到Output下的工程模板.HEX(我的工程叫工程模板,您的工程叫什麼就生成同名HEX檔案)。最後點選程式下載,就會顯示Program download success!表示下載成功。是不是看到了STM32上LED燈開始閃爍了,哈哈。程式只要下載進去STM32就會自動執行main函式的。

  值得注意的是,該燒錄軟體還具備簡單串列埠除錯的功能,以後也許會用到。

  至此,我們成功完成了STM32的第一個LED燈閃爍實驗。下一章繼續用本章的知識去完成一個按鍵與數碼管的實驗。