草帽王子 发表于 2021-5-1 16:24:23

第八十四章:CH32V103应用教程——USB模拟U盘

本帖最后由 草帽王子 于 2021-9-10 18:21 编辑

本章教程主要使用CH32V103 USB模拟U盘设备,此程序是移植而来,仅供参考。


1、USB简介及相关函数介绍

关于USB具体介绍,可参考前面章节。


2、硬件设计

本章教程主要进行USB模拟U盘设备,仅需用到开发板USB口。


3、软件设计

本章程序全在主函数中进行,具体程序如下:
main.c文件
/********************************** (C) COPYRIGHT *******************************
* File Name          : main.c
* Author             : WCH
* Version            : V1.0.0
* Date               : 2019/10/15
* Description      : Main program body.
*******************************************************************************/
#include "debug.h"
#include "string.h"

#define DevEP0SIZE0x40

/* Device Descriptor */
const UINT8MyDevDescr[] =
{
      0x12,            //设备描述符长度,18字节
      0x01,            //描述符类型,0x01为设备描述符
      0x10, 0x01,      //本设备所使用USB版本协议,因为是小端结构,所以低字节在前,即USB1.1版本为0x10,0x01,USB2.0为0x00,0x02
      0x00,            //类代码,此处不在设备描述符中定义设备类,而在接口描述符中定义设备类。对于大多数标准的USB设备类,该字段通常设置为0,而在接口描述符中的bInterfaceClass中指定接口所实现的功能
      0x00,            //子类代码,当类代码bDeviceClass为0时,下面的子类代码bDeviceSubClass也必须为0。
      0x00,            //设备所使用的协议,协议代码由USB协会规定。当该字段为0时,表示设备不使用类所定义的协议。
      DevEP0SIZE,      //端点0的最大包长,可以取值8、16、32、64,此处为64字节
      0x86, 0x1A,      //厂商ID
      0x22, 0x57,      //产品设备ID
      0x00, 0x01,      //设备版本号
      0x01,            //描述厂商的字符串索引值。当该值为0时,表示没有厂商字符串
      0x02,            //描述产品的字符串索引值。当该值为0时,表示没有产品字符串
      0x03,            //描述设备的序列号字符串索引值。当该值为0时,表示没有序列号字符串
      0x01,            //可能的配置数,通常为1
};

/* Configration Descriptor */
const UINT8MyCfgDescr[] =
{
      //配置描述符
      0x09,            //配置描述符长度,标准USB配置描述符长度为9字节
      0x02,            //描述符类型,配置描述符为0x02
      0x20, 0x00,      //配置描述符集合总长度,32字节
      0x01,            //该配置所支持的接口数,1个接口
      0x01,            //表示该配置的值
      0x00,            //描述该配置的字符串的索引值,0x00表示没有字符串
      0xA0,            //描述设备的一些属性,如供电方式和唤醒等,0xA0表示设备总线供电且支持远程唤醒
      0x32,            //设备需要从总线获取的最大电流量,0x32表示最大电流100ma

      //接口描述符,接口描述符不能单独返回,必须附着在配置描述符后一并返回
      0x09,            //接口描述符长度,标准的USB接口描述符长度为9字节
      0x04,            //描述符类型,接口描述符为0x04
      0x00,            //该接口的编号,从0开始,此处为0x00
      0x00,            //该接口的备用编号,通常设置为0
      0x02,            //该接口所使用的端点数,0x02表示使用2个端点。如果该字段为0,则表示没有非0端点,只使用默认的控制端点
      0x08,            //该接口所使用的类,0x08为大容量存储设备类
      0x06,            //该接口所使用的子类,0x06,即SCSI透明命令集
      0x50,            //该接口所使用的协议,协议代码有3种:0x00、0x01、0x50,前两种需要使用中断传输,最后一种仅使用批量传输
      0x00,            //该接口的字符串的索引值,0x00表示没有字符串

      //端点描述符,端点描述符不能单独返回,必须附着在配置描述符后一并返回
      0x07,            //端点描述符长度,标准的USB端点描述符长度为7字节
      0x05,            //描述符类型,端点描述符为0x05
      0x01,            //该端点的地址,0x01表示端点1作为输出,最高位D7为该端点的传输方向,1为输入,0为输出。D3-D0为端点号,可设置为0-7,D6-4保留,设为0.
      //关于端点属性,最低两位D1-0表示该端点的传输类型,0为控制传输,1为等时传输,2为批量传输,3为中断传输。
      0x02,            //该端点的属性,此处为批量传输方式
      DevEP0SIZE, 0x00,//该端点支持的最大包长度,此处设置为64字节
      0x00,            //端点的查询时间

      //端点描述符,端点描述符不能单独返回,必须附着在配置描述符后一并返回
      0x07,            //端点描述符长度,标准的USB端点描述符长度为7字节
      0x05,            //描述符类型,端点描述符为0x05
      0x81,            //该端点的地址,0x81表示端点1作为输入,最高位D7为该端点的传输方向,1为输入,0为输出。D3-D0为端点号,可设置为0-7,D6-4保留,设为0.
      //关于端点属性,最低两位D1-0表示该端点的传输类型,0为控制传输,1为等时传输,2为批量传输,3为中断传输。
      0x02,            //该端点的属性,此处为批量传输方式
      DevEP0SIZE, 0x00,//该端点支持的最大包长度,此处设置为64字节
      0x00,            //端点的查询时间
};

const UINT8 MyProductIDInfo[] =
{
      0x14,0x03, 0x32,0x00,0x30,0x00,0x31,0x00,0x37,0x00,0x2D,0x00,0x32,0x00,0x2D,0x00,0x32,0x00,0x35,0x00
};
/* Language Descriptor */
const UINT8MyLangDescr[] = { 0x04, 0x03, 0x09, 0x04 };

/* Manufactor Descriptor */
const UINT8MyManuInfo[] = { 0x0A,0x03,0x5F,0x6c,0xCF,0x82,0x81,0x6c,0x52,0x60 };

/* Product Information */
const UINT8MyProdInfo[] = {0x14,0x03,0x43,0x00,0x48,0x00,0x35,0x00,0x35,0x00,0x34,0x00,0x5F,0x00,0x43,0x00,0x44,0x00,0x43,0x00, };

//Get Max LUN 请求,该字节表示设备有多少个逻辑单元,值为0时表示有一个逻辑单元,值为1时表示有两个逻辑单元,以此类推,最大可以取15
//定义一个最大逻辑单元的变量,跟描述符类型一样,使用数组的方式来定义,尽管他只有一个元素。
const UINT8MAX_LUN[] = {0};

//INQUIRY inform
const UINT8 DBINQUITY[]=
{
                        0x00,         //Peripheral Device Type
                        0x80,         //
                        0x02 ,          //ISO/ECMA
                        0x02 ,          //
                        0x1f ,          //Additional Length

                        00 ,            //Reserved
                        00 ,            //Reserved
                        00 ,            //Reserved

                        'w' ,         //Vendor Information
                        'c' ,         //
                        'h' ,         //
                        '.' ,         //
                        'c' ,         //
                        'n' ,         //
                        ' ' ,         //
                        ' ' ,         //

                        0xc7,         //Product Identification
                        0xdf,         //
                        0xba,         //
                        0xe3,         //
                        0xb5,         //
                        0xe7,         //
                        0xd7,         //
                        0xd3,         //
                        0x55,         //
                        0xc5,         //
                        0xcc,         //
                        0xb7,         //
                        0xbd,         //
                        0xb0,         //
                        0xb8,         //
                        0x00,         //

                        '1' ,         //Product Revision Level
                        '.' ,         //
                        '1' ,         //
                        '0'             //
};

#define NUMOFBLOCK      0x40
#define LASTLGCBLOCK    0x3f

const UINT8 DBCAPACITY[]={(LASTLGCBLOCK>>24)&0xFF,(LASTLGCBLOCK>>16)&0xFF,(LASTLGCBLOCK>>8)&0xFF,LASTLGCBLOCK&0xFF,0x00,0x00,0x02,0x00};
const UINT8 modesense3F[]={0x0b, 0x00, 0x00/*0x80*/, 0x08 , (NUMOFBLOCK>>24)&0xFF,(NUMOFBLOCK>>16)&0xFF,(NUMOFBLOCK>>8)&0xFF,NUMOFBLOCK&0xFF,0x00, 0x00, 0x02, 0x00 };   //写保护(0x80换成0x00可以去除写保护)

//DBR(DOS Boot Record):磁盘操作系统引导记录
//DBR是每个逻辑分区的一个引导记录,里面记录了该分区的众多重要信息以及引导代码,所以十分重要。在U盘系统中,必须有DBR
//DBR占据逻辑分区的0扇区,大小通常为512字节,DBR各个部分的意义见《圈圈教你玩USB》
const UINT8 DBR=
{
               0xeb,0x3c,0x90,0x4d,0x53,0x44,0x4f,0x53, 0x35,0x2e,0x30,0x00,0x02,0x01,0x06,0x00,
               0x02,0x00,0x02,NUMOFBLOCK&0xff,(NUMOFBLOCK>>8)&0xff,0xf8,0x01,0x00, 0x01,0x00,0x01,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x80,0x00,0x29,0xc3, 0xa5,0x20,0xd8,0x4e,0x4f,0x20,0x4e,0x41,
               0x4d,0x45,0x20,0x20,0x20,0x20,0x46,0x41, 0x54,0x31,0x32,0x20,0x20,0x20,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x55,0xaa,
};

//FAT(File Allocation Table):文件分配表
//在DBR之后,就是FAT区(通常有两个,一个为副本)
const UINT8 FAT=
{
               0xF8,0xFF,0xFF,0xFF,0x0F,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

const UINT8 ZERO=
{
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

//UFI通讯
#define FORMAT_UNIT   0x04
#define INQUIRY         0x12
#define FORMATCAP       0x23
#define MODE_SELECT   0x15
#define MODE_SENSE5   0x5A
#define MODE_SENSE      0x1A
#define PER_RES_IN      0x5E
#define PER_RES_OUT   0x5F
#define PRE_OR_MED      0x1E
#define READ            0x28
#define READ_CAPACITY   0x25
#define RELEASE         0x17
#define REQUEST_SENSE   0x03
#define RESERVE         0x16
#define STA_STO_UNIT    0x1B
#define SYN_CACHE       0x35
#define TEST_UNIT       0x00
#define VERIFY          0x2F
#define WRITE         0x2A
#define WRITE_BUFFER    0x3B

typedef union _CBWCB
{
    unsigned char buf1;
}CBWCB;
typedefunion _MASS_PARA
{
    unsigned char buf;
    struct_SENSE
    {
      unsigned char ErrorCode;
      unsigned char Reserved1;
      unsigned char SenseKey;
      unsigned char Information;
      unsigned char AddSenseLength;
      unsigned char Reserved2;
      unsigned char AddSenseCode;
      unsigned char AddSenseCodeQua;
      unsigned char Reserved3;
    }Sense;
    struct_CBW
    {
      unsigned char dCBWsig;
      unsigned char dCBWTag;
      unsigned long dCBWDatL;
      unsigned char bmCBWFlags;
      unsigned char bCBWLUN;
      unsigned char bCBWCBLength;
      CBWCB         cbwcb;
    }cbw;
    struct _CSW
    {
      unsigned char buf2;
    }csw;
}MASS_PARA;

union {
unsigned long mDataLength;                   //数据长度
unsigned char mdataLen;                   //
} UFI_Length;
unsigned char mdCBWTag;                   //dCBWTag
MASS_PARAMassPara;
UINT8 CH32BULKUP=0;                        //数据上传
UINT8 CH32BULKDOWN = 0;                      //数据下传
UINT8 CH32CSW=0;                           //CSW上传标志
unsigned charBcswStatus;                   //CSW状态
unsigned charmSenseKey;
unsigned charmASC;

unsigned char *pBuf;

unsigned long SecNum;                        //当前操作的扇区号

unsigned char dat_tran_flag = 0;
/**********************************************************/
UINT8   Ready = 0;
UINT8   UsbConfig;
UINT8   SetupReqCode;
UINT16SetupReqLen;
//cdc参数
UINT8 LineCoding={0x00,0xe1,0x00,0x00,0x00,0x00,0x08};   //初始化波特率为57600,1停止位,无校验,8数据位。

#defineSET_LINE_CODING                0x20            // Configures DTE rate, stop-bits, parity, and number-of-character
#defineGET_LINE_CODING                0x21            // This request allows the host to find out the currently configured line coding.
#defineSET_CONTROL_LINE_STATE         0x22            // This request generates RS-232/V.24 style control signals.

volatile UINT8 UpPoint2_Busy= 0;    //上传端点是否忙标志

const UINT8 *pDescr;

/* Endpoint Buffer */
__attribute__ ((aligned(4)))UINT8 EP0_Databuf; //ep0(64)+ep4_out(64)+ep4_in(64)
__attribute__ ((aligned(4)))UINT8 EP1_Databuf;    //ep1_out(64)+ep1_in(64)
__attribute__ ((aligned(4)))UINT8 EP2_Databuf;    //ep2_out(64)+ep2_in(64)
__attribute__ ((aligned(4)))UINT8 EP3_Databuf;    //ep3_out(64)+ep3_in(64)

void USBHD_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

/*******************************************************************************
* Function Name: UFI_Hunding
* Description    : 命令的分类与识别 UFICMD
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void UFI_Hunding(void )
{
      switch(MassPara.cbw.cbwcb.buf1)
      {
            //查询命令 INQUIRY,操作代码为0x12
            case INQUIRY:
                pBuf = (UINT8 *)DBINQUITY;                                                      //查询U盘信息
                if(UFI_Length.mDataLength>sizeof(DBINQUITY)) UFI_Length.mDataLength=sizeof(DBINQUITY);
                BcswStatus=0;
                mSenseKey=0;
                mASC=0;
                break;

            //主机通常使用WRITE命令往设备写入实际的磁盘数据,操作代码为0x2A
            case WRITE:
                UFI_Length.mDataLength=(((UINT32)MassPara.cbw.cbwcb.buf1<<8) | (UINT32)MassPara.cbw.cbwcb.buf1)*512;//发送长度
                SecNum = ((UINT32)MassPara.cbw.cbwcb.buf1<<24) | ((UINT32)MassPara.cbw.cbwcb.buf1<<16) | ((UINT32)MassPara.cbw.cbwcb.buf1<<8) | (UINT32)MassPara.cbw.cbwcb.buf1;//起始扇区号
                dat_tran_flag = 1;
                BcswStatus=0;
                mSenseKey=0;
                mASC=0;
                break;
            case PRE_OR_MED:

            case TEST_UNIT:

            case 0x3b:
                CH32BULKDOWN=0;
                CH32BULKUP=0;
                BcswStatus=0;
                mSenseKey=0;
                mASC=0;
                break;

            //主机通常使用READ命令来读取实际的磁盘数据,操作代码为0x28
            case READ:
                UFI_Length.mDataLength=(((UINT32)MassPara.cbw.cbwcb.buf1<<8) | (UINT32)MassPara.cbw.cbwcb.buf1)*512;//发送长度
                SecNum = ((UINT32)MassPara.cbw.cbwcb.buf1<<24) | ((UINT32)MassPara.cbw.cbwcb.buf1<<16) | ((UINT32)MassPara.cbw.cbwcb.buf1<<8) | (UINT32)MassPara.cbw.cbwcb.buf1;//起始扇区号
                dat_tran_flag = 1;
                BcswStatus=0;
                mSenseKey=0;
                mASC=0;
                break;

            //REQUEST SENSE命令用来探测上一个命令执行失败的原因,主机可在每个命令之后使用该命令来读取命令执行的情况,其命令代码为0x03
            case REQUEST_SENSE:
                MassPara.Sense.ErrorCode=0x70;
                MassPara.Sense.Reserved1=0;
                MassPara.Sense.SenseKey=mSenseKey;
                MassPara.Sense.Information=0;
                MassPara.Sense.Information=0;
                MassPara.Sense.Information=0;
                MassPara.Sense.Information=0;
                MassPara.Sense.AddSenseLength=0x0a;
                MassPara.Sense.Reserved2=0;
                MassPara.Sense.Reserved2=0;
                MassPara.Sense.Reserved2=0;
                MassPara.Sense.Reserved2=0;
                MassPara.Sense.AddSenseCode=mASC;
                MassPara.Sense.AddSenseCodeQua=0;
                MassPara.Sense.Reserved3=0;
                MassPara.Sense.Reserved3=0;
                MassPara.Sense.Reserved3=0;
                MassPara.Sense.Reserved3=0;
                pBuf=MassPara.buf;
                if ( UFI_Length.mDataLength > 18 ) UFI_Length.mDataLength = 18;
                BcswStatus=0;
                mSenseKey=0;
                mASC=0;
                break;

            //读容量命令,可以让主机读取到当前存储媒介的容量,此命令读到的才是实际的磁盘容量
            case READ_CAPACITY:
                if ( UFI_Length.mDataLength > sizeof(DBCAPACITY) ) UFI_Length.mDataLength = sizeof(DBCAPACITY);
                pBuf=(unsigned char*)DBCAPACITY;
                BcswStatus=0;
                mSenseKey=0;
                mASC=0;
                break;

            case MODE_SENSE:
                if ( UFI_Length.mDataLength > sizeof(modesense3F) ) UFI_Length.mDataLength = sizeof(modesense3F);
                pBuf=(unsigned char*)modesense3F;
                BcswStatus=0;
                mSenseKey=0;
                mASC=0;
                break;
            default:
                mSenseKey=5;
                if (MassPara.cbw.cbwcb.buf1 == FORMATCAP)
                  mASC=0x20;
                else
                  mASC=0x24;
                BcswStatus=1;
                if(CH32BULKUP)
                {
                  R8_UEP1_CTRL = R8_UEP1_CTRL | UEP_T_RES_STALL;
                }
                else
                {
                  R8_UEP1_CTRL = R8_UEP1_CTRL | UEP_R_RES_STALL;
                }
                break;
            }
}
/*******************************************************************************
* Function Name: mCH32UpCsw
* Description    : 批量协议状态上传
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void mCH32UpCsw()
{
    unsigned char i;                                                   //如果数据为0
    pBuf=&MassPara.buf;
    CH32CSW=0;                                                         //上传CSW
    CH32BULKUP=0;                                                      //取消数据上传
    MassPara.buf=0x55;                                              //dCSWSignature
    MassPara.buf=0x53;
    MassPara.buf=0x42;
    MassPara.buf=0x53;
    MassPara.buf=mdCBWTag;
    MassPara.buf=mdCBWTag;
    MassPara.buf=mdCBWTag;
    MassPara.buf=mdCBWTag;
    MassPara.buf=UFI_Length.mdataLen;
    MassPara.buf=UFI_Length.mdataLen;
    MassPara.buf=UFI_Length.mdataLen;
    MassPara.buf=UFI_Length.mdataLen;
    MassPara.buf=BcswStatus;
    for(i = 0;i<13;i++)
    {
      pEP1_RAM_Addr = *pBuf;
      pBuf++;
    }
    R8_UEP1_T_LEN = 13;
    R8_UEP1_CTRL = (R8_UEP1_CTRL & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;          // 允许上传
}
/*******************************************************************************
* Function Name: mCH32BulkOnly
* Description    : 批量协议处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void mCH32BulkOnly(){
    if(MassPara.buf==0x55){
      if(MassPara.buf==0x53){
         if(MassPara.buf==0x42){
                if(MassPara.buf==0x43){
                  UFI_Length.mdataLen = *(unsigned char *)(&MassPara.cbw.dCBWDatL);             /* 将PC机的低字节在前的16位字数据转换为C51的高字节在前的数据 */
                  UFI_Length.mdataLen = *( (unsigned char *)(&MassPara.cbw.dCBWDatL) + 1 );
                  UFI_Length.mdataLen = *( (unsigned char *)(&MassPara.cbw.dCBWDatL) + 2 );
                  UFI_Length.mdataLen = *( (unsigned char *)(&MassPara.cbw.dCBWDatL) + 3 );
                  mdCBWTag=MassPara.buf;
                  mdCBWTag=MassPara.buf;
                  mdCBWTag=MassPara.buf;
                  mdCBWTag=MassPara.buf;                                          //取出数据长度
                  if(UFI_Length.mDataLength){
                            CH32BULKDOWN=(MassPara.cbw.bmCBWFlags&0X80)?0:1;                //判断是上传还是下传数据
                            CH32BULKUP=(MassPara.cbw.bmCBWFlags&0X80)?1:0;
                        }
                        CH32CSW=1;
                        dat_tran_flag = 0; //数据传输标志位先清零
                        UFI_Hunding();                                                      //调用UFI协议处理
               }
                else
                R8_UEP1_CTRL = R8_UEP1_CTRL | UEP_T_RES_STALL ;
          }
         else
            R8_UEP1_CTRL = R8_UEP1_CTRL | UEP_T_RES_STALL ;
         }
      else
      R8_UEP1_CTRL = R8_UEP1_CTRL | UEP_T_RES_STALL ;
   }
    else
    R8_UEP1_CTRL = R8_UEP1_CTRL | UEP_T_RES_STALL ;
}

