学习stm32已经一个月时间了,可算是入门了。
相信很多人跟我一样,刚开始学的时候会遇到很多问题,以及对stm32里面众多寄存器的恐惧。要利用stm32官方给的固件库开发,少不了的还是要对寄存器有一定的了解。下面我就谈谈我的入门之路吧。
刚开始接触到的时候,还是看原子的视频,但是那个视频就想大型连续剧一样,一看时长就让人没什么欲望学下去了,而且,前面有有两三个小时都是都是些芯片的基本的介绍和选型手册的介绍,其实刚开始学,没必要了解这么多,后面学习有关功能的使用再去了解,就自然能看懂那些手册了,所以原子的视频,我看了几节,是真没什么学习的欲望了。
后面我自己在b站上找到了海创电子的stm32的视频还是学习,海创的教学视频比原子的讲得通俗易懂,而且刚开始学,讲了最最基本的操纵地址去控制IO口,然后是讲了固件库中的宏替换的地址,然后去推算各个外设,GPIO,时钟等等的地址,然后一步一步仿照库函数优化程序,这其中也包含了结构体,枚举,结构体指针的传递等等c语言的知识,然后就是一边看海创的视频,一边复习c语言,慢慢的基本也就能看懂固件库中那些初始化函数的参数是怎么传递到寄存器中的了。
再次强调一下,一定要多去看C语言后面那部分,因为固件库中的参数传递会涉及到。
即使是使用固件库开发,也还是要对寄存器有一定了解。
下面是刚开学的时候,了解到的最底层的操作方式,加上自己的一些理解。
int main(void)
{
*(unsigned int*)0x4002 1018 |=(1<<4);
//打开时钟
*(unsigned int*)0x40011004 &= ~(0x0F<<(4*5));
//配置输出模式
*(unsigned int*)0x40011004 |= (1<<20);
*(unsigned int*)0x4001 100C&=~(1<<13)//配置输出低电平
*(unsigned int*)0x4001 100C|=(1<<13)//配置输出高电平
}
【stm32|stm32学习之路,方法很重要】Stm32最最底层、完全暴力的方式点亮LED,可读性比较差。
以PC13引脚输出高低电平
//根据单片机的地址映射,查找到相应总线地址,再去查找相应的GPIO的地址(可直接查表,也可根据相对总线的地址去算偏移地址),在相应的GPIO下面,又有对应的寄存器的地址,最后就可以算出要操作的寄存器的地址。
关于上面这段文字,如果看不懂的话,那么你就要去看一看stm32存储器的映射了。
找到相应的地址后,这个还只是一个数字,跟程序没有关系,通过定义一个(unsigned int*)类型的指针,把这个数字转换成单片机能够识别的相应的地址,再在这个地址前面加上*,如*(unsigned int*)0x4002 1018,就可以对它进行赋值了,*表示取地址的内容。
这些数字就是寄存器所在的地址。
下面的程序和上面的是一个意思,只是把相应的地址操作用#define替换成了相应的寄存器名字。
#define PERIPH_BASE0x4000 0000
//片上外设基地址
#defineAPB1PERIPH_BASEPERIPH_BASE// APB1总线地址
#defineAPB2PERIPH_BASE(PERIPH_BASE+0x00010000)//APB2总线地址
#defineAHBPERIPH_BASE(PERIPH_BASE+0x00020000)//AHB总线地址#defineRCC_BASE(AHBPERIPH_BASE+0x1000)//时钟地址
#defineRCC_APB2ENR*(unsigned int*)(RCC_BASE+0x18)
//APB2外设时钟使能寄存器地址#defineGPIOC_BASE(APB2PERIPH_BASE+0x00010000)//GPIOC地址
//GPIOC相关寄存器的地址
#define GPIOC_CRL*(unsigned int *)( GPIOC_BASE+0x00)
#define GPIOC_CRH*(unsigned int *)( GPIOC_BASE+0x04)
#define GPIOC_IDR*(unsigned int *)( GPIOC_BASE+0x08)
#define GPIOC_ODR*(unsigned int *)( GPIOC_BASE+0x0C)
#define GPIOC_BSRR*(unsigned int *)( GPIOC_BASE+0x10)
#define GPIOC_BRR*(unsigned int *)( GPIOC_BASE+0x14)
#define GPIOC_LCKR*(unsigned int *)( GPIOC_BASE+0x18)
这些宏定义可直接定义在头文件中。
以上实现过程都是先找到基地址,再经过偏移取找到相应的地址int main(void)
{
RCC_APB2 |=(1<<4);
//打开时钟
GPIOC_CRH &= ~(0x0F<<(4*5));
//配置输出模式
GPIOC_CRH |= (1<<20);
GPIOC_ODR&=~(1<<13);
//配置输出低电平
GPIOC_ODR|=(1<<13);
//配置输出高电平
}
这样可读性就要强很多了。
后面又是了解到了固件库中对很多寄存器都是封装了的
由于GPIOA—GPIOG有众多组,每组有7个寄存器,如果每次都去找地址再用宏替换,就会写很多,实际上每个GPIO组,里面的7个寄存器都是一样的,所以可以直接把这七个寄存器定义在一起(采用结构体方式)
typedef unsigned int uint32_t;
(4字节)
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
}GPIO_TypeDef;
这样封装的目的,当然是为了方便开发者使用时更方便调用。
下面的解读,很重要!很重要!很重要!
结构体中定义的变量都是连续排列的,恰好每个寄存器都是32位的,占用4个字节,这个就和每个寄存器在实际的存储器映像中偏移量是一样的了,看上面的结构体,我们只需要,定义一个结构体指针,让他指向需要使用到的GPIO口的地址就可以了。就相当于给了一个入口地址,再用结构体指针,就很容易的引用其中的成员变量了。
#define GPIOC ((GPIO_TypeDef*)GPIOC_BASE)
这条语句中,GPIOC_BASE是之前用宏定义过的GPIOC的地址(只是一个数值),前面加上一个(GPIO_TypeDef*)把他转换为单片机能够识别的地址。这里的GPIOC_BASE就相当于给这个结构体传递了一个GPIO口的地址,然后就可以很方便的引用GPIO口中的寄存器了。
上面这条语句的意思就是用GPIOC替换替换了 (GPIO_TypeDef*)类型的GPIOC的地址
如果要引用GPIOC中的寄存器,用结构体成员指针就ok。
GPIOC->CRH GPIOC->ODR
时钟相关的寄存器定义也和GPIO口差不多。
基本学习了这些,对固件库中的一些东西也有了初步的认识了,别说使用库函数开发就不用看这些,那都是些屁话。
后面就是具体学习了
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
这个函数的参数传递过程,不得不说这些工程师是真的牛逼,基本了解完这个函数的参数传递之后,其他的函数传递都差不多。就对stm32这些函数有个初步的认识了吧。
这些了解完之后,就可以开始学习,stm32的模板学习了,刚开始接触的时候,我想大多数人和我一样,并不想用这个模板,因为我们以前写的51,arduino,基本一个工程就一个文件,很简单,stm32的模板一下子多了这么多东西,肯定是不知道从何下手的。
对于模板的学习呢,首先是要了解这个模板是怎么配置的,哪些文件是必须的,哪些不必要的,以及各个文件都是关于什么模块的,这些了解清楚之后,差不多就有一个初步认识了,然后再去了解一下MDK软件是怎么运行的,差不多你就知道为什么需要启动文件,和为什么不需要SYsInit()这个函数了,因为有些东西是在启动文件中定义过了,启动文件也决定了这个程序的入口。
模板配置好之后,就可以开始学习各个模块了,由于是用的库函数开发,所以并不推荐大家像之前那样去学习,因为你会发现学习效率非常低,很难就不说了,众多的寄存器,一会又要操作这个寄存器,一会那个寄存器,很快就绕晕了。
所以学习这部分,我也是转变了思路
1,学习工作原理,比如中断,怎么实现中断,中断检测的是什么,怎么去管理中断,定时器时间的计算,pwm的频率计算等等
2,配置的相关初始化函数,这部分,所用到函数我都会去固件库查功能,相应的参数,再结合原理去理解每个参数干嘛用的。
3,配置自己的模板,这个是我们自己的模板,比如要用pwm模块,自己配置一个PWM.c文件和PWM.h文件。
4,剩下的就是学习相关驱动模块了,比如电机,舵机,编码器,角度传感器等等。
5,使用库函数开发的时候,不要过多的纠结寄存器怎么配置的,用到了哪些寄存器,心里有个数就好,用自己配置好的模板来开发项目。
7.推荐大家找本实战开发的书,再配合原子的视频或者一些博主写的学习笔记学习学习,这样会比单看视频或者书有用得多。
我目前就是这样学习stm32的,我想等我把32的外设资源都学得差不多了,差不多就可以去了解stm32内核哪些事了。
这些学习经历分享给初学的朋友们,希望你们戒骄戒躁,一步一步一个脚印。
推荐阅读
- backup|backup ram不稳定 stm32_STM32信息安全硬件特性
- MCU项目技术总结|基于stm32F4的项目总结(控制层设计(四)直流有刷电机驱动原理及驱动器选型)
- stm32|如何使用STM32F1/F4驱动CS5463
- 单片机毕业设计|800多套单片机毕业设计
- 嵌入式|网上五花八门的单片机教程,到底应该怎么整理学习过程
- 嵌入式|单片机有没有想象中那么好学,很迷茫有说正面有说负面
- 蓝桥杯单片机真题|第十三届蓝桥杯单片机省赛模拟题(客观题程序题)
- 蓝桥杯单片机|【蓝桥杯单片机的备赛旅行日记系列】之省赛初赛代码免费下载
- 蓝桥杯单片机|蓝桥杯单片机 省赛 第13届模拟题