STM32寄存器深入分析
可能很多刚开始学习STM32的小伙伴都有一个疑惑,创建项目时会需要很多头文件,导致学习过程中很难明白那些头文件的作用,虽然知道头文件都是对寄存器的封装,但是怎么封装的就不知道了。这里我以led灯为试验,不需要头文件,自己跟着寄存器的说明写一个简单的demo,应该能加深小伙伴们对STM32的理解。
一、有效地址
C语言功底相对差一些的小伙伴可能看不明白“STM32的寄存器手册”,不明白手册中的地址说明是什么,比如手册中的两个寄存器,他们的偏移地址都是0x00,这样直接给0x00这个寄存器直接复制是不行的。


到这里我们得明白有效地址这个概念,我们操作寄存器的时候,都是操作的寄存器的有效地址,而有效地址等于基地址加偏移地址。
- 有效地址 = 基地址 + 偏移地址
对有效地址还有疑问的小伙伴可以参考偏移地址的理解
二、时钟系统(RCC)与 GPIO 的有效地址
想要知道STM的相关外设的有效地址,那么需要了解一些STM32的系统架构

注意:代码区始终从地址0x0000 0000开始(通过ICode和DCode总线访问)
从图中可知,外设的有效地址都是在系统外设总线的地址上进行偏移的,我们可以通过STM32提供的库文件得知相关寄存器的地址,也可以通过“STM32的寄存器手册”获取相关外设的地址。


从图中可知GPIB的有效地址是0x40010C00,RCC的有效地址是0x40021000
- GPIB = 0x40000000 + 0x10000 + 0xC00 = 0x40010C00
- GPIB = 0x40000000 + 0x20000 + 0x1000 = 0x40021000
除了这样计算之外,还可以通过“STM32的寄存器手册”直接查看即可

