使用裸机树莓派调用 printf 到 uart 时,应用程序挂起 [英] Application hangs when calling printf to uart with bare metal raspberry pi

查看:26
本文介绍了使用裸机树莓派调用 printf 到 uart 时,应用程序挂起的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 raspberry pi 上实现一个裸机应用程序,并希望将标准输出连接到迷你 uart 以进行调试.

I am trying to implement a bare metal application on the raspberry pi and want to hook up stdout to the mini uart for debugging purposes.

我遵循了此处这里

我创建了一个 uart_putc 函数,它似乎运行良好,允许我将消息打印到 PC 的 COM 端口.然后我实现了 _write 系统调用,使其调用我的 uart_putc 函数进行输出.如果我将单个字符串文字传递给 printf 附加文字参数或任何非文字参数,则串行端口不会打印任何内容,并且在几次调用后,应用程序挂起.

I have created a uart_putc function which seems to work perfectly, allowing me to print messages to my PC's COM port. I then implemented the _write syscall, making it call my uart_putc function for output. This works fine if I pass a single string literal into printf additional literal parameters or any non-literal parameters nothing is printed to the serial port and after a few calls, the application hangs.

有没有人有任何想法可能会出错?如果需要,很乐意提供更多信息...

Does anyone have any ideas what might be going wrong? Happy to provide further info if needed...

void uart_putc(char c)
{
    while(1)
    {
        if(aux[AUX_MU_LSR]&0x20) break;

        led_blink(); // Blink the LED off then on again to 
                     // make sure we aren't stuck in here
    }
    aux[AUX_MU_IO] = c;
}

...

int _write(int file, char* ptr, int len)
{
    int todo;

    for (todo = 0; todo < len; todo++) 
    {
        uart_putc(*ptr++);
    }
    return len;
}

...

while(1)
{
    printf("Hello World\r\n"); // Works
}

while(1)
{
    printf("Hello %s\r\n", "World"); // This doesn't print anything
                                     // and will hang after about five calls
}

char* s = (char*)malloc(sizeof(char) * 100); // Heap or stack it doesn't matter
strcpy(s, "Hello World\r\n");
while(1)
{
    printf(s); // This doesn't print anything
               // and will hang after about five calls
}

while(1)
{
    for (i = 0; i < 13; i++) 
    {
        uart_putc(s[i]);  // Works
    }
}

更新

我正在使用 newlib 并且 _write 在直接调用时可以正常工作.snprintf 似乎表现出同样的问题,即

I am using newlib and _write works correctly when called directly. snprintf seems to exhibit the same problem, i.e.

snprintf(s, 100, "hello world\r\n"); // Works
snprintf(s, 100, "hello %s\r\n", "world"); // Doesn't work

我的 _sbrk 实现是从我的 OP 中引用的页面中删除的

My implementation of _sbrk was nicked from the page referenced in my OP

char *heap_end = 0;
caddr_t _sbrk(int incr) {
    extern char heap_low; /* Defined by the linker */
    extern char heap_top; /* Defined by the linker */
    char *prev_heap_end;

    if (heap_end == 0)
    {
        heap_end = &heap_low;
    }
    prev_heap_end = heap_end;

    if (heap_end + incr > &heap_top)
    {
        /* Heap and stack collision */
        return (caddr_t)0;
    }

    heap_end += incr;
    return (caddr_t) prev_heap_end;
 }

链接脚本

OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
          "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x8000)); . = SEGMENT_START("text-segment", 0x8000);
. = 0x8000;
 .ro : {
  *(.text.startup)
  *(.text)
  *(.rodata)
 }
 .rw : {
  *(.data)
  __bss_start__ = .;
  *(.bss)
  __bss_end__ = .;
  *(COMMON)
 }
 . = ALIGN(8);
 heap_low = .; /* for _sbrk */
 . = . + 0x10000; /* 64kB of heap memory */
 heap_top = .; /* for _sbrk */
 . = . + 0x10000; /* 64kB of stack memory */
 stack_top = .; /* for startup.s */
}

start.s

.section ".text.startup"

.global _start

_start:
    ldr sp, =stack_top

    // The c-startup
    b       _cstartup

_inf_loop:
    b       _inf_loop

更新 2

涉及 snprintf 的进一步实验:

Further experiments involving snprintf:

snprintf(s, 100, "hello world\r\n"); // Works

snprintf(s, 100, "hello %s\r\n", "world"); // Doesn't work
snprintf(s, 100, "hello %d\r\n", 1); // Doesn't work

char s[100];
char t[100];

strcpy(s, "hello world\r\n");
snprintf(t, 100, s); // Doesn't work

推荐答案

很抱歉这么晚才为您解决这个问题.我是 Valvers.com 裸机教程的作者.崩溃的原因是由于我知道但没有时间解决的事情.实际上,我不知道它会解决您的问题.

I'm sorry I'm so late to solve this problem for you. I'm the author of the valvers.com bare metal tutorials. The cause of the crash is due to something I was aware of, but hadn't had time to solve. Actually, I didn't know it would be the solution to your problem.

简而言之,问题在于我们告诉工具链处理器是 ARM1176,更重要的是浮点单元是 VFP,我们应该使用硬浮点 ABI.

In short, the problem is that we're telling the toolchain that the processor is an ARM1176 and more importantly that the floating point unit is VFP, and we should use the hard-float ABI.

使用 VFP 是一个重要的选项 - 这意味着我们选择了也使用此选项编译的 C 库.通常不使用 VFP 指令,因此不会绊倒我们.显然, printf 的部分确实使用了 VFP 指令.

Using the VFP is an important option - it means we pick up the C library that's also been compiled with this option. Generally the VFP instructions aren't used and therefore do not trip us up. Clearly, portions of printf do use VFP instructions.

这让我们感到困惑的原因是,负责生成良好 C 运行时环境的启动汇编程序没有启用 VFP,因此当您到达 VFP 指令时,处理器会跳转到未定义的指令异常向量.

The reason this trips us up is because the startup assembler which is responsible for generating a good C runtime environment doesn't enable VFP, so when you reach a VFP instruction the processor jumps to the undefined instruction exception vector.

我就是这样发现问题的.我简单地启用了任何异常向量中的 LED,并在使用 printf 格式时点亮.然后是删除异常向量中的 LED 调用,直到它不再亮起.这发生在未定义指令"异常中.在 ARM 站点上进行快速搜索显示,如果遇到 VFP 指令且未启用 VFP,处理器将转到此处.因此,它提醒我解决这个问题!

This is how I found out that this was the problem. I simple enabled the LED in any of the exception vectors and it lit when using printf formatting. Then it was a case of removing the LED calls in the exception vectors until it didn't light anymore. This happened in the "Undefined Instruction" exception. A quick search on the ARM site reveals the processor will go here if a VFP instruction is encountered and the VFP is not enabled. Hence, it reminded me to sort that out!

解决方案

您需要做一些事情.您需要将 CMakeLists.txt 文件中的 CMAKE_C_FLAGS 复制到 CMAKE_ASM_FLAGS,以便将正确的选项传递给汇编程序,目前它们不是!我会尽快更新教程来解决这个问题!

There are a few things you need to do. You need to replicate the CMAKE_C_FLAGS to CMAKE_ASM_FLAGS in the CMakeLists.txt file so that the correct options are passed to the assembler, currently they are not! I will update the tutorials as soon as possible to fix this!

在 CMakeLists.txt 文件中最后一个 set( CMAKE_C_FLAGS ... ) 命令下方添加 set( CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS} ) ,因为 CMake 使用gcc 作为汇编程序.

Just below the last set( CMAKE_C_FLAGS ... ) command in the CMakeLists.txt file add set( CMAKE_ASM_FLAGS ${CMAKE_C_FLAGS} ) which works okay because CMake uses gcc as the assembler.

接下来,我们需要修改启动汇编程序文件(在我的教程 armc-nnn-start.S 中)以启用 VFP.在 bl _cstartup

Next, we need to modify the startup assembler file (in my tutorials armc-nnn-start.S) to enable the VFP. Insert the code below just above bl _cstartup

(这直接来自 TI 网站)

// Enable VFP/NEON
// r1 = Access Control Register
MRC p15, #0, r1, c1, c0, #2
// enable full access for p10,11
ORR r1, r1, #(0xf << 20)
// ccess Control Register = r1
MCR p15, #0, r1, c1, c0, #2
MOV r1, #0
// flush prefetch buffer because of FMXR below
MCR p15, #0, r1, c7, c5, #4
// and CP 10 & 11 were only just enabled
// Enable VFP itself
MOV r0,#0x40000000
// FPEXC = r0
FMXR FPEXC, r0

您可以从 ARM 找到有关此的一些信息 这里.

You can find some information from ARM about this here.

这些更改足以使 printf 格式正常工作(我已在 UART 上对其进行了测试).如果您有任何其他问题,请随时提出.

Those changes are enough to get printf formatting working okay (I've tested it on the UART). If you have any further problems, don't hesitate to ask.

最后,很抱歉您因为启动代码不正确而感到悲伤!我最不想做的就是浪费别人的时间!!

Lastly, sorry you've had grief because the startup code is not correct! The last thing I'd want to do is cost someone's time!!

这篇关于使用裸机树莓派调用 printf 到 uart 时,应用程序挂起的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