stm32开发系统配置

本节课我们以stm32为基础,keil5为开发平台,进行ISP(在线系统编程).
//注:我们通常用stm32做一些开发应用,所以用的基本上是顶层函数,对于那些用来设置核内寄存器和外设的地址的底层函数,只需要了解即可。除非你真的需要对ARM的架构精通,但首先你必须有很好的汇编和c/c++基础。
注:库是架设(位于)在寄存器和用户驱动程序之间的代码,向上给用户提供配置底层寄存器的接口,​向下得到用户的配置信息后,对底层寄存器进行配置。因为库的存在,用户不需要直接按位配置底层寄存器了,只需配置库提供的函数接口,对寄存器的操作,交给库函数来完成。


一.基本C程序

1.编程步骤
对于keil的基本配置,创建工程。
添加4个组,分别是startup+CMIS+FWib+output+listing+user然后向这四个组里面添加相应的.c文件和.h文件.

对各个源文件和头文件的解释:

A.startup_stm32f10x_hd.s—->startup

相对于ARM上一代的主流ARM7/ARM9内核架构,新一代Cortex内核架构的启动方式有了比较大的变化。ARM7/ARM9内核的控制器在复位后,CPU会从存储空间的绝对地址0x000000取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为0x000000(PC = 0x000000)同时中断向量表的位置并不是固定的。而Cortex-M3内核则正好相反,有3种情况:
1、 SRAM启动模式:通过boot引脚设置可以将中断向量表定位于SRAM区,即起始地址为0x2000000,同时复位后PC指针位于0x2000000处;
2、 用户闪存启动模式:通过boot引脚设置可以将中断向量表定位于FLASH区,即起始地址为0x8000000,同时复位后PC指针位于0x8000000处;
3、 系统存储器启动模式:即系统从芯片内部一块特定的区域启动,通过boot引脚设置可以将中断向量表定位于内置Bootloader区(就这个区域),本文不对这种情况做论述;
而Cortex-M3内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在Cortex-M3内核复位后,会自动从起始地址的下一个32位空间取出复位中断入口向量,跳转执行复位中断服务程序。(这句话啥意思呢?向量表从 FLASH 的 0 地址开始放置,以 4 个字节为一个单位,地址 0 存放的是栈顶地址,0X04 存放的是复位程序的地址)
对比ARM7/ARM9内核,Cortex-M3内核则是固定了中断向量表的位置而起始地址是可变化的。

上文引自http://www.amobbs.com/thread-5462931-1-1.html

http://blog.csdn.net/wqx521/article/details/50925553
http://www.openedv.com/posts/list/5903.htm

                DCD     WWDG_IRQHandler            ; Window Watchdog
                DCD     PVD_IRQHandler             ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler          ; Tamper
                DCD     RTC_IRQHandler             ; RTC
                DCD     FLASH_IRQHandler           ; Flash
                DCD     RCC_IRQHandler             ; RCC
                DCD     EXTI0_IRQHandler           ; EXTI Line 0
                DCD     EXTI1_IRQHandler           ; EXTI Line 1
                DCD     EXTI2_IRQHandler           ; EXTI Line 2
                DCD     EXTI3_IRQHandler           ; EXTI Line 3
                DCD     EXTI4_IRQHandler           ; EXTI Line 4
                DCD     DMA1_Channel1_IRQHandler   ; DMA1 Channel 1
                DCD     DMA1_Channel2_IRQHandler   ; DMA1 Channel 2
                DCD     DMA1_Channel3_IRQHandler   ; DMA1 Channel 3
                DCD     DMA1_Channel4_IRQHandler   ; DMA1 Channel 4
                DCD     DMA1_Channel5_IRQHandler   ; DMA1 Channel 5
                DCD     DMA1_Channel6_IRQHandler   ; DMA1 Channel 6
                DCD     DMA1_Channel7_IRQHandler   ; DMA1 Channel 
 //DCD给相应的中断向量分配内存

外部中断向量表存放的是中断向量(存放中断函数的地址),我们要根据外设的这个中断向量写入我们的中断函数。

B. core_cm3.c—->CMIS.system_stm32f10x.c—->CMIS

core_cm3.c:
CMSIS有三个层:核内外设访问层Core Peripheral Access Layer(CPAL),中间件访问层Middleware Access Layer(MWAL),设备访问层(Device Peripheral Access Layer)。
CPAL用于访问内核的寄存器和组件,如NVIC,调试系统等。该层是由ARM实现的。MWAL用于对中间件的访问,现在该层还未实现。(也不知道所谓的中间件是什么东西)。DPAL用于定义一些硬件寄存器的地址和一些外设访问函数,由芯片制造商实现。
CPAL层的实现就是core_cm3.c文件.
DPAL层的实现就是system_stm32f10x.c文件(似乎还应该加上外设的函数库)。
上引自http://www.cnblogs.com/king-77024128/articles/2512997.html
下面介绍system_stm32f10x.c:
由st公司提供,该文件的功能是设置系统的时钟和总线时钟。

C.stm32f10x_conf.h—->user.stm32f10x_lib.h—->user.

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F10x_CONF_H
#define __STM32F10x_CONF_H

