第四十四章  FATFS实验

 

[mw_shl_code=c,true]1.硬件平台:正点原子探索者STM32F407开发板 2.软件平台:MDK5.1 3.固件库版本:V1.4.0 [/mw_shl_code]

    上一章,我们学习了SD卡的使用,不过仅仅是简单的实现读扇区而已,真正要好好应用SD卡,必须使用文件系统管理,本章,我们将使用FATFS来管理SD卡,实现SD卡文件的读写等基本功能。本章分为如下几个部分:

44.1 FATFS简介

44.2 硬件设计

44.3 软件设计

44.4 下载验证

44.1 FATFS简介

FATFS是一个完全免费开源的FAT 文件系统模块,专门为小型的嵌入式系统而设计。它完全用标准C 语言编写,所以具有良好的硬件平**立性,可以移植到8051、PIC、AVR、SH、Z80、H8、ARM 等系列单片机上而只需做简单的修改。它支持FATl2、FATl6 和FAT32,支持多个存储媒介;有独立的缓冲区,可以对多个文件进行读/写,并特别对8 位单片机和16 位单片机做了优化。

FATFS的特点有:

l  Windows兼容的FAT文件系统(支持FAT12/FAT16/FAT32)

l  与平台无关,移植简单

l  代码量少、效率高

l  多种配置选项

?  支持多卷(物理驱动器或分区,最多10个卷)

?  多个ANSI/OEM代码页包括DBCS

?  支持长文件名、ANSI/OEM或Unicode

?  支持RTOS

?  支持多种扇区大小

?  只读、最小化的API和I/O缓冲区等

FATFS的这些特点,加上免费、开源的原则,使得FATFS应用非常广泛。FATFS模块的层次结构如图44.1.1所示:

图44.1.1 FATFS层次结构图

最顶层是应用层,使用者无需理会FATFS的内部结构和复杂的FAT 协议,只需要调用FATFS模块提供给用户的一系列应用接口函数,如f_open,f_read,f_write 和f_close等,就可以像在PC 上读/写文件那样简单。

中间层FATFS模块,实现了FAT 文件读/写协议。FATFS模块提供的是ff.c和ff.h。除非有必要,使用者一般不用修改,使用时将头文件直接包含进去即可。

需要我们编写移植代码的是FATFS模块提供的底层接口,它包括存储媒介读/写接口(disk I/O)和供给文件创建修改时间的实时时钟。

FATFS的源码,大家可以在:http://elm-chan.org/fsw/ff/00index_e.html 这个网站下载到,目前最新版本为R0.10b。本章我们就使用最新版本的FATFS作为介绍,下载最新版本的FATFS软件包,解压后可以得到两个文件夹:doc和src。doc里面主要是对FATFS的介绍,而src里面才是我们需要的源码。

其中,与平台无关的是:

ffconf.h                 FATFS模块配置文件

ff.h                       FATFS和应用模块公用的包含文件

ff.c                       FATFS模块

diskio.h                 FATFS和disk I/O模块公用的包含文件

interger.h               数据类型定义

option                   可选的外部功能(比如支持中文等)

与平台相关的代码(需要用户提供)是:

diskio.c                 FATFS和disk I/O模块接口层文件

FATFS模块在移植的时候,我们一般只需要修改2个文件,即ffconf.h和diskio.c。FATFS模块的所有配置项都是存放在ffconf.h里面,我们可以通过配置里面的一些选项,来满足自己的需求。接下来我们介绍几个重要的配置选项。

1)_FS_TINY。这个选项在R0.07版本中开始出现,之前的版本都是以独立的C文件出现(FATFS和Tiny FATFS),有了这个选项之后,两者整合在一起了,使用起来更方便。我们使用FATFS,所以把这个选项定义为0即可。

2)_FS_READONLY。这个用来配置是不是只读,本章我们需要读写都用,所以这里设置为0即可。

3)_USE_STRFUNC。这个用来设置是否支持字符串类操作,比如f_putc,f_puts等,本章我们需要用到,故设置这里为1。

4)_USE_MKFS。这个用来定时是否使能格式化,本章需要用到,所以设置这里为1。

5)_USE_FASTSEEK。这个用来使能快速定位,我们设置为1,使能快速定位。

6)_USE_LABEL。这个用来设置是否支持磁盘盘符(磁盘名字)读取与设置。我们设置为1,使能,就可以通过相关函数读取或者设置磁盘的名字了。

