之前做UI设计,每次用到特殊的字符,就要我重新做字库。 后面有几次要我贴图,可想而知,太鸡巴折腾了,做过的朋友都知道,后面就想着能不能让单片机虚拟U盘,然后图片拖进去,就直接显示出来。 PS:其实想做视频解码来着,后面懒了,先做图片解码。 折腾了一两天,最终还是实现了。 硬件组成: 1.STM32F4 2.OLED12864 3.W25Q64 软件组成: 1.DMA做SPI数据传输,基本底层驱动,就不解释了 2.W25Q64上做FATS文件系统,版本是
* C2 f4 s+ L2 J* \" h! C
3.将W25Q64虚拟成U盘 蛮久以前折腾的了 4.编码解码及屏幕显示 其实解码不难,难的是数据对应,解码我大概花了两三小时,数据对应显示花了将近一天。 因为是OLED12864,只能显示8位的数据,也就没有什么颜色好说的了。 简述一下使用步骤。 首先设备上电,看到我用单片机+W25Q64模拟的U盘 5 y6 N/ M6 n; o4 A* n/ S- {
然后,做张BMP图片,放到U盘里面
+ Z$ }+ {& R& R
电脑打开图片是这样的,图片是我随便截图来的 " P( V7 U* @/ Z+ X$ E, a; S: V
然后,设备重启上电,OLED上面就显示这张图片。
/ W4 P7 F; D# j/ J9 J: Y4 g
这样就意味着我之后想贴图就可以直接贴了,不要再搞来搞去的做字库。 首先是了解BMP的组成结构: ! `- G- Z2 e+ _5 ~ a9 _
1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;
. e2 b! Z. U, W1 g4 v其结构定义如下:2 ~6 e: Z: j1 ~( v
[size=1em][size=1em]1
5 u& T$ [. o) w1 L7 t9 A2 V[size=1em]2 0 A# [! i8 R6 S+ s* U
[size=1em]3
; }9 k* b& X0 b$ ?* f E4 \[size=1em]4 7 \5 s+ l3 t: Z3 l
[size=1em]5
5 H) ]: G$ r' ~2 g# r& C, U: Z[size=1em]6 3 O) b y: d$ r3 S& |* S+ O5 p9 ]
[size=1em]7 ' v3 i: M8 \5 T" {4 ]( m1 F2 T; G( Q
[size=1em]8 6 \+ ^* u8 t+ M: E& [0 X% Q- ]
[size=1em]9
1 \4 ]% b6 Z; a; I | [size=1em][size=1em]typedef struct tagBITMAPFILEHEADER
* `; G6 J9 {- ^& F& L[size=1em]{
0 H3 {3 \ Q9 @[size=1em] WORD bfType;//位图文件的类型,必须为BM(1-2字节)6 }6 J; `7 g5 n. U z6 C" ^! K0 e
[size=1em] DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
$ G3 q9 r8 F t% y$ I' r( B; }[size=1em] WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
6 p0 E( A; e$ p$ U3 F- s[size=1em] WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)8 `8 t! |) i, |2 w
[size=1em] DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)
2 R0 R1 q2 X* i. h8 z/ O2 A[size=1em] //文件头的偏移量表示,以字节为单位
; J+ m& k/ B' I8 p[size=1em]}__attribute__((packed)) BITMAPFILEHEADER;4 y( y" Y8 M) D: c4 |
4 `! c! t9 s3 N7 b% L$ L2 @
| |4 Y" R+ g$ d. N- O+ A- T3 r
4 U- l7 U7 x# x$ W' F
$ C9 q6 d" w+ b' d2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
- D2 B+ G. ^) wBMP位图信息头数据用于说明位图的尺寸等信息。% S# d% X* y% k2 U5 O( C4 Z
[size=1em][size=1em]1
3 z# f u s0 e. d4 `[size=1em]2
8 _$ j9 m3 u6 v+ D/ S0 @# |+ h[size=1em]3
) ?3 s: g ]- o! r* y[size=1em]4 , W# P. ~; q( A0 [& {/ H2 l
[size=1em]5 / s' v" H3 ?6 b# J# {- L$ P+ M9 [( D
[size=1em]6
$ s; E5 s3 j" w) @: n3 M[size=1em]7
9 V4 v, ~# h! s! |1 W1 ?: |. N4 m; a2 n[size=1em]8
) B2 t+ C6 R, H, Z[size=1em]9 . s. V! ]: l& E' Q" E: u* I
[size=1em]10 0 c$ H- {7 b. T9 s
[size=1em]11 2 p8 W! [& M' U4 g# Y
[size=1em]12
I& u; G1 Z! ?* `* r: u1 f[size=1em]13 ; q" a* `9 i# _8 N* H
[size=1em]14
1 h' g7 u6 d5 z& p0 ^9 O% U, e[size=1em]15
0 J7 n: ?$ b, C" {( k* m0 N | [size=1em][size=1em]typedef struct tagBITMAPINFOHEADER{( X% {+ J( V( W% i& X
[size=1em]DWORD biSize;//本结构所占用字节数(15-18字节)( A" `3 @/ t/ b1 \
[size=1em]LONG biWidth;//位图的宽度,以像素为单位(19-22字节)
8 x B# B* v4 t: A1 ] ~4 v. V( q[size=1em]LONG biHeight;//位图的高度,以像素为单位(23-26字节)- l' v5 k% y4 N2 _
[size=1em]WORD biPlanes;//目标设备的级别,必须为1(27-28字节), q, i6 \5 K" h A- Q
[size=1em]WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节)
- V9 m; t6 W- V/ ^[size=1em]//4(16色),8(256色)16(高彩色)或24(真彩色)之一& G7 i( V8 Y0 X" ~
[size=1em]DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
6 g5 I/ C8 E1 @( @# W+ H[size=1em]//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
/ u ?% M# q: w$ e# S[size=1em]DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)( G! W Z& ]3 X% N" S8 `- x2 f Q
[size=1em]LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)$ x7 c- P) a+ |1 m$ h/ J
[size=1em]LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)( U3 p& Q; r, v4 ~
[size=1em]DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
/ D! p$ [8 q" q[size=1em]DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)3 R( ]! l; A6 x2 B8 K4 S
[size=1em]}__attribute__((packed)) BITMAPINFOHEADER;
( h" {% { [9 y4 L: _0 D( o0 |7 h: R c# P
|
0 O5 J5 x8 C. u- C
* L4 @5 ?. v: g
9 g0 g; c, M0 x- d3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;0 Y) _! s: j8 M* X+ d
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:6 H9 N( |; }- c" P( _( u
[size=1em][size=1em]1 5 u7 B9 F: q; g2 T Q& Y& E
[size=1em]2
g' W; m9 h5 W[size=1em]3 + x2 R% h* X8 r6 P
[size=1em]4 }' \4 s1 ~$ R5 l z! g
[size=1em]5 , H* K/ C( P; X3 @ |" A' u+ i. P& V
[size=1em]6
8 c1 D4 i$ }. w+ [& M+ o! u | [size=1em][size=1em]typedef struct tagRGBQUAD{* {# b! V8 S: g8 Q; [; s
[size=1em]BYTE rgbBlue;//蓝色的亮度(值范围为0-255); A. W6 p0 i+ C+ R7 s
[size=1em]BYTE rgbGreen;//绿色的亮度(值范围为0-255)5 b) i6 g( Z$ J/ c5 r% P
[size=1em]BYTE rgbRed;//红色的亮度(值范围为0-255)
, |- ~4 n- I/ m) H: L5 \8 g[size=1em]BYTE rgbReserved;//保留,必须为0
# Z+ D$ l, h2 N8 Q$ O! T! z[size=1em]}__attribute__((packed)) RGBQUAD;9 {8 \2 k0 Y. @$ n8 E X: S
" ^; }$ e4 Q& U' @% r
|
; b. [$ I0 k- F% ~- D" p1 s) E8 x' ~3 d2 L
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
$ H6 U, F# s0 q2 M当biBitCount=1,4,8时,分别有2,16,256个表项;7 q9 e8 J3 A$ X3 e) y3 I
当biBitCount=24时,没有颜色表项。
# j" |0 L7 |) ^/ c, G位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:. w4 P1 c$ T$ p% m( _+ N1 ?/ [# P
[size=1em][size=1em]1 . ^2 |7 c. g. v; Z% Z7 ~' J; ]
[size=1em]2 . u5 f9 a' t) b
[size=1em]3
! F- ` w7 Q) P# O \* ~: M[size=1em]4
( g/ z" V& P' g; M+ z& J0 b | [size=1em][size=1em]typedef struct tagBITMAPINFO{
" J8 } ^; d$ M. ?, [0 p k[size=1em]BITMAPINFOHEADER bmiHeader;//位图信息头, b L: B; F2 i* M( f3 k! ?; K$ h
[size=1em]RGBQUAD bmiColors[1];//颜色表
, e, g) b l0 m/ L* L. m* ?) U[size=1em]}__attribute__((packed)) BITMAPINFO;* Z6 s( d8 \+ C9 A b
/ S3 w1 M& }- P/ z | % @' C! R. i8 b p
& ^6 P. W5 n& H r4 ?. g$ Z& U4 T0 U8 {8 g% b8 \, h
: z, ]' T% }& f1 g& M
4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。& H2 k) O( \9 i
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
. l+ k F2 Y- w1 b1 f5 n, P& o# B当biBitCount=1时,8个像素占1个字节;
% e4 P! L+ |1 z$ m5 Z当biBitCount=4时,2个像素占1个字节;; |. d8 p2 f. h- k- ]! ^
当biBitCount=8时,1个像素占1个字节;
" m4 ^: E5 W. P. S- z) S6 _& ^当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;
?$ e W# X% c: UWindows规定一个扫描行所占的字节数必须是
$ ~' N8 a" N# o4的倍数(即以long为单位),不足的以0填充," M4 o1 |$ e- x$ @- L2 ]# i- S: [# R
biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
. j7 e9 K5 I0 X2 e$ [& Z以上内容网上大同小异,都是一样的。 根据以上信息,首先我在程序里面做了一个结构体,存放除显示数据外的所有东西。
1 x! L4 z' ]" _ ?1 l8 R( S' F4 b
我不需要调色板,也就没用结构体了。 计算上面的数据,一共是62个8位数据,用来存放BMP的定义信息。 程序很简单,首先是U盘读取数据 ret = f_open(&fp, "246.bmp", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
- E* R8 n" K6 M. T5 m+ j- A- l ret = f_read(&fp, readbuff, picsize, &unt); //读取文件头信息 然后提取前62个数据保存到上面说的结构体 Getpicture(readbuff);/ Q$ S- A3 s2 x2 V1 E+ s
, W8 \* ?" n D2 r( v+ H
仿真确认数据正确性 然后是提取图片信息
7 L4 `; \% g) H8 Q- \. C' u
一个简单的嵌套逻辑。
/ O9 R' i3 O! \5 X最后是花了我将近一天的东西,数据显示对应 void Display_BMP_DOT(uint8_t bmp[64][16]) i) j1 c* t9 I& t. l1 k" m X
{
! w% B0 x: K, p2 S unsigned char x,y,z,n,m;
( ^ b2 Q p& B uint8_t Oled_Draw_BMP_buff[128];
+ X$ I7 t1 \0 w- p' C% ^: x% Z% X
, G: b5 Z% t& [. ]+ ?* s
for(m=0;m<64;m++)
# M3 j" T0 N+ \# z {
; B# X. Y. d0 A5 k! R2 @6 e4 I for(n=0;n<16;n++)% y' A5 e" w3 i5 o: u: [/ H2 |
{ ) |: E7 [- E! F/ B6 O
z = 0x80;/ e/ ^9 z& t' D4 a. f* m
for(x=0;x<8;x++)
* P& e8 F! e8 I" {- C6 ? { * E& a8 k. Y6 J$ y
Oled_Dot(x+8*n-1,63-m,bmp[m][n]&(z>>x)); 4 O4 V" [! U* y
}6 u7 N( Z) V/ z% _8 N. H O" e
}) a9 i! A0 h$ ^
}
8 r: b9 Q7 G1 t% ]0 o* Q, {% H Display_Process(OLED_Display_Data);
* L. @4 O7 _ m8 y U5 s+ A; c$ Z } 这一步的难度在 OLED12864支持的是页写,之前做驱动的时候,刷屏是从左至右从上到下,但是WINDOWS上面,图片提取是,从左至右,从下至上。
0 K8 l2 M7 _' d+ G% n X- T: X |