周文杰 · 2022年12月24日 · 山东

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

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

硬件连接方面

image.png
image.png

软件程序方面

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
推荐阅读
关注数
10708
内容数
187
中国高性能通用微控制器领域的领跑者兆易创新GD系列芯片技术专栏。
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息