设备与设备之间需要进行通信,如单片机与单片机之间,单片机与电脑之间等等,通信又有两种方式,串行通信和并行通信。
本喵来说一下二者的区别
并行通信
- -传输原理:数据各个位同时传输。
- -优点:速度快
- -缺点:占用引脚资源多
假设两个设备之间传输的是一个8位的数据,上图就是两设备数据线的连接方式,需要八根线,两个设备各占八个IO口,每一个IO口对应数据的一位。
串行通信
- -传输原理:数据按位顺序传输。
- -优点:占用引脚资源少
- -缺点:速度相对较慢
同样还是传输8位的数据,只需要一根数据线就可以实现数据的传送。另一根是共地线。
单工制式:
- 数据传输只支持数据在一个方向上传输
半双工制式:
- 允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
全双工制式:
- 允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
同步通信:带时钟同步信号传输。
- -SPI,IIC通信接口
异步通信:不带时钟同步信号。
- -UART(通用异步收发器),单总线
常用的串行通信接口:
在这里,本喵只讲解异步通信。
串行异步通信有以下特点:
全双工异步通信:
- 分数波特率发生器系统,提供精确的波特率。
-发送和接受共用的可编程波特率,最高可达4.5Mbits/s- 可编程的数据字长度(8位或者9位);
- 可配置的停止位(支持1或者2位停止位);
- 可配置的使用DMA多缓冲器通信。
- 单独的发送器和接收器使能位。
- 检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
- 多个带标志的中断源。触发中断。
- 其他:校验控制,四个错误检测标志。
在使用异步通信时,需要对参数进行设置:
这样的数据格式被叫做帧格式。
大容量STM32F10x系列芯片,包含3个USART(通用同步异步收发器)和2个UART(通用异步收发器),也就是说,作为异步通信的串口可以有5个,它们的端口复用如下图:
RXD是接收端引脚,TXD是发送端引脚。
在接线的时候,一方的RXD要与另一方的TXD相连,并且TXD要与另一方的RXD相连。
数据的发送:
- 当CPU要发送一个数据的时候,首先将数据写入发送数据寄存器(TDR)中,然后通过发送移位寄存器一位一位的发送出去,直到遇到停止位便结束发送。
数据的接收:
- 当外部有数据传送来时,CPU需要接收数据,此时数据一位一位的接收到了接收移位寄存器中,然后再传送到接收数据寄存器(RDR)中,CPU再从该寄存器中读取数据。
结构框图中的蓝色框框中的就是串口所拥有的寄存器。
这是它32位的分布情况,该寄存器中只有0到9位在使用,其他位保留,使用到的位每一位都代表着串行通信的一个状态。
这是它用到的10个位具体代表意义的情况。
在使用过程中通常使用一些库函数来查询串行通信的状态。
这是它32位的分布情况,只使用到了低8位,其余位保留,着8位可以读可以写。无论是发送数据还是接收数据都是对这8位进行操作。
这是它32位的使用情况,只有低14位在使用,其他位保留。
这是它每一位的具体控制情况。
这是它32位的使用情况,通用只使用了低14位,其他位保留。
这是它每一位的具体控制情况。
这是它32位的使用情况,只使用到了第11位,其他位保留。
这是它每一位的具体控制情况。
这是它32位的使用情况,只有低16位在使用,其他位保留。0到3位是波特率的小数部分,4到15位是波特率的整数部分。
这是它使用位的具体意义。
以上6个寄存器是我们经常使用的寄存器,当然还有其他的寄存器,在使用到的时候本喵再详细讲述。
串行接下结构框图中绿色的框框是用来设置串行通信的波特率的。
USART1使用的是fPCLK2,它一般的频率是72MHZ,而其他USART使用的是fPCLK1,一般频率是36MHZ。
波特率就是在fPCLKx的基础上除以USARTDIV(分频系数),再除以16到出来的。
USARTDIV是通过对寄存器USART_BRR设置得到的数值,可以是小数。
这是波特率的计算公式。
下面我们来接收如何通过USARTDIV得到串口USART_BRR寄存器中的值。
假设我们的串口1要设置波特率的值为115200,而PCLK2的时钟为72MHZ。
- 根据上面的公式有:
USARTDIV=72000000/(115200*16)=39.0625那么得到:
- DIV_Fractino = 16*0.0625 = 1 = 0X01;(小数部分)
DIV_Mantissa = 39 = 0X27;(整数部分)这样我们就得到了USART_BRR寄存器中的值为0X271。只要设置串口1的BRR寄存器的值为0X271就可以得到115200的波特率。
这里要注意,小数部分要乘以16再放入USART_BRR寄存器的低4位中。
在本喵的文章端口复用和重映像一文中曾详细讲解了串口的复用,这里我们直接使用。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//PA9和PA10是串口1的复用端口
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);//串口1挂载在APB2总线上
由于使用的端口复用功能,所以需要进行配置,也就是对IO口进行初始化,同样在端口复用和重映像一文中曾详细讲解了串口的复用,这里我们直接使用。
GPIO_InitTypeDef GPIO_InitStructe;
GPIO_InitStructe.GPIO_Mode=GPIO_Mode_AF_PP;//推挽复用输出
GPIO_InitStructe.GPIO_Pin=GPIO_Pin_9;//PA9引脚复用为TX
GPIO_InitStructe.GPIO_Speed=GPIO_Speed_10MHz;//速度无所谓
GPIO_Init(GPIOA,&GPIO_InitStructe);//PA9引脚复用为USART1的TX引脚
GPIO_InitStructe.GPIO_Mode=GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitStructe.GPIO_Pin=GPIO_Pin_10;//PA10引脚复用为RX
GPIO_InitStructe.GPIO_Speed=GPIO_Speed_10MHz;//速度无所谓
GPIO_Init(GPIOA,&GPIO_InitStructe);//PA10引脚复用为USART1的RX引脚
以上代码就将USART1的TXD和RXD引脚复用到了PA9和PA10上。
USART_InitStructe.USART_BaudRate=115200;//波特率设置为115200
USART_InitStructe.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//无硬件流控制
USART_InitStructe.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;//USART1的发送和接受引脚
USART_InitStructe.USART_Parity=USART_Parity_No;//无校验位
USART_InitStructe.USART_StopBits=USART_StopBits_1;//停止位是1位
USART_InitStructe.USART_WordLength=USART_WordLength_8b;//数据位位8位,无校验位
USART_Init(USART1,&USART_InitStructe);//串口进行初始化
以上代码就完成了串口的初始化。
在本喵的文章NVIC中断优先级管理一文中层讲到过中断的分组以及使用。
首先需要在主函数中进行中断的分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组使用分组2
分组完成后我们需要对USART1中断使能,并且设置优先级
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//对USART1的接收中断使能
在上面的控制寄存器 1(USART_CR1) 介绍中有详细的介绍。
然后设置中断优先级
NVIC_InitTypeDef NVIC_InitStructe;
NVIC_InitStructe.NVIC_IRQChannel=USART1_IRQn;//中断类型是USART1中断
NVIC_InitStructe.NVIC_IRQChannelCmd=ENABLE;//使能
NVIC_InitStructe.NVIC_IRQChannelPreemptionPriority=1;//抢占优先级设置为1
NVIC_InitStructe.NVIC_IRQChannelSubPriority=1;//响应优先级设置为1
NVIC_Init(&NVIC_InitStructe);//中断优先级初始化
在所有的初始化设置完成后要使能串口,也就是将串口打开
USART_Cmd(USART1,ENABLE);//使能串口,也就是打开串口
现在串口也已经打开了,我们要编写进入中断后的函数,也就是进入中断后让它干什么
void USART1_IRQHandler(void)
{
u8 ret = 0;
//判断是否是接收中断
if(USART_GetITStatus(USART1,USART_IT_RXNE))
{
ret = USART_ReceiveData(USART1);//发生接收中断后读取数据寄存器中的值
USART_SendData(USART1,ret);//将接收到的值再发送给电脑
}
}
中断处理函数的函数名的格式必须按照这个,因为在启动文件中已经有规定的,返回类型是空类型
进入中断以后,有可能是发送中断,也有可能是接收中断,所以需要用
USART_GetITStatus(USART1,USART_IT_RXNE)
这个函数判断一下中断是否是接收中断,如果是就返回1,如果返回值是0,说明就是发送中断,则不执行中断处理函数中的内容,继续等待。
接收到的值是我们在电脑上输入给单片机的值,然后再将这个这反馈给电脑。
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组使用分组2
my_USART_Init();//进行串口的一系列初始化
while(1);//等待中断的发生
}
以上8个步骤就是整个串口异步通信的寄存器库函数配置过程,这样配置完成后就可以使用了
当然还有一些库函数是上面过程中没有提到的,比如串口传输状态获取
FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG);
void USART_ClearITPendingBit(USART_TypeDef* USARTx, uint16_t USART_IT);
等等,这些函数在后面使用到的时候会详细的讲解。
将代码烧录到STM32中后,打开串口调试助手
将波特率,停止位,数据位,校验位等信息和我们程序中所写的设置成一样。
在发送窗口发送信息
在接收窗口就对接收到对应的信息
下面是整个界面的图
由于单片机并没有现象,所以这里就不展示开发板的状态了。
串口寄存器以及串行通信的配置主要就在于使用库函数的时候,要明确在什么时候使用什么库函数,它的作用是什么,至于具体操作的是哪个寄存器,在ST官方提供了对应的库函数后便对寄存器的了解没有那么重要了,只需要有个印象,万一用到的时候能够找的到。只要严格按照本喵上面所说的配置流程就会很容易配置出来,至于串行通信的具体使用,在后面的串行通信实验中会给大家说到。