USB二:深入解析STM32_USB-FS-Device_Lib 库

发布者:电竞狂人最新更新时间:2019-07-27 来源: eefocus关键字:USB  STM32  USB-FS-Device_Lib 手机看文章 扫描二维码
随时随地手机看文章

USB事务处理:


在 USB 协议中,USB 的数据传输由信息包组成,这些信息包组合 

起来可以构成完整的事务处理。USB 事务处理是 USB 主机和 USB 功能 

设备之间数据传输的基本单位。USB 的信息包和事务处理具有特定的 

格式。 


这里写图片描述

Packet buff的使用

每个双向EP对应两个packet buffer,分别 

用于发送和接收软件通过packet buffer 

• 软件通过packet buffer interface来访问它们 

• 这些packet buffer的位置和大小都可配置,由buffer描述表指定 

• Buffer描述表本身也在这块memory里,它自己的地址是由USB_BTABLE寄存器指定的 

• USB外设硬件不会把本EP的数据溢出到与其相邻的其他packet 

这里写图片描述


usb_prop.c中:CustomHID_Reset(void)中

这里写图片描述

这里写图片描述

端点(EP)的初始化 

首次连接主机,主机会下发从机复位 

这里写图片描述

这里写图片描述

端点(EP)资源:


有8个双向端点 

• 双向EP0是必备的 

• 其他EPi,应用可同时使用收、发两个方向或只使用一个方向 


这里写图片描述

各device demo对EP的使用情况:

这里写图片描述


1. 应用中使用到的EP尽量内部编号考前(0~7),这样可以使得【硬件缓冲描述表】 尽可能小 

2. 库函数默认把EPx的地址设置成x


SetDeviceAddress(Val) @

uint32_t nEP = Device_Table.Total_Endpoint;

for (i = 0; i < nEP; i++)

{

_SetEPAddress((uint8_t)i, (uint8_t)i);

}

_SetDADDR(Val | DADDR_EF); 设置设备地址的同时使能USB模块


IN packet 的处理:


OUT/SETUP packet的处理:


Control transfer的处理 :


双缓冲端点:


结构体初始化:

分配了一个结构体(Device_Info)来记录收到的主机命令,以及设备当前状态;并用一个指针(plnformation)来指向它


//USB驱动将主机发送过来的USB设备的设置包保存在设备信息结构表中

typedef struct _DEVICE_INFO

{

  u8 USBbmRequestType;       /* bmRequestType */

  u8 USBbRequest;            /* bRequest */

  u16_u8 USBwValues;         /* wValue */

  u16_u8 USBwIndexs;         /* wIndex */

  u16_u8 USBwLengths;        /* wLength */


  u8 ControlState;           /* of type CONTROL_STATE */

  u8 Current_Feature;

  u8 Current_Configuration;   /* Selected configuration */

  u8 Current_Interface;       /* Selected interface of current configuration */

  u8 Current_AlternateSetting;/* Selected Alternate Setting of current

                                     interface*/


  ENDPOINT_INFO Ctrl_Info;

}DEVICE_INFO;


typedef struct _ENDPOINT_INFO

{

/*当从设备发送数据时:copydata()可获取数据缓冲区的长度,如果长度为0,copydata()返回数据的总长度如果不支持请求,则返回0,如果返回-1,停止进行下一步,初始化,如果不是0,返回数据指针*/

  u16  Usb_wLength;//待发送数据

  u16  Usb_wOffset;//原始数据偏移量

  u16  PacketSize;

  u8   *(*CopyData)(u16 Length);

}ENDPOINT_INFO;


状态表示:


typedef enum _CONTROL_STATE

{

  WAIT_SETUP,       /* 0 */

  SETTING_UP,       /* 1 */

  IN_DATA,          /* 2 */

  OUT_DATA,         /* 3 */

  LAST_IN_DATA,     /* 4 */

  LAST_OUT_DATA,    /* 5 */

  WAIT_STATUS_IN,   /* 7 */

  WAIT_STATUS_OUT,  /* 8 */

  STALLED,          /* 9 */

  PAUSE             /* 10 */

} CONTROL_STATE;    /* The state machine states of a control pipe */


