OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 117|回复: 0

《ESP32-P4开发指南— V1.0》第四十二章 照相机实验

[复制链接]

1270

主题

1284

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5450
金钱
5450
注册时间
2019-5-8
在线时间
1422 小时
发表于 前天 09:18 | 显示全部楼层 |阅读模式
第四十二章 照相机实验

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


2.jpg

3.png

在前面的章节中,我们已经深入学习了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个字节。如下代码所示。

  1. /* BMP头文件 */
  2. typedef struct
  3. {
  4.     uint16_t  bfType;           /* 文件标志.只对'BM',用来识别BMP位图类型 */
  5.     uint32_t  bfSize;           /* 文件大小,占四个字节 */
  6.     uint16_t  bfReserved1;      /* 保留 */
  7.     uint16_t  bfReserved2;      /* 保留 */
  8.     uint32_t  bfOffBits;        /* 从文件开始到位图数据(bitmap data)开始之间的的偏移量 */
  9. } __attribute__ ((packed)) BITMAPFILEHEADER;
复制代码
2,位图信息头(Bitmap Info Header): 位图信息头紧跟在文件头后面,包含了图像的详细信息,如图像的宽度、高度、颜色深度、压缩类型等。这个部分的长度通常为40个字节(对于Windows BMP格式)。它告诉我们如何解码图像数据,并且包含了关于图像的维度、颜色空间等关键信息,如下代码所示:
  1. /* BMP信息头 */
  2. typedef struct
  3. {
  4.     uint32_t biSize;            /* 说明BITMAPINFOHEADER结构所需要的字数。 */
  5.     long  biWidth;              /* 说明图象的宽度,以象素为单位 */
  6.     long  biHeight;             /* 说明图象的高度,以象素为单位 */
  7.     uint16_t  biPlanes;         /* 为目标设备说明位面数,其值将总是被设为1 */
  8.     uint16_t  biBitCount;       /* 说明比特数/象素,其值为1、4、8、16、24、或32 */
  9.     uint32_t biCompression;     /* 说明图象数据压缩的类型。其值可以是下述值之一
  10.                                  * BI_RGB      :没有压缩
  11.                                  * BI_RLE8     :每个象素8比特的RLE压缩编码
  12.                                  * BI_RLE4     :每个象素4比特的RLE压缩编码
  13.                                  * BI_BITFIELDS:每个象素的比特由指定的掩码决定
  14.                                  */
  15.     uint32_t biSizeImage;                 /* 说明图象的大小,以字节为单位*/
  16.     long  biXPelsPerMeter;      /* 说明水平分辨率,用象素/米表示 */
  17.     long  biYPelsPerMeter;      /* 说明垂直分辨率,用象素/米表示 */
  18.     uint32_t biClrUsed;         /* 说明位图实际使用的彩色表中的颜色索引数 */
  19.     uint32_t biClrImportant;    /* 说明对图象显示有重要影响的颜色索引的数目 */
  20. } __attribute__ ((packed)) BITMAPINFOHEADER;
复制代码
3,颜色表(Color Table): BMP图像的颜色表用于存储图像中使用的颜色。对于8位或更低位深的BMP图像,颜色表是必需的。颜色表的大小由位深(如8位、16位等)决定,每个颜色由4个字节(包括红、绿、蓝和透明度或alpha值)来表示,如下代码所示:
  1. /* 彩色表  */
  2. typedef struct
  3. {
  4.     uint8_t rgbBlue;           /* 指定蓝色强度 */
  5.     uint8_t rgbGreen;          /* 指定绿色强度 */
  6.     uint8_t rgbRed;            /* 指定红色强度 */
  7.     uint8_t rgbReserved;       /* 保留,设置为0 */
  8. } RGBQUAD;
复制代码
5,图形数据(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)结构体:

  1. /* 位图信息头 */
  2. typedef struct
  3. {
  4.     BITMAPFILEHEADER bmfHeader;
  5.     BITMAPINFOHEADER bmiHeader;  
  6.     uint32_t RGB_MASK[3];       /* 调色板用于存放RGB掩码 */
  7.     //RGBQUAD bmiColors[256];
  8. } __attribute__ ((packed)) BITMAPINFO;
