目录
一、关于STM32芯片寄存器1.什么是寄存器
2.怎么找到某个寄存器的地址
3.寄存器映射
二,实验原理
2.1
2.2 stm32f103c8t6介绍
三、C语言实现
四、用示波器仿真查看代码是否正确
1.设置魔法棒信息
2.设置示波器
3.调试运行
五、总结
六、参考资料
一、关于STM32芯片寄存器 1.什么是寄存器
提到单片机,就不得不提到寄存器。根据百度百科介绍,寄存器是中央处理器内的组成部分。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。
简单来说,寄存器就是存放东西的东西。从名字来看,跟火车站寄存行李的地方好像是有关系的。只不过火车站行李寄存处,存放的行李;寄存器可能存放的是指令、数据或地址。 存放数据的寄存器是最好理解的,如果你需要读取一个数据,直接到这个寄存器所在的地方来问问他,数据是多少就行了。问寄存器这个动作,叫做访问寄存器。不同的数据会存放在不同的寄存器,例如引脚PA2与PB8的高低电平数据(1或0)肯定放在不同的寄存器里,那么怎么区分不同的寄存器呢?通过地址,不同的寄存器有不同的地址,就像老张行李寄存处在101号店铺,老王行李寄存处在258号店铺。 指令、地址寄存器与数据寄存器类似,里边存放的都是0和1,毕竟单片机也只认识机器码,机器码都是0或1,只是特别的规定下,数据寄存器里面存放的0和1表示数据,指令寄存器里存放的表示指令。
2.怎么找到某个寄存器的地址
想要找到某个寄存器的地址,可以查看《STM32中文参考手册_V10》,手册可以在这里下载,但是手册中没有直接给出所有的寄存器的地址,需要读者稍加计算,具体可查看这里。
3.寄存器映射
我们知道,存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫 寄存器映射?寄存器到底是什么? 在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共 32bit, 每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到 每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通 过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的 不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个 给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
二,实验原理
2.1
寄存器可以存储数据,指令,也可以担任一些特定的功能,stm32板子里由很多寄存器,如果想实现流水灯操作,就需要对相应的引脚进行操作,想对引脚进行操作,就需要对相应的引脚进行时钟使能配置、端口配置(高or低)寄存器配置、端口输出寄存器配置,比如我的板子是STM32F103C8,我就是A7,B9,C15这三个口,这个是根据每个人不同的板子不同
2.2 stm32f103c8t6介绍
- STM32F103C8T6是一款由意法半导体公司(ST)推出的基于Cortex-M3内核的32位微控制器,硬件采用LQFP48封装,属于ST公司微控制器中的STM32系列。
1.2 stm32f103c8t6点亮流水灯原理 寄存器可以存储数据,指令,也可以担任一些特定的功能,stm32板子里由很多寄存器,如果想实现流水灯操作,就需要对相应的引脚进行操作,想对引脚进行操作,就需要对相应的引脚进行时钟使能配置、端口配置(高or低)寄存器配置、端口输出寄存器配置,也就是一下步骤
1.因为流水灯要操作的引脚都是在GPIO端口的,所以根据系统结构图,属于AHB总线,所以所要用的端口的复位和时间控制都受RCC控制。
?2.再看寄存器组起始地址表,可以看到RCC的地址范围,且可以看到要控制的寄存器都是在APB2总。
3.跳到这里,就是外设时钟使能寄存器,,偏移量为0x18,而在前面一个表可以看到起始地址为0x4002 1000,偏移量为0x18,所以该寄存器的地址为0x4002 1018
4.图中圈处理就是该寄存器里各位的含义,比如第三位也就是2那个位置为1时,就是GPIOA的时钟开启了。这时我们就可以用代码表达出来了,以PA7引脚为例
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018) #时钟使能寄存器
RCC_AP2ENR|=1<<2; //开启APB2-GPIOA外设时钟使能
5.接下来就是配置端口配置寄存器了,这个就比较关键了,可以发现上面的时钟使能寄存器开启时钟是针对一个区域的,并不能确定引脚,而这个寄存器就是确定引脚的,端口配置寄存器有两个,分别为端口配置低寄存器(CRL)和端口配置高寄存器(CRH),每四位配置一个端口,如11 01,11就是选择开启功能,01就是选择模式和确定最大速度,但有一点不一样,低寄存器的偏移地址为0x00,高寄存器的偏移地址为0x04
6.以PA7为示例,相应端口配置器GPIOA_CRL地址为GPIOA的基址+上偏移量,为0x40010800,而这个端口要开启,所以要使对应位为相应的值,我这里是0x20000000,设置推挽输出并设置最大速度为2Mhz,下面为相应代码
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
GPIOA_CRL=0x20000000; //PA7推挽输出,2Mhz
7.接下来就是配置端口输出寄存器(ORD),可以看到偏移量为0xc,所以该寄存器的地址等于端口的基址加上偏移量,在相应的位赋值可以控制输出电压,0为低电压,1为高电压,以pa7引脚为例子,想要输出高电压,就需要在第八位赋1。
#define GPIOA_ORD *((unsigned volatile int*)0x4001080C)
GPIOA_ORD|=1<<7; //设置初始灯为亮
三、C语言实现
新建一个.c文件
//--------------APB2使能时钟寄存器------------------------
#define RCC_AP2ENR *((unsigned volatile int*)0x40021018)
//----------------GPIOA配置寄存器 ------------------------
#define GPIOA_CRL *((unsigned volatile int*)0x40010800)
#define GPIOA_ORD *((unsigned volatile int*)0x4001080C)
//----------------GPIOB配置寄存器 ------------------------
#define GPIOB_CRH *((unsigned volatile int*)0x40010C04)
#define GPIOB_ORD *((unsigned volatile int*)0x40010C0C)
//----------------GPIOC配置寄存器 ------------------------
#define GPIOC_CRH *((unsigned volatile int*)0x40011004)
#define GPIOC_ORD *((unsigned volatile int*)0x4001100C)
//-------------------简单的延时函数-----------------------
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
void A_LED_LIGHT(){
GPIOA_ORD=0x0<<7; //PA7低电平
GPIOB_ORD=0x1<<9; //PB9高电平
GPIOC_ORD=0x1<<15; //PC15高电平
}
void B_LED_LIGHT(){
GPIOA_ORD=0x1<<7; //PA7高电平
GPIOB_ORD=0x0<<9; //PB9低电平
GPIOC_ORD=0x1<<15; //PC15高电平
}
void C_LED_LIGHT(){
GPIOA_ORD=0x1<<7; //PA7高电平
GPIOB_ORD=0x1<<9; //PB9高电平
GPIOC_ORD=0x0<<15; //PC15低电平
}
//------------------------主函数--------------------------
int main()
{
int j=100;
RCC_AP2ENR|=1<<2; //APB2-GPIOA外设时钟使能
RCC_AP2ENR|=1<<3; //APB2-GPIOB外设时钟使能
RCC_AP2ENR|=1<<4; //APB2-GPIOC外设时钟使能
//这两行代码可以合为 RCC_APB2ENR|=1<<3|1<<4;
GPIOA_CRL&=0x0FFFFFFF; //设置位 清零
GPIOA_CRL|=0x20000000; //PA7推挽输出
GPIOA_ORD|=1<<7; //设置PA7初始灯为灭
GPIOB_CRH&=0xFFFFFF0F; //设置位 清零
GPIOB_CRH|=0x00000020; //PB9推挽输出
GPIOB_ORD|=1<<9; //设置初始灯为灭
GPIOC_CRH&=0x0FFFFFFF; //设置位 清零
GPIOC_CRH|=0x30000000; //PC15推挽输出
GPIOC_ORD|=0x1<<15; //设置初始灯为灭
while(j)
{
A_LED_LIGHT();
Delay_ms(10000000);
B_LED_LIGHT();
Delay_ms(10000000);
C_LED_LIGHT();
Delay_ms(10000000);
}
}
点击build,这时在文件夹目录下就可以看到有一个hex文件生成,hex文件就是我们需要下载到板子里的程序 然后就是在FlyMcu中烧录
?索串口,这时你连接的串口就会显示出来,然后点击2处,找到自己的hex文件。 然后就是将板子上的boot0置1,boot1置0,按下reset键
然后点击开始编程就可以了
索串口,这时你连接的串口就会显示出来,然后点击2处,找到自己的hex文件。 然后就是将板子上的boot0置1,boot1置0,按下reset键
然后点击开始编程就可以了
?板子的显示
四、用示波器仿真查看代码是否正确
1.设置魔法棒信息
点开魔法棒,如图设置
?
?
2.设置示波器
点击如图所示按键
设置输出口(灯接的芯片位置)
?因为我的灯连的芯片管脚分别是A0、B0、C15,并且是GPIO口,所以输入(以A0为例):GPIOA_IDR.0 ,之后回车,然后依照上图设置bit,颜色。具体输入形式可以参考如何使用Keil5中的虚拟示波器进行软件仿真
3.调试运行
点击如图所示按钮
一段时间后,可以看到Logic Analyzer里面三个波形的显示,观察图像可以看到与代码一致,说明正确执行了。
?
五、总结
总结下来,这个对初学者真的有很大的难度,尤其是代码的编写,我就参考了很多大佬写的代码,整体繁琐,而且运行不起来不一定是代码问题,还可能是板子问题,所以要细心一点。
六、参考资料
STM32F103寄存器方式点亮LED流水灯_小小星亮晶晶的博客-CSDN博客
STM32F103寄存器方式点亮LED流水灯(软硬结合初尝试)_LaiYiFei25的博客-CSDN博客
STM32F103C8T6实现流水灯_可小阿木的博客-CSDN博客
|