//USB驱动将主机发送过来的USB设备的设置包保存在设备信息结构表中

typedef struct _DEVICE_INFO

{

  u8 USBbmRequestType;       /* bmRequestType */

  u8 USBbRequest;            /* bRequest */

  u16_u8 USBwValues;         /* wValue */

  u16_u8 USBwIndexs;         /* wIndex */

  u16_u8 USBwLengths;        /* wLength */


  u8 ControlState;           /* of type CONTROL_STATE */

  u8 Current_Feature;

  u8 Current_Configuration;   /* Selected configuration */

  u8 Current_Interface;       /* Selected interface of current configuration */

  u8 Current_AlternateSetting;/* Selected Alternate Setting of current

                                     interface*/


  ENDPOINT_INFO Ctrl_Info;

}DEVICE_INFO;


处理主机的setup:


*处理主机的setup请求 80 06 00 01 00 00 40 00

*******************************************************************************/

u8 Setup0_Process(void)

{


  union

  {

    u8* b;

    u16* w;

  } pBuf;

//取的端点0的接收缓冲区地址

  pBuf.b = PMAAddr + (u8 *)(_GetEPRxAddr(ENDP0) * 2); /* *2 for 32 bits addr */


  if (pInformation->ControlState != PAUSE)

  {

    pInformation->USBbmRequestType = *pBuf.b++; /* 请求类型,标明方向和接收对象(设备、接口还是端点),80:设备到主机 */

    pInformation->USBbRequest = *pBuf.b++; /* 请求代码:首次为6:标明主机要获取设备描述符 */

    pBuf.w++;  /* word not accessed because of 32 bits addressing */

    pInformation->USBwValue = ByteSwap(*pBuf.w++); /* 标明是啥请求 */

    pBuf.w++;  /* word not accessed because of 32 bits addressing */

    pInformation->USBwIndex  = ByteSwap(*pBuf.w++); /* 用来说明端点号或 */

    pBuf.w++;  /* word not accessed because of 32 bits addressing */

    pInformation->USBwLength = *pBuf.w; /* 该请求应答的长度 */

  }


  pInformation->ControlState = SETTING_UP;

  if (pInformation->USBwLength == 0)

  {

    /* 第2阶段: */

    NoData_Setup0();

  }

  else

  {

    /*  第1阶段,交互设备描述符。交互完,主机复位USB,进入第2阶段*/

    Data_Setup0();

  }

  return Post0_Process();

}



//完成描述符的输出准备


void DataStageIn(void)

{

    ...

  DataBuffer = (*pEPinfo->CopyData)(Length);//用户描述符缓冲区地址,18字节


  UserToPMABufferCopy(DataBuffer, GetEPTxAddr(ENDP0), Length);//将描述符复制到发送缓冲区


  SetEPTxCount(ENDP0, Length);//设置发送长度


  pEPinfo->Usb_wLength -= Length;

  pEPinfo->Usb_wOffset += Length;

  vSetEPTxStatus(EP_TX_VALID);//使能端点发送,主要主机的令牌包一来,就启动发送


  USB_StatusOut();/* USB接收有效 */


Expect_Status_Out:

  pInformation->ControlState = ControlState;//进入第2阶段

}


DataStageOut


结构体初始化二 

分配了一个结构体(Device_Property)来定义该设备的各个回掉函数; 

并用一个指针(pProperty)来指向它


DEVICE_PROP Device_Property =

  {

    CustomHID_init,

    CustomHID_Reset,

    CustomHID_Status_In,

    CustomHID_Status_Out,

    CustomHID_Data_Setup,

    CustomHID_NoData_Setup,

    CustomHID_Get_Interface_Setting,

    CustomHID_GetDeviceDescriptor,

    CustomHID_GetConfigDescriptor,

    CustomHID_GetStringDescriptor,

    0,

    0x40 /*MAX PACKET SIZE*/

  };