复制代码
上述的结构体其实就是颜色表由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编码的函数。假设图像数据已经存在并且已知其宽度、高度以及颜色信息:

  1. /**
  2. * @brief           BMP编码函数
  3. *   [url=home.php?mod=space&uid=60778]@note[/url]            将图像源保存为16位格式的BMP文件 RGB565格式.
  4. *                     保存为rgb555格式则需要颜色转换,耗时间比较久,所以保存为565是最快速的办法.
  5. *
  6. * [url=home.php?mod=space&uid=271674]@param[/url]              filename    : 包含存储路径的文件名(.bmp)
  7. * @param              image_addr  : 图像源
  8. * @param              width,height: 区域大小
  9. * @param              mode        : 保存模式
  10. *   @arg                     0, 仅仅创建新文件的方式编码;
  11. *   @arg                     1, 如果之前存在文件,则覆盖之前的文件。若没有,则创建新文件;
  12. * @retval            操作结果
  13. *   @arg              0   , 成功
  14. *   @arg             其他, 错误码
  15. */
  16. uint8_t bmp_encode( uint8_t *filename, uint16_t *image_addr, uint16_t width,
  17. uint16_t height, uint8_t mode)
  18. {
  19. #if SHOW_TIME == 1                              
  20.     TickType_t startTick, endTick, diffTick;
  21.     startTick = xTaskGetTickCount();
  22. #endif

  23.     FIL *f_bmp;
  24.     uint32_t bw = 0;
  25.     uint16_t bmpheadsize;       /* bmp头大小 */
  26.     BITMAPINFO hbmp;            /* bmp头 */
  27.     uint8_t res = 0;
  28.     uint16_t *databuf;          /* 数据缓存区地址 */
  29.     uint16_t pixcnt;            /* 像素计数器 */
  30.     uint16_t bi4width;          /* 水平像素字节数 */
  31.     uint16_t row_index = 0;

  32.     uint16_t *img_addr = (uint16_t *)image_addr;

  33.     if (width == 0 || height == 0) return PIC_WINDOW_ERR;   /* 区域错误 */
  34.    
  35. /* 开辟至少bi4width大小的字节的内存区域 ,对240宽的屏,480个字节就够了.
  36. 最大支持1024宽度的bmp编码 */
  37.     databuf = (uint16_t *)piclib_mem_malloc(2160);

  38.     if (databuf == NULL) return PIC_MEM_ERR;               /* 内存申请失败. */

  39.     f_bmp = (FIL *)piclib_mem_malloc(sizeof(FIL));                /* 开辟FIL字节的内存区域 */
  40.     if (f_bmp == NULL)                                     /* 内存申请失败 */
  41.     {
  42.         piclib_mem_free(databuf);
  43.         return PIC_MEM_ERR;
  44.     }
  45.     /* BMP头部设置 */
  46.     bmpheadsize = sizeof(hbmp);                                      /* 得到bmp文件头的大小 */
  47.     memset((uint8_t *)&hbmp, 0, sizeof(hbmp));                        /* 置零空申请到的内存 */

  48.     hbmp.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);    /* 信息头大小 */
  49.     hbmp.bmiHeader.biWidth       = width;                       /* bmp的宽度 */
  50.     hbmp.bmiHeader.biHeight      = height;                      /* bmp的高度 */
  51.     hbmp.bmiHeader.biPlanes      = 1;                           /* 恒为1 */
  52.     hbmp.bmiHeader.biBitCount    = 16;                      /* bmp为16位色bmp */
  53. hbmp.bmiHeader.biCompression = BI_BITFIELDS;/* 每个象素的比特由指定的掩码决定 */
  54. /* bmp数据区大小 */
  55. hbmp.bmiHeader.biSizeImage   = hbmp.bmiHeader.biHeight *
  56. hbmp.bmiHeader.biWidth * hbmp.bmiHeader.biBitCount / 8;                  

  57. hbmp.bmfHeader.bfType    = ((uint16_t)'M' << 8) + 'B';      /* BM格式标志 */
  58. /* 整个bmp的大小 */   
  59.     hbmp.bmfHeader.bfSize    = bmpheadsize + hbmp.bmiHeader.biSizeImage;
  60.     hbmp.bmfHeader.bfOffBits = bmpheadsize;           /* 到数据区的偏移 */

  61.     hbmp.RGB_MASK[0] = 0X00F800;        /* 红色掩码 */
  62.     hbmp.RGB_MASK[1] = 0X0007E0;        /* 绿色掩码 */
  63.     hbmp.RGB_MASK[2] = 0X00001F;        /* 蓝色掩码 */

  64.     if (mode == 1)
  65. {
  66.     /* 尝试打开之前的文件 */
  67.         res = f_open(f_bmp, (const TCHAR *)filename, FA_READ | FA_WRITE);      
  68.     }
  69.    
  70.     if (mode == 0 || res == 0x04)
  71. {
  72.     /* 模式0,或者尝试打开失败,则创建新文件 */
  73.         res = f_open(f_bmp, (const TCHAR *)filename, FA_WRITE | FA_CREATE_NEW);
  74.     }

  75.     if ((hbmp.bmiHeader.biWidth * 2) % 4)       /* 水平像素(字节)不为4的倍数 */
  76. {
  77.     /* 实际要写入的宽度像素,必须为4的倍数 */
  78.         bi4width = ((hbmp.bmiHeader.biWidth * 2) / 4 + 1) * 4;  
  79.     }
  80.     else
  81.     {
  82.         bi4width = hbmp.bmiHeader.biWidth * 2;  /* 刚好为4的倍数 */
  83.     }
  84.     /* 写入图像数据 */
  85.     if (res == FR_OK)   /* 创建成功 */
  86. {
  87.     /* 写入BMP首部 */
  88.         res = f_write(f_bmp, (uint8_t *)&hbmp, bmpheadsize, &bw);      
  89.                 /* 按一行一行操作 */
  90.         for (uint16_t ty = height - 1; hbmp.bmiHeader.biHeight; ty--)   
  91.         {
  92.             pixcnt = 0;

  93.             for (uint16_t xpix_index = 0; pixcnt != (bi4width / 2);)
  94.             {     
  95.                 if (pixcnt < hbmp.bmiHeader.biWidth)
  96.                 {
  97.                     databuf[pixcnt] = img_addr[pixcnt + hbmp.bmiHeader.biWidth
  98. * row_index];   
  99.                 }
  100.                 else
  101.                 {
  102.                     databuf[pixcnt] = 0Xffff;   /* 补充白色的像素 */
  103.                 }

  104.                 pixcnt++;
  105.                 xpix_index++;
  106.             }
  107.             hbmp.bmiHeader.biHeight--;
  108.             row_index++;
  109. /* 写入一行数据 */
  110.             res = f_write(f_bmp, (uint8_t *)databuf, bi4width, &bw);
  111.         }
  112.         f_close(f_bmp);
  113.     }


  114. #if SHOW_TIME == 1   
  115.     endTick = xTaskGetTickCount();
  116.     diffTick = endTick - startTick;
  117.     ESP_LOGI(__FUNCTION__, "elapsed time[ms]:%"PRIu32, diffTick * portTICK_PERIOD_MS);
  118. #endif

  119.     piclib_mem_free(databuf);
  120.     piclib_mem_free(f_bmp);

  121.     return res;
  122. }
复制代码
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 程序流程图

第四十二章 照相机实验9531.png
图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文件夹中:

  1. if (key_scan(0) == BOOT_PRES)       /* 按下BOOT按键进行拍照 */
  2. {
  3.     camera_new_pathname(pname, 0);  /* 得到文件名 */  
  4.     res=bmp_encode(pname,(uint16_t *)draw_buffer,lcddev.width,lcddev.height, 1);

  5.     text_show_string(30, 130, 240, 16, "请耐心等待,正在进行文件写入!", 16, 0, RED);
  6.     if (res)
  7.     {
  8.         text_show_string(30, 130, 240, 16, "写入文件错误!", 16, 0, RED);
  9.     }
  10.     else
  11.     {
  12.         text_show_string(30, 130, 240, 16, "拍照成功!", 16, 0, RED);
  13.         text_show_string(30, 150, 240, 16, "保存为:", 16, 0, RED);
  14.         text_show_string(30 + 56, 150, 240, 16, (char *)pname, 16, 0, RED);
  15.         vTaskDelay(pdMS_TO_TICKS(500));
  16.     }
  17. }
复制代码
lcd_cam_task摄像头任务函数实现已在第三十七章节中介绍过了,因此在此不再重复描述。
2,CMakeLists.txt文件
CMakeLists.txt文件跟摄像头实验一致,代码如下:

  1. idf_component_register(
  2.                         SRC_DIRS
  3.                             "."
  4.                             "APP/MIPI_CAM"
  5.                         INCLUDE_DIRS
  6.                             "."
  7.                             "APP/MIPI_CAM"
  8.                         )
复制代码
3,main.c驱动代码
在main.c里面编写如下代码。

  1. void app_main(void)
  2. {
  3.     esp_err_t ret;
  4.     uint8_t key = 0;

  5.     ret = nvs_flash_init();           /* 初始化NVS */
  6.     if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  7.     {
  8.         ESP_ERROR_CHECK(nvs_flash_erase());
  9.         ESP_ERROR_CHECK(nvs_flash_init());
  10.     }

  11.     led_init();                             /* LED初始化 */
  12.     key_init();                             /* KEY初始化 */
  13.     myiic_init();                           /* MYIIC初始化 */
  14.     lcd_init();                             /* LCD屏初始化 */

  15.     while (sdmmc_init())            /* 检测不到SD卡 */
  16.     {
  17.         lcd_show_string(30, 110, 200, 16, 16, "SD Card Error!", RED);
  18.         vTaskDelay(pdMS_TO_TICKS(200));
  19.         lcd_fill(30, 110, 239, 126, WHITE);
  20.         vTaskDelay(pdMS_TO_TICKS(200));
  21.     }

  22.     ret = exfuns_init();            /* 为fatfs相关变量申请内存 */

  23.     while (fonts_init())            /* 检查字库 */
  24.     {
  25.         lcd_clear(WHITE);
  26.         lcd_show_string(30, 30, 200, 16, 16, "ESP32-P4", RED);
  27.         
  28.         key = fonts_update_font(30, 50, 16, (uint8_t *)"0:", RED);  /* 更新字库 */

  29.         while (key)                 /* 更新失败 */
  30.         {
  31.             lcd_show_string(30, 50, 200, 16, 16, "Font Update Failed!", RED);
  32.             vTaskDelay(pdMS_TO_TICKS(200));
  33.             lcd_fill(20, 50, 200 + 20, 90 + 16, WHITE);
  34.             vTaskDelay(pdMS_TO_TICKS(200));
  35.         }

  36.         lcd_show_string(30, 50, 200, 16, 16, "Font Update Success!   ", RED);
  37.         vTaskDelay(pdMS_TO_TICKS(1000));
  38.         lcd_clear(WHITE);
  39.     }

  40.     text_show_string(30, 50,  200, 16, "正点原子ESP32-P4开发板", 16, 0, RED);
  41.     text_show_string(30, 70,  200, 16, "照相机 实验", 16, 0, RED);
  42.     text_show_string(30, 90,  200, 16, "正点原子@ALIENTEK", 16, 0, RED);
  43.     text_show_string(30, 110, 200, 16, "BOOT:TAKE PHOTO", 16, 0, RED);

  44.     vTaskDelay(pdMS_TO_TICKS(1000));
  45.     lcd_clear(WHITE);
  46.    
  47.     mipi_cam_init();    /* 摄像头初始化 */

  48.     while (1)
  49.     {
  50.         LED0_TOGGLE();
  51.         vTaskDelay(pdMS_TO_TICKS(500));
  52.     }
  53. }
复制代码
上述函数主要实现了硬件初始化、SD卡检测、字库更新、LCD显示和摄像头初始化等功能。程序首先初始化了NVS、LED、按键、I2C总线和LCD屏,随后检测SD卡是否存在并可用,如果未检测到SD卡则提示错误信息并反复重试。接着,程序检查并更新字库,确保显示文字的正确性。字库更新成功后,屏幕会显示开发板和实验相关的信息。在完成硬件和系统初始化后,摄像头被初始化准备拍照,同时LED灯每500毫秒闪烁,提示程序正常运行。

42.4 下载验证
将程序下载到开发板后,可以看到LCD首先显示一些实验相关的信息,如下图所示:

第四十二章 照相机实验13097.png
图42.4.1 显示实验相关信息

显示了上图的信息后,自动进入监控界面。可以看到LED0不停的闪烁,提示程序已经在运行了。此时,我们可以按下BOOT,即可进行bmp拍照。拍照得到的照片效果如下图所示:

第四十二章 照相机实验13200.png
图42.4.2 拍照样图
回复

使用道具 举报

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

本版积分规则


关闭

原子哥极力推荐上一条 /1 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2026-3-5 23:23

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表