你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32实战应用(一)——1602蓝牙时钟1液晶的显示测试

[复制链接]
STMCU小助手 发布时间:2022-2-7 20:41
前言
从51到STM32F4学习这么久了,总算找到点头绪了,目前学习了GPIO,中断,定时器,看门狗的基本使用,所以想试着看看能不能做个什么东西,就是想复习一下最近学习的知识。正好上学期单片机课程设计做过一个可以蓝牙、按键校准、带温度显示的时钟,所以我想看能不能将程序移植到STM32上呢?说做就做,经过三天的时间,几次的程序修改和调试,终于成功了!

3 ?, |$ U7 a. c. R7 b# y
由于STM32内部定时器的精度还是很高的(一小时慢1s),所以就没有使用DS1302时钟芯片(关键是手里没有),显示上和课程设计做的有些不一样,没有增加日期和星期显示,就时间、闹钟和温度的显示。

; H0 }9 M+ q9 e& L今天先学习一下1602液晶的显示,那么如何用STM32控制LCD1602显示字符,字符串或数字呢?/ U4 X4 x# X% V& @
1602液晶介绍
工业字符型液晶,能够同时显示16x02即32个字符。1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块。它由若干个5X7或者5X11等点阵字符位组成,每个点阵字符位都可以显示一个字符,每位之间有一个点距的间隔,每行之间也有间隔,起到了字符间距和行间距的作用,正因为如此所以它不能很好地显示图形(用自定义CGRAM,显示效果也不好)。市面上字符液晶大多数是基于HD44780液晶芯片的,控制原理是完全相同的,因此基于HD44780写的控制程序可以很方便地应用于市面上大部分的字符型液晶。1602LCD是指显示的内容为16X2,即可以显示两行,每行16个字符液晶模块(显示字符和数字)。
2 p* j+ d$ F5 Z! x
由于1602液晶是5V供电,引脚高电平为5v。不像51单片机IO输出高电平5v,STM32高电平只有3.3v,低电平0v,找到了1602的数据手册,里面居然没有说明高电平电压范围,到底能不能驱动呢?我又查找了1602驱动芯片HD44780的数据手册,其中有一个电压特性,如下图" D- w* B2 }$ b
1124009-20170423131826726-1458841834.png 8 E7 b( `; }5 r, ^" @

, ~$ R% S- e& H( t& }
可见用STM32的3.3v电平驱动显示完全没问题,看到这我就放心了,代码经过多次的修改后,终于成功显示了,所以还是要多看看数据手册。
- N  l+ W4 K7 X) G, I
硬件电路连接
. Y7 D& z; I- n5 W
: X4 }& g3 G- V% Z3 T

2 o2 o' J7 N, p/ J6 U: Q* Q 1124009-20170423132248601-1684036602.png ( ~4 E( M# s$ ^- B2 ~4 |
- o# c9 L  Z1 L! r6 A' u' v" C

8 X' n, F) v: @7 ]5 n程序设计
0 A% R& c' B: Z1.控制线宏定义,通过位带操作,以后就可以像51那样RS=1来操作了,是不是很熟悉呢?这里还将数据线的8个端口定义为了一个LCD_DB
5 o2 `# k) r3 r5 [3 f% X1 `% u+ i6 H* e9 H
  1. #define LCD_RS PAout(1)
    - w9 v. [  a) c+ N8 `8 G
  2. #define LCD_RW PAout(4)" Z; \! ~8 A5 t) p
  3. #define LCD_EN PAout(6)
    3 _+ T/ U# t7 n# w5 Z  ]& K: {$ H- K
  4. #define LCD_DB GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 |GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15
复制代码

  G1 ?: x; u' t+ U9 u2.LCD相连的GPIO配置,将数据线端口配置为OD(开漏)输出模式,可以做双向IO使用,在检测LCD是否忙时,需要读取D7位的状态
; C; K$ _6 \' ]0 u
  1. void LCD_GPIO_Config(void)9 w, c, }/ O/ l, E, K
  2. {
    6 {- ?; N" K8 Z
  3.     GPIO_InitTypeDef  IO_Init;
    9 e$ n5 ?* m1 w  n
  4.     ) A: y# f2 C# G' O3 y/ P
  5.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOE,ENABLE);
    ) C! H1 G  w5 I" J( L1 `% Z
  6.    
    9 b* g/ W( G9 N
  7.     /*控制线初始化 : */
    3 V, G" l$ Y6 w
  8.     IO_Init.GPIO_Mode=GPIO_Mode_OUT;        //输出
    , p0 w- D% X' [" c, G4 C8 A
  9.     IO_Init.GPIO_OType=GPIO_OType_PP;        //推挽模式
    * d2 t/ @" p- d) C
  10.     IO_Init.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_6; , Q! g* V  s) T, C: u0 F
  11.     IO_Init.GPIO_PuPd=GPIO_PuPd_UP;            //7 W$ d3 `# w4 F. X
  12.     IO_Init.GPIO_Speed=GPIO_Speed_2MHz;        //GPIO_Speed_2MHz* I' K8 Y( P0 ~2 ^# `& i6 v* R6 _$ J
  13.     GPIO_Init(GPIOA,&IO_Init);  ]( X" P$ b. F
  14.    
    ! z2 U8 V( k4 I. Z; r
  15.     /*数据线初始化*/
    , q/ [( ~! Q- t
  16.     IO_Init.GPIO_Mode=GPIO_Mode_OUT;
    5 c4 M2 C: @+ {% A! n; i0 O$ V
  17.     IO_Init.GPIO_OType=GPIO_OType_OD;        //开漏输出可双向+ Z: B+ Z9 \6 |1 j3 l9 E- H+ h
  18.     IO_Init.GPIO_Pin=LCD_DB;
    3 N# k! _' ?3 b& W% L: B: _0 m2 Q
  19.     IO_Init.GPIO_PuPd=GPIO_PuPd_UP;            //上拉/ G6 Q9 m( z* ~3 v0 c; v7 b) L; Q
  20.     IO_Init.GPIO_Speed=GPIO_Speed_2MHz;        
    ; H, ~) c' B, d! J% w) m* g
  21.     GPIO_Init(GPIOE,&IO_Init);                        ( s2 {0 h4 ?, H1 h9 s: P1 }
  22.     /*测试 : 高电平3.3v& `0 A7 c3 v3 G& ]- ?9 A+ o3 d4 g
  23.     LCD_RS=1;7 R2 A7 R4 M/ i' E9 p$ o2 y( F
  24.     LCD_RW=1;, H, L# o0 R1 L1 u. l
  25.     LCD_EN=1;
    * r% b3 v  n- k3 h7 s6 S5 T
  26.     GPIO_SetBits(GPIOE,LCD_DB); */2 ], I7 J" L, t
  27. }
复制代码
9 X' D' Z9 R  \" n
2 @; C. F7 t+ \
3.实现P1=0xff功能的函数,带参数
0 F* p; L/ Y, P; D
  1. void GPIO_OutData(u8 Dat), B: c0 L$ A( g
  2. {4 ~; a' m- g2 f; K7 a# S
  3.     u16 tmp;4 F# V" a0 d6 d: A* n  X
  4.     tmp = 0;; R- o$ B! k2 W6 w
  5.     tmp =Dat;
    . v$ s" Z* x4 E0 S7 j5 t+ S
  6.     tmp <<= 8;            //数据左移到高8位
    , h0 g0 m8 g( J" |0 D
  7.     GPIO_Write(GPIOE,tmp);        //数据写入到GPIOE高8位
    $ J, i; ]. |6 j
  8. }
复制代码

6 ^5 k# R8 S" }' I, j+ k1 Z4.检测LCD是否忙
: m% Q5 P" p% j1 S3 V3 b- H% Q, p
  1. void LCD_CheckBusy(void)
    6 H* X* f) y/ h" ^
  2. {
    4 m3 {/ y* B2 r1 R6 c% f& f
  3.     u8 sta;7 N0 U( W$ [1 M) D1 o
  4.     GPIO_OutData(0xff);
    ! W: e9 n* m7 e9 Y1 `0 b6 I
  5.     LCD_RS=0;1 j8 V$ m; U' i1 b
  6.     LCD_RW=1;
    % M- t( S- ]; l6 f4 \- W
  7.     do{& q* Q. K% ]) s! f5 h
  8.         LCD_EN=1;
    1 Y  T/ x. }$ k7 k$ X6 b- ^8 i: Z
  9.         delay_ms(5);# G& W' S# X6 ~6 }8 A3 `4 C
  10.         sta = GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_15);
    $ C4 C2 \5 p# U: {! O
  11.         LCD_EN =0;3 S- Y+ s, s9 y% c1 S
  12.     }while(sta & 0x80);, M: ?( \  x+ \' r9 B
  13. }
复制代码

( N% N4 `3 I+ c% d" C9 D- g1 J! \3 P9 z' t
5.向LCD写一字节数据! I4 C, Z& k6 @9 d  \1 c
  1. /* LCD_RS = 1, LCD_RW = 0 */3 w/ L1 n, a+ m
  2. void LCD_WriteData(u8 Dat)
    ; d4 @9 |. k6 Q. \1 ]. K! ^' }
  3. {8 a- S0 \% h% G3 @2 ?

  4. . H8 @" R+ p; {
  5.     LCD_CheckBusy(); //忙则等待3 Q. l3 U, S7 u2 f2 n& i
  6.     LCD_RS=1;* Y6 @) }) E; I5 n( `. \: q  T! C
  7.     LCD_RW=0;
    9 Z# q4 z# D+ D9 n- S& G/ p0 j- @
  8.     GPIO_OutData(Dat);
    $ ]3 e4 P8 ^6 A( X
  9.     LCD_EN=1;
    , d4 ~; H$ X/ W2 r
  10.     delay_ms(1);
    % [; Y; L/ m  j
  11.     LCD_EN = 0;
    + `$ C4 n  F# _
  12.     2 e" P; I. O& G. D& {
  13. }
复制代码
: V7 ^& P( b1 F6 l
6.向LCD写一字节命令. d1 x3 i7 e3 }% a6 R
  1. /*LCD_RS = 0, LCD_RW = 0*/
    3 v& Z( c4 r+ N8 Q. m( y5 Y
  2. void LCD_WriteCmd(u8 Cmd)+ X# H8 [' D5 o2 ?4 P4 S
  3. {
    1 T! l5 z8 h3 W% S  J6 w" a
  4.     LCD_CheckBusy(); //忙则等待/ |% ]2 ~1 ?$ ]6 ?# K
  5.     LCD_RS = 0;) z$ a$ U  S+ X4 Y. \1 r
  6.     LCD_RW = 0;
    ! J: i7 t/ ?9 ?. [' V5 {1 C- u
  7.     GPIO_OutData(Cmd);    . t( I& V; Q8 x, {( R
  8.     LCD_EN = 1;/ t( m, f  m) I) E# \- l
  9.     delay_ms(1);
    0 }& V/ U) j1 C0 k
  10.     LCD_EN = 0;     * W6 P- R% H4 ^; `/ b
  11. }
复制代码
9 O7 g$ Q( {$ O6 M3 N2 Q
7.LCD初始化& u8 @$ S; \& i. U
  1. void LCD_Init(void)
    / Z' u4 j  N. k4 R. a5 t3 u2 [  u
  2. {& A% r+ I2 M' u: G2 j& o# G  ~% c
  3.     LCD_WriteCmd(0x38);1 D% ?2 i1 @+ k) e* y( O) {/ u
  4.     LCD_WriteCmd(0x0C);
    3 K% c) `1 |  B: F0 p+ }
  5.     LCD_WriteCmd(0x06);    /*显示光标移动设置*/    delay_ms(1);% d9 [( B7 g! E" G, \
  6.     LCD_WriteCmd(0x01);    /*显示清屏*/
    * N& D# Q; Y" s" b& I% J
  7. }
复制代码

! V  c. C0 Y% v- i  N8.LCD清屏
$ C$ L5 b. j9 \5 n
  1. <div>void LCD_ClearScrren(void)</div><div>{</div><div>    LCD_WriteCmd(0x01);</div><div>}</div>
复制代码

' L' s* B: W& ^
8 Q* `5 q7 d6 J" d  [% L- Y% p- \
7 Z. `0 p6 ~3 U4 [7 F9.根据xy坐标,写入对应的数据位置
0 O' {4 Z& g$ \/ h8 t; Y1 x
  1. void LCD_SetCursor(u8 x, u8 y)
    8 v/ v2 w1 ?6 @. W% P( ~
  2. {+ e/ Y# [& u' m5 s+ F
  3.     u8 addr;
    ' z$ k; a4 n' R0 v0 `
  4.     if (y == 0)$ A: I* [; E5 k& N- C
  5.         addr = 0x00 + x;
    4 x7 a8 b' b: U8 t+ _6 {/ ]- ~  T
  6.     else4 b% G# [. K( H5 o
  7.         addr = 0x40 + x;1 y4 d! u3 m8 ]8 K9 @9 j5 w; f
  8.     LCD_WriteCmd(addr | 0x80);6 E' h, B! @: S, u. M/ h7 Y9 W1 [
  9. }
复制代码

2 X8 L7 d8 c: d$ U8 { 10.根据xy坐标显示一个字符2 }' g  P: `: ?0 Z- }( b
  1. void LCD_DisChar(u8 x,u8 y,u8 ch)( v. n3 K' ?& W/ n
  2. {
    ) P1 g5 i- j& G* v- V, e. ~
  3.     LCD_SetCursor(x,y);        //字符显示位置设定
    & |/ d, v) V+ z3 M
  4.     LCD_WriteData(ch);4 t' L6 ~) y; Z$ k, J( q
  5. }
复制代码

& W% o% z- B- E* O; v) N11.显示两位的数字- X7 |- T0 Z! @
  1. void LCD_DisNumber(u8 x,u8 y,u8 Num)
    8 c5 }4 O# ?6 X0 D1 y. d: u
  2. {, z  C7 T& Q4 s0 m
  3.     LCD_SetCursor(x,y);
    2 v9 b% d0 R# q' P0 a& m
  4.     LCD_WriteData(0x30+Num/10);
    3 E9 H5 {& g- G- l) K( Q
  5.     LCD_SetCursor(x+1,y);
    ; h3 [: D$ z6 r; [2 _
  6.     LCD_WriteData(0x30+Num%10);
    - f- b; P2 a  N3 o1 l9 I7 y
  7. }
复制代码

- d$ G" d. `, D: d7 I/ T: d  p3 d9 y, v* z0 d0 Z: g
12.显示字符串# x1 ^5 ?" P+ b3 S  S/ E; i& g/ d
  1. void LCD_DisString(u8 x,u8 y,u8 *str)# J7 A/ t+ s* i
  2. {9 J2 ?4 g/ W* C5 {1 C6 V: X# ?7 P. F
  3.     LCD_SetCursor(x, y);
    $ N8 O5 _3 c: r
  4.     while(*str != '\0')
    ! Z  z. C7 y) E; d" j
  5.     {
      V$ D: P. g6 [0 l1 G+ v
  6.         LCD_WriteData(*str++);! R% R: R) t& |' m  W$ b; `
  7.     }# A, ?: o- d0 L* w$ i
  8. }
复制代码
6 H- k$ W" A- m9 ]9 ~
主函数6 m) x, o% Y7 d' @* z8 Y
  1. int main(void)
    7 t) ~/ A' m  g, e
  2. {
    , `! K! r+ l0 h, L2 n8 S& R/ }
  3.     delay_init(168);
    1 |( P& r: _: W% E2 E7 n% F( ?
  4.     LED_Init();
    # [' l+ B; X' t$ z* W6 ?+ {! U
  5.    
    + M8 [3 M, x5 h- `( A$ p6 W! \# s
  6.     LCD_GPIO_Config();
    % B, v% X8 u  c5 h. W
  7.     LCD_Init();
    , |" J9 t# e* T8 W5 B9 m- C. J
  8.     LCD_ClearScrren();  A) U* w6 J( p
  9.     while(1)+ s: u) F/ h% B7 S# `" o
  10.     {0 Y3 e. P1 c$ H3 \8 P7 p/ R
  11.         delay_ms(500);
    / q2 w6 Z- b. H$ I3 U  g
  12.         LED1_ON;
    9 {3 v* c+ e: F3 H
  13.         LCD_DisString(0,0,"abcdefghijklmnop");
    + I& a* _+ L* }/ K5 {* p
  14.         delay_ms(500);' L  _/ A# f6 {
  15.         LED1_OFF;) }1 A4 T# J: W) m( V! f  ?4 K/ j
  16.         LCD_DisNumber(0,1,56);
    " k( j% q) P  d
  17.         LCD_DisChar(2,1,'a');# D) }3 D4 f6 l1 J9 Z8 I4 t
  18.         LCD_DisString(3,1," Hello World!");' {  o& B. ~  p' @
  19.     }    7 v/ i$ e! J# |
  20. }
复制代码

/ P+ F( s! U2 C( x! m8 Z3 B& }1 O3 S6 H, d0 T
实际显示效果:/ g0 n% v2 O4 ]4 u5 s# ~( T

& \8 _* C; h# \4 b$ s0 a7 g2 Z: g4 n 1124009-20170423140758382-1252243185.jpg
3 g( s2 \& Y* ^, @3 x
' ~6 y5 V& Z! T& ]8 g
显示非常完美,和51驱动没有什么区别

' O( O% O4 s0 O1 Z
' n. m2 e9 D5 f4 `& Z! x1 F; Z! E  O总结:1 U9 N2 W% v+ D' O( p
实际运行时,不使用检测忙函数也行,1602显示正常了,接下来就是增加定时器显示时钟了。在设置IO口模式时,可以将模式设置为开漏模式,就可以实现双向IO的目的,即可输出,又可以读取外部输入。通过位带操作和宏定义就可以像51那样直接操作IO口输出高低电平了。 : G  D4 p4 p. n" [
8 W( g4 L3 V: ?3 A2 c2 B
/ {( T; K+ ~# k$ r* s5 ]

1 ?+ \4 j1 S+ R" M6 f
赞 收藏 评论0 发布时间:2022-2-7 20:41

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版