|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。+ X% q% @9 f$ m/ F2 v4 d8 S
取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。9 z+ k% {- w0 C- u# ?
国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:7 c1 O# L! u1 S- \! ~9 N8 Y5 q
offset=32* ( (H-0xa1)*94+(L-0xa1) )9 @. p/ d5 R/ F& x7 K, [
H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。
' R/ _* y4 L5 m# M 获取点阵的代码如下4 {6 r D8 k5 Q2 A' Q% q
! o2 \2 ~3 U' |1 V4 T
- FATFS fs1; // 挂载SD卡的分区用3 G0 Q$ ?5 Q0 `1 [: n! r6 ?
- + w1 V. ^ o$ {+ ?/ l7 W
- FIL f1; // SD卡中字库的文件描述符1 p. |# t# x( R- `; J
- ( ?7 h; r% I6 X. t2 @5 V! [7 J
- FIL ftxt; // 要读取的txt文档的文件描述符6 L. E" r: n1 ~$ y; x* W/ C! m) i
1 i/ E8 Y, {* `5 B" O6 K- {- u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字
5 H; @4 ?+ P$ J8 B - ; q: P* x: ]5 l- p. O% G2 ?' d7 f) \
- {
N7 D% e( N! ?" D; r s+ L5 I - 1 P, B) I+ n$ {- T- q
- u8 res=0;; }* R+ A% `1 A0 F/ ~& S0 f, B \
- 9 `9 X# l% v7 L" E
- u8 H8,L8;
/ H8 w8 J) \' q: X. h - % R& i! M; G2 |. N# c% s6 w
- UINT num;
5 @, S) g0 S- X. K - # x- x5 Y& E" D* y6 f; G4 L
- H8=*p;
2 q9 T( q# X$ c6 S8 U1 g2 j - ( k& @! Q- y3 J
- L8=*(p+1);3 W- t8 v" ~; g1 u& S) C) O
# y, f6 ~1 z* m8 k$ c. t$ A7 q- f_mount(&fs1,"",1); // 挂载SD卡
& h; r5 G; D( E, H0 w' m% w; B
- c- n2 k$ x4 D% O5 r9 ^8 f' R- res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库 J$ M1 J- z2 J1 [: C3 k0 C2 V5 w
/ ?$ x' Z& T% p$ T5 H' E! d- IF(res!=0) // 判断点阵字库是否打开成功9 e7 C" a' i, K; v
- 7 h/ R2 A. i$ j5 e. K0 L9 ~
- {7 o- _' o. R* r7 l: g. A, C8 @* C' }" [
" E: w7 \( R. y" q b- fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);( @. N" r& B; w3 S" ^+ t" L2 ^
$ _4 V0 F( p0 C# b- printf("res=%x",res);
& Q% U5 B* e- d6 U+ P: ` - 4 O) t8 _& P9 x; V2 s
- return 1;
+ t! N6 Q4 w% w( Q - # A6 s, V) ~2 r0 F) x' F; [& F/ V: c
- }
7 i* l+ u9 f; {6 D+ x7 P
1 Z5 N4 u9 y5 z9 l6 Y, H- else
' b: l1 D9 Q- W7 P& b# a% K( W - $ g/ C- J/ F7 u( r6 y% _% }
- {
: Q- g2 x' O+ a1 E$ A0 ^ - ; R( d L- H5 m F' x
- fnShowString(10,120,"open ok",16,RED,WHITE,0);$ n/ j4 I: o+ F" Y5 n
- . o, V1 D1 O" d/ l3 @
- }
' G8 \" X; }5 N - 1 b5 w$ G1 c8 ^/ c
- f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据
) X# I8 U8 U0 v: H' K' k
/ }4 w; Q" f8 I% f- res=f_read(&f1,buff,32,&num);& e& h/ ?6 e2 D$ g4 L' H$ ]
/ m Y" _& i& b# v- f_close(&f1); // 读完后关闭点阵字库文件
1 M$ V' T0 k" z4 ^' Z! p9 `" ` - / w0 c4 \' d U4 o" L/ `& B( I. F0 t
- f_mount(&fs1,"",NULL); // 卸载SD卡( L$ k4 B- G3 z& @
+ C# W$ ^) V$ v2 t: g- return 0;' [. ~0 N$ g2 |" {! t
- 4 Q) q" P/ [2 @, D
- }
复制代码 ' l7 V0 Q& \- Y& k$ Y$ K/ @! U J" ]
这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。
7 P' C, t9 Z* F$ Y 实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。
- q, }! H- l, F" U t 首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。. J0 _0 `- I) u
下面直接贴代码:( h( q* t# D; m7 O7 a
: H# l0 Q- B5 d6 [* d& P- x
- // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示# q. n( b: |8 V; p0 p
* e/ O7 z4 [ W8 k- // color 是画笔的颜色,BkColor 是背景颜色8 @1 P: G2 P! M3 ~7 w# |' W! ]2 k- X
9 v7 W* A9 q* c/ Z+ m9 H- u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor )+ S; Q0 i1 y0 n. Z
3 \4 t* R- r! U! L" q- {9 V6 T, k# o$ C; j* \: c
8 }2 ]- @8 x+ t9 G2 e- u8 res=0; // SD卡函数的返回值) R, D6 W" r# M. P
- ! Z5 T! I ]3 b+ ^; E* Z
- u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码2 Q! Y' {. x0 B! j
7 Y8 J$ g* y% E' o& u- u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据: \. ~- L) f! Q, T
- - H& \( b( |9 k4 b
- u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位
; S9 S8 w7 T( ^% p w& y7 y3 V/ n
" o# }1 L+ P1 [$ O. l( s2 ?- u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了
9 Y0 r% a" g+ U4 e7 `8 \% X& Y - 4 p8 i# R Q9 A
- u16 offset=0; // 读txt文档的偏移地址! K1 R/ c) o8 R
- + M5 `( b7 z# U' r$ a1 K5 c
- u8 i=0;
' u0 }7 ^' S2 \ - 1 J- E$ Q& q6 W2 Q
- UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数
$ t) ^; l9 U; L/ l5 S0 [3 y3 j
3 p7 I+ ]7 O# c- u16 x0,y0; // 点阵显示的位置
2 j7 h$ w; V. O+ W8 e3 l
5 i3 {- y0 Q/ T. g; u/ U0 m! f- c- u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,
: Q! o* V5 J# E; e
* j" V+ X- A B a- // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。0 {8 Q' k5 C4 q N5 C
7 x5 [ `* a1 L: H p/ C- x0=x;
9 E. Y$ D0 I: z( J2 @/ _# e
2 ] h" M% S V7 w' j' C- y0=y;
V. n/ s- x# ?1 B- _1 }& n
! d. E' P+ C/ Q- f_mount(&fs1,"",1); // 挂载SD卡
; e4 y, z T8 U0 U4 Z$ f
& F, f+ r' X$ ?1 I* O. a6 ?/ r+ _( B- res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件5 S$ t1 v+ i7 T' ^
2 }! i+ `% U( c) }- if(res!=0)' [# I) o% |3 N
0 t0 ?7 O7 q6 ]" `& b/ w- {) E- {$ f. o' j# s
- : [; S, O4 {+ z. I' u* m3 k, O
- fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
# b" ~" Z( B4 Y2 G9 d
* Y9 i: s% Q9 C* k- printf("res=%x",res);/ r0 c: v2 D1 K
- " J4 ?2 P& D, `; d8 ?( x" ?3 |
- return 1;
* R- p, h# |# q" D7 e& G, l - $ E- S5 u% y) F+ I. [0 Z
- }' V' i/ [$ H/ ?6 y3 H( V
+ I) p D; ]2 o* p/ J- res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档
$ P1 N+ V/ }( S8 X; p: b, u7 ~4 e - % D* p. ?. K0 l7 B4 x0 V
- if(res!=0)4 h! P. d& Z# I1 B' c
- 6 ^% ~$ L+ i1 f/ l
- {
4 t, k9 U, d2 R2 z- u; O
! i v/ V( S' h1 s* ]- fnShowString(10,120,"open txt fail",16,RED,WHITE,0);
/ s3 B% B" Q7 k K - 0 q8 a. \! Y9 X. O
- printf("res=%x",res);
3 \6 A5 I3 l1 y3 s/ }+ _& i - ; c4 ] g$ E% w& }- f0 _! ]
- return 1;, N r6 C6 f3 L1 V9 {8 U0 ] ~; a
- " O, U+ Y! Q4 _' x8 x6 j6 O
- }/ d4 W4 n, K3 A7 P3 Z, j7 A
- 2 T3 z+ g% ~9 S
- f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。
$ p! V" [" e! J/ J- q8 C - . e: m- ?. O% m
- res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节
3 T3 v4 t/ j, J( X) h; e9 g
& V9 J8 ]( V( P$ }3 { x0 D- while(CntnuF) // 循环判断是不是读到文件末尾了- g9 H3 E7 ~6 \
+ g7 k* J7 {/ d1 ?4 N/ F8 @- {
9 r0 T) s8 w! i; j& \ - - J' H& \' ?2 e: d
- for(i=0;i
4 W/ D+ ]/ z3 u" r5 j
# [: m4 I6 r# z! _: U5 ^7 z" b: s- {
. N1 O& y$ H+ f7 {) l
" G# y+ |" B+ S) W4 E6 M- if(x>220) // 显示位置的判断3 W, R: z8 s, K- f! l- e7 O
- ; C6 C! O3 @ C
- {
4 W) N1 [+ C$ e0 I
( w b( g/ z- ^( G" Y4 P- x=x0;
& ?0 @/ M" `9 E; T' y' g
5 _5 s; v( e; Z+ G. O8 J- y+=16;4 }. w1 U: S) G! b+ s$ F- n8 e
- ' l+ B8 b/ D+ i# U v, c" S% y; `
- }/ u7 {$ h+ _4 u
- 9 U2 K3 @: h/ V+ E
- if((y>300))1 N' L* u3 }) h
- # U3 E6 _: Y6 D6 u
- {
4 w9 c' s: _6 j# U6 K' P8 s
) [: O+ s) K1 e& r- fnRefreshscreen(WHITE);
3 j/ K3 C% p8 W
! C) m3 Q2 q: B- x=x0;0 B8 Z+ _1 \1 C( o
- }6 p; O4 y/ M# c' _4 L, j) ?- y=y0;
3 {- |- q9 B9 T& G - $ `6 M+ K! k: V+ h5 }2 _$ D$ c
- }, r' G |; ~! u8 ~
- " B; m# d& M4 R) v8 W2 W/ }
- if(buff>0x80) // 是不是汉字3 W, X! R+ a1 b* ?* q1 l
- U& u! p+ j& m5 B8 z2 C- {5 R1 Y; m, V8 B! g$ g
* D- s' i' {& G, R2 O+ @- f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移8 l$ H G4 c+ n1 b _$ y
4 [. o8 G! n Z5 t- res=f_read(&f1,bitbuff,32,&bytenum);1 S4 Y$ v, F% V$ V+ h! T
1 Q0 E( X* o2 ?+ c9 _* x- fnShowHzk(x,y,bitbuff,color,BkColor);- y* Z0 e/ Z' }) c# g
- / [8 |# Z$ U, |8 w" H. \, q, P
- x+=16;: x& `+ I* B2 |8 t
+ Y9 F2 e" Y0 p. } Z3 ], r- i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,
( J6 d3 s! W0 n; [: } A; z - 2 a, m- E1 T: I" ^# e
- // 这里需要还要一个. F4 K" P; T) |- ^) t" d9 w
- 8 X; q7 t1 q- \ T$ r$ L8 f2 `
- memset(bitbuff,0,sizeof(bitbuff));
6 z* x$ u5 S$ g2 K" U - , f4 o6 R C* l- f ^: u( Q
- }
+ J5 u) s+ `& T! z - E9 N: F% A i2 j% r+ f# R1 e
- else // 可能是标点也可能是换行符
$ }4 q( t( x O1 j3 H1 z
- ^# o1 }* t$ G4 N: ~$ |' i- {1 ?4 c. o n! \& a2 |4 b9 u+ t
3 p) S+ Z" l. [3 E' Q) p9 _1 ?8 D- if(buff==0x0D) // 换行标志
- \9 C* y/ t4 A - . n0 a. r2 x9 V5 d, \) g1 w
- y+=16;* X% D5 d" L+ n' o' K
- B+ [# g$ T, T2 R5 v4 f- else
) e. J# Y _8 T ?0 h& B3 E
. S; M0 ?* @# w% a5 m) U- fnShowChar( x,y,buff,16,color,BkColor,0); // 字符
: ]1 Y% R7 u9 z- {
i6 {0 t6 b' H3 L6 f' r2 Y, R/ K- x+=8;9 P/ a9 |' P3 Q" B1 A
- 5 i9 e! L3 P1 c
- charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址2 ]; Z B: U; s2 t
1 E6 b( b9 K3 C" ]% O- v0 I0 V I- }
7 d/ z" @# W" J - ! S* ~# y2 M4 b) R
- }
: v$ `+ Q: A' s& K# J
6 A5 m( L& r; e$ |5 S1 B- // 计算txt文档的偏移地址, @2 F+ Z; y& @ y" y- f1 y
- . A# O4 ^/ n8 e4 D0 M; I
- if(Hznum!=100) // 判断是不是读到文档末尾了
8 V* B0 E0 _( o( B
7 b5 W' b/ \3 q, a9 U/ t5 u- {' x8 m( I% c2 f% [; f. e& a4 Z0 i
- ' }" J+ d, O; {: A
- CntnuF=0;
1 Y- t! s) ?. f9 Q1 {
`" u" X6 M) O" K6 a- }
7 g, T& E* z- m2 }) u' _9 g
, r3 O, B% O% \0 Q, }* z& g- else // 没有读到末尾继续读* K" \- L2 ~& {: j0 ]$ x! A
- . d. ~# ^+ A Y6 D0 @ O Q/ q
- {2 }' y; n; X( ?2 C% i
- 5 {+ H% H2 v. g2 w
- if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+1003 C' G8 [. Q, h3 ]% M
- 3 x0 ?0 L' a+ W$ v; c& ~, M
- offset +=100;
4 C% w" b5 t+ w1 J d! @, d - % ~* b$ [+ s9 |% E; @/ f/ T6 p, O
- else$ r+ `+ q, U- K5 a* h
- , u9 Y* b! M+ T c# B
- offset +=99;6 T; ]+ _8 l: F* X+ L2 S5 f
- # D; E0 ~3 T3 {
- memset(buff,0,sizeof(buff));" U s {3 `. Y8 i* ?/ G/ s; g
4 i; F! W" Q8 f, l+ f6 a- f_lseek(&ftxt,offset); // txt 文档地址偏移, u$ j! |+ y& E" V
/ ~! e3 \8 ^% j( g7 [: N- res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据
4 \; i8 B& [( j; B x+ z7 j% K' q
u# C) g) Y! p! w. `- }
8 H. P5 ?$ k+ V: S' U
) d- T% g" L5 h7 a5 ^. `& \- }
6 G6 Z; K8 ~5 P3 z, a4 g9 U2 e - 2 Z7 V. A# d# {& r0 S0 l, R: i
- f_close(&f1); // 关闭打开的点阵字库0 z3 \* B- z: T9 s: v L2 I
- % c g: E+ |( C; N# @* q
- f_close(&ftxt); // 关闭打开的txt文档' ?0 J m" H- o2 p! A) e6 Z$ t
$ R6 u! Y+ F5 A- f_mount(&fs1,"",NULL); // 卸载磁盘
$ s9 Q9 z3 _% J8 j; Q, | - ; E! c& v, @ e ^7 G
- return 0;# B' x7 S) E8 t1 f* g* k+ x
& u* c2 P# i+ {5 s* @$ ~/ e+ x- }
复制代码 ) l/ O A `& [- V5 i6 ~
以上是简单的电子书的实现。4 }5 D4 d. p, I; H
因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。
" A* u: \" R, u2 M/ c 其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。
/ r2 J$ Y9 E9 a* k/ Z! i& } SD卡注意事项:
3 ~1 O$ M; O' S' Y$ m7 L 对一个文件读,必须先打开文件,读完后关闭。5 Z; i5 {. x; u9 X3 x+ X6 E
对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。8 v" e7 ^* k1 U2 b% M! N- ^3 `
文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。1 ^' C1 s. t. O+ |, o
" c4 j. |& ^3 Y! ^; y
% e$ w. q& K O+ w |
|