|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。8 i# z/ U" P9 G4 D
取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。
6 @! d# P# U, E' q) t 国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:
, G3 k* ?* @6 K4 D offset=32* ( (H-0xa1)*94+(L-0xa1) )0 ~8 t! n9 E. n6 e
H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。
6 k/ | S0 Y) t" p% t+ _+ ~ 获取点阵的代码如下& X" e* J0 f0 a! {4 c9 ?/ E
8 T# ?9 D3 O1 c/ D- FATFS fs1; // 挂载SD卡的分区用
2 u$ w" K9 v) P7 X( Y# K6 ~
7 C. t. w! {" Z; \7 \' [* w* y- FIL f1; // SD卡中字库的文件描述符; g8 l* |/ h j: K3 {& T) l
- ! `% J% K+ ?5 R" U; d& x( u
- FIL ftxt; // 要读取的txt文档的文件描述符
w2 u* W/ z; K. c' X# Y0 m) u - % G8 ? N2 B) D0 ~
- u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字- G8 L$ C/ z4 p& O' ~
- e; H/ `& J+ `, J4 l O( `- {% m9 u1 Z+ O: F m. D5 H3 C8 v
: E; H2 ^5 G& z6 t) E5 Q- u8 res=0;$ Y. H* e; G2 ?5 v
3 s+ O3 \0 w/ ^9 K$ ]4 N8 u- u8 H8,L8;
1 g9 F% A0 T0 d( N& B5 E8 B
. \' [" U4 R+ w) ^- UINT num;/ T# W: \: Z8 k
- G) K. A1 z3 ^' Q r! B6 [- H8=*p;) O ^* L1 l/ y/ O7 y$ |
- ; Q& C" ^6 t v$ n4 `
- L8=*(p+1);
% Q _! O; ]4 t! i
$ O% ?1 o3 x2 p1 X% I- f_mount(&fs1,"",1); // 挂载SD卡 _* ~0 W/ c7 w7 y' j: ?( B7 d1 ~
- " R; x+ q# F& p& J; Y6 y; ?! y
- res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库
. o) u. {0 D6 w1 u* g5 U" E6 z - ( x% Y( h& e) X: x
- IF(res!=0) // 判断点阵字库是否打开成功
9 V/ e l6 y# j2 _ - # b* P# \& U5 v2 x1 E- j/ |
- {
/ u2 e6 o& X) n* P. v7 T+ j6 Z( U
% a9 E: W7 x* }+ Q- fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);. I) ?7 S6 R3 u0 a. m$ q
_7 N9 j2 L: I/ v- printf("res=%x",res);
6 D f/ K3 k* N9 N; _9 Z1 B - , {8 L. t5 `& H* J8 v3 \
- return 1;
+ M# I M5 o7 J2 }2 t0 ]5 R" I
& f% c8 R7 i7 R- }: E) y/ O2 t* a9 H- K8 c9 P
- $ R1 V2 h7 G: q+ ?' s# t0 Z: C6 [
- else
- p0 ^% y; t3 f: A/ S" [/ X - # X# S y( \, s- q3 ^0 Q0 q
- {
7 f: D6 C- L" G C
. o, C7 ]! E1 M* R9 t- fnShowString(10,120,"open ok",16,RED,WHITE,0);6 h+ Q# B2 ?8 U8 C0 T1 Q- ]
* F8 s% j8 u7 T; n% M/ d- }
* z6 O% q3 m7 n3 T* D9 Q - + f3 a6 C9 o% ?
- f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据
9 l# {4 I* w: | N - - N8 E* q) X) U. W
- res=f_read(&f1,buff,32,&num);
' s" b W1 p* M2 O
. h6 W: S/ I; s& `, S+ C2 |+ _- f_close(&f1); // 读完后关闭点阵字库文件
) w( Q/ A) R8 E% I5 ? - ' x! Z. R$ p& G% C p
- f_mount(&fs1,"",NULL); // 卸载SD卡
0 _0 C& n5 q, o% G
, f, w5 e/ a z7 @. N# F/ y# N) _4 E2 O- return 0;
9 Z$ b, k2 W1 c. ]' j6 a - : N( e% t2 k! S% v/ ~' y
- }
复制代码
( N$ g$ \& R! B 这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。1 _3 X7 V3 i5 A4 Z1 m
实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。 p* ~, y9 @8 n
首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。& q3 P0 k, C4 V0 b9 G
下面直接贴代码:) d% [2 D5 p% A$ C# X' b
9 Q& v$ l4 [. u1 E9 S B
- // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示( N; ]' t+ f8 p6 c) A: ~
9 m- t/ M$ u- u7 b- l4 L. h4 F- // color 是画笔的颜色,BkColor 是背景颜色
6 S) [/ v$ t- [7 b! Q2 A1 s, A5 d- z - ! n9 M- D6 H( T/ H2 F$ e. d6 a+ B" z9 @
- u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor ) @1 E' g9 c/ T6 r- ^ y
- , Q4 S8 ] ^/ p8 E; x) h9 e% R
- {# I3 _6 K, |8 c
- $ T$ q+ z6 R2 y
- u8 res=0; // SD卡函数的返回值' Q7 i5 B; r' P
- 9 g- n9 i+ d+ U- v7 r# e+ n' Z. b
- u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码$ L8 d* c$ X/ q+ y5 w& H
! G. |3 o+ t: n" A. f4 d# |- u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据2 d, Y# G5 W) U
% Y! \2 N G6 h. W9 m& P' U' D- u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位
6 C Q5 E/ M; U+ U3 l( ^
' P7 Q- X. b- {' _- u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了
7 x* F4 O3 J" R9 K2 v - ! I8 [. G3 a+ t/ o
- u16 offset=0; // 读txt文档的偏移地址
4 w4 |9 G: v6 D: t
+ L& x$ w) V+ M7 I5 q9 }; p5 X- u8 i=0;: c8 z {! g. ?8 `( R; v, B2 p
- 8 x3 M7 y2 X8 l7 a# q+ Q
- UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数/ \- s; U; M, E' V
- ' A( n/ ]) p4 q! t& L) F
- u16 x0,y0; // 点阵显示的位置
* \6 J9 a; E7 g& @
3 B- M0 ]2 m. p3 S) v- u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,! Z9 m# e( Q$ q
- 6 \+ g# |! V) h- ` ~7 q" J
- // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。8 H5 w j4 U7 ` u' N% L+ R
- 9 A+ r. Z$ U3 I. b: L. I8 }. ?
- x0=x;
6 {: c9 U) h% C8 r - & B' G |- w2 R' Q5 ^7 e. n
- y0=y;
$ i. y4 n4 G) E7 | w+ p) A - + n( E) W' a$ G. G) j, ]
- f_mount(&fs1,"",1); // 挂载SD卡
5 m5 K- G! @# O9 b! e9 I/ J - ) M3 h1 S$ h3 r0 U$ L
- res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件
( O/ B6 P9 _" _! E8 E - * H; L; X O O
- if(res!=0)4 M6 |6 C2 ^. R2 o' g& Y
- A% X* n' Q/ @, x! F) J1 |- {! d8 S) {& _- B- b
" r2 o, p& X% Z+ j- G$ n- fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
" ]% k% |0 k3 b& [/ R0 H- m - 3 I* [+ k7 e0 p& s1 r
- printf("res=%x",res);' c" g: [ @' F+ J/ b$ c# }
# L( N6 N4 C7 y1 B; R3 N% G7 M. }- return 1;
7 j) v/ |; i# C
* T0 [; s+ F; Y: z# ^- I* _! J, ]- }
" s4 R6 Y5 p3 A! C4 l2 c/ T - ) A/ X/ q6 _0 U+ M2 a1 Y
- res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档
8 B/ o$ i$ l: g$ ~ x$ ], e
8 h9 C- `2 R, o/ D2 T2 Z- if(res!=0)
' x) u1 T: W0 e# i
( u3 m+ u' R# ]5 C8 @+ b6 x/ V& |, r- {
. J4 ~' ~( B* `
* Q4 k# u8 y8 Y1 @* V- fnShowString(10,120,"open txt fail",16,RED,WHITE,0);
4 J( n; c# ~0 W, |0 K6 J; @' ^6 J - ) H7 a' b! J3 l% Y
- printf("res=%x",res);$ w3 _: O$ C5 v
- 2 i8 Y Q0 J( t9 ^
- return 1;
! d- p' m& x; b+ Y - 8 `( G3 [& e$ Z! ?$ J% l
- }
: B- \- L& l$ t% v2 ?# p$ l1 o; W - / r2 {' |; }5 B9 e p" ]
- f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。
$ Q: ?. e* H& x3 Z2 K+ i$ s& d - - E1 j; W* [2 F
- res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节
) b& |5 k. H# f1 d! B" y - $ c, ~& \0 a7 \5 p. v
- while(CntnuF) // 循环判断是不是读到文件末尾了
" R0 d4 s+ E9 I% Y' Y+ v - ' F9 C! |! D, {) ?: n( S: O+ f
- {
* c* Z- z+ C4 c' z$ _5 ~% N, N
: r" g: x: F0 b3 z" m0 E0 }- for(i=0;i
! a8 c/ a6 q# T { - " O& {2 j5 ? L) h; j! Z n+ Y
- {
9 V8 W r6 D2 ^% E9 J" a/ k - ' `! v, f3 x' S% A1 F( F: j2 G
- if(x>220) // 显示位置的判断8 `) x* S7 W. O; d+ _) D$ I' a
( T& i* P# {6 M! @- {: z& A& I" e. `& [8 }
! |" ]; ^/ R! G" G/ N+ \- x=x0;
9 {3 r9 t: ^' t I" m7 z - , Y4 K2 X0 z6 v) G
- y+=16;: h* i( y" O' A* S* g. W9 l$ ]
- * O2 U9 e" f& q& X
- }
4 L8 A8 S, a: K' J* W: w B
# a% _' F- x7 @- if((y>300))+ c% p! b7 l( r! |
7 X/ @9 j) n5 e Z; r; K- {
+ s9 [; u$ s0 J4 ^9 q5 F
4 m: V9 g1 Q6 q) H' [# f- fnRefreshscreen(WHITE);
( R: D3 R( T' B4 N1 g9 z0 \
- M& N& A( e- V$ i- x=x0;8 A9 f: d8 r9 |$ f/ S* H" n$ O: P
2 }+ n! Y5 g# L1 m) l' H- g6 B U- y=y0;" x" G6 J5 [- e0 n) S! k- a& G# ^
- G' f1 U& u/ Z1 R4 x L- }
( K# N& k. [# D+ W - + \0 b, n8 v' t* e' @7 Y3 m* C6 {
- if(buff>0x80) // 是不是汉字
. D9 {2 ~% X3 \) D - 7 \7 D. y" W0 k& W% R
- {
4 o% H4 U. i: a) M3 g
- f4 }' D9 m( M* ~6 I8 r- k$ c- f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移! A% H2 }- R, h! F3 O
- # v: J- a/ k: R! P
- res=f_read(&f1,bitbuff,32,&bytenum);- c9 E. O5 ]6 B' k" U# K- L, D
a% Y1 G% H* I( {* A" `* H& w: P- fnShowHzk(x,y,bitbuff,color,BkColor);8 p v+ T7 u8 }5 P
- $ c! o" S& e8 ^+ H! Z
- x+=16;4 E7 r I- S+ @ V" e2 l) r
. I4 L6 F7 O& h3 B0 m) K/ o, y6 M: l- i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,4 y' V. ?) K" ` i/ a
9 |# H1 ^+ b w- // 这里需要还要一个
5 V- C- r2 L7 Q' m! t: y
9 M6 C2 p. W9 H7 G s- memset(bitbuff,0,sizeof(bitbuff));
" G0 x, _0 C+ T5 A B
. T0 T8 s" r6 G! D- }
2 d, P z. J" }
7 X3 V+ }+ U: G1 B& X: {( X& d- else // 可能是标点也可能是换行符% d- X) J3 m; q2 J. _! w- J
- ( u7 o+ N5 w8 R4 _/ \
- {
. ?& i6 z9 Y0 ^( e$ U( N; W
% e2 F5 L) R! d' Y5 v# q/ Y- if(buff==0x0D) // 换行标志
" F0 N( [, z& F$ f0 i
& h: Z5 `$ z3 |$ r- d- y+=16;4 ^/ N6 C/ _! J1 C6 {
3 e# p1 [- @8 B7 w! g- else# K* ?0 r4 u, d' f' g- r5 R: `5 q" C
6 \5 D( J9 T5 C2 k; }' W3 F" Y+ z- fnShowChar( x,y,buff,16,color,BkColor,0); // 字符
7 _8 h+ {0 C5 h& J" Z+ ^ - ~. r U/ w6 t% y+ ]
- x+=8;) ]5 w# f3 c% s% W
- " q2 z# X. Q, b9 z* |- a
- charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址
& b: s5 p6 p9 |3 K9 s
: d$ A8 [9 c! V' E J- }
3 N% g1 ~ L' u& X1 P9 s - 8 Z- m5 U* e" N8 t* S! ]; \
- }
0 g! R4 B( G: i) | - ' ~9 f6 ?0 j5 S, \7 b! E* X
- // 计算txt文档的偏移地址: G: e! z1 S$ c' N; J
, D, J$ p7 u7 G6 F! N3 ~0 h- if(Hznum!=100) // 判断是不是读到文档末尾了1 @/ U. q& Y( w8 e
- 7 C& _. Y2 ^# W
- {
6 c2 A+ U* t% A, e* S! `
9 ^/ h" E- q2 L7 I# Y' D- CntnuF=0;
5 s7 ]! H8 p- p8 p, b# b
; W1 q# G7 O. E1 O) p5 k- }0 O' l1 L& M/ B v c
. t! g1 W \& U0 ^0 C- else // 没有读到末尾继续读
5 n& {" X$ d$ C+ P4 |. @ - 5 R8 _! m) y) P& j" W" U! a
- {4 Z s# Q7 g% ^" w/ C- x
$ w0 c+ b- w+ x9 M; {' u0 V- if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+100
, P% t* T: i7 J
$ p; @5 U- s8 C1 U/ [4 a* E, F- offset +=100;
6 M! y8 J% H+ C: ]4 h2 g& r0 A
Q' C* z( m3 Y8 s2 G2 M- else
7 P0 C- S6 ~/ t
- L) o5 }. d: B( v* N: B' R {- offset +=99;
/ a# Z9 ]6 K8 ]# o - & b- Q1 L9 [0 @, r. F' G7 R2 O
- memset(buff,0,sizeof(buff));% j1 j- l7 _9 b$ i' ~; O
6 J0 s+ \( t* f" X5 @" Z( g- f_lseek(&ftxt,offset); // txt 文档地址偏移& n I% v8 p3 u" \4 ^ c% l& V
% }! D: s+ ~( x6 M- res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据7 H0 Y- |# ?1 z( Q3 X* V( j
- 6 m8 p7 E# }% _) M
- }& `5 f, i5 B. B4 r: U m) o5 X. T
- " q" B" g! A: [6 e+ z% p
- }- ]' W4 t1 D5 s6 i8 m
- / b7 U* \1 Y, n" q+ L v
- f_close(&f1); // 关闭打开的点阵字库
* L! ]) G( M' ]9 E! L% F - ' M/ A8 x. w- ~" g$ Y' X0 A8 b
- f_close(&ftxt); // 关闭打开的txt文档* K2 m) M- y' P% J* f
) m! W" ^6 M; C, [0 d6 [; E) ]& z- ~! L- f_mount(&fs1,"",NULL); // 卸载磁盘2 B/ R4 B) e0 L! K% n# Q
- % S% k3 _5 K) }: @8 M. ?
- return 0;- s+ b5 [9 F# i" w
' V8 e; n1 {- G% k5 M+ Z( S! y- }
复制代码 % ], p7 h9 v- m- |
以上是简单的电子书的实现。
/ b$ m4 U2 q( Q6 k 因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。( j9 a! f, n; { D( g p. |
其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。( ~4 \. e6 f: z% J- U2 i
SD卡注意事项:$ [# @1 y) U# `1 N
对一个文件读,必须先打开文件,读完后关闭。
4 q, t9 k/ `/ n+ V/ h( O' _7 K. y H 对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。
3 r) A$ x; f; [# M$ _! V1 \ 文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。. h6 F0 ^) }+ s' J) k9 g
, ]7 O7 _. ]3 X3 ?0 c" c0 T$ g8 O7 q8 h7 C5 ]. V' h
|
|