[STM32 - 野火] - - - 固件库学习笔记 - - -五.串口通信

1 串口通信协议

串口通信协议可分为物理层与协议层。

物理层是硬件部分,规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。

协议层是软件部分,主要规定通讯逻辑,统一收发双方的数据打包、解包标准。

串口数据包的基本组成:
在这里插入图片描述

  • 起始位:由1个逻辑0的数据位表示。

  • 结束位:由 0.5 、1、 1.5 或2个逻辑1的数据位表示。

  • 有效数据:在起始位后紧接着的就是有效数据,有效数据的长度常被约定为5、6、7、或8位。

  • 校验位:检测数据是否出错。

下方链接为 关于UART与USART通信串口数据包的基本组成 更详细的介绍。
UART与USART介绍

2 STM32串口功能框图

串口功能框图: STM32F10X - 参考手册 图248

在这里插入图片描述

2.1 功能引脚

  • TX:发送数据输出引脚。

  • RX:接收数据输入引脚。

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

  • nRTS:请求以发送 (Request To Send), n 表示低电平有效。

如果使能 RTS 流控制,当 USART 接收器准备好接收新数据时就会将 nRTS 变成低电平;当接收寄存器已满时, nRTS 将被设置为高电平。该引脚只适用于硬件流控制。

  • nCTS:清除以发送 (Clear To Send), n 表示低电平有效。

如果使能 CTS 流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。

  • SW_RX:Software UART(软件串行通信)接收引脚。

用于通过软件实现的UART(通用异步收发器)通信,通常用于调试和与其他串行设备通信。

  • IRDA_OUT:红外数据传输的输出引脚。用于红外数据传输,通常用于遥控器、红外数据通信等应用。

  • IRDA_IN:外数据传输的输入引脚。它用于接收红外数据,例如从遥控器或其他红外发射设备接收数据。

在这里插入图片描述
上图为 STM32F103ZET6芯片的USART引脚

还有一些引脚为复用引脚,在 STM32F10X - 参考手册 8.3.8 USART复用功能重映射 查看。

为什么UART没有SCLK、nRTS、nCTS:s表示synchronous(同步),UART为异步,不需要这三个引脚。

2.2 数据寄存器

数据寄存器——USART_DR:9位有效,包含一个发送数据寄存器TDR和一个接收数据寄存器RDR。一个地址对应两个物理内存。

在这里插入图片描述
在这里插入图片描述

2.3 控制器

USART的控制寄存器有三个:USART_CR1、USART_CR2、USART_CR3,其中USART_CR3主要与硬件控制流相关。

章节1串口通信协议 中我们了解了串口数据包的基本组成有起始位、数据位、校验位和停止位。

  • 数据位的长度由USART_CR1寄存器的位12(M)控制:当M为0时数据位为8为;当M为1时数据位为9位。

  • 停止位的长度由USART_CR2寄存器的位12、13(STOP)控制。

  • 校验位由USART_CR1寄存器的位10(PCE:校验控制使能)与位9(PS:校验选择)控制。

与中断相关的寄存器位:PEIE、UE、TE、RE、TXE、TXEIE、TC、TCIE、RXNE、RXNEIE。

  • PEIE:PE中断使能,USART_CR1位8。

    • 0表示禁止产生中断。
    • 1表示当USART_SR中的PE为1时,产生USART中断。
  • UE:USART使能,USART_CR1位13。

    • 0表示USART分频器和输出被禁止。
    • 1表示USART模块使能。
  • TE:发送使能,USART_CR1位3。

    • 0表示禁止发送。
    • 1表示使能发送。
  • RE:接收使能,USART_CR1位2。

    • 0表示禁止接收。
    • 1表示使能接收,并开始搜寻RX引脚上的起始位。
  • TXE:发送数据寄存器空,USART_SR位7。

    • 0表示数据还没有被转移到移位寄存器。
    • 1表示数据已经被转移到移位寄存器。
  • TXEIE:发送缓冲区空中断使能,USART_CR1位7。

    • 0表示禁止产生中断。
    • 1表示当USART_SR中的TC为’1’时,产生USART中断。
  • TC:发送完成 ,USART_SR位6。

    • 0表示发送还未完成。
    • 1表示发送完成。
  • TCIE:发送完成中断使能,USART_CR1位6。

    • 0表示禁止产生中断。
    • 1表示当USART_SR中的TC为’1’时,产生USART中断。
  • RXNE:读数据寄存器非空,USART_SR位5。

    • 0表示数据没有收到。
    • 1表示收到数据,可以读出。
  • RXNEIE:接收缓冲区非空中断使能 ,USART_CR1位5。

    • 0表示禁止产生中断。
    • 1表示当USART_SR中的ORE或者RXNE为’1’时,产生USART中断。

2.3.1 USART_CR1

在这里插入图片描述
在这里插入图片描述

2.3.2 USART_CR2

在这里插入图片描述
在这里插入图片描述

2.3.3 USART_CR3

在这里插入图片描述
在这里插入图片描述

2.3.4 USART_SR

在这里插入图片描述
在这里插入图片描述

2.4 数据发送与接收的具体流程

2.4.1 数据发送

发送数据时:UE = 1, TE = 1。

要发送的数据来自CPU或DMA,从内存中读取;有了数据之后将数据放到发送数据寄存器(TDR),发送TDR的数据时会将数据(8位)放到发送移位寄存器,再由发送移位寄存器一位一位的发送出去,发送到TX引脚。

在这个过程中,有两个寄存器的状态位发送了改变:

  • 1.发送数据寄存器中的数据发送到发送移位寄存器时,TXE(Transmit Empty)为1表示发送数据寄存器为空,但并不表示数据已经发送出去。

  • 2.发送移位寄存器中的数据发送完成了,TC(Transmit Complete)为1表示数据已经发送出去了。

2.4.2 数据接收

接收数据时:UE = 1,RE = 1。

接收数据从RX引脚中来,数据接收的时候是一位一位的接收;首先将接收到的数据放到接收移位寄存器中,然后再把数据传送到接收数据寄存器(RDR),这时RXNE(Receive Not Empty)会置1表示接收数据寄存器不是空的,这时数据可以读出。

2.5 比特率

设置串口通信比特率的寄存器为USART_BRR。

在这里插入图片描述
比特率的计算公式如下:
在这里插入图片描述

  • fCK:串口的时钟,注意区分APB2(36M)和APB1(72M)两条总线。

  • USARTDIV:无符号的定点数。

我们想要使用APB1总线时钟来设置比特率为115200的串口,那么它的USARTDIV该怎么配置呢?

在这里插入图片描述

  • 小数部分为什么要乘16:小数部分有4位,可表示0 ~ 15(0000 ~ 1111),小数部分的最大值不能超过1.0000,因此最小精度为1/16,算出的0.0625除以(1/16)就是DIV_Fraction的值。

3 代码讲解

3.1 硬件设计

在这里插入图片描述

上图为 野火_指南者原理图中的USB转串口模块电路图

我们将指南者上的PA10、PA9与USART1_RX、USART1_TX用跳线帽连接。注意板子上的RX与CH340G的TX相连,TX与CH340G的RX相连。

3.2 软件设计

为了使工程更加有条理,我们把USART相关的代码独立分开存储,方便以后移植。在“工程模板”上新建 usart.c文件 与 usart.h 文件。

在本次学习中,我们需要实现以下功能:

  • 功能1.单片机给电脑发送数据,电脑上位机把数据打印处理。

  • 功能2.电脑上位机给单片机发数据,单片机接收到数据后立马发回给电脑,并打印处理。

  • 功能3.电脑给单片机发送命令,用于控制开发板上的RGB灯。

3.2.1 编程要点:

  • 1.初始化串口需要用到的GPIO:时钟(GPIO)、GPIO口的模式。

  • 2.初始化串口,USART_InitTypeDef:先打开时钟(USART),再配置串口的工作参数。

  • 3.中断配置:接收中断,中断优先级。

  • 4.使能串口。

  • 5.编写发送和接收函数。

  • 6.编写中断服务函数。

