官术网_书友最值得收藏!

1.3 創建工程和執行程序

你編寫和執行的第一個C語言程序將告訴STM32微控制器,讓它發送一條消息給PC機或筆記本電腦。

任務四 你的第一個工程

雙擊Keil u Vision3的圖標,啟動Keil uVision3程序,你會得到圖1.10所示的Keil u Vision3的IDE主界面。IDE表示Integrated Development Environment,即集成開發環境。Keil提供了包括C編譯器、宏匯編、連接器、庫管理及一個功能強大的仿真調試器在內的完整開發方案,通過一個集成開發環境(u Visoin)將這些部分組合在一起,其軟件開發邏輯結構如圖1.11所示。掌握這一軟件的使用,對于進行單片機或ARM系統開發者來說是十分必要的,如果你使用C語言編程,那么Keil是你的不二之選,即使不使用C語言而僅用匯編語言編程,其方便易用的集成環境、強大的軟件仿真調試工具也會令你事半功倍。

圖1.10 Keil uVision3的IDE主界面

圖1.11 Keil軟件開發邏輯結構圖

集成開發環境

早期的程序設計各個階段都要用不同的軟件來進行處理,如先用字處理軟件編輯源程序,然后用鏈接程序進行函數、模塊連接,再用編譯程序進行編譯,開發者必須在幾種軟件間來回切換操作。現在的編程開發軟件將編輯、編譯、調試等功能集成在一個桌面環境中,這樣就大大方便了用戶。這就是集成開發環境(簡稱IDE)。IDE是一個用于程序開發的應用程序,一般包括代碼編輯器、編譯器、調試器和圖形用戶界面工具等一體化的開發軟件服務套件。所有具備這一特性的軟件或者軟件套件(組)都可以叫做集成開發環境。比如,微軟的Visual Studio.Net系列,可以稱為C++、VB、C#等語言的集成開發環境。

通過用Project菜單中的New Project命令建立項目文件(工程文件),過程如下:

(1)創建HelloRobot文件夾,將提供的library文件夾,stm32f10x_heads.h文件和HelloRobot.h文件復制到此目錄下。library文件夾里面的文件是STM32庫文件。

(2)單擊Project,會出現圖1.12所示的畫面,然后選擇New u Vision Project,彈出Create New Project對話框,找到剛才建立好的HelloRobot目錄,如圖1.13所示。

圖1.12 Keil uVision3工程菜單畫面

圖1.13 Create New Project對話框

(3)雙擊HelloRobot文件夾,在文件名中輸入工程文件名:HelloRobot(可不用加后綴名),保存在此目錄下,如圖1.14所示。之后單擊“保存”出現圖1.15所示的窗口。

圖1.14 創建HelloRobot工程

圖1.15 Select Device for Target ‘Target 1’ 對話框

(4)這里要求選擇芯片的類型,雙擊STMicroelectronics,在彈出的下拉菜單中選擇STM32F103xx(與教學開發板一致),就會出現相關介紹信息。例如選擇STM32F103VB,如圖1.16所示。看看選擇不同的STM32F103xx單片機,所顯示的資源有哪些不同。

圖1.16 CPU型號選擇窗口

(5)按OK確定,出現圖1.17所示窗口。詢問是否加載啟動代碼,在這里選擇“否”,不加載(后面將會手工裝載啟動代碼:cortexm3_macro.s,stm32f10x_vector.s,而不用系統提供的默認啟動代碼)。選擇“否”后將出現圖1.18所示窗口,此時項目文件,即工程文件就創建好了。

圖1.17 是否加載啟動代碼提示窗口

圖1.18 目標工程窗口

啟動代碼

啟動代碼(Bootloader)是嵌入式系統啟動時常見的一小段代碼,類似于啟動計算機時的BIOS,一般用于完成CPU的初始化工作和自檢。其他常見的啟動代碼比如ARM9嵌入式系統中的UBoot或Vivi等。同一型號的CPU啟動代碼會隨著開發板的設計不同而略有不同。

STM32這兩個啟動代碼主要完成處理器的初始化工作。其中,啟動文件cortexm3_macro.s的作用:定義Cortex-M3宏指令操作,這些宏指令操作可供用戶在C中調用。如在C文件中有必要使用匯編指令和這些宏指令做預處理時,只要直接在C代碼中使用EXPORT后面的宏指令操作就可以了(類似于在C中嵌入匯編的操作);啟動文件stm32f10x_vector.s的作用:初始化堆棧,定義程序啟動地址、中斷向量表和中斷服務程序入口地址,以及系統復位啟動時,從啟動代碼跳轉到用戶main函數入口地址。

