【GD32F427开发板试用】硬件SPI通信驱动CH376芯片,用单片机实现U盘数据下载

news2024/12/22 10:24:58

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:周文杰

SPI通信作为单片机多种基础数据传输模式中的一种,驱动外部芯片CH376实现数据导出到U盘功能在实际工程项目中是很方便的。本文提供了GD32F427驱动CH376实现数据从U盘导出的完整硬件原理图和软件程序。

硬件连接方面

软件程序方面

SPI0的GPIO引脚初始化

void gpio_config(void)
{
    /* configure SPI0 GPIO */
    gpio_af_set(GPIOA, GPIO_AF_5, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7);

    /* set SPI1_NSS as GPIO*/
    gpio_mode_set(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_4);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_4);
    gpio_init(GPIOB, GPIO_MODE_IPU , GPIO_OSPEED_50MHZ, GPIO_PIN_0);//PB0配置成上拉输入    
}

SPI0的配置初始化

void spi_config(void)
{
    spi_parameter_struct  spi_init_struct;

    /* configure SPI1 parameter */
    spi_init_struct.trans_mode           = SPI_TRANSMODE_FULLDUPLEX;
    spi_init_struct.device_mode          = SPI_MASTER;;
    spi_init_struct.frame_size           = SPI_FRAMESIZE_8BIT;;
    spi_init_struct.clock_polarity_phase = SPI_CK_PL_LOW_PH_2EDGE;
    spi_init_struct.nss                  = SPI_NSS_SOFT;
    spi_init_struct.prescale             = SPI_PSC_32;
    spi_init_struct.endian               = SPI_ENDIAN_MSB;;
    spi_init(SPI0, &spi_init_struct);

}

SPI0的数据发送函数

uint8_t spi0_send_byte(uint8_t spi_byte)
{        
    uint8_t ByteSend,ByteRecv;
    ByteSend=spi_byte;

 while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_TBE));
    spi_i2s_data_transmit(SPI0,ByteSend);
    while(RESET == spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE));
  ByteRecv=spi_i2s_data_receive(SPI0);
    return ByteRecv;

}

CH376芯片的驱动程序文件为bsp_ch376.c何bsp_ch376.h。便于工程移植使用。
bsp_ch376.c

#ifndef BSP_CH376_H
#define BSP_CH376_H
#include "bsp_ch376.h"
#include "gd32f30x_rcu.h"
#include "gd32f30x_gpio.h"
#include "systick.h"
#include "gd32f30x.h"
#include "uart3dma.h"//调试用
#include "bsp_spi.h"


#define FLASH_CS_0()            {gpio_bit_write(GPIOA, GPIO_PIN_4,RESET);delay_1ms(10);}
#define FLASH_CS_1()             {gpio_bit_write(GPIOA, GPIO_PIN_4,SET);delay_1ms(10);}



#define CH376_INTPORT        GPIOB            //定义IO接口
#define CH376_INT            GPIO_PIN_0    //定义IO接口【此处是预留引脚,程序中未使用】

uint8_t retval = 0;                  //返回值
uint32_t u32retcount = 0;            //写入字节数
uint32_t u32count = 0;               //循环变量
char retString[120]="";
char buf[512]="";



void xWriteCH376Cmd(uint8_t mCmd){
    FLASH_CS_1();   /* 防止之前未通过xEndCH376Cmd禁止SPI片选 */
    delay_1us(20);
/* 对于双向I/O引脚模拟SPI接口,那么必须确保已经设置SPI_SCS,SPI_SCK,SPI_SDI为输出
*  方向,SPI_SDO为输入方向 */
    FLASH_CS_0();     /* SPI片选有效 */
    spi0_send_byte( mCmd );  /* 发出命令码 */
    delay_1us(1800);   /* 延时1.5mS确保读写周期大于1.5mS,或者用上面一行的状态查询代替 */
}

void xWriteCH376Data(uint8_t mData){
    spi0_send_byte( mData );
    delay_1us(700);  /* 确保读写周期大于0.6mS */
}

uint8_t xReadCH376Data(void){
    uint8_t i;
    delay_1us(20);
    i = spi0_send_byte(0xFF);
    return(i);
}

void xEndCH376Cmd(void){ //结束命令
    FLASH_CS_1(); //SPI片选无效,结束CH376命令
}

/*******************************************************************************
* 描      述      : 查询CH376中断(INT#低电平).
* 返      回      : 0:无中断.       1:有中断.
******************************************************************************
uint8_t Query376Interrupt(void){
    uint8_t i;
     i = gpio_input_bit_get(CH376_INTPORT,CH376_INT);     
    return( i == 0); 
}    
*/

/*******************************************************************************
* 描      述      : 初始化CH376.
* 返      回      : FALSE:无中断.  TRUE:有中断.
*******************************************************************************/
uint8_t mInitCH376Host(void){
    uint8_t    u8ret;    
    delay_1ms(600);

    FLASH_CS_1();
    
    xWriteCH376Cmd( CMD11_CHECK_EXIST );    /* 测试单片机与CH376之间的通讯接口 */
    xWriteCH376Data( 0x55 );
    u8ret = xReadCH376Data( );
//    printf("res =%02x \n",(unsigned short)res);
    xEndCH376Cmd( );
    if ( u8ret != 0xAA ) return( ERR_USB_UNKNOWN );  /* 通讯接口不正常,可能原因有:接口连接异常,其它设备影响(片选不唯一),串口波特率,一直在复位,晶振不工作 */
    
    xWriteCH376Cmd( CMD11_SET_USB_MODE ); /* 设备USB工作模式 */
    xWriteCH376Data( 0x06 ); //06H=已启用的主机方式并且自动产生SOF包
    delay_1ms(1);
    u8ret = xReadCH376Data( );
//    printf("res =%02x \n",(unsigned short)res);
    xEndCH376Cmd( );

    if ( u8ret == CMD_RET_SUCCESS ){  //RES=51  命令操作成功
                
            UART3_Transmit_DMA(UART3,"#########################################################ok", 200); 
        return( USB_INT_SUCCESS ); //USB事务或者传输操作成功 
    }else{
            UART3_Transmit_DMA(UART3,"not ok", 20);
        return( ERR_USB_UNKNOWN );/* 设置模式错误 */
    }
}

/*******************************************************************************
* 描      述      : 查询CH376中断(INT#低电平).
* 返      回      : 0:无中断.       1:有中断.
*******************************************************************************/
uint8_t Query376Interrupt(void){
    uint8_t i;
    char strings[200]="";
     i = gpio_input_bit_get(CH376_INTPORT, GPIO_PIN_0);
    //i=~i;
    //sprintf(strings, "%x\n", i);
    //UART3_Transmit_DMA(UART3,(uint8_t *)strings, 10);     
    return ( i == 0x00 ); 
}    


/*******************************************************************************
* 函  数  名      : CH376GetIntStatus
* 描      述      : 获取中断状态并取消中断请求.
* 输      入      : 无.
* 返      回      : UINT8 s:
*                    中断状态.
*******************************************************************************/
uint8_t    CH376GetIntStatus( void )
{
    uint8_t    s;
    
    xWriteCH376Cmd( CMD01_GET_STATUS );
    s = xReadCH376Data( );
    xEndCH376Cmd( );    
    return( s );
}



uint8_t    Wait376Interrupt( void )
{
                                                               /* 是否定义了超时时间 */

    uint32_t    i;
    uint8_t ret=0;
    for ( i = 0; i < 50000; i ++ )                                                    /* 计数防止超时,默认的超时时间,与单片机主频有关 */
  {
        ret = Query376Interrupt() ;
        if (ret) 
        {

            return( CH376GetIntStatus());                                             /* 检测到中断 */
        }
        /* 在等待CH376中断的过程中,可以做些需要及时处理的其它事情 */
    }
    return( ERR_USB_UNKNOWN );                                                          /* 不应该发生的情况 */

}

