赞
踩
&ecspi3 { /* 设置当前片选数量为1, 因为就只接了一个ICM20608 */ fsl,spi-num-chipselects = <1>; cs-gpio = <&gpio1 20 GPIO_ACTIVE_HIGH>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ecspi3>; status = "okay"; spidev: icm20608@0 { compatible = "glen,icm20608"; spi-max-frequency = <8000000>; reg = <0>; status = "okay"; }; };
/* * 文件名 : icm20608.c * 作者 : glen * 描述 : icm20608驱动文件 */ #include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/input.h> #include <linux/semaphore.h> #include <linux/timer.h> #include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/of_irq.h> #include <linux/irq.h> #include <linux/platform_device.h> #include <linux/miscdevice.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> #include "icm20608.h" #define ICM20608_CNT 1 #define ICM20608_NAME "icm20608" /** * struct icm20608 * @devid: 设备号 * @cdev_icm: cdev * @cls: 类 * @dev_icm: 设备 * @nd: 设备节点 * @major: 主设备号 * @private_data: 私有数据 * @cs_gpio: 片选所使用的GPIO编号 */ struct icm20608 { dev_t devid; struct cdev cdev_icm; struct class *cls; struct device *dev_icm; struct device_node *np; int major; void *private_data; int cs_gpio; }; static struct icm20608 icm20608_dev; /** * 从icm20608寄存器读取多个数据 * @param : dev icm20608设备 * @param : reg 要读取的寄存器首地址 * @param : buf 读到的数据 * @param : len 要读取的数据长度 * @return : 操作结果 */ static int icm20608_read_reg(struct icm20608 *dev, u8 reg, void *buf, int len) { int ret; unsigned char tx_data[len]; struct spi_message msg; struct spi_transfer *ptrans = NULL; struct spi_device *spi = (struct spi_device *)dev->private_data; ptrans = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); if (ptrans == NULL) { ret = -EINVAL; goto read_fail_alloc; } gpio_set_value(dev->cs_gpio, 0); /* 发送要读取的寄存器地址 */ tx_data[0] = reg | 0x80; ptrans->tx_buf = tx_data; ptrans->len = 1; spi_message_init(&msg); spi_message_add_tail(ptrans, &msg); ret = spi_sync(spi, &msg); /* 读取数据 */ tx_data[0] = 0xff; ptrans->rx_buf = buf; ptrans->len = len; spi_message_init(&msg); spi_message_add_tail(ptrans, &msg); ret = spi_sync(spi, &msg); gpio_set_value(dev->cs_gpio, 1); kfree(ptrans); read_fail_alloc: return ret; } /** * 向icm20608寄存器写入多个数据 * @param : dev icm20608设备 * @param : reg 要读取的寄存器首地址 * @param : buf 读到的数据 * @param : len 要读取的数据长度 * @return : 操作结果 */ static int icm20608_write_reg(struct icm20608 *dev, u8 reg, u8 *buf, u8 len) { int ret; unsigned char tx_data[len]; struct spi_message msg; struct spi_transfer *ptrans = NULL; struct spi_device *spi = (struct spi_device *)dev->private_data; ptrans = kzalloc(sizeof(struct spi_transfer), GFP_KERNEL); if (ptrans == NULL) { ret = -EINVAL; goto write_fail_alloc; } gpio_set_value(dev->cs_gpio, 0); /* 发送要写入的寄存器地址 */ tx_data[0] = reg & ~0x80; ptrans->tx_buf = tx_data; ptrans->len = 1; spi_message_init(&msg); spi_message_add_tail(ptrans, &msg); ret = spi_sync(spi, &msg); /* 发送要写入的数据 */ ptrans->tx_buf = buf; ptrans->len = len; spi_message_init(&msg); spi_message_add_tail(ptrans, &msg); ret = spi_sync(spi, &msg); kfree(ptrans); gpio_set_value(dev->cs_gpio, 1); write_fail_alloc: return ret; } /** * ICM20608内部寄存器初始化函数 * @param : none * @return : noreturn */ void icm20608_reg_init(void) { int ret = 0; u8 value; value = 0x80; icm20608_write_reg(&icm20608_dev, ICM20_PWR_MGMT_1, &value, 1); mdelay(50); value = 0x01; icm20608_write_reg(&icm20608_dev, ICM20_PWR_MGMT_1, &value, 1); mdelay(50); ret = icm20608_read_reg(&icm20608_dev, ICM20_WHO_AM_I, &value, 1); if (ret == 0) { printk("ICM20608 ID = %#X\r\n", value); } value = 0x00; icm20608_write_reg(&icm20608_dev, ICM20_SMPLRT_DIV, &value, 1); value = 0x18; icm20608_write_reg(&icm20608_dev, ICM20_GYRO_CONFIG, &value, 1); icm20608_write_reg(&icm20608_dev, ICM20_ACCEL_CONFIG, &value, 1); value = 0x04; icm20608_write_reg(&icm20608_dev, ICM20_CONFIG, &value, 1); icm20608_write_reg(&icm20608_dev, ICM20_ACCEL_CONFIG2, &value, 1); value = 0x00; icm20608_write_reg(&icm20608_dev, ICM20_PWR_MGMT_2, &value, 1); icm20608_write_reg(&icm20608_dev, ICM20_LP_MODE_CFG, &value, 1); icm20608_write_reg(&icm20608_dev, ICM20_FIFO_EN, &value, 1); } /** * 打开设备 * @param : inode 传递给驱动的inode * @param : filp 设备文件, file结构体有个叫做private_data的成员变量, 一般在open * 时将private_data传递给设备结构体 * @return : 0 成功; 其他 失败 */ static int icm20608_open(struct inode *inode, struct file *filp) { filp->private_data = &icm20608_dev; return 0; } /** * 从设备读取数据 * @param : filp 要打开的设备文件 * @param : buf 返回给用户空间的数据缓冲区 * @param : cnt 要读取数据长度 * @param : offt 相对于文件首地址的偏移 * @return : 读取的字节数, 为负值, 表示读取失败 */ static ssize_t icm20608_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off) { int ret; signed char data[14]; struct icm20608 *dev; struct icm20608_data icm_data; if (filp->private_data == NULL) { ret = -EINVAL; return ret; } dev = (struct icm20608 *)filp->private_data; icm20608_read_reg(dev, ICM20_ACCEL_XOUT_H, data, 14); icm_data.accel_x_adc = (signed short)((data[0] << 8) | (data[1])); icm_data.accel_y_adc = (signed short)((data[2] << 8) | (data[3])); icm_data.accel_z_adc = (signed short)((data[4] << 8) | (data[5])); icm_data.temp_adc = (signed short)((data[6] << 8) | (data[7])); icm_data.gyro_x_adc = (signed short)((data[8] << 8) | (data[9])); icm_data.gyro_y_adc = (signed short)((data[10] << 8) | (data[11])); icm_data.gyro_z_adc = (signed short)((data[12] << 8) | (data[13])); ret = copy_to_user((void *)buf, (const void *)&icm_data, 14); return ret; } /** * 关闭/释放设备 * @param : filp 要关闭的设备文件(文件描述符) * @return : 0 成功; 其他 失败 */ static int icm20608_release(struct inode *inode, struct file *filp) { return 0; } /* icm20608操作函数 */ static const struct file_operations icm20608_ops = { .owner = THIS_MODULE, .open = icm20608_open, .read = icm20608_read, .release = icm20608_release, }; /** * spi驱动的probe函数, 当驱动与设备匹配会执行此函数 * @param client: spi设备 * @param id: spi设备ID */ static int icm20608_probe(struct spi_device *spi) { int ret = 0; /* 构建设备号 */ if (icm20608_dev.major) { icm20608_dev.devid = MKDEV(icm20608_dev.major, 0); register_chrdev_region(icm20608_dev.devid, ICM20608_CNT, ICM20608_NAME); } else { alloc_chrdev_region(&icm20608_dev.devid, 0, ICM20608_CNT, ICM20608_NAME); icm20608_dev.major = MAJOR(icm20608_dev.devid); } printk("icm20608 major is: %d\n", icm20608_dev.devid); /* 注册设备 */ cdev_init(&icm20608_dev.cdev_icm, &icm20608_ops); cdev_add(&icm20608_dev.cdev_icm, icm20608_dev.devid, ICM20608_CNT); /* 创建类 */ icm20608_dev.cls = class_create(THIS_MODULE, ICM20608_NAME); if (IS_ERR(icm20608_dev.cls)) { return PTR_ERR(icm20608_dev.cls); } /* 创建设备 */ icm20608_dev.dev_icm = device_create(icm20608_dev.cls, NULL, icm20608_dev.devid, NULL, ICM20608_NAME); if (IS_ERR(icm20608_dev.dev_icm)) { return PTR_ERR(icm20608_dev.dev_icm); } /* 获取设备树节点 */ icm20608_dev.np = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000"); if (icm20608_dev.np == NULL) { printk("ecspi3 node is not find!\n"); return -EINVAL; } /* 获取设备树中的gpio属性, 得到CS片选所使用的GPIO编号 */ icm20608_dev.cs_gpio = of_get_named_gpio(icm20608_dev.np, "cs-gpio", 0); if (icm20608_dev.cs_gpio < 0) { printk("Can't get cs-gpio\r\n"); return -EINVAL; } /* 设置GPIO1_IO20为输出, 并且输出高电平 */ ret = gpio_direction_output(icm20608_dev.cs_gpio, 1); if (ret < 0) { printk("Can't set gpio!\n"); return -EIO; } /* 初始化spi_device */ spi->mode = SPI_MODE_0; spi_setup(spi); icm20608_dev.private_data = spi; /* 初始化ICM20608内部寄存器 */ icm20608_reg_init(); return 0; } /** * spi驱动的remove函数,移除spi驱动的时候此函数会执行 * @param : client spi设备 * @return : 0 成功; 负值 失败 */ static int icm20608_remove(struct spi_device *spi) { /* 删除设备 */ cdev_del(&icm20608_dev.cdev_icm); unregister_chrdev_region(icm20608_dev.devid, ICM20608_CNT); /* 注销掉类和设备 */ device_destroy(icm20608_dev.cls, icm20608_dev.devid); class_destroy(icm20608_dev.cls); return 0; } /* 传统匹配方式ID列表 */ static const struct spi_device_id icm20608_id[] = { {"glen,icm20608", 0}, {} }; /* 设备树匹配列表 */ static const struct of_device_id icm20608_of_match[] = { {.compatible = "glen,icm20608"}, { /* Sentinel */} }; /* SPI驱动结构体 */ static struct spi_driver icm20608_driver = { .probe = icm20608_probe, .remove = icm20608_remove, .driver = { .owner = THIS_MODULE, .name = "icm20608", .of_match_table = icm20608_of_match, }, .id_table = icm20608_id, }; /** * \brief 驱动模块加载函数 * \param 无 * \retval 无 */ static int __init icm20608_init(void) { return spi_register_driver(&icm20608_driver); } /** * \brief 驱动模块缷载函数 * \param 无 * \return 无 */ static void __exit icm20608_exit(void) { spi_unregister_driver(&icm20608_driver); } /* 设备注册入口, 展开后 * static initcall_t \ * __initcall_icm20608_init6 \ * __used \ * __attribute__((__section__(".initcall6.init"))) \ * = icm20608_init; */ module_init(icm20608_init); /* 设备注册出口, 展开后 * static exitcall_t \ * __exitcall_icm20608_exit \ * __exit_call \ * = icm20608_exit; */ module_exit(icm20608_exit); /* 模块的许可证声明, 展开后 * static const char __UNIQUE_ID_license__COUNTER__[] \ * __used __attribute__((section(".modinfo"), unused, aligned(1))) \ * = "license=GPL"; */ MODULE_LICENSE("GPL"); /* 模块的作者声明, 展开后 * static const char __UNIQUE_ID_author__COUNTER__[] \ * __used __attribute__((section(".modinfo"), unused, aligned(1))) \ * = "author=glen_cao" */ MODULE_AUTHOR("glen");
/* * 文件名 : icm20608_test.c * 作者 : glen * 描述 : icm20608测试程序 */ #include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "sys/ioctl.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" #include "poll.h" #include "sys/select.h" #include "sys/time.h" #include "linux/ioctl.h" #include "signal.h" #include "icm20608.h" /** * @brief : main函数 * @par : argc argv数组元素的个数 * argv 参数数组 * @retval : 0 成功 其它 失败 */ int main(int argc, char *argv[]) { int fd = 0; /* 文件描述符 */ char *filename; struct icm20608_data icm_data; int ret = 0; if (argc != 2) { printf("Error Usage!\r\n"); return -1; } filename = argv[1]; /* 打开驱动文件 */ fd = open(filename, O_RDWR); /* 阻塞访问 */ if (fd < 0) { printf("Can't open file %s\r\n", filename); return -1; } while(1) { ret = read(fd, &icm_data, 14); if (ret == 0) { icm_data.accel_x_act = (float)(icm_data.accel_x_adc) / 2048; icm_data.accel_y_act = (float)(icm_data.accel_y_adc) / 2048; icm_data.accel_z_act = (float)(icm_data.accel_z_adc) / 2048; icm_data.gyro_x_act = (float)(icm_data.gyro_x_adc) / 16.4; icm_data.gyro_y_act = (float)(icm_data.gyro_y_adc) / 16.4; icm_data.gyro_z_act = (float)(icm_data.gyro_z_adc) / 16.4; icm_data.temp_act = ((float)(icm_data.temp_adc) - 25) / 326.8 + 25; printf("\n原始值:\n"); printf("gx = %d, gy = %d, gz = %d\n", icm_data.gyro_x_adc, icm_data.gyro_y_adc, icm_data.gyro_z_adc); printf("ax = %d, ay = %d, az = %d\n", icm_data.accel_x_adc, icm_data.accel_y_adc, icm_data.accel_z_adc); printf("temp = %d\n", icm_data.temp_adc); printf("\n实际值:\n"); printf("act gx = %.2f°/S, act gy = %.2f°/S, act gz = %.2f°/S\n", icm_data.gyro_x_act, icm_data.gyro_y_act, icm_data.gyro_z_act); printf("act ax = %.2fg, act ay = %.2fg, act az = %.2fg\n", icm_data.accel_x_adc, icm_data.accel_y_adc, icm_data.accel_z_adc); printf("temp = %.2f°C\n", icm_data.temp_act); } usleep(200000); } /* 关闭文件 */ ret = close(fd); if (ret < 0) { printf("file %s close failed!\r\n", argv[1]); return -1; } return 0; }
拷贝到NFS文件系统目录下
glen@ubuntu:~/linux/imx6ull/linux/driver/20_spi$ sudo cp icm20608.ko icm20608 ~/linux/nfs/rootfs/lib/modules/4.1.15 -f
/lib/modules/4.1.15 # insmod icm20608.ko
icm20608 major is: 261095424
ICM20608 ID = 0xae
4.2. 运行测试程序
/lib/modules/4.1.15 # ./random: nonblocking pool is initialized /lib/modules/4.1.15 # ./icm20608_test /dev/icm20608 原始值: gx = 37, gy = -25, gz = 10 ax = 4, ay = 7, az = -4 temp = -89 实际值: act gx = 2.26°/S, act gy = -1.52°/S, act gz = 0.61°/S act ax = -nang, act ay = -1.52g, act az = 0.61g temp = 24.65°C 原始值: gx = 34, gy = -27, gz = 47 ax = 21, ay = -46, az = -11 temp = -87 实际值: act gx = 2.07°/S, act gy = -1.65°/S, act gz = 2.87°/S act ax = -nang, act ay = -1.65g, act az = 2.87g temp = 24.66°C 原始值: gx = 37, gy = -25, gz = 9 ax = 3, ay = 2, az = -4 temp = -90 实际值: act gx = 2.26°/S, act gy = -1.52°/S, act gz = 0.55°/S act ax = -nang, act ay = -1.52g, act az = 0.55g temp = 24.65°C
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。