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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM