异步通信还要设置波特率?_深入理解同步/异步通信
上一篇我们解释了串口通信中同步通信和异步通信的区别,详见上篇链接。其中我们分析同步/异步通信最重要的不同点就是是否同步时钟,可能就有很多小伙伴不理解,异步通信不是一字节一个字节的发送嘛?既然不同步时钟,那为什么要设置比特率?其实这是一个误导性的问题。且听我细细道来,
理论篇
- 首先我们再次强调一下,异步通信传输是以字节为单位的,但是同步通信传输是以数据块为单位的。这里我们把数据块称为帧。帧的数据量是远远大于字节的。所以同步通信对时钟同步的要求较高。
- 然后异步通信是在每个字符附加起始位和停止位(有时候还会加校验位),其目的是保证位同步,就是保证发送的单位字节传输成功。发收双方都拥有独立的时钟,任何一方都不像对方提供时钟信号, 所以异步通信里波特率相差不大即可 (比如你的波特率是115200,我的是115100应该也是可以的,但是我们要是使用别人写好的串口小助手波特率都是固定的几个)。这里接收端随时准备接受数据。
同步通信是在数据块中前部后结尾处加一个特殊的字符或者比特序列,标记一个数据块的开始和结束(一般还会加上一个校验序列如CRC),同步传输是以同步的时钟节拍来发送数据信号的,因此在一个串行的数据流中,各信号码元之间的相对位置都是固定的(即同步的)。下面实战篇通过I2C来辅助理解。
图片来自水印
这里博主部分参考了如下链接: https://blog.csdn.net/farsight2009/article/details/53639881
-
所以,到这大家可能已经理解了,异步通信其实也是有时序要求的。只是要求较低,保证位同步即可,而同步通信往往有一根专门的时钟线来协调通信。
-
所以异步通信一般也就用于低速设备,而同步传输常用于高速设备(IIC最高传输速率400Kbps)。
那,为什么说问题本身是有误导性的呢?
单片机串行通信波特率就是每发送两个数据的之间的间隔,或者是每秒钟发送的字节数,你在进行串行通信时,是两个单片机进行通信,那肯定是要进行同步的(波特率要相同),要不然通信没法建立。波特率就好比一首歌曲的节奏,有了这个节奏,串行通讯的各个数据位就会有条不紊地依次排队一个一个地传到对方,要正确收发,两个通讯的主体的节奏必须一致。(此句摘自百度回答,感觉很形象)
所以,你懂了嘛?皮!
实战篇
这里我我们对比一下同步通信的IIC和异步通信的单总线吧,这里你能体验到同样都是半双工(不能同时收发,见上篇),同步通信时控制两根线(数据线和时钟线)有严格的时序,而异步通信一根线解决一切。
PS: UART是我们平时用的比较多的,详细的见上篇实战篇噢!
- 同步通信中I2C原理简介
数据帧的第一部分包含一组同步字符(如:i2c的起始位),它是一个独特的比特组合,类似于前面提到的起始位,用于通知接收方一个帧已经到达,但它同时还能确保接收方的采样速度和比特的到达速度保持一致,使收发双方进入同步。帧的最后一部分是一个帧结束标记(如I2C的结束位)。与同步字符一样,它也是一个独特的比特串,类似于前面提到的停止位,用于表示在下一帧开始之前没有别的即将到达的数据了。
下图为一张I2C同步通信协议的时序(图片来自水印)
//下为I2C的几个基本时序函数,分数据线和时钟线,无论是启动、停止、应答、发送、接收,对时序的要求较高,自己编写一般要参照时序图编写,同学们应该练习看懂时序图的能力。个人认为较为重要。
#include "reg52.h"
#include "intrins.h"
#define DELAY_TIME 5
/** 定义I2C总线时钟线和数据线 */
sbit scl = P2^0;
sbit sda = P2^1;
/**
* @brief I2C总线中一些必要的延时
* @param[in] i - 延时时间调整.
* @return none
*/
void i2c_delay(unsigned char i)
{
do
{
_nop_();
}
while(i--);
}
/**
* @brief 产生I2C总线启动条件.
* @param[in] none
* @param[out] none
* @return none
*/
void i2c_start(void)
{
sda = 1;
scl = 1;
i2c_delay(DELAY_TIME);
sda = 0;
i2c_delay(DELAY_TIME);
scl = 0;
}
/**
* @brief 产生I2C总线停止条件
* @param[in] none
* @param[out] none.
* @return none
*/
void i2c_stop(void)
{
sda = 0;
scl = 1;
i2c_delay(DELAY_TIME);
sda = 1;
i2c_delay(DELAY_TIME);
}
/**
* @brief I2C发送一个字节的数据
* @param[in] byt - 待发送的字节
* @return none
*/
void i2c_sendbyte(unsigned char byt)
{
unsigned char i;
EA = 0;
for(i=0; i<8; i++){
scl = 0;
i2c_delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
i2c_delay(DELAY_TIME);
scl = 1;
byt <<= 1;
i2c_delay(DELAY_TIME);
}
EA = 1;
scl = 0;
}
/**
* @brief 等待应答
* @param[in] none
* @param[out] none
* @return none
*/
unsigned char i2c_waitack(void)
{
unsigned char ackbit;
scl = 1;
i2c_delay(DELAY_TIME);
ackbit = sda; //while(sda); //wait ack
scl = 0;
i2c_delay(DELAY_TIME);
return ackbit;
}
/**
* @brief I2C接收一个字节数据
* @param[in] none
* @param[out] da
* @return da - 从I2C总线上接收到得数据
*/
unsigned char i2c_receivebyte(void)
{
unsigned char da;
unsigned char i;
EA = 0;
for(i=0;i<8;i++){
scl = 1;
i2c_delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
i2c_delay(DELAY_TIME);
}
EA = 1;
//
return da;
}
/**
* @brief 发送应答
* @param[in] ackbit - 设定是否发送应答
* @return - none
*/
void i2c_sendack(unsigned char ackbit)
{
scl = 0;
sda = ackbit; //0:发送应答信号;1:发送非应答信号
i2c_delay(DELAY_TIME);
scl = 1;
i2c_delay(DELAY_TIME);
scl = 0;
sda = 1;
i2c_delay(DELAY_TIME);
}
/**
* @brief 读写操作过程中一些必要的延时
* @param[in] i - 指定延时时间
* @return - none
*/
void operate_delay(unsigned char t)
{
unsigned char i;
while(t--){
for(i=0; i<112; i++);
}
}
//这里是温度传感器DS18B20的驱动程序,注意这里只对DQ这一根总线控制哦。(要是只收发的话跟同步通信有种既当爹又当妈的感觉//手动滑稽)
//此段代码可以直接读取温度数据。
#include "reg52.h"
sbit DQ = P1^4;
//单总线延时函数
#ifndef STC12
void Delay_OneWire(unsigned int t) //STC89C52RC
{
while(t--);
}
#else
void Delay_OneWire(unsigned int t) //STC12C5260S2
{
unsigned char i;
while(t--){
for(i=0;i<12;i++);
}
}
#endif
//通过单总线向DS18B20写一个字节
void Write_DS18B20(unsigned char dat)
{
unsigned char i;
for(i=0;i<8;i++)
{
DQ = 0;
DQ = dat&0x01;
Delay_OneWire(5);
DQ = 1;
dat >>= 1;
}
Delay_OneWire(5);
}
//从DS18B20读取一个字节
unsigned char Read_DS18B20(void)
{
unsigned char i;
unsigned char dat;
for(i=0;i<8;i++)
{
DQ = 0;
dat >>= 1;
DQ = 1;
if(DQ)
{
dat |= 0x80;
}
Delay_OneWire(5);
}
return dat;
}
//DS18B20初始化
bit init_ds18b20(void)
{
bit initflag = 0;
DQ = 1;
Delay_OneWire(12);
DQ = 0;
Delay_OneWire(80); // 延时大于480us
DQ = 1;
Delay_OneWire(10); // 14
initflag = DQ; // initflag等于1初始化失败
Delay_OneWire(5);
return initflag;
}
//DS18B20温度采集程序:整数
unsigned char rd_temperature(void)
{
unsigned char low,high;
char temp;
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0x44); //启动温度转换
Delay_OneWire(200);
init_ds18b20();
Write_DS18B20(0xCC);
Write_DS18B20(0xBE); //读取寄存器
low = Read_DS18B20(); //低字节
high = Read_DS18B20(); //高字节
temp = high<<4;
temp |= (low>>4);
return temp;
}
每篇必皮。欢迎交流!共同进步。
摄自 华为P20