Linux驱动开发——正点原子IMX6ULL核心板ADS1256驱动开发

正点原子IMX6ULL核心板ADS1256驱动开发

一、修改设备树文件

查看正点原子IMX6ULL核心板和底板原理图,决定与 icm20608 共用 ecspi3。

找到 “imx6ull-alientek-emmc.dts” 和 “imx6ull-14x14-evk.dts”,在 “&iomuxc”下修改 “pinctrl_ecspi3” 子节点,添加“pinctrl_ads1256”,子节点。
在这里插入图片描述

pinctrl_ecspi3: ecspi3grp {
			fsl,pins = <
				MX6UL_PAD_UART2_RTS_B__ECSPI3_MISO        	0x100b1  /* MISO */
				MX6UL_PAD_UART2_CTS_B__ECSPI3_MOSI        	0x100b1  /* MOSI */
				MX6UL_PAD_UART2_RX_DATA__ECSPI3_SCLK      	0x100b1  /* CLK */
			>;
		};

		pinctrl_ads1256: ads1256 {
			fsl,pins = <
				MX6UL_PAD_UART2_TX_DATA__GPIO1_IO20       	0x100b0  /* CS0, icm20608片选 */
				MX6UL_PAD_UART3_RX_DATA__GPIO1_IO25			0x100b1  /* CS, ads1256片选 */
				MX6UL_PAD_GPIO1_IO01__GPIO1_IO01			0x100b1	 /* RESET, ads1256复位 */
				MX6UL_PAD_JTAG_MOD__GPIO1_IO10				0x130b1	 /* DRDY, ads1256就绪 */
			>;
		};

将使用相同引脚的设备节点设置为“disabled”状态

在这里插入图片描述

在“&ecspi3”节点下添加 “ads1256” 节点

在这里插入图片描述

&ecspi3 {
	fsl,spi-num-chipselects = <1>;
	// cs-gpios = <&gpio1 20 GPIO_ACTIVE_LOW &gpio1 25 GPIO_ACTIVE_LOW>;
	// cs-gpios = <&gpio1 25 GPIO_ACTIVE_LOW>;
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_ecspi3>;
	status = "okay";

	// spi3dev0: icm20608@0 {
	// compatible = "alientek,icm20608";
    //     spi-max-frequency = <8000000>;
    //     reg = <0>;
    // };

	spidev: ads1256@0 {
		compatible = "alientek,ads1256";
        spi-max-frequency = <1000000>;
		status = "okay";
        reg = <0>;
		pinctrl-0 = <&pinctrl_ads1256>;
		reset-gpio = <&gpio1 1 GPIO_ACTIVE_LOW>;
		drdy-gpio = <&gpio1 10 GPIO_ACTIVE_HIGH>;
		cs-gpio = <&gpio1 25 GPIO_ACTIVE_LOW>;
		cs0-gpio = <&gpio1 20 GPIO_ACTIVE_LOW>;
    };
};

使用开发板资料提供的编译工具,编译设备树文件,更新到开发板。

二、编写驱动程序

ads1256.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.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 "ads1256.h"
#include <linux/unistd.h>

/***************************************************************
Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.
文件名		: ads1256.c
作者	  	: 刘明鑫
版本	   	: V1.0
描述	   	: ADS1256 SPI驱动程序
其他	   	: 无
日志	   	: 初版V1.0 2022/07/31 刘明鑫创建
***************************************************************/
#define ADS1256_CNT 1
#define ADS1256_NAME "ads1256"
#define ADS1256_TRANSFORM_TIME_US	309

/* 总线注册注销 */
static int ads1256_probe(struct spi_device *spi);
static int ads1256_remove(struct spi_device *spi);
/* 文件操作函数集 */
static int ads1256_open(struct inode *inode, struct file *filp);
static ssize_t ads1256_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt);
static ssize_t ads1256_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off);
static int ads1256_release(struct inode *inode, struct file *filp);

struct ads1256_dev
{
	dev_t devid;							/* 设备号 */
	struct cdev cdev;						/* cdev */
	struct class *class;					/* 类 */
	struct device *device;					/* 设备 */
	struct device_node *nd; 				/* 设备节点 */
	int major;								/* 主设备号 */
	int cs0_gpio;							/* icm20608使用的片选,因为接了下拉电阻,所以需要手动上拉,停止启用 */
	int cs_gpio;							/* cs所使用的GPIO编号 */
	int reset_gpio;							/* reset所使用的GPIO编号 */
	int drdy_gpio;							/* drdy所使用的GPIO编号 */
	unsigned char channels[8];				/* 采样通道,最多8个 */
	unsigned char channel_num;				/* 采样通道数量 */
	int delay_us;							/* 采样间隔时间 */
	void *private_data;						/* 私有数据 */
};

static struct ads1256_dev ads1256dev;

/* 传统匹配方式ID列表 */
static const struct spi_device_id ads1256_id[] = {
	{"alientek,ads1256", 0},
	{}};

/* 设备树匹配列表 */
static const struct of_device_id ads1256_of_match[] = {
	{.compatible = "alientek,ads1256"},
	{/* Sentinel */}};

/* SPI驱动结构体 */
static struct spi_driver ads1256_driver = {
	.probe = ads1256_probe,
	.remove = ads1256_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "ads1256",
		.of_match_table = ads1256_of_match,
	},
	.id_table = ads1256_id,
};

/* ads1256操作函数 */
static const struct file_operations ads1256_ops = {
	.owner = THIS_MODULE,
	.open = ads1256_open,
	.read = ads1256_read,
	.write = ads1256_write,
	.release = ads1256_release,
};

static int ads1256_transfer_multibyte(struct ads1256_dev *dev, unsigned char *wbuf, unsigned char *rbuf, unsigned char len)
{
	int ret = 0;
	struct spi_message m;
	struct spi_transfer t = {0};
	struct spi_device *spi = (struct spi_device *)dev->private_data;

	/* 一共发送len个字节的数据*/
	t.tx_buf = wbuf; /* 要发送的数据 */
	t.rx_buf = rbuf;
	t.len = len;				 /* 传输长度 */
	spi_message_init(&m);		 /* 初始化spi_message */
	spi_message_add_tail(&t, &m); /* 将spi_transfer添加到spi_message队列 */
	ret = spi_sync(spi, &m);	 /* 同步发送 */
	return -ret;
}

/*
 * @description	: 数据传输
 * @param - dev : ads1256设备
 * @param - byte: 要通过spi总线发送的数据
 * @return 		: 通过spi总线接收到的数据
 */
static unsigned char ads1256_transfer(struct ads1256_dev *dev, unsigned char byte)
{
	int ret = -1;
	unsigned char txdata[1];
	unsigned char rxdata[1];
	struct spi_message m;
	struct spi_transfer t = {0};
	struct spi_device *spi = (struct spi_device *)dev->private_data;

	/* 一共发送1个字节的数据,第一个字节为
	寄存器首地址,一共要读取len个字节长度的数据,*/
	txdata[0] = byte | 0x80;	 /* 写数据的时候首寄存器地址bit8要置1 */
	t.tx_buf = txdata;			 /* 要发送的数据 */
	t.rx_buf = rxdata;			 /* 要读取的数据 */
	t.len = 1;					 /* 发送的长度 */
	spi_message_init(&m);		 /* 初始化spi_message */
	spi_message_add_tail(&t, &m); /* 将spi_transfer添加到spi_message队列 */
	ret = spi_sync(spi, &m);	 /* 同步发送 */
	if (ret)
	{
		rxdata[0] = 0;
	}

	return rxdata[0];
}


static int ads1256_read_channel(struct ads1256_dev *dev, unsigned char channel)
{
	unsigned char i = 0;
	unsigned int r = 0;
	int sum = 0;
	unsigned char wbuf[8];
	unsigned char rbuf[8];
	/* 设置采样通道 */
	gpio_set_value(dev->cs_gpio, 0);
	while (gpio_get_value(dev->drdy_gpio))
		;
	wbuf[0] = ADS1256_CMD_WREG | (ADS1256_MUX & 0xF);
	wbuf[1] = 0;
	ads1256_transfer_multibyte(dev, wbuf, rbuf, 2);
	wbuf[0] = channel;
	ads1256_transfer_multibyte(dev, wbuf, rbuf, 1);
	while (gpio_get_value(dev->drdy_gpio))
		;
	/* 读取转换结果 */
	wbuf[0] = ADS1256_CMD_SYNC;
	wbuf[1] = ADS1256_CMD_WAKEUP;
	wbuf[2] = ADS1256_CMD_RDATA;
	ads1256_transfer_multibyte(dev, wbuf, rbuf, 3);
	wbuf[0] = 0xFF;
	wbuf[1] = 0xFF;
	wbuf[2] = 0xFF;
	memset(rbuf, 0, 3);
	ads1256_transfer_multibyte(dev, wbuf, rbuf, 3);

	for (i = 0; i < 3; i++)
	{
		sum = sum << 8;
		r = rbuf[i];
		sum |= r;
	}

	if (sum > 0x7FFFFF)
	{ // 负值转换
		sum = sum + 0xFF000000;
	}

	while (gpio_get_value(dev->drdy_gpio))
		;
	gpio_set_value(dev->cs_gpio, 1);
	return sum;
}