查看HelloRobot目錄,你會發現HelloRobot.uv2工程文件。另外,opt文件是關于工程開發環境的參數配置和選項設置文件(Option File)。plg文件是編譯日志文件(Compile Log File),存放編譯器的編譯結果,編譯時采用的命令參數,已經編譯后得到的錯誤和警告信息。

項目文件(工程文件)

在當今的應用軟件開發中,一個軟件系統是由工程文件組成,工程文件包含若干個程序文件、頭文件、甚至庫文件。類似一本書,有目錄和各個章節;或者像一個公司,有好多部門。uv2文件是51、STM32等單片機或者ARM的Keil項目文件(工程文件),打開它就打開了這個工程,即與應用程序相關的全部文件和相應的設置。它包括的文件有頭文件、源文件、匯編文件、庫文件、配置文件等。這些文件的有關信息就保存在稱為“工程”的文件中,每次保存工程時,這些信息都會被更新。在Keil中使用工程文件來管理構成應用程序的所有文件,而且編譯生成的可執行文件是與項目文件(工程文件)同名。

任務五 你的第一個程序

項目文件創建后,這時只有一個框架,緊接著需要向項目文件中添加程序文件內容。Keil uVision支持C語言程序。可以是已經建立好的程序文件,也可以是新建的程序文件。如果是建立好了的程序文件,則直接用后面的方法添加;如果是新建立的程序文件,則先將程序文件.c存盤后再添加。

首先,先添加啟動代碼(匯編文件):cortexm3_macro.s,stm32f10x_vector.s。右鍵單擊“Source Group 1”,單擊“Add Files to Group‘Source Group 1’”,如圖1.19所示。

圖1.19 添加文件

在彈出的對話框中選擇文件類型,然后選中兩個啟動代碼,單擊“Add”按鈕添加進工程文件中,再單擊“Close”關閉此對話框,如圖1.20所示。

圖1.20 添加啟動代碼

文件添加到項目文件中去后,這時“Source Group 1”的前面將出現一個“+”號。單擊“+”,展開“Source Group 1”目錄,如圖1.21所示。

圖1.21 加入啟動文件后的工程

這時可以添加已經建立好的程序文件,如圖1.22所示。如果是新建立的程序文件,則先將程序文件.c存盤后再添加。

圖1.22 加入啟動文件后的工程

單擊按鈕(或通過“File→New”操作)為該項目新建一個C語言程序文件。將該例程鍵入到Keil uVision IDE的編輯器中,并以文件名HelloRobot.c保存。

例程:HelloRobot.c

    #include "stm32f10x_heads.h"
    #include "HelloRobot.h"
    int main(void)
    {
      BSP_Init();                // 開發板初始化
      USART_Configuration();     // 串口1(USART1)初始化
      printf("Hello Robot!\n");
      while (1);
    }

將文件保存在項目文件夾HelloRobot中,在文件類型中填寫.c(這里.c為文件擴展名,表示此文件類型為C語言源文件),如圖1.23所示。

圖1.23 C語言源文件保存對話框

下一步就是添加該文件到目標工程項目了,其具體添加過程如下。

(1)單擊“+”,展開“Source Group 1”目錄,然后右鍵單擊“Source Group 1”,在出現的菜單下選擇“Add File To Group ‘Source Group 1’”,如圖1.24所示。出現Add Files to Group Source‘Group1’對話框。在該對話框中選擇需要添加的程序文件:HelloRobot.c,單擊Add按鈕,把所選文件添加到項目文件中。一次可添加多個文件。

圖1.24 添加文件

(2)程序文件添加到項目文件中去后,如圖1.25所示(注意:圖中顯示的文件名是剛才輸入的文件名)。

圖1.25 添加C語言文件到目標工程中

雙擊源文件即可顯示源文件的編輯界面,如圖1.26所示。

圖1.26 源文件的編輯界面

下面來產生下載需要的可執行文件。圖1.27表示編譯的幾種方式,圖1.27(a)的“Translate current file”表示僅編譯當前源文件;圖1.27(b)的“Build target”表示編譯整個工程文件,編譯時僅編譯修改了的或新的源文件;圖1.27(c)的“Rebuild all target files”表示重新編譯整個工程文件,工程中的文件不管是"否修改,編譯時都將重新編譯。

