编程技术分享

分享编程知识,探讨技术创新

0%

简介:最近迷上了攻城狮工具的制作,因手头有个潘多拉开发板,板载一个STLINK/V2.1,这玩意支持STM32调试,还带了一个虚拟串口和虚拟U盘下载,所以用立创EDA打造了一个小巧的STLINK/V2.1

好的,作为一名高级嵌入式软件开发工程师,很高兴能和你一起探讨这个基于潘多拉开发板和立创EDA打造的STLINK/V2.1项目。这是一个非常棒的实践项目,它涵盖了嵌入式系统开发的多个关键环节。下面我将详细阐述最适合这个项目的代码设计架构,并提供具体的C代码实现,同时还会介绍项目中可能采用的各种技术和方法。
关注微信公众号,提前获取相关推文

项目背景与需求分析

首先,我们需要明确STLINK/V2.1的功能,它主要用于STM32微控制器的调试和固件编程,同时还具备虚拟串口和虚拟U盘功能。基于这些功能,我们可以将项目需求概括如下:

  1. STM32 调试功能 (SWD/JTAG):

    • 支持通过SWD或JTAG接口与STM32目标芯片进行通信。
    • 实现断点设置、单步调试、寄存器查看、内存查看等调试功能。
    • 支持固件下载到目标芯片的Flash存储器。
  2. 虚拟串口 (VCP - Virtual COM Port):

    • 通过USB接口模拟一个串口,方便上位机与目标芯片进行串口通信。
    • 可以用于日志输出、数据传输等应用。
  3. 虚拟U盘 (MSC - Mass Storage Class):

    • 通过USB接口模拟一个U盘,用于固件下载和文件传输。
    • 允许用户将固件文件拖拽到虚拟U盘中,实现自动固件编程。
  4. 可靠性、高效性、可扩展性:

    • 系统需要稳定可靠地运行,避免调试过程中出现错误或崩溃。
    • 代码需要高效,保证调试和数据传输的实时性。
    • 系统架构需要具有良好的可扩展性,方便后续功能扩展和维护升级。

代码设计架构:分层架构与模块化设计

为了构建一个可靠、高效、可扩展的嵌入式系统平台,我推荐采用分层架构模块化设计相结合的方式。这种架构能够将复杂的系统分解为多个独立的模块,降低代码的耦合性,提高代码的可维护性和可重用性。

1. 分层架构:

我们可以将整个系统划分为以下几个层次:

  • 硬件抽象层 (HAL - Hardware Abstraction Layer): 这一层直接与硬件交互,提供对底层硬件资源的统一访问接口。例如,GPIO、USB、SPI、I2C、UART 等外设的驱动函数。HAL 层的主要目的是屏蔽硬件差异,使上层应用代码可以独立于具体的硬件平台。

  • 板级支持包 (BSP - Board Support Package): 这一层位于 HAL 层之上,为特定的开发板提供支持。BSP 层通常包含板级的初始化代码、时钟配置、中断配置、以及特定板载外设的驱动。在这个项目中,BSP 层需要针对潘多拉开发板进行适配。

  • USB 设备驱动层 (USB Device Driver Layer): 这一层负责处理 USB 协议的细节,包括 USB 设备枚举、配置、数据传输等。我们需要实现 USB CDC (虚拟串口) 类驱动和 USB MSC (大容量存储) 类驱动,以及 USB HID (人机接口设备) 类驱动 (可选,用于一些控制功能)。

  • ST-LINK 功能层 (ST-LINK Function Layer): 这一层实现 ST-LINK/V2.1 的核心功能,包括:

    • 调试协议处理模块 (Debug Protocol Handler): 负责解析和处理 ST-LINK 调试协议,例如 SWD/JTAG 命令的解析和执行。
    • 虚拟串口模块 (Virtual COM Port Module): 处理虚拟串口的数据收发。
    • 虚拟 U 盘模块 (Virtual USB Drive Module): 处理虚拟 U 盘的文件系统操作和固件编程逻辑。
  • 应用层 (Application Layer): 这是最顶层,负责系统的初始化、任务调度、以及用户交互逻辑。在这个项目中,应用层主要负责初始化各个模块,并根据 USB 事件和调试指令调度各个功能模块的运行。

2. 模块化设计:

在每一层内部,我们还需要进行模块化设计,将功能进一步细分到不同的模块中。例如,在 USB 设备驱动层,我们可以分为 USB Core 模块、USB CDC 模块、USB MSC 模块等。在 ST-LINK 功能层,我们可以分为调试协议解析模块、SWD/JTAG 通信模块、虚拟串口数据处理模块、虚拟 U 盘文件系统模块等。

代码实现 (C 语言)

下面我将提供一些关键模块的 C 代码示例,由于代码量庞大,我将重点展示框架结构和核心功能实现,并提供详细的注释。为了达到 3000 行代码的要求,我会尽可能详细地展开,并包含一些常用的辅助函数和数据结构。

1. 硬件抽象层 (HAL)

hal_gpio.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>
#include <stdbool.h>

// 定义 GPIO 端口和引脚
typedef enum {
GPIO_PORT_A,
GPIO_PORT_B,
GPIO_PORT_C,
// ... 可以根据具体 MCU 扩展
GPIO_PORT_MAX
} GPIO_PortTypeDef;

typedef enum {
GPIO_PIN_0 = (1U << 0),
GPIO_PIN_1 = (1U << 1),
GPIO_PIN_2 = (1U << 2),
GPIO_PIN_3 = (1U << 3),
GPIO_PIN_4 = (1U << 4),
GPIO_PIN_5 = (1U << 5),
GPIO_PIN_6 = (1U << 6),
GPIO_PIN_7 = (1U << 7),
GPIO_PIN_8 = (1U << 8),
GPIO_PIN_9 = (1U << 9),
GPIO_PIN_10 = (1U << 10),
GPIO_PIN_11 = (1U << 11),
GPIO_PIN_12 = (1U << 12),
GPIO_PIN_13 = (1U << 13),
GPIO_PIN_14 = (1U << 14),
GPIO_PIN_15 = (1U << 15),
GPIO_PIN_ALL = 0xFFFFU
} GPIO_PinTypeDef;

// 定义 GPIO 工作模式
typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF, // Alternate Function
GPIO_MODE_ANALOG
} GPIO_ModeTypeDef;

// 定义 GPIO 输出类型
typedef enum {
GPIO_OUTPUT_PP, // Push-Pull
GPIO_OUTPUT_OD // Open-Drain
} GPIO_OutputTypeTypeDef;

// 定义 GPIO 上下拉电阻
typedef enum {
GPIO_PULL_NONE,
GPIO_PULL_UP,
GPIO_PULL_DOWN
} GPIO_PullTypeDef;

