1、电平标准 

  根据通讯使用的电平标准不同,串口通讯可分为TTL标准和RS-232标准,如下表:

  从图中可以看到,TTL电平标准使用5V表示高电平,使用0V表示低电平。在R232电平标准中,为了增加串口通讯的远距离传输及抗干扰能力,使用的是-15V表示高电平,使用+15V表示低电平。如下图为RS232和TLL电平标准表示同一个信号时的对比。

  在电子电路中,一般使用TTL电平进行通讯,而在PC机中则使用RS232电平进行通讯。所以为了使电子设备可以和PC机进行串口通讯,必须对TTL电平和RS232电平的信号进行互相转换。

2、串口协议

  串口通讯的英文全称为Serial Communication,这是一种在设备间非常常用的串行通讯方式。

  串口通讯的协议,串口通讯的数据包由发送设备通过自身的TXD接口传输到接收设备的RXD接口。在串口通讯的协议中,规定了数据包的内容,该内容由起始位、数据位、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。格式如下图:

  在数据帧格式中,校验位可以要也可以不要。

  一般在串口通信中,空闲状态下,IO口的电平为高电平。

3、串口波特率

  串口通讯一般使用的是异步通讯,异步通讯是没有时钟信号的,为了保证两个设备能够正常通讯,必须在两个设备间约定好收发的速率,波特率就是设备的收发速率,波特率表示的是单位时间内收发的bit位,即一个bit的收发时长。比如波特率为9600的设备,那么该设备1S的时间内可以收发9600个bit,发送一个bit的时长位1/9600≈104us。

4、数据帧的起始信号和停止信号  

  串口通讯的一个数据包是从起始信号开始的,直到停止信号结束。数据包的起始信号由一个逻辑0的数据位表示,而数据包的停止信号可由0.5、1、1.5或2个逻辑1的数据位表示。

  有效数据:

  在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定位5、6、7或8位。

  数据校验:

  在有效数据之后,有一个可选的数据校验位,由于数据通信相对更容易受到外部干扰导致传输数据出现偏差,可以在传输过程中加上校验位来解决这个问题,校验方法有奇校验、偶校验、0校验、1校验及无校验。

  奇校验要求有效数据和校验位中“1”的个数为奇数,比如一个8位长的有效数据位:01101001,此时总共有4个“1”,为达到奇校验的效果,校验位为“1”,最后传输的数据将是8位的有效数据加上1位的校验位总共9位。

  偶校验与奇校验要求刚好相反,要求有效数据和校验位中“1”的个数为偶数,比如数据帧:11001010,此时有效数据“1”的个数位4,所以偶校验位为“0”。

  0校验是不管有效数据中的内容是什么,校验位总为“0”。

  1校验是不管有效数据中的内容是什么,校验位总为“1”。

5、UART和USART的区别 

  UART是指通用异步收发器,UASRT是指通用同步/异步收发器。从名称上可是看出,USART是在UART基础上增加了同步功能,即USART是UART的增强型。

  STM32的USART一般做异步通信,即UART功能。

6、USART功能概述 

  使用USART双向通信至少需要三个脚:GND、RX、TX。

  RX:接收数据引脚,通过采样技术来区别数据和噪音,从而恢复数据。

  TX:发送数据引脚,当发送器被禁止时,输出引脚恢复到它的IO端口配置。当发送器被激活,并且不发送数据时,TX引脚处于高电平,也就是说USART的空闲状态的电平时高电平。这里需要注意一点的是,当与设备相连的时候,如果设备断电,这需要将发送器禁止,使TX口输出低电平,否者有可能引起IO口电流倒灌。

  TX脚位在单线和智能卡模式里被同时用于数据的发送和接收。

  USART总线在发送或接收前应处于空闲状态,即高电平。

  一个起始位,起始位为1。

  一个数据字,可以使8位数据或9位数据。发送或接收数据的时候,是先发送或接收低位数据。

  停止位可设置为0.5、1、1.5、2个停止位,发送或接收停止位说明数据帧发送或接收完成。

  使用分数波特率发生器,由12位整数和4位小数组成。

  一个状态寄存器USART_SR

  数据寄存器USART_DR

  一个波特率寄存器USART_BRR,12位表示波特率的整数,4位表示波特率的小数。

  一个智能卡模式下的保护时间寄存器USART_GTPR

  USART的框图如下:

7、STM32的USART波特率配置 

  STM32的每个串口都有一个独立的波特率寄存器USART_BRR,该寄存器由USART分频器除法因子USARTDIV的整数部分和小数部分组成。bit4~bit15这12位组成了整数部分DIV_Mantissa,bit0~bit3这4个位组成了分数部分DIV_Fraction。

  波特率的计数公式 = CLK/(16*USARTDIV)。 

  CLK是USART的时钟频率,USART2、USART3、USART4、USART5使用的是APB1总线时钟,一般是36MHZ(STM32F103系列);而USART1则使用的是APB2总线时钟,一般是72MHZ(STM32F103系列)。

  可以根据需要的波特率的值换算出USARTDIV的值。比如设置波特率为9600,则USARTDIV为:

    USARTDIV = CLK / (16 * 9600)

  如果CLK = 72MHZ,则:

  USARTDIV = 72000000/(16*9600) = 468.75

  将小数部分换算为16进制:

    DIV_Fraction = 0.75*16 = 12 = 0x0C

  将整数部分转为16进制:

    DIV_Mantissa = 468 = 0x1D4

  所以波特率寄存器USART_BRR的值为0x1D4C。

  设置波特率时的误差如下图:

  注意不要在通信过程中改变波特率寄存器的值。

8、USART中断请求

  USART中断请求表:

  

  USART中断映像图:

  从中断映像图中可以看出,USART的各种中断事件被连接到同一个中断向量,也就是说USART产生的中断都会进入到同一个中断服务函数内。

  各种中断事件:

  发送期间:发送完成、清除发送、发送数据。

  接收期间:空闲总线检测、溢出错误、接收数据寄存器非空、校验错误、LIN断开符号检测、噪音标志(仅在多缓冲器通信)和帧错误(仅在多缓冲器通信)。

  如果设置了对应的使能控制位,这些事件就可以产生各自的中断,可以在中断服务函数内通过判断状态寄存器的位来区分是哪个中断。

9、USART寄存器

  只说明一些用到的位。

  USART_SR状态寄存器:

  Bit7 TXE位:当TDR寄存器中的数据被硬件转移到移位寄存器的时候,TXE被硬件置位。

  Bit6 TC位:当发送完一帧有效数据的时候,TC就会置位。

  Bit5 RXNE位:当USART的RDR移位寄存器中的数据被转移到USART_DR寄存器中时,RXNE位被置1,也就是收到数据的时候RXNE位被置1;如果USART_CR1寄存器中的RXNEIE使能,且USART的中断使能,则产生中断,就是所谓的接收中断。RXNE位可以通过对USART_DR寄存器的读取操作来清除,也可以对USART_SR寄存器的bit5写0来清除。

   USART_DR数据寄存器:

  该寄存器用来发送和接收数据。

  USART_BRR波特比率寄存器:

  该寄存器用来设置USART的波特率。

  USART_CR1控制寄存器:

  Bit13 UE位:这个位用来使能和停止USART模块,这有UE位被置1的时候,USART才能工作。

  Bit12 M位:该位用来定义数据字的长度,当M=0时,数据字的长度为8个bit;当M=1时,数据字的长度为9个bit。

  Bit10 PCE位:该位用来控制是否使用校验,当PCE=0时,禁止校验控制;当PCE=1时,使能校验控制。

  Bit9 PS位:该位用来选择校验方式,当PS=0时,使用偶校验方式;当PS=1时,使用奇校验。

  Bit8 PEIE位:该位用来控制校验错误中断,当PEIE=0时,禁止校验错误产生中断;当PEIE=1时,使能校验错误产生中断。

  Bit7 TXEIE位:这个是发送缓冲区空中断使能位,当TXEIE=0时,禁止发送缓冲区空时产生中断;当TXEIE=1时,使能发送缓冲区空时产生中断。

  Bit6 TCIE位:这个是发送完成 中断使能,当TCIE=0时,禁止发送完成产生中断;当TCIE=1时,使能发送完成产生中断。

  Bit5 RXNEIE位:这个是接收缓冲区非空中断使能,当RXNEIE=0时,禁止接收完成时产生中断;当RXNEIE=1时,使能接收完成时产生中断。

  Bit4 IDLEIE位:这个是IDLE总线空闲中断使能位,当IDLEIE=0时,禁止检测到总线空闲时产生中断;当IDLEIE=1时,使能检测到总线空闲时产生中断。

  Bit3 TE位:这个是发送使能位,当TE=0时,禁止USART发送数据;当TE=1时,使能USART发送数据。

  Bit2 RE位:这个是接收使能位,当RE=0时,禁止USART接收数据;当RE=1时,使能USART接收数据。

  这里主要区分发送缓冲区空和发送完成这两个点,发送缓冲器空是指USART_DR寄存器的数值被硬件转移到以为寄存器的时候,也就是说要发送的数值进入移位寄存器的时候,发送缓冲器空这个条件就会成立。而发送完成是指移位寄存器里的数据发送完成。

  当将一个数据写入USART_DR寄存器的时候,硬件将USART_DR寄存器的值送入移位寄存器,送完之后会置位USART_SR寄存器的bit7 TXE位,而当移位寄存器里的数值发送完成之后会置位USART_SR寄存器的bit6 TC位。

  USART_CR2控制寄存器:

  Bit12~Bit13 STOP位:这个是停止位长度的选择位,当STOP=00时,使用1个停止位;当STOP=01时,使用0.5个停止位;当STOP=10时,使用2个停止位;当STOP=11时,使用1.5个停止位;

  需要注意的是UART4和UART5不能使用0.5和1.5个停止位。

10、HAL库操作USART

  在工程之中添加stm32f1xx_hal_uart.c和stm32f1xx_hal_uart.h文件。

  初始化代码如下:

 UART_HandleTypeDef UART_Handler;

 uint8_t aRxBuffer;

 void USART_Init(void)
{ UART_Handler.Instance = USART1;
UART_Handler.Init.BaudRate = ;
UART_Handler.Init.Mode = UART_MODE_TX_RX;
UART_Handler.Init.StopBits = UART_STOPBITS_1;
UART_Handler.Init.WordLength = UART_WORDLENGTH_8B;
UART_Handler.Init.Parity = UART_PARITY_NONE;
UART_Handler.Init.HwFlowCtl = UART_HWCONTROL_NONE;
HAL_UART_Init(&UART_Handler); //HAL_UART_Receive_IT(&UART_Handler,aRxBuffer,2);
__HAL_UART_ENABLE_IT(&UART_Handler, UART_IT_RXNE); HAL_NVIC_SetPriority(USART1_IRQn,,);
HAL_NVIC_EnableIRQ(USART1_IRQn); }

  使用HAL_UART_Init()函数初始化USART设备。

  从初始化代码内,可以看到,需要选择初始化的USART、波特率、USART的模式(接收和发送)、停止位长度的选择、数据的长度选择、校验的选择、硬件控制流的选择。还需要设置USART的时钟和脚位,但是HAL_UART_Init()函数内会调用USART的时钟和脚位的初始化回调函数,可以在回调函数内设置脚位和时钟。

  初始化完USART之后,打开USART的接收中断,由于HAL库的接收程序比较复杂,这里直接调用宏__HAL_UART_ENABLE_IT(&UART_Handler, UART_IT_RXNE)来打开USART的接收中断。

  IO和RCC初始化函数如下:

 void HAL_UART_MspInit(UART_HandleTypeDef *husart)
{
GPIO_InitTypeDef GPIO_InitStruct; __HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_USART1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); }

  这里注意的是RX脚,既可以设置成浮空输入模式也可以设置成复用输入模式,这两个是一样的。

  中断处理函数如下:

 void USART1_IRQHandler(void)
{
//HAL_UART_IRQHandler(&UART_Handler); if(__HAL_UART_GET_FLAG(&UART_Handler,UART_FLAG_RXNE))
{
aRxBuffer = UART_Handler.Instance->DR;
HAL_UART_Transmit(&UART_Handler,&aRxBuffer,,);
}
}

  由于HAL库的中断服务处理比较繁琐,这里直接读取USART的中断标志位来处理接收中断。

  收到数据到,通过读取USART_DR寄存器的值来获取接收到的数值,这里将接收到的数据再发送出去。

HAL库中断处理函数说明:

  在HAL库中断处理中,进入中断处理函数之后会调用HAL_UART_IRQHandler()函数,在HAL_UART_IRQHandler()函数内判断是那种类型的中断,然后去执行响应的操作。比如接收到数据后,会在HAL_UART_IRQHandler()函数内调用UART_Receive_IT()函数。

  这里需要注意一个问题,在UART_Receive_IT()函数中,接收完成后会清除掉接收中断的使能位,实际在用HAL库来处理中断的时候,接收中断使能位莫名会被清掉。

  在UART_Receive_IT()函数中会调用接收数据的回调函数HAL_UART_RxCpltCallback()来处理数据。

  使用HAL_UART_Receive_IT(&UART_Handler,aRxBuffer,len)函数来接收数据的时候,如果数据长度不为1,则接收到1个数据的时候,不会进入到HAL_UART_RxCpltCallback()函数中,只有收到实际设置的数据长度时,才会进入HAL_UART_RxCpltCallback()函数。通过进入UART_Receive_IT()函数查看代码的时候,会发现,当接收完实际设置的数据长度时,会清除掉接收中断的使能位,这里需要小心,防止收到一次数据之后,不能再进入接收中断。

添加支持printf()函数的代码:

 //加入以下代码,支持printf函数,而不需要选择use MicroLIB
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle; }; FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while(__HAL_UART_GET_FLAG(&UART_Handler,UART_FLAG_TC) == RESET){}; UART_Handler.Instance->DR = (uint8_t) ch; return ch;
}

  加上以上代码后就可以使用printf()函数发送UART数据了,比如printf("hello world!")。

STM32F103ZET6串口通信的更多相关文章

  1. STM32串口通信USART1转USART2问题解决

    使用的是STM32f103ZET6. 1.把文件main.c和usart.c中的所有usart1换成usart2 2.查看手册得知USART2的引脚是Tx->PA2,Rx->PA3,改变u ...

  2. (stm32f103学习总结)—USART串口通信

    一. USART简介 USART即通用同步异步收发器,它能够灵活地与外部设备进行全双工 数据交换,满足外部设备对工业标准 NRZ 异步串行数据格式的要求. UART即通用异步收发器,它是在USART基 ...

  3. .NET 串口通信

    这段时间做了一个和硬件设备通信的小项目,涉及到扫描头.输送线.称重机.贴标机等硬件.和各设备之间通信使用的是串口或网络(Socket)的方式.扫描头和贴标机使用的网络通信,输送线和称重机使用的是串口通 ...

  4. BluetoothChat用于蓝牙串口通信的修改方法

    本人最近在研究嵌入式的串口通信,任务是要写一个手机端的遥控器用来遥控双轮平衡小车.界面只用了一个小时就写好了,重要的问题是如何与板子所带的SPP-CA蓝牙模块进行通信. SPP-CA模块自带代码,在这 ...

  5. Win10 IoT C#开发 4 - UART 串口通信

    Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,既可以开发设备UI与用户交互式操作,又可以控制GPIO等接口,使得原来嵌入式繁琐的开发变得简单.通过Remote Debug ...

  6. 使用Java实现简单串口通信

    最近一门课要求编写一个上位机串口通信工具,我基于Java编写了一个带有图形界面的简单串口通信工具,下面详述一下过程,供大家参考 ^_^ 一: 首先,你需要下载一个额外的支持Java串口通信操作的jar ...

  7. Java 串口通信

    在Windows系统下,用Java开发串口通信相关的程序时,需要用到几个文件. (1)win32com.dll 要放在jdk\jre\bin目录下. (2)comm.jar 和javax.comm.p ...

  8. .NET 串口通信中断接收,包含0X1A(作为EOF)

    .NET串口通信中将`0X1A`当做EOF处理,.NET接收到EOF会触发一次接收中断,此时事件形参`SerialDataReceivedEventArgs`值为枚举 `Eof`,其他为`Chars` ...

  9. STC12C5A60S2 双串口通信

    STC12C5A60S2单片机是一款功能比较强大的单片机,它拥有两个全双工串行通信接口,串口1的功能及操作与传统51单片机串行口相同:特殊的是STC12C5A60S2单片机内部有一个独立波特率发生器, ...

随机推荐

  1. 添加bash命令

    cd ~/.bash vim mya 键入 #!/bin/bash hostname -i :x 保存退出 source ~/.bash_profile 生效

  2. django 从零开始 10 缓存控制

    设置django缓存,比如一些访问频繁的页面  首页  并且不需要进行太大的变化,那么可以设置为一定是时间内请求该url视图 不经过视图计算直接返回缓存的内容 django文档选择了几种方式,但是我这 ...

  3. 回想笔记 瞎比比 域名注册 解析绑定ip 下载证书 设置证书 重定向http到https请求

    2019.7.27 回想笔记 拥有腾讯云服务器一台 阿里云注册5元域名,进行备案 完成之后 使用解析 绑定服务器ip地址 ,使用域名可以访问到web服务器而不是通过直接暴露ip地址进行访问 证书购买 ...

  4. js运算【按位非】~ (index = ~~this.userIndex)(~~ 双破折号 如果是数字返回数字,如果不是数字 返回0)

    index = ~~this.userIndex ~~ 双破折号 如果是数字返回数字,如果不是数字 返回0 这个运算符有点意思:按位非[~] 先来几个例子: ~undefined: -1 ~false ...

  5. [Alg] 文本匹配-多模匹配-AC自动机

    1. 简介 AC自动机是一种多模匹配的文本匹配算法. 如果采用naive的方法,即依次比较文本串s中是否包含模式串p1, p2,...非常耗时.考虑到这些模式串中可能具有相同子串,可以利用已经比较过的 ...

  6. Natas19 Writeup(Session登录,常见编码,暴力破解)

    Natas19: 提示,与上一题源码类似,只是PHPSESSID不连续.随便输入username和password,抓包观察PHPSESSID,发现是输入的信息,按照id-username的格式,由a ...

  7. [剑指Offer]41.和为S的两个数字 VS 和为S的连续正数序列

    [剑指Offer]41 和为S的两个数字 VS 和为S的连续正数序列 Leetcode T1 Two Sum Given an array of integers, return indices of ...

  8. matplotlib 的几种柱状图

    1.x 表示数量,y 表示名字 import matplotlib.pyplot as plt dic = {'a': 22, 'b': 10, 'c': 6, 'd': 4, 'e': 2, 'f' ...

  9. python爬取中国大学排名

    教程来自:[Python网络爬虫与信息提取].MOOC. 北京理工大学 目标:爬取最好大学网前50名大学 代码如下: import requests from bs4 import Beautiful ...

  10. Linux基础篇学习——Linux文件系统之文件存储与读取:inode,block,superblock

    Linux文件类型 代表符号 含义 - 常规文件,即file d directory,目录文件 b block device,块设备文件,支持以"block"为单位进行随机访问 c ...