前面的学习中多次了解到linux内核对于各种常见驱动的支持相当的好,ADC也不例外。但是为了驱动的学习,参考datasheet的内容以及网友的博客文章,依然自己编写单独的ADC驱动模块。下面对ADC驱动的学习做一个简单的总结。
在s3c2440中集成了一个8通道的10位ADC,其从有4个通道即XP, XM, YP, YM连接到触摸屏的接口,用来检测转换触摸屏的模拟信号。剩下的4个通道A0,A1,A2,A3可以外接设备采集外设的模拟信号。其最高转换速率可达到5KHz.
在FL2440的开发板上,ADC的通道0外接一个电位器CN3,当调节电位器CN3的阻值,通道0采集到的电压值会随之在0~3.3V 的范围变化。
参考datasheet和网上的博客文章,下面是本人编写的驱动和测试程序。
/********************************************************************************
* Copyright: (C) 2017 Li Wanneng
* All rights reserved.
*
* Filename: s3c_adc.h
* Description: This head file is a driver for adc
*
* Version: 1.0.0(04/21/2017)
* Author: Li Wanneng
* ChangeLog: 1, Release initial version on "04/21/2017 02:43:21 PM"
*
********************************************************************************/
#include
#include
#include
#include
#include
#include /* copy_to_user copy_from_user */
#include
#include
#include
#include
#include
#include
#include
#include
#define DRV_AUTHOR "Liwanneng "
#define DRV_DESC "S3C2440 ADC DRIVER"
#define DEV_NAME "S3C_ADC"
/* Set ADC dev major number*/
#ifndef ADC_MAJOR
#define ADC_MAJOR 0
#endif
#define DRV_MAJOR_VER 1
#define DRV_MINOR_VER 0
#define DRV_REVER_VER 0
#define DISABLE 0
#define ENABLE 1
#define S3C_ADC_CON 0x58000000
#define S3C_CON_LEN 4
#define S3C_ADC_DATA 0x5800000C
#define S3C_DATA_LEN 4
#define PRSC_EN 1
#define PRSC_VAL 49
#define SEL_MUX 0
static void __iomem *adc_con;
static void __iomem *adc_data;
#define s3c_adc_write(val, reg) __raw_writel((val),(reg))
#define s3c_adc_read(reg) __raw_readl((reg))
static struct clk *adc_clk;
DEFINE_MUTEX(ADC_CLK);
int debug = DISABLE;
int dev_cnt = 1;
int dev_major = ADC_MAJOR;
int dev_minor = 0;
static struct cdev *adc_cdev;
static ssize_t adc_read(struct file *file, char *buff, size_t count,loff_t *ppos)
{
unsigned temp;
volatile unsigned long adc_value;
adc_con = ioremap(S3C_ADC_CON, S3C_CON_LEN);
adc_data = ioremap(S3C_ADC_DATA,S3C_DATA_LEN);
temp = s3c_adc_read(adc_con);
temp = (PRSC_EN << 14) | (PRSC_VAL << 6) | (SEL_MUX << 3);/* 0 1 00000011 000 0 0 0 */
s3c_adc_write(temp, adc_con);/* Enable prescaler */
temp = s3c_adc_read(adc_con);
temp = temp | (1 << 0);
s3c_adc_write(temp, adc_con); /* Enable ADC Conversion start */
printk(KERN_INFO "write in kernel.\n");
while(s3c_adc_read(adc_con) &0x1);
printk(KERN_INFO "write in kernel.\n");
while(!(s3c_adc_read(adc_con) &0x8000));
printk(KERN_INFO "will lock in kernel.\n");
mutex_lock(&ADC_CLK); /* Lock */
printk(KERN_INFO "locked in kernel.\n");
adc_value = s3c_adc_read(adc_data) &0x3ff;/* Acquire adc_value */
printk(KERN_INFO "adc_value getted.\n");
copy_to_user(buff, (char *)&adc_value, sizeof(adc_value));
mutex_unlock(&ADC_CLK);/* Unlock */
return sizeof(adc_value);
}
static int adc_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
file->private_data = (void *)minor;
printk(KERN_INFO "/dev/S3C_ADC%d opened.\n", minor);
return 0;
}
static int adc_release(struct inode *inode, struct file *file )
{
return 0;
}
static struct file_operations adc_fops = {
.owner = THIS_MODULE,
.open = adc_open,
.read = adc_read,
.release = adc_release,
};
static int __init s3c_adc_init(void)
{
int result;
dev_t devno;
if(0!=dev_major)
{
devno = MKDEV(dev_major, 0);
result = register_chrdev_region(devno, dev_cnt, DEV_NAME);
}
else
{
result = alloc_chrdev_region(&devno, dev_minor, dev_cnt, DEV_NAME);
dev_major = MAJOR(devno);
}
if(result<0)
{
printk(KERN_ERR "%s driver can't use major %d\n", DEV_NAME, dev_major);
return -ENODEV;
}
printk(KERN_DEBUG "%s driver use major %d\n", DEV_NAME, dev_major);
adc_clk = clk_get(NULL,"s3c_adc");
if(!adc_clk)
{
printk(KERN_ERR "failed to find adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clk);
if(NULL == (adc_cdev=cdev_alloc()) )
{
printk(KERN_ERR "S3C %s driver can't alloc for the cdev.\n", DEV_NAME);
unregister_chrdev_region(devno, dev_cnt);
return -ENOMEM;
}
adc_cdev->owner = THIS_MODULE;
cdev_init(adc_cdev, &adc_fops);
result = cdev_add(adc_cdev, devno, dev_cnt);
if(0!=result)
{
printk(KERN_INFO "S3C %s driver can't reigster cdev: result=%d\n", DEV_NAME, result);
goto ERROR;
}
printk(KERN_INFO "%s driver[major=%d] version %d.%d.%d installed successfully!\n",DEV_NAME, dev_major, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);
return 0;
ERROR:
printk(KERN_ERR "S3C %s driver installed failure.\n", DEV_NAME);
cdev_del(adc_cdev);
unregister_chrdev_region(devno, dev_cnt);
return result;
}
static void __exit s3c_adc_exit(void)
{
dev_t devno = MKDEV(dev_major, dev_minor);
cdev_del(adc_cdev);
unregister_chrdev_region(devno, dev_cnt);
iounmap(adc_con);
iounmap(adc_data);
if(adc_clk)
{
clk_disable(adc_clk);
clk_put(adc_clk); /* ERROR */
printk("exit4\n");
adc_clk = NULL;
printk("exit5\n");
}
printk(KERN_ERR "S3C %s driver version %d.%d.%d removed!\n",DEV_NAME, DRV_MAJOR_VER, DRV_MINOR_VER,DRV_REVER_VER);
return ;
}
/* This two function defined in */
module_init(s3c_adc_init);
module_exit(s3c_adc_exit);
module_param(debug, int, S_IRUGO);
module_param(dev_major, int, S_IRUGO);
MODULE_AUTHOR(DRV_AUTHOR);
MODULE_DESCRIPTION(DRV_DESC);
MODULE_LICENSE("GPL");
[lwn@localhost adc]$ vim Makefile
LINUX_SRC?=~/fl2440/kernel/linux-lwn-3.0.1
CROSS_COMPILE=/opt/dl/buildroot-2012.08/ARM920t/usr/bin/arm-linux-
obj-m := adc.o
modules:
@make -C $(LINUX_SRC) M=`pwd` modules
@make clean
clean:
rm -f *.ko.* *.o *mod.c *.order *.symvers
/*********************************************************************************
* Copyright: (C) 2017 Li Wanneng
* All rights reserved.
*
* Filename: adc_test.c
* Description: This file test s3c_adc driver
*
* Version: 1.0.0(04/21/2017)
* Author: Li Wanneng
* ChangeLog: 1, Release initial version on "04/21/2017 07:36:33 PM"
*
********************************************************************************/
#include
#include
#include
#include
#include
#include
#include
/********************************************************************************
* Description:
* Input Args:
* Output Args:
* Return Value:
********************************************************************************/
int main (int argc, char **argv)
{
int fd;
fd = open("/dev/S3C_ADC",O_RDONLY);
if(fd<0)
{
printf("open S3C_ADC device failed.\n");
exit(1);
}
printf("fd>0\n");
while(1)
{
int ret;
int data;
printf("while read in user\n");
ret = read(fd, &data, sizeof(data));
printf("readed in user\n");
if(sizeof(data) != ret)
{
if(errno != EAGAIN)
{
printf("Read ADC Device Faild!\n");
}
continue;
}
else
{
printf("Read ADC value is: %d\n", data);
}
sleep(1);
}
close(fd);
return 0;
} /* ----- End of main() ----- */
编写好以上程序之后,接下来下载到开发板测试运行。
1.在虚拟机中用make命令和gcc交叉编译器编译驱动程序s3c_adc.c和测试程序test_adc.c:
2.接下来将编译生成的驱动文件和测试文件下载到开发板测试ADC(因为我开发板的AD模块坏了,所以在同学的板子上运行,就是界面丑了点蛤)