STM32学习笔记七——USART串口

USART串口协议

通信接口

在这里插入图片描述

串口

串口通信

在这里插入图片描述
第一个是USB转串口模块,上面有个芯片型号是CH340,这个芯片可以把串口协议转换为USB协议,它一边是USB口,可以插在电脑上,另一边是串口的引脚,可以和支持串口的芯片接在一起,这样就可以实现串口和电脑的通信了。中间这个图是一个陀螺仪传感器的模块,可以测量角速度、加速度这些姿态参数,它左右各有四个引脚,一边是串口的引脚,一边是I2C的引脚。右边这个图是蓝牙串口模块,下面四个引脚是串口通信的引脚,上面的芯片可以和手机互联,实现手机遥控单片机的功能。

硬件电路

在这里插入图片描述

电平标准

在这里插入图片描述

串口参数及时序

在这里插入图片描述
数据位有两种表示方法:一种是将校验位作为数据位的一部分,数据位为9位;另一种是将数据位和校验位独立开,数据位就是有效载荷。

串口时序

在这里插入图片描述

USART外设

USART简介

在这里插入图片描述

USART框图

接口通过三个引脚与其他设备连接在一起(见图248)。任何USART双向通信至少需要两个脚:接收数据输入(RX)和发送数据输出(TX)。
RX:接收数据串行输。通过过采样技术来区别数据和噪音,从而恢复数据。
TX:发送数据输出。当发送器被禁止时,输出引脚恢复到它的I/O端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平。在单线和智能卡模式里,此I/O口被同时用于数据的发送和接收。
● 总线在发送或接收前应处于空闲状态
● 一个起始位
● 一个数据字(8或9位),最低有效位在前
● 0.5,1.5,2个的停止位,由此表明数据帧的结束
● 使用分数波特率发生器 —— 12位整数和4位小数的表示方法。
● 一个状态寄存器(USART_SR)
● 数据寄存器(USART_DR)
● 一个波特率寄存器(USART_BRR),12位的整数和4位小数
● 一个智能卡模式下的保护时间寄存器(USART_GTPR)
关于以上寄存器中每个位的具体定义,请参考寄存器描述第25.6节:USART寄存器描述。
在同步模式中需要下列引脚:
● CK:发送器时钟输出。此引脚输出用于同步传输的 时钟, (在Start位和Stop位上没有时钟脉冲,软件可选地,可以在最后一个数据位送出一个时钟脉冲)。数据可以在RX上同步被接收。这可以用来控制带有移位寄存器的外部设备(例如LCD驱动器)。时钟相位和极性都是软件可编程的。在智能卡模式里,CK可以为智能卡提供时钟。
在IrDA模式里需要下列引脚:
● IrDA_RDI: IrDA模式下的数据输入。
● IrDA_TDO: IrDA模式下的数据输出。
下列引脚在硬件流控模式中需要:
● nCTS: 清除发送,若是高电平,在当前数据传输结束时阻断下一次的数据发送。
● nRTS: 发送请求,若是低电平,表明USART准备好接收数据
在这里插入图片描述

USART基本结构

在这里插入图片描述

数据帧

字长可以通过编程USART_CR1寄存器中的M位,选择成8或9位(见图249)。在起始位期间,TX脚处于低电平,在停止位期间处于高电平。
空闲符号被视为完全由’1’组成的一个完整的数据帧,后面跟着包含了数据的下一帧的开始位(‘1’的位数也包括了停止位的位数)。
断开符号 被视为在一个帧周期内全部收到’0’(包括停止位期间,也是’0’)。在断开帧结束时,发送器再插入1或2个停止位(‘1’)来应答起始位。
发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。
在这里插入图片描述
发送器根据M位的状态发送8位或9位的数据字。当发送使能位(TE)被设置时,发送移位寄存器中的数据在TX脚上输出,相应的时钟脉冲在CK脚上输出。

字符发送

在USART发送期间,在TX引脚上首先移出数据的最低有效位。在此模式里,USART_DR寄存器包含了一个内部总线和发送移位寄存器之间的缓冲器(见图248)。
每个字符之前都有一个低电平的起始位;之后跟着的停止位,其数目可配置。USART支持多种停止位的配置:0.5、1、1.5和2个停止位。
注: 1.在数据传输期间不能复位TE位,否则将破坏TX脚上的数据,因为波特率计数器停止计数。
正在传输的当前数据将丢失。
2. TE位被激活后将发送一个空闲帧。

