歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

S3C2410多通道adc驅動及測試程序

網上流行很多基於S3C2410的ADC驅動及測試程序。本文所使用的是開發板光盤中自帶的經過修改後的adc驅動。筆者在這個基礎上再作一點修改。由於那個文件已經刪除了版權信息(但還是能找到這些代碼與網上流行的驅動的一些聯系),這裡也不知道如何添加了,可以肯定的是,它使用了GPL,這裡公開源代碼,也算是GPL了。

原來的代碼默認使用ADC第0個通道,本文將添加ioctl接口,可以通過應用層的ioctl來選擇多個通道。

與原來的代碼相比,添加了如下幾個方面:

1、添加頭文件<linux/ioctl.h>,不過經測試,沒有也可以通過編譯。

2、修改原來的調試信息為:

#define DEBUG
#ifdef DEBUG /* define it in Makefile of somewhere */
/* KERN_INFO */
#define DPRINTK(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__)
#else
#define DPRINTK(fmt, ...)
#endif

這個便於查看調試信息。

 

3、添加ioctl相關宏定義:

/* ioctl */
#ifndef u16
#define u16 unsigned short
#endif

#define ADC_IOC_MAGIC 0xd2

#define ADC_SET_CHANNEL _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV _IOW(ADC_IOC_MAGIC, 1, u16)

#define ADC_MAX_IOC 2 /* we only have 2 ioctl commands */
#define MAX_ADC  4 /* we have 4 adc chnnels */
/* end of ioctl */

 

4、添加ioctl接口:

 /* 在應用層調用系統調用ioctl發生錯誤時,會返回-1,並設置errno為相應的錯誤號,而這個錯誤號便是驅動中ioctl中的那個。
     網上有資料說要返回-ENOTTY,不過個人認為這裡返回-EINVAL更恰當一些。  
  */
static int s3c2410_adc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
        if ((_IOC_TYPE(cmd) != ADC_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_MAX_IOC))
                return -EINVAL;

        switch (cmd) {
        /* set channel */
        case ADC_SET_CHANNEL:
                arg &=3;  //多此一舉??
                if (arg > 3)
                        arg = 3;
                adcdev.channel=arg;
                break;
        case ADC_SET_CLKDIV:
                arg &= 0xff; // ??
                if (arg > 0xff)
                        arg = 0xff;
                adcdev.prescale=arg;
                break;
        default:
                return -EINVAL;
                break;
        }
        return 0;
}

 

當然,也要在這個文件的file_operations結構體添加這個接口:

        .ioctl   = s3c2410_adc_ioctl,

 

5、copy_to_user

原來的代碼使用了sprintf將ADC轉換的結果轉換為字符串類型,不過卻在後面添加一個“\n”,不知是何意。

 //len = sprintf(str, "%d\n", value); // why '\n'?
 len = sprintf(str, "%d", value);
         ……
 copy_to_user(buffer, str, len);

也正是這個原因,在測試程序中要使用sscanf將字符串轉換成整數類型才能得到正常的結果。

 

其它的修改不影響使用。

 

測試代碼也簡單,如下:

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

#define ADC "/dev/adc"

#ifdef DEBUG
#define debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#define debug(fmt, ...)
#endif

#ifndef u16
#define u16        unsigned short
#endif

// 控制字,與驅動一致
#define ADC_IOC_MAGIC        0xd2

#define ADC_SET_CHANNEL        _IOW(ADC_IOC_MAGIC, 0, u16)
#define ADC_SET_CLKDIV        _IOW(ADC_IOC_MAGIC, 1, u16)

// 特意的錯誤控制字
// test
#define AAA 333
// test end
int fd;

// 由於是死循環,需要捕獲SIGINT信號
void sig_handle(int sig)
{
        //debug("you press ^C: %d\n", sig);
        close(fd);        /* we colse it here */
        exit(0);
}
int main(void)
{
        char buff[10];
        int val;
        int len;
        int i;
        signal(SIGINT, sig_handle);

        debug("Test of ADC. ^C to exit\n");
        fd = open(ADC, O_RDWR);
        if (fd < 0){
                perror("Open /dev/adc failed");
                exit(1);
        }
        // test
        /* it will return -EINVAL(which specified in ADC driver) */
        if (ioctl(fd, AAA,0) < 0)
                perror("ioctl");

        while (1) {
                /* we have 4 channels ADC*/
                for (i=0; i<4; i++) {
                        ioctl(fd, ADC_SET_CHANNEL, i);
                        len = read(fd, buff,sizeof(buff));
                        if (len < 0) {
                                perror("read adc device failed");
                                exit(0);
                        } else {
                                buff[len] = '\0';
                                sscanf(buff, "%d", &val);
                                printf("read AIN[%d]: %d value:%d\n", i, len, val);
                        }
                        sleep(1); // 每隔1秒采1個通道
                }
                //sleep(1); // 每隔1秒采集4個通道
        }

       close(fd); /* just kidding */
        
        return 0;
}

 

注:文中代碼注釋出現了中文,是為了方便文章的閱讀(根據經驗,有清楚注釋的代碼才算代碼),在實際代碼中是沒有中文注釋的。

Copyright © Linux教程網 All Rights Reserved