STM32F103C8T6开发板入门学习——寄存器和库函数介绍

学习目标:STM32F103C8T6开发板入门学习——寄存器和库函数介绍


学习内容:

1. 寄存器介绍

1.1 存储器映射

存储器本身无固有地址,是具有特定功能的内存单元。它的地址是由芯片厂商或用户分配,给存储器分配地址的过程就叫做存储区映射。给内存单元分配地址之后,就可以通过指针去操作内存地址。

1.2 存储器映射表

STM32作为32位单片机,地址范围为2³²(即4GB)。为降低相同应用场景下的软件复杂度,其存储映射按Cortex-M4处理器规则预先定义:

  • 部分地址空间由Arm Cortex-M4的系统外设占用,且不可更改;
  • 其余地址空间可由芯片供应商定义使用。
1.3 什么是寄存器

寄存器是具有特定功能的内存单元,通过操作这些单元可驱动外设工作。按功能可分为:

  • 指令寄存器、地址寄存器、数据寄存器;
  • 处理器可通过相互独立的总线读取指令、加载/存储数据。
1.4 寄存器映射

程序存储器、数据存储器、寄存器和I/O端口位于同一4GB线性地址空间内:

  • 每个寄存器对应不同功能,操作相应寄存器可配置对应功能;
  • 控制外设时,可通过起始地址以C语言指针方式访问内存单元;
  • 给已分配地址、有特定功能的内存单元取别名的过程,称为寄存器映射,该别名即寄存器。
1.5 寄存器重映射

给寄存器再次分配地址的过程称为寄存器重映射。

1.6 总线基地址

片上外设区域分为三条总线,按外设速度不同挂载相应外设:

  • AHB总线:最高时钟可达72MHz;
  • APB1总线:最高时钟可达36MHz;
  • APB2总线:最高时钟可达72MHz。

根据外设速度的不同,不同的总线挂载着不同的外设。总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。
请添加图片描述

1.7 外设基地址

每条总线上挂载多个外设,每个外设均有自己的地址范围。

请添加图片描述

1.8 外设寄存器地址

在外设的地址范围内,分布着该外设的多个寄存器。以GPIO外设为例:

  • GPIO外设地址范围内有多个寄存器,每个均有特定功能,通过操作对应寄存器可配置GPIO功能;
  • 每个寄存器为32位,占4个字节。

请添加图片描述

1.9 如何操作寄存器

以“让GPIOA端口的16个引脚都置1”为例,操作步骤如下:

  1. 确定目标寄存器:需配置端口输出寄存器GPIOx_ODR;
  2. 计算寄存器地址
    • 由中文参考手册第115页可知,GPIOx_ODR寄存器的地址偏移量为0x0C;
    • GPIOA端口的基地址为0x40010800;
    • 因此,GPIOx_ODR寄存器的地址为:0x40010800 + 0x0C = 0x4001080C。
      请添加图片描述

通过上图可以了解到要想使能所有引脚配置为1,只需要将ODRy(y=0…15)对应的位置1即可。也就是配置GPIOA_ODR寄存器的高16位为0,低16位为1,换成十六进制就是0x0000FFFF。

1.通过绝对地址访问内存单元
// GPIOA 端口的16个引脚全部输出高电平
*(unsigned int*)(0x4001080C) = 0xFFFF;

说明

  • (unsigned int*):将立即数0x4001080C强制转换为无符号整型指针,告诉编译器这是一个内存地址;
  • *:对该地址进行解引用,访问地址对应的内存空间;
  • 整体含义:向0x4001080C(GPIOA_ODR寄存器)对应的内存空间写入0xFFFF,实现GPIOA所有16个引脚输出高电平(低16位置1,高16位为0)。
2. 通过别名访问内存单元

为简化操作并增强可读性,可给寄存器地址定义别名:

方式一:定义地址指针

// 定义 GPIOA_ODR 地址指针
#define GPIOA_ODR (unsigned int*)(0x4001080C)// GPIOA 端口的16个引脚全部输出高电平
*GPIOA_ODR = 0xFFFF;

方式二:直接定义解引用的地址(更常用)

// 定义 GPIOA_ODR 并直接解引用
#define GPIOA_ODR (*(unsigned int*)(0x4001080C))// GPIOA 端口的16个引脚全部输出高电平
GPIOA_ODR = 0xFFFF;
  • 优势:通过GPIOA_ODR这样的别名,可直观理解操作的是GPIOA的输出数据寄存器,无需记忆复杂的十六进制地址,提升代码可读性和可维护性。

2. 库函数介绍

2.1 为什么要使用库函数

从上一节可知,通过寄存器驱动外设虽可行,但STM32寄存器数量极多:

  • 仅定义寄存器就需耗费大量时间,还需逐一查找其功能、地址并配置对应值,在开发难度和时间成本上均不划算。

库函数的出现正是为解决这一问题,其优势在于:

  • 由STM32官方开发,可直接移植到工程中使用,大幅提升开发效率;
  • 无需深入了解硬件底层机制,只需根据需求查找对应函数并调用,降低了开发门槛。
2.2 库函数简单介绍

在创建的工程模板中,包含STM32F1x的标准外设库,可通过打开stm32f10x_gpio.hstm32f10x_gpio.c等文件查看具体实现。

本质上,库函数是在寄存器操作的基础上进行了封装:

  • 底层仍通过操作寄存器实现功能,但上层提供了更简洁的函数接口,使开发操作更简单。

3. 寄存器和库函数的区别

寄存器和库函数最终均通过对地址操作实现功能,但二者存在明显差异,适用场景也不同:

  • 原理与直观性:寄存器操作更易理解底层原理,过程直观;库函数则屏蔽了底层细节,直接面向应用功能。
  • 代码量与冗余:库函数代码量相对更大,因函数需考虑多种使用场景,可能存在代码冗余;寄存器操作代码更精简。
  • 开发难度与效率:库函数使用简单、易上手,可快速开发应用,能显著提高开发效率;寄存器操作需熟悉硬件细节,开发门槛更高。
  • 资源占用与速度:寄存器操作占用内存少、执行速度快,在资源有限(如内存紧张)或对执行速度有高要求的场景下更具优势。

学习时间:8月25日

1. 寄存器介绍

1.1 存储器映射

原文核心:存储器本身无固有地址,分配地址的过程称为存储区映射,映射后可通过指针操作内存单元。

个人理解:存储器就像一堆无编号的储物箱,“存储器映射”就是给每个箱子贴上限号(地址)。有了编号后,CPU才能通过“地址指针”准确找到并操作对应的存储单元。这是硬件与软件交互的基础——没有地址映射,软件就无法“定位”硬件资源。

1.2 存储器映射表

原文核心:STM32作为32位单片机,地址范围为4GB,部分地址由Cortex-M4内核占用(不可改),其余由芯片厂商定义。

个人理解:4GB地址空间是32位处理器的“理论上限”,就像一个巨大的“地址版图”。其中,Cortex-M4内核预留了部分区域(如系统外设),相当于“国家划定的保护区”,不可占用;剩下的区域由STM32厂商分配给片上外设(如GPIO、UART等),形成了固定的“地址规划”。这种标准化的映射表让不同开发者能基于同一套地址规则操作硬件,降低了兼容性成本。

1.3 什么是寄存器

原文核心:寄存器是具有特定功能的内存单元,按功能分为指令、地址、数据寄存器,处理器通过独立总线访问。

个人理解:寄存器是硬件的“控制面板”。比如,指令寄存器存放当前要执行的指令,地址寄存器存放要访问的内存地址,数据寄存器临时存储运算数据。独立总线设计(如指令总线、数据总线)就像“专用通道”,避免了指令读取和数据传输的冲突,提高了CPU效率。简单说,寄存器是CPU与外设“对话”的直接窗口——操作寄存器就是在“告诉”硬件该做什么。

1.4 寄存器映射

原文核心:给已分配地址、有特定功能的内存单元取别名的过程,称为寄存器映射,别名即寄存器。

个人理解:假设某块内存单元的地址是0x4001080C,其功能是控制GPIOA的输出,我们给它起个名字“GPIOA_ODR”,这个过程就是寄存器映射。它的本质是“地址→功能别名”的映射,让开发者不用死记硬背冗长的十六进制地址,而是通过“GPIOA_ODR”这样直观的名字操作硬件,大幅降低了代码的理解难度。

1.6 总线基地址

原文核心:片上外设分为AHB(72MHz)、APB1(36MHz)、APB2(72MHz)三条总线,总线基地址是总线上首个外设的地址。

个人理解:总线就像“高速公路”,外设是“车辆”。AHB和APB2是“快车道”(72MHz),适合高速外设(如GPIO、SPI);APB1是“慢车道”(36MHz),适合低速外设(如I2C、UART)。总线基地址是“高速路的起点”,比如AHB总线基地址是0x40010000,总线上的所有外设地址都从这个起点开始“编号”,方便按总线归类管理外设。

1.7 外设基地址

原文核心:每条总线上的外设都有自己的地址范围。

个人理解:如果总线是“高速公路”,外设就是路上的“服务区”,每个服务区有自己的“管辖范围”(地址范围)。比如,GPIOA挂在APB2总线上,其基地址是0x40010800,地址范围从0x40010800到下一个外设的基地址前,这样CPU就能通过地址范围准确区分不同外设(如GPIOA和GPIOB)。

1.8 外设寄存器地址

原文核心:外设地址范围内有多个寄存器,每个32位(4字节),对应特定功能(以GPIO为例)。

个人理解:一个外设(如GPIOA)就像一个“工具箱”,里面的每个“工具”(寄存器)对应不同功能:比如“GPIOA_CRL”控制低8位引脚模式,“GPIOA_ODR”控制输出电平。每个寄存器占4字节(32位),是因为STM32是32位处理器,一次可处理32位数据,这种设计能高效读写寄存器。

1.9 如何操作寄存器(以GPIOA引脚置1为例)

原文核心:步骤为“确定目标寄存器→计算地址→写入值”,并给出了具体示例。

个人理解:操作寄存器的本质是“地址+值”的精准控制。比如要让GPIOA所有引脚输出高电平,需找到“输出数据寄存器(ODR)”,计算其地址(GPIOA基地址+偏移量0x0C=0x4001080C),再写入0xFFFF(低16位置1)。这个过程像“按地址找到开关,再拨动开关”,需要对硬件地址和寄存器功能有清晰了解。

1. 通过绝对地址访问内存单元

原文示例:*(unsigned int*)(0x4001080C) = 0xFFFF;

个人理解:这种方式直接通过地址操作,优点是“直达底层”,缺点是可读性差——如果不查手册,没人知道0x4001080C是什么。适合对硬件极度熟悉的场景,或调试时快速验证功能。

2. 通过别名访问内存单元

