STM32野火教程学习笔记

2023-05-16

欢迎使用STM32

虽然经历了疫情期间的价格起飞,但是STM32系列的单片机仍然是各个控制领域内主流的微控制器。它是控制人的必修课之一。

STM32的编程方法

我们在学习51单片机的时候,通常是通过编写程序直接对其输入输出(i/o)口进行操作。而STM32单片机更加高级,它有更复杂的底层。因此我们对STM32单片机的编程方法有两种。一个是类似于51单片机的直接控制片内寄存器和i/o口的寄存器编程,一个是为了降低开发难度给开发者提供了巨大帮助的固件库编程。今天所学习的是更加底层的寄存器编程。尽管在开发的时候,我们用的基本上都是固件库编程,但是学习寄存器编程有利于我们了解STM32的底层,学一门技术只有掌握了牢固的底层才是打了一个好的基础。

前言

本人使用板子是野火STM32103指南者,型号是STM32VET6,兼容野火STM32f103霸道,型号是STM32ZET6。本笔记是在学习了野火官方教程的第11节课之后写下的。如何安装keilMDK,使用烧录器,如何新建工程模板,请观看野火stm32f103教程入门篇的前9节课。十分简单这里不再赘述。

用寄存器编程点亮第一个LED灯的地址定义

单片机的点灯是所有微控制器的入门标志,就好比C语言的Hello World!。这里我们类比一下51单片机的点灯方式。如图
51单片机的点灯操作KEIL5版本
可以看到我们直接对LED连接的P2.0口进行位操作拉低电平使其点亮。为何如此简单,奥妙就在于图中第一行REG52.H当中。这是一个C语言中叫做头文件的东西,它用来存放一些已经定义好的东西,比如在经典的stdio.h中有各种数学运算,REG52.H中就有定义每一个寄存器控制的i/o口也叫外设。REG52.H的部分内容
我们只要对这些定义好的东西操作,就可以通过单片机来控制各种电子器件了。
现在我们把目光转移到stm32上来,想要学会寄存器编程就要自己写头文件。先给头文件起个名,非常好起就叫它stm32f103.h好了。在里面我们开始写定义寄存器,从而方便我们控制STM32的i/o口即GPIO口(外设)的代码,这种方法叫寄存器映射。
//用来存放STM32寄存器映射的代码

// 外设 periphral

#define PERIPH_BASE ((unsigned int)0X40000000)
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0X10000)
#define AHBPERIPH_BASE (PERIPH_BASE + 0X20000)

#define RCC_BASE (AHBPERIPH_BASE + 0X1000)
#define GPIOB_BASE (APB2PERIPH_BASE + 0X0C00)
代码在keil中的如下
既然要控制寄存器,那么我们要知道寄存器的在单片机内的地址,就如同警察叔叔找人要知道他的身份证号码知道他住哪一样。根据STM32官方给出的技术手册,外设的基地址(第一个地址,也叫起始位)是0x40000000,用C语言的unsigned int关键字将0x40000000转化为地址位(16进制数)。其名称根据外设的英语单词periphral定义为PERIPH_BASE。注意到这里我们还只是知道了寄存器的地址位还未对寄存器操作。
现在我们看第二行代码,“#define APB1PERIPH_BASE PERIPH_BASE” 它的意思是定义在APB1上面的外设基地址,就是整个单片机的外设的基地址:0x40000000。至于APB1是什么,野火教程前面的课程有讲解,简单点说就是一块放置外设的地方。STM32的外设就分别放在APB1和APB2上,因此第三行是定义APB2上的外设地址,它的地址在APB1基地址上增加0x10000(16进制数)。第四行定义AHB的基地址,AHB在之前的野火课程中也有讲解,这里简单说一下它是桥接控制单片机外设,时钟和单片机内核的部分,单片机想要工作必须打开它从而打开单片机的时钟。因此第四行代码“#define AHBPERIPH_BASE (PERIPH_BASE + 0X20000)”就是对AHB的地址定义了。
定义完外设地址以后,我们还不能着急去定义寄存器实现寄存器映射。还有两个重要的东西需要准备。第一个东西:时钟。单片机的时钟就好像一个人的心脏,时钟打开,单片机的心脏才开始跳动,所以时钟的地址就是AHB的初始地址上+0x1000。如第五行代码所示。第二个东西:我们要控制的GPIO口。野火stm32单片机的LED灯在B号GPIO上,所以我们要控制GPIOB。GPIO就属于单片机上的外设了,它的基地址就是APB2的基地址上+0x0C00。到这里点灯所有所需的地址位已经准备好了,我们要开始通过这些地址位访问内存来控制寄存器点灯了。

