循环冗余校验码(CRC)的基本原理是:在K位信息码后再拼接R位的校验码,整个编码长度为N位,因此,这种编码又叫(N,K)码。对于一个给定的(N,K)码,可以证明存在一个最高次幂为N-K=R的多项式G(x)。根据G(x)可以生成K位信息的校验码,而G(x)叫做这个CRC码的生成多项式。
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校验码
以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
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
*/
SHT30传感器芯片手册连接
#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");
#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;
}
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