USB 协议整理 九:USB 协议调试工程说明

一、前言

目的:使用 STM32F103 从 0 开始实现 USB 调试代码。

硬件:正点原子战舰开发板。

软件

  • USB:用于实现各种功能

  • 串口:打印日志信息

  • LED:显示运行状态

  • 其他:根据需要进行添加

参考:STM 官方 USB 库。

二、工程目录说明

在这里插入图片描述

三、常用驱动说明

1、时钟配置

在这里插入图片描述

2、数据结构设计

1、循环队列

1、数据类型
#define QUEUE_MAXSIZE 128

typedef struct{
    unsigned char data[QUEUE_MAXSIZE];
    unsigned int front,rear;            // fornt:对头、rear:队尾
}queue_t;
2、相关操作
#include "queue.h"

int init_queue(queue_t *q)
{
    q->front = q->rear = 0;
    
    return 0;
}

int en_queue(queue_t *q,unsigned char c)
{
    if((q->rear+1)%QUEUE_MAXSIZE == q->front) return 1;     // 队列满
    q->data[q->rear] = c;                                   // 将 x 插入队尾
    q->rear = (q->rear+1)%QUEUE_MAXSIZE;                    // 队尾指针后移
    
    return 0;
}

int de_queue(queue_t *q,unsigned char *c)
{
    if(q->rear == q->front) return 1;       // 队列为空
    *c = q->data[q->front];                 // 提取对头元素
    q->front = (q->front+1)%QUEUE_MAXSIZE;  // 队头后移
    
    return 0;
}

3、UART

1、缓存设计

1、发送数据

发送数据不进行设计,根据需求进行。

2、接收数据

接收数据采用循环队列完成。

typedef struct{
    queue_t data;
}uart_t;

2、初始化

int uart_init(unsigned int rate)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
    
    //USART1_TX   GPIOA.9
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //USART1_RX	  GPIOA.10
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // NVIC
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
    
    // USART 配置
    USART_InitStructure.USART_BaudRate = rate;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

    USART_Init(USART1, &USART_InitStructure);
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    USART_Cmd(USART1, ENABLE);
    
    // 初始化
    init_queue(&uart_data.data);
    return 0;
}

3、发送数据

uart 作为日志数据发送,需要兼容不同格式类型数据,因此发送数据通过重定向 printf 完成。

#if 1
#pragma import(__use_no_semihosting)                           
struct __FILE 
{ 
	int handle; 

}; 

FILE __stdout;        
void _sys_exit(int x) 
{ 
	x = x; 
} 
int fputc(int ch, FILE *f)
{      
	while((USART1->SR&0X40)==0);
    USART1->DR = (u8) ch;      
	return ch;
}
#endif

4、接收数据

uart 接收数据为了能够及时响应,因此使用中断来完成。

int uart_recive_data(unsigned char c)
{
    en_queue(&uart_data.data,c); // 数据入队
    
    return 0;
}

void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET){
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
        uart_recive_data(USART_ReceiveData(USART1));
    }
}

3、LED

int led_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_SetBits(GPIOB,GPIO_Pin_5);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init(GPIOE, &GPIO_InitStructure);
    GPIO_SetBits(GPIOE,GPIO_Pin_5);

    return 0;
}

void led0_on(void)
{
    GPIO_ResetBits(GPIOB,GPIO_Pin_5);
}

void led0_off(void)
{
    GPIO_SetBits(GPIOB,GPIO_Pin_5);
}

void led1_on(void)
{
    GPIO_ResetBits(GPIOE,GPIO_Pin_5);
}

void led1_off(void)
{
    GPIO_SetBits(GPIOE,GPIO_Pin_5);
}

4、delay

延时使用 Systick 定时器完成,设计思想如下:

1、将 Systick 配置成每 1ms 中断一次。

2、根据延时时间设置中断次数。

代码如下:

static __IO unsigned int timing_delay;

int delay_init(void)
{
    if(SysTick_Config(SystemCoreClock / 1000)){
        printf("delay_init failed\r\n");
    }
    
    return 0;
}

void delay_ms(__IO unsigned int nTime)
{ 
  timing_delay = nTime;

  while(timing_delay != 0);
}

void timing_delay_decrement(void)
{
    if (timing_delay != 0x00)
    { 
        timing_delay--;
    }
}

void SysTick_Handler(void)
{
    timing_delay_decrement();
}

四、usb 驱动

说明:参考 STM 提供 USB 库。

1、GPIO 配置

static int usb_gpio_init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    // 配置时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    // 配置 USB DM/DP 引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    return 0;
}

2、时钟配置

static int usb_clock_init(void)
{
    // 设置 USBCLK 时钟
    RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_1Div5);
    // 设置 USB 时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);
    return 0;
}

