开发版芯片 imx6ull
驱动测量了系统调用read 10000次的时间验证了开启硬件浮点和不开启硬件浮点的运算浮点数时间基本相同
1.驱动代码
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/of.h>
#include <linux/spi/spidev.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "icm.h"
struct icm_dev_t
{
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *device;
struct device_node *node;
int major;
void *private_data;
int cs_gpio;
unsigned long tm[2];
};
static struct icm_dev_t icm_dev;
static const struct regmap_config icm_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
static const struct of_device_id icm_of_table[] = {
{
.compatible = "icm-myz",
},
{},
};
struct regmap *regmap;
static void icm_init(void)
{
int val = 0;
regmap_write(regmap, ICM20_PWR_MGMT_1, 0x80);
msleep(100);
regmap_write(regmap, ICM20_PWR_MGMT_1, 0x1);
usleep_range(5000, 10000);
regmap_read(regmap, 0x75, &val);
printk("read icm id is %#x\n", val);
regmap_write(regmap, ICM20_SMPLRT_DIV, 0x00); /* 输出速率是内部采样率 */
regmap_write(regmap, ICM20_GYRO_CONFIG, 0x18); /* 陀螺仪±2000dps量程 */
regmap_write(regmap, ICM20_ACCEL_CONFIG, 0x18); /* 加速度计±16G量程 */
regmap_write(regmap, ICM20_CONFIG, 0x04); /* 陀螺仪低通滤波BW=20Hz */
regmap_write(regmap, ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz */
regmap_write(regmap, ICM20_PWR_MGMT_2, 0x00); /* 打开加速度计和陀螺仪所有轴 */
regmap_write(regmap, ICM20_LP_MODE_CFG, 0x00); /* 关闭低功耗 */
regmap_write(regmap, ICM20_FIFO_EN, 0x00);
}
ssize_t icm_read(struct file *file, char __user *buf, size_t size, loff_t *offs)
{
u8 data8[14];
u16 data[7] = {0};
int i = 0;
regmap_bulk_read(regmap, ICM20_ACCEL_XOUT_H, (u8 *)&data8, 14);
for (i = 0; i < 7; i++)
{
data[i] = ((u16)data8[i * 2] << 8) | data8[i * 2 + 1];
}
return sizeof(data) - copy_to_user(buf, data, sizeof(data));
}
ssize_t icm_write(struct file *file, const char __user *buf, size_t size, loff_t *offs)
{
return 0;
}
int icm_open(struct inode *node, struct file *file)
{
file->private_data = (void *)&icm_dev;
icm_dev.tm[0] = jiffies;
dev_info(icm_dev.device, "%s\n", __func__);
return 0;
}
int icm_release(struct inode *node, struct file *file)
{
struct icm_dev_t *dev = (struct icm_dev_t *)file->private_data;
dev->tm[1] = jiffies;
dev_info(icm_dev.device, "%s time diff is [%lu]\n", __func__, dev->tm[1] - dev->tm[0]);
return 0;
}
static const struct file_operations icm_ops = {
.open = icm_open,
.read = icm_read,
.write = icm_write,
.release = icm_release,
};
static int icm_probe(struct spi_device *spi)
{
int ret = 0;
regmap = devm_regmap_init_spi(spi, &icm_regmap_config);
if (IS_ERR(regmap))
{
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
ret = alloc_chrdev_region(&icm_dev.devid, 0, ICM_CNT, ICM_NAME);
if (ret < 0)
{
dev_err(&spi->dev, "register_chrdev_region fail with %d\n", ret);
goto register_chrdev_err;
}
icm_dev.major = MAJOR(icm_dev.devid);
cdev_init(&icm_dev.cdev, &icm_ops);
cdev_add(&icm_dev.cdev, icm_dev.devid, ICM_CNT);
icm_dev.class = class_create(THIS_MODULE, ICM_NAME);
if (IS_ERR(icm_dev.class))
{
dev_err(&spi->dev, "class_create fail with %s\n", ICM_NAME);
goto class_err;
}
icm_dev.device = device_create(icm_dev.class, NULL, icm_dev.devid, NULL, ICM_NAME);
if (IS_ERR(icm_dev.device))
{
dev_err(&spi->dev, "device_create fail with %s\n", ICM_NAME);
goto device_err;
}
icm_init();
dev_info(icm_dev.device, "%s\n", __func__);
return 0;
device_err:
device_destroy(icm_dev.class, icm_dev.devid);
class_destroy(icm_dev.class);
class_err:
cdev_del(&icm_dev.cdev);
unregister_chrdev_region(icm_dev.devid, ICM_CNT);
register_chrdev_err:
return -1;
}
static int icm_remove(struct spi_device *spi)
{
dev_info(icm_dev.device, "%s\n", __func__);
cdev_del(&icm_dev.cdev);
unregister_chrdev_region(icm_dev.devid, ICM_CNT);
device_destroy(icm_dev.class, icm_dev.devid);
class_destroy(icm_dev.class);
return 0;
}
static struct spi_driver icm_driver = {
.probe = icm_probe,
.remove = icm_remove,
.driver = {
.of_match_table = icm_of_table,
.name = "icm-spi",
},
};
module_spi_driver(icm_driver);
MODULE_LICENSE("GPL");
2.设备树
&ecspi3 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
fsl,spi-num-chipselects = <1>;
status = "okay";
spidev1: icm@0{
compatible = "icm-myz";
interrupt-parent = <&gpio1>;
interrupts = <1 1>;
irq-gpios = <&gpio1 1 GPIO_ACTIVE_LOW>;
spi-max-frequency = <8000000>;
reg = <0>;
};
};
3.应用层简单app
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define DEV "/dev/icm_myz"
int main(int argc, char *argv[])
{
short buf[7];
int buf32[7];
int fd = open(DEV, O_RDWR);
float data[7] = {0};
unsigned long cnt = 0;
while (1)
{
int ret = read(fd, buf, 14);
printf("[%u]\t", cnt);
for (size_t i = 0; i < 7; i++)
{
buf32[i] = buf[i];
if (i != 3)
{
data[i] = (float)buf32[i];
}
else
{
data[i] = ((float)(buf32[i]) - 25) / 326.8 + 25;
}
switch (i)
{
case 0:
case 1:
case 2:
data[i] = (float)buf32[i] / 2048;
break;
case 3:
data[i] = ((float)(buf32[i]) - 25) / 326.8 + 25;
break;
case 4:
case 5:
case 6:
data[i] = (float)buf32[i] / 16.4;
default:
break;
}
printf("%10f\t", data[i]);
}
printf("\n");
if (cnt > 10000)
break;
cnt++;
}
close(fd);
}
4.头文件
#ifndef __ICM__
#define __ICM__
#define ICM20_SELF_TEST_X_GYRO 0x00
#define ICM20_SELF_TEST_Y_GYRO 0x01
#define ICM20_SELF_TEST_Z_GYRO 0x02
#define ICM20_SELF_TEST_X_ACCEL 0x0D
#define ICM20_SELF_TEST_Y_ACCEL 0x0E
#define ICM20_SELF_TEST_Z_ACCEL 0x0F
/* 陀螺仪静态偏移 */
#define ICM20_XG_OFFS_USRH 0x13
#define ICM20_XG_OFFS_USRL 0x14
#define ICM20_YG_OFFS_USRH 0x15
#define ICM20_YG_OFFS_USRL 0x16
#define ICM20_ZG_OFFS_USRH 0x17
#define ICM20_ZG_OFFS_USRL 0x18
#define ICM20_SMPLRT_DIV 0x19
#define ICM20_CONFIG 0x1A
#define ICM20_GYRO_CONFIG 0x1B
#define ICM20_ACCEL_CONFIG 0x1C
#define ICM20_ACCEL_CONFIG2 0x1D
#define ICM20_LP_MODE_CFG 0x1E
#define ICM20_ACCEL_WOM_THR 0x1F
#define ICM20_FIFO_EN 0x23
#define ICM20_FSYNC_INT 0x36
#define ICM20_INT_PIN_CFG 0x37
#define ICM20_INT_ENABLE 0x38
#define ICM20_INT_STATUS 0x3A
/* 加速度输出 */
#define ICM20_ACCEL_XOUT_H 0x3B
#define ICM20_ACCEL_XOUT_L 0x3C
#define ICM20_ACCEL_YOUT_H 0x3D
#define ICM20_ACCEL_YOUT_L 0x3E
#define ICM20_ACCEL_ZOUT_H 0x3F
#define ICM20_ACCEL_ZOUT_L 0x40
/* 温度输出 */
#define ICM20_TEMP_OUT_H 0x41
#define ICM20_TEMP_OUT_L 0x42
/* 陀螺仪输出 */
#define ICM20_GYRO_XOUT_H 0x43
#define ICM20_GYRO_XOUT_L 0x44
#define ICM20_GYRO_YOUT_H 0x45
#define ICM20_GYRO_YOUT_L 0x46
#define ICM20_GYRO_ZOUT_H 0x47
#define ICM20_GYRO_ZOUT_L 0x48
#define ICM20_SIGNAL_PATH_RESET 0x68
#define ICM20_ACCEL_INTEL_CTRL 0x69
#define ICM20_USER_CTRL 0x6A
#define ICM20_PWR_MGMT_1 0x6B
#define ICM20_PWR_MGMT_2 0x6C
#define ICM20_FIFO_COUNTH 0x72
#define ICM20_FIFO_COUNTL 0x73
#define ICM20_FIFO_R_W 0x74
#define ICM20_WHO_AM_I 0x75
/* 加速度静态偏移 */
#define ICM20_XA_OFFSET_H 0x77
#define ICM20_XA_OFFSET_L 0x78
#define ICM20_YA_OFFSET_H 0x7A
#define ICM20_YA_OFFSET_L 0x7B
#define ICM20_ZA_OFFSET_H 0x7D
#define ICM20_ZA_OFFSET_L 0x7E
#define ICM_NAME "icm_myz"
#define ICM_CNT 1
#endif
5.makefile展示
KDIR :=kerneldir
PWD :=$(shell pwd)
# obj-m :=key.o
obj-m :=icm.o
appname :=icmapp
CCFLAG=-march=armv7-a -mfpu=neon -mfloat-abi=hard
all:default app cp
default:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
cp:
sudo cp ./*.ko $(appname) rootdir -d
app:
# arm-linux-gnueabihf-gcc -o $(appname) $(CCFLAG) $(appname).c
arm-linux-gnueabihf-gcc -o $(appname) $(appname).c