串口通信的目的:将一个设备的数据传送到另一个设备,扩展硬件系统
简单来说,STM32的内部功能有限,如果需要使用其他功能,就需要搭建STM32的外部功能模块,因为是在外部,所以需要搭建通讯线,当然有通信线也就涉及到通信协议等一系列知识
通信协议:制定通信的规则,通信双方按照通信协议规则进行数据收发
通信的目的是进行信息传递,双方约定的规则就是通信协议
STM32典型的通信协议
双工:
(全双工)一般是两根通信线,一根发送,一根接收,可以同时进行,互不干涉
(半双工)一根通信线,双向,也可以发送和接收,发送和接收不能同时进行
(单攻)一根通信线,单向,发送方向和接收方向固定
全双工:打电话 半双工:对讲机 单工:广播
时钟:
(同步)有时钟线,双方约定好时钟信号,接收方根据时钟信号进行采样
(异步)没有时钟线,双方约定好采样频率,接受方根据频率进行采样,并且还加帧头帧尾等,进行采样位置的对齐
电平:
(单端)他们的引脚高低电平都是对GND的电压差,单端通信双方都需要共地,单端除了通信线,还需要接GND
(差分)靠两个差分引脚的电压差来传输信号,不用GND(但是USB协议里面有些地方需要单端信号,所以USB需要共地)用差分信号可以极大提高抗干扰特性
设备
(点对点)只有两个设备,直接传输数据就行
(多设备)需要一个寻址过程,以确定通信对象
USART串口通信
串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信(点对点)
单片机的串口可以使单片机与单片机、单片机与电脑、单片机与各式各样的模块互相通信,极大地扩展了单片机的应用范围,增强了单片机系统的硬件实力
硬件电路
USART串口是全双工通信,有TX发送端和RX接收端,TX和RX是单端信号,他们的高低电平都是相对于共地的,所以要接GND,VCC是供电,如果两个设备都有独立供电,VCC就不用接,如果其中一个设备没有独立供电,有供电的设备就需要向没有独立供电的设备供电
当两个设备电平标准不一致时,需要用电平转换芯片转换电平
对于电平标准,有以下
单片机常用电平标准是TTL电平,当然还有其他电平标准,当两个设备电平标准不一致,就需要用电平转换芯片进行电平转换
串口时序图
这是串口发送一个字节的时序图,每个字节都装在一个数据帧里,每一个数据帧里包含起始位,数据位(8位),停止位,数据位为8位,一个字节8位,每位占一个数据位,一个字节刚好占完,但是一帧共10位,包含了起始位和停止位,如果有校验位,占一位,那么一帧就变成11位。
(左边为无校验,1帧10位) (右边为有校验,1帧11位)
串口参数
波特率:串口通信的速率
USART串口是异步通信,双方需要约定通信速率,假如一个设备每隔一秒发送一位,另一个设备也得每隔一秒接收一位,如果接收快了,就会重复接收某些位,如果接收慢了,就会漏掉某些位,所以双方需要统一速率(波特率),避免接收数据问题
波特率:每秒传输码源个数,单位码元/s
比特率:每秒传输的比特数,单位bit/s,或者叫bps
在二进制调制下,一个码元就是一个bit,这个时候,波特率就等于比特率,单片机一般都是二进制调制
起始位:标志一个数据帧的开始,固定为低电平
图上空闲状态是高电平,当要传输信号时,会产生一个下降沿,打破这个电平状态,用来告诉设备这一帧信号要开始了,这就是起始位
停止位:用于数据帧间隔,固定为高电平
一个字节数据发送完成后,必须有一个停止位,用于数据帧间隔,固定为高电平,也为下一次开始传数据的起始位做准备,要不然下一个字节传过来没有下降沿信号来充当起始位,设备也不知道有数据传过来了
数据位:数据帧的有效载荷,1为高电平,0为低电平,低位先行
假如发送一个字节为0x0F,把0F转换为二进制 0000 1111,低位先行,那么就是这样
校验位:用于数据验证,根据数据位计算得来
串口使用的是奇偶校验位,检查数据是否出错了,如果出错了可以选择丢弃或者重传,校验有3种,寄校验,偶校验,无校验
无校验就是左边的图,有起始位,数据位,停止位三个部分
有校验的话,数据位最后多一位校验位,总共四个部分
寄校验:如果数据位(除校验位)里是偶数个1,校验位就补一个1,让9位数据位呈现出奇数个1
如果数据位(除校验位)里是寄数个1,已经是奇数个1了,那么校验位就补0,让9位数据位保持为奇数个1
假如在传输过程中,由于干扰,有一位由1变为0或者由0变为1,接收方一检验奇偶特性不对,那就认为传输出错
偶校验同理
当然如果有两位出错,而恰好奇偶特性又没影响改变,那么就检验不出来,如果要更精确的校验,可以用CRC校验
USART简介
USART(Universal Synchronous/Asynchronous Receiver/Transmitter)通用同步/异步收发器(s代表同步)USART是同步 UART是异步
USART是STM32内部集成的硬件外设,可根据数据寄存器的一个字节数据自动生成数据帧时序,从TX引脚发送出去,也可自动接收RX引脚的数据帧时序,拼接为一个字节数据,存放在数据寄存器里
自带波特率发生器,最高达4.5Mbits/s 可配置数据位长度(8/9)、停止位长度(0.5/1/1.5/2) 可选校验位(无校验/奇校验/偶校验)
支持同步模式、硬件流控制、DMA、智能卡、IrDA、LIN
(同步模式就是多了个时钟CLK的输出)
(硬件流控制:假设一个设备A向另一个设备B发送数据,A设备发送的太快了,B设备如果接收不过来的情况下,B设备就只能放弃新数据或者覆盖原数据,对于这种情况,解决办法可以是------------------------------>设备B向设备A接一根信号线,设备B如果准备好了接收数据,这根线就置高电平,设备A就传数据,如果设备B没准备好,这根线就置低电平,A就等待,总的来说,设备A按照这根线反馈的信号来选择性地在TX引脚输出数据,可以有效防止设备B因处理慢而导致数据丢失的问题)
(DMA:这个串口支持用DMA转运数据,减小CPU的负担)
STM32F103C8T6 USART资源: USART1(APB2)、 USART2(APB1)、 USART3(APB1)
USART结构框图
这个结构图简化后得到----------->(写程序按照这个结构图配置参数)
时钟来源PLCK2/1,经过波特率发生器,用于产生双方约定好的速率,发送控制器和接收控制器用来控制发送移位和接收移位,发送数据寄存器把数据传给移位寄存器,置TXE标志位,移位寄存器把数据一位一位移出去(数据右移操作),通过GPIO复用输出,输出到TX引脚,产生协议规定的波形,发送出去。
下面,接收移位寄存器(也是右移操作),通过GPIO输入,一位一位移入寄存器,一帧数据完成之后,统一转运到接收数据寄存器,并置RXNE标志位,检查这个标志位就知道有没有收到数据,当然也可以申请中断,执行中断函数,快速读取和保存数据
最后老样子,有一个开关控制,Cmd开启一下外设
波特率 = fPCLK2/1 / (16 * DIV)
最后,熟悉的步骤,过一下初始化代码
第一步:开启时钟,把需要用的USART和GPIO的时钟打开
第二步:GPIO 初始化,把TX配置成复用输出,RX配置成输入
第三步:配置USART,使用结构体配置参数
第四步:如果只需要发送的功能,直接Cmd,开启USART,初始化结束
如果还需要接收的功能,就在Cmd开启之前,加上ITConfig和NVIC的代码就行了,大致是这样
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;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
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);
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_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);
}