1467|7

7047

帖子

11

TA的资源

版主

楼主
 

【CW32L052评测】多个串口重定义实现printf输出 [复制链接]

 

【CW32L052评测】优秀的UART空闲定时器 

一、前言

什么是重定向?重定向是指将fputc里面的输出指向目标设备。因printf函数调用了fputc,而fputc输出有默认指向的目标,且不同库中的fputc输出指向不同,所以需要重写fputc

  1. 标准库实现重定向到串口

若需要printf输出到串口,则需要将fputc里面的输出指向串口,这一过程称为重定向。在我们的CW32L052_StandardPeripheralLib_V1.0的demo中官方给出了我们在mdk下面的串口重定向的示例,路径为\CW32L052_StandardPeripheralLib_V1.0\Utilities。其log.c中定义来设置了板载的UART2为printf输出串口。根据原理图其TXD与RXD分别为PD02与PC12。

在log.c中函数static void SerialInit(uint32_t BaudRate)为初始化指定的波特率的UART2,代码如下:

static void SerialInit(uint32_t BaudRate)

{

uint32_t PCLK_Freq;

GPIO_InitTypeDef GPIO_InitStructure = {0};

UART_InitTypeDef UART_InitStructure = {0};

PCLK_Freq = SystemCoreClock >> pow2_table[CW_SYSCTRL->CR0_f.HCLKPRS];

PCLK_Freq >>= pow2_table[CW_SYSCTRL->CR0_f.PCLKPRS];

// 调试串口使用UART2

// PC12->TX

// PD02<-RX

// 时钟使能

__RCC_GPIOD_CLK_ENABLE();

__RCC_GPIOC_CLK_ENABLE();

__RCC_UART2_CLK_ENABLE();

// 先设置UART TX RX 复用,后设置GPIO的属性,避免口线上出现毛刺

PD02_AFx_UART2RXD();

PC12_AFx_UART2TXD();

GPIO_InitStructure.Pins = GPIO_PIN_12;

GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_Init(CW_GPIOC, &GPIO_InitStructure);

GPIO_InitStructure.Pins = GPIO_PIN_2;

GPIO_InitStructure.Mode = GPIO_MODE_INPUT;

GPIO_Init(CW_GPIOD, &GPIO_InitStructure);

UART_InitStructure.UART_BaudRate = BaudRate;

UART_InitStructure.UART_Over = UART_Over_16;

UART_InitStructure.UART_Source = UART_Source_PCLK;

UART_InitStructure.UART_UclkFreq = PCLK_Freq;

UART_InitStructure.UART_StartBit = UART_StartBit_FE;

UART_InitStructure.UART_StopBits = UART_StopBits_1;

UART_InitStructure.UART_Parity = UART_Parity_No;

UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;

UART_InitStructure.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;

UART_Init(CW_UART2, &UART_InitStructure);

}

函数static void SerialSend(uint8_t Data)的功能为发送一个字节

static void SerialSend(uint8_t Data)

{

UART_SendData_8bit(CW_UART2, Data);

while (UART_GetFlagStatus(CW_UART2, UART_FLAG_TXE) == RESET);

}

【串口重定向】在最后的fputc中重构了标准输出函数。

int fputc(int ch, FILE *f)

{

SerialSend(ch);

return ch;

}

然后重写的pirntf(即write),其代码如下:

size_t __write(int handle, const unsigned char * buffer, size_t size)

{

size_t nChars = 0;

if (buffer == 0)

{

/*

* This means that we should flush internal buffers. Since we

* don't we just return. (Remember, "handle" == -1 means that all

* handles should be flushed.)

*/

return 0;

}

for (/* Empty */; size != 0; --size)

{

SerialSend(*buffer++);

++nChars;

}

return nChars;

}

【注意】

1、调用printf需要先导入stdio.h头文件。

2、在MDK的设置中需要勾选Target中的use MicroLIB选项

EEWORLDIMGTK1

如果我们在gcc的编译环境下,那printf的重定向函数需要修改为:

int _write (int fd, char *pBuffer, int size)

{

size_t nChars = 0;

if (buffer == 0)

{

/*

* This means that we should flush internal buffers. Since we

* don't we just return. (Remember, "handle" == -1 means that all

* handles should be flushed.)

*/

return 0;

}

for (/* Empty */; size != 0; --size)

{

SerialSend(*buffer++);

++nChars;

}

return nChars;

}

以上就是串口重定向固件的串口的实现。

如果我们需要同时操作一个,或者多个串口,比如同时使用UART2来做日志打印,UART1来与WIFI模块通讯,UART3用来与NB-IOT来通信,同时我也需要用printf来组装参数,那么就必需使用到多串口的重定向功能。下面介绍如何实现这个功能的方法:

  1. 为了实现功能,我们需要用到C标准库中的stdarg.h这个库。

