实验 A/D驱动程序编写与测试
【实验内容】
编写一个字符设备驱动程序,驱动FS_S5PC100开发板上的可调电阻,采集AD转换过来的电压值
可调电阻位于开发板第二个串口(COM2)的正下方
【实验目的】
了解AD的工作原理,掌握驱动里操作AD控制器的方法,巩固字符设备驱动程序编写方法
【实验平台】
主机:Ubuntu 10.10 目标板:FS_S5PC100 目标内核版本:2.6.29 交叉编译器版本:arm-unknown-linux-gnueabi-gcc-4.2.2
【实验步骤】
在主机ubuntu环境下:
1、将文件夹s5pc100_adc复制到linux环境中,如:/home/linux/test $ cp s5pc100_adc /home/linux/test –a 2、$ cd /home/linux/test/s5pc100_adc 3、$ make //编译设备驱动,生成adc.ko模块 4、$ arm-linux-gcc adc_test.c -o adc_test //编译应用程序 5、$ sudo cp adc.ko adc_test /source/rootfs
在开发板的串口终端控制台下:
6、通过insmod命令将模块加入内核
# insmod adc.ko //由于驱动采用的是动态获取主设备号的方式,所以根据打印信息建立设备结点 major number of adc is 252
7、 创建设备结点
# mknod /dev/adc c 252 0
9、运行应用程序
# ./adc_test
调整可调电阻上的旋钮,发现输出值也在发生变化
val = 630 val = 623 val = 60b val = 5fb val = 913 val = 925 val = 916 val = 911
10、修改应用程序,将直接从驱动获得的数字量转换为实际的电压值(可调电阻可以承受的电压范围为: 0 —— 3.3V)
附:
1、 驱动程序代码:
#include <linux/input.h> #include <linux/module.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/fs.h>
#include <asm/uaccess.h> // copy_to_user, copy_from_user #include <asm/irq.h> #include <asm/io.h> #include <plat/irqs.h> #include <plat/regs-adc.h>
MODULE_AUTHOR("farsight"); MODULE_LICENSE("Dual BSD/GPL");
#define ADC_BASE_ADDR 0xF3000000 #define MAP_SIZE 36
static int adc_major = 0; static volatile void *adc_base;
#if 0 static irqreturn_t ad_interrupt(int irq, void *dummy) { //input_report_key(ad_dev, BTN_0, inb(BUTTON_PORT) & 1); //input_sync(ad_dev); return IRQ_HANDLED; } #endif
static int adc_open(struct inode *inode, struct file *filp) { //map physical address to virtual address adc_base = ioremap(ADC_BASE_ADDR, MAP_SIZE);
//ADC output resolution seletion : 12-bit A/D conversion writel(readl(adc_base+S3C_ADCCON) | S3C_ADCCON_RESSEL_12BIT, adc_base+S3C_ADCCON); //enable A/D converter prescale and set prescaler as 0xFF writel(readl(adc_base+S3C_ADCCON) | S3C2410_ADCCON_PRSCEN, adc_base+S3C_ADCCON); //set prescaler as 0xFF writel((readl(adc_base+S3C_ADCCON) & ~S3C2410_ADCCON_PRSCVLMASK) | S3C2410_ADCCON_PRSCVL(0xff), adc_base+S3C_ADCCON); //select normal operation mode writel(readl(adc_base+S3C_ADCCON) & ~S3C2410_ADCCON_STDBM, adc_base+S3C_ADCCON);
//select channel as AIN0 writel(readl(adc_base+S3C_ADCMUX) & ~S3C_ADCMUX_MASK, adc_base+S3C_ADCMUX);
#if 0 if (request_irq(IRQ_ADC, ad_interrupt, 0, "adc", NULL)) { printk(KERN_ERR "%s: Can't allocate irq %d\n", __FUNCTION__, IRQ_ADC); return -EBUSY; } #endif
//enable A/D conversion //writel(readl(adc_base+S3C_ADCCON) | S3C2410_ADCCON_ENABLE_START, adc_base+S3C_ADCCON); writel(readl(adc_base+S3C_ADCCON) | S3C_ADCCON_READ_START, adc_base+S3C_ADCCON);
return 0; }
static int adc_close( struct inode *inode, struct file *filp ) { //free_irq(IRQ_ADC, ad_interrupt); return 0; }
static ssize_t adc_read( struct file *filp, char __user *buffer, size_t count, loff_t *offset ) { int val;
//read A/D conversion data from ADCDAT0 val = readl(adc_base+S3C_ADCDAT0) & 0xfff; printk(KERN_INFO "val = %x\n", val);
if( copy_to_user(buffer, &val, sizeof(val)) != 0 ) { printk( KERN_ERR "copy to user failed\n" ); return -1; } return sizeof(val); }
static struct file_operations adc_fops = { .owner = THIS_MODULE, .open = adc_open, .release = adc_close, .read = adc_read, };
static int __init adc_init( void ) { int res; res = register_chrdev( adc_major, "adc", &adc_fops ); if ( res < 0 ) { printk( KERN_ERR "%s failed.\n", __FUNCTION__ ); return res; } else { if ( adc_major ) printk ( KERN_INFO "major number of adc is %d\n", adc_major ); else { adc_major = res; printk ( KERN_INFO "major number of adc is %d\n", adc_major ); } }
return 0; }
static void __exit adc_cleanup( void ) { unregister_chrdev( adc_major, "adc" ); }
module_init(adc_init); module_exit(adc_cleanup);
2、 测试程序代码
#include <stdio.h> #include <fcntl.h>
int main( int argc, char *argv[] ) { int fd = open( "/dev/adc", O_RDWR ); if( fd < 0 ) { perror( "open /dev/adc error" ); return -1; }
int nread = 0; int adc_val = 0; while( 1 ) { nread = read( fd, (void *)&adc_val, sizeof(adc_val) ); if( nread < 0 ) { perror( "read adc" ); return -1; } sleep( 1 ); }
close( fd ); return 0; }
3、 AD转换接口原理图:
|