环境搭建:
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
配置MDK:
添加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_tGPIO_Pin)
功能:设置GPIO引脚输出高电平
参数1:允许指为 GPIOA、GPIOB、GPIOC...GPIOG
参数2:要控制的引脚号 Pin0~Pin15
void GPIO_ResetBits( GPIO_TypeDef *GPIOx, uint16_tGPIO_Pin)
功能:设置GPIO引脚输出低电平
参数1:允许指为 GPIOA、GPIOB、GPIOC...GPIOG
参数2:要控制的引脚号 Pin0~Pin15
-----------------------------------------------------------------------
外设时钟控制:
void RCC_APB2PeriphResetCmd( uint32_tRCC_APB2Periph,FunctionalStateNewState)
功能:开启或关闭的挂接在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_tRCC_APB1Periph, FunctionalStateNewState)
功能:开启或关闭的挂接在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. EXPORTReset_Handler [WEAK]
6.
7.;从外部文件中引入 main 函数
8. IMPORT__main
9.
10.;从外部文件引入 SystemInit 函数
11. IMPORTSystemInit
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_24MHz24000000
4.#else
5./* #define SYSCLK_FREQ_HSE HSE_VALUE */
6./* #define SYSCLK_FREQ_24MHz24000000 */
7./* #define SYSCLK_FREQ_36MHz36000000 */
8./* #define SYSCLK_FREQ_48MHz48000000 */
9./* #define SYSCLK_FREQ_56MHz56000000 */
10.#define SYSCLK_FREQ_72MHz72000000
11.#endif
-----------------------------------------------------------------------