【STM32调试(三)】采集bmp图像保存在SD卡

将图像保存在SD卡

  • 一、思路
  • 二、移植文件系统
  • 三、保存图片
  • 四、实验结果

一、思路

这里保存的是BMP图像,需要先连接bmp图像的数据格式。在STM32上采集的数据格式是RGB565方便在LCD上显示。如果直接发送还需要处理RGB565到RGB555的格式转换,以及bmp的数据头信息。

将bmp保存在sd卡上,这里移植FATFS文件系统。

图片保存的步骤:

  • 配置bmp的图片头信息,
  • 设置数据格式掩码,
  • 写入图像数据。

二、移植文件系统

我们使用FATFS文件系统来管理SD卡,

  1. FATFS文件系统

FATFS文件系统也就是一个软件,直接去官网下载最新版本即可。官网也有基本的介绍。下图是文件系统的结构:
【STM32调试(三)】采集bmp图像保存在SD卡_第1张图片

  • 应用层:

FATFS提供文件操作的API给应用层使用,完成自己的应用开发,不再需要考虑底层硬件的操作。这也是FATFS移植性好的原因。

  • 中间层:

就是FATFS文件系统连接底层和应用层的桥梁,调用底层驱动封装成文件操作的API给应用层使用。这部分不需要修改,只需要简单的修改配置文件即可。
修改共10处,修改如下(正点原子):具体看注释

ffconf.h 
//line13
#define	_FS_TINY		0	/* 0:Normal or 1:Tiny */
//line20
#define _FS_READONLY	0	/* 0:Read/Write or 1:Read only */
//line36
#define	_USE_STRFUNC	1	/* 0:Disable or 1-2:Enable */
//line40
#define	_USE_MKFS		1	/* 0:Disable or 1:Enable */
//line44
#define	_USE_FASTSEEK	1	/* 0:Disable or 1:Enable */
//line48
#define _USE_LABEL		1	/* 0:Disable or 1:Enable */
//line60
#define _CODE_PAGE	936		//采用中文GBK编码
//line92
#define	_USE_LFN	3		/* 0 to 3 设置为1,支持长文件名,并采用动态内存*/
//line135
#define _VOLUMES	2
//line155
#define	_MAX_SS		512
  • 底层:

底层是移植文件系统时需要我们自己根据自己的硬件平台进行编写diskio.c。我直接使用正点原子提供的代码,但是自己不写也要读懂理解啊。
这里有6个函数:

DSTATUS disk_initialize (BYTE pdrv);
DSTATUS disk_status (BYTE pdrv);
DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count);
DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count);
DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
//还有一个 get_fattime 函数,只读配置可以不用写
/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2013        */
/*-----------------------------------------------------------------------*/
/* If a working storage control module is available, it should be        */
/* attached to the FatFs via a glue function rather than modifying it.   */
/* This is an example of glue functions to attach various exsisting      */
/* storage control module to the FatFs module with a defined API.        */
/*-----------------------------------------------------------------------*/

#include "diskio.h"		/* FatFs lower layer API */
#include "mmc_sd.h"
#include "flash.h"
#include "malloc.h"		
//////////////////////////////////////////////////////////////////////////////////	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//FATFS disio.c 驱动代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2014/3/14
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved									  
////////////////////////////////////////////////////////////////////////////////// 

#define SD_CARD	 0  //SD卡,卷标为0
#define EX_FLASH 1	//外部flash,卷标为1

