要模拟串口首先要清楚串口数据传输过程中的原理。
所以最简单的串口传输一个字节总共有10个电平变化,每个电平的宽度由波特率决定的。
下面看一个通过波特率如何计算每个位的电平宽度。
可以计算出传输1位所需要的时间 T1 = 1/9600 约为104us。
上图中就是一个字节的完整波形,起始位为低电平,结束位为高电平,中间8位为数据位,无校验位。数据位是低位在前,高位在后。也就是和起始位挨着的是最低位,和结束位挨着的是最高位。通过这个波形就可以分析出,发送的数据是0x00。
起始位为低电平,结束位为高电平,中间数据位有一个高电平,其余都是低电平,按照低位在前,高位在后规律,数据位就是0000 0001 刚好是16进制的0x01。
//输出1 { } void Send_0( void ) SIM_TXD = 0; //发送一个字节 { unsigned char i; //发送起始位 delay_us( 100 ); for( i = 0; i < 8; i++ ) value = ( sdata & 0x01 ); //先传低位 { } { } sdata = sdata >> 1; //停止位 delay_us( 100 ); 首先发送起始位,将IO口电平拉低,延时104us,下来发送8位数据位,低位在前,高位在后,每发送一位就延时104us。最后发送结束位,将IO口电平置高,在延时104us。这样一个字节就发送结束了。
下面再看接收程序
void ReadByte( void ) unsigned char i, value = 0; { delay_us( 100 ); for( i = 0; i < 8; i++ ) value >>= 1; { } } //delay_us(100); //一次性接收大量数据时,防止程序执行代码时浪费时间,在结束符时可以不用等待 RecBuf = value; } 接收数据就更好理解了,读取IO口的电平,如果出现低电平就开始接收数据,然后读取8个数据位的电平,在等待结束位结束。这样一个字节的数据就接收完成了。
有两种方式去实现,一种是在死循环中用查询方式去判断,一直读取IO的的电平,如果出现低电平就认为串口有数据发送进来了。
//查询方式接收数据 { unsigned char recstr[100] = {0}; while( 1 ) if( !RecFlag ) //未收到数据时扫描 ReadByte(); //扫描数据 } { RecFlag = 0; RecBuf = 0; } { WriteString( recstr ); //发送接收到的数据 i = 0; //清除接收数据数组下标 { } } { return; } 这种方式实现起来比较简单,但是对于程序编写比较麻烦,因为要一直监视者IO口,所以程序干其他事情时,很有可能错过数据的接收。可以用第二种方式,IO口中断来判断什么时候要开始接收数据,将IO口设置为下降沿中断,当有下降沿出现时,说明串口有数据进来了,然后再去读取串口数据。没有中断发生时,程序就可以干其他事情了。
//通道PC3口的下降沿中断检测数据 #pragma vector = 7 // IAR中的中断号,要在STVD中的中断号上加2 { ReadByte(); { { recCNT = 0; } { { RecBuf = 0; else if( RecBuf == 0x0d ) //收到0x0d 标记结束符开始 recEnd |= 0x01; } }
当出现下降沿之后进入中断程序,这时候要关闭外部中断,开始读取IO口电平状态。若不关闭中断,在读取IO电平的过程中中断还会不停的进入,这样就会影响读取数据的准确性。所以进入中断会首先要关闭中断,接收完一个字节之后,在打开中断,接收下一个字节。直到收到了回车换行符(也就是0x0D 0x0A),就认为数据发送已经结束。就退出接收过程,然后主程序就可以去处理接收到的数据了。
|