可配置的停止位

随每个字符发送的停止位的位数可以通过控制寄存器2的位13、12进行编程。

  1. 1个停止位:停止位位数的默认值。
  2. 2个停止位:可用于常规USART模式、单线模式以及调制解调器模式。
  3. 0.5个停止位:在智能卡模式下接收数据时使用。
  4. 1.5个停止位:在智能卡模式下发送和接收数据时使用。
    空闲帧包括了停止位。
    断开帧是10位低电平,后跟停止位(当m=0时);或者11位低电平,后跟停止位(m=1时)。不可能传输更长的断开帧(长度大于10或者11位)。
    在这里插入图片描述

起始位侦测

USART可以根据USART_CR1的M位接收8位或9位的数据字。

起始位侦测

在USART中,如果辨认出一个特殊的采样序列,那么就认为侦测到一个起始位。
该序列为:1 1 1 0 X 0 X 0 X 0 0 0 0
在这里插入图片描述

字符接收

在USART接收期间,数据的最低有效位首先从RX脚移进。在此模式里,USART_DR寄存器包含的缓冲器位于内部总线和接收移位寄存器之间。
配置步骤:

  1. 将USART_CR1寄存器的UE置1来激活USART。
  2. 编程USART_CR1的M位定义字长
  3. 在USART_CR2中编写停止位的个数
  4. 如果需多缓冲器通信,选择USART_CR3中的DMA使能位(DMAR)。按多缓冲器通信所要求的配置DMA寄存器。
  5. 利用波特率寄存器USART_BRR选择希望的波特率。
  6. 设置USART_CR1的RE位。激活接收器,使它开始寻找起始位。
    当一字符被接收到时,
    ● RXNE位被置位。它表明移位寄存器的内容被转移到RDR。换句话说,数据已经被接收并且
    可以被读出(包括与之有关的错误标志)。
    ● 如果RXNEIE位被设置,产生中断。
    ● 在接收期间如果检测到帧错误,噪音或溢出错误,错误标志将被置起,
    ● 在多缓冲器通信时,RXNE在每个字节接收后被置起,并由DMA对数据寄存器的读操作而清零。
    ● 在单缓冲器模式里,由软件读USART_DR寄存器完成对RXNE位清除。RXNE标志也可以通过对它写0来清除。RXNE位必须在下一字符接收结束前被清零,以避免溢出错误。
    注意: 在接收数据时,RE位不应该被复位。如果RE位在接收时被清零,当前字节的接收被丢失。
断开符号

当接收到一个断开帧时,USART像处理帧错误一样处理它。

空闲符号

当一空闲帧被检测到时,其处理步骤和接收到普通数据帧一样,但如果IDLEIE位被设置将产生一个中断。

溢出错误

如果RXNE还没有被复位,又接收到一个字符,则发生溢出错误。数据只有当RXNE位被清零后才能从移位寄存器转移到RDR寄存器。RXNE标记是接收到每个字节后被置位的。如果下一个数据已被收到或先前DMA请求还没被服务时,RXNE标志仍是置起的,溢出错误产生。
当溢出错误产生时:
● ORE位被置位。
● RDR内容将不会丢失。读USART_DR寄存器仍能得到先前的数据。
● 移位寄存器中以前的内容将被覆盖。随后接收到的数据都将丢失。
● 如果RXNEIE位被设置或EIE和DMAR位都被设置,中断产生。
● 顺序执行对USART_SR和USART_DR寄存器的读操作,可复位ORE位
注意: 当ORE位置位时,表明至少有1个数据已经丢失。有两种可能性:
● 如果RXNE=1,上一个有效数据还在接收寄存器RDR上,可以被读出。
● 如果RXNE=0,这意味着上一个有效数据已经被读走,RDR已经没有东西可读。当上一个
有效数据在RDR中被读取的同时又接收到新的(也就是丢失的)数据时,此种情况可能发生。在读序列期间(在USART_SR寄存器读访问和USART_DR读访问之间)接收到新的数据,此种情况也可能发生。

噪音错误

使用过采样技术(同步模式除外),通过区别有效输入数据和噪音来进行数据恢复。
在这里插入图片描述
在这里插入图片描述
当在接收帧中检测到噪音时:
● 在RXNE位的上升沿设置NE标志。
● 无效数据从移位寄存器传送到USART_DR寄存器。
● 在单个字节通信情况下,没有中断产生。然而,因为NE标志位和RXNE标志位是同时被设置,RXNE将产生中断。在多缓冲器通信情况下,如果已经设置了USART_CR3寄存器中EIE位,将产生一个中断。先读出USART_SR,再读出USART_DR寄存器,将清除NE标志位

