文章目录
- 什么是USART
- USART框图
- stm32的Usart串口收发配置
- 初始化
- 发送
- 接收
- 重定向的几种方法
- 串口发送数据包
什么是USART
- USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器
- USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
- 自带波特率发生器,最高达4.5Mbits/s
- 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2)
- 可选校验位(无校验/奇校验/偶校验)
- 支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN //基于串口通信的一些变体通信方式
STM32F103C8T6 USART资源: USART1、 USART2、 USART3
USART框图
- 灰色的两个寄存器 再物理上是独立的两个寄存器,但软件上共用一个地址
- 收发数据的细节 移位寄存器与数据寄存器的工作流程
- 最后都是一位一位的输入或输出.
- 可以发现输入和输出是相反的操作
- 输出:
- 直接写入一个byte的数据到 发送数据寄存器(TDR)
- 从发送数据寄存器一位一位的移动到 发送移位寄存器
- 再串行输出
- 输入:
- 一位一位的串行数据读入到接收移位寄存器
- 当满了一个Byte ,这一个Byte的数据就从 接收移位寄存器 转到接收数据寄存器(RDR)中。
- 再直接读取
stm32的Usart串口收发配置
初始化
- 初始化对应gpio
- 初始化usart外设
- USART_BaudRate //波特率
- USART_HardwareFlowControl //硬件流控制,通常不需要
- USART_Mode //选择收发模式
- USART_Parity //选择是否要奇偶校验
- USART_StopBits //停止位长度,选择1位
- USART_WordLength //选择字长,即为数据帧的长度
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟/*GPIO初始化*/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); //将PA9引脚初始化为复用推挽输出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); //将PA10引脚初始化为上拉输入/*USART初始化*/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; //停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长,选择8位USART_Init(USART1, &USART_InitStructure); //将结构体变量交给USART_Init,配置USART1/*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //开启串口接收数据的中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE); //使能USART1,串口开始运行
}
发送
- 将数据写入发送数据寄存器(TDR)后,要等待写入完成再进行下一次写入(避免数据覆盖)
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte); //将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}
接收
- 通过串口中断实现数据接收
void USART1_IRQHandler(void)
{if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断{Serial_RxData = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量Serial_RxFlag = 1; //置接收标志位变量为1USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除USART1的RXNE标志位//读取数据寄存器会自动清除此标志位//如果已经读取了数据寄存器,也可以不执行此代码}
}
重定向的几种方法
- 直接改fputc 改fputc
(printf就是由fputc实现的) - 自己封装printf函数
void Serial_Printf(char *format, ...)
{char String[100]; //定义字符数组va_list arg; //定义可变参数列表数据类型的变量argva_start(arg, format); //从format开始,接收参数列表到arg变量vsprintf(String, format, arg); //使用vsprintf打印格式化字符串和参数列表到字符数组String中va_end(arg); //结束变量argSerial_SendString(String); //串口发送字符数组(字符串)
}
串口发送数据包
- 两种数据包 ,固定与不固定,文本与十六进制
- 状态机思想 (注意其中状态的判断用if else if级联,不要用if语句)
- 接收数据 -> 根据不同的状态做不同的处理
/*** 函 数:USART1中断函数* 参 数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行* 函数名为预留的指定名称,可以从启动文件复制* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void USART1_IRQHandler(void)
{static uint8_t RxState = 0; //定义表示当前状态机状态的静态变量static uint8_t pRxPacket = 0; //定义表示当前接收数据位置的静态变量if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET) //判断是否是USART1的接收事件触发的中断{uint8_t RxData = USART_ReceiveData(USART1); //读取数据寄存器,存放在接收的数据变量/*使用状态机的思路,依次处理数据包的不同部分*//*当前状态为0,接收数据包包头*/if (RxState == 0){if (RxData == 0xFF) //如果数据确实是包头{RxState = 1; //置下一个状态pRxPacket = 0; //数据包的位置归零}}/*当前状态为1,接收数据包数据*/else if (RxState == 1){Serial_RxPacket[pRxPacket] = RxData; //将数据存入数据包数组的指定位置pRxPacket ++; //数据包的位置自增if (pRxPacket >= 4) //如果收够4个数据{RxState = 2; //置下一个状态}}/*当前状态为2,接收数据包包尾*/else if (RxState == 2){if (RxData == 0xFE) //如果数据确实是包尾部{RxState = 0; //状态归0Serial_RxFlag = 1; //接收数据包标志位置1,成功接收一个数据包}}USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除标志位}
}