用寄存器编程点亮第一个LED灯的寄存器操作

代码如下
#define RCC_APB2ENR (unsigned int)(RCC_BASE + 0x18)//时钟使能

#define GPIOB_CRL (unsigned int)(GPIOB_BASE + 0x00)

#define GPIOB_CRH (unsigned int)(GPIOB_BASE + 0x04)

#define GPIOB_ODR (unsigned int)(GPIOB_BASE + 0x0C)
寄存器操作代码如下
首先我们要打开时钟,那么就要启动时钟使能位,时钟使能寄存器我们把它的名字叫做RCC_APB2ENR,它的地址是时钟基地址上+0x18。
这里有一个难理解的点,我们为什么要进行一个指针强制类型转换这个操作?原因很简单,目前定义的那些地址例如APB1PERIPH_BASE,它还只是反映地址的立即数。打个比方,警察要找你,知道了你的身份证号。但是这不代表找到你了,只是知道了你的一个代号,要找到你还得去线下你的住址找你。这样的行为就像指针强制类型转换的作用了,把代表寄存器地址的立即数,强制转化位指针,这样它就是一个真正的地址了。真正的地址就可以访问寄存器来控制外设GPIO了。(指针不懂的去补补C语言指针,其实指针就是一个地址)
回到寄存器控制上来,我们配置完时钟后,下一步就是配置控制GPIO(外设)的寄存器。点灯操作我们需要至少控制3个。端口配置低寄存器(CRL)、端口配置高寄存器(CRH)、端口输出数据寄存器(ODR)最后3行就是这些寄存器的位和强制类型转换的操作。

用寄存器编程点亮第一个LED灯

C语言控制微控制器的程序基本上都是在main函数里的,因此我们在main函数里开始写如下代码
#include “stm32f10x.h”

int main(void)
{

	//打开GPIOB的时钟

	RCC_APB2ENR |=((1)<<3);
	//清空控制PB0的端口位
  
	GPIOB_CRL &= ~( (0x0F)<< (4*0));

	// 配置IO口为输出

	GPIOB_CRL |=((1)<<(4*0)

	// 控制ODR寄存器
	
	GPIOB_ODR &= ~(1<<0);		
	//GPIOB_ODR |= (1<<0);关闭


}

void SystemInit(void)
{
//函数体为空,目的是为了骗过编译器不报错

}
主程序代码如下

这里我们看到我们可以类似操作51单片机那样直接对外设地址操作从而打开时钟,拉低GPIO口的电平点亮LED灯了。最后面那个骗过编译器不报错是前面几节课的内容,打上就行,不用太深入了解,以后用不到。
我们发现stm32对寄存器的操作不像51单片机可以直接赋值0或1,而是采用C语言里特殊的|=和&=~,|=是用于置1某一位而不改变其他位(一个寄存器上有好几位,stm32单片机的一个寄存器有32位),比如这里的打开时钟是GPIOB的时钟。根据数据手册GPIOB的时钟在RCC_APB2ENR寄存器的第三位,把他第三位置1就单独打开了GPIOB的时钟,这里我把数据手册的截图给大家看一下。第3位置1就是GPIOB使能
另一个&=~是置0操作,置0寄存器某一位而不改变其他位例如主程序的第二行程序 GPIOB_CRL &= ~( (0x0F)<< (40));它的操作是将端口配置低寄存器初始4位全部置0,而不改变其他位。
看我的置0计算公式
第三行代码是控制GPIO口位为10MHz通用推挽输出模式。将1左移4
0位变成0001(此寄存器每4位控制一个GPIO位的输出模式)。
第四行就是将野火stm32指南者的LED绿灯点亮的操作,将ODR寄存器第1位置0(1左移0位)拉低电平。
主要我们就完成了点灯,入门成功!

野火教程B站链接

https://www.bilibili.com/video/BV1yW411Y7Gw/?p=6&spm_id_from=333.880.my_history.page.click&vd_source=16419a44923b86308e680f95ec76193a

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32野火教程学习笔记 的相关文章

  • STM32在Debug时程序运行时间不对,Debug时定时器中断每次进入断点时间不对,定时器一开启就进入中断的解决办法

    一 Debug时程序时间不对解决办法 1 点击魔术棒 2 点击Debug 点击Settings 3 点击Trace 在Core Clock里修改为你的系统时钟 二 Debug时定时器中断每次进入断点时间不对 在Debug时 程序停下来 定时
  • 如何在 Cortex-M3 (STM32) 上从 RAM 执行函数?

    我正在尝试从 Cortex M3 处理器 STM32 上的 RAM 执行函数 该函数会擦除并重写内部闪存 所以我肯定需要在 RAM 中 但我该怎么做呢 我尝试过的是 使用 memcpy 将函数复制到 RAM 中的字节数组 检查它是否正确对齐
  • 在没有 IDE 的情况下如何使用 CMSIS?

    我正在使用 STM32F103C8T6 并想使用 CMSIS 这本质上只是寄存器定义 没有代码 让我的生活更轻松 同时仍保持在较低水平 问题是我不知道如何安装该库以便在命令行上使用 Makefile 使用 所有文档似乎都与特定于供应商的 I
  • 在 MCU 内部 FLASH 中从一个固件跳转到另一个固件

    我目前正在开发针对 STM32F030C8 的引导加载程序固件应用程序 我在分散文件中指定引导加载程序应用程序将占用主内存位置 0x08000000 到 0x08002FFF 扇区 0 到扇区 2 我还编写了一个主固件应用程序 存储在0x0
  • STM32 F072上的软件如何跳转到bootloader(DFU模式)?

    STM32应用笔记2606对此进行了讨论 但没有简单的代码示例 该答案已使用 IAR EWARM 在 STM32F072 Nucleo 板上进行了测试 这个答案使用 STM32标准外设库 仅此而已 请注意 验证您是否成功进入引导加载程序模式
  • STM32 GPIO工作原理详解

    STM32 GPIO介绍 1 STM32引脚说明 GPIO是通用输入 输出端口的简称 是STM32可控制的引脚 GPIO的引脚与外部硬件设备连接 可实现与外部通讯 控制外部硬件或者采集外部硬件数据的功能 以STM32F103ZET6芯片为例
  • HAL 锁定和解锁函数如何使用以及为什么?

    我试图理解另一位程序员编写的代码 它使用了I C http en wikipedia org wiki I C2 B2C通信以将数据写入 STM32 微控制器的 EEPROM 一般来说 我理解他的代码是如何工作的 但我不明白他为什么使用HA
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 擦除后无法写入闪存

    所以我不能在擦除后直接写入内部闪存 如果写操作之前没有擦除操作 那么我可以 有什么想法吗 编程函数返回 成功写入 值 但查看内存时 没有写入任何数据 这是代码 uint32 t pageAddress 0x08008000 uint16 t
  • 1.69寸SPI接口240*280TFT液晶显示模块使用中碰到的问题

    1 69寸SPI接口240 280TFT液晶显示模块使用中碰到的问题说明并记录一下 在网上买了1 69寸液晶显示模块 使用spi接口 分辨率240 280 给的参考程序是GPIO模拟的SPI接口 打算先移植到FreeRtos测试 再慢慢使用
  • Arm:objcopy 如何知道 elf 中的哪些部分要包含在二进制或 ihex 中?

    我正在开发一个项目 其中涉及解析arm elf 文件并从中提取部分 显然 elf 文件中有很多部分没有加载到闪存中 但我想知道 objcopy 到底如何知道要在二进制文件中包含哪些部分以直接闪存到闪存中 以arm elf文件的以下reade
  • 库函数点亮Led

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 pandas是什么 二 使用步骤 1 引入库 2 读入数据 总结 前言 提示 这里可以添加本文要记录的大概内容 例如 随着人工智能的不断发展 机器学习这门
  • 核心耦合内存在 STM32F4xx 上可执行吗?

    尝试从 STM32F429s CCM 运行代码 但每当我命中 CCM 中的第一条指令时 我总是会遇到硬故障 并且 IBUSERR 标志被设置 该指令有效且一致 STM32F4xx 是否可能不允许从 CCM 执行 数据访问效果良好 alios
  • 从没有中断引脚并且在测量准备好之前需要一些时间的传感器读取数据的最佳方法

    我正在尝试将压力传感器 MS5803 14BA 与我的板 NUCLEO STM32L073RZ 连接 根据 第 3 页 压力传感器需要几毫秒才能准备好读取测量值 对于我的项目 我对需要大约 10 毫秒来转换原始数据的最高分辨率感兴趣 不幸的
  • 通过JTAG恢复STM32 MCU磨掉的标记

    我有一块可能带有 STM32 MCU 的板 我想为该板制作定制固件 因为库存板有很多问题 不幸的是 电路板制造商很友善地磨掉了所有标记 有没有办法通过 jtag 获取设备 系列 ID 并将其交叉引用到型号 我能找到的一切都是关于获取芯片的唯
  • 嵌入式 C++11 代码 — 我需要 volatile 吗?

    采用 Cortex M3 MCU STM32F1 的嵌入式设备 它具有嵌入式闪存 64K MCU固件可以在运行时重新编程闪存扇区 这是由闪存控制器 FMC 寄存器完成的 所以它不像a b那么简单 FMC 获取缓冲区指针并将数据刻录到某个闪存
  • STM32F0、ST-link v2、OpenOCD 0.9.0:打开失败

    我在用着发射台 http www ti com ww en launchpad about htmlgcc arm none eabi 4 9 2015q2 为 STM32F0 进行编译 现在我想使用该集合中的 arm none eabi
  • 使用 STM32 USB 设备库将闪存作为大容量存储设备

    我的板上有这个闪存IC 它连接到我的STM32F04 ARM处理器 处理器的USB端口可供用户使用 我希望我的闪存在通过 USB 连接到 PC 时被检测为存储设备 作为第一步 我在程序中将 USB 类定义为 MSC 效果很好 因为当我将主板
  • 使用 STM32F0 ADC 单独读取不同的输入

    STM32F072CBU 微控制器 我有多个 ADC 输入 并且希望单独读取它们 STMcubeMX 生成样板代码 假设我希望按顺序读取所有输入 但我无法弄清楚如何纠正这个问题 这篇博文 http blog koepi info 2015
  • 当端点和 PMA 地址均更改时,CubeMX 生成的 USB HID 设备发送错误数据

    我正在调试我正在创建的复合设备的问题 并在新生成的仅 CubeMX 代码中重新创建了该问题 以使其更容易解决 我添加了少量代码main 让我发送 USB HID 鼠标点击 并在按下蓝色按钮时使 LED 闪烁 uint8 t click re

随机推荐