前言
! s* [' v( B9 L1 _* v, v @本篇用库函数写个简单的串口收发,用的是 STM32F103RCT6 开发板(部分来自正点原子)。9 P+ ^9 S/ M* S J9 b X6 \5 O
STM32F103RCT6 最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工& `3 K5 h) I% |
单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA
5 y5 p5 z# y5 c2 v& u1 T等。0 F) I1 [2 x l1 {' Z
% N4 u1 t, \+ S0 Y5 ~( n/ U& j串口设置的一般步骤可以总结为如下几个步骤:* k7 Z j/ d( O+ S3 B5 d e* ]8 b1 @
7 K" ]& }, L' d串口时钟使能,GPIO 时钟使能* d/ M6 H7 z) N, u$ O
串口复位# N: s: x$ _$ j4 L5 g
GPIO 端口模式设置
9 |% I& E0 l) r( n串口参数初始化9 R0 n" {" @2 z% x* j
开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
$ o$ _" E" a' F. N* N0 I. Y使能串口+ M# W' t# }5 ?
编写中断处理函数
$ i n0 ] u# n5 j8 Z一、串口配置步骤
. O$ p! g$ x* Z# I函数和定义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。
y' u5 |. @! _0 H$ Y0 |' }+ Y$ F- l
1.串口时钟使能 串口是挂载在 APB2 下面的外设,所以使能函数为:
0 Y8 Q6 W X0 V- ?& `9 S+ N' H- X0 Q
* W( ]/ l) G/ O- L- j- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
复制代码
8 B) ~6 j* u0 i5 ^( ]/ H" \9 i2.串口复位 当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置
" L1 T. b, ]2 A7 W: G3 c E% t这个外设达到让其重新工作的目的。: }4 A% [3 Y/ E5 @
复位的是在函数 USART_DeInit()中完成:9 B; e1 ?: q: c, o* u4 n$ L& [) K# b
4 r& R7 ~. s, |0 _- void USART_DeInit(USART_TypeDef* USARTx);//串口复位
复制代码
/ g. H1 F& A1 N2 e* s/ e+ l复位串口 1,方法为:- USART_DeInit(USART1); //复位串口 1
复制代码
3 {6 m0 J3 t6 D& Z3 F0 b3.串口参数初始化 串口初始化是通过 USART_Init()函数实现的,
2 j# K& x+ Q# X$ D3 x) N6 I, d
2 {" c, D. E" ~$ b$ d- void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
复制代码
4 I2 j3 d1 n; y+ n8 O/ u第二个入口参数是USART_InitTypeDef 类型的结构体指针,这个结构体指针的成员变量用& ?6 @0 m$ }$ F) S$ k2 _
来设置串口的一些参数。2 b6 r5 O) Z. i8 M b( k' ^
一般的实现格式为:- USART_InitStructure.USART_BaudRate = bound; //波特率;7 @! p0 @" _" `' L; B1 j
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位数据格式- B' K* \7 O3 i, L
- USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位- @; y6 C7 B5 d. \6 {, E7 T
- USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
' f. E4 T0 n* Q: S - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
7 ?6 b8 g9 j5 c& E0 x; ^ y7 h - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式; ~; V0 r* ]8 R) H0 n. A r3 O
- USART_Init(USART1, &USART_InitStructure); //初始化串口
复制代码
8 k* F7 O/ q0 e1 v6 F# B5 d4 e3 D4.数据发送与接收 STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是: L, u! D% M+ Y! v, A
一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收
& T. f5 F4 J" h! s9 H; t/ m% ~2 `到收据的时候,也是存在该寄存器内。% C* L! g( J' J) w5 {0 ]. O
STM32 库函数操作 USART_DR 寄存器发送数据的函数是:3 m- f# M$ r, j! F" G# P# h
- v" N( V9 e, b8 Z) ~$ v
- void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
复制代码
- E8 z" O0 v: O. X5 dSTM32 库函数操作 USART_DR 寄存器读取串口接收到的数据的函数是:- uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
复制代码 ) {* a( P6 T) o
6.串口使能 串口使能是通过函数 USART_Cmd()来实现的
2 J% Y" G; k# N# i6 r3 {$ Q# D6 j. X) I. i
- USART_Cmd(USART1, ENABLE); //使能串口
复制代码
- g6 X1 p7 K0 C8 d* N2 M* `+ s7.开启串口响应中断 使能串口中断的函数是:- void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
复制代码 5 ^ g% }: z/ P/ @) q8 v
开启其中之一的中断的方法是:8 F, L3 S9 y6 y" q
@; e; S- j5 t1 o- }0 R9 z# M- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断
复制代码
) c: b9 t! X+ E* k! ^5 {6 B B- j% o: L发送数据结束的时候要产生中断: \9 E2 G2 N1 G7 l
! t) H. v% _* _% O0 E
- USART_ITConfig(USART1,USART_IT_TC,ENABLE);
复制代码 0 f C6 o& k% T, T0 ?$ o/ d
8.获取相应中断状态 在中断处理函数中,要判断该中断是哪种中断,使用的函数是:9 z8 n% P- V/ u4 @* z) g
0 V$ B( U3 z7 t n% y6 h- s
- ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
复制代码 $ f" i. f2 ~5 ?0 o7 o
判断是否是串口发送完成中断:
6 G4 g/ X0 S4 Z3 e* ?7 B% \0 i& h3 [2 N" I; |
- USART_GetITStatus(USART1, USART_IT_TC)
复制代码 6 O" y3 D8 B! F/ O% y
返回值是 SET,说明是串口发送完成中断发生。( w3 G; ], S4 s( ?
, m/ ]& \3 b6 c* Y( ^ L ~二、实际编写& j/ B, j, V" Z- s. g
1.代码部分8 G+ k( s% k* ?2 W' A
——uart.c 文件; S7 v9 O% m0 G# T% F# [
$ h3 r( P# d% @! h1 I/ S" e [
①重定向fputc函数# V& K; ?, ]0 G9 b+ J
使用串口1(USART1)输出Printf信息
b* F4 m1 j/ h$ E0 w
* K s2 V) f# S, a2 w- #if 1 2 F& s! ]7 a" ~* T( d+ L: F
- #pragma import(__use_no_semihosting)
- Q7 H5 I9 H8 z - //标准库需要的支持函数 0 i6 d+ A2 x9 B0 B# {7 O
- struct __FILE
. @" \" A8 D: _& L - {
* U0 b# p7 g- Q/ k6 Z5 B, j - int handle;
9 @) x# x# B8 \ - /* Whatever you require here. If the only file you are using is */ 6 K* ^: h+ G2 V" s/ L o% }
- /* standard output using printf() for debugging, no file handling */ $ o5 B* K; f: w( I! ]( E1 p
- /* is required. */
: m7 ?: Z! R) R1 X3 Q [ - }; , w2 \1 A2 q; J( u% I
- /* FILE is typedef’ d in stdio.h. */
0 {. v2 [: {/ o5 U8 ~0 x - FILE __stdout;
: d4 ]# a7 J) e& o; z - //定义_sys_exit()以避免使用半主机模式 ) G& z7 k, _3 _" P2 ]0 [% L) P5 W
- void _sys_exit(int x) 7 o4 F; l. v7 A* z; T
- {
# [0 E+ }* v6 Y7 u - x = x; / E# L9 |6 W" O1 L
- }
, C, L. m+ | o" G" r$ c - //重定向fputc函数 $ i! [ @3 t9 s! y! K
- //printf的输出,指向fputc,由fputc输出到串口
0 q( b4 s3 g' N( Q: |; C. ^ - //这里使用串口1(USART1)输出printf信息 + }8 s6 ~$ r; x- R, u
- int fputc(int ch, FILE *f)
9 P2 q0 D: P) F7 N* t. T0 |9 [1 S - { 8 S0 m5 R% h& }9 R3 N
- while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
% Z3 ~2 Y9 H6 a; X, h- A" c - USART1->DR = (u8) ch;
" m2 o' j: S; b5 T/ y+ Q - return ch; % `( Q/ U! L- i$ `" r8 D$ e
- } , F7 ^" R5 |2 C4 _9 x4 Q
- #endif
复制代码 1 t" [5 w$ m% X# y8 L- }
②GPIO
" h7 D0 K. E; W( A- u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));
" w$ u) W* M* T3 s1 l3 j% j1 H Y' D! g - //接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000.# n' O$ H. z* a& O) z$ v
- u16 USART_RX_STA=0; //接收状态标记 # U, u* `9 k& F+ ], ^; ]
- u16 USART_RX_CNT=0; //接收的字节数
复制代码- void uart_init(u32 bound){
% p: ~1 r8 ~3 X$ B& a& z - //GPIO端口设置(初始化)
8 Q/ H9 c2 f3 B' n) `4 s0 t - GPIO_InitTypeDef GPIO_InitStructure;
" z$ s4 b+ X, u3 n# O - USART_InitTypeDef USART_InitStructure;& T, i. {* G5 Z V; v- a
- NVIC_InitTypeDef NVIC_InitStructure;/ q ^4 l: s5 z5 K4 r) z, @" n
- ) ?6 V9 Q. R2 S, ?& Z
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); + x( N/ |* r3 g* V
- //使能USART1,GPIOA时钟
4 K" b- ?4 V, G9 C - # L. z: w. l# K/ [* g
- //USART1_TX GPIOA.9
+ X3 J% T* b, V- k( _ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
7 p; k7 r$ j8 I1 y2 _9 d - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
% M; V$ W o' P7 R1 l2 m - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出& c: ?% x d T
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.93 K4 [% F( z1 z3 \* \! t0 Q
- & U4 P% j+ j* F0 `
- //USART1_RX GPIOA.10初始化
" G+ ]; r# @. Z% ]/ x }- m - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA101 w1 u" d3 Y h
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
" t9 g0 C8 T, ?7 K* N - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
! z# S2 _, y/ ~# w \- C; C! v4 I
- N; s3 Z0 X$ Q& ~- //Usart1 NVIC 配置: H/ K5 ^; `7 E9 O, d4 `5 r1 v0 [
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;4 T6 @" I* f6 B+ K! s
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3% q5 R$ S4 |6 X* ~; a. k4 w2 @' b
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级33 z4 \8 m8 Q9 K
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能; c. O6 D" d `, F! M4 j2 N& Y
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
. O2 z, Z s% P+ w - . y/ ^5 j% |! J: Z- t% b
- //USART 初始化设置
/ M: `6 z4 m1 W9 z4 D7 R - ; [% ^# [% n, z- b2 R- F- m/ y
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
- x4 E+ \3 s& O2 g2 C1 N; X - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
/ g' k# k+ U1 E! I3 v: N( S - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
3 n3 U+ E6 _* {4 H9 E7 F - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位% o2 q5 U! Y, w; |4 k; ]8 Y$ W6 ~
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制( y% f/ ~+ h5 o, w" x
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
- y% v6 L7 E# p6 q# ^. l- j3 Y
9 J/ _; Q, o. m V& [ P, x$ P- USART_Init(USART1, &USART_InitStructure); //初始化串口1
$ c/ N: V+ j* k, F( J - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
* g0 H5 m% K# e+ z8 o. j& E. J - USART_Cmd(USART1, ENABLE); //使能串口1
$ p% L1 U5 h3 y
# Y# Y0 }5 M" w% u3 f3 c- }
复制代码- #if EN_USART1_RX //如果使能了接收 H/ _$ z% T* X
- void USART1_IRQHandler(void) //串口1中断服务程序; G @$ r1 u4 R- I
- {( x5 e9 q& h1 O& g4 N3 @
- u8 Res;
7 i; k# X- p+ ~9 J7 n j: X - #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.7 W4 E3 [4 H0 f g6 e/ U* Z1 g( C7 K
- OSIntEnter(); ! E% J$ ~* ^- Z" E
- #endif
( V2 U7 v; o5 b' r - if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
& [% I- k1 J0 j5 [3 A4 r* W - {
2 U' ?4 A- B# h. h& p5 K% Z+ q; s - Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
8 w$ a' R# k. ?! t( a - if(USART_RX_CNT<USART_REC_LEN)" `" Q1 `2 P4 A( [5 D4 R$ S
- {
1 j) F* y" ~$ H' @( f; e1 n+ j/ B - USART_RX_BUF[USART_RX_CNT]=Res;' S4 \% O% N! P" u. o6 J3 B
- USART_RX_CNT++;
9 g& ^, L3 t/ G( ^8 c6 z( q - }
* O& E, v' q2 K6 s" k0 K - }
7 |: T2 g$ ~4 U. J; i3 Y, S" `( X - #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.# |! f; w3 P4 a' M
- OSIntExit(); - e- f) p; n! W+ ?; X8 O
- #endif
& E4 L& \. r# J. [ - }
: ]9 w' x( k) B6 y# N - #endif
复制代码 * O3 Y# x5 M( g8 E& [/ A" O& U0 W
——uart.h 文件:; t' E8 u% {1 t# V+ m: S3 a
9 s2 m @& n( O7 T4 O* j7 g; { ?
- #define USART_REC_LEN 41*1024 //定义最大接收字节数 41K6 ^* ~8 M5 A5 ^. P) \! V9 ^" E/ _% G
- #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
# w0 A3 x1 r8 I9 e - - A% s! v2 E; w. F# H0 L
- extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
5 Q$ ?' Q$ L# R" y' q - extern u16 USART_RX_STA; //接收状态标记
6 W: m+ D) D2 v5 } q8 ~+ c - extern u16 USART_RX_CNT; //接收的字节数
5 }8 T* U% }2 q. k/ t - //如果想串口中断接收,请不要注释以下宏定义
+ ?. H8 `; o9 M5 W. d3 \
. P6 |/ N$ C; P& }! g- void uart_init(u32 bound);
. ^* }- O# N! v0 H7 u! N! }4 B0 ? - #endif
复制代码 9 g: X6 A: p' g3 i4 M9 P
——main.c 文件:
' h) c4 R/ n8 j. _/ c$ k1 h; h& u8 ~3 S
- int main(void)7 |& A2 e6 Y6 p; }3 T* Q4 f2 d
- { & z: g* G) R. C- ]) |* }
- u8 t;2 ~' Q( I4 K J, d S; n+ I6 u/ F
- u8 len;
$ P) t7 S1 X. i0 X4 \# O - u16 times=0; 3 `5 A, S" q5 N: {# p% l6 r$ |! o
- 3 y, i% D$ s( [! E
- delay_init(); //延时函数初始化
) [2 U; Y% s/ [, @& J; D" c - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
" L9 ~& w: V2 n3 i. e$ T" q - uart_init(115200); //串口初始化为1152002 S! x! `- a; A7 v5 ]( }
- 1 s7 ? y! c: f: W) w4 w
- while(1)
1 @9 f+ }$ K( }& g% W# @& b - {! g. _; v# ?$ N' E7 |# U
- if(USART_RX_STA&0x8000). Z& Q; t/ u6 |3 z
- { $ k2 s9 ]$ w% f" X! A9 s1 w
- len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度+ s6 m& F3 K0 M, b; K' M9 B
- printf("\r\n您发送的消息为:\r\n"); r) ~" n2 P; f9 }& |
- for(t=0;t<len;t++)% H# q3 e. [2 V# P8 z# E
- {
1 ^( q# Z8 R6 q, _ - USART1->DR=USART_RX_BUF[t];( D! Q# h6 i/ A8 v3 g' X
- while((USART1->SR&0X40)==0);//等待发送结束/ g. {3 A# y- G$ j6 P
- }
% ]2 N9 d/ D7 }7 m/ \ - printf("\r\n\r\n");//插入换行- F2 J/ _' t0 ^0 T# D* x7 |
- USART_RX_STA=0;2 q' }. `* e8 T
- }else
! S$ p; E! p" `! X3 b - {
1 T: R% _3 ]5 M% x9 c8 v5 Y - times++;
: G) r/ U1 `0 S; O2 h' H2 y - if(times%5000==0)! d9 ~# u% H2 N3 a. J
- {) p* Y' V, ^& T( O' w8 C
- printf("\r\n串口发送与接收\r\n");
) n3 D5 ?, J+ N7 ]( Z - printf("\r\n123456\r\n\r\n");
, x: _2 z( }6 N: g/ S: u - }
+ a- J2 U6 h) d1 P8 j8 c5 h2 v -
( v0 U: ~! ^2 y - delay_ms(10);
: i8 _* h6 M1 B! g - }% o/ t, h G$ i2 b# t
- }
9 J2 L( N! x, Y7 n. K* K+ { - }
" R/ i" |" o* `9 x& h& `
复制代码
/ Z! U3 |, o7 q, Y
2 ]) p' ]0 K5 \5 v1 n* A) b
/ s: c, m# A" v9 J' R
; k$ O$ }3 Z) |, U: O
y+ ? q' B) x; Y! T |