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

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

[复制链接]

该用户从未签到

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

EDA365欢迎您登录!

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

x
  今天玩了会液晶屏,原来显示汉字都是也取模软件区模后在液晶屏上显示,显示内容改变以后还需要重新做字模,比较麻烦。这两天有时间,参考网友资料,实现了读取汉字的内码从SD卡的GB2312点阵字库读取点阵在液晶屏上显示,字库的生成软件用的是易木雨的点阵字库生成器。能生成很多种语言的字库。做完了读取显示后,我自己又琢磨了一下,简单的实现了从SD卡中读取txt文档然后再液晶屏上显示txt内容。: T; M; L5 T* o2 ~  U
  取模过程注意点阵的宽、高和字体大小的关系,宽、高是我们在液晶屏上要显示的像素大小,字体是汉字大小,如果宽、高一定,字体大小太到的话,字在液晶屏上只能显示一部分,可以在左侧的预览区看出来,如果字体在宽高像素点的范围内则可以在液晶上显示完整的字,如果不能显示完整则可以调整宽高或者字体大小。& a8 e7 A) }2 h/ n5 P/ O) q
  国标字有GB2312和GBK编码,GBK完全兼容GB2312包括内码的兼容,A而且GBK字库增加了很多字。汉字都是用的两个字节表示,第一个字节是区号,第二个字节是在区内的相对偏移位置。通过内码来获取字库内的偏移位置,偏移位置与内码和生成字库的宽高都有关系。我生成的GB2312字库是16*16个像素点显示一个汉字,那在字库内的偏移位置:9 |! z4 P3 M1 ]. }* l; N5 B6 C" s2 P
  offset=32* ( (H-0xa1)*94+(L-0xa1) )3 L1 v2 b+ n# z9 D! y& N
  H 表示内码的高字节,L表示内码的低字节。GB2312高字节是0xa1~0xfe表示区号,每个区有94个字节所以*94得到区的相对位置,L表示内码低字节也是从0xa1~0xfe,L-0xa1 得到的是在当前区内的偏移位置。 因为每个汉字有16*16=32个自己组成,所以最后乘以32得到在点阵字库中的偏移位置,从这个位置开始取出32个字节的点阵数据然后打点显示就可以把一个汉字显示出来了。
$ B7 Q; g' P  o  获取点阵的代码如下6 \& F/ z5 z' p2 P. }- B
( w7 g! A+ N$ P, P& i: Q
  1. FATFS fs1; // 挂载SD卡的分区用  @5 D9 G' X4 W
  2. 0 S- A5 l( d2 C  E: [
  3.   FIL f1; // SD卡中字库的文件描述符
    2 M: D+ N' |6 K0 Y6 }. J% y- r- y

  4. ) b9 X7 r. I2 ?, z" Q5 r' ]
  5.   FIL ftxt; // 要读取的txt文档的文件描述符9 T5 X( I  J$ u4 K( ]6 m

  6. ( r# u# ]4 r( Y9 r: M
  7.   u8 fnGetChinese(u8 *p,u8 *buff) // 形参是要读取的汉字, F; p% l/ B% q: n
  8. : [- j, w& A. ~4 d2 J8 x
  9.   {
    ) A# m  u+ I* f% d4 S2 @9 V# P8 [

  10. : L) A$ ?$ i' X
  11.   u8 res=0;
    : O6 z, W3 R: K" `2 T. k) H
  12. 7 _9 ]; j' ]! r, D( E# H* w
  13.   u8 H8,L8;8 R' g! g- a8 Y
  14. - q, ^* N; o6 ]# R' V5 j5 }
  15.   UINT num;
    % J; x& Y8 H9 n; F* [7 x. b
  16. 8 f. a% q, b6 Q' w5 a1 y
  17.   H8=*p;5 G# ~1 b) c$ d7 `* b3 Q

  18. 6 k) n% B; ~$ u& C' m' i4 q8 V
  19.   L8=*(p+1);  M% V) W4 J1 q" n

  20. ) G! B6 a6 c5 |* B5 J" y, e
  21.   f_mount(&fs1,"",1); // 挂载SD卡
    ( x" {% y2 S, B- G+ C" L

  22. 4 I% g  |$ }. ]& l& Y) e/ P
  23.   res=f_open(&f1,ReadPath,FA_OPEN_EXIStiNG|FA_READ); // 打开SD卡下的点阵字库) D1 B  H7 v' }. T
  24. 8 A1 ]' y0 w( y% _" b
  25.   IF(res!=0) // 判断点阵字库是否打开成功
    0 g) ^6 d+ c4 ]  y" a& {2 U
  26. 1 K) I& m5 p/ n" ?0 m" D
  27.   {
    : a7 w- T" J. D
  28. # s$ P" W- _# C  g5 f
  29.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
    9 n& a* s" w* B" V' k+ a  B; C0 \
  30. 3 g5 H; p- c; e1 Q* S1 v: i" W
  31.   printf("res=%x",res);
    9 i+ ^# k7 u( v' E0 b, O* M4 I
  32. 3 ^9 v/ F" t' c7 c( a! n! D; `
  33.   return 1;8 s7 O; h0 l1 b6 j# F9 b* p

  34. + N: l& C3 i9 K$ ]
  35.   }
    ( m! O; c% E' ?$ a, ~
  36. * X( \% T$ F+ F" M% _
  37.   else
    ; @+ V( M8 F# _1 t/ ~2 R
  38. " V4 h. E, `: w4 i+ f
  39.   {
      G- q# ^9 g# x6 T& j; J

  40. 2 R# [# H7 X( V1 H& w& ~! z
  41.   fnShowString(10,120,"open ok",16,RED,WHITE,0);
    7 P! c- m- O' w9 @" @, @' w
  42. 9 s. ]/ a4 f5 k* c2 r, K& h) ~) Z- X
  43.   }% G8 W8 d; O7 Y" j8 |+ V) ]8 |

  44. 5 W# N$ v6 ~8 D
  45.   f_lseek(&f1,32*((H8-0xa0-1)*94+(L8-0xa0-1))); // 在字库文件中做偏移取出32个字节的点阵数据8 @+ o9 \* N' y. h5 r
  46. ! r# _2 l% c+ A5 Y
  47.   res=f_read(&f1,buff,32,&num);
    ) P$ J7 K0 C, P9 Q

  48. 7 h( z0 m) H7 m7 F% Y1 r+ C& }4 F
  49.   f_close(&f1); // 读完后关闭点阵字库文件
    $ q0 ^2 v& C1 o, F5 V& o9 r

  50. ( c  i. J: M+ [  A
  51.   f_mount(&fs1,"",NULL); // 卸载SD卡
    2 {  k# N+ c0 |. D+ c2 P  n" C
  52. ! q6 X/ I1 I; I" Y) U
  53.   return 0;  z' O; L1 V( F( }  _% L" L$ b

  54. & A0 D: c; H( L( G' O
  55.   }
复制代码
3 v/ ]/ o: i0 A. H1 }
  这个是得到汉字点阵的实现过程,得到点阵数据后就可以在液晶上打点实现汉字的显示,具体的底层驱动不再详细介绍。
& f9 r! l: X/ `1 f! F" w3 E3 |  实现了在单片内显示汉字串以后,能不能读取SD卡中的txt文档中的汉字在液晶屏上显示呢?这块是我自己想的,不知道与别人的一样不一样,反正是实现了。
( I/ N& W# R1 g  首先我读取SD卡上txt文档上的一个汉字,然后用串口打印出来,发现,读到的就是汉字的内码,百度了下说windows中txt文档的显示的用GB2312的字库。既然读到直接是内码,那就好办了。汉字内码用的是两个字节,字符用的是一个字节,这个一定要注意,因为在以后显示的过程中要用。
: E. U" c3 u& W; y2 B& c+ ?8 C1 M  下面直接贴代码:
$ {/ G5 N# w$ u- a, T! {4 W& d, [  |; x5 k, W1 H4 v
  1. // x,y在液晶屏上的显示位置,我的液晶屏是320*240 ,竖屏显示4 j* f$ o- F0 x0 M8 ?3 @7 Y

  2. ' R) F$ `7 U! ]5 L
  3.   // color 是画笔的颜色,BkColor 是背景颜色5 r. \% f7 @1 B, B5 o
  4. - m: H, H% k8 w
  5.   u8 fnShowTxt( u16 x, u16 y , u16 color ,u16 BkColor ), Q$ E, I6 U, w  z

  6. 0 v( n; i) s4 B; t5 a
  7.   {
    $ F3 Z  T# ?$ R& E9 X
  8. $ n8 Y. d9 s9 a- @; _/ F) W
  9.   u8 res=0; // SD卡函数的返回值
    7 Y# K% T3 C8 T7 k

  10. 8 m- ?/ {# P- W. c8 z
  11.   u8 buff[100]={0}; // 存储从txt文档中读到的100个字节的内码) {( k7 J% J3 B/ M4 R

  12. - d6 W1 N: O/ o1 n1 h2 f
  13.   u8 bitbuff[32]={0}; // 存储从点阵字库获得的32个字节的点阵数据4 u; j7 I: ^9 c0 P: c7 l

  14. " V+ z6 M# W" r# C  y9 E% {/ f
  15.   u8 NeiMaH,NeiMaL; // GB2312内码的高位和低位7 a: c, C- Z/ c. R! Y! S7 ^$ I
  16.   a4 U3 A- I, v, ]
  17.   u8 CntnuF=1; // 用来 判断是不是读到文档的末尾了,如果读到字节的个数小于100则表示读到末尾了; o, X- `2 c* V  r/ h2 X  _6 K6 b

  18. 9 ]5 W# r* X1 `( Z/ D7 p- ?
  19.   u16 offset=0; // 读txt文档的偏移地址" D# S3 m, ^) c1 A) t1 G
  20. ; n7 I+ n% \% _
  21.   u8 i=0;1 X+ g2 a& P6 v" \9 M/ }- E7 H* g3 Q

  22. ; l' {% {. |5 D& v
  23.   UINT Hznum,bytenum; // 实际读回的内码字节个数、点阵字节个数$ G0 ~9 Y3 F& [

  24. 4 J  t9 A. N* p% s: i; b
  25.   u16 x0,y0; // 点阵显示的位置. Z) J- E  u+ {

  26. $ X/ O& m" w1 o0 l1 g& U
  27.   u8 charCnt=0; // 读取的100个字节内码中有几个字符,如果字符个数是偶数则下次偏移再偏移100,如果是奇数,$ d' ^4 I: B: v+ d: n! }: u: U
  28. 4 i+ M$ O2 f- O* T7 e( P( `
  29.   // 则读取的100个字节中的最后一个字节可能是下一个汉字的内码高位字节,则偏移99,下次再把这个字节读上。: D5 P0 W7 a/ r/ z" e/ H" f( r

  30. ' J# \8 h- I! F. [% F5 A% `3 v  d
  31.   x0=x;$ w- N- @$ {; Z. I8 g/ ]8 m5 |

  32. 0 @1 B+ ]0 w+ M8 N- ]
  33.   y0=y;
    ; S1 i1 R# D3 S# F3 ^
  34. : s2 A! h% ]$ t1 p5 @: e# }+ }7 {) R
  35.   f_mount(&fs1,"",1); // 挂载SD卡- O. V7 ?( L8 w

  36. # X1 y$ ^8 j6 a  c5 \* @. d# D
  37.   res=f_open(&f1,ReadPath,FA_OPEN_EXISTING|FA_READ); // 打开字库文件. ~5 ]! w2 v$ a" _3 d
  38. " X: f) R8 G8 Y
  39.   if(res!=0)
    ' W5 l0 u1 V6 h$ c2 q
  40. * r+ B/ i4 p! a# ~
  41.   {
    0 @& _6 v, [( ^! {$ k  f2 t3 n
  42. ) v" L* X3 B0 ~9 ~
  43.   fnShowString(10,120,"open hzk fail",16,RED,WHITE,0);
    ! D' N, Q# Y3 ]2 R( A' b7 M. f& ]
  44. 5 ?/ `* w7 h' p; v& I. K% G
  45.   printf("res=%x",res);' x/ ^/ e' n# ?1 r6 W! Q
  46. 8 _" j3 g5 ?$ p9 @( F5 A
  47.   return 1;
    - M; s# Q; T3 Q0 o

  48. " B* e0 z1 Y" e
  49.   }- E' ]3 ~. y3 S& ?" K- ^

  50. * C1 r% f, ^4 h' W
  51.   res=f_open(&ftxt,TxtPath,FA_OPEN_EXISTING|FA_READ); // 打开txt文档, k  g' O* P* M5 m
  52. & @6 L' @! ?& [& y) @+ }
  53.   if(res!=0)" G! m8 A  {8 |; k
  54. . m- j) |! Z; L3 q
  55.   {8 J5 B: S* H/ k8 P3 J- Q

  56. 9 w; D3 ?' ^& M3 ~
  57.   fnShowString(10,120,"open txt fail",16,RED,WHITE,0);( Z$ x7 i9 T  F+ p4 T
  58. 4 `# u& a* J3 L( q' ]6 G6 Z. u
  59.   printf("res=%x",res);* J% ~+ e) e4 M- D4 l
  60. / r! I3 `) A9 h' y' v8 w% r; Q
  61.   return 1;
    " Q) R3 u% \' D) U- H' Q( W0 G4 `

  62. * E* T/ N+ |& L( |
  63.   }
    & F* t0 V; q% d- o) Y& M

  64. 7 q( B# D6 Q& J
  65.   f_lseek(&ftxt,offset); // 初始化偏移为0从头开始读。
    ; B+ V$ ?  [1 P
  66. % D7 {$ i) R8 Y- ]2 k# H3 f( z
  67.   res=f_read(&ftxt,buff,100,&Hznum); // 每次读100个内码字节6 N) ]. ?: _5 P
  68. ' d9 J; M% i5 t1 q7 m' [) `
  69.   while(CntnuF) // 循环判断是不是读到文件末尾了
    8 Y6 x& B8 J; R' W  W* p

  70. & C; J2 \5 Y+ [: V1 ~
  71.   {
    + b. Y# Y  N% b4 r

  72. 7 }: C1 J* a0 |+ N
  73.   for(i=0;i( K) D( G0 w3 C  m! c( X
  74. & T4 a' T/ Q: ]
  75.   {
    $ O( w: H9 X3 E, ]9 d, R9 a& b( s
  76. " _$ t# P+ x/ q7 ]$ b4 h, n
  77.   if(x>220) // 显示位置的判断
    # m/ q$ Z- V" a' s: h% U
  78. - p0 r  X9 J4 o2 d/ u3 _
  79.   {4 g, X' h4 N4 {7 H: b! F$ |

  80. $ q8 ^, V; ^5 i8 R9 j
  81.   x=x0;
    ! z+ ?+ I! ^. z
  82. 7 v$ o6 ~" M+ d
  83.   y+=16;
    $ `, i( ?% E0 l# q' U4 s0 Z9 b
  84. 4 i: b* Q7 B% E. B
  85.   }
    9 K( ]7 E/ v$ F
  86. 0 K! P6 M; L, X* G: K
  87.   if((y>300)), u) l; R. Z: x1 c+ N, N$ Z( g

  88. % [" N0 A% x1 M2 v8 E$ t
  89.   {
    9 S" U% z" Q( s2 B9 {& e

  90. 7 X5 [. O6 ~! K
  91.   fnRefreshscreen(WHITE);
    ! b0 T- I9 h- W" V: ^

  92. * |$ k) k" P& |, ~
  93.   x=x0;, W  a4 c% o% w# p2 d
  94. ( e) n; E( H6 D8 `
  95.   y=y0;# h5 t, x) \! d

  96. ! U3 w+ z1 g, a+ t' x: F/ e
  97.   }! k$ L$ |' \( f6 X3 B

  98. # T* Z7 d! z! G- V* T
  99.   if(buff>0x80) // 是不是汉字0 ]3 Y6 J( g: {6 j4 d% k
  100. 9 |0 W$ ?1 B9 z1 A: ?& ~  h
  101.   {
    : y( M  E! S4 D& K8 v! l9 P$ \4 K
  102. 3 H9 |* U$ `/ o7 D, A/ P( b
  103.   f_lseek(&f1,32*((buff-0xa0-1)*94+(buff[i+1]-0xa0-1))); // 点阵字库内的偏移' H& v( I- {4 ]6 R, S& `& P* _

  104. + h2 R7 {" t; |( Q
  105.   res=f_read(&f1,bitbuff,32,&bytenum);6 u+ d8 G1 M- V' |6 P% ~
  106. + N8 j( h! P6 N' c
  107.   fnShowHzk(x,y,bitbuff,color,BkColor);
      ~. e7 f( [8 e# ]0 \
  108. 9 [3 Z/ t9 x! e& N) Z
  109.   x+=16;! E6 R4 p, F4 U% c" A7 ?2 D1 D
  110. 0 }4 u9 w5 `2 W$ O2 Q5 F
  111.   i++; // 这个i++非常重要,因为一个汉字两个字节,除了判断语句i++,7 p2 Q( I- e0 L  T+ p% d) F

  112. & i, O% v( N! [$ s8 d2 ?" i
  113.   // 这里需要还要一个) B3 C7 S- n# r* x) F" M! I

  114. . S! Y' i  K% N4 K& F
  115.   memset(bitbuff,0,sizeof(bitbuff));  m. {; m1 e  c$ z  [

  116. 0 e* {" Q, f1 X
  117.   }
    $ m) _, b3 j$ Q3 i+ z9 _% g% R9 F

  118. 2 D+ I$ N) j/ f4 w& o
  119.   else // 可能是标点也可能是换行符+ S% G7 s& X$ ~; }# \. r8 s9 v4 U" A

  120.   W6 I% ^$ q5 Y( x" g
  121.   {
    6 _  E6 G' m2 z8 j

  122. & z4 @; J: P' N5 k
  123.   if(buff==0x0D) // 换行标志8 P; i  e1 w% y1 k% }* f" v, i

  124. ; P" t. ?" r7 c! t) `8 ^7 [2 |- P
  125.   y+=16;. X1 G0 ?2 K- w; b; n

  126. - S' L3 ^+ ?) f8 F
  127.   else2 [5 c7 p4 y. d' E0 V. d4 l

  128. / M) p2 O3 n( ]! ~% _# D+ `
  129.   fnShowChar( x,y,buff,16,color,BkColor,0); // 字符" L1 {5 h4 S9 @0 q

  130. ' M6 X2 ]& _  Z2 ~1 G4 |
  131.   x+=8;0 A  ~8 ^6 ^( m0 }! y* E

  132. 4 e# Q3 C( S2 i) c
  133.   charCnt++; // 字符个数计算,用于判断下一次读txt文档的偏移地址
    9 x* s% S- x% p1 l- Q- J( s: j9 k  y4 X
  134. , L# R0 ?( x: U( F5 A
  135.   }" @, m9 g9 L* _9 V
  136. 8 \6 i/ w& ]( q. W$ v( }
  137.   }
    ! I; x. r+ y" _% X8 K+ v
  138. / k, a2 t: P' `# Z* ^" I( p
  139.   // 计算txt文档的偏移地址# `5 @' A) o7 j0 B- F* ]- r" E7 l. K/ q

  140. 1 L5 A; \! B6 M2 Q. W" j
  141.   if(Hznum!=100) // 判断是不是读到文档末尾了; R5 k( d- I/ E, F# @& F1 I
  142. * b7 Q3 A( \( n4 Q! @8 I* ?, {
  143.   {
    ( ?/ Y5 B. ]8 W

  144. : T% o: V! H9 {- r( J& v; b' U0 q7 g
  145.   CntnuF=0;
    + ~: R5 w! Y" ~
  146. - X# [5 s  e/ ~( q) t
  147.   }4 `' o1 s  j( l. T  n2 v2 W: M
  148. / G* P! w) L7 E* ~$ S! W7 h, |8 K
  149.   else // 没有读到末尾继续读) k6 h! k0 U- u/ ]
  150. . O. B% e8 ?  E. z! m
  151.   {2 c0 Y; Q& X+ m+ ?7 U0 I3 ~

  152. 3 S4 |* y9 \' n2 W. [  F
  153.   if(charCnt%2==0) // 字符个数是偶数,100个字节内码里边正好成对出现,地址偏移+100
    , h% N: Q8 K2 P) S/ a# @. ^- _( j

  154. 7 G+ m$ r, T* F  g
  155.   offset +=100;
    ) @0 K2 r: y: K* l5 ~: c" z. V
  156. 9 U* I0 j8 \: C  p1 I$ J' q
  157.   else
    3 ^% H/ {  d* [4 @( ?5 ^

  158. ' b. G- f1 [. d1 T* X1 I) A
  159.   offset +=99;$ Z6 v1 l; M( a1 _, u- k7 J

  160. 4 H6 r9 L3 S  T4 H
  161.   memset(buff,0,sizeof(buff));
    - v# L9 J3 y+ p8 x

  162. % ~/ t7 f! |4 i& Q1 k
  163.   f_lseek(&ftxt,offset); // txt 文档地址偏移
    & [5 \* H! E& @6 Z7 ?4 Y1 `
  164. 5 q) a' H) |3 p
  165.   res=f_read(&ftxt,buff,100,&Hznum); // 读内码数据
    3 K. A0 F5 y/ ]( w; b
  166. 5 q% C" E. D/ E/ p) w
  167.   }6 j" b/ a% e  H/ Z
  168. 2 H1 G4 P# i6 Q# D# [- a
  169.   }, D$ d7 z$ `$ d. I( s( {
  170. 4 [: s# O( O9 b& P: h/ e
  171.   f_close(&f1); // 关闭打开的点阵字库  G, s2 j- o* U
  172. 2 {8 N! x' [7 `- H; {
  173.   f_close(&ftxt); // 关闭打开的txt文档
    + S& J. _# V4 J* v8 x
  174. 5 d- W% i; k* ~) F
  175.   f_mount(&fs1,"",NULL); // 卸载磁盘
    - ^% w. u; k( _

  176. ) S* u0 F6 a9 G, Y: x: U
  177.   return 0;4 p6 B+ a" \5 F( W7 g
  178. " y: B6 p+ V5 p8 e. R1 Z+ ^- r
  179.   }
复制代码

, W5 o0 Y6 V( _# u9 h  以上是简单的电子书的实现。
2 f8 M$ |" G+ R# l& Q6 m# b* l/ ~$ j& s  因为不同的系统有不同的编码这个要注意,比如我再Windows上的汉字拷贝到CSDN网页上就是乱码,这是因为使用的汉字的编码不同,对于不同的编码格式,还需要做内码的对应转换,把其他的格式转换成GB2312或者GBK格式然后调用字库显示。: {* J5 H2 H8 s
  其他常用编码格式Unicode、utf-8等的具体介绍和转换成GB编码可以百度。( ]3 X! z2 o8 S2 f* S. H
  SD卡注意事项:
# Y2 b* m8 j, ^  对一个文件读,必须先打开文件,读完后关闭。- r: B& M, V5 J
  对一个文件写,必须先打开文件,根据情况确定打开的权限,只读,读、写、创建等,先完后最好调用f_sync()函数,这是一个同步函数,类似于linux中的同步函数。SD卡中的写函数应该是带缓冲(猜的),在关闭之前调用这个函数将缓冲区的内容写入SD卡中,然后关闭文件,否则可能写入失败,不能将内容成功写到文件上。8 O. H( ?. T7 ^, H. k3 f
  文件的打开路径,Windows中的文档可能是隐藏文件类型的,这个一定要注意,隐藏文件类型的a.txt和不隐藏文件类型的a.txt 不是同一个文件,这个一定要非常注意。' T: H, a$ q; W. y5 R" N+ \. h

- t' U: _, r* J. v. A' s) ?* q" U% i
  • 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-11-5 15:14 , Processed in 0.156250 second(s), 23 queries , Gzip On.

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

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

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