你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

BMP图片解码 STM32+FATS+模拟U盘+OLED12864

[复制链接]
不吃肉的羊 发布时间:2018-2-10 20:50

之前做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
收藏 1 评论3 发布时间:2018-2-10 20:50

举报

3个回答
hello_bug 回答时间:2018-2-11 09:15:10
这各厉害,解码速度如何,刷屏效果呢?
不吃肉的羊 回答时间:2018-2-11 12:36:03
12864总共才128*64个点,要不了多久,具体时间没测,效果跟做字库一样的
zero99 回答时间:2018-2-12 14:17:34
感谢分享,已汇总到2月技术原创  https://www.stmcu.org.cn/module/forum/thread-614799-1-1.html

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版