嵌入式学习–spi子系统学习
spi_drv.h
#ifndef SPI_DRV_H
#define SPI_DRV_H
#define ICM20608G_ID 0XAF
#define ICM20608D_ID 0XAE
/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
#define ICM20608_SELF_TEST_X_GYRO 0X00
#define ICM20608_SELF_TEST_Y_GYRO 0X01
#define ICM20608_SELF_TEST_Z_GYRO 0X02
#define ICM20608_SELF_TEST_X_ACCEL 0X0D
#define ICM20608_SELF_TEST_Y_ACCEL 0X0E
#define ICM20608_SELF_TEST_Z_ACCEL 0X0F
/* 陀螺仪静态偏移 */
#define ICM20608_XG_OFFS_USRH 0X13
#define ICM20608_XG_OFFS_USRL 0X14
#define ICM20608_YG_OFFS_USRH 0X15
#define ICM20608_YG_OFFS_USRL 0X16
#define ICM20608_ZG_OFFS_USRH 0X17
#define ICM20608_ZG_OFFS_USRL 0X18
#define ICM20608_SMPLRT_DIV 0x19
#define ICM20608_CONFIG 0x1A
#define ICM20608_GYRO_CONFIG 0x1B
#define ICM20608_ACCEL_CONFIG 0x1C
#define ICM20608_ACCEL_CONFIG2 0x1D
#define ICM20608_LP_MODE_CFG 0x1E
#define ICM20608_ACCEL_WOM_THR 0x1F
#define ICM20608_FIFO_EN 0x23
#define ICM20608_FSYNC_INT 0x36
#define ICM20608_INT_PIN_CFG 0x37
#define ICM20608_INT_ENABLE 0x38
#define ICM20608_INT_STATUS 0x3A
/* 加速度输出 */
#define ICM20608_ACCEL_XOUT_H 0x3B
#define ICM20608_ACCEL_XOUT_L 0x3C
#define ICM20608_ACCEL_YOUT_H 0x3D
#define ICM20608_ACCEL_YOUT_L 0x3E
#define ICM20608_ACCEL_ZOUT_H 0x3F
#define ICM20608_ACCEL_ZOUT_L 0x40
/* 温度输出 */
#define ICM20608_TEMP_OUT_H 0x41
#define ICM20608_TEMP_OUT_L 0x42
/* 陀螺仪输出 */
#define ICM20608_GYRO_XOUT_H 0x43
#define ICM20608_GYRO_XOUT_L 0x44
#define ICM20608_GYRO_YOUT_H 0x45
#define ICM20608_GYRO_YOUT_L 0x46
#define ICM20608_GYRO_ZOUT_H 0x47
#define ICM20608_GYRO_ZOUT_L 0x48
#define ICM20608_SIGNAL_PATH_RESET 0x68
#define ICM20608_ACCEL_INTEL_CTRL 0x69
#define ICM20608_USER_CTRL 0x6A
#define ICM20608_PWR_MGMT_1 0x6B
#define ICM20608_PWR_MGMT_2 0x6C
#define ICM20608_FIFO_COUNTH 0x72
#define ICM20608_FIFO_COUNTL 0x73
#define ICM20608_FIFO_R_W 0x74
#define ICM20608_WHO_AM_I 0x75
/* 加速度静态偏移 */
#define ICM20608_XA_OFFSET_H 0x77
#define ICM20608_XA_OFFSET_L 0x78
#define ICM20608_YA_OFFSET_H 0x7A
#define ICM20608_YA_OFFSET_L 0x7B
#define ICM20608_ZA_OFFSET_H 0x7D
#define ICM20608_ZA_OFFSET_L 0x7E
#endif
spi_drv.c
#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_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/spi/spi.h>
#include <linux/of.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 "spi_drv.h"
struct icm20608_devices {
int major;
dev_t devid;
struct cdev cdev;
struct class *class;
struct device *dev;
struct device_node *dev_nod;
int cs_gpio;
void *private_data;
signed int gyro_x_adc; /* 陀螺仪X轴原始值 */
signed int gyro_y_adc; /* 陀螺仪Y轴原始值 */
signed int gyro_z_adc; /* 陀螺仪Z轴原始值 */
signed int accel_x_adc; /* 加速度计X轴原始值 */
signed int accel_y_adc; /* 加速度计Y轴原始值 */
signed int accel_z_adc; /* 加速度计Z轴原始值 */
signed int temp_adc; /* 温度原始值 */
};
struct icm20608_devices *icm20608_dev;
#if 1
static int icm20608_read_regs(struct icm20608_devices *dev,u8 reg,u8 *buf,u8 len){
int ret;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private_data;
t = (struct spi_transfer *)kzalloc(sizeof(struct spi_transfer), GFP_KERNEL);
gpio_set_value(dev->cs_gpio,0);//片选拉低
//第一次发送要读取寄存器的地址
txdata[0] = reg | 0x80;
t->tx_buf = txdata;
t->len = 1;
spi_message_init(&m);
spi_message_add_tail( t, &m);
ret = spi_sync(spi, &m);
txdata[0] = 0xff;
t->tx_buf = buf;
t->len = len;
spi_message_init(&m);
spi_message_add_tail(t, &m);
ret = spi_sync(spi, &m);
kfree(t);
gpio_set_value(dev->cs_gpio,1);//片选拉高,释放icm20608
return ret;
}
#endif
#if 0
static int icm20608_read_regs(struct icm20608_devices *dev,u8 reg,u8 *buf,u8 len){
u8 data = 0;
struct spi_device *spi = (struct spi_device*)dev->private_data;
//片选拉低
gpio_set_value(dev->cs_gpio, 0);
data = reg | 0x80;
spi_write(spi, &data, 1);
spi_read(spi, buf, len);
gpio_set_value(dev->cs_gpio, 1);
}
#endif
static unsigned char icm20608_read_one_reg(struct icm20608_devices * dev,u8 reg){
u8 data = 0;
icm20608_read_regs(dev,reg,&data,1);
return data;
}
static void icm20608_read_data(struct icm20608_devices *dev){
unsigned char data[14];
icm20608_read_regs(dev,ICM20608_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]);
}
#if 1
static s32 icm20608_write_regs(struct icm20608_devices *dev,u8 reg,u8 *buf,u8 len){
int ret;
unsigned char txdata[len];
struct spi_message m;
struct spi_transfer *t;
struct spi_device *spi = (struct spi_device *)dev->private_data;
t = kzalloc(sizeof(struct spi_transfer),GFP_KERNEL);
gpio_set_value(dev->cs_gpio,0);//片选
//到读取的寄存器
txdata[0] = reg & ~0x80;//写数据时,寄存器bit8要清零
t->tx_buf = txdata;//要发送的数据
t->len = 1;//一个字节
spi_message_init(&m);//初始化spi_message
spi_message_add_tail(t, &m);//讲spi_transfer添加到spi_message队列
ret = spi_sync(spi, &m);//同步发送
//发送要写的数据
t->tx_buf = buf;//写入的数据
t->len = len;//写的字节数
spi_message_init(&m);
spi_message_add_tail(t, &m);
ret = spi_sync(spi, &m);
kfree(t);
gpio_set_value(dev->cs_gpio,1);//片选拉高,释放ICm20608
return ret;
}
#endif
#if 0
static s32 icm20608_write_regs(struct icm20608_devices *dev,u8 reg,u8 *buf,u8 len){
u8 data = 0;
struct spi_device *spi = (struct spi_device *)dev->private_data;
gpio_set_value(dev->cs_gpio,0);
data = reg & ~0x80;
spi_write(spi, &data, 1);
spi_write(spi, buf, len);
gpio_set_value(dev->cs_gpio,1);
}
#endif
static void icm20608_write_one_reg(struct icm20608_devices *dev,u8 reg,u8 value){
//printk("----------icm20608_write_one_reg----------\n");
u8 buf = value;
icm20608_write_regs(dev,reg,&buf,1);
}
//02-2-2
static int icm20608_open(struct inode *inod, struct file *filp){
printk("------------%s-----------\n",__FUNCTION__);
filp->private_data = icm20608_dev;
return 0;
}
//02-2-3
static ssize_t icm20608_read(struct file *filp, char __user *buf , size_t size, loff_t *loft){
//printk("------------%s-----------\n",__FUNCTION__);
signed int data[7];
long err = 0;
struct icm20608_devices *dev = (struct icm20608_devices *)filp->private_data;
icm20608_read_data(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;
}
//02-2-4
static ssize_t icm20608_write(struct file *filp, const char __user *buf, size_t size, loff_t *loft){
printk("------------%s-----------\n",__FUNCTION__);
return 0;
}
//02-2-5
static int icm20608_release(struct inode *inod, struct file *filp){
printk("------------%s-----------\n",__FUNCTION__);
return 0;
}
//02-2-1
static const struct file_operations icm20608_fops = {
.owner = THIS_MODULE,
.open = icm20608_open,
.read = icm20608_read,
.write = icm20608_write,
.release = icm20608_release,
};
//03-5--
void icm20608_reg_init(struct icm20608_devices *dev){
printk("------------icm20608_reg_init----------------\n");
u8 value = 0;
icm20608_write_one_reg(dev,ICM20608_PWR_MGMT_1,0X80);
mdelay(50);
icm20608_write_one_reg(dev, ICM20608_PWR_MGMT_1, 0X01);
mdelay(50);
value = icm20608_read_one_reg(dev, ICM20608_WHO_AM_I);
printk("ICM20608 ID = %x\r\n",value);
icm20608_write_one_reg(dev, ICM20608_SMPLRT_DIV, 0X00);//输出速率是内部采样率
icm20608_write_one_reg(dev, ICM20608_GYRO_CONFIG, 0X18);//陀螺仪±2000dps量程
icm20608_write_one_reg(dev, ICM20608_ACCEL_CONFIG, 0X18);//加速度计±16G量程
icm20608_write_one_reg(dev, ICM20608_CONFIG, 0X04);//陀螺仪低通滤波BW=20Hz
icm20608_write_one_reg(dev, ICM20608_ACCEL_CONFIG2, 0X00);//加速度计低通滤波BW=21.2Hz
icm20608_write_one_reg(dev, ICM20608_PWR_MGMT_2, 0X00);//打开加速度计和陀螺仪所有轴
icm20608_write_one_reg(dev, ICM20608_LP_MODE_CFG, 0X00);//关闭低功耗
icm20608_write_one_reg(dev, ICM20608_FIFO_EN, 0X00);//关闭FIFO
}
//01-1-1
static int icm20608_probe(struct spi_device *spi){
printk("---------------icm20608_probe-------------\n");
//02-1.要和用户打交道,就得用到字符设备那套
int ret = 0;
//构建设备号
if(icm20608_dev->major){
icm20608_dev->devid = MKDEV(icm20608_dev->major,0);
register_chrdev_region(icm20608_dev->devid, 1, "icm20608");
}else{
alloc_chrdev_region(&icm20608_dev->devid, 0, 1, "icm20608");
icm20608_dev->major = MAJOR(icm20608_dev->devid);
}
//02-2,,注册设备
cdev_init(&icm20608_dev->cdev,&icm20608_fops);
cdev_add(&icm20608_dev->cdev,icm20608_dev->devid,1);
//02-4
icm20608_dev->class = class_create(THIS_MODULE, "ICM20608");
if(IS_ERR(icm20608_dev->class)){
return PTR_ERR(icm20608_dev->class);
}
//02-3,创建设备节点
icm20608_dev->dev = device_create(icm20608_dev->class, NULL, icm20608_dev->devid, NULL, "peifeng-icm20608");//在/dev/目录下可见
if(IS_ERR(icm20608_dev->dev)){
return PTR_ERR(icm20608_dev->dev);
}
//03-1
//icm20608_dev->dev_nod = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000");
icm20608_dev->dev_nod = of_get_parent(spi->dev.of_node);
if(icm20608_dev->dev_nod == NULL){
printk("find device node failed!!\n");
return -EINVAL;
}
//03-2
icm20608_dev->cs_gpio = of_get_named_gpio(icm20608_dev->dev_nod, "cs-gpio", 0);
if(icm20608_dev->cs_gpio < 0){
printk("can not get cs-gpio!!\n");
return -EINVAL;
}
printk("---------icm20608_dev->cs_gpio = %d \n",icm20608_dev->cs_gpio);
ret = gpio_request(icm20608_dev->cs_gpio, "cs");
if(ret<0){
printk("cs_gpio request failed\n");
}
printk("---------gpio_request = %d \n",ret);
//03-3
ret = gpio_direction_output(icm20608_dev->cs_gpio, 1);
if(ret < 0){
printk("can not set gpio!!\n");
}
printk("---------gpio_direction_output = %d \n",ret);
//03-4 初始化spi_device
spi->mode = SPI_MODE_0;//CPOL=0,CPHA=0
//setup SPI mode and clock rate
spi_setup(spi);
icm20608_dev->private_data = spi;
//03-5 初始化icm20608内部寄存器
icm20608_reg_init(icm20608_dev);
return 0;
}
//01-1-2,,
static int icm20608_remove(struct spi_device *spi){
cdev_del(&icm20608_dev->cdev);
unregister_chrdev_region(icm20608_dev->devid, 1);
device_destroy(icm20608_dev->class, icm20608_dev->devid);
class_destroy(icm20608_dev->class);
gpio_free(icm20608_dev->cs_gpio);
return 0;
}
//01-2 传统匹配方式
static const struct spi_device_id icm20608_id[] = {
{"peifeng,icm20608",0},
{},
};
//01-3 设备树匹配列表
static const struct of_device_id icm20608_of_match[] =
{
{.compatible = "peifeng,icm20608"},
{},
};
//01-1,,spi驱动结构体
struct spi_driver icm_20608_drv = {
.probe = icm20608_probe,
.remove = icm20608_remove,
.driver = {
.owner = THIS_MODULE,
.name = "icm20608",
.of_match_table = icm20608_of_match,//对应01-3
},
.id_table = icm20608_id,//对应01-2
};
//01.
static int __init spi_drv_init(void){
printk("---------------%s-----------\n",__FUNCTION__);
icm20608_dev = (struct icm20608_devices *)kzalloc(sizeof(struct icm20608_devices),GFP_KERNEL);
if(icm20608_dev == NULL){
printk("icm20608 kzalloc failed\n");
return -ENOMEM;
}
return spi_register_driver(&icm_20608_drv);
}
///02,
static void __exit spi_drv_exit(void){
printk("---------------%s-----------\n",__FUNCTION__);
spi_unregister_driver(&icm_20608_drv);
kfree(icm20608_dev);
}
module_init(spi_drv_init);
module_exit(spi_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("PEIFENG");
spi_app.c
#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 <signal.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
int fd;
char *filename;
signed int databuf[7];
unsigned char data[14];
signed int gyro_x_adc, gyro_y_adc, gyro_z_adc;
signed int accel_x_adc, accel_y_adc, accel_z_adc;
signed int temp_adc;
float gyro_x_act, gyro_y_act, gyro_z_act;
float accel_x_act, accel_y_act, accel_z_act;
float temp_act;
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, databuf, sizeof(databuf));
if(ret == 0) { /* 数据读取成功 */
gyro_x_adc = databuf[0];
gyro_y_adc = databuf[1];
gyro_z_adc = databuf[2];
accel_x_adc = databuf[3];
accel_y_adc = databuf[4];
accel_z_adc = databuf[5];
temp_adc = databuf[6];
/* 计算实际值 */
gyro_x_act = (float)(gyro_x_adc) / 16.4;
gyro_y_act = (float)(gyro_y_adc) / 16.4;
gyro_z_act = (float)(gyro_z_adc) / 16.4;
accel_x_act = (float)(accel_x_adc) / 2048;
accel_y_act = (float)(accel_y_adc) / 2048;
accel_z_act = (float)(accel_z_adc) / 2048;
temp_act = ((float)(temp_adc) - 25 ) / 326.8 + 25;
printf("\r\n 转换前:\r\n");
printf("gx = %d, gy = %d, gz = %d\r\n", gyro_x_adc, gyro_y_adc, gyro_z_adc);
printf("ax = %d, ay = %d, az = %d\r\n", accel_x_adc, accel_y_adc, accel_z_adc);
printf("temp = %d\r\n", temp_adc);
printf(" 转换后:\n");
printf("act gx = %.2f°/S, act gy = %.2f°/S, act gz = %.2f°/S\r\n", gyro_x_act, gyro_y_act, gyro_z_act);
printf("act ax = %.2fg, act ay = %.2fg, act az = %.2fg\r\n", accel_x_act, accel_y_act, accel_z_act);
printf("act temp = %.2f°C\r\n", temp_act);
}
usleep(100000); /*100ms */
}
close(fd); /* 关闭文件 */
return 0;
}