#define FLASH_SECTOR_SIZE 	512			  
//对于W25Q64 
//前4.8M字节给fatfs用,4.8M字节后~4.8M+100K给用户用,4.9M以后,用于存放字库,字库占用3.09M.		 			    
u16	    FLASH_SECTOR_COUNT= 9832;	//4.8M字节,默认为W25Q64
#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_Initialize();//SD_Initialize() 
		 	if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
			{
				SD_SPI_SpeedLow();
				SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
				SD_SPI_SpeedHigh();
			}
  			break;
		case EX_FLASH://外部flash
			SPI_Flash_Init();
			if(SPI_FLASH_TYPE==W25Q64)FLASH_SECTOR_COUNT=9832;	//W25Q64
			else FLASH_SECTOR_COUNT=0;							//其他
 			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);	 
		 	if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
			{
				SD_SPI_SpeedLow();
				SD_SPI_ReadWriteByte(0xff);//提供额外的8个时钟
				SD_SPI_SpeedHigh();
			}
			break;
		case EX_FLASH://外部flash
			for(;count>0;count--)
			{
				SPI_Flash_Read(buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_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;	   
}

//写扇区
//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--)
			{										    
				SPI_Flash_Write((u8*)buff,sector*FLASH_SECTOR_SIZE,FLASH_SECTOR_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:
				SD_CS=0;
		        if(SD_WaitReady()==0)res = RES_OK; 
		        else res = RES_ERROR;	  
				SD_CS=1;
		        break;	 
		    case GET_SECTOR_SIZE:
		        *(WORD*)buff = 512;
		        res = RES_OK;
		        break;	 
		    case GET_BLOCK_SIZE:
		        *(WORD*)buff = 8;
		        res = RES_OK;
		        break;	 
		    case GET_SECTOR_COUNT:
		        *(DWORD*)buff = SD_GetSectorCount();
		        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(size);
}
//释放内存
void ff_memfree (void* mf)		 
{
	myfree(mf);
}

  1. 移植

引用c文件和头文件即可

  1. 使用
mem_init(); //初始化内存池

exfuns_init(); //为 fatfs 相关变量申请内存
f_mount(fs[0],"0:",1); //挂载 SD 卡
f_mount(fs[1],"1:",1); //挂载 FLASH.
//使用接口函数:f_open、f_read、etc.

三、保存图片

bmp头结构:
1、文件头的结构定义了该图片的框架信息,占14个字节:

(1)bfType:指定文件类型,必须是 0x424D,即字符串”BM”,也就是说.bmp文件的关键字都是“BM”。
(2)bfSize:指定文件大小。
(3)bfOffBits:实际数据占文件头的偏移量。

2、信息头的结构定义了该图片的具体信息,占40个字节:

(1)biWidth:指定图像宽度,单位:像素
(2)biHeight:指定图像高度,单位:像素
(3)biBitCount:指定颜色位数,常用的值为1(灰度图),4(16色图),8(256色图),24(真彩色图),32(真彩色图,增加ALPHA通道)

结构体:参考文章

//BMP头文件
typedef __packed struct
{
    u16  bfType ;     //文件标志.只对'BM',用来识别BMP位图类型
    u32  bfSize ;	  //文件大小,占四个字节
    u16  bfReserved1 ;//保留
    u16  bfReserved2 ;//保留
    u32  bfOffBits ;  //从文件开始到位图数据(bitmap data)开始之间的的偏移量
}BITMAPFILEHEADER ;
//BMP信息头
typedef __packed struct
{
    u32 biSize ;		   		//说明BITMAPINFOHEADER结构所需要的字数。
    long  biWidth ;		   	//说明图象的宽度,以象素为单位 
    long  biHeight ;	   	//说明图象的高度,以象素为单位 
    u16  biPlanes ;	   		//为目标设备说明位面数,其值将总是被设为1 
    u16  biBitCount ;	   	//说明比特数/象素,其值为1、4、8、16、24、或32
/*说明图象数据压缩的类型。其值可以是下述值之一:
BI_RGB:没有压缩;
BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);  
BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成
BI_BITFIELDS:每个象素的比特由指定的掩码决定。*/
    u32 biCompression ;  	
    u32 biSizeImage ;		//说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0  
    long  biXPelsPerMeter ;	//说明水平分辨率,用象素/米表示
    long  biYPelsPerMeter ;	//说明垂直分辨率,用象素/米表示
    u32 biClrUsed ;	  	 	//说明位图实际使用的彩色表中的颜色索引数
    u32 biClrImportant ; 	//说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 
}BITMAPINFOHEADER ;
//彩色表 
typedef __packed struct 
{
    u8 rgbBlue ;    //指定蓝色强度
    u8 rgbGreen ;	//指定绿色强度 
    u8 rgbRed ;	  	//指定红色强度 
    u8 rgbReserved ;//保留,设置为0 
}RGBQUAD ;
//整体信息头
typedef __packed struct
{ 
	BITMAPFILEHEADER bmfHeader;
	BITMAPINFOHEADER bmiHeader;  
	RGBQUAD RGB_MASK[3];			//调色板用于存放RGB掩码.
}BITMAPINFO; 

写bmp头:

//打开文件,若不存在就创建
res_sd = f_open(&fnew, "0:test1.bmp", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);

//文件打开成功
if(res_sd == FR_OK)
{
	//填写文件信息头信息  
	bmp.bmfHeader.bfType = 0x4D42;			//bmp类型  "BM"
	bmp.bmfHeader.bfSize= 54 + 320*240*2;	//文件大小(信息结构体+像素数据)
	bmp.bmfHeader.bfReserved1 = 0x0000;		//保留,必须为0
	bmp.bmfHeader.bfReserved2 = 0x0000;  			
	bmp.bmfHeader.bfOffBits=54;				//位图信息结构体所占的字节数
	
	//填写位图信息头信息  
	bmp.bmiHeader.biSize=40;  			//位图信息头的大小
	bmp.bmiHeader.biWidth=320;  		//位图的宽度
	bmp.bmiHeader.biHeight=240;  		//图像的高度
	bmp.bmiHeader.biPlanes=1;  			//目标设别的级别,必须是1
	bmp.bmiHeader.biBitCount=16;        //每像素位数
	bmp.bmiHeader.biCompression=3;  	//RGB555格式
	bmp.bmiHeader.biSizeImage=320*240*2;//实际位图所占用的字节数(仅考虑位图像素数据)
	bmp.bmiHeader.biXPelsPerMeter=0;	//水平分辨率
	bmp.bmiHeader.biYPelsPerMeter=0; 	//垂直分辨率
	bmp.bmiHeader.biClrImportant=0;   	//说明图像显示有重要影响的颜色索引数目,0代表所有的颜色一样重要
	bmp.bmiHeader.biClrUsed=0;  		//位图实际使用的彩色表中的颜色索引数,0表示使用所有的调色板项
	
	//RGB565格式掩码
	bmp.RGB_MASK[0].rgbBlue = 0;
	bmp.RGB_MASK[0].rgbGreen = 0xF8;
	bmp.RGB_MASK[0].rgbRed = 0;
	bmp.RGB_MASK[0].rgbReserved = 0;
	
	bmp.RGB_MASK[1].rgbBlue = 0xE0;
	bmp.RGB_MASK[1].rgbGreen = 0x07;
	bmp.RGB_MASK[1].rgbRed = 0;
	bmp.RGB_MASK[1].rgbReserved = 0;
	
	bmp.RGB_MASK[2].rgbBlue = 0x1F;
	bmp.RGB_MASK[2].rgbGreen = 0;
	bmp.RGB_MASK[2].rgbRed = 0;
	bmp.RGB_MASK[2].rgbReserved = 0;
	
	//写文件头进文件  
	res_sd= f_write(&fnew, &bmp, sizeof(bmp), &fnum);
}

写图像数据:

for(i=0;i<240;i++)
{
	for(j=0;j<320;j++)
	{				
		GPIOB->CRL=0X88888888;
		OV7725_RCK=0;
		color=OV7725_DATA;	//读数据--高8位
		
		OV7725_RCK=1; 
		color<<=8;  
		OV7725_RCK=0;
		color|=OV7725_DATA;	//读数据	--低8位		(高低8+8位合并成一个u16发送)								
		OV7725_RCK=1;
		GPIOB->CRL=0X33333333;
		//LCD显示
		LCD_WR_DATA(color);
		//写位图信息头进内存卡
		f_write(&fnew, &color, sizeof(color), &fnum);
	}
}

四、实验结果

效果挺好的。
【STM32调试(三)】采集bmp图像保存在SD卡_第2张图片
先存后发,有效减少干扰。

你可能感兴趣的:(硬件开发)