串口通信(Serial Communications)是指外设与计算机间,通过数据线按位进行传输数据的一种通讯方式。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。虽然串口通信传输速度不高,但程序简单,能实现远距离通信且成本较低,通信长度可达1200米。常用的仪器仪表大多都支持串口通信协议。
7 ?6 D, p( u2 B& M0 U6 oLabVIEW的自带函数库中有现成的串口通信模块,方便快速搭建堪比串口调试助手的软件。' ]7 d* e) P# i5 ^) [( V* w) D
R8 }( E% M& Y8 x
( j5 c/ k; x: W" i& f/ I. Y J今天分享一个STM32F103C8T6工控板与LabVIEW的串口通讯实例,主要工作如下:; N8 w$ v' P0 R- B, `7 B. k; c# t
1)基于Keil MDK写一个串口通信程序,主要配置STM32F103C8T6芯片的USART1相关参数并创建串口1中断服务函数(对应引脚为PA9和PA10,可在手册中看到,如下图所示);
' [6 l4 s% y# D
$ N" E! M0 q! u0 S8 b4 N
3 P* o4 Y! E( J# b, y! ?$ w
* l, Z) X5 J8 {0 H2)基于LabVIEW编写一个串口调试助手,用于与STM32F103C8T6工控板进行实时通信,具体使用的串口通信模块位于程序框图的函数选板–>Instrument I/O -->Serial里,如下图所示。
# e) i' r2 b/ V& B( x" H( D, V# f" U. {7 |6 d& C; }3 u% }* E: n
/ O6 t/ Q8 k0 k+ }& J2 X
# U* A7 Q0 n$ v% L- R* [: `8 d
3)具体实现的功能为:从LabVIEW发送一个命令到STM32F103C8T6工控板中,然后STM32F103C8T6工控板不进行任何处理,将接收到的命令反馈给LabVIEW。
, r1 Z7 C9 Z' s* W O5 [" m& c+ V) T4 w2 x/ {! p e, T7 N0 o
硬件部分( d" f+ e; r; ?3 {; a% r1 n! X/ i
1)某宝网上购买的STM32F103C8T6工控板,价格50¥左右;4 L- Z3 m1 J1 L3 E
2)某宝网上购买的232转USB数据线(如下图所示),价格15¥左右。
5 { u h* h4 e6 j' ^0 m7 ~/ ]) w7 X$ o( ]% g9 {) i* `
, K" ?* U2 p/ d" r4 S
2 W* ~- r- A6 S# q$ O8 w8 Q4 I; [7 qSTM32F103C8T6工控板部分原理图* _& x1 T9 H5 F
1)下图是STM32F103C8T6工控板的芯片接线,PA9和PA10对应USART1_TX和USART1_RX,PA9和PA10经SP3232EEN-L/TR芯片至9pin的接口处,如下图所示。; L7 O2 _, @- J# O' d7 H9 ^
9 _& y9 G- G a' e B
$ u' E3 O0 u0 @! @2 n- G" n6 D4 v/ m" g U" I+ r! T& H9 D5 s8 N7 E8 j+ V
思路
# X) Z# Q( s- s/ {: U$ V, @1)对于STM32F103C8T6,配置并使能USART1,使能USART1更新中断并创建相关中断服务函数;
: f8 ^1 @1 P( f- }7 Y) x$ g3 v2)对于STM32F103C8T6,在接收到从LabVIEW发来的串口数据后,运行中断服务函数,将接收到的数据传回LabVIEW;3 r1 ]! p! w9 o4 e) x' @' z2 m+ y
3)对于LabVIEW,配置串口通信,相关参数(如波特率、校验位、停止位等必须与STM32F103C8T6的USART1配置的参数一样!!!: N y! a$ B' v D/ O7 U6 t
4)对于LabVIEW,利用事件结构的while循环,每当发送命令的控件发送一次串口数据时,并接收一次串口数据。$ H% m4 X: L* L$ W8 K( n
8 h; @. C4 ]4 J, D( {! [- r* E
相关代码9 V& L" ?+ V! m( [- z8 ^: N
1)针对STM32F103C8T6工控板# n6 T/ T; o6 t- n$ j
1.串口初始化配置函数(头文件)
a0 z% X$ d ]该头文件只包含一个函数,即针对串口1的初始化配置函数,用于跟电脑进行串口通信:0 d, r' L' p, ~
- /**
R) K* o7 U. o6 [% y% M$ R - ******************************** STM32F10x *********************************
& j v5 K5 `" P+ _$ l1 R/ |$ _" t - * @文件名称: serial_communication.h9 p6 b4 i7 `/ R" A( H# v5 K
- * @作者名称: 闲人Ne% w) x, A) A8 k2 ~ v
- * @库版本号: V3.5.09 E, W, K% N4 Q8 h2 \1 O; ]# O( u
- * @工程版本: V1.0.0
) n: g. O; P1 C# I2 W3 Q7 f - * @开发日期: 2021年1月17日
5 |3 @+ A" M6 ], b/ K; \: s - * @摘要简述: serial_communication头文件
- W3 v. ]/ r. w- |3 O/ H% Q1 ?# O - ******************************************************************************/
7 X' _1 x& L# o8 @) m' y) u0 C - /*----------------------------------------------------------------------------9 L' ]' I( T4 R0 ~( t! R) t
- * @更新日志:
. N7 E; j$ h3 V. H& \4 v9 ~ ~ - * @无
" S$ J. [6 X( c0 `2 K - * ---------------------------------------------------------------------------*/
t; n* Q7 H5 F* H- k+ s# y - #ifndef __SERIAL_COMMUNICATION_H5 }0 K2 f1 f$ i- M+ t( ~& x
- #define __SERIAL_COMMUNICATION_H0 j6 u7 K- j4 H! R$ z% B
- /* 函数申明 ------------------------------------------------------------------*/
' u, s( m$ r" I( [8 g - void My_USART1_Init(void);
0 Q+ B! ]1 _& w0 u - #endif /* __SERIAL_COMMUNICATION_H */
+ U3 d' E) Z; x1 q' J3 [ - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 3 ?" g: w% w( C9 t
2.串口初始化配置函数(源文件)% |- ~4 x. T5 z7 F. K4 Q
串口初始化配置常规操作,大家可作为范例参考:9 a! W$ t2 k( z, U: k% C) y' Z+ s# I
- /**
5 i+ O, t0 Q3 L/ N# D' g - ******************************** STM32F10x *********************************+ B9 f, y% K. K) t9 T1 E- E
- * @文件名称: serial_commuication.c
- G. H$ R* `# i) ^! t - * @作者名称: 闲人Ne
m8 M1 O# ~0 c/ ^ h9 M - * @库版本号: V3.5.0
" e" D2 R2 B6 o D - * @工程版本: V1.0.01 j# q) J+ @1 S
- * @开发日期: 2021年1月17日
6 d7 M: \# v7 u& q2 o - * @摘要简述: serial_communication源文件
6 ^9 [5 B" F4 b5 l& _8 \# z - ******************************************************************************/8 j! E s% {; ^9 A/ j9 T+ }. W
- /*-----------------------------------------------------------------------------
% B9 y2 i' v, E; j+ y, @9 Q - * @更新日志:
) ~9 u' {8 J8 g4 i) P# s - * @无
9 g/ `6 H+ X' m Z8 {( p- i - * ---------------------------------------------------------------------------*/9 ?3 Q! n0 t/ I9 O9 T
- /* 包含的头文件 ---------------------------------------------------------------*/1 V. e9 P; ^# i# @; C/ V0 D
- #include "serial_communication.h"
) d" P+ J H0 ]1 a/ d - #include "nvic_configuration.h" b& {6 Z6 Q' k# I
- #include "stm32f10x.h"
3 l+ a4 L9 K" o+ h! x6 T. Y - /************************************************! b k9 V8 K: e* I) X( M( F& K
- 函数名称:My_USART1_Init()7 S% b6 S( \/ I# q+ i( e
- 函数功能:初始化USART1管脚配置,% e M$ L) @: p6 H
- 入口参数:无
. W$ W. N M: E8 E+ R2 Q M - 返回参数:无
% \) R& ]3 k2 c4 l7 o) r - 开发作者:闲人Ne. t) l& g( m# h5 S. F5 U4 @1 d
- *************************************************/
6 V% U/ z4 |6 A$ X - void My_USART1_Init(void)0 `. Z" s4 m6 R* L7 C7 \' h
- {
' t3 \& @* B; S& } - GPIO_InitTypeDef GPIO_InitStruct;
( Q( V" p6 c8 j: m9 ?# k5 f+ x - USART_InitTypeDef USART_InitStruct;
; ^% s. t5 r0 P# F' } - // 第一步,使能相关时钟# r. x* S3 G6 n) ?3 f
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
% x7 e4 G7 R# Z" W4 O0 D - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能USART1时钟# f+ h K2 t4 x9 E
- // 第二步,串口复位
& k6 G/ X$ K' e# ^) e2 L; @$ N - USART_DeInit(USART1); // 将外设 USARTx寄存器重设为缺省值,复位USART17 {& \1 w& a' L! B7 w
- // 第三步,GPIO端口模式设置,PA9对应TX,PA10对应RX( X& c' s+ m$ o2 R
- GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; // 复用推挽输出0 J5 D( ~+ f& [0 J
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; // 选择GPIO引脚9,作为发送端9 u R0 O6 \6 |; N2 m0 ~( n
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz; // 随意设置,不重要8 Q* i; B5 z( n. `
- GPIO_Init(GPIOA,&GPIO_InitStruct);
) I) l$ I/ ~; ? - GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; // 浮空输入% ]* L+ _! b k
- GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; // 选择GPIO引脚10,作为接收端 \8 d) g. o+ L
- GPIO_InitStruct.GPIO_Speed=GPIO_Speed_10MHz; // 随意设置,不重要
* T0 Q( V1 N! m! C: b - GPIO_Init(GPIOA,&GPIO_InitStruct); , M4 n7 D! m( V# {- B- O/ I. f
- // 第四步,串口1参数初始化,在电脑上进行串口通信时,串口调试软件也需按下述参数设置5 ^( s& Z; a: q: o! W; ^" m
- USART_InitStruct.USART_BaudRate=115200; // 波特率为115200
* a6 j! o& L' ]$ m - USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; // 不使用硬件流控制; R8 d- a1 h# o* @
- USART_InitStruct.USART_Mode=USART_Mode_Rx | USART_Mode_Tx; // 收发都使能
) P( q$ k7 X# h- O6 H( Q* r - USART_InitStruct.USART_Parity=USART_Parity_No; // 不用奇偶校验位 & h T0 Q+ C- o, c$ b x% P1 B5 f
- USART_InitStruct.USART_StopBits=USART_StopBits_1; // 设置1个停止位" @7 [- w5 f# ^. y. Y1 k- T( _3 ^( A
- USART_InitStruct.USART_WordLength=USART_WordLength_8b; // 设字长为8,因为不用奇偶校验 d% o& Y; N- P* z' E
- USART_Init(USART1,&USART_InitStruct); - i: t/ ?8 E, Q7 a" s
- // 第五步,初始化NVIC,开启指定中断 3 C, P7 N. v+ D, p
- NVIC_Configuration();' z+ ?1 |' G" g
- USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); // 使能USART1的USART_IT_RXNE中断,RXNE是状态寄存器USART_SR的第5位,意思是接收中断4 H2 F- Y' [* q2 l
- // 第六步,使能串口 ! Y. X ]! C% H
- USART_Cmd(USART1,ENABLE); // 使能USART1外设
" f0 W: r% k* b, `6 {9 U - }: C; D I: K- ^
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 : L" z2 Q) E" [ N% Q8 e* {0 r1 t9 h
3.串口1中断服务函数+ t; I; t. b- ~7 K3 C
通过上面可知,在串口1初始化配置时,使能了串口1的接收中断,因此需要编写对应的中断服务函数。
) ^% c* k9 Q% C9 Z# |" Q- /**********************************************************************************% b3 H D4 B$ M) F
- 函数名称:USART1_IRQHandler()/ X/ ~ G* o- k9 F& K* d& T7 @
- 函数功能:USART1中断,当STM32F103C8T6通过串口1接收到从电脑发来的串口数据时,将所收到的数据再发回去。1 G: N% {' I2 H
- 入口参数:无: u& T# L( D& b& q+ X
- 返回参数:无
5 T5 H6 U0 }5 G - 开发作者:闲人Ne
/ g3 ^6 g( x. z8 h - ***********************************************************************************/; T+ F2 d# t( g9 K" U, p2 Y' j
- void USART1_IRQHandler(void) // 函数名称是由startup_stm32f10x_hs.s文件里定义的
9 y8 f# U. ?; b* Y# B" N6 ` - {
3 W- n1 p$ i6 z. S - u8 data;
% ~4 j {4 w. }$ ^6 N) n- L - if(USART_GetITStatus(USART1,USART_IT_RXNE)) // 检查USART1的接受中断USART_IT_RXNE发生与否
+ Z6 K: F3 F, c* u - {
1 `9 F9 D' ^/ G% T, ^! t1 s - data=USART_ReceiveData(USART1); // 返回USART1最近接收到的数据
( k6 g; M( f* J# d - USART_SendData(USART1,data); // 通过外设USART1发送单个数据
9 E) f. J5 C* ~' K6 P4 A - }* }! ^. T/ Q D$ _. F/ y
- }
复制代码 ' b9 m0 s" M- B' @) t' p' _
4.中断优先级配置
1 B7 Z5 _$ D7 [) z0 b0 P1 L既然有中断服务函数,那么必须先进行中断优先级配置:
$ ^/ @0 Y3 ^( T- /************************************************
. \" r" A' M; h' L - 函数名称:NVIC_Configuration()! O$ X: H( S- Q+ C/ {7 q% P
- 函数功能:中断优先级配置8 P# L0 E) N: l9 S6 M
- 入口参数:无2 C6 r/ r6 V @' f: h+ U$ k( q3 ^
- 返回参数:无
3 i& N# w: k/ b/ D - 开发作者:闲人Ne8 M+ B! C5 V, |0 ~( ]6 \
- *************************************************/
9 m$ [5 y7 u: I' H - void NVIC_Configuration(void)
3 o: b5 A6 b3 [% l - { / w; r( S5 ?( y% `
- NVIC_InitTypeDef NVIC_InitStruct;
0 Q0 b: j8 ^$ [# Z3 r4 M9 |9 r" ~* o - // 串口1中断优先级配置
- K3 A; O) F% r3 U% l5 b - NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn; // 选择位于stm32f10x.h文件中STM32F10X_HD中的USART1_IRQn6 t a3 `& t7 C+ e2 x+ k
- NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; // 使能上述中断通道
. S {' k% x) g4 ?& q' T! O - NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; // 因为没有别的中断,参数可设0~3之间
8 ? L4 p! O2 r/ y- c6 M - NVIC_InitStruct.NVIC_IRQChannelSubPriority=1; // 因为没有别的中断,参数可设0~3之间
& E) T6 z- F e7 m" P - NVIC_Init(&NVIC_InitStruct); 4 ?+ ?8 ]9 F' K: @) I
- }
复制代码 : j; i. a! X. f- k4 H
5.主函数
7 v( J7 @4 r' l& \4 Q( A# P- /**4 n% \* t) K$ P8 c8 f$ y: j
- ******************************** STM32F10x *********************************+ e: H2 K/ l0 e8 _# a6 p* Q2 u
- * @文件名称: main.c( v+ Z3 z, z+ p( A" A/ {% V6 S
- * @作者名称: 闲人Ne A, j6 z! q( L- t$ M+ `! [2 e- W
- * @库版本号: V3.5.0
1 n k- n/ c* s& f - * @工程版本: V1.0.0
$ q2 M, B* a, q$ [) ? - * @开发日期: 2021年1月17日! W! {' e. `0 u
- * @摘要简述: 主函数* E( O! M, I& ~/ ~/ Z
- ******************************************************************************/- q2 K7 M" l3 K: x$ a, J9 L V
- /*-----------------------------------------------------------------------------
6 ]' ?$ O1 s* I8 Z - * @更新日志:
% I" k( x' ` ^ - * @无$ ]3 G d- }+ e2 g
- * ---------------------------------------------------------------------------*/# v4 t8 e1 ?- z. \+ M
- /* 包含的头文件 --------------------------------------------------------------*/) F) `* v$ x/ C6 r6 D
- #include "led.h"( t- ~/ j _* t6 u, p
- #include "sys.h"4 r. @/ z& g# W2 L
- #include "delay.h") C" y* E3 n5 \; N% R. t
- #include "serial_communication.h"
' Z8 q5 z+ d- f - #include "nvic_configuration.h"% }5 r m0 J( |3 t2 l& ?
- /*********************************************************************************, g2 v1 [$ J: Q2 G9 p
- 函数名称:int main()
H4 Q8 g% ~6 N- `- m9 {4 c - 函数功能:主函数,LED灯每隔500ms改变一次状态,表示系统正常运行,串口数据收发由中断控制# J9 T9 f# q w, I i
- 入口参数:无
3 t5 r$ W! V5 y) E2 Z. I% z - 返回参数:int) D# f1 {7 F) o+ u- B+ ]
- 开发作者:闲人Ne& \ v( j* W4 Z. ~
- **********************************************************************************/
& e& N' I2 n ]. H3 }* v4 A# \! ] - int main(void)
9 _6 D4 Q# z; H' B - {. r' w7 L& i2 ]. \3 Q) b
- delay_init();
0 _" E0 c) R- b" | - LED_Init();' O1 |5 `% ?6 k9 W( E- Q
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断优先级配置
# k; R9 p& @7 p4 C8 x8 n9 n - My_USART1_Init();3 X$ r. h& q1 o# i0 M5 k/ m4 V
- D1=1; // LED1灭
1 u+ c. u5 P3 D- O1 A+ c - D2=0; // LED2亮9 h; {- E/ J8 v: u+ {# f3 n
- while(1)9 p7 v1 O( u/ B
- {& G: B* _! X- @* d, r; x( J- n- S
- D1=!D1;; C! g5 y" N6 V5 A2 X
- D2=!D2; ) M5 K" P4 l, ] F9 V% h2 s
- delay_ms(500);3 G( g* N* V" d; }
- }
& \, ~& @# {! S+ }3 X: b - }
' ~5 N& s) m' m& e/ u: {' b* j - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 ! j* |+ _* Q P9 \6 d1 Y
2)针对LabVIEW上位机
, u/ b/ u% l+ K2 @) rLabVIEW工程可参考范例:Continuous Serial Write and Read.vi,该范例是最简的连续串口通信范例,在程序框图钟进行简单的修改:即在最后的While循环里添加等待1000ms模块(如下图所示)" Z8 ~+ l/ p- O; [ d& z7 q
* h6 I! ]9 a4 h% J
1 y1 z# m) `. p- A, M3 e: F
l$ }+ j& n) R8 e: C$ u! J
回到该范例的前面板,硬件连接好后,首先先选择com端口(如下图所示:COM21),然后按照之前在STM32F103C8T6工控板上的串口配置参数进行配置,如波特率设为115200等,其他参数默认即可。
5 h, r4 b2 n# m$ K3 }7 _( G: ^9 g- T7 Q& N. M
Y x3 \& G( K* h- Z
2 B8 m9 B( n; w+ I0 M$ e+ @实验效果
" w) i1 J" Y- l- Q1 ?运行前,将LabVIEW前面板上的Write和Read控件设为真(即激活发送和接收功能),先烧入STM32F103C8T6工控板的程序并运行,然后运行LabVIEW程序,运行结果如下图所示:0 a$ C) W' F! M$ t; M0 f' X
6 b8 o& f; t: |
; a8 {7 l/ R( O+ Z6 z5 l0 s4 k+ [" D8 s7 A0 e. { O
当Response显示字符串的控件每隔1000毫秒接收到一次串口数据时,则证明串口通信成功。
$ T; A: H' H$ x
" T, L0 T9 h) i8 i6 y, z; n. x经验分享
0 x5 q! k m/ a2 ? l! M3 s k* E/ g$ z1.在进行嵌入式开发时,利用仿真器Debug程序是首选方案,当然利用串口调试助手查程序的问题也是工程师比较喜欢的手段;
" P) {: ~2 v9 l+ X& h. \8 S0 P0 o" e2.可以利用LabVIEW,串口通信为一些嵌入式开发板开发定制化的上位机软件:如开发板的功能是采集一些实验数据,利用LabVIEW为期开发一个上位机软件,通过串口通信获取开发板采集的数据,然后记录,生成报告等。这对于实验室一些非标测试的项目,开发的成本是很亲民的~
; A- r* H! Q. m% z& p7 a) Y————————————————$ ^! ^ K9 i7 G( k% ?
版权声明:天亮继续睡+ y7 V: v. M: M) Z. N/ X3 u6 B
/ `3 C2 Z5 l5 M+ N7 I$ A6 f
/ Z8 W% q8 i9 z( r* H L" W9 m |