STM32-Project20-21:通讯的基本概念;串口通信物理层与协议层;STM32的USART框图寄存器固件库理解;串口收发实验与指令控制实验

一 通讯基本概念;

1  串行通讯与并行通讯:
按数据传送的方式,通讯可分为串行通讯与并行通讯,串行通讯是指设备之间通过少
量数据信号线 ( 一般是 8 根以下 ) ,地线以及控制信号线,按数据位形式一位一位地传输数
据的通讯方式。而并行通讯一般是指使用 8 16 32 64 根或更多的数据线进行传输的通
讯方式,它们的通讯传输对比说明见图 20-1 ,并行通讯就像多个车道的公路,可以同时传
输多个数据位的数据,而串行通讯,而串行通讯就像单个车道的公路,同一时刻只能传输
一个数据位的数据。

2  全双工、半双工及单工通讯

3   同步通讯与异步通讯:不是同时收发而是根据相同的时钟同步采集数据线的数据

 根据通讯的数据同步方式,又分为同步和异步两种,可以根据通讯过程中是否有使用
 到时钟信号进行简单的区分。
 在同步通讯中,收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下
 双方进行协调,同步数据。通讯中通常双方会统一规定在时钟信号的上升沿或
 下降沿对数据线进行采样

在异步通讯中不使用时钟信号进行数据同步,它们直接在数据信号中穿插一些同步用
的信号位,或者把主体数据进行打包,以数据帧的格式传输数据 ,某些通讯中
还需要双方约定数据的传输速率,以便更好地同步
在同步通讯中,数据信号所传输的内容绝大部分就是有效数据,而异步通讯中会包含
有帧的各种标识符,所以同步通讯的效率更高,但是同步通讯双方的时钟允许误差较小,
而异步通讯双方的时钟允许误差较大

通讯速率

衡量通讯性能的一个非常重要的参数就是通讯速率,通常以比特率 (Bitrate) 来表示,即
每秒钟传输的二进制位数,单位为比特每秒 (bit/s) 。容易与比特率混淆的概念是“波特率”
(Baudrate) ,它表示每秒钟传输了多少个码元。而码元是通讯信号调制的概念,通讯中常用
时间间隔相同的符号来表示一个二进制数字,这样的信号称为码元。如常见的通讯传输中,
0V 表示数字 0 5V 表示数字 1 ,那么一个码元可以表示两种状态 0 1 ,所以一个码元
等于一个二进制比特位,此时波特率的大小与比特率一致;如果在通讯传输中,有 0V
2V 4V 以及 6V 分别表示二进制数 00 01 10 11 ,那么每个码元可以表示四种状态,
即两个二进制比特位,所以码元数是二进制比特位数的一半,这个时候的波特率为比特率
的一半。因为很多常见的通讯中一个码元都是表示两种状态,人们常常直接以波特率来表
示比特率,虽然严格来说没什么错误,但希望您能了解它们的区别

二  串口通信简介

1.串口通信物理层

a 电平标准

根据通讯使用的电平标准不同,串口通讯可分为 TTL 标准及 RS-232 标准

我们知道常见的电子电路中常使用 TTL 的电平标准,理想状态下,使用 5V 表示二进
制逻辑 1 ,使用 0V 表示逻辑 0 ;而为了增加串口通讯的远距离传输及抗干扰能力,它使用 -
15V 表示逻辑 1 +15V 表示逻辑 0。

 

在上面的通讯方式中,两个通讯设备的“ DB9 接口”之间通过串口信号线建立起连接,
串口信号线中使用“ RS-232 标准”传输数据信号。由于 RS-232 电平标准的信号不能直接
被控制器直接识别,所以这些信号会经过一个“电平转换芯片MA3232”转换成控制器能识别的
TTL 标准”的电平信号,才能实现通讯。
b      RS-232 信号线
在旧式的台式计算机中一般会有 RS-232 标准的 COM ( 也称 DB9 接口),

其中接线口以针式引出信号线的称为公头,以孔式引出信号线的称为母头。在计算机
中一般引出公头接口,而在调制调解器设备中引出的一般为母头,使用上图中的串口线即
可把它与计算机连接起来。通讯时,串口线中传输的信号就是使用前面讲解的 RS-232 标准
调制的。

 

上表中的是计算机端的 DB9 公头标准接法,由于两个通讯设备之间的收发信号 (RXD
TXD) 应交叉相连,所以调制调解器端的 DB9 母头的收发信号接法一般与公头的相反,
两个设备之间连接时,只要使用“直通型”的串口线连接起来即可,见图

在目前的其它工业控制使用的串口通讯中,一般只使用 RXD TXD 以及 GND 三条信
号线,直接传输数据信号,而 RTS CTS DSR DTR DCD 信号都被裁剪掉了。

c usb转串口通信

2 通信接口协议层面

串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在
串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位
组成,通讯双方的数据包格式要约定一致才能正常收发数据。

通讯的起始和停止信号
     串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一
个逻辑 0 的数据位表示,而数据包的停止信号可由 0.5 1 1.5 2 个逻辑 1 的数据位表示,
只要双方约定一致即可。
 
   有效数据
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效
数据的长度常被约定为 5 6 7 8 位长。
数据校验
在有效数据之后,有一个可选的数据校验位。由于数据通信相对更容易受到外部干扰
导致传输数据出现偏差,可以在传输过程加上校验位来解决这个问题。校验方法有奇校验
(odd) 、偶校验 (even) 0 校验 (space) 1 校验 (mark) 以及无校验 (noparity)
奇校验要求有效数据和校验位中“ 1 ”的个数为奇数,比如一个 8 位长的有效数据为:
01101001 ,此时总共有 4 个“ 1 ”,为达到奇校验效果,校验位为“ 1 ”,最后传输的数据
将是 8 位的有效数据加上 1 位的校验位总共 9 位。
偶校验与奇校验要求刚好相反,要求帧数据和校验位中“ 1 ”的个数为偶数,比如数据
帧: 11001010 ,此时数据帧“ 1 ”的个数为 4 个,所以偶校验位为“ 0 ”。
0 校验是不管有效数据中的内容是什么,校验位总为“ 0 ”, 1 校验是校验位总为“ 1

三  STM32 USART

 1.功能框图

有别于 USART 还有一个
UART(Universal Asynchronous Receiver and Transmitter) ,它是在 USART 基础上裁剪掉了同
步通信功能,只有异步通信。简单区分同步和异步就是看通信时需不需要对外提供时钟输
出,我们平时用的串口通信基本都是 UART

. ①功能引脚
TX :发送数据输出引脚。
RX :接收数据输入引脚。
SW_RX :数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引
脚。
nRTS :请求以发送 (Request To Send) n 表示低电平有效。如果使能 RTS 流控制,当
USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时,
nRTS 将被设置为高电平。该引脚只适用于硬件流控制。
nCTS :清除以发送 (Clear To Send) n 表示低电平有效。如果使能 CTS 流控制,发送
器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为
高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。
SCLK :发送器时钟输出引脚。这个引脚仅适用于同步模式。
USART 引脚在 STM32F103ZET6 芯片具体分布见表

②数据寄存器
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 使用将在 DMA 章节讲
解。
③控制器
USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元、中断控制等等。
使用 USART 之前需要向 USART_CR1 寄存器的 UE 位置 1 使能 USART UE 位用来开启
供给给串口的时钟。发送或者接收数据字长可选 8 位或 9 位,由 USART_CR1 M 位控制。
发送器
USART_CR1 寄存器的发送使能位 TE 1 时,启动数据发送,发送移位寄存器的
数据会在 TX 引脚输出,低位在前,高位在后。如果是同步模式 SCLK 也输出时钟信号。
一个字符帧发送需要三个部分:起始位 + 数据帧 + 停止位。起始位是一个位周期的低电
平,位周期就是每一位占用的时间;数据帧就是我们要发送的 8 位或 9 位数据,数据是从
最低位开始传输的;停止位是一定时间周期的高电平。
停止位时间长短是可以通过 USART 控制寄存器 2(USART_CR2) STOP[1:0] 位控制,
可选 0.5 个、 1 个、 1.5 个和 2 个停止位。默认使用 1 个停止位。 2 个停止位适用于正常
USART 模式、单线模式和调制解调器模式。 0.5 个和 1.5 个停止位用于智能卡模式。
当选择 8 位字长,使用 1 个停止位时,具体发送字符时序图见图

 

. ④小数波特率生成
波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,
单位为波特。比特率指单位时间内传输的比特数,单位 bit/s(bps) 。对于 USART 波特率与
比特率相等,以后不区分这两个概念。波特率越大,传输速率越快。
USART 的发送器和接收器使用相同的波特率。计算公式如下:

