|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。
- w; I8 ?" y7 `4 a* m# D 取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。
Q- ~' L& J* k7 R6 x 国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:+ y% ^$ z/ D5 \$ o( }
offset=32* ( (H-0xa1)*94+(L-0xa1) )
8 M& E' n1 ~5 d2 C3 J2 A+ b H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。+ \/ m5 @9 _) `( x
获取点阵的代码如下
' Z% Y) J/ `$ `% M% J5 g8 |8 J9 ?( |4 V! _3 l% e: \& {
- FATFS fs1; // 挂载SD卡的分区用2 t3 g$ j, H! Q# i
- u+ N2 d4 o3 u7 O2 B
- FIL f1; // SD卡中字库的文件描述符: C6 _) p. D9 w( C O
+ m7 X/ Z5 D1 D! H- FIL ftxt; // 要读取的txt文档的文件描述符
4 z0 x/ K, ?( [2 T9 P1 k# z. \
9 D; u% o; t$ ]. }0 b, r0 v) w6 V- z- u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字
. j7 `- I9 `$ x" B6 ~& s - - @! e; n5 p7 u
- {
8 S. j$ H( r' h+ N7 h# q3 p
' G8 G4 W+ ?* _# A% X3 [2 O- u8 res=0;9 N$ X& I6 ?2 I" |1 @: K
3 t0 @# G* w# R- u8 H8,L8;$ ^) g, r e$ `* d% i- C4 m* l
. m0 u* x: I. E4 O- UINT num;
& F( r* m5 [& u6 f" \1 M. V( I& g
o1 I' f; K1 S/ J- H8=*p;
& O; J: u) {2 @( o( o/ [& K# }5 c - 2 {9 m' G' c: c. y; B( G. c
- L8=*(p+1);/ C1 ^1 S9 ~2 X! h0 u7 [3 ]# ^
- ' ~* X# ^" G4 I& v L' C% W" |( K2 |
- f_mount(&fs1,"",1); // 挂载SD卡; a9 v6 V3 k7 U6 r) ^& I) J
+ J4 I7 D* @& t, B3 E- res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库$ `0 d" Z# N$ s8 z# h/ D
8 j3 ?* k: _7 Y3 L- IF(res!=0) // 判断点阵字库是否打开成功3 N7 ~+ z9 s2 ~, F& I- a+ Z& ?! L
- 3 S O2 u) q: k& f9 h
- {3 T4 ]! C$ X5 m! |
w: b; a& f O: p! j6 {# u- fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
5 k1 B$ _( i8 z4 f
$ s$ Y* O! W j: W- x( m% ~9 P- i- printf("res=%x",res);
0 m9 K! ?5 Y. @6 Q, k/ q5 r
& I4 F% p* F( y+ j* l- return 1;
! Q( _3 ~, i: j; y h/ g( j- N7 Y2 Z+ K
5 J* k. b) \" u3 E1 j, e ]- }
2 u* s$ e' `+ T7 Q - 8 m7 u7 b- |4 H! \/ G
- else
; c; |6 p* s* \& N& M8 C4 B
7 d/ ?) @- O# b2 d% L" W, E# R6 j- {! I/ o2 a$ }# H6 V1 f' y, _
7 a: L1 q% L4 X5 t9 p% S7 N1 w- fnShowString(10,120,"open ok",16,RED,WHITE,0);/ x. `9 T! }5 p, F* R2 i+ j: X0 A5 B
- ! k6 ~- d- P2 @. S) \, B% L4 D. U
- }7 ?8 u, x8 C4 \* j
- 8 X' _7 K* v- ^! G# c- O/ {
- f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据' T- s, V! c9 l( _% d
- 5 p& S/ ^$ O2 I
- res=f_read(&f1,buff,32,&num);
; _$ s# m; w% {, g - 4 r- l% c9 `* B0 V
- f_close(&f1); // 读完后关闭点阵字库文件
8 Y/ B9 d- \7 o! w4 i' }) b
. L, R% V5 n! U+ t4 i7 J- f_mount(&fs1,"",NULL); // 卸载SD卡" A, h2 F1 Y3 V2 L1 V) m7 s( v
' G+ _, E7 `2 U0 x8 y! H- return 0;6 O8 a- A' t1 b( w
% Z' j5 R5 {3 w0 J2 x: J# X- }
复制代码 3 C# C# ?- X! ]9 ~, M) y1 X* ^! w
这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。( z; x l: y# |+ ~5 K
实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。
$ k: g$ \0 y* k+ Q- V4 \ 首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。
: ~7 v+ q3 a: {" q Y4 M. Y 下面直接贴代码: m% s* l( K& \" S$ u
3 |5 S. G4 `% y, f- // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示' L6 u' a) W1 Z& Y" y2 j
7 E/ T" P0 P" n, e, M1 h$ j- // color 是画笔的颜色,BkColor 是背景颜色5 ?9 M- ]* W* @
) D# w1 ]& n% }- u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor )$ l( K2 Z% N7 e: u7 e& ^. l n
- 6 w- B6 o; _" N' ^" `
- {, |# y7 q+ \, H& j- L0 w
- ) P& { C7 X! N% I( G* y1 \. v# R
- u8 res=0; // SD卡函数的返回值
5 k7 A, F. V0 U4 ]
1 G+ R5 O0 Q: }. }- u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码
( h& Q2 G; L$ C, h
7 g: X5 A' m+ S2 m/ G$ i- u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据
. {1 g) B' Q1 O# T; b9 v _' ]* Z/ ~
. n0 j0 C2 x9 l8 L1 h+ C% m- u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位0 y! _4 P9 H4 y. Z/ r
" l/ _. D8 }- R1 |( z4 [2 i- u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了
% s- e W/ U0 w
$ x) }3 R8 P# M! V+ K' o- u16 offset=0; // 读txt文档的偏移地址 i$ A7 a" p! [" A9 t: e) ?
- " ]/ Q8 D) Z6 |+ j, g* e4 N
- u8 i=0;
( f9 k: B* r! d* M% Y/ m( j - 0 R; W3 }; Y+ r/ e# A
- UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数
8 x# V& c5 q! n$ V. l2 X# N - 3 z2 S% w6 j: @% v' Z. l
- u16 x0,y0; // 点阵显示的位置, Z# X/ n7 {% _
3 z9 I6 C% ?4 F( P# n3 u- u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,& h% N1 ~; q% f5 C
' r1 E* ?: q6 `- S* H6 i0 Z0 Q# X" \- // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。
& }% J H5 c8 @( B$ f8 m, a
C' x' X( z+ \- x0=x;
. Q9 z! N; R+ U* }
7 n ]1 T4 I! b7 `- y0=y;0 ? o. r4 \, E% I+ u$ O
: J7 |% F; M8 j) a* y7 A+ T- f_mount(&fs1,"",1); // 挂载SD卡
% x& c( O) e2 h7 z# h) w
7 `4 s# ] B, ^3 `- res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件
! Q- B: h+ U1 c5 ^
& D% J5 I* j% M# {8 x- if(res!=0)' Y* L5 B& R% r3 B8 f) j2 [6 o
3 P6 K6 I a+ @& V5 r b7 x8 |3 |- {2 ~' H3 k9 T2 ?1 _
$ ^) K* a* n) g; q- fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
( A0 T/ T2 Z3 ?+ u' t" f" Y
" p+ W, w- e/ t. Z2 A- printf("res=%x",res);
# T- {5 f6 `* e6 @* O7 {
. D- w9 a( G6 [/ f2 A, Y. [- return 1;
$ f6 a+ F# C8 `% A; q! Z
# f, p6 m+ y; t- }& W9 e) \; J1 K
- 9 P( r+ N% ^+ }- p. ]" `
- res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档. Q; z3 l; E! b2 H1 U. \8 R
- - h9 ~5 w$ h, U% L
- if(res!=0)
0 p0 f3 X- F: V5 A: H
# Q+ b4 Z3 x/ O$ W, J, ?- {
6 D6 {) M& Z! O/ `
0 _3 B+ ^' o" G$ v8 ~3 G- fnShowString(10,120,"open txt fail",16,RED,WHITE,0);
4 v6 T* U& {. z3 ]
2 y3 @4 I9 d$ K7 |7 g$ s- printf("res=%x",res);+ b+ h/ f4 z/ a
- * P! k4 |& r' o8 q
- return 1;! q* ]% j# _" M. n1 M
* X- N }6 [ e3 E. D- }# t) Y8 J4 j2 g( H. z/ P' C
- ' d% u7 b; U. s3 E7 h, w# d; {( J; r I) B
- f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。/ a: I2 [$ E* ]& a% H' r
* J3 x6 m! }% A( D& J- res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节
; b9 B$ e. ~5 a5 X) ^/ e
|/ z$ D6 M0 G8 J1 P) P, [- while(CntnuF) // 循环判断是不是读到文件末尾了1 N0 K: E/ s0 f
- & T7 q5 q& R8 `+ Q' @3 k8 }1 x% V
- {1 e1 v# V/ Q3 `! u- y d9 g, q
, y1 Z" M" B$ P- l! v/ j) W- for(i=0;i" k4 l0 d6 u" d7 b% o$ \$ [8 U. M8 [
- ! z9 B* p" t: w6 W* u8 H s/ R3 n
- {
& B7 X/ o8 z6 D* V* a9 ]' [ [) O
0 Z _' f) r3 y5 b: Y) |- if(x>220) // 显示位置的判断5 L7 I! C& d$ v9 ?- b: C" R
- # S _7 c1 }( F4 `, G" x4 z4 H
- {, X+ ?1 V. _4 Y f+ }
# j* G% f9 B& D! i n- x=x0;
o( I p: O; w3 P% `8 s - 9 G/ F( k, s: [9 @2 N; r1 r' M
- y+=16;
; Z" z {" @5 _7 G# b- f" n% J
' a- {6 G1 N( u0 K) V: K- }
) b+ w! ~7 B' C/ P$ x* t' a - 7 x$ F7 `0 i0 ]; t) Z
- if((y>300))% J7 O- f w% W' j u
- B/ d5 v+ O) w3 Q6 P- M6 F- A- {* t1 s) m' }. q8 o" C7 W- Y
% y) k( Q8 u6 q) p/ K4 j$ O- fnRefreshscreen(WHITE);
7 k9 Z4 u5 `+ @- {" h2 X
, @9 I4 d0 ~# f5 t! ?: |+ x5 e$ f- x=x0;3 ?1 m( y' s1 ?" d9 Y0 q- ^8 h
% W0 M6 q' Q4 c4 E- y=y0;/ f3 x [' M- _5 e0 U
2 O! ?, i: {! s. [% c7 b- }5 I" R( j8 X$ r: K- Q9 W# |
6 A% E* h9 w8 n* _- if(buff>0x80) // 是不是汉字% B1 G- f+ k$ a( m; r1 D
" h1 h# a3 b- \, v0 V- H; z- {; a" ~& _+ `' V
- : L4 n4 c5 Z4 N9 u$ `
- f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移+ H/ I; v9 E( a
# ], n% ?7 Q w9 `8 n) N$ I- res=f_read(&f1,bitbuff,32,&bytenum);, S V/ L: b# k, ]
. b/ r9 l! m& d0 x! Z: I- fnShowHzk(x,y,bitbuff,color,BkColor);+ b' a+ }& \" ^: }+ y9 s
0 G$ I* S6 o% i q- x+=16;
$ R! E7 [2 ~$ X# C6 o - M2 ~3 ~9 z) t
- i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,+ A( R r$ X U. U! l; P
5 @5 V0 C+ q. B! `! t$ _4 |- // 这里需要还要一个( |0 W1 d/ _- p
& K" U1 f( a9 x- memset(bitbuff,0,sizeof(bitbuff));
# s; u9 Q' W. g6 I
" \' Y- w- q& t( d- J6 e- W9 T- }
1 q2 R8 s+ ^/ G - 3 y" [: Y3 Q; s/ I
- else // 可能是标点也可能是换行符
$ J( [( j2 W* X% y - 3 W1 j* P0 C, O( Z2 ^3 K% B+ o
- {
9 n5 r+ W; @# Z( X r [* |
o( }; v+ C5 J- if(buff==0x0D) // 换行标志! S" y3 e+ z( c, K% l& V
$ x8 `$ a4 V- @3 b1 {& G- y+=16;# i W6 M+ t# z0 r: L/ R+ D
- 6 Q: y, h' a- [" H1 A* ?2 f3 [
- else) I- Y) v( A4 G2 O! Y* d
- 3 x5 Z% C% w9 R3 k
- fnShowChar( x,y,buff,16,color,BkColor,0); // 字符' E1 h" E1 Y7 u& y" C
O) w, q N, G) L- x+=8;7 h7 L" L1 V3 ^# e" q" T
- - |, h, N$ R4 j! |' t8 |
- charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址
- _$ U" H) T b% j
0 E2 d- A4 ^8 {3 C, W, h% B- }- ^+ }1 \. c- n2 h/ W- T" I9 x9 B
- 3 L/ L6 ~, b2 s1 k
- }
e& ?( [& n9 f/ W. ?3 J - $ @- l1 Z5 Y( p# E
- // 计算txt文档的偏移地址0 ?3 G1 E' P& F8 B
- ! \+ Y- i, ?7 f/ Y) e# [9 z
- if(Hznum!=100) // 判断是不是读到文档末尾了% m Q) _6 `0 @& _; L3 f0 j' m
- + v/ i3 ?( K. W) x0 @9 P# I
- { K+ M& j' X9 K3 c+ m
- " L* e1 f9 \( q1 b6 N% Z
- CntnuF=0;
1 n8 O0 n: \2 Y9 \- `$ \ S: | - ' [6 P+ ?4 x7 [! g
- }* q' N5 N( v* D& g/ U. D
4 f( e A. j) Y9 N- else // 没有读到末尾继续读+ f/ N8 n( y$ J" {/ B- n
- 0 V% Q5 Q, _$ h( F) p. c" U
- {
# c6 ~. g+ [! k) |; R" n4 q
1 ?, ^/ S. N! Z7 B' z- |1 }' b* r- if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+100
2 N9 ~* ?7 d% D+ m' M - . o6 Z3 s, I8 u$ \. ?) R
- offset +=100;- l/ e5 P1 Z9 ^2 b9 B$ J
. t1 h% u, n; B! ]9 w- else" P* b+ D0 I2 z$ ]
- : X- \: D1 J9 f
- offset +=99; l0 z* _2 f5 P7 g+ m( |1 ?. O
- # J/ b: ~. c6 C8 ]
- memset(buff,0,sizeof(buff));% f$ r m$ t4 D) K& U. x2 N
8 A) j; i5 B4 k6 o- f_lseek(&ftxt,offset); // txt 文档地址偏移4 Z( d- H# q- x3 L
& B5 Y3 U) z- n4 ?* I/ k- res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据
$ u* R6 ]0 |' ~. c+ F' l) D, [ - 9 R, x9 s! Y# [
- }
3 D! d* p) ?$ f5 a& d6 v! |: _6 f& O& [
3 N% R, i. d- @4 W* a. k8 X5 ?& B! p- }4 b6 f& o. Q7 ^7 o, b1 S: C
- $ k n( ? ?5 v$ z# v% d& F
- f_close(&f1); // 关闭打开的点阵字库
, K. Y( m+ N" `3 w" W - - @" o5 c7 e$ K! q! f8 D( z
- f_close(&ftxt); // 关闭打开的txt文档
, x+ O2 s# F9 ~ \
5 V; N0 V7 ] x4 H8 I' C$ \- f_mount(&fs1,"",NULL); // 卸载磁盘
( R! h- _! n2 [) L( x2 f$ E) G9 h# e
% n2 l! R- p2 q( G. K9 C2 P; c- return 0;8 h( E2 z& S$ u. {4 Y
' h- }4 f% }, Y9 z8 r- }
复制代码 , k3 R3 V6 {1 o0 S; [( g
以上是简单的电子书的实现。 G3 \3 ~% U1 B% o# u5 Q
因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。( X' M: C7 u/ x/ J* b3 E, i
其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。' @, P) `# _/ M0 z
SD卡注意事项:
) U; S1 Q) d T5 ]3 T 对一个文件读,必须先打开文件,读完后关闭。
( I# B- Z0 y& T1 z0 p) k4 f3 P. y 对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。
4 u: i1 E$ u3 _/ E ?( | 文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。
& g n0 P3 Y) o; v7 f
: {6 J/ A' F3 B& F) t' e B: J7 v+ _0 p4 Q# y
|
|