17320|14

84

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

评估板SD卡学习之FatFs文件系统深入解读…… [复制链接]

这几天由于考试的原因,学习速度有点慢。一下是我这几天学习文件系统获得的知识和心得,和大家分享。在网上也看了一些关于FatFs文件系统代码解读的文章,但是看别人的毕竟是走马观花,所以决定自己沿着计划看下去,并写下自己的理解。由于板子自带的例程里有FatFs的源代码,所以就没有再去下过了,直接打开例程进行研究。

 首先,研究文件系统的目的是在移植之前,先将源代码大概的阅读一遍,主要是了解文件系统的结构、各个函数的功能和接口、与移植相关的代码等等。然后再来研究SD卡的具体操作。FatFs里包含了3个文件:integer.h,了解所用的数据类型,然后是ff.h,了解文件系统所用的数据结构和各种函数声明,然后是diskio.h,了解与硬件相关的数据结构和操作函数。再把ff.cdiskio.c两个文件所实现的函数大致扫描一遍。最后根据用户应用层程序调用函数的次序仔细阅读相关代码。

好,开始了~

一.       Integer.h文件

都是一些这样的定义,使用的都是typedef定义一些数据类型,网上说在一直的时候可能要改里面的内容。下面贴出部分代码:

typedef signed char                   CHAR;

typedef unsigned char     UCHAR;

typedef unsigned char     BYTE;

二:ff.h文件

#ifndef _FATFS

#define _MCU_ENDIAN        1

_MCU_ENDIAN定义了访问方法1。启用Word访问:2。禁用字访问和使用字节按字节访问。当单片机的字节顺序要设置为是大端存储

#define _FS_READONLY    0

很明显,这个定义是用来设置只读的。当它为1实是只读。

#define _FS_MINIMIZE    0

这个是用来定义最小水平_FS_MINIMIZE。作用是用来删除一些功能:

0时表示所有全功能:

1 f_statf_getfreef_unlinkf_mkdirf_chmodf_rename被删除。

2:在1的基础上再删除 f_opendirf_readdir

3:2的基础上再删了f_lseek

#define _DRIVES        2

这定义了被使用的逻辑驱动器的数目,这会影响到内部表的大小。

[ 本帖最后由 xielijuan 于 2010-11-22 10:50 编辑 ]

最新回复

好详细啊,顶!  详情 回复 发表于 2015-3-30 21:33
 
点赞 关注(1)

回复
举报

84

帖子

0

TA的资源

一粒金砂(中级)

推荐
 
#define _USE_FSINFO    0
设置_USE_FSINFO为1时,使FSInfo支持FAT32卷,否则不支持。
#define    _USE_SJIS    1
当_USE_SJIS设置为1,打开Shift - JIS的代码的透明性功能,否则只有美国ASCII(7位)代码可以作为文件/目录名。
#define    _USE_NTFLAG    1
当_USE_NTFLAG设置为1,保持文件名的大小写。注意,文件始终区分大小写访问。
#include "integer.h"
打开integer.h文件

#define    S_MAX_SIZ    512            /* Do not change */
#if S_MAX_SIZ > 512
#define    S_SIZ    (fs->s_size)
#else
#define    S_SIZ    512
#endif
这部分的作用是定义扇区的大小

/*下面是文件系统里的对象结构 */
typedef struct _FATFS {
    WORD    id;                /* 文件系统的挂载ID号*/
    WORD    n_rootdir;        /* 根目录项数*/
                        DWORD    winsect;  /* 出现在win[]里的当前扇区数,这个win[]数组暂时还不知道
    DWORD    sects_fat;        /* 每个文件配置表的扇区 */
    DWORD    max_clust;        /* 最大的簇*/
    DWORD    fatbase;        /* FAT表的初始扇区*/
    DWORD    dirbase;        /* 根目录的启动扇区*/
    DWORD    database;        /* 数据启动扇区*/
#if !_FS_READONLY
    DWORD    last_clust;        /* 最后分配的簇*/
    DWORD    free_clust;        /* 可用簇的数目 */
#if _USE_FSINFO
    DWORD    fsi_sector;        /* 用于fsinfo的扇区 */
    BYTE    fsi_flag;        /* fsinfo dirty flag (1:must be written back) 文件需要回写的标志*/
    BYTE    pad2;
#endif
#endif
    BYTE    fs_type;        /* FAT sub type */
    BYTE    sects_clust;    /* 每簇扇区*/
#if S_MAX_SIZ > 512
    WORD    s_size;            /* 扇区大小*/
#endif
    BYTE    n_fats;            /* Number of FAT copies */
    BYTE    drive;            /* 物理驱动数*/
    BYTE    winflag;        /* win[] dirty flag (1:must be written back) */
    BYTE    pad1;
    BYTE    win[S_MAX_SIZ];    /* 磁盘目录访问窗口*/
} FATFS;

/* 下面是目录对象结构*/
typedef struct _DIR {
    WORD    id;            /* 文件系统的挂载ID*/
    WORD    index;        /* 目前读写索引代码*/
    FATFS*    fs;            /* 指向文件系统对象 */
    DWORD    sclust;        /* 初始簇*/
    DWORD    clust;        /*当前簇 */
    DWORD    sect;        /* 当前扇区 */
} DIR;


/* 下面是文件对象结构 ,这个结构主要描述文件的状态信息,包括文件名13个字符(8+.+3+\0)、属性、修改时间等。*/
typedef struct _FIL {
    WORD    id;                /* 文件系统挂载ID */
    BYTE    flag;            /*文件状态标志 */
    BYTE    sect_clust;        /* 簇的左扇区 */
    FATFS*    fs;                /*指向文件系统对象*/
    DWORD    fptr;            /* 文件读写指针 */
    DWORD    fsize;            /* 文件大小*/
    DWORD    org_clust;       /* 初始簇*/
    DWORD    curr_clust;         /*当前簇 */
    DWORD    curr_sect;       /* 当前扇区 */
#if _FS_READONLY == 0
    DWORD    dir_sect;        /* 扇区包含的目录项 */
    BYTE*    dir_ptr;        /* 指向窗口中的目录项*/
#endif
    BYTE    buffer[S_MAX_SIZ];    /* 文件读写缓冲区 */
} FIL;

/* 下面文件状态结构 */,英文注释一目了然,这里就不多说了。
typedef struct _FILINFO {
    DWORD fsize;            /* Size */
    WORD fdate;                /* Date */
    WORD ftime;                /* Time */
    BYTE fattrib;            /* Attribute */
    char fname[8+1+3+1];    /* Name (8.3 format) */
} FILINFO;


/*这段代码是用来定义多分区的 */

#if _MULTI_PARTITION != 0    /*多分区 cfg */cfg是什么东西?

typedef struct _PARTITION {
    BYTE pd;    /* 物理驱动器# (0-255) */
    BYTE pt;    /* 分区# (0-3) */
} PARTITION;
extern
const PARTITION Drives[];            /* 逻辑驱动器#的物理位置转换表*/
#define LD2PD(drv) (Drives[drv].pd)    /*获得物理驱动器 # */
#define LD2PT(drv) (Drives[drv].pt)    /* 获得分区# */
#else                        /* Single partition cfg */
#define LD2PD(drv) (drv)        /* 物理驱动器#等于逻辑驱动器# */
#define LD2PT(drv) 0            /* 一直挂载第一分区*/
#endif

typedef enum {
    FR_OK = 0,            /* 0 */
    FR_NOT_READY,        /* 1 */
    FR_NO_FILE,            /* 2 */
    FR_NO_PATH,            /* 3 */
    FR_INVALID_NAME,    /* 4 */
    FR_INVALID_DRIVE,    /* 5 */
    FR_DENIED,            /* 6 */
    FR_EXIST,            /* 7 */
    FR_RW_ERROR,        /* 8 */
    FR_WRITE_PROTECTED,    /* 9 */
    FR_NOT_ENABLED,        /* 10 */
    FR_NO_FILESYSTEM,    /* 11 */
    FR_INVALID_OBJECT,    /* 12 */
    FR_MKFS_ABORTED        /* 13 */
} FRESULT;
这个枚举定义了调用文件函数的返回值

接下来是函数的定义,先大概浏览一遍。
FRESULT f_mount (BYTE, FATFS*);        //加载文件系统,BYTE参数是ID,后一个是文件系统定义。
FRESULT f_open (FIL*, const XCHAR*, BYTE);//打开文件,第一个参数是文件信息结构,第二个参数是文件名,第三是文件打开模式
FRESULT f_read (FIL*, void*, UINT, UINT*);        //文件读取函数,参数1为文件对象(文件打开函数中得到),参数2为文件读取缓冲区,参数3为读取的字节数,参数4意义不清晰,等读到源代码就清楚了。
FRESULT f_write (FIL*, const void*, UINT, UINT*);//写文件,参数跟读差不多
FRESULT f_lseek (FIL*, DWORD); //移动文件的读写指针,参数2应该是移动的数目。
FRESULT f_close (FIL*);                        /* 关闭文件*/
FRESULT f_opendir (DIR*, const XCHAR*);        打开目录,返回目录对象
FRESULT f_readdir (DIR*, FILINFO*);                读取目录,获得文件信息
FRESULT f_stat (const XCHAR*, FILINFO*);                                /* 获得文件信息*/
FRESULT f_getfree (const XCHAR*, DWORD*, FATFS**);        /* 获得可用的驱动器上的簇*/
FRESULT f_truncate (FIL*);                        /* 截断文件*/
FRESULT f_sync (FIL*);        /* Flush cached data of a writing file */将缓冲区数据写回文件
FRESULT f_unlink (const XCHAR*);                删除目录中的一个文件
FRESULT        f_mkdir (const XCHAR*);                /* 创建一个新的目录*/
FRESULT f_chmod (const XCHAR*, BYTE, BYTE);        /* Change attriburte of the file/dir */
FRESULT f_utime (const XCHAR*, const FILINFO*);        /* Change time stamp of the file/dir */
FRESULT f_rename (const XCHAR*, const XCHAR*);        /* 重命名或删除一个文件或目录*/
FRESULT f_forward (FIL*, UINT(*)(const BYTE*,UINT), UINT, UINT*);        /* Forward data to the stream */ 这个函数还要提供一个回调函数。
FRESULT f_mkfs (BYTE, BYTE, WORD);                /* 在该驱动器上创建一个文件系统*/

DWORD get_fattime (void);    /* 31-25: Year(0-127 org.1980), 24-21: Month(1-12), 20-16: Day(1-31) */
                            /* 15-11: Hour(0-23), 10-5: Minute(0-59), 4-0: Second(0-29 *2) */
用户定义一个函数给获得当前时间给FatFs模块
还有余下的一些代码定义了文件访问控制以及文件状态标志的变量值,现在也看不懂,用到了再说吧。
OK!大致学习了一下,ff.h文件,接下来就是关于硬件的diskio.h文件了。
 
 

回复

84

帖子

0

TA的资源

一粒金砂(中级)

板凳
 
三.diskio.h

#ifndef _DISKIO

#define _READONLY        0        /* 1: 只读模式 */

#include "integer.h"


/* 磁盘功能状态 */
typedef BYTE        DSTATUS;

/* 调用磁盘函数的结果*/
typedef enum {
        RES_OK = 0,                /* 0: 成功 */
        RES_ERROR,                /* 1: 读写错误 */
        RES_WRPRT,                /* 2: 写保护*/
        RES_NOTRDY,                /* 3: 未准备好 */
        RES_PARERR                /* 4:无效参数 */
} DRESULT;


/* 磁盘控制函数的原型*/
DSTATUS disk_initialize (BYTE);   //磁盘初始化
DSTATUS disk_status (BYTE);    //获取磁盘状态
DRESULT disk_read (BYTE, BYTE*, DWORD, BYTE);
#if        _READONLY == 0     //读磁盘
DRESULT disk_write (BYTE, const BYTE*, DWORD, BYTE);
#endif               //写磁盘
DRESULT disk_ioctl (BYTE, BYTE, void*);        //磁盘控制
void        disk_timerproc (void);           //这个函数不晓得是用来干什么的

还有一些常数的定义,用到了再回来查吧~~
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(中级)

4
 
四.diskio.c
由于我没有下载FatFs,所以,没有diskio.c的文件,在网上查了一下,函数基本都像下面这样,drv表示磁盘的类型。没有实现,用户必须实现这部分代码。关于SD卡的实现,稍后有待学习。

