基于arm-Linux的模拟数据采集模块(A/D转换)设计(s3c6410)

声明:此次设计是基于OK6410开发板做的一系列操作,对于其他的开发板做此次的操作也是大同小异

驱动代码:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>

#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/regs-timer.h>
#include <plat/regs-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;
    int channel;
    int prescale;
} ADC_DEV;

#ifdef CONFIG_TOUCHSCREEN_S3C
extern int X6410_adc_acquire_io(void);
extern void X6410_adc_release_io(void);
#else
static inline int X6410_adc_acquire_io(void) {
    return 0;
}
static inline void X6410_adc_release_io(void) {
    /* Nothing */
}
#endif

static int __ADC_locked = 0;

static ADC_DEV adcdev;
static volatile int ev_adc = 0;
static int adc_data;

static struct clk    *adc_clock;

#define __ADCREG(name)    (*(volatile unsigned long *)(base_addr + name))
#define ADCCON            __ADCREG(S3C_ADCCON)    // ADC control
#define ADCTSC            __ADCREG(S3C_ADCTSC)    // ADC touch screen control
#define ADCDLY            __ADCREG(S3C_ADCDLY)    // ADC start or Interval Delay
#define ADCDAT0            __ADCREG(S3C_ADCDAT0)    // ADC conversion data 0
#define ADCDAT1            __ADCREG(S3C_ADCDAT1)    // ADC conversion data 1
#define ADCUPDN            __ADCREG(S3C_ADCUPDN)    // Stylus Up/Down interrupt status

#define PRESCALE_DIS        (0 << 14)
#define PRESCALE_EN            (1 << 14)
#define PRSCVL(x)            ((x) << 6)
#define ADC_INPUT(x)        ((x) << 3)
#define ADC_START            (1 << 0)
#define ADC_ENDCVT            (1 << 15)

#define START_ADC_AIN(ch, prescale) \
    do { \
        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 (__ADC_locked) {
        adc_data = ADCDAT0 & 0x3ff;

        ev_adc = 1;
        wake_up_interruptible(&adcdev.wait);

        /* clear interrupt */
        __raw_writel(0x0, base_addr + S3C_ADCCLRINT);
    }

    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 (X6410_adc_acquire_io() == 0) {   /* 尝试获得ADC_LOCK信号量,如果能够立刻获得,它就获得信号量并返回0
                                         * 否则,返回非零,它不会导致调用者睡眠,可以在中断上下文使用
                                          */
        __ADC_locked = 1;                /* 表示A/D转换器资源可用 */

        START_ADC_AIN(adcdev.channel, adcdev.prescale);  /* 使能预分频,选择ADC通道,最后启动ADC转换*/

        wait_event_interruptible(adcdev.wait, ev_adc);    /* 等待事件,当ev_adc = 0时,进程被阻塞,直到ev_adc>0 */
        ev_adc = 0;

        DPRINTK("AIN[%d] = 0x%04x, %d\n", adcdev.channel, adc_data, ADCCON & 0x80 ? 1:0);

        
        /* 将在ADC中断处理函数读取的ADC转换结果赋值给value */
        value = adc_data;

        __ADC_locked = 0;
        X6410_adc_release_io();


        } else
        {
        value = -1;    /* 如果A/D转换器资源不可用,将value赋值为-1 */
    }

    len = sprintf(str, "%d\n", value);
    if (count >= len) {
        int r = copy_to_user(buffer, str, len);
        return r ? r : len;
    } else {
        return -EINVAL;
    }
}

static int s3c2410_adc_open(struct inode *inode, struct file *filp)
{
    init_waitqueue_head(&(adcdev.wait));    //等待的阻塞队列先初始化一个等待队列头,因为入口函数里既然有申请ADC中断,
                                            // 那么肯定要使用等待队列,接着设置ADC通道,
                                            //因为6410的ADC输入通道默认是0,设置预分频值为0xff。

    adcdev.channel=0;
    adcdev.prescale=0xff;

    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,  //调用一个adc_open函数
    read:    s3c2410_adc_read,    //调用一个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)
{
    int ret;

    base_addr = ioremap(SAMSUNG_PA_ADC, 0x20);//把物理地址转换为虚拟地址
    if (base_addr == NULL) {
        printk(KERN_ERR "Failed to remap register block\n");
        return -ENOMEM;
    }

    adc_clock = clk_get(NULL, "adc");//通过clk_get是ADC处于开启状态
    if (!adc_clock) {
        printk(KERN_ERR "failed to get adc clock source\n");
        return -ENOENT;
    }
    clk_enable(adc_clock);//是adc驱动处于工作状态
    
    /* normal ADC */
    ADCTSC = 0;                  //ADC 的触摸屏控制寄存器 的触摸屏控制寄存器(ADCTSC)

    ret = request_irq(IRQ_ADC, adcdone_int_handler, IRQF_SHARED, DEVICE_NAME, &adcdev);//接着申请ADC中断,其中断处理函数为
             //adcdone_int_handler,而flags为IRQF_SHARED,即共享中断,因为触摸屏里也要申请ADC中断,最后注册一个混杂设备
    if (ret) {
        iounmap(base_addr);//注销虚拟地址
        return ret;
    }
    printk (DEVICE_NAME"\therr\n");

    ret = misc_register(&misc);

    printk (DEVICE_NAME"\tinitialized\n");
    return ret;
}

static void __exit dev_exit(void)
{
    free_irq(IRQ_ADC, &adcdev);
    iounmap(base_addr);

    if (adc_clock) {
        clk_disable(adc_clock);
        clk_put(adc_clock);
        adc_clock = NULL;
    }

    misc_deregister(&misc);
}

module_init(dev_init);//程序入口(insmod)
module_exit(dev_exit);//注销程序

MODULE_LICENSE("GPL");//许可证,说明许可是开源的
MODULE_AUTHOR("Forlinx Inc.");


测试代码:

     

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <errno.h>
#include <string.h>

int fd;

char str[20];

int main(){

            fd=open("/dev/adc",O_RDWR);
            if(fd<0){

           printf("open adc device fail!");
           return -1;
           }
            printf("open adc device success\n");

         while(1)
             {
              usleep(100);
              memset(str,0,20);
             // usleep(150);
              read(fd,&str,20);
              printf("str=%d\n",str);
              double voltage=atof(str);
              printf("voltage=%f\n",voltage);
              voltage=voltage / 1024 * 3.3;
              printf("voltage=%f\n",voltage);
              
         }    
        close(fd);
      return 0;     

 }

实验结果:

        

这个实验pc机与开发板相连用的是xshell软件,然后我们写好的测试程序先在虚拟机上通过arm-Linux-gcc编译成arm开发板可以识别的语言,然后通过nfs挂载到我们的开发板上。

此次开发的重点是如何配置ADCCON寄存器,代码里面有讲到,第二个问题是看懂他的开发手册,开发手册里有一系列的关于如何将我们写好的驱动代码放在内核中进行编译,如何通过sd卡烧写系统,然后如何通过nandflash启动开发板。在这里我就不一一赘述了,附上开发板用户资料链接就好就好!!

https://pan.baidu.com/s/1x7P2ukTzZ-O9_3NpK4QtzQ

           

猜你喜欢

转载自blog.csdn.net/ltcz99/article/details/85317348