在色彩识别仪的设计中,主要是通过脉冲计数的方式来获取色彩值,因此对脉冲计数功能的实现是必不可少的。 在STM32L073开发板的基础上,配以LCD5110液晶显示器即可构造一个简单的脉冲计数器。LCD5110与STM32L073的连接如下表所示:
! x7 ?0 z8 F1 l6 S1 T$ P7 Y! L7 ]; r; B
那么外部的计数脉冲如何获得呢?最简单的方法就是使用LCD5110的时钟信号SCLK来提供。 为了实现脉冲计数功能,并将计数值显示到LCD5110上,可在STM32L073RZ-Nucleo例程的TIM_InputCapture 的基础上进行改造来实现,主要的任务是添加LCD5110函数。 # I( u! c$ C. Q
为了控制LCD5110各引脚的高低电平,相关的输出引脚定义如下: - #define SetLCD_SCLK_High() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET);}- E6 S+ k+ g1 z3 U E
- #define SetLCD_SCLK_Low() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);}
2 m" P6 j @" c8 z5 i% M- P
; U) \1 S w; p2 ~* W+ j- #define SetLCD_SDIN_High() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);}
% a: \; E, {' C* Q* r - #define SetLCD_SDIN_Low() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);}
4 @% s. l. O* K# h! N1 u7 B7 g - 6 F" V/ m4 V5 }4 V
- #define SetLCD_DC_High() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_SET);}
) ?+ Y5 }# a4 X0 ]6 | - #define SetLCD_DC_Low() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET);}
! q8 G n! e+ P" i! l- k
$ h. \& f# |& K# d4 m- #define SetLCD_RST_High() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET);}
: h0 F. U& [7 z2 N: S$ r& h4 _7 D - #define SetLCD_RST_Low() { HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET);}
2 a* |$ p. U5 U L - & v/ x$ z) A2 J- x) y
- #define SetS2_High() { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);}5 u. e* ^- M# ]' l; M% n
- #define SetS2_Low() { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);}
. \. I/ I( G% c P" ]) I) V - # e& F' k) A7 s4 R4 O5 I
- #define SetS3_High() { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);}# X% `( Z$ r. T: n
- #define SetS3_Low() { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);}& i& s$ y5 X4 ~
复制代码 LCD5110的初始化函数为:! z, H* M( d. r
- void LCD_Init(void)
) `* a' Q# @4 t* L - {
! K7 N. C1 I, p: L; s! ]- q4 L - //SCLK--PC.3& h8 |0 E& q: N9 z2 U
- //DIN--PC.20 P+ r/ f, ]- G8 T p; z+ c Z
- //DC--PC.13 C6 }7 @2 \# g' W
- //RST--PC.04 s; Q& }9 O: x% R& m5 V1 B
- __HAL_RCC_GPIOC_CLK_ENABLE();1 H4 `& \" i. K
- GPIO_InitTypeDef GPIO_InitStruct; g6 e* }8 H" S
- GPIO_InitStruct.Pin = GPIO_PIN_3| GPIO_PIN_2|GPIO_PIN_1| GPIO_PIN_0;
" c* t; u% x1 T( ` - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;5 ^" l3 h3 w2 T V) [& r; V
- GPIO_InitStruct.Pull = GPIO_PULLUP;
9 V& D+ G* _/ Z* C: ? - GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
/ Z7 T& T7 p5 l - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
3 t3 d9 _; h. G9 T1 f4 j. P. z - , T. b9 y0 e/ |0 ~7 U4 X- C
- SetLCD_RST_Low(); 1 X: i+ }4 a4 f7 C4 X6 M- Q
- delay_1us();0 O' q. n- F: W5 v. l
- SetLCD_RST_High();
: R9 k- P2 Z# Q9 q$ C - delay_1us();3 ~5 F# X, l) a \9 A2 Z$ X" f
- delay_1us();: N/ V) R& `+ d& V# D% @
-
% ]% t3 C( B" z% S' P3 Q) B) F+ H - LCD_write_CMD(0x21);2 _' ^! l- v0 b1 Q X
- LCD_write_CMD(0xc0);7 M3 ~! X% t7 q- q# ]/ j2 Q8 ^5 O# l
- LCD_write_CMD(0x06);
n% d# L/ m6 F, l) n, s, J& ^ - LCD_write_CMD(0x13);
5 G4 E) L+ e8 O! U3 k" I9 @& X - LCD_write_CMD(0x20);
- I e0 P0 V& W; B5 ]+ \ - LCD_Clear();
9 v) M+ R. r- C. ]0 R - LCD_write_CMD(0x0c); . ~6 { {, h2 T. a2 k* p
- }; E6 R0 k- e5 N3 L; u5 O
复制代码 所涉及的函数有延时函数delay_1us()和指令输出函数LCD_write_CMD(),具体代码如下:) L( R7 \: z) u( ]; d9 ]
- long int delay_1us(void)
' A! a+ L/ n+ \3 ^, U" m - {- [' b/ m; [6 P6 h \4 L5 c
- long int count=2;
( {9 i+ l& w5 A8 l - while(count--);" d( |& N7 A, ?& e6 f: K- F' A+ O
- return 0;7 a$ I2 F r/ V5 [0 q; f V9 d
- }
8 t: L6 M+ V+ f- t, M6 M y) H - - Z6 Y& @ |# C# O6 V
- void LCD_write_CMD(unsigned char com)8 t" l) g4 w9 \( \
- {
. g# h: o( e8 O1 K5 S" b; E8 a: ? - unsigned char uci;9 t, X0 Q! p5 ^5 M# r# O
- SetLCD_DC_Low();
" }( B6 ^1 o3 U+ d) l# ? - for(uci=0;uci<8;uci++)
1 o( D- N7 c# S# r: n - {
2 b# R) b, |3 z# \ - if(com & 0x80): R% Z) m0 u7 p( P# r* O; r! n
- {
- J: f$ b9 N: h' x/ H - SetLCD_SDIN_High();
. A3 I; _7 U+ g2 `3 } \ - }* b1 f" w6 x% O* ^& h b
- else
8 k8 A3 h; v F' O - {9 _" L; S; i$ P+ J$ P
- SetLCD_SDIN_Low();
1 V* H( ? J) _# r: ]7 Q. E - }
* M* P3 a {7 u" Z - SetLCD_SCLK_Low(); : r! B/ E" P O5 s/ `
- com = com << 1;6 h) G8 ^ T( ?3 T, \# [ E, K
- delay_1us(); * ?' G" U1 ]' J# l
- SetLCD_SCLK_High();6 E& y9 J6 N' p5 d9 ?, ~: k2 S3 a
- } ' |1 U3 z6 d' q. v. Q# X, g
- }& s9 `0 S+ P: ?. i3 T* H
复制代码在LCD5110显示的过程中,离不开清屏函数LCD_Clear()和字符输出函数LCD_write_char()。 清屏函数用到了显示定位函数LCD_set_XY()和数据输出函数LCD_write_Data(); ,其的代码如下:- void LCD_Clear(void)
" M6 s/ ` g4 }9 r7 A4 y Q - {
3 j7 _# ~/ U$ a% W" x2 g - int uii;
6 {5 Y; H, Z# H; e' F2 Z( C - LCD_set_XY(0,0);5 e: _# W1 B% ~# F2 g
- for(uii=0; uii<504; uii++)
* I% ?* G3 e/ w - {
Y: e* N" ^/ y# u6 m* h* f - LCD_write_Data(0x00);
, B [ `9 Z# X& k1 V, j/ N2 p - }6 m% \/ B$ p1 t1 h* [, P. F
- }
2 i- n [: H% {8 N- B
复制代码 数据输出函数是通过读取字符库数组来显示字符的,其代码如下: k# i7 @& N- p! q
- void LCD_write_char(unsigned char c)2 d0 K7 J5 y' G3 u* {: Y2 G9 P2 u% A# e
- {
- o8 N+ v; A" F/ w - unsigned char line;
- @7 F8 ^4 \7 ]9 z6 X& c" A+ Y - c -= 32;
: D8 y( @0 }3 {7 H - for (line=0; line<6; line++)
2 L0 j! E. |% m1 a - { E! `, c. ]+ V: @2 g
- LCD_write_Data(font6x8[c][line]);
# I* j( t# k6 N) g - }2 m8 I, m: L! w
- }2 S& Q: L- T7 K: h) O* v' b
复制代码 字符库数组的结构如下:
* R% Y' y: b+ Y' @) l2 b- unsigned char font6x8[][6] =% G( f, U2 w8 l# p- j" H0 A B
- {
# N$ r) V* `. U& j6 _ - { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // sp# Y& e9 x/ e7 s1 @9 Z
- { 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 }, // !* F0 ^4 [' Z. G9 X' C6 [
- { 0x00, 0x00, 0x07, 0x00, 0x07, 0x00 }, // "7 M+ D. }5 J& T! G/ w
- { 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14 }, // #( }7 s- o! C% _! ]4 \- S
- ... ...( ~* _* C, K5 q6 C
9 ~: I4 |; @3 c6 [, y- { 0x00, 0x44, 0x28, 0x10, 0x28, 0x44 }, // x
3 t( @$ M E! L! z: P( s+ [2 n& e& N - { 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C }, // y" }# ]! r5 L) L
- { 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44 }, // z
4 }2 k4 b/ k- w0 l* r# \ - { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 } // horiz lines
$ G! |2 E2 w' g6 c5 U - }# o! x. x' w+ j q9 c
复制代码 主函数的代码为:
, h: K a' |! U7 @- int main(void)7 j+ d8 y/ @5 ^# J
- {
. L( U; R6 t0 v) V: M6 f# q - HAL_Init();. i0 x6 n# t y! i
- /* 配置系统时钟到2 MHz */8 p" b- U7 M4 L1 D( L; N3 ?
- SystemClock_Config();
) w7 j% a9 V3 ` - /* 配置LED2 */
# n/ _* z8 t. U - BSP_LED_Init(LED2);
0 x k# g9 v1 ?5 X - /* 配置 TIM 的外围情况 */
0 m' m# q5 @9 Q2 P$ `/ s( X- R - /* TIM2 配置: 输入捕捉模式
: E2 u. p+ u. I - 外部信号连接到 TIM2 的通道2 引脚为PB.03
8 X8 e3 ]% X2 B2 C( p* F7 H - 使用上升沿触发,TIM2 的CCR2 用于 计数的频率值 */
3 M. Y6 q3 f' u9 R - TimHandle.Init.Period = 0xFFFF;* Y+ k6 m& ~- L! X: Z% `% n* n
- TimHandle.Init.Prescaler = 0;* A" E, W7 H) V. y& t# V/ }$ A
- TimHandle.Init.ClockDivision = 0;
5 T/ U% v4 e4 l2 [+ N4 z: S S - TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;: q) {& Q2 ?. U' o2 x1 O
- if(HAL_TIM_IC_Init(&TimHandle) != HAL_OK)0 b+ U; R" V8 L4 `, |1 I
- {5 L8 }7 r* V, l2 \0 s
- /* I初始化报错 */; ?' Y8 ]- h# D8 M' t
- Error_Handler();( F6 {$ b4 ^1 s2 u6 w+ D" r
- }
* u$ }3 ?8 b7 z! a( c) B' Z - /* 设置输入捕捉通道2 */ # }. ?/ l1 h: t% v2 p
- sICConfig.ICPolarity = TIM_ICPOLARITY_RISING;* P7 a& X6 a8 G2 H3 W7 J
- sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
7 T) S2 O `5 c3 E& H. o" w* O. k - sICConfig.ICPrescaler = TIM_ICPSC_DIV1;
! C0 b- V% |$ q1 \7 V$ q - sICConfig.ICFilter = 0; + J' z! m; \; ^7 U) e
- if(HAL_TIM_IC_ConfigChannel(&TimHandle, &sICConfig, TIM_CHANNEL_2) != HAL_OK)
, v! @2 h- G* d - {
6 B0 D0 [. X- D% G- ^ - /* 配置报错 */
8 Q3 _& @0 M- \+ O/ \) \ - Error_Handler();
$ Z6 h# R5 g; A* |" [ - }
# e0 N( |; }$ y' @- G+ A" M - 5 }7 C) U/ d% @& o
- /* 以中断模式启动输入捕捉 */
1 N/ {2 h$ x2 S8 q! F! r - if(HAL_TIM_IC_Start_IT(&TimHandle, TIM_CHANNEL_2) != HAL_OK)
) T- X* q, l4 D. ?2 j - {
2 a9 h/ [; B: t% s* G, c: V6 M0 Z - /* 启动报错 */" d* z$ B# z* u" U% [8 O3 j
- Error_Handler();% p5 x! g$ f3 J
- }
% b8 Z# }# `* O& C+ g - LCD_Init(); // LCD5110初始化
0 y9 i: b- Z8 f5 u. T \7 Y - jm(); // 显示界面3 J8 S8 R* K6 e% b' g$ |2 P6 `
- // 设置显示格式4 {4 M( q) v# {) B; G
- LCD_set_XY(0,1);
; O) `; \, k% c0 i w/ p0 T - LCD_write_char('C');& e2 A6 I3 u3 ^
- LCD_write_char('1');" o1 n& U& j. R0 m1 o* O; l/ m7 ^
- LCD_write_char('=');! F2 i$ ~7 M+ a0 r4 w# T. M
- LCD_set_XY(0,2);
" S" y+ P- E5 v+ `) o$ G$ g7 ?0 Z& U - LCD_write_char('C');0 p6 a8 k g) X( e2 a7 y
- LCD_write_char('2');5 |1 W2 c" o# T$ B/ G
- LCD_write_char('=');9 X) r+ l5 i% ]3 }2 {# m% H
- LCD_set_XY(0,3);$ Y1 z H2 n+ u6 {$ b0 M
- LCD_write_char(' ');
# v, y$ h& H6 @6 B6 A3 c, z4 f - LCD_write_char('d');6 a* A) h8 Z/ X) ]: D$ O B
- LCD_write_char('=');
# x r7 q; ]' U - LCD_set_XY(0,4);
8 g% {$ Z! A" l$ s( G U - LCD_write_char(' ');
' U) c3 x# V1 ~% `. w, H7 \2 x& U - LCD_write_char('f');
4 a) s) x7 s" o2 | - LCD_write_char('=');. `! t% d5 ]. ^/ I$ d
- while(1)
3 ]7 ~- V; R1 P1 a5 @ - { // 显示捕捉值
, G4 C H- V# k/ l- Q1 e; }+ p - LCD_write_number(30,1,uwIC2Value1);
. U3 m; @" l) }- |% D - LCD_write_number(30,2,uwIC2Value2);
7 W. X/ d7 D: I, { - LCD_write_number(30,3,uwDiffCapture);7 R" ^% p" ]0 g7 w! _
- LCD_write_number(30,4,uwFrequency);2 J6 v* L2 K4 y" n4 k4 J
- delay_1ms(1000); // 控制采集数据的显示间隔
3 |7 v$ a* w7 ^) i7 a - }3 n1 g! K' m2 j1 G* P5 \
- }0 l. I; S: W" E! N. d7 \# x
复制代码 定时捕捉处理函数代码为:4 l4 D1 }4 V, y1 a$ U) C
- void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)" y- D7 y) N/ K, n
- {$ R2 }9 W! L6 d8 S; S. G
- if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
' a; l3 f4 { J: {% l! ~4 K0 Y2 e+ F - {6 B I% x$ D3 @/ {& e r2 O. Z/ }9 S3 O
- if(uhCaptureIndex == 0)4 z! X4 d0 ~, l3 }$ a- E l
- {' K0 b2 ]/ T" d& Z
- /* 获取第一次捕捉值 */
9 C$ ^* z4 x' z1 I- k! m - uwIC2Value1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);+ Y2 t t! G* p7 E: m6 Y, b+ C
- uhCaptureIndex = 1;4 {5 H Q6 k* L% ?
- }
) e& ?( u1 j8 ]9 i6 y7 Z. t5 u - else if(uhCaptureIndex == 1)+ [9 w9 M2 g4 u# T& _0 n! g
- {
q$ A ]. Q% w% T( C - /* 获取第二次捕捉值 */+ u$ T* @$ }& P& O) N [
- uwIC2Value2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
! O3 c: I9 H6 M# q6 Y# T, } - /* 捕捉计算 */
! A" |+ d9 A/ q- G- D) b - if (uwIC2Value2 > uwIC2Value1)
- n9 t# s2 B' k: e+ m - {
5 _) s% D0 g. ^& q - uwDiffCapture = (uwIC2Value2 - uwIC2Value1); // 计算差值 ) l& w D; X6 T
- }: u$ n( ]$ h( M b. \
- else if (uwIC2Value2 < uwIC2Value1)* \5 n; f* X* P( s7 @
- {
$ f2 b/ p1 w% Y Y - uwDiffCapture = ((0xFFFF - uwIC2Value1) + uwIC2Value2) + 1; // 超界补偿处理, J% H9 P( D! O# i0 R0 Q- L' X
- }; v1 _$ `2 g, W) x. c
- else2 Y9 H, l1 [' _% |# G
- {4 X9 U9 x$ Q, D' _# Y d0 a# g
- /* 假如捕捉值相等,则已达到测量频率的极限 */
|8 m2 ^+ z: N: L: G+ \" I - Error_Handler(); // 捕捉值相等时报错6 C( F9 x) f4 n# @& L3 k" C% J
- }
& A) x9 [" A& l: A; W7 e - /* 频率计算: 这是一个以APB1Clk为TIM2计时的示例 */ Q0 u: v/ [% B5 L1 T- v
- uwFrequency = HAL_RCC_GetPCLK1Freq() / uwDiffCapture;
3 d0 U) R: J1 h6 R5 ? - uhCaptureIndex = 0;
+ [$ ` l0 I1 a* \ - }2 n" F2 V, Z# k5 Z/ H0 q; m
- }0 b9 Y! N- z3 y- i
- }5 O6 K/ h. {2 [( M; C* j
复制代码 数据定位显示函数的代码为:
6 g8 q4 s E1 p! Z- G0 b1 o2 N- void LCD_write_number(unsigned char X,unsigned char Y,uint32_t dat)2 w) }, b/ Q! y6 `
- {
) H5 ?& d; P2 T, a - LCD_set_XY(X,Y);& R8 O$ }- @- f4 c% U" i
- LCD_write_char('C');8 b# F) E" N/ M! E+ {# I
- LCD_write_char('=');5 C1 g2 W' x7 v3 r5 w5 {2 {* L/ M
- if(dat>999999) LCD_write_char(dat%10000000/1000000+0x30);; t2 ]1 s7 t2 Y7 M0 j5 }
- if(dat>99999) LCD_write_char(dat%1000000/100000+0x30);6 f J$ @, X) @% C
- if(dat>9999) LCD_write_char(dat%100000/10000+0x30);
4 d/ C; G# L& X, i4 Z - if(dat>999) LCD_write_char(dat%10000/1000+0x30);
) M" a+ U4 a0 W7 f2 i- z. b - if(dat>99) LCD_write_char(dat%1000/100+0x30);
7 F9 O) a [' M' C5 ?& q ] - if(dat>9) LCD_write_char(dat%100/10+0x30);
* p% Q8 H6 E5 W* l - LCD_write_char(dat%10+0x30);
8 [; F1 I, Y4 C. o - }
3 N. y: D4 F) ?$ Y4 J) T
复制代码
% f- [; o4 K, f9 D
6 H5 ]. p% U$ `3 I0 E: H- g5 `) D |
你代码放到STM32L073的相关例程中进行编译即可,我做的主要工作就是为它配上了LCD5110屏的驱动显示等,你试一试就知道了。
我已经下到了官网的例程,谢谢你