C字符串在循环中无法正确迭代

8

我正在尝试通过Beaglebone Black的UART0打印字符串。引导加载程序初始化了UART0,我只需要写字符。我已经编写了一个简单的函数。

void uart_put(unsigned char c)
{
    while((UART0->LSR_r & 32) == 0);
    UART0->THR = c;     
}

从启动代码调用以下函数,在此函数中我只是打印字符。
#define SIZE_OF_ARRAY 7 
int c_entry(void)
{
unsigned char s_name[SIZE_OF_ARRAY] = "Hello";
unsigned char a_name[SIZE_OF_ARRAY] = {'H','e','l','l','o','\0'};
unsigned int a_int[SIZE_OF_ARRAY] = {9,8,7,6,5,4};
int i = 0;
    for(i = 0; i<6; i++)
    {
      uart_put('0'+i);      /*print value of i*/
      uart_put(9);          /*print a tab*/
      uart_put(s_name[i]);  /*print char from string*/
      uart_put(9);          /*print a tab*/
      uart_put(a_name[i]);  /*print char from array*/
      uart_put(9);          /*print a tab*/
      uart_put('0'+a_int[i]);   /*print int from array*/
      uart_put('\n');
      uart_put('\r');
    }   
    while(1);
    return 0;
}

该函数的字符数组和整数的打印正常,但字符串的打印不正确。

enter image description here

2
不要发布文本图像! - too honest for this site
4
您是如何编译、链接和生成“download.bin”文件的?我怀疑问题可能出现在这个过程中,而不是代码本身(看起来没问题)。可能存在非常微妙的问题,例如堆栈指针与数据重叠,导致局部变量初始化摧毁了它所复制的内容。如果您没有JTAG调试器,另一个技巧就是在代码的各个点上切换一个或多个GPIO,以获得它停滞在什么位置的外部指示(例如,在UART上旋转,出现某些异常等)。 - Notlikethat
2
@Notlikethat 和其他可能知道如何帮助的人,链接脚本、启动代码等可以在聊天中找到。 - The Vee
4
你能比较s_name[i] == a_name[i]来确认字符打印之前的差异吗? - grek40
2
由于这些是可写字符串,它们是建立在堆栈上的,可能堆栈出了问题或编译器注意到它们并没有被写入,所以产生了一些奇怪的情况。如果您交换变量声明的顺序会发生什么,如果您使用不同的字符串(例如用“hello”代替{'w','o','r','l','d',}),会发生什么? - technosaurus
显示剩余24条评论
1个回答

7
在链接脚本中,修改以下内容:
. = 0x402F0400;

to:

. = 0x80000000;

根据AM335X ARM处理器的内存映射0x402F0400是核心SRAM,而0x8000000是DDR2内存。根据TFTP启动输出的截图,您正在将应用程序加载到0x80000000,但您的链接器脚本配置为引用0x402F04000处的内存。

反汇编使用原始链接器脚本构建的二进制文件download.bin结果如下:

% arm-none-eabi-objdump -D -b binary -marm download.bin
download.bin:     file format binary

Disassembly of section .data:

00000000 <.data>:
    0:   e51fd000        ldr     sp, [pc, #-0]   ; 0x8
    4:   eb00000e        bl      0x44
    8:   402f1638        eormi   r1, pc, r8, lsr r6      ; <UNPREDICTABLE>
    c:   e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
    [removed several lines of text]
    4c:   e24dd030        sub     sp, sp, #48     ; 0x30
    50:   e300262c        movw    r2, #1580       ; 0x62c
    54:   e344202f        movt    r2, #16431      ; 0x402f
    [remainder of dump removed]

从内存中将字符串复制到本地变量中。请注意,字符串复制的地址为0x402f062cmovwmovt命令将地址加载到寄存器中)。快速扫描省略的代码未显示任何其他尝试访问内存的操作。
我没有Beaglebone Black,因此无法进行测试。但这解释了观察到的行为。由于程序从未初始化的SRAM中复制字符串,因此不能保证会显示什么内容。由于程序中没有其他尝试访问内存的操作,因此这是唯一的问题。
更改链接脚本并重新构建将生成略有不同的download.bin。原始链接脚本生成的反汇编download.bin与我的建议更改后的版本之间的差异为:
10c10
<    8: 402f1638        eormi   r1, pc, r8, lsr r6      ; <UNPREDICTABLE>
---
>    8: 80001238        andhi   r1, r0, r8, lsr r2
28,29c28,29
<   50: e300262c        movw    r2, #1580       ; 0x62c
<   54: e344202f        movt    r2, #16431      ; 0x402f
---
>   50: e300222c        movw    r2, #556        ; 0x22c
>   54: e3482000        movt    r2, #32768      ; 0x8000

您可以清晰地看到新的地址引用以复制字符串。不幸的是,我不确定另一个更改是什么,以及这个更改是否会导致任何问题。

为了验证字符串位于0x8000022c,我们可以使用xxd来转储download.bin。输出如下:

% xxd download.bin
0000000: 00d0 1fe5 0e00 00eb 3816 2f40 04b0 2de5  ........8./@..-.
0000010: 00b0 8de2 14d0 4de2 0030 a0e1 0d30 4be5  ......M..0...0K.
[... omitted lots of lines ...]
0000220: 0500 53e3 cdff ffda feff ffea 4865 6c6c  ..S.........Hell
0000230: 6f00 0000                                o...

字符串Hello可见于0x22c。当在0x80000000处加载时,复制该字符串的地址似乎是正确的。


1
好棒!期待看看这是否是问题所在。 - Felipe Lavratti
哦!我真是太傻了,我没有想到,谢谢,现在它可以工作了 XD。 - Uint32
恭喜,哇,那真是一个非常棘手的问题!参与的每个人都做得很出色! - The Vee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接