/* 
 * | 命令 | 参数长度 | 参数 |
 * | 1	  | 1       | n   |
 */
struct write_data
{
	unsigned char cmd;
	unsigned char length;
	unsigned char param[8]; /* 最多8个参数 */
};

static int ads1256_write_string_to_dev(struct ads1256_dev *dev, const char *buf, size_t cnt)
{
	size_t index = 0;
	struct write_data *data_p;
	int temp;
	while (index < cnt)
	{
		if (cnt - index < 3)
		{
			return index;
		}
		data_p = (struct write_data *)&buf[index];
		switch (data_p->cmd)
		{
		case SET_CHANNELS:
			if (data_p->length > 8 || index + 1 + data_p->length >= cnt)
			{
				return index;/* 参数错误 或 剩余长度不够 */
			}
			dev->channel_num = data_p->length; /* 设置通道数量 */
			memcpy(dev->channels, data_p->param, dev->channel_num); /* 设置通道序列 */
			ads1256_read_channel(dev, dev->channels[0]);/* 重置通道 */
			index = index + data_p->length + 2;
			break;

		case SET_FREQUENCY:
			if (data_p->length != 4 || index + 1 + data_p->length >= cnt)
			{
				return index;/* 参数错误 或 剩余长度不够 */
			}
			temp = (data_p->param[0] << 24) + (data_p->param[1] << 16) + (data_p->param[2] << 8) + data_p->param[3];
			temp = 1000000 / temp - ADS1256_TRANSFORM_TIME_US;
			dev->delay_us = temp > 0 ? temp : 0;
			index = index + data_p->length + 2;
			break;

		default:
			return index;
		}
	}
	return index;
}

/*
 * @description		: 打开设备
 * @param - inode 	: 传递给驱动的inode
 * @param - filp 	: 设备文件,file结构体有个叫做pr似有ate_data的成员变量
 * 					  一般在open的时候将private_data似有向设备结构体。
 * @return 			: 0 成功;其他 失败
 */
static int ads1256_open(struct inode *inode, struct file *filp)
{
	filp->private_data = &ads1256dev; /* 设置私有数据 */
	return 0;
}

/*
 * @description		: 向设备写数据
 * @param - filp 	: 设备文件,表示打开的文件描述符
 * @param - buf 	: 要写给设备写入的数据
 * @param - cnt 	: 要写入的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 写入的字节数,如果为负值,表示写入失败
 */
static ssize_t ads1256_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
	int retvalue;
	unsigned char *databuf;
	struct ads1256_dev *dev = (struct ads1256_dev *)filp->private_data;

	databuf = kzalloc(cnt, GFP_KERNEL);
	if (!databuf)
	{
		return -ENOMEM;
	}
	retvalue = copy_from_user(databuf, buf, cnt);
	if (retvalue < 0)
	{
		printk("kernel write failed!\r\n");
		retvalue = -EFAULT;
		goto out1;
	}
	retvalue = ads1256_write_string_to_dev(dev, databuf, cnt);
out1:
	kfree(databuf);
	return retvalue;
}

/*
 * @description		: 从设备读取数据
 * @param - filp 	: 要打开的设备文件(文件描述符)
 * @param - buf 	: 返回给用户空间的数据缓冲区
 * @param - cnt 	: 要读取的数据长度
 * @param - offt 	: 相对于文件首地址的偏移
 * @return 			: 读取的字节数,如果为负值,表示读取失败
 */
