野火霸天虎 STM32F407 学习笔记_3 尝试寄存器映射方式点亮 LED 灯

本文主要是介绍野火霸天虎 STM32F407 学习笔记_3 尝试寄存器映射方式点亮 LED 灯,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

新建工程

寄存器方式

要命啊,一看名字我就不想试。寄存器新建不得麻烦死。

哎算了为了学习原理,干了。

我们尝试自己写一个寄存器的库函数来引用。

首先我们需要引用 st 官方启动文件 stmf4xx.s,具体用途后面章节再展开讲解。然后我们自己新建一个 stm32f4xx.h 文件来映射寄存器。不过只是把这个文件包含进项目,编译会报错:

.\Objects\led_reg.axf: Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f40xx.o).

进入启动文件后,可以看到这么一个函数:

; Reset handler
Reset_Handler    PROCEXPORT  Reset_Handler             [WEAK]IMPORT  SystemInitIMPORT  __mainLDR     R0, =SystemInitBLX     R0LDR     R0, =__mainBX      R0ENDP

import 的作用相当于 extern,所以没有找到这个函数的定义,需要我们自己去定义。这就是为什么简单引入了启动文件会报错。

而 __main 是当我们定义了 main() 函数后,编译器会自动链接一些c语言库定义好的函数,用于初始化堆栈并且调用我们的 main().

注意,如果想要生成 __main 函数,必须勾选下面这一项。

image-20231102200243099

野火你讲的是真好啊。我之前草草学了学 stm32 单片机用法,比赛的时候自己想移植代码,改了启动文件也不好使,就是报错。原来是这个原因。

那么我们只需要定义这么一个函数,哪怕内容是空都无所谓。

最终我们定义的初步项目框架如下:

1698926683862

stm32f4xx.h:内容为空,有这么个东西就行。

main.c:

#include "stm32f4xx.h"int main(){while(1){}
}void SystemInit(){}

好了,这个程序可以烧录到板子上的。烧录成功之后没有任何反应(因为本来程序也没做什么哈哈),但是这就是一个大进步了。

点灯——51单片机版

51单片机版就是引用 reg51.h 头文件,在其中声明了各个引脚的地址。我们只需要直接给引脚赋值即可。

调用代码:

#include "reg51.h"#ifdef 0 
void main(){PA0=0xFE;while(1){}
}
#endif

接下来我们需要定义 LED 灯的寄存器位置。阅读原理图如下:

1698939342199

大致可以看出,板子上的这个 RGB LED 通过三个引脚来控制 RGB 亮度。输出低电平则导通点亮。

具体输出方式是通过 ODR 进行输出。查找 stm32f4xx 中文参考手册可见:

1698940158585

1698940257044

那么我们就要给 0x4002 1400 +14 的地址赋值,让 1<<6 1<<7 1<<8 的位分别赋值为低电平.

int main(){*(unsigned int *)(0x40021400+14)&=~(1<<6); while(1){}
}

然而这样也不亮。亮就怪了,stm32 寄存器是需要先做初始化配置的。

点灯——stm32 版

首先我们要设置 GPIO 模式。

1698940961543

想点灯 输出高低电平,是 01 通用输出模式。

*(unsigned int *)(0x40021400+0)&=~(3<<(6*2)); 
*(unsigned int *)(0x40021400+0)|=(1<<(6*2)); 

意思是先把 PF6 模式位置为00,然后赋值为01通用输出。

配置完模式之后,还需要配置时钟,stm32 每个外设都需要配置时钟。

前面提到过 GPIO 是在 AHB1.

1699103516061

1699103724261

全部代码如下:

#include "stm32f4xx.h"int main(){//RCC*(unsigned int *)(0x40023800+0x30)|=(1<<5); //Mode*(unsigned int *)(0x40021400+0)&=~(3<<(6*2)); *(unsigned int *)(0x40021400+0)|=(1<<(6*2)); *(unsigned int *)(0x40021400+0x14)&=~(1<<6); while(1){}
}void SystemInit(){}

接下来,我们把这几个地址值提取出来,宏定义映射寄存器。

//stm32f4xx.h
/* 用来存放寄存器映射相关的代码 */
#define RCC_AHB1_ENR    *(unsigned int *)(0x40023800+0x30)
#define GPIOF_MODER     *(unsigned int *)(0x40021400+0)
#define GPIOF_ODR       *(unsigned int *)(0x40021400+0x14)//main.c
#include "stm32f4xx.h"int main(){//RCCRCC_AHB1_ENR|=(1<<5); //ModeGPIOF_MODER&=~(3<<(6*2)); GPIOF_MODER|=(1<<(6*2)); GPIOF_ODR&=~(1<<6); while(1){}
}void SystemInit(){}
点灯——流水灯闪烁

利用软件延时实现 RGB 流水灯闪烁。很简单,前面已经看了3个 LED 通道 PF678 了。