其中, f PLCK USART 时钟, USARTDIV 是一个存放在波特率寄存器 (USART_BRR)
的 一个 无符 号定 点数。 其中 DIV_Mantissa[11:0] 位 定义 USARTDIV 的 整数 部分 ,
DIV_Fraction[3:0] 位定义 USARTDIV 的小数部分。
例如: DIV_Mantissa=24(0x18) DIV_Fraction=10(0x0A) ,此时 USART_BRR 值为
0x18A ;那么 USARTDIV 的小数位 10/16=0.625 ;整数位 24 ,最终 USARTDIV 的值为
24.625
如果知道 USARTDIV 值为 27.68 ,那么 DIV_Fraction=16*0.68=10.88 ,最接近的正整数
11 ,所以 DIV_Fraction[3:0] 0xB DIV_Mantissa= 整数 (27.68)=27 ,即为 0x1B
波特率的常用值有 2400 9600 19200 115200 。下面以实例讲解如何设定寄存器值
得到波特率的值。
我们知道 USART1 使用 APB2 总线时钟,最高可达 72MHz ,其他 USART 的最高频率
36MHz 。我们选取 USART1 作为实例讲解,即 f PLCK =72MHz 。为得到 115200bps 的波特
率,此时:

校验控制
STM32F103 系列控制器 USART 支持奇偶校验。当使用校验位时,串口传输的长度将
8 位的数据帧加上 1 位的校验位总共 9 位,此时 USART_CR1 寄存器的 M 位需要设置为
1 ,即 9 数据位。将 USART_CR1 寄存器的 PCE 位置 1 就可以启动奇偶校验控制,奇偶校
验由硬件自动完成。启动了奇偶校验控制之后,在发送数据帧时会自动添加校验位,接收
数据时自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会见 USART_SR 寄存
器的 PE 位置 1,并可以产生奇偶校验中断

2.  USART 初始化结构体详解

标准库函数对每个外设都建立了一个初始化结构体,比如 USART_InitTypeDef ,结构
体成员用于设置外设工作参数,并由外设初始化配置函数,比如 USART_Init() 调用,这些
设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个
成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f10x_usart.h 文件
中,初始化库函数定义在 stm32f10x_usart.c 文件中,编程时我们可以结合这两个文件内注
释使用。
USART 初始化结构体
1 typedef struct {
2 uint32_t USART_BaudRate; // 波特率
3 uint16_t USART_WordLength; // 字长
4 uint16_t USART_StopBits; // 停止位
5 uint16_t USART_Parity; // 校验位
6 uint16_t USART_Mode; // USART 模式
7 uint16_t USART_HardwareFlowControl; // 硬件流控制
8 } USART_InitTypeDef;
5) USART_BaudRate :波特率设置。一般设置为 2400 9600 19200 115200 。标准
库函数会根据设定值计算得到 USARTDIV 值,从而设置 USART_BRR 寄存器值。
6) USART_WordLength :数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存
器的 M 位的值。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇
偶校验则一般设置为 9 数据位。
7) USART_StopBits :停止位设置,可选 0.5 个、 1 个、 1.5 个和 2 个停止位,它设定
USART_CR2 寄存器的 STOP[1:0] 位的值,一般我们选择 1 个停止位。
8) USART_Parity : 奇 偶 校 验 控 制 选 择 , 可 选 USART_Parity_No( 无校验 )
USART_Parity_Even( 偶校验 ) 以 及 USART_Parity_Odd( 奇 校 验 ) , 它 设 定
USART_CR1 寄存器的 PCE 位和 PS 位的值。
9) USART_Mode USART 模式选择,有 USART_Mode_Rx USART_Mode_Tx
允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。
10) USART_HardwareFlowControl :硬件流控制选择,只有在硬件流控制模式才有效,
可选有⑴使能 RTS 、⑵使能 CTS 、⑶同时使能 RTS CTS 、⑷不使能硬件流。
当使用同步模式时需要配置 SCLK 引脚输出脉冲的属性,标准库使用一个时钟初始化
结构体 USART_ClockInitTypeDef 来设置,该结构体内容也只有在同步模式才需要设置。
USART 时钟初始化结构体
1 typedef struct {
2 uint16_t USART_Clock; // 时钟使能控制
3 uint16_t USART_CPOL; // 时钟极性
4 uint16_t USART_CPHA; // 时钟相位
5 uint16_t USART_LastBit; // 最尾位时钟脉冲
6 } USART_ClockInitTypeDef;
1) USART_Clock :同步模式下 SCLK 引脚上时钟输出使能控制,可选禁止时钟输出
(USART_Clock_Disable) 或开启时钟输出 (USART_Clock_Enable) ;如果使用同步模
式发送,一般都需要开启时钟。它设定 USART_CR2 寄存器的 CLKEN 位的值。
2) USART_CPOL :同步模式下 SCLK 引脚上输出时钟极性设置,可设置在空闲时
SCLK 引脚为低电平 (USART_CPOL_Low) 或高电平 (USART_CPOL_High) 。它设
USART_CR2 寄存器的 CPOL 位的值。
3) USART_CPHA :同步模式下 SCLK 引脚上输出时钟相位设置,可设置在时钟第一
个变化沿捕获数据 (USART_CPHA_1Edge) 或在时钟第二个变化沿捕获数据。它设
USART_CR2 寄存器的 CPHA 位的值。 USART_CPHA USART_CPOL 配合
使用可以获得多种模式时钟关系。
4) USART_LastBit :选择在发送最后一个数据位的时候时钟脉冲是否在 SCLK 引脚
输 出 , 可 以 是 不 输 出 脉 冲 (USART_LastBit_Disable) 、 输 出 脉 冲
(USART_LastBit_Enable) 。它设定 USART_CR2 寄存器的 LBCL 位的值。
四   USART1 接发通信实验
USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留
USART 接口来实现与其他模块或者控制器进行数据传输,比如 GSM 模块, WIFI 模块、蓝
牙模块等等。在硬件设计时,注意还需要一根“共地线”。
我们经常使用 USART 来实现控制器与电脑之间的数据传输。这使得我们调试程序非
常方便,比如我们可以把一些变量的值、函数的返回值、寄存器标志位等等通过 USART
发送到串口调试助手,这样我们可以非常清楚程序的运行状态,当我们正式发布程序时再
把这些调试信息去除即可。
我们不仅仅可以将数据发送到串口调试助手,我们还可以在串口调试助手发送数据给
控制器,控制器程序根据接收到的数据进行下一步工作。
首先,我们来编写一个程序实现开发板与电脑通信,在开发板上电时通过 USART
送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开
发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。
编程要点
5) 使能 RX TX 引脚 GPIO 时钟和 USART 时钟;
6) 初始化 GPIO ,并将 GPIO 复用到 USART 上;
7) 配置 USART 参数;
8) 配置中断控制器并使能 USART 接收中断;
9) 使能 USART
10) USART 接收中断服务函数实现数据接收和发送。
#ifndef __USART_H//C文件需要add,头文件在软件设置好路径就不要自己add
#define __USART_H
#include "stm32f10x.h" 
#include <stdio.h>

