[求助]CH375B读取U盘时遇到USB_INT_BUF_OVER错误

软件流程:

初始化CH375B,模式6,

当发生状态为USB_INT_CONNECT的中断时,发送CMD_DISK_INIT指令初始化U盘。

接着中断USB_INT_SUCCESS时,发送CMD_DISK_READY指令。

接着中断USB_INT_SUCCESS时,认为U盘插入,开始读取U盘扇区。

读取流程: -> CMD_DISK_READ (以及地址) <- USB_INT_DISK_READ -> CMD_RD_USB_DATA (并取数据) -> CMD_DISK_RD_GO <- USB_INT_DISK_READ ...重复8次,再次GO后收到: <- USB_INT_SUCCESS (读取一个扇区完成) 读取完一个扇区后,重复以上流程继续读取。

当连续读取10多个扇区时(数量不定), 发送完CMD_DISK_READ命令和地址后,会收到错误:USB_INT_BUF_OVER。

请问这种问题该从何处着手寻找原因?

同常即使是失败,中断代码也不会是USB_INT_BUF_OVER,读取的流程基本是正确的,不知道实际程序是不是这么处理的


建议多测试几个U盘,看看是不是都存在这种现象.还有读多少扇区会出现这个错误,是不是有规律. 另外建议初始化U盘的时候,一定要加上获取磁盘容量,和设置块大小. 参考CH375EVT.ZIP,MISCELL文件夹.


搞定了,原来是个低级错误。

最开始调试的时候,为了检测中断,设置的是上升、下降沿都触发中断,然后就忘记了改回来。

今天打算重写所有程序,改成查询方式,在取消中断的时候才发现那里的代码异常。。。

现在连续读取2000个扇区都不出错啦。。。


先前帖子中就曾建议用我们的例程,并采用查询方式,对于调试一般先用最简单的方式去试验,成功后再根据需要修改,便于出错分析。


有新情况再请教:

之前测试2000个扇区不出错的U盘,是SanDisk 256M的(看做工好像不错,可能是正经货)。

但是换回紫光1G的U盘,还是会出现0x17号状态,也就是:USB_INT_BUF_OVER。(这个U盘上印着“紫光电子”,不知道和传说中的“清华紫光”有什么关系,U盘上有一个看起来像指示灯的孔,但是从未亮过,怀疑这个U盘质量不行,因为有1次,电脑识别这个U盘都出了故障,表现是:通过电脑查看U盘文件,发现U盘是空的,拔出重新插,原来的文件又出现了)

现在我有2份代码,一份是中断的,一份是查询的,都会在1G的U盘出相同的故障,而另一个从不出错。

在出错后,我重新初始化U盘,再接着当前扇区继续读取,跌跌撞撞也能走完。而且能读出正确的数据。

另外,这2个U盘扇区确定都是512字节的。

还有一个问题想请教,如果用电脑以扇区方式向U盘上写内容,如何获取U盘每扇区的字节数?


代码:

UI8 Ch375Reset() { UI8 i; UI8 state;

Ch375WriteCmd( CMD_DISK_INIT ); delay_ms1(200); state = Ch375WaitInt(50000); //50ms内返回正确 if(state != USB_INT_SUCCESS) { Usart1_SendByte(0x40); Usart1_SendByte(state); return FALSE; } //else // return TRUE; Ch375WriteCmd(CMD_DISK_READY); state = Ch375WaitInt(2000); // 等待中断并获取状态 if ( state != USB_INT_SUCCESS ) // 出错重试 { Usart1_SendByte(0x41); return FALSE; } Ch375WriteCmd( CMD_DISK_SIZE ); // 获取USB存储器的容量 state = Ch375WaitInt( 2000); // 等待中断并获取状态 if ( state != USB_INT_SUCCESS ) // 出错重试 { Usart1_SendByte(0x42); Usart1_SendByte(state); delay_ms1( 200 ); Ch375WriteCmd( CMD_DISK_SIZE ); // 获取USB存储器的容量 state = Ch375WaitInt(2000 ); // 等待中断并获取状态 } if ( state != USB_INT_SUCCESS ) { Usart1_SendByte(0x43); Usart1_SendByte(state); return FALSE; }

// 可以由CMD_RD_USB_DATA命令将容量数据读出,分析每扇区字节数 // Ch375WriteCmd( CMD_RD_USB_DATA ); // 从CH375缓冲区读取数据块 i = Ch375ReadData( ); // 后续数据的长度 if ( i != 8 ) { Usart1_SendByte(0x44); Usart1_SendByte(i); return FALSE; } for ( i = 0; i != 8; i ++ ) // 根据长度读取数据 { g_DiskSizeBuff[ i ] = Ch375ReadData( ); // 读出数据并保存 // } //Usart1_SendByte(0x60); //Usart_SendBlock(g_DiskSizeBuff, 8); i = g_DiskSizeBuff[ 6 ]; // U盘容量数据中的每扇区字节数,大端格式 // if ( i == 0x04 ) g_BlockPerSector = 1024/CH375_BLOCK_SIZE; // 磁盘的物理扇区是1K字节 else if ( i == 0x08 ) g_BlockPerSector = 2048/CH375_BLOCK_SIZE; // 磁盘的物理扇区是2K字节 else if ( i == 0x10 ) g_BlockPerSector = 4096/CH375_BLOCK_SIZE; //磁盘的物理扇区是4K字节 else g_BlockPerSector = 512/CH375_BLOCK_SIZE; // 默认的磁盘的物理扇区是512字节 g_BytePerSector = g_BlockPerSector*CH375_BLOCK_SIZE; // 物理磁盘的扇区大小

Usart1_SendByte(0x60); Usart1_SendByte(g_BlockPerSector); //Usart1_SendByte(g_BytePerSector);

if(g_BlockPerSector != 8) { Usart1_SendByte(0x45); //Ch375WriteCmd( CMD_SET_PKT_P_SEC ); // 设置USB存储器的每扇区数据包总数 //Ch375WriteCmd( 0x39 ); //Ch375WriteCmd( g_BlockPerSector ); // 设置每扇区数据包总数 return FALSE; } return( TRUE ); // U盘已经成功初始化

}

// 等待CH375中断并获取状态 UI8 Ch375WaitInt(UI32 timeout) // 主机端等待操作完成, 返回操作状态 { //UI32 timeout = 100; for(;;) { delay_us1(0); if(CH375_INT) { delay_us1(0); if(CH375_INT) { Ch375WriteCmd(CMD_GET_STATUS); delay_us1(0); return Ch375ReadData(); } } else { timeout --; //if(!timeout) // return 0; delay_us1(1); } } //return 0; //while( ! CH375_INT ); // 查询等待CH375操作完成中断(INT#低电平) //CH375_WR_CMD_PORT( CMD_GET_STATUS ); /* 产生操作完成中断, 获取中断状态 */ //return( CH375_RD_DAT_PORT( ) ); }

// 从U盘读取多个扇区的数据块到缓冲区 UI8 Ch375ReadSector( UI32 iLbaStart, UI8 iSectorCount, UI8 *iBuffer ) // iLbaStart 是准备读取的线性起始扇区号, iSectorCount 是准备读取的扇区数, iBuffer 是存放返回数据的缓冲区的起址 { unsigned char mIntStatus; unsigned char *mBufferPoint; unsigned int mBlockCount; unsigned char mLength; Ch375WriteCmd( CMD_DISK_READ ); /* 从USB存储器读数据块 */ //delay_us1(0); Ch375WriteData( (unsigned char)iLbaStart ); /* LBA的最低8位 */ //delay_us1(0); Ch375WriteData( (unsigned char)( iLbaStart >> 8 ) ); //delay_us1(0); Ch375WriteData( (unsigned char)( iLbaStart >> 16 ) ); //delay_us1(0); Ch375WriteData( (unsigned char)( iLbaStart >> 24 ) ); /* LBA的最高8位 */ //delay_us1(0); Ch375WriteData( iSectorCount ); /* 扇区数 */ mBufferPoint = iBuffer; /* 指向缓冲区起始地址 */ for ( mBlockCount = iSectorCount * 8; mBlockCount != 0; mBlockCount -- ) { /* 数据块计数 */ mIntStatus = Ch375WaitInt(2000 ); /* 等待中断并获取状态 */ if ( mIntStatus == USB_INT_DISK_READ ) { /* USB存储器读数据块,请求数据读出 */ Ch375WriteCmd( CMD_RD_USB_DATA ); /* 从CH375缓冲区读取数据块 */ //delay_us1(0); mLength = Ch375ReadData( ); /* 后续数据的长度 */ while ( mLength ) { /* 根据长度读取数据 */ *mBufferPoint = Ch375ReadData( ); /* 读出数据并保存 */ mBufferPoint ++; mLength --; } //delay_us1(1); Ch375WriteCmd( CMD_DISK_RD_GO ); /* 继续执行USB存储器的读操作 */ //delay_us1(1); } else // 返回错误状态 { ////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////注,出错就是这里,串口在收到0x90后会收到0x17 ////////////////////////////////////////////////////////////////////////////////// Usart1_SendByte(0x90); Usart1_SendByte(mIntStatus); break; } } if ( mBlockCount == 0 ) { mIntStatus = Ch375WaitInt(2000 ); /* 等待中断并获取状态 */ if ( mIntStatus == USB_INT_SUCCESS ) /* 操作成功 */ return( 0 ); else { Usart1_SendByte(0x91); Usart1_SendByte(mIntStatus); } } // if ( mIntStatus == USB_INT_DISCONNECT ) return( mIntStatus ); /* U盘断开 */ //Ch375ClearError( ); /* 清除U盘错误以便重试 */ return( mIntStatus ); /* 操作失败 */ }


Ch375WaitInt这个函数,改成这个样子,效果也是相同的:

UI8 Ch375WaitInt(UI32 timeout) // 主机端等待操作完成, 返回操作状态 { for(;;) { delay_us1(0); if(CH375_INT) { Ch375WriteCmd(CMD_GET_STATUS); return Ch375ReadData(); } else { timeout --; if(!timeout) return 0; delay_us1(1); } } }


Ch375WaitInt( )这个函数里的参数,你根据什么来送5000,2000的?2000对的你MCU来说会等待多长时间? if(g_BlockPerSector != 8) { Usart1_SendByte(0x45); //Ch375WriteCmd( CMD_SET_PKT_P_SEC ); // 设置USB存储器的每扇区数据包总数 //Ch375WriteCmd( 0x39 ); //Ch375WriteCmd( g_BlockPerSector ); // 设置每扇区数据包总数 return FALSE; } 还是要设置有一下.


(1)在电脑上可以用WinHex软件,查看DBR区中的扇区大小,事先需要熟悉一下FAT文件系统 (2)如果你的单片机系统资源充足(10K以上程序空间,扇区大小+100字节的RAM)的话,可以考虑子程序库,可以扇区级、文件级读写,且兼容性好。


回复9楼:

1,Ch375WaitInt中每个循环都会延时1uS,传入2000,至少延时2mS。 另外请看我的代码,超时判断的代码我已经注释掉了,也就是说,这个参数无论传多少,只要没有中断信号,都不会退出,一直等下去。

2,设置每扇区数据包总数,只是在扇区不是512字节时才用吧?我现在已经确定U盘每扇区就是512字节。如果不是512字节,我会返回FALSE,直接退出整个程序的。

回复10楼:

我用的是IAR 4.22 EWARM,单片机是STM32,刚开始也想用子程序库,但是发现贵公司的子程序库好像没有适合这个编译器的。


这样吧,U盘本身的特性问题,你完全按照我们提供的初始化函数来做.不要在执行命令上做修改.我们的程序都是进行测试的.按照协议来说肯定是可以的.U盘兼容性确实很难调试. 按照经验来说,一般能获取磁盘容量的话基本上表示U盘已经能够正确的读写了.你在做一下其他U盘测试看结果如何. 另外设置块大小的问题.做了也不为多,以前确实碰到过不设置操作不成功的现象. 4.42版本的,5.2版本的都有这个库,建议您更换编译器.



又买了2个U盘,一个正品一个山寨的。刚开始2个都不行,不过稍微改动了下程序又可以了。

最新测试结果: 紫光电子1G:还是老问题,读2-20个扇区出一次0x17号错误,重新初始化后大部分情况可以继续走。(大部分情况都是频频出错,20次里可能有一次,读400个扇区只出一次错的情况。)

SanDisk256M:最稳定,不用做任何特殊处理一直可以连续读几百个扇区。

金士顿8G:在CMD_DISK_INIT命令后发送CMD_DISK_READY会失败,直接把这个指令去掉了。第一次发送CMD_DISK_SIZE指令也会失败,失败后中断返回的状态是0x1F(USB_INT_DISK_ERR),第二次才能成功。

山寨HP 4G:初始化正常,但是CMD_DISK_READ命令返回的特别慢,比其它U盘慢100倍,其它在等待中断时设置为2ms超时,从未发生过超时的状况,但这个设置为200ms才不超时,100ms都会超时。


只有登录才能回复,可以选择微信账号登录