MINI2440 ADC驱动
ADC原理就不赘述了。与裸机开发相同的是需要开启ADC,选择通道,设置预分频值,预分频器使能;与裸机开发不同的是内核为了减小功耗,ADC的时钟是默认关闭的,所以需要clk_get
和clk_enable
对ADC的时钟进行使能。
注:一定要在内核中关掉三星自己写的adc驱动,有兴趣可以看一下源代码,路径是drivers/char/mini2440_adc.c
。
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#include <asm/io.h>
//寄存器地址,2440中文手册中地址是7位,最低位少了,有点坑
#define ADCCON 0x58000000
#define ADCDAT0 0x580000C0
//对寄存器进行进行操作的宏
#define PRSCEN_ENABLE (1<<14)
#define PRSCVL(x) (x<<6)
#define INPUT_CN(x) (x<<3)
#define ADC_START (1<<0)
#define ADC_NAME "myAdc"
static dev_t devno = -1;
static int adc_data = 0;
static int adc_major = 199;
static int adc_minor = 0;
static unsigned long *adccon = NULL;
static unsigned long *adcdat0 = NULL;
static volatile int cond;
static struct clk *adc_clk;
wait_queue_head_t waitQueue;
/*中断处理函数*/
irqreturn_t adc_irq_handler(int irq, void *argv)
{
cond = 1;
wake_up_interruptible(&waitQueue);
adc_data = *adcdat0 & 0x3ff;
return IRQ_HANDLED;
}
/* read函数进来先阻塞,在中断处理函数中去除阻塞
*/
ssize_t adc_read(struct file *filp, char __user *buf, size_t size, loff_t *lfp)
{
adc_start(0, 19); ‣channal: 0 ‣prscvl: 19
wait_event_interruptible(waitQueue, cond);
cond = 0;
copy_to_user(buf, &adc_data, sizeof(adc_data));
return 0;
}
int adc_start(int channal, int prscvl)
{
*adccon = (INPUT_CN(channal) | PRSCVL(prscvl) | PRSCEN_ENABLE);
*adccon |= ADC_START;
return 0;
}
int adc_open(struct inode *inode, struct file *filp)
{
printk("adc open ok!\n");
//adc时钟使能
adc_clk = clk_get(NULL, "adc");
clk_enable(adc_clk);
return 0;
}
int adc_release(struct inode *inode, struct file *filp)
{
printk("adc close ok!\n");
return 0;
}
static struct file_operations adc_fops = {
.owner = THIS_MODULE,
.open = adc_open,
.release = adc_release,
.read = adc_read,
};
static struct cdev adc_cdev;
static __init int adc_init(void)
{
int result = 0;
devno = MKDEV(adc_major, adc_minor);
result = register_chrdev_region(devno, 1, ADC_NAME);
if (result){
printk("register_chrdev_region\n");
}
cdev_init(&adc_cdev, &adc_fops);
cdev_add(&adc_cdev, devno, 1);
adccon = ioremap(ADCCON, 2);
adcdat0 = ioremap(ADCDAT0, 2);
init_waitqueue_head(&waitQueue);
result = request_irq(IRQ_ADC, adc_irq_handler, IRQF_TRIGGER_PROBE, "my_adc", "adc");
if (result){
return -EBUSY;
}
return 0;
}
static __exit void adc_exit(void)
{
iounmap(adccon);
iounmap(adcdat0);
disable_irq(IRQ_ADC);
free_irq(IRQ_ADC, "adc");
cdev_del(&adc_cdev);
unregister_chrdev_region(devno, 1);
printk("-----------exit ok!-----------\n");
return ;
}
module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
应用程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main (int argc, char *argv[])
{
int ret = -1;
int fd = open("/dev/myAdc", O_RDONLY);
if (fd < 0) {
perror("open failed"); ‣s: "open failed"
exit(-1);
}
int adc_data = -1;
while (1) {
read(fd, &adc_data, 1);
printf("adc_data = %d\n", adc_data);
sleep(1); ‣seconds: 1
}
close(fd);
return 0;
}