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

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校验码
在这里插入图片描述

3. c语言代码实现

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

#include <stdio.h>
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 <linux/module.h>
#include <linux/i2c.h>
#include <linux/mod_devicetable.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/cdev.h>
#include <linux/delay.h>
#include <linux/device.h>

#include <uapi/asm-generic/errno-base.h>


#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 <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>

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
  • 5
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
sht30的基于c51单片机驱动程序:#include #include #include "I2C.h" #include "SHT30.h" #define uint unsigned int #define uchar unsigned char void display(); unsigned char code tableduan[]= { 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71 }; uchar data DIS_ROME[6]= {0,0,0,0,0,0}; //显示缓存区(4) uchar DISP=0;//缓存区指针 uchar SCANF=0xDF;//扫描指针 sbit LED1=P1^0; sbit LED2=P1^1; sbit LED3=P1^2; sbit LED4=P1^3; sbit VOC_A=P3^5; sbit VOC_B=P3^6; sbit dula=P2^6; //IO口定义 sbit wela=P2^7; sbit key=P3^4; sbit beep_dr=P2^3; uint pm1 = 0; uint pm2 = 0; uint pm10 = 0; uchar vr=0; uint intrcnt=0; bit F_1HZ; uint voice_time_cnt; uchar Uart_Buf; uchar Rec_Addr=0; uchar mode=0; uchar Rec_Uart=0; uchar Recive_Buf[30]= {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; #define key P34 #define const_key_time1 50 unsigned char ucKeySec=0; //被触发的按键编号 unsigned int uiKeyTimeCnt1=0; //按键去抖动延时计数器 unsigned char ucKeyLock1=0; //按键触发后自锁的变量标志 unsigned char displaycnt=0; void keyscan() { if(key==1)//IO是高电平,说明按键没有被按下,这时要及时清零一些标志位 { ucKeyLock1=0; //按键自锁标志清零 uiKeyTimeCnt1=0;//按键去抖动延时计数器清零,此行非常巧妙,是我实战中摸索出来的。 } else if(ucKeyLock1==0)//有按键按下,且是第一次被按下 { uiKeyTimeCnt1++; //累加定时中断次数 if(uiKeyTimeCnt1>const_key_time1) { uiKeyTimeCnt1=0; ucKeyLock1=1; //自锁按键置位,避免一直触发 ucKeySec=1; //触发1号键 } } } void keyservice() { if(ucKeySec) { displaycnt=!displaycnt; } ucKeySec=0; } void UartInit(void) //9600bps@12.000MHz { TMOD=0x01; //设置定时器0为工作方式1 TH0=0xf8; //重装初始值(65535-500)=65035=0xfe0b TL0=0x2f; SCON=0x50; TMOD=0X21; IP =0x10; //把串口中断设置为最高优先级, EA=1; ES=1; ET0=1; TR0=1; } void T0_time(void) interrupt 1 //定时中断 { TF0=0; //清除中断标志 TR0=0; //关中断 keyscan(); keyservice(); display();

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值