uint8_t    CH376SendCmdWaitInt( uint8_t mCmd )
{
    xWriteCH376Cmd( mCmd );
    xEndCH376Cmd( );
    return( Wait376Interrupt( ) );
}

/*******************************************************************************
* 函  数  名      : CH376SendCmdDatWaitInt
* 描      述      : 发出命令码和一字节数据后,等待中断.
* 输      入      : 无.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376SendCmdDatWaitInt( uint8_t mCmd, uint8_t mDat )
{
    xWriteCH376Cmd( mCmd );
    xWriteCH376Data( mDat );
    xEndCH376Cmd( );
    return( Wait376Interrupt( ) );
}

/*******************************************************************************
* 函  数  名      : CH376ReadVar8
* 描      述      : 读CH376芯片内部的8位变量.
* 输      入      : 无.
* 返      回      : 8位变量.
*******************************************************************************/
uint8_t    CH376ReadVar8( uint8_t var ) 
{
    uint8_t    c0;
    
    xWriteCH376Cmd( CMD11_READ_VAR8 );                                                   /* 读取指定的8位文件系统变量 */
    xWriteCH376Data( var );
    c0 = xReadCH376Data( );
    xEndCH376Cmd( );    
    return( c0 );
}

#define    VAR_DISK_STATUS        0x2B       /* 主机文件模式下的磁盘及文件状态 */
/*******************************************************************************
* 函  数  名      : CH376GetDiskStatus
* 描      述      : 获取磁盘和文件系统的工作状态.
* 输      入      : 无.
* 返      回      : 状态.
*******************************************************************************/
uint8_t    CH376GetDiskStatus( void )
{
    return( CH376ReadVar8( VAR_DISK_STATUS ) );
}


/*******************************************************************************
* 函  数  名      : CH376FileClose
* 描      述      : 关闭当前已经打开的文件或者目录(文件夹)
* 输      入      : PUINT8 UpdateSz:
*                    是否更新文件长度.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376FileClose( uint8_t UpdateSz )
{
    return( CH376SendCmdDatWaitInt( CMD1H_FILE_CLOSE, UpdateSz ) );
}


/*******************************************************************************
* 函  数  名      : CH376DiskMount
* 描      述      : 初始化磁盘并测试磁盘是否就绪.
* 输      入      : 无.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t CH376DiskMount( void )
{
    return( CH376SendCmdWaitInt( 0x31 ) );                                    /* 初始化磁盘并测试磁盘是否就绪 */
}


/*******************************************************************************
* 函  数  名      : CH376WriteReqBlock
* 描      述      : 向内部指定缓冲区写入请求的数据块,返回长度.
* 输      入      : PUINT8 buf:
*                   指向发送缓冲区.
* 返      回      : UINT8 s:后续数据长度.
*******************************************************************************/
uint8_t    CH376WriteReqBlock( uint8_t * buf ){
    uint8_t    s, l;

    xWriteCH376Cmd( CMD01_WR_REQ_DATA );                                                /* 向内部指定缓冲区写入请求的数据块 */
    s = l = xReadCH376Data( );                                                          /* 后续数据长度 */
    if ( l ) 
    {
        do 
        {
            xWriteCH376Data( *buf );
            buf ++;
        } while ( -- l );
    }
    xEndCH376Cmd( );
    return( s );
}


/*******************************************************************************
* 函  数  名      : CH376ByteLocate
* 描      述      : 以字节为单位移动当前文件指针
* 输      入      : UINT32 offset:
*                    指针偏移地址.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376ByteLocate( uint32_t offset )
{
    xWriteCH376Cmd( CMD4H_BYTE_LOCATE );
    xWriteCH376Data( (uint8_t)offset );
    xWriteCH376Data( (uint8_t)((uint16_t)offset>>8) );
    xWriteCH376Data( (uint8_t)(offset>>16) );
    xWriteCH376Data( (uint8_t)(offset>>24) );
    xEndCH376Cmd( );
    return( Wait376Interrupt( ) );
}



/*******************************************************************************
* 函  数  名      : CH376ByteWrite
* 描      述      : 以字节为单位向当前位置写入数据块.
* 输      入      : PUINT8 buf:
*                    指向外部缓冲区.
*                   UINT16 ReqCount:
*                   请求写入的字节数.
*                   PUINT16 RealCount:
*                   实际写入的字节数.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376ByteWrite( uint8_t * buf, uint16_t  ReqCount, uint16_t * RealCount )
{
    uint8_t    s;
    
    xWriteCH376Cmd( CMD2H_BYTE_WRITE );
    xWriteCH376Data( (uint8_t)ReqCount );
    xWriteCH376Data( (uint8_t)(ReqCount>>8) );
    xEndCH376Cmd( );
    if ( RealCount ) 
    {
        *RealCount = 0;
    }
    
    while ( 1 ) 
    {
        s = Wait376Interrupt( );
        if ( s == USB_INT_DISK_WRITE ) 
        {
            s = CH376WriteReqBlock( buf );                                              /* 向内部指定缓冲区写入请求的数据块,返回长度 */
            xWriteCH376Cmd( CMD0H_BYTE_WR_GO );
            xEndCH376Cmd( );
            buf += s;
            if ( RealCount ) *RealCount += s;
        }
        else 
        {
            return( s );                                                                /* 错误 */
        }
    }
}

/*******************************************************************************
* 函  数  名      : CH376Read32bitDat
* 描      述      : 从CH376芯片读取32位的数据并结束命令.
* 输      入      : 无.
* 返      回      : 32位数据.
*******************************************************************************/
uint32_t    CH376Read32bitDat( void )
{
    uint8_t    c0, c1, c2, c3;

    c0 = xReadCH376Data( );
    c1 = xReadCH376Data( );
    c2 = xReadCH376Data( );
    c3 = xReadCH376Data( );    
    xEndCH376Cmd( );
    return( c0 | (uint16_t)c1 << 8 | (uint32_t)c2 << 16 | (uint32_t)c3 << 24 );
}

/*******************************************************************************
* 函  数  名      : CH376ReadVar8
* 描      述      : 读CH376芯片内部的32位变量.
* 输      入      : UINT8 var:
*                   变量地址.
* 返      回      : 32位变量.
*******************************************************************************/
uint32_t    CH376ReadVar32( uint8_t var )
{
    xWriteCH376Cmd( CMD14_READ_VAR32 );
    xWriteCH376Data( var );
    return( CH376Read32bitDat( ) );                                                      /* 从CH376芯片读取32位的数据并结束命令 */
}

/*******************************************************************************
* 函  数  名      : CH376WriteVar32
* 描      述      : 写CH376芯片内部的32位变量.
* 输      入      : UINT8 var:
*                   变量地址.
*                    UINT32 dat:
*                    数据.
* 返      回      : 无.
*******************************************************************************/
void    CH376WriteVar32( uint8_t var, uint32_t dat )
{
    xWriteCH376Cmd( CMD50_WRITE_VAR32 );
    xWriteCH376Data( var );
    xWriteCH376Data( (uint8_t)dat );
    xWriteCH376Data( (uint8_t)( (uint16_t)dat >> 8 ) );
    xWriteCH376Data( (uint8_t)( dat >> 16 ) );
    xWriteCH376Data( (uint8_t)( dat >> 24 ) );
    xEndCH376Cmd( );        
}


