CH37X 文件管理芯片使用及移植指南

近期实习公司想要给仪器开发一个利用USB接口向U盘写测量数据的功能(很神奇开了二十年的公司这个都没有)。于是查询了很多资料,最后选择了CH37X文件管理芯片的方案,也顺利实现了功能。

经笔者个人的实践和对网络资源、官方手册的研究参考,整理成本文。由于水平有限,没有深入了解USB传输协议,文件管理等知识,故文章经验性较强,理论深度较差。并且实践经验仍较少,只希望能够帮助其余像笔者一样,对这块芯片处于初学状态的读者顺利入门。

本文第一部分介绍了CH37X芯片的基本功能和特性;

第二部分介绍了单片机移植CH37X操作程序步骤,以及U盘的准备;

第三部分介绍了各个函数的功能和使用方法;

第四部分简略提及了电路设计;

第五部分分享了一些参考资料、视频。

建议阅读本文时,配合使用各参考文件。

1、基本概念

U盘接口功能主要通过CH37X文件管理控制芯片实现,此文实验部分均基于CH376S芯片叙述。该芯片主要用于单片机系统读写U盘或者SD卡的数据。

芯片具有以下功能:

①CH376支持USB设备方式和USB主机方式,即作为主机读取外部储存中的文件,也可以作为设备,向主机传输自身搭载储存芯片上的数据。并且支持两种状态的动态转换;

②内置了USB通讯协议的基本固件,内置了Mass-Storage海量存储设备专用通讯协议的固件。即可以直接调用提供的库函数控制CH376对U盘、SD卡进行操作,使用者只需要负责编写MCU与CH376通讯即可。

③内置了FAT16、FAT32以及FAT12文件系统的管理固件,支持常用的USB储存设备和SD卡。正常我们的PC机并不是对每个单独存储地址下的单元进行直接操作,而是基于文件系统对存储单元进行管理,总之用了文件系统,就能创建常见的带有后缀名、时间戳、大小等信息的各类文件,管理也更加方便。

④CH376支持三种通讯接口,8位并口、SPI接口和异步串口。应注意,并不是所有CH37X芯片都有全部三类接口,最高通讯速率也有所不同。

⑤CH376的应用框图如下,方便理解芯片、控制器、存储单元间关系:

⑥根据芯片不同支持不同速度的USB通讯,基本都兼容USB V2.0(就是接口颜色黑色,接口颜色蓝色的USB 3.0一般硬件上也兼容USB 2.0)。例如CH376支持1.5Mbps低速和12Mbps全速USB通讯,而CH378支持12Mbps全速和480Mbps高速通讯。

⑦根据芯片型号不同支持的容量、单次可操作的最大文件大小也不同,如CH376支持32GB的U盘和SD卡,最大1GB的单个文件,CH378支持32GB的U盘和4GB的单个文件。且连接CH376的U盘格式应为FAT32、FAT16或FAT12,市面上部分U盘使用的exFAT似乎不行,但具体地也没有进行了解、实验。U盘大小也应匹配,不能过大,否则无法操作。如实验过程中,使用exFAT格式64GB盘无法正常操作,FAT32格式4GB盘正常。

⑧官方提供的库函数可以选择是否使用特定功能,主要作用是减小程序大小。可以在头文件filesys.h内修改宏定义。注意,官方提供的文件里面很多功能默认不启用,若编程时出现某函数重定义的提示,很可能是没有启用对应的宏定义。

2、移植指南

鉴于CH37X芯片内置了USB通讯固件和文件管理系统,故移植到任何一款单片机上时都省去了向单片机移植文件管理系统和PC上位机安装驱动的步骤,只需要加入相关库函数、配置底层通讯驱动即可快速移植到任意一款单片机。若想要不使用该芯片,或想对现有FLASH、单片机SRAM做文件管理,可参考正点原子STM32指南中文件管理部分内容。

第一步,准备一块容量合适、格式正确的U盘。若没有特殊需求,一般淘宝里10块4GB的就可以,反而是好的U盘容易用不了。U盘容量必须在CH37X最大可操作容量下,且格式位FAT32、16、12中一种。不存在64GB盘,芯片支持32GB,让芯片只操作里面前32GB容量的情况。U盘格式化步骤如下图:

第二步,加入相关库函数。主要文件有ch376.c、ch376.h、ch376inc.h、filesys.c、filesys.h共五个。

ch376.c使用了杜洋工作室编写的程序,移植过程中需要修改的部分基本在此文件内。主要包含通讯接口和CH376的初始化函数、向CH376芯片读写字节、写命令、查询中断等函数。ch376.h是对应的头文件,对单片机控制CH376的中断、复位引脚进行了宏定义。

ch376inc.h是官方提供的头文件,定义了所有指令,无需修改。

filesys.h和filesys.c文件是官方提供的文件管理操作函数库,基本不需要修改。

第三步,配置通讯驱动

首先修改ch376.h中的宏定义,根据自己使用的引脚进行修改。中断引脚和复位引脚都是可选的,中断可通过SDO引脚读取,复位引脚可有可无。

然后修改ch376.c中的通讯驱动,在这里使用的硬件SPI进行通讯。需要说明的是CH37X总是在SPI时钟SCK的上升沿输入数据(芯片读数据),在下降沿输出数据(芯片写数据)。

初始化时应采用SPI模式0或模式3,即CHOL=CPHA=0/ CHOL=CPHA=1,或者说空闲电平低+第一边沿采样/空闲电平高+第二边沿采样;数据位高位在前,8位为一字节;通讯模式为全双工,片选NSS由软件控制。通讯引脚上,MOSI和SCLK复用推挽(没有复用则推挽),MISO为浮空输入,若有中断引脚,配置为上拉输入。

之后修改SPI收发函数,先贴出官方给出的通讯步骤图,官方曾多次强调,应特别注意SPI片选的时序问题,另外命令和数据之间应留有短暂延时,具体查询官方手册。一般库函数里面都已经写了延时,没有出错基本不用考虑延时的问题。

先写一个SPI发送一字节并读取一字节的函数,放在了spi.c中。 

若还是使用SPI方式,可以直接把上面的函数和SPI初始化放进ch376.c。若换用其他通讯方式或软件SPI,需要对ch376.c里的函数进行修改,保证能调用到正确的收发函数。之后修改查询中断的函数,保证接收到中断、引脚被芯片拉低时,返回真值即可。

最后在spi.h宏定义各个通讯引脚

至此,移植工作基本完成,单片机其余配置请自行完成。

3、程序示例

此部分对各函数功能进行介绍,由于笔者仍为初学者,并未对功能进行深入研究,必定存在错误和疏漏。后续读者可根据实验结果,对本文件进行修改。

①写命令格式

CH37X手册中指出,在芯片上电后,总是将收到的第一个字节认作命令,随后的认作数据。在手册中“命令”这一章节给出了详细的命令格式、代码、所需要的输入输出和芯片应返回的数据。其中操作状态代指51H操作成功和5FH操作失败。

②初始化

初始化376芯片调用mInitCH376Host函数,此处配置芯片作为主机。

开头的延时用于保证芯片上电后有足够的时间进行准备,官方推荐为200ms。随后在CH376_PORT_INT里初始化了中断输入引脚,若采用SDO输出中断状态的方式则不需要。

之后测试单片机与CH376通讯接口有效性,先发送测试命令CMD11_CHECK_EXIST,再发送一个字节的数据,芯片将把数据逐位取反后发送回来。如此处发送0X51,若芯片返回0XAE则说明通讯正常。

后设置工作模式,此处为06H,已启用的主机方式并自动产生SOF包。此处芯片返回操作状态,若为51H标识操作成功,5FH表示操作失败,但实际可能返回其他值。在实验的过程中,出现过返回60H,但后续程序依然正常运行的情况。总之,能用就行。

③测试磁盘是否插入、是否就绪

首先在循环中利用CH376DiskConnect函数循环查询U盘是否接入并等待。若U盘插入,芯片将产生中断并可查询到中断状态为14H或USB_INT_SUCCESS,若已断开为82H或 ERR_DISK_DISCON。应注意返回状态不仅只有以上两种,在连接出错状态下有可能返回其他值,若单片机具有在线仿真功能,可以打断点查看返回值,并按下表分析错误类型。如笔者使用64GB的exFAT格式U盘时,返回20H。

之后在for循环里利用CH376DiskMount函数测试磁盘是否就绪,同样通过中断状态返回磁盘状态。除了基本的准备就绪和断开状态外,有些U盘可能需要多次才能够准备好,根据官方和杜洋工作室给出的代码,只需要建立连接MOUNTED并尝试250ms以上即可视作准备就绪。函数中判断返回值是否大等于DEF_DISK_MOUNTED,因为从磁盘连接到准备就绪间有多个中间状态,磁盘状态为二者间任意状态即可。

④新建文件并写入内容

进行U盘读写操作时,CH376支持扇区模式和字节模式两种(对SD卡只支持字节模式)。区别在于扇区模式以扇区为单位进行读写,读写速度块但需要额外的文件数据缓冲区,适用于RAM多、数据量大且频繁读写的单片机系统;字节模式以字节为单位进行读写,读写速度慢但无需额外文件数据缓冲区,适用于数据量小、不经常读写的单片机系统。

应注意,若使用字节模式进行高频率读写,应考虑U盘闪存擦写次数的问题,若数秒间隔便读写一次,对于擦写寿命10万次的闪存,可能只有一周寿命。故建议对零碎数据进行整理,单次写入字节数尽量是扇区大小512字节的倍数,以延长闪存使用寿命。

和电脑上的操作类似,单片机对U盘操作文件流程同样为 打开目录-打开文件-读写内容-关闭文件并更新状态。

首先用CH376FileCreatePath函数创建名为456.TXT的文件,函数参数为PathName,即文件路径。全名不可超过11字符且应全部为大写,其中文件名不超过8个字符,后缀名不超过3个字符,后缀名和文件名之间用点隔开 (CH37X部分支持长文件名模式,请自行查阅手册) 。若路径很长,应使用多次打开的方式。函数的作用为,新建多级目录下的文件,若文件已经存在,则先删除再新建。若不希望文件被删除,可以加入下面的一段代码:

先用CH376FileOpenPath函数尝试打开目标文件,若成功打开,则直接写入数据,若未成功,则先创建文件。

随后用sprintf函数拼接字符串,该函数可以在字符串中加入变量值,使用方法请自行查阅。应注意sprintf函数有长度限制,拼接过长的字符串可能出现错误,应采用分段拼接。

使用CH376ByteWrite函数写入数据,函数参数依次为写入数据、写入长度、无效参数(在CH376ByteRead中用于返回实际读取字节数,此处未使用)。此后若再调用写数据函数,将从上一次写入结束的位置继续。写入一行数据后,若需要换行,应写入换行符0X0D,0X0A,注意是直接写入两个字节数据,不是写入0X0D,0X0A的字符串。

⑤读取文件内容

实现较为简单,主要步骤为:打开文件-调用CH376ByteWrite-关闭文件

先看CH376ByteWrite函数的定义

入口参数有三个,外部缓冲buf、请求读出字节数ReqCount、实际读出字节数Realcount,函数执行完毕后将把实际读出字节数放入Realcount中。

用在线仿真观察buf1内容,可以看到和文件内容匹配

 

手册中还提到了该函数的另一个用法,由于Realcount表示实际读出字节数,或者说读出有效字节数,那么在连续读出数据时若Realcount<Reqcount,说明文件内容已经全部读取完毕。

注意,文件存放时的基本单位是簇,文件大小总是簇的倍数(文件中有效数据不是),簇则是扇区的倍数,扇区通常为512字节。当不断写入数据,使文件大小大于当前组成文件的所有簇空间之和,就会链接新的簇给文件扩充大小。此时,必须对文件长度进行更新,否则存放在新的簇中的数据会被认定为无效。

⑥写入指针偏移

官方提供了CH376ByteLocate这一函数用于修改写入指针位置,函数入口参数为偏移量值。由于笔者对txt文件的组织方式并不熟悉,仅从实验效果上对函数功能进行分析。

Ⅰ连续写入两行数据,效果如下:

可以看到有两行相同数据,在中间穿插了一个换行符

Ⅱ写入第一行数据,写入换行符,加入10字节指针偏移,写入第二行,效果如下:

Ⅲ写入第一行数据,写入换行符,加入10字节指针偏移,再次加入10字节指针偏移(调用两次函数实现),写入第二行,效果为:

 

Ⅳ写入第一行数据,写入换行符,加入50字节指针偏移,写入第二行,效果如下:

 

由以上三组测试结果可以得出结论,指针偏移函数是基于文件开始指针进行偏移的,每次都为开始指针+偏移值,多次调用时,偏移值不会累加,也不会随着写入改变;由于txt文件格式的原因,无效数据不予以显示,即使利用函数加入大量偏移,让两次写入位置在存储地址上相差很多,在显示上也是紧紧相邻的效果。

利用该函数更可以实现在现有文件中追加数据的效果,根据芯片评估板参考手册CH376EVT,具体步骤应为:

打开文件-调用CH376ByteLocate函数,参数定为0XFFFFFFFF,将指针移动到文件末尾-写入文件-关闭文件并更新文件长度

具体实现代码如下:

效果如下,可见在最后写入了last line,若想单空一行,就先写0X0D,0X0A进去。

 

同样地,如果在使用中,先进行读文件,则文件指针移动到了最后读取数据的位置,如果想要换为写操作,在文件开始处写入数据,那么步骤应为:

打开文件-读数据-调用CH376ByteLocate,参数为0-写数据-关闭文件并更新长度

⑦更新文件日期

 

在使用前,应先去filesys.c文件中宏定义EN_DIR_CREATE为1,用于启用新建多级子目录的子程序,否则相关函数受条件编译语句影响,是不会编译的。

相关函数CreateFileAndTime定义在ch376.c文件中,官方注释如下:

可见,函数可以操作的文件和文件夹两种目标,修改器创建时间/日期和修改时间/日期。时间/日期数据都有特定的格式要求,可以调用官方的函数直接生成。

注意,创建文件时入口参数cate应为1,文件夹时为0,若不想新创建文件/文件夹,直接修改文件时间信息,可参考该函数写法,打开文件-读取文件信息-修改信息并保存。大体上是将这部分修改为打开文件/文件夹。

最终效果如下图,如预期修改了文件夹日期

另外,修改文件长度、属性等信息的操作也与本函数类似,可以仿照此函数编程。但是这两个信息大多会自动更新,单写函数的意义不大。

⑧定期采集数据

该部分笔者未进行实验(懒),仅为手册的复述,简单做了个流程图,方便理解。

 

4、电路组成

由于笔者不太会画图(学两回都忘了加上懒),电路图可参照CH376评估板原理图设计。这里贴出一小部分,稍微讲一下接线

 

为了方便,这里按笔者使用的SPI方式叙述:

3脚WR#、4脚RD#在SPI方式下需要接地,1脚INT#可用可不用(22脚SDO也可输出中断状态);

21脚SDI接单片机的MOSI(此处单片机为Master,芯片为Slave),22脚SDO接单片机的MISO,18脚SCS可以接SPI_NSS,但在软件片选下其实可以任意,17脚BZ输出忙碌状态,但程序一般用延时代替查询,无需使用;

芯片供电可直接连3.3V,若为5V需要加0.1uF退耦电容;

10脚UD+和11脚UD-连到USB接口,注意,USB必须为+5V供电;

其余引脚不予赘述。

5、相关资料

①官方参考手册

包括CH372、CH376、CH376EVT、CH378 (CH376评估板)共四个文件。建议配合使用前三个文件,CH376和CH378作为基础款的升级版,参考手册内并没有对所有功能进行描述,如CH376的USB部分因为功能相同,被放在了CH372手册中介绍。而CH376EVT作为评估板的参考手册,详细介绍了库函数的使用方法并对例程进行了讲解,此外给出了各功能的实现步骤,是最重要的参考文件。

电路搭建、软件设计、错误排查等细节都在手册中有所描述,但毕竟不可能面面俱到,这里贴出几个网站,以供参考解惑。

CH37X 常见问题梳理与解决流程 - 沁恒微电子社区

单片机通过CH375读写U盘文件的问题解答 - 沁恒微电子社区

沁恒USB蓝牙MCU-WCH沁恒官方技术支持论坛

前两个网站为芯片生产商南京沁恒电子官网社区的答疑帖,囊括了大多数错误情况的解决方法,最后一个是沁恒电子在21ic网站上的技术支持论坛。

另外找不到的错误还可以发送邮件给南京沁恒的技术支持邮箱,大概两天内会回复,但若想要详尽地回答,帮忙代码纠错,可能还得打技术销售电话。

技术支持邮箱 tech@wch.cn

最后附上官方例程、杜洋工作室编写例程和官方提供的stm32f103例程。

官方例程放在EXAM文件夹中的是分模块的函数,主题函数在外层文件夹中,分不同通讯方式给出了库文件,可组合使用,适用于51芯片。

杜洋工作室例程存在一定错误,中断读取引脚初始化错用PA15引脚,库函数的查询中断函数没有配合芯片按照下拉表示出现中断。但可以在b站搜索杜洋工作室出品的视频,配合使用,方便入门。

最后官方提供的stm32f103是笔者在调试时发邮件到技术支持要来的,同芯片使用者可直接编译使用。 

版权声明:本文为CSDN博主「TPenny68」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/TPenny68/article/details/121921636

生成海报
点赞 0

TPenny68

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

RT-Thread Studio移植LAN8720A驱动

RTT网络协议栈驱动移植(霸天虎) 1、新建工程 ​ 工程路径不含中文路径名,工程名用纯英文不含任何符号。 2、用CubeMx配置板子外设 2.1、配置时钟 ​ 按照自己板子配置相应时钟。

【STM32Cube笔记】12-配置外部中断

【STM32Cube笔记】系列文章目录 1-基于STM32的VSCode入门级教程前言 2-STM32Cube安装教程 3-STM32CubeIDE汉化 4-STM32Cube配置时钟设置 5-跑马灯引脚配置 6-Cortex-M7内核基本配

stm32cubemx+HAL+串口接收中断

stm32cubemxHAL串口接收中断 在cubemx配置完串口和global interrupt后需要在keil中添加如下代码。 第一步:在main函数中添加接收中断标志位开启函数 HAL_UART_Receive_IT