DSTATUS disk_initialize (   BYTE drv     /* Physical drive nmuber (0..) */)
{
       DSTATUS stat;
       int result;
       switch (drv) {
       case ATA :
              result = ATA_disk_initialize();
              // translate the reslut code here
              return stat;
       case MMC :
              result = MMC_disk_initialize();
              // translate the reslut code here
              return stat;
       case USB :
              result = USB_disk_initialize();
              // translate the reslut code here
              return stat;
       }
       return STA_NOINIT;
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(中级)

5
 
五,ff.c  
好,到文件系统的主要文件了~~
#include
#include "ff.h"            /* FatFs declarations */
#include "diskio.h"        /* 打开为用户提供的磁盘函数*/

static
FATFS *FatFs[_DRIVES];    /* 定义一个指针指向文件系统对象 */
static
WORD fsid;                /* 文件系统挂载ID*/


static
BOOL move_window (        /* TRUE: successful, FALSE: failed */
    FATFS *fs,            /* File system object */
    DWORD sector        /* Sector number to make apperance in the fs->win[] */
)                        /* Move to zero only writes back dirty window */
{
    DWORD wsect;
wsect = fs->winsect;

  代码省略



    return TRUE;
}
代码看起来很头大,一串串的if 和for。阅读了一下源代码,应该是改变文件系统的当前工作扇区,如果想要操作的扇区就是当前扇区,什么事不做;如果不是,则将原扇区写回;如果是FAT表,还得写入备份区。
这个函数内部使用,外部无法引用。

#if !_FS_READONLY
static
FRESULT sync (            /* FR_OK: successful, FR_RW_ERROR: failed */
    FATFS *fs            /* File system object */
)    //这个函数用于更新FAT32文件系统的FSI_Sector。什么含义还不太清楚。

{
    fs->winflag = 1;
    if (!move_window(fs, 0)) return FR_RW_ERROR;
#if _USE_FSINFO
    if (fs->fs_type == FS_FAT32 && fs->fsi_flag) {        /* 如果需要更新 FSInfo 扇区 */
        fs->winsect = 0;   //查了下什么叫FSIofo扇区,用以记录文件系统中空闲簇的数量以
//及下一个可用簇的簇号等信息,以供操作系统作为参考。
  部分代码省略
    }
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(中级)

6
 
static
DWORD get_cluster (        /* 0,>=2: successful, 1: failed */
    FATFS *fs,            /* File system object */
    DWORD clust            /* Cluster# to get the link information */
)
{
代码省略
return 1;
}
//这个函数顾名思义是用来获取簇的信息的,具体怎么实现的就不研究了,大致是先判断有没有有效空闲的簇,没有簇的信息或发生错误的话就返回1。

static
BOOL put_cluster (        /* TRUE: successful, FALSE: failed */
    FATFS *fs,            /* File system object */
    DWORD clust,        /* Cluster# to change */
    DWORD val            /* New value to mark the cluster */
)
{
代码省略
}
//一样的,这个函数是用来用新的值去标记一个簇。写入新的链接信息。

static
BOOL remove_chain (        /* TRUE: successful, FALSE: failed */
    FATFS *fs,            /* File system object */
    DWORD clust            /* Cluster# to remove chain from */
)
{
代码省略
}  //将下一簇号写为0,也就是该文件的簇到此为止,同时系统的自由簇增加1.

static
DWORD create_chain (    /* 0: no free cluster, 1: error, >=2: new cluster number */
    FATFS *fs,            /* File system object */
    DWORD clust            /* Cluster# to stretch, 0 means create new */
)
{
    DWORD cstat, ncl, scl, mcl = fs->max_clust;


    if (clust == 0) {        /* Create new chain */
        scl = fs->last_clust;            /* Get suggested start point */
        if (scl == 0 || scl >= mcl) scl = 1;
    }
    else {                    /* 延伸现有的链接*/
        cstat = get_cluster(fs, clust);    /* Check the cluster status */
        if (cstat < 2) return 1;        /* 如果是无效的簇,则返回1*/
        if (cstat < mcl) return cstat;    /* It is already followed by next cluster */
        scl = clust;
    }

    ncl = scl;                /*起始簇 */
    for (;;) {
        ncl++;                            /* 查找下一个簇*/
        if (ncl >= mcl) {                /* 循环判断*/
            ncl = 2;
            if (ncl > scl) return 0;    /* 没有空闲的簇,返回 0*/
        }
        cstat = get_cluster(fs, ncl);    /* 获取簇的状态进行判断,有以下三种情况*/
        if (cstat == 0) break;            /* 找到一个空闲的簇 */
        if (cstat == 1) return 1;        /* 发生错误,返回1 */
        if (ncl == scl) return 0;        /* 没有空闲的簇 返回0*/
    }

    if (!put_cluster(fs, ncl, 0x0FFFFFFF)) return 1;        /* 将一个新的簇标记为使用中*/
    if (clust && !put_cluster(fs, clust, ncl)) return 1;    /* 如果需要,将其连接到前一个 */

    fs->last_clust = ncl;                /* 更新 fsinfo */
    if (fs->free_clust != 0xFFFFFFFF) {
        fs->free_clust--;
#if _USE_FSINFO
        fs->fsi_flag = 1;
#endif
    }

    return ncl;        /* 返回新簇号 */
}
#endif /* !_FS_READONLY */)//跟上一个相反,在该簇的位置写入新的下一簇簇号。
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(中级)

7
 
static
DWORD clust2sect (    /* !=0: sector number, 0: failed - invalid cluster# */
    FATFS *fs,        /* File system object */
    DWORD clust        /* Cluster# to be converted */
)
{
    clust -= 2;
    if (clust >= (fs->max_clust - 2)) return 0;        /* Invalid cluster# */
    return clust * fs->sects_clust + fs->database;
}
//这个函数是将簇号转变为对应的扇区号。
clst * fs->csize + fs->database; //这个是算法

static
BOOL next_dir_entry (    /* 返回为真:成功,FALSE,则无法移动*/
    DIR *dirobj            /* 指向目录的指针*/
)
{
    DWORD clust;
    WORD idx;
    FATFS *fs = dirobj->fs;


    idx = dirobj->index + 1;
    if ((idx & ((S_SIZ - 1) / 32)) == 0) {        /* 判定扇区表是否改变*/
        dirobj->sect++;            /* 指向下一个扇区 */
        if (!dirobj->clust) {        /* 静态表*/
            if (idx >= fs->n_rootdir) return FALSE;    /*直至表的末尾 */
        } else {                    /* 动态表 */
            if (((idx / (S_SIZ / 32)) & (fs->sects_clust - 1)) == 0) {    /*簇是否改变 */
                clust = get_cluster(fs, dirobj->clust);        /* 这个函数在上面已经分析过,是获得下一个簇*/
                if (clust < 2 || clust >= fs->max_clust)    /* 直至表的末尾*/
                    return FALSE;
                dirobj->clust = clust;                /* 初始新的簇 */
                dirobj->sect = clust2sect(fs, clust);
            }
        }
    }
    dirobj->index = idx;    /* Lower 4 bit of dirobj->index indicates offset in dirobj->sect */
    return TRUE;
} // 这个函数是根据索引来移动当前的目录项。

static
void get_fileinfo (        /* No return code */
    FILINFO *finfo,     /* Ptr to store the file information */
    const BYTE *dir        /* Ptr to the directory entry */
){代码略}
//这个函数的作用获取一个目录项状态。

static
char make_dirfile (           
    const char **path,        /* Pointer to the file path pointer */
    char *dirname            /* Pointer to directory name buffer {Name(8), Ext(3), NT flag(1)} */
){代码略}
//这个函数的作用是选择一个新的段,创建目录项格式的名称。

其他的一些函数也都是和文件名有关的函数,看到就头大,先了解一下大概是先的功能,具体是先细节以后再研究吧……

接下来是:
FRESULT trace_path (    /* FR_OK(0): successful, !=0: error code */
    DIR *dirobj,        /* Pointer to directory object to return last directory */
    char *fn,            /* Pointer to last segment name to return {file(8),ext(3),attr(1)} */
    const char *path,    /* Full-path string to trace a file or directory */
    BYTE **dir            /* Directory pointer in Win[] to retutn */
)
//该函数给定一个全路径,得到相应的目录对象。
static
FRESULT reserve_direntry (    /* FR_OK: successful, FR_DENIED: no free entry, FR_RW_ERROR: a disk error occured */
    DIR *dirobj,            /* Target directory to create new entry */
    BYTE **dir                /* Pointer to pointer to created entry to retutn */
)
//这个函数用于储备一个目录项。
BYTE check_fs (        /* 0:The FAT boot record, 1:Valid boot record but not an FAT, 2:Not a boot record or error */
    FATFS *fs,        /* File system object */
    DWORD sect        /* Sector# (lba) to check if it is a FAT boot record or not */
)
//该函数用于读取BOOT扇区,并检查是否是FAT文件系统。
static
FRESULT auto_mount (        /* FR_OK(0): successful, !=0: any error occured */
    const char **path,        /* Pointer to pointer to the path name (drive number) */
    FATFS **rfs,            /* Pointer to pointer to the found file system object */
    BYTE chk_wp                /* !=0: Check media write protection for wrinting fuctions
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(中级)

8
 
*/
)
//不太清楚,自动挂载逻辑驱动器?
static
FRESULT validate (        /* FR_OK(0): The object is valid, !=0: Not valid */
    const FATFS *fs,    /* Pointer to the file system object */
    WORD id                /* id member of the target object to be checked */
)
//检查文件系统是否合法。
FRESULT f_mount (
    BYTE drv,        /* Logical drive number to be mounted/unmounted */
    FATFS *fs        /* Pointer to new file system object (NULL for unmount)*/
)
//说这是一个很重要的函数,装载文件系统。也是从这个函数开始,对外输出供用户调用。
if (vol >= _DRIVES)现在只支持卷号0.
FatFs[vol] = fs;将参数文件系统对象指针赋给全局文件对象指针。

FRESULT f_open ()
//打开或创建一个文件。
FRESULT f_read ()
//读一个文件。
FRESULT f_write ()
//写文件
FRESULT f_sync ()
//将文件和磁盘之间进行同步。
FRESULT f_close ()
//关闭一个文件。
FRESULT f_lseek ()
//寻找文件读写指针。
FRESULT f_opendir ()
//创建一个目录对象
FRESULT f_readdir ()
//读一个目录
FRESULT f_stat ()
//获取文件状态
FRESULT f_getfree ()
//获取空闲簇的数目
FRESULT f_unlink ()
//删除一个文件或目录
FRESULT f_mkdir ()
//创建一个目录
FRESULT f_chmod ()
//改变文件属性
FRESULT f_rename ()
//重命名文件或目录
FRESULT f_mkfs ()
//在驱动上创建文件系统
ff.c文件就先到这儿了,大概了解了一下函数的功能。
 
 
 

回复

918

帖子

0

TA的资源

纯净的硅(中级)

9
 

回复 8楼 xielijuan 的帖子

楼主能不能写个简单的入门例程。比如通过超级终端把一段数据保存到SD卡中,然后再同过超级终端读出来。类似于RL中的那个例程,只不过这里用FatFs来实现它。

[ 本帖最后由 academic 于 2010-11-22 14:25 编辑 ]
 
 
 

回复

84

帖子

0

TA的资源

一粒金砂(中级)

10
 

回复 9楼 academic 的帖子

要读写SD卡就一定要有文件系统吧,我也是在研究中,先了解下文件系统。正在研究SD卡的读写实现。8962也是通过超级终端将数据传到UART口,再写入到SD卡的,学习中,共同探讨哦~~
 
 
 

回复

1万

帖子

16

TA的资源

版主

11
 

学习了

 
个人签名http://shop34182318.taobao.com/
https://shop436095304.taobao.com/?spm=a230r.7195193.1997079397.37.69fe60dfT705yr
 
 

回复

545

帖子

0

TA的资源

一粒金砂(高级)

12
 
挺详细的,不错
 
 
 

回复

2641

帖子

0

TA的资源

五彩晶圆(中级)

13
 
谢谢分享,最近忙死了,一直想写点什么都没空
 
 
 

回复

996

帖子

0

TA的资源

一粒金砂(高级)

14
 
那么多楼啊,还得仔细看看~!
 
 
 

回复

3

帖子

0

TA的资源

一粒金砂(初级)

15
 
好详细啊,顶!
 
 
 

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

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表