我使用足够的启动文件在keil中编写了stm32f103c8t6板的代码。我使用数据表中的信息直接写入内存地址。但是st-link上传似乎将hex文件上传到了worng地址,例如: GPIOA = 0X4001 0800 这是根据stm数据表。 但是stlink显示设备数据范围从0x0800 0000到0x0800 03d4。 我的代码是一个简单的程序,用于在portA1处使LED闪烁。我将LED连接到10k电阻上。当我使用指针指定内存位置时,为什么stm分配错误的地址,或者可能有任何其他错误。代码如下。
void delay(int a);
int main(void)
{
unsigned int* GPIO_A;
GPIO_A = (unsigned int*)0x40010800 ; // Assigning GPIOA to the correct memory location
unsigned int* GPIO_A_CRL;
GPIO_A_CRL = GPIO_A + 0x00 ; // Assigning GPIO_A_CRL to the correct memory location
/*unsigned int* GPIO_A_IDR;
GPIO_A_IDR = GPIO_A + 0X08 ; // Assigning GPIO_A_IDR to the correct memory location */
unsigned int* GPIO_A_BSRR;
GPIO_A_BSRR = GPIO_A + 0X10 ; // Assigning GPIO_A_BSRR to the correct memory location
unsigned int* GPIO_A_BRR;
GPIO_A_BRR = GPIO_A + 0X14 ; // Assigning GPIO_A_BSRR to the correct memory location
unsigned int* RCC_APB2ENR;
RCC_APB2ENR = (unsigned int*)(0x40021000 + 0X18) ; // Assigning RCC_APB2ENR to the correct memory location
*RCC_APB2ENR = 0X04; // Set clock for GPIOA
*GPIO_A_CRL = 0X00008888 ; // Defining pin modes for GPIO_A_CRL
while(1) // infinite loop
{
*GPIO_A_BSRR = 0X00000002; // Set bit 1 to 1
delay(2); // delay
*GPIO_A_BRR = 0x00000002; // reset bit 1 to reset value(0)
delay(2); // delay
}
}
void delay(int a)
{
long b = a*1000000;
for(int i=0;i<b;i++)
{
int c=1;
}
}
答案 0 :(得分:1)
编译器/链接器未正确分配地址;你只是误解了内存映射以及编译器和链接器(甚至处理器)的工作方式。
0x08000000到0x080003d4是代码的位置; 0X40010800是GPIOA存储器映射寄存器的地址。
在STM32上,0x0800000是片上闪存的起始地址。当处理器复位时,它从0x08000000加载堆栈指针寄存器,从0x08000004加载程序计数器寄存器。在此之上是interrupt vector table及以上将是您的代码 - 或者更确切地说是编译器从您的源代码生成的机器代码。您提到的启动文件定义了所提到的复位和中断向量等。
指针GPIO_A
在运行时的代码中分配,指针变量的位置将在RAM中,尽管因为你初始化它而且永远不会修改它,编译器可以优化以将地址存储在ROM中或用文字内联替换它。
而不是定义自己的寄存器地址,使用供应商提供的处理器头(在本例中为stm32f10xx.h)会更简单,更安全。当您为特定部件配置项目时,Keil工具链包含此文件。它还包括STM32标准外设库(或从here下载),它简化了低级外设访问,并包含大量外围I / O示例,包括GPIO。
有关STM32F1xx的完整程序员信息,仅依靠数据表是不够的 - 它只是告诉您部件的特定功能;您应该使用更全面的Reference Manual。
答案 1 :(得分:0)
我有来自亚洲的ebay 2美元有相同的部分,并且在C端口13上有一个led。
这是一个完整的基于gnu工具链的示例,您可以将其更改为在端口d引脚0上操作(或将您的指针移至pc13)
flash.s
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
blinker01.c
#define GPIOCBASE 0x40011000
#define RCCBASE 0x40021000
#define RCC_APB2ENR (*((volatile unsigned int *)(RCCBASE+0x18)))
#define GPIO_CONTROL (*((volatile unsigned int *)(GPIOCBASE+0x04)))
#define GPIO_BSRR (*((volatile unsigned int *)(GPIOCBASE+0x10)))
void notmain ( void )
{
volatile unsigned int ra;
RCC_APB2ENR|=1<<4; //enable port c
GPIO_CONTROL=(GPIO_CONTROL&(~0xF<<20))|(0x1<<20);
while(1)
{
GPIO_BSRR=1<<(13+0);
for(ra=0;ra<200000;ra++) continue;
GPIO_BSRR=1<<(13+16);
for(ra=0;ra<200000;ra++) continue;
}
}
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-m3 flash.s -o flash.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -mcpu=cortex-m3 -march=armv7-m -c blinker01.c -o blinker01.o
arm-none-eabi-ld -o blinker01.elf -T flash.ld flash.o blinker01.o
arm-none-eabi-objdump -D blinker01.elf > blinker01.list
arm-none-eabi-objcopy blinker01.elf blinker01.bin -O binary
然后检查列表
08000000 <_start>:
8000000: 20001000 andcs r1, r0, r0
8000004: 08000025 stmdaeq r0, {r0, r2, r5}
8000008: 0800002b stmdaeq r0, {r0, r1, r3, r5}
800000c: 0800002b stmdaeq r0, {r0, r1, r3, r5}
8000010: 0800002b stmdaeq r0, {r0, r1, r3, r5}
8000014: 0800002b stmdaeq r0, {r0, r1, r3, r5}
8000018: 0800002b stmdaeq r0, {r0, r1, r3, r5}
800001c: 0800002b stmdaeq r0, {r0, r1, r3, r5}
8000020: 0800002b stmdaeq r0, {r0, r1, r3, r5}
08000024 <reset>:
8000024: f000 f802 bl 800002c <notmain>
8000028: e7ff b.n 800002a <hang>
0800002a <hang>:
800002a: e7fe b.n 800002a <hang>
向量表必须位于正确的位置。向量必须是1的地址orred(gnu汇编程序中的.thumb_func使下一个标签成为函数,如果我们将该标签用于任何事情,则为我们做一件事)。如果您的二进制文件不是以向量表开头,或者地址是偶数,则游戏结束时您将无法启动。
如果您的工具可以处理,那么二进制文件的英特尔十六进制版本
:020000040800F2
:1000000000100020250000082B0000082B0000082D
:100010002B0000082B0000082B0000082B00000814
:100020002B00000800F002F8FFE7FEE74FF4005550
:1000300000204FF00054154A154E1368154943F03F
:10004000100313603268144B02F07F4242F48012B6
:1000500082B032600D600190019A9A420FD90C6013
:100060000190019A9A42F5D8019A01320192019ABF
:100070009A42F9D90D600190019A9A42EFD8019AFB
:1000800001320192019A9A42F9D9E8E71810024028
:0C00900004100140101001403F0D03005F
:0400000508000000EF
:00000001FF
srecord版同样。
S0110000626C696E6B657230312E73726563CB
S3150800000000100020250000082B0000082B0000081F
S315080000102B0000082B0000082B0000082B00000806
S315080000202B00000800F002F8FFE7FEE74FF4005542
S3150800003000204FF00054154A154E1368154943F031
S31508000040100313603268144B02F07F4242F48012A8
S3150800005082B032600D600190019A9A420FD90C6005
S315080000600190019A9A42F5D8019A01320192019AB1
S315080000709A42F9D90D600190019A9A42EFD8019AED
S3150800008001320192019A9A42F9D9E8E7181002401A
S3110800009004100140101001403F0D030051
S70508000000F2
如果你将你的领导转移到pc13,你可以理想地直接使用其中一个二进制文件。
如果你有openocd,那么telnet到它(telnet localhost 4444)
> halt
> flash write_image erase /path/to/blinker01.srec
> reset
它将开始闪烁led