基于RT-Thread:W25Q128虚拟U盘与文件系自由切换
记录下SPI Flash U盘实现过程中踩过的坑,与您分享。
前提条件是,需要先将SPI Flash 配置到elm fal文件系统,并挂载成功。如下图
然后开始配置USB
1,在CubeMX,选择SUB_OTG_FS
2 选择USB Device
3,确认USB时钟为48MHz
4,生成代码,然后打开生成的工程,如下
【1】将工程中void SystemClock_Config(void)的代码更新到RT-thread 平台board.c相同的函数内替换掉原有代码。
【2】将生成的USB 引脚初始化程序复制到board.c 内。
void HAL_PCD_MspInit(PCD_HandleTypeDef* pcdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspInit 0 */
/* USER CODE END USB_OTG_FS_MspInit 0 */
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* Peripheral clock enable */
__HAL_RCC_USB_OTG_FS_CLK_ENABLE();
/* Peripheral interrupt init */
HAL_NVIC_SetPriority(OTG_FS_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspInit 1 */
/* USER CODE END USB_OTG_FS_MspInit 1 */
}
}
void HAL_PCD_MspDeInit(PCD_HandleTypeDef* pcdHandle)
{
if(pcdHandle->Instance==USB_OTG_FS)
{
/* USER CODE BEGIN USB_OTG_FS_MspDeInit 0 */
/* USER CODE END USB_OTG_FS_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USB_OTG_FS_CLK_DISABLE();
/**USB_OTG_FS GPIO Configuration
PA11 ------> USB_OTG_FS_DM
PA12 ------> USB_OTG_FS_DP
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(OTG_FS_IRQn);
/* USER CODE BEGIN USB_OTG_FS_MspDeInit 1 */
/* USER CODE END USB_OTG_FS_MspDeInit 1 */
}
}
5,配置RT-Thread平台代码
【1】在RT-thread studio中打开RT-Thread Settings,然后在右上角的搜索栏内输入USB
【2】选择使用USB,设置如下
注意,磁盘名是指SPI Flash 文件系统分区时指定的名称,将来USB组件需要通过该名称查找注册的设备进行关联
系统挂载分区时需要用到此名字
6,打开board/Kconfig,加入如下代码后保存。
config BSP_USING_USBD
bool "Enable OTGFS as USB device"
select RT_USING_USB_DEVICE
default n
config BSP_USBD_EP_ISOC
bool
default n
depends on BSP_USING_USBD
config BSP_USING_STM32_SDIO
bool "Enable SDIO"
select RT_USING_SDIO
select PKG_USING_STM32_SDIO
default n
help
BSP_USING_STM32_SDIO use drv_sdio_adapter.c,and
BSP_USING_SDIO use drv_sdio.c
config BSP_USING_ON_CHIP_FLASH
bool "Enable On Chip FLASH (LittleFS)"
select BSP_USING_FS
select RT_USING_DFS
select RT_USING_DFS_ROMFS
select RT_USING_MTD_NOR
select PKG_USING_FAL
select FAL_USING_AUTO_INIT
select FAL_PART_HAS_TABLE_CFG
select PKG_USING_LITTLEFS
default n
7,在RT-Thread Settings中开启USBD功能选项
8,保存RT-Thread Settings更新配置后,重新编译
硬件上确保USB线序正确,D+线配置成上拉或者加上拉电阻。然后重新在程序,将此USB连线插入电脑,几秒钟后显示如下
然后打开U盘,新建文件text.txt,然后操作如下
保存后关闭文件,拔出U盘
在finsh命令端口操作如下
说明新建文件已经成功保存到SPI flash内。
但如果想要将Flash的一个分区挂载到USB上虚拟成U盘,怎么办?经过摸索证明,是可以做得到的。
9, 更改配置,这里我使用了TinyUSB软件包
注意上面划红线的地方必需一致。
10,然后保存配置,然后下载,运行后如下
11,问题是,虽然能保存文件了,但是不能rt-thread和USB之间自由切换,无法在上位机PC上存文件,然后在rt-thread系统里读文件。因为我USB只有D+ 和 D- 可用,Vbus 和 FS_ID已被占用,在Tiny官网也没找到有用答案,只能修改源码来解决问题。
【1】在打开TinyUSB-v0.13.0\rt-thread\port\msc_device_port.c文件,定位到文件开头部分,加入如下定义:
static bool ejected = false;
//static rt_device_t flash_device;
rt_device_t usb_flash_dev = RT_NULL;
uint8_t flag_usb_unplugged = 0;
uint8_t flag_usb_plugged = 0;
static struct rt_device_blk_geometry blk_geom;
其中usb_flash_dev不是必需的,因为和避免spi_flash_sfud.c文件中的变量重名而改。定义了两个标志变量,然后向下到tud_msc_test_unit_ready_cb()内,if (ejected)和if (usb_flash_dev == NULL)之间加入下面代码
if (ejected)
{
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
return false;
}
if (!flag_usb_plugged)
{
flag_usb_plugged = 1;
extern void unmount_from_rtt(void);
unmount_from_rtt();
}
if (usb_flash_dev == NULL)
{
usb_flash_dev = rt_device_find(PKG_TINYUSB_DEVICE_MSC_NAME);
}
【2】定位到TinyUSB-v0.13.0\src\device\usbd.c的void tud_task (void)内,case DCD_EVENT_SUSPEND处,在if ( _usbd_dev.connected )下面如下下面代码:
case DCD_EVENT_SUSPEND:
// NOTE: When plugging/unplugging device, the D+/D- state are unstable and
// can accidentally meet the SUSPEND condition ( Bus Idle for 3ms ), which result in a series of event
// e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected
if ( _usbd_dev.connected )
{
extern uint8_t flag_usb_unplugged;
flag_usb_unplugged = 1;
TU_LOG2(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
}else
{
TU_LOG2(" Skipped\r\n");
}
break;
【3】定位到项目\applications\main.c循环体内,加入如下代码:
while (1)
{
if (flag_usb_unplugged)
{
if (usb_flash_dev != RT_NULL )//&& usb_flash_dev->open_flag != 0
{
extern void dcd_event_unpluged_self (uint8_t rhport, bool in_isr);
dcd_event_unpluged_self(0, true);
rt_device_close(usb_flash_dev);
//usb_flash_dev = RT_NULL;
extern void mount_to_rtt(void);
mount_to_rtt();
}
flag_usb_plugged = 0;
flag_usb_unplugged = 0;
}
rt_thread_mdelay(500);
// //LOG_D("Hello RT-Thread!");
}
【4】定位到TinyUSB-v0.13.0\src\device\usbd.c的dcd_event_bus_signal()和dcd_event_bus_reset()之间,加入dcd_event_unpluged_self()函数,代码如下:
void dcd_event_bus_signal (uint8_t rhport, dcd_eventid_t eid, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = eid };
dcd_event_handler(&event, in_isr);
}
void dcd_event_unpluged_self (uint8_t rhport, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_UNPLUGGED };
event.bus_reset.speed = 0;
dcd_event_handler(&event, in_isr);
}
void dcd_event_bus_reset (uint8_t rhport, tusb_speed_t speed, bool in_isr)
{
dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_BUS_RESET };
event.bus_reset.speed = speed;
dcd_event_handler(&event, in_isr);
}
【5】新建两个函数用于挂载和卸载
#if defined (RT_USB_DEVICE_MSTORAGE) || defined (PKG_TINYUSB_DEVICE_MSC)
void mount_to_rtt(void)
{
rt_device_t dev = rt_device_find(SFLASH_PARTITION_NAME);
if (dev != RT_NULL)//&& usb_device->open_flag != 0
{
//rt_device_close(dev);
//rt_kprintf("close usb device ok!\n");
if (dfs_mount(SFLASH_PARTITION_NAME, SFLASH_FILESYSTEM_PATH, SFLASH_FILESYSTEM_TYPE, 0, 0) == 0) //"/"
{
LOG_I("Mount to /sf ok!\n");
}
}
else
{
LOG_E("Mount to /sf failed!\n");
}
}
//MSH_CMD_EXPORT( mount_to_rtt, mount to rtt);
void unmount_from_rtt(void)
{
char *fullpath = NULL;
fullpath = dfs_normalize_path(NULL, SFLASH_FILESYSTEM_PATH);//"/"
rt_device_t dev = rt_device_find(SFLASH_PARTITION_NAME);
if (dev != RT_NULL && dev->open_flag != 0)
{
rt_device_close(dev);
LOG_I("Unmount from /sf ok!\n");
if (dfs_unmount(fullpath) == RT_EOK)
{
LOG_I("Usb disk unmount ok!\n");
}
else
{
LOG_E("Usb disk unmount failed!\n");
}
}
else
{
LOG_E("Unmount from /sf failed!\n");
}
}
//MSH_CMD_EXPORT(unmount_from_rtt, unmount from rtt);
//INIT_APP_EXPORT(mount_to_usb);
#endif
12,修改后重新编译下载,OK
创建一个新的 Office 插件项目:在 Visual Studio 中,选择"文件" -> “新建项目”,然后在模板中选择"Office/SharePoint",选择适当的 Office 插件项目模板,如 Word 插件、Excel 插件或 PowerPoint 插件。设计用户界面:在解决方案资源管理器中,打开你的插件项目,并在其中打开相应的 Office 文件(如 Word 文件、Excel 文件或 PowerPoint 文件)。你可以在 Office 应用中测试插件的功能,并在开发过程中进行调试。
在应用场景方面,便携式IV功率测试仪广泛应用于光伏电站的日常运维、光伏组件生产过程中的质量控制以及光伏项目的前期评估等环节。在光伏电站运维中,定期对光伏组件进行IV测试,可以及时发现性能下降或损坏的组件,为电站的运维提供有力支持。首先,从工作原理来看,光伏电站便携式IV功率测试仪通过模拟太阳光照射光伏组件,并测量组件在不同电压下的电流输出,从而绘制出IV曲线。此外,测试仪还可以计算光伏组件的功率输出、转换效率等参数,为用户提供全面的性能评估。
文章浏览阅读3.6k次。os: ubuntu 16.04postgresql: 9.6.8ip 规划192.168.56.102 node2 postgresqlhelp create indexpostgres=# \h create indexCommand: CREATE INDEXDescription: define a new indexSyntax:CREATE [ UNIQUE ..._load_categories_hash postgres
文章浏览阅读4.8k次。使用face++,先获取key和secret下方是人脸识别,还添加了画出人脸轮廓的正方形下方是人脸识别,还添加了画出人脸轮廓的正方形 import requests#网络访问控件 from json import JSONDecoder#互联网数据交换标准格式 import cv2 as cv#图像处理控件 http_url ="https://a..._face++人脸识别 html5
文章浏览阅读322次。很多时候要对秘要进行持久化加密,此时的加密采用md5。采用对称加密的时候就采用DES方法了import java.io.IOException;import java.security.MessageDigest;import java.security.SecureRandom;import javax.crypto.Cipher;import javax.crypto.SecretKey;im..._java desencrypt.encrypt(pass)
文章浏览阅读145次。题目链接:https://www.acwing.com/problem/content/description/222/给定整数N,求1<=x,y<=N且GCD(x,y)为素数的数对(x,y)有多少对。GCD(x,y)即求x,y的最大公约数。输入格式输入一个整数N输出格式输出一个整数,表示满足条件的数对数量。数据范围1≤N≤10^7输入样例:4..._线性筛预处理质数表, 并求出欧拉函数, 预处理前缀和即可 bzoj2818boj
文章浏览阅读1.6k次。测试算法的性能 很多时候我们需要对算法的性能进行测试,最简单的方式是看算法在特定的数据集上的执行时间,简单的测试算法性能的函数实现见testSort()。【思想】:用clock_t计算某排序算法所需的时间,(endTime - startTime)/ CLOCKS_PER_SEC来表示执行了多少秒。【关于宏CLOCKS_PER_SEC】:以下摘自百度百科,“CLOCKS_PE_算法性能测试
文章浏览阅读1.2k次。fromhttps://towardsdatascience.com/finding-lane-lines-simple-pipeline-for-lane-detection-d02b62e7572bIdentifying lanes of the road is very common task that human driver performs. This is important ..._lanedetectionlite
文章浏览阅读4.1k次,点赞8次,收藏23次。通过一定的方法找出与给定关键字相同的数据元素的过程叫做查找。也就是根据给定的某个值,在查找表中确定一个关键字等于给定值的记录或数据元素。_读取表元是什么意思
文章浏览阅读8.3k次,点赞4次,收藏18次。我们在非撮合回测模式下,因为无法获知交易价格当时的真实盘口价差、挂单数量,常主观设定一个滑点均值,比如针对螺纹钢等合约,设置 1 跳,针对某些交易不活跃的品种,设置 2 跳。但是这种近乎拍脑袋的方法并不精确。我们今天尝试通过简单的辅助工具,实现尽可能接近准确的 tick 级别滑点设置,代码已写好,不用编程也可获得结果。_滑点设置多少合适
文章浏览阅读551次。尚硅谷大数据技术之 Azkaban—————————————————————————————更多 Java –大数据 –前端 –python 人工智能资料下载,可百度访问:尚硅谷官网尚硅谷大数据技术之 Azkaban(作者:尚硅谷大数据研发部)版本:V3.0一 概述1.1 什么是 AzkabanAzkaban 是由 Linkedin 公司推出的一个批量工作流任务调度器,主要用于在一个工作流内以一个特定的顺序运行一组工作和流程,它的配置是通过简单的 key:value 对的方式,通过配置中_azkaban要建立job之间的依赖关系需要使用
文章浏览阅读5k次,点赞2次,收藏9次。#! python3# encoding: utf-8import osimport chardetdef strJudgeCode(str1): return chardet.detect(str1)"""def readFile(path): with open(path,'r',encoding='utf-16 le') as f: filecontent ..._utf16le转换utf8