帧错误

当以下情况发生时检测到帧错误:
由于没有同步上或大量噪音的原因,停止位没有在预期的时间上接和收识别出来。
当帧错误被检测到时:
● FE位被硬件置起
● 无效数据从移位寄存器传送到USART_DR寄存器。
● 在单字节通信时,没有中断产生。然而,这个位和RXNE位同时置起,后者将产生中断。在多缓冲器通信情况下,如果USART_CR3寄存器中EIE位被置位的话,将产生中断。顺序执行对USART_SR和USART_DR寄存器的读操作,可复位FE位。

接收期间的可配置的停止位

被接收的停止位的个数可以通过控制寄存器2的控制位来配置,在正常模式时,可以是1或2个,在智能卡模式里可能是0.5或1.5个。

  1. 0.5个停止位(智能卡模式中的接收):不对0.5个停止位进行采样。因此,如果选择0.5个停止位则不能检测帧错误和断开帧。
  2. 1个停止位:对1个停止位的采样在第8,第9和第10采样点上进行。
  3. 1.5个停止位(智能卡模式):当以智能卡模式发送时,器件必须检查数据是否被正确的发送出去。所以接收器功能块必须被激活(USART_CR1寄存器中的RE =1),并且在停止位的发送期间采样数据线上的信号。如果出现校验错误,智能卡会在发送方采样NACK信号时,即总线上停止位对应的时间内时,拉低数据线,以此表示出现了帧错误。FE在1.5个停止位结束时和RXNE一起被置起。对1.5个停止位的采样是在第16,第17和第18采样点进行的。1.5个的停止位可以被分成2部分:一个是0.5个时钟周期,期间不做任何事情 。随后是1个时钟周期的停止位,在这段时间的中点处采样。详见第25.3.11节:智能卡。
  4. 2个停止位:对2个停止位的采样是在第一停止位的第8,第9和第10个采样点完成的。如果第一个停止位期间检测到一个帧错误,帧错误标志将被设置。第二个停止位不再检查帧错误。在第一个停止位结束时RXNE标志将被设置。

波特率发生器

在这里插入图片描述

CH340G模块原理图

在这里插入图片描述

数据模式

在这里插入图片描述

串口发送

第一步:新建工程

第二步:连接线路

第三步:编写程序

使用printf函数需要进行下面操作
在这里插入图片描述
使用Serial_Printf函数打印汉字(编码方式为UTF-8),需要进行下面操作:
在这里插入图片描述
或者选择编码方式为GB2312。
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

int main (void)
{
	OLED_Init ();
	
	Serial_Init ();
	
	Serial_SendByte ('A');
	Serial_SendByte (0x41);
	
	uint8_t MyArray[]={0x42,0x43,0x44,0x45};
	Serial_SendArray(MyArray ,4);
	
	Serial_SendString("HelloWorld!");
	Serial_SendString("HelloWorld!\r\n");
	
	Serial_SendNumber(12345, 5);
	
	printf("Num=%d\r\n", 666);
	
	char String[100];
	sprintf(String, "\r\nNum=%d", 666);
	Serial_SendString(String);
	
	Serial_Printf("\r\n你好,世界");
	Serial_Printf("\r\nNum4=%d", 444);
	Serial_Printf("\r\n");
	
	while(1)
	{
		
	}
}

Serial.c文件

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	/*
	USART_InitTypeDef USART_InitStructure: 这是一个结构体类型的变量,用于存储初始化参数的配置信息。
	USART_InitStructure.USART_BaudRate = 9600: 设置USART的波特率为9600,即每秒传输9600个位。
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None: 禁用硬件流控制,即不使用硬件流控制功能。
	USART_InitStructure.USART_Mode = USART_Mode_Tx: 设置USART的工作模式为发送模式(Tx)。
	USART_InitStructure.USART_Parity = USART_Parity_No: 设置USART的奇偶校验位为无校验(None)。
	USART_InitStructure.USART_StopBits = USART_StopBits_1: 设置USART的停止位为1个停止位(1表示有一个额外的停止位)。
	USART_InitStructure.USART_WordLength = USART_WordLength_8b: 设置USART的数据位为8位。
	最后一行代码调用了名为USART_Init的函数,并将USART1和&USART_InitStructure作为参数传递给该函数,以完成USART的初始化配置。
	*/
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

