IIO子系统二 具有硬件触发功能的IIO子系统ADC模块

  • 十八、IIO子系统(二) 具有硬件触发功能的IIO子系统ADC模块
    • 18.1 简介
    • 18.2 设备树
    • 18.3 硬件触发驱动功能分析
      • 18.3.1 睡眠和唤醒
      • 18.3.2 中断管理
    • 18.4 带睡眠的硬件触发ADC代码
    • 18.5 应用代码
    • 18.6 调试

十八、IIO子系统(二) 具有硬件触发功能的IIO子系统ADC模块

18.1 简介

由于17章没有介绍完IIO子系统,本章继续介绍具备硬件触发功能的IIO子系统的ADC模块,ADC转换由硬件触发器启动。将使用按键产生重大触发ADC转换,还需要一个等待队列在进程和中断上下文之间进行同步。
每当按下按键时,产生重大将唤醒用户进程,驱动的读回调函数会把ADC值发送到用户。

18.2 设备树

连接按键的GPIO引脚增加一个int-gpio属性,还需要增加一个pinctrl-0属性,该属性指向一个引脚配置节点,用于将处理器的一个引脚复用为GPIO,将引脚方向设置为输入,并获取引脚对应的Linux中断号。reg属性值是片选信号的CS值。

             spi3: spi@400 {compatible = "atmel,at91rm9200-spi";reg = <0x400 0x200>;interrupts = <23 IRQ_TYPE_LEVEL_HIGH 7>;clocks = <&flx4_clk>;clock-names = "spi_clk";#address-cells = <1>;#size-cells = <0>;pinctrl-names = "default";pinctrl-0 = <&pinctrl_mikrobus_spi &pinctrl_mikrobus1_spi_cs &pinctrl_mikrobus2_spi_cs>;atmel,fifo-size = <16>;status = "okay"; /* Conflict with uart6 and i2c3. *//* ADC: ltc2422@0 {compatible = "arrow,ltc2422";spi-max-frequency = <2000000>;reg = <0>;pinctrl-0 = <&pinctrl_key_gpio_default>;int-gpios = <&pioA PIN_PA29 GPIO_ACTIVE_LOW>;interrupt-parent = <&pioA>;interrupts = <29  IRQ_TYPE_EDGE_FALLING>;}; */};

18.3 硬件触发驱动功能分析

18.3.1 睡眠和唤醒

睡眠和唤醒主要步骤:
1、linux定义的等待队列定义了wait_queue_head_t结构来管理,如下:

linux/linux/wait.h

定义结构如下:

struct ADC_data {struct gpio_desc    *gpio;int irq;wait_queue_head_t wq_data_available;struct spi_device     *spi;u8 buffer[4];bool          conversion_done;struct mutex        lock;
};

在probe()函数进行动态初始化。

用户进程设置睡眠状态,它会假设将来某些条件会变成真。在Linux内核中最简单的一种睡眠方法是调用wait_event宏,它将处理睡眠细节和检查进程等待条件结合在一起。你将在驱动ltc2422_read_raw()函数里调用,如下:

wait_event_interruptible(st->wq_data_available, st->conversion_done);   //变体

只有中断处理程序通知ADC转换开始,即条件为真,wait_event_interruptible才会去唤醒进程。
你将在中断处理函数中唤醒进程,如下:

static irqreturn_t ltc2422_adc_interrupt(int irq, void *data)
{static unsigned long last;unsigned long now;struct ADC_data *st = data;now = jiffies;if ((now - last) < (HZ/10))return IRQ_HANDLED;last = now;st->conversion_done = true;wake_up_interruptible(&st->wq_data_available);return IRQ_HANDLED;
}

18.3.2 中断管理

在probe()函数里,将使用以下函数获取int-gpio属性中的GPIO描述符,如下;

devm_gpiod_get_index(&spi->dev, LTC2422_GPIO_NAME, 0, GPIOD_IN);

然后将描述符参数传递给以下函数,来获取Linux中断号,如下:

gpiod_to_irq(st->gpio);

申请中断号函数,如下:

devm_request_irq(&spi->dev, st->irq, ltc2422_adc_interrupt,IRQF_TRIGGER_FALLING, id->name, st);  //st是私有数据指针

18.4 带睡眠的硬件触发ADC代码

#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#include <linux/of_gpio.h>
#include <linux/iio/iio.h>
#include <linux/wait.h>#define LTC2422_GPIO_NAME  "int"struct ADC_data {struct gpio_desc    *gpio;int irq;wait_queue_head_t wq_data_available;struct spi_device     *spi;u8 buffer[4];bool          conversion_done;struct mutex        lock;
};static irqreturn_t ltc2422_adc_interrupt(int irq, void *data)
{static unsigned long last;unsigned long now;struct ADC_data *st = data;now = jiffies;if ((now - last) < (HZ/10))return IRQ_HANDLED;last = now;st->conversion_done = true;wake_up_interruptible(&st->wq_data_available);return IRQ_HANDLED;
}static const struct iio_chan_spec ltc2422_channel[] = {{.type      = IIO_VOLTAGE,.indexed = 1,.output        = 1,.channel   = 0,.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
}};static int ltc2422_read_raw(struct iio_dev *indio_dev,struct iio_chan_spec const *chan, int *val, int *val2, long m)
{int ret;struct ADC_data *st = iio_priv(indio_dev);dev_info(&st->spi->dev, "Press PB_USER key to start conversion\n");switch (m) {case IIO_CHAN_INFO_RAW:mutex_lock(&st->lock);ret = wait_event_interruptible(st->wq_data_available, st->conversion_done);if (ret) {dev_err(&st->spi->dev, "Failed to request interrupt\n");return ret;}spi_read(st->spi, &st->buffer, 3);*val  = st->buffer[0] << 16;*val |= st->buffer[1] << 8;*val |= st->buffer[2];st->conversion_done = false;mutex_unlock(&st->lock);return IIO_VAL_INT;default:break;}return -EINVAL;
}static const struct iio_info ltc2422_info = {.read_raw = &ltc2422_read_raw,.driver_module = THIS_MODULE,
};static int ltc2422_probe(struct spi_device *spi)
{struct iio_dev *indio_dev;struct ADC_data *st;int ret;dev_info(&spi->dev, "my_probe() function is called.\n");/* get the id from the driver structure to use the name */const struct spi_device_id *id = spi_get_device_id(spi);indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));if (indio_dev == NULL)return -ENOMEM;st = iio_priv(indio_dev); st->spi = spi;spi_set_drvdata(spi, indio_dev);/* * you can also use* devm_gpiod_get(&spi->dev, LTC2422_GPIO_NAME, GPIOD_IN);*/st->gpio = devm_gpiod_get_index(&spi->dev, LTC2422_GPIO_NAME, 0, GPIOD_IN);if (IS_ERR(st->gpio)) {dev_err(&spi->dev, "gpio get index failed\n");return PTR_ERR(st->gpio);}st->irq = gpiod_to_irq(st->gpio);if (st->irq < 0)return st->irq;dev_info(&spi->dev, "The IRQ number is: %d\n", st->irq);indio_dev->dev.parent = &spi->dev;indio_dev->channels = ltc2422_channel;indio_dev->info = &ltc2422_info;indio_dev->name = id->name;indio_dev->num_channels = 1;indio_dev->modes = INDIO_DIRECT_MODE;init_waitqueue_head(&st->wq_data_available);mutex_init(&st->lock);ret = devm_request_irq(&spi->dev, st->irq, ltc2422_adc_interrupt,IRQF_TRIGGER_FALLING, id->name, st);if (ret) {dev_err(&spi->dev, "failed to request interrupt %d (%d)", st->irq, ret);return ret;}ret = devm_iio_device_register(&spi->dev, indio_dev);if (ret < 0)return ret;st->conversion_done = false;return 0;
}static int ltc2422_remove(struct spi_device *spi)
{dev_info(&spi->dev, "my_remove() function is called.\n");return 0;
}static const struct of_device_id ltc2422_dt_ids[] = {{ .compatible = "arrow,ltc2422", },{ }
};
MODULE_DEVICE_TABLE(of, ltc2422_dt_ids);static const struct spi_device_id ltc2422_id[] = {{ .name = "ltc2422", },{ }
};
MODULE_DEVICE_TABLE(spi, ltc2422_id);static struct spi_driver ltc2422_driver = {.driver = {.name  = "ltc2422",.owner   = THIS_MODULE,.of_match_table = ltc2422_dt_ids,},.probe       = ltc2422_probe,.remove        = ltc2422_remove,.id_table = ltc2422_id,
};module_spi_driver(ltc2422_driver);MODULE_AUTHOR(" ");
MODULE_DESCRIPTION("LTC2422 DUAL ADC with triggering");
MODULE_LICENSE("GPL");

