找回密码
 注册
关于网站域名变更的通知
查看: 409|回复: 1
打印 上一主题 下一主题

基于STM32的简单电子书的实现

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2019-9-10 14:13 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

EDA365欢迎您登录!

您需要 登录 才可以下载或查看,没有帐号?注册

x
  今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。
- `2 f% M! r1 ^# k% ]' F( o& X+ a! h  取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。9 C% K; D9 H4 _+ y
  国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:
5 E+ h$ O. ]" H+ n# C  offset=32* ( (H-0xa1)*94+(L-0xa1) )
. R3 z, C; T$ I, T2 o  H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。
7 s" i5 Y9 y& T0 m0 R  获取点阵的代码如下
7 m6 Z# q8 X9 k8 _4 D# w6 X5 S3 E+ _
& F' I4 Y; F" l2 s5 E9 e
  1. FATFS fs1; // 挂载SD卡的分区用
    2 Q7 H" Z9 H" }

  2. ; O8 R! R! `( P5 x; z
  3.   FIL f1; // SD卡中字库的文件描述符4 k. Z2 r. p* k% n
  4. 2 t! ^+ E. z! o, ?+ Y5 y" S) R
  5.   FIL ftxt; // 要读取的txt文档的文件描述符+ v6 b6 ]: k( U! K- H+ ]* D

  6. $ l+ _9 s0 z1 W' n: O' q$ b; X5 F
  7.   u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字- d5 E2 Q' n) C; Z. g1 h9 Q
  8. + D2 b3 b& E; f
  9.   {! K3 h8 j3 w: O
  10. / M, `6 y$ q  n8 Q+ [' B& n1 r
  11.   u8 res=0;0 T# W) A+ S+ b5 o- Z

  12. 0 p- Z8 U5 b2 p4 q# H" h
  13.   u8 H8,L8;
    , W( `* J4 t- [
  14. . L! f  E3 B# t: P
  15.   UINT num;
    + o7 a" P  u" E' [+ Y4 l+ C
  16. 9 }9 n  x  t" U6 ~: l- Z! Q9 j
  17.   H8=*p;
    ( y1 n& G1 l6 @
  18. 7 \* r: S" q2 n
  19.   L8=*(p+1);$ N/ ?1 @# ?. N3 E/ k6 _

  20. , g2 h& I' G* }1 F
  21.   f_mount(&fs1,"",1); // 挂载SD卡5 d, \1 ]4 _6 G. G+ c" E. v$ r1 Q
  22. 8 y8 u( A+ y% U' y
  23.   res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库0 {4 n8 y) k5 J- c- m

  24. 1 A- Q, ?/ a% a( x1 M& G8 E" p+ l
  25.   IF(res!=0) // 判断点阵字库是否打开成功. V% R  Y* p( T

  26. & t  b5 k/ C# i
  27.   {
      F% D9 G( G9 `. {

  28. . J, q) n3 X8 x% m* f1 C
  29.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
    & [7 I5 R& t% b! H( N

  30. ( V! l6 @! k0 O, V
  31.   printf("res=%x",res);
    8 x5 H! [8 |& y) D  ~

  32.   ^3 t4 e# D. h- K# ]8 ^$ `, @
  33.   return 1;
    4 [% o% B4 `6 ?4 E

  34. 0 O2 Y/ g: v5 \% n2 @
  35.   }
    6 O% R! n( D& k

  36. , e. x+ D( D# j3 }# S3 E( o3 u
  37.   else" }& ~3 y% X/ S0 J, c: a9 z! h
  38. & {) u' R. e) G. K
  39.   {
    . I  V0 y. H- Y

  40. 2 ^7 e* y% J# C, J; q* @
  41.   fnShowString(10,120,"open ok",16,RED,WHITE,0);5 l- q% s2 s& W* v3 t% A

  42.   ]1 n# S# a# D
  43.   }
    2 V$ r) [! v. z! i& _) v" M/ N
  44. + o- E; R& ]: L) W( R
  45.   f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据
    8 `/ o/ J0 d8 j1 n$ |* ]7 _. U$ W
  46. # H; [: n4 v" T( W3 G* M% B, C
  47.   res=f_read(&f1,buff,32,&num);# F: T0 B8 M& E
  48. ( R- y9 _; ~  `! f; T9 h1 L2 x' J
  49.   f_close(&f1); // 读完后关闭点阵字库文件
    ( Y% ^/ @; y5 ~- F1 v" \
  50. - u3 }  Q  y& a- q/ d8 y/ i
  51.   f_mount(&fs1,"",NULL); // 卸载SD卡5 D) ]5 K1 p0 _" H3 U' L4 B

  52.   H6 [3 S* g- ~$ l
  53.   return 0;
    . i) t; I: r; K. i& e; o

  54. % C3 _( B* ?9 t; e9 C# `
  55.   }
复制代码
8 B0 y2 ~+ ]$ D+ R  v
  这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。6 c  D6 R" k1 k' Z9 C' H
  实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。
# G3 d* z( I0 d, J+ o# }' f  首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。
7 ]7 Q2 t: Z+ t0 {+ J6 L. u  下面直接贴代码:
4 w9 n; W" B) X+ N- K; f) ], M; d- W, x* O8 M0 f
  1. // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示
    9 L6 b4 I$ N0 e8 Y3 I/ M0 ?
  2. ( u2 R9 @( @* ^
  3.   // color 是画笔的颜色,BkColor 是背景颜色( d4 G3 M3 o/ W% |7 j( I1 {6 q
  4. ! L* \- w6 p+ p5 x3 t
  5.   u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor )
      P' z$ n6 I3 V% j$ n3 N
  6. ( ?* g. t! a, Q5 g$ ^, H
  7.   {
    . u0 a8 I" ?8 J; U- ~

  8. 5 a5 K2 P6 `& `* M
  9.   u8 res=0; // SD卡函数的返回值
    5 e: Z" Q& c/ Q2 }, o" n3 V
  10. ; d' M6 M! H4 _7 d9 ]- z: z* Y
  11.   u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码
    , P8 [* ~0 K6 T1 E/ H0 d& ?
  12. $ @- p, W5 @8 T8 S
  13.   u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据' _" O. F9 c1 N0 F: m0 g

  14. $ W  ^/ h# z& s/ W4 U
  15.   u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位" {1 ~# s! s" q; M2 V; L2 ]4 s- f, Q
  16. ; z, I/ ^7 X" R9 X7 a: Q$ I
  17.   u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了
    * z$ j, r( }: ^8 v% X& }
  18. & A; W! m1 t% t
  19.   u16 offset=0; // 读txt文档的偏移地址
    6 P' R* o; a  j1 ^$ c5 V

  20. 3 o+ Y: W% q( e/ N3 S8 [* O, W
  21.   u8 i=0;
    & L5 `) b/ H( h, e

  22. : L! x' n/ y' ]% Q8 j
  23.   UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数9 O. x& @+ a9 P* P% u% S- l1 D
  24. % R9 O3 G9 q+ {. T- i$ A3 D* n" m
  25.   u16 x0,y0; // 点阵显示的位置
    ! x6 c" f" @3 T( ?
  26. ! \' q. B+ O8 P. F0 M
  27.   u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,
    3 D8 [" k9 @. ^, r8 v4 y6 s

  28. * R" q& Z  D" Y$ m3 u# [- c9 X0 M
  29.   // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。
    * h, T: o; {; ~; e0 A# r  ?5 e, D

  30. 3 Z* F* h+ f1 E: U/ [3 k
  31.   x0=x;% o% B) l. n6 V% G' K) ]
  32. % R5 r7 E2 O' e9 H/ _  R+ a
  33.   y0=y;
    % s" J, t# q4 H. l6 b6 h
  34. ; M' B1 z1 @2 ~4 [3 a" U
  35.   f_mount(&fs1,"",1); // 挂载SD卡
    , G: m6 M3 {/ [7 p3 [* {  }
  36. 6 Q5 m1 q1 [  y
  37.   res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件) ^. y9 Z9 O2 E1 {

  38. * Q  y" n+ }' X( J! I
  39.   if(res!=0)- U! L! G; q9 G/ f; F; I4 m

  40. . y4 h) N3 C% R
  41.   {
    : s4 U0 z8 u1 w# P; p; S0 G% h3 V
  42. 7 E0 K7 o0 f, ~6 a2 l% c, i
  43.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);8 h( C; m0 t: h- }( v( e0 X
  44.   y$ M6 J" ~. [% }# m9 |
  45.   printf("res=%x",res);; _) q7 F2 @0 @# o. r$ j
  46. 5 j* ^4 x& Q' S6 I  c! g
  47.   return 1;
    * v8 ~% o9 U7 i" ]. [2 Y6 w/ J

  48. 6 c; t) L8 b8 @+ K: L2 j
  49.   }3 B" W# o) W9 m, c

  50. $ {' r4 T2 [6 I3 F3 l
  51.   res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档
    ) L# F, Q# J/ T* u
  52. & A8 z3 k; U/ M& y" @. V5 l
  53.   if(res!=0)% T/ B; l& C1 l

  54. / R" s  L, \4 U8 m4 o$ b% d
  55.   {5 N* Z" I. ^. j% L

  56. & Q" C6 Q& u8 Z& |+ {( S
  57.   fnShowString(10,120,"open txt fail",16,RED,WHITE,0);$ }( f% x# S( O* f2 f3 w5 a
  58. ) k' B# Z/ M+ `/ v/ t" c* S
  59.   printf("res=%x",res);
    : M2 R" w# x5 t# D% J+ x

  60. : A1 P' q6 ]1 I- M. x
  61.   return 1;
    9 a0 L5 D4 L( Y# R- K$ u; Z) B! I' |
  62. ! J1 Y: @% E- f- }9 n& C6 T" _
  63.   }
    # p# O, b7 c5 q# i

  64. 6 ?1 a% g8 e# P; n
  65.   f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。
    " u, |8 j( K" H( A0 E" ?# K% g

  66. ' f( x( K5 o+ L  X* \2 L
  67.   res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节: \+ }* t8 T6 }! X+ [
  68. ' p5 D1 i! X$ z/ B7 j
  69.   while(CntnuF) // 循环判断是不是读到文件末尾了. l" G$ `8 M$ z2 P8 y' i
  70. 2 C( x9 P* L8 d/ x
  71.   {. C/ U6 G/ m9 n. m6 K+ x
  72. # D/ e+ d, A! j* r
  73.   for(i=0;i8 {! u( X1 U) p& R4 }& X6 V
  74. ) _1 W. g: p9 [/ M
  75.   {
    0 h5 N- H) p; Q, a' R$ f% _1 f7 [

  76. 7 h6 t8 e8 ~3 a& z' b
  77.   if(x>220) // 显示位置的判断0 I' l# M( g0 |- a9 H$ V

  78. % }! M3 S. r  q0 Y  G
  79.   {8 ]4 c" x3 b3 @9 w. w& y
  80. # J% e# ^2 C/ T0 O/ k1 M
  81.   x=x0;
    1 ?# S6 a/ }- z9 k; t

  82. 2 b; @  S2 f+ T6 s$ V
  83.   y+=16;9 r0 `7 n+ j1 L

  84. ' y1 r" w5 _5 G1 ]
  85.   }
    ; d& Y+ I% Z; W* I, u6 j
  86. : B: o1 d  }6 z" F+ m4 f( o
  87.   if((y>300))
    7 z. Y& Y1 v4 u

  88. 9 n2 r. c. |+ i) `8 s; E( V
  89.   {' X  l, X: N/ M9 C" r
  90. 1 R% U8 f8 ?9 k8 q
  91.   fnRefreshscreen(WHITE);
    7 H- ^  U* b5 x% Z: A

  92. 4 }5 K) w& ~6 u! {2 p
  93.   x=x0;2 f* X8 l! J) W2 F
  94. / _( S: t; V, w% q; h8 V
  95.   y=y0;+ y9 |7 K$ l" _6 M0 s# g' X

  96. + H4 G3 I: h0 f. l
  97.   }
    9 j2 t9 [% j1 w# ~# c8 j3 b4 u) e
  98. + g) `% K( a8 j( L4 F, _
  99.   if(buff>0x80) // 是不是汉字& ?9 g7 A* _! u; A/ V" Z! I6 a
  100. . c( D- t; O' p
  101.   {
    . E$ A) Q% b' o) ]' |+ v

  102. " t8 {. l& G& z8 K7 B2 |
  103.   f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移
    ) x. L; t  v0 x8 ?: j
  104. & x$ L6 m+ r7 Q8 B) i1 G
  105.   res=f_read(&f1,bitbuff,32,&bytenum);
      H" L& u$ g) E3 n" ^3 K

  106. . ^6 y( N7 Q# u4 C1 `( `/ V$ m
  107.   fnShowHzk(x,y,bitbuff,color,BkColor);  Y/ _, z1 {& b0 S

  108. # |. g# p! M8 a% u7 k( j6 c# N" N1 |5 w
  109.   x+=16;
    % P$ @; c8 F" N
  110. ( F: s. I4 ?/ M# L1 U! ~
  111.   i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,
    ; g, o" b* ~% ~" @) r( |2 f
  112. - F  S7 n" L' p) f. H" c6 Q
  113.   // 这里需要还要一个
      ~  V0 D) V& q" N; O5 @8 @

  114. $ e2 r1 p' e$ U6 d* V: [8 ]2 U- j
  115.   memset(bitbuff,0,sizeof(bitbuff));' y9 S7 e, w' [' B4 Q6 n

  116. 3 f" V9 x2 K% \- _+ B; }
  117.   }
    - u; Y( \: Z1 `; F. }
  118. & T% |: c! [- k
  119.   else // 可能是标点也可能是换行符
    . u8 q; F5 m5 h* w$ h" W8 ]: t
  120. ' [/ X3 T& B; j! A0 U& g1 A% r
  121.   {
    . g+ K8 s  g! B1 q# B' U
  122. 5 A+ D  T/ \! \" ^/ P. S: X
  123.   if(buff==0x0D) // 换行标志
    , J% l: s" E1 K* J1 n2 A

  124. $ b; k2 e9 Z# S/ V) B
  125.   y+=16;& \4 I' Y/ r- e  u: p

  126. 3 u- k1 _+ p& ~% \
  127.   else
    * M/ {8 p2 l2 a" A4 U3 V

  128. * J  H, g# a4 l6 o; K% T
  129.   fnShowChar( x,y,buff,16,color,BkColor,0); // 字符
    $ I3 |; s) |. N3 r8 E/ ?
  130. ! v4 m  v( f2 M
  131.   x+=8;) Q4 t  p, q" N  ^5 V( S! j
  132. % Q2 W( n! _0 }$ M3 |) q/ `: \
  133.   charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址- W8 {1 U3 M, w1 a( z  Q# @7 c

  134. / N( O; d8 R; n1 m+ d  X% i
  135.   }
    9 ^# @8 O% c7 u' i% ?2 I3 h; P

  136. , _8 f( ]& z& c; v6 N8 C
  137.   }
    . k3 w/ R) W  |, x% T& Z& Y

  138. $ t0 H* ^) f; ^5 A; T! ?6 G4 A
  139.   // 计算txt文档的偏移地址
    ; u+ _" Z" b9 T2 R1 S

  140. . ~; U1 C1 a/ s. w8 q& [
  141.   if(Hznum!=100) // 判断是不是读到文档末尾了
      Z; G  t# {3 V) ^; j) A& b9 P  \

  142. + p9 g% ^" X2 G* e
  143.   {
    6 c% B! M1 `2 e6 ^1 c

  144. 5 A2 `; d' a$ o2 m
  145.   CntnuF=0;' k, |4 O/ r# F* Y
  146. " t' Y# p0 L! `+ L
  147.   }
    / f2 I+ Y5 }- u, y1 `
  148. 2 T6 |, V5 u: _' o4 H5 E- N3 f
  149.   else // 没有读到末尾继续读& z2 G# ?2 T0 f3 O' N
  150. % m6 P* B% L+ L0 v" H$ s
  151.   {
    & l, w5 H# h( V
  152. - R5 i2 T9 u/ w) n
  153.   if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+100! z. A0 R1 w) S) _' @  O
  154. ! T( A/ l4 b2 }1 x! I9 L
  155.   offset +=100;
    5 w, T+ A( l* K: d$ g0 K! \

  156. 7 g3 P3 _1 A% K2 y; g
  157.   else1 ^$ @1 g3 {; K; f" \
  158. 8 L. Q0 N* t: n% c
  159.   offset +=99;$ {9 F- Q( \8 j- D. n, k6 n0 K

  160. * H* W# f6 ]' I' r( z
  161.   memset(buff,0,sizeof(buff));+ S4 L7 T$ N% Y

  162. 1 m. ^" W3 K0 `  _- N( u
  163.   f_lseek(&ftxt,offset); // txt 文档地址偏移6 ?: T( M- O; O

  164. 5 ~" a5 V! i. r3 R$ O4 V
  165.   res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据
    8 b/ }" x+ B4 C, C# z
  166. / g4 t4 d9 O: K; O2 }
  167.   }
    ) v' ]3 |2 M0 a  l+ ?' `
  168. # u! h1 W6 y8 u6 y5 K
  169.   }% R  w, b7 o% B6 s/ Z* Q$ K: W' z

  170. " ~9 W$ C9 b' c2 S* X
  171.   f_close(&f1); // 关闭打开的点阵字库
    + M4 \( c0 V# F0 s) X
  172. 0 {6 S5 C! C: c+ j6 u" {
  173.   f_close(&ftxt); // 关闭打开的txt文档
    . a7 ^$ Z& o  x6 z5 c

  174. ) K9 K" t, a& m& S& u
  175.   f_mount(&fs1,"",NULL); // 卸载磁盘8 g0 J+ |8 v/ }

  176. 5 S! X0 v" I: ?; o7 l
  177.   return 0;$ d/ @# b' D5 ?/ r! d* u

  178.   B! W7 _9 q( m$ X0 k5 L, d9 {
  179.   }
复制代码
# p4 S0 o: ]6 M) r
  以上是简单的电子书的实现。
- n2 \; a: Y* |2 j$ ^  因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。* x4 \2 a4 o5 U# O  A. [2 n
  其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。* B; [  z3 Y8 K1 [) [& m! Q
  SD卡注意事项:& m- D7 ?+ g, c0 a3 J3 W
  对一个文件读,必须先打开文件,读完后关闭。1 r- k/ A/ ~- X0 y3 k4 j8 G
  对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。7 z) [: }9 v( S. _4 Q
  文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。1 @0 |4 z4 m7 K) E
: S/ ]( m: b+ j  J

6 {1 `2 [& L: w- F4 _+ \
  • TA的每日心情
    开心
    2023-5-15 15:25
  • 签到天数: 1 天

    [LV.1]初来乍到

    2#
    发表于 2020-4-20 10:29 | 只看该作者
    现在STM32单片机用得人很多,资料也很详细
    您需要登录后才可以回帖 登录 | 注册

    本版积分规则

    关闭

    推荐内容上一条 /1 下一条

    EDA365公众号

    关于我们|手机版|EDA365电子论坛网 ( 粤ICP备18020198号-1 )

    GMT+8, 2025-7-19 06:52 , Processed in 0.125000 second(s), 23 queries , Gzip On.

    深圳市墨知创新科技有限公司

    地址:深圳市南山区科技生态园2栋A座805 电话:19926409050

    快速回复 返回顶部 返回列表