STM32(keil5)使用printf串口收发(微库法)、usb_printf实现USB虚拟串口输出

本文介绍如何在Keil5中通过USART串口及USB虚拟串口实现printf函数的重定向,以便于STM32设备的数据发送。包括配置步骤、代码示例及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、USART串口重定向:

1.阻塞式发送

1.在Keil5中点击上侧的魔法棒,勾选 Use MicroLIB 使用微库。

2.在代码中添加 如下代码

3.在程序中即可通过指定的串口使用printf函数进行串口数据发送!

2.DMA发送

二、USB虚拟串口重定向:

 1.CubeMX配置:

2.程序:


一、USART串口重定向:

1.阻塞式发送

1.在Keil5中点击上侧的魔法棒,勾选 Use MicroLIB 使用微库。

不打开编译会报错或上电后程序不运行!!!

2.在代码中添加 如下代码

#include "stdio.h"
int fputc(int ch, FILE *f)//printf
{
	HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1,0xffff);  //发送一个字节的数据到你希望的串口
	return (ch);
}

int fgetc(FILE * f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1,&ch, 1, 0xffff);
  return ch;
}

更改代码中的&huart后的数字即可更改所需要输出的串口!例如&huart1、&huart2、&huart3……

3.在程序中即可通过指定的串口使用printf函数进行串口数据发送!

2.DMA发送

usart.c

volatile uint8_t  usart_dma_tx_over = 1;

int myprintf(const char *format,...)
{
  va_list arg;
  static char SendBuff[200] = {0};
	int rv;
  
	while(!usart_dma_tx_over);//等待前一次DMA发送完成
	
  va_start(arg,format);
  rv = vsnprintf((char*)SendBuff,sizeof(SendBuff)+1,(char*)format,arg);
  va_end(arg);
 
  HAL_UART_Transmit_DMA(&huart2,(uint8_t *)SendBuff,rv);
	usart_dma_tx_over = 0;//清0全局标志,发送完成后重新置1
	
  return rv;
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart->Instance==USART2)
	{
  		 usart_dma_tx_over = 1;
	}
}

usart.h

#include "stdio.h"
#include "stdarg.h"
#define printf myprintf
int myprintf(const char *format,...);

二、USB虚拟串口重定向:

Win10系统下不需要装任何驱动就能使用USB虚拟串口,但更低版本的系统如:Win7、Win8须要安装ST官方提供的VCP驱动:

https://www.st.com/en/development-tools/stsw-stm32102.html

 1.CubeMX配置:

2.程序:

#include "stdarg.h"
void usb_printf(const char *format, ...)                                             // usb_printf()重定向 
{
    va_list args;
    uint32_t length;

    va_start(args, format);
    length = vsnprintf((char *)UserTxBufferFS, APP_TX_DATA_SIZE, (char *)format, args);
    va_end(args);
    CDC_Transmit_FS(UserTxBufferFS, length);
}

使用时: 

CDC_Transmit_FS("hello\r\n",sizeof("hello\r\n"));
//或
usb_printf("hello\r\n");

可加入此句实现回环发送:

<think>好的,我现在需要帮助用户在Keil uVision4(Keil u4)上为display.c文件编写适用于仿真的代码。首先,我需要回顾用户提供的引用内容,了解如何在Keil中添加.c和.h文件,然后结合仿真需求给出具体的代码示例和建议。 根据引用[1]和[2],用户已经知道如何在Keil中添加新的.c和.h文件。现在的问题是如何编写适用于仿真的display.c代码。仿真的代码通常需要与硬件交互,但可能在没有实际硬件的情况下模拟某些功能,或者通过调试输出观察程序行为。 首先,我需要确定仿真的目标是什么。通常,在Keil中进行仿真可能涉及使用软件模拟器来测试代码逻辑,而不需要实际硬件。因此,display.c可能需要实现一些虚拟的显示功能,比如通过串口输出信息,或者使用调试窗口显示数据。 接下来,考虑代码结构。display.c应该包含相应的函数,比如初始化显示、更新显示内容等。同时,需要对应的display.h文件来声明这些函数,以便其他模块调用。 还需要注意,仿真时可能需要避免直接操作硬件寄存器,而是用模拟的方式替代。例如,如果实际硬件有LED屏幕,仿真时可以用printf输出到调试终端。 然后,代码示例需要包含基本的框架,比如包含必要的头文件,定义全局变量或宏,编写初始化函数和显示更新函数。同时,在仿真中可能需要添加调试信息,方便跟踪程序执行。 另外,用户提到在Keil u4中进行仿真,可能需要配置工程选项,启用软件仿真,并设置正确的目标设备。这部分虽然用户没有直接问,但可能对代码编写有影响,比如设备特定的头文件是否需要包含。 最后,确保代码符合嵌入式开发的最佳实践,比如使用volatile关键字防止编译器优化,处理可能的硬件抽象层等。 现在,我需要将这些思考整理成一个清晰的步骤,包括创建文件、编写代码示例,并解释关键部分的作用。同时,生成相关问题,帮助用户进一步理解和解决问题。</think>### 在Keil uVision4中编写仿真用display.c代码的步骤 #### 1. 创建文件 - **创建display.c和display.h**:按照引用[1]的方,右击目标组选择"Add New item",分别创建`.c`和`.h`文件。 - **文件命名规范**:建议使用英文命名(如`display.c`),避免中文路径问题[^1]。 #### 2. 编写头文件(display.h) ```c #ifndef __DISPLAY_H #define __DISPLAY_H // 显示初始化函数 void Display_Init(void); // 更新显示内容函数 void Display_Update(const char* text); #endif ``` **说明**:头文件使用条件编译防止重复包含,声明对外接口函数[^2]。 #### 3. 编写核心代码(display.c) ```c #include "display.h" #include <stdio.h> // 用于仿真输出 // 虚拟显示缓冲区(仿真用) static char display_buffer[32]; void Display_Init(void) { printf("[仿真] 显示模块初始化完成\n"); // 仿真调试输出 memset(display_buffer, 0, sizeof(display_buffer)); } void Display_Update(const char* text) { strncpy(display_buffer, text, sizeof(display_buffer)-1); printf("[仿真] 显示内容: %s\n", display_buffer); // 通过串口模拟显示 } ``` **关键设计点**: 1. 使用`printf`替代实际硬件操作,输出到调试窗口 2. 添加仿真标识`[仿真]`便于区分真实硬件输出 3. 缓冲区设计模拟真实显示设备的帧缓存机制 #### 4. 配置工程选项 - 在`Options for Target`中: - 选择`Use Simulator`(启用软件仿真) - 在`Debug`选项卡启用`Run to main()` - 在`Target`选项卡选择正确芯片型号 #### 5. 编写测试代码(main.c) ```c #include "display.h" int main(void) { Display_Init(); while(1) { Display_Update("Temp:25C"); for(int i=0; i<1000000; i++); // 简单延时 } } ``` #### 6. 查看仿真结果 - 编译后进入调试模式(Ctrl+F5) - 打开`View -> Serial Windows -> UART #1`查看仿真输出 - 预期看到周期性输出显示内容更新信息 ### 仿真代码设计原则 1. **硬件抽象**:通过宏定义隔离硬件依赖 ```c #ifdef SIMULATION #define DISPLAY_OUTPUT(text) printf("[SIM] %s\n", text) #else #define DISPLAY_OUTPUT(text) LCD_ShowString(text) #endif ``` 2. **时序模拟**:使用循环延时代替硬件定时器 3. **状态可视化**:通过调试接口输出关键变量值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值