串口scanf函数重定向解决办法

2019-07-20 23:03发布

本帖最后由 夜雨声烦Roc 于 2019-7-8 18:21 编辑

最近学习原子的stm32教程,在串口实验中看到有printf函数的重定向,想着自己写scanf的重定向,用了三天时间,终于解决,现将过程分享如下。
先说最终结论:scanf函数重定向用到fgetc函数,检测USART_IT_RXNE标志位,同时串口中断函数USART1_IRQHandler里也要检测这个标志位,二者冲突,所以scanf函数不好使,串口初始化时不使能中断scanf函数就好使了。
建议:在调试过程中可以Disable中断,用scanf函数,实际使用时使能中断,不用scanf函数。我觉得scanf函数有点鸡肋,明白原理即可。接下来详细说说遇到的问题,供大家参考:重定向fgetc函数,网上有很多种格式,看自己爱好选择
  1. int fgetc(FILE *f)
  2. {
  3.         while((USART1->SR & USART_IT_RXNE) == RESET);
  4.         //while((USART1->SR & 0X20) == RESET);                两个while函数实现的功能相同,哪个顺眼用哪个
  5.         return (int)(USART1->DR);
  6.         //return(int)USART_ReceiveData(USART1);                两个return功能返回值相同,哪个顺眼用哪个
  7. }
复制代码

复制代码此时遇到第一个问题 Error: L6200E: Symbol __stdout multiply defined (by stdio_streams.o and usart.o).stdout重定义,原因是没有勾选use MircoLIB
勾选以后遇到第二个问题Error: L6915E: Library reports error: __use_no_semihosting was requested, but a semihosting fgetc was linked in
原子有一段代码,可以不选择微库(use MircoLIB),既然选择了微库,那就矛盾了,所以我更改代码如下:简单来说就是不用原子这段代码,选择微库。
  1. //加入以下代码,支持printf函数,而不需要选择use MicroLIB         
  2. #if 0
  3. #pragma import(__use_no_semihosting)            
  4. //标准库需要的支持函数                 
  5. struct __FILE
  6. {
  7.         int handle;

  8. };

  9. FILE __stdout;
  10. //FILE __stdin;
  11. //定义_sys_exit()以避免使用半主机模式   
  12. _sys_exit(int x)
  13. {
  14.         x = x;
  15. }

  16. //重定义fputc函数
  17. int fputc(int ch, FILE *f)
  18. {      
  19.         while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
  20.   USART1->DR = (u8) ch;      
  21.         return ch;
  22.         //return (SendChar(ch));  
  23. }

  24. int fgetc(FILE *f)
  25. {        
  26.         while((USART1->SR & USART_FLAG_RXNE) == RESET);
  27.                 return (int)(USART1->DR);

  28. }

  29. #endif

  30. #if 1

  31. //重定向printf函数
  32. int fputc(int ch, FILE *f)
  33. {      
  34.         //USART_SendData(USART1, (uint8_t) ch);
  35.         while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
  36.     USART1->DR = (u8) ch;      
  37.         return ch;
  38. }
  39. int fgetc(FILE *f)
  40. {
  41.         while((USART1->SR & USART_IT_RXNE) == RESET);
  42.         //while((USART1->SR & 0X20) == RESET);                两个while函数实现的功能相同,哪个顺眼用哪个
  43.         return (int)(USART1->DR);
  44.         //return(int)USART_ReceiveData(USART1);                两个return功能返回值相同,哪个顺眼用哪个
  45. }

  46. #endif
复制代码
后来在发现一篇帖子,http://www.eeworld.com.cn/mcu/article_2017092034576.html可以在原子的代码上加一句:
FILE __stdin;
从而达到不用微库,重定向printf和scanf函数的目的。到此,可以用scanf函数来读取了,但是出现新的问题:数据读取不全,且随机变化,且多次输入才能显示一次数据。比如,我想输入12345空格,但是串口读取的可能只有34,下一次是52,再下一次是1435132。。。这个问题困扰了我两天的时间,最终发现:fgetc函数中检测USART_IT_RXNE标志位,同时串口中断函数USART1_IRQHandler里也要检测这个标志位,二者冲突。将串口中断函数Disable后就可以正常使用scnaf函数了。



0条回答

一周热门 更多>