sd卡 secure digital memory card
nand flash 插入,清除,读写都要以块为单位。
nor flah 只要擦除时以块为单位,读取,写入可以以字节为单位。
sd卡知识
sd存储卡分为sd卡,sd hc卡,sdxc卡。
SDIO protocol
SDIO bus
SDIO的总线类型分为clock,cmd,data总线,其中data总线有1条或者4条。
时序逻辑是clock为上升沿时,cmd,data总线上的数据才有效,因此SDIO protocol 时同步通讯协议。
通信方式:host(主机)通过cmd总线向device发送命令,若device响应,则会执行该命令;若device不响应,则该命令无效。通过data block or data stream的方式在总线上传输数据,其中data block需要增加crc校验验证数据的正确与否。
SDIO 传输1
SDIO 传输2
sd卡是存储器,存储器无论是清除,读取,创建数据都需要一定的时间,因此sd卡采用SDIO协议进行通信时会将D0总线拉低表示数据忙。
fat文件系统(file allocation table)
文件系统是使用存储器的抽象,提供了在存储器中创建文件,读取文件,删除文件,了解存储剩余容量等操作的接口。
相关定义
sectors:扇区,读写存储器的最小抽象存储单位,一般为512 byte,可以更大。
cluster(block): n个sectors组成了cluster(block)。在fat文件系统中,sectors组成存储单位叫做cluster,linux的文件系统叫做block。
fat文件系统的组成
fat文件系统由4个部分组成,分别是boot sector,fat region,root directory region, data region。
boot sector
boot sector引导扇区由Jump Instruction,OEM ID,BPB,end of sector marker组成。
OEM ID的全程是original equipment manufacturer identifier,用来记录系统设备制造商或是该volume创作者的信息。
BPB全程是BIOS parameter block。可能记录了总扇区数目,每个扇区拥有的字节数,每个簇有多少个扇区,保留扇区数,根目录项数,存储介质类型。
file allocation table
file allocation table指明了数据区每个簇分配的地址,每个簇之间的关系和每个簇的情况。
记录一个一个文件的分配情况,fat是映射到每个簇的条目列表,应该记录
- 链中下一个簇的地址
- 一个特殊的簇链结束符(EOC,End Of Cluster-chain,或称End Of Chain)符号指示链的结束
- 一个特殊的符号标示坏簇
- 一个特殊的符号标示保留簇
- 0来表示空闲簇
root directory
是一个表示目录的特殊类型文件(现今通常称为文件夹)。它里面保存的每个文件或目录使用表中的32字节条目表示。每个条目记录名字、扩展名、属性(文件、目录、隐藏、只读、系统和卷)、创建的日期和时间、文件/目录数据第一个簇的地址,最后是文件/目录的大小。
data region
由一些cluster组成
fat文件系统的调用
小项目
需求:
在240*320的lcd上一张名为bibi的图片。图片存储在sd卡上。
实际效果
核心代码
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2007 */
/*-----------------------------------------------------------------------*/
/* This is a stub disk I/O module that acts as front end of the existing */
/* disk I/O modules and attach it to FatFs module with common interface. */
/*-----------------------------------------------------------------------*/
#include "diskio.h"
#include "bsp_sdio_sdcard.h"
/*-----------------------------------------------------------------------*/
/* Correspondence between physical drive number and physical drive. */
#define ATA 0
#define MMC 1
#define USB 2
#define SD_BLOCKSIZE 512
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive */
DSTATUS disk_initialize (
BYTE drv /* Physical drive nmuber (0..) */
)
{
SD_Error SD_Staus ;
DSTATUS DEVICE_Staus = STA_NOINIT;
if ( 0 != drv)
{
return DEVICE_Staus;
}
SD_Staus = SD_Init();
if ( SD_OK == SD_Staus)
{
DEVICE_Staus &= ~STA_NOINIT;
}
return DEVICE_Staus;
}
/*-----------------------------------------------------------------------*/
/* Return Disk Status */
DSTATUS disk_status (
BYTE drv /* Physical drive nmuber (0..) */
)
{
DSTATUS DEVICE_Staus = STA_NOINIT;
if ( 0 != drv)
{
return DEVICE_Staus;
}
else
{
DEVICE_Staus &= ~STA_NOINIT;
}
return DEVICE_Staus;
}
/*-----------------------------------------------------------------------*/
/* Read Sector(s) */
DRESULT disk_read (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE *buff, /* Data buffer to store read data */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to read (1..255) */
)
{
SD_Error SD_Staus = 0;
if (1 == count)
{
SD_Staus = SD_ReadBlock(buff, sector*SD_BLOCKSIZE, SD_BLOCKSIZE);//一个扇区为512字节,乘512字节为实际地址
}
else
{
SD_Staus = SD_ReadMultiBlocks(buff,sector*SD_BLOCKSIZE,SD_BLOCKSIZE, count);
}
SD_WaitReadOperation();
while(SD_GetStatus() == SD_TRANSFER_BUSY);
if (SD_OK == SD_Staus)
{
return RES_OK;
}
else
{
return RES_ERROR;
}
}
/*-----------------------------------------------------------------------*/
/* Write Sector(s) */
#if _READONLY == 0
DRESULT disk_write (
BYTE drv, /* Physical drive nmuber (0..) */
const BYTE *buff, /* Data to be written */
DWORD sector, /* Sector address (LBA) */
BYTE count /* Number of sectors to write (1..255) */
)
{
SD_Error SD_Staus = 0;
if (count == 1)
{
SD_Staus = SD_WriteBlock((uint8_t*)buff,sector*SD_BLOCKSIZE, SD_BLOCKSIZE);
}
else
{
SD_Staus = SD_WriteMultiBlocks((uint8_t*)buff,sector*SD_BLOCKSIZE, SD_BLOCKSIZE,count);
}
SD_WaitReadOperation();
while(SD_GetStatus() == SD_TRANSFER_BUSY);
if (SD_OK == SD_Staus)
{
return RES_OK;
}
else
{
return RES_ERROR;
}
}
#endif /* _READONLY */
/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions */
DRESULT disk_ioctl (
BYTE drv, /* Physical drive nmuber (0..) */
BYTE ctrl, /* Control code */
void *buff /* Buffer to send/receive control data */
)
{
DRESULT res;
int result;
return RES_PARERR;
}
__weak DWORD get_fattime(void) {
/* 返回当前时间戳 */
return ((DWORD)(2015 - 1980) << 25) /* Year 2015 */
| ((DWORD)1 << 21) /* Month 1 */
| ((DWORD)1 << 16) /* Mday 1 */
| ((DWORD)0 << 11) /* Hour 0 */
| ((DWORD)0 << 5) /* Min 0 */
| ((DWORD)0 >> 1); /* Sec 0 */
}
#include "bmp.h"
uint8_t BMP_BGRA[4*LCD_Width];
uint16_t BMP_RGB565[LCD_Width];
uint8_t BMP_Head[54];
uint16_t LCD_Display_BMP(char* PIC_Name)
{
DWORD Offset = 0;
BMP BMPS;
int ROW_Offset;
int COLUMN_Offset;
int OFFSET_Flag;
UINT NUMBER_Read;
WORD DATA_Offset;
FIL BMP_File;
FRESULT FILE_Staus;
int LCD_x;
int LCD_y;
int i;
int j;
FILE_Staus = f_open(&BMP_File,PIC_Name,FA_READ|FA_OPEN_EXISTING);
if (FILE_Staus != FR_OK)//不存在该文件或者读取失败
{
return BMP_Errors;
}
f_read(&BMP_File,&BMP_Head,54,&NUMBER_Read);
BMPS.DATA_Offset = (BMP_Head[13]<<24)+(BMP_Head[12]<<16)+(BMP_Head[11]<<8)+(BMP_Head[10]<<0);
BMPS.BMP_Depth = (BMP_Head[29] << 8) + (BMP_Head[28] << 0);
BMPS.BMP_Width = (BMP_Head[21]<<24)+(BMP_Head[20]<<16)+(BMP_Head[19]<<8)+(BMP_Head[18]<<0);
BMPS.BMP_Height = (BMP_Head[25]<<24)+(BMP_Head[24]<<16)+(BMP_Head[23]<<8)+(BMP_Head[22]<<0);
//判断是否满足该函数的读取格式
//数据存储方式必须是从左上角到右上角,必须是bmo文件
if ( BMPS.BMP_Height < 0)//数据存储方向不是从左上角到右上角,32位深度,无压缩,无颜色表
{
return BMP_Errors;
}
if ( BMPS.BMP_Depth != 32)//没有A
{
return BMP_Errors;
}
if ((BMP_Head[0] != 0x4) || (BMP_Head[1] != 0x2) || (BMP_Head[2] != 0x4) || (BMP_Head[3] != 0xd))//不是bmp文件
{
BMPS.IS_BMP = 1;
}
if ((BMP_Head[33] + BMP_Head[32] + BMP_Head[31] + BMP_Head[30]) != 0)//bmp文件有压缩
{
BMPS.IS_Compression = 1;
}
if ((BMPS.DATA_Offset) != 54)
{
BMPS.IS_CorlorTable = 1;
}
if ((BMPS.IS_CorlorTable*BMPS.IS_Compression*BMPS.IS_BMP) >= 1)
{
return BMP_Errors;
}
Offset = Offset + 54;
f_lseek(&BMP_File,Offset);
//图像缩放处理
ROW_Offset = (int)(BMPS.BMP_Width/LCD_Width);//某一行隔几个像素点作为像素点
COLUMN_Offset = (int)(BMPS.BMP_Height/LCD_Height);//隔几列
if (COLUMN_Offset == 0)
{
COLUMN_Offset = 1;
}
if (ROW_Offset == 0)
{
ROW_Offset = 1;
}
//lcd扫描方向
ILI9341_GramScan(2);
for(i = 0; i < LCD_Height;i = i + 1)
{
LCD_y = i;
for(j = 0; j < LCD_Width; j = j + 1)
{
f_read(&BMP_File,&BMP_BGRA[4*j],1,&NUMBER_Read);
Offset = Offset + 1;
f_lseek(&BMP_File,Offset);
f_read(&BMP_File,&BMP_BGRA[4*j+1],1,&NUMBER_Read);
Offset = Offset + 1;
f_lseek(&BMP_File,Offset);
f_read(&BMP_File,&BMP_BGRA[4*j+2],1,&NUMBER_Read);
Offset = Offset + (ROW_Offset-1)*4 + 2;
BGRA_to_RGB565(&BMP_BGRA[4*j], &BMP_BGRA[4*j+1], &BMP_BGRA[4*j+2], &BMP_RGB565[j]);
}
Offset = Offset + BMPS.BMP_Width*4 - 4*(ROW_Offset)*LCD_Width+ (COLUMN_Offset-1)*LCD_Width*4;
f_lseek(&BMP_File,Offset);//跳过一些行,这些行不取像素点
for(j = 0; j < LCD_Width; j = j + 1)
{
LCD_x = j;
LCD_SetTextColor(BMP_RGB565[j]);
ILI9341_SetPointPixel(LCD_x,LCD_y);
}
}
f_close(&BMP_File);
}
void BGRA_to_RGB565(uint8_t* B,uint8_t*G, uint8_t* R, uint16_t* RGB565)
{
*RGB565 = (((uint16_t)(*R))>>3)<<11 | (((uint16_t)(*B))>>3) | (((uint16_t)(*G))>>2 ) << 5;
}
#ifndef __BMP_H
#define __BMP_H
#include "stm32f10x.h"
#include "ff.h"
#include "bsp_ili9341_lcd.h"
#define LCD_Width 240
#define LCD_Height 320
#define BMP_Errors 0
#define BMP_Success (!0)
typedef struct _BMP
{
uint16_t IS_BMP;
uint16_t BMP_Depth;
uint32_t IS_Compression;
uint32_t IS_CorlorTable;
uint32_t DATA_Offset;
uint32_t BMP_Width;
int32_t BMP_Height;
}BMP;
uint16_t LCD_Display_BMP(char* PIC_Name);
void BGRA_to_RGB565(uint8_t* B,uint8_t*G, uint8_t* R, uint16_t* RGB565);
#endif
总结与评价
fat文件系统调用的逻辑可以参考野火ppt,ff.c文件调用diskio.c文件,diskio文件调用bsp_scdcard.c文件。
该工程不能读取像素很大的图片,如1600*800,否则会造成失真。本项目采用的是十分简单的图片缩放算法,隔几个点读一个点。
图片缩放算法会影响lcd上显示图片的质量
参考资料
SDIO协议
SDIO Protocol | Prodigy Technovations
Wiki - Secure digital input/output interface (SDIO)
野火ppt
fat系统
What is File Allocation Table (FAT)?
https://en.wikipedia.org/wiki/Design_of_the_FAT_file_system#Special_entries