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

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


- 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+ n
1 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
收藏 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 手机版