環境搭建:
1、安裝 Keil uVision4 MDK 略 2、安裝 J-Link 安裝 Setup_JLinkARM_V4501.exe 驅動時,會彈出【SEGGER J-Link DLL Updater V4.501 】對話框, 不要選擇 Keil 直接點擊OK即可,因為Keil對支持M3內核SW接口采用了JL2CM3.dll 這個文件,該文 件的版本號是和該目錄下Jlink驅動版本號配套的,不要擅自改變他們,否則不能使用。 建立工程: 1、在桌面上建立一個文件夾【MyStm32】文件夾 2、在【MyStm32】文件夾下建立【USER】、【FWlib】、【CMSIS】、【Output】、【Listing】文件夾。 【USER】:存放用戶自定義的應用程序 【FWlib】:存放庫文件 【CMSIS】:存放M3系列單片機通用的文件 【Output】:存放編譯器編譯后輸出的文件 【Listing】:編譯器編譯過程中產生的文件 3、將STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver 的【inc】跟【src】 這兩個文件夾拷貝到【FWlib】。 【inc】、【src】片上外設驅動的源文件和頭文件。 4、\STM32F10x_StdPeriph_Lib_V3.5.0\Project\STM32F10x_StdPeriph_Template 下的 main.c、stm32f10x_conf.h、stm32f10x_it.h、stm32f10x_it.c 、system_stm32f10x.c 拷貝到【USER】 stm32f10x_conf.h:配置文件 stm32f10x_it.h、stm32f10x_it.c:中斷函數文件。 system_stm32f10x.c:ARM公司提供的符合CMSIS標準的庫文件 5、STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup的 【arm】文件夾的拷貝到 MyStm32\CMSIS\startup。 這些都是用匯編編寫的驅動文件,STM32F103ZE芯片是大容量Flash,應選擇startup_stm32f10x_hd.s 6、STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\CoreSupport 的 core_cm3.c 和 core_cm3.h 也拷貝到【CMSIS】文件夾下。 7、STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x 的 stm32f10x.h、system_stm32f10x.c、system_stm32f10x.h 拷貝到【CMSIS】文件夾下。 8、用keil MDK 建立工程文件到【USER】文件夾下,工程名為【STM32-DEMO】 在該工程下建立四個組: 【STARTCODE】:存放啟動代碼 添加 startup_stm32f10x_hd.s 文件 【USER】:存放用戶自定義的應用程序 添加main.c、stm32f10x_it.c 【FWlib】:存放庫文件 按需添加src里面的驅動文件 【CMSIS】:存放M3系列單片機通用的文件 添加core_cm3.c、system_stm32f10x.c
添加 USE_STDPERIPH_DRIVER 是為了屏蔽編譯器的默認搜索路徑,轉而使用我們添加 到工程中的 ST 的庫,添加 STM32F10X_HD 是因為我們用的芯片是大容量的,添加了 STM32F10X_HD 這個宏之后,庫文件里面為大容量定義的寄存器我們就可以用了。 芯片是小或中容量的時候宏要換成STM32F10X_LD或者STM32F10X_MD。其實不管是什么容量的,
我們只要添加上 STM32F10X_HD 這個宏即可,當你用小或者中容量的芯 片時,那些為大容量定義的寄存器我不去訪問就是了,反正也訪問不了。
流水燈實驗:
只用到 配置GPIO功能和配置時鐘功能,所以在 stm32f10x_conf.h
只需要包含兩個頭文件即可:
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
流水燈流程:
1、開啟指定GPIO的外設時鐘 -> RCC_APB2PeriphClockCmd()
2、設置 GPIO_InitTypeDef 指定引腳、工作狀態、輸出頻率
3、根據 調用GPIO_Init() 初始化IO -> GPIO_Init()
4、設置IO輸出高低電平,實現控制LED燈的亮滅。
led.c:
#include "led.h"
void LED_GPIO_Config(void)
{
// 定義一個 GPIO_InitTypeDef 類型的結構體
GPIO_InitTypeDef GPIO_InitStructure;
// 開啟 GPIOC 的外設時鐘
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE);
// 選擇要控制的 GPIOC 引腳
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
// 設置引腳模式為通用推挽輸出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
// 設置引腳速率為 50Mhz
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
// 調用庫函數,初始化GPIOC
GPIO_Init(GPIOC, &GPIO_InitStructure);
// 設置GPIOC3、GPIOC4、GPIOC5 為高電平
GPIO_SetBits(GPIOC, GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5);
}
led.h:
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#define ON 0
#define OFF 1
//帶參宏,可以向聯函數一樣使用
#define LED1(a) if(a) \
GPIO_SetBits(GPIOC, GPIO_Pin_3);\
else \
GPIO_ResetBits(GPIOC, GPIO_Pin_3)
#define LED2(a) if(a) \
GPIO_SetBits(GPIOC, GPIO_Pin_4);\
else \
GPIO_ResetBits(GPIOC, GPIO_Pin_4)
#define LED3(a) if(a) \
GPIO_SetBits(GPIOC, GPIO_Pin_5);\
else \
GPIO_ResetBits(GPIOC, GPIO_Pin_5)
void LED_GPIO_Config(void);
#endif
main.c
#include "stm32f10x.h"
#include "led.c"
void Delay(__IO u32 nCount)
{
for(; nCount !=0; nCount--);
}
int main(void)
{
LED_GPIO_Config();
while (1)
{
LED1(ON);
Delay(0x0FFFEF);
LED1(OFF);
Delay(0x0FFFEF);
}
}
-----------------------------------------------------------------------
1. typedef enum
2. {
3. GPIO_Speed_10MHz = 1, //枚舉常量,值為 1,代表輸出速率最高為 10MHz
4. GPIO_Speed_2MHz, //對不賦值的枚舉變量,自動加 1,此常量值為 2
5. GPIO_Speed_50MHz //常量值為 3
6. }GPIOSpeed_TypeDef
1. typedef enum
2. {GPIO_Mode_AIN = 0x0, //模擬輸入模式
3. GPIO_Mode_IN_FLOATING = 0x04, //浮空輸入模式
4. GPIO_Mode_IPD = 0x28, //下拉輸入模式
5. GPIO_Mode_IPU = 0x48, //上拉輸入模式
6. GPIO_Mode_Out_OD = 0x14, //開漏輸出模式
7. GPIO_Mode_Out_PP = 0x10, //通用推挽輸出模式
8. GPIO_Mode_AF_OD = 0x1C, //復用功能開漏輸出
9. GPIO_Mode_AF_PP = 0x18 //復用功能推挽輸出
10. }GPIOMode_TypeDef;
1. typedef struct
2. {
3. uint16_t GPIO_Pin; /*指定將要進行配置的 GPIO 引腳*/
4. GPIOSpeed_TypeDef GPIO_Speed; /*指定 GPIO 引腳可輸出的最高頻率*/
5. GPIOMode_TypeDef GPIO_Mode; /*指定 GPIO 引腳將要配置成的工作狀態*/
6. }GPIO_InitTypeDef;
實例:
31. /*選擇要控制的 GPIOC 引腳*/
32. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5;
33.
34. /*設置引腳模式為通用推挽輸出*/
35. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
36.
37. /*設置引腳速率為 50MHz */
38. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
-----------------------------------------------------------------------
GPIO控制:
void GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_InitStruct);
功能:根據 GPIOx 初始化GPIO
參數1:允許指為 GPIOA、GPIOB、GPIOC...GPIOG
參數2:GPIO_InitTypeDef 類型的指針,包含了指定引腳、輸出工作狀態和頻率
實例:
40. /*調用庫函數,初始化 GPIOC*/
41. GPIO_Init(GPIOC, &GPIO_InitStructure);
void GPIO_SetBits ( GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin)
功能:設置GPIO引腳輸出高電平
參數1:允許指為 GPIOA、GPIOB、GPIOC...GPIOG
參數2:要控制的引腳號 Pin0~Pin15
void GPIO_ResetBits ( GPIO_TypeDef * GPIOx, uint16_t GPIO_Pin)
功能:設置GPIO引腳輸出低電平
參數1:允許指為 GPIOA、GPIOB、GPIOC...GPIOG
參數2:要控制的引腳號 Pin0~Pin15
-----------------------------------------------------------------------
外設時鐘控制:
void RCC_APB2PeriphResetCmd ( uint32_t RCC_APB2Periph, FunctionalState NewState)
功能:開啟或關閉的掛接在APB2總線上的外設時鐘
參數1:
RCC_APB2Periph_AFIO,
RCC_APB2Periph_GPIOA,
RCC_APB2Periph_GPIOB,
RCC_APB2Periph_GPIOC,
RCC_APB2Periph_GPIOD,
RCC_APB2Periph_GPIOE,
RCC_APB2Periph_GPIOF,
RCC_APB2Periph_GPIOG,
RCC_APB2Periph_ADC1,
RCC_APB2Periph_ADC2,
RCC_APB2Periph_TIM1,
RCC_APB2Periph_SPI1,
RCC_APB2Periph_TIM8,
RCC_APB2Periph_USART1,
RCC_APB2Periph_ADC3,
RCC_APB2Periph_TIM15,
RCC_APB2Periph_TIM16,
RCC_APB2Periph_TIM17,
RCC_APB2Periph_TIM9,
RCC_APB2Periph_TIM10,
RCC_APB2Periph_TIM11
參數2:使能外設時鐘或關閉外設時鐘
ENABLE or DISABLE
void RCC_APB1PeriphClockCmd ( uint32_t RCC_APB1Periph, FunctionalState NewState)
功能:開啟或關閉的掛接在APB2總線上的外設時鐘
參數1:
RCC_APB1Periph_TIM2,
RCC_APB1Periph_TIM3,
RCC_APB1Periph_TIM4,
RCC_APB1Periph_TIM5,
RCC_APB1Periph_TIM6,
RCC_APB1Periph_TIM7,
RCC_APB1Periph_WWDG,
RCC_APB1Periph_SPI2,
RCC_APB1Periph_SPI3,
RCC_APB1Periph_USART2,
RCC_APB1Periph_USART3,
RCC_APB1Periph_USART4,
RCC_APB1Periph_USART5,
RCC_APB1Periph_I2C1,
RCC_APB1Periph_I2C2,
RCC_APB1Periph_USB,
RCC_APB1Periph_CAN1,
RCC_APB1Periph_BKP,
RCC_APB1Periph_PWR,
RCC_APB1Periph_DAC,
RCC_APB1Periph_CEC,
RCC_APB1Periph_TIM12,
RCC_APB1Periph_TIM13,
RCC_APB1Periph_TIM14
參數2:使能外設時鐘或關閉外設時鐘
ENABLE or DISABLE
【注意】:如果用到了I/O引腳的復用功能,則還要開啟其復用功能的時鐘。
如GPIOC的Pin4還可以作為ADC1的輸入引腳,我們把它作為ADC1來使用,
那么除了要開啟GPIOC時鐘外,還需要開啟ADC1的時鐘。
例子:
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd( RCC_APB2Periph_ADC1, ENABLE);
-----------------------------------------------------------------------
-----------------------------------------------------------------------
啟動文件:startup_stm32f10x_hd.s
1. ;Reset_Handler 子程序開始 芯片被復位的時候會執行
2. Reset_Handler PROC
3.
4. ;輸出子程序 Reset_Handler 到外部文件
5. EXPORT Reset_Handler [WEAK]
6.
7. ;從外部文件中引入 main 函數
8. IMPORT __main
9.
10. ;從外部文件引入 SystemInit 函數
11. IMPORT SystemInit
12.
13. ;把 SystemInit 函數調用地址加載到通用寄存器 r0
14. LDR R0, =SystemInit
15.
16. ;跳轉到 r0 中保存的地址執行程序(調用 SystemInit 函數 用于設置系統時鐘)
17. BLX R0
18.
19. ;把 main 函數調用地址加載到通用寄存器 r0
20. LDR R0, =__main
21.
22. ;跳轉到 r0 中保存的地址執行程序(調用 main 函數)
23. BX R0
24.
25. ;Reset_Handler 子程序結束
26. ENDP
啟動代碼中,它會調用SystemInit(),SysteInit()會先將與配置時鐘相關的寄存器復位為默認值。
然后它再調用SetSysClock()函數:
1. static void SetSysClock(void)
2. {
3. #ifdef SYSCLK_FREQ_HSE
4. SetSysClockToHSE();
5. #elif defined SYSCLK_FREQ_24MHz
6. SetSysClockTo24();
7. #elif defined SYSCLK_FREQ_36MHz
8. SetSysClockTo36();
9. #elif defined SYSCLK_FREQ_48MHz
10. SetSysClockTo48();
11. #elif defined SYSCLK_FREQ_56MHz
12. SetSysClockTo56();
13. #elif defined SYSCLK_FREQ_72MHz
14. SetSysClockTo72(); // 配置相關寄存器
15. #endif
19. }
SetSysClock()才是真正配置系統時鐘函數,會根據宏來進行不同頻率的配置。
由system_stm32f10x.c文件啟用宏。
1. #if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
2. /* #define SYSCLK_FREQ_HSE HSE_VALUE */
3. #define SYSCLK_FREQ_24MHz 24000000
4. #else
5. /* #define SYSCLK_FREQ_HSE HSE_VALUE */
6. /* #define SYSCLK_FREQ_24MHz 24000000 */
7. /* #define SYSCLK_FREQ_36MHz 36000000 */
8. /* #define SYSCLK_FREQ_48MHz 48000000 */
9. /* #define SYSCLK_FREQ_56MHz 56000000 */
10. #define SYSCLK_FREQ_72MHz 72000000
11. #endif
-----------------------------------------------------------------------
|