第4章-STM32最小系統與嵌入式C語言課件_第1頁
第4章-STM32最小系統與嵌入式C語言課件_第2頁
第4章-STM32最小系統與嵌入式C語言課件_第3頁
第4章-STM32最小系統與嵌入式C語言課件_第4頁
第4章-STM32最小系統與嵌入式C語言課件_第5頁
已閱讀5頁,還剩75頁未讀 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

第4章STM32最小系統

與嵌入式C語言STM32本章知識與能力要求掌握STM32最小系統設計;理解和掌握嵌入式C語言的程序結構;掌握嵌入式C語言的數據類型、const、volatile和extern等關鍵字的使用;掌握嵌入式C語言的條件編譯;掌握嵌入式C語言指針的應用;學會分析HAL庫文件源碼。4.1STM32最小系統4.2嵌入式C語言4.3HAL庫文件源碼分析第4章STM32最小系統與嵌入式C語言4.1STM32最小系統4.1.1電源電路4.1.2時鐘電路(晶振電路)4.1.3復位電路4.1.4調試和下載電路03040102用最少的元件組成單片機可以工作的系統4.1.1電源電路微控制器工作電壓3.4V~5.5V或2.7V~3.6V為系統提供穩定可靠的供電STM32F103系列:2.0V~3.6V電源電壓5VAMS1117系列芯片1.5V1.8V2.5V2.85V3.0V3.3V5.0V供電4.1.1電源電路C1、C3為輸入電容,作用是防止斷電后出現電壓倒置C2、C4為輸出濾波電容,作用是抑制自激振蕩和穩定輸出電壓AMS1117-3.3v降壓電路圖電壓轉換電路提供3.3V電壓供STM32主電源VDD使用4.1.1電源電路STM32內部包含一個電壓調節器,將外部3.3V電壓轉換為1.8V提供給Cortex-M3內核、內存以及外設使用。STM32內部電源框圖內部有兩個可選電源選項:ADC模塊所需的參考電壓VREF+和VREF-。常采用獨立的供電電源。。備用電池電源VBAT。當主電源VDD掉電后,通過VBAT引腳為實時時鐘和備份寄存器提供電源。通常選擇備用電池作為該電源,電壓范圍為1.8~3.6V。若不使用備用電池供電,則VBAT引腳必須與主電源引腳VDD連接。4.1.2時鐘電路(晶振電路)

時鐘系統產生供系統正常工作的穩定的脈沖信號.

單片機的一切指令的執行都依賴于晶振提供的時鐘頻率,時鐘頻率越高,單片機運行速度越快,功耗也就越大。4.1.2時鐘電路(晶振電路)晶振有源晶振(振蕩器)無源晶振(晶體)內部包含晶體管和阻容元件一個完整的諧振振蕩器只需要給它供電不需要外接其他元件通常有四個引腳:VCC、GND、晶振輸出引腳和一個懸空的引腳。只有2個引腳的無極性元件需要借助外部電路起振精度上差一些價格便宜4.1.2時鐘電路(晶振電路)使用低速晶振電路作為系統低速外部時鐘源,頻率為32.768kHz,主要用作RTC的時鐘源,用于精準計時定時電路、萬年歷等。

使用外部高速晶振電路作為系統時鐘源,一般稱為高速外部時鐘信號(HSE)。一般選擇8MHz,方便倍頻。01024.1.3復位電路復位電路將系統進行復位STM32內部復位電路STM32芯片復位管腳持續為低電平時復位,STM32的NRST引腳在內部已經連接了一個上拉電阻,數據手冊建議復位電路需外接一個對地電容,如果認為這個上拉電阻較小,用戶也可以在復位電路中外接一個上拉電阻4.1.3復位電路按鍵S1按下時,RESET和地連通,產生低電平,實現復位。上電復位:

在上電瞬間,由于電容來兩端電壓不能突變,RESET出現短暫低電平,芯片自動復位,之后電容充電,充電時間由電阻和電容共同決定:上電后約1ms左右系統完成復位,之后,單片機開始正常工作。手動復位:4.1.4調試和下載電路

將編譯好的程序下載到單片機中運行以及進行在線調試STM32F103系列微控制器內核集成了SWD/JTAG調試端口(縮寫為SWJ-DP)JTAG調試接口(5引腳的JTAG-DP)SWD(SerialWireDebug,串行單總線調試)(2引腳的SW-DP)作用SWJ-DP引腳名稱JTAG-DPSW-DP引腳號JTMS/SWDIO輸入:JTAG模式選擇輸入輸出:串行數據輸入輸出PA13JTCK/SWCLK輸入:JTAG時鐘輸入:串行時鐘PA14JTDI輸入:JTAG數據輸入

PA15JTDO/TRACESWO輸出:JTAG數據輸出異步跟蹤PB3JNTRST輸入:JTAG模塊復位

PB4STM32調試端口引腳功能4.1.4調試和下載電路Cortex-M3內核有三種啟動方式,可以通過BOOT0和BOOT1的電平組合進行選擇。內核的啟動模式啟動模式選擇引腳啟動模式對應的存儲介質說明BOOT0BOOT10x閃存存儲器,即用戶閃存Flash閃存存儲器被選為啟動區域10系統存儲器,即系統Flash系統存儲器被選為啟動區域11內置SRAM內置SRAM被選為啟動區域在STM32F103系列微控制器中通過設置BOOT[1:0]引腳電平的高低選擇三種不同啟動模式,從而將STM32F103微控制器的存儲空間起始地址0x00000000映射到不同存儲區域的起始地址。4.1.4調試和下載電路保持BOOT1為“0”,當BOOT0=1時,STM32F103系列微控制器從系統存儲器的起始地址0x1FFFF000開始啟動;當BOOT0=0時,STM32F103系列微控制器從用戶Flash的起始地址0x80000000開始啟動運行代碼,這是STM32最常用的啟動方式。若要從片內SRAM起始地址0x20000000啟動,則BOOT0、BOOT1均設為“1”。內核的啟動模式4.2嵌入式C語言4.2.1STM32的數據類型4.2.2const關鍵字4.2.3static關鍵字4.2.4volatile關鍵字4.2.5extern關鍵字4.2.8

typedef4.2.9

#define4.2.10#ifdef、#if條件編譯4.2.11指針4.2.12回調函數4.2.7

enum4.2.6struct結構體4.2嵌入式C語言

嵌入式開發中既有底層硬件的開發又涉及上層應用的開發,即涉及系統的硬件和軟件,C語言既具有匯編語言操作底層的優勢,又具有高級語言功能性強的特點。1程序總是從main函數開始執行;2345函數是C語言的基本結構;函數由兩部分組成:函數說明部分和函數體。

一個C語言程序包含若干個源程序文件(.c文件)和頭文件(.h文件)。采用外設功能模塊化設計方法,一個外設功能模塊包括一個源文件(.c文件)和一個頭文件(.h文件)。4.2嵌入式C語言嵌入式系統開發多采用模塊化、層次化的設計思想,系統層次架構清晰,便于協同開發。嵌入式系統軟件結構框圖4.2.1STM32的數據類型

數據是嵌入式C語言的基本操作對象。數據類型是指數據在計算機內存中的存儲方式。嵌入式C語言的數據類型4.2.1STM32的數據類型不同CPU所定義的數據類型的長度不一致,ST公司為開發人員提供了基于C語言的標準外設庫,標準外設庫中定義的數據類型的長度如下表所示。C語言的數據類型STM32對應的數據類型說明unsignedcharuint8_t8位無符號數據(0~255)unsignedshortintuint16_t16位無符號數據(0~65535)unsignedintuint32_t32位無符號數據(0~232-1)unsignedlonglonguint64_t64位無符號數據(0~264-1)signedcharint8_t8位有符號數據(-128~+127)signedshortintint16_t16位有符號數據(-32768~+32767)signedintint32_t32位有符號數據(-231~231-1)signedlonglongint64_t64位有符號數據(-263~263-1)4.2.1STM32的數據類型v3.5.0版本已不再使用舊的數據類型,為了兼容以前的版本,stm32f10x.h頭文件還對標準外設庫之前版本所使用的數據類型進行了說明。

STM32開發中同一數據類型有多種表示方式。無符號8位整型數據有unsignedchar,uint8_t,u8三種表示方式。例

最新的v3.5.0版本采用標準的C99標準,即uint8_t方式。4.2.1STM32的數據類型__I、__O以及__IO為IO類型限定詞,內核頭文件core_cm3.h定義了標準外設庫所使用的IO類型限定詞。數據類型和IO類型限定詞結合在一起,在標準外設庫中常用來定義寄存器和結構體變量:IO類型限定詞類型說明__Ivolatileconst只讀操作__Ovolatile只寫操作__IOVolatile讀和寫操作4.2.1STM32的數據類型

stm32f10x.h頭文件中常用的布爾類型的變量的定義typedefenum{DISABLE=0,ENABLE=!DISABLE}FunctionalState;typedefenum{RESET=0,

SET=!RESET}FlagStatus,ITStatus;typedef

enum{

ERROR=0,SUCCESS=!ERROR}ErrorStatus;RESET與SETDISABLE與ENABLEERROR與SUCCESS#defineIS_FUNCTIONAL_STATE(STATE)(((STATE)==DISABLE)||((STATE)==ENABLE))4.2.2const關鍵字const作用用于定義只讀的變量,其值在編譯時不能被改變const常量類型常量名=常量表達式;格式為了在編譯時防止變量的值被誤修改,提高程序的安全性和可靠性。目的要求屬性

const關鍵詞修飾的變量在聲明時必須初始化在C標準中,const定義的變量是全局的。4.2.3static關鍵詞修飾變量或函數。修飾后的變量稱為靜態變量。作用

在全局變量之前加上關鍵字static,則該全局變量被定義成為一個靜態全局變量。

作用范圍只在定義該變量的源文件內有效,其他源文件不能引用該全局變量,避免了在其他源文件中因引用相同名字的變量而引發錯誤,有利于模塊化程序設計。操作目的4.2.3static關鍵詞#include"stm32f1xx_hal.h"staticvoidDMA_SetConfig(DMA_HandleTypeDef*hdma,uint32_tSrcAddress,uint32_tDstAddress,uint32_tDataLength);……HAL_StatusTypeDefHAL_DMA_Start_IT(DMA_HandleTypeDef*hdma,uint32_tSrcAddress,uint32_tDstAddress,uint32_tDataLength){HAL_StatusTypeDefstatus=HAL_OK;……if(HAL_DMA_STATE_READY==hdma->State){DMA_SetConfig(hdma,SrcAddress,DstAddress,DataLength);……}……}static編程要點1:

模塊化的程序設計中,用static聲明一個函數,則該函數只能被該模塊內的其它函數調用。

解析:DMA_SetConfig()函數只能被stm32f1xx_hal_dma.c的其它函數調用,不能被其它模塊的文件使用。4.2.3static關鍵詞voidfun_count(){

staticcount_num=0;

//聲明一個靜態局部變量,count_num用作計數器,初值為0。count_num++;printf("%d\n",count_num);}intmain(void){inti=0;for(i=0;i<=5;i++){fun_count();}return0;}static編程要點2:

static除了用于靜態全局變量,還用于定義靜態局部變量,保證靜態局部變量在調用過程中不被重新初始化。

解析:在main函數中每調用一次fun_count()函數,則靜態局部變量count_num加1,而不是每次都被初始化為初值0。4.2.4volatile關鍵字一個類型修飾符,“易變的”。

volatilechari;使用volatile關鍵字定義了一個字符型的變量i,指出i是隨時可能發生變化的,每次使用的時候都必須從i的地址中讀取。

使用volatile就是不讓編譯器進行優化,即每次讀取或者修改值的時候,都必須重新從內存中讀取或者修改,而不是使用保存在寄存器里的備份。中斷服務程序中修改的供其他程序檢測的變量需要使用volatile;多任務環境下各任務間共享的標志應添加volatile;存儲器映射的硬件寄存器通常也要加volatile進行說明。作用使用方式應用場合4.2.5extern關鍵詞externinta;指明此函數或變量的定義在別的文件中,提示編譯器遇到此函數或變量時去其他模塊中尋找其定義。作用使用extern是一個聲明而不是重新定義聲明變量a,而不是在定義變量a,并未為a分配內存空間聲明函數funA(),此函數已在其他文件中定義。extern"C"進行鏈接指定,告知編譯器這是采用C語言定義的函數externintfunA();4.2.5extern關鍵詞#ifdef__cplusplusextern"C"{#endif……#ifdef__cplusplus}#endif

解析:如果定義了__cplusplus(C++編譯器中自定義的宏),則執行extern“C”{語句。C++支持函數重載,而C語言不支持函數重載,在C++環境下使用C函數會出現鏈接時找不到對應函數的情況,這時需要使用extern“C”進行鏈接指定,告知編譯器使用C語言的命名規則來處理函數。#ifdef__cplusplusextern"C"{#endif//函數聲明#ifdef__cplusplus}#endif

當函數有可能被C語言或C++使用時,將函數聲明放在extern“C”中以免出現編譯錯誤。4.2.5extern關鍵詞為保證全局變量和功能函數的使用,extern一般用在.h頭文件中對某個模塊提供給其它模塊調用的外部函數及變量進行聲明,實際編程中只需要將該.h頭文件包含進該模塊對應的.c文件,即在該模塊的.c文件中加入代碼#include"xxx.h"。ADCx.h//聲明全局變量externuint16_tADC_ConvertedValue;//存放ADC的轉換結果……//外部功能函數聲明externvoidADC_Init(void);//ADC初始化函數……ADCx.c#include"ADCx.h"……//定義變量staticuint16_tpwd=1;//局部變量,僅作用于本函數uint16_tADC_ConvertedValue;//全局變量……voidADC_Init(void){……}……4.2.6struct結構體舉例作用格式struct用于定義結構體類型,其作用是將不同數據類型的數據組合在一起,構造出一個新的數據類型。struct一般用法為:struct[結構體名]{

類型標識符成員名1;類型標識符成員名2;

……}結構體變量;structperson{charname[8];intage;charsex[8];charaddress[20];}person_liu;4.2.6struct結構體structperson{charname[8];intage;charsex[8];charaddress[20];};structpersonperson_liu,person_zhang;結構體變量(如上例中的person_liu)可以不在定義結構體時定義,后續需要時再進行定義。

使用這種定義方式可以很方便地同時定義多個結構體變量。結構體變量的使用采用如下形式:

結構體變量名.成員名如:person_liu.age=35;struct結構體名結構體變量;定義格式4.2.7enum關鍵詞enumWeekdays{Monday=1,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday}Mydays,Olddays;enum枚舉名{枚舉成員1,枚舉成員2,……}枚舉變量;用來將一個變量或對象的所有可能的值一一列出,變量取值只限于列舉出來的值。enum枚舉類型的用法:枚舉成員的值是常量,不是變量,不能被賦值,但可以將枚舉值賦給枚舉變量。枚舉類型具有自動編號的功能,第一個枚舉成員其默認值為整型的0,后續枚舉成員的值在前一個成員上加1。可以自定義枚舉成員的值,如果把第一個枚舉元素的值定義為1,那么第二枚舉成員的值就為2,以此類推,如上述例子中Friday的值為5。4.2.8typedef給變量起一個容易記且意義明確的新名字;簡化一些比較復雜的類型聲明。目的:用來為復雜的聲明定義一個簡單的別名,方便記憶作用:不是一個真正意義上的新類型用法一:typedef的基本應用typedef類型名

自定義的別名;格式:

typedefsignedcharint8_t;//給數據類型signedchar起個別名int8_ttypedefsignedintint32_t;//給數據類型signedint起個別名int32_t舉例:為已知的數據類型起一個簡單的別名4.2.8typedeftypedefstruct{

uint16_tGPIO_Pin;

GPIOSpeed_TypeDefGPIO_Speed;

GPIOMode_TypeDefGPIO_Mode;}GPIO_InitTypeDef;STM32標準外設庫中stm32f10x_gpio.h頭文件中利用結構體別名GPIO_InitTypeDef使用typedef為這個新建的結構體起了一個新的名字叫GPIO_InitTypeDef則可以使用GPIO_InitTypeDef定義一個變量GPIO_InitStructure,從而調用GPIO_Mode。GPIO_InitTypeDefGPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;用法二:與結構體struct結合使用4.2.8typedefstm32f10x_gpio.h頭文件中的代碼。typedefenum{ GPIO_Speed_10MHz=1, GPIO_Speed_2MHz, GPIO_Speed_50MHz}GPIOSpeed_TypeDef;

解析1:利用typedef關鍵字將此枚舉類型定義一個別名GPIOSpeed_TypeDef,這里省略了枚舉類型的枚舉名,只用typedef起了個別名。用法三:與enum結合使用解析2:enum枚舉類型共三個成員,并將第一個枚舉成員GPIO_Speed_10MHz賦值為1,enum枚舉類型會將枚舉成員的賦值在第一個枚舉成員賦值的基礎上加1,因此,GPIO_Speed_2MHz默認值為2。4.2.9#definedefine#define是C語言中的預處理命令,它用于宏定義,用來將一個標識符定義為一個字符串,該標識符稱為宏名,被定義的字符串稱為替換文本。所謂預處理是指在編譯之前所做的工作,由預處理程序負責完成,編譯時,系統將自動引用預處理程序對源程序中的預處理部分進行處理。采用宏定義的目的主要是方便程序編寫,一般放在源文件的前面,稱為預處理部分。typedef與#define的區別:typedef是在編譯階段處理的;#define是在預處理階段處理的。4.2.9#define用法一:無參數宏定義例1:#defineUINT8_MAX255解析:定義宏名UINT8_MAX,代表255。例2:#define__IOvolatile;解析:定義宏名__IO,表示volatile,以后程序中再需要用到volatile,就可以使用__IO。例3:#defineRCC_AHBPeriph_DMA1((uint32_t)0x00000001)解析:定義RCC_AHBPeriph_DMA1宏名,代表32位的無符號數據0x00000001。定義格式:

#define<宏名><字符串>所定義的宏名可以是常數、字符串、表達式等4.2.9#define用法一:無參數宏定義標準外設庫v3.5.0的stm32f10x_rcc.h文件中APB2_peripheral外設基地址的定義APB2_peripheral各外設基地址的定義4.2.9#define用法二:帶參數的宏定義例4:#defineSUM(x,y)(x+y)……a=SUM(2,2);解析:將SUM(x,y)定義為x+y,預編譯時會將SUM(x,y)替換為x+y,a的結果是4例5:#defineIS_GPIO_SPEED(SPEED)(((SPEED)==GPIO_Speed_10MHz)||((SPEED)==GPIO_Speed_2MHz)||((SPEED)==GPIO_Speed_50MHz))解析:使用宏定義#define將IS_GPIO_SPEED(SPEED)替換為GPIO_Speed_10MHz或者GPIO_Speed_2MHz或者GPIO_Speed_50MHz。定義格式:#define<宏名>(參數1,參數2,…參數n)<替換列表>4.2.9#define用法二:帶參數的宏定義voidRCC_APB2PeriphClockCmd(uint32_tRCC_APB2Periph,FunctionalStateNewState){

/*Checktheparameters*/assert_param(IS_RCC_APB2_PERIPH(RCC_APB2Periph));assert_param(IS_FUNCTIONAL_STATE(NewState));if(NewState!=DISABLE){RCC->APB2ENR|=RCC_APB2Periph;}else{RCC->APB2ENR&=~RCC_APB2Periph;}}STM32標準外設庫stm32f10x.h頭文件中的代碼:解析:該函數為外設時鐘使能函數,第一個參數為要使能的外設,第二個參數為是否使能。#defineIS_FUNCTIONAL_STATE(STATE)(((STATE)==DISABLE)||((STATE)==ENABLE))在stm32f10x_rcc.c源文件中函數RCC_APB2PeriphClockCmd()使用了IS_FUNCTIONAL_STATE(STATE)這個宏

RCC_APB2PeriphClockCm()源碼如下:4.2.10#ifdef、#ifndef、#else、#if條件編譯只有滿足一定條件才進行編譯,一般用在頭文件或文件開頭部分嵌入式C語言常使用條件編譯,通過條件判斷來確定是否對某段源程序進行編譯。

條件編譯的用途:可以用源程序產生不同版本。條件編譯說明#define宏定義#undef撤銷已定義的宏名#if條件編譯命令,如果#if后面的表達式為true,則執行語句#ifdef判斷某個宏是否被定義,若被定義,則執行語句#ifndef判斷某個宏是否未被定義,若未被定義,則執行語句,與#ifdef相反#elif#else指令用于#if指令之后,當#if指令的條件不為真時,就編譯#else后面的代碼,elif相當于elseif#endif條件編譯的結束命令,用在#if、#ifdef、#ifndef之后嵌入式C語言常用的條件編譯命令4.2.10#ifdef、#ifndef、#else、#if條件編譯形式一:#ifdef標識符

程序段1#else

程序段2#endif功能:當指定的標識符已被#define定義過,則只編譯程序段1,否則編譯程序段2。#ifdefIN_XXX#defineXXX_EXT#else#defineXXX_EXTextern#endif…….XXX_EXTvolatileu16Name;如果定義了IN_XXX,則定義XXX_EXT,否則定義XXX_EXT為extern。4.2.10#ifdef、#ifndef、#else、#if條件編譯形式二:#ifndef

標識符程序段1#else

