学习STM32基础(二)笔记

一、储存器映射和寄存器映射

STM32的寻址范围:

(1)32位的单片机可以有32根地址线(每根地址线有两种状态:导通或不导通)

(2)单片机内存地址访问的存储单元是按字节编址的(而不是bit)

STM32寻址大小:2^{32}=4G(字节)

STM32寻址范围:0x0000 0000 ~ 0xFFFF FFFF

1、存储器映射

        存储器指可以存储数据的设备,本身没有地址信息,对存储器分配地址的过程称为存储器映射。

存储器功能划分(F1为例)

ST将4G(2^{32})地址空间分成了8块

Block0(FLASH)功能划分

 Block1(SRAM)功能划分

  Block2(外设)功能划分

2、寄存器映射

        寄存器是单片机内部一种特殊的内存,可以实现对单片机各个功能的控制(寄存器就是单片机内部的控制机构)。寄存器是特殊的存储器,给寄存器地址命名的过程,就叫寄存器映射。 

STM32寄存器分类

寄存器描述:

寄存器地址计算:

     将寄存器地址分为三个部分:

        (1)总线基地址(BUS_BASE_ADDR

        (2)外设基于总线基地址的偏移量(PERIPH_OFFSET

        (3)寄存器相对外设基地址的偏移量(REG_OFFSET

寄存器地址 = BUS_BASE_ADDRPERIPH_OFFSET + REG_OFFSET

总线基地址

 APB1总线的基地址,也叫外设基地址(PERIPH_BASE);此表的偏移量:是相对外设基地址(PERIPH_BASE)来说的

GPIO外设基地址及偏移量

 此表的偏移量:是相对APB2外设基地址(APB2PERIPH_BASE)来说的

 此表的偏移量:是相对GPIOA外设基地址(GPIOA_BASE)来说的。

例如:GPIOA_ODR寄存器地址计算过程:

二、STM32时钟系统

1.1认识时钟树

        简单来说,时钟是具有周期性的脉冲信号,最常用的是占空比50%的方波(时钟就是*,/(分频))

 STM32F103时钟树简图

 STM32F103时钟树图

从图中可以看到,STM32共有五个时钟源,分别是HSI、HSE、LSI、LSE和PLL ,下面分别对它们进行讲解:

        ①、HSI是高速内部时钟,RC振荡器,频率为8MHz,精度不高。
   ②、HSE是高速外部时钟,可接石英/陶瓷谐振器,或者接外部时钟源,频率范围为4MHz~16MHz。
   ③、LSI是低速内部时钟,RC振荡器,频率为40kHz,提供低功耗时钟。独立看门狗的时钟源只能是 LSI ,同时LSI 还可以作为 RTC 的时钟源。
   ④、LSE是低速外部时钟,接频率为32.768kHz的石英晶体。是主要的RTC时钟源。
   ⑤、PLL为锁相环倍频输出,其时钟输入源可选择为HSI/2、HSE或者HSE/2。倍频可选择为2~16倍,但是其输出频率最大不得超过72MHz。

  上面我们简要概括了STM32 的时钟源,那么这 5 个时钟源是怎么给各个外设以及系统提供时钟的呢?这里我们将一一讲解。这里我们使用官方手册提供的框图(图一)进行讲解,图中我们用A~E 标示我们要讲解的地方。

A 、 MCO 是 STM32 的一个时钟输出 IO(PA8) PA8),它可以选择一个时钟信号输出 可以
选择为 PLL 输出的 2 分频、 HSI 、 HSE 、或者 系统 时钟 。这个时钟可以用来给外
部其他系统提供时钟源。
B  、 RTC 时钟源,从图上可以看出, RTC 的时钟源可以选择 LSI LSE ,以及
HSE 的 128 分频。
C  、 从图中可以看出 C 处 USB 的时钟是来自 PLL 时钟源。 STM32 中有一个全速 功能
的 USB 模块 ,其串行 接口 引擎需要 一个频率为 48MHz 的时钟源。该时钟源只能
从 PLL 输出端获取,可以选择为 1.5 分频或者 1 分频,也就是,当需要使用 USB
模块时, PLL 必须使能,并且时钟频率配置为 48MHz 或 72MHz 。
D 、  STM32 的系统时钟 SYSCLK ,它 是供 STM32 中绝大部分部件 工作 的时
钟源 。 系统时钟可选择为 PLL 输出、 HSI 或者 HSE 。系统时钟最大频率 为 72MHz
当然你也可以超频,不过一般情况为了系统稳定性是没有必要冒风险去超频的。
E  、 其他所有外设。从时钟图上可以看出,其他所有外设的时钟最终来源都是 SYSCLK 。 SYSCLK 通过 AHB 分频器分频后送给各模块使用 。这些模块包括:
        ① AHB 总线、内核、内存和 DMA 使用的 HCLK 时钟。
        ② 通过 8 分频后送给 Cortex 的系统 定时器 时钟 ,也就是 systick 了 。
        ③ 直接送给 Cortex 的空闲运行时钟 FCLK 。
        ④ 送给 APB1 分频器。 APB1 分频器输出一路供 APB1 外设使用 (PCLK1 ,最大频率         36MHz)36MHz),另一路送给定时器 (Timer)2 、 3 、 4 倍频器使用。
        ⑤送给 APB2 分频器。 APB2 分频器分频输出一路供 APB2 外设使用 (PCLK2最大频率         72MHz)72MHz),另一 路送给定时器 (Timer)1 倍频器使用。
        APB1和APB2的区别: APB1上面连的是低速外设,包括电脑接口、备份接口、CAN、USB、I2C1、I2C2 、 UART2 、 UART3 等等, APB2 上面连接的是高速外设包括 UART1 、 SPI1 、 Timer1 、 ADC1 、 ADC2 、所有普通 IO 口 (PA~ 、第二功能 IO 口 等。 APB2 下面所挂的外设的时钟要比 APB1 的高 。

        在以上的时钟输出中,有很多 是带使能控制的,例如 AHB 总线 时钟、内核时钟、各种 APB1外设、 APB2 外设等等。当需要使用某模块时,记得一定要先使能对应的时钟。

STM32CubeMX时钟树(F103)

时钟源名称

频率

材料

用途

高速外部振荡器(HSE)

4~16MHz

晶体/陶瓷

SYSCLK/RTC

低速外部振荡器(LSE)

32.768KHz

晶体/陶瓷

RTC

高速内部振荡器(HSI)

8MHz

RC

SYSCLK

低速内部振荡器(LSI)

40KHz

RC

RTC/IWDG

时钟源名称

频率

材料

用途

高速外部振荡器(HSE)

4~16MHz

晶体/陶瓷

SYSCLK/RTC

低速外部振荡器(LSE)

32.768KHz

晶体/陶瓷

RTC

高速内部振荡器(HSI)

8MHz

RC

SYSCLK

低速内部振荡器(LSI)

40KHz

RC

RTC/IWDG

 STM32F4时钟树简图

STM32 F429时钟树图

STM32CubeMX时钟树(F429 

 STM32F7时钟树简图

2.1 系统时钟配置步骤

        1,配置HSE_VALUE :告诉HAL库外部晶振频率,stm32xxxx_hal_conf.h

        2,调用SystemInit()函数(可选):在启动文件中调用, system_stm32xxxx.c定义

        3,选择时钟源,配置PLL:通过HAL_RCC_OscConfig()函数设置

        4,选择系统时钟源,配置总线分频器:通过HAL_RCC_ClockConfig()函数设置

        5,配置扩展外设时钟(可选):通过HAL_RCCEx_PeriphCLKConfig()函数设置

        STM32时钟系统的配置除了初始化的时候在 system_stm32f10x.c 中的 SystemInit 函数中外,其他的配置主要在 stm32f10x_rcc.c 文件中,里面有很多时钟设置函数,大家可以打开这个文件浏览一下,基本上看看函数的名称就知道这个函数的作用。对于系统时钟,默认情况下是SystemInit 函数的 SetSysClock() 函数中间判断的,而设置是通过宏定义设置的。我们可以看看 SetSysClock() 函数体:

static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSE
  SetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHz
  SetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHz
  SetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHz
  SetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHz
  SetSysClockTo56();  
#elif defined SYSCLK_FREQ_72MHz
  SetSysClockTo72();
#endif
}

上述代码就是判断系统宏定义的时钟是多少,然后设置相应值。系统默认宏定义是 72MHz:

 #define SYSCLK_FREQ_72MHz  72000000 //72MHZ

 #define SYSCLK_FREQ_36MHz  36000000 //36MHZ

 同时还要注意的是,当我们设置好系统时钟后,可以通过变量SystemCoreClock 获取系统时钟
值,如果系统是 72M 时钟,那么 SystemCoreClock =72000000 。这是在 system_stm32f10x.c 文件中设置的。

这里总结一下SystemInit() 函数中设置的系统时钟大小:

  • SYSCLK (系统时钟 =72MHz
  • AHB 总线时钟 使用 SYSCLK) =72MHz
  • APB1 总线时钟 ( =36MHz
  • APB2 总线时钟 ( =72MHz
  • PLL 时钟 =72MHz 

2.2时钟配置总流程

 在STM32单片机复位之后,首先进入startup程序:

; Reset handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  __main
                IMPORT  SystemInit
                LDR     R0, =SystemInit
                BLX     R0               
                LDR     R0, =__main
                BX      R0
                ENDP

可以看出,在进入main主程序之前,先触发了SystemInit()函数,这样就可以保证不需要每次都把时钟配置程序写入main.c文件了,同样,当你想要执行自定义时钟配置程序时也可以改动这个部分。首先我们看一下SystemInit()函数

void SystemInit (void)
{
  /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
  /* Set HSION bit */
  RCC->CR |= (uint32_t)0x00000001;
    SetSysClock();
}

       SystemInit()函数的作用就是使RCC_CR寄存器最低位置1即开启内部8MHz振荡器,然后转到SetSysClock()函数中。刚刚已经讲过了,对于系统时钟,默认情况下就是SystemInit 函数的 SetSysClock() 函数中间判断的

       由于把系统时钟频率通过宏定义设置为72MHz,所以这里直接进入最下面的程序SetSysClockTo72(),这也是为什么有的程序把这一段语句直接写入主函数,原则上来说这是可以的,但是没必要,因为startup启动文件已经帮我们定义好了。

下面就是SetSysClockTo72()程序部分,也就是最关键的部分

static void SetSysClockTo72(void)
{
  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
  
  /* SYSCLK, HCLK, PCLK2 and PCLK1 配置---------------------------*/    
  /* 使能 HSE */    
  RCC->CR |= ((uint32_t)RCC_CR_HSEON);//RCC_CR寄存器第16位置1,开启HSE
 
  /* 等待HSE就绪 超时退出 */
  do
  {
    HSEStatus = RCC->CR & RCC_CR_HSERDY;  //读取RCC_CR第17位
    StartUpCounter++;  
  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
 
  if ((RCC->CR & RCC_CR_HSERDY) != RESET) //读取RCC_CR第17位
  {
    HSEStatus = (uint32_t)0x01;   //HSE状态写1
  }
  else
  {
    HSEStatus = (uint32_t)0x00;   //HSE状态写0
  }  
 
  if (HSEStatus == (uint32_t)0x01) //FLASH寄存器部分可以不用管
  {
    /* 使能 Prefetch Buffer */
    FLASH->ACR |= FLASH_ACR_PRFTBE;
 
    /* Flash 2 wait state */
    FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
    FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    
 
 
    /* HCLK = SYSCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;  //RCC_CFGR寄存器第4~7位写0,即AHB不分频,即HCLK使用72MHz时钟
       
    /* PCLK2 = HCLK */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1; //RCC_CFGR寄存器第11~13位写0,APB2不分频,即PCLK2使用72MHz时钟
    
    /* PCLK1 = HCLK/2 */
    RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; //RCC_CFGR寄存器第8~10位写100,APB1二分频,即PCLK1使用36MHz时钟
 
#else    
    /*  PLL 配置: PLLCLK = HSE * 9 = 72 MHz */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
                                        RCC_CFGR_PLLMULL)); //RCC_CFGR寄存器的16~21位清零
    RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);//RCC_CFGR寄存器第16位置1,使HSE作为PLL输入时钟;17位置0不变,使HSE作为PLL输入时钟时不分频。RCC_CFGR寄存器第18~21位置0111,配置位PLL 9倍频输出给PLLCLK,即PLLCLK为72MHz
#endif /* STM32F10X_CL */
 
    /* 使能 PLL */
    RCC->CR |= RCC_CR_PLLON; //RCC->CR寄存器第24位置1,使能PLL
 
    /* 等待PLL就绪 */
    while((RCC->CR & RCC_CR_PLLRDY) == 0)  //读取PLL时钟就绪标志位(第25位)
    {
    }
    
    /* 选择PLL作为系统时钟源 */
    RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW)); //RCC->CFGR寄存器第0~1位清零
    RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    //RCC->CFGR寄存器第0~1位(SW位)置10,即PLL输出作为系统时钟
 
    /* 等待PLL被用作系统时钟源就绪 */
    while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) //等待RCC->CFGR寄存器第2~3位(SWS位)为10即就绪
    {
    }
  }
  else
  { /* If HSE fails to start-up, the application will have wrong clock 
         configuration. User can add here some code to deal with this error */
  }
}
#endif

 三、MAP文件浅析

          MAP文件是MDK编译代码后,产生的集程序、数据及IO空间的一种映射列表文件。简单说就是包括了:各种.c文件、函数、符号等的地址、大小、引用关系等信息。

MAP文件浅析

MAP文件组成

 四、启动模式

STM32F1启动模式

启动过程(以FLASH为例)

  • 44
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值