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);
}
}
本文是跟着江科大学习的,是本人的学习笔记,如有侵权请联系本人删除!