Hello All,
I'm using the processor imx28 with ltib linux, and I am with difficult to use the ADC of the processor.
In the Freescale document i.MX28_Linux_BSP_RM.pdf in chapter 18, they talk about and ADC Touch Screen Drivers, however, the driver imx_adc_ts.c is not build in linux build. Im try to setect this driver but this is a MSX family dependence.
Can someone help me make the acquisition of ADC IMX28?
Thank you.
已解决! 转到解答。
Hi Raul,
Here, the steps to add Touch screen driver to your rootfs.
$ cd (path of the ltib)
$ ./ltib -m config
Select Package list --->
Select TSLIB
Exit
Exit
Yes
$ ./ltib
$./ltib -p boot_stream.spec -f
Thank you,
RAM
At the end I wrote a minimal kernel module (char device) installed in /dev/my-mxs-lradc (install this via insmod - I used template module in LTIB).
In this module I read LRADC registers when I open this virtual file; when I read this file (for exaple with cat command) ai put all LRADC values in an "INI like" format:
LRAD_chx=val
LRAD_chy=val
This is my first Linux driver...it isn't a good programming example ad I don't know if it is all ok...but it runs for me.
Source (I am not able to uplad file in this post...):
#include <linux/module.h> /* Needed by all modules */
#include <linux/init.h> /* Needed for the macros */
#include <linux/kernel.h> /* printk() */
#include <linux/slab.h> /* kmalloc() */
#include <linux/fs.h> /* everything... */
#include <linux/errno.h> /* error codes */
#include <linux/types.h> /* size_t */
#include <linux/proc_fs.h>
#include <linux/fcntl.h> /* O_ACCMODE */
#include <linux/seq_file.h>
#include <linux/cdev.h>
#include <asm/system.h> /* cli(), *_flags */
#include <asm/uaccess.h> /* copy_*_user */
#include <linux/platform_device.h>
#include <mach/hardware.h>
#include <mach/lradc.h>
#include <mach/device.h>
#include <mach/regs-lradc.h>
#include <linux/imx_adc.h>
#include <linux/err.h>
#include <linux/delay.h>
/* major number of device */
static int gMajor;
static struct class *my_lradc_class;
//extern int hw_lradc_use_channel(int);
extern u32 hw_lradc_vddio(void);
static char myBuff[100] = {0};
//static u32 myhw_lradc_vddio(void);
//static struct ACQ_nxc_adc adcConversion = {0};
#define GAIN_CORRECTION 1012 /* 1.012 */
#define REGS_LRADC_BASE IO_ADDRESS(LRADC_PHYS_ADDR)
/*
mknod /dev/nome_che_vuoi c major minor (nota: minor = 0)
*/
struct my_mxs_lradc_dev {
struct cdev cdev; /* Char device structure */
};
struct my_mxs_lradc_dev *lradc_device = NULL;
static int my_mxs_lradc_major = 0;
static int my_mxs_lradc_minor = 0;
static uint16_t MeasureInternalDieTemperature(void);
static void hw_load_adc(int *vCANBus,int *vACQBus);
static void say_hello(void)
{
printk("\nmy-imx28-lradc installed\n");
}
static void say_goodbye(void)
{
printk("\nmy-imx28-lradc removed\n");
}
static void hw_load_adc(int *vCANBus,int *vACQBus)
{
unsigned int ctrl4_backup,ctrl2_backup;
/* backup dei registri */
ctrl4_backup = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL4);
ctrl2_backup = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL2);
/* imposto i virtual channel come di default */
__raw_writel(BM_LRADC_CTRL4_LRADC6SELECT | BM_LRADC_CTRL4_LRADC1SELECT,
REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
__raw_writel(BF_LRADC_CTRL4_LRADC6SELECT(LRADC_CH6),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
__raw_writel(BF_LRADC_CTRL4_LRADC1SELECT(LRADC_CH1),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
/* Clear the Soft Reset and Clock Gate for normal operation
* Ovvero: avvio il convertitore LRADC*/
__raw_writel(BM_LRADC_CTRL0_SFTRST | BM_LRADC_CTRL0_CLKGATE,
REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
/*
* Clear the divide by two for channel 6 since
* it has a HW divide-by-two built in.
* Ovvero: divido per due tutti i canali (pazienza la risoluzione) ranne il BATT perchè divide già per 4
*/
__raw_writel( (BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << ACQ_NXC_ADC_CANBUS)) |
(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << ACQ_NXC_ADC_ACQBUS)),
REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);
/* Clear the accumulator & NUM_SAMPLES per tutti i canali che voglio acquisire */
__raw_writel(0xFFFFFFFF,REGS_LRADC_BASE + HW_LRADC_CHn_CLR(ACQ_NXC_ADC_CANBUS));
__raw_writel(0xFFFFFFFF,REGS_LRADC_BASE + HW_LRADC_CHn_CLR(ACQ_NXC_ADC_ACQBUS));
/* Clear the interrupt flag */
__raw_writel((BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS),
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/*
* Get VddIO; this is the max scale value for the button resistor
* ladder.
*/
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << ACQ_NXC_ADC_CANBUS) |
BF_LRADC_CTRL0_SCHEDULE(1 << ACQ_NXC_ADC_ACQBUS),
REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
/* wait for completion */
while ((__raw_readl(REGS_LRADC_BASE + HW_LRADC_CTRL1)
& ((BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS))
) != (
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS)
)
)
cpu_relax();
/* Clear the interrupt flag */
__raw_writel((BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_CANBUS) |
(BM_LRADC_CTRL1_LRADC0_IRQ<<ACQ_NXC_ADC_ACQBUS),
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/* read ch value. */
*vCANBus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(ACQ_NXC_ADC_CANBUS)) & BM_LRADC_CHn_VALUE;
*vACQBus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(ACQ_NXC_ADC_ACQBUS)) & BM_LRADC_CHn_VALUE;
__raw_writel(ctrl4_backup,REGS_LRADC_BASE + HW_LRADC_CTRL4);
__raw_writel(ctrl2_backup,REGS_LRADC_BASE + HW_LRADC_CTRL2);
}
ssize_t my_mxs_lradc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
int maxLen = strlen(myBuff);
if(*f_pos + count > maxLen)
count = maxLen - *f_pos;
if (copy_to_user(buf, &myBuff[*f_pos], count)) {
goto errore;
}
*f_pos += count;
return count;
errore:
return -EFAULT;
}
/*
* Use the the lradc channel
* get the die temperature from on-chip sensor.
*/
uint16_t MeasureInternalDieTemperature(void)
{
uint32_t ch8Value, ch9Value, lradc_irq_mask, channel;
uint16_t v;
channel = 0; /* LRADC 0 */
lradc_irq_mask = 1 << channel;
/* power up internal tep sensor block */
__raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD,
REGS_LRADC_BASE + HW_LRADC_CTRL2_CLR);
/* mux to the lradc 8th temp channel */
__raw_writel((0xF << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
__raw_writel((8 << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
/* Clear the interrupt flag */
__raw_writel(lradc_irq_mask,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << 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 again */
__raw_writel(lradc_irq_mask,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
/* read temperature value and clr lradc */
ch8Value = __raw_readl(REGS_LRADC_BASE +
HW_LRADC_CHn(channel)) & BM_LRADC_CHn_VALUE;
__raw_writel(BM_LRADC_CHn_VALUE,
REGS_LRADC_BASE + HW_LRADC_CHn_CLR(channel));
/* mux to the lradc 9th temp channel */
__raw_writel((0xF << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_CLR);
__raw_writel((9 << (4 * channel)),
REGS_LRADC_BASE + HW_LRADC_CTRL4_SET);
/* Clear the interrupt flag */
__raw_writel(lradc_irq_mask,
REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << 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 */
ch9Value = __raw_readl(
REGS_LRADC_BASE + HW_LRADC_CHn(channel))
& BM_LRADC_CHn_VALUE;
__raw_writel(BM_LRADC_CHn_VALUE,
REGS_LRADC_BASE + HW_LRADC_CHn_CLR(channel));
/* power down temp sensor block */
__raw_writel(BM_LRADC_CTRL2_TEMPSENSE_PWD,
REGS_LRADC_BASE + HW_LRADC_CTRL2_SET);
v = (uint16_t)((ch9Value-ch8Value)*GAIN_CORRECTION/4000);
//printk(KERN_NOTICE "T: %d\n",v);
return v;
}
/*
* Open and close
*/
int my_mxs_lradc_open(struct inode *inode, struct file *filp)
{
struct my_mxs_lradc_dev *dev; /* device information */
int internalTemp,vddio;
int vCANBus,vACQBus;
dev = container_of(inode->i_cdev, struct my_mxs_lradc_dev, cdev);
filp->private_data = dev; /* for other methods */
internalTemp = MeasureInternalDieTemperature();
vddio = hw_lradc_vddio();
hw_load_adc(&vCANBus,&vACQBus);
sprintf(myBuff, "[NXC ADC]\nCANBus=%d\nACQBus=%d\nVDDIO=%d\nDIE=%d\n", vCANBus, vACQBus,vddio,internalTemp);
return 0; /* success */
}
int my_mxs_lradc_release(struct inode *inode, struct file *filp)
{
return 0;
}
struct file_operations my_mxs_lradc_fops = {
.owner = THIS_MODULE,
.llseek = NULL,
.read = my_mxs_lradc_read,
.write = NULL,
.ioctl = NULL,
.open = my_mxs_lradc_open,
.release = my_mxs_lradc_release,
};
/*
* Set up the char_dev structure for this device.
*/
static int my_mxs_lradc_setup_cdev(struct my_mxs_lradc_dev *dev)
{
int err, devno = MKDEV(my_mxs_lradc_major, my_mxs_lradc_minor);
printk(KERN_NOTICE "Dev: %d ", devno);
cdev_init(&dev->cdev, &my_mxs_lradc_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_mxs_lradc_fops;
err = cdev_add(&dev->cdev, devno, 1);
/* Fail gracefully if need be */
if (err) {
printk(KERN_NOTICE "Error %d in setup cdev", err);
return err;
}
return 0;
}
void cleanup_module(void)
{
unregister_chrdev(gMajor, "my-mxs-lradc");
device_destroy(my_lradc_class, MKDEV(gMajor, 0));
class_destroy(my_lradc_class);
say_goodbye();
}
static int __devinit my_mxs_lradc_probe(struct platform_device *pdev) {
unsigned int x_plus;
printk(KERN_WARNING "Devinit");
//x_plus = __raw_readl(LRADC_PHYS_ADDR + HW_LRADC_CHn(0)) & BM_LRADC_CHn_VALUE;
printk(KERN_WARNING "x_plus = %d",x_plus);
return 0;
}
static int __devexit mxs_pwm_led_remove(struct platform_device *pdev) {
printk(KERN_WARNING "Devexit");
return 0;
}
static struct platform_driver my_mxs_lradc_driver = {
.probe = my_mxs_lradc_probe,
.remove = __devexit_p(mxs_pwm_led_remove),
.suspend = NULL,
.resume = NULL,
.driver = {
.name = "mxs-pippone",
},
};
int init_module(void)
{
struct device *temp_class;
int M;
/* register a character device */
M = register_chrdev(0, "my-mxs-lradc", &my_mxs_lradc_fops);
if (M < 0) {
printk("\nmy-mxs-lradc: can't get major number\n");
return M;
}
gMajor = M;
my_lradc_class = class_create(THIS_MODULE, "my-mxs-lradc");
if (IS_ERR(my_lradc_class)) {
printk(KERN_ERR "Error creating my-mxs-lradc module class.\n");
unregister_chrdev(gMajor, "my-mxs-lradc");
return PTR_ERR(my_lradc_class);
}
temp_class = device_create(my_lradc_class, NULL,
MKDEV(gMajor, 0), NULL, "my-mxs-lradc");
if (IS_ERR(temp_class)) {
printk(KERN_ERR "Error creating my-mxs-lradc class device.\n");
class_destroy(my_lradc_class);
unregister_chrdev(gMajor, "my-mxs-lradc");
return -1;
}
return 0;
}
MODULE_LICENSE("GPL"); /* Get rid of the taint message */
/* actually this example is public domain */
MODULE_AUTHOR("Emanuele Coli"); /* Who wrote this */
MODULE_DESCRIPTION("Gestione LRADC per iMX28"); /* Helpful info */
Hi Raul,
Here, the steps to add Touch screen driver to your rootfs.
$ cd (path of the ltib)
$ ./ltib -m config
Select Package list --->
Select TSLIB
Exit
Exit
Yes
$ ./ltib
$./ltib -p boot_stream.spec -f
Thank you,
RAM
I also use iMX28 and I have to read LRADC from my programs. I've correctly installed TSLIB (I use touch screen) but I don't know how can read a generic LRADC input in user space via ioctl.
In test unit (imx_adc_test.c) there is an example where is showed the use of ioct with /dev/imx_adc (open("/dev/imx_adc", O_RDONLY);) but unfortunatley I don't have /dev/imx_adc link. How can I enable this file system object?
Thanks