/* Includes ------------------------------------------------------------------*/
/* Uncomment/Comment the line below to enable/disable peripheral header file inclusion */
//#include "stm32f10x_adc.h"
//#include "stm32f10x_bkp.h"
//#include "stm32f10x_can.h"
//#include "stm32f10x_cec.h"
//#include "stm32f10x_crc.h"
//#include "stm32f10x_dac.h"
//#include "stm32f10x_dbgmcu.h"
//#include "stm32f10x_dma.h"
//#include "stm32f10x_exti.h"
//#include "stm32f10x_flash.h"
//#include "stm32f10x_fsmc.h"
#include "stm32f10x_gpio.h"
//#include "stm32f10x_i2c.h"
//#include "stm32f10x_iwdg.h"
//#include "stm32f10x_pwr.h"
#include "stm32f10x_rcc.h"
//#include "stm32f10x_rtc.h"
//#include "stm32f10x_sdio.h"
//#include "stm32f10x_spi.h"
//#include "stm32f10x_tim.h"
//#include "stm32f10x_usart.h"
//#include "stm32f10x_wwdg.h"
#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */

stm32f10x_conf.h中包含了所有外设的头文件,供用户根据自己的需要选择使用,比如点亮LED,用到stm32f10x_gpio.h,stm32f10x_rcc.h.
只需要将其前面的注释//去掉即可。(见尾部添加USE_STDPERIPH_DRIVER的说明)

stm32f10x_lib.h:2.0版本的ST库函数头文件,在我们开发板老版本的寄存器版本例程上面有用到,新版本例程一律没用了,可忽略。

D.stm32f10x_it.h—->user.stm32f10x.h—->user.

stm32f10x_it.h: 相比较老版,系统只给出了内部中断函数(含内部异常中断默认函数,一般是while(1)无限循环的),所以说如果用到外设中断的话,要自己加上去。中断入口函数都是固件库已经定义好的,你可以到启动文件里看那个中断向量表,并找到对应的函数.如串口1的中断函数就是void USART1_IRQHandler().(注:中断函数不需要事先声明)
stm32f10x_it.h:这个就不多说了,给出了寄存器映射地址。


第一种:查询方式

下面开始进入正题:
开始:

A.系统上电初始化

keil5默认系统外部晶振为8MHz,这里可以在options里面改动。
若不是,比如选择12MHz的XTAL,则都要及时修改。

打开stm32f10x.h

#if !defined  HSE_VALUE         //如果HSE_VALUE未定义
 #ifdef STM32F10X_CL             //若是通信型产品
  #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */
 #else 
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
 #endif /* STM32F10X_CL */
#endif /* HSE_VALUE */

再打开system_stm32f10x.h,这里有一个系统时钟的宏定义。

#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
 #define SYSCLK_FREQ_24MHz  24000000
#else
/* #define SYSCLK_FREQ_HSE    HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz  24000000 */ 
/* #define SYSCLK_FREQ_36MHz  36000000 */
/* #define SYSCLK_FREQ_48MHz  48000000 */
/* #define SYSCLK_FREQ_56MHz  56000000 */
#define SYSCLK_FREQ_72MHz  72000000           //也就是103系列能跑到的最大值72M(系统时钟对应的最大晶振频率)
#endif

当系统复位后,首先执行Systeminit函数,再进入Reset Handler()函数,这个函数调用了main(),所以顺理成章的进入main()函数。

__Vectors       DCD     __initial_sp               ; Top of Stack
                DCD     Reset_Handler              ; Reset Handler
                DCD     NMI_Handler                ; NMI Handler
                DCD     HardFault_Handler          ; Hard Fault Handler
                DCD     MemManage_Handler          ; MPU Fault Handler
                DCD     BusFault_Handler           ; Bus Fault Handler
                DCD     UsageFault_Handler         ; Usage Fault Handler

其中void Systeminit()的作用是先将配置时钟相关的寄存器都复位为默认值,再调用SetSysClock()设置时钟。
如下:

* @brief   CMSIS Cortex-M3 Device Peripheral Access Layer System Source File.
  * 
  * 1.  This file provides two functions and one global variable to be called from 
  *     user application:
  *      - SystemInit(): Setups the system clock (System clock source, PLL Multiplier
  *                      factors, AHB/APBx prescalers and Flash settings). 
  *                      This function is called at startup just after reset and 
  *                      before branch to main program. This call is made inside
  *                      the "startup_stm32f10x_xx.s" file.
  *

B.时钟参数配置

说实话这个内容真特么多,调用各种时钟配置函数,一定要看图看表看库才能理解。

/***********************rcc.c************************/

#include "rcc.h"
void RCC_Configuration()
{
      ErrorStatus  HSEStartUpStatus;
      RCC_DeInit();
      RCC_HSEConfig(RCC_HSE_ON);

      HSEStartUpStatus=RCC_WaitForHSEStartUp();

     if(HSEStartUpStatus==SUCCESS)
     {
         RCC_HCLKConfig(RCC_SYSCLK_Div1);
         RCC_PCLK2Config(RCC_HCLK_Div1);
         RCC_PCLK1Config(RCC_HCLK_Div2);
         RCC_ADCCLKConfig(RCC_PCLK2_Div4);

         FLASH_SetLatency(FLASH_Latency_2);       
         FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
         RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
         RCC_PLLCmd(ENABLE);

         while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET)
         {}
         RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
         while(RCC_GetSYSCLKSource()!=0x08)
         {}

            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    //使能GPIOA的时钟
     }
 }