7)_CODE_PAGE。这个用于设置语言类型,包括很多选项(见FATFS官网说明),我们这里设置为936,即简体中文(GBK码,需要c936.c文件支持,该文件在option文件夹)。

8)_USE_LFN。该选项用于设置是否支持长文件名(还需要_CODE_PAGE支持),取值范围为0~3。0,表示不支持长文件名,1~3是支持长文件名,但是存储地方不一样,我们选择使用3,通过ff_memalloc函数来动态分配长文件名的存储区域。

9)_VOLUMES。用于设置FATFS支持的逻辑设备数目,我们设置为2,即支持2个设备。

10)_MAX_SS。扇区缓冲的最大值,一般设置为512。

其他配置项,我们这里就不一一介绍了,FATFS的说明文档里面有很详细的介绍,大家自己阅读即可。下面我们来讲讲FATFS的移植,FATFS的移植主要分为3步:

①     数据类型:在integer.h 里面去定义好数据的类型。这里需要了解你用的编译器的数

据类型,并根据编译器定义好数据类型。

②     配置:通过ffconf.h配置FATFS的相关功能,以满足你的需要。

③     函数编写:打开diskio.c,进行底层驱动编写,一般需要编写6 个接口函数,如

图44.1.2 所示:

图44.1.2 diskio 需要实现的函数

通过以上三步,我们即可完成对FATFS的移植。

第一步,我们使用的是MDK5.11a编译器,器数据类型和integer.h里面定义的一致,所以此步,我们不需要做任何改动。

第二步,关于ffconf.h里面的相关配置,我们在前面已经有介绍(之前介绍的10个配置),我们将对应配置修改为我们介绍时候的值即可,其他的配置用默认配置。

第三步,因为FATFS模块完全与磁盘I/O 层分开,因此需要下面的函数来实现底层物理磁盘的读写与获取当前时间。底层磁盘I/O 模块并不是FATFS的一部分,并且必须由用户提供。这些函数一般有6个,在diskio.c里面。

首先是disk_initialize函数,该函数介绍如图44.1.3所示:

图44.1.3 disk_initialize函数介绍

第二个函数是disk_status函数,该函数介绍如图44.1.4所示:

图44.1.4 disk_status函数介绍

第三个函数是disk_read函数,该函数介绍如图44.1.5所示:

图44.1.5 disk_read函数介绍

第四个函数是disk_write函数,该函数介绍如图44.1.6所示:

图44.1.6 disk_write函数介绍

第五个函数是disk_ioctl函数,该函数介绍如图44.1.7所示:

图44.1.7 disk_ioctl函数介绍

最后一个函数是get_fattime函数,该函数介绍如图44.1.8所示:

图44.1.8 get_fattime函数介绍

以上六个函数,我们将在软件设计部分一一实现。通过以上3个步骤,我们就完成了对FATFS的移植,就可以在我们的代码里面使用FATFS了。

FATFS提供了很多API函数,这些函数FATFS的自带介绍文件里面都有详细的介绍(包括参考代码),我们这里就不多说了。这里需要注意的是,在使用FATFS的时候,必须先通过f_mount函数注册一个工作区,才能开始后续API的使用,关于FATFS的介绍,我们就介绍到这里。大家可以通过FATFS自带的介绍文件进一步了解和熟悉FATFS的使用。

44.2 硬件设计

本章实验功能简介:开机的时候先初始化SD卡,初始化成功之后,注册两个工作区(一个给SD卡用,一个给SPI FLASH用),然后获取SD卡的容量和剩余空间,并显示在LCD模块上,最后等待USMART输入指令进行各项测试。本实验通过DS0指示程序运行状态。

本实验用到的硬件资源有:

1)  指示灯DS0

2)  串口

3)  TFTLCD模块

4)  SD卡

5)  SPI FLASH

这些,我们在之前都已经介绍过,如有不清楚,请参考之前内容。

44.3 软件设计

打开本章实验目录可以看到,我们在工程目录下新建了一个FATFS的文件夹,然后将FATFS R0.10b程序包解压到该文件夹下。同时,我们在FATFS文件夹里面新建了一个exfuns的文件夹,用于存放我们针对FATFS做的一些扩展代码。设计完如图44.3.1所示:

图44.3.1 FATFS文件夹子目录

然后打开我们实验工程可以看到,我们新建了FATFS分组,将必要的源文件添加到了FATFS分组之下。打开diskio.c,代码如下:

#define SD_CARD 0  //SD卡,卷标为0

#define EX_FLASH 1   //外部flash,卷标为1

#define FLASH_SECTOR_SIZE 512

//对于W25Q128

//前12M字节给fatfs用,12M字节后,用于存放字库,字库占用3.09M.   剩余部分,

//给客户自己用

u16  FLASH_SECTOR_COUNT=2048*12;       //W25Q1218,前12M字节给FATFS占用

#define FLASH_BLOCK_SIZE         8         //每个BLOCK有8个扇区

//初始化磁盘

DSTATUS disk_initialize (

BYTE pdrv                          /* Physical drive nmuber (0..) */

)

{

u8 res=0;

switch(pdrv)

{

case SD_CARD://SD卡

res=SD_Init();//SD卡初始化

break;

case EX_FLASH://外部flash

W25QXX_Init();

FLASH_SECTOR_COUNT=2048*12;//W25Q1218,前12M字节给FATFS占用

break;

default:

res=1;

}

if(res)return  STA_NOINIT;

else return 0; //初始化成功

}

//获得磁盘状态

DSTATUS disk_status (

BYTE pdrv            /* Physical drive nmuber (0..) */

)

{

return 0;

}

//读扇区

//drv:磁盘编号0~9

//*buff:数据接收缓冲首地址

//sector:扇区地址

//count:需要读取的扇区数

DRESULT disk_read (

BYTE pdrv,           /* Physical drive nmuber (0..) */

BYTE *buff,         /* Data buffer to store read data */

DWORD sector,     /* Sector address (LBA) */

UINT count           /* Number of sectors to read (1..128) */

)

{

u8 res=0;

if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误

switch(pdrv)

{

case SD_CARD://SD卡

res=SD_ReadDisk(buff,sector,count);

break;

case EX_FLASH://外部flash

for(;count>0;count--)

{

W25QXX_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_SI

ZE);

sector++;

buff+=FLASH_SECTOR_SIZE;

}

res=0;

break;

default:

res=1;

}

//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值

if(res==0x00)return RES_OK;

else return RES_ERROR;

}

//写扇区

//drv:磁盘编号0~9

//*buff:发送数据首地址

//sector:扇区地址

//count:需要写入的扇区数

#if _USE_WRITE

DRESULT disk_write (

BYTE pdrv,                  /* Physical drive nmuber (0..) */

const BYTE *buff, /* Data to be written */

DWORD sector,            /* Sector address (LBA) */

UINT count                  /* Number of sectors to write (1..128) */

)

{

u8 res=0;

if (!count)return RES_PARERR;//count不能等于0,否则返回参数错误

switch(pdrv)

{

case SD_CARD://SD卡

res=SD_WriteDisk((u8*)buff,sector,count);

break;

case EX_FLASH://外部flash

for(;count>0;count--)

{

W25QXX_Write((u8*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECT

OR_SIZE);

sector++;

buff+=FLASH_SECTOR_SIZE;

}

res=0;

break;

default:

res=1;

}

//处理返回值,将SPI_SD_driver.c的返回值转成ff.c的返回值

if(res == 0x00)return RES_OK;

else return RES_ERROR;

}

#endif

//其他表参数的获得

//drv:磁盘编号0~9

//ctrl:控制代码

//*buff:发送/接收缓冲区指针

#if _USE_IOCTL

DRESULT disk_ioctl (

BYTE pdrv,           /* Physical drive nmuber (0..) */

BYTE cmd,           /* Control code */

void *buff             /* Buffer to send/receive control data */

)

{

DRESULT res;

if(pdrv==SD_CARD)//SD卡

{

switch(cmd)

{

case CTRL_SYNC:

res = RES_OK;

break;

case GET_SECTOR_SIZE:

*(DWORD*)buff = 512;

res = RES_OK;

break;

case GET_BLOCK_SIZE:

*(WORD*)buff = SDCardInfo.CardBlockSize;

res = RES_OK;

break;

case GET_SECTOR_COUNT:

*(DWORD*)buff = SDCardInfo.CardCapacity/512;

res = RES_OK;

break;

default:

res = RES_PARERR;

break;

}

}else if(pdrv==EX_FLASH) //外部FLASH

{

switch(cmd)

{

case CTRL_SYNC:

res = RES_OK;

break;

case GET_SECTOR_SIZE:

*(WORD*)buff = FLASH_SECTOR_SIZE;

res = RES_OK;

break;

case GET_BLOCK_SIZE:

*(WORD*)buff = FLASH_BLOCK_SIZE;

res = RES_OK;

break;

case GET_SECTOR_COUNT:

*(DWORD*)buff = FLASH_SECTOR_COUNT;

res = RES_OK;

break;

default:

res = RES_PARERR;

break;

}

}else res=RES_ERROR;//其他的不支持

return res;

}

#endif

//获得时间

//User defined function to give a current time to fatfs module      */

//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) */

DWORD get_fattime (void)

{

return 0;

}

//动态分配内存

void *ff_memalloc (UINT size)

{

return (void*)mymalloc(SRAMIN,size);

}

//释放内存

void ff_memfree (void* mf)

{

myfree(SRAMIN,mf);

}

该函数实现了我们44.1节提到的6个函数,同时因为在ffconf.h里面设置对长文件名的支持为方法3,所以必须实现ff_memalloc和ff_memfree这两个函数。本章,我们用FATFS管理了2个磁盘:SD卡和SPI FLASH。SD卡比较好说,但是SPI FLASH,因为其扇区是4K字节大小,我们为了方便设计,强制将其扇区定义为512字节,这样带来的好处就是设计使用相对简单,坏处就是擦除次数大增,所以不要随便往SPI FLASH里面写数据,非必要最好别写,如果频繁写的话,很容易将SPI FLASH写坏。

打开ffconf.h可以看到,我们根据前面讲解修改了相关配置,此部分就不贴代码了,请大家参考本例程源码。另外,cc936.c主要提供UNICODE到GBK以及GBK到UNICODE的码表转换,里面就是两个大数组,并提供一个ff_convert的转换函数,供UNICODE和GBK码互换,这个在中文长文件名支持的时候,必须用到!

前面提到,我们在FATFS文件夹下还新建了一个exfuns的文件夹,该文件夹用于保存一些FATFS一些针对FATFS的扩展代码,本章,我们编写了4个文件,分别是:exfuns.c、exfuns.h、fattester.c和fattester.h。其中exfuns.c主要定义了一些全局变量,方便FATFS的使用,同时实现了磁盘容量获取等函数。而fattester.c文件则主要是为了测试FATFS用,因为FATFS的很多函数无法直接通过USMART调用,所以我们在fattester.c里面对这些函数进行了一次再封装,使得可以通过USMART调用。这几个文件的代码,我们就不贴出来了,请大家参考本例程源码,

最后,我们打开main.c, main函数如下:

int main(void)

{

u32 total,free;

u8 t=0; u8 res=0;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2

delay_init(168);  //初始化延时函数

uart_init(115200);         //初始化串口波特率为115200

LED_Init();                                //初始化LED

usmart_dev.init(84);             //初始化USMART

LCD_Init();                                //LCD初始化

KEY_Init();                                //按键初始化

W25QXX_Init();                         //初始化W25Q128

my_mem_init(SRAMIN);            //初始化内部内存池

my_mem_init(SRAMCCM);         //初始化CCM内存池

POINT_COLOR=RED;//设置字体为红色

LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");

LCD_ShowString(30,70,200,16,16,"FATFS TEST");

LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");

LCD_ShowString(30,110,200,16,16,"2014/5/15");

LCD_ShowString(30,130,200,16,16,"Use USMART for test");

while(SD_Init())//检测不到SD卡

{

LCD_ShowString(30,150,200,16,16,"SD Card Error!"); delay_ms(500);

LCD_ShowString(30,150,200,16,16,"Please Check! "); delay_ms(500);

LED0=!LED0;//DS0闪烁

}

exfuns_init();                                     //为fatfs相关变量申请内存

f_mount(fs[0],"0:",1);                        //挂载SD卡

res=f_mount(fs[1],"1:",1);                  //挂载FLASH.

if(res==0X0D)//FLASH磁盘,FAT文件系统错误,重新格式化FLASH

{

LCD_ShowString(30,150,200,16,16,"Flash Disk Formatting...");      //格式化FLASH

res=f_mkfs("1:",1,4096);//格式化FLASH,1,盘符;1,不需要引导区,8个扇区为1个簇

if(res==0)

{

f_setlabel((const TCHAR *)"1:ALIENTEK");//设置磁盘的名字为:ALIENTEK

LCD_ShowString(30,150,200,16,16,"Flash Disk Format Finish");//格式化完成

}else LCD_ShowString(30,150,200,16,16,"Flash Disk Format Error ");//格式化失败

delay_ms(1000);

}

LCD_Fill(30,150,240,150+16,WHITE);      //清除显示

while(exf_getfree("0",&total,&free))          //得到SD卡的总容量和剩余容量

{

LCD_ShowString(30,150,200,16,16,"SD Card Fatfs Error!"); delay_ms(200);

LCD_Fill(30,150,240,150+16,WHITE); delay_ms(200);    //清除显示

LED0=!LED0;//DS0闪烁

}

POINT_COLOR=BLUE;//设置字体为蓝色

LCD_ShowString(30,150,200,16,16,"FATFS OK!");

LCD_ShowString(30,170,200,16,16,"SD Total Size:     MB");

LCD_ShowString(30,190,200,16,16,"SD  Free Size:     MB");

LCD_ShowNum(30+8*14,170,total>>10,5,16);         //显示SD卡总容量 MB

LCD_ShowNum(30+8*14,190,free>>10,5,16);          //显示SD卡剩余容量 MB

while(1)

{

t++;

delay_ms(200);

LED0=!LED0;

}

}

在main函数里面,我们为SD卡和FLASH都注册了工作区(挂载),在初始化SD卡并显示其容量信息后,进入死循环,等待USMART测试。

最后,我们在usmart_config.c里面的usmart_nametab数组添加如下内容:

(void*)mf_mount,"u8 mf_mount(u8* path,u8 mt)",

(void*)mf_open,"u8 mf_open(u8*path,u8 mode)",

(void*)mf_close,"u8 mf_close(void)",

(void*)mf_read,"u8 mf_read(u16 len)",

(void*)mf_write,"u8 mf_write(u8*dat,u16 len)",

(void*)mf_opendir,"u8 mf_opendir(u8* path)",

(void*)mf_closedir,"u8 mf_closedir(void)",

(void*)mf_readdir,"u8 mf_readdir(void)",

(void*)mf_scan_files,"u8 mf_scan_files(u8 * path)",

(void*)mf_showfree,"u32 mf_showfree(u8 *drv)",

(void*)mf_lseek,"u8 mf_lseek(u32 offset)",

(void*)mf_tell,"u32 mf_tell(void)",

(void*)mf_size,"u32 mf_size(void)",

(void*)mf_mkdir,"u8 mf_mkdir(u8*pname)",

(void*)mf_fmkfs,"u8 mf_fmkfs(u8* path,u8 mode,u16 au)",

(void*)mf_unlink,"u8 mf_unlink(u8 *pname)",

(void*)mf_rename,"u8 mf_rename(u8 *oldname,u8* newname)",

(void*)mf_getlabel,"void mf_getlabel(u8 *path)",

(void*)mf_setlabel,"void mf_setlabel(u8 *path)",

(void*)mf_gets,"void mf_gets(u16 size)",

(void*)mf_putc,"u8 mf_putc(u8 c)",

(void*)mf_puts,"u8 mf_puts(u8*c)",

这些函数均是在fattester.c里面实现,通过调用这些函数,即可实现对FATFS对应API函数的测试。 至此,软件设计部分就结束了。

44.4 下载验证

在代码编译成功之后,我们通过下载代码到ALIENTEK探索者STM32F4开发板上,可以看到LCD显示如图44.4.1所示的内容(假定SD卡已经插上了):

图44.4.1 程序运行效果图

打开串口调试助手,我们就可以串口调用前面添加的各种FATFS测试函数了,比如我们输入mf_scan_files("0:"),即可扫描SD卡根目录的所有文件,如图44.4.2所示:

图44.4.2 扫描SD卡根目录所有文件

其他函数的测试,用类似的办法即可实现。注意这里0代表SD卡,1代表SPI FLASH。另外,提醒大家,mf_unlink函数,在删除文件夹的时候,必须保证文件夹是空的,才可以正常删除,否则不能删除。

实验详细手册和源码下载地址:http://www.openedv.com/posts/list/41586.htm

正点原子探索者STM32F407开发板购买地址:http://item.taobao.com/item.htm?id=41855882779

  

实验39 FATFS实验.zip

第四十四章 FATFS实验-STM32F4开发指南-正点原子探索者STM32开发板.pDF

http://www.openedv.com/forum.php?mod=viewthread&tid=43377

【正点原子探索者STM32F407开发板例程连载+教学】第44章 FATFS文件系统实验相关推荐

  1. 【正点原子探索者STM32F407开发板例程连载+教学】第43章 sd卡实验-SDIO

    第四十三章  SD卡实验         1.硬件平台:正点原子探索者STM32F407开发板  2.软件平台:MDK5.1  3.固件库版本:V1.4.0  很多单片机系统都需要大容量存储设备,以存 ...

  2. stm32l0的停止模式怎么唤醒_探索者 STM32F407 开发板资料连载第二十二章 待机唤醒实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板 2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第二十二章 待机唤醒实 ...

  3. 看门狗寄存器c语言代码_「正点原子NANO STM32F103开发板资料连载」第十一章 看门狗实验...

    1)实验平台:[正点原子] NANO STM32F103 开发板 2)摘自<正点原子STM32 F1 开发指南(NANO 板-HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 ...

  4. 韦东山 IMX6ULL和正点原子_「正点原子NANO STM32开发板资料连载」第三章 MDK5 软件入门1...

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板 2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第三章 M ...

  5. 异步fifo_正点原子开拓者FPGA开发板资料连载第十五章 IP核之FIFO实验

    1)实验平台:正点原子开拓者FPGA 开发板 2)摘自<开拓者FPGA开发指南>关注官方微信号公众号,获取更多资料:正点原子 3)全套实验源码+手册+视频下载地址:http://www.o ...

  6. 485串口测试工具软件_探索者 STM32F407 开发板资料连载第三十一章 485 实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第三十一章 485 实验 ...

  7. stm32看门狗_「正点原子NANO STM32开发板资料连载」第十一章 独立看门狗实验

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十一章 独 ...

  8. 嵌入式实时操作系统ucos-ii_「正点原子NANO STM32开发板资料连载」第三十八章 UCOSII 实验 3...

    1)实验平台:alientek NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第三十八章 ...

  9. 基于原子探索者stm32f407开发板的ucos-iii+lwip1.4.1的tcp server并发服务器完美解决例程(转)...

    源:基于原子探索者stm32f407开发板的ucos-iii+lwip1.4.1的tcp server并发服务器完美解决例程 转载于:https://www.cnblogs.com/LittleTig ...

  10. 正点原子探索者STM32F407ZGT6开发板资料:

    3)正点原子探索者STM32F407ZGT6开发板资料:      [百度网盘链接]:         A盘文件夹形式:https://pan.baidu.com/s/1jHWI8fG   A盘文件夹 ...

