SPI通信协议
分析硬件电路
- 开发板原理图
- 核心板原理图
编写设备树
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_ecspi3: icm20608 {
fsl,pins = <
MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20 0x10b0
MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK 0x10b1
MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO 0x10b1
MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI 0x10b1
>;
};
};
};
&ecspi3 {
fsl,spi-num-chipselects = <1>;
cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi3>;
status = "okay";
spidev: icm20608@0 {
compatible = "myboard,icm20608";
spi-max-frequency = <8000000>;
reg = <0>;
};
};
编写SPI设备驱动
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/input.h>
#include <linux/spi/spi.h>
#include <linux/delay.h>
#include "icm20608.h"
#define DEV_CNT 1
#define DEV_NAME "icm20608"
struct icm20608_cdev {
dev_t devt;
struct cdev cdev;
unsigned int major;
struct class *class;
struct device *device;
void *private_data;
signed int gyro_x_adc;
signed int gyro_y_adc;
signed int gyro_z_adc;
signed int accel_x_adc;
signed int accel_y_adc;
signed int accel_z_adc;
signed int temp_adc;
};
static struct icm20608_cdev icm20608cdev;
int icm20608_read_regs(struct icm20608_cdev *dev, u8 addr, void *buf, size_t len)
{
int ret=0;
u8 data=0;
struct spi_device *spi = (struct spi_device *)dev->private_data;
data= addr | 0x80;
ret = spi_write_then_read(spi, &data, 1, buf, len);
return ret;
}
int icm20608_write_regs(struct icm20608_cdev *dev, u8 addr, void *buf, size_t len)
{
int ret=0;
u8 *txdata;
struct spi_device *spi = (struct spi_device *)dev->private_data;
txdata = kzalloc(len+1, GFP_KERNEL);
txdata[0]=addr & ~0x80;
memcpy(&txdata[1],buf,len);
ret = spi_write(spi, txdata, len+1);
kfree(txdata);
return ret;
}
int icm20608_read_reg(struct icm20608_cdev *dev, u8 addr)
{
u8 data=0;
icm20608_read_regs(dev, addr, &data, 1);
return data;
}
int icm20608_write_reg(struct icm20608_cdev *dev, u8 addr, u8 value)
{
u8 buf = value;
return icm20608_write_regs(dev, addr, &buf, 1);
}
void icm20608_readdata(struct icm20608_cdev *dev)
{
unsigned char data[14];
icm20608_read_regs(dev, ICM20_ACCEL_XOUT_H, data, 14);
dev->accel_x_adc = (signed short)((data[0] << 8) | data[1]);
dev->accel_y_adc = (signed short)((data[2] << 8) | data[3]);
dev->accel_z_adc = (signed short)((data[4] << 8) | data[5]);
dev->temp_adc = (signed short)((data[6] << 8) | data[7]);
dev->gyro_x_adc = (signed short)((data[8] << 8) | data[9]);
dev->gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
dev->gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
}
static int icm20608_open(struct inode *inode, struct file *file)
{
file->private_data = &icm20608cdev;
return 0;
}
static ssize_t icm20608_read(struct file *file, char __user *buf, size_t cnt, loff_t *off)
{
signed int data[7];
long err = 0;
struct icm20608_cdev *dev = (struct icm20608_cdev *)file->private_data;
icm20608_readdata(dev);
data[0] = dev->gyro_x_adc;
data[1] = dev->gyro_y_adc;
data[2] = dev->gyro_z_adc;
data[3] = dev->accel_x_adc;
data[4] = dev->accel_y_adc;
data[5] = dev->accel_z_adc;
data[6] = dev->temp_adc;
err = copy_to_user(buf, data, sizeof(data));
return 0;
}
int icm20608_release(struct inode *inode, struct file *file)
{
return 0;
}
struct file_operations icm20608_file_operations = {
.owner = THIS_MODULE,
.open = icm20608_open,
.read = icm20608_read,
.release = icm20608_release,
};
void icm20608_device_init(struct icm20608_cdev *cdev)
{
int value=0;
icm20608_write_reg(&icm20608cdev, ICM20_PWR_MGMT_1, 0x80);
mdelay(50);
icm20608_write_reg(&icm20608cdev, ICM20_PWR_MGMT_1, 0x01);
mdelay(50);
value = icm20608_read_reg(&icm20608cdev, ICM20_WHO_AM_I);
printk("ICM20608 ID = %#X\r\n", value);
value = icm20608_read_reg(&icm20608cdev, ICM20_PWR_MGMT_1);
printk("ICM20_PWR_MGMT_1 value = %#X\r\n", value);
}
static int icm20608_probe(struct spi_device *spi)
{
printk("spi driver and device was matched!\r\n");
if(icm20608cdev.major)
{
icm20608cdev.devt = MKDEV(icm20608cdev.major,0);
register_chrdev_region(icm20608cdev.devt, DEV_CNT, DEV_NAME);
}
else
{
alloc_chrdev_region(&icm20608cdev.devt, 0, DEV_CNT, DEV_NAME);
icm20608cdev.major = MAJOR(icm20608cdev.devt);
}
cdev_init(&icm20608cdev.cdev, &icm20608_file_operations);
cdev_add(&icm20608cdev.cdev, icm20608cdev.devt, DEV_CNT);
icm20608cdev.class = class_create(THIS_MODULE, DEV_NAME);
if(IS_ERR(icm20608cdev.class))
{
return PTR_ERR(icm20608cdev.class);
}
icm20608cdev.device = device_create(icm20608cdev.class, NULL, icm20608cdev.devt, NULL, DEV_NAME);
if(IS_ERR(icm20608cdev.device))
{
return PTR_ERR(icm20608cdev.device);
}
spi->mode = SPI_MODE_0;
spi_setup(spi);
icm20608cdev.private_data = spi;
icm20608_device_init(&icm20608cdev);
return 0;
}
static int icm20608_remove(struct spi_device *spi)
{
cdev_del(&icm20608cdev.cdev);
unregister_chrdev_region(icm20608cdev.devt, DEV_CNT);
device_destroy(icm20608cdev.class, icm20608cdev.devt);
class_destroy(icm20608cdev.class);
return 0;
}
static const struct spi_device_id icm20608_device_id[] = {
{"myboard,icm20608", 0},
{}
};
static const struct of_device_id icm20608_driver_of_match_table[] = {
{.compatible = "myboard,icm20608"},
{}
};
struct spi_driver icm20608_driver = {
.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {
.name = "icm20608",
.owner = THIS_MODULE,
.of_match_table = icm20608_driver_of_match_table,
},
.id_table = icm20608_device_id,
};
static int __init icm20608_init(void)
{
int ret = 0;
ret = spi_register_driver(&icm20608_driver);
return ret;
}
static void __exit icm20608_exit(void)
{
spi_unregister_driver(&icm20608_driver);
}
module_init(icm20608_init);
module_exit(icm20608_exit);
MODULE_AUTHOR("AUTHOR");
MODULE_LICENSE("GPL");