iot-fan

联系: iotfan123#163.com
注意:
1,本博客之内容来源于网上收集以及相关技术人员提供,如果有侵犯到您的权益,请电邮我沟通;
2,本博客之内容乃分享,交流,学习,研究之目的,作者不对内容的真实性,有效性,及时性负责,也不对因本博客的任何内容导致的任何后果负责;
3,本博客之内容禁止转发到CSDN网站,转到别的网站请保留出处.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

本文目的

本文将记录如何在沁恒的ble芯片上修改printf的串口重定向,禁用,以及相关的注意事项

适用芯片

  • CH579/ch578/ch577
  • CH573/CH571
  • CH583/CH582/CH581
  • CH592/CH591

printf 中特性

  • 默认冲过UART1出输出,即PA9,波特率115200
  • 半阻塞模式(没有软件缓冲实现,只有芯片UART带的8字节硬件fifo),切记不要滥用,要考虑打印占用的时间
  • SDK中使用PRINT 宏来实现

一,SDK中 printf重定向实现原理

在KEIL中

串口输出

keil 中主要是是需要实现fputc函数进行串口输出,下面是CH579 SDK中的定义:
image

修改串口

初始化串口

#ifdef DEBUG
  GPIOA_SetBits(bTXD1);
  GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);
  UART1_DefInit( );
#endif

修改宏:
image

在mounriver studio中

宏定义

官网的默认例程中,printf 通过PRINT宏来定义:
在CH573SFR.h文件中

#ifdef  DEBUG
#define PRINT(X...) printf(X)
#else
#define PRINT(X...)
#endif

其中这个DEBUG,官方的默认工程在IDE的全局定义中是:
image
这里还可以定义为其他值(CH57x_common.h)

#define Debug_UART0        0
#define Debug_UART1        1
#define Debug_UART2        2
#define Debug_UART3        3

初始化

在main函数中,上来进行串口初始化:

#ifdef DEBUG
  GPIOA_SetBits(bTXD1);
  GPIOA_ModeCfg(bTXD1, GPIO_ModeOut_PP_5mA);
  UART1_DefInit( );
#endif

串口输出

以下定义在文件"CH57x_sys.c"中
其中这里的Debug_UART0-Debug_UART3 对应就是0-3,
实际上printf最终会调用下面的函数,并根据对应的宏对不同的串口寄存器阻塞方式写数据.

#ifdef DEBUG
int _write(int fd, char *buf, int size)
{
    int i;
    for(i=0; i<size; i++)
    {
#if  DEBUG == Debug_UART0
        while( R8_UART0_TFC == UART_FIFO_SIZE );                        /* 等待数据发送 */
        R8_UART0_THR = *buf++;                                               /* 发送数据 */
#elif DEBUG == Debug_UART1
        while( R8_UART1_TFC == UART_FIFO_SIZE );                        /* 等待数据发送 */
        R8_UART1_THR = *buf++;                                               /* 发送数据 */
#elif DEBUG == Debug_UART2
        while( R8_UART2_TFC == UART_FIFO_SIZE );                        /* 等待数据发送 */
        R8_UART2_THR = *buf++;                                               /* 发送数据 */
#elif DEBUG == Debug_UART3
        while( R8_UART3_TFC == UART_FIFO_SIZE );                        /* 等待数据发送 */
        R8_UART3_THR = *buf++;                                               /* 发送数据 */
#endif
    }
    return size;
}

#endif

二,修改printf 的串口

在mounriver studio中

很明显,PRINT 最终是调用
int _write(int fd, char *buf, int size) 函数实现的,
而这个函数里,根据不同的宏,写不同串口的输出寄存器,所以我们修改printf串口只需要两步:

  • 1, main函数一开始的地方,初始化对应的要printf输出串口
  • 2, 在全局宏里面修改 DEBUG的宏的值即可

三,低功耗下的串口打印的注意事项

由于WCH的串口是有8字节的硬件fifo的,由上面的代码我们可以看出,其打印并不会等到硬件为空时候才退出,而在进入睡眠后串口是不能正常工作的,如果此时串口的TX fifo非空,我们就需要等待空了后再睡眠
在WCH 提供的代码的sleep.c 函数中,我们可以看到大概下面的处理方式: (很明显,wch的sdk中只实现了UAR1的的串口处理,其他的我们需要手动添加.)

u32 CH58X_LowPower( u32 time )
{
#if (defined (HAL_SLEEP)) && (HAL_SLEEP == TRUE)
    <...>
#if( DEBUG == Debug_UART1 )  // 使用其他串口输出打印信息需要修改这行代码
    while( ( R8_UART1_LSR & RB_LSR_TX_ALL_EMP ) == 0 )
    {
        __nop();
    }
#endif
    <...>
#endif
  return 0;
}

四,注意事项

  • wch 官网CH573/CH583 的例程中即使把DEBUG全局宏去了,仍然会存在定义为UART0,如果这时候没有初始化UART0,会导致卡在printf 函数里, 解决方法是注释掉"CH57x_common.h" 文件中的 #ifndef DEBUG 下面的 "#define DEBUG Debug_UART0 "
  • 如果是取消printf打印,一定是关闭宏,不可以直接注释掉打印串口的初始化,否则会引起后面调用打印的时候,卡到具体的串口输出函数里
  • wch 的print是阻塞的,纯靠硬件8字节的fifo,打印过多时候,将等待fifo不满时候才继续写入,所以要有节制的利用串口日志,而不是肆无忌惮的去使用,导致整个芯片都在打印,干不了其他事了
  • 提高串口的打印速度是一个很好的使用习惯, 建议大家使用wch的第三代USB转串芯片,最高波特率支持到6Mbps.

其他事项

关于浮点打印

在mounriver studio中

下面图的两种颜色的框,任选其一打勾
image

mounriver studio中的printf实现差异

wch官方提供的SDK里面,printf的实现来自于toolchain内置的newlib-nano库,这个库的的printf会使用堆。
当工程的ram使用非常紧张的时候,堆和栈可能会冲突,从而导致代码异常,为了避免这种情况,我们可以选择wch提供的printf实现

关于一些标准函数诸如sprintf 之类的函数

关于串口打印占用的资源

posted on 2021-05-08 14:06  iot-fan  阅读(3344)  评论(0编辑  收藏  举报