现在就可以通过“STM32的寄存器手册”提供的偏移地址定义我们要使用的变量,当然也可以参考我之前的STM32时钟系统的配置寄存器和源码分析
#define RCC_BASE ((uint32_t)0x40021000)
#define GPIOB_BASE ((uint32_t)0x40010C00)
#define FLASH_ACR ((uint32_t *)0x40022000)
#define GPIOB_CRH ((uint32_t *)(GPIOB_BASE + 0x04))
#define GPIOB_ODR ((uint32_t *)(GPIOB_BASE + 0x0C))
#define RCC_CR ((uint32_t *)(RCC_BASE + 0x00))
#define RCC_CFGR ((uint32_t *)(RCC_BASE + 0x04))
#define RCC_CIR ((uint32_t *)(RCC_BASE + 0x08))
#define RCC_APB2RSTR ((uint32_t *)(RCC_BASE + 0x0C))
#define RCC_APB1RSTR ((uint32_t *)(RCC_BASE + 0x10))
#define RCC_AHBENR ((uint32_t *)(RCC_BASE + 0x14))
#define RCC_APB2ENR ((uint32_t *)(RCC_BASE + 0x18))
#define RCC_APB1ENR ((uint32_t *)(RCC_BASE + 0x1C))
三、初始化时钟系统
- 把所有时钟系统复位
/*------------------------------------------------------------
把所有时钟寄存器复位
------------------------------------------------------------*/
void RCC_DeInit(void)
{
*RCC_APB2RSTR = 0x00000000;//外设复位
*RCC_APB1RSTR = 0x00000000;
*RCC_AHBENR = 0x00000014; //flash时钟,闪存时钟使能.DMA时钟关闭
*RCC_APB2ENR = 0x00000000; //外设时钟关闭.
*RCC_APB1ENR = 0x00000000;
*RCC_CR |= 0x00000001; //使能内部高速时钟HSION
*RCC_CFGR &= 0xF8FF0000; //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]
*RCC_CR &= 0xFEF6FFFF; //复位HSEON,CSSON,PLLON
*RCC_CR &= 0xFFFBFFFF; //复位HSEBYP
*RCC_CFGR &= 0xFF80FFFF; //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
*RCC_CIR = 0x009F0000; //关闭所有中断
}
- 通过8MHz的外部时钟配置72MHz的系统时钟
/*------------------------------------------------------------
外部8M,则得到72M的系统时钟
------------------------------------------------------------*/
void Stm32_Clock_Init(void)
{
unsigned char temp=0;
u8 timeout=0;
RCC_DeInit();
RCC_CR|=0x00010000; //外部高速时钟使能HSEON
timeout=0;
while(!(RCC_CR>>17)&&timeout<200)timeout++;//等待外部时钟就绪
//0-24M 等待0;24-48M 等待1;48-72M等待2;(非常重要!)
FLASH_ACR|=0x32;//FLASH 2个延时周期
RCC_CFGR|=0X001D2400;//APB1/2=DIV2;AHB=DIV1;PLL=9*CLK;HSE作为PLL时钟源
RCC_CR|=0x01000000; //PLLON
timeout=0;
while(!(RCC_CR>>25)&&timeout<200)timeout++;//等待PLL锁定
RCC_CFGR|=0x00000002;//PLL作为系统时钟
while(temp!=0x02&&timeout<200) //等待PLL作为系统时钟设置成功
{
temp=RCC->CFGR>>2;
timeout++;
temp&=0x03;
}
}
- 程序我就不过多介绍了,这里相对比较简单,有感兴趣的小伙伴可以通过寄存器对照一下就明白了,或者参考STM32时钟系统的配置寄存器和源码分析
四、程序源码
main.c文件
typedef unsigned int uint32_t;
typedef unsigned char uint8_t;
#define CLOCK 72/8 //时钟=72M
#define RCC_BASE ((uint32_t)0x40021000)
#define GPIOB_BASE ((uint32_t)0x40010C00)
#define FLASH_ACR ((uint32_t *)0x40022000)
#define GPIOB_CRH ((uint32_t *)(GPIOB_BASE + 0x04))
#define GPIOB_ODR ((uint32_t *)(GPIOB_BASE + 0x0C))
#define RCC_CR ((uint32_t *)(RCC_BASE + 0x00))
#define RCC_CFGR ((uint32_t *)(RCC_BASE + 0x04))
#define RCC_CIR ((uint32_t *)(RCC_BASE + 0x08))
#define RCC_APB2RSTR ((uint32_t *)(RCC_BASE + 0x0C))
#define RCC_APB1RSTR ((uint32_t *)(RCC_BASE + 0x10))
#define RCC_AHBENR ((uint32_t *)(RCC_BASE + 0x14))
#define RCC_APB2ENR ((uint32_t *)(RCC_BASE + 0x18))
#define RCC_APB1ENR ((uint32_t *)(RCC_BASE + 0x1C))
/*------------------------------------------------------------
把所有时钟寄存器复位
------------------------------------------------------------*/
void RCC_DeInit1(void)
{
*RCC_APB2RSTR = 0x00000000;//外设复位
*RCC_APB1RSTR = 0x00000000;
*RCC_AHBENR = 0x00000014; //flash时钟,闪存时钟使能.DMA时钟关闭
*RCC_APB2ENR = 0x00000000; //外设时钟关闭.
*RCC_APB1ENR = 0x00000000;
*RCC_CR |= 0x00000001; //使能内部高速时钟HSION
*RCC_CFGR &= 0xF8FF0000; //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]
*RCC_CR &= 0xFEF6FFFF; //复位HSEON,CSSON,PLLON
*RCC_CR &= 0xFFFBFFFF; //复位HSEBYP
*RCC_CFGR &= 0xFF80FFFF; //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE
*RCC_CIR = 0x009F0000; //关闭所有中断
}
void Stm32_Clock_Init1(void)
{
unsigned char temp=0;
uint8_t timeout=0;
RCC_DeInit1();
*RCC_CR|=0x00010000; //外部高速时钟使能HSEON
timeout=0;
while(!(*RCC_CR>>17)&&timeout<200)timeout++;//等待外部时钟就绪
//0-24M 等待0;24-48M 等待1;48-72M等待2;(非常重要!)
*FLASH_ACR|=0x32;//FLASH 2个延时周期
*RCC_CFGR|=0X001D2400;//APB1/2=DIV2;AHB=DIV1;PLL=9*CLK;HSE作为PLL时钟源
*RCC_CR|=0x01000000; //PLLON
timeout=0;
while(!(*RCC_CR>>25)&&timeout<200)timeout++;//等待PLL锁定
*RCC_CFGR|=0x00000002;//PLL作为系统时钟
while(temp!=0x02&&timeout<200) //等待PLL作为系统时钟设置成功
{
temp = *RCC_CFGR>>2;
timeout++;
temp&=0x03;
}
}
/*------------------------------------------------------------
us延时函数
------------------------------------------------------------*/
void delay_us(unsigned int us)
{
uint8_t n;
while(us--)for(n=0;n<CLOCK;n++);
}
/*------------------------------------------------------------
ms延时函数
------------------------------------------------------------*/
void delay_ms(unsigned int ms)
{
while(ms--)delay_us(1000);
}
/*------------------------------------------------------------
主函数
------------------------------------------------------------*/
int main()
{
Stm32_Clock_Init1();
*RCC_APB2ENR|=0X0000001c;//先使能外设IO PORTa,b,c时钟
*RCC_APB2ENR |= 1 << 12;
*GPIOB_CRH = 0X00030000; //设置GPIOB的12引脚为推挽输出
while (1)
{
delay_ms(1000);
//GPIOB->ODR = ~(1 << 12); //设置12引脚输出0
*GPIOB_ODR = ~(1 << 12);
delay_ms(1000);
//GPIOB->ODR |= 1 << 12; //设置12引脚输出1
*GPIOB_ODR |= 1 << 12;
}
}
五、测试
由于没有使用任何库文件,所以创建项目就比较简单了,我就不在进行演示了,有知道怎么创建项目的小伙可以浏览我之前的STM32新建模板之库文件和STM32新建模板之寄存器
这里只需要使用startup_stm32f10x_hd.s启动文件和main.c文件即可


