串口通信(Serial Communications)是指外设与计算机间,通过数据线按位进行传输数据的一种通讯方式。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。虽然串口通信传输速度不高,但程序简单,能实现远距离通信且成本较低,通信长度可达1200米。常用的仪器仪表大多都支持串口通信协议。
5 y3 x3 c) S( C' J# O; b$ pLabVIEW的自带函数库中有现成的串口通信模块,方便快速搭建堪比串口调试助手的软件。
1 K) B4 f9 m8 `4 V% H% s w+ u
7 G/ \) Q/ @7 s8 p
. T& C5 v) T! l5 ?1 @, \( o2 v今天分享一个STM32F103C8T6工控板与LabVIEW的串口通讯实例,主要工作如下:
0 Q; s( D. `, u1 F. x% }8 w1)基于Keil MDK写一个串口通信程序,主要配置STM32F103C8T6芯片的USART1相关参数并创建串口1中断服务函数(对应引脚为PA9和PA10,可在手册中看到,如下图所示);
, ^+ ]: i, I7 @* k6 S1 x [; O6 n
9 M" Z, M# p) J/ f9 E
9 X/ C# V, M& B& l9 i
) `6 h2 |- z2 Y/ v2)基于LabVIEW编写一个串口调试助手,用于与STM32F103C8T6工控板进行实时通信,具体使用的串口通信模块位于程序框图的函数选板–>Instrument I/O -->Serial里,如下图所示。9 T) ?) Y7 N$ F& y9 s
! d* M2 U+ g7 F; c$ X
4 q, J2 f# r; A( }
1 ]2 e. [7 L& K3)具体实现的功能为:从LabVIEW发送一个命令到STM32F103C8T6工控板中,然后STM32F103C8T6工控板不进行任何处理,将接收到的命令反馈给LabVIEW。; o9 b5 {3 b- T! v6 ^
) t& N _' m& L7 R: x- L% F3 @& ]0 C硬件部分
, _, z- n- ?& s) k2 ?0 d( ]2 Z1)某宝网上购买的STM32F103C8T6工控板,价格50¥左右;
" d' G R/ J" x2 d! P$ ?9 m2)某宝网上购买的232转USB数据线(如下图所示),价格15¥左右。
: W4 N- V, g7 R3 c2 }
- ?& A% G8 j9 L4 m( |- z- h
. v* W% V4 J* |
3 v. v9 O- p! T5 j) v {( M" Y0 _
STM32F103C8T6工控板部分原理图
+ V }' N2 [; s1)下图是STM32F103C8T6工控板的芯片接线,PA9和PA10对应USART1_TX和USART1_RX,PA9和PA10经SP3232EEN-L/TR芯片至9pin的接口处,如下图所示。8 k( t4 e% l2 h- @, g& b5 I- m# \* i
2 K& R4 I5 g: ]$ E* A& Z9 i' T, x y
- h/ |( z( c, ]3 S! _% h! h
" [9 O0 ]* }6 h
思路
0 h' n2 }) _' S7 M& N% d3 O3 x3 o1)对于STM32F103C8T6,配置并使能USART1,使能USART1更新中断并创建相关中断服务函数;
/ k1 R7 _$ g. N; |, }8 L1 w9 Y2 Q2)对于STM32F103C8T6,在接收到从LabVIEW发来的串口数据后,运行中断服务函数,将接收到的数据传回LabVIEW;( W/ X+ ] _8 ^" O5 A/ p3 ?4 z/ M
3)对于LabVIEW,配置串口通信,相关参数(如波特率、校验位、停止位等必须与STM32F103C8T6的USART1配置的参数一样!!!( W" B5 Z5 I, G/ {/ b
4)对于LabVIEW,利用事件结构的while循环,每当发送命令的控件发送一次串口数据时,并接收一次串口数据。
1 ]* l& `9 i* F/ F- X! G. ?8 J- F7 P( ^5 [, Y. {+ f j
相关代码
' F" Y# q$ ? s8 U- k4 K+ P, u G1)针对STM32F103C8T6工控板
. h5 }+ y5 K$ y9 N h* U1.串口初始化配置函数(头文件)4 N. B! {+ ~5 X6 x( z: V
该头文件只包含一个函数,即针对串口1的初始化配置函数,用于跟电脑进行串口通信:) v: ~# k \4 O% S
- /**
- I# |9 ^ x# Y9 ` - ******************************** STM32F10x *********************************
- g# H7 ] l z" d/ `# l - * @文件名称: serial_communication.h, a: g7 G8 g0 |& G
- * @作者名称: 闲人Ne+ T$ H5 m5 D; Z
- * @库版本号: V3.5.03 Z0 Q2 a1 z& o" D
- * @工程版本: V1.0.0& h+ P( u0 g$ m% e! f
- * @开发日期: 2021年1月17日
5 Y3 Q$ R) v- n2 j1 Y - * @摘要简述: serial_communication头文件
* D8 ^3 C% N4 m! i. W' m1 y - ******************************************************************************/
* b, G8 a+ {! a% B. J d( Z/ Y - /*----------------------------------------------------------------------------
# O; q$ m- V3 B+ Z1 n H! P6 }; G& u - * @更新日志:
! {, {, a4 a3 T, @ S4 h% N - * @无& f0 s- l' C1 H/ H1 Y: V. E$ _: z
- * ---------------------------------------------------------------------------*/0 _7 z; Z9 M" M9 u; Y
- #ifndef __SERIAL_COMMUNICATION_H- c1 q" Q* j, ~! Z- z
- #define __SERIAL_COMMUNICATION_H' K/ Q+ l5 c2 w( D: t% ^/ z; C
- /* 函数申明 ------------------------------------------------------------------*/
) g z+ d* M0 \5 N* _4 b - void My_USART1_Init(void);
! s v; ?( [0 Y& M - #endif /* __SERIAL_COMMUNICATION_H */
9 n O& f& Y4 ^# V - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
9 L) i! t# d7 q4 p% F* K2.串口初始化配置函数(源文件)$ v( R* |0 J9 t- ]2 f2 s3 k
串口初始化配置常规操作,大家可作为范例参考:! r' w! J. e6 c) f! N' U! t
- /**
' ?" `3 N7 U! E% d5 r' m! P - ******************************** STM32F10x *********************************
2 p6 u! s$ G9 ^# m. d - * @文件名称: serial_commuication.c/ G* R; Z, J6 [8 Q, ?2 ^$ r
- * @作者名称: 闲人Ne
u7 ]! X. P# _ - * @库版本号: V3.5.0
2 P7 D- V$ K, ]) e - * @工程版本: V1.0.0$ [- |0 `+ d" b* U
- * @开发日期: 2021年1月17日2 j) H3 U' W2 v( S* x2 M
- * @摘要简述: serial_communication源文件
/ N4 z! \; P! M; G' h - ******************************************************************************// r7 ]4 M- B( [! }- \
- /*-----------------------------------------------------------------------------
\" l7 Q8 T! w0 I' I9 y" ` - * @更新日志:& T$ h% {4 z, l( i: n8 O
- * @无
2 {- q/ N4 F6 D. x - * ---------------------------------------------------------------------------*/
7 Y1 a0 G \+ J3 C2 \ - /* 包含的头文件 ---------------------------------------------------------------*/
' w5 A1 F5 [5 A. \% z& G0 x - #include "serial_communication.h"
; [$ ^4 j# _! F q2 D/ C - #include "nvic_configuration.h"
+ D, a' f. l3 H- { - #include "stm32f10x.h"
0 S1 ~) @% ]; ]" ~$ V1 x! @ - /************************************************
' X" {: c! ?4 N7 ]3 ^. }. V0 y- @ - 函数名称:My_USART1_Init()) D9 k8 @& j1 M* T' _+ C
- 函数功能:初始化USART1管脚配置,
7 S5 x. F: Z+ k - 入口参数:无
- M1 I0 Y X, e( z% k8 L - 返回参数:无
+ v. J" e; X+ b - 开发作者:闲人Ne
$ C( `% v' _( n: r$ W - *************************************************/" b1 c7 `" o( A6 Z4 B* J4 g
- void My_USART1_Init(void)9 w& p1 H1 G* P; U0 Y( J, R
- {
$ s3 N; L2 O' r3 [ - GPIO_InitTypeDef GPIO_InitStruct;
* |2 l: S' |$ Y0 h" t9 m. g - USART_InitTypeDef USART_InitStruct;
4 n; V0 e3 ^) W0 l* Y - // 第一步,使能相关时钟
/ \3 r" B, ]8 c( Q2 w+ T - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟# s/ v6 E) X' m9 l0 H, q
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1时钟% v0 L7 y1 c0 W9 ^6 }0 X
- // 第二步,串口复位
9 k* b7 U# @& l: p; F - USART_DeInit(USART1); // 将外设 USARTx寄存器重设为缺省值,复位USART1
1 J8 u( ~3 M4 A - // 第三步,GPIO端口模式设置,PA9对应TX,PA10对应RX4 g( x: J" i+ ~) q
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出1 ~5 f: p$ E9 n+ ]
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; // 选择GPIO引脚9,作为发送端. _! o @ L7 y5 j6 P. P! p
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz; // 随意设置,不重要
4 ?7 O* S1 E: ~$ G4 ? - GPIO_Init(GPIOA,&GPIO_InitStruct);
. ^4 u/ O5 p% R% }$ z& W - GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; // 浮空输入/ P. m, A/ A# h0 c
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; // 选择GPIO引脚10,作为接收端
: t+ w8 f9 @( K" ]2 x - GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz; // 随意设置,不重要
) G9 H# r" I" J' `# z- B/ c - GPIO_Init(GPIOA,&GPIO_InitStruct); % }7 B; N2 _8 t+ h G
- // 第四步,串口1参数初始化,在电脑上进行串口通信时,串口调试软件也需按下述参数设置
7 I" D) Z$ G/ [0 o7 l2 W& D - USART_InitStruct.USART_BaudRate=115200; // 波特率为115200
3 I1 s+ k8 _5 s& p; B- [: a, {) q - USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; // 不使用硬件流控制
" F% p: @6 D9 Q% ` - USART_InitStruct.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; // 收发都使能
( a2 M8 B+ ^9 i4 I1 T5 M - USART_InitStruct.USART_Parity=USART_Parity_No; // 不用奇偶校验位 ! Z! i0 `, q" o) A1 O( P
- USART_InitStruct.USART_StopBits=USART_StopBits_1; // 设置1个停止位
' K3 J6 ]) {# I, N9 D - USART_InitStruct.USART_WordLength=USART_WordLength_8b; // 设字长为8,因为不用奇偶校验
6 W9 I1 b, ]4 c6 t' g - USART_Init(USART1,&USART_InitStruct); 4 W- o3 k8 {4 n7 d; V% K
- // 第五步,初始化NVIC,开启指定中断
* }! R4 Z8 C+ i - NVIC_Configuration();8 X/ r( _2 b5 `0 D& m/ w. U3 j, c
- USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); // 使能USART1的USART_IT_RXNE中断,RXNE是状态寄存器USART_SR的第5位,意思是接收中断
' ]4 g3 z4 G4 _; v1 `+ @5 t - // 第六步,使能串口 7 m! v6 W u5 Q" O) n
- USART_Cmd(USART1,ENABLE); // 使能USART1外设
! ~5 c* p: Y5 E3 j/ [$ z# ~8 G - }' y( Y( v6 K0 P7 k2 s
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
. q! F- w/ {# ?7 k0 V9 V3.串口1中断服务函数
d- ^; L" F9 x$ b+ y通过上面可知,在串口1初始化配置时,使能了串口1的接收中断,因此需要编写对应的中断服务函数。
% p% D0 s8 w" Q- /**********************************************************************************
; v7 o7 f1 W# U- Q; r' e8 Z z5 } - 函数名称:USART1_IRQHandler(); B( @7 J' u5 b8 K. c2 p1 ^$ b
- 函数功能:USART1中断,当STM32F103C8T6通过串口1接收到从电脑发来的串口数据时,将所收到的数据再发回去。! F! x6 U+ q5 c
- 入口参数:无
1 B9 Q) t) ^4 b( v - 返回参数:无- ~# \+ ?9 c" M% b
- 开发作者:闲人Ne
- w N: v& V- |+ m, ^! m - ***********************************************************************************/
+ T8 ?5 j; d& A# w$ S$ X- H$ S - void USART1_IRQHandler(void) // 函数名称是由startup_stm32f10x_hs.s文件里定义的
$ Z" J0 l) i v6 R: f - { ]4 j% E6 `* G' X
- u8 data;1 W. }% y4 S" U$ t( P- `- n! `4 A
- if(USART_GetITStatus(USART1,USART_IT_RXNE)) // 检查USART1的接受中断USART_IT_RXNE发生与否 9 ]$ U& f `7 J: f
- {' f5 ~% m! z1 P# e6 P; j' x
- data=USART_ReceiveData(USART1); // 返回USART1最近接收到的数据
j0 D! H+ f: v; [1 V - USART_SendData(USART1,data); // 通过外设USART1发送单个数据
) {: w* U' u) D3 E8 g - }
5 \5 t, a' G0 v, ^+ N. x - }
复制代码
( @" f& k8 b& H' _( P: A# d4.中断优先级配置+ G W% p. D) k2 a! C
既然有中断服务函数,那么必须先进行中断优先级配置:
3 T$ J" x7 X2 S- m( e5 |- /************************************************
! Y$ L0 j+ o; e7 b2 E x - 函数名称:NVIC_Configuration()
& Z. g5 G2 `1 I% C* q9 B9 S4 F - 函数功能:中断优先级配置
, ?: d5 s+ N* i. [0 S - 入口参数:无
8 T' F! H$ C3 V) o& _9 j( H/ G* I# ~ - 返回参数:无
8 y' m9 L# t; L6 i - 开发作者:闲人Ne2 J. {& H) V- d: ~$ y. F, s/ ^" ], N
- *************************************************/
. X1 `$ G' f. @5 j; L7 X - void NVIC_Configuration(void)! X; O; r2 b0 g- b
- {
9 V5 _' ~5 z5 g, K( \! }; Y0 \ - NVIC_InitTypeDef NVIC_InitStruct;
2 C6 l% j5 K$ s# K3 _+ Y \ - // 串口1中断优先级配置
: r% x+ |' x% t - NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn; // 选择位于stm32f10x.h文件中STM32F10X_HD中的USART1_IRQn, u s+ s4 l$ `5 @8 R
- NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; // 使能上述中断通道6 N' G; W) M% s( @ N7 N
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; // 因为没有别的中断,参数可设0~3之间
$ U4 i$ T# v1 x; M - NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; // 因为没有别的中断,参数可设0~3之间
0 i2 ~: T' r! x; C$ d - NVIC_Init(&NVIC_InitStruct);
' r0 C7 } y5 c( n/ {$ M - }
复制代码 & Y' e" g6 q, s' L5 d
5.主函数
. U# p Q9 i+ A, P" A6 W$ U- /**
$ q9 o1 T( b! }$ g2 I - ******************************** STM32F10x *********************************$ N! j/ Y9 p; a& e3 E5 I' }
- * @文件名称: main.c
. {5 i, C' [+ e# K - * @作者名称: 闲人Ne
5 C/ C, q/ g, M$ ?- \7 \: b - * @库版本号: V3.5.0
) P/ t* O) z# G- @ - * @工程版本: V1.0.0, v2 f" g1 x0 r' Y' d* j" t
- * @开发日期: 2021年1月17日% g1 O6 ~ y+ }
- * @摘要简述: 主函数
" u/ I% x9 i3 `( ^' B8 M( H& ` - ******************************************************************************/* g# e4 w5 ?3 W& B8 y$ y
- /*-----------------------------------------------------------------------------) P- A" i6 E+ _: i+ m
- * @更新日志:
7 {! N- V5 O# y; O - * @无; M' T: ]; \5 K* t' q
- * ---------------------------------------------------------------------------*/
$ }2 B/ m8 p; |7 u - /* 包含的头文件 --------------------------------------------------------------*/
( X O1 b r% t0 T" W - #include "led.h"
- o) y9 {7 s- z! O1 G - #include "sys.h"
; L5 O- s8 g @) q$ \# q: Z - #include "delay.h"! ~, h7 @9 d) @* x, M1 Q
- #include "serial_communication.h"
6 R8 w* w3 j2 S& @% \ - #include "nvic_configuration.h"% A. n! `; s o( I
- /*********************************************************************************6 i! F+ o4 T6 x
- 函数名称:int main()
0 h o- R- J# _3 o. \* z6 ^) {+ h - 函数功能:主函数,LED灯每隔500ms改变一次状态,表示系统正常运行,串口数据收发由中断控制
2 V X7 ^' Q# x6 z* o( K6 r - 入口参数:无
}# X9 {0 H7 [8 \& u, ~ - 返回参数:int
" D4 `7 o- w1 r k& [& s - 开发作者:闲人Ne
+ `2 e/ o x7 [1 Q# Q; n - **********************************************************************************/: w- e1 k+ [" o* F' a
- int main(void)
) B) }1 A% v' W3 |# M3 ? - {" [) X0 w2 ^( i3 s& k9 I
- delay_init();1 E- Y c- S* \0 _
- LED_Init();: \, D: z7 G, C+ T# E/ G" P( ^3 h
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级配置
) q' P) Z- L1 G: N# n - My_USART1_Init();! o" Y4 w# A7 j$ V' q" H
- D1=1; // LED1灭8 y. v+ T4 \* m
- D2=0; // LED2亮
1 C6 Y: Q0 k5 R" [, ]# C - while(1)
P, V$ c6 E' z0 T1 X' W1 d, a - {, a- N% s: D* h& A
- D1=!D1; [/ [. V# Q# T5 }% [
- D2=!D2;
2 g; J1 F- v5 a4 e6 p - delay_ms(500);4 P+ p' D7 Y B5 G) G
- }$ O; M4 C1 v' j7 n/ a# p
- }
$ P& P, ^* ]9 ]. j/ m- c - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 & e6 F% S% T8 W1 U7 }# Z6 y" P
2)针对LabVIEW上位机
+ D$ E2 e9 {* ?9 h; b0 w+ m" xLabVIEW工程可参考范例:Continuous Serial Write and Read.vi,该范例是最简的连续串口通信范例,在程序框图钟进行简单的修改:即在最后的While循环里添加等待1000ms模块(如下图所示)
+ ^" c! ?/ D4 d) Q$ C
% d" Y: a# Y, `; O, f6 M- Q1 J
* F1 R8 `; K6 O6 B( i
/ Z+ \: h! X+ L0 N- D% x# u3 F回到该范例的前面板,硬件连接好后,首先先选择com端口(如下图所示:COM21),然后按照之前在STM32F103C8T6工控板上的串口配置参数进行配置,如波特率设为115200等,其他参数默认即可。
" I# D, v3 I/ X7 \( W1 F2 q. m3 s O1 V! L5 Q
/ O; N5 p4 c+ p1 M3 I1 J* Q! {3 F5 p. ?( b
实验效果
+ U$ N: `" h" [8 g运行前,将LabVIEW前面板上的Write和Read控件设为真(即激活发送和接收功能),先烧入STM32F103C8T6工控板的程序并运行,然后运行LabVIEW程序,运行结果如下图所示:4 X& V( O4 j; X1 y
7 z. ~& {* i S: K/ H1 y) _
~- c9 x4 u7 X- R8 o6 G. d" ?% c; ~
当Response显示字符串的控件每隔1000毫秒接收到一次串口数据时,则证明串口通信成功。# Y) b2 n! r$ q; f+ u
( m" b! Y! |6 I4 p7 q' N8 k经验分享
* b7 O" F$ J5 T2 [; }2 [0 A1.在进行嵌入式开发时,利用仿真器Debug程序是首选方案,当然利用串口调试助手查程序的问题也是工程师比较喜欢的手段;
7 o ]5 n( e+ {5 X4 F& w2.可以利用LabVIEW,串口通信为一些嵌入式开发板开发定制化的上位机软件:如开发板的功能是采集一些实验数据,利用LabVIEW为期开发一个上位机软件,通过串口通信获取开发板采集的数据,然后记录,生成报告等。这对于实验室一些非标测试的项目,开发的成本是很亲民的~+ B" k# s; ?5 F' A" ~
————————————————
! k* q2 H, `, o( a0 J% H* `版权声明:天亮继续睡
( a5 L: D, }! R
a5 d- }/ ?0 u; S
6 x7 e/ z9 ?& H y1 V |