|
前言 从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
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
( ~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
- #define LCD_RS PAout(1)
- w9 v. [ a) c+ N8 `8 G - #define LCD_RW PAout(4)" Z; \! ~8 A5 t) p
- #define LCD_EN PAout(6)
3 _+ T/ U# t7 n# w5 Z ]& K: {$ H- K - #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- void LCD_GPIO_Config(void)9 w, c, }/ O/ l, E, K
- {
6 {- ?; N" K8 Z - GPIO_InitTypeDef IO_Init;
9 e$ n5 ?* m1 w n - ) A: y# f2 C# G' O3 y/ P
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOE,ENABLE);
) C! H1 G w5 I" J( L1 `% Z -
9 b* g/ W( G9 N - /*控制线初始化 : */
3 V, G" l$ Y6 w - IO_Init.GPIO_Mode=GPIO_Mode_OUT; //输出
, p0 w- D% X' [" c, G4 C8 A - IO_Init.GPIO_OType=GPIO_OType_PP; //推挽模式
* d2 t/ @" p- d) C - IO_Init.GPIO_Pin=GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_6; , Q! g* V s) T, C: u0 F
- IO_Init.GPIO_PuPd=GPIO_PuPd_UP; //7 W$ d3 `# w4 F. X
- IO_Init.GPIO_Speed=GPIO_Speed_2MHz; //GPIO_Speed_2MHz* I' K8 Y( P0 ~2 ^# `& i6 v* R6 _$ J
- GPIO_Init(GPIOA,&IO_Init); ]( X" P$ b. F
-
! z2 U8 V( k4 I. Z; r - /*数据线初始化*/
, q/ [( ~! Q- t - IO_Init.GPIO_Mode=GPIO_Mode_OUT;
5 c4 M2 C: @+ {% A! n; i0 O$ V - IO_Init.GPIO_OType=GPIO_OType_OD; //开漏输出可双向+ Z: B+ Z9 \6 |1 j3 l9 E- H+ h
- IO_Init.GPIO_Pin=LCD_DB;
3 N# k! _' ?3 b& W% L: B: _0 m2 Q - IO_Init.GPIO_PuPd=GPIO_PuPd_UP; //上拉/ G6 Q9 m( z* ~3 v0 c; v7 b) L; Q
- IO_Init.GPIO_Speed=GPIO_Speed_2MHz;
; H, ~) c' B, d! J% w) m* g - GPIO_Init(GPIOE,&IO_Init); ( s2 {0 h4 ?, H1 h9 s: P1 }
- /*测试 : 高电平3.3v& `0 A7 c3 v3 G& ]- ?9 A+ o3 d4 g
- LCD_RS=1;7 R2 A7 R4 M/ i' E9 p$ o2 y( F
- LCD_RW=1;, H, L# o0 R1 L1 u. l
- LCD_EN=1;
* r% b3 v n- k3 h7 s6 S5 T - GPIO_SetBits(GPIOE,LCD_DB); */2 ], I7 J" L, t
- }
复制代码 9 X' D' Z9 R \" n
2 @; C. F7 t+ \
3.实现P1=0xff功能的函数,带参数
0 F* p; L/ Y, P; D- void GPIO_OutData(u8 Dat), B: c0 L$ A( g
- {4 ~; a' m- g2 f; K7 a# S
- u16 tmp;4 F# V" a0 d6 d: A* n X
- tmp = 0;; R- o$ B! k2 W6 w
- tmp =Dat;
. v$ s" Z* x4 E0 S7 j5 t+ S - tmp <<= 8; //数据左移到高8位
, h0 g0 m8 g( J" |0 D - GPIO_Write(GPIOE,tmp); //数据写入到GPIOE高8位
$ J, i; ]. |6 j - }
复制代码
6 ^5 k# R8 S" }' I, j+ k1 Z4.检测LCD是否忙
: m% Q5 P" p% j1 S3 V3 b- H% Q, p- void LCD_CheckBusy(void)
6 H* X* f) y/ h" ^ - {
4 m3 {/ y* B2 r1 R6 c% f& f - u8 sta;7 N0 U( W$ [1 M) D1 o
- GPIO_OutData(0xff);
! W: e9 n* m7 e9 Y1 `0 b6 I - LCD_RS=0;1 j8 V$ m; U' i1 b
- LCD_RW=1;
% M- t( S- ]; l6 f4 \- W - do{& q* Q. K% ]) s! f5 h
- LCD_EN=1;
1 Y T/ x. }$ k7 k$ X6 b- ^8 i: Z - delay_ms(5);# G& W' S# X6 ~6 }8 A3 `4 C
- sta = GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_15);
$ C4 C2 \5 p# U: {! O - LCD_EN =0;3 S- Y+ s, s9 y% c1 S
- }while(sta & 0x80);, M: ?( \ x+ \' r9 B
- }
复制代码
( N% N4 `3 I+ c% d" C9 D- g1 J! \3 P9 z' t
5.向LCD写一字节数据! I4 C, Z& k6 @9 d \1 c
- /* LCD_RS = 1, LCD_RW = 0 */3 w/ L1 n, a+ m
- void LCD_WriteData(u8 Dat)
; d4 @9 |. k6 Q. \1 ]. K! ^' } - {8 a- S0 \% h% G3 @2 ?
. H8 @" R+ p; {- LCD_CheckBusy(); //忙则等待3 Q. l3 U, S7 u2 f2 n& i
- LCD_RS=1;* Y6 @) }) E; I5 n( `. \: q T! C
- LCD_RW=0;
9 Z# q4 z# D+ D9 n- S& G/ p0 j- @ - GPIO_OutData(Dat);
$ ]3 e4 P8 ^6 A( X - LCD_EN=1;
, d4 ~; H$ X/ W2 r - delay_ms(1);
% [; Y; L/ m j - LCD_EN = 0;
+ `$ C4 n F# _ - 2 e" P; I. O& G. D& {
- }
复制代码 : V7 ^& P( b1 F6 l
6.向LCD写一字节命令. d1 x3 i7 e3 }% a6 R
- /*LCD_RS = 0, LCD_RW = 0*/
3 v& Z( c4 r+ N8 Q. m( y5 Y - void LCD_WriteCmd(u8 Cmd)+ X# H8 [' D5 o2 ?4 P4 S
- {
1 T! l5 z8 h3 W% S J6 w" a - LCD_CheckBusy(); //忙则等待/ |% ]2 ~1 ?$ ]6 ?# K
- LCD_RS = 0;) z$ a$ U S+ X4 Y. \1 r
- LCD_RW = 0;
! J: i7 t/ ?9 ?. [' V5 {1 C- u - GPIO_OutData(Cmd); . t( I& V; Q8 x, {( R
- LCD_EN = 1;/ t( m, f m) I) E# \- l
- delay_ms(1);
0 }& V/ U) j1 C0 k - LCD_EN = 0; * W6 P- R% H4 ^; `/ b
- }
复制代码 9 O7 g$ Q( {$ O6 M3 N2 Q
7.LCD初始化& u8 @$ S; \& i. U
- void LCD_Init(void)
/ Z' u4 j N. k4 R. a5 t3 u2 [ u - {& A% r+ I2 M' u: G2 j& o# G ~% c
- LCD_WriteCmd(0x38);1 D% ?2 i1 @+ k) e* y( O) {/ u
- LCD_WriteCmd(0x0C);
3 K% c) `1 | B: F0 p+ } - LCD_WriteCmd(0x06); /*显示光标移动设置*/ delay_ms(1);% d9 [( B7 g! E" G, \
- LCD_WriteCmd(0x01); /*显示清屏*/
* N& D# Q; Y" s" b& I% J - }
复制代码
! V c. C0 Y% v- i N8.LCD清屏
$ C$ L5 b. j9 \5 n- <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- void LCD_SetCursor(u8 x, u8 y)
8 v/ v2 w1 ?6 @. W% P( ~ - {+ e/ Y# [& u' m5 s+ F
- u8 addr;
' z$ k; a4 n' R0 v0 ` - if (y == 0)$ A: I* [; E5 k& N- C
- addr = 0x00 + x;
4 x7 a8 b' b: U8 t+ _6 {/ ]- ~ T - else4 b% G# [. K( H5 o
- addr = 0x40 + x;1 y4 d! u3 m8 ]8 K9 @9 j5 w; f
- LCD_WriteCmd(addr | 0x80);6 E' h, B! @: S, u. M/ h7 Y9 W1 [
- }
复制代码
2 X8 L7 d8 c: d$ U8 { 10.根据xy坐标显示一个字符2 }' g P: `: ?0 Z- }( b
- void LCD_DisChar(u8 x,u8 y,u8 ch)( v. n3 K' ?& W/ n
- {
) P1 g5 i- j& G* v- V, e. ~ - LCD_SetCursor(x,y); //字符显示位置设定
& |/ d, v) V+ z3 M - LCD_WriteData(ch);4 t' L6 ~) y; Z$ k, J( q
- }
复制代码
& W% o% z- B- E* O; v) N11.显示两位的数字- X7 |- T0 Z! @
- void LCD_DisNumber(u8 x,u8 y,u8 Num)
8 c5 }4 O# ?6 X0 D1 y. d: u - {, z C7 T& Q4 s0 m
- LCD_SetCursor(x,y);
2 v9 b% d0 R# q' P0 a& m - LCD_WriteData(0x30+Num/10);
3 E9 H5 {& g- G- l) K( Q - LCD_SetCursor(x+1,y);
; h3 [: D$ z6 r; [2 _ - LCD_WriteData(0x30+Num%10);
- f- b; P2 a N3 o1 l9 I7 y - }
复制代码
- 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
- void LCD_DisString(u8 x,u8 y,u8 *str)# J7 A/ t+ s* i
- {9 J2 ?4 g/ W* C5 {1 C6 V: X# ?7 P. F
- LCD_SetCursor(x, y);
$ N8 O5 _3 c: r - while(*str != '\0')
! Z z. C7 y) E; d" j - {
V$ D: P. g6 [0 l1 G+ v - LCD_WriteData(*str++);! R% R: R) t& |' m W$ b; `
- }# A, ?: o- d0 L* w$ i
- }
复制代码 6 H- k$ W" A- m9 ]9 ~
主函数6 m) x, o% Y7 d' @* z8 Y
- int main(void)
7 t) ~/ A' m g, e - {
, `! K! r+ l0 h, L2 n8 S& R/ } - delay_init(168);
1 |( P& r: _: W% E2 E7 n% F( ? - LED_Init();
# [' l+ B; X' t$ z* W6 ?+ {! U -
+ M8 [3 M, x5 h- `( A$ p6 W! \# s - LCD_GPIO_Config();
% B, v% X8 u c5 h. W - LCD_Init();
, |" J9 t# e* T8 W5 B9 m- C. J - LCD_ClearScrren(); A) U* w6 J( p
- while(1)+ s: u) F/ h% B7 S# `" o
- {0 Y3 e. P1 c$ H3 \8 P7 p/ R
- delay_ms(500);
/ q2 w6 Z- b. H$ I3 U g - LED1_ON;
9 {3 v* c+ e: F3 H - LCD_DisString(0,0,"abcdefghijklmnop");
+ I& a* _+ L* }/ K5 {* p - delay_ms(500);' L _/ A# f6 {
- LED1_OFF;) }1 A4 T# J: W) m( V! f ?4 K/ j
- LCD_DisNumber(0,1,56);
" k( j% q) P d - LCD_DisChar(2,1,'a');# D) }3 D4 f6 l1 J9 Z8 I4 t
- LCD_DisString(3,1," Hello World!");' { o& B. ~ p' @
- } 7 v/ i$ e! J# |
- }
复制代码
/ 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
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 |