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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
  今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。% |0 N- l, H9 d
  取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。1 p6 P" E9 v& S% \/ F2 y+ Z
  国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:0 T9 u, j6 E/ x
  offset=32* ( (H-0xa1)*94+(L-0xa1) )
9 H5 L' t( m# p: f$ y  H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。% s: y/ R+ L) |: v8 {- v
  获取点阵的代码如下3 `# l( u( b4 v+ q  j3 V+ r3 v% l

, j( X+ d( O3 Z1 ]9 t1 ~. t: ]+ r
  1. FATFS fs1; // 挂载SD卡的分区用( s) \' D  P& Y) |: J
  2. 6 B6 A, e8 h8 A% w  H" x
  3.   FIL f1; // SD卡中字库的文件描述符+ U4 u$ [. n& Z  Y, `

  4. 9 a9 K' E' p0 F+ t8 l3 a
  5.   FIL ftxt; // 要读取的txt文档的文件描述符
    7 \; Z( R( g9 G4 Q5 j0 o: b

  6. , {# P( N1 @" E: [
  7.   u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字# F6 f0 X# T6 q6 S) y2 E7 W0 Z

  8. & {& [/ |$ [8 K! m  l! ]! Y
  9.   {1 m+ i- o' G& d8 J' X* }

  10. " n5 q5 s& Y$ d# ]
  11.   u8 res=0;! |7 O, R: U# Y" T+ K! Q9 a. c

  12. . O$ p# l. W' o9 }
  13.   u8 H8,L8;2 M  q+ g: ~2 \

  14. 6 A" L) p1 j1 F
  15.   UINT num;# t1 l6 b# B9 C7 g) ^  y0 n1 t

  16. " V# ^( m; @/ R5 [# A# Z
  17.   H8=*p;/ j/ v1 C+ f& M0 i/ ~
  18. 7 g' z8 R8 [, e4 l# y: B. r
  19.   L8=*(p+1);
    . d4 U6 o3 R7 E. k6 w0 b4 Y3 U3 k8 X
  20. ! u& X8 k1 c- |* x
  21.   f_mount(&fs1,"",1); // 挂载SD卡
    ) |! Q1 k. P8 Z2 Q+ _% x8 o! E

  22. : l! C- S# o: g9 e5 A* t
  23.   res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库
    1 ?) C$ n" X0 a1 @  c8 Y4 W7 @# V
  24. 5 w( m- {4 e/ o# j  ~
  25.   IF(res!=0) // 判断点阵字库是否打开成功
    . w$ Q5 T4 S& ~9 n" Q+ s

  26. ! T2 m$ C: I8 k+ ]# \/ p
  27.   {
    5 D8 S' G7 W( t- V- M

  28. & e! R& F6 V+ T9 k8 f
  29.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);" y( W- [! v1 p* I/ a8 J% ~
  30. 2 a9 E; `2 O8 b7 T$ ]
  31.   printf("res=%x",res);
    + b' a5 g) t; V% [- z

  32. 7 _; [/ Q: ~# e0 v1 {/ X/ K
  33.   return 1;
    & d: ^- g1 m% m" p) @

  34. / j" l% v# Y" D9 [
  35.   }. r2 P, E6 n) I! c  C; c

  36. 1 @! G# X3 o( o, q
  37.   else+ `1 L" S) p! M* t( W4 _
  38. 6 V2 \) ~1 r& l6 b
  39.   {
    , z) w; d% }+ G3 {

  40. 8 d% C; ?7 I6 u
  41.   fnShowString(10,120,"open ok",16,RED,WHITE,0);
    ) |- P" r4 S4 d7 q  b/ e

  42. 5 d( X" o; h/ n# H% r
  43.   }
    4 t9 K4 v$ f/ {% }  w& C5 y- h) Z/ b
  44. + K, A: _. {2 K1 F
  45.   f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据
    % _3 ~( ]+ x. v0 D3 m' h, ^
  46. 6 z3 p: Q5 |. K0 U
  47.   res=f_read(&f1,buff,32,&num);* E8 k( S8 W4 o

  48. ! Z0 Z' h4 ]+ V( j9 _# E
  49.   f_close(&f1); // 读完后关闭点阵字库文件& G6 C# O- d9 ?4 e" W* ^# U
  50. : y' p9 @; S: e, ~: B
  51.   f_mount(&fs1,"",NULL); // 卸载SD卡
    ; v. ?* t2 z/ n+ I
  52. * F/ h* F1 o+ O3 {  e& h8 g  b3 N& C
  53.   return 0;! Q0 f8 G, d- _* [; \9 q6 Y
  54. . P! o+ u. O1 e
  55.   }
复制代码

9 N8 J  ?$ c7 D  R  这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。
+ a5 R  {" R# x+ H7 p% |  实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。
( \8 t6 ?" o, J1 j8 x  首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。5 E+ B4 u7 `. P: t, |1 \( k/ s
  下面直接贴代码:8 k! N" _* t: w- E
& |$ J/ y% Y# u' u$ n# [4 |
  1. // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示% n% R# B" ~/ ]" h' O+ b7 S

  2. 2 V2 @" v' j" a# U
  3.   // color 是画笔的颜色,BkColor 是背景颜色
    & E" s- F* y9 \% L  T- f+ u
  4. ! B8 l/ Z' h* c
  5.   u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor )0 |# G  i) B" H2 J  |# q2 R' ]

  6. 0 D$ u$ B/ Y) y3 j
  7.   {
    # X+ W) B# L/ E) X# e

  8. 1 s; y3 A4 s5 N: Z+ E  C- q
  9.   u8 res=0; // SD卡函数的返回值
    9 K# R! E+ y- t: `) v
  10. ) r" H4 Y5 D3 r5 o" P- t3 P
  11.   u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码, G( b) X, M0 Y
  12. 7 f& r. M3 T1 C, D% s; F( y
  13.   u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据) K% C: J/ q& |  N" {9 S, _6 ~

  14. / {( D( D& X  u$ Z
  15.   u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位5 K1 O4 z: `  M0 Z  z% l

  16. # p- Q, A5 E0 U, I1 K. f
  17.   u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了3 ^* H, }$ Z1 I: W8 N9 [

  18. ; O6 ?2 v! o1 r
  19.   u16 offset=0; // 读txt文档的偏移地址. Q* j* U4 C( [/ g) Q1 g
  20. + j6 M5 }$ N0 h3 M, b  ]- N
  21.   u8 i=0;/ d6 Z7 g) e* g# L2 j6 D
  22.   j& U. W8 R! C* x1 c
  23.   UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数3 o' _" S, ~8 }
  24. 0 b3 r+ C& T  k6 D: S5 \( ~
  25.   u16 x0,y0; // 点阵显示的位置" o' M. \7 R' R6 }- b
  26. + w4 [' E- f* u& }! |
  27.   u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,
    # d  |! h& v, S

  28. , r: G# c7 [' J' O- q
  29.   // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。
    7 G7 A2 w0 X# Z/ a7 Y2 y- t
  30. 0 U2 c7 }  ]3 O8 M0 C; [" X
  31.   x0=x;- g8 ^: |( Z0 f2 I* p/ U* @( Q: [
  32. 7 ~0 l$ Z# H8 z  a/ v" G' T
  33.   y0=y;
    . F- e/ ]4 T2 D
  34. # ?' y0 ^2 d* p7 H- G& u
  35.   f_mount(&fs1,"",1); // 挂载SD卡! G& Z& t5 a- ~; Z& ?
  36. 3 l- i  w" P  I, |
  37.   res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件
    * n7 e& L# ^- A- ]
  38. ; R# B% Z4 E% H- u& e; e2 Y
  39.   if(res!=0)4 F4 I% X6 g% b8 m

  40. 8 I' ?7 v  G9 O
  41.   {" W, \! b' ~0 D4 h; t* R
  42. * {+ k4 D. V( S7 B6 m3 j
  43.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);1 z8 y  l- y. C9 o0 G7 _& u
  44. + X6 \7 C. a! ]) F# P
  45.   printf("res=%x",res);
    % T0 ?' X5 X) h# ~2 S6 H" ]) O
  46. + B: g! x( t8 \  _5 e
  47.   return 1;
    7 R5 v. i; s% A9 Q5 A+ a

  48. 2 ^2 V% e3 s# _* D; E2 @8 c
  49.   }
    / l: |: ^7 s4 D

  50. , H( f# f/ b5 z9 z  h+ A
  51.   res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档  V( ^5 \% r: `6 A' b  Z! S% Y

  52. 9 X9 T% s; b+ @- J( e
  53.   if(res!=0)
      g7 f$ f7 i6 `6 _3 u
  54. : P2 k- h  v6 ?0 t
  55.   {5 t4 \+ }6 o5 c: a

  56. ( V9 ~7 a2 v$ J( s
  57.   fnShowString(10,120,"open txt fail",16,RED,WHITE,0);& I. q4 q8 ^8 S$ |2 f* y6 g& _
  58. & S0 a& H) w  P/ J4 U! H. k
  59.   printf("res=%x",res);
    8 Y9 D/ V  {2 J3 v( t

  60. $ {5 }9 F0 ]' K# [# g  S
  61.   return 1;) B; |2 h; g( V# t. K- i9 \
  62. % {2 M! s1 d- X2 X- b9 E6 o; t
  63.   }( u2 |" b8 ^, s* D
  64. 6 L# |% Q3 n1 O+ S7 h$ L0 _6 k
  65.   f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。
    * x5 t0 o3 h$ q" A3 B9 i0 R

  66. 2 _. l2 M  M/ A9 e/ S0 J
  67.   res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节0 D! S% p8 h& h& o4 ~

  68. 8 ]+ ]# X6 u8 G
  69.   while(CntnuF) // 循环判断是不是读到文件末尾了
    , Z) h# o& E: e' Z: y

  70. 5 a5 ^8 a4 {1 r" R
  71.   {
    + X3 v' C2 a4 ~

  72. ; k0 T* x) B: i! f9 S
  73.   for(i=0;i
    # {! x7 z1 l* F1 Q- G

  74. ' a: s5 {2 g* l
  75.   {$ S& {' c% b3 I/ P

  76. # ]  ~, v  |& j) B& P( R
  77.   if(x>220) // 显示位置的判断: L/ i+ E. B/ _

  78. , y: H4 p( q% v: m) y
  79.   {+ Q9 i! a$ _3 T; p

  80. " s: z0 ^3 x: \& `& f) {- Y
  81.   x=x0;) I' l# v  h* b$ L/ U

  82. / N; e: D: s+ e7 n. T# e9 M
  83.   y+=16;$ J4 d) _) b5 R
  84. + Q3 v9 v4 ]/ t* R% p
  85.   }1 p8 Y1 w2 u% O" m7 T- H' M* W
  86. ) W& J# i5 |. ~& J  \' s# h
  87.   if((y>300))
    " o. y; M3 w1 @* w$ S& y
  88. 9 v' _, X' l6 m" q% q4 E( B: e
  89.   {
    3 t- P6 {- j# b5 R5 C# X) D
  90. # w# U  R! k. a5 y
  91.   fnRefreshscreen(WHITE);  t5 q* k% u0 s; q# C

  92. * M* E' U/ F" L1 i' o
  93.   x=x0;
    3 [" ^% N% L7 c! @

  94. 5 K! d3 ]9 Y* c/ x! z' E% ~2 N
  95.   y=y0;6 d8 T# d" H) B# I+ u+ p% F* j* |# R

  96. ) Q, n+ J# W. g2 a2 Z8 ]0 Y
  97.   }; r" X9 m* |* b2 v( C

  98. " B& j: e3 b# m' n: x. Y7 C
  99.   if(buff>0x80) // 是不是汉字; _# V: x% u0 V8 Z% T' `. v+ {

  100. 1 b/ G$ M* G7 t7 ~( l) j
  101.   {4 ?! T7 `( e. h- G4 b
  102. 9 W- R5 H0 d8 `# u8 R
  103.   f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移* h: \+ H. o& I9 k" d5 d+ m: H' z

  104. + P3 t2 i5 E9 V# e9 Z
  105.   res=f_read(&f1,bitbuff,32,&bytenum);
    + p0 Q1 N8 `" t, P, m  |

  106. ( s* d) B' A' k) v3 u4 \" ?: \& i
  107.   fnShowHzk(x,y,bitbuff,color,BkColor);: d" N- x2 e2 E7 r6 i# l# l

  108. 4 ]* o4 F3 G( E( l. ^; f9 X
  109.   x+=16;* f5 M) w4 i! b* q
  110. 9 f' C- f, {( b  G0 |2 ]# k
  111.   i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,( Q$ L% s5 i: X5 \0 p
  112. + M& q6 n, H- T" |% T- v% P, ^
  113.   // 这里需要还要一个* _/ j* c) `$ [6 Z1 P5 g" d

  114. ! Z7 h! a# J) P1 v) h
  115.   memset(bitbuff,0,sizeof(bitbuff));/ [. T& S2 w' }* R1 u
  116. % T+ M, I- y6 A$ T8 [0 L
  117.   }
    * }) d6 t, E" b1 \+ X) h
  118. * j, D/ f3 G* z( |
  119.   else // 可能是标点也可能是换行符2 f# A4 @; x0 m# ?# I

  120. * \, N2 d/ u3 q7 i- l2 g: g
  121.   {& p; D3 T3 b+ M$ k: W0 X0 N! w
  122. 4 K/ \1 v9 h+ f7 x& N
  123.   if(buff==0x0D) // 换行标志
    . B5 ~. i& b5 D8 o$ h

  124.   D- {/ |( O- F. l% x$ r" _) n
  125.   y+=16;
    5 K+ R8 \1 ~* |$ f5 G* k% I

  126. - f  i$ J- L4 d% S% i- T8 [+ p. q
  127.   else* X" \8 g% P  N
  128. 9 q% G% {8 Q# O" F
  129.   fnShowChar( x,y,buff,16,color,BkColor,0); // 字符
    . q- N( W/ z8 Q+ I, u: }* |

  130. , D7 q6 I* D4 q) z0 e* T% g. U% K
  131.   x+=8;
    * U7 `! k) s% `! f2 C& b4 ]
  132. # n7 r0 Z3 n& T' v
  133.   charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址
    0 j/ N# Z! I" G" U2 i

  134. " Z; I: y! G7 ~: z" A
  135.   }* p9 q1 M; I1 a' X) E4 ]

  136. 8 }; B# ]  C8 V( I! H  u9 F: U- j
  137.   }7 r2 \# K# `. K) a! j$ Z' K0 j

  138. 3 U# G0 {* i; b) a* b
  139.   // 计算txt文档的偏移地址
    6 `* T$ s3 g1 X& u1 C

  140. 3 x4 x+ h' d5 j- M1 }
  141.   if(Hznum!=100) // 判断是不是读到文档末尾了
    & y1 t* _: Q* g- o* X8 ~2 y

  142. : v# i) u4 N, r+ Q6 h
  143.   {, ?6 W; a4 A' N1 d5 m* G6 T' U
  144. % q' n( O9 f6 d6 _) k1 b
  145.   CntnuF=0;
    5 y6 L1 L# K, o9 B' w0 l
  146. 5 r# P- s( w  p; n
  147.   }4 D) n1 ^3 d0 T+ T8 }: Z
  148.   h  e$ e! X4 o
  149.   else // 没有读到末尾继续读
    ! F8 y4 A# S2 W8 j; n

  150. 4 N3 ^1 h( V. }$ M+ m2 I$ |* o
  151.   {
    2 y; f7 P6 \' a: o0 H8 @
  152. - V( f' n: t6 O; z' M5 D- J  c
  153.   if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+100
    % Q1 W1 z6 o7 f9 v
  154. / S) s4 g4 t6 k: `$ `& M; h, V
  155.   offset +=100;- H$ N" E) _$ c! z5 y. h

  156. ' P) ~4 t9 h& l) e( D! w
  157.   else
    - u9 d$ X# Z! M* x2 A
  158. 2 @: v5 m# ^, o6 {: C, D
  159.   offset +=99;, m. I2 p: I/ M# }1 b  r! v0 j& f  t5 R

  160. 7 l. o( u( w/ v% |* K1 Y
  161.   memset(buff,0,sizeof(buff));
    . f( ^* c8 o9 W- X. J" l
  162. . [4 P, ~; H# u1 ]! T: F" M5 U
  163.   f_lseek(&ftxt,offset); // txt 文档地址偏移" v+ a' [3 u' m7 ?/ X: C

  164. 7 r( K$ Y/ ^, Y- h1 M
  165.   res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据, ~, u/ M8 c& ^8 Q! G5 d; D2 K
  166. 1 \& ~! e% ~1 P+ ?
  167.   }
    1 s& D: J3 j- }. Z9 l9 J

  168. * B$ L! g) l; E) @% D
  169.   }, j3 Q* w3 R" h1 _* \3 Y: U
  170. 7 u! M8 _5 _2 q" ?' N* ^! }
  171.   f_close(&f1); // 关闭打开的点阵字库$ A; e" w% j/ U% {$ @
  172. ; [+ [% C9 B( K
  173.   f_close(&ftxt); // 关闭打开的txt文档; ^& P  u5 n8 ~1 l5 Z# v8 ~5 Z" k

  174. 9 d6 P" _  n2 C4 g3 V( R0 G3 F
  175.   f_mount(&fs1,"",NULL); // 卸载磁盘
    ! \$ z) \' B  n' f* c) C
  176. 4 A* A3 A. W: [5 `4 Y
  177.   return 0;% ]6 T+ f. [1 S, X4 b2 k7 b

  178. 3 o: t2 q2 m! s# J( l* X, a7 x
  179.   }
复制代码

( W" x# {0 L0 Q+ g* N) d1 E  以上是简单的电子书的实现。
' N% c% l- d" m  {  因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。
; ~* t! I2 t6 j7 Y: U' e4 q- m  v  其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。- o" \9 N7 P. A, s0 b
  SD卡注意事项:. ~5 I/ O* a, b: F- E
  对一个文件读,必须先打开文件,读完后关闭。* c4 X1 x- v& u# N6 F
  对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。4 r( g1 g! G. s: G, v4 p* r5 E
  文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。
4 h5 g# f6 g9 m9 @
/ Q1 C+ W* ~2 Q' S
* q; `$ M- m8 l# c3 t
  • 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-10-6 21:05 , Processed in 0.156250 second(s), 24 queries , Gzip On.

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

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

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