圖1.27 編譯工程文件的幾種方式

一般地,我們單擊Keil uVision IDE快捷工具欄中的,編譯整個工程文件,如圖1.28所示。Keil的C編譯器根據要生成的目標文件類型對目標工程項目中的C語言源文件進行編譯。編譯過程中,可以觀察到源文件中有沒有錯誤產生,如果沒有錯誤產生,在IDE主窗口的下面出現“0 Error(s),0 Warning(s)”提示信息,表明已成功生成了可執行文件,并存儲在HolloRobot目錄中。

圖1.28 編譯過程的輸出信息

查看HelloRobot目錄,你會發現生成了HelloRobot.axf文件。ax(f arm excute file)是ARM芯片使用的文件格式,它包含bin代碼外,還包括了調試信息。與axf文件相似,單片機系統開發經常也會用到hex文件,hex文件包括地址信息,可直接用于燒寫或下載。

如要產生可執行的.hex文件,需要對目標工程“Target 1”進行編譯設置,右鍵單擊“Target 1”,選擇“Option for target ‘Target 1’”。單擊“Output”,選擇其中的“Create HEX File”,如圖1.29所示,單擊OK按鈕確定,關閉設置窗口。

圖1.29 設置目標工程的編譯輸出文件類型

再次單擊Keil uVision IDE快捷工具欄中的,Keil的C編譯器開始根據要生成的目標文件類型對目標工程項目中的C語言源文件進行編譯。這時HelloRobot目錄中,你會發現生成了HelloRoBot.hex文件。

任務六 下載可執行文件到教學開發板

將ULink下載工具的一端通過USB線連接到電腦USB口上,另一端連接到教學開發板上的JTAG口上。接好后,按圖1.30(a)~(c)所示過程依次配置,安裝ULink驅動。

圖1.30 ULink下載工具驅動的安裝

將ULink和教學開發板連接好,打開教學開發板電源開關。單擊Project下的Options for Target(工程屬性),彈出“Options for Target”對話框。或者單擊“Flash”菜單下的“Configure Flash Tools”,按照如圖1.31(a)~(f)所示進行配置。

圖1.31 ULink下載配置

圖1.31 ULink下載配置(續)

圖1.31 ULink下載配置(續)

這樣ULink下載工具就配置好了。如圖1.32所示,單擊圖標(或通過“Flash→Download”操作),程序就開始下載了,下載結束如圖1.33所示。

圖1.32 程序下載

圖1.33 程序下載結束

下載時,先擦除上次Flash存儲器中的程序,再將剛才編譯好的程序下載,最后經校驗無誤后,下載結束。此時,按教學開發板的Reset復位鍵,下載的程序開始運行。

Flash存儲器

Flash是存儲芯片的一種,通過特定的程序可以修改里面的數據。Flash存儲器又稱閃存,它結合了ROM和RAM的長處,不僅具備電子可擦除可編程(EEPROM)的性能,還不會斷電丟失數據,同時可以快速讀取數據(NVRAM的優勢)。U盤和MP3里用的就是這種存儲器。嵌入式系統以前一直使用ROM(EPROM)作為它們的存儲設備,20世紀90年代中期開始,Flash全面代替了ROM(EPROM)在嵌入式系統中的地位,用做存儲程序代碼或者Bootloader及操作系統,現在則直接當U盤使用。

目前Flash主要有兩種:NOR Flash和NAND Flash。NOR Flash的讀取和我們常見的SDRAM的讀取是一樣的,用戶可以直接運行裝載在NOR Flash里面的代碼,這樣可以減少SRAM的容量,從而節約了成本。NAND Flash沒有采取內存的隨機讀取技術,它的讀取是以一次讀取一塊的形式來進行的,通常是一次讀取512字節,采用這種技術的Flash比較廉價。用戶不能直接運行NAND Flash上的代碼,因此使用NAND Flash的嵌入式系統開發板除了使用NAND Flash以外,還做上了一塊小的NOR Flash來運行啟動代碼。

一般小容量的用NOR Flash,因為其讀取速度快,多用來存儲操作系統等重要信息,而大容量的用NAND Flash,最常見的NAND Flash應用是嵌入式系統采用的DOC(Disk On Chip)和我們通常用的“閃盤”,可以在線擦除。目前市面上的Flash主要來自Intel,AMD,Fujitsu和Mxic,而生產NAND Flash的主要廠家有Samsung和Toshiba及Hynix。

