STM32F簡單應用

From:http://www.cnblogs.com/51mcu/p/3330444.html

今天我就就像在這裏記錄下自己一無所有建立stm32工程的過程,是我自己的一個探索過程,同時也是大家互相交流的過程。

 

第一步:當然是新建一個工程我把它命名爲small(這個隨便你,青菜蘿蔔各有所愛……)

捕獲新建工程

第二部:就是選擇芯片的型號了,這個按照每個人手上的板子的不同就選擇不同的芯片型號。

 

選擇芯片型號

選擇完了之後按確認,然後會出來一個對話框,是問你要不要添加啓動文件的,這個簡單我們都是白手起家了,別人好不容易送你點東西我們當然照單全收,直接點是啊!哈哈。。。。。

啓動文件

點完是後我們的工程就是差不多建立好了,大家可以看到裏面就一個代碼文件,是以.s結尾的,是一個啓動文件裏面的代碼全是彙編的,看了有點暈死。。。。,以後再說吧這個。。。。

初步工程

要不我們編譯了看看結果?

報錯

一串鳥文的錯誤,看不怎麼懂,但是好像是說什麼沒有main函數。想想也是啊,自己確實沒寫main函數,要不我們自己建立一個.c文件,然後寫一個main函數?說做就做。。。。。

直接點擊file下面的新建圖標,然後寫一個名字保存,注意別忘保存好之後在工程裏面「add files to group」,然後我們再自己寫一個空的main函數,大家看看我做的對不對?

添加main

這下可以編譯了吧?

編譯警告

 

編譯有警告說什麼main函數的返回值必須是int。。。。不知道爲什麼?

百度看了下好像是編譯器和c語言標準規範的問題,沒辦法那就修改下main函數的返回值吧,把前面的main前面的void變成int就好了。

編譯通過了。。。

編譯通過了

要不我們接下來設置工程屬性看看?這個是很多教程中有的,我不想多少,大家自己找找吧。。。。

可以仿真

你看都可以仿真,哈哈那就說明系統運行起來了。。。。但是我們什麼都看不到,接下來我們的任務就是想辦法點亮一個led燈。

首先我們要控制燈的話就要操作寄存器,還記得我們在51裏面要操作P0口嗎?是用P0=0x00,這裏面簡單的說下51裏面操作IO口的原理,我們看到這裏有一個P0,這個P0是哪裏來的?顯然不像是int一樣是是c語言本身自帶的,也不是某一個變量是我們自己定的,其實這個是在reg52.h裏面定義的,有圖有真相。

52頭文件

可以看到在我們一直使用reg52.h裏面他做了這樣一件事情,就是把我們的真實物理地址是0x90的這個P1寄存器和P1這個代名詞相互聯繫了,其實P1只是一個代名詞,假如我在reg52.h裏面修改 把sfr P1 = 0x90;修改爲sfr XX = 0x90;這樣也是可以用的,只不過下一次你要對P1口進行操作的時候要寫XX=0x00;了,所以爲了好記我們就把名字取成P1,現在我們知道了51的原理,我們可以依葫蘆畫瓢來操作stm32的GPIO,先不管我們要操作什麼寄存器,我們要接解決的第一個問題怎麼使用c語言操作單片機中知道絕對物理地址的寄存器,比如我們通過查資料RM0008的179頁知道了關於IO操作的一個寄存器GPIOA_CRL的絕對地址是0x40010800+0x00(其中0x40010800是起始地址,0x00是偏移地址),接下來我們怎麼操作他呢?用 sfr?好像sfr是51彙編特有的指令,在arm裏面沒有。。。這時候我們是否想到c語言的一個和地址緊密相關的內容----指針,我們在上課的時候知道指針的本質就是地址,這樣我們是否可以通過它來把實際的物理地址和c語言變量建立關係呢?首先我們要把絕地地址變爲指針變量 ,肯定是強制類型轉換了(int*(0x40010800+0x00))通過這一步我們已經有一個int類型的指針,這個指針指向的地址就是我們GPIOA_CRL的絕對地址(0x40010800+0x00),有了指針之後我們要取變量,那麼很簡單隻要一個簡單的*取變量運算符就可以了(int*(0x40010800+0x00)),這下變量有了那麼我們是不是需要給變量取一個名字啊?

