使用嵌入式 rust 设置寄存器

     2023-02-19     7

关键词:

【中文标题】使用嵌入式 rust 设置寄存器【英文标题】:Setting registers using embedded rust 【发布时间】:2022-01-15 09:37:21 【问题描述】:

所以...我一直在关注embedded rust book...,我目前正在阅读有关寄存器的信息。现在,这本书确实建议我使用STM32F303VC discovery 来避免问题,但我找不到,因此我得到了Nucleo F303RE instead。货物的目标和材料保持不变。所以我认为不会有任何问题。

所以,我使用的 MCU 将 LED 连接到端口 A (0x48000000),其 BSRR 偏移量为 0x18。现在,我在datasheet 中读到,端口 A 的默认值为 0xa8000000,我不明白为什么。但是当我尝试使用 ptr::write_volatile(PORTA_BSRR as *mut u32, 1 << 5); 没有任何反应。甚至我的 gdb 终端也没有反映任何变化。因此,我尝试按照原始教程的建议(0x48001018)检查portE。但即便如此,寄存器值也不会改变。我无法调试此问题。

现在,我可以运行之前的教程,并且可以检查变量和内容。我的 stm 似乎没有任何问题,因为我可以使用 stmc32cubeide 很好地控制它。

这里是代码以供您参考

编辑:所以,我阅读了@Ikolbly 的评论,并查看了 RCC_AHBENR 寄存器,我猜这就像在 arduino 中设置 pinMode(pin, HIGH) 一样,它会打开端口。

我已修改代码以设置该位,但似乎没有任何变化。我猜辅助代码已经为 portE 做了这件事,这就是为什么我不必为此做任何初始化......但即使更改 portE 的寄存器值也不起作用。

//#![deny(unsafe_code)]
#![no_main]
#![no_std]

use aux5::entry;
use core::ptr;

#[entry]
fn main() -> ! 


    const RCC_AHBENR: u32 = 0x48000014;

    const PORTA_BSRR: u32 = 0x48000018;
    let _y;
    let x = 42;
    _y = x;

    unsafe 
        // EDIT enabling portA
        ptr::write_volatile(RCC_AHBENR as *mut u32, 1 << 17);


        // Toggling pin PA5
        ptr::write_volatile(PORTA_BSRR as *mut u32, 1 << 5);

        // Toggling random shit to see if it works
        ptr::write_volatile(PORTA_BSRR as *mut u32, 1 << 6);
        ptr::write_volatile(PORTA_BSRR as *mut u32, 1 << 7);
        ptr::write_volatile(PORTA_BSRR as *mut u32, 1 << 8);
    
    // infinite loop; just so we don't leave this stack frame
    loop 

【问题讨论】:

是否需要设置 RCC_AHBENR 中的 IOPAEN 位来启用 GPIO bank? RCC 的基地址为 0x4002_1000,因此 RCC_AHBENR 将位于地址 0x4002_1014。对于 GPIOE,如果您调用 init:github.com/rust-embedded/discovery/blob/master/f3discovery/src/… 看起来辅助代码是这样做的 非常感谢...我仍然无法打开 LED,但我可以操作寄存器并使用 gdb 检查它们...我想我必须阅读数据表更多...另外...如果我在生锈之前使用嵌入式 C 进行探索会有所帮助吗?还是几乎一样? 是的,必须启用外围设备已经让我好几次了。由于我是谁,我对 Rust 有偏见,我认为使用嵌入式 C 而不是 Rust 没有任何真正的好处。你只会遇到同样的问题,除了 C 带来的问题。 最低限度是您需要在 rcc 中启用 gpio,上面的讨论是关于 IOPAEN(I/O 端口 A 启用)。然后,您需要设置匹配的调制解调器寄存器位,以使 gpio 引脚成为输出。 otypr 寄存器,如果你使用的单片机有,你不需要触摸,也不需要速度寄存器。您只需要将输出模式设置为输出,推拉即可。 【参考方案1】:

