之前做UI设计,每次用到特殊的字符,就要我重新做字库。 后面有几次要我贴图,可想而知,太鸡巴折腾了,做过的朋友都知道,后面就想着能不能让单片机虚拟U盘,然后图片拖进去,就直接显示出来。 PS:其实想做视频解码来着,后面懒了,先做图片解码。 折腾了一两天,最终还是实现了。 硬件组成: 1.STM32F4 2.OLED12864 3.W25Q64 软件组成: 1.DMA做SPI数据传输,基本底层驱动,就不解释了 2.W25Q64上做FATS文件系统,版本是 0 R5 j4 J( p2 E+ E
3.将W25Q64虚拟成U盘 蛮久以前折腾的了 4.编码解码及屏幕显示 其实解码不难,难的是数据对应,解码我大概花了两三小时,数据对应显示花了将近一天。 因为是OLED12864,只能显示8位的数据,也就没有什么颜色好说的了。 简述一下使用步骤。 首先设备上电,看到我用单片机+W25Q64模拟的U盘
! W6 ~* H" G( g
然后,做张BMP图片,放到U盘里面 % d, C' t) ]/ p2 M7 G6 ?
电脑打开图片是这样的,图片是我随便截图来的 ; n9 P. K/ K+ h; X8 Q6 L
然后,设备重启上电,OLED上面就显示这张图片。
, O9 C- e* X5 P
这样就意味着我之后想贴图就可以直接贴了,不要再搞来搞去的做字库。 首先是了解BMP的组成结构: ! n4 I, E; T# U4 R7 X; R
1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;3 S) `) ]# a X/ A% S
其结构定义如下:7 j! \, O; o( g. l
[size=1em][size=1em]1 , E. Q* Q, z, c6 l
[size=1em]2
& n! N+ x. h$ v0 B6 ~; \8 `8 Q[size=1em]3 , W6 d: G3 r0 {5 P1 [, T
[size=1em]4
1 s) P/ R5 s6 z! b[size=1em]5
: O; R" ?+ {# e6 R5 N0 B" L- \[size=1em]6
: m, @; M/ f7 z. \. S' v[size=1em]7 " ~9 w+ k& ~# y8 g5 w; E4 U1 P
[size=1em]8 # \7 Z- c7 f- x1 z [/ ]8 M4 Z- `0 A
[size=1em]9
; S! w; X R+ G, _ | [size=1em][size=1em]typedef struct tagBITMAPFILEHEADER ^1 j& d. y) T. Z' l3 P2 t# `
[size=1em]{) {! F M- K3 @
[size=1em] WORD bfType;//位图文件的类型,必须为BM(1-2字节)
1 l# s1 m @* G[size=1em] DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
$ Z+ t% ~# o( H; X% [[size=1em] WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
1 z* ^% K% |- \) _& F3 U9 y- K[size=1em] WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)9 m3 P$ e8 t3 U6 y
[size=1em] DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)" }- E" o. k2 g7 Y7 g4 Y
[size=1em] //文件头的偏移量表示,以字节为单位! {7 Y( J% s6 L- b6 _3 H
[size=1em]}__attribute__((packed)) BITMAPFILEHEADER;
0 j/ p e# F1 u7 V' D
8 j0 m3 T5 k" q A+ Y |
8 d, ~& ~9 p4 ?! w! t) j$ I3 X* ^8 y- O7 t1 _7 B
& c$ ~7 f1 v9 u+ @2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
1 G: T) P6 L% }8 qBMP位图信息头数据用于说明位图的尺寸等信息。* m; `. c3 Y% Y/ }; a
[size=1em][size=1em]1
0 i2 m9 r7 K6 F `, G$ T+ @5 \/ { V[size=1em]2 5 d8 L! d; n1 o- v3 ?" m/ g
[size=1em]3
1 G9 @' S) s) y5 l+ S4 G' h0 B8 e% e[size=1em]4 - W9 l% e; @7 L! ?! b
[size=1em]5
0 z9 b* W/ K9 t H6 i% T) ^[size=1em]6 6 L. V4 K# t% \0 a" y2 w; h8 S
[size=1em]7 + }, A M( w; c+ h$ [ P4 j w4 j- M/ I
[size=1em]8 % Q3 r4 L( i4 ?9 ~5 b1 c) M
[size=1em]9 - O4 n' X9 I$ Y" N5 ?4 L% q. K( w/ n
[size=1em]10 4 n3 Z$ e" Y9 Z* V
[size=1em]11 ! [7 `1 I! z9 @9 a* l/ |
[size=1em]12
5 D* C" i2 z; [3 P[size=1em]13
- v4 r# D6 h* _, C* H[size=1em]14 1 y9 F' N. p. {7 V% _
[size=1em]15 . b& h7 m& V9 a0 m4 @1 f9 s# K
| [size=1em][size=1em]typedef struct tagBITMAPINFOHEADER{7 Y! M& b1 X% {# m6 s
[size=1em]DWORD biSize;//本结构所占用字节数(15-18字节): f; G/ j5 \/ p! W& L3 v
[size=1em]LONG biWidth;//位图的宽度,以像素为单位(19-22字节): k% u# q+ o V
[size=1em]LONG biHeight;//位图的高度,以像素为单位(23-26字节), x5 N) g# ]* g* L1 \9 m
[size=1em]WORD biPlanes;//目标设备的级别,必须为1(27-28字节), `2 X. d" v. C: X. Z0 Z
[size=1em]WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节)0 S. Z/ X/ l b1 p. s" u
[size=1em]//4(16色),8(256色)16(高彩色)或24(真彩色)之一, S5 d. @& I; ^' v" g
[size=1em]DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)& r1 V! }7 D0 V' A0 r# S5 L! O+ j5 @
[size=1em]//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
. V o$ ]# _% ^' A[size=1em]DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)9 @) U( q2 Y3 f& M( v; _: k( u3 l
[size=1em]LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
" b/ @: M. Y+ U' \4 z/ P[size=1em]LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
' h, t2 D( U, [2 o[size=1em]DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
6 ]4 r! S9 o1 `5 m; K[size=1em]DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)
! @. `, s) ?# | r, ], ^) P }[size=1em]}__attribute__((packed)) BITMAPINFOHEADER;
, D; _. n# I8 C f. T: _5 ?. j2 Q) J0 A3 b
| % I) P$ E5 Z* b5 e
, z' z5 I- G2 d, Y
' L8 x% c6 V; u- w$ d4 L2 V3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
/ e7 V. n/ s9 ~( |, C% d& W颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:2 C# K1 u9 [% t
[size=1em][size=1em]1 3 t. s' M$ B" T+ x/ Q9 a3 j
[size=1em]2 ! t% O4 Z# J# x4 w- R6 c/ N
[size=1em]3
. ^+ o% v6 G' s5 ]* Y[size=1em]4 " F+ p6 p8 H5 u! B- x- Q, h. B& ?' z
[size=1em]5 2 i1 a+ r$ O3 g5 V* m
[size=1em]6
, X% J# C0 C v0 s8 r | [size=1em][size=1em]typedef struct tagRGBQUAD{
* l6 G$ Z# G+ l) L. x0 m[size=1em]BYTE rgbBlue;//蓝色的亮度(值范围为0-255)# Q, f& {) r5 U( k/ t; U
[size=1em]BYTE rgbGreen;//绿色的亮度(值范围为0-255)% |7 u+ ~8 }- y3 A/ u# n1 p, u B
[size=1em]BYTE rgbRed;//红色的亮度(值范围为0-255); T2 a- n7 Q3 j T
[size=1em]BYTE rgbReserved;//保留,必须为0) E& G! z9 S3 e( I% d
[size=1em]}__attribute__((packed)) RGBQUAD;
4 h$ i3 x q1 ?, Q$ A2 O3 f5 ]5 N$ a" T' `+ G5 b% N
|
" G b& z+ ]+ U" e2 [
7 J. |! ^" F; | m0 t X颜色表中RGBQUAD结构数据的个数有biBitCount来确定:% n; o: J. K* c' M& g
当biBitCount=1,4,8时,分别有2,16,256个表项;
# [1 b- t' |( x4 Z" o# N! N6 |7 `当biBitCount=24时,没有颜色表项。
% j3 F+ @; w/ y! O- X% X2 j& L位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
. N& |3 d" g% w& H% K3 B3 f$ q[size=1em][size=1em]1 4 N) r5 Z+ i9 q! {5 p- @0 O4 x# \
[size=1em]2 9 x z8 H0 E6 I7 b. \
[size=1em]3
8 i9 o3 o) d y+ J, G. R[size=1em]4
' i; m" \ I, z | [size=1em][size=1em]typedef struct tagBITMAPINFO{
+ [( T0 j7 R4 L% c; r$ P( B[size=1em]BITMAPINFOHEADER bmiHeader;//位图信息头
% S, t1 o) ]1 f% P* u5 s1 z[size=1em]RGBQUAD bmiColors[1];//颜色表5 _( E7 U4 u2 C- @ o) I6 v
[size=1em]}__attribute__((packed)) BITMAPINFO;6 m- s( X: h) u: |
% p) Y! F% ^$ _6 ]) p |
1 j2 B+ d8 R+ m2 ]; ^0 x% g% S& j2 `; Z# o
& f+ E4 a& C- D' e* j9 _3 a2 J- d g1 v x, ~8 p
4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。
) e1 [, j6 u3 _$ Y位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:9 c2 v$ b1 v3 g0 p3 m% C
当biBitCount=1时,8个像素占1个字节;2 {8 P J3 }6 g# d1 R
当biBitCount=4时,2个像素占1个字节;/ {( C3 x: s9 k, f0 Z" I; v- z1 L
当biBitCount=8时,1个像素占1个字节;
, l0 C+ {5 `) s7 v3 |2 u, b当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;
, n9 ~9 S' ?& Z' K* ^7 x7 _Windows规定一个扫描行所占的字节数必须是
( t0 z5 L/ Z8 ]* ^& U3 K3 @4的倍数(即以long为单位),不足的以0填充,9 c5 K. E8 \& Y9 ~3 O) i
biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
) W& X( U$ q( ^& T2 r' Y3 O0 N以上内容网上大同小异,都是一样的。 根据以上信息,首先我在程序里面做了一个结构体,存放除显示数据外的所有东西。
7 `6 Z+ [& V0 Q8 a( U; l
我不需要调色板,也就没用结构体了。 计算上面的数据,一共是62个8位数据,用来存放BMP的定义信息。 程序很简单,首先是U盘读取数据 ret = f_open(&fp, "246.bmp", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);* B( N/ s8 }6 a6 @
ret = f_read(&fp, readbuff, picsize, &unt); //读取文件头信息 然后提取前62个数据保存到上面说的结构体 Getpicture(readbuff);- s' L5 S$ s# v' m' v# `. t
1 M9 u" i' y- Q( [2 {- _
仿真确认数据正确性 然后是提取图片信息 9 z+ p+ g! y6 N. x& K9 x
一个简单的嵌套逻辑。 , f' |0 ?& d3 h9 _8 Z% r- o9 \$ \
最后是花了我将近一天的东西,数据显示对应 void Display_BMP_DOT(uint8_t bmp[64][16])% D+ Y" D" H" S0 e1 x0 B
{: g3 R: I1 y0 B$ n& e3 l* a% @8 F( I
unsigned char x,y,z,n,m;1 V. W$ Z/ D& D
uint8_t Oled_Draw_BMP_buff[128];
1 G! E$ u4 \' L/ K
% t6 e' ?, \4 E; v
0 q4 ?6 F- s: ~ for(m=0;m<64;m++)
0 j g2 M& r# f: L0 m1 k* B% e {5 V' h1 j6 J( z+ T- X6 a( b9 `& \- ]
for(n=0;n<16;n++)2 k$ L+ T) o6 A9 m- p" R% [1 M
{
! H2 _: X3 j$ ^# {2 }; A z = 0x80;6 f2 M0 X! P& P9 B
for(x=0;x<8;x++)3 B% q6 p& B! ]9 K, n0 r$ _
{ $ b2 L/ b* g5 @- D x
Oled_Dot(x+8*n-1,63-m,bmp[m][n]&(z>>x));
$ y! o" [0 ~! I; s0 f }% P- I' ?- @0 F: d
}
I9 e* _, G1 u- e$ h }
7 B4 a0 u( {) n6 ?: q C. t6 S Display_Process(OLED_Display_Data); 6 @2 S) O4 `/ V- h; O) }
} 这一步的难度在 OLED12864支持的是页写,之前做驱动的时候,刷屏是从左至右从上到下,但是WINDOWS上面,图片提取是,从左至右,从下至上。 ' F3 ^ s- h0 }0 M6 i( ^- N
|