Драйвер LRADC - датчик температуры на внешнем диоде
Pavel Ivanchenko
Admin
Пункты: 92788
Регистрация: 24.03.2009
Пол: Мужчина
sasamy пишет:
Цитата Чудесный форум не дает создавать новые сообщения :) напишу в старой теме, касается lradc - написал небольшой драйвер для измерения температуры при помощи простого диода 1n4148. Как известно сопротивление p-n перехода в прямом направлении прямо пропорционально температуре а у i.mx233 на физических каналах 0 и 1 есть встроенные программируемые источники тока, в даташите есть формула по которой можно посчитать температуру, исходя из нее и сделано все. Драйер для hwmon так что будет виден как датчик в lm-sensors. Точность невелика, поэтому усреднил большое количесво выборок - 64 (8 значений напряжения для каждого из 8 значений температуры которые устредняются), разброс значений получается порядка 2 град. Диод подключается на разъем Х6: 11 - анод , 1 - катод. Нужно будет калибровать для каждого конкретного случа, например у меня показывает больше чем реальная темепература градусов на 6-7.
Код
/*
* i.mx233 external temperature sensing with a diode
*
* 2010 Alexander Kudjashev
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/hwmon.h>
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <asm/processor.h> /* cpu_relax */
#include <mach/hardware.h>
#include <mach/lradc.h>
#include <mach/regs-power.h>
#include <mach/regs-lradc.h>
#define REGS_LRADC_BASE IO_ADDRESS(LRADC_PHYS_ADDR)
#define TERM_CHANNEL 0
#define LRADC_IRQ_MASK (1 << TERM_CHANNEL)
#define KELVIN_TO_CELSIUS_CONST (273*1000)
struct mxsterm {
struct device *hwmon_dev;
struct mutex lock;
};
static uint32_t get_codevalue(void)
{
int i;
uint32_t codevalue;
codevalue = 0;
for (i = 0; i < 8; i++) {
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << TERM_CHANNEL),
REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
/* Wait for conversion complete*/
while (!(__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1)
& LRADC_IRQ_MASK))
cpu_relax();
/* Clear the interrupt flag */
__raw_writel(LRADC_IRQ_MASK,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/* read temperature value and clr lradc */
codevalue += __raw_readl(REGS_LRADC_BASE +
HW_LRADC_CHn(TERM_CHANNEL)) & BM_LRADC_CHn_VALUE;
__raw_writel(BM_LRADC_CHn_VALUE,
REGS_LRADC_BASE + HW_LRADC_CHn_CLR(TERM_CHANNEL));
}
return (codevalue >> 3);
}
/*
* Use the the lradc0 channel
*
*/
static int measure_temperature(void)
{
int code1, code2, ret_temp, i;
/* Enable temperature sensor current source */
__raw_writel(BM_LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);
/* mux to the lradc 0th temp channe0 */
__raw_writel((0xF << (4 * TERM_CHANNEL)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
/* Clear the interrupt flag */
__raw_writel(LRADC_IRQ_MASK,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
ret_temp = 0;
for (i = 0; i < 8; i++) {
__raw_writel(BM_LRADC_CTRL2_TEMP_ISRC0,
REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR);
__raw_writel(BF_LRADC_CTRL2_TEMP_ISRC0(BV_LRADC_CTRL2_TEMP_ISRC0__300),
REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);
code1 = get_codevalue();
__raw_writel(BM_LRADC_CTRL2_TEMP_ISRC0,
REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR);
__raw_writel(BF_LRADC_CTRL2_TEMP_ISRC0(BV_LRADC_CTRL2_TEMP_ISRC0__20),
REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);
code2 = get_codevalue();
/* degrees Kelvin = (Codemax – Codemin) * 1.104 */
ret_temp += (code1 - code2) * 1104;
}
/* Disable temperature sensor current source */
__raw_writel(BM_LRADC_CTRL2_TEMP_SENSOR_IENABLE0,
REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR);
return ((ret_temp >> 3) - KELVIN_TO_CELSIUS_CONST);
}
static ssize_t mxsterm_sense_temp(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mxsterm *mxsterm_data = dev_get_drvdata(dev);
int status;
int val;
if (mutex_lock_interruptible(&mxsterm_data->lock))
return -ERESTARTSYS;
val = measure_temperature();
status = sprintf(buf, "%d\n", val);
mutex_unlock(&mxsterm_data->lock);
return status;
}
static DEVICE_ATTR(temp1_input, S_IRUGO, mxsterm_sense_temp, NULL);
static ssize_t mxsterm_show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
return sprintf(buf, "%s\n", "mxsterm");
}
static DEVICE_ATTR(name, S_IRUGO, mxsterm_show_name, NULL);
static int __init mxsterm_probe(struct platform_device *pdev)
{
struct mxsterm *mxsterm_data;
int status;
mxsterm_data = kzalloc(sizeof *mxsterm_data, GFP_KERNEL);
if (!mxsterm_data)
return -ENOMEM;
mutex_init(&mxsterm_data->lock);
/* sysfs hook */
mxsterm_data->hwmon_dev = hwmon_device_register(&pdev->dev);
if (IS_ERR(mxsterm_data->hwmon_dev)) {
dev_dbg(&pdev->dev, "hwmon_device_register failed.\n");
status = PTR_ERR(mxsterm_data->hwmon_dev);
goto out_dev_reg_failed;
}
platform_set_drvdata(pdev, mxsterm_data);
if ((status = device_create_file(&pdev->dev, &dev_attr_temp1_input))
|| (status = device_create_file(&pdev->dev, &dev_attr_name))) {
dev_dbg(&pdev->dev, "device_create_file failure.\n");
goto out_dev_create_file_failed;
}
return 0;
out_dev_create_file_failed:
device_remove_file(&pdev->dev, &dev_attr_temp1_input);
hwmon_device_unregister(mxsterm_data->hwmon_dev);
out_dev_reg_failed:
platform_set_drvdata(pdev, NULL);
kfree(mxsterm_data);
return status;
}
static int mxsterm_remove(struct platform_device *pdev)
{
struct mxsterm *mxsterm_data = platform_get_drvdata(pdev);
hwmon_device_unregister(mxsterm_data->hwmon_dev);
device_remove_file(&pdev->dev, &dev_attr_temp1_input);
device_remove_file(&pdev->dev, &dev_attr_name);
platform_set_drvdata(pdev, NULL);
kfree(mxsterm_data);
return 0;
}
static struct platform_driver mxsterm_driver = {
.driver = {
.name = "mxsterm",
},
.probe = mxsterm_probe,
.remove = mxsterm_remove,
};
static struct platform_device *mxsterm_device;
static int __init mxsterm_init(void)
{
int ret = 0;
ret = platform_driver_register(&mxsterm_driver);
if (!ret) {
mxsterm_device = platform_device_alloc("mxsterm", 0);
if (mxsterm_device)
ret = platform_device_add(mxsterm_device);
else
ret = -ENOMEM;
if (ret) {
platform_device_put(mxsterm_device);
platform_driver_unregister(&mxsterm_driver);
}
}
return ret;
}
static void __exit mxsterm_exit(void)
{
platform_device_unregister(mxsterm_device);
platform_driver_unregister(&mxsterm_driver);
}
module_init(mxsterm_init);
module_exit(mxsterm_exit);
MODULE_AUTHOR("Alexander Kudjashev");
MODULE_DESCRIPTION("External temperature sensing with a diode");
MODULE_LICENSE("GPL");
snacker
Пункты: 300
Регистрация: 09.01.2010
Пол: Мужчина
Павел, я новичок в программировании под Линукс. Не подскажете, как я могу использовать ваш драйвер в своем приложении?
Я пишу свой код с функцией main, в которой мне, к примеру, нужно узнать температуру, что я должен сделать? Какие заголовочные файлы включать? И ваш драйвер в каком виде должен быть - отдельно компилиться или его нужно будет включать в проект?
Pavel Ivanchenko
Admin
Пункты: 92788
Регистрация: 24.03.2009
Пол: Мужчина
Я не являюсь автором драйвера (скажем спасибо sasamy ).
Включите его в ядро, по аналогии - положите в соответствующую папку с драйверами, добавте его сборку в makefile и добавте его активацию в konfig, далее через menuconfig включите его в ядро и пересоберите систему.
Как его в собственном приложении использовать, вариантов много можно придумать:
"Правильный"
Посмотреть как lm-sensors опрашивает и пересчитывает датчики и сдеоать по аналогии.
"Чайниковый №1"
Запустить из своей апликухи lm-sensors, вывод которой перенаправить в файл, потом вычленить температуру из лога.
...
snacker
Пункты: 300
Регистрация: 09.01.2010
Пол: Мужчина
Я так понимаю, что если я пойду по первому пути - напишу код по аналогии, то мне не надо будет ничего включать в ядро.
Pavel Ivanchenko
Admin
Пункты: 92788
Регистрация: 24.03.2009
Пол: Мужчина
В любом случае нужно будет включить драйвер в ядро (lm-sensors от драйвера данные берет).
Jury093
Пункты: 54271
Регистрация: 25.05.2009
Пол: Мужчина
Из: Санкт-Петербург
Цитата Я так понимаю, что если я пойду по первому пути - напишу код по аналогии, то мне не надо будет ничего включать в ядро.
В данном случае драйвер - это некая прокладка между железом (датчиком, диодом) и стандартными программами, которые знать не знают о таком железе и черпают информацию через драйвер.
Если в своей программе вы подмените на свои функции драйвера Александра - настройку GPIO, работу с необходимой периферией АРМа, опрос датчика. То да, можно драйвер не собирать.
В этом случае вы получите жесткую связку из железа и своей проги без возможности использовать сторонние утилиты..
На любой вопрос есть любой ответ.
sasamy
Пункты: 83542
Регистрация: 14.08.2009
Цитата
Я пишу свой код с функцией main, в которой мне, к примеру, нужно узнать температуру, что я должен сделать?
Если конкретно для этого датчика - доcтаточно прочитать через sysfs известный файл. В общем случае lm-sensors сканирует налчие датчиков - по какому принципу я не интересовался. Для этого драйвера например в командной строке (значение будет в миллиградусах)
Код
#cat /sys/class/hwmon/hwmon0/device/temp1_input
Или из своей программы
Код
#include <stdio.h>
int main(void)
{
FILE *f;
int val;
f = fopen("/sys/class/hwmon/hwmon0/device/temp1_input", "r");
if (f == NULL) {
fprintf (stderr, "Cannot open file\n");
return 1;
}
if (EOF != fscanf(f, "%d", &val))
printf ("the approximated value %d C\n", val / 1000);
else
printf ("read error\n");
return 0;
}
snacker
Пункты: 300
Регистрация: 09.01.2010
Пол: Мужчина
Цитата Если в своей программе вы подмените на свои функции драйвера Александра - настройку GPIO, работу с необходимой периферией АРМа, опрос датчика. То да, можно драйвер не собирать.
Jury093 , я так и подумал.
sasamy , спасибо.
sasamy
Пункты: 83542
Регистрация: 14.08.2009
Цитата Я так понимаю, что если я пойду по первому пути - напишу код по аналогии, то мне не надо будет ничего включать в ядро.
А завтра понадобится более точное измерение температуры - поставите туда датчик например с i2c интерфейсом и будете весь код переписывать, тогда как используя lm-sensors нужно будет поменять пару строк в программе, или захочется на другом микроконтроллере сделать. OC предоставляет уровень абстракции над "железом" - используя его абсолютно неважно как подключен и даже на какой архитектуре все сделано.
Jury093
Пункты: 54271
Регистрация: 25.05.2009
Пол: Мужчина
Из: Санкт-Петербург
Цитата OC предоставляет уровень абстракции над "железом" - используя его абсолютно неважно как подключен и даже на какой архитектуре все сделано.
Ja-ja, их бин натюрлих, Саш, ты прав на все 100. Я использовал слегка другие слова
Код В этом случае вы получите жесткую связку из железа и своей проги без возможности использовать сторонние утилиты..
Но вопрошавшему может нет нужды ковырять линух/ядро, а есть желание написать что-то простейшее под свои нужды.
Не помню, писал тут или нет - спасибо тебе что разрабатываешь и выкладываешь исходники - есть чему у тебя поучиться..
На любой вопрос есть любой ответ.