STM32(2):点亮LED(下)

发布者:电子创意达人最新更新时间:2024-12-31 来源: jianshu关键字:STM32  点亮LED  RCC 手机看文章 扫描二维码
随时随地手机看文章

概述

点亮LED表面看起来貌似很简单,但是如何想要搞清楚其背后牵涉的每一行代码的具体含义,还是需要花费一些功夫的,而且,只有把LED的背后只是搞清楚了,才算嵌入式开发的基础入门。
今天我们就来研究一下LED的重头戏,RCC_Init;什么是RCC?上手册:


file

RCC

RCC,Reset and Clock Control,重置以及时钟控制;STM32手册使用了两个章节来对其进行描述,可见它的重要性;对于RCC的初始化也是比较复杂,里面包含了STM32对于时钟的相关机制,

代码总览

void RCC_init(uint16_t PLL){
    uint32_t temp=0;  

    *((uint32_t *)RCC_CR) |= 0x00010000; 
    while(!( *((uint32_t *)RCC_CR) >>17));

    *((uint32_t *)RCC_CFGR) = 0X00000400;

    PLL -= 2;
    *((uint32_t *)RCC_CFGR) |= PLL<<18;   

    *((uint32_t *)RCC_CFGR) |= 1<<16;

    *((uint32_t *)FLASH_ACR)|=0x2;
    *((uint32_t *)RCC_CR) |= 0x01000000;
    while(!(*((uint32_t *)RCC_CR) >> 25));

    *((uint32_t *)RCC_CFGR) |= 0x00000002;
    while(temp != 0x02)
    {  
        temp = *((uint32_t *)RCC_CFGR) >> 2;
        temp &= 0x03;
    }   }

使能HSE

第一行有效代码,是熟悉的味道,前一节我们说过,或运算一般用于设定指定位(而且还不影响其他位)

*((uint32_t *)RCC_CR) |= 0x00010000;

看到了RCC_CR,首先就是翻手册,RCC的章节的RCC_CR章节:


file

然后查看寄存器内部32位的定义:


file


0x00010000转换为32bit的二进制:0000 0000 0000 0001 0000 0000 0000 0000;这里有一个小技巧,就是学会分割来看,手册里面的寄存器的定义一般定义是分为上下两行的;从16进制来看,从左往右,前面4为是负责上面RCC_CFGR的高位16bit,后面4位是对应(二进制)低位16bit,即下面的16bit;对此次而言,低4位都是0,可以不需要care,直接关注高4位即可,其中高4为只有最后一位是有效设置,1对应的四位二进制是0001,即16位设置为1,查看寄存器定义的16位对应的是HSEON位,即使能HSEON。

HSE和时钟

什么是HSEON?需要拆开来看:HSE ON,HSE是High Speed External,外部高速时钟,所以HSEON就是使能外部高速时钟;
为什么要配置HSE呢?因为在硬件系统中,各个部件的工作、协调,都是基于系统时钟的,比如我们通常讲的CPU的速度快慢,就是指CPU的时钟频率,即每秒钟能够工作多少个时钟;


file

小贴士:

在win10系统中可以看到有两个频率,如下图,分别是1.9GHz以及2.11GHz;其中第一个是Intel提供的标准频率,其实是CPU名字的一部分,第二个频率是win10系统自己计算出来的,很多时候和Intel提供的频率值并不相等;不过两者相差也不会太大。

系统时钟有三个来源,分别是HSE(High Spped External),HSI(High Speed Internal)以及PLLCLK,引入了PLL就是因为很多时候,需要基于系统时钟进行分频倍频,比如有的设备(RAM,DMA)需要比系统时钟快,于是需要通过PLL来进行加倍频率,还有的低俗的设备需要低于系统频率工作,也是通过PLL来进行减速,这样只需要一个系统时钟就可以同时满足高速和低俗设备。

所以PLL的时钟源还是HSE,HSI,不过经过PLL倍频调节输出的时钟称之为PLLCLK;
再回到我们的代码里面,这里配置时钟源就是HSEON(如果需要使用PLL则还需要将PLLx位配置为1);

确认HSE使能生效

完成了时钟源的配置,下面一步是等待HSEON的配置的生效:

while(!( *((uint32_t *)RCC_CR) >>17));

即第等待第18位(编号17)HSERDY的值变成1,当且仅当配置HSEON生效之后,该位置才会由硬件设置为1,注意在寄存器定义里面HSERDY配置为“r”,这个代表软件层面是无法改变这个bit的值,只能够读取:

file


然后再查看寄存器定义里面对于这一位的解释,0就是HSEON位设置并未生效,1就是设置已经生效:

file


等待的过程是使用位运算里面右移“>>'实现的,右移的运算规则里面是低位直接丢弃,例如11111右移4bit,最后结果就是1;这行代码的逻辑意义就是RCC_CR右边17位直接丢弃,即016为抛弃,只是保留了3118位(注意数字方向,是从左向右逐次减小的,和寄存器定义一致),共计15bit;
又因为上一步骤中通过和0x00010000进行与运算将除了HSEON位之外的位置全部置为“0”了,所以之类RCC_CR右移17位之后,包括HSERDY在内的位全部都是0,只有当HSEON生效之后,HSERDY才是1,即*((uint32_t *)RCC_CR) >>17 = 1,于是此时退出while循环。


配置其他时钟

CR位配置解决了,下面我们看一下CFGR的配置:

*((uint32_t *)RCC_CFGR) = 0X00000400;

看到这里,我觉得我们可以总结一下写寄存器的基本套路:

  1. 查看手册相应章节,并了解缩写意义,比如CFGR,全称Clock Configuration Register,时钟配置寄存器:


    file

  2. 查看寄存器定义:


    file

  3. 查看具体的某一位的定义,比如我们这里设置为0X00000400,4是在后四位,所以重点关注低16bit,即从15 ~ 0bit:0000 0100 0000 0000,发现正好配置的PPRE1,值为100,手册介绍如下:


    file

用来配置APB1的从HCLK中获得时钟的分频系数,二进制100对应的分频系数是2;HCLK是AHB总线的时钟;然后AHB经过AHB-APB桥接将时钟分配到APB1和APB2总线,APB1的总线对应PCLK1,APB2总线对应的PCLK2;这里配置的就是APB1时钟频率的分频值,为HCLK/2;

这里有一个坑,虽然显式的为PCLK1赋值了,但是其实隐式的同时将PCLK2(对应APB2),AHB都赋值了;只不过配置的是0;

比如PPRE2配置为000,对应的就是不分频,或者说分频数为1:


file

还有HPRE位,配置也是000,对应的不分频,或者说频数为1:


file

所以CFGR的配置重要的指定了AHB总线以及APB1和APB2总线的分频/倍频数;

设置PLL倍频数

继续看后面的代码,PLL是参数,即倍频数,PLL上面已经介绍了,专门用于接入时钟源然后后对其进行分频倍频再分配给各个总线(下面挂载的设备):

PLL -= 2;*((uint32_t *)RCC_CFGR) |= PLL<<18;

那么这里为什么要-2呢?我们先存疑,理解了下一行代码谜底就自然打开了;

PLL值左移18位,左移是位运算,左移+或运算 = 赋值操作,即将PLL值赋给18位起始的后面四位,通过查看寄存器定义可以看到CFGR的18位起始到后面四位是PLLMUL,再查看手册对于PLLMUL的解释:PLL的倍频;不过看一下赋值情况0010(十进制2)对应是4倍频,0011(十进制3)对应的是5倍频,以此类推,就是寄存器的只是和真实的倍频差2,看到这里你就明白了为什么在PLL -=2了:


file

紧随其后的,可以知道是要给CFGR的第16bit赋值为1:

*((uint32_t *)RCC_CFGR) |= 1<<16;

查看手册,是描述PLL时钟源,1对应是PLL的时钟原始PREDIV1:

时钟树

关于PLLSRC以及PREDIV1他们之间关系在时钟树(Clock Tree)上面有比较明确的关系说明,从下图可以看到作为时钟源最开始是HSE,然后通过时钟被分频后成为了PREDIV1,然后再除以2,获得了PLLSRC:


file

所以如果PLLMUL的值设置为1,即PLL的时钟源采用内部高速时钟(HSI),最终输出时钟信号在HSI/2即可;
如果值设置为1即采用外部高速时钟(HSE),在经历了分频后输出为PREDIV1(时钟)信号,还需要再分频一下,分频系数为2;

FLASH ARC配置

再看下面的代码就没有那么怕了,直接明白,目的就是要给FLASH的ACR寄存器赋值:

*((uint32_t *)FLASH_ACR)|=0x2;

不过这次要找FLash的ACR似乎并不那么顺利,很难直接从目录中查找到,需要全局搜一下ACR,不过还好,没有让我们搜索太多时间,在3.3.3章节中找到了:


file

寄存器的定义如下:


file

关于16进制转2进制补0

这里其实有一个补0问题,就是对于32bit,0x2究竟是0x0000 0002,还是0x2000 0000呢?回归本源,这两种表达形式哪一个是2呢?毫无疑问,是第一个;
所以这里的0x2,切换到32bit二进制表示就是:0000 0000 0000 0000 0000 0000 0000 0010;可以看到其实有效赋值是LATENCY,位的值是010,查看寄存器定义:


file