/***********************rcc.h************************/

#ifndef _RCC_H
#define _RCC_H

#include "stm32f10x_rcc.h"
#include "stm32f10x_flash.h"

 void RCC_Configuration(void);

#endif

这里写图片描述

内部时钟由RC振荡器产生,其中HSI为8MHz,LSI为40kHz。
外部时钟以外部晶振作为时钟源,HSE可取4~16MHz,LSE为32.768kHz.
其中在芯片刚上电的时候,由于内部时钟起振较快,所以默认采用内部高速时钟HSI(复位时依然是这个)。而外部时钟是由外部晶振输入,在精度和稳定性上面都有很大优势,所以我们上电之后再通过RCC_Configuetion(),采用外部时钟信号。
分频是因为stm32有高速外设和低速外设,各种外设的工作频率不一样,所以要把高速外设和低速外设分开管理,每个外设都有相应的时钟使能,所以用到哪个开哪个。

这里写图片描述

AHB常和DMA连接,
APB1总线连接高速外设,APB2总线连接系统外设和中断控制。

这里写图片描述

C.引脚参数配置

/********************gpio.c*************************/

#include "gpio.h"
void GPIOConfiguration()
{
     GPIO_InitTypeDef  GPIO_InitStructure;

     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
     GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
     GPIO_Init(GPIOA,&GPIO_InitStructure);

     GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
     GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
     GPIO_Init(GPIOA,&GPIO_InitStructure);
}

/********************gpio.h*************************/

#ifndef _GPIO_H     //如果宏定义未定义,则...
#define _GPIO_H

#include "stm32f10x_gpio.h"

void GPIO_Configuration(void);  

#endif

看库文件能明白

D.查询点亮

#include "stm32f10x.h"
#include "gpio.h"
#include "rcc.h"

extern void Delay(vu32 nCount)
{
    for(;nCount>0;nCount--)
        ;
}

void main()
 {
     #ifdef DEBUG
            debug();
     #endif

     RCC_Configuration();
     GPIO_Configuration();

     while(1)
     {
            if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_2)==0x00)
            {
                GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction) (1));
                GPIO_WriteBit(GPIOA,GPIO_Pin_1,(BitAction) (1));
                Delay(1000);
            }       
            else
         {
                GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction) (0));
                GPIO_WriteBit(GPIOA,GPIO_Pin_1,(BitAction) (0));
                Delay(1000);
            }
     }
 }

对于开头的条件编译指令:

#ifdef DEBUG
    debug();
#endif

在工程设置里有一些设置会对该工程自动产生一系列的宏,用以控制程序的编译和运行。如果你把代码夹在#ifdef DEBUG 和对应的 #endif 中间,那么这段代码只有在调试(DEBUG)下才会被编译。也就是说,如果你在RELEASE模式下,这些代码根本就不会存在于你的最终代码里头。

注:在keil配置中应该

这里写图片描述

1)在c/c++栏的处理器标识定义里面添加USE_STDPERIPH_DRIVER和STM32F10X_MD.其中:
A.USE_STDPERIPH_DRIVER:使用标准外设文件.在stm32f10x.h中查找可得

#ifdef USE_STDPERIPH_DRIVER
  #include "stm32f10x_conf.h"
#endif

若添加USE这样的操作,可以使用外设头文件,否则就要自己手动添加了.

B.STM32F10X_MD:根据所用芯片FLASH大小选择宏定义,在stm32f10x.h中查找可得

#if !defined (STM32F10X_LD) && !defined (STM32F10X_LD_VL)\&& !defined (STM32F10X_MD) && !defined (STM32F10X_MD_VL)\
&& !defined (STM32F10X_HD) && !defined (STM32F10X_HD_VL)&& !defined (STM32F10X_XL) && !defined (STM32F10X_CL) 

  /* #define STM32F10X_LD */     /*!< STM32F10X_LD: STM32 Low density devices */
  /* #define STM32F10X_LD_VL */  /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */  
  /* #define STM32F10X_MD */     /*!< STM32F10X_MD: STM32 Medium density devices */
  /* #define STM32F10X_MD_VL */  /*!< STM32F10X_MD_VL: STM32 Medium density Value Line devices */  
  /* #define STM32F10X_HD */     /*!< STM32F10X_HD: STM32 High density devices */
  /* #define STM32F10X_HD_VL */  /*!< STM32F10X_HD_VL: STM32 High density value line devices */  
  /* #define STM32F10X_XL */     /*!< STM32F10X_XL: STM32 XL-density devices */
  /* #define STM32F10X_CL */     /*!< STM32F10X_CL: STM32 Connectivity line devices */
#endif

这里写图片描述

2)include paths:凡是所用到的头文件一律添加到相应路径。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值