嵌入式开发(6)复位与时钟控制体系(RCC)

一、复位

       我们经常看到很多开发板、评测板会带个RESET键,即复位键。
       那我们为什么会有复位?举个简单的例子:在你玩电脑的时候,突然有个软件卡死了,导致整个电脑卡死了怎么办?重启呗。那在debug程序,调试固件的时候,烧录固件怎么没有显示,怎么死循环了,那也得给它重启一下,那总不能拔电源,一直关开电源叭,那这时就多了个复位键,相当于电脑的重启。

       M3STM32F3)复位与M4(STM32F4)差不多,M4在M3基础上延申,所以本文将以M4为例着重述说。

M3复位类型应用
上电复位接通电源后复位,复位整个系统。冷复位
系统复位复位处理器内核和系统元件,调试元件除外
JTAG-DP 复位复位 JTAG-DP 逻辑

注意:nTRST 复位对 JTAG-DP 控制器的状态初始化。nTRST 复位通常被 RealView™ ICE 模块用作调试器与系统的热插拔连接。nTRST 可以在不影响处理器正常工作的情况下对 JTAG-DP 控制器进行初始化。由于 nTRST 在处理器内已经被同步了,所以不必使之再同步。

M4复位类型

系统复位

只要发生以下事件之一,就会产生系统复位:

  1. NRST 引脚低电平(外部复位
  2. 窗口看门狗计数结束(WWDG 复位
  3. 独立看门狗计数结束(IWDG 复位
  4. 软件复位(SW 复位)(请参见软件复位)
  5. 低功耗管理复位(请参见低功耗管理复位)

软件复位

       可通过查看 RCC 时钟控制和状态寄存器 (RCC_CSR) 中的复位标志确定。要对器件进行软件复位,必须将 Cortex™-M4F 应用中断和复位控制寄存器中的SYSRESETREQ 位置 1。有关详细信息,请参见 Cortex™-M4F 技术参考手册。

低功耗管理复位

引发低功耗管理复位的方式有两种:

  1. 进入待机模式时产生复位:
    此复位的使能方式是清零用户选项字节中的 nRST_STDBY 位。使能后,只要成功执行
    进入待机模式序列,器件就将复位,而非进入待机模式。
  2. 进入停止模式时产生复位:
    此复位的使能方式是清零用户选项字节中的 nRST_STOP 位。使能后,只要成功执行
    进入停止模式序列,器件就将复位,而非进入停止模式

电源复位

只要发生以下事件之一,就会产生电源复位:

  1. 上电/掉电复位(POR/PDR 复位)或欠压 (BOR) 复位
  2. 在退出待机模式时
    在这里插入图片描述

备份域复位

       备份域复位会将所有 RTC 寄存器和 RCC_BDCR 寄存器复位为各自的复位值。BKPSRAM 不受 此复位影响。BKPSRAM 的唯一复位方式是通过 Flash 接口将 Flash 保护等级从 1 切换到 0。只要发生以下事件之一,就会产生备份域复位:

  1. 软件复位,通过将 RCC 备份域控制寄存器 (RCC_BDCR) 中的 BDRST 位置 1 触发。
  2. 在电源 VDD 和 VBAT 都已掉电后,其中任何一个又再上电。

在复位后以及处理器开始执行程序之前,Cortex_M处理会从存储中读取头两个字。
从地址 0x0000,0000 处取出 MSP 的初始值。
从地址 0x0000,0004 处取出 PC 的初始值——这个值是复位向量,LSB 必须是 1。然
后从这个值所对应的地址处取指。
在这里插入图片描述
处理器读出这两个字后,就会将这些数值赋给MSP和程序计数器(PC)。

扩充知识:

固件(就是.bin文件,自己写的代码生成的)设计中,有个BootLoader,它是拥有从外部接收数据,更新Flash(也就是APP),跳转至APP功能的特殊APP罢了。
       例如:两份程序(固件),bootloader+app,通常一个项目不止烧录一份程序,比如一个音响:会烧录蓝牙的固件(程序代码)、Bootloader、功能模块固件等。
在这里插入图片描述
BootLoader的设计中,STM32 单片机启动流程中介绍了单片机上电后的启动流程,其实也就主要干了两件事:

  1. 将栈顶位置加载到MSP寄存器中
  2. 将复位中断服务函数的入口地址给PC寄存器

然后单片机执行复位中断服务函数,在复位中断服务函数中设置中断向量表的偏移地址,准备C环境,最后跳转到main()函数。同理,从Bootloader跳到APP也需要干这两件事情,只不过上电时是单片机自动加载的MSPPC,而从Bootloader跳到APP则需要我们编写函数进行跳转。后期在详细出一期Bootloader的文章。

回到复位这,MSP的设置是非常必要的,这是因为在复位的很短时间内有产生NMI或HardFault的可能,在异常处理前将处理器状态压栈时需要栈存储和MSP。

在这里插入图片描述
在这里插入图片描述
       CM3 和CM4使用的是向下生长的满栈,所以MSP的初始值必须是堆栈内存的末地址加 1。
举例来说,如果你的堆栈区域在 0x20007C00‐0x20007FFF 之间,那么 MSP 的初始值就必须是
0x20008000
       向量表跟随在 MSP 的初始值之后——也就是第 2 个表目。要注意因为 CM3 是在 Thumb
态下执行,所以向量表中的每个数值都必须把 LSB 置 1(也就是奇数)。正是因为这个原因,
中使用 0x101 来表达地址 0x100。当 0x100 处的指令得到执行后,就正式开始了程序的执行。在此之前初始化 MSP 是必需的,因为可能第 1 条指令还没执行就会被 NMI 或是其它 fault 打断。MSP 初始化好后就已经为它们的服务例程准备好了堆栈。

二、时钟体系

       给单片机提供一个时钟信号(一个非常稳定的频率信号),使单片机各内部组件同步工作,并且在和外部设备通信时也能达到同步。
       就像是在广场上做广播体操一样,体操动作的频率取决于广播读数的速度。动态调整运行频率,就可以控制性能与功耗!
       参考手册 《STM32F4xx 中文参考手册.pdf》 第 106 页

1、时钟源

a.可以使用三种不同的时钟源来驱动系统时钟 (SYSCLK),CPU 运行的频率为 168MHz
HSI 振荡器时钟,也就是高速内部时钟,一般来说很少用,因为精度没有外部高速时钟那么高。
HSE 振荡器时钟,也就是高速外部时钟。
● 主 PLL (PLL) 时钟
在这里插入图片描述
电路原理图类似以上的电路

b.器件具有以下两个次级时钟源:
● 32 kHz 低速内部 RC (LSI RC),该 RC 用于驱动独立看门狗,也可选择提供给 RTC 用于停机/待机模式下的自动唤醒。
● 32.768 kHz 低速外部晶振(LSE 晶振),用于驱动 RTC 时钟 (RTCCLK)对于每个时钟源来说,在未使用时都可单独打开或者关闭,以降低功耗。
128*256 先分频128 再分频256 32.768KHZ
在这里插入图片描述
       在特殊的应用场景,为了达到最高的能效比,没有必要使用到PLL,可将HSE、HSI作为系统时钟源。例如,在智能手表锁屏的情况下,如果使用PLL配置过后输出的频率会造成过多的功耗,降低自身的续航能力;同时要维持计步与测量心率功能。因此,PLL在锁屏下的应用场景并不合适,在保证功能实现的前提下,尽可能降低功耗,可以切换频率更低的时钟源提供给系统时钟。
在这里插入图片描述

1.选择PLL作为系统时钟源
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;	

2.选择HSI作为系统时钟源
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_HSI;	

3.选择HSE作为系统时钟源
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_HSE;

2、时钟树

       时钟树就是关注时钟源和时钟的流向,嵌入式系统中的模块和外设工作都以时钟为基准。有了时钟树,就有了时钟域。嵌入式中除了内核,还有各个单元,每个单元工作在不同的时钟频率下,给每个单元提供不同的时钟。
       实际应用中根据需要配置外设的时钟控制开关,选择需要的时钟频率,并可关闭不用外设时钟。
在这里插入图片描述

在这里插入图片描述

APB1高级外设总线1

HSE 时钟

高速外部时钟信号 (HSE) 有 2 个时钟源: ● HSE 外部晶振/陶瓷谐振器 ● HSE 外部用户时钟

外部晶振/陶瓷谐振器(HSE 晶振)
HSE 的特点是精度非常高。

RCC 时钟控制寄存器 (RCC_CR) 中的 HSERDY 标志指示高速外部振荡器是否稳定。在启动
时,硬件将此位置 1 后,此时钟才可以使用。如在 RCC 时钟中断寄存器 (RCC_CIR) 中使能
中断,则可产生中断。这段话看着比较生涩,主要大家得先理解寄存器,控制寄存器 (RCC_CR) 中的 HSERDY 标志,简单得说,RCC_CR这个寄存器,代码编写比如像if,去判断这个标志位HSERDY ,得到振荡器是否稳定,才可以继续保证能使用。

HSE 晶振可通过 RCC 时钟控制寄存器 (RCC_CR) 中的 HSEON 位打开或关闭。

HSI 时钟

HSI 时钟信号由内部 16 MHz RC 振荡器生成,可直接用作系统时钟,或者用作 PLL 输入。

HSI RC 振荡器的优点是成本较低(无需使用外部组件)。此外,其启动速度也要比 HSE 晶
振块,但即使校准后,其精度也不及外部晶振或陶瓷谐振器。

LSE 时钟

LSE 晶振是 32.768 kHz 低速外部 (LSE) 晶振或陶瓷谐振器,可作为实时时钟外设 (RTC) 的 时钟源来提供时钟/日历或其它定时功能,具有功耗低且精度高的优点。

LSE 晶振通过 RCC 备份域控制寄存器 (RCC_BDCR) 中的 LSEON 位打开和关闭。
RCC 备份域控制寄存器 (RCC_BDCR) 中的 LSERDY 标志指示 LSE 晶振是否稳定。在启动
时,硬件将此位置 1 后,LSE 晶振输出时钟信号才可以使用。如在 RCC 时钟中断寄存器
(RCC_CIR) 中使能中断,则可产生中断。

LSI 时钟

LSI RC 可作为低功耗时钟源在停机和待机模式下保持运行,供独立看门狗 (IWDG) 和自动唤 醒单元 (AWU) 使用。时钟频率在32 kHz 左右。有关详细信息,请参见数据手册的电气特性 部分。

LSI RC 可通过 RCC 时钟控制和状态寄存器 (RCC_CSR) 中的 LSION 位打开或关闭。
RCC 时钟控制和状态寄存器 (RCC_CSR) 中的 LSIRDY 标志指示低速内部振荡器是否稳定。
在启动时,硬件将此位置 1 后,此时钟才可以使用。如在 RCC 时钟中断寄存器 (RCC_CIR) 中
使能中断,则可产生中断。

RTC/AWU 时钟

一旦选定 RTCCLK 时钟源后,要想修改所做选择,只能复位电源域。

RTCCLK 时钟源可以是HSE 1 MHz(HSE 由一个可编程的预分频器分频)、LSE 或者 LSI时钟。选择方式是编程 RCC 备份域控制寄存器 (RCC_BDCR) 中的 RTCSEL[1:0] 位和 RCC 时钟配置寄存器 (RCC_CFGR) 中的 RTCPRE[4:0] 位。所做的选择只能通过复位备份域的方式修改。

时钟安全系统 (CSS)

时钟安全系统可通过软件激活。激活后,时钟监测器将在 HSE 振荡器启动延迟后使能,并 在此振荡器停止时被关闭。

看门狗时钟

如果独立看门狗 (IWDG) 已通过硬件选项字节或软件设置的方式启动,则 LSI 振荡器将强制 打开且不可禁止。在 LSI振荡器稳定后,时钟将提供给 IWDG。

PLL

1.1 概述

PLL(Phase Locked Loop): 为锁相回路或锁相环,用来统一整合时钟信号,使高频器件正常工作,如内存的存取资料等。(来自HSI HSE)
PLL基于振荡器中的反馈技术,许多电子设备要正常工作,通常需要外部的输入信号与内部的振荡信号同步。
一般的晶振由于工艺与成本原因,做不到很高的频率,而在需要高频应用时,由相应的器件VCO,实现转成高频,但并不稳定,故利用锁相环路就可以实现稳定且高频的时钟信号。
PLLQ:IIS-----》音频输出
PLLR:RNG----》随机数
PLLP:SW----》选择器

1.2 基本组成

       锁相环路是一种反馈控制电路, 简称锁相环(PLL,Phase-Locked Loop)。锁相环的特点是:利用外部输入的参考信号控制环路内部振荡信号的频率和相位。因锁相环可以实现输出信号频率对输入信号频率的自动跟踪,所以锁相环通常用于闭环跟踪电路。
       锁相环在工作的过程中,当输出信号的频率与输入信号的频率相等时,输出电压与输入电压保持固定的相位差值,即输出电压与输入电压的相位被锁住,这就是锁相环名称的由来。锁相环通常由鉴相器(PD,Phase Detector)、环路滤波器(LF,Loop Filter)和压控振荡器(VCO,Voltage Controlled Oscillator)三部分组成。
       锁相环的工作原理是检测输入信号和输出信号的相位差,并将检测出的相位差信号通过鉴相器转换成电压信号输出,经低通滤波器滤波后形成压控振荡器的控制电压,对振荡器输出信号的频率实施控制,再通过反馈通路把振荡器输出信号的频率、相位反馈到鉴相器。
锁相环在工作过程中,当输出信号的频率成比例地反映输入信号的频率时,输出电压与输入电压保持固定的相位差值,这样输出电压与输入电压的相位就被锁住了。
在这里插入图片描述

1.3 类比说明

       我们刚开始学车的时候,在道路上开车,眼睛就好像一个鉴相器,负责发现车行驶的方向(反馈)和前方的路(输入)是否有差别,把差别输入大脑进行判断,然后指挥双手旋转方向盘,旋转方向盘的动作转换成车的行驶方向,如下图所示。

在这里插入图片描述
我们通过这么一个闭环过程不断地调节方向盘,保证车行驶在正道上。

2.3 相位差

       两个频率相同的交流电相位的差叫做相位差,或者叫做相差,又称“相角差”、“相差”、“周相差”或“位相差”。两个作周期变化的物理量的相之间的差值。它为正值时称前者超前于后者,为负值时则滞后于后者。它为零或π的偶数倍时,两物理量同相;为π的奇数倍时则称反相。
       这两个频率相同的交流电,可以是两个交流电流,可以是两个交流电压,可以是两个交流电动势,也可以是这三种量中的任何两个。两个同频率正弦量的相位差就等于初相之差。是一个不随时间变化的常数。也可以是一个元件上的电流与电压的相位变化。任意一个正弦量y = Asin(wt+ j0)的相位为(wt+ j0),两个同频率正弦量的相位差(与时间t无关)。设第一个正弦量的初相为 j01,第二个正弦量的初相为 j02,则这两个正弦量的相位差为j12 = j01 - j02。
在这里插入图片描述

2.4 PLL配置参数

在这里插入图片描述

不同的芯片,倍频(频率翻倍)公式是不一样的,需要查询芯片手册!

三、SystemInit系统初始化函数

系统时钟 (SYSCLK) 选择
       在系统复位后,默认系统时钟为 HSI。在直接使用 HSI 或者通过 PLL 使用时钟源来作为系统 时钟时,该时钟源无法停止。
       只有在目标时钟源已就绪时(时钟在启动延迟或 PLL 锁相后稳定时),才可从一个时钟源切 换到另一个。如果选择尚未就绪的时钟源,则切换在该时钟源就绪时才会进行。RCC 时钟 控制寄存器 (RCC_CR) 中的状态位指示哪个(些)时钟已就绪,以及当前哪个时钟正充当系 统时钟。

1.第一个执行的文件是汇编文件**

栈的初始化,提供函数调用的时候进行现场保护和现场恢复
堆的初始化,为申请内存提供空间,调用malloc
执行Reset_Handler,意思说上电复位后执行的动作
执行SystemInit函数 跳转到main函数

2.初始化Flash接口,更新PLL系统频率**

/**
  * @brief  Setup the microcontroller system
  *         Initialize the Embedded Flash Interface, the PLL and update the
  *         SystemFrequency variable.
  * @param  None
  * @retval None
  */
void SystemInit(void)
{
  ................
  /* Configure the System clock source, PLL Multiplier and Divider factors,
     AHB/APBx prescalers and Flash settings ----------------------------------*/
  SetSysClock();
 
  ................
 
}

3.调用SetSysClock函数设置PLL时钟,然后进行分频**

/**
  * @brief  Configures the System clock source, PLL Multiplier and Divider factors,
  *         AHB/APBx prescalers and Flash settings
  * @Note   This function should be called only once the RCC clock configuration  
  *         is reset to the default reset state (done in SystemInit() function).   
  * @param  None
  * @retval None
  */
static void SetSysClock(void)
{
      .....................
    /* Configure the main PLL */
    RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
                   (RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
.....................
}

system_stm32f4xx.c文件有以下倍频(PLL_N)与分频(PLL_M、PLL_P)因子:

/* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N */
#define PLL_M      8     //(记得修改为8)修改PLL_M倍频因子(25——》8)
/* USB OTG FS, SDIO and RNG Clock =  PLL_VCO / PLLQ */
#define PLL_Q      7
 
#if defined (STM32F40_41xxx)
#define PLL_N      336
/* SYSCLK = PLL_VCO / PLL_P */
#define PLL_P      2
#endif /* STM32F40_41xxx */

       由于官方的代码是使用外部高速晶振25MHz,GEC-M4开发板是使用外部高速晶振8MHz,所以PLL的倍频因子要进行修改,只修改PLL_M为8。

4.阅读system_stm32f4xx.c文件的头部注释**

*=============================================================================
  *=============================================================================
  *                    Supported STM32F40xxx/41xxx devices
  *-----------------------------------------------------------------------------
  *        System Clock source                    | PLL (HSE)
  *-----------------------------------------------------------------------------
  *        SYSCLK(Hz)                             | 168000000
  *-----------------------------------------------------------------------------
  *        HCLK(Hz)                               | 168000000
  *-----------------------------------------------------------------------------
  *        AHB Prescaler                          | 1
  *-----------------------------------------------------------------------------
  *        APB1 Prescaler                         | 4
  *-----------------------------------------------------------------------------
  *        APB2 Prescaler                         | 2
  *-----------------------------------------------------------------------------
  *        HSE Frequency(Hz)                      | 25000000   粤嵌开发板外部晶振是8MHz,我们要将25MHz修改为8MHz
  *-----------------------------------------------------------------------------
  *        PLL_M                                  | 25
  *-----------------------------------------------------------------------------
  *        PLL_N                                  | 336
  *-----------------------------------------------------------------------------
  *        PLL_P                                  | 2
  *-----------------------------------------------------------------------------
  *        PLL_Q                                  | 7
  *-----------------------------------------------------------------------------
  *        PLLI2S_N                               | NA
  *-----------------------------------------------------------------------------
  *        PLLI2S_R                               | NA
  *-----------------------------------------------------------------------------
  *        I2S input clock                        | NA
  *-----------------------------------------------------------------------------
  *        VDD(V)                                 | 3.3
  *-----------------------------------------------------------------------------
  *        Main regulator output voltage          | Scale1 mode
  *-----------------------------------------------------------------------------
  *        Flash Latency(WS)                      | 5
  *-----------------------------------------------------------------------------
  *        Prefetch Buffer                        | ON
  *-----------------------------------------------------------------------------
  *        Instruction cache                      | ON
  *-----------------------------------------------------------------------------
  *        Data cache                             | ON
  *-----------------------------------------------------------------------------
  *        Require 48MHz for USB OTG FS,          | Disabled
  *        SDIO and RNG clock                     |
  *-----------------------------------------------------------------------------
  *=============================================================================

去掉stm32f4xx.h的只读属性
在这里插入图片描述

接着修改stm32f4xx.h以下内容,行127将外部晶振频率值修改为8MHz。

#if !defined  (HSE_VALUE)
  #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */

最后按照PLL的运算公式,最终得到输出频率为168MHz。
在这里插入图片描述

#注意事项
在《STM32F4xx中文参考手册》 P117页,PLL_M、PLL_N、PLL_P,这三个参数都有一定的范围限制,详细如下:

2≤ PLL_M ≤63
192≤ PLL_N ≤432
PLL_P:2、4、6、8

四、寄存器边界地址

详见《STM32F4xx中文参考手册》的P52 ~ P54页,部分展示如下:
在这里插入图片描述
STM32F4xx中文参考手册》重的RCC寄存器,可以不用去记,但是一般实际工作重书写代码都是建议用寄存器去写,这时候就需要查手册了。
在这里插入图片描述

五、代码例程

代码1例程proj1_168/96Mhz:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
按照第六节将PLL_M和HSE_VALUE配置,PLL_M:8或者25,HSE_VALUE:8000000或者25000000。

#include <stm32f4xx.h>

static GPIO_InitTypeDef  GPIO_InitStructure;

// 粗延时(软件延时,不精确)
void sleep(void)
{
	int i = 0x500000;
	while(i--);
}

// 初始化4个LED
void LED_Init(void)
{
	// PF9 PF10 PE13 PE14
	/* 1.打开外设时钟(根据需求再开启,节省功耗) */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOE, ENABLE);
	
	/* 2.配置GPIO */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;	// GPIO引脚编号
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;		// 模式:输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		// 类型:推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	// 输出速率:跟功耗相关
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	// 无上下拉
	GPIO_Init(GPIOF, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;	// GPIO引脚编号
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
	// 3.给LED一个初始化电平(全灭)
	GPIO_SetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);
	GPIO_SetBits(GPIOE, GPIO_Pin_13 | GPIO_Pin_14);
}

// 初始化蜂鸣器
void BEEP_Init(void)
{
	// PF8
	/* 1.打开外设时钟(根据需求再开启,节省功耗) */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
	
	/* 2.配置GPIO */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;			// GPIO引脚编号
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;		// 模式:输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		// 类型:推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	// 输出速率:跟功耗相关
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	// 无上下拉
	GPIO_Init(GPIOF, &GPIO_InitStructure);
	
	// 3.给蜂鸣器一个初始化电平(不响)
	GPIO_ResetBits(GPIOF, GPIO_Pin_8);
}


// 初始化按键
void KEY_Init(void)
{
	// PA0
	/* 1.打开外设时钟(根据需求再开启,节省功耗) */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	/* 2.配置GPIO */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;			// GPIO引脚编号
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;		// 模式:输入模式
	//GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		// 类型:推挽(输入模式无效)
	//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	// 输出速率:跟功耗相关(输入模式无效)
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	// 无上下拉
	GPIO_Init(GPIOF, &GPIO_InitStructure);
}

int main()
{
	//uint8_t ret;
	
	LED_Init();		// 初始化4个LED
	BEEP_Init();	// 初始化蜂鸣器
	KEY_Init();		// 初始化按键
	
	while(1)
	{
		GPIO_SetBits(GPIOF, GPIO_Pin_8);	//PF8高电平,蜂鸣器响
		sleep();
		GPIO_ResetBits(GPIOF, GPIO_Pin_8);	//PF8低电平,蜂鸣器不响 
		sleep();
	}
}

代码2切换系统时钟源:

include <stm32f4xx.h>

#define PFout(n)	*(volatile uint32_t *)(0x42000000 + (GPIOF_BASE + 0x14 - 0x40000000)*32 + n*4)
#define PEout(n)	*(volatile uint32_t *)(0x42000000 + (GPIOE_BASE + 0x14 - 0x40000000)*32 + n*4)

#define PAin(n)	*(volatile uint32_t *)(0x42000000 + (GPIOA_BASE + 0x10 - 0x40000000)*32 + n*4)
#define PEin(n)	*(volatile uint32_t *)(0x42000000 + (GPIOE_BASE + 0x10 - 0x40000000)*32 + n*4)

#define BEEP PFout(8)

#define LED1 PFout(9)
#define LED2 PFout(10)
#define LED3 PEout(13)
#define LED4 PEout(14)

#define KEY1 PAin(0)
#define KEY2 PEin(2)
#define KEY3 PEin(3)
#define KEY4 PEin(4)

static GPIO_InitTypeDef  GPIO_InitStructure;

// 粗延时(软件延时,不精确)
void sleep(void)
{
	volatile int i = 0x200000;
	while(i--);
}

// 初始化4个LED
void LED_Init(void)
{
	// PF9 PF10 PE13 PE14
	/* 1.打开外设时钟(根据需求再开启,节省功耗) */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOE, ENABLE);
	
	/* 2.配置GPIO */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;	// GPIO引脚编号
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;		// 模式:输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		// 类型:推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	// 输出速率:跟功耗相关
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	// 无上下拉
	GPIO_Init(GPIOF, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14;	// GPIO引脚编号
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	
	// 3.给LED一个初始化电平(全灭)
	GPIO_SetBits(GPIOF, GPIO_Pin_9 | GPIO_Pin_10);
	GPIO_SetBits(GPIOE, GPIO_Pin_13 | GPIO_Pin_14);
}

// 初始化蜂鸣器
void BEEP_Init(void)
{
	// PF8
	/* 1.打开外设时钟(根据需求再开启,节省功耗) */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
	
	/* 2.配置GPIO */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;			// GPIO引脚编号
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;		// 模式:输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		// 类型:推挽
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	// 输出速率:跟功耗相关
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	// 无上下拉
	GPIO_Init(GPIOF, &GPIO_InitStructure);
	
	// 3.给蜂鸣器一个初始化电平(不响)
	GPIO_ResetBits(GPIOF, GPIO_Pin_8);
}


// 初始化按键
void KEY_Init(void)
{
	// PA0
	/* 1.打开外设时钟(根据需求再开启,节省功耗) */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	/* 2.配置GPIO */
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;			// GPIO引脚编号
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;		// 模式:输入模式
	//GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		// 类型:推挽(输入模式无效)
	//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;	// 输出速率:跟功耗相关(输入模式无效)
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;	// 无上下拉
	GPIO_Init(GPIOF, &GPIO_InitStructure);
}
	
int main()
{
	LED_Init();		// 初始化4个LED
	BEEP_Init();	// 初始化蜂鸣器
	KEY_Init();		// 初始化按键
	
	while(1)
	{
		if(KEY1 == 0)
		{
		    /* 按键1:选择PLL(168MHz)			*/
			RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
			RCC->CFGR |= RCC_CFGR_SW_PLL;
		}
		else if(KEY2 == 0)
		{
		    /* 按键2:选择HSI(16MHz)			*/
			RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
			RCC->CFGR |= RCC_CFGR_SW_HSI;
		}
		else if(KEY3 == 0)
		{
		    /* 按键3:选择HSE(8MHz)			*/
			RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
			RCC->CFGR |= RCC_CFGR_SW_HSE;
		}
		
		
		LED1 = ~LED1;
		sleep();
		LED2 = ~LED2;
		sleep();
		LED3 = ~LED3;
		sleep();
		LED4 = ~LED4;
		sleep();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yank_k

点个关注加分享,一起探讨学习!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值