3、中断配置

static int usb_interrupts_config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    
    // USB低优先级中断(通道20):可由所有USB事件触发(正确传输,USB复位等)
    NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    
    // USB高优先级中断(通道19):仅能由同步和双缓冲批量传输的正确传输事件触发,目的是保证最大的传输速率
    NVIC_InitStructure.NVIC_IRQChannel = USB_HP_CAN1_TX_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    
    // USB唤醒中断(通道42):由USB挂起模式的唤醒事件触发
    NVIC_InitStructure.NVIC_IRQChannel = USBWakeUp_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_Init(&NVIC_InitStructure);
    
    return 0;
}

特别说明:中断优先级等级根据需要进行调整。
中断处理函数如下:

int usb_istr(void)
{
    unsigned int r_istr = 0;
    r_istr = _GetISTR();
    
    if(r_istr & ISTR_SOF){
        _SetISTR((uint16_t)CLR_SOF);
        printf("ISTR_SOF IRQHandler\r\n");
    }
    if(r_istr & ISTR_CTR){
        printf("ISTR_CTR IRQHandler\r\n");
    }
    if(r_istr & ISTR_RESET){
        _SetISTR((uint16_t)CLR_RESET);
        printf("ISTR_RESET IRQHandler\r\n");
    }
    if(r_istr & ISTR_DOVR){
        _SetISTR((uint16_t)CLR_DOVR);
        printf("ISTR_DOVR IRQHandler\r\n");
    }
    if(r_istr & ISTR_ERR){
        _SetISTR((uint16_t)CLR_ERR);
        printf("ISTR_ERR IRQHandler\r\n");
    }
    if(r_istr & ISTR_WKUP){
        _SetISTR((uint16_t)CLR_WKUP);
        printf("ISTR_WKUP IRQHandler\r\n");
    }
    if(r_istr & ISTR_SUSP){
        _SetISTR((uint16_t)CLR_SUSP);
        printf("ISTR_SUSP IRQHandler\r\n");
    }
    if(r_istr & ISTR_ESOF){
        _SetISTR((uint16_t)CLR_ESOF);
        printf("ISTR_ESOF IRQHandler\r\n");
    }
    
    return 0;
}

void USB_LP_CAN1_RX0_IRQHandler(void)
{
    usb_istr();
    printf("USB_LP_CAN1_RX0_IRQHandler\r\n");
}

void USB_HP_CAN1_TX_IRQHandler(void)
{
    printf("USB_HP_CAN1_TX_IRQHandler\r\n");
}

void USBWakeUp_IRQHandler(void)
{
    printf("USBWakeUp_IRQHandler\r\n");
}

4、usb外设配置

int usb_register_init(void)
{
    // 1、强制复位
    _SetCNTR(CNTR_FRES);
    // 2、清除 USB_CNTR 寄存器所有信号
    _SetCNTR(0);
    // 3、等待 USB 复位请求完成
    while((_GetISTR()&ISTR_RESET) == 1);
    // 4、清除无效挂起中断
    _SetISTR(0);
    // 5、开启USB复位中断、挂起中断、唤醒中断
    _SetCNTR(CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM);
    // 6、清除无效挂起中断
    _SetISTR(0);
    // 7、开启正确传输中断、唤醒中断、挂起中断、出错中断、帧首中断、期望帧首中断、USB复位中断
    _SetCNTR(CNTR_CTRM|CNTR_WKUPM|CNTR_SUSPM|CNTR_ERRM|CNTR_SOFM|CNTR_ESOFM|CNTR_RESETM);
    
    return 0;
}

五、工程模板测试

1、串口打印日志如下

