当前位置:   article > 正文

CLion 野火STM32 工程 printf问题_stm32f4 printf重定向 clion

stm32f4 printf重定向 clion

读了一些文章,添加syscalls.c到工程中,放在user文件夹下,syscall.c文件来自之前自己用STM32CubeMX创建的工程目录的Core文件夹下:

拷贝到User文件夹下,刷新CMake, 再次Debug:

提示:undefined reference to `_sbrk'

这份syscalls.c唯独缺了这个函数定义,所以只有这个提示,没有加入syscall.c文件的时候,还会有很多其他函数未定义的提示。

搜索syscalls.c,电脑里好多地方都有,当然都是跟STM32的库和工程有关:

这些syscalls.c 有些有_sbrk函数,有些没有,_sbrk函数定义如下:

  1. caddr_t _sbrk(int incr)
  2. {
  3. extern char end asm("end");
  4. static char *heap_end;
  5. char *prev_heap_end;
  6. if (heap_end == 0)
  7. heap_end = &end;
  8. prev_heap_end = heap_end;
  9. if (heap_end + incr > stack_ptr)
  10. {
  11. // write(1, "Heap and stack collision\n", 25);
  12. // abort();
  13. errno = ENOMEM;
  14. return (caddr_t) -1;
  15. }
  16. heap_end += incr;
  17. return (caddr_t) prev_heap_end;
  18. }

把这个函数添加进去工程里的syscalls.c里面,再次build:

提示:error: 'stack_ptr' undeclared (first use in this function); did you mean 'stack_t'?

回到拷贝的地方,原来的syscalls.c里有声明这个:

  1. /* Variables */
  2. //#undef errno
  3. extern int errno;
  4. extern int __io_putchar(int ch) __attribute__((weak));
  5. extern int __io_getchar(void) __attribute__((weak));
  6. register char * stack_ptr asm("sp");
  7. char *__env[1] = { 0 };
  8. char **environ = __env;
  9. /* Functions */
  10. void initialise_monitor_handles()
  11. {
  12. }
  13. int _getpid(void)
  14. {
  15. return 1;
  16. }

所以再加上这个声明,编译通过:

不过这样,还是没法对串口输出printf的。

接下来,要解决重定向的问题:

用CLION 构建STM32的工程,用的是ARM_GCC, Keil用的是ARMCC

与在keil5中重定义fputs()函数不一样,在GCC编译器中(stm32标准库)需要重定义的是__io_putchar(int ch),而这个函数,在_write中被使用,即syscalls.c中重写了_write()函数。

参考文章【教程】手把手教你用Clion进行STM32开发【如何优雅の进行嵌入式开发】 - 知乎 (zhihu.com)

的   3.10 、更优雅的方法实现串口输入输出流   部分操作(原理可看3.8 / 3.9)

新建retarget.h:

  1. #ifndef UART1_RXTX_RETARGET_H
  2. #define UART1_RXTX_RETARGET_H
  3. #include <stdio.h>
  4. #include "stm32f10x_usart.h"
  5. #ifdef __GNUC__
  6. /* With GCC, small printf (option LD Linker->Libraries->Small printf
  7. set to 'Yes') calls __io_putchar() */
  8. #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
  9. #else
  10. #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
  11. #endif /* __GNUC__ */
  12. #ifdef __GNUC__
  13. #define GETCHAR_PROTOTYPE int __io_getchar (void)
  14. #else
  15. #define GETCHAR_PROTOTYPE int fgetc(FILE * f)
  16. #endif /* __GNUC__ */
  17. /**
  18. * @brief 注册重定向串口
  19. * @param usartx 需要重定向输入输出的串口
  20. */
  21. void RetargetInit(USART_TypeDef *huart);
  22. #endif //UART1_RXTX_RETARGET_H

和retarget.c文件:

  1. #include "retarget.h"
  2. /* 定义USART端口,用于注册重定向的串口(也可以用UART,根据实际情况来改写) */
  3. static USART_TypeDef *sg_retargetUsart;
  4. /**
  5. * @brief 注册重定向串口
  6. * @param usartx 需要重定向输入输出的串口
  7. */
  8. void RetargetInit(USART_TypeDef *usartx) {
  9. /* 注册串口 */
  10. sg_retargetUsart = usartx;
  11. /* Disable I/O buffering for STDOUT stream, so that
  12. * chars are sent out as soon as they are printed. */
  13. setvbuf(stdout, NULL, _IONBF, 0);
  14. /* Disable I/O buffering for STDIN stream, so that
  15. * chars are received in as soon as they are scanned. */
  16. setvbuf(stdin, NULL, _IONBF, 0);
  17. }
  18. /**
  19. * @brief Retargets the C library printf function to the USART.
  20. * @param None
  21. * @retval None
  22. */
  23. PUTCHAR_PROTOTYPE {
  24. /* 发送一个字节数据到串口 */
  25. /* 很简单,直接调用库函数中的 串口发送数据函数 */
  26. USART_SendData(sg_retargetUsart, (uint8_t) ch);
  27. /*等待发送完毕*/
  28. while (USART_GetFlagStatus(sg_retargetUsart, USART_FLAG_TXE) == RESET);
  29. /* 返回发送的字符 */
  30. return ch;
  31. }
  32. /**
  33. * @brief Retargets the C library scanf, getchar function to the USART.
  34. * @param None
  35. * @retval None
  36. */
  37. GETCHAR_PROTOTYPE {
  38. /* 很简单,直接调用库函数中的接收 */
  39. /* 等待串口输入数据 */
  40. while (USART_GetFlagStatus(sg_retargetUsart, USART_FLAG_RXNE) == RESET);
  41. /* 直接返回接收到的字符 */
  42. return (int) USART_ReceiveData(sg_retargetUsart);
  43. }

在main.c文件中包含retarget.h,并调用RetargetInit(DEBUG_USARTx);

  1. #include "stm32f10x.h"
  2. #include "bsp_usart.h"
  3. #include "retarget.h"
  4. /**
  5. * @brief 主函数
  6. * @param 无
  7. * @retval 无
  8. */
  9. int main(void) {
  10. /**
  11. * 注册 USART1 用于重定向
  12. * 当然其他端口也行
  13. */
  14. RetargetInit(DEBUG_USARTx);
  15. /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
  16. USART_Config();
  17. /* 发送一个字符串 */
  18. Usart_SendString(DEBUG_USARTx, "这是一个串口中断接收回显实验\n");
  19. printf("欢迎使用野火STM32开发板\n\n\n\n");
  20. while (1) {
  21. }
  22. }

然而build的时候却出现问题,提示

AR19DD~1.EXE: error: User/Inc/retarget.h: No such file or directory

AR19DD~1.EXE: error: User/Src/retarget.c: No such file or directory

搜索了一遍,参考文章  【Clion】Clion运行C/C++文件报错问题(解决方法二)-CSDN博客

 在CMakeLists.txt中找到  set(LINKER_SCRIPT  这行:

删除后面的 “User/Inc/retarget.h  User/Src/retarget.c” , 变成这样:

再次build,就好了

然而Run程序,下载到开发板,串口USB接电脑,打开野火的串口调试器,却没有任何打印输出。

检查retarget.c,没有加禁用半主机模式,加上#pragma import(__use_no_semihosting)就好了:

  1. #include "retarget.h"
  2. /* 定义USART端口,用于注册重定向的串口(也可以用UART,根据实际情况来改写) */
  3. static USART_TypeDef *sg_retargetUsart;
  4. /* 告知连接器不从C库链接使用半主机的函数 */
  5. /*重要!!!,不然串口没有打印输出*/
  6. #pragma import(__use_no_semihosting)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/535078
推荐阅读
相关标签
  

闽ICP备14008679号