STM32F10X入門指南2---流水燈(初級篇)

來源:互聯網
上載者:User

    如何使用STM32來點亮一個流水燈呢?說白了也是使用STM32的IO口來產生高低電平的變化。下面先簡單的來介紹一下STM32的IO口吧。


STM32 IO連接埠介紹

不同於傳統的51單片機,大部分傳統的51單片機的輸出只有輸入輸出兩種方式,而STM32的輸入輸出的方式有輸入浮空,輸入上拉,輸入下拉,類比輸入,開漏輸出,推挽式輸出,推挽式複用,開漏複用8種方式,並且每種輸出方式的速度還可以配置成為10MHz,2MHz,50MHz。詳情參見STM32資料手冊中文版105頁。

另外,STM32還有一個獨特的設計,就是每一個模組(比如說IO口,I2C,SPI,USART等等介面)都有自己獨立的時鐘。要想對這些模組進行操作,首先要開啟這些模組的時鐘,否則操作是無效的。官方對這樣設計的解釋是,STM32作為CM3的核心,速度是一方面的優勢,另外,功耗也是另一方面的優勢。STM32F10X定位在中低端市場,也主打低功耗。在某一些模組不需要使用的時候,關閉他們的時鐘會更加省電。

說到這裡,可能會對STM32F10X的產品有一點點認識了吧。先放下STM32,我們先來看看KEIL MDK。

首先,還是建立一個工程,選擇自己使用的晶片,這個工程可以先添加一個檔案main.cpp。今後我們的所有自己編寫的檔案都是以CPP作為尾碼的,這樣可以使用C++的很多特性。

選擇晶片時編譯器為我們做了什嗎?

    建立一個main.cpp檔案,添加到工程中,千帆用的晶片是STM32F103C8,在建立工程的時候選定。在main函數中添加下面的代碼:

#ifdef STM32F10X_MD#error "User Error:Stm32f10x_Md"#endif#ifdef STM32F10X_HD#error "User Error: stm32f10x_Hd"#endifint main(){}
然後點一下編譯看看,這時候編譯器會給出下面的警告:

..\Source\main.cpp(3): error: #35: #error directive: "User Error:Stm32f10x_Md"

#error "User Error:Stm32f10x_Md"

這說明了什麼呢?看看我們的代碼,也就是說如果定義了宏STM32F10X_MD,那麼就讓編譯器

提示一個錯誤,錯誤的內容就是引號裡面的。一定要注意,千帆的晶片是STM32F103C8。

那麼還可以試著點擊Project->Select device for target,這裡STM32F103VC,

在試著編譯一下,這時候會發現編譯器提出下面的錯誤:

..\Source\main.cpp(7): 

error: #35: #error directive: "User Error: stm32f10x_Hd"
..\Source\main.cpp: Error: stm32f10x_Hd"

上面的兩個錯誤說明了什麼呢?也就是說當我們選擇晶片型號的時候,編譯器實際上只是給我們定義了一個宏,

這個宏是STM32F10X_XX後面的兩個字母代表晶片的容量。STM官方給的庫中很多地方都使用了這些宏。比如說:

#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL) && !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL) && !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL) && !defined (STM32F10X_XL) && !defined (STM32F10X_CL) #error "Please select first the target STM32F10x device used in your application (in stm32f10x.h file)"#endif

根據容量的不同,stm的庫定義不同的內容:

typedef struct{  __IO uint32_t CR;  __IO uint32_t SWTRIGR;  __IO uint32_t DHR12R1;  __IO uint32_t DHR12L1;  __IO uint32_t DHR8R1;  __IO uint32_t DHR12R2;  __IO uint32_t DHR12L2;  __IO uint32_t DHR8R2;  __IO uint32_t DHR12RD;  __IO uint32_t DHR12LD;  __IO uint32_t DHR8RD;  __IO uint32_t DOR1;  __IO uint32_t DOR2;#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)  __IO uint32_t SR;#endif} DAC_TypeDef;
這是一個DAC的寄存器,可以看到有的晶片有SR寄存器,有的晶片沒有。

STM官方對這些容量有著這樣的說明:

/*  Tip: To avoid modifying this file each time you need to switch between these        devices, you can define the device in your toolchain compiler preprocessor. - Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers   where the Flash memory density ranges between 16 and 32 Kbytes. - Low-density value line devices are STM32F100xx microcontrollers where the Flash   memory density ranges between 16 and 32 Kbytes. - Medium-density devices are STM32F101xx, STM32F102xx and STM32F103xx microcontrollers   where the Flash memory density ranges between 64 and 128 Kbytes. - Medium-density value line devices are STM32F100xx microcontrollers where the    Flash memory density ranges between 64 and 128 Kbytes.    - High-density devices are STM32F101xx and STM32F103xx microcontrollers where   the Flash memory density ranges between 256 and 512 Kbytes. - High-density value line devices are STM32F100xx microcontrollers where the    Flash memory density ranges between 256 and 512 Kbytes.    - XL-density devices are STM32F101xx and STM32F103xx microcontrollers where   the Flash memory density ranges between 512 and 1024 Kbytes. - Connectivity line devices are STM32F105xx and STM32F107xx microcontrollers.  */

看到這裡,在想想某一些教程上面自己定義一個宏STM32F10X_HD,這樣是不對的。也就是說,你自己不需要定義這個宏。這個宏是你在選擇晶片的時候自動幫你定義了。


Add Lib

看到了這些,下面就真正來寫我們的流水燈了。

首先,將庫檔案裡面的core_cm3.c   ,startup_stm32f10x_hd.s   ,stm32f10x_rcc.c   ,system_stm32f10x.c複製到我們建立的檔案夾的Core-》Source中,

把相應的標頭檔複製到建立的檔案夾core-》Include檔案夾下面。修改標頭檔的路徑,使之包含Core-》include。

注意:這個startup_stm32f10x_hd.s要根據自己的晶片實際選擇。如果你用的是STM32F103C8,那麼就選擇startup_stm32f10x_md.s,

如果是STM32F103VC,那麼就選擇startup_stm32f10x_hd.s.

注意,上面說的這四個檔案core_cm3.c ,startup_stm32f10x_hd.s,stm32f10x_rcc.c ,system_stm32f10x.c

是今後我們建立工程之後必須要添加進去的,其中startup_stm32f10x_xx.s是需要你自己使用的晶片來選擇的。

這四個檔案都可以在STM的官方庫V3.50中找到。

在Source Group 1上面雙擊(下左圖),彈出添加檔案的對話方塊,添加剛才上面所說的四個檔案以及STM32F10X_GPIO.C。添加完成如(下右圖)所示:

中顯示一些檔案上面有一個小鑰匙是因為STM在完成庫檔案之後,為了防止不小心將庫更改,特意將檔案設為唯讀。如果你自己以後的代碼修改完成之後

不需要更改,為了防止發生意外,也應該將其設定成為唯讀。


到此,我們流水燈的所有需要的檔案都已經添加完畢了。下面開始寫程式:

Coding

#include"Stm32f10x_rcc.h"#include"Stm32f10x_gpio.h"int main(){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);//open gpioc clock//config all pinsGPIO_InitTypeDef init;init.GPIO_Mode=GPIO_Mode_Out_PP;init.GPIO_Speed=GPIO_Speed_10MHz;init.GPIO_Pin=GPIO_Pin_13;GPIO_Init(GPIOC,&init);while(1){GPIO_SetBits(GPIOC,GPIO_Pin_13);for(int i=0;i<7200000;i++) ;//for delay times,about 1sGPIO_ResetBits(GPIOC,GPIO_Pin_13);for(int i=0;i<7200000;i++) ;//for delay times}}
前面我們說到,每一個模組都有自己的時鐘,要想讓模組工作,必須先開啟他的時鐘(這是必須的,如果時鐘沒有開啟,對模組的操作是無效的)這點一定要記住。這就好像家裡的電視機那樣,電源就是他工作的保障,如果你的電源沒有開啟,你怎麼讓電視機換台呢?千帆一開始學習的時候沒有注意到這點,也沒人說是一定要先開啟時鐘。那時候有個錯覺:以為配置的時候需要先將寄存器配置好,然後開啟時鐘讓他工作,其實這正是錯的。正確的方法是先開啟時鐘,只有開啟時鐘了,我們才能對他的寄存器進行操作。

Build

下面來編譯一下:呀!怎麼這麼多錯誤啊!

1.標頭檔的錯誤,系統提示標頭檔沒有找到,或者不能開啟標頭檔;

解決方案:

設定一下include path。

我們知道,編譯器在處理<>和""引用的標頭檔是不同的。在處理<>的時候,編譯器首先到安裝目錄中尋找。如果沒有找到,Error。

如果是"",先到你的Project所在的檔案夾裡尋找,如果沒找到,再到IDE的安裝目錄尋找。如果還沒有找到,Error伺候。

當我們添加一個include path的時候,相當於我們添加的這個Include path也是安裝目錄的一部分。也可以使用<>,最好還是""(至少""讓我一眼就能看出來這個標頭檔是自己編寫的)。

下面是添加的方法:依次單擊Options for taget->c/c++ ->new ->然後添加自己的路徑。如所示的那樣。



2.assert_param(expr)和assert_failed沒有定義的

我們一路追蹤這兩條代碼,最後在STM32F10X.h的8296行發現了下面的代碼:

#ifdef USE_STDPERIPH_DRIVER  #include "stm32f10x_conf.h"#endif

開啟上述代碼include的標頭檔後發現,裡面有這樣的代碼:

/* Exported types ------------------------------------------------------------*//* Exported constants --------------------------------------------------------*//* Uncomment the line below to expanse the "assert_param" macro in the    Standard Peripheral Library drivers code *//* #define USE_FULL_ASSERT    1 *//* Exported macro ------------------------------------------------------------*/#ifdef  USE_FULL_ASSERT/**  * @brief  The assert_param macro is used for function's parameters check.  * @param  expr: If expr is false, it calls assert_failed function which reports   *         the name of the source file and the source line number of the call   *         that failed. If expr is true, it returns no value.  * @retval None  */  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))/* Exported functions ------------------------------------------------------- */  void assert_failed(uint8_t* file, uint32_t line);#else  #define assert_param(expr) ((void)0)#endif /* USE_FULL_ASSERT */

奧,原來如此啊,在檔案Stm32f10x_conf.h中的宏定義就是assert_param(expr)的定義,這個宏根據expr運算式來進行選擇。

如果expr==true,那麼相當於什麼也不執行,如果expr==false,那麼調用assert_failed((uint8_t *)__FILE__, __LINE__))。而這個assert_failed((uint8_t *)__FILE__, __LINE__))又是一個條件編譯的函數。

如果define了USE_FALL_ASSERT,那麼只是給你聲明一個函數assert_failed((uint8_t *)__FILE__, __LINE__)),這個函數的實現需要你自己來實現。一般的情況可以是通過串口將資訊發送出去。比如說:

void assert_failed((uint8_t *)__FILE__, __LINE__)){while(1){serial.Write("Error! In File:   ");serial.WriteLine(__FILE__);serial.Print("In Line: %d \n",__LINE__);}}

這個serial類是一個串口的類,在今後的部落格中千帆還會為大家實現的。這裡面有兩個比較古怪的參數,一個是__LINE__,一個是__FILE__。如果你對這兩個參數不熟悉的話,那還要好好地補一下C語言。這兩個參數(注意,前面後面都是兩個底線)是C99標準中提出來的。__LINE__代表當前代碼的行數,__FILE__代表當前代碼所在的檔案名稱。只要支援C99標準的IDE都會支援這兩個宏的。

這個assert_param宏定義是用來檢測參數是不是符合規範的。一般中文的翻譯是斷言;在代碼主要體現在檢查參數上面,比如說:

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct){  uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;  uint32_t tmpreg = 0x00, pinmask = 0x00;  /* Check the parameters */  assert_param(IS_GPIO_ALL_PERIPH(GPIOx));  assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));  assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));    /*---------------------------- GPIO Mode Configuration -----------------------*/  currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F);  if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)  {     /* Check the parameters */  //...請查看源碼獲得詳細的解釋}
說到這裡,該怎麼解決這個錯誤呢?很簡單,只要簡單的定義一下USE_STDPERIPH_DRIVER就好了。

方法:在option for taget->C/C++->define中添加宏,如:



現在在編譯下,0 Error,0 warning,好的,下載!燈閃爍了。怎麼樣?用LIB編程是不是很簡單呢?



Function:

當然這個庫裡面不止這一個函數,下面是所有的函數的聲明:

void GPIO_DeInit(GPIO_TypeDef* GPIOx);void GPIO_AFIODeInit(void);void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);void GPIO_StructInit(GPIO_InitTypeDef* GPIO_InitStruct);uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx);uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx);void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal);void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal);void GPIO_PinLockConfig(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);void GPIO_EventOutputConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);void GPIO_EventOutputCmd(FunctionalState NewState);void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState);void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);void GPIO_ETH_MediaInterfaceConfig(uint32_t GPIO_ETH_MediaInterface);

想瞭解每個函數,請參閱庫的協助文檔:


這裡面可以很方便的查看源碼,函數的功能,參數的意思。在下載的V3.50的庫中可以找到。


Download可能有人會問了,代碼已經編寫完了,編譯也完成了,可是怎麼將自己的代碼下載到STM32中呢?前面我們也說到,如果有一個調試器會特別的方便。特別是J-LINK 或者ST-LINK。下面千帆就以ST-LINK為例說一下如何下載代碼;1.安裝ST-LINK的驅動。去官網下載ST-LINK的驅動,或者百度一下ST-LINK驅動,選擇安裝。如何判斷ST-LINK的驅動已經安裝成功了呢?這需要開啟電腦的裝置管理員(在案頭我的電腦上面右擊,在快顯功能表中選擇裝置管理員;或者按下win+R快速鍵,在運行中輸入devmgmt.msc)。開啟裝置管理員後在通用序列匯流排中找到圖片中標註的一項:

如果標註的那一項正常,那麼驅動就已經正常安裝。可以使用STLINK燒寫程式了。
2.在MDK中設定DEBUG選項

先選擇1標註的ST-LINK Debugger,然後點擊2標註的Settings。點擊2之後會出現下面的對話方塊:






先將1標註的地方切換成為SW,這時候右邊2標註的地方會自動出現。然後選擇第二個選項卡Trace,將Core頻率,一開始是10.000000MHz,設定成為72.000000MHz(不設定也可以,只不過不設定的話調試的時候代碼執行的時間是不準的)。再選擇第三個選項卡Flash DownLoad,先將1標註的地方打上勾,然後點擊2標註的Add,根據自己新片的容量選擇一種密度。選擇完成之後是3標註的樣子。最後點擊確定整個設定就完成了。







最後,點擊功能表列裡面的Load就行啦。





聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.