原文给出两种方式:定义地址指针(#define GPIOA_ODR (unsigned int*)(0x4001080C))或直接定义解引用地址(#define GPIOA_ODR (*(unsigned int*)(0x4001080C)))。

个人理解:这是实际开发中更推荐的方式。通过“GPIOA_ODR”这样的别名,代码可读性大幅提升,且修改时只需改宏定义,无需逐个修改地址。就像用“家门钥匙”代替“钥匙的物理编号”,更符合人类的记忆习惯。

2. 库函数介绍

2.1 为什么要使用库函数

原文核心:寄存器数量多,手动定义和配置耗时,库函数由官方开发,可直接移植,降低门槛、提高效率。

个人理解:STM32的外设和寄存器极其庞大(仅GPIO就有近10个寄存器,全芯片有上百个外设),如果每个寄存器都手动定义地址、查手册配置值,开发效率会极低。库函数就像“封装好的快捷指令”,比如要配置GPIO输出,直接调用GPIO_Init()即可,无需关心底层寄存器的地址和具体值,适合快速开发。

2.2 库函数简单介绍

原文核心:库函数基于寄存器操作封装,在stm32f10x_gpio.h等文件中实现,提供简洁接口。

个人理解:库函数不是“魔法”,其底层依然是操作寄存器。比如GPIO_SetBits(GPIOA, GPIO_Pin_All)函数,内部其实是对GPIOA_ODR寄存器写入0xFFFF。它的价值在于“抽象”——把复杂的寄存器配置逻辑(如时钟使能、模式设置、输出控制)打包成函数,让开发者只需关注“要做什么”,而非“怎么做”。

3. 寄存器和库函数的区别

原文从原理、代码量、开发效率、资源占用等方面对比了两者。

个人感悟:

  • 寄存器是“手动挡”:直接操作硬件,代码精简、执行快,但需要深入了解硬件细节,开发门槛高,适合底层驱动开发或资源受限场景(如内存极小的设备)。
  • 库函数是“自动挡”:屏蔽底层细节,开发效率高、易上手,但代码量较大(可能有冗余),执行速度略慢,适合应用层开发或快速原型验证。

实际开发中,两者并非对立:简单功能可用库函数快速实现,对性能要求高的部分(如中断处理)可结合寄存器操作优化。初学者建议先通过库函数入门,熟悉后再深入寄存器,理解底层原理。


学习产出:

  • 了解并学习STMSTM32F103C8T6开发板的寄存器和库函数介绍
  • 技术文章的1篇

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/95401.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/95401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CouponHub项目开发】使用RocketMQ5.x实现延时修改优惠券状态,并通过使用模板方法模式重构消息队列发送功能

在上个章节中我实现了创建优惠券模板的功能,但是,优惠券总会有过期时间,我们怎么去解决到期自动修改优惠券状态这样一个功能呢?我们可以使用RocketMQ5.x新出的任意定时发送消息功能来解决。 初始方案:首先在创建优惠券…

Claude Code SDK 配置Gitlab MCP服务

一、MCP配置前期准备 (一)创建个人令牌/群组令牌 我这里是创建个人令牌,去到首页左上角,点击头像——>偏好设置——>访问令牌——>添加新令牌 (二)配置mcp信息 去到魔塔社区,点击mc…

Eclipse 常用搜索功能汇总

Eclipse 常用搜索功能汇总 Eclipse 提供了多种搜索功能,帮助开发者快速定位代码、文件、类、方法、API 等资源。以下是详细的使用方法和技巧。 一、常用搜索快捷键快捷键功能描述Ctrl H打开全局搜索对话框,支持文件、Java 代码、任务等多种搜索。Ctrl …

关于Spring的一些理解

Spring整体结构:Spring实际运行场景:基础 Spring启动过程 传统Spring: (1)初始化准备阶段 (2)容器创建与注入 (3)Bean工厂后置处理 (4)Bean工厂后…

Windows右下角系统托盘图标快速显示或隐藏

系统托盘指的是Windows电脑桌面右下角的区域,包括时间、wifi(网络)、音量、电源、输入法、一些程序/应用等。启动了应用后,Windows会把部分应用的图标显示或隐藏在系统托盘区。我们可以根据需要快速显示或隐藏相关应用&#xff0c…

Kotlin编程学习记录2

Kotlin编程学习记录2——条件与循环 条件语句:if 与 when ​ Kotlin 的控制流把“表达式优先”作为设计原则——if、when 不只是控制语句,都可以作为表达式使用并返回值,这影响了日常代码风格(更函数式、可组合)。笔…

印度物联网崛起:政策红利下的千亿蓝海与本土化博弈

印度物联网市场正处于快速发展阶段,2025年市场规模预计达到200亿美元,主要得益于政府"数字印度"计划和智能城市项目的推动。蜂窝物联网连接数在2024年同比增长34%,2025年Q1增速进一步提升至32%,其中智能电表部署和资产追踪应用成为核心驱动力。然而,市场也面临着…

html学习:

ok,今天准备学一下html&#xff0c;花费点时间整理一下&#xff1a; HTML标签的语法格式 HTML 中的标签就像关键字一样&#xff0c;每个标签都有自己的语义&#xff08;含义&#xff09;。 HTML 标签的语法格式 属性↓ <div class"begin">cyx</div>↑…

CRYPT32!PkiAsn1Decode函数分析的一个例子

第一部分&#xff1a; 0: kd> g Breakpoint 35 hit CRYPT32!PkiAsn1Decode: 001b:75c9af0c 55 push ebp 1: kd> kc# 00 CRYPT32!PkiAsn1Decode 01 CRYPT32!PkiAsn1DecodeAndAllocInfo 02 CRYPT32!PkiAsn1DecodeAndAllocInfoEx 03 CRYPT32!Asn1InfoDecode…

大模型蒸馏、大模型微调、RAG

前言&#xff1a; 有很多同学想学习大模型开发&#xff0c;又无从下手&#xff0c;网上一搜&#xff0c;铺天盖地的付费课程。又不想当韭菜&#xff0c;打破认知障碍&#xff0c;通过自学&#xff0c;改变自己&#xff0c;改变世界&#xff01; 一、大模型蒸馏、微调、RAG的适…

25高教社杯数模国赛【E题保姆级思路+问题分析】

注&#xff1a;本内容由”数模加油站“ 原创出品&#xff0c;虽无偿分享&#xff0c;但创作不易。 欢迎参考teach&#xff0c;但请勿抄袭、盗卖或商用。 E 题 AI辅助智能体测 《国家学生体质健康标准》的颁布&#xff0c;有效地促进了大中小学生关注自身体质健康的发展&#xf…

基于单片机车内换气温度检测空气质量检测系统Proteus仿真(含全部资料)

全套资料包含&#xff1a;Proteus仿真源文件keil C语言源程序AD原理图流程图元器件清单说明书等 资料下载&#xff1a; 通过网盘分享的文件&#xff1a;资料分享 链接: 百度网盘 请输入提取码 提取码: tgnu 目录 资料下载&#xff1a; Proteus仿真功能 项目文件资料&#…

MySQL子查询的分类讲解与实战

精选专栏链接 &#x1f517; MySQL技术笔记专栏Redis技术笔记专栏大模型搭建专栏Python学习笔记专栏深度学习算法专栏 欢迎订阅&#xff0c;点赞&#xff0b;关注&#xff0c;每日精进1%&#xff0c;与百万开发者共攀技术珠峰 更多内容持续更新中&#xff01;希望能给大家带来…

Leetcode 206. 反转链表 迭代/递归

原题链接&#xff1a;Leetcode 206. 反转链表 解法一&#xff1a;迭代 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* …

C++11新特性 | 欢迎来到现代C++的世界!

左值与右值 左值与右值的概念 可以被取地址的值为左值&#xff08;left value&#xff0c;简称lvalue&#xff09;&#xff0c;否则为右值&#xff08;right value&#xff0c;简称rvalue&#xff09;。 常见的左值、右值例子&#xff1a; // >>>>>>> 左值…

因为对象装箱拆箱导致的空指针异常

今天业务突然提了个事件单&#xff0c;客户添加承租人一直报错&#xff0c;但是很奇怪&#xff0c;报错信息是空的二话不说&#xff0c;先跟API组要了接口和参数&#xff0c;然后看日志然鹅&#xff0c;这个接口并没有打印日志&#xff0c;只有一个e.printStackTrace()然后静下…

Rust 在 Windows 环境下交叉编译其他操作系统可执行文件的详细指南

前言&#xff1a;为什么需要交叉编译&#xff1f; 想象一下这样的场景&#xff1a;你在 Windows 电脑上开发了一个 Rust 程序&#xff0c;希望它能在 Linux 服务器上运行&#xff0c;或者在朋友的 macOS 电脑上测试 —— 总不能为了编译不同系统的版本&#xff0c;专门买一台 …

Ubuntu系统配置镜像源

要修改的文件 /etc/apt/sources.list操作步骤 1. 备份原文件 sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak2. 编辑文件 sudo nano /etc/apt/sources.list3. 清空文件内容&#xff0c;填入以下内容 先获取Ubuntu版本代号&#xff1a; lsb_release -cs然后根据版本填…

【Spring Cloud Alibaba】Sentinel(一)

【Spring Cloud Alibaba】Sentinel&#xff08;一&#xff09;1. 简介2. 服务雪崩1. 简介 Sentinel 是阿里巴巴开源的分布式系统的流量防护组件&#xff0c;主要用来做流量控制、熔断降级、系统保护。它的定位类似于 Hystrix&#xff0c;但更侧重在 流量治理 上。目前在微服务…