static ssize_t ads1256_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	int err = 0;
	int *result;
	int length;
	int index = 1;	/* 从1开始,实际采样是从通道0开始 */
	struct ads1256_dev *dev = (struct ads1256_dev *)filp->private_data;
	/* 计算采样点个数 */
	length = cnt / 4;
	/* 开内存空间 */
	result = kzalloc(length * 4, GFP_KERNEL);
	if (!result)
	{
		return -ENOMEM;
	}
	/* 采样 */
	while (index < length + 1)
	{
		result[index - 1] = ads1256_read_channel(dev, dev->channels[index % dev->channel_num]);
		index++;
		udelay(dev->delay_us);
	}
	/* 重置通道 */
	ads1256_read_channel(dev, dev->channels[0]);
	err = copy_to_user(buf, result, length * 4);

	kfree(result);
	return length * 4;
}

/*
 * @description		: 关闭/释放设备
 * @param - filp 	: 要关闭的设备文件(文件描述符)
 * @return 			: 0 成功;其他 失败
 */
static int ads1256_release(struct inode *inode, struct file *filp)
{
	return 0;
}

/*
 * ads1256内部寄存器初始化函数
 * @param  	: 无
 * @return 	: 无
 */
void ads1256_reginit(struct ads1256_dev *dev)
{
	unsigned char wbuf[8];
	unsigned char rbuf[8];
	gpio_set_value(dev->cs_gpio, 1);
	gpio_set_value(dev->reset_gpio, 0);
	mdelay(2);
	gpio_set_value(dev->reset_gpio, 1);
	mdelay(20);
	gpio_set_value(dev->cs_gpio, 0);
	usleep_range(100, 200);
	ads1256_transfer(dev, ADS1256_CMD_REST);
	mdelay(10);
	while (gpio_get_value(dev->drdy_gpio))
		;
	wbuf[0] = ADS1256_CMD_SYNC;
	wbuf[1] = ADS1256_CMD_WAKEUP;
	ads1256_transfer_multibyte(dev, wbuf, rbuf, 2);
	while (gpio_get_value(dev->drdy_gpio))
		;
	wbuf[0] = ADS1256_CMD_WREG | ADS1256_STATUS;
	wbuf[1] = 3;
	wbuf[2] = 0x04;
	wbuf[3] = dev->channels[0];
	wbuf[4] = ADS1256_GAIN_1;
	wbuf[5] = ADS1256_DRATE_30000SPS;
	ads1256_transfer_multibyte(dev, wbuf, rbuf, 6);
	usleep_range(100, 200);

	while (gpio_get_value(dev->drdy_gpio))
		;
	ads1256_transfer(dev, ADS1256_CMD_SELFCAL);
	gpio_set_value(dev->cs_gpio, 1);
	mdelay(100);
}

/*
 * @description     : spi驱动的probe函数,当驱动与
 *                    设备匹配以后此函数就会执行
 * @param - spi     : spi设备
 *
 */
