|
之前做UI设计,每次用到特殊的字符,就要我重新做字库。 后面有几次要我贴图,可想而知,太鸡巴折腾了,做过的朋友都知道,后面就想着能不能让单片机虚拟U盘,然后图片拖进去,就直接显示出来。 PS:其实想做视频解码来着,后面懒了,先做图片解码。 折腾了一两天,最终还是实现了。 硬件组成: 1.STM32F4 2.OLED12864 3.W25Q64 软件组成: 1.DMA做SPI数据传输,基本底层驱动,就不解释了 2.W25Q64上做FATS文件系统,版本是 $ O; Q# q; h8 ?* T! `
3.将W25Q64虚拟成U盘 蛮久以前折腾的了 4.编码解码及屏幕显示 其实解码不难,难的是数据对应,解码我大概花了两三小时,数据对应显示花了将近一天。 因为是OLED12864,只能显示8位的数据,也就没有什么颜色好说的了。 简述一下使用步骤。 首先设备上电,看到我用单片机+W25Q64模拟的U盘 
; f) Q7 G9 `' U" p. ]
然后,做张BMP图片,放到U盘里面 3 e2 n( X. z% @9 b
电脑打开图片是这样的,图片是我随便截图来的 , A( a% d; [, O5 a
然后,设备重启上电,OLED上面就显示这张图片。 6 T6 l3 r5 T, J" }: J U6 D3 Z! x
这样就意味着我之后想贴图就可以直接贴了,不要再搞来搞去的做字库。 首先是了解BMP的组成结构: * t1 ?' P, K( w: b# X9 [2 {
1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;
& x) {. ]0 [+ o. E其结构定义如下:
3 ] z# V# k7 {% R z' N[size=1em][size=1em]1
. c: P- N K+ d0 i0 ]; `/ |[size=1em]2 / k* W: a" S5 a! L/ Y( D
[size=1em]3 % S% ]! z+ v* ~2 l; Y/ o
[size=1em]4 ' r3 m! S$ q" w0 p- I
[size=1em]5
6 m. t2 l) H2 d5 }) w c( {1 x v: J[size=1em]6
2 {: @) v6 Z& e0 ^[size=1em]7
" c1 D- L# h$ d. F+ f[size=1em]8
8 n9 i0 H1 o+ T/ Z! }[size=1em]9 & Y7 {2 Z" u' U6 ^# `4 L6 e
| [size=1em][size=1em]typedef struct tagBITMAPFILEHEADER
8 X, ^/ m- a( e- @. d+ s6 d. w[size=1em]{8 Q- s2 Z2 ]# T0 O4 w, i1 @
[size=1em] WORD bfType;//位图文件的类型,必须为BM(1-2字节)
- C( k: W5 W3 R0 W2 K[size=1em] DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
4 Y7 @& g: S2 j9 S/ k S* @! L[size=1em] WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
, ^4 H- `! Z" X% q[size=1em] WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
0 p+ ~, W* X& A" v[size=1em] DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)) [* \5 c4 F4 r/ Y2 z! _: ]
[size=1em] //文件头的偏移量表示,以字节为单位
" p) e; T T1 m" O) b5 ]9 [[size=1em]}__attribute__((packed)) BITMAPFILEHEADER;
5 T! `$ [1 t& l% a. A4 N8 ~3 M. j: F; n
| ! F- r- ` p# F" N, s; U
9 t7 l, [$ Y& {2 f) v' A2 y
% r" b2 q& z" D( Y' Z5 y- t+ F9 B
2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
3 v* F, w/ ~' i/ v, E2 S% LBMP位图信息头数据用于说明位图的尺寸等信息。 K* T; y; R8 _# G3 A* y3 L
[size=1em][size=1em]1
1 z7 z& }* F* J& \[size=1em]2 * E& h# O# b: k5 W& M& ^# O
[size=1em]3
" `3 o; l3 `2 k4 v% E' n[size=1em]4 0 Q4 [/ ^" s# q% E$ X6 Q
[size=1em]5
5 q: z; V. g. n f# h" v[size=1em]6
5 S/ m: t( H7 \6 i2 {# A$ v# y4 z[size=1em]7 . s& D' J( B4 O$ N( g! F% M
[size=1em]8
- h7 q- f+ A9 L$ P[size=1em]9 8 t y5 |! R# P8 `( Q: p( L
[size=1em]10 # z O8 k* C8 e" t' t
[size=1em]11 " y: _) }1 j1 J3 U* k0 w; s: p
[size=1em]12
% `" }0 o# o J+ ][size=1em]13
; I% h6 b, O+ e7 e; d; [' D[size=1em]14 ( Y! `& p) r3 ?& o8 a
[size=1em]15
6 G- Z5 {* g: Q | [size=1em][size=1em]typedef struct tagBITMAPINFOHEADER{
; G* z% B4 x, W3 T2 Y. x4 M+ \[size=1em]DWORD biSize;//本结构所占用字节数(15-18字节)
! W5 @/ H" V. {4 V5 K[size=1em]LONG biWidth;//位图的宽度,以像素为单位(19-22字节)" W+ J: x! M- X- {# r) B3 G+ I" x
[size=1em]LONG biHeight;//位图的高度,以像素为单位(23-26字节)- \/ f8 I' w5 j, N/ G, B' t" C
[size=1em]WORD biPlanes;//目标设备的级别,必须为1(27-28字节)
+ A7 F4 T, S( S1 T. c9 W: k; C8 X[size=1em]WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节)
0 E0 T6 e5 S- ~" \: _[size=1em]//4(16色),8(256色)16(高彩色)或24(真彩色)之一
( V6 u- G$ k1 W/ I& P[size=1em]DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)/ H1 R" ^3 [( Z
[size=1em]//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一; g7 q/ e$ `/ \, E
[size=1em]DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)9 s2 b8 \; A7 N( z- `9 f
[size=1em]LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)* Y& o& o# O; Q* `5 \
[size=1em]LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
& n$ s3 i' i; m5 g5 c. ~2 @[size=1em]DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)
0 }) p# o8 ~; ]/ \2 n* A+ c+ M[size=1em]DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节)# d' Y* Q9 `4 ?6 A
[size=1em]}__attribute__((packed)) BITMAPINFOHEADER;
7 c" @. K1 m+ }* [2 A+ ~
. n; f: U1 \/ E: i( d |
[. m1 v/ Z$ C' ]3 F. Q4 Z% Q9 z# b: N" h3 R$ {
* L+ m+ Z0 T: o) }+ P# f9 X3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
6 p( J6 j7 P( ]& }! J+ Q颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
( M% }, h- f+ D+ T# h[size=1em][size=1em]1
& x, G$ A& N! \[size=1em]2 & U6 ?6 B& n7 s
[size=1em]3
9 f2 Q7 Q1 C3 h: z6 Q; o8 |+ F) r[size=1em]4
5 i* K; P9 {) g- R1 O[size=1em]5
0 [% K, T5 j! ]0 E- d) G[size=1em]6
0 T. E* X6 u+ j6 O | [size=1em][size=1em]typedef struct tagRGBQUAD{
: l( h3 v( B* ]! W! k[size=1em]BYTE rgbBlue;//蓝色的亮度(值范围为0-255)
7 Q; ]& y6 z8 V5 n' V[size=1em]BYTE rgbGreen;//绿色的亮度(值范围为0-255). C" N% f; i% j+ C/ ~6 H1 e7 V3 a
[size=1em]BYTE rgbRed;//红色的亮度(值范围为0-255)
- h2 C Q- ?1 U: P: z* M[size=1em]BYTE rgbReserved;//保留,必须为09 C: {5 _8 Z; K( y( e( e7 b# m/ Q
[size=1em]}__attribute__((packed)) RGBQUAD;9 K1 w8 Z! \6 a2 }$ S
9 m; ~- a8 B% T2 Q0 o" Q
| " D$ p- Q6 K) T @7 f! \7 U
1 p4 J* l* Q% @颜色表中RGBQUAD结构数据的个数有biBitCount来确定:8 w8 w; s. g8 Y1 M7 K. J
当biBitCount=1,4,8时,分别有2,16,256个表项;
- C6 r% p# J6 J7 @1 K4 m当biBitCount=24时,没有颜色表项。
. g$ k+ _! Q- L7 U C# d. Y. B位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:! X! ]9 @# Z2 ]2 }8 A
[size=1em][size=1em]1 - v2 X7 l/ H% ]' t4 F
[size=1em]2
: b' ?; C! [2 D% G' N[size=1em]3
# N; b2 I9 G* g[size=1em]4
3 w# _# l& [( @ | [size=1em][size=1em]typedef struct tagBITMAPINFO{* V7 y4 V c* p; C; l" A! Q: p2 e
[size=1em]BITMAPINFOHEADER bmiHeader;//位图信息头, {+ w; g8 e9 c% `/ R* l$ a
[size=1em]RGBQUAD bmiColors[1];//颜色表4 l5 V2 g) ^( R; |4 P
[size=1em]}__attribute__((packed)) BITMAPINFO;; e) x0 V$ V1 ?/ i( O q
, ` {5 N# k3 c7 z( v
|
! L ^0 X: N O/ p e* h
5 H- x2 u7 [" G9 I9 T& V2 C1 X I0 |) v! h- w% v8 x4 y( o
' l' c/ Q6 _% R* A n- M4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。$ |! w" w3 j( M/ V) |# {/ ?3 W. _
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
$ s# C9 G4 O8 ?当biBitCount=1时,8个像素占1个字节;/ [+ K* q3 `% R' m2 l
当biBitCount=4时,2个像素占1个字节;% x' _3 M# i+ F" |# _% x
当biBitCount=8时,1个像素占1个字节;1 Y$ y# N' U; o8 p
当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;4 N# F' U" j$ e: q1 Y
Windows规定一个扫描行所占的字节数必须是! X) ^5 d1 `, d1 o' K
4的倍数(即以long为单位),不足的以0填充,
' {/ n3 N# B( }& {# D) n5 x! k, ybiSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
2 L) l8 v5 v3 }6 P: {. ^以上内容网上大同小异,都是一样的。 根据以上信息,首先我在程序里面做了一个结构体,存放除显示数据外的所有东西。 - {) S: ^' D* W2 U
我不需要调色板,也就没用结构体了。 计算上面的数据,一共是62个8位数据,用来存放BMP的定义信息。 程序很简单,首先是U盘读取数据 ret = f_open(&fp, "246.bmp", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
! r' ^/ D7 H% y# g. m- ?9 X3 k ret = f_read(&fp, readbuff, picsize, &unt); //读取文件头信息 然后提取前62个数据保存到上面说的结构体 Getpicture(readbuff);
% @) G$ O8 s8 z/ @5 \8 ` 
) g2 \0 Y( c- r6 Q
仿真确认数据正确性 然后是提取图片信息 
5 k: r; u6 I- g5 G1 n4 j/ @
一个简单的嵌套逻辑。
0 F# l4 {. _0 Q2 r最后是花了我将近一天的东西,数据显示对应 void Display_BMP_DOT(uint8_t bmp[64][16])2 g1 X+ m- i* r9 S% M+ p
{( E! I6 s+ V! y8 d( W; O
unsigned char x,y,z,n,m;* m" ?) v, H9 y; B; `- y4 A7 T+ X
uint8_t Oled_Draw_BMP_buff[128];
4 l+ ?. v2 u5 t/ u% Y; x8 h' J7 J! e) S
7 Q0 K$ ~, d7 J1 z1 T/ e
for(m=0;m<64;m++)/ ?- N6 j' u( r' m+ O& t/ [
{
2 H0 L6 v2 V) |5 L& Q6 d0 L! E for(n=0;n<16;n++)
1 a, P5 ]! A, P- I {
8 q' w2 W' G9 d* e z = 0x80;
( p$ ?0 r" B1 d7 X4 F4 I for(x=0;x<8;x++)
9 T+ a% K3 V0 X% j1 x$ ^; j {
3 z7 o- s! n# M( o+ {6 j Oled_Dot(x+8*n-1,63-m,bmp[m][n]&(z>>x));
$ N, U: A8 [& b. y8 A$ X }
9 e" x. J! _ g- h( i. f& _. x }) G8 a& u) ?# C7 R4 q# |+ E4 m
}
' E( |+ c% v# r$ C7 `$ v Display_Process(OLED_Display_Data); 0 c) ^. K4 [1 e$ c
} 这一步的难度在 OLED12864支持的是页写,之前做驱动的时候,刷屏是从左至右从上到下,但是WINDOWS上面,图片提取是,从左至右,从下至上。
9 F' T2 c9 o% d) L |