stm32學習(一)


公司開發需要用到stm32,nxp,gd32等系列的單片機,大學幾乎沒接觸過單片機的學習,所以只能自己下來慢慢學,如有錯誤,歡迎指出。

ARM(公司,處理器,架構)

ARM處理器家族

  • 經典:ARM7,ARM9,ARM11
  • Cortex-M:單片機驅動(控制)
  • Cortex-A:開發式系統的高性能處理器(智能手機 ,數字電視)
  • Cortex-R:實時系統,實時的控制需求(汽車制動,動力系統)

 

想要操作stm32的外設通常可以使用寄存器或者stm32庫,在當今stm32庫已經十分成熟,並且操作十分簡單。

寄存器:寄存器則是內置於各個 IP 外設中,是一種用於配置外設功能的存儲器,就是一種內存,並且有相對應的地址。

 

GPIO:通用輸入輸出接口

按鍵:通過輸入電平的高低感知來得到按鍵的按下與否。

led燈:通過輸出高低電平控制led的亮與滅。

GPIO也可以作為串口數據的收發管腳或AD接口等復用功能,根據數據手冊配置相應的功能寄存器。

 

GPIO寄存器MODER

32位兩位為一組,分為了16個,分別對應每一組GPIO的16個 管腳。如PA0-PA15。

 

點燈:學習嵌入式開發最開始的操作就是點燈,以下使用寄存器操作和庫函數操作來點燈。

寄存器:從技術參考手冊中可以找到ODR 寄存器的地址偏移是: 0CH,這個偏移地址是基於端口的起始地址而言的。在 STM32 中,每個外設都有一個起始地址,叫做外設基址,外設的寄存器就以這個基地址為標准按照順序排列,跟結構體里面的成員差不多。

假設我們需要點亮的燈是由GPIOB-0這個管腳去控制的,找到GPIOB對應的基地址,再加上ODR的偏移地址就可以去實現輸出功能。

 

 

其中 GPIOB 的起始地址是: 0X4001 0C00,這樣就可以算出 GPIOB_ODR 寄存器的地址是: 0X4001 0C00 + 0X0C = 0X4001 0C0C。現在我們就可以定義 GPIOB_ODR 這個寄存
器了:
 
#define     GPIOB_ODR     *(volatile unsigned long *)0x40010C0C
有了這個寄存器定義,我們就可以直接操作 GPIOB_ODR 了。雖然配置了 ODR 寄存器,但是這個時候還不能點亮 LED ,因為 STM32 的 IO 口還要配置方向,這個由端口配置寄存器來控制。端口配置寄存器分為高低兩個,每 4bit 控制一個 IO 口,所以端口配置低寄存器: CRL 控制這 IO 口的低 8 位,端口配置高寄存器: CRH控制這 IO 口的高 8位。在 4 位一組的控制位中, CNFy[1:0] 用來控制端口的輸入輸出,MODEy[1:0] 用來控制輸出模式的速率,即輸出時, IO 電平翻轉的速度。
輸入有三種模式,輸出有 4 中模式,我們在控制 LED 的時候選擇通用推挽輸出。
輸出速率有三種模式: 2M、10M 、50M,這里我們選擇 2M。 
 
同 GPIOB_ODR 一樣,我們也可以算出 GPIO_CRL 的地址為: 0x40010C00。那么設置PB0 為通用推挽輸出,輸出速率為 2M 的代碼則如下所示:
#define GPIOB_CRL *(volatile unsigned long *)0x40010C00
// 配置 PB0 為通用推挽輸出,輸出速率為 2M
GPIOB_CRL = (2<<0) | (0<<2); 

 

當我們設置了 IO 口的方向,並在相應的輸出寄存器里面輸入了值的時候,以為現在總算可以點亮 LED 了吧,其實還差最后一步。
STM32 外設很多,為了降低功耗,每個外設都對應着一個時鍾,在系統復位的時候這些時鍾都是被關閉的,如果想要外設工作,必須把相應的時鍾打開。
STM32 的所有外設的時鍾由一個專門的外設來管理,叫 RCC(reset and clockcontrol ), RCC 在 STM32 中文參考手冊的第六章。 
 
STM32 的外設因為速率的不同,分別掛載到三條總系上: AHB 、APB2 、APB1 ,APB為高速總線, APB2 次之, APB1 再次之。
所有的 IO 口都掛載到 APB2 總線上,屬於高速外設。時鍾由 APB2 外設時鍾使能寄存器 (RCC_APB2ENR) 來控制,其中 PB 端口的時鍾由該寄存器的位 3 寫 1 使能。
 
同 ODR 和 CRL,我們可以算出 RCC_APB2ENR 的地址為: 0x40021018。那么使能PB 口的時鍾代碼則如下所示:
#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
// 開啟端口 B 時鍾
RCC_APB2ENR |= 1<<3;
我們雖然開了端口時鍾,那這個時鍾到底是多大?時鍾到底是從哪里來的?
如果我們用的是庫,那么有個庫函數 SystemInit ,會幫我們把系統時鍾設置成 72M 。現在我們沒有使用庫,那現在時鍾是多少?
答案是 8M,當外部 HSE 沒有開啟或者出現故障的時候,系統時鍾由內部低速時鍾 LSI 提供,現在我們是沒有開啟 HSE,所以系統默認的時鍾是 LSI=8M 。
如果你想自己先嘗鮮,那么看 RCC 外設中的:時鍾控制寄存器 (RCC_CR) 和時鍾配置寄存器(RCC_CFGR) 這兩個寄存器即可。
 
控制了電平,配置了方向,開啟了時鍾,經過這三步,我們總算可以控制一個 LED了。
現在我們完整組織下用 STM32 控制一個 LED 的代碼:
#define RCC_APB2ENR *(volatile unsigned long *)0x40021018
#define GPIOB_CRL *(volatile unsigned long *)0x40010C00
#define GPIOB_ODR *(volatile unsigned long *)0x40010C0C
int main( void )
{ // 開啟端口 B 的時鍾
RCC_APB2ENR |= 1<<3;

// 配置 PB0 為通用推挽輸出模式,速率為 2M
GPIOB_CRL = (2<<0) | (0<<2);

// PB0 輸出低電平,點亮 LED
GPIOB_ODR = 0<<0;
}

void SystemInit( void )
{

}

這種就是比較愚蠢的配置方式,每次配置一個外設就去從參考手冊中去找,浪費時間且容易出錯,但新手入門是需要理解並且操作學習一下的。

如果要以寄存器配置的話,會將所有的寄存器位置都先定義好,這樣就不用一個個去找。實際開發過程中很少使用寄存器操作,一般都是庫函數的操作,下面介紹庫的操作。

 

keilstm32環境自行去百度配置,篇幅太長就不寫出了。

固件庫分析:看看每個文件的作用是什么,這對我們能否清晰的調用庫函數編程非常重要。

STM32 Cortex-M3 內核和內核之外的各種外設組成,庫在編寫的時候也遵循這中組成結構,把代碼分成兩大部分,一種是操作內核外設的,

