【问题标题】:Unable to use printf() outside of main, unless printf() is used at least once in main无法在 main 之外使用 printf(),除非 printf() 在 main 中至少使用一次
【发布时间】:2019-03-15 17:05:09
【问题描述】:

我正在 TrueSTM Atollic IDE 上调试错误的串行连接。

监视窗口,Expressions 要求我选择一个变量并将其显示为一个数组。缺点是我需要在每次重新编译时重新选择它,对于 +100 值它会变得非常慢,而且我看起来不太清楚。

所以我创建了一个函数,它简单地打印出保存在其中一个缓冲区中的字节,然后将这些字节显示给我到 SWV 控制台。

freertos.c

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "stdbool.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

uint8_t X [4] = {0xFF,0xFF,0xFF,0xFF};
uint8_t * xt = X;

 osThreadDef(CAN_Producer, FrameExtractorNEW, osPriorityNormal, 0, 128);
 defaultTaskHandle = osThreadCreate(osThread(CAN_Producer), NULL);

void FrameExtractorNEW(void const * argument){

            Print_Bytes(xt,4);  // fails
            printf("Cheese\n")  // fails
}

main.c

#include "main.h"
#include "cmsis_os.h"
#include "can.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>

int main(void)
{

  HAL_Init();
  SystemClock_Config();

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_CAN2_Init();
  MX_CAN1_Init();
  MX_USART3_UART_Init();

  MX_FREERTOS_Init();
  osKernelStart();      
  while (1)
  {}
}


// accept pointer to first element, and number of consecutive byte values to display
void Print_Bytes(uint8_t * a1, int bytes_to_read){  
    for (int i = 0; i<bytes_to_read; i++){
        printf("0x%02X " , *(a1+i));        
    }   
}

到目前为止,简单直接的东西,或者看起来如此。

我的问题是,如果我尝试在freertos.c 中使用函数Print_Bytes(),它将起作用,但前提是它至少在main.c 中被调用过一次。在main.c 中使用printf() 打印出任何内容至少一次也可以使其在其他文件的其他位置工作。

如果没有“启用”程序执行将转到 HardFault_Handler() 并出现以下错误。

对于调用它们的函数,我对 printf()Print_Bytes() 有必要的包含,但它似乎不足以让它像我预期的那样工作。

【问题讨论】:

  • 您可能在程序的其他地方调用了undefined behavior。请使用minimal reproducible example 更新您的问题,其他人可以运行它应该会重现该问题。
  • 另请注意,对于任何指针或数组a和索引i,表达式*(a + i)完全等于@987654346 @。后者通常更容易阅读和理解。并且少了几个字符。
  • 这不是关于printf 而是关于一些内存访问冲突。鉴于中止是不精确的(异步),我会说它可能在您的 printfs 被调用之前发生了很多
  • @Eugene 正如你所看到的,人们在猜测。除非 OP 更新他的问题,否则没有人能够回答。
  • 您已经写了几个问题,并且成为会员已有几年了。即使作为发帖人不是很活跃,你仍然应该知道最好不要发布这样的问题。请复习the help pagesthe SO tour。也请重新阅读how to ask good questions,以及this question checklist。最后不要忘记如何创建minimal reproducible example

标签: c debugging printing runtime-error stm32


【解决方案1】:

我敢打赌,第一次运行时的printf 会初始化一些内部变量。在主程序中,它使用主程序堆和堆栈。如果您第一次在任务中使用它,它会在任务堆和堆栈上执行它,并且在这种情况下,任何标准分配都可能不起作用(printf 使用 malloc 函数)。

就我个人而言,我已经编写了自己的 freeRTOS 安全版本的这个函数系列。在多线程环境中使用线程不安全函数是非常糟糕的做法。

【讨论】:

    【解决方案2】:

    您的最小堆大小可能不够大(ld 文件 ide 选项...) 默认库(nano 等 ..)将调用 sbrake" 来拆分堆栈和扩大堆,但这并不意味着要在 rtos 上下文中使用,因此如果堆栈指针不是主堆栈,它将无法工作:(

    因此,如果您在 oskernel 启动之前在 main 内执行第一次打印或 malloc 等操作,它将通过扩大您的堆来帮助(sp 是主堆栈)。 如果您第一次在线程中执行此操作,则不会成功,因为 sbrake 使用的“sp”是 freertos 任务内存而不是“主堆栈”,因此 printf 无法分配内存

    您可能会注意到 printf 依赖或 std lib malloc 不是可重入的,也不是多线程安全的......可以通过 vsnprintf 格式化字符串到任务拥有的缓冲区来完成更安全的格式化打印。

    【讨论】:

      猜你喜欢
      • 2015-06-08
      • 1970-01-01
      • 2012-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多