SRAM存儲器

SRAM是英文Static RAM的縮寫,它是一種具有靜止存取功能的內存,不需要刷新電路即能保存它內部存儲的數據。而DRAM(Dynamic Random Access Memory)每隔一段時間,要刷新充電一次,否則內部的數據即會消失,因此SRAM具有較高的性能,訪問速度快。但是SRAM也有它的缺點,即價格高、集成度較低、功耗較大,相同容量的DRAM內存可以設計為較小的體積,但是SRAM卻需要很大的體積。SRAM只用來存儲變量數據和提供給堆棧來使用。在嵌入式系統中,Flash的容量一般要大于SRAM的容量,如STM32F103VB芯片,其Flash容量為128K,SRAM容量為20K;STM32F103VE芯片,其Flash容量為512K,SRAM容量為64K。其他,8/16位單片機、ARM9或ARM11等嵌入式系統也是如此。

當生成的可執行文件大于Flash存儲空間時,則不能被下載到Flash中。如果出現類似下面的錯誤,則表示生成的可執行文件大于Flash存儲空間:

    Error: L6406W: No space in execution regions with .ANY selector matching Section .text(***.o).

這時,可以對程序進行優化,例如,減小緩存尺寸,減少全局變量,少定義尺寸大的數組而多用指針等方法。此外,合理調整RealView MDK的編譯和鏈接配置,也可以減小生成的可執行文件大小。比如,在鏈接腳本中指定代碼的存儲布局,將代碼段、只讀數據段、可讀寫數據段分別存放,以減小生成的可執行文件大小。常有下面3種解決方法。

(1)使用微庫:在圖1.31(a)所示的對話框中選中“USE MicroLib”,以使代碼減少。

(2)修改鏈接腳本:在“Options For Target”對話框的“Linker”頁面,將Use Memory Layout from Target Dialog前面的復選框勾上,如圖1.34所示。然后在“Target”頁面(圖1.31(a)所示的對話框)中修改存儲空間中只讀部分(Read/Only Memory Areas)和可讀寫部分(Read/Write Memory Areas)的起始和大小,一般來說加大只讀部分大小(該部分存放程序中的指令),而減小可讀寫部分的大小(該部分存放堆棧、局部變量等)。

圖1.34 代碼尺寸優化:配置“Linker”頁面

(3)修改優化級別:在“Options For Target”對話框的“C/C++”頁面,可使用編譯選項“-Ospace”進行編譯,將著重對空間進行優化,讓編譯器自動減小代碼大小。另外,還可以選更高的選優化級別“Level 3(-O3)”,如圖1.35所示。Level 3的優化等級最高,最適合下載到最終的產品芯片中,而Level 0為不優化,這種模式最適合調試,因為它不會優化掉代碼。在學習時,使用“Level 0(-O0)”,以方便程序的調試。

圖1.35 代碼尺寸優化:配置C/C++頁面

關于MicroLib

使用微庫,將以更精簡短小的C庫替代標準C庫,減小代碼大小。MicroLib是默認C庫的備選庫。它主要用于內存有限的嵌入式應用程序中。這些應用程序不在操作系統中運行。

如果你發現在Keil RealView MDK中使用printf函數,不能向串口輸出信息,或者今后發現可以軟件仿真,不能硬件仿真,注意要在圖1.31(a)中的設置對話框選中“USE MicroLib”。MicroLib提供了一個有限的stdio子系統,它僅支持未緩沖的stdin、stdout和stderr。這樣,即可使用printf()來顯示應用程序中的診斷消息。

要使用高級I/O函數,就必須提供自己實現的以下基本函數,以便與自己的I/O設備(如串口)配合使用。

為所有輸出函數:fprintf()、printf()、fwrite()、fputs()、puts()、putc()和putchar()等需要實現fputc()函數。

為所有輸入函數:fscanf()、scanf()、fread()、read()、fgets()、gets()、getc() 和getchar()等需要實現fgetc()函數。

由于MicroLib進行了高度優化,以使代碼變得很小。因此,MicroLib不完全符合ISO C99庫標準,僅提供有限的支持,不具備某些ISO C特性。并且其他特性具有的功能比默認C庫少,MicroLib與默認C庫之間的主要差異是:

