本帖最后由 shipeng1989 于 2021-7-1 15:29 编辑
从事单片机开发有些年头,受益于LCD的成本不断降低我们的产品终于也用上了彩色LCD。经过最近一年的摸索我总结出了一套不同于常规的单片机彩屏控制方法,现把它分享出来希望可以得到高人的进一步完善或加强。
GUI我还没来得及研究,操作系统也是。所以我的方法比较低端,谈不上高大上。唯一能值得说道的也就是用上了FATFS文件系统来存储图片文件,单片机在取用图片的时候还算方便简洁,最关键是为了方便后期图片文件调整。另外本方法只适用于RGB565格式的bmp位图。
我的这个方法始于一个执念:怎样让单片机直接打开标准图片文件,从而在图片格式上实现电脑与单片机“统一度量衡”。考虑到单片机的算力和刷屏速度不可与电脑同日而语,这个统一的格式必须一边倒的偏袒单片机。那么有没有这样一种符合要求的图片格式呢?确实有,只是这种格式似乎早已被常用的图片处理软件所摒弃,很难找到它的踪迹。它的名字叫16-BPP-BF-565,采用十六位颜色编码其中红色和蓝色各占5位,绿色占用6位。这种编码格式刚好和我用的LCD屏格式吻合,颜色编码问题解决了还有一个图像扫描顺序的问题需要解决:在电脑上的图片一般的扫描顺序都是:从图片的左下角开始从左到右从下至上一行一行存储图片的像素数据
但是这种扫描顺序却不适用于我用的LCD屏,LCD屏的扫描顺序是:从图片的左上角开始从左到右从上至下一行一行写入像素数据
对于这个问题要如何统一呢?其实16-BPP-BF-565对于扫描顺序是有定义的:在图片文件起始的偏移0x16位置,长度4个字节,表示图片的垂直方向像素数(图片高度)。
当这个数为正数时即表示扫描顺序为从下至上,为负数则为从上至下扫描(高度为其绝对值)。至此统一度量衡已经是水到渠成了。
BMP格式之16bpp-BF-565.zip
(13.58 KB)
关于16-BPP-BF-565详情请点击此链接
另外再分享一个我用Visual Studio写的将普通的BMP位图文件(BMP888)转为BMP565(扫描顺序也转为从上至下)的工具软件
BMP888to565CN.zip
(6.28 KB)
使用方法:将此文件解压后得到“BMP888To565.exe”可执行文件,将这个可执行文件放在你要转换的BMP888图片文件同一路径下,双击运行此可执行文件即可在同路径下创建BMP565文件夹并将转换后的原图片同名文件放置其中非常方便。接下来今天的重点来了,会单片机编程的往往都不会电脑编程,一直以来我都认为电脑编程和单片机编程之间有一道难以逾越的鸿沟,对于一个懒癌晚期的我来说是一辈子都难以跨越的。现在好了我发现只要掌握几个库函数的使用只有单片机C基础的人也可以使用电脑编程来服务于自己的不时之需。下面我把我写的“BMP888To565”源代码分享出来大家一起来围观一下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <io.h>
#include <windows.h>
#include <direct.h>
#include <wingdi.h>
#include "main.h"
int main()
{
int res = 0,file_count = 0;
int32_t buf32; long Handle = 0;
uint8_t array_buffer[0x42] = { 0 };
FILE* fps = 0;
struct _wfinddata_t FileInfo; TCHAR lpFileName[MAX_PATH];
GetModuleFileName(NULL, lpFileName, MAX_PATH);
uint16_t path_end = 0;
for (; path_end < MAX_PATH - 6; path_end++)if (lpFileName[path_end] == L'\0')break;
for (; path_end != 0; path_end--)if (lpFileName[path_end - 1] == L'\\')break;
for (uint16_t i = 0; i < 6; i++)lpFileName[path_end + i] = *(L"*.BMP\0" + i);
Handle = _wfindfirst(lpFileName, &FileInfo);
DO_NEXT:
if (Handle == -1L)
{
//_findclose(Handle);
printf_s("There Is No Matching File!\n");
fps = 0;
}
else
{
if (FileInfo.attrib != 0x20 || FileInfo.size == 0)goto JMP1;
size_t s_len = wcslen(FileInfo.name);
wprintf_s(L"CONVERTIONG:%ws\n",FileInfo.name);
memcpy_s(lpFileName + path_end, s_len*2, FileInfo.name, s_len*2);
if (path_end + s_len < MAX_PATH)
{
lpFileName[path_end + s_len] = L'\0';
_wfopen_s(&fps, lpFileName, L"rb");
}
else fps = 0 , printf_s("Source Path Is Too Long!\n");
}
if (fps!=NULL)
{
fread(array_buffer, 1, 0x36, fps);
if (*(uint16_t*)(&array_buffer[bfType]) != ('M' << 8 | 'B'))
{
printf_s("Not A BMP File!\n");
res = 1;
}
else if (*(uint32_t*)(&array_buffer[bfSize]) > 10UL*1024UL*1024UL)
{
printf_s("File Size Over Flow!\n");
res = 2;
}
else if (*(uint32_t*)(&array_buffer[biWidth]) > 4096UL)
{
printf_s("Width is out of range!\n");
res = 3;
}
else if (*(uint32_t*)(&array_buffer[biBitCount]) != 24UL)
{
printf_s("Not A BMP888 File!\n");
res = 4;
}
else
{
FILE* fpd = 0;uint32_t width32 = *(uint32_t*)(&array_buffer[biWidth]);
uint8_t* data_buffer = (uint8_t*)malloc(10UL * 1024UL * 1024UL);
uint8_t* put_cache = (uint8_t*)malloc(10UL * 1024UL * 1024UL);//scanf_s("%s", FileNameString, FILENAME_MAX);
size_t s_len = wcslen(FileInfo.name);
for (uint16_t i = 0; i < path_end + 8; i++)lpFileName[i] = i < path_end ? lpFileName[i] : *(L"BMP565\\" + i - path_end);
if (_waccess(lpFileName, 0) != 0 && _wmkdir(lpFileName) != 0)printf_s("Path Creating Faild!\n");
memcpy_s(lpFileName + path_end + 7, s_len*2, FileInfo.name, s_len*2);
if (path_end + 7 + s_len < MAX_PATH)
{
lpFileName[path_end + 7 + s_len] = L'\0';
_wfopen_s(&fpd, lpFileName, L"wb");
}
else fpd = 0, printf_s("Dest. Path Is Too Long!\n");
if (fpd != NULL && data_buffer != 0 && put_cache != 0)
{
int32_t height32 = 0;
uint32_t BitMapSize = *(uint32_t*)(&array_buffer[biSizeImage]);
printf_s("File Checking Passed! \n Converting...\n");
buf32 = *(uint32_t*)(&array_buffer[bfOffBits]);
*(uint32_t*)(&array_buffer[bfOffBits]) = 0x42;
height32 = *(int32_t*)(&array_buffer[biHeight]);
fseek(fps, buf32, SEEK_SET);
if (BitMapSize == fread(data_buffer, 1, BitMapSize, fps))
{
uint16_t* put_ptr16 = (uint16_t*)(put_cache + 0x42);
if (height32 > 0)
{
*(int32_t*)(&array_buffer[biHeight]) = 0 - height32;
*(uint32_t*)(&array_buffer[bfSize]) = 0x42 + width32 * height32 * 2;
*(uint32_t*)(&array_buffer[biSizeImage]) = width32 * height32 * 2;
for (uint32_t i = height32; i != 0; i--)
{
for (uint32_t j = 0; j < width32; j++)
{
buf32 = *(uint32_t*)&data_buffer[((i - 1) * width32 + j) * 3] & 0x00FFFFFF;
*put_ptr16++ = (buf32 >> 8 & 0xF800) | (buf32 >> 5 & 0x07E0) | (buf32 >> 3 & 0x001F);
}
}
}
else
{
height32 = 0 - height32;
*(uint32_t*)(&array_buffer[bfSize]) = 0x42 + width32 * height32 * 2;
*(uint32_t*)(&array_buffer[biSizeImage]) = width32 * height32 * 2;
for (uint32_t i = 0; i < width32 * height32 / 2; i++)
{
*put_ptr16++ = (data_buffer[i * 6 + 2] << 8 & 0xF800) | (data_buffer[i * 6 + 1] << 3 & 0x07E0) | (data_buffer[i * 6 + 0] >> 3 & 0x001F);
*put_ptr16++ = (data_buffer[i * 6 + 5] << 8 & 0xF800) | (data_buffer[i * 6 + 4] << 3 & 0x07E0) | (data_buffer[i * 6 + 3] >> 3 & 0x001F);
}
}
*(uint16_t*)(&array_buffer[biBitCount]) = 16;
*(uint32_t*)(&array_buffer[biCompression]) = BI_BITFIELDS;
*(uint32_t*)(&array_buffer[RedMask]) = 0x0000F800;
*(uint32_t*)(&array_buffer[GreenMask]) = 0x000007E0;
*(uint32_t*)(&array_buffer[BlueMask]) = 0x0000001F;
memcpy(put_cache, array_buffer, 0x42);
fwrite(put_cache, 2, 0x42/2 + width32 * height32, fpd);
printf_s("%u. File Converting Completed!\n", ++file_count);
}
else printf_s("Bit Map Size Don't Match The File Size!\n");
fclose(fpd);
}
else printf_s("Dest. File Pointer Is Null!\n");
free(data_buffer); free(put_cache);
}
fclose(fps);
}
else printf_s("Source File Pointer Is Null!\n");
JMP1:
if (_wfindnext(Handle, &FileInfo) == 0)goto DO_NEXT;
printf_s("Totle Files:%d\n", file_count);
_findclose(Handle);
system("pause");
return res;
}
另外还有几个main.h头文件的宏定义:
#include <stdint.h>
#define bfType 0x0000
#define bfSize 0x0002
#define bfOffBits 0x000a
#define biWidth 0x0012
#define biHeight 0x0016
#define biBitCount 0x001c
#define biCompression 0x001e
#define biSizeImage 0x0022UL
#define RedMask 0x0036UL
#define GreenMask 0x003aUL
#define BlueMask 0x003eUL
都是用单片机编程很常见的C语言写的,怎么样是不是很简单为表诚意再附上Visual Studio工程包
BMP888to565CN.zip
(538.39 KB)
|