结构体定义如下:


  void (*Init)(void);        /* Initialize the device */

  void (*Reset)(void);       /* Reset routine of this device */


  /* Device dependent process after the status stage */

  void (*Process_Status_IN)(void);

  void (*Process_Status_OUT)(void);

 RESULT (*Class_Data_Setup)(u8 RequestNo);

  RESULT (*Class_NoData_Setup)(u8 RequestNo);


  RESULT  (*Class_Get_Interface_Setting)(u8 Interface, u8 AlternateSetting);


  u8* (*GetDeviceDescriptor)(u16 Length);

  u8* (*GetConfigDescriptor)(u16 Length);

  u8* (*GetStringDescriptor)(u16 Length);


  u8* RxEP_buffer;

  u8 MaxPacketSize;


结构体初始化三、


User_Standard_Requests 

分配了一个结构体(User_Standard_Requests)来定义该设备用来响应主机标准命令时的各种回调函数; 并用一个指针(pUser_Standard_Requests) 来指向它 


这里写图片描述

这里写图片描述

初始化: 

void USB_Init(void)

这里写图片描述

看一下设备本身的属性和方法:


这里写图片描述


看一下初始化的过程: 

pProperty->Init(); 


这里写图片描述

• 获取序列号 

根据该芯片的Unique ID来更新“序号字符串”(MASS_StringSerial[26])

这里写图片描述 

 

• 连接设备: PowerOn() 

• 拉低PB14来使能USB_D+线上的上拉电阻 

• 置位并复位FRES@USB_CNTR来对USB外设进行复位 

• 复位ISTR所有位来清除可能pending的中断 

• 设置本应用关心的中断事件: RESET、 SUSP、 WKUP


整个USB通信是由中断驱动的

这里写图片描述

不同应用关心不同的中断事件:

这里写图片描述

这里写图片描述

首先分析下RESET中断: 

首次连接主机,主机会下发从机复位


1、 CustomHID_Reset

这里写图片描述

这里写图片描述

• 初始化EP

• EP0:控制类型;发送NAK、接收Valid;设置接收buffer地址和长度;设置发送buffer地址 

• EP1:批量类型;发送NAK、接收Disable;设置发送buffer地址 

• EP2:批量类型;发送Disable、接收VALID;设置接收buffer地址和长度


这里写图片描述

这里写图片描述

设置状态标志:

这里写图片描述


2、CTR中断

 这里写图片描述 

• 硬件置位表示一个transaction正确完成了 

• 软件需要根据EP_ID和DIR来得知这次transaction是发生在哪个EP上的何种 

transaction(SETUP/OUT/IN transaction?)


这里写图片描述 


void CTR_LP(void)


这里写图片描述

1、extract highest priority endpoint number


这里写图片描述


(wIstr = _GetISTR());

EPindex = (u8)(wIstr & ISTR_EP_ID);


端口号为0时: 

/* save RX & TX status */ 

/* and set both to NAK */


这里写图片描述


参看传输方向


if((wIstr&ISTR_DIR)==0)//方向为IN,主机发送完成

{

    //首先清除CTR_TX

    _ClearEP_CTR_TX(ENDP0);      

    In0_Process();

     //恢复EP0的RX和TX的状态

     _SetEPRxStatus(ENDP0, SaveRState);

     _SetEPTxStatus(ENDP0, SaveTState);

}

else//接收,分为setup接收和数据接收

{

。。。

}


Setup0_Process()

这里写图片描述


第一步:从EP0的接收Packet buf中读取主机命令 

这里写图片描述


pInformation->ControlState = SETTING_UP;


第二步: 


这里写图片描述

交互设备描述符:

这里写图片描述


//函数指针参数为16位参数,返回值是指向8位变量的指针

u8 *(*CopyRoutine)(u16);

这里写图片描述 

【作用和意义】 通过这个函数获得用户的数据缓冲区地址,从而可以在OUT过程中把收到的数据拷贝到用户缓冲区,或在IN过程中把用户缓冲区的数据拷贝到USB发送缓冲区 

