前言
在之前《GD32E103移植USB-Host》的文章基础上做优化,之前的移植只是把裸机程序移植到RTT,实质上存在BUG和内存分配不合理的地方,需要进一步修改。
一、优化USB-Host内存分配
1、修改usb_host.c中usb_host_init函数,注意对应.h文件也要改一下函数声明,你懂的,该函数主要修改有:
- 将USB相关的结构体、数组的全局定义改成使用动态内存去分配,有利于内存管理。
- 添加一个事件的初始化,为了后续修复BUG。
- 将初始化顺序做了调整,内存申请、事件初始化、设备注册和线程创建成功后才能进行usb初始化,最后再启动USB线程。
int usb_host_init(void)
{
rt_err_t res = -RT_ERROR;
rt_device_t dev = RT_NULL;
rt_thread_t thread = RT_NULL;
if((local_buffer = (uint8_t *)rt_malloc(LOCAL_BUFFER_SIZE)) == RT_NULL)
{
rt_kprintf("local_buffer malloc failed\r\n");
return -1;
}
rt_memset(local_buffer, 0, LOCAL_BUFFER_SIZE);
if((usbh_cfg_desc = (uint8_t *)rt_malloc(USBH_CFG_DESC_SIZE)) == RT_NULL)
{
rt_kprintf("usbh_cfg_desc malloc failed\r\n");
return -1;
}
rt_memset(usbh_cfg_desc, 0, USBH_CFG_DESC_SIZE);
if((usbh_msc_param = (msc_param_struct *)rt_malloc(sizeof(msc_param_struct))) == RT_NULL)
{
rt_kprintf("usbh_msc_param malloc failed\r\n");
return -1;
}
rt_memset(usbh_msc_param, 0, sizeof(msc_param_struct));
if((msc_machine = (msc_machine_struct *)rt_malloc(sizeof(msc_machine_struct))) == RT_NULL)
{
rt_kprintf("msc_machine malloc failed\r\n");
return -1;
}
rt_memset(msc_machine, 0, sizeof(msc_machine_struct));
if((usbh_state_core = (usbh_state_handle_struct *)rt_malloc(sizeof(usbh_state_handle_struct))) == RT_NULL)
{
rt_kprintf("usbh_state_core malloc failed\r\n");
return -1;
}
rt_memset(usbh_state_core, 0, sizeof(usbh_state_handle_struct));
if((usbh_msc_cbw_data = (host_cbwpkt_union *)rt_malloc(sizeof(host_cbwpkt_union))) == RT_NULL)
{
rt_kprintf("usbh_msc_cbw_data malloc failed\r\n");
return -1;
}
rt_memset(usbh_msc_cbw_data, 0, sizeof(host_cbwpkt_union));
if((usbh_msc_csw_data = (host_cswpkt_union *)rt_malloc(sizeof(host_cswpkt_union))) == RT_NULL)
{
rt_kprintf("usbh_msc_csw_data malloc failed\r\n");
return -1;
}
rt_memset(usbh_msc_csw_data, 0, sizeof(host_cswpkt_union));
if((usbh_msc_botxfer_param = (usbh_botxfer_struct *)rt_malloc(sizeof(usbh_botxfer_struct))) == RT_NULL)
{
rt_kprintf("usbh_msc_botxfer_param malloc failed\r\n");
return -1;
}
rt_memset(usbh_msc_botxfer_param, 0, sizeof(usbh_botxfer_struct));
/* allocate memory for the usbh_usr_struct */
if((usbh_usr = (usbh_usr_struct *)rt_malloc(sizeof(usbh_usr_struct))) == RT_NULL)
{
rt_kprintf("malloc memory for usbh_usr_struct failed!\r\n");
return -1;
}
rt_memset(usbh_usr, 0, sizeof(usbh_usr_struct));
/* allocate memory for the usbh_data_in_buffer */
if((usbh_data_in_buffer = (uint8_t *)rt_malloc(USBH_DATA_BUFFER_SIZE)) == RT_NULL)
{
rt_kprintf("malloc memory for usbh_data_in_buffer failed!\r\n");
return -1;
}
rt_memset(usbh_data_in_buffer, 0, USBH_DATA_BUFFER_SIZE);
/* allocate memory for the usbh_data_in_buffer */
if((usbh_data_out_buffer = (uint8_t *)rt_malloc(USBH_DATA_BUFFER_SIZE)) == RT_NULL)
{
rt_kprintf("malloc memory for usbh_data_out_buffer failed!\r\n");
return -1;
}
rt_memset(usbh_data_out_buffer, 0, USBH_DATA_BUFFER_SIZE);
if((dev = (rt_device_t)rt_malloc(sizeof(struct rt_device))) == RT_NULL)
{
rt_kprintf("dev malloc failed\r\n");
return -1;
}
rt_memset(dev, 0, sizeof(struct rt_device));
if((res = rt_device_register(dev, UDISK_DEVIVE_NAME, RT_DEVICE_FLAG_DEACTIVATE)) != RT_EOK)
{
rt_kprintf("register usb host failed res = %d\r\n", res);
return -2;
}
dev->init = rt_udisk_init;
dev->read = rt_udisk_read;
dev->write = rt_udisk_write;
dev->control = rt_udisk_control;
/* 初始化事件对象 */
if(rt_event_init(&udisk_event, "udisk event", RT_IPC_FLAG_FIFO) != RT_EOK)
{
rt_kprintf("udisk event init failed.\n");
return -3;
}
if((thread = rt_thread_create("usbh", rt_usbh_thread_entry, RT_NULL,USB_THREAD_STACK_SIZE, 8, 20)) == RT_NULL)
{
rt_kprintf("create usb host thread failed.\n");
return -4;
}
/* usb rcu init */
usb_rcu_init();
/* timer nvic initialization */
timer_nvic_init();
/* configure GPIO pin used for switching VBUS power */
usb_hwp_vbus_config(&usb_core_dev);
/* host de-initializations */
usbh_deinit(&usb_core_dev, &usb_host, usbh_state_core);
/* start the USB core */
hcd_init(&usb_core_dev, USB_FS_CORE_ID);
/* init usr call back */
usb_host.usr_cb->init();
/* enable interrupts */
usb_hwp_interrupt_enable(&usb_core_dev);
/* startup usb host thread */
rt_thread_startup(thread);
return RT_EOK;
}
2、以下变量均可以在工程中找到,我们将其修改成指针即可,注意对应的.h文件的外部声明也要修改成指针,并对其赋RT_NULL初值,原本没有外部声明的变量就需要自己添加一个外部声明,修改后原代码使用的是“.”去引用结构体成员变量,我们需要将其改成“->”,原码使用“&”获取结构体地址,我们就需要删掉,不需要“&”了,修改的地方有特别多这边篇幅有限就不一一讲了,可以使用MDK自带的全局替换功能区快速搞定。
local_buffer、local_buffer
usbh_msc_param
msc_machine
usbh_state_core
usbh_msc_cbw_data、usbh_msc_csw_data、usbh_msc_botxfer_param
3、在usb_host.h中添加以下宏定义。
#define UDISK_MOUNTPOINT "/udisk"
#define UDISK_DEVIVE_NAME "usbh"
二、修复USB-Host移植后BUG
1、之前移植过后的工程,时而会发现操作一段时间或者快速连续操作几次后USB就挂了,需要重新挂载或者重启整个系统,这是因为原USB框架下只允许在其提供的用户函数usbh_usr_msc_application中进行USB操作,在其他地方操作USB可能会出现意向不到的错误,我们使用事件就是为了线程之间的同步,让我们在作其他任何线程中操作USB时都会等待到USB用户线程usbh_usr_msc_application和其同步后再进行操作。
2、定义和声明事件
3、修改rt_udisk_read、rt_udisk_write函数,这两个函数提供给文件系统使用的,顶层的文件系统操作函数最终都要调用这个函数来对U盘读写操作。
- 读、写函数和下面的流程都一样
/**
* This function will read some data from a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of reading
* @param buffer the data buffer to save read data
* @param size the size of buffer
*
* @return the actually read size on successful, otherwise negative returned.
*/
rt_size_t rt_udisk_read(rt_device_t dev, rt_off_t pos, void* buffer,
rt_size_t size)
{
rt_uint32_t res;
BYTE status = USBH_MSC_OK;
if (!size)
return 0;
rt_event_send(&udisk_event, WR_EVENT);
if (rt_event_recv(&udisk_event, ALLOW_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 1000, &res) != RT_EOK)
{
rt_kprintf("Recv ALLOW_EVENT timeout!\n");
goto err;
}
do
{
status = usbh_msc_read10(&usb_core_dev, buffer, pos, 512 * size);
usbh_msc_handle_botxfer(&usb_core_dev, &usb_host, usbh_state_core);
if(!hcd_is_device_connected(&usb_core_dev))
goto err;
}while(USBH_MSC_BUSY == status);
err:
rt_event_send(&udisk_event, END_EVENT);
if(USBH_MSC_OK == status)
return size;
return 0;
}
/**
* This function will write some data to a device.
*
* @param dev the pointer of device driver structure
* @param pos the position of written
* @param buffer the data buffer to be written to device
* @param size the size of buffer
*
* @return the actually written size on successful, otherwise negative returned.
*/
rt_size_t rt_udisk_write (rt_device_t dev, rt_off_t pos, const void* buffer,
rt_size_t size)
{
rt_uint32_t res;
BYTE status = USBH_MSC_OK;
if (!size)
return 0;
if (stat & USBH_MSC_STA_PROTECT)
return 0;
rt_event_send(&udisk_event, WR_EVENT);
if (rt_event_recv(&udisk_event, ALLOW_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 1000, &res) != RT_EOK)
{
rt_kprintf("Recv WR_EVENT timeout!\n");
goto err;
}
do
{
status = usbh_msc_write10(&usb_core_dev, (BYTE*)buffer, pos, 512 * size);
usbh_msc_handle_botxfer(&usb_core_dev, &usb_host, usbh_state_core);
if (!hcd_is_device_connected(&usb_core_dev))
goto err;
} while(USBH_MSC_BUSY == status);
err:
rt_event_send(&udisk_event, END_EVENT);
if(USBH_MSC_OK == status)
return size;
return 0;
}
4、添加头文件
5、添加宏定义
6、修改usbh_usr.c函数
int usbh_usr_msc_application(usb_core_handle_struct *pudev, uint8_t id)
{
rt_uint32_t res;
if(usbh_usr->init_status == USBH_USR_NOT_READY)
{
rt_kprintf("usbh msc init successfully.\r\n");
usbh_usr->init_status = USBH_USR_READY;
}
if (rt_event_recv(&udisk_event, WR_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 1, &res) == RT_EOK)
{
rt_event_send(&udisk_event, ALLOW_EVENT);
if (rt_event_recv(&udisk_event, END_EVENT, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 2000, &res) == RT_EOK)
{
}
}
return 0;
}
7、编译、下载测试,快速使用ls命令列出USB根目录下文件,看看会不会挂,不会挂,那就ok了。
三、添加应用层代码
1、实现系统启动后自动挂载GD25QXX和USB初始化,插入U盘后自动挂载U盘,拔出U盘后自动卸载U盘,具体代码自己看,对于移植这个顶层的代码是很简单的了,不懂的再留言吧。
2、修改usb_host.c中的udisk_mount和udisk_unmount函数,并在.h文件中进行声明。
int udisk_mount(void)
{
/*查找Flash设备*/
if(rt_device_find(UDISK_DEVIVE_NAME) != RT_NULL)
{
/*挂载Flash*/
if(dfs_mount(UDISK_DEVIVE_NAME, UDISK_MOUNTPOINT, "elm", 0, 0) == 0)
{
rt_kprintf("flash mount success!\n");
usbh_usr->mount_status = UDISK_MOUNT;
}
else
{
rt_kprintf("flash mount failed!\n");
return -1;
}
}
return RT_EOK;
}
MSH_CMD_EXPORT(udisk_mount,udisk mount);
int udisk_unmount(void)
{
if(dfs_unmount(UDISK_MOUNTPOINT) == 0)
{
usbh_usr->mount_status = UDISK_UNMOUNT;
rt_kprintf("unmount %s success!\n",UDISK_MOUNTPOINT);
}
else
{
rt_kprintf("unmount %s failed!\n",UDISK_MOUNTPOINT);
return -1;
}
return RT_EOK;
}
MSH_CMD_EXPORT(udisk_unmount,udisk unmount);
3、在usbh_usr.h中添加宏,和挂载先关的结构体变量
4、修改gd25qxx.c和gd25qxx.h文件
gd25qxx.c
#include "gd25qxx.h"
gd25qxx_struct *gd25qxx_sta = RT_NULL;
int gd25qxx_init(void)
{
if((gd25qxx_sta = (gd25qxx_struct *)rt_malloc(sizeof(gd25qxx_struct))) == RT_NULL)
{
rt_kprintf("gd25qxx_sta malloc failed\r\n");
return -1;
}
rt_memset(gd25qxx_sta, 0, sizeof(gd25qxx_struct));
if(gd25qxx_mount() != RT_EOK)
return -1;
return RT_EOK;
}
/*******************************************************************************
* 函 数 名 : gd25qxx_create_fatfs
* 输 入 : 空
* 输 出 : 空
* 函数功能 : 创建文件系统
* 详细描述 : 空
*******************************************************************************/
int gd25qxx_create_fatfs(void)
{
/*格式化指定存储设备,并创建文件系统*/
if(dfs_mkfs("elm", "gd25q") == RT_EOK)
{
rt_kprintf("create file system succeed!\n");
}
else
{
rt_kprintf("create file system failed!\n");
return -1;
}
return RT_EOK;
}
MSH_CMD_EXPORT(gd25qxx_create_fatfs,gd25qxx create fatfs);
/*******************************************************************************
* 函 数 名 : w25qxx_mount
* 输 入 : 空
* 输 出 : 空
* 函数功能 : 挂载Flash文件系统
* 详细描述 : 空
*******************************************************************************/
int gd25qxx_mount(void)
{
/*查找Flash设备*/
if(rt_device_find("gd25q") != RT_NULL)
{
/*挂载Flash*/
if(dfs_mount("gd25q", FLASH_MOUNTPOINT, "elm", 0, 0) == 0)
{
gd25qxx_sta->fatfs_status = FATFS_NORMAL;
gd25qxx_sta->mount_status = GD25QXX_MOUNT;
rt_kprintf("mount %s success!\n",FLASH_MOUNTPOINT);
}
else
{
gd25qxx_sta->fatfs_status = FATFS_FAILURE;
gd25qxx_sta->mount_status = GD25QXX_UNMOUNT;
rt_kprintf("mount %s failed!\n",FLASH_MOUNTPOINT);
return -1;
}
}
return 0;
}
MSH_CMD_EXPORT(gd25qxx_mount,gd25qxx mount);
/*******************************************************************************
* 函 数 名 : w25qxx_unmount
* 输 入 : 空
* 输 出 : 空
* 函数功能 : 卸载Flash文件系统
* 详细描述 : 空
*******************************************************************************/
int gd25qxx_unmount(void)
{
if(dfs_unmount(FLASH_MOUNTPOINT) == 0)
{
rt_kprintf("unmount %s success!\n",FLASH_MOUNTPOINT);
}
else
{
rt_kprintf("unmount %s failed!\n",FLASH_MOUNTPOINT);
return -1;
}
return RT_EOK;
}
MSH_CMD_EXPORT(gd25qxx_unmount,gd25qxx unmount);
gd25qxx.h
#ifndef __GD25QXX_H
#define __GD25QXX_H
#include <rtthread.h>
#include "spi_flash.h"
#include "spi_flash_sfud.h"
#include "drv_spi.h"
#include <dfs_elm.h>
#include <dfs_fs.h>
#define FLASH_MOUNTPOINT "/"
#define FATFS_FAILURE 0
#define FATFS_NORMAL 1
#define GD25QXX_MOUNT 0
#define GD25QXX_UNMOUNT 1
typedef struct
{
uint8_t fatfs_status:1; //文件系统状态
uint8_t mount_status:1; //挂载状态
}gd25qxx_struct;
extern gd25qxx_struct *gd25qxx_sta;
int gd25qxx_init(void);
int gd25qxx_create_fatfs(void);
int gd25qxx_mount(void);
int gd25qxx_unmount(void);
#endif
5、在applications文件夹下新建disk.h、disk.c文件,并添加以下代码。
disk.c
#include "disk_app.h"
#define DISK_THREAD_STACK_SIZE 1024
static void rt_disk_thread_entry(void* parameter)
{
while(1)
{
if((usbh_usr->init_status == USBH_USR_READY) && (usbh_usr->mount_status == UDISK_UNMOUNT))
{
usbh_usr->mount_status = UDISK_MOUNT;
udisk_mount();
}
if((usbh_usr->connected_status == USBH_USR_DISCONNECTED) && (usbh_usr->mount_status == UDISK_MOUNT))
{
usbh_usr->mount_status = UDISK_UNMOUNT;
udisk_unmount();
}
rt_thread_mdelay(100);
}
}
int disk_init(void)
{
rt_thread_t thread = RT_NULL;
rt_thread_mdelay(100);
gd25qxx_init();
usb_host_init();
if((thread = rt_thread_create("disk", rt_disk_thread_entry, RT_NULL,DISK_THREAD_STACK_SIZE, 10, 20)) != RT_NULL)
rt_thread_startup(thread);
else
rt_kprintf("create disk thread failed.\n");
return RT_EOK;
}
MSH_CMD_EXPORT(disk_init,disk init);
INIT_APP_EXPORT(disk_init);
disk.h
#ifndef __DISK_APP_H
#define __DISK_APP_H
#include "gd25qxx.h"
#include "usb_host.h"
#endif
6、修改usb_host.c中rt_usbh_thread_entry函数。
static void rt_usbh_thread_entry(void* parameter)
{
while(1)
{
host_state_polling_fun(&usb_core_dev, &usb_host, usbh_state_core);
rt_thread_mdelay(10);
}
}
7、编译下载运行后发现初始化就会死机。
加大man线程的堆内存即可。
8、编译下载运行。