// 定义 GPIO 初始化结构体
typedef struct {
GPIO_PinTypeDef Pin; // 指定要配置的引脚
GPIO_ModeTypeDef Mode; // 指定工作模式
GPIO_OutputTypeTypeDef OutputType; // 指定输出类型 (仅输出模式有效)
GPIO_PullTypeDef Pull; // 指定上下拉电阻
uint32_t Speed; // 指定速度 (可选,根据 MCU 定义)
uint32_t Alternate; // 指定复用功能 (仅 AF 模式有效)
} GPIO_InitTypeDef;

// GPIO 初始化函数
void HAL_GPIO_Init(GPIO_PortTypeDef Port, GPIO_InitTypeDef *GPIO_Init);

// 设置 GPIO 引脚输出电平
void HAL_GPIO_WritePin(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin, bool PinState);

// 读取 GPIO 引脚输入电平
bool HAL_GPIO_ReadPin(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);

// 切换 GPIO 引脚输出电平
void HAL_GPIO_TogglePin(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);

#endif // HAL_GPIO_H

hal_gpio.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "hal_gpio.h"
// 假设我们使用 STM32 的 HAL 库,这里只是示例代码
// 实际项目中需要根据具体的 MCU 平台进行适配

// GPIO 初始化函数
void HAL_GPIO_Init(GPIO_PortTypeDef Port, GPIO_InitTypeDef *GPIO_Init) {
// ... 根据 Port 和 GPIO_Init 配置 GPIO 寄存器
// 例如,配置 GPIO 模式、输出类型、上下拉电阻、速度、复用功能等
// 这里只是一个占位符,实际需要根据 MCU 的寄存器操作进行实现
(void)Port; // 避免编译器警告 unused parameter
(void)GPIO_Init;
// ... 实际的 GPIO 初始化代码
}

// 设置 GPIO 引脚输出电平
void HAL_GPIO_WritePin(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin, bool PinState) {
// ... 根据 Port 和 Pin 设置 GPIO 输出数据寄存器
// 例如,设置 ODxR 寄存器
(void)Port;
(void)Pin;
(void)PinState;
// ... 实际的 GPIO 输出控制代码
}

// 读取 GPIO 引脚输入电平
bool HAL_GPIO_ReadPin(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
// ... 根据 Port 和 Pin 读取 GPIO 输入数据寄存器
// 例如,读取 IDxR 寄存器
(void)Port;
(void)Pin;
// ... 实际的 GPIO 输入读取代码
return false; // 占位符
}

// 切换 GPIO 引脚输出电平
void HAL_GPIO_TogglePin(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
// ... 读取当前输出电平,然后取反,再设置输出
(void)Port;
(void)Pin;
// ... 实际的 GPIO 电平切换代码
}

hal_usb.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef HAL_USB_H
#define HAL_USB_H

#include <stdint.h>
#include <stdbool.h>

// 定义 USB 相关类型和函数
typedef enum {
USB_SPEED_FULL,
USB_SPEED_HIGH,
USB_SPEED_LOW
} USB_SpeedTypeDef;

typedef enum {
USB_EP_TYPE_CTRL,
USB_EP_TYPE_ISO,
USB_EP_TYPE_BULK,
USB_EP_TYPE_INTR
} USB_EndpointTypeTypeDef;

typedef struct {
uint8_t Address; // 端点地址
USB_EndpointTypeTypeDef Type; // 端点类型
uint16_t MaxPacketSize; // 最大包大小
uint8_t Direction; // 方向 (IN/OUT)
} USB_EndpointInitTypeDef;

// USB 初始化函数
bool HAL_USB_Init(USB_SpeedTypeDef Speed);

// USB 端点配置函数
bool HAL_USB_Endpoint_Config(USB_EndpointInitTypeDef *EndpointInit);

// USB 发送数据
bool HAL_USB_Transmit(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size);

// USB 接收数据
bool HAL_USB_Receive(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size);

// USB 中断处理函数 (需要在中断服务例程中调用)
void HAL_USB_IRQHandler(void);

#endif // HAL_USB_H

hal_usb.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include "hal_usb.h"
// 假设使用 STM32 的 USB 外设,这里仅为示例

// USB 初始化函数
bool HAL_USB_Init(USB_SpeedTypeDef Speed) {
// ... 初始化 USB PHY, 使能 USB 时钟,配置 USB 相关寄存器
(void)Speed;
// ... 实际 USB 初始化代码
return true;
}

// USB 端点配置函数
bool HAL_USB_Endpoint_Config(USB_EndpointInitTypeDef *EndpointInit) {
// ... 配置 USB 端点寄存器,例如 EPxCSR, EPxCOUNT, EPxADDR 等
(void)EndpointInit;
// ... 实际 USB 端点配置代码
return true;
}

// USB 发送数据
bool HAL_USB_Transmit(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size) {
// ... 将数据写入 USB 发送 FIFO,并触发发送
(void)EndpointAddress;
(void)pData;
(void)Size;
// ... 实际 USB 数据发送代码
return true;
}

// USB 接收数据
bool HAL_USB_Receive(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size) {
// ... 从 USB 接收 FIFO 读取数据
(void)EndpointAddress;
(void)pData;
(void)Size;
// ... 实际 USB 数据接收代码
return true;
}

// USB 中断处理函数
void HAL_USB_IRQHandler(void) {
// ... 处理 USB 中断事件,例如端点中断、复位中断、挂起中断等
// ... 在中断处理函数中,需要调用 USB 设备驱动层提供的回调函数
// ... 以通知上层 USB 事件
// ... 例如: USB_Device_IRQHandler(); // 调用 USB 设备驱动层的中断处理
}

2. 板级支持包 (BSP)

bsp_pandora.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef BSP_PANDORA_H
#define BSP_PANDORA_H

#include "hal_gpio.h"
#include "hal_usb.h"

// 板级初始化函数
void BSP_Init(void);

// 系统时钟配置函数
void BSP_SystemClock_Config(void);

// LED 初始化和控制函数
void BSP_LED_Init(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);
void BSP_LED_On(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);
void BSP_LED_Off(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);
void BSP_LED_Toggle(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);

// 按键初始化和读取函数
void BSP_Button_Init(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);
bool BSP_Button_GetState(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin);

// ST-LINK 相关引脚定义 (假设使用 GPIO 控制 ST-LINK 接口)
#define STLINK_SWCLK_PORT GPIO_PORT_A
#define STLINK_SWCLK_PIN GPIO_PIN_5
#define STLINK_SWDIO_PORT GPIO_PORT_A
#define STLINK_SWDIO_PIN GPIO_PIN_6
#define STLINK_NRST_PORT GPIO_PORT_A
#define STLINK_NRST_PIN GPIO_PIN_7

// 初始化 ST-LINK 接口引脚
void BSP_STLINK_Interface_Init(void);

#endif // BSP_PANDORA_H

bsp_pandora.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include "bsp_pandora.h"

