串口通信(Serial Communications)是指外设与计算机间,通过数据线按位进行传输数据的一种通讯方式。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。虽然串口通信传输速度不高,但程序简单,能实现远距离通信且成本较低,通信长度可达1200米。常用的仪器仪表大多都支持串口通信协议。
9 U; Z4 k0 E4 v# z& bLabVIEW的自带函数库中有现成的串口通信模块,方便快速搭建堪比串口调试助手的软件。
+ k! W0 p$ d0 a) K+ n1 a7 C
; x6 b0 U2 K6 b1 [/ c& G: d: D1 ^$ U8 q$ |5 E8 o- q
今天分享一个STM32F103C8T6工控板与LabVIEW的串口通讯实例,主要工作如下:5 A! o& K5 e+ |+ {+ L' E! _- ?
1)基于Keil MDK写一个串口通信程序,主要配置STM32F103C8T6芯片的USART1相关参数并创建串口1中断服务函数(对应引脚为PA9和PA10,可在手册中看到,如下图所示);
; A$ c, \( B" [. f0 ?: @4 ?3 Z' m9 V% P! K
' ?. X4 s B, [: `! E4 Z
( I* t( i' x; Q: Z# j) m7 q2)基于LabVIEW编写一个串口调试助手,用于与STM32F103C8T6工控板进行实时通信,具体使用的串口通信模块位于程序框图的函数选板–>Instrument I/O -->Serial里,如下图所示。# z' i; \# H$ R- d9 ~* C3 j, n
- Q6 ?4 V. n& P- S
! z, j# y% a& z4 j" V; r% k5 T
8 N- _6 t9 o8 E, P3)具体实现的功能为:从LabVIEW发送一个命令到STM32F103C8T6工控板中,然后STM32F103C8T6工控板不进行任何处理,将接收到的命令反馈给LabVIEW。
9 {& b; Y, h3 B
: w% |) ^& }9 O; r- B硬件部分) K, q& ?5 y- s# U, K
1)某宝网上购买的STM32F103C8T6工控板,价格50¥左右;
) [* o# K: u: k w: v9 I& X. z2)某宝网上购买的232转USB数据线(如下图所示),价格15¥左右。" N! N1 D8 r$ b7 L7 M) ~ w
* \3 X, ?8 s k: L6 @8 H8 P! i
* [# X. v0 I& q3 S6 N
" w# w0 B- M! D# U. D- aSTM32F103C8T6工控板部分原理图6 i- q2 N3 Y F4 R$ Y! s/ s
1)下图是STM32F103C8T6工控板的芯片接线,PA9和PA10对应USART1_TX和USART1_RX,PA9和PA10经SP3232EEN-L/TR芯片至9pin的接口处,如下图所示。
0 C$ _# }& a& }- `% U- z
& n. O* P; [. G/ C, C4 i( V
) `2 z# y9 V- r1 J& i# W, g) c
6 N0 j+ ]' u/ ?# P- H4 v* `思路" C$ ?& t2 P. I5 V( L/ O- ]' M
1)对于STM32F103C8T6,配置并使能USART1,使能USART1更新中断并创建相关中断服务函数;7 z4 k" M4 m# v) N( W
2)对于STM32F103C8T6,在接收到从LabVIEW发来的串口数据后,运行中断服务函数,将接收到的数据传回LabVIEW;
+ q+ F* I. F* h! i* o3)对于LabVIEW,配置串口通信,相关参数(如波特率、校验位、停止位等必须与STM32F103C8T6的USART1配置的参数一样!!!
[7 B: V' C3 ?) [8 U: G4 ]2 R4)对于LabVIEW,利用事件结构的while循环,每当发送命令的控件发送一次串口数据时,并接收一次串口数据。6 t5 ]9 l% h" ?$ C6 `
9 J9 D- S0 i$ n$ M1 [! t
相关代码
! H" q% Y3 E* Q. ~2 C1 a( X \1 I+ w1)针对STM32F103C8T6工控板
, ?, D6 J8 ^3 _3 n6 e' T1.串口初始化配置函数(头文件)! n$ Y6 ]- Y0 {8 ~5 y' z" U
该头文件只包含一个函数,即针对串口1的初始化配置函数,用于跟电脑进行串口通信:
- z& A2 E% y% d6 k2 a- /**
- x& R U* _4 \: o% O - ******************************** STM32F10x *********************************
8 D _' N' o6 ~, |/ k% \1 w - * @文件名称: serial_communication.h
6 r0 S/ t) c7 e; d( i - * @作者名称: 闲人Ne
0 c6 G7 j1 O) S0 D# ?6 ? - * @库版本号: V3.5.0% B" E; S0 a5 K! z. ^
- * @工程版本: V1.0.0
/ ?* j: \( |8 E% v! P - * @开发日期: 2021年1月17日; |* `% i3 ~* L1 j& J& P
- * @摘要简述: serial_communication头文件
* @+ L* t6 p2 p - ******************************************************************************/* i( ]1 y2 v- w, m9 t/ I
- /*----------------------------------------------------------------------------' F+ g9 e4 e: X' `2 I. K
- * @更新日志:
5 k& s, w' X& ]" v8 x - * @无
5 [7 Z1 u( h: B! V o% V N - * ---------------------------------------------------------------------------*/
7 M# q' m8 U4 \6 n - #ifndef __SERIAL_COMMUNICATION_H
+ D% R( d% l" _! ]. J ^ - #define __SERIAL_COMMUNICATION_H6 g( ~% |+ ^) _3 P
- /* 函数申明 ------------------------------------------------------------------*/
, ~( J+ w% L3 P) F" r& ~0 u' w4 [ - void My_USART1_Init(void);4 k- J: m: }, B* n) R- M
- #endif /* __SERIAL_COMMUNICATION_H */1 `% t! \. @# ^1 Q1 i/ S" [
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
* y9 P0 B3 S! W# O- t2.串口初始化配置函数(源文件)
- W. _6 p- J s/ {串口初始化配置常规操作,大家可作为范例参考:
7 H, M3 @: ?; A) @- /**+ L! {( A" ?0 ]% s) T5 Z- i* C
- ******************************** STM32F10x *********************************( e% G- z( D% l, P0 ^0 m
- * @文件名称: serial_commuication.c
d+ N1 ^3 h; O* N9 ], s - * @作者名称: 闲人Ne+ K W1 W7 V! a, V5 b7 ?4 ^
- * @库版本号: V3.5.0
5 }1 I" _+ b2 E8 F - * @工程版本: V1.0.0* P, c. v9 w! U/ @
- * @开发日期: 2021年1月17日* v, `) k4 C1 V- G J5 ~% |
- * @摘要简述: serial_communication源文件
* I- F" |0 `) R' ^% F& L - ******************************************************************************/
2 c1 P5 H. h# W! l+ f) G - /*-----------------------------------------------------------------------------
8 A8 I' L4 ~' U: Y! V8 H - * @更新日志:
8 g2 m ~% t" {5 H7 Z - * @无4 @! e: ?) K3 G0 V% o0 j
- * ---------------------------------------------------------------------------*/' Z: h( T0 O- s( e# t7 ^
- /* 包含的头文件 ---------------------------------------------------------------*/
0 s7 ^, V* Q9 ]0 I( U - #include "serial_communication.h"$ O# U6 L8 F, K& x) ^: x
- #include "nvic_configuration.h"
$ t- J8 U2 Z' `; W) `, H - #include "stm32f10x.h"% `# T& i! A a& X% [
- /************************************************, s- s* X' i! b: Y4 I
- 函数名称:My_USART1_Init()
3 g! V8 E9 g; p - 函数功能:初始化USART1管脚配置,
5 o$ Z( W" O* E- K) A+ i8 B. n7 q - 入口参数:无8 ?- G6 E* L% P# ?3 |, W7 F7 t
- 返回参数:无5 D4 u: d' K h# Z8 D3 U
- 开发作者:闲人Ne
3 l- }" ]4 m s' b9 d) k - *************************************************/- O7 k- J6 e6 F3 o
- void My_USART1_Init(void)
: I" f0 B4 q) {: O - {
- F# f& g# d% }; r; c; x - GPIO_InitTypeDef GPIO_InitStruct;
$ x) A/ x3 |5 r3 g* Z - USART_InitTypeDef USART_InitStruct;
% M# ^+ d/ Y1 _: Q - // 第一步,使能相关时钟3 O; u: k( ?) \0 N! Y
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
; l0 c' v& H/ v+ ~! k& W0 |- P - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1时钟
# v8 z8 T2 L) g$ E4 i - // 第二步,串口复位
% ]4 P6 Y6 ?& e4 R# r) f - USART_DeInit(USART1); // 将外设 USARTx寄存器重设为缺省值,复位USART1# t+ W0 i" Q: y5 ^- s- }
- // 第三步,GPIO端口模式设置,PA9对应TX,PA10对应RX
) L* N" q- y7 s/ j, L7 z - GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出# p9 N+ h9 f( v# _8 G% `! Q6 }0 T$ L
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; // 选择GPIO引脚9,作为发送端* r H9 }# \, U- N& @. z k
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz; // 随意设置,不重要4 a9 D1 z: [* p8 R* @
- GPIO_Init(GPIOA,&GPIO_InitStruct);
$ m8 U V& }" o8 o I: X2 W6 m! |% ` - GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; // 浮空输入
( {/ N- P* u& W - GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; // 选择GPIO引脚10,作为接收端
3 B; J$ t8 q$ ?8 P( z2 z$ p9 j - GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz; // 随意设置,不重要# R9 R. X! ]% I P5 U! C: f! ^* |
- GPIO_Init(GPIOA,&GPIO_InitStruct);
: U( n1 H+ c4 ]* t. i - // 第四步,串口1参数初始化,在电脑上进行串口通信时,串口调试软件也需按下述参数设置
4 S" G- j& |+ s& Y - USART_InitStruct.USART_BaudRate=115200; // 波特率为115200
( `6 E& ?: l' a+ [ - USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; // 不使用硬件流控制
# _8 w0 p" _& e! G* F - USART_InitStruct.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; // 收发都使能" C: A4 p: z+ T* `, |1 z6 b
- USART_InitStruct.USART_Parity=USART_Parity_No; // 不用奇偶校验位
, ]0 F4 B5 s8 S3 u+ I. S - USART_InitStruct.USART_StopBits=USART_StopBits_1; // 设置1个停止位
2 C2 S' C( b4 s8 | - USART_InitStruct.USART_WordLength=USART_WordLength_8b; // 设字长为8,因为不用奇偶校验
) Z% J# y8 C; r/ @ - USART_Init(USART1,&USART_InitStruct);
. d" ~! r e( b+ o/ I - // 第五步,初始化NVIC,开启指定中断 / p! A& {; {( B0 q: u w f
- NVIC_Configuration();
" q4 D" r+ C5 M b; Q - USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); // 使能USART1的USART_IT_RXNE中断,RXNE是状态寄存器USART_SR的第5位,意思是接收中断
: x& \8 l( W" a3 _" y9 ] - // 第六步,使能串口 {& ]9 A2 z/ G' q7 u" i/ H
- USART_Cmd(USART1,ENABLE); // 使能USART1外设
: [, D* u- X' q# i+ k+ P - }; p2 g" i7 m1 o+ a' `' e& V4 b
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
7 E1 ^8 M& z- e: w3.串口1中断服务函数! k+ G: w: h7 h1 `9 }. J- `
通过上面可知,在串口1初始化配置时,使能了串口1的接收中断,因此需要编写对应的中断服务函数。
6 }& J# {+ r: ~, d& ?. x, [- /**********************************************************************************
$ v m, i8 h% f - 函数名称:USART1_IRQHandler()+ Z- d% I) t6 n4 k7 d N2 @
- 函数功能:USART1中断,当STM32F103C8T6通过串口1接收到从电脑发来的串口数据时,将所收到的数据再发回去。
( c& ^1 G& a/ H5 ^ - 入口参数:无; s! i _, G$ q3 E: `; |2 U; Z
- 返回参数:无
' R6 U6 \2 [, x! E1 c9 i - 开发作者:闲人Ne
5 K1 s) V' B/ T- j6 l( t. B - ***********************************************************************************/1 p- _+ Z' E( ]" E! I
- void USART1_IRQHandler(void) // 函数名称是由startup_stm32f10x_hs.s文件里定义的 2 H; i: x& @6 L! r, F
- {4 \# l; g; y. |( v$ j
- u8 data;; \4 @- y9 \+ r P
- if(USART_GetITStatus(USART1,USART_IT_RXNE)) // 检查USART1的接受中断USART_IT_RXNE发生与否 9 Q" ?3 W2 @- N8 {1 t% h t
- {
4 i0 O, }4 n4 F! ~. f+ E - data=USART_ReceiveData(USART1); // 返回USART1最近接收到的数据
3 }+ K' g$ c& \$ | - USART_SendData(USART1,data); // 通过外设USART1发送单个数据
9 {- o+ Z2 q- q7 D& P# p5 {9 n1 }5 m) Q - }
$ l1 R+ ~6 N, o2 I G - }
复制代码
# g! A6 Y6 X% ?4 B! q/ ^, Y+ Q4.中断优先级配置
0 v. Z2 Z1 p$ {, }, i6 @既然有中断服务函数,那么必须先进行中断优先级配置:
: W+ ?/ a% v4 |: n3 i2 ?- /************************************************! ?9 Q" M: W2 T* m! U- E/ W1 u5 t
- 函数名称:NVIC_Configuration()
. z/ N2 I) m+ N) }( K4 c - 函数功能:中断优先级配置9 z* l" ?& s/ V* H
- 入口参数:无
8 x5 L, q5 ^- m3 S1 O - 返回参数:无
* S+ ]7 Q B/ E( A$ r' c, F3 F - 开发作者:闲人Ne0 b. s d) d4 f% E
- *************************************************/; y" X) Z: W3 l- L7 [
- void NVIC_Configuration(void)& w: L2 S: a5 C. m5 P# F
- {
2 d8 w" R. z3 T+ n7 u1 s - NVIC_InitTypeDef NVIC_InitStruct;7 G" R. j$ o- m1 f" G j
- // 串口1中断优先级配置
9 m# o# i3 i' l8 N - NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn; // 选择位于stm32f10x.h文件中STM32F10X_HD中的USART1_IRQn3 S- R2 q% U; z" ^2 ^- y
- NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; // 使能上述中断通道( w. U+ V2 v& _3 e
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; // 因为没有别的中断,参数可设0~3之间& |6 f( Q( Z3 @1 p9 g% a
- NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; // 因为没有别的中断,参数可设0~3之间3 ~6 Y" n) x5 S; m- x5 W/ e
- NVIC_Init(&NVIC_InitStruct); 3 {3 D* x8 g! F" T. w* @ [6 H
- }
复制代码
' @; r, d; I ?3 D1 I5.主函数
* K4 `: [6 J; r( ~1 a& a- /**
1 `; i: ^* p3 \3 d- y$ ~ - ******************************** STM32F10x *********************************
- K% P: O) u$ @ - * @文件名称: main.c
1 p3 u# r% ^* W' O1 ~7 ` S9 R - * @作者名称: 闲人Ne
7 C0 h+ v0 {4 O% ], g - * @库版本号: V3.5.0
4 }" s+ x/ F G {3 d/ l - * @工程版本: V1.0.0, m7 r1 [! v7 i* E" m, c
- * @开发日期: 2021年1月17日
' G2 X! U& Q7 S$ ~6 ~: G - * @摘要简述: 主函数 Y8 Q! J8 p+ y) n9 u8 z
- ******************************************************************************/
, b& }! L9 I$ w& B - /*-----------------------------------------------------------------------------
+ D% y% R' J& h1 R( M+ x. E - * @更新日志:
( U) A5 ~1 M E. @ - * @无( a0 s G% n# L* }/ h1 A
- * ---------------------------------------------------------------------------*/
/ T2 O* T9 ^7 ^6 C! |, A3 ^8 u4 A - /* 包含的头文件 --------------------------------------------------------------*/, f2 O' D q8 H
- #include "led.h"
: M I6 S8 u: T6 n1 L- m - #include "sys.h"
( E5 y/ _5 r9 f8 h - #include "delay.h"2 a `4 N4 W' Z- k6 v! }
- #include "serial_communication.h"# i m7 s2 f- [) t- ?; I4 n, t
- #include "nvic_configuration.h"
1 f& t1 C" I" x - /*********************************************************************************
8 j% |: T8 P- q/ O" c+ X; f$ w - 函数名称:int main()7 h8 G# T: I1 W2 d! h+ s( g: n) s
- 函数功能:主函数,LED灯每隔500ms改变一次状态,表示系统正常运行,串口数据收发由中断控制0 H* r) l9 L2 z# {/ v' ]
- 入口参数:无
Y( T7 D4 W; s" P - 返回参数:int {# C) t5 S" |
- 开发作者:闲人Ne) I4 b, v% N1 Y) [; N' `0 X2 F
- **********************************************************************************/
3 v P6 z$ A" ~: H% j; h1 l - int main(void)
. |& }+ r5 {7 K6 F2 Y& n - {4 L: e5 J) ?3 Y0 s$ X0 w! N
- delay_init();/ v& j: S3 M( T- n4 @; D) Q( e+ ]
- LED_Init();
% k9 O2 u' v( e" y0 G - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级配置 * ^, D2 ~% g- |: Q
- My_USART1_Init();, Y7 @$ z8 {6 ^, q
- D1=1; // LED1灭4 m* B: I9 o; a: C1 j
- D2=0; // LED2亮# o4 X; R$ U+ h. }
- while(1), j, y, [. W) I F8 B
- {
. N. ~; I* j, b - D1=!D1;
1 `! b; O5 W* a4 } - D2=!D2;
3 M. v$ x2 ?4 J3 d9 o/ q( i+ m" H+ { - delay_ms(500);
; s2 ^# }2 w# C; \8 Z - }+ t, C3 p0 `* |# G$ _
- }
! Q) e/ r2 f8 q. i1 c3 v - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
" c0 w' o" U1 v: O8 Q0 e2)针对LabVIEW上位机
. ~5 q) ~. }2 B/ lLabVIEW工程可参考范例:Continuous Serial Write and Read.vi,该范例是最简的连续串口通信范例,在程序框图钟进行简单的修改:即在最后的While循环里添加等待1000ms模块(如下图所示)
3 k- K0 N" D% R$ }
5 `& L$ ~6 z4 ?8 ]" F$ P' D
: D g2 o; ~% E
9 ^3 o1 ^8 l3 _! k* c( A7 B回到该范例的前面板,硬件连接好后,首先先选择com端口(如下图所示:COM21),然后按照之前在STM32F103C8T6工控板上的串口配置参数进行配置,如波特率设为115200等,其他参数默认即可。
; C* w/ }: x; F9 p% S+ d2 }6 y* U0 y6 S& o& }& s0 O8 M
. j* x- S4 j" c0 n" s0 z
, m" t1 P" d4 c3 Y" G实验效果, V5 Q' x4 x0 Z$ r) o0 M/ h0 P8 t6 V
运行前,将LabVIEW前面板上的Write和Read控件设为真(即激活发送和接收功能),先烧入STM32F103C8T6工控板的程序并运行,然后运行LabVIEW程序,运行结果如下图所示:6 N) ?( ?' x% z, b. o
# R5 z; x- Y- r3 \
7 e* A) o. o7 e x, V; [# t& D- C- g3 X
当Response显示字符串的控件每隔1000毫秒接收到一次串口数据时,则证明串口通信成功。8 Y3 I/ V) O- j) Q9 F6 Q: J
' M/ L! L! z! `; B7 |$ }& t: a4 Y: e经验分享
; Z+ g5 U3 T0 ]9 Y: v1.在进行嵌入式开发时,利用仿真器Debug程序是首选方案,当然利用串口调试助手查程序的问题也是工程师比较喜欢的手段;# }9 t1 `$ v1 }& S" Q% t5 w6 T R& D
2.可以利用LabVIEW,串口通信为一些嵌入式开发板开发定制化的上位机软件:如开发板的功能是采集一些实验数据,利用LabVIEW为期开发一个上位机软件,通过串口通信获取开发板采集的数据,然后记录,生成报告等。这对于实验室一些非标测试的项目,开发的成本是很亲民的~
, ]( B* _2 ^# u+ {————————————————
7 r' V3 I: ]; h$ H版权声明:天亮继续睡
! y+ V/ W+ u4 S1 n1 b) W* ?' `' ?
/ f+ B9 p g8 I4 P
|