16 . USART 串口通信实验

USART 串口通信实验

我们知道STM32F1 内部含有非常多的通信接口,我们就来学习下USART 串口通信。要实现的功能是:STM32F1 通过USART1 实现与PC 机对话,STM32F1 的USART1 收到PC 机发来的数据后原封不动的返回给PC 机显示。分为如下几部分内容:

  1. 通信的基本概念
  2. STM32F4 的USART 介绍
  3. USART 串口通信配置步骤
  4. 硬件设计
  5. 软件设计

1 . 通信的基本概念

我们知道STM32F1 芯片内含有非常多的通信接口,学习这些通信接口前,我们很有必要了解下通信的基本概念。通信的方式可以分为多种:

  • 按照数据传送方式可分为串行通信和并行通信
  • 按照通信的数据同步方式,可分为异同通信和同步通信
  • 按照数据的传输方向又可分为单工、半双工和全双工通信
    下面我们就来简单介绍这几种通信方式:

串行通信与并行通信
(1)串行通信
串行通信是指使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。其只需要少数几条线就可以在系统间交换信息,特别适用于计算机与计算机、计算机与外设之间的远距离通信。如图所示:
在这里插入图片描述
串行通信的特点:传输线少,长距离传送时成本低,且可以利用电话网等现成的设备,但数据的传送控制比并行通信复杂。

(2)并行通信
并行通信通常是将数据字节的各位用多条数据线同时进行传送,通常是8位、16 位、32 位等数据一起传输。如图所示:
在这里插入图片描述
并行通信的特点:控制简单、传输速度快;由于传输线较多,长距离传送时成本高且接收方的各位同时接收存在困难,抗干扰能力差。

异步通信与同步通信
(1)异步通信
异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。为使双方的收发协调,要求发送和接收设备的时钟尽可能一致。异步通信是以字符(构成的帧)为单位进行传输,字符与字符之间的间隙(时间间隔)是任意的,但每个字符中的各位是以固定的时间传送的,即字符之间不一定有“位间隔”的整数倍的关系,但同一字符内的各位之间的距离均为“位间隔”的整数倍。异步通信数据格式如图所示:
在这里插入图片描述
在这里插入图片描述

异步通信的特点:不要求收发双方时钟的严格一致,实现容易,设备开销较小,但每个字符要附加2~3 位用于起止位,各帧之间还有间隔,因此传输效率不高。

(2)同步通信
同步通信时要建立发送方时钟对接收方时钟的直接控制,使双方达到完全同步。此时,传输数据的位之间的距离均为“位间隔”的整数倍,同时传送的字符间不留间隙,即保持位同步关系,也保持字符同步关系。发送方对接收方的同步可以通过两种方法实现。同步通信数据格式如图所示:
在这里插入图片描述
单工、半双工与全双工通信
(1)单工通信
单工是指数据传输仅能沿一个方向,不能实现反向传输。如下图所示:
在这里插入图片描述

(2)半双工通信
半双工是指数据传输可以沿两个方向,但需要分时进行。如下图所示:
在这里插入图片描述

(3)全双工通信
全双工是指数据可以同时进行双向传输。如下图所示:
在这里插入图片描述

通信速率
衡量通信性能的一个非常重要的参数就是通信速率,通常以**比特率(Bitrate)**来表示。比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps)。如每秒钟传送240 个字符,而每个字符格式包含10 位(1 个起始位、1 个停止位、8个数据位),这时的比特率为:
10 位×240 个/秒= 2400 bps
在后面会遇到一个“波特率”的概念,它表示每秒钟传输了多少个码元。而码元是通信信号调制的概念,通信中常用时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。如常见的通信传输中,用0V 表示数字0, 5V 表示数字1,那么一个码元可以表示两种状态0 和1,所以一个码元等于一个二进制比特位,此时波特率的大小与比特率一致;如果在通信传输中,有0V、2V、
4V 以及6V 分别表示二进制数00、01、10、11,那么每个码元可以表示四种状态,即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率的一半。由于很多常见的通信中一个码元都是表示两种状态,所以我们常常直接以波特率来表示比特率。

STM32F1 的USART 介绍

串口通信简介
串口通信(Serial Communication),是指外设和计算机间通过数据信号线、地线等按位进行传输数据的一种通信方式,属于串行通信方式。串口是一种接口标准,它规定了接口的电气标准,没有规定接口插件电缆以及使用的协议。
(1)接口标准
串口通信的接口标准有很多,有RS-232C、RS-232、RS-422A、RS-485 等。常用的就是RS-232 和RS-485。RS-232 其实是RS-232C 的改进,原理是一样的。这里我们就以RS-232C 接口进行讲解。RS-232C 是EIA(美国电子工业协会)1969 年修订RS-232C 标准。RS-232C定义了数据终端设备(DTE)与数据通信设备(DCE)之间的物理接口标准。RS-232C 接口规定使用25 针连接器,简称DB25,连接器的尺寸及每个插针的排列位置都有明确的定义,如图所示:
在这里插入图片描述
RS-232C 还有一种9 针的非标准连接器接口,简称DB9。串口通信使用的大多都是DB9 接口。DB25 和DB9 接头有公头和母头之分,其中带针状的接头是公头,而带孔状的接头是母头。9 针串口线的外观图如图所示:
在这里插入图片描述
中可以看到公头和母头的管脚定义顺序是不一样,这一点需要特别注意。这些管脚都有什么作用呢?9 针串口和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 的电压

