1. 引言
截图是很多应用场合都需要的一项功能,对于无外存等小内存场合,同时又无法读取像素点的情况,截图通常需要多个组件的配合。本文采用的STM32G4微控制器,使用的组件如下:
- GUI图形库:LVGL;
- 文件系统:FATFS。
其中,显示驱动采用ST7789,存储截图的介质为SD卡。另,为方便读写和查看,本文采用BMP编码。
2. 准备工作
- ST7789驱动及LVGL移植:参见《ST7789驱动 》和《STM32移植LVGL驱动ST7789》;
- SD驱动及FATFS移植:参见《SPI驱动SD卡及FATFS移植》;
- BMP编码的相关代码实现:这里我们采用最适配的16位无颜色图RGB555格式的存储方式,注意!是RGB555格式,无像素掩码信息时Windows系统默认采用的时该格式,与GUI图形库常用的RGB565是不一样的,之前截图颜色总是有类似噪点的颜色偏差,后经“lvgl技术交流”群中@Def、大哥的指出才发现原来是因为颜色格式搞错了,详细信息可参阅微软的相关文档。为实现BMP编码,主要需要给出BMP文件头的定义和初始化方法,分
ubmp.h
和ubmp.c
两个文件。
ubmp.h
文件完整代码如下:
/**
*******************************************************************************
* @file ubmp.h
* @author xixizhk
*******************************************************************************
* @version V2022 @ Nov 1, 2022 \n
* Initial version, only 16bit BMP without compression supported.
* @version V2022 @ Nov 29, 2022 \n
* Specified for ONLY RGB555, with bugs fixed. @see:
* https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/
*******************************************************************************
*/
/* Define to prevent recursive inclusion **************************************/
#ifndef _UBMP_H
#define _UBMP_H
#ifdef __cplusplus
extern "C" {
#endif
/**
*******************************************************************************
* @addtogroup Includes
* @{
*/
#include "stdint.h"
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Definitions
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Types
* @{
*/
/**
* @brief File header, 14 bytes, packed.
*/
typedef struct
{
/* File type, "BM" (0x424D) for .bmp file. */
uint16_t bfType;
/* File size in byte, including all the header, etc.. */
uint32_t bfFileSize;
/* Reserved, 0. */
uint16_t bfReserved1;
/* Reserved, 0. */
uint16_t bfReserved2;
/* Offset of pixel data from the beginning of the file header, in byte. */
uint32_t bfOffsetBytes;
} __attribute__((__packed__)) ubmp_file_header_t;
/**
* @brief Information header, 40 bytes, packed.
*/
typedef struct
{
/* Size of the information header in byte, 40 */
uint32_t biHeaderSize;
/* Width of the picture, in pixel. */
uint32_t biWidth;
/* Height of the picture, in pixel. */
uint32_t biHeight;
/* Planes of bit plane, 1. */
uint16_t biPlanes;
/* Number of bits for one pixel. */
uint16_t biBitCount;
/* Compression options. */
uint32_t biCompression;
/* Size of the pixel data in byte, NOT equal to biWidth * biHeight. */
uint32_t biDataSize;
/* Pixels per meter (X). */
uint32_t biXPelsPerMeter;
/* Pixels per meter (Y). */
uint32_t biYPelsPerMeter;
/* Used color, 0. */
uint32_t biColorUsed;
/* Important color, 0.*/
uint32_t biColorImportant;
} __attribute__((__packed__)) ubmp_info_header_t;
/**
* @brief Type definition for BMP handler.
*/
typedef struct
{
/* File header. */
ubmp_file_header_t FileHeader;
/* Information header. */
ubmp_info_header_t InfoHeader;
/* Header size in byte. */
uint32_t HeaderSize;
/* Line size in byte. */
uint32_t LineSize;
} __attribute__((__packed__)) ubmp_rgb555_handler;
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Constants
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Variables
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Macros
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Functions
* @{
*/
void ubmp_init_rgb555(ubmp_rgb555_handler* _ubmp, uint32_t _w, uint32_t _h);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* _UBMP_H */
/**************************** ALL RIGHTS RESERVED *****************************/
ubmp.c
文件完整代码如下:
/**
*******************************************************************************
* @file ubmp.c
* @author xixizhk
*******************************************************************************
* @version V2022 @ Nov 1, 2022 \n
* Initial version, only 16bit BMP without compression supported.
* @version V2022 @ Nov 29, 2022 \n
* Specified for ONLY RGB555, with bugs fixed. @see:
* https://learn.microsoft.com/zh-cn/windows/win32/api/wingdi/
*******************************************************************************
*/
/**
*******************************************************************************
* @addtogroup Includes
* @{
*/
#include "ubmp.h"
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Definitions
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Types
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Constants
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Variables
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Macros
* @{
*/
/**
* @}
*/
/**
*******************************************************************************
* @addtogroup Functions
* @{
*/
/**
* @brief Initialize a BMP handler for RGB555.
* @param _ubmp [Out]: point to the BMP handler.
* @param _w [In]: width of the picture in pixel.
* @param _h [In]: height of the picture in pixel.
* @retval None.
*/
void ubmp_init_rgb555(ubmp_rgb555_handler* _ubmp, uint32_t _w, uint32_t _h)
{
_ubmp->FileHeader.bfType = 0x4D42;
_ubmp->FileHeader.bfReserved1 = 0;
_ubmp->FileHeader.bfReserved2 = 0;
_ubmp->FileHeader.bfOffsetBytes = 54;
_ubmp->InfoHeader.biHeaderSize = 40;
_ubmp->InfoHeader.biWidth = _w;
_ubmp->InfoHeader.biHeight = _h;
_ubmp->InfoHeader.biPlanes = 1;
/* RGB555. */
_ubmp->InfoHeader.biBitCount = 16;
_ubmp->InfoHeader.biCompression = 0;
_ubmp->InfoHeader.biXPelsPerMeter = 10000;
_ubmp->InfoHeader.biYPelsPerMeter = 10000;
_ubmp->InfoHeader.biColorUsed = 0;
_ubmp->InfoHeader.biColorImportant = 0;
_ubmp->LineSize = (((_w << 4) + 31) >> 5) << 2;
_ubmp->InfoHeader.biDataSize = _ubmp->LineSize * _h; /* Can also be 0. */
_ubmp->FileHeader.bfFileSize = 54 + _ubmp->InfoHeader.biDataSize;
_ubmp->HeaderSize = 54;
}
/**
* @}
*/
/**************************** ALL RIGHTS RESERVED *****************************/
3. 实现方法
为保持截图数据的完整,建议采用类似“触发”的方式结合lvgl定时器实现截图。
- Step 1:定义相关的变量:
static volatile uint8_t sstrigr = 0U;
static FATFS ssfs;
static FIL ssfile;
static FRESULT ssfres;
static UINT ssbn;
static char sspath[32];
static lv_res_t sslvres;
static uint8_t ssbuffer[1024];
static lv_obj_t* ssscreen;
static ubmp_rgb555_handler ss;
- Step 2:实现截图函数:
/**
* @brief Callback function for snapshot.
* @param timer [In]: pointer to the timer.
* @retval None.
*/
static void ss_cb(lv_timer_t * timer)
{
lv_img_dsc_t dsc;
lv_coord_t y1, y2;
uint16_t i, j, w, tmp;
char diskletter[2] = "0";
if (sstrigr != 1U)
{
return;
}
y1 = ssscreen->coords.y1;
y2 = ssscreen->coords.y2;
w = ssscreen->coords.x2 - ssscreen->coords.x1 + 1;
ubmp_init_rgb555(&ss, w, y2 - y1 + 1);
diskletter[0] = sspath[0];
ssfres = f_mount(&ssfs, sspath, 1);
ssfres = f_open(&ssfile, sspath, FA_CREATE_ALWAYS | FA_WRITE |FA_READ);
ssfres = f_write(&ssfile, &ss, ss.HeaderSize, &ssbn);
for (i = y2; i >= y1; i--)
{
ssscreen->coords.y1 = i;
ssscreen->coords.y2 = i;
sslvres = lv_snapshot_take_to_buf
(
ssscreen,
LV_IMG_CF_TRUE_COLOR,
&dsc,
ssbuffer,
sizeof(ssbuffer)
);
/* Convert to RGB555. */
for (j = 0; j < w; j++)
{
tmp = *(((uint16_t* )ssbuffer) + j);
*(((uint16_t* )ssbuffer) + j) = (tmp & 0x001F) + ((tmp & 0xFFC0) >> 1U);
}
for (j = 2 * w; j < ss.LineSize; j++)
{
ssbuffer[j] = 0;
}
ssbn = ss.LineSize;
ssfres = f_write(&ssfile, ssbuffer, ssbn, &ssbn);
if (i == 0U)
{
break;
}
}
ssfres = f_close(&ssfile);
ssfres = f_mount(0, diskletter, 1);
ssscreen->coords.y1 = y1;
ssscreen->coords.y2 = y2;
sstrigr = 0U;
}
- Step 3:新建LVGL定时器对象,并将上述
ss_cb
函数作为其回调:
lv_timer_create(ss_cb, 0, NULL);
- Step 4:采用“触发”加信息传递的方式实现供外部调用的截图函数:
/**
* @brief Take the snapshot of specified screen to SD card.
* @note: screen with border NOT supported.
* @param _path [In]: path to save the snapshot.
* @note: format: diskletter + : + file name, eg: 0:ss.bmp.
* @param _src [In]: pointer to the screen object.
* @retval None.
*/
void snapshot(char* _path, lv_obj_t* _scr)
{
uint8_t i;
for (i = 0; i < sizeof(sspath) - 1; i++)
{
sspath[i] = _path[i];
}
sspath[sizeof(sspath) - 1] = '\0';
ssscreen = _scr;
sstrigr = 1U;
}
4. 截图效果
如下。
Provided by 昨夜三更雨, see https://www.cnblogs.com/zysgy/p/16937248.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)