/*******************************************************************************
* 函  数  名      : CH376SetFileName
* 描      述      : 设置将要操作的文件的文件名 .
* 输      入      : PUINT8 name:
*                    指向文件名缓冲区.
* 返      回      : 无.
*******************************************************************************/
void    CH376SetFileName( uint8_t * name )
{
    uint8_t    c;

#ifndef    DEF_IC_V43_U                                                                    /* 默认支持低版本 */
    uint8_t    s;

    xWriteCH376Cmd( CMD01_GET_IC_VER );                                                    /* 获取芯片版本 */
    if (  xReadCH376Data( ) < 0x43 ) 
    {
        if ( CH376ReadVar8( VAR_DISK_STATUS ) < DEF_DISK_READY ) 
        {
            xWriteCH376Cmd( CMD10_SET_FILE_NAME );
            xWriteCH376Data( 0 );
            s = CH376SendCmdWaitInt( CMD0H_FILE_OPEN );
            if ( s == USB_INT_SUCCESS ) 
            {
                s = CH376ReadVar8( 0xCF );
                if ( s ) 
                {
                    CH376WriteVar32( 0x4C, CH376ReadVar32( 0x4C ) + ( (uint16_t)s << 8 ) );
                    CH376WriteVar32( 0x50, CH376ReadVar32( 0x50 ) + ( (uint16_t)s << 8 ) );
                    CH376WriteVar32( 0x70, 0 );
                }
            }
        }
    }
#endif
    xWriteCH376Cmd( CMD10_SET_FILE_NAME );
    c = *name;
    xWriteCH376Data( c );
    while ( c ) 
    {
        name ++;
        c = *name;
        if ( c == DEF_SEPAR_CHAR1 || c == DEF_SEPAR_CHAR2 ) 
        {
            c = 0;                                                                      /* 强行将文件名截止 */
        }
        xWriteCH376Data( c );
    }
    xEndCH376Cmd( );    
}


/*******************************************************************************
* 函  数  名      : CH376FileOpen
* 描      述      : 在根目录或者当前目录下打开文件或者目录(文件夹).
* 输      入      : 无.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376FileOpen( uint8_t * name ) 
{
    CH376SetFileName( name );                                                              /* 设置将要操作的文件的文件名 */
#ifndef    DEF_IC_V43_U
    if ( name[0] == DEF_SEPAR_CHAR1 || name[0] == DEF_SEPAR_CHAR2 ) 
    {
        CH376WriteVar32( VAR_CURRENT_CLUST, 0 );
    }
#endif
    return( CH376SendCmdWaitInt( CMD0H_FILE_OPEN ) );
}

/*******************************************************************************
* 函  数  名      : CH376FileCreate
* 描      述      : 在根目录或者当前目录下新建文件,如果文件已经存在那么先删除.
* 输      入      : 无.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376FileCreate( uint8_t * name )
{
    if ( name ) 
    {
        CH376SetFileName( name );      /* 设置将要操作的文件的文件名 */
    }
    return( CH376SendCmdWaitInt( CMD0H_FILE_CREATE ) );
}

/*******************************************************************************
* 函  数  名      : CH376DirCreate
* 描      述      : 在根目录下新建目录(文件夹)并打开,如果目录已经存在那么直接打开.
* 输      入      : 无.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376DirCreate( uint8_t * name )
{
    CH376SetFileName( name );      /* 设置将要操作的文件的文件名 */
#ifndef    DEF_IC_V43_U
    if ( name[0] == DEF_SEPAR_CHAR1 || name[0] == DEF_SEPAR_CHAR2 ) 
    {
        CH376WriteVar32( VAR_CURRENT_CLUST, 0 );
    }
#endif
    return( CH376SendCmdWaitInt( CMD0H_DIR_CREATE ) );
}

/*******************************************************************************
* 函  数  名      : CH376SeparatePath
* 描      述      : 从路径中分离出最后一级文件名或者目录(文件夹)名
* 输      入      : PUINT8 path:
*                    指向路径缓冲区.
* 返      回      : 返回最后一级文件名或者目录名的字节偏移.
*******************************************************************************/
uint8_t    CH376SeparatePath( uint8_t * path )
{
    uint8_t *    pName;

    for ( pName = path; *pName != 0; ++ pName );                                          /* 到文件名字符串结束位置 */
    while ( *pName != DEF_SEPAR_CHAR1 && *pName != DEF_SEPAR_CHAR2 && pName != path ) 
    {    
        pName --;                                                                          /*  搜索倒数第一个路径分隔符 */
    }
    if ( pName != path ) 
    {
        pName ++;                                                                          /* 找到了路径分隔符,则修改指向目标文件的最后一级文件名,跳过前面的多级目录名及路径分隔符 */
    }
    return( pName - path );
}

/*******************************************************************************
* 函  数  名      : CH376FileOpenDir
* 描      述      : 打开多级目录下的文件或者目录的上级目录,支持多级目录路径,
*                    支持路径分隔符,路径长度不超过255个字符
* 输      入      : PUINT8 path:
*                    指向路径缓冲区.
*                    UINT8 StopName:
*                    指向最后一级文件名或者目录名
* 返      回      : 返回最后一级文件名或者目录名的字节偏移.
*******************************************************************************/
uint8_t    CH376FileOpenDir( uint8_t * PathName, uint8_t StopName )
{
    uint8_t    i, s;

    s = 0;
    i = 1;                                                                              /* 跳过有可能的根目录符 */
    while ( 1 ) 
    {
        while ( PathName[i] != DEF_SEPAR_CHAR1 && PathName[i] != DEF_SEPAR_CHAR2 && PathName[i] != 0 ) 
        {
            ++ i;                                                                          /* 搜索下一个路径分隔符或者路径结束符 */
        }

        if ( PathName[i] ) 
        {
            i ++;                                                                          /* 找到了路径分隔符,修改指向目标文件的最后一级文件名 */
        }
        else 
        {
            i = 0;                                                                      /* 路径结束 */
        }
        
        s = CH376FileOpen( &PathName[s] );                                              /* 打开文件或者目录 */
        
        if ( i && i != StopName )                                                         /* 路径尚未结束 */    
        {              
            if ( s != ERR_OPEN_DIR )                                                     /* 因为是逐级打开,尚未到路径结束,所以,如果不是成功打开了目录,那么说明有问题 */
            {  
                if ( s == USB_INT_SUCCESS ) 
                {
                    return( ERR_FOUND_NAME );                                              /* 中间路径必须是目录名,如果是文件名则出错 */
                }
                else if ( s == ERR_MISS_FILE ) 
                {
                    return( ERR_MISS_DIR );                                              /* 中间路径的某个子目录没有找到,可能是目录名称错误 */
                }
                else 
                {
                    return( s );                                                          /* 操作出错 */
                }
            }
            s = i;                                                                      /* 从下一级目录开始继续 */
        }
        else 
        {
            return( s );                                                                  /* 路径结束,USB_INT_SUCCESS为成功打开文件,ERR_OPEN_DIR为成功打开目录(文件夹),其它为操作出错 */
        }
    }
}

/*******************************************************************************
* 函  数  名      : CH376FileOpenPath
* 描      述      : 打开多级目录下的文件或者目录(文件夹),支持多级目录路径,
*                    支持路径分隔符,路径长度不超过255个字符
* 输      入      : PUINT8 path:
*                    指向路径缓冲区.
* 返      回      : 返回最后一级文件名或者目录名的字节偏移.
*******************************************************************************/
uint8_t    CH376FileOpenPath( uint8_t * PathName )
{
    return( CH376FileOpenDir( PathName, 0xFF ) );
}


/*******************************************************************************
* 函  数  名      : CH376FileCreatePath
* 描      述      : 新建多级目录下的目录(文件夹)并打开,支持多级目录路径,支持路
*                    径分隔符,路径长度不超过255个字符.
* 输      入      : PUINT8 path:
*                    指向路径缓冲区.
* 返      回      : 中断状态.
*******************************************************************************/
uint8_t    CH376FileCreatePath( uint8_t * PathName )
{
    uint8_t    s;
    uint8_t    Name;

    Name = CH376SeparatePath( PathName );                                                  /* 从路径中分离出最后一级文件名,返回最后一级文件名的偏移 */
    if ( Name )                                                                         /* 是多级目录 */
    {  
        s = CH376FileOpenDir( PathName, Name );                                          /* 打开多级目录下的最后一级目录,即打开新建文件的上级目录 */
        if ( s != ERR_OPEN_DIR )                                                         /* 因为是打开上级目录,所以,如果不是成功打开了目录,那么说明有问题 */    
        {  
            if ( s == USB_INT_SUCCESS ) 
            {
                return( ERR_FOUND_NAME );                                                  /* 中间路径必须是目录名,如果是文件名则出错 */
            }
            else if ( s == ERR_MISS_FILE ) 
            {
                return( ERR_MISS_DIR );                                                  /* 中间路径的某个子目录没有找到,可能是目录名称错误 */
            }
            else 
            {
                return( s );                                                              /* 操作出错 */
            }
        }
    }
    return( CH376FileCreate( &PathName[Name] ) );                                          /* 在根目录或者当前目录下新建文件 */
}

/*******************************************************************************
* 函  数  名      : CH376DiskConnect
* 描      述      : 检查U盘是否连接,不支持SD卡.
* 输      入      : 无.
* 返      回      : U盘是否连接状态.
*******************************************************************************/
uint8_t    CH376DiskConnect( void )
{
    uint8_t ret = 0;
    char strings[20]="";
    if ( Query376Interrupt()) 
    {
        ret = CH376GetIntStatus();  
    }
    return( CH376SendCmdWaitInt( CMD0H_DISK_CONNECT ) );                                /* 检查磁盘是否连接 */
}



/*******************************************************************************
* 函  数  名      : CH376_INIT
* 描      述      : CH376初始化. U盘操作之前第一步
* 输      入      : 无.
* 返      回      : 0 代表U盘已经准备好
*******************************************************************************/
uint8_t    CH376_INIT( void )
{
    uint8_t retval = 0;
    uint32_t u32count = 0;
    retval = mInitCH376Host();
    retval = CH376DiskConnect();     //读出U盘的状态 
    if(retval == USB_INT_SUCCESS){ //检查U盘是否连接//等待U盘插入
        delay_1ms(1); //每次操作后必要的延时
    }else{
        delay_1ms(1); //每次操作后必要的延时
        return -1;
    } 

        for ( u32count = 0; u32count < 10000; u32count ++ ){ 
            delay_1ms( 10 );
            retval = CH376DiskMount( );  //初始化磁盘并测试磁盘是否就绪.  
        
            if ( retval == USB_INT_SUCCESS ) //准备好 
            {
                 return 0;
                
            }else if ( retval == ERR_DISK_DISCON )// 检测到断开,重新检测并计时 
            {
                break;  
            }
            if ( CH376GetDiskStatus() >= DEF_DISK_MOUNTED && u32count >= 5 ) // 有的U盘总是返回未准备好,不过可以忽略,只要其建立连接MOUNTED且尝试5*50mS 
            {
                //UART3_Transmit_DMA(UART3,(uint8_t *)"STATUSOK", 10);
                break;
            }
        }
 
        delay_1ms(1); //每次操作后必要的延时
}

/*******************************************************************************
* 函  数  名      : CH376_INIT
* 描      述      : CH376初始化. U盘操作之前第一步
* 输      入      : filename 类型 uint8_t * 
* 返      回      : USB_INT_SUCCESS 代表U盘已经准备好
*******************************************************************************/
uint8_t    CH376_OPEN( uint8_t * filename )
{
    uint32_t u32retcount = 0;

    u32retcount=CH376FileOpenPath( filename ); /* 打开已存在的文件 */
    if(u32retcount==ERR_MISS_FILE ){
            u32retcount=CH376FileCreatePath(filename ); /* 文件不存在则创建 */
    }
    
    u32retcount = CH376ByteLocate( 0xFFFFFFFF );  /* 移动文件指针 0xFFFFFFFF 到最后 */
    if ( u32retcount != USB_INT_SUCCESS ) 
    {
        return( u32retcount );
    }
        
}

#endif /* BSP_CH376_H */

bsp_ch376.h

#ifndef BSP_CH376_H
#define BSP_CH376_H
//#include "sys.h"
#include "gd32f30x.h"
#include "bsp_spi.h"

#ifndef        TRUE
#define        TRUE    1
#define        FALSE    0
#endif
#ifndef        NULL
#define        NULL    0
#endif

//#define SPI2PORT        GPIOB    //
//#define SPI2_MOSI        GPIO_PIN_15    //
//#define SPI2_MISO        GPIO_PIN_14    //
//#define SPI2_SCK        GPIO_PIN_13    //
//#define SPI2_NSS        GPIO_PIN_12    //


#define CH376_INTPORT        GPIOB            //定义IO接口
#define CH376_INT            GPIO_PIN_0    //定义IO接口【此处是预留引脚,程序中未使用】

/* 硬件特性 */

#define        CH376_DAT_BLOCK_LEN        0x40    /* USB单个数据包, 数据块的最大长度, 默认缓冲区的长度 */


extern uint8_t retval ;                  //返回值
extern uint32_t u32retcount;            //写入字节数
extern uint32_t u32count;               //循环变量
extern char retString[120];
extern char buf[512];




/* ********************************************************************************************************************* */
/* 命令代码 */
/* 部分命令兼容CH375芯片, 但是输入数据或者输出数据的可能局部不同) */
/* 一个命令操作顺序包含:
          一个命令码(对于串口方式,命令码之前还需要两个同步码),
          若干个输入数据(可以是0个),
          产生中断通知 或者 若干个输出数据(可以是0个), 二选一, 有中断通知则一定没有输出数据, 有输出数据则一定不产生中断
       仅CMD01_WR_REQ_DATA命令例外, 顺序包含: 一个命令码, 一个输出数据, 若干个输入数据
   命令码起名规则: CMDxy_NAME
       其中的x和y都是数字, x说明最少输入数据个数(字节数), y说明最少输出数据个数(字节数), y如果是H则说明产生中断通知,
       有些命令能够实现0到多个字节的数据块读写, 数据块本身的字节数未包含在上述x或y之内 */
/* 本文件默认会同时提供与CH375芯片命令码兼容的命令码格式(即去掉x和y之后), 如果不需要, 那么可以定义_NO_CH375_COMPATIBLE_禁止 */

/* ********************************************************************************************************************* */
/* 主要命令(手册一), 常用 */

#define    CMD01_GET_IC_VER    0x01            /* 获取芯片及固件版本 */
/* 输出: 版本号( 位7为0, 位6为1, 位5~位0为版本号 ) */
/*           CH376返回版本号的值为041H即版本号为01H */

#define    CMD21_SET_BAUDRATE    0x02            /* 串口方式: 设置串口通讯波特率(上电或者复位后的默认波特率为9600bps,由D4/D5/D6引脚选择) */
/* 输入: 波特率分频系数, 波特率分频常数 */
/* 输出: 操作状态( CMD_RET_SUCCESS或CMD_RET_ABORT, 其它值说明操作未完成 ) */

#define    CMD00_ENTER_SLEEP    0x03            /* 进入睡眠状态 */

#define    CMD00_RESET_ALL        0x05            /* 执行硬件复位 */

#define    CMD11_CHECK_EXIST    0x06            /* 测试通讯接口和工作状态 */
/* 输入: 任意数据 */
/* 输出: 输入数据的按位取反 */

#define    CMD20_CHK_SUSPEND    0x0B            /* 设备方式: 设置检查USB总线挂起状态的方式 */
/* 输入: 数据10H, 检查方式 */
/*           00H=不检查USB挂起, 04H=以50mS为间隔检查USB挂起, 05H=以10mS为间隔检查USB挂起 */

#define    CMD20_SET_SDO_INT    0x0B            /* SPI接口方式: 设置SPI的SDO引脚的中断方式 */
/* 输入: 数据16H, 中断方式 */
/*           10H=禁止SDO引脚用于中断输出,在SCS片选无效时三态输出禁止, 90H=SDO引脚在SCS片选无效时兼做中断请求输出 */

#define    CMD14_GET_FILE_SIZE    0x0C            /* 主机文件模式: 获取当前文件长度 */
/* 输入: 数据68H */
/* 输出: 当前文件长度(总长度32位,低字节在前) */

#define    CMD50_SET_FILE_SIZE    0x0D            /* 主机文件模式: 设置当前文件长度 */
/* 输入: 数据68H, 当前文件长度(总长度32位,低字节在前) */

#define    CMD11_SET_USB_MODE    0x15            /* 设置USB工作模式 */
//00H=未启用的设备方式, 01H=已启用的设备方式并且使用外部固件模式(串口不支持), 
//02H=已启用的设备方式并且使用内置固件模式 03H=SD卡主机模式/未启用的主机模式,用于管理和存取SD卡中的文件 
//04H=未启用的主机方式, 05H=已启用的主机方式, 06H=已启用的主机方式并且自动产生SOF包, 07H=已启用的主机方式并且复位USB总线 */
//输出: 操作状态( CMD_RET_SUCCESS或CMD_RET_ABORT, 其它值说明操作未完成 ) 

#define    CMD01_GET_STATUS    0x22            /* 获取中断状态并取消中断请求 */
/* 输出: 中断状态 */

#define    CMD00_UNLOCK_USB    0x23            /* 设备方式: 释放当前USB缓冲区 */

#define    CMD01_RD_USB_DATA0    0x27            /* 从当前USB中断的端点缓冲区或者主机端点的接收缓冲区读取数据块 */
/* 输出: 长度, 数据流 */

#define    CMD01_RD_USB_DATA    0x28            /* 设备方式: 从当前USB中断的端点缓冲区读取数据块, 并释放缓冲区, 相当于 CMD01_RD_USB_DATA0 + CMD00_UNLOCK_USB */
/* 输出: 长度, 数据流 */

#define    CMD10_WR_USB_DATA7    0x2B            /* 设备方式: 向USB端点2的发送缓冲区写入数据块 */
/* 输入: 长度, 数据流 */

#define    CMD10_WR_HOST_DATA    0x2C            /* 向USB主机端点的发送缓冲区写入数据块 */
/* 输入: 长度, 数据流 */

#define    CMD01_WR_REQ_DATA    0x2D            /* 向内部指定缓冲区写入请求的数据块 */
/* 输出: 长度 */
/* 输入: 数据流 */

#define    CMD20_WR_OFS_DATA    0x2E            /* 向内部缓冲区指定偏移地址写入数据块 */
/* 输入: 偏移, 长度, 数据流 */

#define    CMD10_SET_FILE_NAME    0x2F            /* 主机文件模式: 设置将要操作的文件的文件名 */
/* 输入: 以0结束的字符串(含结束符0在内长度不超过14个字符) */

/* ********************************************************************************************************************* */
/* 主要命令(手册一), 常用, 以下命令总是在操作结束时产生中断通知, 并且总是没有输出数据 */

#define    CMD0H_DISK_CONNECT    0x30            /* 主机文件模式/不支持SD卡: 检查磁盘是否连接 */
/* 输出中断 */

#define    CMD0H_DISK_MOUNT    0x31            /* 主机文件模式: 初始化磁盘并测试磁盘是否就绪 */
/* 输出中断 */

#define    CMD0H_FILE_OPEN        0x32            /* 主机文件模式: 打开文件或者目录(文件夹),或者枚举文件和目录(文件夹) */
/* 输出中断 */

#define    CMD0H_FILE_ENUM_GO    0x33            /* 主机文件模式: 继续枚举文件和目录(文件夹) */
/* 输出中断 */

#define    CMD0H_FILE_CREATE    0x34            /* 主机文件模式: 新建文件,如果文件已经存在那么先删除 */
/* 输出中断 */

#define    CMD0H_FILE_ERASE    0x35            /* 主机文件模式: 删除文件,如果已经打开则直接删除,否则对于文件会先打开再删除,子目录必须先打开 */
/* 输出中断 */

#define    CMD1H_FILE_CLOSE    0x36            /* 主机文件模式: 关闭当前已经打开的文件或者目录(文件夹) */
/* 输入: 是否允许更新文件长度 */
/*          00H=禁止更新长度, 01H=允许更新长度 */
/* 输出中断 */

#define    CMD1H_DIR_INFO_READ    0x37            /* 主机文件模式: 读取文件的目录信息 */
/* 输入: 指定需要读取的目录信息结构在扇区内的索引号 */
/*           索引号范围为00H~0FH, 索引号0FFH则为当前已经打开的文件 */
/* 输出中断 */

#define    CMD0H_DIR_INFO_SAVE    0x38                                                        /* 主机文件模式: 保存文件的目录信息 */
/* 输出中断 */

#define    CMD4H_BYTE_LOCATE    0x39                                                        /* 主机文件模式: 以字节为单位移动当前文件指针 */
/* 输入: 偏移字节数(总长度32位,低字节在前) */
/* 输出中断 */

#define    CMD2H_BYTE_READ        0x3A                                                        /* 主机文件模式: 以字节为单位从当前位置读取数据块 */
/* 输入: 请求读取的字节数(总长度16位,低字节在前) */
/* 输出中断 */

#define    CMD0H_BYTE_RD_GO    0x3B            /* 主机文件模式: 继续字节读 */
/* 输出中断 */

#define    CMD2H_BYTE_WRITE    0x3C            /* 主机文件模式: 以字节为单位向当前位置写入数据块 */
/* 输入: 请求写入的字节数(总长度16位,低字节在前) */
/* 输出中断 */

#define    CMD0H_BYTE_WR_GO    0x3D            /* 主机文件模式: 继续字节写 */
/* 输出中断 */

#define    CMD0H_DISK_CAPACITY    0x3E            /* 主机文件模式: 查询磁盘物理容量 */
/* 输出中断 */

#define    CMD0H_DISK_QUERY    0x3F            /* 主机文件模式: 查询磁盘空间信息 */
/* 输出中断 */

#define    CMD0H_DIR_CREATE    0x40            /* 主机文件模式: 新建目录(文件夹)并打开,如果目录已经存在那么直接打开 */
/* 输出中断 */

#define    CMD4H_SEC_LOCATE    0x4A            /* 主机文件模式: 以扇区为单位移动当前文件指针 */
/* 输入: 偏移扇区数(总长度32位,低字节在前) */
/* 输出中断 */

#define    CMD1H_SEC_READ        0x4B            /* 主机文件模式/不支持SD卡: 以扇区为单位从当前位置读取数据块 */
/* 输入: 请求读取的扇区数 */
/* 输出中断 */

#define    CMD1H_SEC_WRITE        0x4C            /* 主机文件模式/不支持SD卡: 以扇区为单位在当前位置写入数据块 */
/* 输入: 请求写入的扇区数 */
/* 输出中断 */

#define    CMD0H_DISK_BOC_CMD    0x50            /* 主机方式/不支持SD卡: 对USB存储器执行BulkOnly传输协议的命令 */
/* 输出中断 */

#define    CMD5H_DISK_READ        0x54            /* 主机方式/不支持SD卡: 从USB存储器读物理扇区 */
/* 输入: LBA物理扇区地址(总长度32位, 低字节在前), 扇区数(01H~FFH) */
/* 输出中断 */

#define    CMD0H_DISK_RD_GO    0x55            /* 主机方式/不支持SD卡: 继续执行USB存储器的物理扇区读操作 */
/* 输出中断 */

#define    CMD5H_DISK_WRITE    0x56            /* 主机方式/不支持SD卡: 向USB存储器写物理扇区 */
/* 输入: LBA物理扇区地址(总长度32位, 低字节在前), 扇区数(01H~FFH) */
/* 输出中断 */

#define    CMD0H_DISK_WR_GO    0x57            /* 主机方式/不支持SD卡: 继续执行USB存储器的物理扇区写操作 */
/* 输出中断 */

/* ********************************************************************************************************************* */
/* 辅助命令(手册二), 不太常用或者是为了与CH375和CH372兼容 */

#define    CMD10_SET_USB_SPEED    0x04            /* 设置USB总线速度, 在每次CMD11_SET_USB_MODE设置USB工作模式时会自动恢复到12Mbps全速 */
/* 输入: 总线速度代码 */
/*           00H=12Mbps全速FullSpeed(默认值), 01H=1.5Mbps(仅修改频率), 02H=1.5Mbps低速LowSpeed */

#define    CMD11_GET_DEV_RATE    0x0A            /* 主机方式: 获取当前连接的USB设备的数据速率类型 */
/* 输入: 数据07H */
/* 输出: 数据速率类型 */
/*           位4为1则是1.5Mbps低速USB设备, 否则是12Mbps全速USB设备 */

#define    CMD11_GET_TOGGLE    0x0A            /* 获取OUT事务的同步状态 */
/* 输入: 数据1AH */
/* 输出: 同步状态 */
/*           位4为1则OUT事务同步, 否则OUT事务不同步 */

#define    CMD11_READ_VAR8        0x0A            /* 读取指定的8位文件系统变量 */
/* 输入: 变量地址 */
/* 输出: 数据 */

/*#define    CMD11_GET_MAX_LUN    = CMD11_READ_VAR8( VAR_UDISK_LUN )*/    /* 主机方式: 获取USB存储器最大和当前逻辑单元号 */

#define    CMD20_SET_RETRY        0x0B            /* 主机方式: 设置USB事务操作的重试次数 */
/* 输入: 数据25H, 重试次数 */
/*       位7为0则收到NAK时不重试, 位7为1位6为0则收到NAK时无限重试, 位7为1位6为1则收到NAK时最多重试3秒, 位5~位0为超时后的重试次数 */

#define    CMD20_WRITE_VAR8    0x0B            /* 设置指定的8位文件系统变量 */
/* 输入: 变量地址, 数据 */

/*#define    CMD20_SET_DISK_LUN    = CMD20_WRITE_VAR8( VAR_UDISK_LUN )*/    /* 主机方式: 设置USB存储器的当前逻辑单元号 */

#define    CMD14_READ_VAR32    0x0C            /* 读取指定的32位文件系统变量 */
/* 输入: 变量地址 */
/* 输出: 数据(总长度32位,低字节在前) */

#define    CMD50_WRITE_VAR32    0x0D            /* 设置指定的32位文件系统变量 */
/* 输入: 变量地址, 数据(总长度32位,低字节在前) */

#define    CMD01_DELAY_100US    0x0F            /* 延时100uS(串口不支持) */
/* 输出: 延时期间输出0,延时结束输出非0 */

#define    CMD40_SET_USB_ID    0x12            /* 设备方式: 设置USB厂商VID和产品PID */
/* 输入: 厂商ID低字节, 厂商ID高字节, 产品ID低字节, 产品ID高字节 */

#define    CMD10_SET_USB_ADDR    0x13            /* 设置USB地址 */
/* 输入: 地址值 */

#define    CMD01_TEST_CONNECT    0x16            /* 主机方式/不支持SD卡: 检查USB设备连接状态 */
/* 输出: 状态( USB_INT_CONNECT或USB_INT_DISCONNECT或USB_INT_USB_READY, 其它值说明操作未完成 ) */

#define    CMD00_ABORT_NAK        0x17            /* 主机方式: 放弃当前NAK的重试 */

#define    CMD10_SET_ENDP2        0x18            /* 设备方式(串口不支持): 设置USB端点0的接收器 */
/* 输入: 工作方式 */
/*           位7为1则位6为同步触发位, 否则同步触发位不变 */
/*           位3~位0为事务响应方式:  0000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define    CMD10_SET_ENDP3        0x19            /* 设备方式(串口不支持): 设置USB端点0的发送器 */
/* 输入: 工作方式 */
/*           位7为1则位6为同步触发位, 否则同步触发位不变 */
/*           位3~位0为事务响应方式:  0000~1000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define    CMD10_SET_ENDP4        0x1A            /* 设备方式(串口不支持): 设置USB端点1的接收器 */
/* 输入: 工作方式 */
/*           位7为1则位6为同步触发位, 否则同步触发位不变 */
/*           位3~位0为事务响应方式:  0000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define    CMD10_SET_ENDP5        0x1B            /* 设备方式(串口不支持): 设置USB端点1的发送器 */
/* 输入: 工作方式 */
/*           位7为1则位6为同步触发位, 否则同步触发位不变 */
/*           位3~位0为事务响应方式:  0000~1000-就绪ACK, 1110-正忙NAK, 1111-错误STALL */

#define    CMD10_SET_ENDP6        0x1C            /* 设置USB端点2/主机端点的接收器 */
/* 输入: 工作方式 */
/*           位7为1则位6为同步触发位, 否则同步触发位不变 */
/*           位3~位0为事务响应方式:  0000-就绪ACK, 1101-就绪但不返回ACK, 1110-正忙NAK, 1111-错误STALL */

#define    CMD10_SET_ENDP7        0x1D            /* 设置USB端点2/主机端点的发送器 */
/* 输入: 工作方式 */
/*           位7为1则位6为同步触发位, 否则同步触发位不变 */
/*           位3~位0为事务响应方式:  0000-就绪ACK, 1101-就绪但无须应答, 1110-正忙NAK, 1111-错误STALL */

#define    CMD00_DIRTY_BUFFER    0x25            /* 主机文件模式: 清除内部的磁盘和文件缓冲区 */

#define    CMD10_WR_USB_DATA3    0x29            /* 设备方式(串口不支持): 向USB端点0的发送缓冲区写入数据块 */
/* 输入: 长度, 数据流 */

#define    CMD10_WR_USB_DATA5    0x2A            /* 设备方式(串口不支持): 向USB端点1的发送缓冲区写入数据块 */
/* 输入: 长度, 数据流 */

/* ********************************************************************************************************************* */
/* 辅助命令(手册二), 不太常用或者是为了与CH375和CH372兼容, 以下命令总是在操作结束时产生中断通知, 并且总是没有输出数据 */

#define    CMD1H_CLR_STALL        0x41            /* 主机方式: 控制传输-清除端点错误 */
/* 输入: 端点号 */
/* 输出中断 */

#define    CMD1H_SET_ADDRESS    0x45            /* 主机方式: 控制传输-设置USB地址 */
/* 输入: 地址值 */
/* 输出中断 */

#define    CMD1H_GET_DESCR        0x46            /* 主机方式: 控制传输-获取描述符 */
/* 输入: 描述符类型 */
/* 输出中断 */

#define    CMD1H_SET_CONFIG    0x49            /* 主机方式: 控制传输-设置USB配置 */
/* 输入: 配置值 */
/* 输出中断 */

#define    CMD0H_AUTO_SETUP    0x4D            /* 主机方式/不支持SD卡: 自动配置USB设备 */
/* 输出中断 */

#define    CMD2H_ISSUE_TKN_X    0x4E            /* 主机方式: 发出同步令牌, 执行事务, 该命令可代替 CMD10_SET_ENDP6/CMD10_SET_ENDP7 + CMD1H_ISSUE_TOKEN */
/* 输入: 同步标志, 事务属性 */
/*           同步标志的位7为主机端点IN的同步触发位, 位6为主机端点OUT的同步触发位, 位5~位0必须为0 */
/*           事务属性的低4位是令牌, 高4位是端点号 */
/* 输出中断 */

#define    CMD1H_ISSUE_TOKEN    0x4F            /* 主机方式: 发出令牌, 执行事务, 建议用CMD2H_ISSUE_TKN_X命令 */
/* 输入: 事务属性 */
/*           低4位是令牌, 高4位是端点号 */
/* 输出中断 */

#define    CMD0H_DISK_INIT        0x51            /* 主机方式/不支持SD卡: 初始化USB存储器 */
/* 输出中断 */

#define    CMD0H_DISK_RESET    0x52            /* 主机方式/不支持SD卡: 控制传输-复位USB存储器 */
/* 输出中断 */

#define    CMD0H_DISK_SIZE        0x53            /* 主机方式/不支持SD卡: 获取USB存储器的容量 */
/* 输出中断 */

#define    CMD0H_DISK_INQUIRY    0x58            /* 主机方式/不支持SD卡: 查询USB存储器特性 */
/* 输出中断 */

#define    CMD0H_DISK_READY    0x59            /* 主机方式/不支持SD卡: 检查USB存储器就绪 */
/* 输出中断 */

#define    CMD0H_DISK_R_SENSE    0x5A            /* 主机方式/不支持SD卡: 检查USB存储器错误 */
/* 输出中断 */

#define    CMD0H_RD_DISK_SEC    0x5B            /* 主机文件模式: 从磁盘读取一个扇区的数据到内部缓冲区 */
/* 输出中断 */

#define    CMD0H_WR_DISK_SEC    0x5C            /* 主机文件模式: 将内部缓冲区的一个扇区的数据写入磁盘 */
/* 输出中断 */

#define    CMD0H_DISK_MAX_LUN    0x5D            /* 主机方式: 控制传输-获取USB存储器最大逻辑单元号 */
/* 输出中断 */


#define    USB_INT_SUCCESS        0x14            /* USB事务或者传输操作成功 */
#define    USB_INT_CONNECT        0x15            /* 检测到USB设备连接事件, 可能是新连接或者断开后重新连接 */
#define    USB_INT_DISCONNECT    0x16            /* 检测到USB设备断开事件 */
#define    USB_INT_BUF_OVER    0x17            /* USB传输的数据有误或者数据太多缓冲区溢出 */
#define    USB_INT_USB_READY    0x18            /* USB设备已经被初始化(已经分配USB地址) */
#define    USB_INT_DISK_READ    0x1D            /* USB存储器请求数据读出 */
#define    USB_INT_DISK_WRITE    0x1E            /* USB存储器请求数据写入 */
#define    USB_INT_DISK_ERR    0x1F            /* USB存储器操作失败 */

/* 附加的USB操作状态定义 */
#define ERR_USB_UNKNOWN        0xFA        /* 未知错误,不应该发生的情况,需检查硬件或者程序错误 */

#define    CMD_RET_SUCCESS        0x51            /* 命令操作成功 */
#define    CMD_RET_ABORT        0x5F            /* 命令操作失败 */

//void SPI2_Init(void);
//uint8_t SPI2_SendByte(uint8_t Byte);
void xWriteCH376Cmd(uint8_t mCmd);
void xWriteCH376Data(uint8_t mData);
uint8_t xReadCH376Data(void);
void xEndCH376Cmd(void);
void spi0_init(void);
uint8_t mInitCH376Host(void);

uint8_t CH376DiskMount( void );

uint8_t    CH376FileClose( uint8_t UpdateSz );

uint8_t    CH376GetDiskStatus( void );
uint8_t    CH376FileCreatePath( uint8_t * PathName );
uint8_t    CH376ByteWrite( uint8_t * buf, uint16_t  ReqCount, uint16_t * RealCount );
uint8_t    CH376ByteLocate( uint32_t offset );
uint8_t    CH376FileOpenPath( uint8_t * PathName );

#endif /* BSP_CH376_H */

main.c函数中调用实现数据导出到U盘的1234.txt文件

retval = mInitCH376Host();
    retval = CH376DiskConnect();     //读出U盘的状态 
    if(retval == USB_INT_SUCCESS)
    { //检查U盘是否连接//等待U盘插入
        UART3_Transmit_DMA(UART3,(uint8_t *)"U OK", 10);//------------------------------------------调试用
    }
    else
    {
        UART3_Transmit_DMA(UART3,(uint8_t *)"没检测到U盘", 20);//------------------------------------调试用
    } 

    for ( u32count = 0; u32count < 10000; u32count ++ )
    { 
        delay_1ms( 10 );
        retval = CH376DiskMount( );  //初始化磁盘并测试磁盘是否就绪.  
    
        if ( retval == USB_INT_SUCCESS ) //准备好 
        {
             //UART3_Transmit_DMA(UART3,(uint8_t *)"INTOK", 10);//----------------------------------调试用
             break;
            
        }
        else if ( retval == ERR_DISK_DISCON )// 检测到断开,重新检测并计时 
        {
            break;  
        }
        if ( CH376GetDiskStatus( ) >= DEF_DISK_MOUNTED && u32count >= 5 ) // 有的U盘总是返回未准备好,不过可以忽略,只要其建立连接MOUNTED且尝试5*50mS 
        {
            //UART3_Transmit_DMA(UART3,(uint8_t *)"STATUSOK", 10);//--------------------------------调试用
            break;
        }
    }
 
    delay_1ms(1); //每次操作后必要的延时

    u32retcount=CH376FileOpenPath( "/1234.TXT" );
    if(u32retcount==ERR_MISS_FILE ){
        u32retcount=CH376FileCreatePath( "/1234.TXT" ); 
    }
        
    UART3_Transmit_DMA(UART3,(uint8_t *)"===\n", 20);//---------------------------------------------调试用
    u32retcount = CH376ByteLocate( 0xFFFFFFFF );
    if ( u32retcount != USB_INT_SUCCESS ) 
    {
        return( u32retcount );
    }
        
    delay_1ms(200); //每次操作后必要的延时
    
    u32retcount = sprintf( (char *)buf , "时间  状态 \n");
    u32retcount=CH376ByteWrite((uint8_t *) buf, u32retcount, NULL ); // 以字节为单位向当前位置写入数据块
    
    for(u32count=0;u32count<10;u32count++)
    {
        memset((void *)buf, 255, 0);
        u32retcount = sprintf( (char *)buf , "   %d %d %d %d %d %d %d %d %s\n",  u32count,retval,retval,retval,retval,retval,retval,retval,"a");
        u32retcount=CH376ByteWrite((uint8_t *) buf, u32retcount, NULL ); // 以字节为单位向当前位置写入数据块 
        u32retcount=CH376ByteWrite((uint8_t *) buf, 0, NULL ); // 以字节为单位向当前位置写入数据块         
        UART3_Transmit_DMA(UART3,(uint8_t *)buf, 60);//------------------------------------单条数据下载成功
    }
    delay_1ms(200); //每次操作后必要的延时
    retval=CH376FileClose(1);   // 关闭文件,对于字节读写建议自动更新文件长度 
    
    while ( CH376DiskConnect() == USB_INT_SUCCESS ) delay_1ms(500);  // 等待U盘拔出
    delay_1ms(200); //每次操作后必要的延时
    UART3_Transmit_DMA(UART3,(uint8_t *)"DONE!", 10);//------------------------------------下载结束

参考资料

  • GD32F427xx_Datasheet_Rev1.2.pdf
  • GD32F4xx_User_Manual_Rev2.7_CN.pdf
  • GD32F4xx_gujiankushiyongzhinan_V1.0.pdf
  • GD32F4xx_Firmware_Library_V3.0.2.zip
  • GD32F4xx_Demo_Suites_V2.6.1.rar
  • GD32F4xx_AddOn_V3.0.0.rar

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/185999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

字符设备驱动之mmap、select

一、mmap mmap&#xff0c;简而言之就是将内核空间的一段内存区域映射到用户空间。映射成功后&#xff0c;用户对这段内存区域的修改可以直接反映到内核空间&#xff0c;相反&#xff0c;内核空间对这段区域的修改也直接反映用户空间。那么对于内核空间与用户空间两者之…

Prometheus + Grafana + Alertmanager 本地安装调试

一、简介 Prometheus 是一款强大的监控软件&#xff0c;一般会与Grafana和Alertmanager一起配合使用&#xff0c;而且多用于k8s集群。简介的话网上很多&#xff0c;官网 更是详细&#xff0c;这里就不班门弄斧了。k8s集群环境下的安装网上很多&#xff0c;但是k8s集群搭建时间…

【实际开发10】- 远程调用 ( Feign )

目录 1. Feign 调用注意事项 - ★★★ 1. 【原则】: 禁止遍历 - 多次跨服务调用接口 ( 提需求 : idList ) 1. 单一数据查询 , 可直接用 Feign单一查询接口 2. List数据查询 , 需进行 Feign 数据转换 , 禁止遍历 Feign 3. stream() : 从List<对象> , 取出 id 和 name…

关于PS VR2和独占,开发者和分析师都怎么看

近期&#xff0c;索尼正式宣布了PS VR2首发游戏列表&#xff0c;共计37款游戏&#xff0c;其中包括备受关注的IP大作《地平线&#xff1a;山之召唤》等。从这37款首发阵容中可以看到一个现象&#xff0c;大部分游戏是非新作&#xff0c;而是已经在PS VR1或其它VR平台上线&#…

C++基础——C++数组

C基础——C数组C 数组声明数组初始化数组访问数组元素C 中数组详解C 数组 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 数组的声明并不是声明一个个单独的…

【数据结构基础】线性表 - 链表

n个节点离散分配&#xff0c;彼此通过指针相连&#xff0c;每个节点只有一个前驱节点&#xff0c;每个节点只有一个后续节点&#xff0c;首节点没有前驱节点&#xff0c;尾节点没有后续节点。确定一个链表我们只需要头指针&#xff0c;通过头指针就可以把整个链表都能推出来。知…

设计模式-UML图

目录 2&#xff0c;UML图 2.1 类图概述 2.2 类图的作用 2.3 类图表示法 2.3.1 类的表示方式 2.3.2 类与类之间关系的表示方式 2&#xff0c;UML图 统一建模语言&#xff08;Unified Modeling Language&#xff0c;UML&#xff09;是用来设计软件的可视化建模语言。它的特…

Matlab pdetool

云溪岩绵迎彩霞,博主精神压力大呀,没人说说知心话啊,SCU物理要命啦........基本物理方程静电磁场交流电磁场热传导Options->ApplicationGeneric Scalar泛型标量Generic System通用系统Structural Mechanics,Plane Stress结构力学 - 平面应力Structural Mechanics,Plane Stra…

Flashback Oracle文档阅读

和Flashback相关的文档大多位于备份和恢复用户指南 和Oracle 数据库开发指南中。 基本概念 请参看备份和恢复用户指南的1.4 About Oracle Flashback Technology。 Oracle Flashback Technology的定义&#xff1a; A set of Oracle Database features that provide an additi…

Verilog HDL门级建模

⭐本专栏针对FPGA进行入门学习&#xff0c;从数电中常见的逻辑代数讲起&#xff0c;结合Verilog HDL语言学习与仿真&#xff0c;主要对组合逻辑电路与时序逻辑电路进行分析与设计&#xff0c;对状态机FSM进行剖析与建模。 &#x1f525;文章和代码已归档至【Github仓库&#xf…

数字电路设计:Logicly 最新版Crack

Logicly有效地教授逻辑门 数字电路 — 使用 Logicly 现代直观的用户界面支持拖放、复制/粘贴、缩放等功能&#xff0c;可快速轻松地设计电路。 通过暂停模拟并在您逐步推进时观察信号传播来控制调试。 不用担心学生计算机上的多个平台。在 Windows 和 macOS 上安装 创建引人入…

子查询-MySQL

文章目录理解举例基本使用语法分类分类方式1单行子查询多行子查询分类方式2&#xff1a;单行子查询单行比较操作符代码示例HAVING 中的子查询CASE中的子查询子查询中的空值问题非法使用子查询多行子查询多行比较操作符代码示例相关子查询相关子查询执行流程代码示例EXISTS 与 N…

USART学习笔记

目录 1. USART框图 2. 传输帧图 3. 配置步骤 4.配置编码&#xff08;使用库函数&#xff09; 5. 函数调用缩略图 1. USART框图 2. 传输帧图 起始位的特征&#xff1a;时钟引脚CK处于低电平&#xff0c;TX引脚处于低电平&#xff0c;持续1个SCLK长度&#xff08;位长度&…

2020-ICLR-Memory-Based Graph Networks

2020-ICLR-Memory-Based Graph Networks Paper&#xff1a;https://arxiv.org/abs/2002.09518 Code: https://github.com/amirkhas/GraphMemoryNet 基于内存的图网络 图神经网络&#xff08;GNN&#xff09;是一类可对任意拓扑结构的数据进行操作的深度模型。 作者为GNN引入了…

Python类调用实例方法

通常情况下&#xff0c;我们习惯使用类对象调用类中的实例方法。但如果想用类调用实例方法&#xff0c;不能像如下这样&#xff1a;class CLanguage: definfo(self): print("我正在学 Python") #通过类名直接调用实例方法 CLanguage.info()运行上面代码&#xff0c;程…

线路板行业含铜废水处理,铜箔废水深度处理和铜回收

产品介绍 传统沉淀法不能满足日益提的环保要求(如电镀表三镍含量要求0.1mg/l以下)。针对特定重金属离子的特点&#xff0c;利用螯合树脂的特种功能基团与重金属离子形成络合物的特性&#xff0c;实现重金属离子的回收利用及深度去除。 CH-90Na对除铜镍铅锌钴锰等具有特定的选择…

GBASE亮相第四代英特尔® 至强® 可扩展处理器新品发布

1月11日&#xff0c;英特尔&#xff08;中国&#xff09;有限公司成功举办“芯加速 行至远”——第四代英特尔发布会。GBASE南大通用作为英特尔的长期战略合作伙伴&#xff0c;双方联合推出GBase 8a大规模分布式并行处理&#xff08;MPP&#xff09;数据库集群系统解决方案&…

在spring boot3中使用native image

文章目录简介安装GraalVM添加Native Image支持构建spring boot3应用总结简介 在之前spring boot3文章中我们介绍了&#xff0c;spring boot3的一个重要特性就是支持把spring boot3的应用编译成为GraalVM的Native Image。 今天我们用具体的例子来给大家演示一下如何正确的将sp…

nacos 删除过期实例源码分析

nacos 删除过期实例也是注册中心的一个重要功能&#xff0c;今天我们从入口到结束分析一下&#xff0c;首先确定删除的入口在服务端注册接口的源码里&#xff0c;此处可以参考&#xff1a;参考注册源码 一、注册入口 1、创建空服务 public void registerInstance(String name…

行测笔记(主要知识点)

文章目录&#xff1a; 一&#xff1a;言语理解 1.技巧关系 2.逻辑填空 二&#xff1a;判断推理 1.图形推理 2.定义判断 3.类比推理 4.逻辑判断 三&#xff1a;资料分析 1.增长率 2.增长量 3.比重 4.平均数 5.倍数与比值 三&#xff1a;数量关系 1.解题…