代表了比例,系统时钟比FLASH存取周期的值,010对应的2,即系统时间是Flash的两倍,所以同步的时候,需要做两个周期的延迟用以同步CPU和Flash之间时钟;
为什么需要做此配置呢?首先程序都是存放在FLASH里面,CPU需要从FLASH里面取出执行指令,所以通信之前需要首先同步时钟;那么就需要知道Flash的时钟和系统时钟(CPU时钟)之间的差别;又因为我们配置时钟是72MHz,这里010的时钟范围包含了72MHz;

使能PLL

下面是对于RCC_CR寄存器最后的配置:

*((uint32_t *)RCC_CR) |= 0x01000000;while(!(*((uint32_t *)RCC_CR) >> 25));

还是套路:拆解0x0100 0000,只有高16位有有效值,低16位全零;高16位转换为2进制之后值为:0000 0001 0000 0000,所以我们看到对应操作的是PLLON位,PLLON位说明如下:


file

写入1则代表打开PLL,上面我们做了关于PLL的倍频的配置,PLL时钟源的配置,都是需要在PLL使能之后才会生效,在嵌入式开发中,所有的配置都是基于设备/ 组件使能的前提下才会配置生效;
至于while语句和上面配置HSE使能的语句的等待READY的功效是一致的;可以通过查看寄存器定义,25位是PLLRDY,while循环就是等待这一位置为1,是否需要担心其他位有1从而影响循环判断?大可不必,因为31~26都是Reserved,必然为0,唯一的有效位就是PLLRDY位。
可以看到在RCC初始化的代码中,每次配置一个使能都是需要通过while循环来确认配置成功了;

配置PLLCLK为系统时钟

最后一部分代码,让RCC_CFGR与0x00000002做或运算:

*((uint32_t *)RCC_CFGR) |= 0x00000002;while(temp != 0x02){  
        temp = *((uint32_t *)RCC_CFGR) >> 2;
        temp &= 0x03;}

有效为发生在低16位,0000 0000 0000 0010,有效配置位是最后两位,即SW位(Switch,切换系统时钟之意),解释如下,可以看到10代表使用PLL的输出(PLLCLK)作为系统时钟:


file

之后的while语句则是轮训查看使能配置是否生效;注意这里的使能生效并没有采用上面单句while循环的方式,而是采用赋值方式来进行的,就是因为会有其他位配置会影响判断;
首先看一下SWS位说明:


file

赋值判断的方式也是非常巧妙,首先是RCC_CFGR右移(>>)2位,挤掉了SW位,现在SWS(Switch Status)位在最后面,然后将其和0x03进行与运算,与运算的目的就是:0位清零,1位保留原值(或运算目的:0位维持原值不变,1位用于置1)。

所以,和temp进行与运算0x03的其实是30bit数(最后两位已经通过右移2位挤掉了):0000 0000 0000 0000 0000 0000 0000 11,所以就是要取用SWS的位的值,当返回值为“10”的时候,即0x02,代表PLL已经被设置为系统时钟,说明RCC_CFGR的配置已经生效。


关键字:STM32  点亮LED  RCC 引用地址:STM32(2):点亮LED(下)

上一篇:STM32(3):番外篇之Let's GO!
下一篇:STM32(1):点亮LED(上)

推荐阅读最新更新时间:2026-03-25 09:26