程序段2#endif功能:當指定的標識符沒有被#define定義過,則編譯程序段1,否則編譯程序段2。#ifndefSTM32F10X_CL#defineRCC_USBCLKSource_PLLCLK_1Div5((uint8_t)0x00)#defineRCC_USBCLKSource_PLLCLK_Div1((uint8_t)0x01)#defineIS_RCC_USBCLK_SOURCE(SOURCE)(((SOURCE)==RCC_USBCLKSource_PLLCLK_1Div5)||\((SOURCE)==RCC_USBCLKSource_PLLCLK_Div1))#else#defineRCC_OTGFSCLKSource_PLLVCO_Div3((uint8_t)0x00)#defineRCC_OTGFSCLKSource_PLLVCO_Div2((uint8_t)0x01)#defineIS_RCC_OTGFSCLK_SOURCE(SOURCE)(((SOURCE)==RCC_OTGFSCLKSource_PLLVCO_Div3)||\((SOURCE)==RCC_OTGFSCLKSource_PLLVCO_Div2))#endif標準外設庫v3.5.0版本中的stm32f10x_rcc.h頭文件中的源碼4.2.10#ifdef、#ifndef、#else、#if條件編譯形式三:#ifdef標識符1(或表達式1)程序段1#elif標識符2(或表達式2)程序段2#endif功能:當定義了標識符1或表達式1,則編譯程序段1;否則,如果定義了標識符2或表達式2,則編譯程序段2。#ifdefSTM32F10X_CL/*PREDIV1clocksource(forSTM32connectivitylinedevices)*/#defineRCC_PREDIV1_Source_HSE((uint32_t)0x00000000)#defineRCC_PREDIV1_Source_PLL2((uint32_t)0x00010000)#defineIS_RCC_PREDIV1_SOURCE(SOURCE)(((SOURCE)==RCC_PREDIV1_Source_HSE)||\((SOURCE)==RCC_PREDIV1_Source_PLL2))#elifdefined(STM32F10X_LD_VL)||defined(STM32F10X_MD_VL)||\defined(STM32F10X_HD_VL)#defineRCC_PREDIV1_Source_HSE((uint32_t)0x00000000)#defineIS_RCC_PREDIV1_SOURCE(SOURCE)(((SOURCE)==RCC_PREDIV1_Source_HSE))#endif標準外設庫v3.5.0版本中的stm32f10x_rcc.h頭文件中的源碼。4.2.11指針指針用于存放地址的變量兩個要素:值:指的是某個對象的位置(即內存地址);類型:是指對象所在位置上所存儲數據的類型。有確定的數據類型

數據類型

*變量名;

如int*p;特殊的:void*類型代表通用指針指針理解1取地址運算符&指向一個變量的存儲地址;

inti;int*p;p=&i;

//將int類型的指針p指向變量i的地址指針理解24.2.11指針取值運算符*訪問指針變量所指向地址單元的內容;inti=0;int*p;p=&i;printf("%d\n",*p);

//通過*p獲取i的值用于輸出*p=200;//通過*p修改變量i的值printf("%d\n",i);

//輸入i的值,i=200指針理解3指針和數組在數組中使用指針,可以通過移動指針,對數組中的元素進行搜索。intData_Array[]={1,2,3,4,5,6};

//定義一個數組int*pr;//定義一個指針prpr=&Data_Array[0];

//將指針指向數組的首地址則,可以利用指針對數組的元素進行賦值*pr=8;//用指針給數組的第一個元素賦值*(pr+1)=9;//用指針給數組的第二個元素賦值*(pr+2)=7;//用指針給數組的第三個元素賦值指針理解44.2.11指針將指針從一種類型強制轉換成另外一種類型。pr=(int*)0x42210188;解析:0x42210188是一個32位的數據,表示的是存儲空間中的一個地址,但是在程序中如果寫成pr=0x42210188;只是將0x42210188這個32位的數據賦給pr,而使用強制類型轉換(int*),則是告知編譯器這是一個整型數據所占內存空間的首地址,如果沒有這個類型轉換,則編譯器會報錯。*pr表示取這個指針所指向內存空間中存儲的數據,如圖,*pr=0x0055。指針理解532位微處理器中int類型的變量占4個字節的空間。在此輸入您的標題在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字,在此輸入您的文字4.2.11指針編譯器會為函數分配一段連續的存儲空間,其首地址就是這個函數名所定義的變量的地址定義一個指針變量來存放該地址,這個指針變量就叫作函數指針變量,簡稱函數指針。在程序中可以通過這個函數指針變量調用這個函數。函數指針的定義方式為:函數返回值類型(*指針變量名)(函數參數列表);例如:intf(intx,int*p);//函數……int(*fp)(int,int*);

//聲明一個指針fp

fp=f;

//將函數f賦給該指針……inty=1;intresult=fp(3,&y);

