0

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

因此,我使用的 MCU 将 Led 连接到端口 A (0x48000000),其 BSRR 偏移量为 0x18。现在,我在数据表中读到,端口 A 的默认值为 0xa8000000,我不明白为什么。但是当我尝试使用没有任何反应来设置 LED 引脚 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 {}
}
4

1 回答 1

1

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

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

在 STM3F303R 的参考手册中...

基地址为

RCC 0x40021000
GPIOA 0x48000000

从评论中显示,您的 RCC 基础错误。

RCC_AHBENR 位于 rcc base + 0x14。第 17 位是 IOPAEN(I/O 端口 A(时钟)启用)复位值 0x00000014 闪存和 sram 在复位时启用,启用这是一件非常好的事情。

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

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

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

作为一般规则(有例外),您想要执行读取-修改-写入。只需将 1<<17 写入 RCC_AHBENR 即可启用 porta。现在您刚刚在睡眠模式下禁用了 sram 和 flash。现在,如果你不进入睡眠模式,你在技术上是可以的,但你真的应该

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<<(0+5) 写入 GPIOA_BSRR。在一个版本的代码中。然后在另一个中写入 1<<(16+5) 。第一个应该让led保持打开状态,第二个应该让led关闭。(然后搞砸延迟并试图眨眼)。

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

于 2021-12-13T15:17:20.600 回答