/*******************************************************************************
* Function Name: CH32bulkUpData
* Description    : 批量协议上传
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
unsigned char file_dat; //文件大小固定64字节

//文件前四个字节(16H 75H 73H 62H) //测试阶段暂用“fusb”

void CH32bulkUpData()
{                              //调用端点1上传数据
    unsigned char len,i;
    unsigned char rootdir = 0;   //当前读目录项标志
    unsigned char datfield = 0;//数据区标志
    //根据读取的地址,分析是否切换pBuf
    if(dat_tran_flag)
    {
      if((UFI_Length.mDataLength&0x1ff)==0)
      {
            if(SecNum==0)
            {
                pBuf = (UINT8 *)DBR;
            }
            else if(SecNum==6 || SecNum==7)
            {
                pBuf = (UINT8 *)FAT;
            }
            else
            {
                pBuf = (UINT8 *)ZERO;
            }
            if(SecNum == 8)
            {
                rootdir = 1;
            }
            if(SecNum==40)
            {
                datfield = 1;
            }
            SecNum++;
      }
    }

    if(UFI_Length.mDataLength>MAX_PACKET_SIZE)
    {
      len=MAX_PACKET_SIZE;
      UFI_Length.mDataLength-=MAX_PACKET_SIZE;
    }
    else
    {
      len= (unsigned char) UFI_Length.mDataLength;
      UFI_Length.mDataLength=0;
      CH32BULKUP=0;
    }

    for(i = 0;i<len;i++)
    {
      pEP1_RAM_Addr = *pBuf;
      pBuf++;
    }
    /* 改写 */
    if(rootdir)       /* 改写根目录 */
    {
      memcpy(&pEP1_RAM_Addr,"WCH_CFG TXT ",12);
      pEP1_RAM_Addr=2;
      pEP1_RAM_Addr= 64;
    }
    if(datfield)      /* 改写数据区 */
    {
      //ReadDataFlash(0,64,&pEP1_RAM_Addr);
    }
    R8_UEP1_T_LEN = len;
    R8_UEP1_CTRL = (R8_UEP1_CTRL & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;      // 允许上传
}
void mCH32BulkDownData()
{
    UINT8 len;
    len = R8_USB_RX_LEN;
    if(dat_tran_flag)   //传输数据
    {
      if((UFI_Length.mDataLength&0x1ff)==0)
      {
            if((pEP1_RAM_Addr=='f') && (memcmp(&pEP1_RAM_Addr,"usb",3)==0))
            {
                //FLASH_Write(0,&pEP1_RAM_Addr,64);   //同时写入Flash
            }
      }
    }
    UFI_Length.mDataLength-=len;
    if(UFI_Length.mDataLength==0)
    {
      CH32BULKDOWN=0;
      mCH32UpCsw();
    }
}
/*******************************************************************************
* Function Name: USB_DevTransProcess
* Description    : USB device transfer process.
* Input          : None
* Return         : None
*******************************************************************************/
void USB_DevTransProcess( void )
{
    UINT8len, length, chtype;
    UINT8intflag, errflag = 0;

    intflag = R8_USB_INT_FG;

    if( intflag & RB_UIF_TRANSFER )
    {
      switch ( R8_USB_INT_ST & ( MASK_UIS_TOKEN | MASK_UIS_ENDP ) )
      {
            case UIS_TOKEN_SETUP:
                R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
                len = R8_USB_RX_LEN;
                if ( len == sizeof( USB_SETUP_REQ ) )
                {
                  SetupReqLen = pSetupReqPak->wLength;
                  SetupReqCode = pSetupReqPak->bRequest;
                  chtype = pSetupReqPak->bRequestType;
                  len = 0;
                  errflag = 0;
                  if ( ( pSetupReqPak->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )
                  {
                        if(SetupReqCode == 0xFE)                                 //GET MAX LUN
                        {
                            pDescr = (PUINT8)( &MAX_LUN );
                            len = 1;
                            if ( SetupReqLen > len )
                            {
                              SetupReqLen = len;                                 // 限制总长度
                            }
                            len = SetupReqLen >= DEFAULT_ENDP0_SIZE ? DEFAULT_ENDP0_SIZE : SetupReqLen;// 本次传输长度
                            memcpy( pEP0_RAM_Addr, pDescr, len );                  /* 加载上传数据 */
                            //SetupLen -= len;
                            pDescr += len;
                        }
                        else
                            errflag = 0xFF;
                  }
                  else
                  {
                        switch( SetupReqCode )
                        {
                            case USB_GET_DESCRIPTOR:
                            {
                              switch( ((pSetupReqPak->wValue)>>8) )
                              {
                                    case USB_DESCR_TYP_DEVICE:
                                        pDescr = MyDevDescr;
                                        len = MyDevDescr;
                                        break;

                                    case USB_DESCR_TYP_CONFIG:
                                        pDescr = MyCfgDescr;
                                        len = MyCfgDescr;
                                        break;

                                    case USB_DESCR_TYP_STRING:
                                        switch( (pSetupReqPak->wValue)&0xff )
                                        {
                                          case 1:
                                                pDescr = MyManuInfo;
                                                len = MyManuInfo;
                                                break;

                                          case 2:
                                                pDescr = MyProdInfo;
                                                len = MyProdInfo;
                                                break;

                                          case 0:
                                                pDescr = MyLangDescr;
                                                len = MyLangDescr;
                                                break;

                                          case 3:
                                                pDescr = (PUINT8)( &MyProductIDInfo );
                                                len = sizeof( MyProductIDInfo );
                                                break;

                                          default:
                                                errflag = 0xFF;
                                                break;
                                        }
                                        break;

                                    default :
                                        errflag = 0xff;
                                        break;
                              }

                              if( SetupReqLen>len )   SetupReqLen = len;
                              len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
                              memcpy( pEP0_DataBuf, pDescr, len );
                              //SetupReqLen -= len;
                              pDescr += len;
                            }
                            break;

                            case USB_SET_ADDRESS:
                              SetupReqLen = (pSetupReqPak->wValue)&0xff;
                              break;

                            case USB_GET_CONFIGURATION:
                              pEP0_DataBuf = UsbConfig;
                              if ( SetupReqLen > 1 ) SetupReqLen = 1;
                              break;

                            case USB_SET_CONFIGURATION:
                              UsbConfig = (pSetupReqPak->wValue)&0xff;
                              break;

                            case USB_CLEAR_FEATURE:
                              if( ( pSetupReqPak->bRequestType & 0x1F ) == USB_REQ_RECIP_DEVICE )                  /* 清除设备 */
                              {
                                    if((pSetupReqPak->wValue) == 0x01 )
                                    {
                                        if( MyCfgDescr[ 7 ] & 0x20 )
                                        {
                                          /* 唤醒 */
                                        }
                                        else
                                        {
                                          errflag = 0xFF;                                        /* 操作失败 */
                                        }
                                    }
                                    else
                                    {
                                        errflag = 0xFF;                                          /* 操作失败 */
                                    }
                              }
                              else if ( ( pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )
                              {
                                    switch( (pSetupReqPak->wIndex)&0xff )
                                    {
                                        case 0x83:
                                          R8_UEP3_CTRL = (R8_UEP3_CTRL & ~ ( RB_UEP_T_TOG | MASK_UEP_T_RES )) | UEP_T_RES_NAK;
                                          break;

                                        case 0x03:
                                          R8_UEP3_CTRL = (R8_UEP3_CTRL & ~ ( RB_UEP_T_TOG | MASK_UEP_R_RES )) | UEP_R_RES_ACK;
                                          break;

                                        case 0x82:
                                          R8_UEP2_CTRL = (R8_UEP2_CTRL & ~( RB_UEP_T_TOG|MASK_UEP_T_RES )) | UEP_T_RES_NAK;
                                          break;

                                        case 0x02:
                                          R8_UEP2_CTRL = (R8_UEP2_CTRL & ~( RB_UEP_R_TOG|MASK_UEP_R_RES )) | UEP_R_RES_ACK;
                                          break;

                                        case 0x81:
                                          R8_UEP1_CTRL = (R8_UEP1_CTRL & ~( RB_UEP_T_TOG|MASK_UEP_T_RES )) | UEP_T_RES_NAK;
                                          if(CH32CSW)
                                          {
                                                CH32CSW=0;
                                                mCH32UpCsw();
                                          }
                                          break;

                                        case 0x01:
                                          R8_UEP1_CTRL = (R8_UEP1_CTRL & ~( RB_UEP_R_TOG|MASK_UEP_R_RES )) | UEP_R_RES_ACK;
                                          if(CH32CSW)
                                          {
                                                CH32CSW = 0;
                                                mCH32UpCsw();
                                          }
                                          break;

                                        default:
                                          errflag = 0xFF;
                                          break;

                                    }
                              }
                              else    errflag = 0xFF;
                              break;

                            case USB_SET_FEATURE:                                          /* Set Feature */
                              if( ( pSetupReqPak->bRequestType & 0x1F ) == USB_REQ_RECIP_DEVICE )                  /* 设置设备 */
                              {
                                    if(( pSetupReqPak->wValue) == 0x01 )
                                    {
                                        if( MyCfgDescr[ 7 ] & 0x20 )
                                        {
                                          ;
                                        }
                                        else
                                        {
                                          errflag = 0xFF;                                        /* 操作失败 */
                                        }
                                    }
                                    else
                                    {
                                        errflag = 0xFF;                                          /* 操作失败 */
                                    }
                              }
                              else if( ( pSetupReqPak->bRequestType & 0x1F ) == USB_REQ_RECIP_ENDP )             /* 设置端点 */
                              {
                                    if(( pSetupReqPak->wValue) == 0x00 )
                                    {
                                        switch(pSetupReqPak->wIndex)
                                        {
                                          case 0x83:
                                                R8_UEP3_CTRL = (R8_UEP3_CTRL & (~RB_UEP_R_TOG)) | UEP_T_RES_STALL;/* 设置端点3 IN STALL */
                                                break;
                                          case 0x03:
                                                R8_UEP3_CTRL = (R8_UEP3_CTRL & (~RB_UEP_R_TOG)) | UEP_R_RES_STALL;/* 设置端点3 OUT Stall */
                                                break;
                                          case 0x82:
                                                R8_UEP2_CTRL = (R8_UEP2_CTRL & (~RB_UEP_R_TOG)) | UEP_T_RES_STALL;/* 设置端点2 IN STALL */
                                                break;
                                          case 0x02:
                                                R8_UEP2_CTRL = (R8_UEP2_CTRL & (~RB_UEP_R_TOG)) | UEP_R_RES_STALL;/* 设置端点2 OUT Stall */
                                                break;
                                          case 0x81:
                                                R8_UEP1_CTRL = (R8_UEP1_CTRL & (~RB_UEP_R_TOG)) | UEP_T_RES_STALL;/* 设置端点1 IN STALL */
                                                break;
                                          case 0x01:
                                                R8_UEP1_CTRL = (R8_UEP1_CTRL & (~RB_UEP_R_TOG)) | UEP_R_RES_STALL;/* 设置端点1 OUT Stall */
                                                break;
                                          default:
                                                errflag = 0xFF;                                    /* 操作失败 */
                                                break;
                                        }
                                    }
                                    else
                                    {
                                        errflag = 0xFF;                                    /* 操作失败 */
                                    }
                              }
                              else
                              {
                                    errflag = 0xFF;                                          /* 操作失败 */
                              }
                              break;

                            case USB_GET_INTERFACE:
                              pEP0_DataBuf = 0x00;
                              if ( SetupReqLen > 1 ) SetupReqLen = 1;
                              break;

                            case USB_GET_STATUS:
                              pEP0_DataBuf = 0x00;
                              pEP0_DataBuf = 0x00;
                              if ( SetupReqLen > 2 ) SetupReqLen = 2;
                              break;

                            default:
                              errflag = 0xff;
                              break;
                        }
                  }
                }
                else    errflag = 0xff;

                if( errflag == 0xff)
                {
//                  SetupReqCode = 0xFF;
                  R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
                }
                else
                {
                  if( chtype & 0x80 )
                  {
                        len = (SetupReqLen>DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
                        SetupReqLen -= len;
                  }
                  elselen = 0;

                  R8_UEP0_T_LEN = len;
                  R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
                }
                break;

            case UIS_TOKEN_IN:
                switch( SetupReqCode )
                {
                  case USB_GET_DESCRIPTOR:
                        len = SetupReqLen >= DevEP0SIZE ? DevEP0SIZE : SetupReqLen;
                        memcpy( pEP0_DataBuf, pDescr, len );
                        SetupReqLen -= len;
                        pDescr += len;
                        R8_UEP0_T_LEN = len;
                        R8_UEP0_CTRL ^= RB_UEP_T_TOG;
                        break;

                  case USB_SET_ADDRESS:
                        R8_USB_DEV_AD = (R8_USB_DEV_AD&RB_UDA_GP_BIT) | SetupReqLen;
                        R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                        break;

                  default:
                        R8_UEP0_T_LEN = 0;
                        R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                        break;

                }
                break;

            case UIS_TOKEN_OUT:
                R8_UEP0_T_LEN = 0;
                R8_UEP0_CTRL ^= RB_UEP_R_TOG;//状态阶段,对IN响应NAK

                break;

            case UIS_TOKEN_OUT | 1:
                if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
                {
                  if(CH32BULKDOWN)
                  {
                        mCH32BulkDownData();                              //如果上传数据阶段则调用数据上传
                  }
                  else
                  {                                                      //不是数据下传则判断是否
                        length = R8_USB_RX_LEN;
                        if(!length)break;                                  //数据包长度为零则跳出
                        for(len=0;len!=length;len++)
                        {
                            MassPara.buf=pEP1_RAM_Addr;          //将数据读入到缓冲区
                        }
                        mCH32BulkOnly();
                        if(BcswStatus ==0 )
                        {
                            if(!CH32BULKDOWN){
                              if(CH32BULKUP) CH32bulkUpData();         //调用批量数据上传
                              else mCH32UpCsw();                        //test
                            }
                        }
                }
                }
                break;

            case UIS_TOKEN_IN | 1:
                if(CH32BULKUP)
                {
                  CH32bulkUpData();                           //调用数据上传
                }
                else if(CH32CSW)
                {
                  CH32CSW = 0;
                  mCH32UpCsw();                                 //上传CSW
                }
                else
                {
                  R8_UEP1_T_LEN = 0;
                  R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                }
                break;

            case UIS_TOKEN_OUT | 2:
                break;

            case UIS_TOKEN_IN | 2:
                R8_UEP2_T_LEN = 0;
                R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                UpPoint2_Busy = 0;
                break;

            case UIS_TOKEN_OUT | 3:
                if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
                {
                  len = R8_USB_RX_LEN;
                  DevEP3_OUT_Deal( len );
                }
                break;

            case UIS_TOKEN_IN | 3:
                R8_UEP3_T_LEN = 0;
                R8_UEP3_CTRL = (R8_UEP3_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                break;

            case UIS_TOKEN_OUT | 4:
                if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
                {
                  R8_UEP4_CTRL ^= RB_UEP_R_TOG;
                  len = R8_USB_RX_LEN;
                  DevEP4_OUT_Deal( len );
                }
                break;

            case UIS_TOKEN_IN | 4:
                R8_UEP4_T_LEN = 0;
                R8_UEP4_CTRL ^=RB_UEP_T_TOG;
                R8_UEP4_CTRL = (R8_UEP4_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                break;

            default:
                break;
      }
      R8_USB_INT_FG = RB_UIF_TRANSFER;
    }
    else if( intflag & RB_UIF_BUS_RST )
    {
      R8_USB_DEV_AD = 0;
      R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
      R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
      R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
      R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
      R8_USB_INT_FG |= RB_UIF_BUS_RST;
    }
    else if( intflag & RB_UIF_SUSPEND )
    {
      if ( R8_USB_MIS_ST & RB_UMS_SUSPEND ) {;}
      else{;}
      R8_USB_INT_FG = RB_UIF_SUSPEND;
    }
    else
    {
      R8_USB_INT_FG = intflag;
    }
}
/*******************************************************************************
* Function Name: Set_USBConfig
* Description    : Set USB clock.
* Input          : None
* Return         : None
*******************************************************************************/
void USBHD_ClockCmd(UINT32 RCC_USBCLKSource,FunctionalState NewState)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, NewState);
    EXTEN->EXTEN_CTR |= EXTEN_USBHD_IO_EN;
    RCC_USBCLKConfig(RCC_USBCLKSource);             //USBclk=PLLclk/1.5=48Mhz
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHD,NewState);
}
/*******************************************************************************
* Function Name: main
* Description    : Main program.
* Input          : None
* Return         : None
*******************************************************************************/
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
    USART_Printf_Init(115200);

    printf("USBHD Device Test\r\n");

    pEP0_RAM_Addr = EP0_Databuf;
    pEP1_RAM_Addr = EP1_Databuf;
    pEP2_RAM_Addr = EP2_Databuf;
    pEP3_RAM_Addr = EP3_Databuf;
    USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);
    USB_DeviceInit();
    NVIC_EnableIRQ( USBHD_IRQn );
    while(1)
    {
      ;
    }
}

