本帖最后由 会笑的星星 于 2019-12-30 23:11 编辑
我们之前讲了采用计数法来检测方波,虽然这种方法比较简单,也能试用一定的场合,但是无法解决精确识别方波的问题。有时候,我们需要比较精确的检测方波的每一个周期,因为每一个周期代表的是不同的信息,比如一种常用的无线编码格式 --- 1527编码,周期为16T(T的长度一般在几百微秒以上)的方波在占空比为4:16表示0,占空比为12:16时表示1,如下图所示。
事实上,在实际应用中有不少无线通信协议采用的就是类似1527的编码方式。这种方式编码简单,容易检测,同样能满足一些应用需求。下面我们就来解决如何检测类似1527编码的方波问题,这种方法也被为称之为查询法,它只需要一个硬件定时器外加一个IO口读取信号电平就可以完成检测,电路接口如下图所示。
在检测信号之前,我们先制定一下协议。把0表示为高电平:低电平为1T:3T。把1表示为高电平:低电平为3T:1T,其中1T我定为300us,如下图所示。
有了0、1的表示方法,我们就知道将一个或者多个字节的数据表示成方波的方法,接收者按照这个方法就能知道发送者发了什么数据。由于发送者需要与接收者处于同步状态,以便接收者知道发送者要开始发送数据了,因此还需要在发送数据之前增加一个被称为同步头的方波(事实上,UART、IIC等协议都有类似的同步头)。同步头方波必须与表示0、1的方波明显的区分开来,我把高电平:低电平定为1T:9T,如下图所示。
有了同步头,数据,还需要一个结束码,这是为了让最后一位数据对应的方波信号能被正确的识别出来(IIC、UART等协议都有类似的结束机制)。结束码采用高电平:低电平=1T:1T,如下图所示。
最后,做一个约定,数据传输时高位在前,低位在后,对于接收者而言先接收到位的放在字节高位。这样同步头、数据位、结束码组成的完整数据帧就有了,如下是发送数据的例子。
有了协议上的准备,我们就可以检测方波信号了。
先描述一下我们要解决的问题。检测一个由上述编码方式编码的4个字节数据,其中前3个字节为信息字节,第四个字节为前3个字节的校验和。电路接口如下图所示。
我们先分析一下这个问题。我们知道1T的时间是300us,一位(也就是一个方波周期)的时间总长度就是4*300=1.2ms。由于1T的时间较长,所以我们可以考虑在一个定时间隔小于1T的硬件定时器中读取信号脚的电平状态,从而确认方波周期表示0还是表示 1。这里要注意一下,过小的定时间隔会导致程序总被无故中断,显然也是不可取的,因此需要选取一个合适的定时间隔是必要的,这里取100us(小于1T的1/3),这样确保能比较精确的查询方波信号的同时也不会过多的中断程序的运行。这样我们只需要使用一个硬件定时器资源加一个外部IO口就能解决这个问题。如下图所示。
有了上述的分析,我们就可以编写程序了。
//字节数量
#define FRAME_BYTE_LENGTH 3
//字节数量对应的位数量
#define FRAME_BIT_LENGTH (FRAME_BYTE_LENGTH*8)
//信号脚
#define SIGNAL_GPIO GPIO
struct
{
unsigned char done_flag;
unsigned char fifo[FRAME_BYTE_LENGTH];
}rev_data;
void app_data_detect(void )
{
static INT8U prev_read = 0,read_period = 0,\
read_low_cnt = 0,read_high_cnt = 0,read_bit_cnt = 0;
static INT8U read_buff[FRAME_BYTE_LENGTH];
unsigned char i;
if (SIGNAL_GPIO == 1)
{
if (prev_read == 0)
{
//read_period表示代表一个数据位的方波持续多少个100us。在这里一个方波的
//长度是4*300 = 1200us,read_period = 1200 / 100 = 12
//考虑误差因素,取read_period ± 4做为有效方波
if ((read_period >= 8) && (read_period <= 16))
{
//如果对方波高电平计时大于对低电平的计时,这个方波表示的数据位为1,并把
//数据为保存到缓存中
if (read_high_cnt > read_low_cnt)
{
read_buff[read_bit_cnt / 8] |= (0x01 << (0x07 - (read_bit_cnt % 8)));
}
read_bit_cnt++;
if (read_bit_cnt >= FRAME_BIT_LENGTH)
{
//数据接收完成
rev_data.done_flag = 1;
read_bit_cnt = 0;
for (i = 0; i < FRAME_BYTE_LENGTH; i++)
{
rev_data.fifo[i] = read_buff[i]
read_buff[i] = 0x00;
}
}
}
else
{
read_bit_cnt = 0;
for (i = 0; i < FRAME_BYTE_LENGTH; i++)
{
read_buff[i] = 0x00;
}
}
read_high_cnt = 0;
read_low_cnt = 0;
read_period = 0;
}
read_high_cnt++;
read_period++;
prev_read = 1;
}
else
{
read_period++;
read_low_cnt++;
prev_read = 0;
}
}
//硬件定时器,定时间隔为100us。
void timer_interrupt()
{
app_data_detect()
}
main()
{
while(1)
{
if(rev_data.done_flag)
{
unsigned char sum;
sum = rev_data[0] + rev_data[1] + rev_data[2];
if(sum == rev_data[3])
{
//如果校验成功,处理数据
}
rev_data.done_flag = 0;
}
}
}
使用查询方法检测方波信号我们就说完了,虽然这种方法在能比较准确的识别方波信号,但是如果方波信号的周期很短(比如小于10us),我们就不能使用这个方法。因此,这种方法也有一定的局限性。对于如何检测频率更高的方波,我们下一篇**在讨论。希望本文所讲的方波检测方法能对你有启发。
|