前言
# K1 |2 E$ l" _9 ]' Y本篇用库函数写个简单的串口收发,用的是 STM32F103RCT6 开发板(部分来自正点原子)。
) y1 B/ N: ^2 F+ uSTM32F103RCT6 最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工* v3 c( I" f" I( }2 J
单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA
: M- E$ \% a6 F- J2 |. r3 i ?等。
3 n- l8 H0 X0 k' ~. w, I$ e8 @0 @- Y
串口设置的一般步骤可以总结为如下几个步骤:
) I9 _' q! L! q$ M3 G* d
4 Q7 G8 v! @" V7 T- ^串口时钟使能,GPIO 时钟使能
" B, A1 J2 ~, ~串口复位
( O0 r! Q2 S$ y$ k, y$ ?GPIO 端口模式设置
/ ?, ?0 q) B6 D" G* A) [串口参数初始化& r7 D8 E6 d( C: N: e: b) ^
开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
3 `" K- L$ s/ Q, f$ N* s使能串口
& f1 }4 R* ?- _' M& ~7 p7 m编写中断处理函数
3 q0 r8 W8 t5 `/ R% g$ K" [一、串口配置步骤( W8 W$ t0 L- g2 W% ^
函数和定义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。$ \3 }' e; w( Q
7 S$ x2 D5 \6 m0 ~1.串口时钟使能 串口是挂载在 APB2 下面的外设,所以使能函数为:
% K. M1 h- c9 j' X: P6 `$ Y+ S7 Q0 [3 }8 K+ t
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
复制代码
. ?) x" E" D5 Y: Q2.串口复位 当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置2 G5 `# m |7 ^; c* q, c( R7 b
这个外设达到让其重新工作的目的。$ i9 b% Z4 Q. Z/ ]7 K6 {
复位的是在函数 USART_DeInit()中完成:
, X/ g) G5 v3 Z9 c6 O2 |8 L6 w! [( H4 A0 H+ i4 g5 ~
- void USART_DeInit(USART_TypeDef* USARTx);//串口复位
复制代码
( y: A. E h, L- ?& @+ `复位串口 1,方法为:- USART_DeInit(USART1); //复位串口 1
复制代码 5 r/ a d: l- b. X
3.串口参数初始化 串口初始化是通过 USART_Init()函数实现的,
/ G( W6 _6 K% I. i5 m5 c* f7 o) N+ h0 h9 i6 r1 ~( U
- void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
复制代码
5 E# z' _% g! m8 i6 |9 x% g9 f第二个入口参数是USART_InitTypeDef 类型的结构体指针,这个结构体指针的成员变量用/ V5 e' O& z/ U( w) t) i
来设置串口的一些参数。1 j8 }7 `% c' ]* z& {
一般的实现格式为:- USART_InitStructure.USART_BaudRate = bound; //波特率;) \2 V' i9 y" l, Y9 l! v4 {! {
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位数据格式
/ K" R3 y* t4 S - USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
1 }4 O$ l5 d+ C# y9 c! F - USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位# D& v' v |1 Q: k, n5 G) R
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制" u3 T7 B6 O! @9 {: P, |
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
1 F- q9 S, P g! j. V: h) ~ - USART_Init(USART1, &USART_InitStructure); //初始化串口
复制代码
8 |' n4 G7 V2 i0 \- w0 e4.数据发送与接收 STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是* |7 Q6 w# J" [, |0 ^+ C" K4 M( G# d
一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收
7 M7 R0 R) Y+ n1 r到收据的时候,也是存在该寄存器内。
/ a% j* a" R9 T# g5 l, ySTM32 库函数操作 USART_DR 寄存器发送数据的函数是:! c3 F9 T8 F$ L% R+ {1 n
- ~9 _1 j4 }! k8 o
- void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
复制代码 ! }, o5 a' X" ]0 c5 [6 y
STM32 库函数操作 USART_DR 寄存器读取串口接收到的数据的函数是:- uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
复制代码 + ~9 H! t% f- Q# h* E0 _
6.串口使能 串口使能是通过函数 USART_Cmd()来实现的% g) z4 Q) c$ Y9 q1 x
1 l/ {; P6 c5 t: r8 @9 \. h- USART_Cmd(USART1, ENABLE); //使能串口
复制代码 M# i; m1 C: `
7.开启串口响应中断 使能串口中断的函数是:- void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
复制代码
7 U. g5 R/ ?6 J# w& M6 O3 A% w6 c开启其中之一的中断的方法是: _: w' R& b2 I% F5 X
9 c0 J: a: D: N2 W- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断
复制代码
+ J7 ~% n8 d0 k0 E8 T发送数据结束的时候要产生中断:
9 H9 y; ?2 J8 L& ^) x7 v+ ?) D' i$ Z% e4 O; X
- USART_ITConfig(USART1,USART_IT_TC,ENABLE);
复制代码
X' v' V2 u( U( _& ?) Z6 [' U8.获取相应中断状态 在中断处理函数中,要判断该中断是哪种中断,使用的函数是:
' ?2 H# n. h& Y. F6 ?! M1 i
. @2 j/ x5 q* p( c3 g- ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
复制代码
; E- r- n: M$ \% f# {判断是否是串口发送完成中断:/ h, N, u! {5 c1 c1 K
5 k8 i: o9 @% G& H3 L; K6 b, O
- USART_GetITStatus(USART1, USART_IT_TC)
复制代码 6 A/ ?6 B m7 V. p/ a9 b. L7 i/ O
返回值是 SET,说明是串口发送完成中断发生。
2 M; _3 J) Q; m G3 O, f# U7 M/ E$ h: M R6 \
二、实际编写
0 e1 U5 I, d: c- O, Z" k1.代码部分
; S4 B% O9 }* L& L+ _8 C——uart.c 文件1 _! w i( Z' O$ d8 _. b
, d1 E3 O& l2 s& j# D4 p+ c% ^" e
①重定向fputc函数# R' @& [- x0 r" `% l7 O N7 b3 ?
使用串口1(USART1)输出Printf信息$ [7 r& R5 l% Y0 L m) [
; b0 J( Z" F9 c$ z: ] A; X8 P: t" ^8 T- #if 1 5 u) c& o |4 a# @
- #pragma import(__use_no_semihosting)
% u! g) ~3 d# P. P - //标准库需要的支持函数
% p0 s& E/ i* K6 b, o3 \ - struct __FILE
$ L/ h, L! @3 {: Q' @! [ - {
6 i$ j% f+ q5 d" y& U( n) F - int handle; $ h1 D8 R% e6 w% \, Q
- /* Whatever you require here. If the only file you are using is */ 6 D F; L$ S# ~& e" I% |5 B6 u# y$ b
- /* standard output using printf() for debugging, no file handling */ # g) n9 F$ `: q2 m! H8 d [1 N7 P' F
- /* is required. */ ; \4 x1 P3 D6 Y6 d5 n; W# h" b
- };
7 J* U/ U4 C: q- {9 Z8 r+ J - /* FILE is typedef’ d in stdio.h. */
4 W1 ], I# w0 p - FILE __stdout;
: `4 d4 H% T- c5 q - //定义_sys_exit()以避免使用半主机模式
' S2 k' T* ?0 Q. n - void _sys_exit(int x)
) ?0 e2 P; n# M- h5 \/ [! ]4 m+ M - { 7 f# o8 _3 m J
- x = x;
5 q i% n4 u" ~ - } 8 M4 I$ p+ x. a) \
- //重定向fputc函数
. n# k' i. P8 Z- M. W' E; _ - //printf的输出,指向fputc,由fputc输出到串口 * H" O! ?: a8 e* i
- //这里使用串口1(USART1)输出printf信息
9 X& }& {% o3 s# U& [4 F: C - int fputc(int ch, FILE *f)
( O; {( N+ Y5 A" ^3 r- e - {
; j t% {) Z2 u9 q" w* a) l - while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
5 k4 ?* U' a0 c* o" r2 E - USART1->DR = (u8) ch;
7 h) z! i6 g3 y, J+ R4 I# r H+ c - return ch; % }8 i6 R, c2 m' u# V9 i
- } 1 o$ x: B: P) F, m! A6 I' y$ m: {
- #endif
复制代码 1 H" v" _5 R U5 ^$ ^
②GPIO* H3 L8 N) X6 E3 V& a3 q# Y- o
- u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));
u3 H- {1 I" M - //接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.
3 N! Z* Q: d; X9 E - u16 USART_RX_STA=0; //接收状态标记
" F- S c4 R( f. s - u16 USART_RX_CNT=0; //接收的字节数
复制代码- void uart_init(u32 bound){# L' U: X4 L) i$ ]4 D7 b* `
- //GPIO端口设置(初始化)
3 M. V, ]6 `& P. b) s - GPIO_InitTypeDef GPIO_InitStructure;
8 n, I" e4 G: r8 }% \ S - USART_InitTypeDef USART_InitStructure;
# W' i) S3 B& A( D - NVIC_InitTypeDef NVIC_InitStructure;
) o% k/ V/ b0 W! {. e: \ -
: S5 p- ?- X, ~( r3 R - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);
d& a- {& ?: n% ?3 b - //使能USART1,GPIOA时钟
/ |! Q" d# k. h( _' w4 Q6 ? - ( C/ C4 a1 \% \& Q7 `- p
- //USART1_TX GPIOA.9
/ t7 C# L% Y+ ^8 Y - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
5 ?8 u$ U# j( r: V2 |- M, x - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
t+ Q. u1 X# M, K9 C/ r - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
6 O2 X4 M6 t7 W W% v - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
. m* Z: d& D( O& s, P: {
% k! [. o$ U% P- p2 C1 k- //USART1_RX GPIOA.10初始化
4 S# X+ ?" K+ Q0 f2 Z8 h - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10 G1 ^( ^0 N+ u: q3 @/ d& H
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入, h; g1 _* u# O3 t2 x+ Y( Q, u4 Y0 S
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
, y" V3 C5 W6 g: I! f: u% K' }! K - & N6 f4 o7 ~3 y) n" X* T
- //Usart1 NVIC 配置
' Z/ v$ ?3 Q! ~$ H - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
) w6 q3 ?' `. s' G0 R, Q( ` - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级35 f1 w8 r$ p& v" n
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
( y5 ^/ n7 z- R4 @+ j }, }) j - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
# u3 D+ C8 |- r5 V: O( [ R- d2 T - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器2 g; \8 e- r! Q
) z0 V/ V9 B) C6 Z! _- //USART 初始化设置
( P T( j1 a3 O* ~1 l: C - . d/ ]/ }# X( m/ `0 h" C( E! j, N
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
7 v. f8 v5 z% ]# L - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
d& N5 r0 H, ] - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位/ {7 s O. c' Q3 l6 d
- USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位5 s2 l4 F7 B3 ]. Y# F
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
4 U; l3 ]- U( @/ O% b! U7 ^, h - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
o" P9 h4 Z* B! E - ) l X* K( C: a5 O0 E0 {
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
4 x0 W7 j- P8 s# v9 p5 ]9 u - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断. J; T$ A) W5 _8 I1 O
- USART_Cmd(USART1, ENABLE); //使能串口1
1 |, U1 }. {6 U1 m5 s+ H3 P - ) A5 ~% S2 R& w/ v9 P
- }
复制代码- #if EN_USART1_RX //如果使能了接收3 j z! o+ D5 U4 X9 Y! Q( m7 P
- void USART1_IRQHandler(void) //串口1中断服务程序
. c m' v) _+ J$ f) U - {
p. Y7 l" r6 s- G - u8 Res;1 ?, p K3 o0 A# w% a4 X
- #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.9 l: q. P& |; m2 z- N( e
- OSIntEnter();
( f' y2 T" P% Z# s - #endif8 x* I/ c" k# A: h' c
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
7 H; i1 `" F1 Z4 n5 j' h - {. g" N: y% |4 d x
- Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
% R: ?9 w2 c/ [0 Y6 _ - if(USART_RX_CNT<USART_REC_LEN)* |9 v, Z8 b2 a. n$ i& `
- {
% x: F6 |# q& c# C3 h, q0 n& d - USART_RX_BUF[USART_RX_CNT]=Res;
" ]- j X# a+ Y' p - USART_RX_CNT++;
" ~+ a% e: j- a - }
. c B( h0 D8 _" U! f' X5 u - } : F0 V7 h8 v& P: X* q1 j
- #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
6 C' F8 T. X" v O - OSIntExit(); - R' @( ^! B7 U
- #endif' M, R5 Q" Z0 F9 r \
- } 6 C0 n0 V% X% k1 Q* `4 \
- #endif
复制代码
% c0 n. G8 c5 y) S4 f7 S! G——uart.h 文件:3 P9 y/ q3 a# b- e/ d3 ?
4 e% c8 F; |. ^. B- _5 t- c
- #define USART_REC_LEN 41*1024 //定义最大接收字节数 41K1 ?; J/ q& a9 ?0 W2 o/ A
- #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收+ Q2 Y# a p m& h1 J; K4 s
- 0 k8 U" S- Z; U
- extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 3 u8 M. y, I3 x3 h K; e$ \
- extern u16 USART_RX_STA; //接收状态标记 % I5 a* \) @, a! k/ V6 c+ G
- extern u16 USART_RX_CNT; //接收的字节数
+ `+ k9 d' Q0 h' n2 Q0 r& v; b8 P - //如果想串口中断接收,请不要注释以下宏定义2 x) k/ f9 t+ }. p# f7 x# v0 ?
4 ^! w- N; r! V- q3 ~- void uart_init(u32 bound);; {0 D* M" ?, T
- #endif
复制代码
! A( ~8 K. r1 M# g/ m——main.c 文件:
2 ^! H2 |( v i( D7 e) h2 B8 s9 O; w! c
- int main(void)
0 f( g; y- C0 \7 X# W - { : D8 i: [* j6 U* ?
- u8 t;( m Y* V3 Y# y+ q
- u8 len;
) r) y- B" d# G/ i. J5 ^/ w) n - u16 times=0; ; E- @- O$ x: ~ [) K" G) N; E
; f$ L. V7 @8 O2 d+ Z$ C' y- delay_init(); //延时函数初始化 0 R' F. V4 v: `; U* u, m0 h
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组26 Y2 o3 u: u% O2 {
- uart_init(115200); //串口初始化为115200
0 W* u7 P z* O2 o9 g. q. K - r- l5 k0 q& P( D& p0 M h4 v
- while(1)
' [' P7 |7 k* x# r+ p2 Y5 ~ - {) v% w K/ c7 ^# R7 f( o" C
- if(USART_RX_STA&0x8000)
' N; l7 w# m2 B2 g- x" H - {
# k b8 ^9 m& z' L+ o* m! \4 L - len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
5 S. m6 x: ~- ~0 C! s - printf("\r\n您发送的消息为:\r\n");8 p9 R+ C* ]2 k* r" p4 i
- for(t=0;t<len;t++)$ J5 o. v4 c: a; Q2 R) F
- {9 N: {- W5 m8 c0 M. c& h: B
- USART1->DR=USART_RX_BUF[t];
8 x" d' I \# T - while((USART1->SR&0X40)==0);//等待发送结束0 Y' }% z: i! P1 x
- }
; B+ G% ~6 k3 ^. P9 }: Z. O - printf("\r\n\r\n");//插入换行
. ~. `/ P5 D( `% A6 e5 h& e* d - USART_RX_STA=0;, i# u g: s4 ?0 m9 x% h
- }else
. m' K. b9 ~3 n# W8 Q0 o - {
. g \ k4 ]& I2 M9 A/ a) O - times++;8 f% w% Y7 E* I N
- if(times%5000==0)
) Y9 `2 e. b" Z/ f/ C0 e - {
6 f- R8 W( I. W - printf("\r\n串口发送与接收\r\n");; ?5 W. O1 _" b$ b
- printf("\r\n123456\r\n\r\n");
- i2 r6 J0 S; x# ~. ^$ L - }
/ _9 k; S3 H; Q m - % o! b8 n1 {- e3 f) d% Q
- delay_ms(10);
6 V8 L% {) ?2 K* @. X - }( H! B# `$ H. B( ?& y+ u
- }
. a! J% `: }- G( o' Y - }
3 Q) ~$ m3 z; s/ k; m, j6 k
复制代码
- ]+ `- z9 T" [4 V7 ~; u4 z3 K1 |# m4 k- _
- l2 V0 _7 Y) t. X
, O# d2 u# d3 q$ C8 C. S
+ Q6 j; _/ i* s2 }/ E+ P" U5 T% M |