(1)MicroLib不支持IEEE 754關于二進制浮點算法標準,否則會產生不可預測的輸出的結果,如NaN、無窮大。

(2)MicroLib中不支持的轉換為%lc、%ls和%a。

(3)MicroLib進行了高度優化,以使代碼變得很小。

(4)MicroLib不支持與操作系統交互的所有函數,如abort()、exit()、atexit()、clock()、time()、system()和getenv()。不能將main()聲明為帶參數的,并且不能返回內容。

(5)不支持與文件指針交互的所有stdio函數,否則將返回錯誤。僅支持三個標準流:stdin、stdout和stderr。即不完全支持stdio,僅支持未緩沖的stdin、stdout和stderr。

(6)MicroLib不提供互斥鎖來防止非線程安全的代碼。

(7)MicroLib不支持寬字符或多字節字符串。如果使用這些函數,則會產生鏈接器錯誤。

(8)與stdlib不同,MicroLib不支持可選擇的單或雙區內存模型。MicroLib只提供雙區內存模型,即單獨的堆棧和堆區。

該你了!——比較一下這幾種優化方法所生成的可執行文件大小

任務七 用串口調試軟件查看單片機輸出信息

打開串口調試軟件,如圖1.36所示進行設置。一般地,若臺式PC機(或筆記本電腦)有串口,則選擇串口“COM1”;若使用的臺式PC機(或筆記本電腦)沒有串口,則使用USB轉串口適配器,如圖1.7(b)所示。安裝好對應的USB驅動程序后,在“我的電腦”上右擊→屬性→硬件,查看“設備管理器”中的“端口(COM和LPT)”信息,如顯示“通信端口(COM5)”,則將串口號設為COM5即可。

圖1.36 串口調試軟件設置

單擊“連接”按鈕,串口連接成功,如圖1.37所示。如串口沒有連接成功,注意檢查串口連接線是否連接正確,并關掉其他串口調試軟件。

圖1.37 串口連接成功

這時,你在接收區看到了什么?什么也沒有!為什么呢?因為當你把執行文件成功地下載到單片機的那個時刻開始,程序就開始運行了:單片機已經向PC發送了信息。你錯過了接收,再按一下教學開發板上的“Reset”按鍵,你將看到調試軟件上的接收區顯示了一條信息,如圖1.38所示。

圖1.38 串口調試終端顯示的信息

恭喜你!

如果串口調試終端可以顯示信息,則說明你的STM32開發環境都已成功建立,包括硬件連接和軟件配置。

HelloRobot.c是如何工作的?

例程中前兩行代碼是HelloRoBot.c所包含的頭文件:“stm32f10x_heads.h”和“HelloRobot.h”。這兩個頭文件在本書的后續章節和任務中都要用到,“stm32f10x_heads.h”文件主要包含STM32庫文件的定義,它在編譯過程中用來將程序需要用到的標準數據類型和一些標準函數、中斷服務函數等包括進來,生成可執行代碼。頭文件中可以嵌套頭文件,同時也可以直接定義一些常用的功能函數。

HelloRobot.h則包含了本例程中以及后面例程中都要用到的幾個重要函數的定義和實現,在后面的章節中會進一步講解,包括:

(1)數據結構的定義;

(2)系統時鐘配置函數:RCC_Configuration;

(3)GPIO模式配置函數:GPIO_Configuration;

(4)中斷控制配置函數:NVIC_Configuration;

(5)串口1配置函數:USART_Configuration;

(6)將C語言printf庫函數輸出重定向到串口輸出函數:fputc;

(7)開發板初始化函數:BSP_Init;

(8)微秒延時函數:delay_nus;

(9)毫秒延時函數:delay_nms。

main函數的第一行語句是開發板初始化函數:BSP_Init,用來配置系統時鐘、GPIO模式和中斷控制,分別是RCC_Configuration()、GPIO_Configuration()、NVIC_Configuration()。這幾個函數將在后面的章節講解。這行語句中“//”后的是注釋。注釋是一行會被編譯器忽視的文字,因為注釋是為了給人閱讀,編譯器不對其進行編譯。

main函數的第二行語句是串口1初始化配置函數:USART_Configuration,用來規定單片機串口是如何與PC通信的。串口配置函數將在后面的章節講解。第三行語句是printf函數:它是單片機通過串口向PC機發送一條信息:Hello Robot!。

