IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> USB 协议整理 九:USB 协议调试工程说明 -> 正文阅读

[嵌入式]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 模块

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:44:59  更:2022-03-15 22:47:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:30:22-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码