OpenWrt读写SDI-12传感器

首先什么是SDI-12协议,SDI-12协议:SDI-12 Serial Digital Interface 即串行数字接口,是一种基于微处理器的智能化传感器系统。 SDI-12通讯标准是由美国水文组织提出的的一种串行数据通讯接口协议,在SDI-12协会支持下,近年来欧美国家在环境监测中加以推广使用。 此技术广泛应用在工农业多参数测控、江河湖海的水文和气象等地球环境监测、养殖和食品生产中,可以远距离传送数据。

硬件上,SDI-12是用一条数据线、一条电源线、一条底线组成,数据线采用高低电平来表示数据的变化。我这里使用OpenWrt系统,单GPIO来作为数据线。

先看一下串行数据的逻辑和电压值

状态位状态电压范围
传号1-0.5~1.0v
空号03.5~5.5v
跃变未定义1.0~3.5v

通信过程:

  • 发送中断信号让数据线变为空号状态,持续12ms。
  • 发送8.33ms的传号。
  • 发送数据
  • 数据发送完成在7.5ms内释放数据线
  • 在8.33ms后获取数据

大体通信时序就是这样的一个过程,这里理解了,再看一下数据内容。SDI-12的高低信号和电平是相反的,SDI-12中的1表示为低电平。SDI-12上的数据都是可打印字符。

SDI-12一段数据位10位,第1位为起始位(0),后7位为数据位,第9位为校验位,第10位为停止位(1),这里要注意,举个例子发送字符0,字符0的二进制表示为00110000,所以发送的电平信号为0 1100111 1 1,发送字符2,字符2的二进制表示为00110010,所以发送的电平信号为 0 1100110 1 1,这里要注意,校验位为原始数据中1的个数,奇数个则校验位电平为1。

我这里使用的openwrt,下面是读写部分代码

/*写部分的驱动,因为对时间要求较高,所以这部分用了驱动*/
 static ssize_t sdi_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	int i,j = 0;
	char databuf[50] = {0};
	char data[200] = {0};
	unsigned char value = 0;
	char vs[2] = {0};
	char datacs[200] = {0};
	int count = 100;
	int check = 0;
	unsigned char stat;
	struct sdi_dev *dev = filp->private_data;
	gpio_direction_output(sdi.sdi_gpio, 1);
	retvalue = copy_from_user(databuf, buf, cnt);
	if(retvalue < 0) {
		printk("kernel write failed!\r\n");
		return -EFAULT;
	}
	str_to_bin(databuf, data);
	gpio_set_value(dev->sdi_gpio, 1);
	mdelay(12);
	gpio_set_value(dev->sdi_gpio, 0);
	mdelay(8.33);
	for(i = 0; i < strlen(data)/8; i++)
	{
		gpio_set_value(dev->sdi_gpio, 1);
		mdelay(0.833);
		check = 0;
		for(j = 7; j > -1; j--)
		{
			if(j == 0 && check < 0){
				gpio_set_value(dev->sdi_gpio, 0);
				mdelay(0.833);
				break;
			}
			if(data[i*8+j] == '1')
			{
				check = ~check;
				gpio_set_value(dev->sdi_gpio, 0);
			}
			else if(data[i*8+j] == '0')
			{
				gpio_set_value(dev->sdi_gpio, 1);
			}
			mdelay(0.833);
		}
		gpio_set_value(dev->sdi_gpio, 0);
		mdelay(0.833);
	}
	gpio_set_value(dev->sdi_gpio, 0);
	mdelay(0.833);
	/*切换io方向*/
	gpio_direction_input(dev->sdi_gpio);
	return 0;
}
/*驱动读取数据部分*/
static ssize_t sdi_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt)
{
	int ret = 0;
	int sta = 0;
	int count = 500;
	int times = 4000;
	char end[] = "110001100101000010";
	char value_ch[2] = {0};
	char data[600] = {0};
	/*io值定义*/
	unsigned char value = 0;
	unsigned char value_last = 0;
	struct sdi_dev *dev = filp->private_data;
	//5毫秒过后等待数据
	mdelay(5);
	value = gpio_get_value(dev->sdi_gpio);
	while(times)
	{
		//记录电平变化
		value_last = value;
		value = gpio_get_value(dev->sdi_gpio);
		//判断是否为起始位
		if(value != value_last &&  value == 1){
			break;
		}
		mdelay(0.833);
		times--;
	}
	if(times == 0)
	{
		//超时未读到数据
		return -EFAULT;
	}
	//读取数据,写入数组
	while(count)
	{
		value = gpio_get_value(dev->sdi_gpio);
		sprintf(value_ch, "%d", value == 1 ? 0 : 1);
		strcat(data, value_ch);
		mdelay(0.833);
		if(end[sta] == value_ch[0])
		{
			sta++;
		}
		else
		{
			sta = 0;
		}
		if(sta == strlen(end))
		{
			break;
		}
		count--;
	}
	if(strlen(data) != 500){
		//返回数据,去掉非数据部分
		copy_to_user(buf, data, strlen(data)-strlen(end)-2);
	}
	return 0;
}

/*应用程序读写部分*/
int main(int argc, char *argv[])
{
    int fd, retvalue;
    char *filename = "/dev/sdi";
    char data[600] = {0};
    char ascstr[100] = {0};
    char handle_data[10] = {0};
    char u8[10] = {0};
    char cs[10] = {0};
    int count = 200;
    int ret = 0;
    int flag = 0;
    int again_count = 2;
    // int count1 = 0;
    int check = 0;
    int k = 0;
    if(argc != 2){
        printf("Error Usage!\r\n");
        return -1;
    }
    /* 打开 sdi 驱动 */
    fd = open(filename, O_RDWR);
    if(fd < 0){
        printf("file %s open failed!\r\n", argv[1]);
        return -1;
    }
again:
    again_count--;
    printf("\r                  \r");
    if(again_count < 0){
        printf("\rFailed to get data!\n");
        return 0;
    }
    /* 向/dev/sdi 文件写入数据 */
    retvalue = write(fd, argv[1], strlen(argv[1]));
    ret = read(fd, data, strlen(data));
    if(ret < 0){
        // printf("Read failed, sleep for one second, try again\r\n");
        sleep(2);
        goto again;
    }
    if(strlen(data) > 0)
    {
        for(int len = 0; len < floor(strlen(data)/10); len++){
            //起始位判断
            if(strncmp("0",data+len*10,1) == 0)
            {
                //停止位判断
                if(strncmp("1",data+len*10+9,1) == 0){
                    check = 0;
                    strncpy(u8, data+len*10+1, 8);
                    for(int j = 0; j < strlen(u8); j++)
                    {
                        handle_data[j] = u8[strlen(u8)-j-1];
                        if(handle_data[j] == '1'&& j != 0){
                           check = ~check;
                        }
                    }
                        if(check < 0){
                            handle_data[0] = '0';
                        }   
                        //00001101疑是结束标志
                        if((strcmp("00001101", handle_data) == 0)){
                            printf(" ");
                            continue;
                        }
                        BinToChar(ascstr,handle_data, 8);
                        printf("%s",ascstr);
                }
                else{
                    if(len < floor(strlen(data)/10) -2){
                        sleep(2);
                        goto again;
                    }
                }
            }else{
                    if(len < floor(strlen(data)/10) -2){
                        sleep(2);
                        goto again;
                    }
            }
        }
    }
    else
    {
        sleep(2);
        goto again;
    }
    printf("\r\n");
    /* 关闭文件 */
    retvalue = close(fd); 
    if(retvalue < 0){
        printf("file %s close failed!\r\n", argv[1]);
        return -1;
    }
    return 0;
 }

代码部分仅供参考,重要的是思路以及理解SDI-12协议,明白数据组成和通信时序,具体的代码需要根据自己的设备修改。

SDI-12协议链接:SDI-12协议 V14.pdf-原创力文档 (book118.com)

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值