// 板级初始化函数
void BSP_Init(void) {
BSP_SystemClock_Config(); // 配置系统时钟
// ... 初始化其他板载外设,例如 UART, SPI, I2C 等 (如果需要)
BSP_LED_Init(GPIO_PORT_B, GPIO_PIN_0); // 初始化 LED
BSP_Button_Init(GPIO_PORT_C, GPIO_PIN_13); // 初始化 按键
BSP_STLINK_Interface_Init(); // 初始化 ST-LINK 接口
}

// 系统时钟配置函数 (示例,需要根据具体的 MCU 和时钟配置进行修改)
void BSP_SystemClock_Config(void) {
// ... 配置系统时钟,例如 HSE 振荡器使能,PLL 配置,时钟分频等
// ... 确保系统时钟频率满足 USB 和其他外设的要求
// ... 具体的时钟配置代码需要根据 MCU 的时钟树和寄存器进行编写
}

// LED 初始化函数
void BSP_LED_Init(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.OutputType = GPIO_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULL_NONE;
GPIO_InitStruct.Speed = 0; // 可根据需要调整速度
HAL_GPIO_Init(Port, &GPIO_InitStruct);
BSP_LED_Off(Port, Pin); // 初始状态熄灭
}

// LED 控制函数
void BSP_LED_On(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
HAL_GPIO_WritePin(Port, Pin, true); // 假设高电平点亮 LED
}

void BSP_LED_Off(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
HAL_GPIO_WritePin(Port, Pin, false);
}

void BSP_LED_Toggle(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
HAL_GPIO_TogglePin(Port, Pin);
}

// 按键初始化函数
void BSP_Button_Init(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULL_UP; // 假设默认高电平,按下低电平
HAL_GPIO_Init(Port, &GPIO_InitStruct);
}

// 读取按键状态
bool BSP_Button_GetState(GPIO_PortTypeDef Port, GPIO_PinTypeDef Pin) {
return !HAL_GPIO_ReadPin(Port, Pin); // 按下返回 true
}

// 初始化 ST-LINK 接口引脚
void BSP_STLINK_Interface_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// SWCLK
GPIO_InitStruct.Pin = STLINK_SWCLK_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF; // 复用功能
GPIO_InitStruct.OutputType = GPIO_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULL_NONE;
GPIO_InitStruct.Speed = 0; // 可根据需要调整速度
GPIO_InitStruct.Alternate = 0; // 需要根据 MCU 的 AF 定义配置
HAL_GPIO_Init(STLINK_SWCLK_PORT, &GPIO_InitStruct);

// SWDIO
GPIO_InitStruct.Pin = STLINK_SWDIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF; // 复用功能
GPIO_InitStruct.OutputType = GPIO_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULL_UP; // 上拉,避免浮空
GPIO_InitStruct.Speed = 0;
GPIO_InitStruct.Alternate = 0; // 需要根据 MCU 的 AF 定义配置
HAL_GPIO_Init(STLINK_SWDIO_PORT, &GPIO_InitStruct);

// NRST (可选,如果需要硬件复位功能)
GPIO_InitStruct.Pin = STLINK_NRST_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT; // 输出模式,控制 NRST 信号
GPIO_InitStruct.OutputType = GPIO_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULL_UP; // 上拉,默认高电平
GPIO_InitStruct.Speed = 0;
HAL_GPIO_Init(STLINK_NRST_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(STLINK_NRST_PORT, STLINK_NRST_PIN, true); // 初始高电平,不复位
}

3. USB 设备驱动层

usb_device.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#ifndef USB_DEVICE_H
#define USB_DEVICE_H

#include <stdint.h>
#include <stdbool.h>

// USB 设备描述符结构体 (简化示例)
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdUSB;
uint8_t bDeviceClass;
uint8_t bDeviceSubClass;
uint8_t bDeviceProtocol;
uint8_t bMaxPacketSize0;
uint16_t idVendor;
uint16_t idProduct;
uint16_t bcdDevice;
uint8_t iManufacturer;
uint8_t iProduct;
uint8_t iSerialNumber;
uint8_t bNumConfigurations;
} USB_DeviceDescriptorTypeDef;

// USB 配置描述符结构体 (简化示例)
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t wTotalLength;
uint8_t bNumInterfaces;
uint8_t bConfigurationValue;
uint8_t iConfiguration;
uint8_t bmAttributes;
uint8_t bMaxPower;
} USB_ConfigurationDescriptorTypeDef;

// USB 接口描述符结构体 (简化示例)
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bNumEndpoints;
uint8_t bInterfaceClass;
uint8_t bInterfaceSubClass;
uint8_t bInterfaceProtocol;
uint8_t iInterface;
} USB_InterfaceDescriptorTypeDef;

// USB 端点描述符结构体 (简化示例)
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bEndpointAddress;
uint8_t bmAttributes;
uint16_t wMaxPacketSize;
uint8_t bInterval;
} USB_EndpointDescriptorTypeDef;


// USB 设备初始化函数
bool USB_Device_Init(void);

// USB 处理控制传输函数 (Setup Stage)
bool USB_Device_CtlRequestHandler(void);

// USB 数据发送函数 (用于 Control Endpoint)
bool USB_Device_CtlSendData(uint8_t *pData, uint16_t Size);

// USB 数据接收函数 (用于 Control Endpoint)
bool USB_Device_CtlReceiveData(uint8_t *pData, uint16_t Size);

// USB 发送数据函数 (用于 Bulk/Interrupt/Iso Endpoint)
bool USB_Device_SendData(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size);

// USB 接收数据函数 (用于 Bulk/Interrupt/Iso Endpoint)
bool USB_Device_ReceiveData(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size);

// USB 中断处理函数 (供 HAL 层调用)
void USB_Device_IRQHandler(void);

#endif // USB_DEVICE_H

usb_device.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#include "usb_device.h"
#include "hal_usb.h" // 引用 HAL 层 USB 驱动
#include "usb_cdc.h" // 引用 USB CDC 类驱动
#include "usb_msc.h" // 引用 USB MSC 类驱动

// USB 设备描述符
const USB_DeviceDescriptorTypeDef USB_DeviceDescriptor = {
.bLength = sizeof(USB_DeviceDescriptorTypeDef),
.bDescriptorType = 0x01, // 设备描述符类型
.bcdUSB = 0x0200, // USB 版本 2.0
.bDeviceClass = 0x00, // 在接口描述符中定义类
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
.bMaxPacketSize0 = 64, // 端点 0 最大包大小
.idVendor = 0xABCD, // 厂商 ID (需要注册)
.idProduct = 0x1234, // 产品 ID (自定义)
.bcdDevice = 0x0100, // 设备版本 1.0
.iManufacturer = 0x01, // 厂商字符串索引
.iProduct = 0x02, // 产品字符串索引
.iSerialNumber = 0x03, // 序列号字符串索引
.bNumConfigurations = 0x01 // 配置数量
};

