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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
  今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。! _, y, X7 T4 m/ ]& z
  取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。
- d% [4 z& _9 Q  国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:8 C6 Y$ e; \; P, f9 F
  offset=32* ( (H-0xa1)*94+(L-0xa1) )' j+ r/ d) r8 d" Q5 g% s& t
  H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。# U8 p! y. A- U: l
  获取点阵的代码如下( F; ]! q$ `7 g# F. w$ d
9 b! a4 @4 s/ o9 C/ b( k* U7 X
  1. FATFS fs1; // 挂载SD卡的分区用7 ?2 n! {5 |  @
  2.   e: w2 y) _6 B  @1 V, C
  3.   FIL f1; // SD卡中字库的文件描述符
    ( g' F& B( ~; M: a

  4. ; O- o. H9 U# K4 D- m% l3 I- i
  5.   FIL ftxt; // 要读取的txt文档的文件描述符
    , @, z' j2 {6 T3 M% a: G

  6. $ k; I* n: m2 G4 a$ X
  7.   u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字: e; l8 L% ^0 A

  8. * L9 F/ L1 N* U, a7 h8 X2 O( p
  9.   {3 \* o5 D4 V  o

  10. 3 B! G& y8 k( ~! I% ^
  11.   u8 res=0;% g* I: m& F6 `8 L7 R$ w7 b( d
  12. 8 @$ _* Y7 v. m* X: n
  13.   u8 H8,L8;
    : T7 B2 k; m! L& U) G& n' C
  14. 2 R- g; q7 N% p  l, ?6 k- Y
  15.   UINT num;8 q! C) S* }, V. h
  16. . K; E2 B, a( c, s% M
  17.   H8=*p;
    % y7 [% O- H6 |8 v" o3 `0 U, l
  18. 1 J; u5 B1 w5 J5 \
  19.   L8=*(p+1);
    9 T9 N2 E8 W9 m7 r$ d- t
  20. ; U$ n7 N) [: H; P( N, y: ]. D
  21.   f_mount(&fs1,"",1); // 挂载SD卡
    , L" F' a* @# R8 \% {
  22. 1 F' [* L/ c5 s" v( \
  23.   res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库4 k# y8 _% |8 D5 D: v

  24. . O- Y" [# p9 X0 {
  25.   IF(res!=0) // 判断点阵字库是否打开成功
    ) b4 _, y# V) V$ _9 c( q' ]

  26. : n9 }& I' p. b4 T& R! C
  27.   {  y% r0 n7 J- K1 ^( H

  28. - }/ b+ N! ]. a; t6 s
  29.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
      n0 S5 f8 H7 h' g3 N
  30. ' X' t& U7 g: A" B) @+ k& O: `; n
  31.   printf("res=%x",res);
    / X5 C% {2 j0 p
  32. / P6 E7 v. ^9 ?
  33.   return 1;
    ! k1 l' n5 m- f9 n& n

  34. ( U7 U. i3 N" e  c
  35.   }, `, r  u) {2 Y) n" a

  36. # Z" M; J/ A2 O- Z' Z$ M) j
  37.   else
    - X8 H! W; b9 P5 [1 |

  38. 3 L% D+ Q, U2 a- O2 g
  39.   {
    9 k0 c* A+ ?' b6 d, N
  40. / Y# `7 z) `0 J& p( R
  41.   fnShowString(10,120,"open ok",16,RED,WHITE,0);
    4 v1 T  U* h  e! o0 H5 l1 T

  42.   H5 ^! u9 A" e, B; s8 n5 g$ f2 O
  43.   }; }& z1 o, y; m

  44. * h1 [, B+ Q. k  T
  45.   f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据% J! [0 }+ w* n5 v; x4 S

  46. 1 b0 v: S& d1 @4 p) S2 o9 P
  47.   res=f_read(&f1,buff,32,&num);
    $ W; Y1 H' O' [* M: a
  48. % J8 ]  {9 `9 O$ g. P
  49.   f_close(&f1); // 读完后关闭点阵字库文件
    ( S# `. [4 u9 h& d" B  D, V

  50. + Q1 R$ g, z4 h. ?
  51.   f_mount(&fs1,"",NULL); // 卸载SD卡
    / y7 |7 ^* ]7 ~- j/ W

  52. / P: {9 L6 q3 j. Y) N
  53.   return 0;( V- f5 S$ v" U& `

  54. $ U4 @3 W1 ~3 r
  55.   }
复制代码

) e! }* X3 g, x$ ?2 k* Y! Q  这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。+ W& ~8 W- Y$ a8 ?# p1 B
  实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。
" S0 O+ I' K; m9 t5 ]" _; i+ Y  首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。* N+ z$ O- ?7 Z4 u3 ?# D
  下面直接贴代码:5 a( }3 _! J* \! L- |5 w

/ U0 g' v$ F! C& j0 U- I9 r
  1. // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示; I9 ]$ e! d8 I2 W
  2. 1 R# s% S/ @) s# K: m* I3 ?# _
  3.   // color 是画笔的颜色,BkColor 是背景颜色( J- P' C4 o; L- @

  4. - l, K0 ~+ U6 r% g
  5.   u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor )# Z/ l  T- s* U2 O1 I
  6. 2 b% I9 o- v3 O4 ?$ S8 {7 d
  7.   {. p* X( _; ~4 @- G$ C8 a1 b

  8. / k3 G4 e, G0 |5 W: o2 K4 i' F/ k
  9.   u8 res=0; // SD卡函数的返回值
    + S0 z2 c- O4 o+ ^
  10. : ?/ g! q; w) m0 a8 h2 R
  11.   u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码
    ( A! L! h8 s$ z1 L; j

  12. - v# G9 }* U" y; p/ i% ^
  13.   u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据
    5 u  \# k9 n' t" V! }1 \
  14. " h; u& U8 H" ^9 X) s3 Z- b. j
  15.   u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位
    5 p0 Z0 @4 F3 a1 H  F
  16. ) O+ f1 g1 B3 ]/ F$ e- P: ?; Q
  17.   u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了6 ~0 u9 F2 U4 J% q2 F0 Q2 w

  18. 6 r5 m; j9 \8 Y2 R& i, t5 y# @, N4 @7 z
  19.   u16 offset=0; // 读txt文档的偏移地址
    6 y3 p$ w+ J9 `4 V% r3 l1 |- Z; Q3 q9 ?1 r
  20. 3 [* t5 B; L& n) `" i
  21.   u8 i=0;
    2 {! n1 g: h% a/ D% |; q

  22. 2 U8 w! S+ @- ^2 D8 a  \$ v8 o: o/ _! e
  23.   UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数
    ; H  w1 `' {3 b. e- G7 i4 H- a5 S
  24. 5 S* a! {* E' C
  25.   u16 x0,y0; // 点阵显示的位置
    0 w9 c* r; w% X

  26. 6 ]* e$ M8 H8 O0 a- V- E5 H
  27.   u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,- j3 d' Q& o# F" d

  28. / b; c7 E4 @! y
  29.   // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。: s0 ^' g3 F/ K. c6 T% \) H
  30. & F7 d8 F( q0 K. t' `9 b5 g, i
  31.   x0=x;( c3 e7 M: Q) |3 _
  32. 8 E- `; x. o: N5 [! Q3 m
  33.   y0=y;8 n6 V: B. o5 D, I, j% p
  34. - o! a: Y' D/ }
  35.   f_mount(&fs1,"",1); // 挂载SD卡
    3 J! P: T  B3 q* _, u* Z; n

  36.   l0 U- ]; Q8 I3 e7 G; _6 s, [
  37.   res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件. M" v2 |$ x/ D2 O) a; r
  38. 9 ^4 F; O1 \; N' u- B5 e4 Q
  39.   if(res!=0)
    ( {) H1 Y# x, q: H, t% W8 o

  40. 0 v8 R9 f/ g& ~
  41.   {
    ' T6 E6 T3 I# l$ V4 k; r  F+ g. l
  42. $ o0 S7 j, c) H& }4 E
  43.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
    1 i: m; E8 h% |, Y8 \1 l

  44. 6 X9 t* T( u  {5 _
  45.   printf("res=%x",res);+ |! K/ n: r: S' c  B1 e
  46. ; l7 B+ R8 L( Y# e6 Y2 `: O0 g3 _# T' g
  47.   return 1;
    + |# C3 ~! ~& {4 b) P
  48. 6 J* b, r0 ^, W, t
  49.   }7 [( e2 [6 U; o" }

  50. " ?6 ?. M0 }& e2 M! ^
  51.   res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档; D! Z( C$ `& |" L5 l! I: T

  52. 1 o" s" V* e) u% K1 F
  53.   if(res!=0)
    : J& E8 @) v& g. J
  54. 4 i0 q) I( B/ O7 x) w
  55.   {
    ( x% L) u8 h1 U$ t; s) v

  56. / S6 N! |. T$ U/ j
  57.   fnShowString(10,120,"open txt fail",16,RED,WHITE,0);% m6 r6 u, q7 L5 d9 G

  58. 2 u$ B1 u: L! P1 X
  59.   printf("res=%x",res);
    & R, N+ `& {$ @) V2 R
  60.   E( G/ k2 z3 L9 P6 p$ \, B8 R
  61.   return 1;; x  L2 a- E8 r% R
  62. " J7 ~3 r  P: _  F. l7 h# S
  63.   }
    6 G7 j' s) \' M+ j8 `6 l4 O4 C( U
  64. 4 I. T( |& j2 T8 `6 L
  65.   f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。
    8 G  M, @! j- p# f) ^+ s
  66. ' Z" o8 P# r4 t1 u
  67.   res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节
    $ }  w0 Z' R1 d6 O
  68. & ?. v& g0 U, b" p
  69.   while(CntnuF) // 循环判断是不是读到文件末尾了" M! r; g$ {5 V( P0 q0 B0 m

  70.   h/ w# |" ]: O( y
  71.   {
    . ~& l5 w1 B3 v2 r4 K
  72. ) W0 R* `0 Y: {. d! `
  73.   for(i=0;i1 T4 O8 F) V1 G1 h5 ~7 b9 b

  74. ) L6 Z$ p! |4 z0 x: Y% x, |9 ^
  75.   {3 D0 U6 J. c* W; h
  76. 6 a& u3 V2 b: M( B
  77.   if(x>220) // 显示位置的判断
    + w0 t! V' p  ^+ T# `; y2 ?

  78. 5 ^3 ^# x( P! z2 H/ T  Q! K
  79.   {
    2 B' {" o$ J1 j& n# R8 w

  80. ; t3 Q7 _4 Z2 ]& y2 ^
  81.   x=x0;
    / q' S7 G4 ?/ ?7 |3 e6 a2 ?

  82. 4 x* H6 l  r- _! s; I/ @0 A* g
  83.   y+=16;$ A6 Z2 ^1 \# `1 Z$ x) t

  84. 5 G1 J! I1 s- W
  85.   }
    5 M, F  K# [! s  f

  86. ' |5 E/ I& Z8 J* F; O
  87.   if((y>300))
    " z2 O; b+ l. l- g7 t6 G. y

  88. # N4 N" L7 v9 Z7 `' h" O0 B4 a; L9 ^
  89.   {
    $ C; O& ?3 l/ {  v5 e

  90. , ^$ N# \' f- p8 M% ^
  91.   fnRefreshscreen(WHITE);
    - J. p6 S3 H# w% {. e" H9 T
  92. 2 @! `% n- D8 B
  93.   x=x0;- x* G6 _6 Q; Q: x
  94. 9 P4 r  V7 e  P% C$ b# g
  95.   y=y0;% q) m$ a1 B" k

  96. : d: F/ [4 h, }
  97.   }
    ! `  N  H8 C6 N9 s' Z

  98. ; P6 X8 t$ i7 Z. J
  99.   if(buff>0x80) // 是不是汉字
    . Z5 m/ p/ t2 D: Q/ A! q
  100. / L" a! K  |5 l) N
  101.   {9 R/ d+ z' p- m0 m5 o9 L

  102. 2 F- I6 a: j8 g7 ~4 i8 W
  103.   f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移
    0 p( L# v9 c* B2 W
  104. 5 B6 `" {, Y5 v9 v. x; j6 y
  105.   res=f_read(&f1,bitbuff,32,&bytenum);
      B* T% S) V  g6 m; F2 }6 ]5 Y0 B) c
  106. " y. Z3 H% ~$ x  J( q
  107.   fnShowHzk(x,y,bitbuff,color,BkColor);! U4 Y* P  p8 T2 U" l0 l1 Y; ^

  108.   x( E5 u9 p  R; V! D) E
  109.   x+=16;; t: b3 e! {: H! p& g0 E6 a! M" o1 T
  110. 1 q+ @, [4 O6 `
  111.   i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,/ F2 Y1 ^# B  u. M9 w
  112. 0 e$ [6 i6 k# e/ q
  113.   // 这里需要还要一个5 Y2 q* u- k4 \" v; j$ v) F
  114. 9 ~: B  Z  C% Z, ~
  115.   memset(bitbuff,0,sizeof(bitbuff));
    : P& k3 n% H9 P' i: G

  116. 1 z) D, y3 _& a6 ]; k
  117.   }
    & Q# |5 M9 I9 I8 z( k( O2 g

  118. " t2 e( P: ~# [
  119.   else // 可能是标点也可能是换行符
    * `9 X9 o) [$ y9 T% C6 k6 Q
  120.   G( q5 d7 Y% ]5 G% A" o, J
  121.   {
    ! V1 Z6 D1 \& F9 }. x0 j

  122. ( e" n7 t# i. |! ^# J
  123.   if(buff==0x0D) // 换行标志
    . |9 h0 L  R7 n- c* g. j+ I
  124. $ \& S- R7 z$ h
  125.   y+=16;; S! a9 A- S4 g/ ?4 d& H' a- l
  126. 9 F% f: y! r+ ?8 O" k; Q
  127.   else+ N4 i  E, K6 e% w
  128. & e0 ?% h6 d2 U  ^& C: A8 v+ t& B6 Q
  129.   fnShowChar( x,y,buff,16,color,BkColor,0); // 字符
    8 ?& _* _* e4 U/ p
  130. . q, f- B" q" ^! C
  131.   x+=8;) q- o0 \- D# d( U
  132. & A! X- W5 C5 O' s
  133.   charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址
    9 R/ T0 [  e- R, m. j

  134. ; Y, T3 F/ u$ d. q4 \$ H! Y6 ~
  135.   }
      F6 e( M" G4 `" t& h% w( N% @. B

  136. " O8 a8 `: z1 {4 T
  137.   }/ c: V  K) I# t+ c( G- b

  138. 4 _7 ?0 J  A1 p* p) G$ {
  139.   // 计算txt文档的偏移地址
    * {, j# v- @5 g  x

  140. 4 D  U' K& i5 ]
  141.   if(Hznum!=100) // 判断是不是读到文档末尾了& |2 n: Q% m& S- T
  142. 5 r( k$ q& E7 X' L" i5 K
  143.   {" h( O/ L3 s, \. @6 m
  144. 0 b9 \8 p4 m; a0 @# ?
  145.   CntnuF=0;9 u; s' s) j& a# z% w
  146. / Y, D) j- G5 }- W; _1 q
  147.   }( P/ @. q+ w3 g/ G

  148. 1 u2 f$ a: X1 [( G* D( t" J$ G8 F
  149.   else // 没有读到末尾继续读# l1 a1 G  d6 S$ ?. V% l# w

  150. ! @9 e2 @) j% {( N' Q% z
  151.   {" E+ b% K4 d: V! N
  152. ! o5 q" E% b- a! I% b
  153.   if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+100
    5 R3 L+ ]$ y( m2 g. n2 q0 {

  154. 3 G7 j2 M" X* m' n- A0 g
  155.   offset +=100;
    $ y2 k  g! M+ h& Y$ G
  156.   U8 K1 m+ f: R+ v" D6 ?
  157.   else
    % C* s0 f* n! v" q4 s+ `6 ?

  158.   O/ P7 R5 }1 N, D+ F2 U8 l
  159.   offset +=99;
    * t, V6 K0 h/ V8 W* l* s. d2 a

  160. 2 S1 k+ x! m7 l6 D+ m9 v# k
  161.   memset(buff,0,sizeof(buff));
    - t% B* y. s# C1 U8 h% Y

  162. 6 g! P$ x% s2 h; O9 ]
  163.   f_lseek(&ftxt,offset); // txt 文档地址偏移% a$ Y" [" u# x  g% n
  164. - ^( Z* h2 B( B
  165.   res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据
    ! r) U$ M+ q7 p# @- y7 P
  166. 2 e9 }9 ~% i5 U6 Y, M( ?$ i7 w
  167.   }+ J+ K! Q$ J6 T+ g
  168. " a2 }5 D8 a8 I! |( \( j4 m  g: c3 B
  169.   }
    2 I: k5 P2 h; J" E8 `
  170. . X  ^7 g- m1 J& d( O
  171.   f_close(&f1); // 关闭打开的点阵字库
    : w7 c% m' _- g# b; |
  172. 6 f) r) x3 |, h
  173.   f_close(&ftxt); // 关闭打开的txt文档
    5 N2 g: h" v$ K) U* S% ]; t7 U( D

  174. 4 ?2 T: @% r; V4 W
  175.   f_mount(&fs1,"",NULL); // 卸载磁盘1 L" t/ R. B8 j

  176. ! o) v2 {& P( a9 L( F
  177.   return 0;
    1 }, P; g: T9 v

  178. . ?' P0 g$ ^1 ^. N- N* D
  179.   }
复制代码

, ?. P" V! {+ A  P4 ^! N  以上是简单的电子书的实现。
% @8 h% f+ |) e& m# ]) C  因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。: o. F8 P' R! j; p  ?/ o7 j
  其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。% s8 d1 t. N0 |" Q# Y
  SD卡注意事项:
# G- f+ N  Q0 m% _# e/ R; \* F7 o  对一个文件读,必须先打开文件,读完后关闭。
- R6 k" n0 \0 j* P' I  对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。
4 n: M; ^$ [, H9 @4 `) g  文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。, r4 Q9 }4 O- J. Y8 n0 v

$ a9 i; v- I* @1 S$ n8 T
5 Z0 V6 T4 w+ Z& `3 W5 ~$ n
  • 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-11 11:43 , Processed in 0.078125 second(s), 23 queries , Gzip On.

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

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

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