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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
  今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。; f- v" h' @4 C8 G+ C' @+ R5 Z
  取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。. h( q% F& ~: l$ F3 _
  国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:
3 v& E/ i# M9 m: z1 l9 X  offset=32* ( (H-0xa1)*94+(L-0xa1) )
" m( [+ T# s1 `- U! C, O+ a8 ~  H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。$ S& _% ?+ X" l" D  _
  获取点阵的代码如下
% K8 U( j: Q" g# t6 m1 A- i+ v0 R8 i0 I  Y5 t% C; G1 }) E/ u
  1. FATFS fs1; // 挂载SD卡的分区用
    ( Z2 E, \; q) R' G

  2.   H: g/ B; t& {
  3.   FIL f1; // SD卡中字库的文件描述符
    ) j" B- S+ x* {( ?* o
  4. 6 }7 |: y+ s# I8 u
  5.   FIL ftxt; // 要读取的txt文档的文件描述符
    ! W2 T7 L1 z. {- \3 G  l7 r; U
  6. 3 I+ W9 h% H4 n* W6 i
  7.   u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字3 K9 i3 n) F0 B* s$ D1 e9 D
  8. & X3 J( L. [  {" G  b
  9.   {
    / P; U: G5 Q3 P& C

  10. , [& `7 F4 S' l3 ^
  11.   u8 res=0;
    4 |2 {  A* ]7 o3 Z
  12. 4 X3 c, U) V" D' j( b1 l
  13.   u8 H8,L8;3 d/ O$ n( s' f; C
  14. 9 W, O7 Z3 b/ q# `$ Z/ t2 h: G# F
  15.   UINT num;: e4 \- j  o9 w; |. U5 n( O

  16. ' ?5 J' C$ a4 [: q7 F
  17.   H8=*p;
    - m# N! q7 ]/ u$ I8 ^3 Q4 W2 o
  18. ) B0 }9 L4 K$ b& S
  19.   L8=*(p+1);
    ( ?, _4 w* \! g% b
  20. ! E3 r3 R9 z: {( E# N
  21.   f_mount(&fs1,"",1); // 挂载SD卡
    4 ]' c1 n0 h" E* u( X6 B
  22. 5 U3 J! c2 C, Z8 i1 h+ R
  23.   res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库
    $ A1 t' S( r3 i, u

  24. 6 n4 n  q3 E/ H9 V, O
  25.   IF(res!=0) // 判断点阵字库是否打开成功
    : A/ p' r' I7 V. L0 C- p2 j

  26. 3 W% i: T2 d/ [, |* U+ }% C
  27.   {3 a  K  f% B6 S5 H: I

  28. # Q7 B& U+ l) _) a) s
  29.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
    $ P* o! Z$ Q! L# Y3 Q3 m
  30. * D0 b7 ]7 E7 u
  31.   printf("res=%x",res);
    3 @7 y( J# ^3 ]( q4 t

  32. 7 g  }5 N" k) s1 R. L
  33.   return 1;* `: k. f3 m' p2 y
  34. ! Z) y1 ?, Z8 b0 r
  35.   }$ y# D/ {7 N/ \  p
  36. ! q: \1 F/ Y& G( D# c& r
  37.   else% G$ o' V" C* j4 G- \5 C; G

  38. ; k+ r  c. I: D0 H
  39.   {5 \% O' r$ s; s
  40. 2 J% O$ L' n5 z2 ]# ?
  41.   fnShowString(10,120,"open ok",16,RED,WHITE,0);
    ( i4 x( h" S8 l- T& ?: M

  42. / N4 |  Z/ U  h  t
  43.   }
    & F4 t+ `4 c8 ^. n" b- |) Z
  44. 0 ]  @; w1 L8 l6 X  A
  45.   f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据
    " V, _* g7 A+ U0 a
  46. ' `+ |4 S# u. M; b! T
  47.   res=f_read(&f1,buff,32,&num);) `  j# y  Q: q( Y( E0 R
  48.   ], W1 A0 j' E+ K
  49.   f_close(&f1); // 读完后关闭点阵字库文件
    $ K3 X& H/ H* o  ^1 z4 \* J

  50. 3 q, {* M! e7 G+ P" ?
  51.   f_mount(&fs1,"",NULL); // 卸载SD卡$ Z  k  e* N7 C8 }1 e. B, \

  52. 0 g, |+ J$ F) }, ]
  53.   return 0;
    0 ~3 K* }7 k5 u+ q7 G
  54. " ~- q( J' u% p( _  n7 c9 m
  55.   }
复制代码
( _4 w2 N, r5 V3 Z
  这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。" y4 `6 [: K( a& `* M5 [
  实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。+ P3 ~: i) k8 E$ R3 R
  首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。; i7 w5 w2 @$ @% P- l' g
  下面直接贴代码:) Y3 T1 [: p- b( ?& r

& \. o% H9 ]3 o8 q& b5 P
  1. // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示2 K. v! P( y7 J5 x
  2. 4 u+ V8 g$ G+ a% d" Y0 [
  3.   // color 是画笔的颜色,BkColor 是背景颜色
    3 B- N* L' W4 P* u. N6 W

  4. ! b$ o3 N* P$ R3 O' H, \/ m4 ~
  5.   u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor )
    - g3 G2 \% C3 z/ A

  6. " o7 |8 d3 I9 \7 w, o  u! {8 }
  7.   {0 W' U( A" q. n6 o( S4 V
  8. 0 O$ V& p6 I) a3 ]
  9.   u8 res=0; // SD卡函数的返回值
    8 x# f) h) U8 C' j

  10. : R9 N3 Y( ?3 V& M4 o' F
  11.   u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码) Y3 |, O2 x. B" D( a

  12. 7 ]  |% N' s6 b( D/ H
  13.   u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据
    0 Z* l& q+ m9 R! q6 ~) A( o

  14. - X. z2 V6 _' d4 N" ]
  15.   u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位$ l% S& x  ]9 G
  16. 6 }7 |0 D% `' K( ~; n3 C5 g) }6 w
  17.   u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了
    4 j; P* |( ^- q' u

  18. : R' |3 N. w6 O& p  q$ O& y( }
  19.   u16 offset=0; // 读txt文档的偏移地址
    " }5 E8 g) y' E* J, [

  20. * u' J- R- U7 b/ O' x' i
  21.   u8 i=0;) r$ O4 {7 F2 u
  22. : Q9 N8 z$ Q/ N, ]( |
  23.   UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数
    , T6 ^# ?) ?: q; V

  24. ) p3 D! {7 z1 Y, t0 u7 K& {4 {
  25.   u16 x0,y0; // 点阵显示的位置
    ' T. B2 s0 ]: D* f, O

  26. 1 V) D- d4 S' a4 I! M' R
  27.   u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,6 p9 p' D8 q) I+ X9 N' v
  28. 0 h- O' D: p  Y
  29.   // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。
    ' R; T) X( C( J

  30. ' Z" l5 d# O) B: o5 S) S) d+ }
  31.   x0=x;
      S7 v) K: |9 o4 R' M) P; i
  32.   I& j7 h; q' d- q1 _8 T" Y3 W  E
  33.   y0=y;
    . ?2 \( H# @3 V

  34. 9 g0 p0 a' O7 ^- r+ J7 H
  35.   f_mount(&fs1,"",1); // 挂载SD卡( N8 S" y( `: |# i$ T" u
  36. - a- b0 v/ j8 Z# U
  37.   res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件% ?: h/ b3 p1 k" B9 F8 o+ `
  38. % s$ Z" F0 Y# P5 Z; s  E0 h
  39.   if(res!=0)% i1 d3 `6 N% F2 @) `  M0 G4 e3 n

  40. 0 r0 e: B& ?! Q/ g+ c
  41.   {0 B& b6 V* p6 ~! E, N. r
  42. : W+ U- V: r, e; @& Z
  43.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
    4 t* ^' B$ f' W. Y5 h8 U

  44. / s/ W% i4 l$ ^
  45.   printf("res=%x",res);1 v7 t; v9 S5 o+ ~; e, f
  46. ' z8 w0 d) I4 o* I$ I3 _8 m; {
  47.   return 1;
    . k( w  c; H4 G6 S2 T

  48. ; f8 C3 ]3 j! j6 p  O  }( L
  49.   }
    ( @- `9 o) W/ Z1 {

  50. 5 o8 ~& t0 a: b4 O2 B, s
  51.   res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档( f0 l2 c3 g* x5 X4 i
  52. 0 E/ v7 W) @9 b
  53.   if(res!=0)- ^- I  I* z7 f; ^6 {( ^. y# {
  54. ; F# x5 {% b) ~0 l; ]  M' Z! g: X* n. s
  55.   {
    ) R/ F- `& K2 @0 W+ O2 b
  56. 0 _7 N! e- R( J
  57.   fnShowString(10,120,"open txt fail",16,RED,WHITE,0);" z9 x3 X% C, Q7 L
  58. 4 O) N' c3 e- R3 x! C4 H2 d6 e  P
  59.   printf("res=%x",res);  t2 r: b: f# _/ s2 v5 y
  60. % A/ j, k4 S- J
  61.   return 1;+ \( o# O% r% ^7 \* Y& F# o

  62. 1 h1 \( C- v, U9 K4 @) _5 X3 G
  63.   }3 u; D6 [  L( O" \; f/ P! x

  64. 6 C' {# z5 C/ z% l2 Q8 D" |
  65.   f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。9 V% q; H! [4 m6 V* W# m
  66. 6 F, X: |! L4 u+ ]  D
  67.   res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节6 e. ]1 i0 t1 z7 q4 m( w
  68. : T( @  g$ M8 T2 ]
  69.   while(CntnuF) // 循环判断是不是读到文件末尾了1 D/ l3 z+ j8 {* t) g# b1 d
  70. : F+ n* H) ~0 q' W" b
  71.   {
    * {. \% l% z+ O, \0 j" ~

  72. % u. l" v# K. F
  73.   for(i=0;i
    ( V% U3 B2 H/ K9 `
  74. ; w  o4 n, H8 w1 C3 v4 \
  75.   {2 I8 d4 @5 l1 G; D% i
  76. 8 M) `% p2 x. k+ g
  77.   if(x>220) // 显示位置的判断
    % ~% H0 k- I( T* U! k

  78. ( |0 p( k' h/ s9 s  I/ N1 A& a# U
  79.   {5 _  m7 {7 q4 z8 d/ S- W6 J
  80. 4 v; [) a" _9 f6 x6 N
  81.   x=x0;
    & l. a  Y3 k& J5 d

  82. 3 X% q4 I1 y' K* [
  83.   y+=16;/ S' w# \* B# |2 j0 h8 n
  84. : d, O6 z" ?5 d5 P5 J4 I
  85.   }
    " l# O2 F) M2 O

  86. 3 d" i& F+ ?/ x7 B
  87.   if((y>300))
    5 R( Q7 }" K& j$ d! y$ a+ t: z, h
  88. : G6 P+ i+ V* E) B# ], c9 e$ f
  89.   {9 I* z" b* @- d
  90. 6 {; B5 x  ~% y& P
  91.   fnRefreshscreen(WHITE);, r# g- @- J# G4 q  }8 e( N! r

  92. 2 ^9 O; Z' p2 Z& x# w8 j
  93.   x=x0;
    . S2 e& H" K4 e! U2 X; C6 Z' m9 R
  94. & a7 S1 E& M2 i* b
  95.   y=y0;
    5 \* K) C, Q8 N2 P. @5 d

  96.   @- o2 k: H8 z1 J8 U/ ~
  97.   }
    & A" u" Y3 {; q# s) T4 [3 Z

  98. 3 O) D: v4 e5 y, G& c  P
  99.   if(buff>0x80) // 是不是汉字
    . }! }7 @+ K& h. a" }* H; d- N
  100. 1 q4 b. b& v- E' t, ]
  101.   {" ^3 Z8 b9 P  \0 Y7 P1 l7 m
  102. ! h) x# `; K, I0 D
  103.   f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移
    0 A! ^. j" m7 A- }6 Y- X. }

  104. 4 W3 s% T% w6 \7 E/ B
  105.   res=f_read(&f1,bitbuff,32,&bytenum);: `# l( i( i) E5 f6 [, p6 K: u& O# a
  106. . R% k& a" T; S1 i! o
  107.   fnShowHzk(x,y,bitbuff,color,BkColor);
    % k" q5 n0 o/ b0 L

  108. - o, X5 W9 n9 _/ g- v
  109.   x+=16;
    : G) [$ ]( t) p
  110. & d1 c5 w* i, b' [
  111.   i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,
    9 g% Q9 S; e+ n0 t' \
  112. % p! p4 P4 T$ ?! M- l6 g& F4 i! g
  113.   // 这里需要还要一个
    5 \& A8 ^, n7 M  d5 d* I9 k% m
  114. 9 t( _' h* s' f% X" C3 Z! [
  115.   memset(bitbuff,0,sizeof(bitbuff));, `4 o3 ]/ U3 N, C# a
  116. 2 B3 V7 k- c, o1 E. x: D+ k9 l
  117.   }" X* W9 D, F) m) ]& w  j

  118. 5 \7 ]" G( B( I2 d7 Q) V) m8 @$ D' L
  119.   else // 可能是标点也可能是换行符# a! U9 A1 j6 |0 H, X' |0 T

  120. & ?4 s( Y: ?: r7 F2 e1 M) V7 w
  121.   {: ~# l, q1 O; x1 s( V  o
  122. 7 p4 z4 c* b  }9 A9 i6 O% Z3 [
  123.   if(buff==0x0D) // 换行标志9 ~  L/ R7 V  W8 g' F! \7 W& S4 u
  124. 1 Q; f3 N# z' s% E& g9 s1 s
  125.   y+=16;$ S- X- @; B# G1 F
  126. 5 z4 t/ R* L5 C) N
  127.   else9 c5 h9 D. _/ j$ d% i
  128.   U, p- x9 n* }( t% a) g# L
  129.   fnShowChar( x,y,buff,16,color,BkColor,0); // 字符4 p% j4 j, E3 c+ C
  130. $ }* ~$ o+ Z; U) ], B; n2 f
  131.   x+=8;+ [) Q) o9 g1 X( p! J
  132. ) V) v7 B, g+ F. j
  133.   charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址) A3 i! p- T: H3 _! j1 ~

  134. ) |, `. r6 V) Q+ N
  135.   }
    . l6 E7 z% H& b% T$ S
  136. 7 {1 I; Q9 C! S( M
  137.   }- ~" Z2 a9 u  j  n# ?. ]+ t  @; R
  138. 5 Q7 l6 n" Q. q+ h3 E0 c( k
  139.   // 计算txt文档的偏移地址$ J* B# y8 W0 Q$ q
  140. / W* y0 z3 A3 V4 h' y
  141.   if(Hznum!=100) // 判断是不是读到文档末尾了
    8 f3 ~% L& C7 O/ S2 T+ a$ V+ @
  142. ) f* I8 P% J& ~; P' `: H( t2 B
  143.   {9 c. t( A1 U' G& f) R( \

  144. + G4 [0 |- F" I2 y6 {* O- n3 a
  145.   CntnuF=0;
    + S. S" R# @- i, \

  146. : F) ~6 y- U% q# L) I! y6 \
  147.   }
      c' O  w' j  r! m/ J; i" V" N

  148. 5 R9 x& v+ d) G5 f: o* H
  149.   else // 没有读到末尾继续读
    ; N, N0 H& C3 v' Z3 P. @6 T! }& M
  150. ' h, x$ e. D# P- ]5 O( D
  151.   {& O* y$ {$ X: ^1 q

  152. % D$ R5 w) o) w0 A0 j( _3 {( N
  153.   if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+100, {* I" X4 n3 S/ |/ G$ R- ~# {4 p

  154. 8 m7 b* p6 a/ Y* i8 Q; J7 P
  155.   offset +=100;4 X4 q$ s1 h( N% W

  156.   A+ V/ r  U5 Q4 O* r. @) ~
  157.   else
    ! b4 d  h; P& f, M0 Y. Z) D
  158. / I$ o  S4 f' D* {8 Q
  159.   offset +=99;
    ! F6 ^6 {; r9 G# `9 X
  160. 2 w: l4 u7 l& h* A4 s" t' r( k3 z# o
  161.   memset(buff,0,sizeof(buff));0 K  Y- @+ c2 n7 n  ?, D2 F
  162. & O- N7 Z) f: K( g$ S( ^8 `
  163.   f_lseek(&ftxt,offset); // txt 文档地址偏移
    9 P5 w) Y7 o% u/ H

  164. 7 r6 k4 |4 K: x
  165.   res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据
    9 n  z7 v2 p7 V/ W" K

  166. 3 v' e0 ^3 L7 H  g% ]
  167.   }
    " F7 Q5 i6 s  J- A! s5 R

  168. 2 Z! w  m  C8 h6 E) D, H
  169.   }# g9 c. m& ~: X) b: v% b" ]

  170. ) R+ t! ]) t1 j+ X
  171.   f_close(&f1); // 关闭打开的点阵字库
    5 P$ J# K# C, B: P& o) `, i

  172. 8 e5 |" T/ }" o
  173.   f_close(&ftxt); // 关闭打开的txt文档
    : I0 t9 ]) v/ f7 c3 c

  174. - ]; A2 t0 v2 w6 z# d/ z! x
  175.   f_mount(&fs1,"",NULL); // 卸载磁盘5 L; }% [: b- o* i  c. d" n* M
  176. 7 i5 J9 ~% {' D+ Q
  177.   return 0;% ]8 n5 H8 k/ U1 f8 F

  178. 1 a# l- u% `: B  B: H2 o
  179.   }
复制代码
6 x0 W3 {1 T( a1 o) z' _
  以上是简单的电子书的实现。
+ @' Z0 I) N4 `0 o. j! X) @  因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。' o1 h4 _) {, O# E" F- i2 Z# z3 ?
  其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。! V/ k  q5 `$ H1 S' R( A; o( F+ Y
  SD卡注意事项:
. T1 ~0 Q; {& ^! X. c  对一个文件读,必须先打开文件,读完后关闭。1 A: Q3 f6 O) m; n& Q4 h) S. k
  对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。: o/ T2 Z3 S+ o9 j5 y5 @3 R/ N
  文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。
  @2 u8 S( i& U4 j. k0 N* G8 ]& q) Y; G8 V/ {  Z

. s- `- k7 [; A; r
  • 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-6-13 02:03 , Processed in 0.078125 second(s), 23 queries , Gzip On.

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

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

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