2026/1/9 15:33:16
网站建设
项目流程
网页显示站点不安全,怎么做网站注册系统,网站优化推广公司推荐,免费网络推广怎么做(1)实验平台#xff1a;普中STM32F103朱雀、玄武开发板 我们知道 STM32F1 内部含有非常多的通信接口#xff0c; 这一章我们就来学习下USART 串口通信。 本章要实现的功能是#xff1a; STM32F1 通过 USART1 实现与 PC 机对话#xff0c; STM32F1 的 USART1 收到 PC 机发来…(1)实验平台普中STM32F103朱雀、玄武开发板我们知道 STM32F1 内部含有非常多的通信接口 这一章我们就来学习下USART 串口通信。 本章要实现的功能是 STM32F1 通过 USART1 实现与 PC 机对话 STM32F1 的 USART1 收到 PC 机发来的数据后原封不动的返回给 PC 机显示。 学习本章可以参考《STM32F10x 中文参考手册》 -25 通用同步异步收发器USART 章节。 本章分为如下几部分内容21.1 通信的基本概念21.1.1 串行通信与并行通信21.1.2 异步通信与同步通信21.1.3 单工、 半双工与全双工通信21.1.4 通信速率21.2 STM32F1 的 USART 介绍21.2.1 串口通信简介21.2.2 USART 简介21.2.3 USART 结构框图21.3 USART 串口通信配置步骤21.4 硬件设计21.5 软件设计21.5.1 USART1 初始化函数21.5.2 USART1 中断函数21.5.3 主函数21.6 实验现象课后作业21.1 通信的基本概念我们知道 STM32F1 芯片内含有非常多的通信接口 学习这些通信接口前 我们很有必要了解下通信的基本概念。 通信的方式可以分为多种 按照数据传送方式可分为串行通信和并行通信。 按照通信的数据同步方式 可分为异同通信和同步通信。 按照数据的传输方向又可分为单工、 半双工和全双工通信。 下面我们就来简单介绍这几种通信方式。21.1.1 串行通信与并行通信1 串行通信串行通信是指使用一条数据线 将数据一位一位地依次传输 每一位数据占据一个固定的时间长度。 其只需要少数几条线就可以在系统间交换信息 特别适用于计算机与计算机、 计算机与外设之间的远距离通信。 如下图所示串行通信的特点 传输线少 长距离传送时成本低 且可以利用电话网等现成的设备 但数据的传送控制比并行通信复杂。2 并行通信并行通信通常是将数据字节的各位用多条数据线同时进行传送 通常是 8位、 16 位、 32 位等数据一起传输。 如下图所示并行通信的特点 控制简单、 传输速度快 由于传输线较多 长距离传送时成本高且接收方的各位同时接收存在困难 抗干扰能力差。21.1.2 异步通信与同步通信1 异步通信异步通信是指通信的发送与接收设备使用各自的时钟控制数据的发送和接收过程。 为使双方的收发协调 要求发送和接收设备的时钟尽可能一致。异步通信是以字符构成的帧 为单位进行传输 字符与字符之间的间隙时间间隔 是任意的 但每个字符中的各位是以固定的时间传送的 即字符之间不一定有“位间隔” 的整数倍的关系 但同一字符内的各位之间的距离均为“位间隔” 的整数倍。 如下图所示异步通信的特点 不要求收发双方时钟的严格一致 实现容易 设备开销较小 但每个字符要附加 23 位用于起止位 各帧之间还有间隔 因此传输效率不高。2 同步通信同步通信时要建立发送方时钟对接收方时钟的直接控制 使双方达到完全同步。 此时 传输数据的位之间的距离均为“位间隔” 的整数倍 同时传送的字符间不留间隙 即保持位同步关系 也保持字符同步关系。 发送方对接收方的同步可以通过两种方法实现。 如下图所示21.1.3 单工、 半双工与全双工通信1 单工通信单工是指数据传输仅能沿一个方向 不能实现反向传输。 如下图所示2 半双工通信半双工是指数据传输可以沿两个方向 但需要分时进行。 如下图所示3 全双工通信全双工是指数据可以同时进行双向传输。 如下图所示21.1.4 通信速率衡量通信性能的一个非常重要的参数就是通信速率 通常以比特率(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 那么每个码元可以表示四种状态 即两个二进制比特位 所以码元数是二进制比特位数的一半 这个时候的波特率为比特率的一半。 由于很多常见的通信中一个码元都是表示两种状态所以我们常常直接以波特率来表示比特率。21.2 STM32F1 的 USART 介绍21.2.1 串口通信简介串口通信(Serial Communication) 是指外设和计算机间 通过数据信号线、地线等 按位进行传输数据的一种通信方式 属于串行通信方式。 串口是一种接口标准 它规定了接口的电气标准 没有规定接口插件电缆以及使用的协议。1 接口标准串口通信的接口标准有很多 有 RS-232C、 RS-232、 RS-422A、 RS-485 等。常用的就是 RS-232 和 RS-485。 RS-232 其实是 RS-232C 的改进 原理是一样的。这里我们就以 RS-232C 接口进行讲解 RS-485 在后面章节中会介绍。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 为-3~-15V 的电压②逻辑 0 为 3~15V 的电压在 RTS、 CTS、 DSR、 DTR 和 DCD 等控制线上 ①信号有效ON 状态 为 3~15V 的电压②信号无效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、 15200 等。“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。21.2.2 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 输出函数将这些信息打印到串口助手上显示 这样一来就给我们调试程序带来了极大的方便。21.2.3 USART 结构框图其实 USART 能够有这么多功能 取决于它的内部结构。 其内部结构框图如下图所示我们把上图分为几个模块进行介绍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 位控制。①发送器发送器可发送 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 中断事件被连接到相同的中断向量 如下由于文章篇幅限制 具体介绍可以参考《STM32F10x 中文参考手册》 25.4 USART 中断请求章节内容。4 标号 4 波特率生成波特率的概念在前面介绍比特率的时候已经提过 常用的串口通信中都把波特率当作比特率。 波特率越大 传输速度就越快。接收器和发送器 Rx 和 Tx 的波特率均设置为相同值。 波特率计算公式如下其中 fCK 为 USART 时钟频率 USARTDIV 是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。 其中 DIV_Mantissa[11:0]位定义 USARTDIV的整数部分 DIV_Fraction[3:0]位定义 USARTDIV 的小数部分。串口通信中常用的波特率为 4800、 9600、 115200 等。21.3 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_PinGPIO_Pin_9;//TX //串口输出 PA9 GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA,GPIO_InitStructure); /* 初始化串口输入 IO */ GPIO_InitStructure.GPIO_PinGPIO_Pin_10;//RX //串口输入 PA10 GPIO_InitStructure.GPIO_ModeGPIO_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_ModeUSART 模式选择。 可以为 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 115200;//波特率设置 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); //初始化串口 14 使能串口配置好串口后 我们还需要使能它 使能串口库函数如下void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);例如我们要使能 USART1 如下USART_Cmd(USART1, ENABLE); //使能串口 15 设置串口中断类型并使能对串口中断类型和使能设置的函数如下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)6 设置串口中断优先级 使能串口中断通道在上一步我们已经使能了串口的接收中断 只要使用到中断 就必需对 NVIC初始化 NVIC 初始化库函数是 NVIC_Init() 这个在前面讲解 STM32 中断时就已经介绍过 不清楚的可以回过头看下。7 编写串口中断服务函数最后我们还需要编写一个串口中断服务函数 通过中断函数处理串口产生的相关中断。 串口中断服务函数名在 STM32F1 启动文件内就有 USART1 中断函数名如下USART1_IRQHandler因为串口的中断类型有很多 所以进入中断后 我们需要在中断服务函数开头处通过状态寄存器的值判断此次中断是哪种类型 然后做出相应的控制。 库函数中用来读取串口中断状态标志位的函数如下ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_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 ((uint16_t)0x0200) #define USART_FLAG_LBD ((uint16_t)0x0100) #define USART_FLAG_TXE ((uint16_t)0x0080) #define USART_FLAG_TC ((uint16_t)0x0040) #define USART_FLAG_RXNE ((uint16_t)0x0020) #define USART_FLAG_IDLE ((uint16_t)0x0010) #define USART_FLAG_ORE ((uint16_t)0x0008) #define USART_FLAG_NE ((uint16_t)0x0004) #define USART_FLAG_FE ((uint16_t)0x0002) #define USART_FLAG_PE ((uint16_t)0x0001)比如判断串口进入接收中断后 我们就会把串口接收寄存器内数据读取出来 然后再通过串口发送至上位机 等待发送完成后我们就会清除发送完成标志位 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_t USART_FLAG);USART_GetITStatus 与 USART_GetFlagStatus 功能类似 区别就是USART_GetITStatus 函数会先判断是否使能串口中断 使能后才读取状态标志而 USART_GetFlagStatus 函数直接读取状态标志。将以上几步全部配置好后 我们就可以正常使用串口中断了。21.4 硬件设计在我们开发板上 含有一个 USB 转串口 两个 RS232 串口。 其硬件电路如下图①、 ②所示①USB 转串口可用于程序下载、 串口 1 通信、 供电。②RS232 串口可用于串口 2、 3 的 RS232 通信。从图①中可以看出 通过 CH340 芯片把 STM32F103 的串口 1 与 PC 机的 USB口进行连接 实现串口通信。 根据前面介绍 串口通信需将数据收发管脚交叉连接 所以可以看到在 P5 端子中已做处理。 如果把 P5 端子的 2 个短接片取下 开发板上该模块即是一个 USB 转 TTL 模块 可供其他单片机下载或调试一些串口模块等设备。 出厂时默认已经将 P4 端子短接片短接好 即管脚 1 和 2 短接 3 和 4短接 用户不用再去修改。 电路中其他部分是自动下载电路部分 目的是控制BOOT 的启动模式与复位。从图②中可以看出 STM32F103 使用的是串口 2 和 3 即 PA2、 PA3 和 PB10、PB11 管脚。 此电路是按照 RS232 接口标准搭建 使用了一个 DB9 的公头和母头电平转换芯片使用的是 MAX3232 与 SP3232 兼容。 母头可作为下位机和上位机PC 进行串口通信 公头可作为上位机和其他母头设备进行串口通信 当然也可以和计算机的公头进行通信 需使用交叉型串口线。 电路中还有 P10 和 P6 两个端子 P10 端子是用来选择是 RS232 公头通信还是 WIFI/蓝牙/GPS 等模块通信出厂默认我们切换的是公头通信 即 4、 6 短接 3、 5 短接。 P6 端子是用来选择是 RS232 母头通信还是 RS485 模块通信 出厂默认我们使用的是 RS232 通信 即3、 5 短接 4、 6 短接。 当然如果你要使用 RS485 母头通信 可以将 P6 插针的 1、3 短接 2、 4 短接。RS232 母头通信需要通过一根 USB 转 RS232 串口线与 PC 进行串口通信 还需要给开发板供电 因为 RS232 接口并没有提供电源。本章实验我们讲解的是 USART1 串口 即使用 USB 转串口模块与 PC 机进行串口通信 只需要一根 USB 数据线即可。21.5 软件设计本章所要实现的功能是 STM32F1 通过 USART1 实现与 PC 机对话 STM32F1的 USART1 收到 PC 机发来的数据后原封不动的返回给 PC 机显示。 同时使用 DS0指示灯不断闪烁提示系统正常运行。 程序框架如下1 初始化 USART1 并使能串口接收中断等2 编写 USART1 中断函数3 编写主函数在前面介绍串口配置步骤时 就已经讲解如何初始化串口。 下面我们打开“\4--实验程序\1--基础实验\13-USART 串口通信实验” 工程 在 Public 工程组中可以看到添加了 usart.c 文件 在 StdPeriph_Driver 工程组中添加了stm32f10x_usart.c 库文件。 串口操作的库函数都放在 stm32f10x_usart.c 和stm32f10x_usart.h 文件中 所以使用到串口就必须加入 stm32f10x_usart.c 文件 同时还要包含对应的头文件路径。21.5.1 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_PinGPIO_Pin_9;//TX //串口输出PA9 GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz; GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF_PP; //复用推挽输出 GPIO_Init(GPIOA,GPIO_InitStructure); /* 初始化串口输入IO */ GPIO_InitStructure.GPIO_PinGPIO_Pin_10;//RX //串口输入PA10 GPIO_InitStructure.GPIO_ModeGPIO_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_IRQChannelPreemptionPriority3;//抢占优先级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 串口的波特率 方便大家修改。21.5.2 USART1 中断函数初始化 USART1 后 接收中断就已经开启了 当上位机发送数据过来 STM32F1的串口接收寄存器内即为非空 触发接收中断 具体代码如下//串口1中断服务程序 //注意,读取USARTx-SR能避免莫名其妙的错误 u8 USART1_RX_BUF[USART1_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节. //接收状态 //bit15 接收完成标志 //bit14 接收到0x0d //bit13~0 接收到的有效字节数目 u16 USART1_RX_STA0; //接收状态标记 /******************************************************************************* * 函 数 名 : USART1_IRQHandler * 函数功能 : USART1中断函数 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void USART1_IRQHandler(void) //串口1中断服务程序 { u8 r; if(USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) //接收中断 { r USART_ReceiveData(USART1);//(USART1-DR); //读取接收到的数据 if((USART1_RX_STA0x8000)0)//接收未完成 { if(USART1_RX_STA0x4000)//接收到了0x0d { if(r!0x0a)USART1_RX_STA0;//接收错误,重新开始 else USART1_RX_STA|0x8000; //接收完成了 } else //还没收到0X0D { if(r0x0d)USART1_RX_STA|0x4000; else { USART1_RX_BUF[USART1_RX_STA0X3FFF]r; USART1_RX_STA; if(USART1_RX_STA(USART1_REC_LEN-1))USART1_RX_STA0;//接收数据错误,重新开始接收 } } } } }为了确认 USART1 是否发生接收中断 调用了读取串口中断状态标志位函数USART_GetITStatus 如果确实产生接收中断事件 那么就会执行 if 内的语句将串口接收到的数据保存在变量 r 内 然后有通过串口发送出去 通过USART_GetFlagStatus 函数读取串口状态标志 如果数据发送完成 则退出 while循环语句。这里我们设计了一个小小的接收协议 通过这个函数 配合一个数组USART1_RX_BUF[] 一个接收状态变量 USART1_RX_STA 实现对串口数据的接收管理。 USART1_RX_BUF 的大小由 USART1_REC_LEN 定义 也就是一次接收的数据最大不能超过 USART1_REC_LEN 个字节。 USART1_RX_STA 是一个接收状态变量 其各的定义如下图所示设计思路如下当接收到从电脑发过来的数据 把接收到的数据保存在 USART1_RX_BUF 中同时在接收状态寄存器USART1_RX_STA 中计数接收到的有效数据个数 当收到回车回车的表示由 2 个字节组成 0X0D 和 0X0A 的第一个字节 0X0D 时计数器将不再增加 等待 0X0A 的到来 而如果 0X0A 没有来到 则认为这次接收失败 重新开始下一次接收。 如果顺利接收到 0X0A 则标记 USART1_RX_STA 的第 15 位 这样完成一次接收 并等待该位被其他程序清除 从而开始下一次的接收 而如果迟迟没有收到 0X0D 那么在接收数据超过 USART1_REC_LEN 的时候 则会丢弃前面的数据 重新接收。21.5.3 主函数编写好串口初始化和中断服务函数后 接下来就可以编写主函数了 代码如下#include system.h #include SysTick.h #include led.h #include usart.h int main() { u8 i0; u16 t0; u16 len0; SysTick_Init(72); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组 USART1_Init(115200); LED_Init(); while(1) { if(USART1_RX_STA0x8000) { lenUSART1_RX_STA0x3fff;//得到此次接收到的数据长度 for(t0;tlen;t) { USART_SendData(USART1, USART1_RX_BUF[t]); //向串口1发送数据 while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!SET);//等待发送结束 } USART1_RX_STA0; } i; if(i%200) { LED1!LED1; } delay_ms(10); } }主函数实现的功能很简单 首先调用之前编写好的硬件初始化函数 包括SysTick 系统时钟 LED 初始化等。 然后调用我们前面编写的 USART1 初始化函数这里我们设定串口通信波特率为 115200。 最后进入 while 循环语句 判断USART1_RX_STA 最高位是否为 1 如果为 1 表示串口接收完成 然后获取接收数据的长度。 然后再通过串口发送出去。 同时不断让 LED 指示间隔 200ms 闪烁。其实如果你学会了 USART1 的使用 对于其他的串口都是类似的。21.6 实验现象工程程序编译后下载到开发板内 可以看到 DS0 指示灯不断闪烁 表示程序正常运行。 打开“\5--开发工具\4-常用辅助开发软件\串口调试助手\串口调试助手丁丁” 内串口调试助手 然后设置好波特率等参数 在字符输入框中输入所要发送的数据 点击“发送” 键后 串口助手上即会收到芯片发送过来的内容。 实验现象如下 前提一定要连接好线路 USB 线一端连接电脑 另一端连接开发板“USB 转串口模块” 上的 USB 下载口 并且在“USB 转 TTL电源” 模块上 P4 端子短接片已插上课后作业1 使用开发板上的 RS232 模块的母头 USART2 来与 PC 机通信 波特率设置为 115200。 温馨提示 需将 RS232 模块的 P6 端子上短接片短接到 CMM 端即使用 RS232 的 DB9 母头 软件中只需要将 USART1 改为 USART2 即可