STM32笔记_9(USART—串口通讯)

USART—串口通讯

串口通讯协议简介

串口通讯 (Serial Communication) 是一种设备间非常常用的串行通讯方式,通常用来调试。

物理层

RS-232 标准的串口设备间常见的通讯结构见图串口通讯结构图 。

两个通讯设备的“DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232 标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL 标准”的电平信号,才能实现通讯

电平标准

串口通讯可分为 TTL 标准RS-232 标准,见表 TTL 电平标准与RS232 电平标准 。

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

RS232 与 TTL 电平校准表示同一个信号时的对比见图 RS-232 与 TTL 电平标准下表示同一个信号。

因为控制器一般使用 TTL 电平标准,所以常常会使用 MA3232 芯片对 TTL 及 RS-232 电平的信号进行互相转换。

RS-232 信号线

RS-232 串口标准常用于计算机、路由与调制调解器 (MODEN,俗称“” ) 之间的通讯,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由) 和数据通讯设备DCE(调制调解器)。

在旧式的台式计算机中一般会有 RS-232 标准的 COM 口 (也称 DB9 接口),见图电脑主板上的COM 口及串口线 。

接线口以式引出信号线的称为公头,以式引出信号线的称为母头

计算机中一般引出公头接口,而在调制调解器设备中引出的一般为母头

通讯时,串口线中传输的信号就是使用前面讲解的 RS-232 标准调制的。

DB9 接口中的公头及母头的各个引脚的标准信号线接法见图 DB9 标准的公头及母头接法 及表 DB9 信号线说明 。

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

串口线中的 RTS、 CTS、 DSR、 DTR 及 DCD 信号,使用逻辑 1 表示信号有效,逻辑 0 表示信号无效

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

协议层

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

波特率

串口异步通讯,异步通讯中由于没有时钟信号 (如前面讲解的 DB9 接口中是没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,图串口数据包的基本组成 中用虚线分开的每一格就是代表一个码元。常见的波特率为 4800、 9600、 115200 等。

通讯的起始和停止信号

串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 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 简介

通用同步异步收发器 (Universal Synchronous Asynchronous Receiver and Transmitter) 是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换

有别于 USART 还有一个 UART(UniversalAsynchronous Receiver and Transmitter),它是在 USART 基础上裁剪掉了同步通信功能,只有异步通信

串行通信一般是以帧格式传输数据,即是一帧一帧的传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息

USART 满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。 USART 支持同步单向通信和半双工单线通信;还支持局域互连网络 LIN、智能卡 (SmartCard) 协议与 lrDA(红外线数据协会) SIR ENDEC 规范。

USART 功能框图

功能引脚

TX:发送数据输出引脚。

RX:接收数据输入引脚。

SW_RX:数据接收引脚,只用于单线和智能卡模式,属于内部引脚,没有具体外部引脚。

nRTS:请求以发送 (Request To Send), n 表示低电平有效。如果使能 RTS 流控制,当 USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时, nRTS 将被设置为高电平。该引脚只适用于硬件流控制。(一般不用)

nCTS:清除以发送 (Clear To Send), n 表示低电平有效。如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。(一般不用)

SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式

数据寄存器

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 传输,可以实现高速数据传输。

控制器

USART 有专门控制发送的发送器、控制接收的接收器,还有唤醒单元中断控制等等。使用USART 之前需要向 USART_CR1 寄存器的 UE 位置 1 使能 USART, UE 位用来开启供给给串口的时钟

发送器

当 USART_CR1 寄存器的发送使能位 TE 置 1 时,启动数据发送,发送移位寄存器的数据会在 TX引脚输出,低位在前,高位在后

一个字符帧发送需要三个部分:起始位 + 数据帧 + 停止位

起始位是一个位周期的低电平位周期就是每一位占用的时间;数据帧就是我们要发送的 8 位或 9 位数据,数据是从最低位开始传输的停止位是一定时间周期的高电平

停止位时间长短是可以通过 USART 控制寄存器 2(USART_CR2) 的 STOP[1:0] 位控制,可选 0.5个、 1 个、 1.5 个和 2 个停止位。默认使用 1 个停止位。 2 个停止位适用于正常 USART 模式、单线模式和调制解调器模式。 0.5 个和 1.5 个停止位用于智能卡模式。

当选择 8 位字长,使用 1 个停止位时,具体发送字符时序图见图字符发送时序图 。

当发送使能位 TE 置 1 之后,发送器开始会先发送一个空闲帧 (一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据。在写入最后一个数据后,需要等待 USART 状态寄存器 (USART_SR) 的 TC 位为 1,表示数据传输完成,如果 USART_CR1 寄存器的 TCIE 位置1,将产生中断。

接收器

USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,使得接收器在 RX 线开始搜索起始位

确定到起始位后就根据 RX 线电平状态把数据存放在接收移位寄存器内

接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置 1,同时如果USART_CR2 寄存器的 RXNEIE 置 1 的话可以产生中断

小数波特率生成

波特率指数据信号对载波的调制速率,它用单位时间内载波调制状态改变次数来表示,单位为波特

比特率单位时间内传输的比特数单位 bit/s(bps)。

对于 USART 波特率与比特率相等

波特率越大,传输速率越快

USART 的发送器和接收器使用相同的波特率。计算公式如下:

fPLCK 为 USART 时钟, USARTDIV 是一个存放在波特率寄存器 (USART_BRR) 的一个无符号定点数。其中 DIV_Mantissa[11:0] 位定义 USARTDIV 的整数部分, DIV_Fraction[3:0] 位定义USARTDIV 的小数部分。

我们知道 USART1 使用 APB2 总线时钟,最高可达 72MHz,USART1时钟最高16MHz,其他 USART 的最高频率为 36MHz。

我们选取 USART1 作为实例讲解,即 fPLCK=72MHz。为得到 115200bps 的波特率,此时:115200 =7200000016 ∗ USARTDIV。

解得 USARTDIV=39.0625,可算得 DIV_Fraction=0.0625*16=1=0x01, DIV_Mantissa=39=0x27,即应该设置 USART_BRR 的值为 0x271。

校验控制

STM32F103 系列控制器 USART 支持奇偶校验。当使用校验位时,串口传输的长度将是 8 位的数据帧加上 1 位的校验位总共 9 位,此时 USART_CR1 寄存器的 M 位需要设置为 1,即 9 数据位。

将 USART_CR1 寄存器的 PCE 位置 1 就可以启动奇偶校验控制,奇偶校验由硬件自动完成。

使能了奇偶校验控制后,每个字符帧的格式将变成:起始位 + 数据帧 + 校验位 + 停止位

中断控制

USART 初始化结构体详解

1) USART_BaudRate:波特率设置。一般设置为 2400、 9600、 19200、 115200。标准库函数会根据设定值计算得到 USARTDIV 值,从而设置 USART_BRR 寄存器值。

2) USART_WordLength:数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存器的 M 位的值。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位。

