GD32F30x_使用外部FLASH模拟U盘

一、工具

  1、硬件:GD32F30x系列单片机

  2、编译环境:KEIL

  3、Flash芯片:GD25Q256DF

  4、一根能够单片机连接电脑的USB数据线

二、需求分析

  类似于我们平常使用的U盘,当单片机与电脑通过USB数据线进行连接的时候,电脑能够识别出单片机通过外部Flash模拟出的U盘,在电脑上能够对该U盘进行文件的相互拷贝,并且重新上电后数据不丢失。通过对USB的了解,USB分设备(Device)模式和主机(Host)模式,使用单片机模拟U盘是让USB工作在设备(Device)模式下。

三、查看当前使用单片支持的USB功能

  目前市面上出现的32位的基于ARM架构的单片机基本都具有USB功能,根据单片机性能的不同对USB功能的支持也有所不同,下面来看一下我当前使用的这款单片机具备USB的哪些功能。

  通过查阅单片机的用户手册,发现数据手册上有两种介绍,如下图所示:

   用户手册上介绍USBD只适用于GD32F303系列芯片,如下图所示,而我用的芯片是GD32F305系列的,所以这一部分的内容不看。

   USBFS的介绍中包含GD32F305,包含我当前使用的芯片型号,下面我们阅览这一块对USB的介绍内容。

   概述中基本已经说明了当前我使用芯片USB的所具有的功能,当然我本次需要的USB设备(Device)功能当然也包含在内,如下图所示:

   该芯片USBFS的结构图,如下图所示:

   以及信号线描述,如下图所示:

   (其实我也不太懂USB的协议,用户手册上的大多介绍看的也是一脸懵逼,因为只是用,对很多细节的地方并没有去深究,也望读者不要计较太多。)

三、USB驱动库移植

  1、在官方提供的固件库中找到USB驱动文件,全部拷贝到自己的工程中。

   2、在官方的固件库例程中找到USBFS->USB_Device->MSC例程,打开该文件夹将inc和src中关于usb的文件全部拷贝到自己工程中,具体内容如下图所示:

  inc文件夹打开所示:

  src文件夹打开所示

  我这里拷贝到了自己工程中的usb文件夹中,同时在该文件夹中创建一个usbd_norflash_access.c文件和usbd_norflash_access.h文件为后面添加外部FLASH驱动程序使用,如下图所示:

 

   3、打开自己的工程结构,添加上面拷贝的文件,添加完成的结果如下图所示:

   4、指定添加的头文件路径,同时添加USBFS的宏定义USE_USBFS,如下图所示:

   由于官方给的例程使用的方式是单片机的内部SRAM模拟U盘,做完以上后可以尝试编译一下,错误应该会很少。当然也直接可以把官方提供的例程直接下载到自己单片机上,先观察一下效果。