// USB 配置描述符 (包含 CDC 和 MSC 接口)
const uint8_t USB_ConfigurationDescriptor[] = {
// 配置描述符
0x09, // bLength: 描述符长度
0x02, // bDescriptorType: 配置描述符类型
0xC9, // wTotalLength: 总长度 (包括所有接口、端点描述符)
0x00,
0x02, // bNumInterfaces: 接口数量 (CDC + MSC)
0x01, // bConfigurationValue: 配置值
0x04, // iConfiguration: 配置字符串索引
0xC0, // bmAttributes: 属性 (总线供电, 自供电)
0x32, // bMaxPower: 最大功率 (单位 2mA, 100mA)

// CDC 接口关联描述符 (Interface Association Descriptor)
0x08, // bLength
0x0B, // bDescriptorType
0x00, // bFirstInterface
0x02, // bInterfaceCount
0x02, // bFunctionClass (CDC)
0x02, // bFunctionSubClass (ACM)
0x01, // bFunctionProtocol (AT commands)
0x00, // iFunction

// CDC 控制接口描述符
0x09, // bLength
0x04, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x02, // bInterfaceClass (CDC)
0x02, // bInterfaceSubClass (ACM)
0x01, // bInterfaceProtocol (AT commands)
0x05, // iInterface

// CDC 功能描述符 (Header Functional Descriptor)
0x05, // bLength
0x24, // bDescriptorType (CS_INTERFACE)
0x00, // bDescriptorSubType (Header Functional)
0x10, // bcdCDC (版本 1.10)
0x01,

// CDC 功能描述符 (Call Management Functional Descriptor)
0x05, // bLength
0x24, // bDescriptorType (CS_INTERFACE)
0x01, // bDescriptorSubType (Call Management Functional)
0x00, // bmCapabilities (无呼叫管理)
0x01, // bDataInterface

// CDC 功能描述符 (Abstract Control Management Functional Descriptor)
0x04, // bLength
0x24, // bDescriptorType (CS_INTERFACE)
0x02, // bDescriptorSubType (Abstract Control Management Functional)
0x02, // bmCapabilities (支持 SET_LINE_CODING, GET_LINE_CODING, SET_CONTROL_LINE_STATE, GET_CONTROL_LINE_STATE 请求)

// CDC 功能描述符 (Union Functional Descriptor)
0x05, // bLength
0x24, // bDescriptorType (CS_INTERFACE)
0x06, // bDescriptorSubType (Union Functional)
0x00, // bMasterInterface (控制接口)
0x01, // bSlaveInterface0 (数据接口)

// CDC 控制端点描述符 (Interrupt IN Endpoint)
0x07, // bLength
0x05, // bDescriptorType
0x81, // bEndpointAddress (IN, 端点 1)
0x03, // bmAttributes (Interrupt Endpoint)
0x0A, // wMaxPacketSize (10 bytes)
0x00,
0xFF, // bInterval (255 ms)

// CDC 数据接口描述符
0x09, // bLength
0x04, // bDescriptorType
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x0A, // bInterfaceClass (CDC Data)
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface

// CDC 数据端点描述符 (Bulk OUT Endpoint)
0x07, // bLength
0x05, // bDescriptorType
0x02, // bEndpointAddress (OUT, 端点 2)
0x02, // bmAttributes (Bulk Endpoint)
0x40, // wMaxPacketSize (64 bytes)
0x00,
0x00, // bInterval (Not used for Bulk)

// CDC 数据端点描述符 (Bulk IN Endpoint)
0x07, // bLength
0x05, // bDescriptorType
0x83, // bEndpointAddress (IN, 端点 3)
0x02, // bmAttributes (Bulk Endpoint)
0x40, // wMaxPacketSize (64 bytes)
0x00,
0x00, // bInterval (Not used for Bulk)

// MSC 接口描述符
0x09, // bLength
0x04, // bDescriptorType
0x02, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x08, // bInterfaceClass (MSC)
0x06, // bInterfaceSubClass (SCSI transparent command set)
0x50, // bInterfaceProtocol (Bulk-Only Transport)
0x06, // iInterface

// MSC 端点描述符 (Bulk OUT Endpoint)
0x07, // bLength
0x05, // bDescriptorType
0x04, // bEndpointAddress (OUT, 端点 4)
0x02, // bmAttributes (Bulk Endpoint)
0x40, // wMaxPacketSize (64 bytes)
0x00,
0x00, // bInterval (Not used for Bulk)

// MSC 端点描述符 (Bulk IN Endpoint)
0x07, // bLength
0x05, // bDescriptorType
0x85, // bEndpointAddress (IN, 端点 5)
0x02, // bmAttributes (Bulk Endpoint)
0x40, // wMaxPacketSize (64 bytes)
0x00,
0x00 // bInterval (Not used for Bulk)
};

// USB 字符串描述符
const uint8_t USB_StringDescriptor[] = {
// 语言 ID (US-English)
0x04, 0x03, 0x09, 0x04,

// 厂商字符串
0x14, 0x03, 'O', 0x00, 'M', 0x00, 'E', 0x00, 'D', 0x00, 'A', 0x00, ' ', 0x00, 'E', 0x00, 'D', 0x00, 'A', 0x00,

// 产品字符串
0x1A, 0x03, 'S', 0x00, 'T', 0x00, 'L', 0x00, 'I', 0x00, 'N', 0x00, 'K', 0x00, '/', 0x00, 'V', 0x00, '2', 0x00, '.', 0x00, '1', 0x00,

// 序列号字符串
0x12, 0x03, '1', 0x00, '2', 0x00, '3', 0x00, '4', 0x00, '5', 0x00, '6', 0x00, '7', 0x00, '8', 0x00,

// 配置字符串
0x10, 0x03, 'C', 0x00, 'o', 0x00, 'n', 0x00, 'f', 0x00, 'i', 0x00, 'g', 0x00, '1', 0x00,

// 接口字符串 (CDC 控制接口)
0x12, 0x03, 'V', 0x00, 'i', 0x00, 'r', 0x00, 't', 0x00, 'u', 0x00, 'a', 0x00, 'l', 0x00, ' ', 0x00, 'COM', 0x00,

// 接口字符串 (MSC 接口)
0x10, 0x03, 'U', 0x00, 'S', 0x00, 'B', 0x00, ' ', 0x00, 'D', 0x00, 'i', 0x00, 's', 0x00, 'k', 0x00
};


// USB 设备状态
typedef enum {
USB_DEVICE_STATE_DEFAULT,
USB_DEVICE_STATE_ADDRESSED,
USB_DEVICE_STATE_CONFIGURED
} USB_DeviceStateTypeDef;

static USB_DeviceStateTypeDef USB_DeviceState = USB_DEVICE_STATE_DEFAULT;
static uint8_t USB_DeviceAddress = 0;

// USB 设备初始化函数
bool USB_Device_Init(void) {
HAL_USB_Init(USB_SPEED_FULL); // 初始化 USB HAL 层
// ... 初始化 USB 端点,例如 Control Endpoint (端点 0)
USB_DeviceState = USB_DEVICE_STATE_DEFAULT;
USB_DeviceAddress = 0;
USB_CDC_Init(); // 初始化 CDC 类驱动
USB_MSC_Init(); // 初始化 MSC 类驱动
return true;
}

// USB 处理控制传输函数 (Setup Stage)
bool USB_Device_CtlRequestHandler(void) {
// ... 解析 Setup 包,根据请求类型和参数进行处理
// ... 例如,处理 GET_DESCRIPTOR, SET_ADDRESS, SET_CONFIGURATION, GET_STATUS 等标准请求
// ... 以及 CDC 类请求和 MSC 类请求

uint8_t bmRequestType = /* ... 从 USB 控制寄存器读取 bmRequestType */ 0;
uint8_t bRequest = /* ... 从 USB 控制寄存器读取 bRequest */ 0;
uint16_t wValue = /* ... 从 USB 控制寄存器读取 wValue */ 0;
uint16_t wIndex = /* ... 从 USB 控制寄存器读取 wIndex */ 0;
uint16_t wLength = /* ... 从 USB 控制寄存器读取 wLength */ 0;

if ((bmRequestType & 0x60) == 0x00) { // 标准请求
switch (bRequest) {
case 0x06: // GET_DESCRIPTOR
{
uint8_t descriptorType = (wValue >> 8) & 0xFF;
uint8_t descriptorIndex = wValue & 0xFF;

if (descriptorType == 0x01) { // 设备描述符
USB_Device_CtlSendData((uint8_t *)&USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
} else if (descriptorType == 0x02) { // 配置描述符
USB_Device_CtlSendData((uint8_t *)USB_ConfigurationDescriptor, sizeof(USB_ConfigurationDescriptor));
} else if (descriptorType == 0x03) { // 字符串描述符
if (descriptorIndex < (sizeof(USB_StringDescriptor) / 256)) {
uint8_t *pStrDesc = (uint8_t *)USB_StringDescriptor + descriptorIndex * 256;
USB_Device_CtlSendData(pStrDesc, pStrDesc[0]);
}
} else {
// ... 错误处理,返回 STALL
}
}
break;

case 0x05: // SET_ADDRESS
USB_DeviceAddress = wValue & 0x7F;
// ... 设置 USB 设备地址
USB_DeviceState = USB_DEVICE_STATE_ADDRESSED;
USB_Device_CtlSendData(NULL, 0); // 返回零长度数据包 (ZLP) 表示成功
break;

case 0x09: // SET_CONFIGURATION
if (wValue == 1) { // 配置值为 1 表示选择配置
USB_DeviceState = USB_DEVICE_STATE_CONFIGURED;
// ... 配置 CDC 和 MSC 接口和端点
USB_CDC_SetConfigured(true);
USB_MSC_SetConfigured(true);
} else {
USB_DeviceState = USB_DEVICE_STATE_ADDRESSED;
USB_CDC_SetConfigured(false);
USB_MSC_SetConfigured(false);
}
USB_Device_CtlSendData(NULL, 0); // 返回 ZLP
break;

case 0x0A: // GET_STATUS
{
uint16_t status = 0x0000; // 设备状态,默认为 0
USB_Device_CtlSendData((uint8_t *)&status, 2);
}
break;

default:
// ... 其他标准请求处理或 STALL
break;
}
} else if ((bmRequestType & 0x60) == 0x20) { // 类请求 (接口请求)
uint8_t interfaceNumber = wIndex & 0xFF;
if (interfaceNumber == 0) { // CDC 控制接口
USB_CDC_CtlRequestHandler(bmRequestType, bRequest, wValue, wIndex, wLength);
} else if (interfaceNumber == 2) { // MSC 接口
USB_MSC_CtlRequestHandler(bmRequestType, bRequest, wValue, wIndex, wLength);
} else {
// ... 错误处理,返回 STALL
}
} else if ((bmRequestType & 0x60) == 0x40) { // 厂商请求
// ... 处理厂商自定义请求 (可选)
} else {
// ... 保留或错误请求,返回 STALL
}
return true;
}

// USB 控制端点数据发送函数
bool USB_Device_CtlSendData(uint8_t *pData, uint16_t Size) {
// ... 通过 HAL 层 USB 驱动发送数据到端点 0
return HAL_USB_Transmit(0x00, pData, Size); // 端点 0 地址为 0x00
}

// USB 控制端点数据接收函数
bool USB_Device_CtlReceiveData(uint8_t *pData, uint16_t Size) {
// ... 通过 HAL 层 USB 驱动接收数据从端点 0
return HAL_USB_Receive(0x00, pData, Size);
}

// USB 数据发送函数 (用于 Bulk/Interrupt/Iso Endpoint)
bool USB_Device_SendData(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size) {
// ... 通过 HAL 层 USB 驱动发送数据到指定端点
return HAL_USB_Transmit(EndpointAddress, pData, Size);
}

// USB 数据接收函数 (用于 Bulk/Interrupt/Iso Endpoint)
bool USB_Device_ReceiveData(uint8_t EndpointAddress, uint8_t *pData, uint16_t Size) {
// ... 通过 HAL 层 USB 驱动接收数据从指定端点
return HAL_USB_Receive(EndpointAddress, pData, Size);
}

// USB 中断处理函数 (供 HAL 层调用)
void USB_Device_IRQHandler(void) {
// ... 处理 USB 中断事件,例如 Setup 包接收完成、数据包发送完成、数据包接收完成等
// ... 根据中断类型调用相应的处理函数

if (/* ... 检测到 Setup 包中断 */ true) {
USB_Device_CtlRequestHandler(); // 处理控制请求
}

if (/* ... 检测到端点 1 IN 中断 (CDC Interrupt Endpoint) */ true) {
USB_CDC_DataInHandler(0x81); // 调用 CDC 类驱动的 IN 数据处理函数
}

if (/* ... 检测到端点 2 OUT 中断 (CDC Bulk OUT Endpoint) */ true) {
USB_CDC_DataOutHandler(0x02); // 调用 CDC 类驱动的 OUT 数据处理函数
}

if (/* ... 检测到端点 3 IN 中断 (CDC Bulk IN Endpoint) */ true) {
USB_CDC_DataInHandler(0x83); // 调用 CDC 类驱动的 IN 数据处理函数
}

if (/* ... 检测到端点 4 OUT 中断 (MSC Bulk OUT Endpoint) */ true) {
USB_MSC_DataOutHandler(0x04); // 调用 MSC 类驱动的 OUT 数据处理函数
}

if (/* ... 检测到端点 5 IN 中断 (MSC Bulk IN Endpoint) */ true) {
USB_MSC_DataInHandler(0x85); // 调用 MSC 类驱动的 IN 数据处理函数
}

// ... 其他 USB 中断处理,例如复位中断、挂起中断等
}

4. USB CDC 类驱动 (虚拟串口)

usb_cdc.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef USB_CDC_H
#define USB_CDC_H

#include <stdint.h>
#include <stdbool.h>

// CDC 初始化函数
bool USB_CDC_Init(void);

// CDC 设置配置状态函数 (由 USB 设备驱动层调用)
void USB_CDC_SetConfigured(bool isConfigured);

// CDC 控制请求处理函数 (由 USB 设备驱动层调用)
bool USB_CDC_CtlRequestHandler(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength);

// CDC 数据 IN 端点处理函数 (由 USB 设备驱动层调用)
void USB_CDC_DataInHandler(uint8_t EndpointAddress);

// CDC 数据 OUT 端点处理函数 (由 USB 设备驱动层调用)
void USB_CDC_DataOutHandler(uint8_t EndpointAddress);

// CDC 发送数据函数
bool USB_CDC_Transmit(uint8_t *pData, uint16_t Size);

// CDC 接收数据函数 (回调函数,需要用户实现)
typedef void (*USB_CDC_ReceiveCallbackTypeDef)(uint8_t *pData, uint16_t Size);
void USB_CDC_RegisterReceiveCallback(USB_CDC_ReceiveCallbackTypeDef callback);

#endif // USB_CDC_H

usb_cdc.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include "usb_cdc.h"
#include "usb_device.h" // 引用 USB 设备驱动层
#include "string.h" // 字符串操作函数

// CDC Line Coding 结构体 (串口参数)
typedef struct {
uint32_t dwDTERate; // 数据传输速率 (bps)
uint8_t bCharFormat; // 停止位 (0: 1 Stop Bit, 1: 1.5 Stop Bits, 2: 2 Stop Bits)
uint8_t bParityType; // 校验类型 (0: None, 1: Odd, 2: Even, 3: Mark, 4: Space)
uint8_t bDataBits; // 数据位 (5, 6, 7, 8, 16)
} CDC_LineCodingTypeDef;

static CDC_LineCodingTypeDef CDC_LineCoding = {
.dwDTERate = 115200,
.bCharFormat = 0x00, // 1 Stop Bit
.bParityType = 0x00, // None
.bDataBits = 0x08 // 8 Data Bits
};

static bool CDC_Configured = false;
static USB_CDC_ReceiveCallbackTypeDef CDC_ReceiveCallback = NULL;

// 发送缓冲区和接收缓冲区 (环形缓冲区)
#define CDC_TX_BUFFER_SIZE 1024
#define CDC_RX_BUFFER_SIZE 1024
static uint8_t CDC_TxBuffer[CDC_TX_BUFFER_SIZE];
static uint8_t CDC_RxBuffer[CDC_RX_BUFFER_SIZE];
static uint16_t CDC_TxHead = 0;
static uint16_t CDC_TxTail = 0;
static uint16_t CDC_RxHead = 0;
static uint16_t CDC_RxTail = 0;

// CDC 初始化函数
bool USB_CDC_Init(void) {
CDC_Configured = false;
CDC_ReceiveCallback = NULL;
CDC_TxHead = CDC_TxTail = 0;
CDC_RxHead = CDC_RxTail = 0;
return true;
}

// CDC 设置配置状态函数 (由 USB 设备驱动层调用)
void USB_CDC_SetConfigured(bool isConfigured) {
CDC_Configured = isConfigured;
if (isConfigured) {
// ... 如果需要,可以在配置完成后执行一些操作
}
}

// CDC 控制请求处理函数 (由 USB 设备驱动层调用)
bool USB_CDC_CtlRequestHandler(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
(void)wIndex; // 避免编译器警告 unused parameter

if ((bmRequestType & 0x60) == 0x21) { // CDC 类特定请求 (接口请求, OUT)
switch (bRequest) {
case 0x20: // SET_LINE_CODING
USB_Device_CtlReceiveData((uint8_t *)&CDC_LineCoding, sizeof(CDC_LineCodingTypeDef));
break;

case 0x21: // GET_LINE_CODING
USB_Device_CtlSendData((uint8_t *)&CDC_LineCoding, sizeof(CDC_LineCodingTypeDef));
break;

case 0x22: // SET_CONTROL_LINE_STATE
// ... 根据 wValue 设置控制线状态 (例如 RTS, DTR)
USB_Device_CtlSendData(NULL, 0); // 返回 ZLP
break;

case 0xA1: // GET_CONTROL_LINE_STATE (非标准 CDC 请求,某些主机可能会发送)
{
uint16_t controlLineState = 0x0000; // 默认为 0
USB_Device_CtlSendData((uint8_t *)&controlLineState, 2);
}
break;

default:
// ... 错误处理,返回 STALL
return false;
}
return true;
}
return false; // 非 CDC 类请求
}

// CDC 数据 IN 端点处理函数 (由 USB 设备驱动层调用)
void USB_CDC_DataInHandler(uint8_t EndpointAddress) {
(void)EndpointAddress; // 避免编译器警告 unused parameter
// ... 数据发送完成中断处理
// ... 可以继续发送缓冲区中的数据

if (CDC_TxHead != CDC_TxTail) { // 如果发送缓冲区不为空
uint16_t sendSize = 0;
if (CDC_TxHead > CDC_TxTail) {
sendSize = CDC_TxHead - CDC_TxTail;
} else {
sendSize = CDC_TX_BUFFER_SIZE - CDC_TxTail;
}
if (sendSize > 64) sendSize = 64; // 最大发送 64 字节 (Bulk Endpoint MaxPacketSize)

USB_Device_SendData(0x83, &CDC_TxBuffer[CDC_TxTail], sendSize);
CDC_TxTail = (CDC_TxTail + sendSize) % CDC_TX_BUFFER_SIZE;
}
}

// CDC 数据 OUT 端点处理函数 (由 USB 设备驱动层调用)
void USB_CDC_DataOutHandler(uint8_t EndpointAddress) {
(void)EndpointAddress; // 避免编译器警告 unused parameter
// ... 数据接收完成中断处理
uint8_t rxData[64]; // 接收缓冲区
uint16_t rxSize = USB_Device_ReceiveData(0x02, rxData, sizeof(rxData)); // 接收数据

if (rxSize > 0) {
// 将接收到的数据写入接收缓冲区
for (uint16_t i = 0; i < rxSize; i++) {
CDC_RxBuffer[CDC_RxHead] = rxData[i];
CDC_RxHead = (CDC_RxHead + 1) % CDC_RX_BUFFER_SIZE;
if (CDC_RxHead == CDC_RxTail) { // 缓冲区溢出处理 (可选)
CDC_RxTail = (CDC_RxTail + 1) % CDC_RX_BUFFER_SIZE; // 丢弃最旧的数据
}
}
if (CDC_ReceiveCallback != NULL) {
CDC_ReceiveCallback(rxData, rxSize); // 调用接收回调函数
}
}
}

// CDC 发送数据函数 (将数据写入发送缓冲区,并触发发送)
bool USB_CDC_Transmit(uint8_t *pData, uint16_t Size) {
if (!CDC_Configured) return false; // 未配置时不能发送

for (uint16_t i = 0; i < Size; i++) {
CDC_TxBuffer[CDC_TxHead] = pData[i];
CDC_TxHead = (CDC_TxHead + 1) % CDC_TX_BUFFER_SIZE;
if (CDC_TxHead == CDC_TxTail) { // 发送缓冲区满,无法发送
return false; // 可以考虑返回错误码或阻塞等待
}
}

// 触发发送 (如果当前没有正在发送,或者上次发送已完成)
if (CDC_TxHead != CDC_TxTail) {
// 检查 USB 端点状态,确保可以发送
USB_CDC_DataInHandler(0x83); // 触发一次发送
}

return true;
}

// CDC 接收数据函数 (从接收缓冲区读取数据)
uint16_t USB_CDC_Receive(uint8_t *pBuffer, uint16_t BufferSize) {
uint16_t receivedSize = 0;
while (receivedSize < BufferSize && CDC_RxTail != CDC_RxHead) {
pBuffer[receivedSize++] = CDC_RxBuffer[CDC_RxTail];
CDC_RxTail = (CDC_RxTail + 1) % CDC_RX_BUFFER_SIZE;
}
return receivedSize;
}

// CDC 注册接收回调函数
void USB_CDC_RegisterReceiveCallback(USB_CDC_ReceiveCallbackTypeDef callback) {
CDC_ReceiveCallback = callback;
}

5. USB MSC 类驱动 (虚拟 U 盘)

(由于代码量限制,MSC 类驱动的代码实现会比较复杂,这里只提供框架,具体实现需要根据文件系统和存储介质进行)

usb_msc.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef USB_MSC_H
#define USB_MSC_H

#include <stdint.h>
#include <stdbool.h>

// MSC 初始化函数
bool USB_MSC_Init(void);

// MSC 设置配置状态函数 (由 USB 设备驱动层调用)
void USB_MSC_SetConfigured(bool isConfigured);

// MSC 控制请求处理函数 (由 USB 设备驱动层调用)
bool USB_MSC_CtlRequestHandler(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength);

// MSC 数据 IN 端点处理函数 (由 USB 设备驱动层调用)
void USB_MSC_DataInHandler(uint8_t EndpointAddress);

// MSC 数据 OUT 端点处理函数 (由 USB 设备驱动层调用)
void USB_MSC_DataOutHandler(uint8_t EndpointAddress);

// ... 其他 MSC 相关函数,例如读取扇区,写入扇区,获取容量等

#endif // USB_MSC_H

usb_msc.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include "usb_msc.h"
#include "usb_device.h" // 引用 USB 设备驱动层

// MSC 初始化函数
bool USB_MSC_Init(void) {
// ... 初始化 MSC 相关资源,例如分配缓冲区,初始化文件系统 (如果需要)
return true;
}

// MSC 设置配置状态函数 (由 USB 设备驱动层调用)
void USB_MSC_SetConfigured(bool isConfigured) {
// ... 根据配置状态进行 MSC 相关操作,例如挂载/卸载文件系统
}

// MSC 控制请求处理函数 (由 USB 设备驱动层调用)
bool USB_MSC_CtlRequestHandler(uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) {
(void)wValue;
(void)wIndex;
(void)wLength;

if ((bmRequestType & 0x60) == 0x21) { // 类特定请求 (接口请求, OUT)
switch (bRequest) {
case 0xFE: // MSC Reset
// ... 执行 MSC 复位操作
USB_Device_CtlSendData(NULL, 0); // 返回 ZLP
break;

default:
// ... 错误处理,返回 STALL
return false;
}
return true;
} else if ((bmRequestType & 0x60) == 0xA1) { // 类特定请求 (接口请求, IN)
switch (bRequest) {
case 0x00: // Get Max LUN
{
uint8_t maxLun = 0; // 假设只有一个 LUN
USB_Device_CtlSendData(&maxLun, 1);
}
break;

default:
// ... 错误处理,返回 STALL
return false;
}
return true;
}
return false; // 非 MSC 类请求
}

// MSC 数据 IN 端点处理函数 (由 USB 设备驱动层调用)
void USB_MSC_DataInHandler(uint8_t EndpointAddress) {
(void)EndpointAddress;
// ... 处理 MSC IN 数据传输完成事件,例如发送下一个数据块
}

// MSC 数据 OUT 端点处理函数 (由 USB 设备驱动层调用)
void USB_MSC_DataOutHandler(uint8_t EndpointAddress) {
(void)EndpointAddress;
// ... 处理 MSC OUT 数据接收完成事件,例如接收数据块,写入存储介质
}

// ... 实现 MSC 的 SCSI 命令处理函数,例如 TEST UNIT READY, REQUEST SENSE, INQUIRY, READ CAPACITY, READ/WRITE 等
// ... 这些函数会根据 SCSI 命令操作底层的存储介质 (例如 Flash 模拟的 U 盘)

6. ST-LINK 功能层 (核心逻辑)

(ST-LINK 功能层代码实现非常复杂,涉及到 JTAG/SWD 协议的解析和实现,以及固件下载算法。这里只给出框架和一些关键模块的思路)

stlink_debug.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef STLINK_DEBUG_H
#define STLINK_DEBUG_H

#include <stdint.h>
#include <stdbool.h>

// ST-LINK 调试协议初始化函数
bool STLINK_Debug_Init(void);

// ST-LINK 调试协议处理函数 (接收上位机调试命令并执行)
bool STLINK_Debug_ProcessCommand(uint8_t *pCommand, uint16_t CommandSize);

// ST-LINK 发送调试响应数据
bool STLINK_Debug_SendData(uint8_t *pData, uint16_t Size);

// ... 其他 ST-LINK 调试相关函数,例如设置断点,读取寄存器,写入内存等

#endif // STLINK_DEBUG_H

stlink_debug.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include "stlink_debug.h"
#include "usb_cdc.h" // 使用虚拟串口作为调试通道

// ST-LINK 调试协议初始化函数
bool STLINK_Debug_Init(void) {
// ... 初始化 ST-LINK 调试接口,例如 GPIO 初始化,SWD/JTAG 接口初始化
return true;
}

// ST-LINK 调试协议处理函数 (接收上位机调试命令并执行)
bool STLINK_Debug_ProcessCommand(uint8_t *pCommand, uint16_t CommandSize) {
// ... 解析 ST-LINK 调试协议命令,例如读取寄存器,写入内存,设置断点,单步调试等
// ... 根据命令类型调用相应的处理函数
// ... 与 STM32 目标芯片进行 SWD/JTAG 通信,执行调试操作

uint8_t commandCode = pCommand[0]; // 假设命令的第一个字节是命令码

switch (commandCode) {
case 0x01: // 读取寄存器命令
{
uint32_t registerAddress = /* ... 从命令数据中解析寄存器地址 */;
uint32_t registerValue = /* ... 通过 SWD/JTAG 读取寄存器值 */;
// ... 将寄存器值封装成响应数据,并发送给上位机
uint8_t responseData[8]; // 假设响应数据格式
// ... 填充响应数据
STLINK_Debug_SendData(responseData, sizeof(responseData));
}
break;

case 0x02: // 写入内存命令
{
uint32_t memoryAddress = /* ... 从命令数据中解析内存地址 */;
uint32_t memoryValue = /* ... 从命令数据中解析要写入的内存值 */;
// ... 通过 SWD/JTAG 写入内存
// ... 发送成功响应
uint8_t responseData[2] = {0x00, 0x00}; // 假设成功响应
STLINK_Debug_SendData(responseData, sizeof(responseData));
}
break;

// ... 其他调试命令处理

default:
// ... 未知命令处理,发送错误响应
break;
}

return true;
}

// ST-LINK 发送调试响应数据 (通过虚拟串口发送)
bool STLINK_Debug_SendData(uint8_t *pData, uint16_t Size) {
// ... 通过 USB CDC 虚拟串口发送数据给上位机
return USB_CDC_Transmit(pData, Size);
}

// ... 实现 SWD/JTAG 通信的底层驱动函数,例如 SWD_WriteAP, SWD_ReadAP, JTAG_WriteIR, JTAG_ReadDR 等
// ... 这些函数需要直接操作 GPIO 引脚或 MCU 的 JTAG/SWD 外设

7. 应用层 (main.c)

main.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "bsp_pandora.h"
#include "usb_device.h"
#include "usb_cdc.h"
#include "usb_msc.h"
#include "stlink_debug.h"

// CDC 接收回调函数
void CDC_Receive_Callback(uint8_t *pData, uint16_t Size) {
// ... 接收到虚拟串口数据,根据数据类型判断是调试命令还是串口数据
// ... 如果是调试命令,则调用 STLINK_Debug_ProcessCommand 处理
// ... 如果是串口数据,则可以转发给目标 STM32 (如果需要) 或进行其他处理

if (Size > 0) {
// 假设第一个字节是命令类型
if (pData[0] == 0x80) { // 假设 0x80 是调试命令类型
STLINK_Debug_ProcessCommand(pData + 1, Size - 1); // 去掉命令类型字节,处理调试命令
} else {
// ... 其他串口数据处理,例如回显,转发等
USB_CDC_Transmit(pData, Size); // 回显数据 (示例)
}
}
}

int main(void) {
BSP_Init(); // 板级初始化
USB_Device_Init(); // USB 设备初始化
STLINK_Debug_Init(); // ST-LINK 调试初始化
USB_CDC_RegisterReceiveCallback(CDC_Receive_Callback); // 注册 CDC 接收回调函数

while (1) {
// ... 主循环,可以处理其他任务,例如 LED 闪烁,按键检测等
BSP_LED_Toggle(GPIO_PORT_B, GPIO_PIN_0); // LED 闪烁
// HAL_Delay(500); // 延时 (需要实现 HAL_Delay 或使用 RTOS 延时)
}
}

// USB 中断服务例程 (需要在启动文件或中断向量表中配置)
void USB_IRQHandler(void) {
HAL_USB_IRQHandler(); // 调用 HAL 层 USB 中断处理函数
USB_Device_IRQHandler(); // 调用 USB 设备驱动层中断处理函数
}

编译和链接

需要配置合适的编译环境 (例如 GCC for ARM) 和链接脚本,将上述代码编译链接成可执行文件,并烧录到潘多拉开发板上运行。

测试验证和维护升级

  • 测试验证:

    • 单元测试: 针对每个模块进行单元测试,例如 HAL 层驱动测试,USB 设备驱动测试,CDC/MSC 类驱动测试,ST-LINK 调试功能测试等。
    • 集成测试: 将各个模块集成起来进行测试,验证模块之间的协同工作是否正常。
    • 系统测试: 进行完整的系统测试,验证 ST-LINK/V2.1 的所有功能是否符合需求,例如 STM32 调试功能、虚拟串口功能、虚拟 U 盘功能是否正常工作。可以使用常用的 STM32 调试工具 (例如 STM32CubeIDE, Keil MDK) 和串口工具进行测试。
  • 维护升级:

    • 模块化设计: 分层架构和模块化设计使得代码易于维护和升级。当需要修改或扩展某个功能时,只需要修改相应的模块,而不会影响其他模块。
    • 版本控制: 使用版本控制系统 (例如 Git) 管理代码,方便代码的版本管理和协同开发。
    • 日志和错误处理: 在代码中添加适当的日志输出和错误处理机制,方便调试和问题定位。
    • 固件升级: 可以通过虚拟 U 盘功能实现固件升级,方便后续的维护和功能扩展。

项目中采用的技术和方法

  • 分层架构和模块化设计: 提高代码的可维护性、可重用性和可扩展性。
  • HAL 硬件抽象层: 屏蔽硬件差异,提高代码的移植性。
  • USB 协议栈: 实现 USB 设备功能,包括 USB 设备驱动、CDC 类驱动、MSC 类驱动。
  • ST-LINK 调试协议: 实现 STM32 调试功能,包括 SWD/JTAG 通信、调试命令解析和执行。
  • 虚拟串口 (VCP): 通过 USB CDC 类驱动实现虚拟串口功能。
  • 虚拟 U 盘 (MSC): 通过 USB MSC 类驱动实现虚拟 U 盘功能。
  • C 语言编程: 使用 C 语言进行嵌入式软件开发,C 语言是嵌入式系统开发中最常用的语言。
  • 中断处理: 使用中断机制处理 USB 事件和调试事件,保证系统的实时性。
  • 环形缓冲区: 在 CDC 虚拟串口中使用环形缓冲区管理数据收发,提高数据处理效率。
  • 错误处理和日志: 在代码中添加错误处理和日志输出,方便调试和问题定位。

总结

这个基于潘多拉开发板的 STLINK/V2.1 项目是一个非常具有挑战性和实用价值的嵌入式系统开发项目。通过采用分层架构和模块化设计,结合 HAL 硬件抽象层、USB 协议栈、ST-LINK 调试协议等技术,可以构建一个可靠、高效、可扩展的嵌入式系统平台。代码实现部分虽然只是框架和关键模块的示例,但已经展示了整个系统的基本结构和设计思路。在实际项目中,还需要根据具体的硬件平台和功能需求,进行更加详细的设计和实现。希望这个详细的解答和代码示例能够帮助你更好地理解嵌入式系统开发流程和代码架构设计。

欢迎关注我的其它发布渠道