✅作者简介:热爱科研的嵌入式开发者,修心和技术同步精进
❤欢迎关注我的知乎:对error视而不见
代码获取、问题探讨及文章转载可私信。
☁ 愿你的生命中有够多的云翳,来造就一个美丽的黄昏。
🍎获取更多嵌入式资料可点击链接进群领取,谢谢支持!👇
一、引言
在嵌入式系统开发中,USB(Universal Serial Bus)通信是一种广泛应用的通信方式。STM32微控制器系列提供了强大的USB外设支持,能够实现多种USB通信功能,如USB设备、USB主机等。本文将详细介绍STM32的USB通信,包括基本原理、硬件连接、软件实现以及代码示例。
二、STM32 USB通信基本原理
2.1 USB协议概述
USB是一种通用的串行通信协议,具有高速、可靠、易于使用等特点。USB协议定义了设备和主机之间的通信方式、数据传输格式以及设备的枚举过程。USB设备在连接到主机后,需要经过枚举过程,主机才能识别设备的类型和功能,并为其分配相应的驱动程序。
2.2 STM32 USB外设
STM32微控制器集成了USB外设,不同系列的STM32芯片可能支持不同的USB标准,如USB 2.0。USB外设主要包括USB控制器、端点(Endpoint)和缓冲区等部分。端点是USB通信的基本数据传输单元,每个端点有不同的方向(输入或输出)和类型(控制、批量、中断、同步)。
三、硬件连接
3.1 USB接口
STM32芯片通常具有专门的USB引脚,如D+和D-。这些引脚需要连接到USB接口,常见的USB接口有Mini - USB、Micro - USB等。在连接时,需要注意引脚的电平匹配和信号完整性。
3.2 上拉电阻
对于USB设备模式,需要在D+引脚上连接一个上拉电阻(通常为1.5KΩ),以便主机能够检测到设备的连接。
3.3 电源
USB接口可以为STM32芯片提供电源,同时也需要注意电源的稳定性和滤波处理。
四、软件实现
4.1 开发环境
使用STM32CubeMX工具进行工程配置和初始化代码生成,结合Keil MDK或GCC等编译器进行代码编译和调试。
4.2 配置USB外设
在STM32CubeMX中,选择相应的STM32芯片型号,然后在“Pinout & Configuration”选项卡中配置USB外设。选择USB设备模式,并配置端点的参数,如端点类型、缓冲区大小等。
4.3 生成代码
配置完成后,点击“Generate Code”生成初始化代码。生成的代码包含了USB外设的初始化、中断处理等基本功能。
4.4 编写应用代码
在生成的代码基础上,编写应用代码实现具体的USB通信功能。以下是一个简单的USB设备(虚拟串口)通信的代码示例:
#include "stm32f1xx_hal.h"
#include "usbd_cdc_if.h"
extern USBD_HandleTypeDef hUsbDeviceFS;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USB_DEVICE_Init(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USB_DEVICE_Init();
uint8_t buffer[] = "Hello, USB Communication!\r\n";
uint16_t length = sizeof(buffer);
while (1)
{
if (CDC_Transmit_FS(buffer, length) == USBD_OK)
{
HAL_Delay(1000);
}
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
/*Configure GPIO pin : PA5 */
GPIO_InitStruct.Pin = GPIO_PIN_5;
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);
}
static void MX_USB_DEVICE_Init(void)
{
USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS);
USBD_RegisterClass(&hUsbDeviceFS, &USBD_CDC);
USBD_CDC_RegisterInterface(&hUsbDeviceFS, &USBD_Interface_fops_FS);
USBD_Start(&hUsbDeviceFS);
}
void Error_Handler(void)
{
while(1)
{
}
}
代码解释
- 初始化部分:在
main
函数中,首先调用HAL_Init
进行HAL库的初始化,然后依次调用SystemClock_Config
、MX_GPIO_Init
和MX_USB_DEVICE_Init
进行系统时钟、GPIO和USB设备的初始化。 - 数据发送:定义了一个包含字符串的缓冲区
buffer
,并在主循环中通过CDC_Transmit_FS
函数将数据发送到主机。如果发送成功,程序会延时1秒后再次发送。 - USB设备初始化:在
MX_USB_DEVICE_Init
函数中,调用USBD_Init
初始化USB设备,USBD_RegisterClass
注册USB设备类(这里是虚拟串口类),USBD_CDC_RegisterInterface
注册接口函数,最后调用USBD_Start
启动USB设备。
五、USB设备枚举过程
当USB设备连接到主机后,会经历以下枚举过程:
- 设备连接检测:主机检测到USB设备的连接,通过D+和D-线上的电平变化判断设备的连接状态。
- 复位设备:主机向设备发送复位信号,设备接收到复位信号后进行复位操作,将所有寄存器和端点恢复到初始状态。
- 获取设备描述符:主机向设备发送请求,获取设备的描述符信息,包括设备的厂商ID、产品ID、设备版本等。
- 配置设备:主机根据设备描述符信息,为设备选择合适的配置,并向设备发送配置请求。
- 设备就绪:设备完成配置后,进入就绪状态,可以进行数据传输。
六、注意事项
- 电源管理:USB设备在通信过程中需要注意电源的稳定性,避免因电源波动导致通信错误。
- 数据传输速率:不同的USB设备类和端点类型支持不同的数据传输速率,需要根据实际需求进行选择。
- 中断处理:USB通信涉及到大量的中断处理,需要确保中断服务函数的执行时间尽可能短,避免影响系统的实时性。
七、总结
STM32的USB通信功能为嵌入式系统开发提供了强大的支持。通过合理配置硬件和编写软件代码,可以实现各种USB通信应用,如虚拟串口、USB存储设备等。在开发过程中,需要深入理解USB协议和STM32 USB外设的工作原理,同时注意电源管理、数据传输速率和中断处理等问题,以确保通信的稳定性和可靠性。