//用指針來調用f函數4.2.11指針STM32標準外設庫中,指針的類型為32位整型數據STM32中片上外設都是掛接在不同的總線上的,stm32f10x.h文件中利用C語言的宏定義和指針來一步步地封裝總線和外設的基地址。通過宏定義為外設基地址0x40000000取一個宏名PERIPH_BASE,方便使用,stm32f10x.h中FLASH、SRAM和PERIPH(外設)的基地址定義如下:#defineFLASH_BASE((uint32_t)0x08000000)#defineSRAM_BASE((uint32_t)0x20000000)#definePERIPH_BASE((uint32_t)0x40000000)STM32的總線有APB1、APB2和AHB總線組成,各自的總線基地址定義如下:#defineAPB1PERIPH_BASEPERIPH_BASE#defineAPB2PERIPH_BASE(PERIPH_BASE+0x10000)#defineAHBPERIPH_BASE(PERIPH_BASE+0x20000)STM32標準外設庫中的指針應用4.2.11指針stm32f10x.h文件中定義了這7組GPIO端口的基地址的宏。#defineGPIOA_BASE(APB2PERIPH_BASE+0x0800)#defineGPIOB_BASE(APB2PERIPH_BASE+0x0C00)#defineGPIOC_BASE(APB2PERIPH_BASE+0x1000)#defineGPIOD_BASE(APB2PERIPH_BASE+0x1400)#defineGPIOE_BASE(APB2PERIPH_BASE+0x1800)#defineGPIOF_BASE(APB2PERIPH_BASE+0x1C00)#defineGPIOG_BASE(APB2PERIPH_BASE+0x2000)

ST公司的工程師采用了C語言結構體的形式封裝了這些寄存器組:#defineGPIOA((GPIO_TypeDef*)GPIOA_BASE)#defineGPIOB((GPIO_TypeDef*)GPIOB_BASE)#defineGPIOC((GPIO_TypeDef*)GPIOC_BASE)#defineGPIOD((GPIO_TypeDef*)GPIOD_BASE)#defineGPIOE((GPIO_TypeDef*)GPIOE_BASE)#defineGPIOF((GPIO_TypeDef*)GPIOF_BASE)#defineGPIOG((GPIO_TypeDef*)GPIOG_BASE)結構體類型后面加上“*”號,表示是結構體類型的指針,(GPIO_TypeDef*)則是把GPIOA_BASE等的地址強制轉換為GPIO_TypeDef結構體類型的指針,實際編程時可以直接用該宏名訪問寄存器。4.2.11指針GPIO_TypeDef的相關定義:typedefstruct{__IOuint32_tCRL;__IOuint32_tCRH;__IOuint32_tIDR;__IOuint32_tODR;__IOuint32_tBSRR;__IOuint32_tBRR;__IOuint32_tLCKR;}GPIO_TypeDef;聲明了名為GPIO_TypeDef的結構體類型,C語言中struct結構體中成員在內存中是按順序存儲的。ST工程師把對底層寄存器的操作進行了封裝,以API的形式供開發人員調用,如STM32提供的庫函數GPIO_SetBits(),用于設置某位引腳為高電平:voidGPIO_SetBits(GPIO_TypeDef*GPIOx,uint16_tGPIO_Pin){

GPIOx->BSRR=GPIO_Pin;}

庫函數GPIO_SetBits()實質上還是對寄存器BSRR進行讀寫操作,只是進行了封裝,開發人員只需調用此函數就可以實現對BSRR寄存器的操作。有了寄存器的地址,就可以使用指針進行讀寫操作了。GPIOA_>BSRR|=(1<<10);

//等同于*((int*)(0x0x40010810))|=(1<<10);GPIOA->ODR=0xFF;

//等同于*((int*)(0x0x4001080C))=0xFF;4.2.12回調函數通過函數指針調用的函數。操作系統中的某些函數常需要調用用戶定義的函數來實現其功能,由于與常用的用戶程序調用系統函數的調用方向相反,因此將這種調用稱為“回調”(Callback),而被系統函數調用的函數稱為“回調函數”。回調函數是指通過調用其他函數反過來調用某個函數。voidHAL_GPIO_EXTI_IRQHandler(uint16_tGPIO_Pin){if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin)!=RESET){

__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);

HAL_GPIO_EXTI_Callback(GPIO_Pin);}}__weakvoidHAL_GPIO_EXTI_Callback(uint16_tGPIO_Pin){UNUSED(GPIO_Pin);}STM32的HAL庫中GPIO引腳觸發中斷后的回調函數GPIO中斷處理函數STM32的HAL庫stm32f1xx_hal_gpio.c源碼調用回調函數STM32的HAL庫的回調函數的函數體需要用戶自己編寫,其實質是通過中斷處理函數調用回調函數來實現中斷服務功能4.3HAL庫文件源碼分析4.3.1stm32f1xx.h源碼分析4.3.2stm32f103xe.h4.3.3stm32f1xx_hal.c和stm32f1xx_hal.h4.3.4stm32f1xx_hal_gpio.c和stm32f1xx_hal_gpio.h4.3.1stm32f1xx.h源碼分析設備選型(根據具體的芯片型號包含對應芯片的頭文件)是否使用基于HAL庫的開發選擇對常用的布爾類型的變量進行了定義stm32f1xx.h是HAL庫中非常重要的頭文件,是STM32F1xx系列MCU的頂層頭文件,意味著凡是使用STM32F1xx系列的芯片,其應用程序都需要包含這個頭文件。4.3.1stm32f1xx.h源碼分析#ifndef__STM32F1XX_H#define__STM32F1XX_H#ifdef__cplusplusextern"C"{#if!defined(STM32F1)#defineSTM32F1#endif/*STM32F1*/解析:如果沒有定義宏__STM32F1XX_H,則定義宏__STM32F1XX_H。這是個條件編譯,目的是防止頭文件被重復包含。解析:這是一個條件編譯,__cplusplus是C++中自定義的一個宏,plus是“+”的意思。這段代碼的作用是如果定義了C++,則用extern"C"{"和"}"來告訴編譯器:這是一個用C語言寫成的庫函數,請用C語言的方式來鏈接編譯它們。主要是因為c++中存在重載,c++編譯時會把函數參數一起編譯,而c語言只編譯函數名。解析:如果沒定義STM32F1,則定義STM32F1。4.3.1stm32f1xx.h源碼分析#if!defined(STM32F100xB)&&!defined(STM32F100xE)&&!defined(STM32F101x6)&&\!defined(STM32F101xB)&&!defined(STM32F101xE)&&!defined(STM32F101xG)&&!defined(STM32F102x6)&&!defined(STM32F102xB)&&!defined(STM32F103x6)&&\!defined(STM32F103xB)&&!defined(STM32F103xE)&&!defined(STM32F103xG)&&!defined(STM32F105xC)&&!defined(STM32F107xC)/*#defineSTM32F100xB*//*#defineSTM32F100xE*//*#defineSTM32F101x6*//*#defineSTM32F101xB*//*#defineSTM32F101xE*//*#defineSTM32F101xG*//*#defineSTM32F102x6*//*#defineSTM32F102xB*//*#defineSTM32F103x6*//*#defineSTM32F103xB*//*#defineSTM32F103xE*//*#defineSTM32F103xG*//*#defineSTM32F105xC*//*#defineSTM32F107xC*/#endif解析:這段宏定義作用是編譯器提示進行設備選型,如果沒有發現任何STM32設備,則編譯器在編譯時會提示錯誤信息,如:#error"PleaseselectfirstthetargetSTM32F1xxdeviceusedinyourapplication(instm32f1xx.hfile)",這時就需要在這段代碼中找到所使用的MCU,把相應系列的STM32芯片的注釋去掉,就可以了,如使用的是STM32F103ZET6芯片,則需要把STM32F103xE這段代碼前后的注釋去掉。4.3.1stm32f1xx.h源碼分析#if!defined(USE_HAL_DRIVER)/***@briefCommentthelinebelowifyouwillnotusetheperipheralsdrivers.Inthiscase,thesedriverswillnotbeincludedandtheapplicationcodewillbebasedondirectaccesstoperipheralsregisters*//*#defineUSE_HAL_DRIVER*/#endif/*USE_HAL_DRIVER*/解析:這段代碼用于定義是否使用外設驅動庫編寫外設應用程序(這兒指HAL庫或標準外設庫,即調用API函數的開發方式),如果想使用基于寄存器的開發方式,即不使用ST提供的HAL庫,則把下面的代碼注釋掉。#define__STM32F1_CMSIS_VERSION_MAIN(0x04)

/*!<[31:24]mainversion*/#define__STM32F1_CMSIS_VERSION_SUB1(0x03)

/*!<[23:16]sub1version*/#define__STM32F1_CMSIS_VERSION_SUB2(0x01)

/*!<[15:8]sub2version*/#define__STM32F1_CMSIS_VERSION_RC(0x00)

/*!<[7:0]releasecandidate*/#define__STM32F1_CMSIS_VERSION((__STM32F1_CMSIS_VERSION_MAIN<<24)\|(__STM32F1_CMSIS_VERSION_SUB1<<16)\|(__STM32F1_CMSIS_VERSION_SUB2<<8)\|(__STM32F1_CMSIS_VERSION_RC))解析:CMSIS設備版本號的定義。4.3.1stm32f1xx.h源碼分析#ifdefined(STM32F100xB)#include"stm32f100xb.h"#elifdefined(STM32F100xE)#include"stm32f100xe.h"#elifdefined(STM32F101x6)#include"stm32f101x6.h"#elifdefined(STM32F101xB)#include"stm32f101xb.h"#elifdefined(STM32F101xE)#include"stm32f101xe.h"#elifdefined(STM32F101xG)#include"stm32f101xg.h"#elifdefined(STM32F102x6)#include"stm32f102x6.h"#elifdefined(STM32F102xB)#include"stm32f102xb.h"解析:stm32f1xx.h是STM32F1MCU的頂層頭文件,由于STM32F1xx系列的芯片特別多,ST針對每種具體芯片型號定義了一個特有的設備驅動頭文件,用戶在使用某種具體芯片進行開發時,需要在這里選擇包含具體芯片頭文件,如果沒有定義,則提示錯誤信息:PleaseselectfirstthetargetSTM32F1xxdeviceusedinyourapplication(instm32f1xx.hfile)。#elifdefined(STM32F103x6)#include"stm32f103x6.h"#elifdefined(STM32F103xB)#include"stm32f103xb.h"#elifdefined(STM32F103xE)#include"stm32f103xe.h"#elifdefined(STM32F103xG)#include"stm32f103xg.h"#elifdefined(STM32F105xC)#include"stm32f105xc.h"#elifdefined(STM32F107xC)#include"stm32f107xc.h"#else#error"PleaseselectfirstthetargetSTM32F1xxdeviceusedinyourapplication(instm32f1xx.hfile)"#endif4.3.1stm32f1xx.h源碼分析typedefenum{RESET=0,SET=!RESET}FlagStatus,ITStatus;typedefenum{DISABLE=0,ENABLE=!DISABLE}FunctionalState;#defineIS_FUNCTIONAL_STATE(STATE)(((STATE)==DISABLE)||((STATE)==ENABLE))解析:為枚舉類型enum{RESET=0,SET=!RESET}定義了兩個別名FlagStatus和ITStatus,FlagStatus為標志狀態,其值有兩種RESET=0(重置)和SET=1(置位);ITStatus為中斷標志類型,其值有兩種RESET=0(重置)和SET=1(置位)。解析:FunctionalState為函數狀態,有兩種狀態,分別是DISABLE=0(失能),ENABLE=1(使能)。解析:帶參數的宏定義,用于功能狀態參數判斷,在函數中調用它來判斷所輸入的狀態參數是不是ENABLE(使能)或DISABLE(失能),如果是則返回1,如果參數不合法則返回0。4.3.1stm32f1xx.h源碼分析#ifdefined(USE_HAL_DRIVER)#include"stm32f1xx_hal.h"#endif/*USE_HAL_DRIVER*/#ifdef__cplusplus}#endif/*__cplusplus*/#endif/*__STM32F1xx_H*/解析:ErrorStatus錯誤狀態,SUCCESS=0(正確),ERROR=1(錯誤)。typedefenum{SUCCESS=0U,ERROR=!SUCCESS}ErrorStatus;#defineSET_BIT(REG,BIT)((REG)|=(BIT))#defineCLEAR_BIT(REG,BIT)((REG)&=~(BIT))#defineREAD_BIT(REG,BIT)((REG)&(BIT))#defineCLEAR_REG(REG)((REG)=(0x0))#defineWRITE_REG(REG,VAL)((REG)=(VAL))#defineREAD_REG(REG)((REG))#defineMODIFY_REG(REG,CLEARMASK,SETMASK)WRITE_REG((REG),(((READ_REG(REG))&(~(CLEARMASK)))|(SETMASK)))#definePOSITION_VAL(VAL)(__CLZ(__RBIT(VAL)))解析:STM32寄存器位的宏定義,其作用相當于函數。解析:如果使用HAL驅動函數庫,則應用程序中要include一個stm32f1xx_hal.h頭文件。4.3.2stm32f103xe.hstm32f103xe.h是STM32F1系列專門針對STM32F103xE芯片定義的特有的片上外設頭文件。它包含了整個HAL庫所共用的配置(中斷向量、寄存器的定義等),也是所有外設與用戶函數的頭文件所必須包含的一個頭文件。STM32F10x系列MCU的中斷向量的定義存儲空間的地址映射所有片上外設寄存器的結構體定義4.3.2stm32f103xe.h解析:與Cortex-M3內核相關的宏定義。#ifndef__STM32F103xE_H#define__STM32F103xE_H#ifdef__cplusplusextern"C"{#endif#define__CM3_REV0x0200U

