&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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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