超过 99% 的裸机正在阅读。

所以您从核数据表中发现 D13 是 LD2,而 F303 变体上是 PA5。端口 A 引脚 5。

在 STM3F303R 的参考手册中...

基地址

RCC 0x40021000
GPIOA 0x48000000

从 cmets 中显示的 RCC 基础错误。

RCC_AHBENR 位于 rcc 基数 + 0x14。第 17 位是 IOPAEN(I/O 端口 A(时钟)启用)复位值 0x00000014 flash 和 sram 在复位时启用,这是一个非常好的启用。

现在您需要为这些 ST 端口闪烁 LED 做的最低要求是启用 gpio 端口,在这种情况下需要设置 RCC_AHBENR 的第 17 位。然后设置 GPIOA_MODER 寄存器以将端口设置为输出,您不需要弄乱上拉/下拉或速度,并且输出类型寄存器已经设置为复位时输出。所以启用端口,并使其成为推挽输出。然后使用 bsrr 闪烁。

GPIOA_MODER 重置为 0xA8000000 使 15,14,13 在重置后交替功能,您会发现这些是 jtag 寄存器,其中两个也是 SWD 数据和 I/O,您可以使用 SWD 调试器(内置在 nucleo木板)。将二进制文件复制到虚拟拇指驱动器比直接使用 swd 更容易(调试 mcu 然后使用 swd 写入目标 mcu 并重置它)。

如上所述,RCC_AHBENR 已启用 sram 和 flash。

作为一般规则(有例外),您希望执行读取-修改-写入。只需将 1

x = read RCC_AHBENR
x|= 1<<17
write RCC_AHBENR = x

这可以用一个或相等来完成,作为我建议反对的习惯,它,但是对于这个寄存器的手部优化,它很好。我还不是锈病专家(有一天),所以不知道如何做到这一点。我认为您应该先尝试 asm 或 C 并成功,然后将这些知识生锈。

对于MODER寄存器来说,读-修改-写的性质变得更加明显,因为它不止一位。

x = read GPIOA_MODER
x&=(~(3<<10))
x|=1<<10
write GPIOA_MODER = x

这将是一种适当的通用方法,现在查看文档并查看其余值,从技术上讲,只需在一次写入中写入 0xA8000400,或者您可以执行读取-修改-写入

x = read
x |= 1<<10
write = x

不清除第 11 位。

现在一些 stm32 部分记录了这一点,有些没有,大多数人很幸运,因为使用罐头工具或读取-修改-写入习惯,特别是现代寄存器的逻辑对它有一种陌生感(可能让他们的代码工作)。

如果您要预先准备寄存器并进行背靠背写入

ldr r0,=0x40021014
ldr r1,=0x00020014
ldr r2,=0x48000000
ldr r3,=0xA8000400
str r1,[r0]
str r3,[r2]

假设我的位和地址正确(这不是评论/问题的重点)背靠背写入不适用于所有 stm32 部件,您有一个竞争条件写入启用 i/o 端口在与 i/o 端口通话之前需要延迟。现在即使在那些芯片上,这个

ldr r0,=0x40021014
ldr r1,=0x00020014
ldr r2,=0x48000000
str r1,[r0]
ldr r3,[r2]
modify
write

确实有效,我敢肯定不是运气不好(它具有相同的竞争条件)。

当然,如果您将时钟从重置速度调整为更快的值,情况会变得更糟。

如果您使用高级语言进行背靠背写入,则无法保证它会生成上面可能生成的内容

ldr r0,=0x40021014
ldr r1,=0x00020014
ldr r2,=0x48000000
ldr r3,=0xA8000400
str r1,[r0]
one or both of the r2/r3 inserted here
str r3,[r2]

至少在我可以打破的 stm32 部件的重置时钟速度下,这是可行的。但是我已经看到一个编译器具有一组命令行选项,这些选项生成的代码会损坏,而其他选项或其他选项没有在它们之间插入指令,只是由于代码生成的性质(在这种情况下制作相同的 C 代码工作和不工作都基于愚蠢的运气)。

所以我建议你不要背靠背写,和/或你检查编译器的输出(你应该在任何新项目上做这件事,尤其是像你的第一个裸机 rust 程序一样)目前生成这样的裸机代码)。 (相当可行,但说 C 的人的数量仍然是百万分之一,但对于生锈的人来说,在地球的总人口中只有一小部分)。

您应该检查向量表、二进制文件中的位置、加载和存储以及地址和数据等内容。同样,当从任何二进制格式转换为复制到核板所需的原始二进制格式时,检查该二进制文件也可以看到它以向量表开头并且所需的任何填充都已到位。

所以....

修复你的rcc寄存器地址,我也会修复数据。

在 rcc 寄存器写入后添加读取或延迟,如果需要,可以对 gpio 模式进行简单的一次性读取,或者更好地执行读取-修改-写入。

将位 11:10 的模式写入 0b01 以使其成为通用输出

那么你可以在 BSRR 中弄乱第 5 位和第 16+5 位。

最后,从 nucleo 文档中,您甚至不必查看原理图。他们在 LD2 下很好地记录了这一点

the I/O is HIGH value, the LED is on

所以你想设置 PA5 来打开 LED 并重置来关闭它。

将 1

最后的无限循环只需要引导程序或主代码在返回时弄乱了东西,如果你编写了自己的引导程序,那么你可以简单地让它在从主返回时执行一个无限循环(这就是我更喜欢它,有时是 wfi/wfe 循环,一些库虽然会在返回时软管东西,因此这种习惯)。由于您应该非常了解任何裸机项目(供应商沙箱或您自己的)的启动过程和代码,因此您应该在编写 main 之前知道答案并知道要求是什么。也许你在这种情况下会这样做,只是没有在这里展示。

【讨论】:

c还是rust:选择哪个用于硬件抽象编程

...进行交互,而这些交互几乎总是通过硬件提供的内存映射寄存器来完成的。通常,你通过对某些固定宽度的数字类型进行按位运算来与这些寄存器进行交互。例如,假设一个8位寄存器具有三个字段:字段名称下方的数字规定了... 查看详情

使用数组参数从 C 调用 Rust 方法

...015-09-2303:46:23【问题描述】:我正在尝试从我的C项目中为嵌入式设备调用Rust代码。设备通过UART打印,所以我可以看到我的调用结果是什么。以下C和Rust代码按预期工作(我省略了许多使其编译所需的样板Rust代码)。C:uint8_tinput[]... 查看详情

嵌入式串口初始化(代码片段)

【嵌入式】串口初始化1.将UART通道的引脚配置为UART功能,接收RXD,发送TXD2.时钟源选择及工作模式设置UCON03.设置波特率4.设置数据传输格式ULCON05.启用或禁止FIFO6.发送数据UTXH0寄存器和接收数据URXH0寄存器7.发收数据状态的控制UTRS... 查看详情

嵌入式:什么是atpcs

ATPCS介绍ATPCS(ARM-ThumbProduceCallStandard)是ARM程序和Thumb程序中子程序调用的基本规则,目的是为了使单独编译的C语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则... 查看详情

20155218《嵌入式基础》

20155218《嵌入式基础》要求:在作业本上完成附图作业,要认真看题目要求。提交作业截图作弊本学期成绩清零(有雷同的,不管是给别人传答案,还是找别人要答案都清零)课上做错的内容:C语言对寄存器的操作;提取位和插... 查看详情

嵌入式arm设计编程简单数据搬移

...需要的朋友们自取。或者关注公众号【AIShareLab】,回复嵌入式也可获取。一、实验目的熟悉实验开发环境,掌握简单ARM汇编指令的使用方法。二、实验环境硬件:PC机软件:ADS1.2集成开发环境三、实验内容熟悉开发环境并使用LDR... 查看详情

gpio接口是啥

