基于stm32的USB模拟UART的实现

基于stm32的USB模拟UART的实现

本文目标:基于stm32的USB模拟UART的实现

按照本文的描述,应该可以在对应的硬件上通实验并举一反三。

先决条件:拥有C语言基础,装有编译和集成的开发环境,比如:Keil uVision5

使用外设:USB、USART1、GPIO

HAL库版本:STM32H5xx HAL Driver version number 1.1.0

STMCubeMX版本:6.10.0

Keil uVision5版本:V5.38.0.0

实验目的

记录项目学习,学习在项目中进行的USB虚拟UART编程,体验串口的高效接收,设计一个实验,实现基于usb模拟的串口实验。

场景使用原理图

在我的应用场景中,原理图的内容如下:

在这里插入图片描述

在这里插入图片描述

基于以上的原理设计,使用usb接口进行接口通讯实验。usb是一个复杂的外设,笔者并没有深入研究,我这里也只是进行简单记录,设计一个实验将USB发来的数据,通过显示屏进行显示。

USBX 组件

使用一个开源的组件来辅助这次的实验。

参考:https://wiki.stmicroelectronics.cn/stm32mcu/wiki/Introduction_to_USBX

关于USBX :USBX 是 Azure®RTOS USB 主机和 USB 设备嵌入式堆栈。它与 ThreadX 紧密耦合。在某些类中,它需要 FileX 和 NetX Duo 堆栈。它允许使用具有多种配置的 USB 设备、复合设备和USB OTG 进行操作。它支持 USB 电源管理。USBX 为 USB 主机和 USB 设备堆栈提供了大量的 USB 类。一旦低级驱动程序能够响应USBX 请求,模块化架构就可以更容易地移植到不同的 USB 硬件 IP 上。所有 STM32 USB IP(主机、设备、OTG、高速和全速)均由 USBX 通过通用 STM32 HAL驱动程序 API 透明支持。

USBX 分为三层,如下图所示:

① 控制器层:最底层,USB 设备控制器的驱动程序,通常是 HAL 库

② stack layer:实现 USB 设备的基本操作,比如描述符的操作、使用 endpoint 进行数据传输

③ Class layer:实现各类 USB 设备的操作,比如 HID 设备、音频设备、虚拟串口,给 APP

提供接口
在这里插入图片描述

在这里插入图片描述

在 STM32 的固件中,可以看到 USBX 目录,比如:

在这里插入图片描述

移植USBX实现虚拟串口

移植 Controller layer、stack layer、Class layer ,重点在于 2 点:

① 怎么初始化硬件以确保 Controller layer 可以正常运行

② 怎么编写 APP:提供设备信息、传输数据

配置USB

在这里插入图片描述

想添加USBX的代码,发现STMCubeMX这里是灰色的,需要依赖另一个操作系统:threadx,所以我选择生成代码,手动从源仓库进行添加。

在这里插入图片描述

移植USBX源码

将仓库中的的源码进行移植。

在这里插入图片描述

工程中添加对应源码

需要添加 USBX 的 3 层源码,按照模板来进行添加,我添加的文件结构如下:

在这里插入图片描述

添加含有“ux_device_class_cdc_acm”前缀的 C 文件,需要先选择目录,然后用搜索的方式来进行添加,进行回车之后然后选择文件添加。

在这里插入图片描述

再仿照下图添加“stack layer”源码,可以从文件名的前面看出它们的作用,比如

“ux_device_stack”表示这是 stack 源码,“ux_utility”表示这是辅助函数,“ux_system”表示是这是系统函数:

在这里插入图片描述

修改usb.c

使用 STM32CubeMX 配置 usb 后生成的 usb.c 里,只是初始化了 USB 控制器,并未启动它,也没有跟 USBX 建立联系,需要修改代码。

在这里插入图片描述

将框起来的函数进行添加。

创建 USBX 任务

使用单独模式( STANDALONE ) 时 , 需 要 创 建 一 个 任 务 , 不 断 运 行

“_ux_system_tasks_run”函数。在默认任务中进行测试

void StartDefaultTask(void *argument)
{
  /* USER CODE BEGIN defaultTask */
  /* Infinite loop */
  for(;;)
  {
    // HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    // vTaskDelay(100);
    ux_system_tasks_run();
  }
  /* USER CODE END defaultTask */
}

将相关的头文件路径添加进来,然后一顿猛的编译,还是报很多错,这个时候需要耐心的一个个解决。

在这里插入图片描述

经过一番的添加对应的文件之后,编译问题解决。

添加使用串口的代码

static void SPILCDTaskFunction( void *pvParameters ) 
{
    char buf[100];
    int cnt = 0;
    
    while (1)
    {
        sprintf(buf, "LCD Task Test : %d", cnt++);
        int ux_device_cdc_acm_send(uint8_t *datas, uint32_t len, uint32_t timeout);
        ux_device_cdc_acm_send((uint8_t *)buf, strlen(buf), 1000);
        vTaskDelay(1000); 
    }
}

在这个文件中usbx\app\ux_device_cdc_acm.c添加如下的代码片段实现接收

static UINT ux_device_class_cdc_acm_read_callback(struct UX_SLAVE_CLASS_CDC_ACM_STRUCT *cdc_acm, UINT status, UCHAR *data_pointer, ULONG length)
{
    
    int Draw_String(uint32_t x, uint32_t y, char *str, uint32_t front_color, uint32_t back_color);
    if (status == UX_SUCCESS)
    {
        data_pointer[length] = '\0';
        Draw_String(0, 0, (char *)data_pointer, 0x0000ff00, 0);
    }
		return 0;
}

上机现象

烧写运行程序后,接上 USB 线,在电脑上可以识别出 USB 串口,查看设备管理器,可以看到如下设备:

在这里插入图片描述

使用串口工具打开这个串口,可以连续不断接收到数据,如下所示:

在这里插入图片描述

同时上位机将数据发送给板子,板子上的显示屏也有对应的数据,此次实验成功。

工程实验成功,后续将会继续记录项目中的实验,感谢关注。

本文中使用的测试工程

https://download.csdn.net/download/weixin_44317448/89237586

  • 19
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
STM32中,我们可以通过使用GPIO模拟UART的方式来打印log。下面是一个简单的例子: 1. 配置GPIO 首先,我们需要选择一个GPIO口,作为模拟UART的TX口。在本例中,我们将使用PA8口。 ```c GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); /*Configure GPIO pin : PA8 */ GPIO_InitStruct.Pin = GPIO_PIN_8; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); ``` 2. 定义打印函数 接下来,我们需要定义一个打印函数,该函数将接收一个字符串并将其转换为模拟UART信号发送到TX口。 ```c void uartLog(char* str) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); // start bit for(int i = 0; i < strlen(str); ++i) { for(int j = 0; j < 8; ++j) { if(str[i] & (1 << j)) { // send bit 1 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); } else { // send bit 0 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET); } HAL_Delay(1); // wait for 1ms } } HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_SET); // stop bit } ``` 3. 使用打印函数 现在,我们可以在代码中使用uartLog函数来打印log了。 ```c uartLog("Hello, world!"); ``` 这就是通过GPIO模拟UART来打印log的简单例子。需要注意的是,该方法的速度较慢,不适合在高速数据传输的场景下使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

独处东汉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值