之前做UI设计,每次用到特殊的字符,就要我重新做字库。 后面有几次要我贴图,可想而知,太鸡巴折腾了,做过的朋友都知道,后面就想着能不能让单片机虚拟U盘,然后图片拖进去,就直接显示出来。 PS:其实想做视频解码来着,后面懒了,先做图片解码。 折腾了一两天,最终还是实现了。 硬件组成: 1.STM32F4 2.OLED12864 3.W25Q64 软件组成: 1.DMA做SPI数据传输,基本底层驱动,就不解释了 2.W25Q64上做FATS文件系统,版本是
- F6 i+ u' ]6 @- g
3.将W25Q64虚拟成U盘 蛮久以前折腾的了 4.编码解码及屏幕显示 其实解码不难,难的是数据对应,解码我大概花了两三小时,数据对应显示花了将近一天。 因为是OLED12864,只能显示8位的数据,也就没有什么颜色好说的了。 简述一下使用步骤。 首先设备上电,看到我用单片机+W25Q64模拟的U盘
3 {& q9 F% {8 f; d3 L4 v
然后,做张BMP图片,放到U盘里面 ( t) C) m1 S2 p) X0 A
电脑打开图片是这样的,图片是我随便截图来的 ' B1 K# B- }# x* F q
然后,设备重启上电,OLED上面就显示这张图片。
4 ?, s/ ^" Q: X: A
这样就意味着我之后想贴图就可以直接贴了,不要再搞来搞去的做字库。 首先是了解BMP的组成结构:
) b3 b: \# H# h. Z5 p' f1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;; X; ], R" A1 M7 n( K6 _' y- G
其结构定义如下:* i3 c% C8 l$ b* ~% `
[size=1em][size=1em]1 % F9 H5 S/ Z0 M
[size=1em]2 0 c; c- G( n8 j9 x, K9 {
[size=1em]3
' R [8 T, ^# p" L[size=1em]4
. @( S% x( U7 V9 k5 t[size=1em]5
& G& l) W, I% }; s7 ^; ~ G8 r+ I8 G2 d[size=1em]6
) a4 ]0 ^, U1 g[size=1em]7 7 O: w; J5 ]1 c* U2 }6 E
[size=1em]8
1 Y0 y; D/ h1 Q; H' Y# ?! M" K1 U[size=1em]9 * D! J* p" j1 T* ^
| [size=1em][size=1em]typedef struct tagBITMAPFILEHEADER& B) @. j# K. l! E2 E" r% q
[size=1em]{
2 B) \! s; _* |- j, a! ^2 p9 j[size=1em] WORD bfType;//位图文件的类型,必须为BM(1-2字节)
* ^2 d' Y% j$ V1 g- J7 o[size=1em] DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)
3 s( j! h' c& W( ~[size=1em] WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)
3 M, `5 R! t( |5 i; N[size=1em] WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
6 z3 j0 n! f5 q+ n( y[size=1em] DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)% |5 ]: {) w0 r. T" R* j: d9 y% E
[size=1em] //文件头的偏移量表示,以字节为单位
# ^* l' a7 \% m[size=1em]}__attribute__((packed)) BITMAPFILEHEADER;3 a" j9 N$ W$ E/ Z7 V
9 }+ ?2 u: \5 g9 l- f2 D4 q2 N) Q | ' m- y) p q5 a
5 }' {. S* G! x$ D& R5 l( o2 t6 |( X! B2 v
2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
) d U. ~$ W. X$ `BMP位图信息头数据用于说明位图的尺寸等信息。
0 k, d/ {( ]1 c0 i[size=1em][size=1em]1
, \5 e' V6 z6 ~; r4 i* T# r r: b5 v[size=1em]2 ! j/ V& {0 n# m9 A
[size=1em]3
[; ^4 Z8 }7 x% q% n+ O1 I. v[size=1em]4 1 E- Q; L5 u: T- e v
[size=1em]5 # B" Z7 C. H$ s F3 |* a" u" I
[size=1em]6
( R4 f0 w. d* a: F4 H[size=1em]7
( p- E0 h4 P1 Q- |0 r0 J[size=1em]8 7 A5 o S1 s; s& B. J3 Q
[size=1em]9 / o0 `9 p; R; n, g$ M6 F4 j L+ X
[size=1em]10
1 f X; ], i6 b- C; m/ z+ x[size=1em]11 , f i" h& B& x \7 J
[size=1em]12
# f- k8 n$ w5 z[size=1em]13
4 G q& s( V7 v( M[size=1em]14 . z- b& r9 t* _
[size=1em]15 3 }9 y4 _. H; f# _" M9 {- }4 I3 v
| [size=1em][size=1em]typedef struct tagBITMAPINFOHEADER{/ F' n! G( A$ X& n: p
[size=1em]DWORD biSize;//本结构所占用字节数(15-18字节)3 |9 r1 \: T7 T0 [
[size=1em]LONG biWidth;//位图的宽度,以像素为单位(19-22字节)6 E* v( @1 [+ Q& q3 j
[size=1em]LONG biHeight;//位图的高度,以像素为单位(23-26字节). O* r: o+ c. @$ D5 _8 x
[size=1em]WORD biPlanes;//目标设备的级别,必须为1(27-28字节)/ o$ w$ s! [" S6 L, P/ Z! O1 _
[size=1em]WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节). v- ~* v u3 D6 D+ A1 Q: y/ e! n+ n
[size=1em]//4(16色),8(256色)16(高彩色)或24(真彩色)之一
" N' Z% Q5 ~! M Y[size=1em]DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节); f- {3 h/ y5 B
[size=1em]//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一1 @0 Z5 ^, V2 H1 `2 e$ L
[size=1em]DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)1 J6 Q% q+ n& s
[size=1em]LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节). I5 V- b2 p& U" o4 |# d
[size=1em]LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
2 q5 | q* n/ ]9 A6 U2 H4 q; \[size=1em]DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)* `9 Z3 H7 F& H3 o/ E2 {
[size=1em]DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节): A+ m8 f) r: K( B
[size=1em]}__attribute__((packed)) BITMAPINFOHEADER;
( B# ^" _8 l2 c: B" E3 b9 d! M8 r, s% `; x3 L# a# a
| ; A: g* h7 b. E z
" R- h6 y& P9 {. @2 E4 b) {
) t% k) v- z0 ?. _3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板; r5 v& H- e6 i. F2 K; J
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
! \ R! v% M% d# Y; S# D X[size=1em][size=1em]1 $ u/ F* ^: Y3 H6 s6 W( z
[size=1em]2
& }& Z4 Y& {+ E g( c% j[size=1em]3 1 s% p% ]* M! o; x/ m
[size=1em]4
; x W2 x0 ~7 t& c% M4 y/ l* T[size=1em]5
$ @0 D' o! V* {: U1 Z) k6 B5 y[size=1em]6
, H- B- E5 _+ {* U2 i% Z9 O; j | [size=1em][size=1em]typedef struct tagRGBQUAD{
) l$ T' n& d$ Q2 B[size=1em]BYTE rgbBlue;//蓝色的亮度(值范围为0-255)
4 C% l* Y8 P( W0 B! p( q0 k" k8 {[size=1em]BYTE rgbGreen;//绿色的亮度(值范围为0-255)! g. |% ` L/ b5 m
[size=1em]BYTE rgbRed;//红色的亮度(值范围为0-255)0 T9 T* M, w- Q! h4 Q |
[size=1em]BYTE rgbReserved;//保留,必须为04 P1 L+ ~6 \) a
[size=1em]}__attribute__((packed)) RGBQUAD;! e( @7 ?, j0 J& t
2 ^# @8 |- s0 p# @7 G3 K |
' x! T. y" m1 v8 P& d; L+ n1 a1 D) R3 h4 {% ?0 D
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:+ `' N" D' X+ G* S
当biBitCount=1,4,8时,分别有2,16,256个表项;- ^! i- e N, G" ?4 ^/ _8 n" N! C: e( u
当biBitCount=24时,没有颜色表项。
. d7 Y* K+ p$ i# u位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:8 d! ~1 T q$ o3 B( m/ [
[size=1em][size=1em]1 $ X" |4 i) i/ j0 Z+ \+ \
[size=1em]2
: s3 p1 ~. V$ [0 d[size=1em]3 , L% l; t# \4 a" r2 t, D4 y0 l/ A
[size=1em]4 8 a y- N! S. j5 M
| [size=1em][size=1em]typedef struct tagBITMAPINFO{" {5 j9 Y' b& U3 u6 b
[size=1em]BITMAPINFOHEADER bmiHeader;//位图信息头+ ~# T$ H! G; ^! }; w
[size=1em]RGBQUAD bmiColors[1];//颜色表
# T" N/ M* N& Z. j( \1 t; Z[size=1em]}__attribute__((packed)) BITMAPINFO;8 O$ A+ q7 p/ h3 u$ u! Y4 ~
& {- ]' o1 _8 [- y' L9 o" o
|
, t2 N. u0 ?$ f
W* u. G% X+ ~. q, z9 P c- i( X% l
- t5 _; z" w) k8 s; [% a4 e5 |; y1 _. a3 r
4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。
1 d0 \5 L+ \- o; W5 q0 G位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
( |$ k# B% E1 U, g当biBitCount=1时,8个像素占1个字节; U6 D) b- ]. n1 T. {( }
当biBitCount=4时,2个像素占1个字节;9 J; Z5 f1 b2 l8 O9 b- i
当biBitCount=8时,1个像素占1个字节;
! X; F. \9 b9 M9 C% w) Q当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;
+ S+ d. Y E# P" D% D p3 pWindows规定一个扫描行所占的字节数必须是
5 A: E# s) F( C: n. e, y U4的倍数(即以long为单位),不足的以0填充,: G8 H% M8 B4 C' ?2 F4 u! B; i/ T
biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;
" ^+ [' _- L2 o& Y1 X+ i以上内容网上大同小异,都是一样的。 根据以上信息,首先我在程序里面做了一个结构体,存放除显示数据外的所有东西。
" I- J# N% \8 C, w6 j0 {6 z) E
我不需要调色板,也就没用结构体了。 计算上面的数据,一共是62个8位数据,用来存放BMP的定义信息。 程序很简单,首先是U盘读取数据 ret = f_open(&fp, "246.bmp", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
$ g, A& a, X2 _ ret = f_read(&fp, readbuff, picsize, &unt); //读取文件头信息 然后提取前62个数据保存到上面说的结构体 Getpicture(readbuff);
. S- d- G8 V; }5 I# p + R- @' J V3 L, T
仿真确认数据正确性 然后是提取图片信息 0 M1 W- e5 z3 E2 _1 G) E
一个简单的嵌套逻辑。 & s* t8 N0 V, \, r. c& E0 u
最后是花了我将近一天的东西,数据显示对应 void Display_BMP_DOT(uint8_t bmp[64][16])" `$ k! `- C; V: M4 j
{& m3 g: ?& U: `( H& s& q* Q
unsigned char x,y,z,n,m;( P+ S- ] z% Y; m1 q( y
uint8_t Oled_Draw_BMP_buff[128];5 p: J3 s* A/ g' k4 E
' U+ t/ J% r$ o [( R5 b0 O
7 s$ v; Q5 M P$ | for(m=0;m<64;m++)
2 X9 A! b; N4 w4 \2 v {8 H( g. x5 r4 x: ^/ L7 i, W
for(n=0;n<16;n++)
' q3 U, S& B( s% ?) z- W { . a& u `* f& ?# G. v! B! q# }: j
z = 0x80;
4 F) ^$ m+ W# t# ?1 k( z7 V. R for(x=0;x<8;x++)
1 Z0 B' v; u @% \ H! d" I3 c! g {
9 Q8 \) f9 S/ |) d$ e! r( s! h { Oled_Dot(x+8*n-1,63-m,bmp[m][n]&(z>>x));
& g& U( ?1 d5 d5 O3 X }
) b+ _. s0 o0 E" D; x* i; P }
0 o2 L" [1 W7 V9 u } }
6 ~ U! T2 x; s* s) [* h Display_Process(OLED_Display_Data);
" x0 Y: @! N& Z1 r } 这一步的难度在 OLED12864支持的是页写,之前做驱动的时候,刷屏是从左至右从上到下,但是WINDOWS上面,图片提取是,从左至右,从下至上。 ( N/ C, U+ p: O
|