main函數的第四行語句是“while(1);”。編譯好后的可執行文件是下載到單片機Flash存儲器上的,并且是從頭開始往下加載的。當你把可執行文件加載上去的時候,填滿了整個Flash空間嗎?當然沒有!那么,當程序執行完printf函數之后,它還將向下繼續執行,但后面的空間并沒有存放程序代碼,這時程序后會亂運行,也就發生了“跑飛”現象。加上while(1);語句,讓程序一直停止在這里,就是為了防止程序跑飛。

C語言中的兩種文件

在教材各章節中,會多次使用以“#”號開頭的預處理命令,如包含命令#include,宏定義命令#define等。一般都放在源文件的前面,稱為預處理部分。所謂預處理是指在進行編譯的第一遍掃描(詞法掃描和語法分析)之前所做的工作。預處理是C語言的一個重要功能,它由預處理程序負責完成。當對一個源文件進行編譯時,將首先對源程序中的預處理部分作處理,處理完畢后再對源程序進行編譯。

常用的預處理命令有:

(1)宏定義

在C語言源程序中允許用一個標識符來表示一個字符串,稱為“宏”。“define”為宏定義命令,被定義為“宏”的標識符稱為“宏名”。在編譯預處理時,對程序中所有出現的“宏名”,都用宏定義中的字符串去代換,這稱為“宏代換”或“宏展開”。

(2)文件包含

文件包含是C預處理程序的另一個重要功能。文件包含命令的功能是把指定的文件插入該命令行位置取代該命令行,從而把指定的文件和當前的源程序文件連成一個源文件。

在程序設計中,文件包含是很有用的。一個大的程序可以分為多個模塊,由多個程序員分別編程。有些公用的符號常量或宏定義等可單獨組成一個文件,在其他文件的開頭用包含命令包含該文件即可使用。這樣,可避免在每個文件開頭都去書寫那些公用量,從而節省時間,并減少出錯。

包含命令中的文件名可以用雙引號(“”)括起來,也可以用尖括號(< >)括起來。使用尖括號表示在包含安裝軟件的目錄中去查找(這個目錄是安裝軟件時,設置的安裝路徑),而不在源文件目錄去查找,一般是安裝路徑(如C:\Keil MDK***)下的Include目錄;使用雙引號則表示首先在當前的源文件目錄中查找(即當前的HelloRobot目錄),若未找到才到包含安裝軟件的目錄中去查找。用戶編程時可根據自己文件所在的目錄來選擇某一種命令形式。如“stm32f10x_heads.h”和“HelloRobot.h”2個文件表示在當前源文件所在目錄。

一個include命令只能指定一個被包含文件,若有多個文件要包含,則需用多個include命令。文件包含允許嵌套,即在一個被包含的文件中又可以包含另一個文件。

任務八 做完實驗關斷電源

把電源從教學開發板上斷開很重要,原因有幾點:首先,如果系統在不使用時沒有消耗電能,電池可以用的更久;其次,在以后的學習中,你將在教學開發板上的面包板上搭建電路,搭建電路時,應使面包板斷電。如果是在教室,老師可能會有額外的要求,如斷開串口電纜,把教學開發板存放到安全的地方等。總之,你做完實驗后最重要的一步是斷開電源。斷開電源比較容易,只要三位開關撥到左邊的0位即可,如圖1.9(a)所示。

提倡節約用電,實踐低碳生活。

通過創建第一個程序,你可能感覺到了學習基于ARM Cortex-M3內核的STM32單片機不是很容易!確實不錯,要學好單片機,還要有一定的C語言基礎,嵌入式系統的開發確實是一項非常具有挑戰性的任務。即使是一個簡單的程序,也需要進行大量的準備,學習大量的計算機知識。萬事開頭難!

主站蜘蛛池模板: 个旧市| 通州市| 扎赉特旗| 南投县| 黄冈市| 台前县| 元谋县| 浪卡子县| 邹城市| 会泽县| 福州市| 宁远县| 榆树市| 双峰县| 尚志市| 吕梁市| 阳原县| 纳雍县| 济源市| 高尔夫| 凤凰县| 当雄县| 鸡西市| 洪江市| 西和县| 广昌县| 鄂温| 翼城县| 监利县| 子洲县| 从化市| 万盛区| 灵寿县| 新郑市| 乐昌市| 达日县| 和平区| 桦甸市| 乌兰浩特市| 恩平市| 新泰市|