static int ads1256_probe(struct spi_device *spi)
{
	int ret;
	/* 1、构建设备号 */
	if (ads1256dev.major)
	{
		ads1256dev.devid = MKDEV(ads1256dev.major, 0);
		register_chrdev_region(ads1256dev.devid, ADS1256_CNT, ADS1256_NAME);
	}
	else
	{
		alloc_chrdev_region(&ads1256dev.devid, 0, ADS1256_CNT, ADS1256_NAME);
		ads1256dev.major = MAJOR(ads1256dev.devid);
	}

	/* 2、注册设备 */
	cdev_init(&ads1256dev.cdev, &ads1256_ops);
	cdev_add(&ads1256dev.cdev, ads1256dev.devid, ADS1256_CNT);

	/* 3、创建类 */
	ads1256dev.class = class_create(THIS_MODULE, ADS1256_NAME);
	if (IS_ERR(ads1256dev.class))
	{
		return PTR_ERR(ads1256dev.class);
	}

	/* 4、创建设备 */
	ads1256dev.device = device_create(ads1256dev.class, NULL, ads1256dev.devid, NULL, ADS1256_NAME);
	if (IS_ERR(ads1256dev.device))
	{
		return PTR_ERR(ads1256dev.device);
	}

	/*初始化spi_device */
	spi->mode = SPI_MODE_1; /*MODE1,CPOL=0,CPHA=1*/
	spi_setup(spi);
	ads1256dev.private_data = spi; /* 设置私有数据 */

	/* 设置ads1256所使用的GPIO */
	/* 1、获取设备节点:ads1256 */
	ads1256dev.nd = of_find_node_by_path("/soc/aips-bus@02000000/spba-bus@02000000/ecspi@02010000/ads1256@0");
	if (ads1256dev.nd == NULL)
	{
		printk("ads1256 node not find!\r\n");
		return -EINVAL;
	}
	else
	{
		printk("ads1256 node find!\r\n");
	}
	/* 2、 获取设备树中的gpio属性 */
	ads1256dev.cs0_gpio = of_get_named_gpio(ads1256dev.nd, "cs0-gpio", 0);
	ads1256dev.cs_gpio = of_get_named_gpio(ads1256dev.nd, "cs-gpio", 0);
	ads1256dev.reset_gpio = of_get_named_gpio(ads1256dev.nd, "reset-gpio", 0);
	ads1256dev.drdy_gpio = of_get_named_gpio(ads1256dev.nd, "drdy-gpio", 0);
	if (ads1256dev.drdy_gpio < 0 || ads1256dev.reset_gpio < 0 || ads1256dev.cs_gpio < 0 || ads1256dev.cs0_gpio < 0)
	{
		printk("can't get all gpio");
		return -EINVAL;
	}
	printk("ads1256 cs0-gpio num = %d\r\n", ads1256dev.cs0_gpio);
	printk("ads1256 cs-gpio num = %d\r\n", ads1256dev.cs_gpio);
	printk("ads1256 reset-gpio num = %d\r\n", ads1256dev.reset_gpio);
	printk("ads1256 drdy-gpio num = %d\r\n", ads1256dev.drdy_gpio);

	/* 3、配置输入输出
	 * 	设置cs1-gpio为输出
	 * 	设置reset-gpio为输出
	 * 	设置drdy-gpio为输入 */
	ret = gpio_direction_output(ads1256dev.cs0_gpio, 1);
	ret = gpio_direction_output(ads1256dev.cs_gpio, 1);
	ret = gpio_direction_output(ads1256dev.reset_gpio, 1);
	ret = gpio_direction_input(ads1256dev.drdy_gpio);
	if (ret < 0)
	{
		printk("can't set gpio!\r\n");
	}

	/* 初始化通道和延时 */
	ads1256dev.delay_us = 100;
	ads1256dev.channel_num = 1;
	ads1256dev.channels[0] = ADS1256_MUXP_AIN0 | ADS1256_MUXN_AIN1;
	/* 初始化ads1256内部寄存器 */
	ads1256_reginit(&ads1256dev);
	return 0;
}

/*
 * @description     : spi驱动的remove函数,移除spi驱动的时候此函数会执行
 * @param - spi 	: spi设备
 * @return          : 0,成功;其他负值,失败
 */
static int ads1256_remove(struct spi_device *spi)
{
	/* 注销设备的时候关闭片选 */
	gpio_set_value(ads1256dev.cs_gpio, 1);
	/* 删除设备 */
	cdev_del(&ads1256dev.cdev);
	unregister_chrdev_region(ads1256dev.devid, ADS1256_CNT);

	/* 注销掉类和设备 */
	device_destroy(ads1256dev.class, ads1256dev.devid);
	class_destroy(ads1256dev.class);
	return 0;
}

/*
 * @description	: 驱动入口函数
 * @param 		: 无
 * @return 		: 无
 */
static int __init ads1256_init(void)
{
	return spi_register_driver(&ads1256_driver);
}

/*
 * @description	: 驱动出口函数
 * @param 		: 无
 * @return 		: 无
 */
static void __exit ads1256_exit(void)
{
	spi_unregister_driver(&ads1256_driver);
}

module_init(ads1256_init);
module_exit(ads1256_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("liumingxin");


ads1256.h

#ifndef __ADS1256_H__
#define __ADS1256_H__

// define commands
#define ADS1256_CMD_WAKEUP   	0x00     //完成SYNC和退出待机模式
#define ADS1256_CMD_RDATA    	0x01     //读数据
#define ADS1256_CMD_RDATAC   	0x03     //连续读数据
#define ADS1256_CMD_SDATAC   	0x0f     //停止连续读数据
#define ADS1256_CMD_RREG     	0x10     //从寄存器度数据
#define ADS1256_CMD_WREG     	0x50     //向寄存器写数据
#define ADS1256_CMD_SELFCAL  	0xf0     //偏移和增益自动校准
#define ADS1256_CMD_SELFOCAL 	0xf1     //偏移自动校准
#define ADS1256_CMD_SELFGCAL 	0xf2     //增益自动校准
#define ADS1256_CMD_SYSOCAL  	0xf3     //系统失调校准
#define ADS1256_CMD_SYSGCAL  	0xf4     //系统增益校准
#define ADS1256_CMD_SYNC     	0xfc     //同步AD转换
#define ADS1256_CMD_STANDBY  	0xfd     //待机模式开始
#define ADS1256_CMD_REST    	0xfe     //复位

// define the ADS1256 register values
#define ADS1256_STATUS       	0x00
#define ADS1256_MUX          	0x01
#define ADS1256_ADCON        	0x02
#define ADS1256_DRATE        	0x03
#define ADS1256_IO           	0x04
#define ADS1256_OFC0         	0x05
#define ADS1256_OFC1         	0x06
#define ADS1256_OFC2         	0x07
#define ADS1256_FSC0         	0x08
#define ADS1256_FSC1         	0x09
#define ADS1256_FSC2         	0x0A

// define multiplexer codes
#define ADS1256_MUXP_AIN0   	0x00
#define ADS1256_MUXP_AIN1   	0x10
#define ADS1256_MUXP_AIN2   	0x20
#define ADS1256_MUXP_AIN3   	0x30
#define ADS1256_MUXP_AIN4   	0x40
#define ADS1256_MUXP_AIN5   	0x50
#define ADS1256_MUXP_AIN6   	0x60
#define ADS1256_MUXP_AIN7   	0x70
#define ADS1256_MUXP_AINCOM 	0x80

#define ADS1256_MUXN_AIN0   	0x00
#define ADS1256_MUXN_AIN1   	0x01
#define ADS1256_MUXN_AIN2   	0x02
#define ADS1256_MUXN_AIN3   	0x03
#define ADS1256_MUXN_AIN4   	0x04
#define ADS1256_MUXN_AIN5   	0x05
#define ADS1256_MUXN_AIN6   	0x06
#define ADS1256_MUXN_AIN7   	0x07
#define ADS1256_MUXN_AINCOM 	0x08

// define gain codes	
#define ADS1256_GAIN_1      	0x00
#define ADS1256_GAIN_2      	0x01
#define ADS1256_GAIN_4      	0x02
#define ADS1256_GAIN_8      	0x03
#define ADS1256_GAIN_16     	0x04
#define ADS1256_GAIN_32     	0x05
#define ADS1256_GAIN_64     	0x06

//define drate codes
#define ADS1256_DRATE_30000SPS  0xF0
#define ADS1256_DRATE_15000SPS  0xE0
#define ADS1256_DRATE_7500SPS   0xD0
#define ADS1256_DRATE_3750SPS   0xC0
#define ADS1256_DRATE_2000SPS   0xB0
#define ADS1256_DRATE_1000SPS   0xA1
#define ADS1256_DRATE_500SPS    0x92
#define ADS1256_DRATE_100SPS    0x82
#define ADS1256_DRATE_60SPS     0x72
#define ADS1256_DRATE_50SPS     0x63
#define ADS1256_DRATE_30SPS     0x53
#define ADS1256_DRATE_25SPS     0x43
#define ADS1256_DRATE_15SPS     0x33
#define ADS1256_DRATE_10SPS     0x23
#define ADS1256_DRATE_5SPS      0x13
#define ADS1256_DRATE_2_5SPS    0x03

enum write_cmd
{
	SET_CHANNELS = 0,
	SET_FREQUENCY
};

#endif



Makefile

#先写生成的中间文件是什么,-m 的意思是把我们的驱动编译成模块
NAME = ads1256
obj-m += $(NAME).o
#KDIR: 内核源码所在路径
KDIR:=/home/lmx/workplace/IMX6ULL/linux-imx-4.1.15-2.1.0-g0423506-v2.2
#PWD: 获取当前目录的变量
PWD?=$(shell pwd)
#make 会进入内核源码的路径,然后把当前路径下的代码编译成模块
all:
	make -C $(KDIR) M=$(PWD) modules
#-C表示 指定进入指定的目录即KERN,是内核源代码目录,调用该目录顶层下的Makefile,目标为modules。
#M=$(PWD)选项让该Makefile在构造modules目标之前返回到模块源代码目录并在当前目录生成obj-m指定的xxx.o目标模块。
clean:
	rm $(NAME).o $(NAME).ko $(NAME).mod.o $(NAME).mod.c modules.order Module.symvers

注意编译前先设置好环境变量

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-

将编译好的模块 “ads1256.ko” 拷贝到NFS共享目录下。

开发板挂载NFS共享目录:

mount -t nfs -o nolock,nfsvers=3,vers=3 192.168.3.85:/home/lmx/workplace/share /mnt/nfs

取消挂载:

umount /mnt/nfs

使用 lsmod 命令发现已经加载了 icm20608 模块,先将模块注销,然后加载模块

rmmod icm20608
insmod ads1256.ko

三、编写应用层库

ads1256.c

#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/ioctl.h"
#include <poll.h>
#include <sys/select.h>
#include <signal.h>
#include <fcntl.h>
#include "ads1256.h"
#include "stdlib.h"

#define REFERENCE_VOLTAGE   5

union Data
{
    unsigned char data_8[4];
    int data_32;
};

int ads1256_init(const char *filename)
{
    int fd;
    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        return -1;
    }
    return fd;
}