// usart.h 文件
#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"

/** 
  * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
	* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线
	* 2-修改GPIO的宏
  */
	
// 串口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);

#endif /* __USART_H */
// usart.c 文件
#include "usart.h"

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
	/* 提示 NVIC_PriorityGroupConfig() 在整个工程只需要调用一次来配置优先级分组 */
	/* 这时只有串口接收的中断,组可以随便分。抢占优先级与子优先级也可以随便配置。 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 1.1 打开串口GPIO的时钟
	RCC_APB2PeriphClockCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 2.1 打开串口外设的时钟
	RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE);

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

  // 1.3 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	/* RX引脚为什么要选择浮空输入:外部发送什么数据到引脚,引脚就体现出什么电平 */
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 2.2 配置串口的工作参数
	
	/* 上位机串口调试助手的参数要与软件设置的参数一致。 */

	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	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(DEBUG_USARTx, &USART_InitStructure);
	
	// 3.1 串口中断优先级配置
	NVIC_Configuration();
	
	// 3.2 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 4 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

3.2.2 串口发送数据

3.2.2.1 发送一个字节
// usart.c 文件
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data)
{
	USART_SendData(pUSARTx, data); // 这个函数可以在 stm32f10x.h 文件中找到
	
	// 等待发送数据寄存器为空
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) != SET);
	// while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC != SET); 这样写也可以
}

// usart.h 文件
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);
// main.c 文件
#include "stm32f10x.h"

#include "usart.h"

int main(void)
{

	USART_Config();
	Usart_SendByte(DEBUG_USARTx, 0xff);
	
	while(1)
	{
		
	}
	
}

将编译后的代码烧录到板子中,可以在上位机串口助手看到单片机发送的数据。

在这里插入图片描述

点击波特率上方的更多串口设置,出现下面的以下设置,这些设置要与 USART_Config() 函数中设置的参数一致。

在这里插入图片描述

3.2.2.2 发送两个字节
// usart.c 文件
/* 发送两个字节,需要在 usart.h 文件中声明 */
void Usart_SendTwoByte(USART_TypeDef* pUSARTx, uint16_t data)
{
	uint8_t temp_h, temp_l;
	temp_h = (data&0xFF00) >> 8;
	temp_l = data&0xFF;
	
	Usart_SendByte(pUSARTx, temp_h);
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) != SET);
	
	Usart_SendByte(pUSARTx, temp_l);
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) != SET);
	
}

发送一个字节判断USART_FLAG_TXE,发送一连串数据判断用USART_FLAG_TC。

3.2.2.3 发送8位数据的数组
// usart.c 文件
/* 发送8位数组,需要在 usart.h 文件中声明*/
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num)
{
	uint8_t i;
	for(i = 0; i<num; i++)
	{
		Usart_SendByte(pUSARTx, array[i]);
		// USART_SendData(pUSARTx, array[i]); 只会发送最后一个数据
	}
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) != SET);
}
  • 如果将 Usart_SendByte(pUSARTx, array[i]) 更换为 USART_SendData(pUSARTx, array[i]) ,我们在串口助手上只能看到数组array的最后一个数据:前面的数据还没等发送完成,后面的数据就存入到发送数据寄存器中,覆盖掉了前面的数据。
3.2.2.4 发送一个字符串
// usart.c 文件
/* 发送一个字符串,需要在 usart.h 文件中声明 */
void Usart_SendString(USART_TypeDef* pUSARTx, uint8_t *str)
{
	uint8_t i = 0;
	while(*(str+i) != '\0')
	{
		Usart_SendByte(pUSARTx, *(str+i));
		i++;
	}
	while(USART_GetFlagStatus(pUSARTx, USART_FLAG_TC) != SET);
}
3.2.2.5 重定向函数
// usart.c 文件
// 重定向C库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
	USART_SendData(DEBUG_USARTx, (uint8_t) ch);
	
	while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) != SET);
	
	return (ch);
}

// 重定向C库函数scanf到串口,重定向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
	while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) != SET);
	
	return (int)USART_ReceiveData(DEBUG_USARTx);
}
3.2.2.6 函数实现结果
int main(void)
{
	uint8_t array[3] = {0xff, 0x12, 0x11};

	USART_Config();
	
	Usart_SendByte(DEBUG_USARTx, 0xff);
	
	Usart_SendTwoByte(DEBUG_USARTx, 0x1234);
	
	Usart_SendArray(DEBUG_USARTx, array, 3);
	
	Usart_SendString(DEBUG_USARTx, "每天坚持学习!\n");
	
	printf("每天坚持学习!\n");
	
	while(1)
	{
		
	}
	
}

在这里插入图片描述

  • 在查看串口助手时,如果勾上HEX显示(十六进制显示),那么就看不到中文字符(如上图所示)。

  • 在查看串口助手时,如果不勾上HEX显示,那么数据就会以ASCII码的形式显示,没有对应能够显示ASCII的数据就不能在串口助手上看到

在这里插入图片描述

3.2.3 串口2345代码移植

// usart.h 文件
#ifndef __USART_H
#define __USART_H

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

/** 
  * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
	* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线
	* 2-修改GPIO的宏
  */
	
/* 要使用哪个串口,将对应的串口置1,其他的清零 */
#define DEBUG_USART1 0 
#define DEBUG_USART2 1 
#define DEBUG_USART3 0 
#define DEBUG_USART4 0 
#define DEBUG_USART5 0 

#if DEBUG_USART1
// 串口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

#elif DEBUG_USART2
//串口2-USART2
#define  DEBUG_USARTx                   USART2
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#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_2
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3

#define  DEBUG_USART_IRQ                USART2_IRQn
#define  DEBUG_USART_IRQHandler         USART2_IRQHandler

#elif DEBUG_USART3
//串口3-USART3
#define  DEBUG_USARTx                   USART3
#define  DEBUG_USART_CLK                RCC_APB1Periph_USART3
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOB)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOB   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
#define  DEBUG_USART_RX_GPIO_PORT       GPIOB
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

#define  DEBUG_USART_IRQ                USART3_IRQn
#define  DEBUG_USART_IRQHandler         USART3_IRQHandler

#elif DEBUG_USART4
//串口4-UART4
#define  DEBUG_USARTx                   UART4
#define  DEBUG_USART_CLK                RCC_APB1Periph_UART4
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
#define  DEBUG_USART_RX_GPIO_PORT       GPIOC
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

#define  DEBUG_USART_IRQ                UART4_IRQn
#define  DEBUG_USART_IRQHandler         UART4_IRQHandler

#elif DEBUG_USART5
//串口5-UART5
#define  DEBUG_USARTx                   UART5
#define  DEBUG_USART_CLK                RCC_APB1Periph_UART5
#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_12
#define  DEBUG_USART_RX_GPIO_PORT       GPIOD
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_2

#define  DEBUG_USART_IRQ                UART5_IRQn
#define  DEBUG_USART_IRQHandler         UART5_IRQHandler

#endif

void USART_Config(void);
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t data);
void Usart_SendTwoByte(USART_TypeDef* pUSARTx, uint16_t data);
void Usart_SendArray(USART_TypeDef* pUSARTx, uint8_t *array, uint8_t num);
void Usart_SendString(USART_TypeDef* pUSARTx, uint8_t *str);