Serial.h文件

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

#endif

在串口助手软件中显示实验现象。

串口发送+接收

第一步:新建工程

第二步:连接线路

第三步:编写程序

main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"

uint8_t RxData;

int main (void)
{
	OLED_Init ();
	OLED_ShowString(1, 1, "RxData:");
	
	Serial_Init ();

	while(1)
	{
		if (Serial_GetRxFlag() == 1)
		{
			RxData = Serial_GetRxData();
			Serial_SendByte(RxData);
			OLED_ShowHexNum(1, 8, RxData, 2);
		}
	}
}

Serial.c文件

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

uint8_t Serial_GetRxData(void)
{
	return Serial_RxData;
}

void USART1_IRQHandler(void)
{
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		Serial_RxData = USART_ReceiveData(USART1);
		Serial_RxFlag = 1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

Serial.h文件

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);


#endif

USART串口数据包

HEX数据包

HEX数据包是一种二进制数据包,用于在计算机网络中传输数据。在STM32中,可以使用串口发送和接收HEX数据包。以下是一些关于如何配置HEX数据包的信息:

  • 发送模式和接收模式都选择HEX模式。
  • HEX数据包以FF开头,以FE结尾,是固定格式。
  • 发送区的开头和结尾都是0xFF或0xFE。
  • 发送区的数据由16进制表示,每个字节之间用空格分隔。
  • 接收区的数据也是由16进制表示,每个字节之间用0xFF填充。
    在这里插入图片描述

文本数据包

文本数据包是一种二进制数据包,用于在计算机网络中传输文本信息。在串口通信中,可以使用文本数据包来发送和接收文本信息。以下是一些关于如何配置文本数据包的信息:

  • 文本数据包格式:可变包长、含包头包尾、包头@、载荷数据字节不固定、包尾
  • 包头:用于标识文本数据包的开始和结束位置,通常包含一些控制信息,如波特率、数据位、停止位等。
  • 载荷数据:实际要传输的文本信息。
  • 包尾:用于标识文本数据包的结束位置。
    在这里插入图片描述

HEX数据包接收

在这里插入图片描述

文本数据包接收

在这里插入图片描述

串口收发HEX数据包

第一步:新建工程

第二步:连接线路

第三步:编写程序

实验现象:按下按键,串口助手的接收区显示FF 01 02 03 04 EE,OLED屏上第二行显示01 02 03 04,每按一次,数据加一;在发送区输入FF FE FF 88 FE FE或FF 11 22 33 44 FE等类型的数据包,在OLED屏上第四行显示 FE FF 88 FE或11 22 33 44等数据。
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"

uint8_t KeyNum;

int main (void)
{
	OLED_Init ();
	Key_Init ();
	Serial_Init ();
	
	OLED_ShowString (1,1,"TxPacket");
	OLED_ShowString (3,1,"RxPacket");
	
	Serial_TxPacket[0]=0x01;
	Serial_TxPacket[1]=0x02;
	Serial_TxPacket[2]=0x03;
	Serial_TxPacket[3]=0x04;
	
	while(1)
	{
		KeyNum =Key_GetNum ();
		if (KeyNum == 1)
		{
			Serial_TxPacket[0] ++;
			Serial_TxPacket[1] ++;
			Serial_TxPacket[2] ++;
			Serial_TxPacket[3] ++;
			
			Serial_SendPacket ();
			
			OLED_ShowHexNum (2,1,Serial_TxPacket [0],2);
			OLED_ShowHexNum (2,4,Serial_TxPacket [1],2);
			OLED_ShowHexNum (2,7,Serial_TxPacket [2],2);
			OLED_ShowHexNum (2,10,Serial_TxPacket [3],2);
		}
		
		if (Serial_GetRxFlag () == 1)
		{
			OLED_ShowHexNum (4,1,Serial_RxPacket [0],2);
			OLED_ShowHexNum (4,4,Serial_RxPacket [1],2);
			OLED_ShowHexNum (4,7,Serial_RxPacket [2],2);
			OLED_ShowHexNum (4,10,Serial_RxPacket [3],2);
		}
	}
}

Serial.c文件

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Serial_TxPacket[4];
uint8_t Serial_RxPacket[4];
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

void Serial_SendPacket(void)
{
	Serial_SendByte (0xFF);
	Serial_SendArray (Serial_TxPacket ,4);
	Serial_SendByte (0xEE);
}