另外一種是內核之外的外設,為了聽起來不那么繞,下面我們把內核之外的外設用處理器外設來代替。

  • startup_stm32f10x_hd.s:
    這個是由匯編編寫的啟動文件,是 STM32 上電啟動的第一個程序,啟動文件主要實現了:1、初始化堆棧指針SP;2、設置 PC 指針 =Reset_Handler ;3、設置向量表的地址,並初始化向量表,向量表里面放的是 STM32 所有中斷函數的入口地址4、調用庫函數SystemInit ,把系統時鍾配置成 72M ,SystemInit 在庫文件 stytem_stm32f10x.c 中定義;5、跳轉到標號 _main,最終去到 C 的世界。
  • system_stm32f10x.c:
    這個文件的作用是里面實現了各種常用的系統時鍾設置函數,有 72M,56M, 48,36,24,8M,我們使用的是把系統時鍾設置成 72M。
  • stm32f10x.h:
    這個頭文件非常重要,可以說是上帝之手。這個頭文件實現了: 1、處理器外設寄存器的結構體定義 2、處理器外設的內存映射 3、處理器外設寄存器的位定義。
  • stm32f10x_xxx.h:
    外設 xxx 應用函數庫頭文件,這里面主要定義了實現外設某一功能的結構體,比如通用定時器有很多功能,有定時功能,有輸出比較功能,有輸入捕捉功能,而通用定時器有非常多的寄存器要實現某一個功能,比如定時功能,我們根本不知道具體要操作哪些寄存器,這個頭文件就為我們打包好了要實現某一個功能的寄存器,是以結構體的形式定義的,比如通用定時器要實現一個定時的功能,我們只需要初始化TIM_TimeBaseInitTypeDef 這個結構體里面的成員即可,里面的成員就是定時所需要操作的寄存器。 有了這個頭文件,我們就知道要實現某個功能需要操作哪些寄存器,然后再回手冊中精度這些寄存器的說明即可。
  • stm32f10x_xxx.c :
    外設 xxx 應用函數庫,這里面寫好了操作 xxx 外設的所有常用的函數,我們使用庫編程的時候,使用的最多的就是這里的函數。
  • cor_cm3.h:
    這個頭文件實現了: 1、內核結構體寄存器定義 2、內核寄存器內存映射 3、內存寄存器位定義。跟處理器相關的頭文件 stm32f10x.h 實現的功能一樣,一個是針對內核的寄存器,一個是針對內核之外,即處理器的寄存器。
  • misc.c:
    內核應用函數庫文件,對應 stm32f10x_xxx.c 。在 CM3 這個內核里面還有一些功能組件,如 NVIC、SCB、ITM 、MPU、CoreDebug ,CM3 帶有非常豐富的功能組件,但是芯片廠商在設計 MCU 的時候有一些並不是非要不可的,是可裁剪的,比如 MPU、ITM 等在STM32 里面就沒有。其中 NVIC 在每一個 CM3 內核的單片機中都會有,但都會被裁剪,只能是 CM3 NVIC 的一個子集。在 NVIC 里面還有一個 SysTick,是一個系統定時器,可以提供時基,一般為操作系統定時器所用。
  • misc.h:
    內核應用函數庫頭文件,對應 stm32f10x_xxx.h 。

 

直接寫庫函數操作了:

void LED_GPIO_Config(void)
{        
    /*定義一個GPIO_InitTypeDef類型的結構體*/
    GPIO_InitTypeDef GPIO_InitStructure;

    /*開啟GPIOC的外設時鍾*/
    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); 

    /*選擇要控制的GPIOB引腳*/                                                               
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    

    /*設置引腳模式為通用推挽輸出*/
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   

    /*設置引腳速率為50MHz */   
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; 

    /*調用庫函數,初始化GPIOB*/
      GPIO_Init(GPIOB, &GPIO_InitStructure);          

    /* 關閉led燈    */
    GPIO_SetBits(GPIOB, GPIO_Pin_0);     

    /* 開啟led燈    */
  GPIO_ResetBits(GPIOB, GPIO_Pin_0);
}

 

typedef struct
{
uint16_t GPIO_Pin; 
/*指定將要進行配置的 GPIO 引腳*

GPIOSpeed_TypeDef GPIO_Speed;    
/*指定 GPIO 引腳可輸出的最高頻率*/

GPIOMode_TypeDef GPIO_Mode;    
/*指定 GPIO 引腳將要配置成的工作狀態*/

}GPIO_InitTypeDef;

Mode和Speed相關結構體也是被設定好的,可以自行查找這里就不寫出來了,直接賦值就可以了,十分方便並且相對於寄存器操作便於理解。

GPIO_Init(GPIO_TypeDef *, GPIO_InitTypeDef *);第一個參數,說明它將要對GPIOB 端口進行初始化。初始化的配置以第二個參數 GPIO_InitStructure 結構 體的成員值為准。這個結構體的成員,我們在調用GPIO_Init()前,已對它們 賦予了控制參數。

 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM