/*********************************************************/
/****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;
}
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)
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- CGD and Qorvo to jointly revolutionize motor control solutions
- CGD and Qorvo to jointly revolutionize motor control solutions
- Keysight Technologies FieldFox handheld analyzer with VDI spread spectrum module to achieve millimeter wave analysis function
- Infineon's PASCO2V15 XENSIV PAS CO2 5V Sensor Now Available at Mouser for Accurate CO2 Level Measurement
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- A new chapter in Great Wall Motors R&D: solid-state battery technology leads the future
- Naxin Micro provides full-scenario GaN driver IC solutions
- Interpreting Huawei’s new solid-state battery patent, will it challenge CATL in 2030?
- Are pure electric/plug-in hybrid vehicles going crazy? A Chinese company has launched the world's first -40℃ dischargeable hybrid battery that is not afraid of cold
- [Ended] Live broadcast of "wearing" and "chip": Infineon's nanny-level solution for wearable devices
- 【GD32L233C-START Review】1. Getting to know GD32L233C-START
- BearPi-HM Nano Development Board Review 2 + HelloWorld Download
- How to design PCB to achieve the best EMC effect
- TMS320C6748 Learning [Timer Learning]
- Wistron Technology is recruiting embedded development and embedded test engineers for 12-20K, located in Huadu, Guangzhou
- Vivado simulation issues
- Differential filter PCB layout needs to consider,,,
- Studying Things to Gain Knowledge 06: A Drop of Water Generator
- C port of pic16F877A