mini2440 ADC adjustable resistor driver development source code (miscellaneous device driver framework)

Publisher:SereneMeadow7Latest update time:2024-06-20 Source: elecfansKeywords:mini2440  ADC Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

/*********************************************************/

/****s3c2440 ADC adjustable resistor driver development source code (miscellaneous device driver framework)****/

/********************************************************/

#include

#include

#include

#define DEVICE_NAME "adc_driver" /*Device name*/

static void __iomem *adc_base; /*Defines a memory address used to save virtual mapping*/
static struct clk *adc_clk; /*Save the ADC clock obtained from the platform clock queue*/


DECLARE_MUTEX(ADC_LOCK); /*Declare and initialize a semaphore ADC_LOCK for mutually exclusive access to ADC resources*/
static DECLARE_WAIT_QUEUE_HEAD(adc_waitq); /*Define and initialize a waiting queue adc_waitq for blocking access to ADC resources*/


static volatile int ev_adc = 0; ///*Used to identify whether the data after AD conversion can be read, 0 means it cannot be read*/
static int adc_data; /*Used to save the read AD converted value, which is read in the ADC interrupt*/

/*ADC interrupt service program, this service program mainly reads the AD converted value from the ADC data register*/
static irqreturn_t adc_irq(int irq, void *dev_id)
{
/*Ensures that the application reads the AD converted value once here,
avoiding multiple interruptions and multiple readings of the AD converted value after the application reads once*/
if(!ev_adc)
{
/*The read AD converted value is saved in the global variable adc_data. S3C2410_ADCDAT0 is defined in regs-adc.h.
Why is it necessary to AND the previous 0x3ff here? It's very simple, because the AD converted data is saved in bits 0-9 of ADCDAT0,
so after ANDing with 0x3ff (i.e.: 1111111111), the data of bits 0-9 is obtained, and the remaining bits are all 0*/
adc_data = readl(adc_base + S3C2410_ADCDAT0) & 0x3ff;
ev_adc = 1; //Set the readable flag to 1 and wake up the waiting queue
wake_up_interruptible(&adc_waitq);
}
return IRQ_HANDLED;
}

/*ADC device driver open interface function*/
static int adc_open(struct inode *inode, struct file *file)
{
int ret;

/*Request ADC interrupt service. Here we use the shared interrupt: IRQF_SHARED. Why do we use the shared interrupt? Because
this interrupt number is also used in the touch screen driver. The interrupt service program is: adc_irq is implemented below. IRQ_ADC is the interrupt number of ADC. Note:
The last parameter of the interrupt request function must not be NULL, otherwise the interrupt request will fail. If this parameter is not used in the interrupt service program
, just give a random value. I will give 1 here*/
ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED, DEVICE_NAME, 1);
if (ret)
{
/*Error handling*/
printk(KERN_ERR "IRQ%d error %dn", IRQ_ADC, ret);
return -EINVAL;
}

return 0;
}


/*Set ADC control register and start AD conversion*/
static void start_adc(void)
{
unsigned int tmp;

tmp = (1 << 14) | (255 << 6) | (0 << 3);/* 0 1 00000011 000 0 0 0 */
writel(tmp, adc_base + S3C2410_ADCCON); /*AD prescaler enabled, analog input channel set to AIN0*/

tmp = readl(adc_base + S3C2410_ADCCON);
tmp = tmp | (1 << 0); /* 0 1 00000011 000 0 0 1 */
writel(tmp, adc_base + S3C2410_ADCCON); /*AD conversion starts*/
}


/*ADC device driver read interface function*/
static ssize_t adc_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
/*Try to get the semaphore (ie: lock)*/
if (down_trylock(&ADC_LOCK))
{
return -EBUSY;
}

if(!ev_adc)/*Indicates that there is no AD converted data yet, and it cannot be read*/
{
if(filp->f_flags & O_NONBLOCK) //If the application uses non-blocking reading, an error will be returned
{
return -EAGAIN;
}
else/*Read in blocking mode*/
{
start_adc(); /*Set the ADC control register and start AD conversion*/
wait_event_interruptible(adc_waitq, ev_adc); /*Put the waiting queue to sleep*/
}
}
/*Getting here means that there is AD converted data, so the flag is cleared to 0 for the next reading*/
ev_adc = 0;
/*Send the read AD converted value to the upper application*/
copy_to_user(buffer, (char *)&adc_data, sizeof(adc_data));
up(&ADC_LOCK); /*Release the acquired semaphore (ie: unlock)*/

return sizeof(adc_data);
}


/*ADC device driver shutdown interface function*/
static int adc_release(struct inode *inode, struct file *filp)
{
return 0;
}


/*Related operations of character devices*/
static struct file_operations adc_fops =
{
.owner = THIS_MODULE,
.open = adc_open,
.read = adc_read,
.release = adc_release,
};

/*misc device structure implementation*/
static struct miscdevice adc_miscdev =
{
.minor = MISC_DYNAMIC_MINOR, /*minor device number, defined in miscdevice.h, 255*/
.name = DEVICE_NAME, /* device name*/
.fops = &adc_fops, /* ADC device file operation*/
};


static int __init adc_init(void)
{
int ret;
/*Get the ADC clock from the platform clock queue. Why do we need to get this clock here? Because the conversion frequency of the ADC is related to the clock.
Some system clocks are defined in arch/arm/plat-s3c24xx /s3c2410-clock.c*/
adc_clk = clk_get(NULL, "adc");
if (!adc_clk)
{
/*Error handling*/
printk(KERN_ERR "failed to find adc clock sourcen");
return -ENOENT;
}

/*After the clock is acquired, it must be enabled before it can be used. clk_enable is defined in arch/arm/plat-s3c/clock.c*/
clk_enable(adc_clk);

/*Map the IO space occupied by the ADC IO port to the virtual address of the memory. ioremap is defined in io.h.
Note: IO space can only be used after mapping. The operation of virtual address in the future is the operation of IO space. S3C2410_PA_ADC
is the base address of ADC controller, defined in mach-s3c2410/include/mach/map.h. 0x20 is the length of virtual address*/
adc_base = ioremap(S3C2410_PA_ADC, 0x20);
if (adc_base == NULL)
{

printk(KERN_ERR "Failed to remap register blockn"); /*Error handling*/
ret = -EINVAL;
goto err_noclk;
}

/*Register ADC as misc device, misc_register is defined in miscdevice.h. The adc_miscdev structure definition
and internal interface functions are described in step ②. MISC_DYNAMIC_MINOR is the minor device number, defined in miscdevice.h*/
ret = misc_register(&adc_miscdev);
if (ret)
{
/*Error handling*/
printk(KERN_ERR "cannot register miscdev on minor=%d (%d)n",
MISC_DYNAMIC_MINOR, ret);
goto err_nomap;
}

printk(DEVICE_NAME " initialized!n");

return 0;
//The following is the jump point of the error handling above
err_noclk:
clk_disable(adc_clk);
clk_put(adc_clk);

err_nomap:
iounmap(adc_base);

return ret;
}

static void __exit adc_exit(void)
{
free_irq(IRQ_ADC, 1);/*Release interrupt*/
iounmap(adc_base);/*Release virtual address mapping space*/

if (adc_clk)/*Shield and destroy clock*/
{
clk_disable(adc_clk);
clk_put(adc_clk);
adc_clk = NULL;
}

misc_deregister(&adc_miscdev);/*Cancel misc device*/
}

/*The exported semaphore ADC_LOCK is used in the touch screen driver, because the touch screen driver and ADC driver share
93. Related registers. In order to avoid resource competition, semaphores are used to ensure mutual exclusive access to resources*/
//EXPORT_SYMBOL(ADC_LOCK);
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("youshaohui 2012.10.31 in ruanjianyuan");
MODULE_DESCRIPTION("s3c2440 ADC Driver");

//=======================================================================

The application test code is developed as follows:

//=======================================================================


#include

#define MYADC "/dev/adc_driver"

void Delay_MS( unsigned int time)  //50 ns
{
unsigned int i,j;

for ( i=0; i {
for(j=0;j<30000;j++)
{

}
}
}
//adc可调电阻
int main(void)
{
int fd;
int i=0;
unsigned int value = -1;
char buf[30]={0};

fd = open(MYADC,O_RDWR,0666);
if (fd < 0)
{
perror("open device adc_driver errorn");
exit(1);
}
printf("open /dev/adc_driver success!n");


while(1)
{
read(fd,&value,4);

printf("result value=%dn",value);

Delay_MS(1000);
}

if(close(fd)<0)
{
perror("close errorn");
exit(1);
}

return 0;
}


Keywords:mini2440  ADC Reference address:mini2440 ADC adjustable resistor driver development source code (miscellaneous device driver framework)

Previous article:Summary and analysis of IIC timing problems in mini2440 debugging
Next article:3 ways to compile the driver into the kernel (using mini2440 key as an example)

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号