Visual Studio 2022 开发 STM32 单片机 - 环境搭建点亮LED
安装 VS2022社区版 软件 选择基础的功能就好 安装 VisualGDB 软件(CSDN资源) 按照提示一步一步安装就好 VisualGDB激活软件(CSDN资源) 将如下软件放在VisualGDB的安装目录下直接运行就好 打开VisualStudio软件 创建新项目 选择Embedded ProjectWizard项目 设置工程参数 按照默认的参数不修改 以上参数不修改 如果在filter中没有搜到芯片,在Download more devices下载对应设备 查看开发板的LED灯的硬件链接引脚 设置对应的参数
[单片机]
Visual Studio 2022 开发 <font color='red'>STM32</font> 单片机 - 环境搭建<font color='red'>点亮</font><font color='red'>LED</font>灯
stm32填坑之旅一 - stm32f103c8t6点亮板载贴片蓝色LED
开篇 没有时间只是自己找的借口罢了! 开篇一定要精彩,不然路人不理睬!下述是笔者作为arm小白的填坑之旅 没错,这个之前一直从事软件开发的笔者,开始搞硬件了,当然仅仅是数电!模电需要有很扎实的电路基础,而笔者有的只有“扎实”的逻辑基础。 那为什么笔者要开始搞硬件呢? 其实早在大学期间,笔者所在专业(计算机科学与技术)中就有一门课就专门讲了硬件-软件的连接以及实现,只怪当初没有好好学,只是心中有那么个印象,就是时钟驱动逻辑电路去处理每一个指令然后完成整个逻辑(当然,这个印象很重要,在arm中,时钟就是它的心脏!)。 毕业后若干年,物联网行业开始兴起,于是手痒痒了,仅凭这一印象,开始入手了人生中的第一块板子——树莓派3B,用来做了一
[单片机]
树莓派玩转STM32开发(三)——先从点亮一个LED灯开始
在开始正式写代码之前,首先要解决一件事,那就是我们该如何去写这代码。 在树莓派搭建的Linux环境中,使用基于Xorg的桌面环境在本地开启图形化界面开发太耗费系统资源,这个方案首先不考虑。不过还剩下两种其他可行的方案,一是直接通过终端远程登陆进行开发,开发工作全部在文本模式下进行;另一种则使用Samba服务将Linux的文件系统挂载到Windows中,然后在Windows上面使用图形化界面的IDE开发。 如果你比较Geek,你可能会尝试远程登陆的方式,通过Vim或者Emacs两大神器之一进行开发。不过他们两者对新手不够友好,具体可以自己体验一下,不过网上有个段子: 问:如何生成一个随机字符串? 答:让新手退出vim. 而且在没
[单片机]
使用STM32、SFPGA和I.MX6ULL IO点亮LED
摘要:你点亮过多少板子的LED灯呢?有很多小伙伴要求讲一下STM32、FPGA、Liunx他们之间有什么不同。不同点很多,口说无凭,今天就来点亮一下STM32、FPGA和Liunx板子的LED灯,大家大致看一下点灯流程和点灯环境以及点灯流程,就能大概的了解一下三者的区别,可以有选择的去学习! 一、使用STM32点亮LED灯 STM32从字面上来理解ST是意法半导体,M是Microelectronics的缩写,32 表示32位,合起来理解,STM32就是指ST公司开发的32位微控制器。在如今的32 位控制器当中,STM32可以说是最璀璨的新星,它受宠若娇,大受工程师和市场的青睐,无芯能出其右。首先使用STM32电亮一个led灯,
[单片机]
使用<font color='red'>STM32</font>、SFPGA和I.MX6ULL IO<font color='red'>点亮</font><font color='red'>LED</font>灯
STM32入门系列-使用库函数点亮LEDLED初始化函数
要点亮LED,需要完成LED的驱动, 在工程模板上新建一个led.c和led.h文件,将其存放在led文件夹内。这两个文件需要我们自己编写。 通常xxx.c文件用于存放编写的驱动程序,xxx.h文件用于存放xxx.c内的stm32头文件、管脚定义、全局变量声明、函数声明等内容。 因此在led.c文件内编写如下代码: #include led.h /******************************************************************************* * 函 数 名 : LED_Init * 函数功能 : LED 初始化函数 * 输 入 : 无 * 输 出 : 无
[单片机]
STM32第一次学习——使用库函数点亮LED
使用库函数点亮LED灯 led.h #ifndef _LED_H #define _LED_H #include stm32f10x.h #define LED_PORT_RCC RCC_APB2Periph_GPIOC #define LED_PIN GPIO_Pin_0 #define LED_PORT GPIOC void LED_Init(void); #endif led.c #include led.h void LED_Init() { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_G
[单片机]
STM32 Keil下编程实现LED点亮与闪烁
前言 基于STM32F10C8T6在Keil下编程实现LED灯点亮与闪烁 一、原理图 1.STM32F103C8T6 STM32F103C8T6是ST在2007年发布的一款MCU,截止目前ST已经发布了速度高达400MHz的STM32H7。三点好处,一是封装比较大,方便初学者焊接,二是价格低廉,学习成本比较低,三是网上有大量的资料供初学者学习使用。 2.LED2 LED2控制一个绿色通讯指示灯,管脚为PB3。 3.LED3 LED3控制8个小灯点亮,管脚为PC13。 二、Keil代码 1.Led.h 代码如下: #ifndef _LED_H_ #define _LED_H_ #include stm32f10x
[单片机]
<font color='red'>STM32</font> Keil下编程实现<font color='red'>LED</font>灯<font color='red'>点亮</font>与闪烁
STM32使用寄存器点亮LED
前言 给有特定功能的内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。 一、什么是寄存器? 1、什么是储存器映射? 存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射。 2、什么是寄存器映射? 给存储器分配地址的过程叫存储器映射,再分配一个地址叫重映射。 二、STM32寄存器映射 1.总线基地址 2.GPIO基地址 3.GPIOB端口寄存器列表 4.GPIOx端口数据输出寄存器ODR描述 三、C语言对寄存器的封装 1.C语言对寄存器的封装 2.实现让PBO输出低/高电平 3.
[单片机]
<font color='red'>STM32</font>使用寄存器<font color='red'>点亮</font><font color='red'>LED</font>
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved