打印

请问at91sam9260自带ADC转换的问题....

[复制链接]
4473|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
Lx_mcu|  楼主 | 2011-3-14 20:19 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
我在linux上修改了一下sam9260的adc驱动,单个通道转换正常,如果把4个通道都使能,定时2秒触发一次,通道0 1 2正常, 通道3要10多秒才产生一次中断;
各位大侠帮忙看看,问题在哪里,谢谢!

下面是代码:

/*
* driver/char/at91_adc.c
*
* Copyright (C) 2007 Embedall Technology Co., Ltd.
*
* Analog-to-digital Converter(ADC) Driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/ioctl.h>
#include <linux/poll.h>
//#include <linux/class.h>
#include <mach/at91_adc.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <mach/at91_adc.h>
#include <mach/at91_tc.h>
#include <mach/at91_pmc.h>
#include "adc.h"

#define DRV_NAME "at91_adc"
#define DEV_NAME "adc"
#define adc_readl(adc,reg) (__raw_readl((adc)->membase + (reg)))
#define adc_writel(adc,reg,v) (__raw_writel((v), (adc)->membase + (reg)))
#define tc_readl(adc,reg) (__raw_readl((adc)->tcxbase + (reg)))
#define tc_writel(adc,reg,v) (__raw_writel((v), (adc)->tcxbase + (reg)))

#define ADC_MAX_CHANNEL 4
#define BUF_SIZE 8
#define buf_cnt(channel) (((channel)->head - (channel)->tail) & ((BUF_SIZE)-1))
#define buf_space(channel) (((channel)->tail-((channel)->head+1))&(BUF_SIZE-1))
struct adc;
struct adc_channel
{
struct cdev cdev;
struct device *dev;
struct class_device *class_dev;

int id;

int buf[BUF_SIZE];
int head;
int tail;

struct fasync_struct *fasync;
struct adc *adc;
};
struct adc
{
dev_t devt;
struct class *class;
void __iomem *membase;
void __iomem *tcbbase;
void __iomem *tcxbase;
unsigned int irq;
struct adc_mode mode;

spinlock_t lock;

struct adc_channel *channel[ADC_MAX_CHANNEL];
};

static struct adc *adc;
static int buf_in(struct adc_channel *channel, int v)
{
channel->buf[channel->head] = v;
channel->head = (channel->head + 1) & (BUF_SIZE - 1);
return v;
}
static int adc_init_tc(struct adc *adc)
{
unsigned int dummy = 0;
spin_lock(&adc->lock);

if (adc->mode.trigger != ADC_TRIGGER_TIMER)
{
  at91_sys_write(AT91_PMC_PCDR, 1 << AT91SAM9260_ID_TC2);
  spin_unlock(&adc->lock);
  return 0;
}
tc_writel(adc, AT91_TC_CCR, AT91_TC_CLKDIS);
dummy |= (AT91_TC_TIMER_CLOCK5 | AT91_TC_CPCTRG | AT91_TC_WAVE |
AT91_TC_WAVESEL_UP_AUTO | AT91_TC_ACPA_SET |
AT91_TC_ACPC_CLEAR );
tc_writel(adc, AT91_TC_CMR, dummy);

if (adc->mode.trigger_time)
{
  dummy = (adc->mode.trigger_time*1000000)/(1000000000/32768);
  if (dummy > 0xffff) dummy = 0xffff;
  tc_writel(adc, AT91_TC_RC, dummy);
  tc_writel(adc, AT91_TC_RA, dummy * 3 / 5);
}
else
{
  tc_writel(adc, AT91_TC_RC, 32768);
  tc_writel(adc, AT91_TC_RA, 32768 * 3 / 5);
}

at91_sys_write(AT91_PMC_PCER, 1 << AT91SAM9260_ID_TC2);
tc_writel(adc, AT91_TC_CCR, AT91_TC_CLKEN | AT91_TC_SWTRG);
spin_unlock(&adc->lock);
return 0;
}
static int adc_hw_init(struct adc *adc)
{
adc_writel(adc, AT91_ADC_CR, AT91_ADC_SWRST); //软件复位
adc_writel(adc, AT91_ADC_IER, AT91_ADC_DRDY); //enables the corresponding interrput ,Data ready interrupt enable
at91_sys_write(AT91_PMC_PCER, 1 << adc->irq); //clock enable
return 0;
}
static int adc_fasync(int fd, struct file *file, int mode)
{
struct adc_channel *channel = file->private_data;
return fasync_helper(fd, file, mode, &channel->fasync);
}
static int adc_open(struct inode *inode, struct file *file)
{
struct adc *adc;
struct adc_channel *channel;
channel = container_of(inode->i_cdev, struct adc_channel, cdev);
file->private_data = channel;
adc = channel->adc;

spin_lock(&adc->lock); //获取自旋锁
at91_set_multi_drive(PIN_BASE + 0x40 + channel->id, 1);
adc_writel(adc, AT91_ADC_IER, (1 << channel->id));
adc_writel(adc, AT91_ADC_CHER, (1 << channel->id));
spin_unlock(&adc->lock);

return nonseekable_open(inode, file);
}
static int adc_release(struct inode *inode, struct file *file)
{
struct adc *adc;
struct adc_channel *channel;
channel = container_of(inode->i_cdev, struct adc_channel, cdev);
adc = channel->adc;

// adc_fasync(-1, file, 0);

spin_lock(&adc->lock);
adc_writel(adc, AT91_ADC_IDR, 1 << channel->id);
adc_writel(adc, AT91_ADC_CHDR, 1 << channel->id);
spin_unlock(&adc->lock);

return 0;
}
static ssize_t adc_read(struct file *file, char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
static ssize_t adc_write(struct file *file, const char __user *buf, size_t count, loff_t *f_pos)
{
return 0;
}
/* static void adc_sigio(struct adc_channel *channel) */
/* { */
/* if (channel->fasync) */
/* kill_fasync(&channel->fasync, SIGIO, POLL_IN); */
/* } */
static int adc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct adc *adc;
struct adc_channel *channel;
struct adc_mode *mode;

int ret = 0;
unsigned int dummy = 0;

void __user *argp = (void __user *)arg;
int __user *p = argp;

channel = container_of(inode->i_cdev, struct adc_channel, cdev);
adc = channel->adc;
mode = &adc->mode;

switch (cmd)
{
  case ADCCTL_RESET:
   adc_writel(adc, AT91_ADC_CR, AT91_ADC_SWRST);
   return 0;
   
  case ADCCTL_START:
   adc_writel(adc, AT91_ADC_CR, AT91_ADC_START);
   return 0;
  
  case ADCCTL_SETMODE:
   ret = copy_from_user(mode, argp, sizeof(struct adc_mode));
   
   if (mode->trigger == ADC_TRIGGER_TIMER)
    dummy |= AT91_ADC_TRGEN | AT91_ADC_TRGSEL_TC2;
   else if (mode->trigger == ADC_TRIGGER_EXT)
    dummy |= AT91_ADC_TRGEN | AT91_ADC_TRGSEL_EXTERNAL;
   
   if (mode->resolution & ADC_M_8BIT)
    dummy |= AT91_ADC_LOWRES;
   
   if (mode->sleep_mode)
    dummy |= AT91_ADC_SLEEP;
   
   if (mode->adc_clock)
    dummy |= AT91_ADC_PRESCAL_(mode->adc_clock);
   
   if (mode->startup_time)
    dummy |= AT91_ADC_STARTUP_(mode->startup_time);
   
   if (mode->sample_time)
    dummy |= AT91_ADC_SHTIM_(mode->sample_time);
   
   adc_init_tc(adc);
   spin_lock(&adc->lock);
   adc_writel(adc, AT91_ADC_MR, dummy);
   spin_unlock(&adc->lock);
   return 0;
  
  case ADCCTL_GETMODE:
   ret = copy_to_user(argp, mode, sizeof(struct adc_mode));
   return 0;
  
  case ADCCTL_GETDATA:
  ret = buf_cnt(channel);
   if (ret > 0)
   {
    if (!put_user(channel->buf[channel->tail], p))
     channel->tail = (channel->tail + 1) & (BUF_SIZE -1);
    return 0;
   }
   return -EFAULT;
  
  case ADCCTL_GETCNT:
   return put_user(buf_cnt(channel), p);
  
  case ADCCTL_GETSTATUS:
   return put_user(adc_readl(adc, AT91_ADC_SR), p);
  
  default:
   return -EINVAL;
}

return -EINVAL;
}
static struct file_operations adc_fops = {
.owner = THIS_MODULE,
.read = adc_read,
.write = adc_write,
.open = adc_open,
.release = adc_release,
.ioctl = adc_ioctl,
.fasync = adc_fasync,
};
static irqreturn_t adc_interrupt(int irq, void *dev_id)
{
struct adc *adc = dev_id;
struct adc_channel *channel;
int ivar;
unsigned int status;

status = adc_readl(adc, AT91_ADC_SR) & adc_readl(adc, AT91_ADC_IMR);
while (status)
{
  printk(KERN_INFO "at91_adc: interrupt status reg 0x%08x\n", status);
  
  if (status & AT91_ADC_EOC(0))
  {
   channel = adc->channel[0];
   ivar = buf_in(channel, adc_readl(adc, AT91_ADC_CHR(0)));
   printk(KERN_INFO "the channel 0 data is %d\n", ivar);
   // adc_sigio(channel);
  }
  
  if (status & AT91_ADC_EOC(1))
  {
   channel = adc->channel[1];
   ivar = buf_in(channel, adc_readl(adc, AT91_ADC_CHR(1)));
   printk(KERN_INFO "the channel 1 data is %d\n", ivar);
   // adc_sigio(channel);
  }
  
  if (status & AT91_ADC_EOC(2))
  {
   channel = adc->channel[2];
   ivar = buf_in(channel, adc_readl(adc, AT91_ADC_CHR(2)));
   printk(KERN_INFO "the channel 2 data is %d\n", ivar);
   // adc_sigio(channel);
  }
  
  if (status & AT91_ADC_EOC(3))
  {
   channel = adc->channel[3];
   ivar = buf_in(channel, adc_readl(adc, AT91_ADC_CHR(3)));
   printk(KERN_INFO "the channel 3 data is %d\n", ivar);
   // adc_sigio(channel);
  }
   
  if (status & AT91_ADC_DRDY)
   adc_readl(adc, AT91_ADC_LCDR);
  
  status = adc_readl(adc, AT91_ADC_SR) & adc_readl(adc, AT91_ADC_IMR);
}

return IRQ_HANDLED;
}
static int __init adc_probe(struct platform_device *pdev)
{
struct adc_channel *channel;
int ret;
channel = kmalloc(sizeof(struct adc_channel), GFP_KERNEL);
if (!channel)
{
  printk(KERN_ERR "at91_adc: failed to kmalloc channel %d\n", pdev->id);
  return -ENOMEM;
}
// channel->fasync = kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
// if (!channel->fasync) return -ENOMEM;
channel->id = pdev->id;
channel->dev = &pdev->dev;
channel->adc = adc;
channel->head = 0;
channel->tail = 0;
cdev_init(&channel->cdev, &adc_fops);
channel->cdev.owner = THIS_MODULE;
ret = cdev_add(&channel->cdev, MKDEV(MAJOR(adc->devt), pdev->id), 1);
if (ret)
{
  printk(KERN_ERR "at91_adc: failed to add channel %d device\n", pdev->id);
  
  kfree(channel);
  return ret;
}  

channel->class_dev = device_create(adc->class, NULL,  MKDEV(MAJOR(adc->devt),
pdev->id),
channel->dev,
DEV_NAME"%d", pdev->id);

if (IS_ERR(channel->class_dev))
{
  cdev_del(&channel->cdev);
  kfree(channel);
  return PTR_ERR(channel->class_dev);
}
adc->channel[pdev->id] = channel;
platform_set_drvdata(pdev, channel);

printk(KERN_INFO "at91_adc.%d: register at /dev/adc%d (%d:%d)\n",
pdev->id, pdev->id, MAJOR(adc->devt), pdev->id);
return 0;
}
static int __exit adc_remove(struct platform_device *pdev)
{
struct adc_channel *channel = platform_get_drvdata(pdev);
device_destroy(adc->class, MKDEV(MAJOR(adc->devt), pdev->id));//delete device node under /dev
cdev_del(&channel->cdev);  
kfree(channel);  
return 0;
}
static struct platform_driver adc_driver = {
.probe = adc_probe,
.remove = __exit_p(adc_remove),
.driver =
{
  .name = DRV_NAME,
  .owner = THIS_MODULE,
},
};
void  adc_dev_release(struct device * dev)
{

}
static struct platform_device adc_channel_device[ADC_MAX_CHANNEL];
static int __init adc_add_channel_device(void)
{
int i = 0;

for (i=0; i<ADC_MAX_CHANNEL; i++)
{
  adc_channel_device[i].name = DRV_NAME;
  adc_channel_device[i].id = i;
  adc_channel_device[i].dev.release = adc_dev_release;
  platform_device_register(&adc_channel_device[i]);
}

return 0;
}
static int __init adc_del_channel_device(void)
{
int i = 0;

for (i=0; i<ADC_MAX_CHANNEL; i++)
{
  adc_channel_device[i].name = DRV_NAME;
  adc_channel_device[i].id = i;
  //adc_channel_device[i].dev.platform_data = NULL;
  platform_device_unregister(&adc_channel_device[i]);
}

return 0;
}
static int __init adc_init(void)
{
int ret = 0;

adc = kmalloc(sizeof(struct adc), GFP_KERNEL);
if (!adc)
  return -ENOMEM;
  
//申请分配指定的IO内存资源;
if (!request_mem_region(AT91SAM9260_BASE_ADC, SZ_16K, DRV_NAME))
{
  kfree(adc);
  return -EBUSY;
}

adc->membase = ioremap(AT91SAM9260_BASE_ADC, SZ_16K);
if (adc->membase == NULL)
  goto adc_release_mem;

if (!request_mem_region(AT91SAM9260_BASE_TC0, SZ_16K, DRV_NAME))
  goto adc_iounmap;

adc->tcbbase = ioremap(AT91SAM9260_BASE_TC0, SZ_16K);
if (adc->tcbbase == NULL)
  goto adc_release_mem_tc;

adc->irq = AT91SAM9260_ID_ADC;
adc->tcxbase = adc->tcbbase + 0x80;
spin_lock_init(&adc->lock);

adc->class = class_create(THIS_MODULE, DEV_NAME);
if (IS_ERR(adc->class))
{
  printk(KERN_ERR "at91_adc: faile to create device class\n");
  goto adc_iounmap_tc;
}

ret = alloc_chrdev_region(&adc->devt, 0, ADC_MAX_CHANNEL, DEV_NAME);
if (ret < 0)
{
  printk(KERN_ERR"%s: failed to allocate dev region\n", __FILE__);
  goto adc_destroy_class;
}

if (request_irq(adc->irq, adc_interrupt, IRQF_SHARED, DRV_NAME, adc))
{
  printk(KERN_ERR"%s: request irq failed\n", __FILE__);
  goto adc_del_channel;
}

adc_hw_init(adc); //ADC init ,复位,中断使能,时钟使能

platform_driver_register(&adc_driver);
printk(KERN_INFO "Analog-to-Digital Converter (irq %d)\n", adc->irq);

adc_add_channel_device();

return 0;

adc_del_channel:
unregister_chrdev_region(adc->devt, ADC_MAX_CHANNEL);
adc_destroy_class:
class_destroy(adc->class);
adc_iounmap_tc:
iounmap(adc->tcbbase);
adc_release_mem_tc:
release_mem_region(AT91SAM9260_BASE_TC0, SZ_16K);
adc_iounmap:
iounmap(adc->membase);
adc_release_mem:
release_mem_region(AT91SAM9260_BASE_ADC, SZ_16K);
kfree(adc);
return ret;
}

static void __exit adc_exit(void)
{  
platform_driver_unregister(&adc_driver);  
unregister_chrdev_region(adc->devt, ADC_MAX_CHANNEL);  
class_destroy(adc->class);  
free_irq(adc->irq, adc);
iounmap(adc->tcbbase);
release_mem_region(AT91SAM9260_BASE_TC0, SZ_16K);
iounmap(adc->membase);
release_mem_region(AT91SAM9260_BASE_ADC, SZ_16K);
kfree(adc);
  
adc_del_channel_device();
}

module_init(adc_init);
module_exit(adc_exit);

相关帖子

沙发
l0p0c| | 2011-3-15 22:00 | 只看该作者
如此之长啊……

使用特权

评论回复
板凳
盈安信科技| | 2011-3-16 21:49 | 只看该作者
看代码我就晕了,

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

11

主题

30

帖子

0

粉丝