/*!<CoreRevisionr2p0

*/#define__MPU_PRESENT0U

/*!<OtherSTM32devicesdoesnotprovideanMPU*/#define__NVIC_PRIO_BITS4U

/*!<STM32uses4BitsforthePriorityLevels*/#define__Vendor_SysTickConfig0U

/*!<Setto1ifdifferentSysTickConfigisused*/解析:條件編譯,類似if-else結構。當用C++編譯器編譯時,extern"C"{告知編譯器下面這段程序是用C語言編寫的,請用C的方式進行編譯。解析:條件編譯,防止頭文件被重復包含。4.3.2stm32f103xe.hMCU中斷向量定義/*!<InterruptNumberDefinition*/typedefenum{/***Cortex-M3ProcessorExceptionsNumbers***/

NonMaskableInt_IRQn=-14,

/*!<2NonMaskableInterrupt

*/

HardFault_IRQn=-13,

/*!<3Cortex-M3HardFaultInterrupt*/

MemoryManagement_IRQn=-12,

/*!<4Cortex-M3MemoryManagementInterrupt*/BusFault_IRQn=-11,

/*!<5Cortex-M3BusFaultInterrupt*/UsageFault_IRQn=-10,

/*!<6Cortex-M3UsageFaultInterrupt*/

SVCall_IRQn=-5,

/*!<11Cortex-M3SVCallInterrupt*/DebugMonitor_IRQn=-4,

/*!<12Cortex-M3DebugMonitorInterrupt*/PendSV_IRQn=-2,

/*!<14Cortex-M3PendSVInterrupt*/

SysTick_IRQn=-1,

/*!<15Cortex-M3SystemTickInterrupt*/解析:與Cortex-M3內核有關的異常中斷,由ARM公司統一定義,編號均小于0,編號越小,意味著中斷優先級越高。ARM公司設計的Cortex-M3內核可支持256個中斷,包括15個內核中斷和240個外部中斷,并具有256級的可編程中斷優先級設置,即除了3個固定的高優先級(Reset、NMI、硬件失效),其他中斷和異常的優先級是可以由用戶設置的。但使用Cortex-M3內核的芯片制造商(如ST、NXP公司等)往往不需要用到那么多中斷,ST根據需求針對具體的MCU進行了裁剪。4.3.2stm32f103xe.hMCU中斷向量定義/**STM32specificInterruptNumbers******/

WWDG_IRQn

=0,

/*!<WindowWatchDogInterrupt*/

PVD_IRQn

=1,

/*!<PVDthroughEXTILinedetectionInterrupt*/

TAMPER_IRQn

=2,/*!<TamperInterrupt*/RTC_IRQn=3,/*!<RTCglobalInterrupt*/

FLASH_IRQn=4,/*!<FLASHglobalInterrupt*/

RCC_IRQn=5,/*!<RCCglobalInterrupt*/

EXTI0_IRQn=6,/*!<EXTILine0Interrupt*/

EXTI1_IRQn=7,/*!<EXTILine1Interrupt*/

……………..DMA2_Channel3_IRQn=58,/*!<DMA2Channel3globalInterrupt*/

DMA2_Channel4_5_IRQn=

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論