#include "stm32f4xx.h"void delay_ms(int time);int main(){//RCCRCC_AHB1_ENR|=(1<<5); //ModeGPIOF_MODER&=~(3<<(6*2)); GPIOF_MODER|=(1<<(6*2)); GPIOF_MODER&=~(3<<(7*2)); GPIOF_MODER|=(1<<(7*2)); GPIOF_MODER&=~(3<<(8*2));   GPIOF_MODER|=(1<<(8*2)); while(1){GPIOF_ODR|=(7<<6);GPIOF_ODR&=~(1<<6);delay_ms(1000);GPIOF_ODR|=(7<<6);GPIOF_ODR&=~(1<<7); delay_ms(1000);GPIOF_ODR|=(7<<6);GPIOF_ODR&=~(1<<8);delay_ms(1000);}
}void SystemInit(){}//毫秒级的延时
void delay_ms(int time)
{    int i=0;  while(time--){i=4000;while(i--) ;    }
}
点灯——GPIO 具体功能框图对应

GPIO:通用输入输出引脚。我们可以通过编程来输出或者读取数据。大部分 GPIO 是已经连接、定义好了一些功能(比如上面尝试过的 PF6 LED),有的引脚有多个功能支持重新映射。

STM32 GPIO 除了 adc 是 3.3v,其他 GPIO 都是 5v 容忍。

GPIO 框图(重点)如下:

image-20231104221140325

先从输出开始看。最右侧的 IO 引脚是连接在芯片周围一圈的144个引脚之一。除了 IO 引脚,此图中其他所有部分都是封装在芯片内部我们看不到的。

往左有两个保护二极管。当电压大于 5V,电流会往上 VDD_FT 走。当电压为负电压,电流会由 VSS 往 IO 引脚走。

上下拉电阻:比武外接一个低电平工作的设备,但是我们不希望一上电外设就工作,可以设置上拉电阻,稳定一段时间。

GPIO 输出的数据来源:复位寄存器 BSRR,或者 ODR 设置(图中的3下路部分)。复位寄存器高16位复位(写1置0)低16位置位(写1置1),置位优先级更高。

配置 GPIO 模式(输入/输出,选择哪一路)通过前面用过的 MODER 配置。

输出模式(图中输出控制部分)配置端口输出类型寄存器 OTYPER,比如推挽输出,开漏输出。

推挽输出:有直接驱动能力,输出0就是低电平,输出1就输出可以工作的高电平。原理是采用了一个放大的电路?

1699356797818

输入(INT)为高电平时,反向后 PMOS 导通,输出高电平。输入为低电平时,反向后 NMOS 导通,输出低电平。我们可以用一个小电流去驱动出来一个大电流。

开漏输出:自己本身没有输出高电平的手段。低电平可以接地,高电平没有 PMOS 管,是浮空状态。需要外接一个电阻。

1699357078909

stm32 输出 5V 电压的方法就是开漏输出外接电阻。通过接两个三极管的方式反向。

1699357296750

框图中的模拟部分输入输出则不用配置这些模式信息,直接由外设接到保护二极管再接到输出引脚。

框图中的输入部分经过保护电压后,还需要施密特触发器调整一下。比如原来电压的数值并非精确的0或 3.3V,施密特触发器将高于 1.8V 的全部视作1,低于的全部视作0后输入芯片。模拟部分则不需要经过施密特触发器。

因此配置 GPIO 输出的步骤如下:

  1. GPIO 功能,通用输出、复用功能、模拟输入等 MODER;
  2. 输出推挽 or 开漏 OTYPER;
  3. 输出速度 OSPEEDR;
  4. 上下拉电阻是否需要开启 PUPDR;
  5. 具体输出内容 BSRR or ODR.

输入部分后面输入实验介绍~

按整个流程重新串一遍代码,如下:(其实和前面差不多,就是重新按照流程串了一遍)

/* 用来存放寄存器映射相关的代码 */
#define RCC_BASE    (unsigned int *)    0x40023800
#define GPIOF_BASE  (unsigned int *)    0x40021400#define RCC_AHB1ENR         *(RCC_BASE+0x30)#define GPIOF_MODER         *(GPIOF_BASE+0x00)
#define GPIOF_OSPEEDR       *(GPIOF_BASE+0x08)
#define GPIOF_PUPDR         *(GPIOF_BASE+0x0C)
#define GPIOF_ODR           *(GPIOF_BASE+0x14)
#define GPIOF_BSRR          *(GPIOF_BASE+0x18)//main.c
#include "stm32f4xx.h"int main()
{RCC_AHB1ENR |= (1<<5);GPIOF_MODER &= ~(3<<(6*2));GPIOF_MODER |= (1<<(6*2));while (1){}
}void SystemInit()
{
}

烧录前记得勾选:use MicroLib.

这篇关于野火霸天虎 STM32F407 学习笔记_3 尝试寄存器映射方式点亮 LED 灯的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/370343

相关文章

gradle第三方Jar包依赖统一管理方式

《gradle第三方Jar包依赖统一管理方式》:本文主要介绍gradle第三方Jar包依赖统一管理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景实现1.顶层模块build.gradle添加依赖管理插件2.顶层模块build.gradle添加所有管理依赖包

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

Java学习手册之Filter和Listener使用方法

《Java学习手册之Filter和Listener使用方法》:本文主要介绍Java学习手册之Filter和Listener使用方法的相关资料,Filter是一种拦截器,可以在请求到达Servl... 目录一、Filter(过滤器)1. Filter 的工作原理2. Filter 的配置与使用二、Listen

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代