STM32之一步一步點(diǎn)亮led
入手stm32以來(lái),一直想快速上手,所以在各大論壇閑逛,各個(gè)達(dá)人的blog上學(xué)習(xí),正所謂欲速則不達(dá),心急是吃不了熱豆腐的!有木有?
最終決定使用st官網(wǎng)的庫(kù)開(kāi)發(fā),據(jù)大俠們寫道使用庫(kù)可以快速上手,貌似的確如此,一個(gè)個(gè)教程寫的那么好,直接拿過(guò)來(lái)用就是了。可是那么多個(gè)庫(kù),聰明的你請(qǐng)告訴到底選擇哪一個(gè)啊?My God!實(shí)話實(shí)說(shuō),我被這些庫(kù)折騰了個(gè)夠!好吧,我最后還是承認(rèn)最后用的是v3.4的庫(kù),是很方便!
切入正題,點(diǎn)亮LED。
硬件:紅牛開(kāi)發(fā)板,STM32F103ZET6(144封裝).
軟件:RealView MDK 4.12
stm32固件庫(kù):v3.4 附上自己整理后的庫(kù):V3.4_clean.rar
根據(jù)官網(wǎng)庫(kù)自己整理了下,新建了工程模板如下圖:(主要參考文章《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù).pdf》)在KeilMDK+環(huán)境下使用STM32V3.4庫(kù).pdf
入圖所示:新建一個(gè)目錄01_ProLed,建議放在英文路徑下,避免不必要的麻煩。將上面的庫(kù)v3.4解壓到此目錄,再新建一個(gè)project目錄,存放工程。
說(shuō)明:
CMSIS:最底層接口。StartUp:系統(tǒng)啟動(dòng)文件。StdPeriph_Lib:stm32外圍設(shè)備驅(qū)動(dòng)文件。Project:工程文件。User:用戶文件。新建工程步驟:此處略去300字。
簡(jiǎn)單說(shuō)明:
1.core_cm3.c/core_cm3.h
該文件是內(nèi)核訪問(wèn)層的源文件和頭文件,查看其中的代碼多半是使用匯編語(yǔ)言編寫的。在線不甚了解。--摘自《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù)》
2.stm32f10x.h
該文件是外設(shè)訪問(wèn)層的頭文件,該文件是最重要的頭文件之一。就像51里面的reg51.h一樣。例如定義了 CPU是哪種容量的 CPU,中斷向量等等。除了這些該頭文件還定義了和外設(shè)寄存器相關(guān)的結(jié)構(gòu)體,例如:
typedef struct
{
__IO uint32_t CR;
__IO uint32_t CFGR;
__IO uint32_t CIR;
__IO uint32_t APB2RSTR;
__IO uint32_t APB1RSTR;
__IO uint32_t AHBENR;
__IO uint32_t APB2ENR;
__IO uint32_t APB1ENR;
__IO uint32_t BDCR;
__IO uint32_t CSR;
#ifdef STM32F10X_CL
__IO uint32_t AHBRSTR;
__IO uint32_t CFGR2;
#endif/*STM32F10X_CL*/
#ifdefined(STM32F10X_LD_VL)||defined(STM32F10X_MD_VL)||defined(STM32F10X_HD_VL)
uint32_t RESERVED0;
__IO uint32_t CFGR2;
#endif/*STM32F10X_LD_VL||STM32F10X_MD_VL||STM32F10X_HD_VL*/
}RCC_TypeDef;
包含了那么多寄存器的定義,那么在應(yīng)用文件中(例如自己編寫的 main 源文件)只需要包含
stm32f10x.h即可,而不是以前固件庫(kù)的需要包含 stm32f10x_conf.h這個(gè)頭文件。--摘自《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù)》
3.system_stm32f10x.c/h
該頭文件也可以稱為外設(shè)訪問(wèn)層的頭文件和源文件。在該文件中可以定義系統(tǒng)的時(shí)鐘頻率,定義低速時(shí)鐘
總線和高速時(shí)鐘總線的頻率,其中最關(guān)鍵的函數(shù)就是 SystemInit()了,這個(gè)后面會(huì)詳細(xì)介紹。總之這兩
個(gè)文件是新固件庫(kù)的重點(diǎn),有了它糧也大大簡(jiǎn)化了使用 stm32的初始化工作。--摘自《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù)》
4.stm32f10x_conf.h
這個(gè)文件和 V2 版本的庫(kù)的內(nèi)容是一樣的,需要使用哪些外設(shè)就取消哪些外設(shè)的注釋。例如需要使用 GPIO功能,但不使用 SPI功能,就可以這樣操作。--摘自《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù)》
#include"stm32f10x_gpio.h"
/*#include"stm32f10x_spi.h"*/
5.main.c
這個(gè)文件就不用多說(shuō)了,自己編寫。--摘自《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù)》
6.stm32f10x_it.c/h
這兩個(gè)文件包含了 stm32中斷函數(shù),在源文件和頭文件中并沒(méi)有把所有的中斷入口函數(shù)都寫出來(lái),而
只寫了 ARM內(nèi)核的幾個(gè)異常中斷,其他的中斷函數(shù)需要用戶自己編寫。--摘自《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù)》
OK,開(kāi)始寫代碼了。
由于3.4的庫(kù)在啟動(dòng)的時(shí)候已經(jīng)設(shè)置好時(shí)鐘了(將在后面有講述),所以我們只需設(shè)置好對(duì)應(yīng)的GPIO即可。
查看硬件連接:
來(lái)點(diǎn)亮PF6.
新建led.c與led.h,添加到User Code下面。
led.h
#ifndef _LED_H_
#define _LED_H_
void Delay(uint32_t times);
void LedInit(void);
#endif
led.c
#include"stm32f10x.h"
/************************************************************************
*函數(shù)名:LedInit(void)
*描述:
*輸入 :無(wú)
*輸出 :無(wú)
*返回 :無(wú)
************************************************************************/
void LedInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*初始化 GPIOF的 Pin_6為推挽輸出*/
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOF,&GPIO_InitStructure);
}
/************************************************************************
*函數(shù)名:Delay(uint32_t times)
*描述:延時(shí)函數(shù)
*輸入 :uint32_t times
*輸出 :無(wú)
*返回 :無(wú)
************************************************************************/
void Delay(uint32_t times)
{
while(times--)
{
uint32_t i;
for(i=0;i<0xffff;i++)
;
}
}
在main.c中加入led初始化與點(diǎn)亮關(guān)閉即可。
/*!
thisisdone through SystemInit()functionwhichiscalled from startup
file(startup_stm32f10x_xx.s)beforetobranchtoapplication main.
Toreconfigure the default setting of SystemInit()function,referto
system_stm32f10x.c file
*/
/*Add your application code here
*/
/*初始化 GPIOF時(shí)鐘*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);
LedInit();
/*Infiniteloop*/
while(1)
{
/*關(guān)閉 LED1*/
GPIO_SetBits(GPIOF,GPIO_Pin_6);
/*延時(shí)*/
Delay(50);
/*點(diǎn)亮 LED1*/
GPIO_ResetBits(GPIOF,GPIO_Pin_6);
/*延時(shí)*/
Delay(50);
}
認(rèn)真學(xué)習(xí)下這段代碼,其實(shí)也非常簡(jiǎn)單,參考自《在KeilMDK+環(huán)境下使用STM32V3.4庫(kù)》。我想請(qǐng)大家注意的是前面的一段英文注釋,這段英文注釋什么意思呢?!霸谶\(yùn)行 main 函數(shù)之前,系統(tǒng)時(shí)鐘已經(jīng)完成初始化工作,在main函數(shù)之前,通過(guò)調(diào)用啟動(dòng)代碼運(yùn)行了 SystemInit函數(shù),而這個(gè)函數(shù)位于system_stm32f10x.c”。根據(jù)文中的提示我們回到 system_stm32f10x.c 看看 SystemInit如何初始化系統(tǒng)的。在 system_stm32f10x.c 的開(kāi)頭便定義了系統(tǒng)的時(shí)鐘頻率,從下面的這段代碼可以看出系統(tǒng)的頻率被定
義為 72MHZ,這也是絕大多數(shù) STM32運(yùn)行時(shí)的頻率。
#ifdefined(STM32F10X_LD_VL)||(defined STM32F10X_MD_VL)||(defined STM32F10X_HD_VL)
/*#define SYSCLK_FREQ_HSE HSE_VALUE*/
#define SYSCLK_FREQ_24MHz 24000000
#else
/*#define SYSCLK_FREQ_HSE HSE_VALUE*/
/*#define SYSCLK_FREQ_24MHz 24000000*/
/*#define SYSCLK_FREQ_36MHz 36000000*/
/*#define SYSCLK_FREQ_48MHz 48000000*/
/*#define SYSCLK_FREQ_56MHz 56000000*/
#define SYSCLK_FREQ_72MHz 72000000
#endif
緊接著根據(jù)這個(gè)宏定義程序試圖把系統(tǒng)時(shí)鐘初始化為 72MHz,代碼有點(diǎn)冗長(zhǎng),這里就不一一列出。在
SystemInit 函數(shù)中,調(diào)用了 SetSysClock 函數(shù),如果設(shè)定時(shí)鐘的頻率為 72MHZ 則 SetSysCloc 調(diào)用
SetSysClockTo72函數(shù),該函數(shù)和 V2 版本固件庫(kù)中的各范例中的 RCC_Configuration很相似,主要完
成把外部時(shí)鐘 9 倍頻后分配給系統(tǒng)時(shí)鐘,APB1 時(shí)鐘和 APB2又由系統(tǒng)時(shí)鐘分頻獲得。關(guān)鍵代碼如下:
/*HCLK=SYSCLK*/
RCC->CFGR|=(uint32_t)RCC_CFGR_HPRE_DIV1;
/*PCLK2=HCLK*/
RCC->CFGR|=(uint32_t)RCC_CFGR_PPRE2_DIV1;
/*PCLK1=HCLK*/
RCC->CFGR|=(uint32_t)RCC_CFGR_PPRE1_DIV2;
從上面的分析可以看出,SystemInit 并不需要用戶調(diào)用,啟動(dòng)代碼會(huì)自動(dòng)執(zhí)行,這樣相當(dāng)于少了一
個(gè) RCC_Configuration 函數(shù)的絕大多數(shù)內(nèi)容。請(qǐng)大家注意是絕大多數(shù)內(nèi)容而不是全部,但是請(qǐng)大家格外
注意使用到的外設(shè)還是要第一時(shí)間使得該外設(shè)的時(shí)鐘,像這樣的一句千萬(wàn)不要忘了
/*初始化 GPIOF時(shí)鐘*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF,ENABLE);