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

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文件系统,版本是


* 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
收藏 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 手机版