超级版主
 
- 积分
- 5391
- 金钱
- 5391
- 注册时间
- 2019-5-8
- 在线时间
- 1393 小时
|
|
第四十二章 照相机实验
1)实验平台:正点原子DNESP32P4开发板
2)章节摘自【正点原子】ESP32-P4开发指南— V1.0
3)购买链接:https://detail.tmall.com/item.htm?id=873309579825
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/esp32/ATK-DNESP32P4.html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子DNESP32S3开发板技术交流群:132780729
在前面的章节中,我们已经深入学习了MIPI CSI(Mobile Industry Processor Interface Camera Serial Interface)相关的基本原理,了解了MIPI摄像头如何通过CSI接口与处理器进行数据传输,以及图像数据的处理流程。本章节将基于MIPI CSI摄像头实验的基础,进一步实现照相机功能,并将拍摄的图像数据保存到SD卡的BMP文件中。
本章分为如下几个小节:
42.1 BMP编码简介
42.2 硬件设计
42.3 程序设计
42.4 下载验证
42.1 BMP编码简介
在前面的章节中,我们学习了各种图片格式的解码方法,并了解了如何解析图像文件以及如何从中提取图像数据。为进一步掌握图像处理的基本原理,本章将介绍最简单的图片编码方法——BMP(Bitmap)图片编码。BMP格式是一种非常基础且广泛使用的图像格式,它不使用压缩,因此解码和编码都相对简单。通过这个例程,我们将学习如何将图像数据编码为BMP格式,并理解BMP文件的结构。下面的程序是在bmp.c/.h文件下定义的。
BMP文件由以下四个主要部分组成:
1,文件头(File Header): 文件头是BMP格式的第一部分,它包含了文件的基本信息,如文件大小、图像的起始位置等。文件头是BMP文件中固定长度的部分,长度通常为14个字节。如下代码所示。
- <font size="3">/* BMP头文件 */</font>
- <font size="3">typedef struct</font>
- <font size="3">{</font>
- <font size="3"> uint16_t bfType; /* 文件标志.只对'BM',用来识别BMP位图类型 */</font>
- <font size="3"> uint32_t bfSize; /* 文件大小,占四个字节 */</font>
- <font size="3"> uint16_t bfReserved1; /* 保留 */</font>
- <font size="3"> uint16_t bfReserved2; /* 保留 */</font>
- <font size="3"> uint32_t bfOffBits; /* 从文件开始到位图数据(bitmap data)开始之间的的偏移量 */</font>
- <font size="3">} __attribute__ ((packed)) BITMAPFILEHEADER;</font>
复制代码 2,位图信息头(Bitmap Info Header): 位图信息头紧跟在文件头后面,包含了图像的详细信息,如图像的宽度、高度、颜色深度、压缩类型等。这个部分的长度通常为40个字节(对于Windows BMP格式)。它告诉我们如何解码图像数据,并且包含了关于图像的维度、颜色空间等关键信息,如下代码所示:
- <font size="3">/* BMP信息头 */</font>
- <font size="3">typedef struct</font>
- <font size="3">{</font>
- <font size="3"> uint32_t biSize; /* 说明BITMAPINFOHEADER结构所需要的字数。 */</font>
- <font size="3"> long biWidth; /* 说明图象的宽度,以象素为单位 */</font>
- <font size="3"> long biHeight; /* 说明图象的高度,以象素为单位 */</font>
- <font size="3"> uint16_t biPlanes; /* 为目标设备说明位面数,其值将总是被设为1 */</font>
- <font size="3"> uint16_t biBitCount; /* 说明比特数/象素,其值为1、4、8、16、24、或32 */</font>
- <font size="3"> uint32_t biCompression; /* 说明图象数据压缩的类型。其值可以是下述值之一</font>
- <font size="3"> * BI_RGB :没有压缩</font>
- <font size="3"> * BI_RLE8 :每个象素8比特的RLE压缩编码 </font>
- <font size="3"> * BI_RLE4 :每个象素4比特的RLE压缩编码 </font>
- <font size="3"> * BI_BITFIELDS:每个象素的比特由指定的掩码决定 </font>
- <font size="3"> */</font>
- <font size="3"> uint32_t biSizeImage; /* 说明图象的大小,以字节为单位*/</font>
- <font size="3"> long biXPelsPerMeter; /* 说明水平分辨率,用象素/米表示 */</font>
- <font size="3"> long biYPelsPerMeter; /* 说明垂直分辨率,用象素/米表示 */</font>
- <font size="3"> uint32_t biClrUsed; /* 说明位图实际使用的彩色表中的颜色索引数 */</font>
- <font size="3"> uint32_t biClrImportant; /* 说明对图象显示有重要影响的颜色索引的数目 */</font>
- <font size="3">} __attribute__ ((packed)) BITMAPINFOHEADER;</font>
复制代码 3,颜色表(Color Table): BMP图像的颜色表用于存储图像中使用的颜色。对于8位或更低位深的BMP图像,颜色表是必需的。颜色表的大小由位深(如8位、16位等)决定,每个颜色由4个字节(包括红、绿、蓝和透明度或alpha值)来表示,如下代码所示:
- <font size="3">/* 彩色表 */</font>
- <font size="3">typedef struct</font>
- <font size="3">{</font>
- <font size="3"> uint8_t rgbBlue; /* 指定蓝色强度 */</font>
- <font size="3"> uint8_t rgbGreen; /* 指定绿色强度 */</font>
- <font size="3"> uint8_t rgbRed; /* 指定红色强度 */</font>
- <font size="3"> uint8_t rgbReserved; /* 保留,设置为0 */</font>
- <font size="3">} RGBQUAD;</font>
复制代码 4,图形数据(Pixel Data): 位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=16时,1个像素占2个字节;
当biBitCount=24时,1个像素占3个字节;
当biBitCount=32时,1个像素占4个字节;
biBitCount=1 表示位图最多有两种颜色,缺省情况下是黑色和白色,你也可以自己定义这两种颜色。图像信息头装调色板中将有两个调色板项,称为索引0和索引1。图象数据阵列中的每一位表示一个像素。如果一个位是0,显示时就使用索引0的RGB值,如果位是1,则使用索引1的RGB值。
biBitCount=16 表示位图最多有65536种颜色。每个像素用16位(2个字节)表示。这种格式叫作高彩色,或叫增强型16位色,或64K色。它的情况比较复杂,当biCompression成员的值是BI_RGB时,它没有调色板。16位中,最低的5位表示蓝色分量,中间的5位表示绿色分量,高的5位表示红色分量,一共占用了15位,最高的一位保留,设为0。这种格式也被称作555 16位位图。如果biCompression成员的值是BI_BITFIELDS,那么情况就复杂了,首先是原来调色板的位置被三个DWORD变量占据,称为红、绿、蓝掩码。分别用于描述红、绿、蓝分量在16位中所占的位置。在Windows 95(或98)中,系统可接受两种格式的位域:555和565,在555格式下,红、绿、蓝的掩码分别是:0x7C00、0x03E0、0x001F,而在565格式下,它们则分别为:0xF800、0x07E0、0x001F。你在读取一个像素之后,可以分别用掩码“与”上像素值,从而提取出想要的颜色分量(当然还要再经过适当的左右移操作)。在NT系统中,则没有格式限制,只不过要求掩码之间不能有重叠。(注:这种格式的图像使用起来是比较麻烦的,不过因为它的显示效果接近于真彩,而图像数据又比真彩图像小的多,所以,它更多的被用于游戏软件)。
biBitCount=32 表示位图最多有4294967296(2的32次方)种颜色。这种位图的结构与16位位图结构非常类似,当biCompression成员的值是BI_RGB时,它也没有调色板,32位中有24位用于存放RGB值,顺序是:最高位—保留,红8位、绿8位、蓝8位。这种格式也被成为888 32位图。如果 biCompression成员的值是BI_BITFIELDS时,原来调色板的位置将被三个DWORD变量占据,成为红、绿、蓝掩码,分别用于描述红、绿、蓝分量在32位中所占的位置。在Windows 95(or 98)中,系统只接受888格式,也就是说三个掩码的值将只能是:0xFF0000、0xFF00、0xFF。而NT系统,只要注意使掩码之间不产生重叠就行。(注:这种图像格式比较规整,因为它是DWORD对齐的,所以在内存中进行图像处理时可进行汇编级的代码优化(简单)。
通过以上了解,我们对BMP有了一个比较深入的了解,本章,我们采用16位BMP编码(因为我们的LCD就是16位色的,而且16位BMP编码比24位BMP编码更省空间),故我们需要设置biBitCount的值为16,这样得到新的位图信息(BITMAPINFO)结构体:
- <font size="3">/* 位图信息头 */</font>
- <font size="3">typedef struct</font>
- <font size="3">{ </font>
- <font size="3"> BITMAPFILEHEADER bmfHeader;</font>
- <font size="3"> BITMAPINFOHEADER bmiHeader; </font>
- <font size="3"> uint32_t RGB_MASK[3]; /* 调色板用于存放RGB掩码 */</font>
- <font size="3"> //RGBQUAD bmiColors[256];</font>
- <font size="3">} __attribute__ ((packed)) BITMAPINFO;</font>
复制代码 上述的结构体其实就是颜色表由3个RGB掩码代替。最后,我们来看看将LCD的显存保存为BMP格式的图片文件的步骤:
1)创建BMP位图信息,并初始化各个相关信息
这里,我们要设置BMP图片的分辨率为LCD分辨率、BMP图片的大小(整个BMP文件大小)、BMP的像素位数(16位)和掩码等信息。
2)创建新BMP文件,写入BMP位图信息
我们要保存BMP,当然要存放在某个地方(文件),所以需要先创建文件,同时先保存BMP位图信息,之后才开始BMP数据的写入。
3)保存位图数据。
这里就比较简单了,只需要从LCD的GRAM里面读取各点的颜色值,依次写入第二步创建的BMP文件即可。注意:保存顺序(即读GRAM顺序)是从左到右,从下到上。
4)关闭文件。
使用FATFS,在文件创建之后,必须调用f_close,文件才会真正体现在文件系统里面,否则是不会写入的!这个要特别注意,写完之后,一定要调用f_close。
下面是基于之前的四个步骤实现的BMP编码函数。我们将按照文件头、位图信息头、颜色表、像素数据的顺序来编写BMP编码的函数。假设图像数据已经存在并且已知其宽度、高度以及颜色信息:
- <font size="3">/**</font>
- <font size="3"> * @brief BMP编码函数</font>
- <font size="3"> * [url=home.php?mod=space&uid=60778]@note[/url] 将图像源保存为16位格式的BMP文件 RGB565格式.</font>
- <font size="3"> * 保存为rgb555格式则需要颜色转换,耗时间比较久,所以保存为565是最快速的办法.</font>
- <font size="3"> *</font>
- <font size="3"> * [url=home.php?mod=space&uid=271674]@param[/url] filename : 包含存储路径的文件名(.bmp)</font>
- <font size="3"> * @param image_addr : 图像源</font>
- <font size="3"> * @param width,height: 区域大小</font>
- <font size="3"> * @param mode : 保存模式</font>
- <font size="3"> * @arg 0, 仅仅创建新文件的方式编码;</font>
- <font size="3"> * @arg 1, 如果之前存在文件,则覆盖之前的文件。若没有,则创建新文件; </font>
- <font size="3"> * @retval 操作结果</font>
- <font size="3"> * @arg 0 , 成功</font>
- <font size="3"> * @arg 其他, 错误码</font>
- <font size="3"> */</font>
- <font size="3">uint8_t bmp_encode( uint8_t *filename, uint16_t *image_addr, uint16_t width, </font>
- <font size="3">uint16_t height, uint8_t mode)</font>
- <font size="3">{</font>
- <font size="3">#if SHOW_TIME == 1 </font>
- <font size="3"> TickType_t startTick, endTick, diffTick;</font>
- <font size="3"> startTick = xTaskGetTickCount();</font>
- <font size="3">#endif</font>
- <font size="3"> FIL *f_bmp;</font>
- <font size="3"> uint32_t bw = 0;</font>
- <font size="3"> uint16_t bmpheadsize; /* bmp头大小 */</font>
- <font size="3"> BITMAPINFO hbmp; /* bmp头 */</font>
- <font size="3"> uint8_t res = 0;</font>
- <font size="3"> uint16_t *databuf; /* 数据缓存区地址 */</font>
- <font size="3"> uint16_t pixcnt; /* 像素计数器 */</font>
- <font size="3"> uint16_t bi4width; /* 水平像素字节数 */</font>
- <font size="3"> uint16_t row_index = 0;</font>
- <font size="3"> uint16_t *img_addr = (uint16_t *)image_addr;</font>
- <font size="3"> if (width == 0 || height == 0) return PIC_WINDOW_ERR; /* 区域错误 */</font>
- <font size="3"> </font>
- <font size="3">/* 开辟至少bi4width大小的字节的内存区域 ,对240宽的屏,480个字节就够了.</font>
- <font size="3">最大支持1024宽度的bmp编码 */</font>
- <font size="3"> databuf = (uint16_t *)piclib_mem_malloc(2160);</font>
- <font size="3"> if (databuf == NULL) return PIC_MEM_ERR; /* 内存申请失败. */</font>
- <font size="3"> f_bmp = (FIL *)piclib_mem_malloc(sizeof(FIL)); /* 开辟FIL字节的内存区域 */</font>
- <font size="3"> if (f_bmp == NULL) /* 内存申请失败 */</font>
- <font size="3"> {</font>
- <font size="3"> piclib_mem_free(databuf);</font>
- <font size="3"> return PIC_MEM_ERR;</font>
- <font size="3"> }</font>
- <font size="3"> /* BMP头部设置 */</font>
- <font size="3"> bmpheadsize = sizeof(hbmp); /* 得到bmp文件头的大小 */</font>
- <font size="3"> memset((uint8_t *)&hbmp, 0, sizeof(hbmp)); /* 置零空申请到的内存 */</font>
- <font size="3"> hbmp.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); /* 信息头大小 */</font>
- <font size="3"> hbmp.bmiHeader.biWidth = width; /* bmp的宽度 */</font>
- <font size="3"> hbmp.bmiHeader.biHeight = height; /* bmp的高度 */</font>
- <font size="3"> hbmp.bmiHeader.biPlanes = 1; /* 恒为1 */</font>
- <font size="3"> hbmp.bmiHeader.biBitCount = 16; /* bmp为16位色bmp */</font>
- <font size="3">hbmp.bmiHeader.biCompression = BI_BITFIELDS;/* 每个象素的比特由指定的掩码决定 */</font>
- <font size="3">/* bmp数据区大小 */</font>
- <font size="3">hbmp.bmiHeader.biSizeImage = hbmp.bmiHeader.biHeight * </font>
- <font size="3">hbmp.bmiHeader.biWidth * hbmp.bmiHeader.biBitCount / 8; </font>
- <font size="3">hbmp.bmfHeader.bfType = ((uint16_t)'M' << 8) + 'B'; /* BM格式标志 */</font>
- <font size="3">/* 整个bmp的大小 */ </font>
- <font size="3"> hbmp.bmfHeader.bfSize = bmpheadsize + hbmp.bmiHeader.biSizeImage; </font>
- <font size="3"> hbmp.bmfHeader.bfOffBits = bmpheadsize; /* 到数据区的偏移 */</font>
- <font size="3"> hbmp.RGB_MASK[0] = 0X00F800; /* 红色掩码 */</font>
- <font size="3"> hbmp.RGB_MASK[1] = 0X0007E0; /* 绿色掩码 */</font>
- <font size="3"> hbmp.RGB_MASK[2] = 0X00001F; /* 蓝色掩码 */</font>
- <font size="3"> if (mode == 1)</font>
- <font size="3">{</font>
- <font size="3"> /* 尝试打开之前的文件 */</font>
- <font size="3"> res = f_open(f_bmp, (const TCHAR *)filename, FA_READ | FA_WRITE); </font>
- <font size="3"> }</font>
- <font size="3"> </font>
- <font size="3"> if (mode == 0 || res == 0x04)</font>
- <font size="3">{</font>
- <font size="3"> /* 模式0,或者尝试打开失败,则创建新文件 */</font>
- <font size="3"> res = f_open(f_bmp, (const TCHAR *)filename, FA_WRITE | FA_CREATE_NEW); </font>
- <font size="3"> }</font>
- <font size="3"> if ((hbmp.bmiHeader.biWidth * 2) % 4) /* 水平像素(字节)不为4的倍数 */</font>
- <font size="3">{</font>
- <font size="3"> /* 实际要写入的宽度像素,必须为4的倍数 */</font>
- <font size="3"> bi4width = ((hbmp.bmiHeader.biWidth * 2) / 4 + 1) * 4; </font>
- <font size="3"> }</font>
- <font size="3"> else</font>
- <font size="3"> {</font>
- <font size="3"> bi4width = hbmp.bmiHeader.biWidth * 2; /* 刚好为4的倍数 */</font>
- <font size="3"> }</font>
- <font size="3"> /* 写入图像数据 */</font>
- <font size="3"> if (res == FR_OK) /* 创建成功 */</font>
- <font size="3">{</font>
- <font size="3"> /* 写入BMP首部 */</font>
- <font size="3"> res = f_write(f_bmp, (uint8_t *)&hbmp, bmpheadsize, &bw); </font>
- <font size="3"> /* 按一行一行操作 */</font>
- <font size="3"> for (uint16_t ty = height - 1; hbmp.bmiHeader.biHeight; ty--) </font>
- <font size="3"> {</font>
- <font size="3"> pixcnt = 0;</font>
- <font size="3"> for (uint16_t xpix_index = 0; pixcnt != (bi4width / 2);)</font>
- <font size="3"> { </font>
- <font size="3"> if (pixcnt < hbmp.bmiHeader.biWidth)</font>
- <font size="3"> {</font>
- <font size="3"> databuf[pixcnt] = img_addr[pixcnt + hbmp.bmiHeader.biWidth </font>
- <font size="3">* row_index]; </font>
- <font size="3"> }</font>
- <font size="3"> else</font>
- <font size="3"> {</font>
- <font size="3"> databuf[pixcnt] = 0Xffff; /* 补充白色的像素 */</font>
- <font size="3"> }</font>
- <font size="3"> pixcnt++;</font>
- <font size="3"> xpix_index++;</font>
- <font size="3"> }</font>
- <font size="3"> hbmp.bmiHeader.biHeight--;</font>
- <font size="3"> row_index++;</font>
- <font size="3">/* 写入一行数据 */</font>
- <font size="3"> res = f_write(f_bmp, (uint8_t *)databuf, bi4width, &bw);</font>
- <font size="3"> }</font>
- <font size="3"> f_close(f_bmp);</font>
- <font size="3"> }</font>
- <font size="3">#if SHOW_TIME == 1 </font>
- <font size="3"> endTick = xTaskGetTickCount();</font>
- <font size="3"> diffTick = endTick - startTick;</font>
- <font size="3"> ESP_LOGI(__FUNCTION__, "elapsed time[ms]:%"PRIu32, diffTick * portTICK_PERIOD_MS);</font>
- <font size="3">#endif</font>
- <font size="3"> piclib_mem_free(databuf);</font>
- <font size="3"> piclib_mem_free(f_bmp);</font>
- <font size="3"> return res;</font>
- <font size="3">}</font>
复制代码 BMP编码过程是将图像数据保存为BMP格式文件的操作。通过设置BMP文件的头部信息(如图像宽度、高度、颜色深度等),并将图像的每一行像素数据逐行写入文件(必须是从左到右,从下到上依次写入)。该过程使用RGB565格式保存图像数据,通过内存分配和文件操作来完成图像的存储,并确保数据按4字节对齐。最终,BMP文件包含图像的元数据和实际像素数据,成功生成BMP图像文件。
42.2 硬件设计
42.2.1 程序功能
该实验通过检测SD卡根目录中的PHOTO文件夹是否存在并创建它,确保拍照功能的正常使用。成功创建文件夹后,系统初始化MIPI摄像头并在LCD屏幕上显示相关信息,按下BOOT按键触发拍照功能。同时,LED0会闪烁,提示程序正常运行。如果SD卡或摄像头初始化失败,系统会显示相应的错误信息。
42.2.2 硬件资源
1)LED灯
LED 0 - IO51
2)MIPI CSI
3)RGBLCD/MIPILCD(引脚太多,不罗列出来)
4)KEY按键
BOOT - IO35
42.2.3 原理图
MIPI CSI原理图已在37.2.3小节中详细阐述,为避免重复,此处不再赘述。
42.3 程序设计
42.3.1 程序流程图
图42.3.1 照相机实验程序流程图
42.3.2 程序解析
在33_photograph例程中,基于28_mipicamera例程进行了修改,主要是在33_photograph\components\Middlewares文件夹下添加了MYFATFS、PICTURE和TEXT这三个组件。这些组件的具体功能和作用,笔者已经在前面的章节中详细讲解过,因此在此不再重复描述。
1,图像存储代码
在main/APP/MIPI_CAM/mipi_cam.c文件下的lcd_cam_task摄像头任务函数中,首先通过PPA(Pixel Processing Accelerator)将摄像头模块采集到的原始图像数据转换为适合显示的格式和大小,然后将转换后的图像数据展示在LCD屏幕上。当按下BOOT按键时,系统会触发拍照功能,将当前显示的图像数据传递给bmp_encode函数进行BMP格式编码,最终将编码后的图片保存到SD卡的PHOTO文件夹中:
- <font size="3">if (key_scan(0) == BOOT_PRES) /* 按下BOOT按键进行拍照 */</font>
- <font size="3">{</font>
- <font size="3"> camera_new_pathname(pname, 0); /* 得到文件名 */ </font>
- <font size="3"> res=bmp_encode(pname,(uint16_t *)draw_buffer,lcddev.width,lcddev.height, 1);</font>
- <font size="3"> text_show_string(30, 130, 240, 16, "请耐心等待,正在进行文件写入!", 16, 0, RED);</font>
- <font size="3"> if (res)</font>
- <font size="3"> {</font>
- <font size="3"> text_show_string(30, 130, 240, 16, "写入文件错误!", 16, 0, RED);</font>
- <font size="3"> }</font>
- <font size="3"> else</font>
- <font size="3"> {</font>
- <font size="3"> text_show_string(30, 130, 240, 16, "拍照成功!", 16, 0, RED);</font>
- <font size="3"> text_show_string(30, 150, 240, 16, "保存为:", 16, 0, RED);</font>
- <font size="3"> text_show_string(30 + 56, 150, 240, 16, (char *)pname, 16, 0, RED);</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(500));</font>
- <font size="3"> }</font>
- <font size="3">}</font>
复制代码 lcd_cam_task摄像头任务函数实现已在第三十七章节中介绍过了,因此在此不再重复描述。
2,CMakeLists.txt文件
CMakeLists.txt文件跟摄像头实验一致,代码如下:
- <font size="3">idf_component_register(</font>
- <font size="3"> SRC_DIRS </font>
- <font size="3"> "."</font>
- <font size="3"> "APP/MIPI_CAM" </font>
- <font size="3"> INCLUDE_DIRS </font>
- <font size="3"> "."</font>
- <font size="3"> "APP/MIPI_CAM" </font>
- <font size="3"> )</font>
复制代码 3,main.c驱动代码
在main.c里面编写如下代码。
- <font size="3">void app_main(void)</font>
- <font size="3">{</font>
- <font size="3"> esp_err_t ret;</font>
- <font size="3"> uint8_t key = 0;</font>
- <font size="3"> ret = nvs_flash_init(); /* 初始化NVS */</font>
- <font size="3"> if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)</font>
- <font size="3"> {</font>
- <font size="3"> ESP_ERROR_CHECK(nvs_flash_erase());</font>
- <font size="3"> ESP_ERROR_CHECK(nvs_flash_init());</font>
- <font size="3"> }</font>
- <font size="3"> led_init(); /* LED初始化 */</font>
- <font size="3"> key_init(); /* KEY初始化 */</font>
- <font size="3"> myiic_init(); /* MYIIC初始化 */</font>
- <font size="3"> lcd_init(); /* LCD屏初始化 */</font>
- <font size="3"> while (sdmmc_init()) /* 检测不到SD卡 */</font>
- <font size="3"> {</font>
- <font size="3"> lcd_show_string(30, 110, 200, 16, 16, "SD Card Error!", RED);</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(200));</font>
- <font size="3"> lcd_fill(30, 110, 239, 126, WHITE);</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(200));</font>
- <font size="3"> }</font>
- <font size="3"> ret = exfuns_init(); /* 为fatfs相关变量申请内存 */</font>
- <font size="3"> while (fonts_init()) /* 检查字库 */</font>
- <font size="3"> {</font>
- <font size="3"> lcd_clear(WHITE);</font>
- <font size="3"> lcd_show_string(30, 30, 200, 16, 16, "ESP32-P4", RED);</font>
- <font size="3"> </font>
- <font size="3"> key = fonts_update_font(30, 50, 16, (uint8_t *)"0:", RED); /* 更新字库 */</font>
- <font size="3"> while (key) /* 更新失败 */</font>
- <font size="3"> {</font>
- <font size="3"> lcd_show_string(30, 50, 200, 16, 16, "Font Update Failed!", RED);</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(200));</font>
- <font size="3"> lcd_fill(20, 50, 200 + 20, 90 + 16, WHITE);</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(200));</font>
- <font size="3"> }</font>
- <font size="3"> lcd_show_string(30, 50, 200, 16, 16, "Font Update Success! ", RED);</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(1000));</font>
- <font size="3"> lcd_clear(WHITE); </font>
- <font size="3"> }</font>
- <font size="3"> text_show_string(30, 50, 200, 16, "正点原子ESP32-P4开发板", 16, 0, RED);</font>
- <font size="3"> text_show_string(30, 70, 200, 16, "照相机 实验", 16, 0, RED);</font>
- <font size="3"> text_show_string(30, 90, 200, 16, "正点原子@ALIENTEK", 16, 0, RED);</font>
- <font size="3"> text_show_string(30, 110, 200, 16, "BOOT:TAKE PHOTO", 16, 0, RED);</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(1000));</font>
- <font size="3"> lcd_clear(WHITE);</font>
- <font size="3"> </font>
- <font size="3"> mipi_cam_init(); /* 摄像头初始化 */</font>
- <font size="3"> while (1)</font>
- <font size="3"> {</font>
- <font size="3"> LED0_TOGGLE();</font>
- <font size="3"> vTaskDelay(pdMS_TO_TICKS(500));</font>
- <font size="3"> }</font>
- <font size="3">}</font>
复制代码 上述函数主要实现了硬件初始化、SD卡检测、字库更新、LCD显示和摄像头初始化等功能。程序首先初始化了NVS、LED、按键、I2C总线和LCD屏,随后检测SD卡是否存在并可用,如果未检测到SD卡则提示错误信息并反复重试。接着,程序检查并更新字库,确保显示文字的正确性。字库更新成功后,屏幕会显示开发板和实验相关的信息。在完成硬件和系统初始化后,摄像头被初始化准备拍照,同时LED灯每500毫秒闪烁,提示程序正常运行。
42.4 下载验证
将程序下载到开发板后,可以看到LCD首先显示一些实验相关的信息,如下图所示:
图42.4.1 显示实验相关信息
显示了上图的信息后,自动进入监控界面。可以看到LED0不停的闪烁,提示程序已经在运行了。此时,我们可以按下BOOT,即可进行bmp拍照。拍照得到的照片效果如下图所示:
图42.4.2 拍照样图 |
|