[16:49:54.963] uart init successful!
[16:49:54.963] ISTR_ESOF IRQHandler
[16:49:54.963] USB_LP_CAN1_RX0_IRQHandler
[16:49:54.963] ISTR_SUSP IRQHandler
[16:49:54.963] ISTR_ESOF IRQHandler
……
[16:49:55.052] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.055] ISTR_RESET IRQHandler
[16:49:55.101] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.113] ISTR_SOF IRQHandler
[16:49:55.113] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.113] ISTR_SOF IRQHandler
[16:49:55.113] USB_LP_CAN1_RX0_IRQHandler
[16:49:55.113] ISTR_SOF IRQHandler
[16:49:55.122] USB_LP_CAN1_RX0_IRQHandler
……
[16:49:57.084] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.084] ISTR_SOF IRQHandler
[16:49:57.084] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.084] ISTR_SOF IRQHandler
[16:49:57.092] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.092] ISTR_SOF IRQHandler
[16:49:57.092] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.092] ISTR_SOF IRQHandler
[16:49:57.102] ISTR_ESOF IRQHandler
[16:49:57.102] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.102] ISTR_SUSP IRQHandler
[16:49:57.102] ISTR_ESOF IRQHandler
[16:49:57.111] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.111] ISTR_SUSP IRQHandler
[16:49:57.111] ISTR_ESOF IRQHandler
[16:49:57.111] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.123] ISTR_SUSP IRQHandler
[16:49:57.123] ISTR_ESOF IRQHandler
[16:49:57.123] USB_LP_CAN1_RX0_IRQHandler
[16:49:57.123] ISTR_SUSP IRQHandler
[16:49:57.123] ISTR_ESOF IRQHandler
[16:49:57.132] USB_LP_CAN1_RX0_IRQHandler
……
  • SOF:USB 模块检测到总线上的 SOF 分组时由硬件置位,标志一个新的 USB 帧的开始。中断服务程序可以通过检测 SOF 事件来完成与主机的 1ms 同步,并正确读出寄存器在收到 SOF 分组时 的更新内容(此功能在同步传输时非常有意义)。

  • ESOF:USB 模块未收到期望的 SOF 分组时由硬件置位。主机应该每毫秒都发送 SOF 分组,但如果 USB 模块没有收到,挂起定时器将触发此中断。如果连续发生 3 次 ESOF 中断也就是连续 3 次未收到 SOF 分组,将产生 SUSP 中断。

  • SUSP:USB 线上超过 3ms 没有信号传输时由硬件置位,用以指示一个来自 USB 总线的挂起请求。USB 复位后硬件立即使能对挂起信号的检测,但在挂起模式下(FSUSP=1)硬件不会再检 测挂起信号直到唤醒过程结束。

2、PC 枚举 usb 模块

在这里插入图片描述

驱动器 D 中的卷没有标签。 卷的序列号是 B837-9C57 D:\download\USB助手 源码 2008-12-30 08:44 . 2008-12-30 08:44 .. 2002-10-07 15:21 1,536 ChildFrm.cpp 2002-10-07 15:21 1,397 ChildFrm.h 2002-10-07 15:21 17,103 DevicesDlg.cpp 2002-10-07 15:21 2,330 DevicesDlg.h 2008-12-30 08:44 18 dir list.bat 2008-12-30 08:44 0 dir.txt 2002-10-07 15:21 5,469 Exporter.cpp 2002-10-07 15:21 2,098 Exporter.h 2002-10-07 15:21 4,947 ExportLogDlg.cpp 2002-10-07 15:21 1,712 ExportLogDlg.h 2002-10-07 15:21 6,135 MainFrm.cpp 2002-10-07 15:21 1,894 MainFrm.h 2002-10-07 15:21 2,954 MyMemFile.cpp 2002-10-07 15:21 1,682 MyMemFile.h 2002-10-07 15:21 1,582 ProgressStatusBar.cpp 2002-10-07 15:21 982 ProgressStatusBar.h 2002-10-07 15:21 4,754 ReadMe.txt 2002-10-07 15:21 2,271 ReadmeDlg.cpp 2002-10-07 15:21 1,264 ReadmeDlg.h 2008-12-30 08:41 Res 2002-10-07 15:21 4,735 Resource.h 2002-10-07 15:21 14,777 SetupDIMgr.cpp 2002-10-07 15:21 1,943 SetupDIMgr.h 2002-10-07 15:21 11,745 SnoopyPro.cpp 2002-10-07 15:21 7,277 SnoopyPro.dsp 2002-10-07 15:21 2,704 SnoopyPro.h 2002-10-07 15:21 23,501 SnoopyPro.rc 2002-10-07 15:21 704 SnoopyPro.reg 2008-12-11 19:15 16,773 SnoopyPro.vcproj 2002-10-07 15:21 211 StdAfx.cpp 2002-10-07 15:21 1,212 StdAfx.h 2002-10-07 15:21 57,986 URB.cpp 2002-10-07 15:21 9,642 URB.h 2002-10-07 15:21 3,613 URBLogListBox.cpp 2002-10-07 15:21 1,454 URBLogListBox.h 2002-10-07 15:21 13,776 URBLogListCtrl.cpp 2002-10-07 15:21 3,209 URBLogListCtrl.h 2002-10-07 15:21 3,192 URLStatic.cpp 2002-10-07 15:21 1,285 URLStatic.h 2002-10-07 15:21 11,998 USBLogDoc.cpp 2002-10-07 15:21 2,929 USBLogDoc.h 2002-10-07 15:21 28,721 USBLogView.cpp 2002-10-07 15:21 3,470 USBLogView.h 42 个文件 286,985 字节 3 个目录 12,592,160,768 可用字节
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值