// 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

void USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);


#endif
#include "usart.h"

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);/* 嵌套向量中断控制器组选择:随便来 */
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 使能中断大门 */  
  NVIC_Init(&NVIC_InitStructure);  /* 初始化配置NVIC */
}


void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	//1 GPIO初始化配置与时钟使能
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 打开串口GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);// 打开串口外设的时钟

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式:外部电平状态决定你这个引脚什么电平状态
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	
	//2 配置串口初始化结构体:要与电脑上位机软件配置一样
	USART_InitStructure.USART_BaudRate = 115200;// 配置波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置 针数据字长
	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);// 完成串口的初始化配置
	
	//3 串口中断配置:在中断服务函数把接收到数据发送给电脑
	NVIC_Configuration();//接收数据寄存器非空标志 USART_IT_RXNE
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	// 使能串口接收完成中断:中断小门
	
	//4 使能串口
	USART_Cmd(USART1, ENABLE);	    
}


void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)//发送一个字节
{
	
	USART_SendData(pUSARTx,ch);/* 发送一个字节数据到USART */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	/* 等待发送数据寄存器为空 */
}


void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)//发送8位的数组 
{
  uint8_t i;
	for(i=0; i<num; i++)
  {
	    Usart_SendByte(pUSARTx,array[i]);	 /* 发送一个字节数据到USART */
  }
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);/* 等待发送完成 */
}


void Usart_SendString( USART_TypeDef * pUSARTx, char *str)//发送字符串
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); /* 等待发送完成 */
  
}


void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)//发送一个16位数
{
	uint8_t temp_h, temp_l;
	temp_h = (ch&0XFF00)>>8;/* 取出高八位 */
	temp_l = ch&0XFF;/* 取出低八位 */
	
	USART_SendData(pUSARTx,temp_h);/* 发送高八位 */	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	USART_SendData(pUSARTx,temp_l);	/* 发送低八位 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}



//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);/* 发送一个字节数据到串口 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);			/* 等待发送完毕 */
		return (ch);
}

