前言
. L) r( L. }) @1 M* W本篇用库函数写个简单的串口收发,用的是 STM32F103RCT6 开发板(部分来自正点原子)。
$ v! ?8 W) t* \$ w; Z1 ySTM32F103RCT6 最多可提供 5 路串口,有分数波特率发生器、支持同步单线通信和半双工: g1 g2 F4 b' i4 W0 f
单线通讯、支持 LIN、支持调制解调器操作、智能卡协议和 IrDA SIR ENDEC 规范、具有 DMA2 Z/ ?5 q; [3 h( b6 i5 K; i
等。
1 M& e) W0 x0 s5 l2 G: o3 Y% E( v0 b0 s" g5 {
串口设置的一般步骤可以总结为如下几个步骤:4 ]/ _6 P, ]! x/ `6 V; {7 E# Y
- t0 N' y: i! U/ ]1 f# d# l; m
串口时钟使能,GPIO 时钟使能
! X7 g. I+ P3 x8 |串口复位
$ t# ] s; g! r3 T% I6 s5 p& e+ X8 ^GPIO 端口模式设置
9 d* ?; e7 { G5 Z- @串口参数初始化
: P0 O0 A. |& N2 u, k# F5 }开启中断并且初始化 NVIC(如果需要开启中断才需要这个步骤)
' \& l, ]. U0 b+ }: v$ ?( }使能串口
$ N' X$ R; R( W( i编写中断处理函数+ ?4 A/ r P- O4 d' w5 N
一、串口配置步骤
9 X8 J: f6 j. l, @3 S函数和定义主要分布在 stm32f10x_usart.h 和 stm32f10x_usart.c 文件中。+ j1 n: [3 c+ C: x2 |8 R
0 w( f* w; v0 v' l7 r+ a1.串口时钟使能 串口是挂载在 APB2 下面的外设,所以使能函数为:' Z' s7 O! s. y
! { w- w v7 `( J1 q
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1);
复制代码
$ Q( j7 y* [* f% s7 @& H8 i2 f2.串口复位 当外设出现异常的时候可以通过复位设置,实现该外设的复位,然后重新配置0 p8 _% [5 L; V: C6 ~/ w
这个外设达到让其重新工作的目的。
" W# f8 f* N. t9 a6 @" P$ W8 Q+ {复位的是在函数 USART_DeInit()中完成:
~& `8 j7 n1 `5 b' e# `" J
/ \! I1 }. F$ d2 |- void USART_DeInit(USART_TypeDef* USARTx);//串口复位
复制代码
" c) K9 S" ?+ C% O- r* o复位串口 1,方法为:- USART_DeInit(USART1); //复位串口 1
复制代码
1 _6 R* w# T" g; S3.串口参数初始化 串口初始化是通过 USART_Init()函数实现的,) W- H4 H% D0 ^4 c
; U/ P3 V* w/ ~* H& G# _! L- void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);
复制代码
0 q5 V$ ?/ A0 p: Q0 G/ x4 b第二个入口参数是USART_InitTypeDef 类型的结构体指针,这个结构体指针的成员变量用% L: X. {+ T. b" D/ U% ~6 L
来设置串口的一些参数。' g" m4 `4 o+ E* @5 m1 @$ ^/ X
一般的实现格式为:- USART_InitStructure.USART_BaudRate = bound; //波特率;
8 O& w( }1 U0 P$ j% \ - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8 位数据格式
( Q4 G" X% D3 |/ \; t0 _* O - USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位4 a. w% s" A8 N& P+ P) v
- USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位' G' H9 A3 a* q4 o5 I, t
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制9 M* j; ~6 o) w0 M5 E+ g% a& M
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式& m4 _; f! v, n3 f# x" @
- USART_Init(USART1, &USART_InitStructure); //初始化串口
复制代码
; Y( |' [ T5 D# S0 b. ]# v( v4.数据发送与接收 STM32 的发送与接收是通过数据寄存器 USART_DR 来实现的,这是) |, |( e: g: C8 }7 S
一个双寄存器,包含了 TDR 和 RDR。当向该寄存器写数据的时候,串口就会自动发送,当收
8 h' l* R* Z8 k! k: O; F到收据的时候,也是存在该寄存器内。
# k, V! {' A5 eSTM32 库函数操作 USART_DR 寄存器发送数据的函数是:- E d& E M5 \
, T/ n2 ]$ @. j; X, j2 W; M
- void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
复制代码
# w) o5 ?: l L( g8 m/ aSTM32 库函数操作 USART_DR 寄存器读取串口接收到的数据的函数是:- uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
复制代码
8 S. p; Y( H2 K9 L6.串口使能 串口使能是通过函数 USART_Cmd()来实现的2 F8 P+ L9 Z E& H, F0 s: t; Z
- `4 i7 A; r6 I" _- n& W+ w
- USART_Cmd(USART1, ENABLE); //使能串口
复制代码 ! Z' e4 U. Z$ m1 e6 a! K+ ~
7.开启串口响应中断 使能串口中断的函数是:- void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
复制代码 $ i' h1 g! n* N8 L( } F" Z3 U
开启其中之一的中断的方法是:
! m( @* l i2 L- f3 j: I L; k" }$ X! I- ~! T9 X$ y4 E9 ~
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启中断,接收到数据中断
复制代码
M% h7 X* \1 O1 Y发送数据结束的时候要产生中断:
3 {* g# H: e3 P4 m: h8 {
. B6 x# ]1 p3 B% |4 C+ @- USART_ITConfig(USART1,USART_IT_TC,ENABLE);
复制代码 7 }3 W/ j: |. T) C; a/ H
8.获取相应中断状态 在中断处理函数中,要判断该中断是哪种中断,使用的函数是:
# o! n- Y) Z9 t: i; P. h r& j8 |+ ~7 e( _- @
- ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)
复制代码
' t4 ?+ @( f% @7 {* N2 j判断是否是串口发送完成中断:
* S1 K7 L# y. M9 d$ w
4 Z. N% \9 a, l& b# h2 S- USART_GetITStatus(USART1, USART_IT_TC)
复制代码 " \- `! p9 y# l$ S
返回值是 SET,说明是串口发送完成中断发生。+ ]' O+ ]* n' J: c4 b
: H8 X( W$ W. ^7 |) }& [$ X8 y
二、实际编写! a# p ?* n' L4 Y' O/ _
1.代码部分4 G+ Y1 I: K2 e- O8 A9 f1 y
——uart.c 文件
+ c. \* n8 Q+ ^# N2 }3 K
) e' a- j5 m( L& e# F①重定向fputc函数! a0 @5 h$ r& c* S& w7 {
使用串口1(USART1)输出Printf信息' v1 K2 F6 ~7 C% U Z
4 K7 _4 r1 z m( A j. `
- #if 1 , [+ F8 j k9 a9 u. p
- #pragma import(__use_no_semihosting)
& B" x0 e$ \: L2 e8 a8 I - //标准库需要的支持函数 : T3 W" }* G1 i& n7 p5 x1 \
- struct __FILE / q" _! S$ n% E4 `
- {
7 t t4 i/ r6 u$ u1 z7 w - int handle;
y5 j# I2 t5 k& ^ - /* Whatever you require here. If the only file you are using is */
7 L# F1 R6 I+ l) x! ~ - /* standard output using printf() for debugging, no file handling */ : i9 d! C% K3 `$ k5 l" _1 ?
- /* is required. */
) c4 }6 A: y5 A H: y; M G - }; ) n5 U# p' {6 a* _' P s8 q
- /* FILE is typedef’ d in stdio.h. */
q) H# h2 w+ L7 B - FILE __stdout; 9 u/ H6 f& [+ P; G& S: e
- //定义_sys_exit()以避免使用半主机模式 : r$ V$ {: z# V; U) K7 n
- void _sys_exit(int x) 9 z% J) K% e1 A) z& B
- { 2 {7 E! K% M o" F# T( C
- x = x; 8 `: h, R9 i8 ]: H$ ~
- }
' E5 f& {$ J0 `) t- [ - //重定向fputc函数 ; I& k! y5 R0 J: f
- //printf的输出,指向fputc,由fputc输出到串口
+ j& a; N" {* i/ P - //这里使用串口1(USART1)输出printf信息
- ~6 M- x3 d0 A$ f; a8 w - int fputc(int ch, FILE *f)
! M' V1 @8 f% r4 J - { ( S! q. B8 I; i# J
- while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
5 d, D& A7 Z3 p( F, }! ] D - USART1->DR = (u8) ch; / J ^0 O+ b# |! `
- return ch; " r7 u; r. M& Q9 J
- }
3 w0 M# d/ K; v4 E - #endif
复制代码 5 o6 p8 T O, B9 d8 _' x
②GPIO
, f% s x# G/ B5 z& r9 E/ P8 \- u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));7 i3 d/ S- V* E3 G- T, C
- //接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000." ]0 W2 Q3 N. \' T* K. h, [) D0 F- _
- u16 USART_RX_STA=0; //接收状态标记
- p1 ^# T% o) k1 C - u16 USART_RX_CNT=0; //接收的字节数
复制代码- void uart_init(u32 bound){
* `7 t# C+ R j7 S; y3 s - //GPIO端口设置(初始化)
y8 i" u) p) N8 s0 E) H - GPIO_InitTypeDef GPIO_InitStructure;
3 @1 Y- w: Z- ?0 q% W/ ` F8 V - USART_InitTypeDef USART_InitStructure;* G/ s& u% E, \5 B' P. X* o" R
- NVIC_InitTypeDef NVIC_InitStructure;' X1 K! k' S, R0 Y, j+ M
-
5 T% P& J( ?8 \0 {% C - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); * H& K/ A5 B& `( G; A+ S% G3 P
- //使能USART1,GPIOA时钟
7 B) i3 H# U* D& a
$ o1 N$ P0 V1 I- //USART1_TX GPIOA.9; @( Q- J ~5 q8 T- }
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
( H# e4 I: J: }4 @; a+ |2 s* S$ O" j - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 g) s* F5 _3 @6 V. J6 i8 I
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
5 H0 Q% \7 |, K$ ?$ @ - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
: R) _ A' w8 `2 A3 P0 I - - }( e" l0 p% A9 x7 j0 e% m* }) x
- //USART1_RX GPIOA.10初始化
! G' V8 p1 I0 l! J; A. E - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10' c8 R$ b, c" W) X, t
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入! A! L0 Q% m ~/ T
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
8 J& d- H" h9 @5 D2 w - / x3 h! i' Z8 p: X0 l0 h) Q8 D: s
- //Usart1 NVIC 配置
$ g# f. C" b! ?3 W" K, i' r* A0 o - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;; N& R" N' c2 h8 i
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
# q X& `, H! S3 X - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3/ U/ t6 ?/ \0 u$ u
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能2 j {! G0 ?* |& y4 @
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器' F& F, b/ G6 L, o
+ ]. |+ x4 @3 Z6 ?- //USART 初始化设置
8 V* Q3 {% B; ?2 P# P% s' y - 5 H6 x! d( V( }- I: R: C
- USART_InitStructure.USART_BaudRate = bound;//串口波特率( V3 u7 M" T9 E# @. ^: a
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式5 ~; I1 r- Y8 u. y! l
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
+ q* j" A5 p4 g/ `& c - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
' t1 N5 }8 E/ E! G - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制8 H$ }. M" j$ O/ g m X
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
! L$ ~) Z+ b( d: p
% s$ v; m- j! x* l% R' y! i- USART_Init(USART1, &USART_InitStructure); //初始化串口1
* ]6 h# e* P. A1 z9 ^' ?; L - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
1 H R0 o6 y% T - USART_Cmd(USART1, ENABLE); //使能串口1
6 X/ ~ C3 A1 e8 L; @
+ A( _* r( ^$ d. x. @7 i- H0 \- }
复制代码- #if EN_USART1_RX //如果使能了接收
6 X7 J d; z2 J8 D - void USART1_IRQHandler(void) //串口1中断服务程序8 e7 B# s0 ?+ n+ Q" o
- {
- y) C$ j" l0 a/ a - u8 Res; }1 q: ^: I3 u
- #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
* G2 F2 ?) c' K" D - OSIntEnter();
; \/ V* ^: T0 S h0 l/ O - #endif
; ]/ x2 ~4 X; P& C - if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)9 F/ D- ] }4 R
- {
4 i8 y/ b! V6 e: o8 q! K- H0 R - Res =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据 F" O: o' f9 @$ e
- if(USART_RX_CNT<USART_REC_LEN)
! f2 V) X6 [) z3 V1 M& B - {4 `, _7 m% J1 g
- USART_RX_BUF[USART_RX_CNT]=Res;( C5 x2 [& H) @* q
- USART_RX_CNT++; 1 _" \) f0 z; L8 G! b3 ?9 Z! d, D
- }
w# a1 L2 r& Y( D% P+ R - } 0 l7 [3 ?: p$ Z. _. N6 A
- #if SYSTEM_SUPPORT_OS //如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
- q! ~$ T) Z; ?7 o' @ - OSIntExit();
0 b3 z" \; D0 H3 H. B. g - #endif
5 \7 `- m" k" t' M - }
. ~+ F4 @1 J( T$ @6 h2 P( M - #endif
复制代码
# R B5 t; n6 [( J' L& G8 ?——uart.h 文件: ^ T- c3 }2 v
- t) L3 {5 V" P9 E- #define USART_REC_LEN 41*1024 //定义最大接收字节数 41K
' g! ?' B$ U" @% Q0 N - #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收9 `$ d* Q* T. ]* T( |) x. ]" G
-
* L" U$ x4 Y, k) g5 O( l - extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
' C: L4 M+ `! ?, q0 b - extern u16 USART_RX_STA; //接收状态标记 7 G: V: Z* k* ~( `. F. h- H2 i
- extern u16 USART_RX_CNT; //接收的字节数
9 W3 G5 Y+ h/ r5 K) d - //如果想串口中断接收,请不要注释以下宏定义- \( ^) S* J* Q4 }- T6 C6 }
- ) O- n/ r& j: _8 d: ~, m
- void uart_init(u32 bound);# ?& `3 P% A3 P9 l& p, O/ @
- #endif
复制代码
3 K- M$ Z3 m x4 l. u——main.c 文件:
: r" J' R/ A( L& K! ]: P# F) m- G# a
- int main(void), w+ R/ Z, \: g. W) M }0 I$ ~' @
- {
% q+ C6 S: M8 F2 _# p - u8 t;
$ ^( ?! p' i3 w; [5 G: F, E - u8 len;
9 E5 T1 W: l8 e- o; l - u16 times=0;
$ N% K2 B% c& W
' N( m* o2 ~2 B& l1 j* B- delay_init(); //延时函数初始化
! n; g, ?' d- V - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组27 _" B, U* [! h% W
- uart_init(115200); //串口初始化为115200
& Q/ w4 X) F# V. _* _5 Q3 b
6 a9 U% M) \, }6 e. m' K- while(1)4 o, p4 N3 D# a
- {
$ [9 W+ ^6 R9 N# \+ l - if(USART_RX_STA&0x8000)
; c1 n- G, Z: x! D: ? - {
5 }: A, @$ z0 z4 z6 g8 \0 k4 H - len=USART_RX_STA&0x3fff;//得到此次接收到的数据长度
; V1 g+ J4 V) d) g* H - printf("\r\n您发送的消息为:\r\n");
- ]! [8 K/ q& [0 ^ - for(t=0;t<len;t++)0 m' @ T* m1 y, u5 ]
- {6 [! R. I& f' i r3 e
- USART1->DR=USART_RX_BUF[t];2 A6 J8 g* r$ H! }
- while((USART1->SR&0X40)==0);//等待发送结束3 Z7 M* c; i- G, Y
- }
% n. _2 N/ t$ w1 ~ - printf("\r\n\r\n");//插入换行! B% r2 h$ s% v1 M$ E5 H
- USART_RX_STA=0;0 ~$ w! s& I8 B% L) s9 r( `
- }else0 F3 b2 c5 ]- Q% D1 A& B( ]
- {
& [" j$ L* u, D8 y - times++;3 N# x* D/ F0 l% W6 ^6 ~
- if(times%5000==0)
& C! g5 A; ~ ~ - {
% C$ j! j: |7 r7 Y$ D% L ? - printf("\r\n串口发送与接收\r\n");0 }& [) @/ @/ `) ]/ l2 R
- printf("\r\n123456\r\n\r\n");7 s% ~. e) W/ \5 a& [ ~* Q
- }9 f6 R' I3 K) K6 Z
-
0 t3 h$ q, F" w, Z - delay_ms(10); 6 O) [6 p% m% `4 R# ?/ }
- }
; }3 _. E" \' F! e9 ~+ _ - }
* T5 U; {" i7 V; ]( K1 P - }% E9 u; v/ K$ ?% n) A
复制代码 1 {7 `) B/ k' O) R2 D$ @) M
. \! o" Q' @5 T2 y/ f1 H
. F. v1 c* y( I+ M9 d, m8 W" r; |, Q4 `: j2 l/ b& s& T! H) x
]$ H9 h- D/ K9 S$ K6 z% i: ]
|