3) USART_StopBits:停止位设置,可选 0.5 个、 1 个、 1.5 个和 2 个停止位,它设定 USART_CR2寄存器的 STOP[1:0] 位的值,一般我们选择 1 个停止位。

4) USART_Parity:奇偶校验控制选择,可选 USART_Parity_No(无校验)、 USART_Parity_Even(偶校验) 以及 USART_Parity_Odd(奇校验),它设定 USART_CR1 寄存器的 PCE 位和 PS 位的值。

5) USART_Mode: USART 模式选择,有 USART_Mode_Rx 和 USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。

6) USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式才有效,可选有 使能 RTS、 使能 CTS、 同时使能 RTS 和 CTS、 不使能硬件流。

当使用同步模式时需要配置 SCLK 引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef 来设置,该结构体内容也只有在同步模式才需要设置。

USART 时钟初始化结构体

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 接发通信实验

编程要点

1) 使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟;

2) 初始化 GPIO,并将 GPIO 复用到 USART 上;

3) 配置 USART 参数;

4) 配置中断控制器并使能 USART 接收中断;

5) 使能 USART;

6) 在 USART 接收中断服务函数实现数据接收和发送。

usart.c

#include "./usart/usart.h"

void NVIC_Config(void)
{
	//NVIC结构体
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//中断优先级分组配置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	//NVIC结构体配置
	NVIC_InitStruct.NVIC_IRQChannel = USART_IRQ;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	
	//初始化NVIC
	NVIC_Init(&NVIC_InitStruct);	
}

void usart_init(void)
{
	//GPIO及USART结构体
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	
	//使能GPIO时钟和USART时钟
	USART_GPIO_APBxClkCmd(USART_GPIO_CLK,ENABLE);
	USART_APBxClkCmd(USART_CLK,ENABLE);
	
	//GPIO结构体配置及初始化
	//TX的GPIO
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = USART_TX_GPIO_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(USART_TX_GPIO_PORT,&GPIO_InitStruct);
	//RX的GPIO
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin = USART_RX_GPIO_PIN;
	GPIO_Init(USART_RX_GPIO_PORT,&GPIO_InitStruct);
	
	//USART结构体配置
	USART_InitStruct.USART_BaudRate = USART_BAUDRATE;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	//初始化USART
	USART_Init(USARTx,&USART_InitStruct);
	//使能USART
	USART_Cmd(USARTx,ENABLE);
	
	//中断相关配置
	//串口中断配置
	NVIC_Config();
	
	//USART接受中断使能
	USART_ITConfig(USARTx,USART_IT_RXNE,ENABLE);
}

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

//发送一个数组数据
void usart_sendarr(USART_TypeDef* pUSARTx, uint8_t *arr, uint8_t len)
{
	uint8_t i;
	for(i = 0; i < len; i++)
	{
		//发送一个字节数据
		usart_sendbyte(pUSARTx,*arr++);
	}
	//等待发送完成
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}

//发送字符串数据
void usart_sendstr(USART_TypeDef* pUSARTx, char *str)
{
	do
	{
		//发送一个字节数据
		usart_sendbyte(pUSARTx,*str++);
	}while((*str) != '\0');
	//等待发送完成
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}

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

//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
	//等待接受数据寄存器为空
	while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE) == RESET);
	return USART_ReceiveData(USARTx);
}

usart.h

#ifndef _USART_H
#define _USART_H

#include "stm32f10x.h"
#include <stdio.h>

#define  USARTx                   USART1
#define  USART_CLK                RCC_APB2Periph_USART1
#define  USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  USART_TX_GPIO_PORT         GPIOA   
#define  USART_TX_GPIO_PIN          GPIO_Pin_9
#define  USART_RX_GPIO_PORT       GPIOA
#define  USART_RX_GPIO_PIN        GPIO_Pin_10

#define  USART_IRQ                USART1_IRQn
#define  USART_IRQHandler         USART1_IRQHandler

void usart_init(void);
void usart_sendbyte(USART_TypeDef* pUSARTx, uint8_t data);
void usart_sendarr(USART_TypeDef* pUSARTx, uint8_t *arr, uint8_t len);
void usart_sendstr(USART_TypeDef* pUSARTx, char *str);

#endif

main.c

#include "stm32f10x.h"
#include "./usart/usart.h"

int main(void)
{
	//uint8_t arr[10] = {1,2,3,4,5,6,7,8,9,10};
	//char *str = "small 陀螺";
	
	usart_init();
	
	//printf("%s\n",str);
	//usart_sendbyte(USARTx,'a');
	//usart_sendarr(USARTx,arr,10);
	//usart_sendstr(USARTx,"small 陀螺\n");
	//putchar('a');
	
	while(1)
	{

	}
}

串口中断服务函数

void USART_IRQHandler(void)
{
	uint8_t temp;
	if(USART_GetITStatus(USARTx,USART_IT_RXNE) != RESET)
	{
		temp = USART_ReceiveData(USARTx);
		USART_SendData(USARTx,temp);
	}
}

USART1 指令控制 RGB 彩灯实验

编程要点

1) 初始化配置 RGB 彩色灯 GPIO;

2) 使能 RX 和 TX 引脚 GPIO 时钟和 USART 时钟;

3) 初始化 GPIO,并将 GPIO 复用到 USART 上;

4) 配置 USART 参数;

5) 使能 USART;

6) 获取指令输入,根据指令控制 RGB 彩色灯。

与上一个实验不同的是我们这里不使用接收中断,而是靠查询标志位的方式来实现接收。

usart.c

#include "./usart/usart.h"

void NVIC_Config(void)
{
	//NVIC结构体
	NVIC_InitTypeDef NVIC_InitStruct;
	
	//中断优先级分组配置
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	//NVIC结构体配置
	NVIC_InitStruct.NVIC_IRQChannel = USART_IRQ;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	
	//初始化NVIC
	NVIC_Init(&NVIC_InitStruct);	
}

