【问题标题】:Setting registers using embedded rust使用嵌入式 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 寄存器,如果你使用的单片机有,你不需要触摸,也不需要速度寄存器。您只需要将输出模式设置为输出,推拉即可。

标签: rust embedded bare-metal stm32f3


【解决方案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 之前知道答案并知道要求是什么。也许你在这种情况下会这样做,只是没有在这里展示。

【讨论】:

    猜你喜欢
    • 2020-11-13
    • 2012-12-08
    • 1970-01-01
    • 2019-06-19
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多