STM32裸机编程的基础知识(1)

发布时间:2024-08-05  

这个系列将介绍 STM32 裸机编程的基础知识,以便更好地理解 STM32Cube、Keil 等框架和 IDE 是如何工作的。本指南完全从头开始,只需要编译器和芯片数据手册,而不依赖任何其它软件工具和框架。


这个系列涵盖了以下话题:


存储和寄存器

中断向量表

启动代码

链接脚本

使用

make

进行自动化构建

GPIO 外设和闪烁 LED

SysTick 定时器

UART 外设和调试输出

printf

重定向到 UART

用 Segger Ozone 进行调试

系统时钟配置

实现一个带设备仪表盘的 web 服务器


我们将使用 Nucleo-F429ZI 开发板 (淘宝购买) 贯穿整个指南的实践,每个章节都有一个相关的完整小项目可以实战。最后一个 web 服务器项目非常完整,可以作为你自己项目的框架,因此这个示例项目也提供了其他开发板的适配:


STM32 Nucleo-F429ZI

TI EK-TM4C1294XL

树莓派 Pico-W

对其他板子的适配支持还在进行中,可以提交 issue 来建议适配你正在用的板子。


工具配置

为继续进行,需要以下工具:


ARM GCC, https://launchpad.net/gcc-arm-embedded - for compiling and linking

GNU make, http://www.gnu.org/software/make/ - for build automation

ST link, https://github.com/stlink-org/stlink - for flashing

Mac 安装

打开终端,执行:


$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

$ brew install gcc-arm-embedded make stlink

Linux (Ubuntu) 安装

打开终端,执行:


$ sudo apt -y install gcc-arm-none-eabi make stlink-tools

Windows 安装

scoop install gcc-arm-none-eabi make stlink

验证安装:


下载这个仓库,解压到

C:

打开命令行,执行:

cd C:are-metal-programming-guide-mainstep-0-minimal

make

数据手册

STM32F429 MCU datasheet

Nucleo-F429ZI board datasheet

微控制器介绍

微控制器(microcontroller,uC 或 MCU)是一个小计算机,典型地包含 CPU、RAM、存储固件代码的 Flash,以及一些引脚。其中一些引脚为 MCU 供电,通常被标记为 VCC 和 GND。其他引脚通过高低电压来与 MCU 通信,最简单的通信方法之一就是把一个 LED 接在引脚上:LED 一端接地,另一端串接一个限流电阻,然后接到 MCU 信号引脚。在固件代码中设置引脚电压的高低就可以使 LED 闪烁:

image.png

存储和寄存器

MCU 的 32 位地址空间按区分割。例如,一些存储区被映射到特定的地址,这里是 MCU 的片内 flash,固件代码指令在这些存储区读和执行。另一些区是 RAM,也被映射到特定的地址,我们可以读或写任意值到 RAM 区。


从 STM32F429 数据手册的 2.3.1 节,我们可以了解到 RAM 区从地址 0x20000000 开始,共有 192KB。从 2.4 节我们可以了解到 flash 被映射到 0x08000000,共 2MB,所以 flash 和 RAM 的位置像这样:

image.png

从数据手册中我们也可以看到还有很多其它存储区,它们的地址在 2.3 节”Memory Map” 给出,例如:”GPIOA” 区从地址 0x40020000 开始,长度为 1KB。


这些存储区被关联到 MCU 芯片内部不同的外设电路上,以特殊的方式控制外设引脚的行为。一个外设存储区是一些 32 位寄存器的集合,每个寄存器有 4 字节的空间,在特定的地址,控制着外设的特定功能。通过向寄存器写入值,或者说向特定的地址写一个 32 位的值,我们就可以控制外设的行为。通过读寄存器的值,我们就可以得到外设的数据或配置。


MCU 通常有许多不同的外设,其中比较简单的就是 GPIO(General Purpose Input Output,通用输入输出),它允许用户将 MCU 引脚设为输出模式,然后置 “高” 或置 “低”;或者设置为输入模式,然后读引脚电压的 “高” 或 “低”。还有 UART 外设,可以使用串行协议通过两个引脚收发数据。还有许多其它外设。


在 MCU 中,一个相同外设通常会有多个 “实例”,比如 GPIOA、GPIOB 等等,它们控制着 MCU 引脚的不同集合。类似地,也有 UART1、UART2 等等,可以实现多通道。在 Nucleo-F429 上,有多个 GPIO 和 UART 外设。


例如,GPIOA 外设起始地址为 0x40020000,我们可以从数据手册 8.4 节找到 GPIO 寄存器的描述,上面说

GPIOA_MODER

寄存器偏移为 0,意味着它的地址是

0x40020000 + 0

,寄存器地址格式如下:

图片

数据手册显示 MODER 这个 32 位寄存器是由 16 个 2 位的值组成。因此,一个 MODER 寄存器控制 16 个物理引脚,0-1 位控制引脚 0,2-3 位控制引脚 1,以此类推。这个 2 位的值编码了引脚模式:’00’代表输入,’01’代表输出,’10’代表替代功能 —— 在其它部分进行描述,’11’代表模拟引脚。因为这个外设命名为’GPIOA’,所以对应引脚名为’A0’、’A1’,等等。对于外设’GPIOB’,引脚则对应叫’B0’、’B1’,等等。


