This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADS1292RECG-FE: continuous data reading and calibration

Part Number: ADS1292RECG-FE
Other Parts Discussed in Thread: ADS1292R, ADS1292

We are interfacing the ads1292R ic with snapdragon processor for ecg application. We have written the driver and application to get both channel data. We are reading data on the interrupt with all default chip configurations.

For channel 1 data, we are getting continuous changed values and for channel 2 data, we are getting values changed only when we put fingers on pcb electrodes.

I have following queries related to the ADS1292R chip functionality.

  1. Is there any specific use or application for particular channels?
  2. After getting raw data from device, is there any algorithm need to applied in code to calculate ecg values? As we are not getting graph like actual ecg.
  3. Please share if there is any specific guide to calibrate the sensor.

Attaching our design pictures. Let me know if you need more information.

  • Hi Vijay,

    Welcome to the E2E forum !!! Thanks for the post, and please see my comments below.

     1. Is there any specific use or application for particular channels?

    The schematic shows CH1 is set up for respiration impedance measurement and CH2 for ECG signals. Please refer to the application section of the datasheet (section 9.2) for the typical application where ADS1292R is used for the acquisition of ECG signals in combination with a respiration impedance measurement.

    2. After getting raw data from device, is there any algorithm need to applied in code to calculate ecg values? As we are not getting graph like actual ecg.

    You can apply additional digital filters or algorithms to process the acquired ECG signal further for improvement in baseline wander, powerline noise, etc. The use of RLD will improve the quality of the ECG signals in your application. Please refer to this app notes (SBAA188) for further information in using the RLD to improve the CMRR.

    3. Please share if there is any specific guide to calibrate the sensor.

    There is no calibration need for the ADS in the acquisition of ECG signals.

    Thanks.

    -TC

  • We want to measure channel 2 only as we are interested in ecg.

    Whenever I put both the fingers on both electrodes, we are not getting an updated readings on it. I changed the sps and gain both values, but still we are not getting an updated result here.

    When I use only one electrode and put finger only on that electrode, at that time I am getting changed data. Does it measures correct ecg values using only one electrode?  

    We are not giving 3.3v on AVDD. For both AVDD and DVDD, we are giving 1.8v supply. Does it affect the performance? 



  • Ho Vijay,

    You will need both the RA and LA electrodes for a single lead ECG application (Lead I = LA - RA). The minimum analog power supply (AVDD - AVSS) for the ADS1292 is 2.7V. Please refer to the datasheet for the recommended operating conditions for the device. 

    Thanks

    -TC

  • okay, I have given 3.3v at AVDD and checked again with both fingers.

    The result is same. The ecg channel data is not changing. Sometimes it changes and most of the time it doesn't changing.

  • Hi Vijay,

    There can be a lot of things that are causing the issues. I recommend debugging the issue step by step. Please take a look at the ADS129x BioPotential FAQ for additional resources on the device setup and operations. Making sure the device register the read/write function and the signal-chain data capture are working correctly. It is also helpful if you can first verify the design with a patient simulator. There are a lot of helpful resources on the E2E by doing a simple search on the forum.

    Thanks

    -TC

  • Apology, I was debugging the ecg value change issue.

    The 250 sps value with gain of 2 for channel-2 gives better result than others.
    When I increase the samples more than 250 the driver miss some of the interrupts lets say it processes 4 interrupts and overrides the 5th one. So it will miss the each 5th sample data.

    I am sending the driver to you. Can you please check if is there any way to optimise interrupt handling. So we don't miss any sample.

    6622.ads1292.c
    /**
     * ADS1292R - Linux Kernel Driver Module
     * Licensed under the GPL-2
     */
    
    #include <linux/device.h>
    #include <linux/kernel.h>
    #include <linux/slab.h>
    #include <linux/sysfs.h>
    #include <linux/spi/spi.h>
    #include <linux/err.h>
    #include <linux/delay.h>
    #include <linux/module.h>
    #include <linux/interrupt.h>
    #include <linux/irq.h>
    #include <linux/gpio.h>
    #include <linux/of_gpio.h>
    #include <linux/uaccess.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/of_irq.h>
    #include <linux/of_device.h>
    #include <linux/of_address.h>
    
    #include "ads1292.h"
    
    #define ADS_DEVICE_NAME		"ads1292_cdev"
    #define SPI_BUFF_SIZE		64
    #define SPI_BUS_SPEED_SLOW	100000
    #define SPI_BUS_SPEED_FAST	15000000
    #define ACQ_BUF_SIZE		9
    
    
    struct ads1292_chip{
    	struct spi_device *spi;
    	char *rx_buff;
    	char *tx_buff;
    	struct spi_message msg;
    	struct spi_transfer transfer;
    };
    
    struct ads1292_dev {
    	struct cdev dev;
    	dev_t dev_no;
    	struct class *cl;
    	struct ads1292_chip chip;
    
    	struct mutex mutex;
    	struct semaphore fop_sem;
    	wait_queue_head_t wait_queue;
    
    	int irq;
    	int gpio_initialized;
    	volatile int rx_count;
    	volatile int sample_req;
    };
    static struct ads1292_dev ads_inst;
    
    struct gpio ads1292_gpio_reset = { .label = "reset-gpio", .flags = GPIOF_OUT_INIT_HIGH };
    struct gpio ads1292_gpio_start = { .label = "start-gpio", .flags = GPIOF_OUT_INIT_LOW };
    
    static void ads1292_spi_xfer_complete(void *arg)
    {
    	struct ads1292_dev *ads = arg;
    	pr_debug("%s", __func__);
    	ads->rx_count = 1;
    	wake_up_interruptible(&ads->wait_queue);
    }
    
    static irqreturn_t ads1292_irq_handler(int irq, void *id)
    {
    	pr_debug("%s", __func__);
    	if (!ads_inst.sample_req)
    		return IRQ_HANDLED;
    	ads_inst.sample_req = 0;
    	spi_async(ads_inst.chip.spi, &ads_inst.chip.msg);
    	return IRQ_HANDLED;
    }
    
    static int ads1292_init_io_from_dt(struct device *dev, struct gpio *pgpio)
    {
    	int ret;
    
    	/* node to get the information of a device tree node*/
    	struct device_node *np = dev->of_node;
        
        	/* Get a GPIO number to use with GPIO API */
    	pgpio->gpio = of_get_named_gpio(np, pgpio->label, 0);
    	//printk(KERN_DEBUG "ads1292: %s: %d\n", pgpio->label, pgpio->gpio);
    
    	/* check if gpio is valid on the system */
    	if (!gpio_is_valid(pgpio->gpio)) {
    		pr_err("ads1292: No %s gpio", pgpio->label);
    		return -EINVAL;
    	} else {
            	/* request a single GPIO with initial setup */
    		ret = devm_gpio_request_one(dev, pgpio->gpio, pgpio->flags, pgpio->label);
    		if (ret < 0) {
    			pr_err("ads1292: Error requesting gpio %s:%d",
    				pgpio->label, pgpio->gpio);
    			return ret;
    		}
    	}
    
    	return 0;
    }
    
    static int ads1292_init_gpio_pins(struct ads1292_dev *ads, struct device *dev, struct spi_device *spi)
    {
    	int ret;
        	
    	if (ads->gpio_initialized == 0) {
    		
    		ret = ads1292_init_io_from_dt(dev, &ads1292_gpio_reset);
    		if (ret < 0)
    			goto error_exit;
    
    		ret = ads1292_init_io_from_dt(dev, &ads1292_gpio_start);
    		if (ret < 0)
    			goto error_exit;
    
    		if (spi->irq < 0) {
    			pr_err("ads1292: IRQ resource missing");
    			ret = -EINVAL;
    			goto error_exit;
    		} else {
     			pr_info("ads1292: IRQ: %d", spi->irq);
    		}
    	
    		ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, 
    				ads1292_irq_handler,
    				IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
    				"ads1292-drdy",
    				ads);
    		if (ret) {
    			pr_err("ads1292: Cannot claim IRQ: %d", spi->irq);
    			ret = -EINVAL;
    			goto error_exit;
    		}
    		ads->gpio_initialized = 1;
    	
    	}
    	return ads->gpio_initialized;
    
    error_exit:
    	ads->gpio_initialized = ret;
    	return ret;
    }
    
    
    static int ads_send_byte(struct ads1292_chip *chip, int data)
    {
    	struct spi_message msg = { };
    	struct spi_transfer transfer = { };
    	//struct device *dev = &chip->spi->dev;
    	int status;
    
        	/* zero each element in the structure and init transfer list */
    	spi_message_init(&msg);
    	chip->tx_buff[0] = data;
    	transfer.tx_buf = chip->tx_buff;
    	transfer.rx_buf = chip->rx_buff;
    	transfer.len = 1;
    	transfer.speed_hz = SPI_BUS_SPEED_SLOW;
        	/* enqueuing the transfer into the transfer list */
    	spi_message_add_tail(&transfer, &msg);
    	/* cann't be used in an atomic context as it can sleep */
        	status = spi_sync(chip->spi, &msg);
    	//dev_info(dev, "%s (%#x) %d\n", __func__, data, status);
    	return status;
    }
    
    static int ads_read_registers(struct ads1292_chip *chip, u8 reg, u8 size)
    {
    	struct spi_message msg = { };
    	struct spi_transfer transfer = { };
    	//struct device *dev = &chip->spi->dev;
    	int ret;
    
    	spi_message_init(&msg);
    	chip->tx_buff[0] = ADS1292_RREG | reg;
    	chip->tx_buff[1] = size - 1; 			// datasheet: no of regs - 1
    	memset(chip->rx_buff + 2, 0, size);
    	memset(chip->tx_buff + 2, 0, size);
    	transfer.speed_hz = SPI_BUS_SPEED_SLOW;
    	transfer.tx_buf = chip->tx_buff;
    	transfer.rx_buf = chip->rx_buff;
    	transfer.len = size + 2;			// 2 is for command tx
    	transfer.delay_usecs = 40; 			// datasheet: 4 clocks after each message
    	transfer.cs_change = 1; 			// Always toggle CS line after transfer
    	spi_message_add_tail(&transfer, &msg);
    	ret = spi_sync(chip->spi, &msg);
    	//dev_info(dev, "%s(%#x,%d) %#x .. -> %d\n",
    	//	__func__, reg, size, chip->rx_buff[2], ret);
    	if (ret<0) {
    		pr_err("%s - spi_sync failed!", __func__);
    		return ret;
    	}
    	return ret; 
    }
    
    static int ads_send_RREG(struct ads1292_chip *chip, char __user * buf, u8 reg, u8 size)
    {
    	int status;
    	status = ads_read_registers(chip, reg, size);
    	if (unlikely(status))
    		return status;
    	status = copy_to_user(buf, chip->rx_buff + 2, size);
    	return status;
    }
    
    static int ads_send_WREG(struct ads1292_chip *chip, char __user * buf, u8 reg, u8 size)
    {
    	struct spi_message msg = { };
    	struct spi_transfer transfer = { };
    	//struct device *dev = &chip->spi->dev;
    	int ret;
    	
    	spi_message_init(&msg);
    	chip->tx_buff[0] = ADS1292_WREG | reg;
    	chip->tx_buff[1] = size - 1;
    	ret = copy_from_user(chip->tx_buff + 2, buf, size);
    	if (unlikely(ret))
    		return ret;
    	transfer.speed_hz = SPI_BUS_SPEED_SLOW;
    	transfer.tx_buf = chip->tx_buff;
    	transfer.rx_buf = chip->rx_buff;
    	transfer.len = size + 2;
    	transfer.delay_usecs = 50;
    	transfer.cs_change = 1;
    	spi_message_add_tail(&transfer, &msg);
    	ret = spi_sync(chip->spi, &msg);
    	//dev_info(dev, "%s(%#x,%d) %#x .. -> %d\n",
    	//	__func__, reg, size, chip->tx_buff[2], ret);
    	if (ret<0) {
    		pr_err("%s - spi_sync failed!", __func__);
    		return ret;
    	}
    	return ret;
    }
    
    
    static loff_t ads_cdev_llseek(struct file *filp, loff_t off, int whence)
    {
    	pr_debug("ads1292: %s", __func__);
    	return 0;
    }
    
    static ssize_t ads_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
    {
    	struct ads1292_dev *ads = filp->private_data;
    	struct device *dev = &ads->chip.spi->dev;
    	int status, bytes_send=0;
    
    	if (*f_pos > 0)
    		return 0;
    
    	if (unlikely(down_interruptible(&ads->fop_sem)))
    		return -ERESTARTSYS;
    
    	dev_dbg(dev, "%s %ld", __func__, count);
    
    	ads->chip.tx_buff[0] = ADS1292_RDATA;
    	memset(ads->chip.tx_buff+1, 0, ACQ_BUF_SIZE);
    	memset(ads->chip.rx_buff+1, 0, ACQ_BUF_SIZE);
    	ads->chip.transfer.tx_buf = ads->chip.tx_buff;
    	ads->chip.transfer.rx_buf = ads->chip.rx_buff;
    	ads->chip.transfer.len = ACQ_BUF_SIZE + 1;
    	ads->chip.transfer.speed_hz = SPI_BUS_SPEED_FAST;
    	ads->chip.transfer.cs_change = 1;
    	spi_message_init(&ads->chip.msg);
    	spi_message_add_tail(&ads->chip.transfer, &ads->chip.msg);
    	ads->chip.msg.context = ads;
    	ads->chip.msg.complete = ads1292_spi_xfer_complete;
    
    	dev_dbg(dev, "Setting chip in RDATAC");
    	ads_send_byte(&ads->chip, ADS1292_RDATAC);
    	gpio_set_value_cansleep(ads1292_gpio_start.gpio, 1);
    	up(&ads->fop_sem);
    
    	while (bytes_send <= (count - ACQ_BUF_SIZE)) 
    	{
    		ads->rx_count = 0;
    		ads->sample_req = 1;
    		status = wait_event_interruptible_timeout(ads->wait_queue, ads->rx_count == 1, HZ*1);
    		ads->sample_req = 0;
    		if (unlikely(status <= 0))
    		{
    			if (status == 0)
    				status = -ETIMEDOUT;	// 1s without any data -> report timeout
    			goto read_error;
    		}
    		if (unlikely(copy_to_user(&buf[bytes_send], ads->chip.rx_buff, ACQ_BUF_SIZE)))
    		{
    			status = -EFAULT;
    			goto read_error;
    		}
    		bytes_send += ACQ_BUF_SIZE;
    	}
    
    	ads_send_byte(&ads->chip, ADS1292_SDATAC);
    	dev_dbg(dev, "Setting chip in SDATAC");
    	*f_pos += bytes_send;
    	return bytes_send;
    
    read_error:
    	return status;
    }
    
    static ssize_t ads_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
    {
    	//pr_info("ads1292: %s\n", __func__);
    	struct ads1292_dev *ads = filp->private_data;
    	struct device *dev = &ads->chip.spi->dev;
    	struct spi_message msg;
    	struct spi_transfer transfer;
    	int status = -ENODEV;
    
    	dev_dbg(dev, "%s %ld", __func__, count);
    
    	if (unlikely(down_interruptible(&ads->fop_sem)))
    		return -ERESTARTSYS;
    
    	if (count > SPI_BUFF_SIZE)
    		count = SPI_BUFF_SIZE;
    
    	if (unlikely(copy_from_user(ads->chip.tx_buff, buf, count)))
    	{
    		status = -EFAULT;
    	}
    	else
    	{
    		spi_message_init(&msg);
    		transfer.tx_buf = ads->chip.tx_buff;
    		transfer.rx_buf = ads->chip.rx_buff;
    		transfer.len = count;
    		transfer.speed_hz = SPI_BUS_SPEED_SLOW;
    		spi_message_add_tail(&transfer, &msg);
    		status = spi_sync(ads->chip.spi, &msg);
    		if (status == 0)
    		{
    			status = count;
    		}
    	}
    
    	up(&ads->fop_sem);
    	return status;
    }
    
    static long ads_cdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
    	struct ads1292_dev *ads = filp->private_data;
    	struct device *dev = &ads->chip.spi->dev;
    	int status, cmd_nr, cmd_size;
    	//dev_dbg(dev, "%s %#x\n", __func__, cmd);
    	
        	if (_IOC_TYPE(cmd) != ADS1292_IOC_MAGIC)
    	{
    		dev_err(dev, "failed to decode ioctl!");
    		return -ENOTTY;
    	}
    
    	if (dev == NULL)
    	{
    		dev_err(dev, "ioctl without device!");
    		return -ENODEV;
    	}
    
    	if (unlikely(down_interruptible(&ads->fop_sem)))
    		return -ERESTARTSYS;
    
    	cmd_nr = _IOC_NR(cmd) & 0x7F;
    	cmd_size = _IOC_SIZE(cmd) & 0x1F;
    	status = -ENODEV;
    	dev_dbg(dev, "%s %#x %d %#lx", __func__, cmd_nr, cmd_size, arg);
    
    	if (cmd_nr < ADS1292_RREG)
    	{
    		if ((cmd_nr & ADS1292_CMD_MASK) == ADS1292_SDATAC) 
    		{
    			dev_info(dev, "%s gpio start=0", __func__);
    			gpio_set_value_cansleep(ads1292_gpio_start.gpio, 0);
    		}
    		status = ads_send_byte(&ads->chip, cmd_nr);
    		if ((cmd_nr & ADS1292_CMD_MASK) == ADS1292_RDATAC) 
    		{
    			dev_info(dev, "%s gpio start=1", __func__);
    			gpio_set_value_cansleep(ads1292_gpio_start.gpio, 1);
    		}
    	}
    	else
    	{
    		if (gpio_get_value(ads1292_gpio_start.gpio))
    		{
    			dev_warn(dev, "Cannot change registers during aquisition, send SDATAC first");
    			status = -EBUSY;
    		}
    		else
    		{
    			switch (cmd_nr & (ADS1292_RREG | ADS1292_WREG))
    			{
    				case ADS1292_RREG:
    					dev_dbg(dev, "in RREG");
    					status = ads_send_RREG(&ads->chip, (char __user *)arg, cmd_nr, cmd_size);
    					break;
    				case ADS1292_WREG:
    					dev_dbg(dev, "in WREG");
    					status = ads_send_WREG(&ads->chip, (char __user *)arg, cmd_nr, cmd_size);		
    					break;
    				default:
    					dev_dbg(dev, "in default");
    					status = -ENOTTY;
    					break;
    			}
    		}
    	}
    
    	up(&ads->fop_sem);
    	return status;
    }
    
    static int ads_cdev_open(struct inode *inode, struct file *filp)
    {
    	struct ads1292_dev *ads = container_of(inode->i_cdev, struct ads1292_dev, dev);
    	struct device *dev = &ads->chip.spi->dev;
    	dev_dbg(dev, "%s", __func__);
    	if (down_interruptible(&ads->fop_sem))
    		return -ERESTARTSYS;
    	filp->private_data = ads;
    	/* stop data conversion */
    	ads_send_byte(&ads->chip, ADS1292_SDATAC);
    	up(&ads->fop_sem);	
    	return 0;
    }
    
    static int ads_cdev_release(struct inode *inode, struct file *filp)
    {
    	struct ads1292_dev *ads = filp->private_data;
    	struct device *dev = &ads->chip.spi->dev;
    	dev_dbg(dev, "%s", __func__);
    	if (down_interruptible(&ads->fop_sem))
    		return -ERESTARTSYS;
    	gpio_set_value_cansleep(ads1292_gpio_start.gpio, 0);
    	up(&ads->fop_sem);
    	return 0;
    }
    
    static const struct file_operations fops = {
    	.owner = THIS_MODULE,
    	.llseek = ads_cdev_llseek,
    	.read = ads_cdev_read,
    	.write = ads_cdev_write,
    	.unlocked_ioctl = ads_cdev_ioctl,
    	.open = ads_cdev_open,
    	.release = ads_cdev_release,
    };
    
    static int ads1292_init_cdev(struct ads1292_dev *ads)
    {
    	int ret;
    
    	/**
    	 * Allocates a range of char device numbers. The major number will be chosen dynamically,
    	 * and returned (along with the first minor number) in dev. Returns zero or a negative error code
    	 */
    	ret = alloc_chrdev_region( &ads->dev_no, 0, 1, ADS_DEVICE_NAME);
    	if (ret < 0) {
    		pr_err("ads1292: alloc_chrdev_region failed!");
    		goto error_exit;
    	}
    
        	/* creates a class directory under /sys/class/ */
    	ads->cl = class_create(THIS_MODULE, ADS_DEVICE_NAME);
    	if (ads->cl == NULL) {
    		pr_err("ads1292: Cannot create class!");
    		goto cleanup_region;
    	}
    
        	/* creates a device and registers it with sysfs */
    	if (device_create(ads->cl, NULL, ads->dev_no, NULL, ADS_DEVICE_NAME) == NULL) {
    		pr_err("ads1292: Cannot create character device!");
    		goto cleanup_class;
    	}
        
        	/* Initializes cdev, remembering fops, making it ready to add to the system with cdev_add */
    	cdev_init(&ads->dev, &fops );
    	ret = cdev_add(&ads->dev, ads->dev_no, 1);
    	if (ret < 0) {
    		pr_err("ads1292: Cannot add character device!");
    		goto cleanup_dev;
    	}
    	return 0;
    
    cleanup_dev:
    	device_destroy(ads->cl, ads->dev_no);
    cleanup_class:
    	class_destroy(ads->cl);
    cleanup_region:
    	unregister_chrdev_region(ads->dev_no, 1);
    error_exit:
    	return ret;
    }
    
    static int ads1292_probe(struct spi_device *spi)
    {
    	struct ads1292_chip *chip;
    	int ret;
    	char id;
    
    	chip = &ads_inst.chip;
    	chip->spi = spi;
    	mutex_lock(&ads_inst.mutex);
    
    	/**
    	 * resource-managed kzalloc - memory allocated with this function is automatically 
    	 * freed on driver detach. returns pointer to allocated memory on success, NULL on failure.
             */
    	chip->rx_buff = devm_kzalloc(&spi->dev, SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
    	if (chip->rx_buff == NULL) {
    		ret = -ENOMEM;
    		goto error_exit;
    	}
    	chip->tx_buff = devm_kzalloc(&spi->dev, SPI_BUFF_SIZE, GFP_KERNEL | GFP_DMA);
    	if (chip->tx_buff == NULL) {
    		ret = -ENOMEM;
    		goto error_exit;
    	}
    
    	ret = ads1292_init_gpio_pins(&ads_inst, &spi->dev, spi);
    	if (ret < 0)
    		goto error_exit;
    
    	/* read chip id read to check if chip present */
    	if (gpio_is_valid(ads1292_gpio_reset.gpio)) {
    		dev_info(&spi->dev, "sending reset pulse\n");
    		gpio_set_value_cansleep(ads1292_gpio_reset.gpio, 1);
    		gpio_set_value(ads1292_gpio_reset.gpio, 0);
    		udelay(10);
    		gpio_set_value(ads1292_gpio_reset.gpio, 1);
    	} else {
    		ads_send_byte(chip, ADS1292_RESET);
    	}
    	udelay(10);
    
    	ads_send_byte(chip, ADS1292_SDATAC);
    	ads_send_RREG(chip, &id, ADS1292_ID, sizeof(id));
    	if (id != 0x73) {
    		dev_err(&spi->dev, "probe failed! id = %#x", id);
    		return -ENODEV;
    	}
    
    	dev_info(&spi->dev, "probe successful");
    	mutex_unlock(&ads_inst.mutex);
    	return 0;
    
    error_exit:
    	mutex_unlock(&ads_inst.mutex);
    	return ret;
    }
    
    static int ads1292_remove(struct spi_device *spi)
    {
    	mutex_lock(&ads_inst.mutex);
    	mutex_unlock(&ads_inst.mutex);
    	pr_info("ads1292: remove successfully.");
    	return 0;
    }
    
    /* to match devices in the DT */
    static const struct of_device_id ads1292_of_match[] = {
    	{ .compatible = "ti,ads1292" },
    	{ }
    };
    MODULE_DEVICE_TABLE(of, ads1292_of_match);
    
    /**
     * declare a device ids supported by this driver
     * in order to inform the SPI core about the device ID
     * we need to manage in the driver, and 
     * call the MODULE_DEVICE_TABLE macro on the driver structure
     */
    static const struct spi_device_id ads1292_id[] = {
    	{"ADS1292", 0},	/* {name, driver_data} */
    	{ }
    };
    MODULE_DEVICE_TABLE(spi, ads1292_id);
    
    /* represents the driver developed to manage our SPI device */
    static struct spi_driver ads1292_driver = {
    	.driver = {
    		.name	= "ads1292",
    		.of_match_table = ads1292_of_match,
    		.owner	= THIS_MODULE,
    	},
    	.probe		= ads1292_probe,
    	.remove		= ads1292_remove,
    	.id_table	= ads1292_id,
    };
    
    static int __init ads1292_init(void)
    {
    	int ret;
    	
    	/* init mutex */
    	mutex_init(&ads_inst.mutex);
    	/* init semaphore(s) */
    	sema_init(&ads_inst.fop_sem, 1);
    
    	ads_inst.gpio_initialized = 0;
    	ads_inst.chip.spi = NULL;
    	ads_inst.sample_req = 0;
    
    	ret = ads1292_init_cdev(&ads_inst);
    	if (ret < 0) {
    		pr_err("%s cdev init failed!", __func__);
    		goto error_exit;
    	}
    
    	init_waitqueue_head(&ads_inst.wait_queue);
    
    	ret = spi_register_driver(&ads1292_driver);
    	if (ret < 0) {
    		pr_err("ads1292: spi driver registration failed!");
    		goto error_exit;
    	}
    	pr_info("%s successfully", __func__);
    	return 0;
    
    error_exit:
    	return ret;
    }
    subsys_initcall(ads1292_init);
    
    static void __exit ads1292_exit(void)
    {
    	devm_free_irq(&ads_inst.chip.spi->dev, ads_inst.irq, NULL);
    	cdev_del(&ads_inst.dev);
    	device_destroy(ads_inst.cl, ads_inst.dev_no);
    	class_destroy(ads_inst.cl);
    	unregister_chrdev_region(ads_inst.dev_no, 1);
    	spi_unregister_driver(&ads1292_driver);
    }
    module_exit(ads1292_exit);
    
    MODULE_AUTHOR("Vijay Prajapati <vijay.prajapati@teksun.com>");
    MODULE_DESCRIPTION("TI ADS1292R - Low-Power, 2-Channel, 24-Bit AFE for Biopotential Measurements");
    MODULE_LICENSE("GPL v2");
    

  • Hi Vijay,

    Sorry, but I am unable to support the development and debug of the Linux driver. Please reach out to the Linux community for any suggestions.

    Thanks

    -TC