void usart_init(void)
{
	//GPIO及USART结构体
	GPIO_InitTypeDef GPIO_InitStruct;
	USART_InitTypeDef USART_InitStruct;
	
	//使能GPIO时钟和USART时钟
	USART_GPIO_APBxClkCmd(USART_GPIO_CLK,ENABLE);
	USART_APBxClkCmd(USART_CLK,ENABLE);
	
	//GPIO结构体配置及初始化
	//TX的GPIO
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin = USART_TX_GPIO_PIN;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(USART_TX_GPIO_PORT,&GPIO_InitStruct);
	//RX的GPIO
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_InitStruct.GPIO_Pin = USART_RX_GPIO_PIN;
	GPIO_Init(USART_RX_GPIO_PORT,&GPIO_InitStruct);
	
	//USART结构体配置
	USART_InitStruct.USART_BaudRate = USART_BAUDRATE;
	USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStruct.USART_Parity = USART_Parity_No;
	USART_InitStruct.USART_StopBits = USART_StopBits_1;
	USART_InitStruct.USART_WordLength = USART_WordLength_8b;
	//初始化USART
	USART_Init(USARTx,&USART_InitStruct);
	//使能USART
	USART_Cmd(USARTx,ENABLE);
	
	//中断相关配置
	//串口中断配置
//	NVIC_Config();
//	
//	//USART接受中断使能
//	USART_ITConfig(USARTx,USART_IT_RXNE,ENABLE);
}

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

//发送一个数组数据
void usart_sendarr(USART_TypeDef* pUSARTx, uint8_t *arr, uint8_t len)
{
	uint8_t i;
	for(i = 0; i < len; i++)
	{
		//发送一个字节数据
		usart_sendbyte(pUSARTx,*arr++);
	}
	//等待发送完成
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}

//发送字符串数据
void usart_sendstr(USART_TypeDef* pUSARTx, char *str)
{
	do
	{
		//发送一个字节数据
		usart_sendbyte(pUSARTx,*str++);
	}while((*str) != '\0');
	//等待发送完成
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC) == RESET);
}

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

//重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
	//等待接受数据寄存器为空
	while(USART_GetFlagStatus(USARTx,USART_FLAG_RXNE) == RESET);
	return (int)USART_ReceiveData(USARTx);
}

usart.h

#ifndef _USART_H
#define _USART_H

#include "stm32f10x.h"
#include <stdio.h>

#define  USARTx                   USART1
#define  USART_CLK                RCC_APB2Periph_USART1
#define  USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  USART_TX_GPIO_PORT         GPIOA   
#define  USART_TX_GPIO_PIN          GPIO_Pin_9
#define  USART_RX_GPIO_PORT       GPIOA
#define  USART_RX_GPIO_PIN        GPIO_Pin_10

#define  USART_IRQ                USART1_IRQn
#define  USART_IRQHandler         USART1_IRQHandler

void usart_init(void);
void usart_sendbyte(USART_TypeDef* pUSARTx, uint8_t data);
void usart_sendarr(USART_TypeDef* pUSARTx, uint8_t *arr, uint8_t len);
void usart_sendstr(USART_TypeDef* pUSARTx, char *str);

#endif

main.c

#include "stm32f10x.h"
#include "led.h"
#include "./usart/usart.h"


void led_menu(void)
{
	printf("\r\n   这是一个通过串口通信指令控制RGB彩灯实验 \n");
	printf("   指令   ------ 彩灯颜色 \n");
	printf("     1    ------    红 \n");
	printf("     2    ------    绿 \n");
	printf("     3    ------    蓝 \n");
}

int main(void)
{
	//uint8_t arr[10] = {1,2,3,4,5,6,7,8,9,10};
	//char *str = "small 陀螺";
	
	char led_chr;
	
	LED_Init();
	usart_init();
	
	//printf("%s\n",str);
	//usart_sendbyte(USARTx,'a');
	//usart_sendarr(USARTx,arr,10);
	//usart_sendstr(USARTx,"small 陀螺\n");
	//putchar('a');
	
	while(1)
	{
		led_chr = getchar();
		printf("接收到字符:%c\n",led_chr);
		switch(led_chr)
		{
			//字符
			case '1':
				LED_Config(LED_R,ON);
			break;
			
			case '2':
				LED_Config(LED_G,ON);
			break;
			
			case '3':
				LED_Config(LED_B,ON);
			break;
			//16进制
			case 0x01:
				LED_Config(LED_R,OFF);
			break;
			
			case 0x02:
				LED_Config(LED_G,OFF);
			break;
			
			case 0x03:
				LED_Config(LED_B,OFF);
			break;
			
			default:
				led_menu();
			break;
		}
	}
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

small陀螺

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

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

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

打赏作者

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

抵扣说明:

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

余额充值