最新文章

  1. kafaka使用 消息队列_Java使用消息队列还是直接使用线程池ExecutorService异步处理?...
  2. 获取设置本地当前默认打印机
  3. osmnx 应用 可视化两张图异同的点和边
  4. onenote快捷键_onenote链接系列:4种链接笔记的地址
  5. Lucene实战之基于StandardAnalyzer读写索引
  6. web安全学习-验证机制存在的问题
  7. 马尔可夫链 (Markov Chain)是什么鬼
  8. Python找出某元素的索引下标
  9. 省选+NOI 第十部分 概率统计与多项式
  10. python中number函数_Python 数字(Number)
  11. iphone控制电脑_这可能是首款能在电脑上控制iPhone的工具
  12. java中四种操作(DOM、SAX、JDOM、DOM4J)xml方式详解与比较(转)
  13. 消暑圣品!这部宝马“鬼片”你看过没,第一个被无人驾驶车辆吓跑的女鬼?...
  14. 《深度学习》李宏毅 -- task1机器学习介绍
  15. 静态的通讯录(C语言)
  16. cocos2dx lua 升级spine兼容Spine库v3.8到最新版本
  17. Unity官方支持中文版啦!!!附教程——无需破解!
  18. 误入 GitHub 游戏区,意外地收获颇丰
  19. CSDN如何搜索自己的博客;使用Google搜索自己的博客
  20. 移动互联网—电子商务的下一个”战场

热门文章

  1. spring源码下载安装,导入idea以及编译报错问题详细解决过程
  2. NXP iMX8 SCFW和Boot Container Image编译
  3. python实现微信自动发信息软件_Python实现给微信好友自动发送消息的示例
  4. 【2021最新版】Linux面试题总结(48道题含答案解析)
  5. linux邮件客户端软件,适用于Linux的4款最佳电子邮件客户端 | MOS86
  6. 四川大学计算机绘图cad网站,计算机绘图: Auto CAD版
  7. 致初入职场的兄弟姐妹
  8. Robocode简介
  9. 最高响应比优先算法(HRRF)及例题详解
  10. 灰色系统预测matlab代码,灰色预测MATLAB代码