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

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

3 Q8 C. [% X& k$ _  U! H

3.将W25Q64虚拟成U盘

蛮久以前折腾的了

4.编码解码及屏幕显示

其实解码不难,难的是数据对应,解码我大概花了两三小时,数据对应显示花了将近一天。

因为是OLED12864,只能显示8位的数据,也就没有什么颜色好说的了。

简述一下使用步骤。

首先设备上电,看到我用单片机+W25Q64模拟的U盘

4 L' w# I) e( Z3 I  N$ @: f

然后,做张BMP图片,放到U盘里面


& ?- ^4 w; }" e' I" R" N+ e

电脑打开图片是这样的,图片是我随便截图来的


( x% d8 R! ^& y( p: U1 c

然后,设备重启上电,OLED上面就显示这张图片。

8 F6 i! r6 z+ T! R- ?" n

这样就意味着我之后想贴图就可以直接贴了,不要再搞来搞去的做字库。

首先是了解BMP的组成结构:

5 b( w1 }% r9 s( V) }1 r
1:位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;: n7 I- M3 [# Q* V) F/ c  Z$ M! X
其结构定义如下:
+ M5 E( _8 H0 l- O[size=1em]
[size=1em]1

. A% v# A# J$ F  d$ a
[size=1em]2
- i, r8 U0 E6 K2 D/ y1 g
[size=1em]3

: T; L' U( U6 U4 G4 H
[size=1em]4
7 }/ V4 }0 D+ k' g
[size=1em]5
9 _0 z  F8 d9 a* ?
[size=1em]6
$ u3 k1 t6 r: M3 [) |
[size=1em]7

- P' x* D+ q- P
[size=1em]8

, m/ m, G& G) i. H
[size=1em]9

  s4 b$ b) ?1 V% d
[size=1em][size=1em]typedef struct tagBITMAPFILEHEADER) p- _- w- O1 _! b5 V
[size=1em]{  v' M+ k4 _4 |7 i6 I( m1 B# c& K
[size=1em]    WORD bfType;//位图文件的类型,必须为BM(1-2字节)
2 Z$ _9 {$ G. \+ Q& k. [& }[size=1em]    DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)5 {. ]# ?* h3 Q9 a  ?
[size=1em]    WORD bfReserved1;//位图文件保留字,必须为0(7-8字节)" C! z- g) ?( S. {& d1 t
[size=1em]    WORD bfReserved2;//位图文件保留字,必须为0(9-10字节)
  R4 P( ^5 {6 I0 d& e( B9 E[size=1em]    DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)9 ~6 q% ]* p) S3 e8 a2 a3 x  L
[size=1em]    //文件头的偏移量表示,以字节为单位- P; R2 J$ e; N% Z: D- x" w
[size=1em]}__attribute__((packed)) BITMAPFILEHEADER;
, B* U5 [" N" Q9 v" ^2 G+ e+ R% y  O5 H: C5 g

6 N: Z1 N& ^) d2 _, O7 S

  H; ]& O& Q  W* f/ [: |
  B, H: C* y( }4 W/ y2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;  j/ G" p0 w+ G
BMP位图信息头数据用于说明位图的尺寸等信息。! y; J. L! n# j" p3 z6 l& d8 h
[size=1em]
[size=1em]1
' `. z" H( h# P0 q: m$ ?* J+ p
[size=1em]2
9 j2 u4 \# `6 b# S; Y$ f" h
[size=1em]3
- g0 s/ [# U6 r! S
[size=1em]4
# ^6 x4 }  F1 c1 B. u
[size=1em]5
2 D5 W; R. ]# w9 ^' E3 d
[size=1em]6

& U* l% J% p# r/ h8 v6 P7 ?
[size=1em]7

" M; W1 }3 I* }) g$ U
[size=1em]8
$ s4 S3 A# c! w0 J
[size=1em]9
0 [) u) N) m3 Q( s
[size=1em]10

8 B- G# q  _7 y; p% g+ `3 h3 T
[size=1em]11

0 \1 d5 A$ L$ S- F
[size=1em]12

* b7 F: Z1 z7 m6 G$ O
[size=1em]13

/ I6 [+ Y  n+ |6 J+ q3 d
[size=1em]14
) v. p+ Y! q! d# \
[size=1em]15
+ P) g6 ]/ L0 n7 Q; y5 @
[size=1em][size=1em]typedef struct tagBITMAPINFOHEADER{
+ ~+ p5 v9 L" z' W- B2 ?8 |; ^[size=1em]DWORD biSize;//本结构所占用字节数(15-18字节)0 ^: Q: U$ Q- g6 V
[size=1em]LONG biWidth;//位图的宽度,以像素为单位(19-22字节), {8 l2 F" B, p7 P& w, a
[size=1em]LONG biHeight;//位图的高度,以像素为单位(23-26字节)7 q3 C' d* Y1 g1 o
[size=1em]WORD biPlanes;//目标设备的级别,必须为1(27-28字节)
5 ~2 p- @) O" I- r: \  x[size=1em]WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节)% J% z, J% }* N- L4 _# F% Q8 X
[size=1em]//4(16色),8(256色)16(高彩色)或24(真彩色)之一! E% ?/ l3 g2 Z4 F
[size=1em]DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节)
! D* k- q. T2 S6 B0 p" |, K! E[size=1em]//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一2 T0 J1 c3 i) e8 b
[size=1em]DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)' s; m& I% Z9 ]1 h( d
[size=1em]LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节)
: b7 X0 F! m6 q- y/ ?5 c! d! F# g: W[size=1em]LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节)
; m" ~" W3 ~1 C; u4 e7 B6 N[size=1em]DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节)- ^7 Q. H9 b( j; L# g; @
[size=1em]DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节). K; S9 Y$ `) l, h9 ^
[size=1em]}__attribute__((packed)) BITMAPINFOHEADER;
; C! K4 Y7 L; Q& @: x5 ]/ X0 q1 |  g
. t) \' s2 l" _# S/ z
; L7 @* L* F9 j/ P3 ]; ?$ d
5 k6 H0 C( [6 F) ~  F
7 Q- X& ?, i7 N* }8 y. g
3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
" ^4 q: `& v$ k+ v- k. [4 }颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
* o" y' x+ C; C0 T. I[size=1em]
[size=1em]1

