CRC校验——以SHT30温湿度传感器为例(内附SHT30的驱动代码)

文章目录

  • 1. 基本原理
  • 2. 计算方法
  • 3. c语言代码实现
  • 4. SHT30代码
    • 4.1 drv.c
    • 4.2 test.c
    • 4.3 Makefile

1. 基本原理

 循环冗余校验码(CRC)的基本原理是:在K位信息码后再拼接R位的校验码,整个编码长度为N位,因此,这种编码又叫(N,K)码。对于一个给定的(N,K)码,可以证明存在一个最高次幂为N-K=R的多项式G(x)。根据G(x)可以生成K位信息的校验码,而G(x)叫做这个CRC码的生成多项式。

2. 计算方法

 1. 预置一个值为0xFFFF的16位寄存器,此寄存器为CRC寄存器
 2. 把第一个8位二进制数与16位的CRC寄存器相异或,异或的结果存在CRC寄存器中
 3. CRC寄存器的内容右移一位,用0填补最高位,并检测移出位是0还是1
 4. 如果移出位是0,则重复步骤3
  如果移出位是1,则与多项式进行异或
 5. 重复步骤3、4,直到右移8位,这样整个8位数据都进行了处理
 6. 重复步骤2~5,进行下一个字节的处理
 7. 最后得到的CRC寄存器的内容即为CRC校验码
CRC校验——以SHT30温湿度传感器为例(内附SHT30的驱动代码)_第1张图片

3. c语言代码实现

 以SHT30温湿度传感器为例,对CRC校验的过程进行代码实现,其中SHT30 CRC检验的相关信息如下图。从图中可以看出:该传感器采用的是CRC8校验,多项式位x8+x5+x4+1,对应的代码为1 0011 0001
 而CRC校验码的位数=多项式的位数-1
 多项式位数=最高次幂+1
 所以得到CRC多项式校验码是0x31,手册中还给到了一个示例,数据是0xBEEF时,生成的校验码是0x92,
CRC校验——以SHT30温湿度传感器为例(内附SHT30的驱动代码)_第2张图片
以下是crc8的代码实现,对于crc16,crc32或者其他多项式的校验,只需要更改代码9行和10行的初始值即可。

#include 
typedef unsigned char uint8_t;

/*
 * crc8校验函数,data为要校验的数据,len为要校验的数据的字节数
 */
uint8_t crc8(const uint8_t *data, int len)
{
     const uint8_t POLYNOMIAL = 0x31;
     uint8_t crc = 0xFF;
     int i, j;
   
	 for (i=0; i<len; ++i) 
	 {
     	crc ^= *data++;
 
     	for (j=0; j<8; ++j) 
		{
        	crc = ( crc & 0x80 )? (crc << 1) ^ POLYNOMIAL: (crc << 1);
       	}
  	 }
	
	 return crc;
}

int main(int argc, const char *argv[])
{
	unsigned char data1[2] = {0x61, 0x04};
	unsigned char data2[2] = {0xBE, 0xEF};
	printf("0x%02X\n", crc8(data1, 2));
	printf("0x%02X\n", crc8(data2, 2));

	return 0;
}
/* 输出结果
linux@linux-VirtualBox:~$ ./a.out 
0xE4
0x92
*/

4. SHT30代码

 SHT30传感器芯片手册连接

4.1 drv.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 


#define SHT30_MAJOR 255
#define SHT30_MINOR 0
#define SHT30_NAME "mpu6050_cdev"

struct sht30_dev{
	struct i2c_client *client;
	struct cdev cdev;
};

struct class  *cls = NULL;
struct device *dev = NULL;

static int sht30_open(struct inode *inode, struct file *filp)
{
	struct sht30_dev *sht30 = container_of(inode->i_cdev, struct sht30_dev, cdev);
	filp->private_data = sht30;

	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	return 0;
}

static int sht30_release(struct inode *inode, struct file *filp)
{
	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	return 0;
}

static ssize_t sht30_read(struct file *filp, char __user *userbuf, size_t size, loff_t *offset)
{
	unsigned char buf[6];
	int ret;
	struct sht30_dev *sht30 = filp->private_data;

//	printk("%s -- %d.\n", __FUNCTION__, __LINE__);

	ret = i2c_master_recv(sht30->client, buf, 6);
	if (ret < 0)
	{
		printk("i2c_master_recv failed.\n");
		return ret;
	}
	printk("0X%02X 0X%02X 0X%02X 0X%02X 0X%02X 0X%02X.\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);

	ret = copy_to_user(userbuf, buf, sizeof(buf));
	if (ret < 0)
	{
		printk("copy_to_user failed.\n");
		return ret;
	}
	
	return ret;
}

static ssize_t sht30_write(struct file *filp, const char __user *userbuf, size_t size, loff_t *offset)
{
	unsigned char buf[2];
	int ret;
	struct sht30_dev *sht30 = filp->private_data;

//	printk("%s -- %d.\n", __FUNCTION__, __LINE__);
	
	ret = copy_from_user(buf, userbuf, size);
	if (ret < 0)
	{
		printk("copy_from_user failed.\n");
		return ret;
	}

	ret = i2c_master_send(sht30->client, buf, size);
	if (ret < 0)
	{
		printk("i2c_master_send failed.\n");
		return ret;
	}

	return ret;
}

const struct file_operations sht30_ops = {
	.open = sht30_open,
	.release = sht30_release,
	.read = sht30_read,
	.write = sht30_write,
};

static int sht30_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	dev_t dev_no;
	int ret;
	struct sht30_dev *sht30;

	printk("%s -- %d.\n", __FUNCTION__, __LINE__);
	printk("%p.\n", client);

	dev_no = MKDEV(SHT30_MAJOR, SHT30_MINOR);
	ret = register_chrdev_region(dev_no, 1, SHT30_NAME);
	if ( ret )
	{
		printk("register_chrdev_region failed.\n");
		goto reg_err;
	}

	sht30 = kzalloc(sizeof(struct sht30_dev), GFP_KERNEL);	/* GFP_KERNEL = 0 */
	if ( IS_ERR(sht30) )		/* 判断指针是否有误 */
	{
		printk("kzalloc failed.\n");
		ret = -PTR_ERR(sht30);	/* 返回错误码 ,在include/uapi/asm-generic/errno-base.h中定义 */
		goto kzalloc_err;
	}
	i2c_set_clientdata(client, sht30);	 /* 把数据保存到 client->dev.driver_data */
	sht30->client = client;

	cdev_init(&sht30->cdev, &sht30_ops);

	ret = cdev_add(&sht30->cdev, dev_no, 1);
	if ( ret )
	{
		printk("cdev_add failed.\n");
		goto add_err;
	}

	cls = class_create(THIS_MODULE, "sht30_cls");
	if ( IS_ERR(cls) )
	{
		printk("class_create failed.\n");
		ret = PTR_ERR(cls);
		goto cls_err;
	}

	dev = device_create(cls, NULL, dev_no, NULL, "sht30dev%d", 0);
	if ( IS_ERR(dev) )
	{
		printk("device_create failed.\n");
		ret = PTR_ERR(dev);
		goto dev_err;
	}

	return 0;

dev_err:
	class_destroy(cls);
cls_err:
	cdev_del(&sht30->cdev);
add_err:
	kfree(sht30);
kzalloc_err:
	unregister_chrdev_region(dev_no, 1);
reg_err:
	return ret;
}

static int sht30_remove(struct i2c_client *client)
{
	dev_t dev;
	struct sht30_dev *sht30;

	printk("%s -- %d.\n", __FUNCTION__, __LINE__);
	printk("%p.\n", client);

	dev = MKDEV(SHT30_MAJOR, SHT30_MINOR);
	sht30 = i2c_get_clientdata(client);	/* client->dev.driver_data */

	device_destroy(cls, dev);
	class_destroy(cls);
	cdev_del(&sht30->cdev);
	kfree(sht30);
	unregister_chrdev_region(dev, 1);

	return 0;
}

struct i2c_device_id sht30_id[] = {
	{.name = "sht30"},
	{}
};

struct i2c_driver sht30_driver = {
	.probe  = sht30_probe,
	.remove = sht30_remove,
	.driver = {
		.name  = "my_i2cdrv",
		.owner = THIS_MODULE, 
	},
	.id_table = sht30_id,
};

static int __init i2c_init(void)
{
	return i2c_add_driver(&sht30_driver);
}

static void __exit i2c_exit(void)
{
	i2c_del_driver(&sht30_driver);
}

module_init(i2c_init);
module_exit(i2c_exit);
MODULE_LICENSE("GPL");	

4.2 test.c

#include 
#include 
#include 
#include 
#include 
#include 
#include 

const char *sht30_pathname = "/dev/sht30dev0";

unsigned char crc8(const unsigned char *data, int len)
{
     const unsigned char POLYNOMIAL = 0x31;
     unsigned char crc = 0xFF;
     int i, j;

   
	 for (i=0; i<len; ++i) 
	 {
     	crc ^= *data++;
 
     	for (j=0; j<8; ++j) 
		{
        	crc = ( crc & 0x80 )? (crc << 1) ^ POLYNOMIAL: (crc << 1);
       	}
  	 }

	 return crc;
}

int main()
{
	int fd, i = 0, flag = 0;
	unsigned char write_buf[] = {0x2C, 0x06};

	unsigned char read_buf[6] = {0};

	fd = open(sht30_pathname, O_RDWR, 0666);
	if (fd < 0)
	{
		printf("open failed\n");
		return -1;
	}

	/* init */
	
	while (1)
	{
		do
		{
			write(fd, write_buf, sizeof(write_buf));
			read(fd, read_buf, sizeof(read_buf));			
		}
		while ( !(crc8(read_buf, 2)==read_buf[2] && crc8(read_buf+3, 2)==read_buf[5]) );

		printf("temp = %.3f℃\n", ( 1.0*175*(read_buf[0]*256 + read_buf[1]) / 65535 - 45 ) );
		printf("humi = %.3f%% \n", ( 1.0*100*(read_buf[3]*256 + read_buf[4]) / 65535 ) );
		printf("----------------- count = %d\n", ++i);
		memset(read_buf,0,sizeof(read_buf));
		if(i == 36000)
		{
			i = 0;
		}

		sleep(1);
	}

	close(fd);

	return 0;
}

4.3 Makefile

KERNELDIR ?= /home/linux/ti-processor-sdk-linux-am335x-evm-05.02.00.10/board-support/linux-4.14.79/
PWD := $(shell pwd)

all:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) modules
	arm-linux-gnueabihf-gcc test.c -o app
install:
	sudo cp *.ko  app /tftpboot
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) clean
	rm app
clean:
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -C $(KERNELDIR) M=$(PWD) clean
	rm app

obj-m += drv.o

你可能感兴趣的:(其他)