stdarg.h 头文件定义了一个变量类型 va_list 和三个宏,这三个宏可用于在参数个数未知(即参数个数可变)时获取函数中的参数。可变参数的函数通在参数列表的末尾是使用省略号(,...)定义的。

其中val_list 是一个适用于 va_start()、va_arg() 和 va_end() 这三个宏存储信息的类型。

  • EEWORLDLINKTK0
    这个宏允许使用了 va_start 宏的带有可变参数的函数返回。如果在从函数返回之前没有调用 va_end,则结果为未定义。
  1. 先定义 需要用到串口的和发送字符串口组数:
    #define UART1_TXBUFF_SIZE 128
    __align(8) char Uart1_TxBuff[UART1_TXBUFF_SIZE];
  2. 新建u1_printf函数,流程为先创建一个valist 变量ap,然后获取可变参数地址 fmt地址赋给ap,使用参数列表发送格式化输出到字符串,函数功能将可变参数格式化输出到一个字符数组。最近通过指定的UART_SendString函数发送到指定的串口。代码如下:

void u1_printf(char* fmt,...)

{

va_list ap;

 

//va_list 可变参数列表,存参数地址

va_start(ap, fmt); //获取可变参数地址 fmt地址赋给ap

 

/*

* 使用参数列表发送格式化输出到字符串,函数功能将可变参数格式化输出到一个字符数组

 

*/

vsprintf(Uart1_TxBuff, fmt, ap);

 

va_end(ap); //清空参数列表

 

UART_SendString(CW_UART1, Uart1_TxBuff);

}

这样,我们可以创建多个u1_prntf的函数来实现多个串口定向的功能了。

【测试】

我们在主程序中添加u1_printf("rcv:%s\r\n",uart1_rx_buff);编译下载后就可以实现基功能了:

最新回复

重定向输出描述符?哈哈。   详情 回复 发表于 2023-7-24 19:43
点赞(1) 关注
 
 

回复
举报

7671

帖子

2

TA的资源

五彩晶圆(高级)

沙发
 

多个串口恐怕不好都定向到 printf 调用吧。

点评

我在这里就是用stdarg.h这个库来实现了重定向的功能,可以用u1_printf来实现。  详情 回复 发表于 2023-7-18 20:32
个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复

7047

帖子

11

TA的资源

版主

板凳
 
freebsder 发表于 2023-7-18 20:26 多个串口恐怕不好都定向到 printf 调用吧。

我在这里就是用stdarg.h这个库来实现了重定向的功能,可以用u1_printf来实现。

 
 
 

回复

6107

帖子

4

TA的资源

版主

4
 
本帖最后由 damiaa 于 2023-7-21 14:21 编辑

void uall_printf(char* fmt,...)

{

....

UART_SendData_8bit(CW_UART1, Data);

UART_SendData_8bit(CW_UART2, Data);

UART_SendData_8bit(CW_UART3, Data);

UART_SendData_8bit(CW_UART4, Data);

while (UART_GetFlagStatus(CW_UART1, UART_FLAG_TXE) == RESET);

while (UART_GetFlagStatus(CW_UART2, UART_FLAG_TXE) == RESET);

while (UART_GetFlagStatus(CW_UART3, UART_FLAG_TXE) == RESET);

while (UART_GetFlagStatus(CW_UART4, UART_FLAG_TXE) == RESET);

往所有串口打印。

点评

66666,这个方法好!  详情 回复 发表于 2023-7-21 14:21
 
 
 

回复

7047

帖子

11

TA的资源

版主

5
 
damiaa 发表于 2023-7-21 14:20 void uall_printf(char* fmt,...) { .... UART_SendData_8bit(CW_UART1, Data); UART_SendData ...

66666,这个方法好!

 
 
 

回复

7671

帖子

2

TA的资源

五彩晶圆(高级)

6
 

我的意思是printf是一个公共接口,u1_printf是定制接口,如果可以用从定向到printf的话,应该是不太好做的。定制的话怎么做都行。

点评

看能不能加个参数,比如在不定长参数中加入一个串口的结构体,应该可以实现。  详情 回复 发表于 2023-7-21 15:52
个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复

7047

帖子

11

TA的资源

版主

7
 
freebsder 发表于 2023-7-21 15:21 我的意思是printf是一个公共接口,u1_printf是定制接口,如果可以用从定向到printf的话,应该是不太好做的 ...

看能不能加个参数,比如在不定长参数中加入一个串口的结构体,应该可以实现。

点评

重定向输出描述符?哈哈。  详情 回复 发表于 2023-7-24 19:43
 
 
 

回复

7671

帖子

2

TA的资源

五彩晶圆(高级)

8
 
lugl4313820 发表于 2023-7-21 15:52 看能不能加个参数,比如在不定长参数中加入一个串口的结构体,应该可以实现。

重定向输出描述符?哈哈。

个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/7 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表