printf函数作为标准库定义的格式化输出方式,本文将介绍其在AWorksLP下默认适配以及重映射至热拔插设备端口的实现。
默认适配
AWorksLP中默认已经对printf函数完成相关适配工作,且默认被适配在UART设备。用户可以在图形化配置界面中使能 support the stdio functions ,并选择期望UART设备进行输出,具体配置如下图所示。
注:若用户未使能 stdio function 时,调用printf函数时,将不会有任何输出。
本文将使用 EPC6450-AWI 平台,选择标有丝印为DUART的调试串口(UART0设备)进行printf功能演示测试。将TTL转USB串口模块的TXD与板子的RXD丝印连接,RXD与板子的TXD丝印,将另一端的USB口接入电脑。启动串口调试助手,搜索并打开串口模块的设备端口号后,在工程中调用printf函数,根据下图可知,printf函数适配UART0设备成功。
重定向至其他设备嵌入式的诸多应用在UART设备资源受限的情况下,可能存在将printf函数重定向到其他设备需求。为此,笔者将以EPC6450-AWI平台的USB串口设备为例进行说明。1. 实施步骤
与UART设备不同,USB设备为动态设备,因此重定向printf函数时,需要注意以下几个关键步骤:
1.1 支持NEWLIB标准库函数
由于AWorksLP中利用posix file相关操作接口对printf函数进行适配,故在重映射端口时,需将 support libc file operations 使能,并取消默认选择UART设备作为printf函数的适配,具体如下图所示。
1.2 检测动态设备
USB设备为动态设备,因此需要持续检测设备的是否存在情况。可通过初始化一个动态设备检测任务,对设备的是否存在情况进行周期性检测。
while true: access (device) delay()
1.3 关联标准文件流
在检测到USB设备存在时,仅需将设备与标准文件流(stdio中的stdin、stdout、stderr,且在C库中被假定为交互设备,并约定了这些设备的文件描述符依次为0、1、2)关联起来。故在使用时,我们仅需将描述符0、1、2与USB串口设备即可,其伪代码如下所示。
while true: if access (device): 0 = open (device) duplicate 1 to 0 duplicate 2 to 0 delay()
1.4 清理文件描述符
检测到USB设备不存在时,需及时取消设备与标准文件流的关联。即根据设备的打开情况,对文件描述符进行清理,以便之后重新关联标准文件流。
while true: if access (device): 0 = open (device) duplicate 1 to 0 duplicate 2 to 0 else: close (device) delay()
2. 基础配置在EPC6450-AWI平台标有丝印为Type-C的接口处,插上Type-C线,将Type-C线的另一端USB口连接电脑。并在图形化配置界面,将USB设备选择为CDC串口设备。
3. 简单示例
static int __dynamic_stdin_fd = -1;static aw_err_t __dynamic_stdout_ret = -AW_EBADF;static aw_err_t __dynamic_stderr_ret = -AW_EBADF;
aw_err_t aw_printf_redirect_dynamic_dev(void){ int find = -AW_ENODEV;
// 检测动态设备 find = aw_access(AW_DYNAMIC_DEV_PATH, AW_F_OK);
if(find == AW_OK) { // 关联标准文件流 if(__dynamic_stdin_fd < 0) { __dynamic_stdin_fd = \ aw_open_at(AW_DYNAMIC_DEV_PATH,AW_O_RDWR,0,0); __dynamic_stdout_ret = aw_dup2(0, 1); __dynamic_stderr_ret = aw_dup2(0, 2); return AW_OK; } } else { // 清理文件描述符 if(__dynamic_stdin_fd >= 0) { aw_close(0); __dynamic_stdin_fd = -1; } if (__dynamic_stdout_ret == AW_OK) { aw_close(1); __dynamic_stdout_ret = -AW_EBADF; } if (__dynamic_stderr_ret == AW_OK) { aw_close(2); __dynamic_stderr_ret = -AW_EBADF; } }
return -AW_ENODEV;}
int aw_main(void){ int ret;
aw_kprintf("hello world\n"); printf("hello world\n");
while(1) { ret = aw_printf_redirect_dynamic_dev(); if (AW_OK == ret) break;
// 设置检测周期 AW_TASK_DELAY(100); }
aw_kprintf("hello world, ZLG\n"); printf("hello world, ZLG\n");
return 0;}
启动串口调试助手,搜索并打开DEBUG UART设备与CDC串口设备的端口号后,运行上文示例程序。根据下图可知,USB设备枚举后,printf函数成功重定向到了CDC串口设备。
总结实现重定向printf函数时主要关注以下两个关键点:
扩展阅读
本文所演示平台使用的是GCC编译器,其对应C库为NEWLIB标准库。在AWorksLP中printf函数的底层输出接口在AWorksLP中实现为_write_r 函数,其具体代码实现如下所示。
__attribute__((__used__)) _ssize_t_write_r(struct _reent *ptr, int fd, const void *buf, size_t nbytes){ return aw_write(fd,buf,nbytes);}
需要值得注意的是,上述适配方式仅兼容NEWLIB,若是其他编译器,其实现以及接口不尽相同,下表仅给出部分以供参考,在使用时需根据实际情况进行调整。
工具链 | 标准库 | 底层接口 |
GCC | NEWLIB | _write_r |
ARMCC | ARMCLIB | _sys_write |
ARMCLANG | ARMCLIB | _sys_write |
全部0条评论
快来发表一下你的评论吧 !