//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);/* 等待串口输入数据 */
		return (int)USART_ReceiveData(DEBUG_USARTx);
}

 

#include "stm32f10x.h"  
#include "led.h" 
#include "key.h"
#include "rcc.h"
#include "exti.h"
#include "systick.h"
#include "usart.h"
#include <stdio.h>

/*如果想要验证其他串口的功能功能,修改相关宏定义即可,实验以串口1为例
 单片机发送数据到电脑上位机显示:几个发送函数完成,发送数据均是8位一字节为单位
															  	或者利用单片机发送print的数据,让单片机串口重定向输出到上位机

  单片机接收数据到电脑上位机显示:单片机接收到数据后通过中断函数将接收的数据发送给电脑上位机
																	里面通过中断服务函数完成。
*/

int main(void)
{
	
	  USART_Config();
	
		//Usart_SendByte(USART1,'a');发送8位字符
		//Usart_SendHalfWord(USART1, 0xffff);发送16位数
		 Usart_SendString(USART1, "abcdefg\n");//发送字符串
		 printf("用串口重定向输出到上位机\n");
	  
	
	while (1){
	 
	}
	
}



五 USART1 指令控制 灯实验  

. 编程要点
1) 初始化配置 RGB 彩色灯 GPIO
2) 使能 RX TX 引脚 GPIO 时钟和 USART 时钟;
3) 初始化 GPIO ,并将 GPIO 复用到 USART 上;
4) 配置 USART 参数;
5) 使能 USART
6) 获取指令输入,根据指令控制 RGB 彩色灯。
与上一个实验不同的是我们这里不使用接收中断,而是靠查询标志位的方式来实现接收。
#include "stm32f10x.h"  
#include "led.h" 
#include "key.h"
#include "rcc.h"
#include "exti.h"
#include "systick.h"
#include "usart.h"
#include <stdio.h>

/*

给单片机发送命令,单片机解析命令,做出一些控制LED反应
上位机发送一个字符1 LED0就反转一次

*/

int main(void)
{
		uint8_t ch;
	  USART_Config();
	  LED0_GPIO_Config();
	
	
	 while (1){
	  ch = getchar();//上位机发送ch到单片机
		printf("ch = %c\n",ch);
		 if (ch == '1')
			 LED0_TOGGLE;
	}
	
}



#include "usart.h"

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);/* 嵌套向量中断控制器组选择:随便来 */
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;/* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; /* 使能中断 */
  NVIC_Init(&NVIC_InitStructure);  /* 初始化配置NVIC */
}


void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	//1 GPIO初始化配置
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 打开串口GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);// 打开串口外设的时钟

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式:外部电平状态决定你这个引脚什么电平状态
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	
	//2 配置串口初始化结构体:要与电脑上位机软件配置一样
	USART_InitStructure.USART_BaudRate = 115200;// 配置波特率
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置 针数据字长
	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);// 完成串口的初始化配置
	
	//3 串口中断优先级配置:在中断服务函数把接收到数据发送给电脑
//	NVIC_Configuration();
	//USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);	// 使能串口接收中断
	
	//4 使能串口
	USART_Cmd(USART1, ENABLE);	    
}


void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)//发送一个字节
{
	
	USART_SendData(pUSARTx,ch);/* 发送一个字节数据到USART */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	/* 等待发送数据寄存器为空 */
}


void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)//发送8位的数组 
{
  uint8_t i;
	for(i=0; i<num; i++)
  {
	    Usart_SendByte(pUSARTx,array[i]);	 /* 发送一个字节数据到USART */
  }
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);/* 等待发送完成 */
}


void Usart_SendString( USART_TypeDef * pUSARTx, char *str)//发送字符串
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET); /* 等待发送完成 */
  
}


void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)//发送一个16位数
{
	uint8_t temp_h, temp_l;
	temp_h = (ch&0XFF00)>>8;/* 取出高八位 */
	temp_l = ch&0XFF;/* 取出低八位 */
	
	USART_SendData(pUSARTx,temp_h);/* 发送高八位 */	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	USART_SendData(pUSARTx,temp_l);	/* 发送低八位 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}



//重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);/* 发送一个字节数据到串口 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);			/* 等待发送完毕 */
		return (ch);
}

//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);/* 等待串口输入数据 */
		return (int)USART_ReceiveData(DEBUG_USARTx);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dpq666dpq666

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值