#endif /* __USART_H */
  • 使用其他串口发送数据,将RXD、TXD与PA9、PA10的跳帽线拿掉,用杜邦线将要使用的串口与RXD、TXD相连。(RXD与TX连,TXD与RX连)

  • 更改了串口(如串口1改为串口2),但在串口助手上只能看到数据0x00。

    • 原因:在串口初始化函数 USART_Config() 中,没有将相对应的外设时钟打开。

    • 解决方法:在串口初始化函数 USART_Config() 中,开启外设时钟 RCC_APB1PeriphClockCmd(DEBUG_USART_CLK, ENABLE);

  • 为什么会发送无效数据?

    • 原因:这是由于STATUS寄存器的上电初始值有问题,Usart的发送数据寄存器有一个缓冲移位寄存器,并且发送有两个状态,一个是发送数据寄存器为空,一个是移位寄存器发送完成。上电后Usart的状态寄存器的发送完成位为0,造成了上电以后会始终会有一个值被发送,这个值就是发送移位寄存器中的值,而这个值有时候是0X00有时候是0XFE 。

    • 解决方法:在初始化的时候先初始化Usart,然后再配置Usart的GPIO位。

3.2.4 串口接收函数

为了方便中断管理,我们将串口中断服务函数放在 stm32f10x_it.c 文件中。

// stm32f10x_it.c 文件
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
	uint8_t Temp;
	if(USART_GetFlagStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
	{
		Temp = USART_ReceiveData(DEBUG_USARTx);
		USART_SendData(DEBUG_USARTx, Temp);
	}
}

3.2.5 串口发送命令控制RGB灯

3.2.5.1 中断

通过使用中断接收数据,可以在主函数中对接收到的数据进行处理,也可以直接在中断中对接收到的数据进行处理。

以下为 “在主函数中对接收到的数据进行处理” 的部分代码。

// stm32f10x_it.c 文件
#include "usart.h"

uint8_t Temp = 0;
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
	if(USART_GetFlagStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
	{
		Temp = USART_ReceiveData(DEBUG_USARTx);
		USART_SendData(DEBUG_USARTx, Temp);
	}
}
// main.c
#include "stm32f10x.h"

#include "bsp_led.h"
#include "usart.h"

extern uint8_t Temp;

int main(void)
{
	USART_Config();
	LED_G_GPIO_Config();
	LED_B_GPIO_Config();
	LED_R_GPIO_Config();
	
	while(1)
	{
		switch(Temp)
		{
			case 0x12:
				LED_B(1);
				break;
			
			case 0x34:
				LED_R(1);
				break;
			
			case 0x56:
				LED_G(1);
				break;
			
			default:
				LED_B(0);
				LED_R(0);
				LED_G(0);
				break;
		}
	}
}

以下为在 “中断中对接收到的数据进行处理” 的部分代码。

// stm32f10x_it.c 文件
#include "usart.h"
#include "bsp_led.h"

// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
	uint8_t Temp ;
	if(USART_GetFlagStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
	{
		Temp = USART_ReceiveData(DEBUG_USARTx);
		USART_SendData(DEBUG_USARTx, Temp);
		switch(Temp)
		{
			case 0x12:
				LED_B(1);
				break;
			
			case 0x34:
				LED_R(1);
				break;
			
			case 0x56:
				LED_G(1);
				break;
			
			default:
				LED_B(0);
				LED_R(0);
				LED_G(0);
				break;
		}
	}
}
// main.c
#include "stm32f10x.h"

#include "bsp_led.h"
#include "usart.h"

int main(void)
{
	USART_Config();
	LED_G_GPIO_Config();
	LED_B_GPIO_Config();
	LED_R_GPIO_Config();
	
	while(1)
	{
	
	}
}
3.2.5.2 查询

使用查询的方法需要将中断相关的代码都注释掉。

#include "stm32f10x.h"

#include "bsp_led.h"
#include "usart.h"

int main(void)
{
	uint8_t ch;

	USART_Config();
	LED_G_GPIO_Config();
	LED_B_GPIO_Config();
	LED_R_GPIO_Config();

	while(1)
	{
		ch = getchar();
		switch(ch)
		{
			case 0x12:
				LED_B(1);
				break;
			
			case 0x34:
				LED_R(1);
				break;
			
			case 0x56:
				LED_G(1);
				break;
			
			default:
				LED_B(0);
				LED_R(0);
				LED_G(0);
				break;
		}
		
	}
	
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值