找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 11104|回复: 12
收起左侧

CH375_CH376 U盘读写模块在51单片机上的应用(原理图+测试程序+资料)

  [复制链接]
ID:296720 发表于 2018-3-27 15:43 | 显示全部楼层 |阅读模式
本程序硬件十分简单,只需要一个CH375  U盘读写模块就行。

但是这个程序的代码因为涉及到操作USB设备文件系统,因此代码量相当大。希望能够对一些想钻研的同学们一个参考。
同时手动艾特一下管理员 大神,希望能给代码进行一个简单剖析。
大家有啥想法也可以在这个帖子下面交流一下~~

谢谢大家!

0.png

电路原理图如下:
0.png

ch375资料手册:
0.png


本程序的接线图:
0.jpg

单片机源程序如下(host.c文件):
  1. /*
  2. ****************************************
  3. **  Copyright  (C)  W.ch  1999-2004   **
  4. ****************************************
  5. **  USB Host File Interface for CH375 **
  6. **  TC2.0@PC, KC7.0@MCS51             **
  7. ****************************************
  8. */
  9. /* CH375 主机文件系统接口 */
  10. /* 支持: FAT12/FAT16/FAT32 */

  11. /* MCS-51单片机C语言的U盘文件读写示例程序, 适用于89C52或者更大程序空间的单片机,也适用于ATMEL/PHILIPS/SST等具有1KB内部RAM的单片机 */
  12. /* 该程序将U盘中的/C51/CH375HFT.C文件中的前200个字符显示出来,
  13.    如果找不到原文件CH375HFT.C, 那么该程序将显示C51子目录下所有以CH375开头的文件名,
  14.    如果找不到C51子目录, 那么该程序将显示根目录下的所有文件名,
  15.    最后将程序ROM中的一个字符串写入写入新建的文件"NEWFILE.TXT"中
  16. */
  17. /* CH375的INT#引脚采用查询方式处理, 数据复制方式为"单DPTR复制",兼容性最好但是速度最慢,
  18.    本例只使用512字节的外部RAM, 同时作为磁盘数据缓冲区和文件数据缓冲区, 演示没有外部RAM但是单片机的内置RAM大于768字节的应用 */

  19. /* 本例以字节为单位读写U盘文件,读写速度较扇区模式慢,但是由于字节模式读写文件不需要文件数据缓冲区FILE_DATA_BUF,
  20.    所以总共只需要600字节的RAM,适用于单片机硬件资源有限、数据量小并且读写速度要求不高的系统 */

  21. /*#define         NO_DEFAULT_CH375_INT                1*/        /* 在应用程序中定义NO_DEFAULT_CH375_INT可以禁止默认的中断处理程序,然后用自行编写的程序代替它 */
  22. /*#define                CH375HF_NO_CODE                1*/
  23. #include "CH375.H"

  24. #ifdef        NO_DEFAULT_CH375_INT                        /* 自行编写中断处理程序,加上了超时处理,并且在等待中断的过程中可以做其它事 */
  25. void xQueryInterrupt( void )                        /* 查询CH375中断并更新中断状态,该程序基本框架可以参考CH375HF?.H文件 */
  26. {
  27.         UINT16        i;
  28.         for ( i = 65535; i != 0; i -- ) {  /* 正常情况下该过程为几毫秒到几十毫秒,偶尔也会达到几百毫秒 */
  29.                 if ( CH375_INT_WIRE == 0 ) break;  /* 如果CH375的中断引脚输出低电平则说明CH375操作完成 */
  30. /*                if ( ( CH375_CMD_PORT & 0x80 ) == 0 ) break;  对于CH375B芯片,也查询CH375B的命令端口的位7为0说明中断引脚输出低电平 */
  31.         }
  32.         if ( i == 0 )
  33.         CH375_CMD_PORT = CMD_GET_STATUS;  /* 获取当前中断状态 */
  34.         mDelay2uS( );  /* 操作无意义,用于至少延时2uS,可以用多个NOP空操作指令实现 */
  35.         CH375IntStatus = CH375_DAT_PORT;  /* 获取中断状态 */
  36.         if ( CH375IntStatus == USB_INT_DISCONNECT ) CH375DiskStatus = DISK_DISCONNECT;  /* 检测到USB设备断开事件 */
  37.         else if ( CH375IntStatus == USB_INT_CONNECT ) CH375DiskStatus = DISK_CONNECT;  /* 检测到USB设备连接事件 */
  38. }
  39. #endif

  40. /* 以毫秒为单位延时,不精确,适用于24MHz时钟 */
  41. void        mDelaymS( UINT8 delay )
  42. {
  43.         unsigned char        i, j, c;
  44.         for ( i = delay; i != 0; i -- ) {
  45.                 for ( j = 200; j != 0; j -- ) c += 3;  /* 在24MHz时钟下延时500uS */
  46.                 for ( j = 200; j != 0; j -- ) c += 3;  /* 在24MHz时钟下延时500uS */
  47.         }
  48. }

  49. /* 将程序空间的字符串复制到内部RAM中,返回字符串长度 */
  50. UINT8        mCopyCodeStringToIRAM( UINT8 idata *iDestination, UINT8 code *iSource )
  51. {
  52.         UINT8        i = 0;
  53.         while ( *iDestination = *iSource ) {
  54.                 iDestination ++;
  55.                 iSource ++;
  56.                 i ++;
  57.         }
  58.         return( i );
  59. }

  60. /* 检查操作状态,如果错误则显示错误代码并停机 */
  61. void        mStopIfError( UINT8 iError )
  62. {
  63.         if ( iError == ERR_SUCCESS ) return;  /* 操作成功 */
  64.         printf( "Error: %02X\n", (UINT16)iError );  /* 显示错误 */
  65.         while ( 1 ) {
  66.                 LED_UDISK_IN( );  /* LED闪烁 */
  67.                 mDelaymS( 100 );
  68.                 LED_UDISK_OUT( );
  69.                 mDelaymS( 100 );
  70.         }
  71. }

  72. void host( ) {
  73.         UINT8        i, c, TotalCount;
  74.         UINT8        code *pCodeStr;
  75.         UINT16        EnumCount;

  76. #if DISK_BASE_BUF_LEN == 0
  77.         pDISK_BASE_BUF = &my_buffer[0];  /* 不在.H文件中定义CH375的专用缓冲区,而是用缓冲区指针指向其它应用程序的缓冲区便于合用以节约RAM */
  78. #endif

  79.         i = CH375LibInit( );  /* 初始化CH375程序库和CH375芯片,操作成功返回0 */
  80.         mStopIfError( i );
  81.         while ( 1 ) {
  82.                 printf( "Insert USB disk\n" );
  83.                 while ( CH375DiskStatus < DISK_CONNECT ) {  /* 等待U盘插入 */
  84.                
  85. /*                        if ( CH375_INT_WIRE == 0 ) xQueryInterrupt( );*/  /* 如果CH375中断,那么查询CH375中断并更新中断状态,可以改成中断方式 */
  86.                         mDelaymS( 100 );  /* 没必要频繁查询 */
  87.                         if ( CH375DiskConnect( ) == ERR_SUCCESS ) break;        /* 查询方式: 检查磁盘是否连接,返回成功说明连接 */
  88.                 }
  89.                 LED_UDISK_IN( );  /* LED亮 */
  90.                 mDelaymS( 250 );  /* 延时,可选操作,有的USB存储器需要几十毫秒的延时 */

  91. /* 检查U盘是否准备好,但是某些U盘必须要执行这一步才能工作 */
  92.                 for ( i = 0; i < 5; i ++ ) {  /* 有的U盘总是返回未准备好,不过可以被忽略 */
  93.                         mDelaymS( 100 );
  94.                         printf( "Ready ?\n" );
  95. //                        if ( CH375DiskReady( ) == ERR_SUCCESS ) break;  /* 查询磁盘是否准备好,不支持CH375S,节约代码空间 */
  96.                         if ( CH375sDiskReady( ) == ERR_SUCCESS ) break;  /* 查询磁盘是否准备好,支持CH375S和CH375A,但占用更多的代码空间 */
  97.                 }

  98. #if DISK_BASE_BUF_LEN
  99.                 if ( DISK_BASE_BUF_LEN < CH375vSectorSize ) {  /* 检查磁盘数据缓冲区是否足够大,CH375vSectorSize是U盘的实际扇区大小 */
  100.                         printf( "Too large sector size\n" );
  101.                         while ( CH375DiskConnect( ) == ERR_SUCCESS ) mDelaymS( 100 );
  102.                         continue;
  103.                 }
  104. #endif
  105. /* 读取原文件 */
  106.                 printf( "Open\n" );
  107.                 mCopyCodeStringToIRAM( mCmdParam.Open.mPathName, "/LCSOFT.TXT" );  /* 文件名,该文件在C51子目录下 */
  108.                 i = CH375FileOpen( );  /* 打开文件 */
  109.                 if ( i == ERR_MISS_DIR || i == ERR_MISS_FILE ) {  /* 没有找到C51子目录,没有找到CH375HFT.C文件 */
  110. /* 列出文件 */
  111.                         if ( i == ERR_MISS_DIR ) pCodeStr = "/*";  /* C51子目录不存在则列出根目录下的所有文件 */
  112.                         else pCodeStr = "/LC*";  /* CH375HFT.C文件不存在则列出\C51子目录下的以CH375开头的文件 */
  113.                         printf( "List file %s\n", pCodeStr );
  114.                         for ( EnumCount = 0; EnumCount < 10000; EnumCount ++ ) {  /* 最多搜索前10000个文件,实际上没有限制 */
  115.                                 i = mCopyCodeStringToIRAM( mCmdParam.Open.mPathName, pCodeStr );  /* 搜索文件名,*为通配符,适用于所有文件或者子目录 */
  116.                                 mCmdParam.Open.mPathName[ i ] = 0xFF;  /* 根据字符串长度将结束符替换为搜索的序号,从0到254,如果是0xFF即255则说明搜索序号在CH375vFileSize变量中 */
  117.                                 CH375vFileSize = EnumCount;  /* 指定搜索/枚举的序号 */
  118.                                 i = CH375FileOpen( );  /* 打开文件,如果文件名中含有通配符*,则为搜索文件而不打开 */
  119. /* CH375FileEnum 与 CH375FileOpen 的唯一区别是当后者返回ERR_FOUND_NAME时那么对应于前者返回ERR_SUCCESS */
  120.                                 if ( i == ERR_MISS_FILE ) break;  /* 再也搜索不到匹配的文件,已经没有匹配的文件名 */
  121.                                 if ( i == ERR_FOUND_NAME ) {  /* 搜索到与通配符相匹配的文件名,文件名及其完整路径在命令缓冲区中 */
  122.                                         printf( "  match file %04d#: %s\n", (unsigned int)EnumCount, mCmdParam.Open.mPathName );  /* 显示序号和搜索到的匹配文件名或者子目录名 */
  123.                                         continue;  /* 继续搜索下一个匹配的文件名,下次搜索时序号会加1 */
  124.                                 }
  125.                                 else {  /* 出错 */
  126.                                         mStopIfError( i );
  127.                                         break;
  128.                                 }
  129.                         }
  130.                 }
  131.                 else {  /* 找到文件或者出错 */
  132.                         mStopIfError( i );
  133.                         TotalCount = CH375vFileSize;  /* 准备读取总长度 */
  134.                         printf( "从文件中读出的前%d个字符是:\n",(UINT16)TotalCount );
  135.                         while ( TotalCount ) {  /* 如果文件比较大,一次读不完,可以再调用CH375ByteRead继续读取,文件指针自动向后移动 */
  136.                                 if ( TotalCount > MAX_BYTE_IO ) c = MAX_BYTE_IO;  /* 剩余数据较多,限制单次读写的长度不能超过 sizeof( mCmdParam.ByteRead.mByteBuffer ) */
  137.                                 else c = TotalCount;  /* 最后剩余的字节数 */
  138.                                 mCmdParam.ByteRead.mByteCount = c;  /* 请求读出几十字节数据 */
  139.                                 i = CH375ByteRead( );  /* 以字节为单位读取数据块,单次读写的长度不能超过MAX_BYTE_IO,第二次调用时接着刚才的向后读 */
  140.                                 mStopIfError( i );
  141.                                 TotalCount -= mCmdParam.ByteRead.mByteCount;  /* 计数,减去当前实际已经读出的字符数 */
  142.                                 for ( i=0; i!=mCmdParam.ByteRead.mByteCount; i++ ) printf( "%C", mCmdParam.ByteRead.mByteBuffer[i] );  /* 显示读出的字符 */
  143.                                 if ( mCmdParam.ByteRead.mByteCount < c ) {  /* 实际读出的字符数少于要求读出的字符数,说明已经到文件的结尾 */
  144.                                         printf( "\n" );
  145.                                         printf( "文件已经结束\n" );
  146.                                         break;
  147.                                 }
  148.                         }
  149. /*            如果希望从指定位置开始读写,可以移动文件指针
  150.                 mCmdParam.ByteLocate.mByteOffset = 608;  跳过文件的前608个字节开始读写
  151.                 CH375ByteLocate( );
  152.                 mCmdParam.ByteRead.mByteCount = 5;  读取5个字节
  153.                 CH375ByteRead( );   直接读取文件的第608个字节到612个字节数据,前608个字节被跳过

  154.             如果希望将新数据添加到原文件的尾部,可以移动文件指针
  155.                 CH375FileOpen( );
  156.                 mCmdParam.ByteLocate.mByteOffset = 0xffffffff;  移到文件的尾部
  157.                 CH375ByteLocate( );
  158.                 mCmdParam.ByteWrite.mByteCount = 13;  写入13个字节的数据
  159.                 CH375ByteWrite( );   在原文件的后面添加数据,新加的13个字节接着原文件的尾部放置
  160.                 mCmdParam.ByteWrite.mByteCount = 2;  写入2个字节的数据
  161.                 CH375ByteWrite( );   继续在原文件的后面添加数据
  162.                 mCmdParam.ByteWrite.mByteCount = 0;  写入0个字节的数据,实际上该操作用于通知程序库更新文件长度
  163.                 CH375ByteWrite( );   写入0字节的数据,用于自动更新文件的长度,所以文件长度增加15,如果不这样做,那么执行CH375FileClose时也会自动更新文件长度
  164. */
  165.                         printf( "Close\n" );
  166.                         i = CH375FileClose( );  /* 关闭文件 */
  167.                         mStopIfError( i );
  168.                 }

  169. #ifdef EN_DISK_WRITE  /* 子程序库支持写操作 */
  170. /* 产生新文件(覆盖原文件数据),关于向原有文件中添加数据的例子请参考EXAM7和EXAM8 */
  171.                 LED_WR_NOW( );  /* 写操作 */
  172.                 printf( "Create\n" );
  173.                 mCopyCodeStringToIRAM( mCmdParam.Create.mPathName, "/LCSOFT.TXT" );  /* 新文件名,在根目录下,中文文件名 */
  174.                 i = CH375FileCreate( );  /* 新建文件并打开,如果文件已经存在则先删除后再新建 */
  175.                 mStopIfError( i );
  176.                 printf( "Write\n" );
  177.                 pCodeStr = "\xd\xa$LC工作室$\xd\xa^O^欢迎你^O^\xd\xa";
  178.                 while( 1 ) {  /* 分多次写入文件数据 */
  179.                         for ( i=0; i<MAX_BYTE_IO; i++ ) {
  180.                                 c = *pCodeStr;
  181.                                 mCmdParam.ByteWrite.mByteBuffer[i] = c;
  182.                                 if ( c == 0 ) break;  /* 源字符串结束 */
  183.                                 pCodeStr++;
  184.                         }
  185.                         if ( i == 0 ) break;  /* 源字符串结束,完成写文件 */
  186.                         mCmdParam.ByteWrite.mByteCount = i;  /* 写入数据的字符数,单次读写的长度不能超过MAX_BYTE_IO,第二次调用时接着刚才的向后写 */
  187.                         i = CH375ByteWrite( );  /* 向文件写入数据 */
  188.                         mStopIfError( i );
  189.                 }
  190.                 printf( "Close\n" );
  191.                 mCmdParam.Close.mUpdateLen = 1;  /* 自动计算文件长度,以字节为单位写文件,建议让程序库关闭文件以便自动更新文件长度 */
  192.                 i = CH375FileClose( );
  193.                 mStopIfError( i );
  194.                 LED_NOT_WR( );
  195. #endif

  196.                 printf( "Take out USB disk\n" );
  197. //                while ( CH375DiskStatus != DISK_DISCONNECT ) xQueryInterrupt( );  /* 查询CH375中断并更新中断状态,等待U盘拔出 */
  198.                 while ( CH375DiskStatus >= DISK_CONNECT ) {  /* 查询CH375中断并更新中断状态,等待U盘拔出 */
  199.                         if ( CH375DiskConnect( ) != ERR_SUCCESS ) break;
  200.                         mDelaymS( 100 );
  201.                 }
  202.                 LED_UDISK_OUT( );  /* LED灭 */
  203.                 mDelaymS( 100 );
  204.         }
  205. }
复制代码
  1. /* 2005.01.01
  2. ****************************************
  3. **  Copyright  (C)  W.ch  1999-2005   **
  4. ****************************************
  5. **  KC7.0@MCS51                       **
  6. ****************************************
  7. */
  8. /* CH375评估板演示程序: 演示USB-HOST主机接口和USB-DEVICE设备接口的应用 */
  9. /* MCS-51单片机C语言的示例程序, 适用于89C52或者更大程序空间的单片机,也适用于ATMEL/PHILIPS/SST等具有1KB内部RAM的单片机 */

  10. /* 关于本程序中的CH375主机接口: HOST.C
  11.        可以连接U盘, 支持U盘文件系统FAT12/FAT16/FAT32, 容量不限,
  12.        插入U盘后, 该程序将U盘中的/C51/CH375HFT.C文件中的前600个字符显示出来,
  13.        如果找不到原文件CH375HFT.C, 那么该程序将显示C51子目录下所有以CH375开头的文件名,
  14.        如果找不到C51子目录, 那么该程序将显示根目录下的所有文件名,
  15.        最后将程序ROM中的一个字符串写入写入新建的文件"NEWFILE.TXT"中,
  16.        CH375的INT#引脚采用查询方式处理, 数据复制方式为"单DPTR复制", 兼容性最好但是速度最慢,
  17.        以字节为单位读写U盘文件, 读写速度较扇区模式慢, 不需要文件数据缓冲区FILE_DATA_BUF,
  18.        总共只需要600字节的RAM, 适用于单片机硬件资源有限、数据量小并且读写速度要求不高的系统,
  19.        计算机端可以通过串口监控/调试工具软件以9600bps查看演示情况, 也可以使用CH341的串口工具或者CH375模块的演示工具 */
  20. /* 关于本程序中的CH375设备接口: DEVICE.C
  21.        采用请求+应答模式通讯结构, 强调可靠性和交互性, 不追求传输速度,
  22.        计算机端可以通过CH372/CH375的调试工具中的MCS51监控工具程序CH37XDBG.EXE实现对MCS51单片机的"完全"控制,
  23.        可以读写MCS51单片机的任意外部RAM、内部RAM以及绝大多数SFR, 当然也能够进行数据通讯 */
  24. /* 关于主从切换:
  25.        本程序默认工作于USB-HOST主机方式, 当有USB设备连接时自动处理, 需要作为USB设备与计算机通讯时, 可以按评估板上的按钮由主程序进行切换 */


  26. /* C51   CH375.C */
  27. /* C51   HOST.C */
  28. /* C51   DEVICE.C */
  29. /* LX51  CH375.OBJ, HOST.OBJ, DEVICE.OBJ, CH375HF6.LIB */
  30. /* OHX51 CH375 */


  31. #define                CH375HF_NO_CODE                1
  32. #include "CH375.H"

  33. /* 为printf和getkey输入输出初始化串口 */
  34. void        mInitSTDIO( )
  35. {
  36.         SCON = 0x50;
  37.         //PCON = 0x80;
  38.         TMOD = 0x21;
  39.         TH1 = 0xfd;  /* 11.0592MHz晶振, 9600bps */
  40.         TR1 = 1;
  41.         TI = 1;
  42. }



  43. main( ) {
  44. //        LED_OUT_INIT( );
  45. //        LED_OUT_ACT( );  /* 开机后LED闪烁一下以示工作 */
  46. //        mDelaymS( 100 );  /* 延时100毫秒 */
  47. //        LED_OUT_INACT( );
  48.         mDelaymS( 100 );

  49.         mInitSTDIO( );  /* 为了让计算机通过串口监控演示过程 */
  50.         printf( "Start CH375 demo ...\n" );

  51.         EA = 1;
  52.         LED_OUT_ACT( );  /* LED亮以示工作 */
  53.         while ( 1 ) {  /* 用户按键导致USB主从模式来回切换 */
  54.                 LED_HOST( );
  55.                 printf( "Set USB host mode\n" );
  56.                 host( );
  57.         //        LED_DEVICE( );
  58.         //        printf( "Set USB device mode\n" );
  59.         //        device( );
  60.         }
  61. }
复制代码

0.jpg

所有资料51hei提供下载:
CH375_CH376_U盘模块.zip (455.72 KB, 下载次数: 231)

评分

参与人数 2黑币 +60 收起 理由
多德 + 10 共享资料的黑币奖励!
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:85598 发表于 2018-8-3 00:54 | 显示全部楼层
做个记号慢慢学
回复

使用道具 举报

ID:413669 发表于 2018-10-22 19:57 | 显示全部楼层
你好,我想问一下这个CH375一定要先初始化吗?还有这个.lib子程序库怎么放到代码里面去呢
回复

使用道具 举报

ID:413669 发表于 2018-10-22 19:58 | 显示全部楼层
你好,咨询您一个问题,这个CH375芯片一定要先初始化吗
回复

使用道具 举报

ID:100672 发表于 2019-2-28 10:14 | 显示全部楼层
确定能用?
回复

使用道具 举报

ID:407977 发表于 2019-5-26 14:58 | 显示全部楼层
正在借鉴,暂不评价。
回复

使用道具 举报

ID:448994 发表于 2019-7-22 13:39 | 显示全部楼层
zhp19930812 发表于 2018-10-22 19:58
你好,咨询您一个问题,这个CH375芯片一定要先初始化吗

需要初始化,初始化的过程可以参考如下:
回复

使用道具 举报

ID:448994 发表于 2019-7-22 13:40 | 显示全部楼层
zhp19930812 发表于 2018-10-22 19:58
你好,咨询您一个问题,这个CH375芯片一定要先初始化吗

初始化,进行任何一项文件操作之前的必要步骤
① 调用 mInitCH376Host 初始化,进入 USB-HOST 工作方式或者 SD 卡主机工作方式(模式 3)
② 等待 U 盘或者 SD 卡连接,U 盘可以由 CH376 自动检测并产生中断通知,或者由单片机调用子程序
CH376DiskConnect 定期查询,SD 卡必须由单片机自行检测
③ 调用 CH376DiskMount,初始化 U 盘或者 SD 卡,并测试磁盘是否就绪,失败后可以重试最多 5 次
④ 上述步骤只需执行一次,除非 U 盘或者 SD 卡断开后重新连接,那么必须回到步骤②
回复

使用道具 举报

ID:607972 发表于 2019-9-6 10:40 | 显示全部楼层
学习学习
回复

使用道具 举报

ID:363363 发表于 2020-1-12 12:58 | 显示全部楼层
感谢分享!!!!!!!
回复

使用道具 举报

ID:169559 发表于 2020-2-18 12:51 | 显示全部楼层
ch375/376可以作为USB_CDC主机使用吗?
回复

使用道具 举报

ID:960623 发表于 2021-8-15 21:28 | 显示全部楼层
CH376和CH375一样吗
回复

使用道具 举报

ID:994109 发表于 2021-12-18 23:12 | 显示全部楼层
很不错,标记下后面应该会用到。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表