# c  J7 l$ v; I( k  i) u
[size=1em]2

2 M2 r7 J8 s1 y" N. w: n
[size=1em]3

) ?" J% n" ]4 I# L* I1 `% H7 E4 C
[size=1em]4

" B- |- a* Q( c1 k; b/ `7 u4 b
[size=1em]5

, G) H, N" t) N0 j1 u! Y% P
[size=1em]6

+ X5 y3 D7 H! S
[size=1em][size=1em]typedef struct tagRGBQUAD{
& F1 E: I9 I! r4 j- O[size=1em]BYTE rgbBlue;//蓝色的亮度(值范围为0-255)8 M, ~* q- d' n7 ^. R1 t$ @* C
[size=1em]BYTE rgbGreen;//绿色的亮度(值范围为0-255)/ `$ V/ ]% x9 {
[size=1em]BYTE rgbRed;//红色的亮度(值范围为0-255)
) P" z. E  Q2 O9 J! I( {[size=1em]BYTE rgbReserved;//保留,必须为07 V* a8 `0 T5 |+ s9 \! u
[size=1em]}__attribute__((packed)) RGBQUAD;
" u# }! Y. a" q/ Y% `
% I6 Y3 I7 m  E5 X$ V
+ y- Y! l6 K" r! v3 B

) g( {/ \5 D$ o6 l8 e- b, K/ M颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
1 ~8 ?$ e0 d  n4 ]& k+ H当biBitCount=1,4,8时,分别有2,16,256个表项;
/ [1 E8 f# B7 A' @! ^6 v当biBitCount=24时,没有颜色表项。4 @. p* x4 Q& r8 e2 j
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
( ?2 L4 z0 w+ |- `[size=1em]
[size=1em]1
! L3 J" w; g  ?9 _9 y1 I" @
[size=1em]2

) l: A# \7 E) i4 c6 U
[size=1em]3

: j9 r( r4 F% Z$ H- {& F
[size=1em]4

  @. t8 C+ }4 M! g- x, P
[size=1em][size=1em]typedef struct tagBITMAPINFO{; d( n* s; Y0 n
[size=1em]BITMAPINFOHEADER bmiHeader;//位图信息头- l; u! w, W1 [$ Q$ M4 N" y% x" j
[size=1em]RGBQUAD bmiColors[1];//颜色表7 c$ u9 @% X2 x9 [: d' {# P
[size=1em]}__attribute__((packed)) BITMAPINFO;' g1 O9 e) b' t( I
( ]9 K; G; a- D) D% k: R- W: \

& t, \0 I/ r) O) k% S
, m0 @( G5 H& j8 r, y
( I7 q7 J7 ]& U) E5 b, r3 U1 a

: i* a1 {& N1 O7 v( L8 P  @) x4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。
0 `" e( r! E2 h$ {: K位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:6 T; P) ^7 l6 ]' F7 f
当biBitCount=1时,8个像素占1个字节;4 y3 x3 C$ G+ v4 b9 L: M
当biBitCount=4时,2个像素占1个字节;& S: l7 P/ L+ n7 ^- \
当biBitCount=8时,1个像素占1个字节;
/ D* E! ~/ `0 m& q当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;
6 `- i( Y  Y. O' mWindows规定一个扫描行所占的字节数必须是7 X* q% g5 M+ b$ W6 I  c5 F+ H) k
4的倍数(即以long为单位),不足的以0填充,
  A6 y3 M: c$ k- H* zbiSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight;' M$ O" j" G$ m: N9 [

以上内容网上大同小异,都是一样的。

根据以上信息,首先我在程序里面做了一个结构体,存放除显示数据外的所有东西。


) n0 r0 }( B# {3 Q7 L1 J7 ]. H& w" V

我不需要调色板,也就没用结构体了。

计算上面的数据,一共是62个8位数据,用来存放BMP的定义信息。

程序很简单,首先是U盘读取数据

ret = f_open(&fp, "246.bmp", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
% E/ z. H  [. A( L1 v! K

                        ret = f_read(&fp, readbuff, picsize, &unt); //读取文件头信息

然后提取前62个数据保存到上面说的结构体

Getpicture(readbuff);2 T$ p: D4 b& z! v9 g

' P' n. }- C* k1 y5 A) n$ ?

仿真确认数据正确性

然后是提取图片信息

  T9 O$ d- S7 q* T% y& Z! v7 t- \

一个简单的嵌套逻辑。

' v$ S6 w; h) @% t/ C& X$ y

最后是花了我将近一天的东西,数据显示对应

void Display_BMP_DOT(uint8_t bmp[64][16])7 [8 t  ]% E$ i/ `, K
{$ e7 ~: b( o2 j: a9 a5 m: |
        unsigned char x,y,z,n,m;
' V1 Y5 f" j+ A        uint8_t Oled_Draw_BMP_buff[128];
; g; s7 M, m1 m; I
/ B. N" z+ C. Z7 m
: n4 W* k- j' M7 x        for(m=0;m<64;m++)
+ c9 F" `8 \' n; n        {' d! W0 H. [2 h, S
                for(n=0;n<16;n++)
4 u) B9 D- Q4 Y$ u) e  C9 ~2 b! g                {
3 m+ \9 o( c# y7 R                        z = 0x80;
5 F7 h' f  l  T0 E+ ^5 E  D+ `% v+ @; C                        for(x=0;x<8;x++)4 S  s1 }* u' g
                        {         # w4 y* @2 e! m, ]
                                Oled_Dot(x+8*n-1,63-m,bmp[m][n]&(z>>x));       
9 y; q7 P& t7 P6 ^9 s/ ~                        }' q  c; R: D# D- y* b' N/ T
                }
- ?' I, P5 r6 W* H: E% w& Y        }% m8 B8 Q# ~/ W2 ^8 Q1 n
        Display_Process(OLED_Display_Data);       
; G0 v, Z8 r, Z+ E& |

}

这一步的难度在

OLED12864支持的是页写,之前做驱动的时候,刷屏是从左至右从上到下,但是WINDOWS上面,图片提取是,从左至右,从下至上。

. t# [3 c9 ]- ]
收藏 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管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版