在 ARM STM32G030K6 上启用 GPIO
Enable GPIO on ARM STM32G030K6
我正在尝试通过直接操作寄存器(不依赖于 CubeMX)来学习对 STM32G030K6 进行编程。我的程序旨在将引脚 PA5 设置为高电平。
// Target: STM32G030K6T6
// Goal: Set pin PA5 to high
#include "stm32g0xx.h" // Device header
int main(void)
{
RCC->IOPENR |= 1; // Enable GPIOA Clock
GPIOA->MODER |= 0x400; // Set GPIOA MODE5 to a general purpose output
GPIOA->ODR = 0x20; // Set PA5 high
while(1)
{
}
}
程序完全不影响PA5。
我已经使用 CubeMX 闪烁程序成功测试了设置,证明这不是硬件问题。
到目前为止我从你那里了解到的是你bought/acquired把这部分放在了分线板上。已施加电源和接地,添加了一个 LED 和电阻器,并连接了一个 stlink。可以使用 CubeMX 并使用 Kiel 使其工作。
所以我制作了很多分线板,将 LED 等放在板上,因为我厌倦了单独接线。我用过的部件需要确保 VDD 和 VDDA 已连接,但你的是同一个引脚,请检查。 VDD 和 VSS 毫无疑问,如果你有它的工作。 NRST 拉高了很好的措施,虽然我认为不需要,因为有一个内部拉高,但 BOOT0 确实需要拉低,但这是一个 STM32G,你已经指出 SWCLK 和 BOOT0 共享相同的引脚。遗憾的是,ST 正在放弃片上引导加载程序,或者至少它被工厂禁用了
ST production value: 0xDFFF E1AA
Bit 24 nBOOT_SEL
0: BOOT0 signal is defined by BOOT0 pin value (legacy mode)
1: BOOT0 signal is defined by nBOOT0 option bit
因此,新部件 BOOT0 不是您可以依赖的进入引导加载程序并使用 uart 解决方案将代码下载到闪存中的东西,也不能在执行此级别时使用它来让自己畅通无阻工作。
所以 stlink 已连接,您说 Kiel 可以与该部件通话,所以理论上一切都很好,不是问题。
我手边没有 Kiel,但每个人都可以获得 gnu 交叉编译器或从源代码构建一个。
apt-get install binutils-arm-linux-gnueabi gcc-arm-linux-gnueabi
下面的代码不关心 arm-non-eabi- vs arm-linux-gnueabi- 交叉编译器的变化它独立于这些差异,它只需要编译器汇编器和链接器。
现在这可能会再次与某些其他 SO 用户展开个人意见战。在噪音中工作。我特别避免 CMSIS,我已经看到了实现,你应该检查它,因为现在你不想将这种风险添加到你的代码中,删除它并在以后根据需要添加它。这是我的风格它专门控制用于访问的指令,一切都是基于大量经验即使你没有看到,设计用于reader有很高的成功机会。让它成为你自己的 if/when 你让它工作 and/or 我真正的目标是旁注,它可以帮助你检查你正在用你自己的工具构建的二进制文件,以消除常见的陷阱。
这不仅仅是让 main() 中的 C 代码正确以使 bare-metal 代码正常工作的情况,您需要从重置开始的整个过程都是正确的。
基于 Flash 的版本:
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
notmain.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define RCC_BASE 0x40021000
#define RCC_IOPENR (RCC_BASE+0x34)
#define GPIOA_BASE 0x50000000
#define GPIOA_MODER (GPIOA_BASE+0x00)
#define GPIOA_OTYPER (GPIOA_BASE+0x04)
#define GPIOA_BSRR (GPIOA_BASE+0x18)
#define DCOUNT 2000000
int notmain ( void )
{
unsigned int ra;
unsigned int rx;
ra=GET32(RCC_IOPENR);
ra|=1<<0; //enable port a
PUT32(RCC_IOPENR,ra);
ra=GET32(GPIOA_MODER);
ra&=~(3<<(5<<1)); //clear bits 10,11
ra|= (1<<(5<<1)); //set bit 10
PUT32(GPIOA_MODER,ra);
ra=GET32(GPIOA_OTYPER);
ra&=~(1<<5); //clear bit 5
PUT32(GPIOA_OTYPER,ra);
for(rx=0;;rx++)
{
PUT32(GPIOA_BSRR, (1<<(5+ 0)) );
for(ra=0;ra<DCOUNT;ra++) dummy(ra);
PUT32(GPIOA_BSRR, (1<<(5+16)) );
for(ra=0;ra<DCOUNT;ra++) dummy(ra);
}
return(0);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
建造
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
同样,您可以将 arm-none-eabi 替换为 arm-linux-gnueabi,如果您 have/found 是 arm-linux-gnueabi。此代码不关心差异。
这里的重点是让处理器启动:
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000017 stmdaeq r0, {r0, r1, r2, r4}
800000c: 08000017 stmdaeq r0, {r0, r1, r2, r4}
08000010 <reset>:
8000010: f000 f808 bl 8000024 <notmain>
8000014: e7ff b.n 8000016 <hang>
08000016 <hang>:
8000016: e7fe b.n 8000016 <hang>
应用程序闪存从 ARM 内存中的 0x08000000 开始 space,在参考手册中称为 Main Flash Memory。根据引导设置,0x08000000 将在 0x00000000 处进行镜像,如 ARM 手册中所述,这是向量 table 所在的位置。第一个字是复位时加载到堆栈指针中的值,地址 0x00000004 处的字(将镜像到 0x08000004)是复位向量。
上面使用了反汇编程序,因此它试图将这些值作为指令进行反汇编 values/vectors 忽略 table 的反汇编。
假设我们可以获得将此二进制文件放入闪存中所需位置的工具,那么
08000000 <_start>:
8000000: 20001000 value loaded into sp on reset
8000004: 08000011 reset vector
重置向量是为该异常执行的代码的地址,其中 lsbit 设置为指示拇指模式,lsbit 被剥离,它不会进入 pc。所以这里的复位向量地址是0x08000010是正确的:
08000010 <reset>:
8000010: f000 f808 bl 8000024 <notmain>
8000014: e7ff b.n 8000016 <hang>
并且可以按照这个到 notmain,C 入口点的名称并不重要,一些工具会添加额外的东西,它会看到标签 main(),已经很多年没见过了,但会继续这样做也证明一点没关系。
因此,如果将其放入主闪存中的 arm 地址 0x08000000,则该代码将启动并且 运行 直到 C 代码。
注意 sram 从 0x20000000 开始并且 RM 显示这部分有 32MBytes 的 sram 所以它至少有 0x1000 字节来覆盖这个项目有足够的额外空间。
8000026: 481b ldr r0, [pc, #108] ; (8000094 <notmain+0x70>)
8000028: f7ff fff8 bl 800001c <GET32>
800002c: 2101 movs r1, #1
800002e: 4301 orrs r1, r0
8000030: 4818 ldr r0, [pc, #96] ; (8000094 <notmain+0x70>)
8000032: f7ff fff1 bl 8000018 <PUT32>
...
8000094: 40021034 andmi r1, r2, r4, lsr r0
无论是我编写的程序还是通过您的程序和 CMSIS 或 HAL headers,您应该会看到 0x40021034 以某种形式被使用。请注意,您的这部分是 cortex-m0+,因此它只有有限数量的 thumb2 扩展,请注意 bl 是两个独立的 16 条指令,可以 spaced 分开,但几乎总是成对出现,它们是两条指令,其余指令需要是 16 位,如果您在反汇编中看到 something.w 或 bl 以外的指令是 32 或 16*2 位,那么这可能是 thumb2 指令,但不会运行 在此处理器上,可能是您在构建此代码时使用的一些设置,您可以通过此工具链看到我有特定的从指令集的角度(架构 armv6-m)来看,m0 实际上与 m0+ 相同。您不希望 armv7-m 因为该芯片无法工作,armv7-m 中大约有 100 条左右的指令无法在基于 armv6-m 的芯片上工作。
io 使能寄存器中位的 orring 应该类似于从 0x40021034 读取 (ldr)、修改读取值和写入 (str) 到同一地址。
您发布的代码适用于其他 STM32 部件,因为它们中的许多部件将 MODER 寄存器(如果该部件使用那种 GPIO 外设)初始化为大多数输入引脚的零。这部分记录了大多数引脚重置为模拟模式的 0b11,好奇为什么但无论如何。
Reset value:
0xEBFF FFFF for port A
0xFFFF FFFF for other ports
所以你不能简单地设置两个位之一来改变模式,如果这些位开始为 0b00 然后设置一个可以把它变成 0b01,但是对于这部分你可以只清除位 11 或更好控制两个位而不依赖于复位状态,因此清除两个位并设置其中一个或清除一个并设置另一个
5<<1 表示 5 左移一个 0b101 从右移一个零得到 0b1010,它是一个 0xA,它是 10 这是一种直观的方式,可以看出我正在弄乱 PA5 并且数字 5 在那里, 但对于此寄存器引脚 5 模式设置是第 10 位和第 11 位。3 << (5<<1) 表示 3<<10,即第 10 位和第 11 位。波浪号表示反转整个事物,因此 00000C00 是 3<< 10 反转你得到的 FFFFF3FF 与现代值的 anded 将使第 10 位和第 11 位归零。现在用 00000400 或 1<<10 设置第 10 位。
我们希望输出至少现在是 push-pull 而非开漏,因此即使重置值已经是 push-pull,我还是将其清除以备不时之需。现在我通常不理会上拉或其他 gpio 设置寄存器,我为使用此 GPIO 外围设备的 STM32 部件弄乱了这两个 MODER 和 OTYPER(您会看到并非所有 STM32 部件都使用相同的 IP,STM32F103例如使用不同的,请查看。
因此,以某种方式确认 CMSIS 是否生成的代码与这些寄存器混淆。从文档 GPIOA 开始于 0x50000000。所以 0x50000000 和 0x50000004 寄存器。
因为这部分有一个 GPIO BSRR 注册它是一个很好的功能,现在就使用它,这样你就不会不小心弄乱其他引脚。
虚拟循环会消耗时间,因此在这种情况下,led 会闪烁,当你得到这个 运行ning 时,你必须根据处理器使用的时钟调整 DCOUNT 不会太快太慢了,刚刚好。使用外部函数以这种方式执行它不再是死代码( for(ra=0;ra
不,代码实际上并没有命中 return(0);一些编译器不那么聪明并且抱怨。 (有些人那么聪明,抱怨你无法到达那里,YMMV)
所有这些部分都需要到位才能有一半的机会发挥作用。它不仅仅是几行 C 代码。
使用 stlink kiel 工具很好,我希望有一种方法可以检查内存 space,您需要检查 0x08000000 并将其与该工具生成的二进制文件进行比较,希望有是一种检查工具输出以及查看它构建了什么的方法,使用 gnu 很容易做到。
您可以使用 openocd 而不是 kiel 从命令行加载和检查内容,形式为
openocd -f stlink.cfg -f target.cfg
然后在另一个 window
telnet localhost 4444
gdb 添加了更多未知数...
那么你可以使用
mdw 0x08000000 40
在 telnet window 中查看主闪存中有什么,然后将其与二进制文件的可加载部分进行比较,以查看您的程序是否真的存在,如果您的程序实际上不存在则没有无论您对 C 代码做什么,它都不会闪烁。
有一些方法可以使用 openocd 来刷写部件,但它非常 vendor/part 具体,因为他们必须将这种功能添加到 openocd,而且你必须有正确的版本,从记忆中它是一些东西行
flash write_image erase notmain.elf
如果使用其中包含地址信息的“二进制文件”,如果您使用的是内存映像,则需要将地址放在该命令行上 0x08000000
一些 st 零件被锁定,或者说像一些蓝色药丸这样的电路板在这不起作用,原始零件我不知道我看到过锁定,您购买的零件看起来很松散,所以它们不应该被锁定.
如果您可以使用 openocd 和 gnu,那么您也可以尝试使用 sram,而无需最初支持 flash。
sram.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
ldr r0,=0x20001000
mov sp,r0
bl notmain
b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
sram.ld
MEMORY
{
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.bss : { *(.bss*) } > ram
}
由于这部分使用了vectortable,而接下来要介绍的是使用调试器在sram,volatile中放置和运行一个程序,所以当你reset/reboot 它丢失了,但它提供了一种无需让 flash 写入工作即可进行实验的方法。
我们将告诉调试器从 0x20000000 处开始执行,因此我们希望那里有一条指令而不是向量 table。
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 sram.s -o sram.o
arm-none-eabi-ld -o notmain.elf -T sram.ld sram.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
总是在 运行ning
之前检查新项目上的二进制文件
Disassembly of section .text:
20000000 <_start>:
20000000: 4804 ldr r0, [pc, #16] ; (20000014 <dummy+0x2>)
20000002: 4685 mov sp, r0
20000004: f000 f808 bl 20000018 <notmain>
20000008: e7fe b.n 20000008 <_start+0x8>
2000000a <PUT32>:
2000000a: 6001 str r1, [r0, #0]
2000000c: 4770 bx lr
2000000e <GET32>:
2000000e: 6800 ldr r0, [r0, #0]
20000010: 4770 bx lr
20000012 <dummy>:
20000012: 4770 bx lr
20000014: 20001000 andcs r1, r0, r0
20000018 <notmain>:
20000018: b570 push {r4, r5, r6, lr}
看起来不错。
您现在可以使用 openocd
重置暂停
load_image notmain.elf
恢复 0x20000000
到运行程序(可能需要一个路径,如果你运行openocd在elf文件所在的目录and/or你将elf文件复制到你所在的目录启动openocd(不是telnet,openocd)然后你通常不需要放一个路径。
这是在 sram 中而不是闪存中,因此可能 运行 更快并且可能需要在延迟循环中使用更大的值。
如果你只是想让输出高或低,那么只需使用所需的 bsrr 行并摆脱循环,当你从 notmain return , 一个不会干扰 gpio 端口的,作为你用你的工具构建的二进制文件的调查的一部分,你需要确认你放置的 while 循环实际上不是死代码并且已经实现(已知 clang死代码,所以其他人也可以)和一些沙箱当你从 main return 撤消一些东西,所以你的代码现在可能没问题,但是从 main 退出并且 bootstrap 撤消你所做的PA5比你看到的还要快
到目前为止我能做的就是这些,我有一个 stm32 cortex-m0+ 部分和一个工作的 openocd 配置,如果有帮助的话,这是一个不同的部分,但核心是相同的,如果没有另一个水龙头那么它应该只是工作,但你永远不知道。
简短回答,您的现代代码将无法工作,否则它看起来不错,但 C 代码只是成功所需故事的一部分。这个长答案强调了成功启动和设置 LED 灯所必须具备的要点。有可能我们都错过了一个额外的启用,我没有这个部分,所以我不能真正拉出一个并且 运行 这个代码就可以了。
我正在尝试通过直接操作寄存器(不依赖于 CubeMX)来学习对 STM32G030K6 进行编程。我的程序旨在将引脚 PA5 设置为高电平。
// Target: STM32G030K6T6
// Goal: Set pin PA5 to high
#include "stm32g0xx.h" // Device header
int main(void)
{
RCC->IOPENR |= 1; // Enable GPIOA Clock
GPIOA->MODER |= 0x400; // Set GPIOA MODE5 to a general purpose output
GPIOA->ODR = 0x20; // Set PA5 high
while(1)
{
}
}
程序完全不影响PA5。 我已经使用 CubeMX 闪烁程序成功测试了设置,证明这不是硬件问题。
到目前为止我从你那里了解到的是你bought/acquired把这部分放在了分线板上。已施加电源和接地,添加了一个 LED 和电阻器,并连接了一个 stlink。可以使用 CubeMX 并使用 Kiel 使其工作。
所以我制作了很多分线板,将 LED 等放在板上,因为我厌倦了单独接线。我用过的部件需要确保 VDD 和 VDDA 已连接,但你的是同一个引脚,请检查。 VDD 和 VSS 毫无疑问,如果你有它的工作。 NRST 拉高了很好的措施,虽然我认为不需要,因为有一个内部拉高,但 BOOT0 确实需要拉低,但这是一个 STM32G,你已经指出 SWCLK 和 BOOT0 共享相同的引脚。遗憾的是,ST 正在放弃片上引导加载程序,或者至少它被工厂禁用了
ST production value: 0xDFFF E1AA
Bit 24 nBOOT_SEL
0: BOOT0 signal is defined by BOOT0 pin value (legacy mode)
1: BOOT0 signal is defined by nBOOT0 option bit
因此,新部件 BOOT0 不是您可以依赖的进入引导加载程序并使用 uart 解决方案将代码下载到闪存中的东西,也不能在执行此级别时使用它来让自己畅通无阻工作。
所以 stlink 已连接,您说 Kiel 可以与该部件通话,所以理论上一切都很好,不是问题。
我手边没有 Kiel,但每个人都可以获得 gnu 交叉编译器或从源代码构建一个。
apt-get install binutils-arm-linux-gnueabi gcc-arm-linux-gnueabi
下面的代码不关心 arm-non-eabi- vs arm-linux-gnueabi- 交叉编译器的变化它独立于这些差异,它只需要编译器汇编器和链接器。
现在这可能会再次与某些其他 SO 用户展开个人意见战。在噪音中工作。我特别避免 CMSIS,我已经看到了实现,你应该检查它,因为现在你不想将这种风险添加到你的代码中,删除它并在以后根据需要添加它。这是我的风格它专门控制用于访问的指令,一切都是基于大量经验即使你没有看到,设计用于reader有很高的成功机会。让它成为你自己的 if/when 你让它工作 and/or 我真正的目标是旁注,它可以帮助你检查你正在用你自己的工具构建的二进制文件,以消除常见的陷阱。
这不仅仅是让 main() 中的 C 代码正确以使 bare-metal 代码正常工作的情况,您需要从重置开始的整个过程都是正确的。
基于 Flash 的版本:
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
notmain.c
void PUT32 ( unsigned int, unsigned int );
unsigned int GET32 ( unsigned int );
void dummy ( unsigned int );
#define RCC_BASE 0x40021000
#define RCC_IOPENR (RCC_BASE+0x34)
#define GPIOA_BASE 0x50000000
#define GPIOA_MODER (GPIOA_BASE+0x00)
#define GPIOA_OTYPER (GPIOA_BASE+0x04)
#define GPIOA_BSRR (GPIOA_BASE+0x18)
#define DCOUNT 2000000
int notmain ( void )
{
unsigned int ra;
unsigned int rx;
ra=GET32(RCC_IOPENR);
ra|=1<<0; //enable port a
PUT32(RCC_IOPENR,ra);
ra=GET32(GPIOA_MODER);
ra&=~(3<<(5<<1)); //clear bits 10,11
ra|= (1<<(5<<1)); //set bit 10
PUT32(GPIOA_MODER,ra);
ra=GET32(GPIOA_OTYPER);
ra&=~(1<<5); //clear bit 5
PUT32(GPIOA_OTYPER,ra);
for(rx=0;;rx++)
{
PUT32(GPIOA_BSRR, (1<<(5+ 0)) );
for(ra=0;ra<DCOUNT;ra++) dummy(ra);
PUT32(GPIOA_BSRR, (1<<(5+16)) );
for(ra=0;ra<DCOUNT;ra++) dummy(ra);
}
return(0);
}
flash.ld
MEMORY
{
rom : ORIGIN = 0x08000000, LENGTH = 0x1000
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > rom
.rodata : { *(.rodata*) } > rom
.bss : { *(.bss*) } > ram
}
建造
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mcpu=cortex-m0 -mthumb -c notmain.c -o notmain.o
arm-none-eabi-ld -o notmain.elf -T flash.ld flash.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
同样,您可以将 arm-none-eabi 替换为 arm-linux-gnueabi,如果您 have/found 是 arm-linux-gnueabi。此代码不关心差异。
这里的重点是让处理器启动:
Disassembly of section .text:
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000011 stmdaeq r0, {r0, r4}
8000008: 08000017 stmdaeq r0, {r0, r1, r2, r4}
800000c: 08000017 stmdaeq r0, {r0, r1, r2, r4}
08000010 <reset>:
8000010: f000 f808 bl 8000024 <notmain>
8000014: e7ff b.n 8000016 <hang>
08000016 <hang>:
8000016: e7fe b.n 8000016 <hang>
应用程序闪存从 ARM 内存中的 0x08000000 开始 space,在参考手册中称为 Main Flash Memory。根据引导设置,0x08000000 将在 0x00000000 处进行镜像,如 ARM 手册中所述,这是向量 table 所在的位置。第一个字是复位时加载到堆栈指针中的值,地址 0x00000004 处的字(将镜像到 0x08000004)是复位向量。
上面使用了反汇编程序,因此它试图将这些值作为指令进行反汇编 values/vectors 忽略 table 的反汇编。
假设我们可以获得将此二进制文件放入闪存中所需位置的工具,那么
08000000 <_start>:
8000000: 20001000 value loaded into sp on reset
8000004: 08000011 reset vector
重置向量是为该异常执行的代码的地址,其中 lsbit 设置为指示拇指模式,lsbit 被剥离,它不会进入 pc。所以这里的复位向量地址是0x08000010是正确的:
08000010 <reset>:
8000010: f000 f808 bl 8000024 <notmain>
8000014: e7ff b.n 8000016 <hang>
并且可以按照这个到 notmain,C 入口点的名称并不重要,一些工具会添加额外的东西,它会看到标签 main(),已经很多年没见过了,但会继续这样做也证明一点没关系。
因此,如果将其放入主闪存中的 arm 地址 0x08000000,则该代码将启动并且 运行 直到 C 代码。
注意 sram 从 0x20000000 开始并且 RM 显示这部分有 32MBytes 的 sram 所以它至少有 0x1000 字节来覆盖这个项目有足够的额外空间。
8000026: 481b ldr r0, [pc, #108] ; (8000094 <notmain+0x70>)
8000028: f7ff fff8 bl 800001c <GET32>
800002c: 2101 movs r1, #1
800002e: 4301 orrs r1, r0
8000030: 4818 ldr r0, [pc, #96] ; (8000094 <notmain+0x70>)
8000032: f7ff fff1 bl 8000018 <PUT32>
...
8000094: 40021034 andmi r1, r2, r4, lsr r0
无论是我编写的程序还是通过您的程序和 CMSIS 或 HAL headers,您应该会看到 0x40021034 以某种形式被使用。请注意,您的这部分是 cortex-m0+,因此它只有有限数量的 thumb2 扩展,请注意 bl 是两个独立的 16 条指令,可以 spaced 分开,但几乎总是成对出现,它们是两条指令,其余指令需要是 16 位,如果您在反汇编中看到 something.w 或 bl 以外的指令是 32 或 16*2 位,那么这可能是 thumb2 指令,但不会运行 在此处理器上,可能是您在构建此代码时使用的一些设置,您可以通过此工具链看到我有特定的从指令集的角度(架构 armv6-m)来看,m0 实际上与 m0+ 相同。您不希望 armv7-m 因为该芯片无法工作,armv7-m 中大约有 100 条左右的指令无法在基于 armv6-m 的芯片上工作。
io 使能寄存器中位的 orring 应该类似于从 0x40021034 读取 (ldr)、修改读取值和写入 (str) 到同一地址。
您发布的代码适用于其他 STM32 部件,因为它们中的许多部件将 MODER 寄存器(如果该部件使用那种 GPIO 外设)初始化为大多数输入引脚的零。这部分记录了大多数引脚重置为模拟模式的 0b11,好奇为什么但无论如何。
Reset value:
0xEBFF FFFF for port A
0xFFFF FFFF for other ports
所以你不能简单地设置两个位之一来改变模式,如果这些位开始为 0b00 然后设置一个可以把它变成 0b01,但是对于这部分你可以只清除位 11 或更好控制两个位而不依赖于复位状态,因此清除两个位并设置其中一个或清除一个并设置另一个
5<<1 表示 5 左移一个 0b101 从右移一个零得到 0b1010,它是一个 0xA,它是 10 这是一种直观的方式,可以看出我正在弄乱 PA5 并且数字 5 在那里, 但对于此寄存器引脚 5 模式设置是第 10 位和第 11 位。3 << (5<<1) 表示 3<<10,即第 10 位和第 11 位。波浪号表示反转整个事物,因此 00000C00 是 3<< 10 反转你得到的 FFFFF3FF 与现代值的 anded 将使第 10 位和第 11 位归零。现在用 00000400 或 1<<10 设置第 10 位。
我们希望输出至少现在是 push-pull 而非开漏,因此即使重置值已经是 push-pull,我还是将其清除以备不时之需。现在我通常不理会上拉或其他 gpio 设置寄存器,我为使用此 GPIO 外围设备的 STM32 部件弄乱了这两个 MODER 和 OTYPER(您会看到并非所有 STM32 部件都使用相同的 IP,STM32F103例如使用不同的,请查看。
因此,以某种方式确认 CMSIS 是否生成的代码与这些寄存器混淆。从文档 GPIOA 开始于 0x50000000。所以 0x50000000 和 0x50000004 寄存器。
因为这部分有一个 GPIO BSRR 注册它是一个很好的功能,现在就使用它,这样你就不会不小心弄乱其他引脚。
虚拟循环会消耗时间,因此在这种情况下,led 会闪烁,当你得到这个 运行ning 时,你必须根据处理器使用的时钟调整 DCOUNT 不会太快太慢了,刚刚好。使用外部函数以这种方式执行它不再是死代码( for(ra=0;ra 不,代码实际上并没有命中 return(0);一些编译器不那么聪明并且抱怨。 (有些人那么聪明,抱怨你无法到达那里,YMMV) 所有这些部分都需要到位才能有一半的机会发挥作用。它不仅仅是几行 C 代码。 使用 stlink kiel 工具很好,我希望有一种方法可以检查内存 space,您需要检查 0x08000000 并将其与该工具生成的二进制文件进行比较,希望有是一种检查工具输出以及查看它构建了什么的方法,使用 gnu 很容易做到。 您可以使用 openocd 而不是 kiel 从命令行加载和检查内容,形式为 然后在另一个 window gdb 添加了更多未知数... 那么你可以使用 在 telnet window 中查看主闪存中有什么,然后将其与二进制文件的可加载部分进行比较,以查看您的程序是否真的存在,如果您的程序实际上不存在则没有无论您对 C 代码做什么,它都不会闪烁。 有一些方法可以使用 openocd 来刷写部件,但它非常 vendor/part 具体,因为他们必须将这种功能添加到 openocd,而且你必须有正确的版本,从记忆中它是一些东西行 如果使用其中包含地址信息的“二进制文件”,如果您使用的是内存映像,则需要将地址放在该命令行上 0x08000000 一些 st 零件被锁定,或者说像一些蓝色药丸这样的电路板在这不起作用,原始零件我不知道我看到过锁定,您购买的零件看起来很松散,所以它们不应该被锁定. 如果您可以使用 openocd 和 gnu,那么您也可以尝试使用 sram,而无需最初支持 flash。 sram.s sram.ld 由于这部分使用了vectortable,而接下来要介绍的是使用调试器在sram,volatile中放置和运行一个程序,所以当你reset/reboot 它丢失了,但它提供了一种无需让 flash 写入工作即可进行实验的方法。 我们将告诉调试器从 0x20000000 处开始执行,因此我们希望那里有一条指令而不是向量 table。 总是在 运行ning 看起来不错。 您现在可以使用 openocd 重置暂停
load_image notmain.elf
恢复 0x20000000 到运行程序(可能需要一个路径,如果你运行openocd在elf文件所在的目录and/or你将elf文件复制到你所在的目录启动openocd(不是telnet,openocd)然后你通常不需要放一个路径。 这是在 sram 中而不是闪存中,因此可能 运行 更快并且可能需要在延迟循环中使用更大的值。 如果你只是想让输出高或低,那么只需使用所需的 bsrr 行并摆脱循环,当你从 notmain return , 一个不会干扰 gpio 端口的,作为你用你的工具构建的二进制文件的调查的一部分,你需要确认你放置的 while 循环实际上不是死代码并且已经实现(已知 clang死代码,所以其他人也可以)和一些沙箱当你从 main return 撤消一些东西,所以你的代码现在可能没问题,但是从 main 退出并且 bootstrap 撤消你所做的PA5比你看到的还要快 到目前为止我能做的就是这些,我有一个 stm32 cortex-m0+ 部分和一个工作的 openocd 配置,如果有帮助的话,这是一个不同的部分,但核心是相同的,如果没有另一个水龙头那么它应该只是工作,但你永远不知道。 简短回答,您的现代代码将无法工作,否则它看起来不错,但 C 代码只是成功所需故事的一部分。这个长答案强调了成功启动和设置 LED 灯所必须具备的要点。有可能我们都错过了一个额外的启用,我没有这个部分,所以我不能真正拉出一个并且 运行 这个代码就可以了。openocd -f stlink.cfg -f target.cfg
telnet localhost 4444
mdw 0x08000000 40
flash write_image erase notmain.elf
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
ldr r0,=0x20001000
mov sp,r0
bl notmain
b .
.thumb_func
.globl PUT32
PUT32:
str r1,[r0]
bx lr
.thumb_func
.globl GET32
GET32:
ldr r0,[r0]
bx lr
.thumb_func
.globl dummy
dummy:
bx lr
MEMORY
{
ram : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > ram
.rodata : { *(.rodata*) } > ram
.bss : { *(.bss*) } > ram
}
arm-none-eabi-as --warn --fatal-warnings -mcpu=cortex-m0 sram.s -o sram.o
arm-none-eabi-ld -o notmain.elf -T sram.ld sram.o notmain.o
arm-none-eabi-objdump -D notmain.elf > notmain.list
arm-none-eabi-objcopy notmain.elf notmain.bin -O binary
Disassembly of section .text:
20000000 <_start>:
20000000: 4804 ldr r0, [pc, #16] ; (20000014 <dummy+0x2>)
20000002: 4685 mov sp, r0
20000004: f000 f808 bl 20000018 <notmain>
20000008: e7fe b.n 20000008 <_start+0x8>
2000000a <PUT32>:
2000000a: 6001 str r1, [r0, #0]
2000000c: 4770 bx lr
2000000e <GET32>:
2000000e: 6800 ldr r0, [r0, #0]
20000010: 4770 bx lr
20000012 <dummy>:
20000012: 4770 bx lr
20000014: 20001000 andcs r1, r0, r0
20000018 <notmain>:
20000018: b570 push {r4, r5, r6, lr}