笔记到这里就完成了,相信到这里的小伙伴对STM32的库文件都有一定的了解,也知道怎么去学习,接下载就进入实战学习了,通过编写不同的外设来提升自己对库文件的了解,如果那些写得不好的忘大家指出。
参考文献
偏移地址的理解:https://www.jianshu.com/p/9704c5e758bf
STM32寄存器深入分析的更多相关文章
- stm32寄存器版学习笔记07 ADC
STM32F103RCT有3个ADC,12位主逼近型模拟数字转换器,有18个通道,可测量16个外部和2个内部信号源.各通道的A/D转换可以单次.连续.扫描或间断模式执行. 1.通道选择 stm32把A ...
- stm32寄存器版学习笔记06 输入捕获(ETR脉冲计数)
STM32外部脉冲ETR引脚:TIM1-->PA12;TIMER2-->PA0:TIMER3-->PD2;TIMER4-->PE0… 1.TIM2 PA0计数 配置步骤 ①开启 ...
- stm32寄存器版学习笔记05 PWM
STM32除TIM6和TIM7外都可以产生PWM输出.高级定时器TIM1和TIM8可以同时产生7路PWM,通用定时器可以产生4路PWM输出. 1.TIM1 CH1输出PWM配置步骤 ①开启TIM1时钟 ...
- stm32寄存器版学习笔记03 外部中断
stm32的每个I/O口都可以作为中断输入,要把I/O口设置为外部中断输入,必须将I/O口设置为上拉/下拉输入 或 浮空输入(但浮空的时候外部一定要带上拉或下拉电阻,否则可能导致 中断不停的触发),干 ...
- 对stm32寄存器的理解(个人理解,大神轻喷)
学习了stm32有一年了,今天想来写写自己对寄存器的理解,帮助那些有志学习stm32的朋友们少走一些弯路. ---------------------------------------------- ...
- stm32寄存器版学习笔记08 DMA
DMA(Direct Memory Access),直接存储器访问.DMA传输方式无需CPU直接控制传输,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU效率大大提高.stm32f10 ...
- stm32寄存器版学习笔记04 定时计数器中断
STM32共有8个定时计数器,其中TIME1和TIME8是高级定时器,TIME2~TIME5是通用定时器,TIME6和TIME7是基本定时器.以TIME3为例总结定时计数器的基本用法. 1.TIM3的 ...
- stm32寄存器版学习笔记02 串口通信
stm32F103RCT6提供5路串口.串口的使用,只要开启串口时钟,设置相应的I/O口的模式,然后配置下波特率.数据位长度.奇偶校验等信息,即可使用. 1.串口的配置步骤 ①串口时钟使能 APB2外 ...
- stm32寄存器版学习笔记01 GPIO口的配置(LED、按键)
STM32的I/O口可以由软件配置成如下8种模式:输入浮空.输入上拉.输入下拉.模拟输入.开漏输出.推挽输出.推挽式复用功能及开漏复用功能.每个I/O口由7个寄存器来控制:配置模式的端口配置寄存器CR ...
随机推荐
- Spring5 概述及Spring IOC学习
Spring Framework 5 1. Spring框架 1.1 Spring框架概述 1.2 主要内容 Spring框架是一个开源的JavaEE的应用程序 主要核心是 IOC(控制反转)和AOP ...
- THUSC 2021 游记
想了想不往博客园放不行,还是放上来了. 原文 \[\texttt{Brief Introduction} \] 众所周知,THUSC2021 5 月 15-16 日在杭州市 XJ 中学举办,然而由于 ...
- LuoguP7094 [yLOI2020] 金陵谣 题解
Content 有 \(t\) 组询问,每组询问给定四个整数 \(a,b,c,d\),请求出满足 \[\dfrac{a}{x}+\dfrac{b}{c}=\dfrac{d}{y} \] 的正整数对 \ ...
- CF1291A Even But Not Even 题解
Content 有 \(t\) 组数据,每组数据给定一个整数 \(n\),接着给出一个长度为 \(n\) 的数字串.请从中删除一些数,使得剩下的数字串不是偶数,但是其和为偶数,或者不存在这样的方案. ...
- 盘点 2021|「避坑宝典」为大家分享一下笔者在 2021 年所遇到“匪夷所思”的 Bug 趣事(上)
正版内容:https://xie.infoq.cn/article/3145cd5f525fe26ce9d574c8d 2021尾声想跟大家说的话 虚则实之 引用 https://xie.infoq. ...
- libevent源码学习(5):TAILQ_QUEUE解析
目录 前言 结点定义 链表初始化 链表查询及遍历 链表查询 链表遍历 插入结点 头插法 尾插法 前插法 后插法 删除结点 替换结点 总结 前言 在libevent中使用到了TAILQ数据结构,看了一下 ...
- Mybatis-Plus一键生成代码
Mybatis-Plus一键生成代码 一.闲言碎语 闲来无事看了看了MP的官网看到一键生成的代码更新了! 整个Ui风格都变了,遂决定瞅一眼新的代码生成器 官网地址~~ 二.引入依赖 新的代码生成只有在 ...
- docker启动报错:Failed to Setup IP tables: Unable to enable SKIP DNAT rule
Creating network "kafka_default" with the default driverERROR: Failed to Setup IP tables: ...
- 🍃【Spring专题】「原理系列」SpringMVC的运行工作原理(补充修订)
承接相关之前的SpringMVC的框架技术的流程分析 初始化流程(initStrategies) 执行流程 寻找相关HandlerMapping 请求到DispatcherServlet类进行执行相关 ...
- c++设计模式概述之观察者
代码写的不够规范,目的是缩短篇幅,实际情况请不要这样做 1.概述 观察者模式,类比生活中的场景,比如看电影,观众对播放的内容有不同的反应, 再比如订阅,公众号订阅,只要你订阅了其公众号,你就会收到其推 ...