查看: 82|回复: 0

[教程] 添加串口和虚拟终端输出帮助调试

[复制链接]

36

主题

1

好友

502

积分

举人

Rank: 4

  • TA的每日心情

    2015-8-5 11:09
  • 签到天数: 3 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2017-8-3 16:19:11 |显示全部楼层
    在使用IAR开发STM32项目时,使用串口或者是虚拟终端来输出我们想看的信息是一个非常好而且简便的方式。
    首先来看看串口怎么实现信息输出。简单来说串口输出信息就是将标准输出重定向到串口,在上位机的超级终端或者串口助手等工具中查看结果。
    在使用串口之前要配置串口,这一点是串口应用的基本知识,如果要通过串口输入参数可能还需要使用中断。关于串口配置和终端配置,应根据具体是用来实现。这里主要说一下标准输出的重定向。
    标准库中的printf函数实际调用了int fputc(int ch, FILE *f)函数,所以我们要重载这个函数,实现从串口输出数据。
    1. int fputc(int ch, FILE *f)
    2. {
    3.   while (USART_GetFlagStatus(USARTn, USART_FLAG_TC) == RESET)
    4.   {}

    5.   USART_SendData(USARTn, (uint8_t) ch);

    6.   return ch;
    7. }
    复制代码
    当然,如果想通过串口输入参数还需要从在标准输入函数,如:
    1. int GetKey (void)
    2. {
    3.   while (!(USARTn->SR & USART_FLAG_RXNE));

    4.   return ((int)( USARTn->DR & 0x1FF));
    5. }
    复制代码
    完成以上重载后,在程序中使用printf函数打印信息时,就会从串口输出信息。接到上位软件的画就可以在上位机看到这些信息,便于查看调试。
    接下来我们看一看在IAR虚拟终端中查看输出结果,这个相对要简单得多。只需要做一些必要的设置即可将标准输出重定向到IAR虚拟终端中。
    在开始之前,必须在使用printf函数的的文件中引用stdio.h。这一点毋庸置疑,与任何外部函数的调用是一样的。然后开始设置:
    右键点击项目名称并在弹出菜单中点击“Options”或者在软件“Project”菜单中点击“Options”。
    564295-20161028164557203-591509030.png


    564295-20161028164639453-835388154.png


    弹出Options界面后在左侧列表框中选择“General Options”,在右侧的Tab界面中选择“Library Configuration”标签,如下图红框所示:
    564295-20161028164647515-920570998.png


    在“Library Configuration”标签下,将Library改为全库“Full”,将“stdout/stderr”改为“Via SWO”,如下图红框所示。
    564295-20161028164654718-488229528.png


    在完成上述设置后如果没有其他的输出设置,当在线调试时,打开View菜单中的“Terminal IO”(如下图红框所示),就可在IAR中显示输出了,当然也可以输入数据。
    564295-20161028164734390-35806892.png


    如果在使用IAR虚拟终端时重载了输入输出函数,则不会从IAR虚拟终端输出,每次修改很麻烦,我们可以写一个源文件通过条件编译来实现我们想要的输出方式。
    定义两个宏来控制我们要要的方式:
    1. /*启用调试使用功能代码*/

    2. #ifndef DEBUG_CODE_ENABLE

    3. #define DEBUG_CODE_ENABLE (1)

    4. #endif
    复制代码
    在调试时,将DEBUG_CODE_ENABLE设为1,方便查看,在项目正式发布时则可将DEBUG_CODE_ENABLE设为0将这段代码屏蔽。
    1. /*定义开关串口打印的宏,当为1时使用串口输出,当为0时使用IAR虚拟终端*/

    2. #ifndef USART_PRINT_ENABLE

    3. #define USART_PRINT_ENABLE (0)

    4. #endif
    复制代码
    而在使用时,如果输出的内容数量和种类比较多的话,实现起来比较乱而且可能破环其他部分代码的整体性和可读性。我们可以使用函数指针来集中操作,这样无论是代码还是输出信息的完整性都可以保证,而且实现条件编译也变得更简洁。
    先定义一个打印对象类型的枚举类型和一个打印对象的结构体,如下:
    1. /*定义一个打印对象数据类型的枚举,有新类型是在此添加即可*/

    2. typedef enum{

    3.      pChar,

    4.      pInt8,

    5.      pInt16,

    6.      pInt32,

    7.      pFloat,

    8.      pString

    9. }PrintType;



    10. /*定义一个答应对象的结构体类型*/

    11. typedef struct{

    12.      PrintType printType;

    13.      void * objValue;

    14. }PrintObject;
    复制代码
    再定义一个函数指针数组用于存放打印函数的指针:
    int (*ObjPrintf[])(void *objValue)={CharPrintf,Int8Printf,Int16Printf,Int32Printf,FloatPrintf,StringPrintf};
    并定义一个调用打印的函数:
    /*调试时批量打印输出函数*/
    void DebugOutput(PrintObject * printObject,uint16_t amount);
    对于具体的实现函数根据自己的要求实现就可以了。
    在调用时只需要定义一个要输出的对象数组并将其作为参数传递给DebugOutput汗数据可以了。
    1. /*启用调试辅助代码时,初始化显影的参数*/

    2. #if DEBUG_CODE_ENABLE > (0)

    3.   char title[]="\nThe result is:";

    4.   PrintObject printObject[]={{pString,title},{pFloat,&o2Concentration},{pFloat,&h2Concentration},{pFloat,&ch4Concentration}};



    5.   /*如果启用了串口打印输出功能,则初始化串口配置*/

    6. #if USART_PRINT_ENABLE > (0)

    7.   PrintUsartConfiguration();

    8. #endif



    9. #endif



    10.     /*用于测试*/

    11. #if DEBUG_CODE_ENABLE > (0)

    12.     DebugOutput(printObject,sizeof(printObject)/sizeof(PrintObject));

    13. #endif
    复制代码
    如此就可以非常方便的输出信息、并可根据自己的需要控制是否输出调试信息,采用何种信息输出方式了,对调试来说非常有帮助。

    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    关闭

    站长推荐上一条 /5 下一条

    手机版|爱板网 |网站地图  

    GMT+8, 2017-8-20 00:46 , Processed in 0.070349 second(s), 11 queries , Memcache On.

    苏公网安备 32059002001056号

    Powered by Discuz!

    回顶部