用I/O口模拟总线时序
在做总线通信过程中,我们很少会用到这样方法,一般在我们选择MCU的时候都会带有你所需要的通信接口。但是,对于一些简单的通信应该用的场合,一般在一些传感器的数据通信过程中,传感器厂商会将通信协议做一些改变,这些通信协议也没有一个标准的协议规定。以至于传感器的兼容性很差,甚至有时候找不到能够与其通信的MCU,这个时候有一种方法就是用I/O口来模拟通信总线(由于I/O速度的限制一般只适用于低速的通信总线)的时序。之前,用I2C通信做一个温湿度测量的工程,本篇文章就以一个例子来看看如何用I/O口对总线时序进行模拟。
我们平时计算机常用的RS232/485工作在异步工作状态时是有严格的数据时钟限制,也就是我们所说的波特率,通信的两个设备有相同的波特率才能正确的通信。对于同步通信一般没有严格的时间限制,总线通过高低电平来分辨数据是“0”还是“1”,有两个关键的时刻:上升沿,下降沿。它是用过上升沿和下降沿的时刻来读写数据的,也就是说这样的话通信频率不是固定的,因为通信的设备“数”的是上升沿和下降沿的数目,然后读写数据线上的数据。笔者做过实验,将I2C通信的频率降到了10Hz左右,这样用示波器能够很好的捕捉到每一个时钟,通信的结果也是正确的。
好了,直接来看案例吧。
通常我们的I2C的通信时序应该如下图所示,在时钟线拉高的情况下,将数据线拉低就会产生一个启动信号。但是传感器SHT的启动信号却是一个数据线拉低后,时钟线产生一个脉冲,而后再将数据线拉高,这样做的好处是在一定程度上确保了总线正确的启动,但是几乎与之匹配的MCU。这个时候就需要通过I/O模拟的方式来与SHT11完成通信。
1 #define IIC_SCL RC0 //I2C时钟线 2 #define IIC_SDA RC1 //I2C数据线 3 #define IIC_SCL_DIR TRISC0 //I2C时钟线传输方向 4 #define IIC_SDA_DIR TRISC1 //I2C数据线传输方向 5 #define PORT_INPUT 1 6 #define PORT_OUTPUT 0 7 8 #define IIC_SCL_HIGH() IIC_SCL_DIR = PORT_INPUT //时钟线拉高 9 #define IIC_SCL_LOW() IIC_SCL_DIR = PORT_OUTPUT;IIC_SCL=0//时钟线拉低 10 #define IIC_SDA_HIGH() IIC_SDA_DIR = PORT_INPUT //数据线拉高 11 #define IIC_SDA_LOW() IIC_SDA_DIR = PORT_OUTPUT;IIC_SDA=0//数据线拉低 12 13 14 /*************** 15 *SHT11启动时序 16 ***************/ 17 void SHT_START(void) 18 { 19 IIC_SCL_HIGH(); 20 IIC_SDA_HIGH(); 21 delay_us(5); 22 IIC_SDA_LOW(); 23 delay_us(5); 24 IIC_SCL_LOW(); 25 delay_us(5); 26 IIC_SCL_HIGH(); 27 delay_us(5); 28 IIC_SDA_HIGH(); 29 delay_us(5); 30 IIC_SCL_LOW(); 31 } 32 /*************** 33 *SHT11发送数据时序 34 ***************/ 35 void SHT_SEND(uchar data) 36 {uchar i,data1; 37 for(i=0;i<8;i++) 38 { 39 data1=data<<i; 40 if(!(data1&0x80)) 41 IIC_SDA_LOW(); 42 if(data1&0x80) 43 IIC_SDA_HIGH(); 44 IIC_SCL_LOW(); //写完1位数据将时钟线拉低,等待发送 45 delay_us(5); 46 IIC_SCL_HIGH(); //时钟线上升沿,发送1位数据 47 delay_us(5); //等待1位数据发送完成 48 } 49 IIC_SCL_LOW(); 50 IIC_SDA_HIGH(); //8位数据发送完成,数据线拉高,等待SLAVE器件响应 51 delay_us(5); 52 IIC_SCL_HIGH(); //时钟线拉高,产生上升沿读取数据线是否SLAVE器件有响应 53 //while(IIC_SDA==1); 54 delay_us(5); 55 IIC_SCL_LOW(); 56 IIC_SDA_HIGH(); //数据线拉高,时钟线拉低,等待转换完成 57 } 58 /*************** 59 *SHT11接收数据时序 60 ***************/ 61 uint SHT_REC(void) 62 { 63 uint i; 64 uint REC1=0,REC0=0,REC=0; 65 for(i=0;i<8;i++) 66 { 67 IIC_SCL_HIGH(); //转换完成,SLAVE器件将数据线拉低,时钟线产生上升沿读取高8位数据 68 REC1=(REC1<<1)+IIC_SDA; 69 delay_us(5); 70 IIC_SCL_LOW(); //将时钟线拉低,等待下一个上升沿的到来 71 delay_us(5); 72 } 73 SHT_ASK(); //高8位数据接收完毕,发送应答信号 74 for(i=0;i<8;i++) 75 { 76 IIC_SCL_HIGH(); //转换完成,SLAVE器件将数据线拉低,时钟线产生上升沿读取低8位数据 77 REC0=(REC0<<1)+IIC_SDA; 78 delay_us(5); 79 IIC_SCL_LOW(); 80 delay_us(5); 81 } 82 SHT_STOP(); //低8位数据接收完毕,结束 83 REC=(REC1<<8)+REC0; 84 return REC; 85 } 86 /*************** 87 *SHT11应答时序 88 ***************/ 89 void SHT_ASK(void) 90 { 91 IIC_SCL_LOW(); 92 IIC_SDA_LOW(); //数据线拉低 93 delay_us(5); 94 IIC_SCL_HIGH(); //时钟线拉高才生应答信号 95 delay_us(5); 96 IIC_SDA_HIGH(); 97 IIC_SCL_LOW(); 98 delay_us(5); 99 } 100 /*************** 101 *SHT11停止时序 102 ***************/ 103 void SHT_STOP(void) 104 { 105 IIC_SDA_HIGH(); 106 IIC_SCL_LOW(); 107 delay_us(5); 108 IIC_SCL_HIGH(); 109 delay_us(5); 110 IIC_SDA_HIGH(); 111 IIC_SCL_LOW(); 112 }
原谅我这一生不羁放纵爱自由,也怕有天我会跌倒!