USART 串口通信实验
我们知道STM32F1 里面有很多通信接口。让我们学习USART 串口通信。要实现的功能是:STM32F1 通过USART1 实现与PC 机对话,STM32F1 的USART1 收到PC 机器发送的数据原封不动地返回PC 机器显示器分为以下部分:
- 通信的基本概念
- STM32F4 的USART 介绍
- USART 串口通信配置步骤
- 硬件设计
- 软件设计
1 . 通信的基本概念
我们知道STM32F1 芯片中有许多通信接口。在学习这些通信接口之前,有必要了解通信的基本概念。通信可分为多种方式:
- 按数据传输方式可分为。
- 根据通信的数据同步,可分为。
- 根据数据的传输方向可分为。 让我们简要介绍这些通信方式:
(1)串行通信 串行通信是指使用数据线一个接一个地传输数据,每个数据占据一个固定的时间长度。它只需要几条线就可以在系统之间交换信息,特别适用于计算机与计算机、计算机与外设之间的远程通信。如图所示: 串行通信的特点:传输线路少,长途传输成本低,可使用电话网等现成设备,但数据传输控制比并行通信复杂。
(2)并行通信 并行通信通常用多条数据线同时传输数据字节,通常是8位和16位 位、32 等数据一起传输。如图所示: 并行通信的特点:控制简单,传输速度快;由于传输线多,长途传输成本高,接收方同时接收困难,抗干扰能力差。
异步通信和同步通信 (1)异步通信 异步通信是指通信的发送和接收设备。为了协调双方的收发,要求发送和接收设备的时钟尽可能一致。异步通信是以字符(构成帧)为单位传输的。字符之间的间隙(时间间隔)是任意的,但每个字符中的每个人都在固定的时间传输,即字符之间不一定有位间隔的整数倍关系,但同一个字符中的每个人之间的距离都是位间隔的整数倍。异步通信数据格式如图所示:
异步通信的特点:不要求双方收发时钟严格一致,容易实现,设备成本小,但每个字符要加2~3 位于起止位,每帧之间有间隔,因此传输效率不高。
(2)同步通信 ,使双方完全同步。此时,传输数据的位置之间的距离是位置间隔的整数倍。同时,传输的字符之间没有间隙,即保持位置同步关系和字符同步关系。发送人对接收人的同步可以通过两种方式实现。同步通信数据格式如图所示: (1)单工通信 。如下图所示:
(2)半双工通信 。如下图所示:
(3)全双工通信 。如下图所示:
通信速率是衡量通信性能的一个非常重要的参数,通常是**比特率(Bitrate)**来表示。比特率是每秒传输二进制代码的位数,单位:位/秒(bps)。如每秒传输240 每个字符格式包含10个字符 位(1 个起始位、1 比特率为: 10 位×240 个/秒= 2400 bps 后面会有一个波特率的概念,表示每秒传输多少码元。代码元是通信信号调制的概念通信中常用相同的时间间隔来表示二进制数字,称为码元。在常见的通信传输中,使用0V 表示数字0, 5V 数字1表示,一个码元可以表示两种状态0 和1,所以一个码元等于一个二进制比特,波特率的大小与比特率一致;如果在通信传输中有0V、2V、 4V 以及6V 分别表示二进制数00、01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。由于很多常见的通信中一个码元都是表示两种状态,所以我们常常直接以波特率来表示比特率。
STM32F1 的USART 介绍
串口通信(Serial Communication),它是指外设和计算机之间通过数据信号线机之间传输数据的通信方式,属于串行通信方式。串口是一种界面标准,规定了界面的电气标准,没有界面插件电缆和使用协议。 (1)接口标准 串口通信的接口标准有很多,包括 等等。常用的是RS-232 和RS-485。RS-232 其实是RS-232C 改进的原则是一样的。在这里,我们将使用它RS-232C 解释界面。 是EIA1969年(美国电子工业协会) 年修订RS-232C 标准。RS-232C定义数据终端设备(DTE)与数据通信设备(DCE)之间的。RS-232C 界面规定使用25 针式连接器,简称DB25所示: RS-232C 还有一种9 针的非标准连接器接口简称DB9.串口通信大多使用DB9 接口。DB25 和DB9 接头可分为公头和母头,其中针状接头为公头,孔状接头为母头。 针串口线的外观图如图所示: 可以看出,公头和母头的管脚定义顺序不同,需要特别注意。这些管脚有什么作用? 针串口和25 管脚的功能说明如图所示: 在串口通信中,我们通常只使用2、3、5 三管脚,即TXD、RXD、SGND,看不懂其他管脚功能也没关系。RS-232C 还规定了逻辑电平
在TXD 和RXD 数据线上: 1.逻辑1 为-3~-15V 的电压 2.逻辑0 为3~15V 的电压 在RTS、CTS、DSR、DTR 和DCD 等控制线: 1.信号有效(ON 状态)为3~15V 的电压 2.信号无效(OFF 状态)为-3~-15V 的电压
由此可见,,。。通常在串口通信中PC机的DB9 单片机,单片机上使用的串口DB9 作为母头,通过直接串口线连接。串口线(COM)母头连接计算机DB9 单片机上使用的公头与串口线公头连接DB9 母头是2、3、5 管脚直接相连。如果要实现两台电脑串口通信,需要一条交叉串口线,2 对3、3 对2、5 对5 连接,交叉串口线一般两端都是母头。串口通信中还需要注意的是,串口数据收发线要交叉连接,计算的TXD要对应单片机的RXD,计算机的RXD 要对应单片机的TXD,并且共GND,如下图:有的朋友就会问了,在计算机与单片机进行串口通信时,使用的不是直通线吗,这时候怎么让TXD 与RXD 交叉连接?前面我们说了单片机处理的是TTL 电平,需要使用RS232 电平转换芯片,将RS232 电平转换芯片串行数据输出管脚交叉连接在DB9 母头上即可。
(2)通信协议 RS232 的通信协议比较简单,通常遵循 格式。“96”表示的是通信波特率为9600。串口通信中通常使用的是异步串口通信,即没有时钟线,所以两个设备要通信,必须要保持一致的波特率,当然,波特率常用值还有4800、115200 等。“N”表示的是无校验位,由于串口通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验(odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无校验(noparity)。“8”表示的是数据位数为8 位,其数据格式在前面介绍异步通信中已讲过。当然数据位数还可以为5、6、7 位长度。“1”表示的是1 位停止位,串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑0 的数据位表示,而数据包的停止信号可由 0.5、1、1.5 或 2 个逻辑1 的数据位表示,只要双方约定一致即可。了解了串口通信的标准,我们就来看下STM32F1 芯片的串口USART。
,它能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准 NRZ 异步串行数据格式的要求。,同步和异步主要看其时钟是否需要对外提供,这个前面也介绍了, 我们平时使用的串口通信基本上都是UART。STM32F103ZET6 芯片含有3 个USART,2 个UART 外设,它们都具有串口通信功能。USART 支持同步单向通信和半双工单线通信;还支持LIN(域互连网络)、智能卡协议与IrDA(红外线数据协会) SIR ENDEC 规范,以及调制解调器操作(CTS/RTS)。而且,它还支持多处理器通信和DMA 功能,使用DMA 可实现高速数据通信。USART 通过小数波特率发生器提供了多种波特率。USART 在STM32 中应用最多的是printf 输出调试信息,当我们需要了解程序内的一些变量数据信息时,可以通过printf 输出函数将这些信息打印到串口助手上显示,这样一来就给我们调试程序带来了极大的方便。
其实USART 能够有这么多功能,取决于它的内部结构。其内部结构框图如图:
我们把图21.2.4 分为几个模块进行介绍: (1)标号1:功能引脚
- TX:发送数据输出引脚。
- RX:接收数据输入引脚。
- SW_RX:数据接收引脚,只用于单线和智能卡模式,。
- nRTS:请求以发送(Request To Send),。如果使能RTS 流控制,当USART接收器准备好接收新数据时就会将nRTS 变成低电平;当接收寄存器已满时,nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
- nCTS:清除以发送(Clear To Send),n 表示低电平有效。如果使能CTS 流控制,发送器在发送下一帧数据之前会检测nCTS引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
- SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。前面我们说了,STM32F103ZET6 芯片具有5个串口外设,其对应的管脚可在芯片数据手册上查找到,也可以直接查看我们开发板原理图,我们已经将芯片所有的IO口功能都标注在管脚上了。 USART1 挂接在APB2 总线上,其他的挂接在APB1 总线,由于UART4 和UART5只有异步传输功能,所以没有SCLK、nCTS 和nRTS脚,如下: (2)标号2:数据寄存器 USART 数据寄存器(USART_DR)只有低9 位有效,并且第9 位数据是否有效要取决于USART 控制寄存器1(USART_CR1)的M 位设置,当M 位为0 时表示8 位数据字长,当M 位为1 表示9 位数据字长,我们一般使用8 位数据字长。USART_DR 包含了已发送的数据或者接收到的数据。USART_DR 实际是包含了两个寄存器,一个专门用于发送的可写TDR,一个专门用于接收的可读RDR。当进行发送操作时,往USART_DR 写入数据会自动存储在TDR 内;当进行读取操作时,向USART_DR 读取数据会自动提取RDR 数据。TDR 和RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,发送时把TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到RDR。USART 支持DMA 传输,可以实现高速数据传输,具体DMA 使用在后面会更新介绍。 (3)标号3:控制器 。使用USART 之前需要向USART_CR1 寄存器的UE 位置1 使能USART。发送或者接收数据字长可选8 位或9 位,由USART_CR1 的M 位控制。 发送器可发送8 位或9 位的数据,具体取决于M 位的状态。发送使能位(TE) 置1 时,发送移位寄存器中的数据在TX 引脚输出,如果是同步通信模式,相应的时钟脉冲在SCLK 引脚输出。 如果将USART_CR1 寄存器的RE 位置1,使能USART 接收,使得接收器在RX 线开始搜索起始位。在确定到起始位后就根据RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到RDR 内,并把USART_SR 寄存器RXNE 位置1,同时如果USART_CR2 寄存器的RXNEIE 置1的话可以产生中断。 USART 有多个中断请求事件,如下: USART 中断事件被连接到相同的中断向量,如下: (4)标号4:小数波特率生成 波特率的概念在前面介绍比特率的时候已经提过,常用的串口通信中都把波特率当作比特率。波特率越大,传输速度就越快。接收器和发送器( Rx 和Tx)的波特率均设置为相同值。波特率计算公式如下: 其中,fCK 为USART 时钟频率,USARTDIV 是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。其中DIV_Mantissa[11:0]位定义USARTDIV的整数部分,DIV_Fraction[3:0]位定义USARTDIV 的小数部分。串口通信中常用的波特率为4800、9600、115200 等。
USART 串口通信配置步骤
在上面的介绍中,可能有的朋友很不理解,不过没有关系,下面我们讲解如何使用库函数对USART 进行配置。这个也是在编写程序中必须要了解的。具体步骤如下:(USART 相关库函数在stm32f10x_usart.c 和stm32f10x_usart.h 文件中)
前面说过STM32F103ZET6 芯片具有5 个串口,对应不同的引脚,串口1 挂接在APB2 总线上,串口2-串口5 挂接在APB1 总线上,根据自己所用串口使能总线时钟和端口时钟。例如使用USART1,其挂接在APB2 总线上,并且USART1 对应STM32F103ZET6 芯片管脚的PA9 和PA10,因此使能时钟函数如下:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);// 使能USART1 时钟
因为使用引脚的串口功能,所以在配置GPIO 时要将设置为复用功能,这里把串口的Tx 引脚配置为, Rx 引脚为,数据完全由外部输入决定。如下:
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出PA9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入IO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX // 串口输入PA10
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; // 模拟输入
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
(3)初始化串口参数,包含波特率、字长、奇偶校验等参数要使用串口功能,必须对串口通信相关参数初始化,其库函数如下:
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef*USART_InitStruct);
想必不用说,大家也知道第一个参数是什么意思,它是用来选择串口。第二个参数是一个结构体指针变量,结构体类型是 USART_InitTypeDef,其内包含了串口初始化的成员变量。下面我们就来看下这个结构体:
typedef struct
{
uint32_t USART_BaudRate; //波特率
uint16_t USART_WordLength; //字长
uint16_t USART_StopBits; //停止位
uint16_t USART_Parity; //校验位
uint16_t USART_Mode; //USART 模式
uint16_t USART_HardwareFlowControl; //硬件流控制
} USART_InitTypeDef;
下面就来简单介绍下每个成员变量的功能: 波特率设置。常用的波特率为4800、9600、115200 。标准库函数会根据设定值计算得到USARTDIV 值, 并设置USART_BRR寄存器值。 数据帧字长。可以选择为8 位或者9 位,通过USART_CR1寄存器的M 位的值决定。如果没有使能奇偶校验控制,一般使用8 数据位;如果使能了奇偶校验则一般设置为9 数据位。 停止位设置。可选0.5 个、1 个、1.5 个和2 个停止位,它设定USART_CR2 寄存器的STOP[1:0]位的值,一般我们选择1 个停止位。 奇偶校验控制选择。可选USART_Parity_No( 无校验) 、USART_Parity_Even( 偶校验) 以及USART_Parity_Odd( 奇校验) ,它设定USART_CR1 寄存器的PCE 位和PS 位的值。 USART 模式选择。可以为USART_Mode_Rx 和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定USART_CR1 寄存器的RE 位和TE 位。**USART_HardwareFlowControl:**硬件流控制选择。只有在硬件流控制模式才有效, 可以选择无硬件流USART_HardwareFlowControl_None 、RTS 控制USART_HardwareFlowControl_RTS、CTS 控制USART_HardwareFlowControl_CTS、RTS 和CTS 控制USART_HardwareFlowControl_RTS_CTS。
了解结构体成员功能后,就可以进行配置,例如我们配置USART1,如下:
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 9600;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8 位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl =USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
配置好串口后,我们还需要使能它,使能串口库函数如下:
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
例如我们要使能USART1,如下:
USART_Cmd(USART1, ENABLE); //使能串口1
对串口中断类型和使能设置的函数如下:
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState);
第一个参数用来选择串口,第二个参数用来选择串口中断类型,第三个参数用来使能或者失能对应中断。由于串口中断类型比较多,所以使用哪种中断,我们就需要对它进行配置。比如在接收到数据的时候( RXNE 读数据寄存器非空),我们要产生中断,那么我们开启中断的方法是:
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断
又比如我们发送完数据时,要产生中断,可以配置如下:
USART_ITConfig(USART1,USART_IT_TC, ENABLE);
对应的串口中断类型可在stm32f10x_usart.h 中查找到,如下:
#define USART_IT_PE ((uint16_t)0x0028)
#define USART_IT_TXE ((uint16_t)0x0727)
#define USART_IT_TC ((uint16_t)0x0626)
#define USART_IT_RXNE ((uint16_t)0x0525)
#define USART_IT_IDLE ((uint16_t)0x0424)
#define USART_IT_LBD ((uint16_t)0x0846)
#define USART_IT_CTS ((uint16_t)0x096A)
#define USART_IT_ERR ((uint16_t)0x0060)
#define USART_IT_ORE ((uint16_t)0x0360)
#define USART_IT_NE ((uint16_t)0x0260)
#define USART_IT_FE ((uint16_t)0x0160)
在上一步我们已经使能了串口的接收中断,只要使用到中断,就必需对NVIC初始化,NVIC 初始化库函数是NVIC_Init(),这个在前面讲解STM32 中断时就已经介绍过,不清楚的可以回过头看下。
最后我们还需要编写一个串口中断服务函数,通过中断函数处理串口产生的相关中断。串口中断服务函数名在STM32F1 启动文件内就有,USART1 中断函数名如下:
USART1_IRQHandler
因为串口的中断类型有很多,所以进入中断后,我们需要在中断服务函数开头处通过状态寄存器的值判断此次中断是哪种类型,然后做出相应的控制。库函数中用来读取串口中断状态标志位的函数如下:
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_tUSART_IT);
此函数功能是判断USARTx 的中断类型USART_IT 是否产生中断,例如我们要判断USART1 的接收中断是否产生,可以调用此函数:
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
...//执行USART1 接收中断内控制
}
如果产生接收中断,那么调用USART_GetITStatus 函数后返回值为1,就会进入到if 函数内执行中断控制功能程序。否则就不会进入中断处理程序。在编写串口中断服务函数时,最后通常会调用一个清除中断标志位的函数,如下:
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
第二个参数为状态标志选项,可选参数可在stm32f10x_usart.h 中查找到,如下:
#define USART_FLAG_CTS
#define USART_FLAG_LBD
#define USART_FLAG_TXE
#define USART_FLAG_TC
#define USART_FLAG_RXNE
#define USART_FLAG_IDLE
#define USART_FLAG_ORE
#define USART_FLAG_NE
#define USART_FLAG_FE
#define USART_FLAG_PE
比如本实验中判断串口进入接收中断后,我们就会把串口接收寄存器内数据读取出来,然后再通过串口发送至上位机,等待发送完成后我们就会清除发送完成标志位 USART_FLAG_TC。代码如下:
void USART1_IRQHandler(void) //串口1 中断服务程序
{
u8 r;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
USART_SendData(USART1,r);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
}
USART_ClearFlag(USART1,USART_FLAG_TC);
}
串口接收函数是:
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
串口发送函数是:
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
库函数中还有一个函数用来读取串口状态标志位:
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_tUSART_FLAG);
USART_GetITStatus 与USART_GetFlagStatus 功能类似, 区别就是USART_GetITStatus 函数会先判断是否使能串口中断,使能后才读取状态标志,而USART_GetFlagStatus 函数直接读取状态标志。将以上几步全部配置好后,我们就可以正常使用串口中断了。
硬件设计
在开发板上,含有一个USB 转串口,一个RS232 串口,其硬件电路如图所示:
PZ6806L 开发板串口3 电路: 从图中可以看出,通过CH340 芯片把STM32F1 的串口1 与 PC 机的USB 口进行连接,实现串口通信。根据前面介绍,串口通信需将数据收发管脚交叉连接,所以可以看到在CH340 芯片的2 和3 脚已做处理。电路中其他部分是自动下载电路部分,目的是控制BOOT 的启动模式与复位。 STM32F1 使用的是串口3,即PB9 和PB10 管脚。此电路是按照RS232 接口标准搭建,使用了一个DB9 的母头,电平转换芯片使用的是SP3232,与MAX3232 一样。母头可作为下位机和上位机PC 进行串口通信,需使用交叉型串口线。电路中还有一个P232 插针,它是用来选择是USART3 功能还是IIC2 功能,如果我们需要使用这个DB9 做串口通信,需将P232 插针的3、5 短接,4、6 短接,如果做EEPROM 实验时需将P232 插针的1、3 短接,2、4 短接,开发板出厂时P232 插针默认选择的是IIC2 功能。
本章实验所采用的是串口1 与PC 机USB 口进行通信,只需使用一根USB 线连接电脑USB 口即可实现串口通信。若大家需要使用DB9 接口,可以选择图2 电路,此时使用的是串口3,程序和串口1 通信是类似的,只需修改串口号即可。
软件设计
所要实现的功能是:STM32F1 通过USART1 实现与PC 机对话,STM32F1的USART1 收到PC 机发来的数据后原封不动的返回给PC 机显示。同时使用D1指示灯不断闪烁提示系统正常运行。程序框架如下: (1)初始化USART1,并使能串口接收中断等 (2)编写USART1 中断函数 (3)编写主函数 在前面介绍串口配置步骤时,就已经讲解如何初始化串口。
USART1 初始化函数 要使用串口中断,我们必须先对它进行配置。USART1 初始化代码如下:
/****************************************************************
***************
* 函数名: USART1_Init
* 函数功能: USART1 初始化函数
* 输入: bound:波特率
* 输出: 无
*****************************************************************
**************/
void USART1_Init(u32 bound)
{
//GPIO 端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
/* 配置GPIO 的模式和IO 口*/
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出PA9
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA,&GPIO_InitStructure);
/* 初始化串口输入IO */
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX //串口输入PA10
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化GPIO */
//USART1 初始化设置
USART_InitStructure.USART_BaudRate = bound;//波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8 位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl =
USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_Cmd(USART1, ENABLE); //使能串口1
USART_ClearFlag(USART1, USART_FLAG_TC);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1 中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;// 抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC 寄存器
}
/****************************************************************
***************
* 函数名: USART1_IRQHandler
* 函数功能: USART1 中断函数
* 输入: 无
* 输出: 无
*****************************************************************
**************/
void USART1_IRQHandler(void) //串口1 中断服务程序
{
u8 r;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
USART_SendData(USART1,r);
while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
}
USART_ClearFlag(USART1,USART_FLAG_TC);
}
为了确认USART1 是否发生接收中断,调用了读取串口中断状态标志位函数USART_GetITStatus,如果确实产生接收中断事件,那么就会执行if 内的语句,将串口接收到的数据保存在变量r 内,然后有通过串口发送出去,通过USART_GetFlagStatus 函数读取串口状态标志,如果数据发送完成,则退出while循环语句,且清除发送完成状态标志位USART_FLAG_TC。
主函数
编写好串口初始化和中断服务函数后,接下来就可以编写主函数了,代码如下:
/****************************************************************
***************
* 函数名: main
* 函数功能: 主函数
* 输入: 无
* 输出: 无
*****************************************************************
**************/
int main()
{
u8 i=0;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组分2 组
LED_Init();
USART1_Init(9600);
while(1)
{
i++;
if(i%20==0)
{
led1=!led1;
}
delay_ms(10);
}
}
主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括SysTick 系统时钟,中断分组,LED 初始化等。然后调用我们前面编写的USART1初始化函数,这里我们设定串口通信波特率为9600。最后进入while 循环语句,不断让D1 指示间隔200ms 闪烁。如果发生接收中断事件,即会进入中断执行,执行完后回到主函数内继续运行。其实如果你学会了USART1 的使用,对于其他的串口都是类似的。