STM32F103寄存器方式点亮LED流水灯
嵌入式系统第六周作业
1、学习和理解STM32F103系列芯片的地址映射和寄存器映射原理;了解GPIO端口的初始化设置三步骤(时钟配置、输入输出模式设置、最大速率设置)。 2、以 STM32最小系统核心板(STM32F103C8T6)+面板板+3只红绿蓝LED 搭建电路,使用GPIOB、GPIOC、GPIOD这3个端口控制LED灯(最高时钟2Mhz),轮流闪烁,间隔时长1秒。
1)写出程序设计思路,包括GPIOx端口的各寄存器地址和详细参数;
2)分别用汇编语言,C语言编程实现。
一、原理
STM32F103系列芯片的地址映射和寄存器映射原理
STM32寄存器简介
寄存器的功能是存储二进制代码,它是由具有存储功能的触发器组合起来构成的。一个触发器可以存储1位二进制代码,故存放n位二进制代码的寄存器,需用n个触发器来构成。 按照功能的不同,可将寄存器分为基本寄存器和移位寄存器两大类。基本寄存器只能并行送入数据,也只能并行输出。移位寄存器中的数据可以在移位脉冲作用下依次逐位右移或左移,数据既可以并行输入、并行输出,也可以串行输入、串行输出,还可以并行输入、串行输出,或串行输入、并行输出,十分灵活,用途也很广。
- 如何找到某个寄存器的地址
不同的寄存器有不同的地址。
- 找到GPIOB的基地址:所有GPIOB相关的寄存器,都住在0x4001 0C00到0x4001 0FFF范围内。
- 找到端口输入寄存器的地址偏移
- 找到知道数据的那个人
地址映射和寄存器映射原理
-
映射:两个非空集合A与B间存在着对应关系f,而且对于A中的每一个元素a,B中总有唯一的一个元素b与它对应,就这种对应为从A到B的映射,记作f:A→B。其中,b称为元素a在映射f下的像 [1] ,记作:b=f(a)。a称为b关于映射f的原像 [1] 。集合A中所有元素的像的集合称为映射f的值域,记作f(A)。
- 外围设备的内存映射原理是一样的,只不过A集合变成了CPU,B集合变成了外围设备,对应关系就是连接CPU和外设地址引脚的地址总线。
- 存储器本身不具有地址信息,它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就称为存储器映射。
- 内存映射:外设为了加快处理速度都有自己的片内RAM,分出去的地址空间也就与片内RAM物理连接起来,这样CPU就能像访问内存一样去访问外设的片内RAM。
- 在存储器的区域单元中,每一个单元对应不同的功能,控制这些单元时可以驱动外设工作。首先找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,但是每次都通过这种地址的方式来访问,耗费更多时间。所以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是寄存器,给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
GPIO端口的初始化设置三步骤(时钟配置、输入输出模式设置、最大速率设置)
1.GPIO简介
GPIO(英语:General-purpose input/output),通用型之输入输出的简称,功能类似8051的P0—P3,其接脚可以供使用者由程控自由使用,PIN脚依现实考量可作为通用输入(GPI)或通用输出(GPO)或通用输入与输出(GPIO),如当clk generator, chip select等。 既然一个引脚可以用于输入、输出或其他特殊功能,那么一定有寄存器用来选择这些功能。对于输入,一定可以通过读取某个寄存器来确定引脚电位的高低;对于输出,一定可以通过写入某个寄存器来让这个引脚输出高电位或者低电位;对于其他特殊功能,则有另外的寄存器来控制它们。
stm32的时钟是由内部或外部振荡器产生的“频率”,而被人们形象的称为“系统时钟”。最大为72MHz换成周期T为:1/72MHz≈13.9ns
- 为什么要用时钟?
使用哪个外设就给哪个外设时钟(频率),不使用的就关掉(不震荡)。此做法大大降低了功耗,续航持久。
2.GPIO模式
- GPIO_Mode_AIN 模拟输入
- GPIO_Mode_IN_FLOATING 浮空输入
- GPIO_Mode_IPD 下拉输入
- GPIO_Mode_IPU 上拉输入
- GPIO_Mode_Out_OD 开漏输出
- GPIO_Mode_Out_PP 推挽输出
- GPIO_Mode_AF_OD 复用开漏输出
- GPIO_Mode_AF_OD 复用开漏输出
在传统的51单片机中,IO口只是输出高低电平就行,但是stm32因为功能的强大,所以IO口就被赋予了非常重要的使命——让CPU与各种外部芯片、元器件等进行“沟通”,因为模式的不同,所以一个引脚就可以接非常多的外部器件,而又引申出了“AFIO端口重映射”功能
3.输出和输入
输出是CPU计算后进行控制,输入是读取后给CPU进行计算。 输出:GPIO的输出与51的 IO口是差不多的概念,都是输出高、低电平来控制外部电路。 处理过程:CPU下达输出高或低电平指令,指令配置“位设置/清除寄存器(GPIOx_BSRR)”(设置就是“1”高电平,清除就是“0”低电平),再由位寄存器配置输出数据寄存器(GPIOx_ODR),经过一个选择器(选择是一般输出还是复用功能输出),然后进行输出控制,然后输出高或低电平到IO口。
输入:进行数据的采集,外部电路通过IO口输入模拟量,然后通过“TTL肖特基触发器”(肖特基触发器是将相对缓慢变化的模拟信号变成矩形信号,便于后面读取),进入输入数据寄存器,最后就能给CPU读取数据。
4.GPIO初始化步骤
- 使能GPIO时钟,RCC_APB2PeriphClockCmd。
- 设置GPIO参数:输出OR输入,工作模式,端口翻转速率;
- 调用初始化函数:GPIO_Init
- 使用GPIO。
二、STM32F103C8T6实现LED灯轮流闪烁
1.连接电路
2.构建项目
1.c语言实现
- 新建项目
New uVision Project,文件名LED: 选择芯片:根据实验要求选择F103c8 添加文件main.c: 将startup_stm32f10x_md.s 粘贴到程序目录下 下载地址野火产品资料下载中心 添加文件 魔棒进行一些设置 首先在main.c中写入空的main函数:
int main(){
}
编译,发现报错,添加SystemInit函数:
void SystemInit(void);
void SystemInit(){
}
加上循环等总代码:
#define GPIOB_BASE 0x40010C00
#define GPIOC_BASE 0x40011000
#define GPIOA_BASE 0x40010800
#define RCC_APB2ENR (*(unsigned int *)0x40021018)
#define GPIOB_CRL (*(unsigned int *)0x40010C00)
#define GPIOC_CRH (*(unsigned int *)0x40011004)
#define GPIOA_CRL (*(unsigned int *)0x40010800)
#define GPIOB_ODR (*(unsigned int *)0x40010C0C)
#define GPIOC_ODR (*(unsigned int *)0x4001100C)
#define GPIOA_ODR (*(unsigned int *)0x4001080C)
void SystemInit(void);
void Delay_ms(volatile unsigned int);
void Delay_ms( volatile unsigned int t)
{
unsigned int i;
while(t--)
for (i=0;i<800;i++);
}
int main(){
RCC_APB2ENR |= (1<<3);
RCC_APB2ENR |= (1<<4);
RCC_APB2ENR |= (1<<2);
GPIOB_CRL |= (1<<0);
GPIOB_CRL &= ~(0xE);
GPIOC_CRH |= (1<<28);
GPIOC_CRH &= ~(0xE0000000);
GPIOA_CRL |= (1<<0);
GPIOA_CRL &= ~(0xE);
GPIOB_ODR |= (1<<0);
GPIOC_ODR |= (1<<15);
GPIOA_ODR |= (1<<0);
while(1){
GPIOB_ODR &= ~(1<<0);
Delay_ms(1000000);
GPIOB_ODR |= (1<<0);
Delay_ms(1000000);
GPIOC_ODR &= ~(1<<15);
Delay_ms(1000000);
GPIOC_ODR |= (1<<15);
Delay_ms(1000000);
GPIOA_ODR &= ~(1<<0);
Delay_ms(1000000);
GPIOA_ODR |= (1<<0);
Delay_ms(1000000);
}
}
void SystemInit(){
}
- 连接到电脑,打开FlyMcu,上传.hex文件到stm32f103c8t6上:
这里第一次编译的 时候红光常亮,不会进入循环参考博客https://blog.csdn.net/hlhe_14/article/details/118313421加了一个 成功!
实验结果:由于无法上传大于5M的视频以及外链图片,这里以gif的网址的形式呈现
2.汇编语言实现
汇编语言其余步骤与c语言一致,但是需要建立.s文件
RCC_APB2ENR EQU 0x40021018;配置RCC寄存器,时钟,0x40021018为时钟地址
GPIOB_BASE EQU 0x40010C00
GPIOC_BASE EQU 0x40011000
GPIOA_BASE EQU 0x40010800
GPIOB_CRL EQU 0x40010C00
GPIOC_CRH EQU 0x40011004
GPIOA_CRL EQU 0x40010800
GPIOB_ODR EQU 0x40010C0C
GPIOC_ODR EQU 0x4001100C
GPIOA_ODR EQU 0x4001080C
Stack_Size EQU 0x00000400;栈的大小
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;NOINIT: = NO Init,不初始化。READWRITE : 可读,可写。ALIGN =3 : 2^3 对齐,即8字节对齐。
Stack_Mem SPACE Stack_Size
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
bl LED_Init;bl:带链接的跳转指令。当使用该指令跳转时,当前地址(PC)会自动送入LR寄存器
MainLoop BL LED_ON_C
BL Delay
BL LED_OFF_C
BL Delay
BL LED_ON_A
BL Delay
BL LED_OFF_A
BL Delay
BL LED_ON_B
BL Delay
BL LED_OFF_B
BL Delay
B MainLoop;B:无条件跳转。
LED_Init;LED初始化
PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
;控制时钟
LDR R0,=RCC_APB2ENR;LDR是把地址装载到寄存器中(比如R0)。
ORR R0,R0,#0x1c
LDR R1,=RCC_APB2ENR
STR R0,[R1]
;初始化GPIOA_CRL
LDR R0,=GPIOA_CRL
BIC R0,R0,#0x0fffffff;BIC 先把立即数取反,再按位与
LDR R1,=GPIOA_CRL
STR R0,[R1]
LDR R0,=GPIOA_CRL
ORR R0,#0x00000001
LDR R1,=GPIOA_CRL
STR R0,[R1]
;将PA0置1
MOV R0,#0x01
LDR R1,=GPIOA_ORD
STR R0,[R1]
;初始化GPIOB_CRL
LDR R0,=GPIOB_CRL
BIC R0,R0,#0x0fffffff;BIC 先把立即数取反,再按位与
LDR R1,=GPIOB_CRL
STR R0,[R1]
LDR R0,=GPIOB_CRL
ORR R0,#0x00000001
LDR R1,=GPIOB_CRL
STR R0,[R1]
;将PB0置1
MOV R0,#0x01
LDR R1,=GPIOA_ORD
STR R0,[R1]
;初始化GPIOC
LDR R0,=GPIOC_CRH
BIC R0,R0,#0x0fffffff
LDR R1,=GPIOC_CRH
STR R0,[R1]
LDR R0,=GPIOC_CRH
ORR R0,#0x01000000
LDR R1,=GPIOC_CRH
STR R0,[R1]
;将PC15置1
MOV R0,#0x8000
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC};将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
LED_ON_A
PUSH {R0,R1, LR}
MOV R0,#0x00
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_A
PUSH {R0,R1, LR}
MOV R0,#0x01
LDR R1,=GPIOA_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_B;亮灯
PUSH {R0,R1, LR}
MOV R0,#0x00
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_B;熄灯
PUSH {R0,R1, LR}
MOV R0,#0x01
LDR R1,=GPIOB_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_ON_C;亮灯
PUSH {R0,R1, LR}
MOV R0,#0x00
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF_C;熄灯
PUSH {R0,R1, LR}
MOV R0,#0x0100
LDR R1,=GPIOC_ORD
STR R0,[R1]
POP {R0,R1,PC}
Delay
PUSH {R0,R1, LR}
MOVS R0,#0
MOVS R1,#0
MOVS R2,#0
DelayLoop0
ADDS R0,R0,#1
CMP R0,#330
BCC DelayLoop0
MOVS R0,#0
ADDS R1,R1,#1
CMP R1,#330
BCC DelayLoop0
MOVS R0,#0
MOVS R1,#0
ADDS R2,R2,#1
CMP R2,#15
BCC DelayLoop0
POP {R0,R1,PC}
NOP
END
![请添加图片描述](https://img-blog.csdnimg.cn/41e3c109d97e4ac0baaa7619082564f3.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBAempzemQ=,size_19,color_FFFFFF,t_70,g_se,x_16)
需要取消勾选
三、总结
通过这次实验,我了解了STM32F103系列芯片的地址映射和寄存器映射原理以及GPIO端口的初始化设置三步骤等,在实验课也努力完成LED的闪烁,但是当时只实现了亮灯没有实现闪烁,后来在查找资料的过程中解决了这个问题。
四、参考文献
https://blog.csdn.net/geek_monkey/article/details/86291377 https://blog.csdn.net/qq_46467126/article/details/120737655 https://blog.csdn.net/asdfg1075511750/article/details/79663568?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.no_search_link https://blog.csdn.net/asdfg1075511750/article/details/79721878 https://blog.csdn.net/weixin_46628481/article/details/120800967
|