STM32 串口通信USART

USART简介

USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器(UART是异步收发器)

USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里

自带波特率发生器(相当于分频器),最高达4.5Mbits/s

可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)

可选校验位(无校验/奇校验/偶校验)

支持同步模式、硬件流控制(告诉对方是否准备好工作)、DMA、智能卡、IrDA、LIN

STM32F103C8T6 USART资源: USART1、 USART2、 USART3。具体IO引脚查看GPIO定义图

发送器控制发送移位寄存器,当有数据进入到发送数据寄存器时,会把数据转移到移位寄存器,同时会给标志位置1,可以告诉是否可以继续写入数据。接收移位寄存器和接收数据寄存器一样的道理。

其中SCLK只支持输出,不支持输入,所以两个USART之间不能实现同步的串口通信,但可以兼容别的协议,例如兼容SPI。也可以做自适应波特率。唤醒单元可以实现对多设备的通信,发送相应的地址时该设备就会工作。

在输入时对于每一位还会进行16次的采样,以确保数据的准确性,同时也会在噪声标志位NE置1。具体原理图见PPT107。

 发送器和接收器的波特率由波特率寄存器BRR里的DIV确定

计算公式:波特率 = fPCLK2/1 / (16 * DIV)

当以文本显示时,对应的HEX会转换为相应的字符

在USART时,如果使用字节一个个传送的话会造成一些错误,这时可以把几个字节封装起来发送。这就是数据包

 数据包分为这两种,包头包尾相当于一个标志位,当接收到一个特定的字节或字符的时候可以开始接收或者结束,例如然后把他们放到数组。其中数据包接收使用到的是状态机方法接收,当接收的不是指定的字节或者字符为包头时就会一直等待,直到接收到,接收到就会置S=0,进入到收集数据中…流程图可见上述。

#ifndef __SERIAL_H
#define __SERIAL_H

 #include <stdio.h> 
extern uint8_t Serial_TxPacket[];
extern char Serial_RxPacket[];
void Serial_Init(void);
void Serial_SendArry(uint8_t *Array,uint16_t Length);
void Serial_SendByte(uint8_t Byte);
void Serial_SendString(char*String);
void Serial_SendNumber(uint32_t Number,uint8_t Length);
void Serial_printf(char*format,...);
uint8_t Serial_chetByte();
uint8_t Serial_GetRxFlag(void);
uint8_t Serial_GetRxData(void);
void Serial_SendPacket(void);
#endif

#include "stm32f10x.h"                  // Device header
#include <stdio.h>                
#include <stdarg.h> 
uint8_t Serial_RxData;
uint8_t Serial_RxFlag;

uint8_t Serial_TxPacket[4];
char Serial_RxPacket[100];
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;//这里数据手册上PA9可以直接用USART1
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
 	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//这里数据手册上PA9可以直接用USART1
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
 	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的输出
	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);//等待一下查看标志位是否置1,以免覆盖原来的数据
	
}
uint8_t Serial_chetByte()//直接读取寄存器的值,但会一直占用软件资源
{

	while(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==RESET);
	Serial_RxData=USART_ReceiveData(USART1);
	return Serial_RxData;
	
}

void Serial_SendArry(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;  //相当于X的Y次方
	}
	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+0x30);
	}
	
}

int fputc(int ch,FILE*f)//printf
{
	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 Serial_SendPacket(void)//发送数据包函数
{
	Serial_SendByte(0xFF);
	Serial_SendArry(Serial_TxPacket,4);
	Serial_SendByte(0xFE);
	
}

void USART1_IRQHandler(void)//中断函数
{
	static uint8_t RxState=0;
	static uint8_t pRxPacket=0;//用来指示数组位置

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

使用Serial_printf函数即可输出,例如Serial_printf("Num=%d\r\n",666);stm32就会从TX发送666的数据到连接的设备。 USART_ReceiveData(USART1); 这个函数是stm32用来接收数据的,上述代码是有带协议的,需要@[]/r/n,带协议的意思就是要完整的格式才能真正接收到,例如@2/r/n,只有2才是真正想要的数据,其他的是协议格式,也不一定是这些字符,可以自已定义。带协议的主要目的是为了防止错误,可以保证程序的严谨性。

想要无协议的只需把中断函数改成以下的就可以

//无协议

void USART1_IRQHandler(void)//中断函数
{


	
	if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
	{
			Serial_RxData=USART_ReceiveData(USART1);
			
	}
}

本文是跟着江科大学习的,是本人的学习笔记,如有侵权请联系本人删除!

猜你喜欢

转载自blog.csdn.net/m0_71827453/article/details/133269081