那就這樣#define GPIOA_CRL *((int*)(0x40010800+0x00)) 好了完成了我們終於可以再c語言的環境中操作我們的寄存器了,接下來我們只要給GPIOA_CRL 這個變量賦值就是我們在給GPIOA_CRL 這個寄存器賦值,先休息下再說。。。。。

    這篇文章其實我是一邊寫代碼做測試一邊寫的,就在上面我想要控制IO的時候花了好長的時間,就是因爲自己的不仔細吧!犯了幾個小錯誤。先看我的最終代碼。

最終完成代碼非常的簡單,但是爲了這幾行代碼花了我好久的時間,我已經在代碼後面寫了註釋了,具體的怎麼來的讓我婉婉道來,首先是上面那幾行的define的機構我在上面已經介紹過了,具體的地址是怎麼知道的?下面我來簡單的說下,在stm32的

RM0008 
Reference manual 
STM32F101xx, STM32F102xx, STM32F103xx, STM32F105xx 
and STM32F107xx advanced ARM-based 32-bit MCUs

這個開發文檔中的44頁內存映射表,如下圖

內存映射

我截圖的只是上半部分,還有下半部分,在下半部分我們可以看到

GPIOA

從這裏可以看到和GPIOA相關的寄存器的起始地址是0x40010400所以代碼中的一開始的基址是這個,然後我們需要知道不同的寄存器的偏移地址,這個是在後面的跳轉的鏈接可以看到的,跳轉到179頁,偏移地址的表格

偏移地址

這樣的話上面的幾個define的計算也就水落石出了。

        這裏簡單的說下自己當時犯的一個錯誤。我一開始寫的#define GPIOA_CRL *((int*)0x40010800+0x00) 是這樣的,調試後死活不是我想要的工作效果,後來花了好大的力氣纔想起來原來是宏定義的括號的問題,大家仔細比對下我在代碼中的寫的和我上面的寫法,發現什麼端倪沒?#define GPIOA_CRL *((int*)0x40010800+0x00) 看到紅色的括號沒?就是因爲一個小小的括號耽誤了很長時間。

    花了這麼長的時間我們終於可以操作IO口了,那怎麼操作呢?在學過51的人的眼裏那是很簡單的,就是直接把我們要IO口輸出的值送到數據寄存器中不就好了,但是畢竟他是高級的stm32,所以要複雜點,首先我們要開啓GPIOA的端口時鐘,這也許大家會疑惑了,怎麼還和時鐘有關係了。。。。還是看資料吧!

B總線

可以看到GPIOA是掛載在AHB2總線上面的,他們都有自己的時鐘信號的控制端,這是由stm32的機構決定的,我想之所以這樣做,一方面可以降低系統功耗,讓工作的模塊的時鐘使能,不工作的就不使能,其實這裏的時鐘信號就好比是模塊的心臟一樣,只有先讓他工作了我們纔可以去對他進行操作,這就是代碼上     RCC_APB2ENR=0x00000004;//開啓GPIOA的端口時鐘這一句的作用,我嘗試過,假如去掉這一句話的話,即使我後面對寄存器賦值了,也是沒有作用的。所以這一句很重要,而且與下面的順序是不好交換的。接下來就是設置GPIO的工作方式什麼輸入輸出 模式之類的,對於只學過51的人來說有點新鮮感覺,如果學過其他高級點單片機的 估計已經習以爲常了,就那麼回事,具體的寄存器的每一位我就多說了,直接看stm32f10XXXX參考手冊的113頁上滿寫的很清楚了。

這裏再順便解釋下下我看到的一個現象,先看文檔

仿真口

從這裏面我們知道任何IO在復位之後都處於浮空輸入狀態,這是PDF上說的通過我的代碼大家看到我只是改變GPIOA0這位的狀態所以其他IO口應該還是浮空輸入的模式,但是實際上是的嗎?

看仿真截圖:

仿真奇怪

          大家可以看到其他幾個口還是聽話的就是PA12 PA13 PA14 PA15不怎麼對勁,怎麼回事呢?一開始我也疑惑,後來突然想起來了,這不是我們仿真用的jtag口嗎?這樣的話就對了,於是爲了驗證我的想法,我查看jtag用的其他IO口的情況,都不是默認的浮空輸入模式,這樣的話就應該是這個原因了。(提醒大家一下以後設計硬件的時候儘量避免使用jtag口,如果實在避免不了的話在設計程序的時候就要注意關閉jtag模式釋放那幾個IO口,在此做一個友情提醒因爲被坑過幾次。。。

      代碼寫完了,然後編譯下載,不出意外的話就可以看到PA0上面接的led燈亮了,這是必然的結果,這一次入門教程也就差不多了。。。,謝謝!