参考技术AGPIO,通用I/O端口。在嵌入式系统中,经常需要控制许多结构简单的外部设备或者电路,这些设备有的需要通过CPU控制,有的需要CPU提供输入信号。对设备的控制,使用传统的串口或者并口就显得比较复杂,所以,在嵌... 查看详情

gpio接口是啥

GPIO,通用I/O端口。在嵌入式系统中,经常需要控制许多结构简单的外部设备或者电路,这些设备有的需要通过CPU控制,有的需要CPU提供输入信号。对设备的控制,使用传统的串口或者并口就显得比较复杂,所以,在嵌入式微处... 查看详情

在 Rust 程序和嵌入式 WebAssembly 运行时之间进行通信的最佳实践是啥?

】在Rust程序和嵌入式WebAssembly运行时之间进行通信的最佳实践是啥?【英文标题】:WhatisthebestpracticetocommunicatebetweenaRustprogramandanembeddedWebAssemblyruntime?在Rust程序和嵌入式WebAssembly运行时之间进行通信的最佳实践是什么?【发布时... 查看详情

嵌入式开发是对gpio口控制的一般步骤是怎样的

...态~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~`多看看朱友鹏的嵌入式教程!!里面讲的很详细。。本回答被提问者采纳 查看详情

手把手教你用rust开发嵌入式(代码片段)

...c;友好的编译器和清晰的错误提示信息为什么要用Rust进行嵌入式开发Rust的设计理念:既要安全 查看详情

嵌入式:arm汇编语言程序设计基础教程

汇编语言程序设计的步骤①合理地分配存储器资源,将前述的目标系统‘数据结构模型’表示到各存储器单元。②CPU寄存器数量有限,在程序中,大多数操作都要使用寄存器;并且有的操作使用特定的寄存器(如堆栈操作使用SP/... 查看详情

嵌入式arm设计编程字符串拷贝

...需要的朋友们自取。或者关注公众号【AIShareLab】,回复嵌入式也可获取。一、实验目的通过实验掌握使用LDB/STB,b等指令完成较为复杂的存储区访问和程序分支,学习使用条件码二、实验环境硬件:PC机软件:ADS1.2集成开发环境... 查看详情

人工智能深入参与嵌入式控制器调试---chatgpt回答tc377psri寄存器act位置位失效问题

做嵌入式开发的小伙伴们,有福利了!当遇到调试过程中遇到难题,我们往往会找手册,上网搜索,找FAE来解决, 现在有了ChatGPT,尝试问了下调试的问题,如下 首先我的问题描述:当设置MCMC... 查看详情

Rust 和 Wasm 初始设置

...间】:2020-12-2914:47:53【问题描述】:我正在关注thistutorial使用rust创建webassembly应用程序,但是当我尝试使用node运行捆绑的web程序集代码时(在添加我自己的任何代码之前并完全按照教程进行操作)$npmrunstart>create-wasm-app@0.1.0star... 查看详情

如何在 Rust 可执行文件中嵌入资源?

】如何在Rust可执行文件中嵌入资源?【英文标题】:HowtoembedresourcesinRustexecutable?【发布时间】:2014-11-2603:06:20【问题描述】:这是thisquestionoverhere的表弟为C提出同样的问题。基本上,有没有比将它变成一个巨大的字节数组并将... 查看详情

嵌入式:load/store之单寄存器的存取指令

ARM处理器是Load/Store型的,即它对数据的操作是通过将数据从存储器加载到片内寄存器中进行处理,处理完成后的结果经过寄存器存回到存储器中,以加快对片外存储器进行数据处理的速度。ARM的数据存取指令Load/Store是唯一用于... 查看详情

gpio接口是啥(gpio接口是干啥的)

...接口和io接口。4、gpio口的功能。1.GPIO,通用I/O端口。2.在嵌入式系统中,经常需要控制许多结构简单的外部设备或者电路,这些设备有的需要通过CPU控制,有的需要CPU提供输入信号。3.对设备的控制,使用传统的串口或者并口就... 查看详情