/*******************************************************************************
* Function Name: DevEP1_OUT_Deal
* Description    : Deal device Endpoint 1 OUT.
* Input          : l: Data length.
* Return         : None
*******************************************************************************/
void DevEP1_OUT_Deal( UINT8 l )
{
    ;
}

/*******************************************************************************
* Function Name: DevEP2_OUT_Deal
* Description    : Deal device Endpoint 2 OUT.
* Input          : l: Data length.
* Return         : None
*******************************************************************************/
void DevEP2_OUT_Deal( UINT8 l )
{
    ;
}

/*******************************************************************************
* Function Name: DevEP3_OUT_Deal
* Description    : Deal device Endpoint 3 OUT.
* Input          : l: Data length.
* Return         : None
*******************************************************************************/
void DevEP3_OUT_Deal( UINT8 l )
{
    ;
}

/*******************************************************************************
* Function Name: DevEP4_OUT_Deal
* Description    : Deal device Endpoint 4 OUT.
* Input          : l: Data length.
* Return         : None
*******************************************************************************/
void DevEP4_OUT_Deal( UINT8 l )
{
    ;
}

/*******************************************************************************
* Function Name: USB_IRQHandler
* Description    : This function handles USB exception.
* Input          : None
* Return         : None
*******************************************************************************/
void USBHD_IRQHandler (void)
{
    USB_DevTransProcess();
}main.c文件中描述符部分都进行了注释,便于大家理解,其余部分可以参考《圈圈教你玩USB》。关于USB设备传输过程,可结合应用手册关于USB寄存器介绍进行理解学习。


4、下载验证

将编译好的程序下载到开发板并复位,打开串口调试助手,串口打印如下:
用公对公USB线将开发板与电脑连接起来,打开磁盘驱动器可以看到多了一个U盘设备,如图所示:
查看此电脑,可以看到多了一个U盘,如图所示:

83、CH32V103 USB模拟U盘.rar

链接:https://pan.baidu.com/s/1NVWBRbfnxL2WLHEFdXFs1Q
提取码:sw7n
复制这段内容后打开百度网盘手机App,操作更方便哦



页: [1]
查看完整版本: 第八十四章:CH32V103应用教程——USB模拟U盘