OUT过程,多个DATA_OUT传输,库需要多次调用回调函数CopyRoutine,返回将要容纳已经收在硬件buf中数据的缓 冲区指针 

IN过程,多个DATA_IN传输, 每次需要向主机传输数据时, USB库都会调用一次回调函数CopyRoutine,它返回一 个包含要发送的数据的缓冲区指针,库再把这个缓冲区的内容拷贝的硬件buf以择机发送出去


这里写图片描述

这里写图片描述

这里写图片描述

In0_Process(void)


这里写图片描述

完成描述符的输出准备:


DataStageln(void)

 ENDPOINT_INFO *pEPinfo = &pInformation->Ctrl_Info; 


ENDPOINT_INFO


这里写图片描述

pInformation

这里写图片描述


Out0_Process()

这里写图片描述


UserToMABufferCopy

这里写图片描述

这里写图片描述


关键字:USB  STM32  USB-FS-Device_Lib 引用地址:USB二:深入解析STM32_USB-FS-Device_Lib 库

上一篇:STM32 USB 枚举分析
下一篇:stm32f407系列单片机usb高速模式下的速度测试

推荐阅读最新更新时间:2024-11-12 17:18

STM32开发笔记53:STM32F4+DP83848以太网通信指南系列(七)
本章为系列指南的第七章,讲述如何在之前的基础上,编写程序在STM32上发送一个网络包,并使用WireShark进行验证。 先回顾一下之前的章节我们做好的准备工作,在《STM32F4+DP83848以太网通信指南第五章:MAC+DMA配置》结束时我们封装了一个DP83848的初始化函数,该函数完成了PHY的配置,MAC层的配置,DMA的配置,并且启用了以太网中断,函数命名为DP83848Init(),那么今天,我们要做的主要任务就是编写一个类似的DP83848Send(u8* data, u16 length)函数。 可以在本章的一开始跟大家剧透一个好消息,有了《STM32F4+DP83848以太网通信指南第四章:PHY配置》 和
[单片机]
<font color='red'>STM32</font>开发笔记53:STM32F4+DP83848以太网通信指南系列(七)
赛普拉斯USB-C 技术为三星DeX提供先进的移动计算体验
EZ-PD™控制器实现即插即用的USB-C连接和快速充电 加利福尼亚州圣何塞,2018年5月14日 - 嵌入式解决方案领导者赛普拉斯半导体公司(纳斯达克股票代码:CY)今日宣布,其USB-C技术为三星的DeX Pad带来多功能连接和快速充电能力,为三星DeX用户提供全屏幕桌面体验。三星DeX是一项为移动设备提供类似PC体验的服务。DeX Pad附件可轻松连接外围设备,如显示器、键盘和鼠标等,便于Samsung DeX的使用。赛普拉斯EZ-PD™控制器允许三星解决方案将兼容的Samsung Galaxy设备无缝连接到DeX Pad的USB-C端口,以访问外设并进行USB PD快速充电。连接后,智能手机将把类似PC的用户界面投影到
[嵌入式]
赛普拉斯<font color='red'>USB</font>-C 技术为三星DeX提供先进的移动计算体验
STM32 之 Unique_Device_ID
芯片的独有ID号,可以用来对芯片进行加密。 包含函数: (1)Main C语言: Codee#14694 /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 实验平台 : ST 官方三合一套件 + 硬件 : STM32F103C8T6 + 开发平台 : IAR For ARM 5.40 + 仿真器 : J-Link + 日期 : 2010-10-28 + 频率 :HSE = 8MHz ,主频 = 72MHz +++++++++++++++++++++++++++++++++++++++++
[单片机]
<font color='red'>STM32</font> 之 Unique_Device_ID
STM32固件的小结
储存器地址 由51引入   实际上,单片机对任何端口的控制都是通过操作内存实现的,说出这句话很抽象,我们可以以从51单片机类比来说明这是什么意思。 如下的电路图: 如果我们想点亮LED_G,在51中我们可能会如此编程 #include reg51.h #define PB = 0xfe 或者 #include reg51.h sbit LED_G = PB^5; void main() { LED_G = 0; } 其中第一种叫宏定义,第二种叫位定义。那么为什么我们使用这些操作后PB0就输出低电平了呢。我们可以从 reg51.h 这个文件中看出原因。 可以看见,在头文件中对类似P0的IO口进行
[单片机]
<font color='red'>STM32</font>固件<font color='red'>库</font>的小结
STM32应用相关问答解析
这里就STM32用户咨询到的几个问题,稍加整理分享出来供君参考。 第1问:我目前使用STM32G0B1,代码里有设计BOOT代码,想在跳转前清理各类使用过的外设寄存器。请问STM32有没有复位所有外设寄存器的函数? 答:其实,每个STM32系列都有相关寄存器和应用函数用来针对指定外设或挂在指定总线上的外设进行批量复位,即令其回归到初始复位状态。以STM32G0系列为例,在STM32HAL库里就有类似下面的各种实现函数。 关于这个话题可以参考本公众号另外一篇文章《话说STM32外设复位》,那里有更多详细解读,此处就不再赘述。 第2问:我在使用STM32F730的时候,如果SRAM设置到0x20010000 - 0x200
[单片机]
<font color='red'>STM32</font>应用相关问答解析
STM32-串口实验学习笔记
USART1_IRQHandler(void)函数: 当串口1发生了相应的中断,就会跳到改函数执行。这里设计了一个小小的接收协议(系统并未定义):通过这个函数,配合一个数组USART_RX_BUF ,一个接收状态寄存器USART_RX_STA实现对串口的数据的接收管理。USART_RX_BUF 最大值为64,也就是一次接收的数据最大不能超过64字节。USART_RX_STA是一个接收状态寄存器,其各位的定义如表所示: (注意:这个是作者设计的协议,怎样判断串口接收一组数据完毕?由于每次接收的数据长度不一样,少的就3个8位数据,多的时候有十多个,这个数据个数是不定的,且没规律的数据,有什么好的方法让它接收完整? 协议的设
[单片机]
STM32-串口实验学习笔记
STM32 CAN总线调试经验
前言 STM32 CAN代码网上很多,但大都是讲如何配置的,对于一些原理以及注意事项没有很清楚的说明。在实际调试过程中,两个设备间的通信只要设备CAN的配置一样基本就可以调通,但在增加设备的过程中,很容易出现多设备无法通信的问题,这里主要就这一问题进行说明。 硬件 STM32F042G4 + MAX3051 测试过程 测试板回来后,两个设备间的通信轻松搞定,但在增加设备的过程中,增加的设备总是不能正常通信。详细比对了一下配置,也没有任何问题。后来,仔细研读MAX3051的芯片手册,得到以下几个重要信息: MAX3051有四种工作模式 高速模式:数据传输速率可达1Mbps,但抗干扰能力弱,需要双绞线 斜率控制:可
[单片机]
基于STM32的便携体检装置的设计与实现
我国正在大力推进全面小康社会建设,社区卫生医疗体系是其中的一个重点。国外的社区医疗保险制度已经相当成熟,我国的社区卫生医疗体系还处于刚起步阶段。现在的社区医疗现状是: 大病小病都往大医院跑,因而大中医院就要承担大部分的医疗压力。虽然政府推出大力发展社区医院的政策,由于社区医院资金紧张不可能配备完善的医疗检测仪器,所以便携式医疗仪器的发展及在社区中推广使用就显得尤为重要。本文设计的一个便携式体检装置,用以检测人体的主要基本生理指标,如身高、体重、血压等,辅助社区医生的诊断。 1. 系统组成及设计方法 体检箱采用STM32 为核心控制模块,该核心模块包括STM32 小系统,液晶触摸屏电路,SD 卡存储电路,按键电路等常用的面向用户
[单片机]
基于<font color='red'>STM32</font>的便携体检装置的设计与实现
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved