C51+CH375读U盘实例

发布者:Ziyu2022最新更新时间:2016-11-14 来源: eefocus关键字:C51  CH375  读U盘 手机看文章 扫描二维码
随时随地手机看文章

找到了一个现成的例子,对于读写U盘,文件系统是个大麻烦。现在一直还没有弄明白,先收藏一下这个程序先,等到后面慢慢研究。

     作者这个编程规范不错,注释很丰富,值得学习。

#include  
#include "CH375INC.H" 
#include   /* 以下定义适用于MCS-51单片机 */ 
#define  UINT8     unsigned char 
#define  UINT16    unsigned short 
#define  UINT32    unsigned long 
#define  UINT8X    unsigned char xdata 
#define  UINT8VX   unsigned char volatile xdata 
UINT8VX    CH375_CMD_PORT _at_ 0xBDF1;  /* CH375命令端口的I/O地址 */ 
UINT8VX    CH375_DAT_PORT _at_ 0xBCF0;  /* CH375数据端口的I/O地址 */ 
#define    CH375_INT_WIRE    INT0       /* P3.2, 连接CH375的INT#引脚,用于查询中 
断状态 */ 
UINT8X     DISK_BUFFER[512*32] _at_ 0x0000;  /* 外部RAM数据缓冲区的起始地址 */ 

UINT32  DiskStart;    /* 逻辑盘的起始绝对扇区号LBA */ 
UINT8   SecPerClus;   /* 逻辑盘的每簇扇区数 */ 
UINT8   RsvdSecCnt;   /* 逻辑盘的保留扇区数 */ 
UINT16 ; FATSz16;      /* FAT16逻辑盘的FAT表占用的扇区数 */  


/* ********** 硬件USB接口层,无论如何这层省不掉,单片机总要与CH375接口吧 */ 

void  mDelaymS( UINT8 delay ) { 
  UINT8  i, j, c; 
  for ( i = delay; i != 0; i -- ) { 
    for ( j = 200; j != 0; j -- ) c += 3; 
    for ( j = 200; j != 0; j -- ) c += 3; 
  } 


void CH375_WR_CMD_PORT( UINT8 cmd ) {  /* 向CH375的命令端口写入命令 */ 
  CH375_CMD_PORT=cmd; 
  for ( cmd = 2; cmd != 0; cmd -- );  /* 发出命令码前后应该各延时2uS */ 

void CH375_WR_DAT_PORT( UINT8 dat ) {  /* 向CH375的数据端口写入数据 */ 
  CH375_DAT_PORT=dat;          /* 因为MCS51单片机较慢所以实际上无需延时 */

UINT8 CH375_RD_DAT_PORT( void ) {    /* 从CH375的数据端口读出数据 */ 
  return( CH375_DAT_PORT );      /* 因为MCS51单片机较慢所以实际上无需延时 */ 

UINT8 mWaitInterrupt( void ) {  /* 等待CH375中断并获取状态,返回操作状态 */ 
  while( CH375_INT_WIRE );  /* 查询等待CH375操作完成中断(INT#低电平) */ 
  CH375_WR_CMD_PORT( CMD_GET_STATUS );  /* 产生操作完成中断,获取中断状态 */ 
  return( CH375_RD_DAT_PORT( ) ); 


/* ********** BulkOnly传输协议层,被CH375内置了,无需编写单片机程序 */ 

/* ********** RBC/SCSI命令层,虽然被CH375内置了,但是要写程序发出命令及收发数据   
*/ 

UINT8  mInitDisk( void ) {  /* 初始化磁盘 */ 
  UINT8 Status; 
  CH375_WR_CMD_PORT( CMD_GET_STATUS );  /* 产生操作完成中断, 获取中断状态 */ 
  Status = CH375_RD_DAT_PORT( ); 
  if ( Status == USB_INT_DISCONNECT ) return( Status );  /* USB设备断开 */ 
  CH375_WR_CMD_PORT( CMD_DISK_INIT );  /* 初始化USB存储器 */ 
  Status = mWaitInterrupt( );  /* 等待中断并获取状态 */ 
  if ( Status != USB_INT_SUCCESS ) return(Status );  /* 出现错误 */ 
  CH375_WR_CMD_PORT( CMD_DISK_SIZE );  /* 获取USB存储器的容量 */ 
  Status = mWaitInterrupt( );  /* 等待中断并获取状态 */ 
  if ( Status != USB_INT_SUCCESS ) {  /* 出错重试 */ 
/* 对于CH375A芯片,建议在此执行一次CMD_DISK_R_SENSE命令 */ 
    mDelaymS( 250 ); 
    CH375_WR_CMD_PORT( CMD_DISK_SIZE );  /* 获取USB存储器的容量 */ 
    Status = mWaitInterrupt( );  /* 等待中断并获取状态 */ 
  } 
  if ( Status != USB_INT_SUCCESS ) return( Status );  /* 出现错误 */ 
  return( 0 );  /* U盘已经成功初始化 */ 
}  


UINT8  mReadSector( UINT32 iLbaStart, UINT8 iSectorCount, UINT8X *oDataBuffer )  

  UINT16  mBlockCount; 
  UINT8  c; 
  CH375_WR_CMD_PORT( CMD_DISK_READ );  /* 从USB存储器读数据块 */ 
  CH375_WR_DAT_PORT( (UINT8)iLbaStart );  /* LBA的最低8位 */ 
  CH375_WR_DAT_PORT( (UINT8)( iLbaStart >> 8 ) ); 
  CH375_WR_DAT_PORT( (UINT8)( iLbaStart >> 16 ) ); 
  CH375_WR_DAT_PORT( (UINT8)( iLbaStart >> 24 ) );  /* LBA的最高8位 */ 
  CH375_WR_DAT_PORT( iSectorCount );  /* 扇区数 */ 
  for ( mBlockCount = iSectorCount * 8; mBlockCount != 0; mBlockCount -- ) { 
    c = mWaitInterrupt( );  /* 等待中断并获取状态 */ 
    if ( c == USB_INT_DISK_READ ) {  /* 等待中断并获取状态,请求数据读出 */ 
      CH375_WR_CMD_PORT( CMD_RD_USB_DATA );  /* 从CH375缓冲区读取数据块 */ 
      c = CH375_RD_DAT_PORT( );  /* 后续数据的长度 */ 
      while ( c -- ) *oDataBuffer++ = CH375_RD_DAT_PORT( ); 
      CH375_WR_CMD_PORT( CMD_DISK_RD_GO );  /* 继续执行USB存储器的读操作 */ 
    } 
    else break;  /* 返回错误状态 */ 
  } 
  if ( mBlockCount == 0 ) { 
    c = mWaitInterrupt( );  /* 等待中断并获取状态*/ 
    if ( c== USB_INT_SUCCESS ) return( 0 );  /* 操作成功 */ 
  } 
  return( c );  /* 操作失败 */ 
}  


/* ********** FAT文件系统层,这层程序量实际较大,不过,该程序仅演示极简单的功能,所 
以精简 */ 

UINT16  mGetPointWord( UINT8X *iAddr ) {  /* 获取字数据,因为MCS51是大端格式 */ 
  return( iAddr[0] | (UINT16)iAddr[1] << 8 ); 


UINT8  mIdenDisk( void ) {    /* 识别分析当前逻辑盘 */ 
  UINT8  Status; 
  DiskStart = 0;  /* 以下是非常简单的FAT文件系统的分析,正式应用绝对不应该如此简 
单 */ 
  Status = mReadSector( 0, 1, DISK_BUFFER );  /* 读取逻辑盘引导信息 */ 
  if ( Status != 0 ) return( Status ); 
  if ( DISK_BUFFER[0] != 0xEB && DISK_BUFFER[0] != 0xE9 ) {  /* 不是逻辑引导扇 
区 */ 
    DiskStart = DISK_BUFFER[0x1C6] | (UINT16)DISK_BUFFER[0x1C7] << 8 
        | (UINT32)DISK_BUFFER[0x1C8] << 16 | (UINT32)DISK_BUFFER[0x1C9] << 24; 
    Status = mReadSector( DiskStart, 1, DISK_BUFFER ); 
    if ( Status != 0 ) return( Status ); 
  } 
  SecPerClus = DISK_BUFFER[0x0D];  /* 每簇扇区数 */ 
  RsvdSecCnt = DISK_BUFFER[0x0E];  /* 逻辑盘的保留扇区数 */ 
  FATSz16 = mGetPointWord( &DISK_BUFFER[0x16] );  /* FAT表占用扇区数 */ 
  return( 0 );  /* 成功 */ 


UINT16  mLinkCluster( UINT16 iCluster ) {  /* 获得指定簇号的链接簇 */ 
/* 输入: iCluster 当前簇号, 返回: 原链接簇号, 如果为0则说明错误 */ 
  UINT8  Status; 
  Status = mReadSector( DiskStart + RsvdSecCnt + iCluster / 256, 1,  
DISK_BUFFER ); 
  if ( Status != 0 ) return( 0 );  /* 错误 */ 
  return( mGetPointWord( &DISK_BUFFER[ ( iCluster + iCluster ) & 0x01FF ] ) ); 


UINT32  mClusterToLba(UINT16 iCluster ) {  /* 将簇号转换为绝对LBA扇区地址 */ 
  return( DiskStart + RsvdSecCnt + FATSz16 * 2 + 32 + ( iCluster - 2 ) *  
SecPerClus ); 
}  


void  mInitSTDIO( void ) {  /* 仅用于调试用途及显示内容到PC机,与该程序功能完全无 
关 */ 
  SCON = 0x50; PCON = 0x80; TMOD = 0x20; TH1 = 0xf3; TR1=1; TI=1;  /* 24MHz,  
9600bps */ 

void  mStopIfError( UINT8 iErrCode ) {  /* 如果错误则停止运行并显示错误状态 */ 
  if ( iErrCode == 0 ) return; 
  printf( "Error status, %02X 
", (UINT16)iErrCode ); 


main( ) { 
  UINT8  Status; 
  UINT8X  *CurrentDir; 
  UINT16  Cluster; 
  mDelaymS( 200 );  /* 延时200毫秒 */ 
  mInitSTDIO( ); 
  CH375_WR_CMD_PORT( CMD_SET_USB_MODE );  /* 初始化CH375,设置USB工作模式 */ 
  CH375_WR_DAT_PORT( 6 );  /* 模式代码,自动检测USB设备连接 */ 
  while ( 1 ) { 
    printf( "Insert USB disk 
" ); 
    while ( mWaitInterrupt( ) != USB_INT_CONNECT );  /* 等待U盘连接 */ 
    mDelaymS( 250 );  /* 延时等待U盘进入正常工作状态 */ 
    Status = mInitDisk( );  /* 初始化U盘,实际是识别U盘的类型,必须进行此步骤 */ 
    mStopIfError( Status ); 
    Status = mIdenDisk( );  /* 识别分析U盘文件系统,必要操作 */ 
    mStopIfError( Status ); 
    Status = mReadSector( DiskStart + RsvdSecCnt + FATSz16 * 2, 32,  
DISK_BUFFER ); 
    mStopIfError( Status );  /* 读取FAT16逻辑盘的根目录,通常根目录占用32个扇区  
*/ 
    for ( CurrentDir = DISK_BUFFER; CurrentDir[0] != 0; CurrentDir += 32 ) { 
      if ( ( CurrentDir[0x0B] & 0x08 ) == 0 && CurrentDir[0] != 0xE5 ) { 
        CurrentDir[0x0B] = 0;  /* 为了便于显示,设置文件名或者目录名的结束标志 */ 
        printf( "Name: %s 
", CurrentDir );  /* 通过串口输出显示 */ 
      } 
    }  /* 以上显示根目录下的所有文件名,以下打开第一个文件,如果是C文件的话 */
    if ( (DISK_BUFFER[0x0B]&0x08)==0 && DISK_BUFFER[0]!=0xE5 && DISK_BUFFER[8] 
=='C' ) { 
      Cluster = mGetPointWord( &DISK_BUFFER[0x1A] );  /* 文件的首簇 */ 
      while ( Cluster < 0xFFF8 ) {  /* 文件簇未结束 */ 
        if ( Cluster == 0 ) mStopIfError( 0x8F );  /* 对于首簇,可能是0长度文件  
*/ 
        Status = mReadSector( mClusterToLba( Cluster ), SecPerClus,  
DISK_BUFFER ); 
        mStopIfError( Status );  /* 读取首簇到缓冲区 */ 
        DISK_BUFFER[30] = 0; printf( "Data: %s 
", DISK_BUFFER );  /* 显示首行  
*/ 
        Cluster = mLinkCluster( Cluster );  /* 获取链接簇,返回0说明错误 */ 
      } 
    } 
    while ( mWaitInterrupt( ) != USB_INT_DISCONNECT );  /* 等待U盘拔出 */ 
    mDelaymS( 250 ); 
  } 

关键字:C51  CH375  读U盘 引用地址:C51+CH375读U盘实例

上一篇:I2C总线(主)C51源程序
下一篇:C51的PID程序

推荐阅读最新更新时间:2024-03-16 15:20

Keil C51 Code Banking
1. 简介 MCS-51是8根数据线,16根地址线,所以MCS-51最大只能访问64KB(216)的地址。很多较复杂的C51代码,其整个代码生成Bin文件可能大小64KB。针对这种情况,Keil C51提出了Code Banking机制来解决这个问题。 2. 基本原理 MCS-51内核的代码执行机制已经固定死了,代码运行的地址范围只能在0-64KB之间。一般情况下,我们编译生成的Bin文件,其代码执行地址和代码存储地址是一致。MCU执行到哪个地址,直接去ROM上取相应地址的内容然后执行即可。 MCU要访问超过64KB的地址范围,肯定需要增加地址线。增加4根地址线,则能够访问到2MB的地址范围。由于MCS-51只能执行64K
[单片机]
Keil <font color='red'>C51</font> Code Banking
PID调节C51程序(4)
typedef struct PID { double SetPoint; // Desired Value double Proportion; // Proportional Const double Integral; // Integral Const double Derivative; // Derivative Const double LastError; // Error double PrevError; // Error double SumError; // Sums of Errors
[单片机]
C51编译器-高级编程技巧(2)-程序段命名约定
Segment Naming Conventions程序段命名约定 Cx51编译器生成的目标(程序代码,程序数据和常量数据)都以程序段的形式存放,段是代码和数据的单元。一个段可能是可重定位的,也可能地址是绝对的。每一个可重定位的段都有一个类型和一个名字。 段名包括:module_name。module_name是声明这个段的源文件的名字(不包含驱动器名,路径和扩展名)。为了能适应现有的各种各样的软件和硬件工具,所有段名都一律大写。 每个段名都有一个前缀,这个前缀对应于段所用的存储器类型。这个前缀放在两个问号(?)之间。下面列出几种标准的段名前缀: 段前缀 存储器类型 描述 ?PR? program
[单片机]
C51单片机0~60计数器
源码 #includeunsigned char code table ={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; unsigned char second=0; bit a=0; char t=0; int0_srv() interrupt 1{ t++; if(t==20){t=0;a=1;} TH0=0x3c; TL0=0xb0; } main(){ P0=P2=table ; TMOD=0x01; TH0=0x3c; TL0=0xb0; ET0=1; EA=1; TR0=1; while(1){ if(a){
[单片机]
<font color='red'>C51</font>单片机0~60计数器
C51入门1,跑马灯和LED实验
刚开始接触的是C51,自己以前写的程序,在自己的实验板上实现的,现在整理一下留做纪念哈!!! 实验一:一个LED灯闪烁 //一个LED灯闪烁实验 #define uchar unsigned char #define uint unsigned int #define ulong unsigned long //头文件 #include reg52.h //端口设置 sbit P10=P1^0; char code dx516 _at_ 0x003b; //主函数 void main(void) { uint i; P10=0; while(1) { P10=~P10; //取反 for (i=0;i 200
[单片机]
Keil C51 中使用Printf()进行串口输出
如何使用printf在串口显示信息,有两种办法,因为在keil C51中 printf函数是调用putchar进行数据传送的,而putchar应该是先判断TI是否为1,不为1则等待为1。 如果为1则清0,然后送出一个字符。因此如果你直接使用printf函数,你的程序就会在putchar函数中等待TI为1。这时你的程序就相当于直接死掉了。你可以通过改写putchar函数实现自己的目的。TI相当于是初始化不给赋初值就不干活。 所以第一种办法就是在初始化的时候,给TI赋值为1,比如 void uart_init() { PCON &= 0x7F; //波特率不倍速 SCON = 0x50; //8位数据,可变波特率
[单片机]
LCD1602+ADC0832数字电压表(asm/c51
..................................LCD1602+ADC0832数字电压表(已调试通过 )................................. rs bit P2.4 rw bit P2.5 e bit P2.6 clk bit P1.3 do bit P1.4 di bit P1.5 cs bit P1.6 com data 60h dat data 61h adcb data 62h adcs data 63h adcg data 64h adc data 65h chx data 66h org 0000h sjmp main org 0030h main:mov sp
[单片机]
LCD1602+ADC0832数字电压表(asm/<font color='red'>c51</font>)
keil C51语言编程如何缩减编译代码长度
1、整形数字符做乘除法的C51程序,代码比较大。约9B左右,所以尽量不做乘除法。 例如:uchar a=6;a=a*6;第二行代码要9个字节,而平时的语句只有2B。 2、定义变量的语句,不赋值或者不参与运算是不算代码的,能用短变量就用短变量,省RAM也省代码空间. 例如:uchar a; 这句有没有代码长度不变。但是赋非0值 uchar a=5;字符型,就要3B的代码长度。 uint a=5;整形变量赋值要6B字节,比较大,所以能定义短变量就定义短变量。 unsigned long int a=6.0;或者 long int a=6.0;会增加9B的代码。赋值整数浮点数一样。 3、对长整形变量,有符号变量比无符号变量
[单片机]
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved