Lesson 4-ADC

Source: Internet
Author: User

Soon, I entered the fourth class of ADC-driven learning. After the first three lessons, I believe that everyone, like me, feels a little bit about the driver. Based on this, I quickly master the general framework of the ADC Driver. This course focuses on mutex semaphores, memory ing, and other issues. For more internal details, also, please read the official comments.

 

The code is pasted as follows:

# Include <Linux/errno. h>
# Include <Linux/kernel. h>
# Include <Linux/module. h>
# Include <Linux/slab. h>
# Include <Linux/input. h>
# Include <Linux/init. h>
# Include <Linux/Serio. h>
# Include <Linux/delay. h>
# Include <Linux/CLK. h>
# Include <Linux/Wait. H>
# Include <Linux/sched. h>
# Include <ASM/IO. h>
# Include <ASM/IRQ. h>
# Include <ASM/uaccess. h>
# Include <Mach/regs-clock.h>
# Include <plat/regs-timer.h>

# Include <plat/regs-adc.h>
# Include <Mach/regs-gpio.h>
# Include <Linux/cdev. h>
# Include <Linux/miscdevice. h>

# Include "s3c24xx-adc.h"

 

# UNDEF debug
// # Define debug
# Ifdef debug
# Define dprintk (X...) {printk (_ function _ "(% d):" ,__ line _); printk (# X );}
# Else
# Define dprintk (X...) (void) (0)
# Endif

# Define device_name "ADC"

 

Static void _ iomem * base_addr;

 

Typedef struct

{
Wait_queue_head_t wait; // each adswitch device should have its own waiting queue
Int channel;
Int prescale;
} Adc_dev;

 

Declare_mutex (adc_lock); // semaphore, mutex mode, and initialized to 1, which can be used by a device

// Declare_mutex_locked (adc_lock); the initialization value is 0.

Static int ownadc = 0;

Static adc_dev adcdev;
Static volatile int ev_adc = 0;
Static int adc_data;

Static struct CLK * adc_clock;

 

# Define adccon (* (volatile unsigned long *) (base_addr + s3c2410_adccon ))

// ADC Control
# Define adctsc (* (volatile unsigned long *) (base_addr + s3c2410_adctsc ))

// ADC Touch Screen Control
# Define adcdly (* (volatile unsigned long *) (base_addr + s3c2410_adcdly ))

// ADC start or interval Delay
# Define adcdat0 (* (volatile unsigned long *) (base_addr + s3c2410_adcdat0 ))

// ADC conversion data 0
# Define adcdat1 (* (volatile unsigned long *) (base_addr + s3c2410_adcdat1 ))

// ADC conversion data 1
# Define adcupdn (* (volatile unsigned long *) (base_addr + 0x14 ))

// Stylus up/down interrupt status

// The hardware address is used for memory ing. For details, see the note at the end of this article.

 

# Define prescale_dis (0 <14)
# Define prescale_en (1 <14)
# Define prscvl (x) <6)
# Define adc_input (x) <3)
# Define adc_start (1 <0)
# Define adc_endcvt (1 <15) // macro defines the adccon register bit

 

# Define start_adc_ain (CH, prescale )/
Do {// start ad ing
Adccon = prescale_en | prscvl (prescale) | adc_input (CH ));/
Adccon | = adc_start ;/
} While (0)

Static irqreturn_t adcdone_int_handler (int irq, void * dev_id)
{
If (ownadc)

{// Can execute the ad ing
Adc_data = adcdat0 & 0x3ff; // read the adconversion result and store the variable adc_data

Ev_adc = 1;
Wake_up_interruptible (& adcdev. Wait); // wake up the waiting queue
}

Return irq_handled;
}

 

Static ssize_t s3c2410_adc_read (struct file * filp, char * buffer, size_t count, loff_t * PPOs)
{
Char STR [20];
Int value;
Size_t Len;

If (down_trylock (& adc_lock) = 0)

{// Semaphores minus 1. If the semaphores are not negative, 0 is returned successfully. Otherwise, a non-0 value is returned immediately and will never sleep.
Ownadc = 1; // The Notification interrupt handler function obtains the right to use the ADC.
Start_adc_ain (adcdev. Channel, adcdev. prescale); // starts the ad ing
Wait_event_interruptible (adcdev. Wait, ev_adc); // enter the waiting queue

Ev_adc = 0;

Dprintk ("Ain [% d] = 0x % 04x, % d/N", adcdev. Channel, adc_data, adccon & 0x80? 1-0 );

Value = adc_data;

Ownadc = 0;
Up (& adc_lock); // release the semaphore (semaphore plus 1)
}

Else

{
Value =-1;
}

 

Len = sprintf (STR, "% d/N", value); // Save the adconverted value (INT) to STR (char) and return the length.
If (count> = Len)

{
Int r = copy_to_user (buffer, STR, Len); // transmit data to the user space (Application Layer)
Return R? R: Len;
}

Else

{
Return-einval;
}
}

 

Static int s3c2410_adc_open (struct inode * inode, struct file * filp)
{
Init_waitqueue_head (& (adcdev. Wait); // register the waiting queue

Adcdev. Channel = 0; // default channel 0
Adcdev. prescale = 0xff; // default pre-division value 255

Dprintk ("ADC opened/N ");
Return 0;
}

 

Static int s3c2410_adc_release (struct inode * inode, struct file * filp)
{
Dprintk ("ADC closed/N ");
Return 0;
}

Static struct file_operations dev_fops =

{
Owner: this_module,
Open: s3c2410_adc_open,
Read: s3c2410_adc_read,
Release: s3c2410_adc_release,
};

 

Static struct miscdevice MISC =

{
. Minor = misc_dynamic_minor,
. Name = device_name,
. Fops = & dev_fops,
};

 

Static int _ init dev_init (void)
{// Device Initialization
Int ret;

Base_addr = ioremap (s3c2410_pa_adc, 0x20); // map the physical address to the memory.
If (base_addr = NULL)

{// Ing failed
Printk (kern_err "failed to remap register block/N ");
Return-enomem;
}

Adc_clock = clk_get (null, "ADC"); // get the ad clock
If (! Adc_clock)

{
Printk (kern_err "failed to get ADC clock source/N ");
Return-enoent;
}
Clk_enable (adc_clock); // enables clock
 
/* Normal ADC */
Adctsc = 0; // disable the touch screen to enable the adswitch

Ret = request_irq (irq_adc, adcdone_int_handler, ir1__shared, device_name, & adcdev); // requests for ad interruption. The end of the AD conversion triggers the interruption.
If (RET)

{
Iounmap (base_addr); // The application for AD interruption fails and the memory is released.
Return ret;
}

 

Ret = misc_register (& MISC );

Printk (device_name "/tinitialized/N ");
Return ret;
}

 

Static void _ exit dev_exit (void)
{
Free_irq (irq_adc, & adcdev); // release interruption
Iounmap (base_addr); // releases the memory.

If (adc_clock)

{
Clk_disable (adc_clock); // disable the ADC clock
Clk_put (adc_clock );
Adc_clock = NULL;
}

Misc_deregister (& MISC );
}

 

Export_symbol (adc_lock); // provide external function call mutex adc_lock
Module_init (dev_init );
Module_exit (dev_exit );
Module_license ("GPL ");
Module_author ("friendlyarm Inc .");

 

Note:

1. Why does mutex need to be used?

Because the adconverter and the touch screen controller use the same ADC hardware resource on the S3C2440, the mutex semaphores are required to avoid competing states.

 

2. physical address ing

Base_addr = ioremap (s3c2410_pa_adc, 0x20 );

Its prototype is: void * ioremap (unsigned long offset, unsigned long size );

This function maps physical addresses to virtual kernel spaces. The parameter offset can be understood as the base address, while the size is the length to be mapped. The function completed in the program is to map the physical address s3c2410_pa_adc to the kernel space base_addr, with a length of 20 bytes. The s3c2410_pa_adc is defined in.../mach-s3c2410/include/Mach and is the adccon address of the Register (0x58000000 ).

Iounmap (base_addr );

Release memory space and use it in pairs with ing.

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.