如果我们向 MODER 寄存器写入 32 位的值’0’,就会把从 A0 到 A15 这 16 个引脚设为输入模式:


* (volatile uint32_t *) (0x40020000 + 0) = 0;  // Set A0-A15 to input mode

通过设置独立的位,我们就可以把特定的引脚设为想要的模式。例如,下面的代码将 A3 设为输出模式:


* (volatile uint32_t *) (0x40020000 + 0) &= ~(3 < < 6);  // CLear bit range 6-7

* (volatile uint32_t *) (0x40020000 + 0) |= 1 < < 6;     // Set bit range 6-7 to 1

我来解释下上面的位操作。我们的目标是把控制 GPIOA 外设引脚 3 的位,也就是 6-7,设为特定值,在这里是 1。这个需要 2 步,首先,我们必须将 6-7 位的当前值清除,也就是清’0’,因为这两位可能已经有值;然后,我们再将 6-7 设为期望值。


所以,第一步,我们先把 6-7 位清’0’,怎么做呢?4 步:


使一个数有连续的 N 位’1’

1 位用 1:

0b1

2 位用 3:

0b11

3 位用 7:

0b111

4 位用 15:

0b1111

以此类推,对于 N 位,数值应为

2^N - 1

。对于 2 位,数值为

3

,或者写为二进制

0b00000000000000000000000000000011

将数字左移位。如果我们需要设置位 X-Y,则将数字左移 X 位。在我们的例子中,左移 6 位:

(3 << 6)

,得到

0b00000000000000000000000011000000

取反:0 变 1,1 变 0:

~(3 << 6)

, 得到

0xb11111111111111111111111100111111

现在,将寄存器值与我们的数字进行逻辑” 与” 操作,6-7 位与’0’后会变 0,其它位与’1’后不变,这就是我们想要的:

REG &= ~(3 << 6)

。注意,保持其它位的值不变是重要的,我们并不想改变其它位的配置。

一般地,如果我们想将 X-Y 位清除,或者说设为 0,这样做:


PERIPHERAL- >REGISTER &= ~(NUMBER_WITH_N_BITS < < X);

最后,我们把那些位设为我们想要的值,则需要把想要的值左移 X 位,然后与寄存器当前值进行逻辑” 或” 运算:


PERIPHERAL- >REGISTER |= VALUE < < X;

现在,你应该明白了,下面的两行代码将把 GPIOA MODER 寄存器的 6-7 位设为 1,即输出模式:


* (volatile uint32_t *) (0x40020000 + 0) &= ~(3 < < 6);  // CLear bit range 6-7

* (volatile uint32_t *) (0x40020000 + 0) |= 1 < < 6;     // Set bit range 6-7 to 1

还有一些寄存器没有被映射到 MCU 外设,而是被映射到了 ARM CPU 的配置和控制。例如,有一个”Reset and clock control” 单元(RCC),在数据手册第 6 节有描述,这些寄存器用来配置系统时钟和一些其它的事情。


文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

相关文章

    实际应用场景,例如自动化生产线、电力系统、交通运输等领域。在学习和实践中培养对PLC编程的兴趣和对工业自动化控制的理解,更好地掌握PLC编程的基础知识。   plc编程要学多久   PLC编程的......
    试过程中,需要不断地检查程序的运行情况,并根据实际情况进行调整和修改,直至程序可以正常运行。   实践应用:掌握了数控切割机编程的基础知识和技能后,可以将其应用到实际的生产和制造中。在实践中,可以......
    、软件使用、基本指令系统及编程、定时器及计数器等。需要注意的是,学习PLC编程要有一定的电工基础知识。如果没有这些知识,可以先学一些电工的基础知识。 学习以上内容后,您将能够达到以下目标: 了解......
    基础的8个PLC编程实例解析;PLC是现代工业自动化控制系统中最为常见的控制器之一。它具有高可靠性、强抗干扰能力、易于维护等优点,广泛应用于各种生产设备和机械控制系统中。本文将介绍PLC编程的基础知识......
    PLC可编程逻辑控制器基础知识大全分享;PLC可编程逻辑控制器,是一种采用一类可编程的存储器,用于其内部存储程序,执行逻辑运算、顺序控制、定时、计数与算术操作等面向用户的指令,并通......
    电工必会的基础知识,使用得心应手; 电工必会的基础知识,只有打好了基础,后面的学习才会得心应手 干货★★★★★资料......
    单片机c语言基础知识,c语言必背的100代码;我记得刚开始接触编程的时候,觉得太难了。 也很好奇,写代码的那些人也太厉害了吧?全是英文的,他们的英文水平一定很好吧? 他们......
    与这个确切的版本有关。 不同的ARM版本的命名也可能令人困惑: 编写汇编 我们首先需要了解汇编语言编程的基础知识,这需要在开始之前有一些背景知识。 您不需要知道汇编语言的每一个小细节,但其......
    的评测,评测内容也是我们这么多年总结工作中必备的基础知识。 比如说零基础的学员,那首先要学习c语言和硬件基础。 针对单片机的c语言教程我们也是开源的,大家可以找无际单片机编程......
    就是需要懂一些C++的基础语法,本文讲述的内容比较基础,用到C++很基础的知识。比如:类、对象这些基础内容。没学过C++也没关系,只要懂C语言,在网上现学C++基础知识,难度都不大。3、使用......

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>