uint8_t Serial_GetRxFlag(void)
{
	if (Serial_RxFlag == 1)
	{
		Serial_RxFlag = 0;
		return 1;
	}
	return 0;
}

void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;
	static uint8_t pRxPacket = 0;
	
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData (USART1 );
		if(RxState == 0)
		{
			if (RxData == 0xFF)
			{
				RxState=1;
				pRxPacket = 0;
			}
		}
		else if (RxState == 1)
		{
			Serial_RxPacket [pRxPacket]=RxData ;
			pRxPacket ++;
			if (pRxPacket>=4)
			{
				RxState =2;
			}
		}
		else if (RxState == 2)
		{
			if (RxData == 0xFE)
			{
				RxState = 0;
				Serial_RxFlag = 1;
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

Serial.h文件

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

extern uint8_t Serial_TxPacket[];
extern uint8_t Serial_RxPacket[];


void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

void Serial_SendPacket(void);

uint8_t Serial_GetRxFlag(void);


#endif

串口收发文本数据包

第一步:新建工程

第二步:连接线路

第三步:编写程序

实验现象:在串口助手的发送区输入@darea12342‘Enter’,在OLED显示屏的第四行显示darea12342。若发送LED_ON,则LED1点亮,返回接收区LED_ON_OK;若发送LED_OFF,则LED1熄灭,返回接收区LED_OFF_OK;若发送其他内容,返回接收区ERROR_COMMAND。
main.c文件

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include <string.h>

int main (void)
{
	OLED_Init ();
	LED_Init ();
	Serial_Init ();
	
	OLED_ShowString (1,1,"TxPacket");
	OLED_ShowString (3,1,"RxPacket");
	
	while(1)
	{
		if (Serial_RxFlag == 1)
		{
			OLED_ShowString (4,1,"                " );
			OLED_ShowString (4,1,Serial_RxPacket );
			
			if (strcmp (Serial_RxPacket ,"LED_ON") ==0)
			{
				LED1_ON ();
				Serial_SendString ("LED_ON_OK\r\n");
				OLED_ShowString (2,1,"                " );
				OLED_ShowString (2,1,"LED_ON_OK" );
			}
			else if (strcmp (Serial_RxPacket ,"LED_OFF") ==0)
			{
				LED1_OFF ();
				Serial_SendString ("LED_OFF_OK\r\n");
				OLED_ShowString (2,1,"                " );
				OLED_ShowString (2,1,"LED_OFF_OK" );
			}
			else 
			{
				Serial_SendString ("ERROR_COMMAND\r\n");
				OLED_ShowString (2,1,"                " );
				OLED_ShowString (2,1,"ERROR_COMMAND" );
			}
			
			Serial_RxFlag = 0;
		}
	}
}

Serial.c文件

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

char Serial_RxPacket[100];
uint8_t Serial_RxFlag;

void Serial_Init(void)
{
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	USART_InitTypeDef USART_InitStructure;
	USART_InitStructure.USART_BaudRate = 9600;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_Init(USART1, &USART_InitStructure);
	
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	USART_Cmd(USART1, ENABLE);
}

void Serial_SendByte(uint8_t Byte)
{
	USART_SendData(USART1, Byte);
	while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte(String[i]);
	}
}

uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;
	while (Y --)
	{
		Result *= X;
	}
	return Result;
}

void Serial_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + '0');
	}
}

int fputc(int ch, FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format, ...)
{
	char String[100];
	va_list arg;
	va_start(arg, format);
	vsprintf(String, format, arg);
	va_end(arg);
	Serial_SendString(String);
}