四、将外部Flash的读写驱动程序添加到USB驱动中

   从这里开始我们才算是添加自己的东西,前面的工作只是对官方库的移植(外部Flash的读写函数我在另一篇文章中有介绍,有兴趣的可以去查看,链接:https://www.cnblogs.com/wenhao-Web/p/14052266.html)。

  1、打开usbd_norflash_access.h和usbd_norflash_access.c文件,进行如下修改:

  usbd_norflash_access.c文件中的内容

#include "./usb/usb_conf.h"
#include "./usb/usbd_norflash_access.h"
#include "./gd25qxx/gd25q256.h"

/*!
    \brief      read data from multiple blocks of internal NORFLASH
    \param[in]  pbuf: pointer to user buffer
    \param[in]  read_addr: address to be read
    \param[in]  block_size: size of block
    \param[in]  block_num: number of block
    \param[out] none
    \retval     operation status
*/
uint32_t  norflash_multi_blocks_read (uint8_t *pbuf,
                                  uint32_t read_addr,
                                  uint16_t block_size,
                                  uint32_t block_num)
{
    gd25q256df_read_data(pbuf, read_addr, block_num*block_size);
    return 0;
}

/*!
    \brief      write data from multiple blocks of internal NORFLASH
    \param[in]  pbuf: pointer to user buffer
    \param[in]  write_addr: address to be write
    \param[in]  block_size: size of block
    \param[in]  block_num: number of block
    \param[out] none
    \retval     operation status
*/
uint32_t norflash_multi_blocks_write (uint8_t *pbuf,
                                  uint32_t write_addr,
                                  uint16_t block_size,
                                  uint32_t block_num)
{
    gd25q256df_write_data(pbuf, write_addr, block_num*block_size);
    return 0;
}

  usbd_norflash_access.h文件中的内容

#ifndef USBD_NORFLASH_ACCESS_H
#define USBD_NORFLASH_ACCESS_H

#include "gd32f30x.h"

#define NORFLASH_BLOCK_SIZE         512            /* 固定每个块大小为512 */
#define NORFLASH_BLOCK_NUM          65536         /* (1024*1024*32/512) 使用32Mbyte */

/* function declarations */
/* read data from multiple blocks of internal NORFLASH */
uint32_t  norflash_multi_blocks_read (uint8_t *pbuf,
                                  uint32_t read_addr,
                                  uint16_t block_size,
                                  uint32_t block_num);
/* write data from multiple blocks of internal NORFLASH */
uint32_t  norflash_multi_blocks_write (uint8_t *pbuf,
                                   uint32_t write_addr,
                                   uint16_t block_size,
                                   uint32_t block_num);

#endif /* USBD_NORFLASH_ACCESS_H */

   上面两个文件的内容很简单,就是读和写,和官方提供的SRAM实现的写法基本一样。

   2、接着是对usbd_bbb_scsi.c文件中的内容进行修改

  修改的内容主要是把官方提供的SRAM的内容替换成NORFLASH的内容即可。

/* USB mass storage format capacities data */
uint8_t format_capacities_data[FORMAT_CAPACITIES_DATA_LENGTH] =
{
    0x00, 0x00, 0x00,                             /* reserved */
    0x08,                                          /* capacity list length */
    (uint8_t)((NORFLASH_BLOCK_NUM - 1) >> 24),   /* number of blocks (MSB) */
    (uint8_t)((NORFLASH_BLOCK_NUM - 1) >> 16),   /* number of blocks (MSB) */
    (uint8_t)((NORFLASH_BLOCK_NUM - 1) >> 8),    /* number of blocks (MSB) */
    (uint8_t)(NORFLASH_BLOCK_NUM - 1),           /* number of blocks (MSB) */
    0x02,                                         /* bit0 - bit1:descriptor code */
    (uint8_t)((NORFLASH_BLOCK_SIZE) >> 16),      /* block length (MSB) */
    (uint8_t)((NORFLASH_BLOCK_SIZE) >> 8),       /* block length (MSB) */
    (uint8_t)(NORFLASH_BLOCK_SIZE)               /* block length (MSB) */
};

/* USB mass storage read capacities data */
uint8_t read_capacities_data[READ_CAPACITIES_DATA_LENGTH] = 
{
    (uint8_t)((NORFLASH_BLOCK_NUM - 1) >> 24),   /* last logical block address (MSB) */
    (uint8_t)((NORFLASH_BLOCK_NUM - 1) >> 16),   /* last logical block address (MSB) */
    (uint8_t)((NORFLASH_BLOCK_NUM - 1) >> 8),    /* last logical block address (MSB) */
    (uint8_t)(NORFLASH_BLOCK_NUM - 1),           /* last logical block address (MSB) */

    (uint8_t)((NORFLASH_BLOCK_SIZE) >> 24),      /* block length in bytes (MSB) */
    (uint8_t)((NORFLASH_BLOCK_SIZE) >> 16),      /* block length in bytes (MSB) */
    (uint8_t)((NORFLASH_BLOCK_SIZE) >> 8),       /* block length in bytes (MSB) */
    (uint8_t)(NORFLASH_BLOCK_SIZE)               /* block length in bytes (MSB) */
};

  读的修改部分:

  写的修改部分: 

   3、USB启动部分程序,其实就是把官方的那一套内容全部拷贝到自己工程中。

  相关变量的定义和初始化

usb_core_handle_struct usbhs_core_dev =
{
    .dev = 
    {
        .dev_desc = (uint8_t *)&device_descripter,
        .config_desc = (uint8_t *)&configuration_descriptor,    
        .strings = usbd_strings,                                
        .class_init = msc_init,                                    
        .class_deinit = msc_deinit,                                
        .class_req_handler = msc_req_handler,                    
        .class_data_handler = msc_data_handler
    },

    .udelay = delay_us,
    .mdelay = delay_ms
};

void usb_clock_config(void);
void usb_gpio_config(void);
void usb_interrupt_config(void);

uint8_t timer_prescaler = 0;
uint32_t usbfs_prescaler = 0;

 

  主函数中的内容

/*!
    \brief      main function
    \param[in]  none
    \param[out] none
    \retval     none
*/
int main(void)
{
    /* configure 4 bits pre-emption priority */
    nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
    
    bsp_spi1_init();
    gd25q256df_init();
    flash_id = gd25q256df_read_id();
    if(flash_id == 0xC84019)
    {        
        /* configure USB clock */
        usb_clock_config();    
        /* USB timer configure */
        timer_nvic_init();
        /* USB device stack configure */
        usbd_init(&usbhs_core_dev, USB_FS_CORE_ID);
        /* USB interrupt configure */
        usb_interrupt_config();    
        
        /* check if USB device is enumerated successfully */
        while (usbhs_core_dev.dev.status != USB_STATUS_CONFIGURED) {}            
    }    
    
    while(1);
}

  USB时钟配置

/*!
    \brief      configure USB clock
    \param[in]  none
    \param[out] none
    \retval     none
*/
void usb_clock_config(void)
{
    uint32_t system_clock = rcu_clock_freq_get(CK_SYS);
  
    if (system_clock == 48000000) {
        usbfs_prescaler = RCU_CKUSB_CKPLL_DIV1;
        timer_prescaler = 3;
    } else if (system_clock == 72000000) {
        usbfs_prescaler = RCU_CKUSB_CKPLL_DIV1_5;
        timer_prescaler = 5;
    } else if (system_clock == 120000000) {
        usbfs_prescaler = RCU_CKUSB_CKPLL_DIV2_5;
        timer_prescaler = 9;
    } else {
        /*  reserved  */
    }

    rcu_usb_clock_config(usbfs_prescaler);
    rcu_periph_clock_enable(RCU_USBFS);
}

  USB驱动所使用到的中断配置

/*!
    \brief      configure USB interrupt
    \param[in]  none
    \param[out] none
    \retval     none
*/
void usb_interrupt_config(void)
{
    nvic_irq_enable((uint8_t)USBFS_IRQn, 4U, 0U);

    /* enable the power module clock */
    rcu_periph_clock_enable(RCU_PMU);

    /* USB wakeup EXTI line configuration */
    exti_interrupt_flag_clear(EXTI_18);
    exti_init(EXTI_18, EXTI_INTERRUPT, EXTI_TRIG_RISING);
    exti_interrupt_enable(EXTI_18);

    nvic_irq_enable((uint8_t)USBFS_WKUP_IRQn, 1U, 0U);
}

  USBFS中断函数

/*!
    \brief      this function handles USBD interrupt
    \param[in]  none
    \param[out] none
    \retval     none
*/
void  USBFS_IRQHandler (void)
{
    usbd_isr (&usbhs_core_dev);
}

  USBFS唤醒中断函数

/*!
    \brief      this function handles USBD wakeup interrupt request.
    \param[in]  none
    \param[out] none
    \retval     none
*/
void USBFS_WKUP_IRQHandler(void)
{
    if (usbhs_core_dev.cfg.low_power) {
        SystemInit();
        rcu_usb_clock_config(usbfs_prescaler);

        rcu_periph_clock_enable(RCU_USBFS);

        usb_clock_ungate(&usbhs_core_dev);
    }

    exti_interrupt_flag_clear(EXTI_18);
}

  定时器中断函数

/*!
    \brief      this function handles Timer0 updata interrupt request.
    \param[in]  none
    \param[out] none
    \retval     none
*/
void TIMER0_UP_TIMER9_IRQHandler(void)
{
    timer_delay_irq();
}

   至此编译一下,没有问题,单片机与电脑连接正常就会在电脑上显示一个U盘,如果是第一次使用还需格式化。试试向该U盘中创建、拷贝文件是否正常,最好是用一个与该U盘大小差不多的文件测试。

   如下图所示,是我连接电脑模拟出的U盘效果(在使用的过程中发现一个问题,就是拷贝大文件传输速度不连续,不知道是为什么,希望有知道的给与指导)

 #endif

 

posted @ 2020-11-28 15:50  不要让自己太懒  阅读(5939)  评论(7编辑  收藏  举报