由此可见,RS-232C 是用正负电压来表示逻辑状态与晶体管-晶体管逻辑集成电路(TTL)以高低电平表示逻辑状态的规定正好相反而我们STM32 芯片使用的就是TTL 电平,所以要实现STM32 与计算机的串口通信,需要进行TTL与RS-232C电平转换,通常使用的电平转换芯片是MAX3232。在串口通信中通常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-N-8-1 格式。“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。

USART 简介
USART 即通用同步异步收发器,它能够灵活地与外部设备进行全双工数据交换,满足外部设备对工业标准 NRZ 异步串行数据格式的要求。UART 即通用异步收发器,它是在USART 基础上裁剪掉了同步通信功能,同步和异步主要看其时钟是否需要对外提供,这个前面也介绍了, 我们平时使用的串口通信基本上都是UART。STM32F103ZET6 芯片含有3 个USART,2 个UART 外设,它们都具有串口通信功能。USART 支持同步单向通信和半双工单线通信;还支持LIN(域互连网络)、智能卡协议与IrDA(红外线数据协会) SIR ENDEC 规范,以及调制解调器操作(CTS/RTS)。而且,它还支持多处理器通信和DMA 功能,使用DMA 可实现高速数据通信。USART 通过小数波特率发生器提供了多种波特率。USART 在STM32 中应用最多的是printf 输出调试信息,当我们需要了解程序内的一些变量数据信息时,可以通过printf 输出函数将这些信息打印到串口助手上显示,这样一来就给我们调试程序带来了极大的方便。

USART 结构框图
其实USART 能够有这么多功能,取决于它的内部结构。其内部结构框图如图:

在这里插入图片描述
我们把图21.2.4 分为几个模块进行介绍:
(1)标号1:功能引脚

  • TX:发送数据输出引脚。
  • RX:接收数据输入引脚。
  • SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚
  • nRTS:请求以发送(Request To Send),n 表示低电平有效。如果使能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 之前需要向USART_CR1 寄存器的UE 位置1 使能USART。发送或者接收数据字长可选8 位或9 位,由USART_CR1 的M 位控制。
    1.发送器
    发送器可发送8 位或9 位的数据,具体取决于M 位的状态。发送使能位(TE) 置1 时,发送移位寄存器中的数据在TX 引脚输出,如果是同步通信模式,相应的时钟脉冲在SCLK 引脚输出。
    2.接收器
    如果将USART_CR1 寄存器的RE 位置1,使能USART 接收,使得接收器在RX 线开始搜索起始位。在确定到起始位后就根据RX 线电平状态把数据存放在接收移位寄存器内。接收完成后就把接收移位寄存器数据移到RDR 内,并把USART_SR 寄存器RXNE 位置1,同时如果USART_CR2 寄存器的RXNEIE 置1的话可以产生中断。
    3.中断控制
    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 文件中)

(1)使能串口时钟及GPIO 端口时钟

前面说过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 时钟

(2)GPIO 端口模式设置,设置串口对应的引脚为复用功能

因为使用引脚的串口功能,所以在配置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;

下面就来简单介绍下每个成员变量的功能:
USART_BaudRate: 波特率设置。常用的波特率为4800、9600、115200 。标准库函数会根据设定值计算得到USARTDIV 值, 并设置USART_BRR寄存器值。
USART_WordLength: 数据帧字长。可以选择为8 位或者9 位,通过USART_CR1寄存器的M 位的值决定。如果没有使能奇偶校验控制,一般使用8 数据位;如果使能了奇偶校验则一般设置为9 数据位。
USART_StopBits: 停止位设置。可选0.5 个、1 个、1.5 个和2 个停止位,它设定USART_CR2 寄存器的STOP[1:0]位的值,一般我们选择1 个停止位。
USART_Parity: 奇偶校验控制选择。可选USART_Parity_No( 无校验) 、USART_Parity_Even( 偶校验) 以及USART_Parity_Odd( 奇校验) ,它设定USART_CR1 寄存器的PCE 位和PS 位的值。
USART_Mode: 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

(5)使能串口

配置好串口后,我们还需要使能它,使能串口库函数如下:

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);

例如我们要使能USART1,如下:

USART_Cmd(USART1, ENABLE); //使能串口1

(6)设置串口中断类型并使能

对串口中断类型和使能设置的函数如下:

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)

(7)设置串口中断优先级,使能串口中断通道

在上一步我们已经使能了串口的接收中断,只要使用到中断,就必需对NVIC初始化,NVIC 初始化库函数是NVIC_Init(),这个在前面讲解STM32 中断时就已经介绍过,不清楚的可以回过头看下。

(8)编写串口中断服务函数

最后我们还需要编写一个串口中断服务函数,通过中断函数处理串口产生的相关中断。串口中断服务函数名在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_Init()函数中,首先使能USART1 串口及端口时钟,并初始化GPIO为复用功能。其次配置串口结构体USART_InitTypeDef,使能串口并开启接收中断,为了防止串口发送状态标志位的影响,我们清除下串口状态标志位(TC),最后配置相应的NVIC 并使能对应中断通道,我们将USART1 的抢占优先级设置为3,响应优先级设置为3。这一过程在前面步骤介绍中已经提了。USART1_Init()函数有一个参数bound,用来设置USART1 串口的波特率,方便大家修改。 USART1 中断函数初始化USART1 后,接收中断就已经开启了,当上位机发送数据过来,STM32F1的串口接收寄存器内即为非空,触发接收中断,具体代码如下:

/****************************************************************
***************
* 函数名: 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 的使用,对于其他的串口都是类似的。

  • 6
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值