void ads1256_deinit(int fd)
{
    close(fd); /* 关闭文件 */
}

int ads1256_set_frequency(int fd, int frequency)
{
    int ret = 0;
    unsigned char buf[6];
    buf[0] = SET_FREQUENCY;
    buf[1] = 4;
    buf[2] = frequency >> 24;
    buf[3] = frequency >> 16;
    buf[4] = frequency >> 8;
    buf[5] = frequency & 0xFF;
    ret = write(fd, buf, 6);
    if (ret != 6)
    {
        return -1;
    }
    return 0;
}

int ads1256_set_channels(int fd, const unsigned char *channels, unsigned char num)
{
    int ret = 0;
    int i;
    unsigned char buf[10];
    if (num > 8)
    {
        return -1;
    }
    buf[0] = SET_CHANNELS;
    buf[1] = num;
    for (i = 0; i < num; i++)
    {
        buf[2 + i] = channels[i];
    }
    ret = write(fd, buf, num + 2);
    if (ret != num + 2)
    {
        return -1;
    }
    return 0;
}

int ads1256_read_voltage(int fd, float *buf, int length)
{
    union Data *databuf;
    int ret;
    int i;
    databuf = malloc(sizeof(union Data) * length);
    if (!databuf)
    {
        return -1;
    }
    ret = read(fd, databuf, length * 4);
    for (i = 0; i < ret / 4; i++)
    {
        buf[i] = (float)(REFERENCE_VOLTAGE * databuf[i].data_32) / 0x800000;
    }
    free(databuf);
    return ret / 4;
}


ads1256.h

#ifndef __ADS1256_H__
#define __ADS1256_H__

#ifdef __cplusplus
extern "C" {
#endif

#define ADS1256_MUXP_AIN0   	0x00
#define ADS1256_MUXP_AIN1   	0x10
#define ADS1256_MUXP_AIN2   	0x20
#define ADS1256_MUXP_AIN3   	0x30
#define ADS1256_MUXP_AIN4   	0x40
#define ADS1256_MUXP_AIN5   	0x50
#define ADS1256_MUXP_AIN6   	0x60
#define ADS1256_MUXP_AIN7   	0x70
#define ADS1256_MUXP_AINCOM 	0x80

#define ADS1256_MUXN_AIN0   	0x00
#define ADS1256_MUXN_AIN1   	0x01
#define ADS1256_MUXN_AIN2   	0x02
#define ADS1256_MUXN_AIN3   	0x03
#define ADS1256_MUXN_AIN4   	0x04
#define ADS1256_MUXN_AIN5   	0x05
#define ADS1256_MUXN_AIN6   	0x06
#define ADS1256_MUXN_AIN7   	0x07
#define ADS1256_MUXN_AINCOM 	0x08

enum write_cmd
{
	SET_CHANNELS = 0,
	SET_FREQUENCY
};

int ads1256_init(const char *filename);
void ads1256_deinit(int fd);
int ads1256_set_frequency(int fd, int frequency);
int ads1256_set_channels(int fd, const unsigned char *channels, unsigned char num);
int ads1256_read_voltage(int fd, float *buf, int length);

#ifdef __cplusplus
}
#endif

#endif // !__LIBADS1256_H__

编译成静态库:

arm-linux-guneabihf-gcc -c ads1256.c
arm-linux-gnueabihf-ar rcs libads1256.a ads1256.o
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值