文章目录

  • 一、USART是什么?
  • 二、硬件基础
    • 1.USART主机接USART从机
    • 2.USART主机接电脑USB
    • 3.USART主机接RS232从机
    • 4.USART主机接RS485从机
  • 三、功能框图
    • 1.功能引脚
    • 2.数据寄存器
    • 3.控制与状态寄存器
      • (1)发送
      • (2)接收
    • 4.波特比率寄存器
    • 5.保护时间和预分频寄存器
  • 四、代码编写
    • 1.中断接收
      • (1)发送一个字节
      • (2)发送8位的数组
      • (3)发送字符串
      • (4)发送一个16位数
      • (5)串口中断服务函数
    • 2.DMA
    • 3.printf重定向
    • 4.标准库函数解释
  • 总结
  • 参考文献

一、USART是什么?

USART全称Universal Synchronous/Asynchronous Receiver/Transmitter,即通用同步异步收发器,是一个全双工通用同步/异步串行收发模块。在嵌入式领域,它还有一个更口语化的称呼,即“串口”。

跟USART相似的还有一个UART,它是在USART基础上裁剪掉了同步通信功能,UART应用更为普遍。

二、硬件基础

USART仅规定了协议层通信标准,物理层硬件可以根据通讯对象灵活选择。

1.USART主机接USART从机

这种情况不用进行任何转换,直接将信号线交叉连接,地线直接连接即可。

2.USART主机接电脑USB

一般的单片机硬件设计都会预留一个USART连接电脑,用于在调试程序时可以将调试信息打印在电脑端的串口调试助手。
但单片机的USART无法直接与PC通讯,需要借助CH340CP2102等芯片实现USB与USART的相互转化。

3.USART主机接RS232从机

单片机的USART无法直接与232设备通讯,需要借助MAX232等芯片实现232与USART的相互转化。

4.USART主机接RS485从机

单片机的USART无法直接与485设备通讯,需要借助MAX485等芯片实现485与USART的相互转化。

三、功能框图

1.功能引脚

TX:数据发送
RX:数据接收
SW_RX:
nRTS:硬件流控制,略
nCTS:硬件流控制,略
SCLK:仅适用于同步模式,略

2.数据寄存器

数据寄存器USART_DR只有低9位有效,并且第9位是否有效受CR1寄存器的M位控制,详见控制寄存器部分。

USART_DR实际包含两个寄存器,一个用于发送的TDR,一个用于接收的RDR。发送时往USART_DR写入数据会自动存入TDR,读取USART_DR会自动提取RDR的数据。

3.控制与状态寄存器

该部分寄存器囊括了USART接收发送的功能控制,收发状态监视,中断控制。

RE:接收使能
TE:发送使能
RXNEIE:接收缓冲区非空中断使能
TCIE:发送完成中断使能
PS:校验位(0:偶校验,1:奇校验)
PCE:校验控制使能
M:字长(0:8位,1:9位)
UE:USART使能

STOP:停止位
00:1个停止位;
01:0.5个停止位;
10:2个停止位;
11:1.5个停止位;


DMAT:DMA使能发送
DMAR:DMA使能接收


PE:校验错误
RXNE:读数据寄存器非空
TC:发送完成
TXE:发送数据寄存器空


TC与TXE的区别:数据从TDR发送到发送移位寄存器之后触发TXE置位,但数据还没有从TX引脚发送出去,只有数据从TX引脚发送出去之后,才算完成一次数据发送,触发TC置位。也就是说,TC相比TXE更进一步。


(1)发送

第一步:一般设置数据位为8位,停止位为1位,无校验位;//M=0,STOP=00,PCE=0;

此时字符帧格式为“起始位+数据帧+停止位”。


注意:奇偶校验只能验错,不能验对,校验准确率在50%以下,故一般设置为无校验。


第二步:使能USART,使能TE,即可启动数据发送,发送顺序为低位在前,高位在后


举例:以发送字符’A’为例,其ASCII码为65,二进制为0100 0001,则发送次序为1000 0010。


第三步:发送数据过程中需要等待TC标志位1,当其置位时,表示传输完成,如果之前有设置TCIE为1,则在数据传输完成时产生中断。

(2)接收

跟接收相似,使能RE 之后开始接收数据,接收完成后就把数据从移位寄存器移到RDR内,并把RXNE置位,如果之前将RXNEIE置位的话将触发中断。

4.波特比率寄存器

USART发送器和接收器使用相同的波特率,计算公式为:

波特率=fPLCK16×USARTDIV波特率=\frac{fPLCK}{16\times USARTDIV} 波特率=16×USARTDIVfPLCK​
其中fPCLK为USART时钟,以F1为例,USART1挂载在APB2总线上,时钟频率为72M,USART2挂载在APB1总线上,时钟频率为36M。

USARTDIV由BRR寄存器定义,低4位定义其小数部分,高12位定义其整数部分。


例子:如果要波特率为115200,则由:
115200=7200000016×USARTDIV115200=\frac{72000000}{16\times USARTDIV} 115200=16×USARTDIV72000000​
得到USARTDIV=39.0625,DIV_Fraction=0.0625*16=0x01,DIV_Mantissa=39=0x17,则BRR应设置为0x171。

有时候不能保证经过配置后的实际波特率与目标波特率完全相等,但允许10%以内的误差,要知道USART是支持异步通信的,收发双方波特率不用完全一致。



波特率与比特率的区别:比特率容易理解,即bit/s,波特率表示每秒传输多少个码元,常见的通讯中一个码元表示两种状态(0或1),要知道一个比特也表示两种状态(0或1),所以大部分情况下比特率与波特率从数值上是划等号的(单位不一样)。

假设某种通讯中一个码元表示4种状态(ABCD),物理上可以用0V、2V、4V、6V表示。此时必须用两个二进制才能表示此种通讯下的一个状态(00表示A,01表示B,10表示C,11表示D),也就是一个码元对应两个二进制位,码元数是二进制数的一半,故波特率是比特率的一半。

波特率=比特率/每符号含的比特数波特率=比特率/每符号含的比特数 波特率=比特率/每符号含的比特数

比特率仅是二进制下的概念,波特率是所有进制下的概念,比特率是波特率的子集。



关于不同波特率对应的传输速度
常见波特率从2400到460800不等,2400即

2400/8/1024=0.29kb/s2400/8/1024=0.29kb/s 2400/8/1024=0.29kb/s
115200即

115200/8/1024=14.06kb/s115200/8/1024=14.06kb/s 115200/8/1024=14.06kb/s
460800即

460800/8/1024=56.25kb/s460800/8/1024=56.25kb/s 460800/8/1024=56.25kb/s

USART对波特率其实没有上限,需要考虑到实际通信双方的参数要求,F1的波特率最高支持4.5Mb/s


5.保护时间和预分频寄存器

智能卡模式或红外模式下需要配置GTPR,此处省略。

四、代码编写

下面的代码都是基于“USART主机接电脑USB”硬件基础编写的。

1.中断接收

功能:主机可向PC发送字节、数组、字符串、16位数据;主机通过中断方式接收数据。
首先是硬件初始化:

void USART_Config(void)
{GPIO_InitTypeDef GPIO_InitStructure;USART_InitTypeDef USART_InitStructure;// 打开串口用到的GPIO的时钟DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);// 打开串口外设的时钟DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);// 将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);// 将USART Rx的GPIO配置为浮空输入模式GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);// 配置串口的工作参数// 配置波特率(BRR寄存器)USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;// 配置数据字长(CR1寄存器M位)USART_InitStructure.USART_WordLength = USART_WordLength_8b;// 配置停止位(CR2寄存器STOP位)USART_InitStructure.USART_StopBits = USART_StopBits_1;// 配置校验位(CR1寄存器PCE位)USART_InitStructure.USART_Parity = USART_Parity_No ;// 配置硬件流控制USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;// 配置工作模式,收发一起(CR1寄存器TE/RE位)USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;// 完成串口的初始化配置USART_Init(DEBUG_USARTx, &USART_InitStructure);// 串口中断优先级配置NVIC_Configuration();// 使能串口接收中断(CR1寄存器RXNEIE位)USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);   // 使能串口(CR1寄存器UE位)USART_Cmd(DEBUG_USARTx, ENABLE);
}

为什么TXD/RXD要配置为推挽复用和浮空输入模式?

答:数据手册规定,具体可看【STM32】GPIO末尾


其中的串口中断优先级配置函数:

static void NVIC_Configuration(void)
{NVIC_InitTypeDef NVIC_InitStructure;/* 嵌套向量中断控制器组选择 */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);
}

到此初始化已经完成,可以开始发送数据。

(1)发送一个字节

void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{/* 发送一个字节数据到USART */USART_SendData(pUSARTx,ch);/* 等待发送数据寄存器为空,置位后即退出循环 *//* 但TXE置位并不代表数据已从TXD引脚发送完毕,只是说明发送寄存器空了,可以填装下一个数据了 */while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

USART_SendData是标准库函数,其定义如下:

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)
{/* Check the parameters */assert_param(IS_USART_ALL_PERIPH(USARTx));assert_param(IS_USART_DATA(Data)); /* Transmit Data *//* 可以看出它是往DR寄存器直接装填数据*//* DR寄存器是8位的,个人觉得形参写uint8_t更直接,可能是为了跟其他函数保持参数格式一致吧 */USARTx->DR = (Data & (uint16_t)0x01FF);
}

(2)发送8位的数组

void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{uint8_t i;for(i=0; i<num; i++){/* 发送一个字节数据到USART *//* 调用上一个函数 */Usart_SendByte(pUSARTx,array[i]);    }/* 等待发送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

(3)发送字符串

void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{unsigned int k=0;do {Usart_SendByte( pUSARTx, *(str + k) );k++;} while(*(str + k)!='\0');/* 等待发送完成 */while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET){}
}

(4)发送一个16位数

void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{uint8_t temp_h, temp_l;/* 取出高八位 */temp_h = (ch&0XFF00)>>8;/* 取出低八位 */temp_l = ch&0XFF;/* 发送高八位 */USART_SendData(pUSARTx,temp_h);   while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);/* 发送低八位 */USART_SendData(pUSARTx,temp_l);  while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

(5)串口中断服务函数

void DEBUG_USART_IRQHandler(void)
{uint8_t ucTemp;if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET){      ucTemp = USART_ReceiveData(DEBUG_USARTx);USART_SendData(DEBUG_USARTx,ucTemp);    }
}

2.DMA

此部分留到总结完DMA之后再补充

3.printf重定向

我们想直接使用c语言里面的printf、scanf等函数,就需要对其进行重定向。

由于printf 最终是调用 fputc 输出数据,而 fputc 是一个 weak(弱引用)函数,覆写即可重定向 printf。scanf也是如此。

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{/* 发送一个字节数据到串口 */USART_SendData(DEBUG_USARTx, (uint8_t) ch);/* 等待发送完毕 */while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);      return (ch);
}///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{/* 等待串口输入数据 */while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);return (int)USART_ReceiveData(DEBUG_USARTx);
}

注意需要勾选微库:

这样,只要加上#include<stdio.h>,就可以用printf打印数据了。

4.标准库函数解释

void USART_DeInit(USART_TypeDef* USARTx);//将USART寄存器重置为默认值 ,即恢复出厂设置
void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct);//将USART_InitStruct里的参数写入寄存器
void USART_StructInit(USART_InitTypeDef* USART_InitStruct);//给USART_InitStruct填充默认值
void USART_ClockInit(USART_TypeDef* USARTx, USART_ClockInitTypeDef* USART_ClockInitStruct);//同步通讯时需要用到
void USART_ClockStructInit(USART_ClockInitTypeDef* USART_ClockInitStruct);//给USART_ClockInitStruct填充默认值
void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);//使能USART
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState);//中断使能
void USART_DMACmd(USART_TypeDef* USARTx, uint16_t USART_DMAReq, FunctionalState NewState);//DMA使能
void USART_SetAddress(USART_TypeDef* USARTx, uint8_t USART_Address);//USART节点地址设置
void USART_WakeUpConfig(USART_TypeDef* USARTx, uint16_t USART_WakeUp);//USART唤醒方式
void USART_ReceiverWakeUpCmd(USART_TypeDef* USARTx, FunctionalState NewState);//静默模式使能
void USART_LINBreakDetectLengthConfig(USART_TypeDef* USARTx, uint16_t USART_LINBreakDetectLength);
void USART_LINCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);//发送数据
uint16_t USART_ReceiveData(USART_TypeDef* USARTx);//接收数据
void USART_SendBreak(USART_TypeDef* USARTx);//发送断开字符
void USART_SetGuardTime(USART_TypeDef* USARTx, uint8_t USART_GuardTime);
void USART_SetPrescaler(USART_TypeDef* USARTx, uint8_t USART_Prescaler);
void USART_SmartCardCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_SmartCardNACKCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_HalfDuplexCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OverSampling8Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_OneBitMethodCmd(USART_TypeDef* USARTx, FunctionalState NewState);
void USART_IrDAConfig(USART_TypeDef* USARTx, uint16_t USART_IrDAMode);
void USART_IrDACmd(USART_TypeDef* USARTx, FunctionalState NewState);
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);//获取状态
void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);//清除状态
ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT);//检查指定的USART中断是否发生。
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);

总结

USART作为STM32除了GPIO外最重要的外设,在几乎每个工程中都有应用。
usart优点
(1)只要两根数据线就可以实现通讯(简单)
(2)由于只用于两个设备间一对一通讯,不需要寻址
usart缺点
(1)速度慢(一般应用都在Kb级别)
(2)仅适合于两个设备之间的通讯
(3)数据帧大小有限

参考文献

1.《STM32库开发实战指南》
2.超简单的一种通信,2分钟搞懂,串口通讯的工作原理!https://www.bilibili.com/video/BV1y34y147s5?spm_id_from=333.337.search-card.all.click&vd_source=53d27d53fc6ff276be6e93437afd3e0e
3.https://www.rfwireless-world.com/Terminology/Advantages-and-Disadvantages-of-UART.html

【STM32】USART相关推荐

  1. 【STM32】USART相关函数和类型

    00. 目录 文章目录 00. 目录 01. USART固件库概述 02. USART相关类型 03. USART相关其它宏 04. USART相关函数 05. USART其它 06. 附录 07. ...

  2. 【STM32】USART收发---内嵌中断向量控制器

    目录 几个概念: 1.USART初始化 2.USART中断配置---内嵌中断向量控制器 3.一种串口发送格式化数据方法 几个概念: 内嵌向量中断控制器:Nested Vectored Interrup ...

  3. 【STM32】USART接收不定长数据、防止数据溢出

    给大家分享一种usart接收不定长数据,并且防止数据溢出卡死等情况: 在这里需要引用到两个中断,接收数据非空中断(RXNE)&接收总线空闲中断(IDLE),废话不多说,直接上代码. 中断服务函 ...

  4. 【STM32】STM32系列教程汇总(暂时暂停更新...)

    00. 目录 文章目录 00. 目录 01. STM32平台简介 02. STM32开发环境 03. STM32初级教程 04. STM32中级教程 05. STM32高级教程 06. FreeRTO ...

  5. 【STM32】串口收发主要程序代码分析

    文章目录 数据发送与接收 串口状态 开启串口响应中断 获取相应中断状态 main.c usart.c usart.h 串口设置的一般步骤可以总结为如下几个步骤: 串口时钟使能,GPIO 时钟使能 串口 ...

  6. 【STM32】FreeRTOS列表应用示例

    00. 目录 文章目录 00. 目录 01. 概述 02. 任务设计 03. 相关设置 04. 程序设计 05. 实验结果 06. 附录 07. 参考 01. 概述 掌握FreeRTOS中列表和列表项 ...

  7. 【STM32】FreeRTOS任务挂起和恢复示例

    00. 目录 文章目录 00. 目录 01. 概述 02. 功能描述 03. 任务设计 04. 程序设计 05. 执行结果 06. 附录 07. 参考 01. 概述 任务挂起和恢复,当某个任务要停止运 ...

  8. 【STM32】FreeRTOS创建和删除任务示例(静态方法)(了解)

    00. 目录 文章目录 00. 目录 01. 概述 02. 功能描述 03. 任务设计 04. 程序设计 05. 结果验证 06. 附录 07. 参考 01. 概述 FreeRTOS中创建和删除任务A ...

  9. 【STM32】FreeRTOS创建和删除任务示例(动态方法)

    00. 目录 文章目录 00. 目录 01. 概述 02. 功能描述 03. 任务设计 04. 程序设计 05. 结果验证 06. 附录 07. 参考 01. 概述 FreeRTOS中创建和删除任务A ...

最新文章

  1. 第一次搜索-连连看= =
  2. PowerDesigner 使用的一些技巧(转)
  3. WebDriver原理分析
  4. pdftk — PDF万用命令行工具
  5. Eigen教程(3)之矩阵和向量的运算
  6. 《程序是怎样跑起来的》第六章有感
  7. 找不到匹配的key exchange算法_AC自动机 | 多字符串匹配你会吗?
  8. 三维模型免费查看器(AnyCAD Viewer)
  9. C语言全局变量和数组的应用
  10. STM32单片机USB扫码枪开发笔记
  11. 高通CSRA6640单芯片DDFA放大器解决方案
  12. HTML与CSS的使用与总结
  13. 小知识:软件开发的权限控制和权限验证
  14. ERP/MIS开发 LLBL Gen多表操作
  15. 计算机触摸屏维修,工控触摸屏常见的故障问题和维修方法分别是什么
  16. arduino陀螺仪蓝牙通讯手势小车
  17. 一般3d模型代做多少钱_代做su模型一般怎么收费
  18. Java中Stream的使用
  19. java 手机号码生成_用R语言和java实现随机生成手机号码
  20. 代码随想录训练营day33

热门文章

  1. C++调用Java方法
  2. cordova 将cesium 打包成Android App
  3. python字典操作函数_【python】字典的操作方法和函数
  4. 鹏程万里-----python开发中遇到的问题
  5. 使用JS实现无刷新读取json接口数据
  6. clone fail smartgit_SmartGit
  7. Sliding Mode Control and Observation 学习 Chapter 1: Fast Terminal Sliding Mode 快速终端滑模
  8. 【热书推荐】《传奇战神》全章节在线阅读
  9. 利用js点击小眼睛图片实现转换明文暗码的效果
  10. 新浪微博APP开发demo