18.5 应用代码

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>int8_t read_adc();/* The LTC2422 least significant bit value with 5V full-scale */
float LTC2422_lsb = 4.7683761E-6;  /* The LTC2422 least significant bit value with 3.3V full-scale */
/* float LTC2422_lsb = 3.1471252E-6; *//* check which number is the ADC iio:deviceX and replace x by the number */
#define LTC2422_FILE_VOLTAGE    "/sys/bus/iio/devices/iio:device2/out_voltage0_raw"
#define SPI_DATA_CHANNEL_OFFSET 22
#define SPI_DATA_CHANNEL_MASK   (1 << SPI_DATA_CHANNEL_OFFSET)
#define LTC2422_CONVERSION_TIME     137 /* ms *//* * Returns the Data and Channel Number(0- channel 0, 1-Channel 1)* Returns the status of the SPI read. 0=successful, 1=unsuccessful.*/
int8_t LTC2422_read(uint8_t *adc_channel, int32_t *code);/* Returns the Calculated Voltage from the ADC Code */
float LTC2422_voltage(uint32_t adc_code, float LTC2422_lsb);int8_t LTC2422_read(uint8_t *adc_channel, int32_t *code)
{int a2dReading = 0;FILE *f = fopen(LTC2422_FILE_VOLTAGE, "r");int read = fscanf(f, "%d", &a2dReading);if (read <= 0) {printf("ERROR: Unable to read values from voltage input file.\n");exit(-1);}/* Determine the channel number */*adc_channel = (a2dReading & SPI_DATA_CHANNEL_MASK) ? 1 : 0;*code = a2dReading;fclose(f);return(0);
}/* Returns the Calculated Voltage from the ADC Code */
float LTC2422_voltage(uint32_t adc_code, float LTC2422_lsb)
{float adc_voltage;if (adc_code & 0x200000){adc_code &= 0xFFFFF; /* Clears Bits 20-23 */adc_voltage=((float)adc_code)*LTC2422_lsb;}else{adc_code &= 0xFFFFF; /* Clears Bits 20-23 */adc_voltage = -1*((float)adc_code)*LTC2422_lsb;}return(adc_voltage);
}void delay(unsigned int ms)
{usleep(ms*1000);
}int8_t read_adc()
{float adc_voltage;int32_t adc_code;uint8_t adc_channel;int32_t  adc_code_array;           int8_t return_code;int a2dReading = 0;LTC2422_read(&adc_channel, &adc_code);delay(LTC2422_CONVERSION_TIME);LTC2422_read(&adc_channel, &adc_code);adc_voltage = LTC2422_voltage(adc_code, LTC2422_lsb);printf("the value of ADC channel %d\n", adc_channel);printf("     is : %6.4f\n", adc_voltage);delay(LTC2422_CONVERSION_TIME);LTC2422_read(&adc_channel, &adc_code);adc_voltage = LTC2422_voltage(adc_code, LTC2422_lsb);printf("the value of ADC channel %d\n", adc_channel);printf("     is : %6.4f\n", adc_voltage);return(0);
}int main(void)
{read_adc();printf("Application termined\n");return 0;
}

18.6 调试

insmod ltc2422_imx_trigger.ko
./LTC2422_apprmmod ltc2422_imx_trigger.ko

感谢阅读,祝君成功!
-by aiziyou

嵌入式Linux设备驱动程序开发指南18(IIO子系统(二)具有硬件触发功能的IIO子系统ADC模块)——读书笔记相关推荐

  1. 嵌入式Linux设备驱动程序开发指南17(IIO子系统一)——读书笔记

    IIO子系统一 十七.IIO子系统(一) 17.1 简介 17.2 数模转换--DAC实验 17.2.1 IIO缓冲区 17.2.2 触发器 17.2.3 工业I/O事件 17.2.4 iio工具 1 ...

  2. 嵌入式Linux设备驱动程序开发指南9(平台设备驱动)——读书笔记

    平台设备驱动 九.平台设备驱动 9.1 平台设备驱动概述 9.2 GPIO驱动 9.2.1 简介 9.2.2 硬件名称 9.2.3 引脚控制器 9.2.4 引脚控制子系统 9.2.5 GPIO控制器驱 ...

  3. 嵌入式Linux设备驱动程序开发指南14(Linux设备驱动使用DMA)——读书笔记

    Linux设备驱动使用DMA 十四.Linux设备驱动使用DMA 14.1 简介 14.2 缓存一致性 14.3 DMA控制器接口 14.4 流式DMA模块 14.4.1 sdma_sam_m2m.c ...

  4. 嵌入式Linux设备驱动程序开发指南3(构建Microchip SAMA5D2嵌入式 Linux系统)——读书笔记

    构建Microchip SAMA5D2嵌入式 Linux系统 三.构建Microchip SAMA5D2嵌入式 Linux系统 3.1 获取驱动代码 3.2 配置编译 3.2.1 bootstrap编 ...

  5. 嵌入式Linux设备驱动程序开发指南20(Linux USB设备驱动)——读书笔记

    Linux USB设备驱动 二十.Linux USB设备驱动 20.1 USB简介 20.1.1 USB2.0总线拓扑 20.1.2 USB总线枚举和设备布局 20.1.3 USB数据传输 20.1. ...

  6. 嵌入式Linux设备驱动程序:在运行时读取驱动程序状态

    嵌入式Linux设备驱动程序:在运行时读取驱动程序状态 Embedded Linux device drivers: Reading driver state at runtime 在运行时了解驱动程 ...

  7. VxWorks设备驱动程序开发指南---驱动程序的分类

    8D Spaces Reliability & Stability & Efficiency 目录视图 摘要视图 订阅 VxWorks设备驱动程序开发指南(三)---驱动程序的分类 2 ...

  8. 嵌入式Linux设备驱动程序:用户空间中的设备驱动程序

    嵌入式Linux设备驱动程序:用户空间中的设备驱动程序 Embedded Linux device drivers: Device drivers in user space Interfacing ...

  9. 《精通Linux设备驱动程序开发》——1.5 Linux发行版

    本节书摘来自异步社区<精通Linux设备驱动程序开发>一书中的第1章,第1.5节,作者:[印]Sreekrishnan Venkateswaran(斯里克里斯汉 温卡特斯瓦兰)著,更多章节 ...

最新文章

  1. 正确修改MySQL最大连接数的三种好用方案
  2. 图像检索:几类基于内容的图像分类技术
  3. python: 多线程实现的两种方式及让多条命令并发执行
  4. 换种方法学操作系统,轻松入门Linux内核
  5. 基于h5的跳一跳游戏的开发与实现_「南宁小程序开发」企业开发小程序有哪些好处?...
  6. (转)生产者/消费者问题的多种Java实现方式 (待整理)
  7. kafka入门之broker--日志存储设计
  8. NULL的陷阱:Merge
  9. 取模运算性质_求余、取模运算在RTOS中计算优先级的理解
  10. 2019年python黑马_决心在2019年让Python成为您的朋友
  11. 唯美红色圣诞节背景素材,节日气氛尽显
  12. 实验五 单表查询(V2.0版)
  13. 51NOD 1185 威佐夫游戏 V2(威佐夫博弈)
  14. c# export server 调用sql_C# 如何调用 SPL 脚本
  15. 火狐一键检测自己的邮箱是否被泄露信息 输入邮箱即可查看是否安全
  16. 中兴力维动环监控_深圳中兴力维技术有限公司
  17. 【Direct3D】纹理过滤
  18. Java反序列化漏洞:在受限环境中从漏洞发现到获取反向Shell
  19. 计算机编辑学,计算机常识及电文档编辑学习.doc
  20. redis高可用:keepalived+redis主从部署

热门文章

  1. html实时获取树莓派温湿度,在树莓派上获取美国天气预报
  2. python中def main是什么意思_Python中’__main__’模块的作用
  3. 天津大学计算机学院博士招生目录,天津大学计算机科学与技术学院考博招生人数和专业.pdf...
  4. CTO们接着吼:创业公司几乎全是坑!
  5. 一个执念重度患者的自白
  6. MPEG-4与H264区别,编码及应用
  7. ARM嵌入式开发1:keil软件安装
  8. [bzoj2563] 阿狸和桃子的游戏 贪心
  9. EMW3031下模拟I2C实现
  10. pmu2008终端服务器,PMU升级指导.doc