emouse原創文章,轉載請注明出處http://www.cnblogs.com/emouse/
應部分網友要求,最新加入固件庫以及開發環境使用入門視頻教程,同時提供例程模板,個人錄制,歡迎指正。下載地址:
http://115.com/file/e78l0xlo# emouse-STM32系列視頻教程.rar
歡迎大家針對本博客文章提出寶貴意見。
關於使用固件庫建立工程請參考
使用Keil MDK以及標准外設庫創建STM32工程
關於固件庫的學習請參考
STM32固件庫詳解
1.1.1 使用Keil MDK運行第一個STM32F10X程序
在上一小節中已經詳細介紹了使用Keil MDK和標准外設庫創建一個工程的過程,下面將介紹基於這個工程來編寫一個小程序,通過這個程序我們可以初步了解:
- STM32標准外設庫的簡單使用過程
- STM32外設的使用方法和大致流程
- 程序的編譯、鏈接、下載步驟
- 利用Keil MDK的在線仿真功能進行軟件仿真的簡要步驟
1. 程序的編寫
(1)程序實現的功能
為了方便各位讀者的入門和理解,這個小程序的功能非常簡單,作為本書功能實踐的第一個程序,其功能當然也是最為經典的“Hello World!”了,只不過不是簡單的屏幕輸出,而是利用硬件的串口進行輸出,同時作為單片機類的第一個程序自然少不了LED閃爍的功能,這就是這個小程序的兩個主要的功能:
l 利用串口1輸出“Hello World!”字符。
l 控制兩個LED閃爍。
(2)程序的實現
在基於標准外設庫進行程序開發時一定要充分利用標准外設庫下面的幫助文件stm32f10x_stdperiph_lib_um.chm以及庫中自帶的工程實例,同時結合STM32F10x系列的芯片手冊來完成程序的開發,stm32f10x_stdperiph_lib_um.chm幫助文件如圖 5‑25所示。此文件中已經包含了標准外設庫的全部內容,並根據根據內容結構進行了重新的編排和整理,更加方便程序的閱讀和理解。
STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples文件夾下包括了眾多外設的使用例程,這里的例程多是針對官方的開發板而編寫,然而這些例程卻非常全面的展示了相關外設的各種使用方法,對我們做基於標准外設庫的開發有着非常重要的意義。
我們的第一個程序就通過這些參考來完成,首先來看串口部分。串口部分的詳細信息可以參考本書第8章,這兒只進行簡單的說明和介紹簡便的開發方法,首先先找一個與我們使用的功能最近的一個例程,STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Examples\USART文件夾下給出了多達12中的USART例程,這里選擇較為相近的Interrupt文件夾下得例程,打開文件夾下面的mian.c文件,通過簡單的瀏覽可以找到如下一段程序:
/* USARTy and USARTz configuration ------------------------------------------------------*/ /* USARTy and USARTz configured as follow: - BaudRate = 9600 baud - Word Length = 8 Bits - One Stop Bit - No parity - Hardware flow control disabled (RTS and CTS signals) - Receive and transmit enabled */ USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; /* Configure USARTy */ USART_Init(USARTy, &USART_InitStructure); /* Configure USARTz */ USART_Init(USARTz, &USART_InitStructure); /* Enable USARTy Receive and Transmit interrupts */ USART_ITConfig(USARTy, USART_IT_RXNE, ENABLE); USART_ITConfig(USARTy, USART_IT_TXE, ENABLE); /* Enable USARTz Receive and Transmit interrupts */ USART_ITConfig(USARTz, USART_IT_RXNE, ENABLE); USART_ITConfig(USARTz, USART_IT_TXE, ENABLE); /* Enable the USARTy */ USART_Cmd(USARTy, ENABLE); /* Enable the USARTz */ USART_Cmd(USARTz, ENABLE);
上面的這段的程序配合注釋可以很容易理解,USART_InitTypeDef定義了一個包括USART主要參數的結構體,因此首先對USART的相關參數進行配置,使用標准外設庫進行配置的優勢就體現出來了,通過程序可以很容易讀出這個串口的配置:
l 波特率9600Kbps
l 數據長度8
l 停止位1
l 奇偶校驗:無
l 硬件流控制:無
l 工作模式:收、發
然后利用USART_Init函數進行初始化,這段程序中設置了兩個串口,使用同樣的配置,然后配置相應的中斷。最后通過USART_Cmd函數使能相應的串口,前面有過介紹,這些例程里的程序是針對官方的開發套件的,因此程序中並沒有指名具體的端口,而是使用了宏定義USARTy、USARTz。通過這段程序就可以很方便的更改相關的參數得到我們需要的配置程序。
這兒只是完成了USART的配置,下面來看一下對應的I/O設置,仍然在這個文件中可以找到GPIO_Configuration(void)這個函數,程序如下:
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
#ifdef USE_STM3210C_EVAL
/* Enable the USART3 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_PartialRemap_USART3, ENABLE);
/* Enable the USART2 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
#elif defined USE_STM3210B_EVAL || defined USE_STM32100B_EVAL
/* Enable the USART2 Pins Software Remapping */
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
#endif
/* Configure USARTy Rx as input floating */
GPIO_InitStructure.GPIO_Pin = USARTy_RxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
/* Configure USARTz Rx as input floating */
GPIO_InitStructure.GPIO_Pin = USARTz_RxPin;
GPIO_Init(USARTz_GPIO, &GPIO_InitStructure);
/* Configure USARTy Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = USARTy_TxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(USARTy_GPIO, &GPIO_InitStructure);
/* Configure USARTz Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Pin = USARTz_TxPin;
GPIO_Init(USARTz_GPIO, &GPIO_InitStructure);
}
這段函數完成了相關的I/O配置,首先通過宏定義判斷是官方的哪一款開發套件,然后進行相應的端口映射(端口映射請參加官方的數據手冊),然后進行相應的端口配置,同串口配置一樣,這段程序中德端口用的也是宏定義USARTy_RxPin替代的,改為我們使用的實際I/O,端口時鍾設置為50MHz,串口所使用到的端口設置為復用(GPIO_Mode_AF_PP)完成端口初始化。
另外仍然通過觀察這個例程可以很容易發現,在使用一個外設時還需要首先打開對應的外設時鍾,這部分程序如下:
void RCC_Configuration(void)
{
/* Enable GPIO clock */
RCC_APB2PeriphClockCmd(USARTy_GPIO_CLK | USARTz_GPIO_CLK | RCC_APB2Periph_AFIO, ENABLE);
#ifndef USE_STM3210C_EVAL
/* Enable USARTy Clock */
RCC_APB2PeriphClockCmd(USARTy_CLK, ENABLE);
#else
/* Enable USARTy Clock */
RCC_APB1PeriphClockCmd(USARTy_CLK, ENABLE);
#endif
/* Enable USARTz Clock */
RCC_APB1PeriphClockCmd(USARTz_CLK, ENABLE);
}
這段程序中需要注意兩點,首先,GPIO、USART等都是連在APB1、APB2兩條總線上的,各外設具體的總線連接情況參見圖 5‑2,因此首先應該確定外設對應的總線,例如USART1是APB2總線,而USART2是APB1總線。其次使能相應的時鍾時不光要使能對應的I/O端口,還要使能總線的復用端口,這點也容易忽略。
最后根據庫中的例程,借鑒庫中例程的編寫風格,就可以得出我們需要的程序,程序在工程的mian.c中編寫,函數如下:
#include "stm32f10x.h"
void USART_Configuration(void);
void GPIO_Configuration(void);
void Delay(__IO uint32_t nCount);
void USART1_Puts(char * str);
int main(void)
{
USART_Configuration();
GPIO_Configuration();
USART1_Puts("Hello Wrold!\n");
while (1)
{
GPIOF->BSRR = 0x000000C0;
Delay(0xAFFFF);
GPIOF->BRR = 0x000000C0;
Delay(0xAFFFF);
USART1_Puts("Hello Wrold!\n");
}
}
void USART_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
//使能串口、串口所用的I/O口以及端口復用時鍾
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_AFIO, ENABLE);
/* A9 USART1_Tx */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽輸出-TX
GPIO_Init(GPIOA,&GPIO_InitStructure);
/* A10 USART1_Rx */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入-RX
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = 9600;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* Enable the USARTx */
USART_Cmd(USART1, ENABLE);
}
void GPIO_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOF, ENABLE);
/* 設置LED對應的引腳 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOF, &GPIO_InitStructure);
}
void Delay(__IO uint32_t nCount)
{
for(; nCount != 0; nCount--);
}
void USART1_Puts(char * str)
{
while(*str)
{
USART_SendData(USART1, *str++);
/* Loop until the end of transmission */
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}
至此完成了主函數的編寫。接下來還需要到stm32f10x_conf.h文件中選擇相應的頭文件,這兒去掉需要使用的頭文件之前的注釋,去掉注釋的頭文件如下:
l #include "stm32f10x_gpio.h"
l #include "stm32f10x_gpio.h"
l #include "stm32f10x_usart.h"
這樣,我們的程序編寫就完成了,下面可以進行我們的編譯與調試了。
2. 程序的編譯
在Keil MDK的工具欄上自左至右依次有
三個圖標,分別為編譯指定文件、生成工程、重新生成所有工程。首先在mian.c下點擊編譯圖標,如果編譯沒有錯誤,則輸出窗口如圖 5‑26所示。如果編譯存在錯誤,點擊錯誤的地方可以直接定位到錯誤行,根據錯誤提示進行修改。
編譯不存在錯誤后點擊
圖標生成工程,生成沒有錯誤則輸入如圖 5‑27所示。如果存在錯哦無則可以直接點擊對應的錯誤跳轉到對應的文件對應的行,根據錯誤提示進行再次修改。
第一次生成工程成功以后,如果修改了相應的文件再次點擊生成工程時只會對有改動的文件進行重新編譯、生成,而重新生成全部工程會重新生成工程中的所有文件。
3. 程序的調試
在實際開發時,可以首先利用軟件進行仿真,利用軟件進行仿真查看具體的寄存器配置,外設的工作情況等,同時通過斷點的配合可以查找出程序中絕大多數的錯誤。軟件仿真完成后可以下載到硬件平台去運行,如果程序仍然沒有達到需要的效果,在硬件檢查完成后可以使用在線仿真的方式,在硬件平台上直接運行程序,進一步查找問題。
首先,先看一下這個小程序的軟件仿真,首先在工程設置Debug選項中選擇使用軟件模擬,如圖 5‑28所示。
點擊
圖標,或者從菜單欄-Debug啟動調試。啟動后界面如圖 5‑29所示。調試啟動后自動運行到主程序。
在工具欄可以找到與調試相關的快捷圖標
,同樣在菜單欄Debug菜單下可以找到對應的選項,自左向右分別為復位,運行,點擊后直接運行程序;停止,運行時點擊停止當前運行;單步,單步執行當前程序;跳過,可以直接跳過子函數的運行;跳出,在某個子函數中運行時可以直接跳出當前函數;運行到光標位置處。
在程序行號的左側直接雙擊可以非常方便的添加或者取消斷點,如圖 5‑30所示。
在菜單Peripherals中可以打開需要查看的外設,這里點擊Peripherals-USART1彈出USART1的相關觀察窗口,點擊運行后,窗口如圖 5‑31所示,就會顯示運行的程序對USART1所完成的相關配置。從中可以清晰的看出,程序對USART1的相關寄存器配置,從地面Settings一欄中則可以直觀的看出USART1當前的工作狀態,從這兒就可以檢查我們的相關參數設置正確與否,對應時鍾有無開啟等。需要說明的是這里寄存器在進行仿真的時候是可以進行改寫的,也就是說允許我們在程序運行的時候直接進行相關的操作來觀察運行結果。但是此時程序本身也是可以對寄存器進行操作的,因此直接更改相應的寄存器觀察時還要注意你的操作是否和程序沖突。例如在本程序的運行過程中如果將寄存器中發送使能TE的勾給去掉,則串口就不會在輸出數據。
接下來繼續查看I/O的配置,本程序中使用的是F端口,則點擊菜單Peripherals-General-Purpose I/O,選擇GPIOF,出現GPIOF的配置窗口,程序運行后可以通過窗口查看GPIOF的相關配置,如圖 5‑32所示。在這個窗口中可以查看引腳的配置情況,當程序運行時就能看到對應引腳的勾在閃爍。
通過這些操作,可以非常方便、直觀的查看程序運行后對應的配置是否與我們預想的一致,那么在本程序中通過串口輸出了“Hello World!”又該怎樣查看運行結果呢?在工具欄
中可以非常方便的打開相應的查看窗口,查看相關寄存器和外設等。由於我們需要使用串口1輸出字符,同時LED閃爍顯示,因此首先打開串口1的輸出,點擊
圖標,彈出如所示下拉菜單,選擇UART#1如圖 5‑33所示,或者點擊View菜單點擊Serial Windows選擇UART#1即可打開UART的輸出窗口,運行程序后會在此窗口中輸出串口信息,如圖 5‑34所示。
至此,已經利用Keil MDK的軟件仿真功能完成了程序的調試,看到了設計的效果。通過這個例子也能夠讓我們深刻的體會到Keil MDK在線調試功能的強大之處。接下來我們將編寫好的程序下載到開發板中運行,看看運行效果。
4. 程序的下載
編寫好的程序工程生成完成后下載到硬件平台的方法有很多種,一是直接利用Flash燒寫工具燒寫相應的hex文件,在工程配置時需要設置才能生成hex文件,具體設置參加圖 5‑16,生成hex文件之后就可以利用燒寫工具燒寫到硬件平台,常用的仿真器都帶有相應的軟件工具,如筆者使用的JLink-V8,可以使用自帶的J-Flash工具,使用J-Flash工具如圖 5‑35所示,在工程管理中選擇所使用的硬件平台,點擊Target-Connect后就可以連接上硬件平台,在圖中左側顯示了仿真器以及對應的硬件平台信息。然后選擇所生產的hex文件,就可以講編譯的程序下載到硬件平台中。
這種下載方式非常適合將程序源碼不公開的情況下將程序固件提供給第三方使用,然而在使用Keil MDK時還有更為方便的方式,在工程設置中設置好了下載工具后可以點擊
圖標,直接將程序下載到硬件平台中運行,非常方便。在使用仿真器進行在線仿真時也可以通過設置在在線仿真時將程序下載到flash中。
程序下載到硬件平台后通過實際的串口來查看程序運行情況了,將硬件平台的串口1連接至PC,筆記本等電腦上沒有物理串口的可以使用USB轉串口設備,注意在設備管理器中查看實際所分配的串口號,本機分配的串口號為COM4,如圖 5‑36所示,串口號也可以在高級選項中進行更改。
這里使用本章所介紹的TKStudio中的串口調試助手工具來進行查看,設置串口的相應參數與程序一致,串口設置於運行結果如圖 5‑37所示,從圖中可以看出串口接數據正確,開發板上的兩個LED也在不停閃爍。
至此,我們完成了一個簡單的程序開發過程。本小節通過實現串口輸出與LED閃爍的功能系統的介紹了怎樣基於標准外設庫進行程序的開發與調試,這兒只是列舉了一個非常簡單的小例子,更多的聯系讀者可以參考標准外設庫中的例程自行完成。

![clip_image002[6] clip_image002[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUwNjQxMjYucG5n.png)
![clip_image006[6] clip_image006[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUwODI0OTkuanBn.png)
![clip_image010[6] clip_image010[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUwODgwMDYuanBn.png)
![clip_image012[6] clip_image012[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUxMDMxNDAucG5n.png)
![clip_image016[6] clip_image016[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUxNDkzMzEucG5n.png)
![clip_image020[6] clip_image020[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUxNzQ2Mi5qcGc=.png)
![clip_image022[6] clip_image022[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUxODgzNzgucG5n.png)
![clip_image024[6] clip_image024[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUyMTgwMTUucG5n.png)
![clip_image029[6] clip_image029[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUyNjI1MzYuanBn.png)
![clip_image031[6] clip_image031[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUzMDg3NTkuanBn.png)
![clip_image033[6] clip_image033[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUzMzIzMzMucG5n.png)
![clip_image037[6] clip_image037[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUzNDMzMTQuanBn.png)
![clip_image039[6] clip_image039[6]](/image/aHR0cHM6Ly9pbWFnZXMuY25ibG9ncy5jb20vY25ibG9nc19jb20vZW1vdXNlLzIwMTIwMy8yMDEyMDMwOTIwMDUzNjY0OTYucG5n.png)