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.cnblogs.com/jzcn/p/15784150.html
如本博客的内容侵犯了你的权益,请与以下地址联系,本人获知后,马上删除。同时本人深表歉意,并致以崇高的谢意! cn_jiaozhu@qq.com
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现