void USART1_IRQHandler(void)
{
	static uint8_t RxState = 0;
	static uint8_t pRxPacket = 0;
	
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{
		uint8_t RxData = USART_ReceiveData (USART1 );
		if(RxState == 0)
		{
			if (RxData == '@' && Serial_RxFlag  == 0)
			{
				RxState=1;
				pRxPacket = 0;
			}
		}
		else if (RxState == 1)
		{
			if (RxData == '\r')
			{
				RxState = 2;
			}
			else 
			{
				Serial_RxPacket [pRxPacket ]=RxData ;
				pRxPacket ++;
			}
		}
		else if (RxState == 2)
		{
			if (RxData == '\n')
			{
				RxState = 0;
				Serial_RxPacket [pRxPacket ]= '\0';
				Serial_RxFlag = 1;
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

Serial.h文件

#ifndef __SERIAL_H
#define __SERIAL_H

#include <stdio.h>

extern char Serial_RxPacket[];
extern uint8_t Serial_RxFlag;

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void Serial_SendString(char *String);
void Serial_SendNumber(uint32_t Number, uint8_t Length);
void Serial_Printf(char *format, ...);

#endif

FlyMCU串口下载

ST-LINK Utility

  • 19
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《STM32自学笔记第3版》是一本关于STM32微控制器的自学教程。该书深入介绍了STM32微控制器的原理、应用和编程方法。 本书第3版增加了最新的STM32系列微控制器的内容,包括了更多实际应用的案例和项目。读者通过该书可以学习到如何使用STM32微控制器进行各种应用开发,如控制系统、嵌入式系统、通讯系统等。 本书首先对STM32微控制器的硬件结构进行了详细的介绍,包括了芯片的内部结构、引脚的功能和特性。然后,通过具体的实验项目,教授了STM32的基本编程方法和技巧。读者可以通过这些实验,了解STM32的各种功能模块的使用方法,如ADC、USART、SPI等。同时,还介绍了如何使用STM32CubeMX和Keil MDK等常用的开发工具进行项目开发。 此外,本书还对STM32的外设扩展进行了介绍,如通过I2C总线连接外部设备,通过CAN总线实现通讯等。读者可以学习到如何通过外设扩展,实现更复杂的应用系统。 总的来说,《STM32自学笔记第3版》是一本系统、全面的STM32自学教程。通过学习该书,读者可以深入了解STM32微控制器,并掌握其在各种应用领域的开发方法。无论是初学者还是有一定STM32开发经验的人都可以从中获益。 ### 回答2: 《STM32自学笔记 第3版》是一本适合自学STM32开发板的参考书籍。本书以STM32微控制器为基础,详细介绍了STM32的硬件结构和软件开发环境。作者结合自身经验和实际案例,将复杂的知识点以简洁明了的方式呈现,使读者能够快速入门。 第3版的内容相比前两版有所扩充和更新。首先,在硬件方面,本书详细介绍了STM32的引脚布局和外设接口,包括GPIO、USART、I2C、SPI等,使读者能够充分了解STM32的基本硬件结构和功能。 其次,在软件方面,本书提供了详细的开发环境配置和编程指南。包括了Keil MDK开发环境的安装和使用、编译、调试和下载等操作步骤,并且给出了一些常见问题的解决方法。同时,作者还介绍了如何使用STM32的标准外设库进行编程,包括GPIO控制、中断处理、定时器、串口通信等。 除此之外,本书还介绍了一些实际应用案例,以帮助读者更好地理解和应用所学知识。例如,如何使用STM32控制LED灯、驱动液晶显示屏、读取温度传感器数据等。 总体而言,本书是一本系统全面的STM32自学教程。通过学习本书,读者可以了解STM32的基本原理和应用,掌握STM32的硬件配置和软件开发,从而能够独立完成STM32的项目开发。无论是初学者还是有一定基础的开发者都可以从本书中获得很大的收益。 ### 回答3: 《STM32自学笔记第3版》是一本专门介绍如何自学STM32开发的书籍。本书主要针对初学者,通过详细的讲解和实践项目的演示,帮助读者快速上手STM32开发。 书中首先介绍了STM32系列微控制器的基本知识,包括硬件架构、外设功能和寄存器的使用等。对于没有接触过STM32的读者来说,这一部分对于理解后面的内容非常重要。 接着,书中从简单的LED控制开始,逐步引导读者学习各种外设的使用,如GPIO、定时器、UART等。同时,每个外设的使用都配有具体的实例代码和详细的注解,方便读者理解和实践。 书中还介绍了STM32开发中常用的开发工具和环境配置方法。从芯片选择、开发板选购到软件安装和项目配置,都有详细的指导。同时,书中还提供了一些调试技巧和常见问题的解答,帮助读者避开一些容易遇到的坑。 最后,书中还介绍了一些常用的外设应用案例,如按键控制、LCD显示、温湿度传感器等。通过这些案例,读者可以更好地理解STM32的应用场景和开发思路,提升自己的技能水平。 总的来说,《STM32自学笔记第3版》是一本适合学习STM32开发的入门书籍。通过系统完整的内容和实例演示,读者可以快速掌握STM32的基本知识和开发技巧。无论是初学者还是有一定经验的开发者,都可以从中获得实际的帮助和启示。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值