| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 嵌入式 -> STM32F103寄存器方式点亮LED流水灯 -> 正文阅读 |
|
[嵌入式]STM32F103寄存器方式点亮LED流水灯 |
目录 ???????
|
寄存器名称 | 寄存器地址 | 相对GPIOB地址的偏移 |
GPIOB_CRL | 0x4001 0c00 | 0x00 |
GPIOB_CRH | 0x4001 0c04 | 0x04 |
GPIOB_IDR | 0x4001 0c08 | 0x08 |
GPIOB_ODR | 0x4001 0c0c | 0x0c |
GPIOB_BSRR | 0x4001 0c10 | 0x10 |
GPIOB_BRR | 0x4001 0c14 | 0x14 |
GPIOB_LCKR | 0x4001 0c18 | 0x18 |
输入模式
? ? -输入浮空(GPIO_Mode_IN_FLOATING)
? ? -输入上拉(GPIO_Mode_IPU)
? ? -输入下拉(GPIO_Mode_IPD)
? ? -模拟输入(GPIO_Mode_AIN)
输出模式
? ? -开漏输出(GPIO_Mode_Out_OD)
? ? -开漏复用功能(GPIO_Mode_AF_OD)
? ? -推挽式输出(GPIO_Mode_Out_PP)
? ? -推挽式复用功能(GPIO_Mode_AF_PP)
内存映射:事实并非如此,一般的外设为了加快处理速度都有自己的片内RAM(比如说显存,你也知道显存对显卡性能的重要性),分出去的地址空间也就与片内RAM物理连接起来,这样CPU就能像访问内存一样去访问外设的片内RAM,这也就是所谓的内存映射?
存储器映射原理:在存储器的区域单元中,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射
?
选择STM32F103C8芯片:
添加文件:
不要忘记点击魔法棒勾选生成HEX文件:
GPIO端口的初始化设置三步骤
- 时钟配置
- 输入输出模式设置
- 最大速率设置
时钟配置:
GPIO端口B的地址从0x4001 0C00开始。只寻找时钟使能寄存器的地址:复位和时钟控制RCC的地址从0x4002 1000开始;找到APB2外设时钟使能寄存器(RCC_APB2ENR),偏移地址是0x18,所以APB2的地址就是0x4002 1018。RCC_APB2ENR,位3是IOPBEN,名字是IO端口B时钟使能,就是我们想要的。把RCC_APB2ENR的位3赋值为1,就是开启GPIOB时钟。在本次实验中,使用按位操作即可开启GPIO
?RCC->APB2ENR|=1<<3; ? ?//开启GPIOB端口的时钟
?RCC->APB2ENR|=1<<4; ? ?//开启GPIOC端口的时钟
?RCC->APB2ENR|=1<<5; ? ?//开启GPIOD端口的时钟
输入输出模式:点亮led灯应该为推挽模式
?
?这里我分别使用GPIOB、GPIOC、GPIOD这3个端口控制LED灯,在stm32手册中我们选择PB5、PC3、PD2这三个IO口来作为本次实验的输出口
GPIOB->CRL|=0X00003000;PB4推挽输出
GPIOC->CRL|=0X00003000;PC4推挽输出
GPIOD->CRL|=0X00003000;PD4推挽输出
因为00与11组合为3,即输出模式为推挽输出模式,最大速度为50MHz
关于流水灯的有关函数在led.h
中声明
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//LED端口定义
#define LED0 PBout(5)// PB5
#define LED1 PCout(3) // PC3
#define LED2 PDout(2) // PD2
void LED_Init(void); //初始化
#endif
?led.c:通过配置寄存器的值,来改变IO口的值进行变化
#include "sys.h"
#include "led.h"
void LED_Init(void)
{
RCC->APB2ENR|=1<<3; //开启GPIOB端口的时钟
RCC->APB2ENR|=1<<4; //开启GPIOC端口的时钟
RCC->APB2ENR|=1<<5; //开启GPIOD端口的时钟
GPIOB->CRL&=0XFF0FFFFF;
GPIOB->CRL|=0X00300000;//PB5 推挽输出
GPIOB->ODR|=1<<5; //PB5 输出高电平
GPIOC->CRL&=0XFFFF0FFF;
GPIOC->CRL|=0X00003000; //PC3 推挽输出
GPIOC->ODR|=1<<3; //PC3 输出高电平
GPIOD->CRL&=0XFFFFF0FF;
GPIOD->CRL|=0X00000300;//PD2 推挽输出
GPIOD->ODR|=1<<2; //PD2 输出高电平
}
主函数test.c函数?:编写了一个延迟函数进行延时,另外在主函数中采用一个while的死循环控制灯一直轮流亮灭且能观察清楚。
#include "sys.h"
#include "led.h"
void delay(unsigned int i) //延迟函数
{
unsigned char j;
unsigned char k;
for(;i>0;i--)
for(j=500; j>0; j--)
for(k =200; k>0; k--);
}
int main(void)
{
LED_Init(); //初始化与LED连接的硬件接口
while(1)
{
LED0=0;
LED1=1;
LED2=1; //PB5接口连接的LED灯亮
delay(20);
LED0=1;
LED1=0;
LED2=1; //PC3接口连接的LED灯亮
delay(20);
LED0=1;
LED1=1;
LED2=0; //PD2接口连接的LED灯亮
delay(20);
}
}
正点原子STM32mini开发板,杜邦线若干,不同颜色led灯3只,面包板一块
LED灯的短脚连接IO口,长脚连接3.3V(注:连接5V的端口可能会容烧坏LED灯),使用面包板能够更加方便的连线
(1)如图上操作步骤对Fly Mcu V0.188进行操作,该步骤仅为个人习惯,可与之不同
(2)最后一步选择前面工程生成的HEX文件,点击开始编程?
?
?
1.汇编语言代码如下:
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
GPIOA_ODR EQU 0x4001080C
GPIOB_CRL EQU 0x40010C00 ;寄存器映射
GPIOB_ODR EQU 0x40010C0C
GPIOC_CRH EQU 0x40011004
GPIOC_ODR EQU 0x4001100C
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
DCD Reset_Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
MainLoop BL LED2_Init
BL LED2_ON
BL Delay ;LED2灯闪烁
BL LED2_OFF
BL Delay
BL LED1_Init
BL LED1_ON
BL Delay ;LED1灯闪烁
BL LED1_OFF
BL Delay
BL LED3_Init
BL LED3_ON
BL Delay ;LED3灯闪烁
BL LED3_OFF
BL Delay
B MainLoop
LED1_Init
PUSH {R0,R1, LR} ;R0,R1,LR中的值放入堆栈
LDR R0,=RCC_APB2ENR ;LDR是把地址装载到寄存器中(比如R0)。
ORR R0,R0,#0x08 ;开启端口GPIOB的时钟,ORR 按位或操作,01000将R0的第二位置1,其他位不变
LDR R1,=RCC_APB2ENR
STR R0,[R1] ;STR是把值存储到寄存器所指的地址中,将r0里存储的值给rcc寄存器
;上面一部分汇编代码是控制时钟的
LDR R0,=GPIOB_CRL
ORR R0,R0,#0X00000020 ;GPIOB_Pin_1配置为通用推挽输出;开启的是pb1,所以是2,为0010,是推挽输出模式,最大速度为2mhz
LDR R1,=GPIOB_CRL
STR R0,[R1]
LDR R0,=GPIOB_ODR
BIC R0,R0,#0X00000002 ;BIC 先把立即数取反,再按位与
LDR R1,=GPIOB_ODR ;GPIOB_Pin_1输出为0;由r1控制ord寄存器
STR R0,[R1] ;将ord寄存器的值变为r0的值
POP {R0,R1,PC} ;将栈中之前存的R0,R1,LR的值返还给R0,R1,PC
LED1_OFF
PUSH {R0,R1, LR}
LDR R0,=GPIOB_ODR
BIC R0,R0,#0X00000002 ;因为是PB1所以对应二进制0010;GPIOB_Pin_1输出为0,LED1熄灭
LDR R1,=GPIOB_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED1_ON
PUSH {R0,R1, LR}
LDR R0,=GPIOB_ODR
ORR R0,R0,#0X00000002 ;GPIOB_Pin_1输出为1,LED1亮
LDR R1,=GPIOB_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED2_Init
PUSH {R0,R1, LR};R0,R1,LR中的值放入堆栈
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x04 ;打开GPIOA的时钟
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOA_CRH
ORR R0,R0,#0X00020000 ;GPIOA_Pin_12配置为通用推挽输出
LDR R1,=GPIOA_CRH
STR R0,[R1]
LDR R0,=GPIOA_ODR
BIC R0,R0,#0X00001000
LDR R1,=GPIOA_ODR ;GPIOA_Pin_12输出为0
STR R0,[R1]
POP {R0,R1,PC}
LED2_OFF
PUSH {R0,R1, LR}
LDR R0,=GPIOA_ODR
BIC R0,R0,#0X00001000 ;GPIOA_Pin_12输出为0,LED2熄灭
LDR R1,=GPIOA_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED2_ON
PUSH {R0,R1, LR}
LDR R0,=GPIOA_ODR
ORR R0,R0,#0X00001000 ;GPIOA_Pin_12输出为1,LED2亮
LDR R1,=GPIOA_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED3_Init
PUSH {R0,R1, LR}
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x10 ;打开GPIOC的时钟
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOC_CRH
ORR R0,R0,#0X02000000 ;GPIOC_Pin_14配置为通用推挽输出
LDR R1,=GPIOC_CRH
STR R0,[R1]
LDR R0,=GPIOC_ODR
BIC R0,R0,#0X00004000 ;GPIOC_Pin_14输出为0
LDR R1,=GPIOC_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED3_OFF
PUSH {R0,R1, LR}
LDR R0,=GPIOC_ODR
BIC R0,R0,#0X00004000 ;GPIOC_Pin_14输出为0,LED3熄灭
LDR R1,=GPIOC_ODR
STR R0,[R1]
POP {R0,R1,PC}
LED3_ON
PUSH {R0,R1, LR}
LDR R0,=GPIOC_ODR
ORR R0,R0,#0X00004000 ;GPIOC_Pin_14输出为1,LED3亮
LDR R1,=GPIOC_ODR
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,#300
BCC DelayLoop0
MOVS R0,#0
ADDS R1,R1,#1
CMP R1,#300
BCC DelayLoop0
MOVS R0,#0
MOVS R1,#0
ADDS R2,R2,#1
CMP R2,#15
BCC DelayLoop0
POP {R0,R1,PC}
END
(1)这部分采用的是软件延时,具体原理为R0
、R1
、R2
初始化为0,R0
加1,当R0大于300时,R1加1,然后R0为变为0,R0继续加1,循环往复,当R1大于300时,R2加1,R0、R1变为0,继续此操作。
(2)这部分汇编语言的代码本质上也是通过改变寄存器的值来实现电平的变化,进行LED的轮流点亮,但是代码部分重复,我是参考同学的代码,不清楚该如何修改。
(3)HEX文件烧录同上步骤,不在重复,运行结果如下:
?
在开始学习STM32F103系列芯片的地址映射和寄存器映射原理时,在最初查看零死角玩转STM32—F103指南者时,觉得概念太过抽象,其中映射的概念并不是很懂,但在CSDN上查看博客时,发现寄存器映射的关系和数学中函数的映射可以类比,然后在慢慢理解。LED程序的编写,关于汇编语言的时候,是借鉴的同学的代码,而且关于汇编语言特别陌生而且不知道从何下手。整次实验下来,在代码改编的时候只有C语言能做到,而且出现了很多问题,包括程序的烧录,串口软件的使用,汇编程序代码的调试和改变,直到最后在同学的帮助下才完成。本次实验在同学的帮助下最后成功点亮了LED流水灯十分高兴,也有一点成就感。在以后的学习中,希望继续能够坚持下去。
参考网站:
?STM32的8种GPIO输入输出模式深入详解_baidu_37366055的博客-CSDN博客
(stm32学习总结)—对寄存器的理解 - 北极星! - 博客园?
STM32从地址到寄存器_geekYatao-CSDN博客_stm32寄存器地址
【嵌入式07】寄存器映射原理详解,GPIO端口的初始化设置步骤_噗噗的罐子博客-CSDN博客?
?参考书籍:
野火之零死角玩转STM32—F103指南者
正点原子STM32mini板开发手册
|
嵌入式 最新文章 |
基于高精度单片机开发红外测温仪方案 |
89C51单片机与DAC0832 |
基于51单片机宠物自动投料喂食器控制系统仿 |
《痞子衡嵌入式半月刊》 第 68 期 |
多思计组实验实验七 简单模型机实验 |
CSC7720 |
启明智显分享| ESP32学习笔记参考--PWM(脉冲 |
STM32初探 |
STM32 总结 |
【STM32】CubeMX例程四---定时器中断(附工 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 6:22:18- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |