所以。。。我一直在关注嵌入的铁锈书。。。我现在正在读关于寄存器的文章。现在,这本书确实建议我使用STM32F303VC发现来避免问题,但我找不到,因此我得到了Nucleo F303RE。货物的目标和材料保持不变。所以我认为不会有任何问题。
因此,我使用的MCU将Led连接到端口a(0x48000000),端口a的BSRR偏移量为0x18。现在,我在数据表中读到,端口A的默认值是0xa8000000,我不明白为什么。但是当我尝试使用ptr::write_volatile(PORTA_BSRR as *mut u32, 1 << 5);
什么也没发生。甚至我的gdb终端也没有反映出任何变化。因此,我尝试按照原始教程的建议使用portE进行检查(0x48001018)。但即便如此,寄存器值也不会改变。我无法调试此问题。
现在,我可以运行以前的教程,并能够检查变量和其他东西。我的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 {}
}
99%以上的裸金属都在读数。
所以你从核子数据表中发现,D13是LD2,在F303变体上是PA5。端口A针脚5。
在STM3F03R的参考手册中。。。
的基本地址
RCC 0x40021000
GPIOA 0x48000000
从评论中可以看出,你的RCC基础是错误的。
RCC_AHBENR位于RCC基底+0x14处。位17是0x00000014闪存的IOPAEN(I/O端口A(时钟)启用)重置值,并且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<lt;17到RCC_AHBENR将启用porta。现在您刚刚在睡眠模式下禁用了sram AND flash。现在,如果你没有进入睡眠模式,你在技术上是可以的,但你真的应该
x = read RCC_AHBENR
x|= 1<<17
write RCC_AHBENR = x
这可以用一个或相等的来完成,作为一个习惯,我建议不要这样做,但对于这个寄存器的手动优化来说,这是可以的。我还不是铁锈专家,所以不知道该怎么做。我认为你应该先尝试asm或C,然后成功地将这些知识应用到Rust。
对于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部分记录了这一点,有些没有,大多数人都很幸运,因为他们使用了一个固定的工具或读写习惯,而且逻辑对它有一种奇怪的感觉,尤其是moder寄存器(可能会使他们的代码工作)。
如果你要预先准备寄存器并进行背靠背写入
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族的人数仍然是百万分之一,但对于铁锈来说,这只是地球上总人口中的一小部分)。
您应该检查矢量表、二进制文件中的位置、加载、存储、地址和数据等。同样,当从任何二进制格式转换为复制到nucleo板所需的原始二进制格式时,也要检查该二进制文件,以查看它是否从矢量表开始,并且所需的任何填充都已到位。
所以。。。。
修正你的rcc寄存器地址,我也会修正数据。
在rcc寄存器写入后添加读取或延迟,如果需要,可以对gpio模式进行简单的一次性读取,或者最好进行读取-修改-写入。
将位11:10的moder写入0b01,使其成为通用输出
然后你可以在BSRR中处理第5位和16+5位。
最后,从核子文件中,你甚至不需要看示意图。他们在LD2下很好地记录了
the I/O is HIGH value, the LED is on
所以你想设置PA5打开led和重置关闭它。
写一个1<lt;(0+5)到GPIOA_BSRR。在代码的一个版本中。然后写入1<lt;(16+5)。第一个应该打开led,第二个应该关闭led。
结尾的无限循环只需要引导程序或预主代码在返回时出错,如果您编写自己的引导程序,那么您可以简单地让它在从主返回时执行无限循环(这就是我喜欢的方式,有时是wfi/wfe循环,但有些库会在返回时处理事情,因此养成了这种习惯)。由于您应该非常了解任何裸机项目(供应商沙盒或您自己的项目)的启动过程和代码,因此在编写main之前应该知道答案,并知道需求是什么。在这种情况下,也许你会这样做,只是没有在这里展示。