前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >i.MX283开发板MISC设备驱动——LRADC

i.MX283开发板MISC设备驱动——LRADC

作者头像
知否知否应是绿肥红瘦
发布于 2025-02-19 13:20:49
发布于 2025-02-19 13:20:49
7600
代码可运行
举报
文章被收录于专栏:Linux知识Linux知识
运行总次数:0
代码可运行

MISC设备:

​​​​​​MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。MISC 设备会自动创建 cdev,也不需要创建class和device,它实际上也属于字符设备——只不过是简化的字符设备。

下面是misc设备结构体的定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct miscdevice  {
	int minor;
	const char *name;
	const struct file_operations *fops;
	struct list_head list;
	struct device *parent;
	struct device *this_device;
	const char *nodename;
	mode_t mode;
};
/*
minor:次设备号 
name:设备名字
fops:file_operations 结构体
parent:这个指针决定了在/sys文件系统里面,它是创建在哪个目录下。如果为空就在/sys/class根目录下创建,如果不为空都是在/sys/class/misc 文件下面创建的一些属性文件。
this_device:这个就代表当前设备的设备结构体
*/

主要就是minor、name、fops三个参数,其中minor如果定义为MISC_DYNAMIC_MINOR(255),表示由内核动态分配次设备号;name 就是此 MISC 设备名字;fops就是open、read、write这些调用的集合。

所以对于MISC设备,我们只需填充上面三个参数就可以了。

定义并填充好一个miscdevice类型的结构体后,就可以用以下函数注册设备了,它会自动创建设备节点并在/dev 目录下生成一个名为name的设备文件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int misc_register(struct miscdevice * misc)

 对应的注销函数如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int misc_deregister(struct miscdevice *misc)

下面就利用MISC设备驱动框架,编写一个ADC的驱动程序。

LRADC(低精度ADC)

imx283开发板有16个LRADC物理通道,同时还有8个虚拟通道,这8个虚拟通道可以同时使用,且每一个通道都可以映射到16个物理信道。但是只有通道0~6可以用于一般用途,它们内部都有硬件除2电路可供配置,虚拟通道7专门测量电池电压,其内部有一个硬件除4电路。

LRADC主要特点如下:

  • 在未开启除 2 电路时,其量程为 0~1.85V,开启除 2 电路时,量程为 0~3.7V,有寄存器可配置是否开启
  • LRADC内部参考电压 1.85V
  • LRADC 主时钟为24MHz,可以配置4/6/8/12分频,采样率=CLK/16

LRADC配置流程如下:

ADC复位——配置时钟——配置采样率、采集次数、数据长度等参数——配置中断、清除相关标志位——启动ADC——等待转换完成——读取数据——清除标志位——开启下一次转换

LRADC的寄存器地址如下:

注意:imx28x系列芯片大多数寄存器的置位、复位、翻转操作是写不同的寄存器。比如HW_LRADC_CTRL0

寄存器的地址是0x80050000

HW_LRADC_CTRL0: 0x80050000 HW_LRADC_CTRL0_SET: 0x80050004 HW_LRADC_CTRL0_CLR: 0x80050008 HW_LRADC_CTRL0_TOG: 0x8005000C

它的置位、复位、翻转寄存器依次偏移4字节。

该组寄存器的基地址是0x80050000,若是裸机开发,我们可以直接对这个地址进行读写,但是在Linux上是不行的,Linux是不允许程序直接访问物理地址的,它有一个MMU内存管理单元,MMU的一个作用就是负责内存保护,设置存储器的访问权限。但MMU的另一个功能就是内存映射,就是将一段物理内存映射到一段虚拟内存上,这样实际上就间接的访问了真实的物理内存,这里需要用到两个函数:ioremap 和 iounmap。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#define ioremap(cookie,size)		__arm_ioremap(cookie, size, MT_DEVICE)
void __iomem *__arm_ioremap(unsigned long phys_addr, size_t size,
			    unsigned int mtype)
{
	return (void __iomem *)phys_addr;
}


#define iounmap(cookie)			__iounmap(cookie)
void __iounmap(volatile void __iomem *io_addr)
{
	void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr);
#ifndef CONFIG_SMP
	struct vm_struct **p, *tmp;
	write_lock(&vmlist_lock);
	for (p = &vmlist ; (tmp = *p) ; p = &tmp->next) {
		if ((tmp->flags & VM_IOREMAP) && (tmp->addr == addr)) {
			if (tmp->flags & VM_ARM_SECTION_MAPPING) {
				unmap_area_sections((unsigned long)tmp->addr,
						    tmp->size);
			}
			break;
		}
	}
	write_unlock(&vmlist_lock);
#endif

	vunmap(addr);
}

ioremap 参数cookie就是要映射的起始地址,size就是需要映射空间的大小

iounmap 只有一个参数 addr,此参数就是要取消映射的虚拟地址空间首地址

除此之外,我们还需要内存访问函数来访问映射后的地址,使用 ioremap 函数将寄存器的物理地址映射到虚拟地址以后,我们就可以直接通过指针访问这些地址,但是 Linux 内核不建议这么做,而是推荐使用一组操作函数来对映射后的内存进行读写操作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static inline void __raw_writeb(u8 b, volatile void __iomem *addr)
{
	*(volatile u8 __force *) addr = b;
}

static inline void __raw_writew(u16 b, volatile void __iomem *addr)
{
	*(volatile u16 __force *) addr = b;
}

static inline void __raw_writel(u32 b, volatile void __iomem *addr)
{
	*(volatile u32 __force *) addr = b;
}

#define writeb __raw_writeb
#define writew(b,addr) __raw_writew(__cpu_to_le16(b),addr)
#define writel(b,addr) __raw_writel(__cpu_to_le32(b),addr)

 writeb、writew 和 writel 这三个函数分别对应 8bit、16bit 和 32bit 写操作,参数 value 是要写入的数值,addr 是要写入的地址。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static inline u8 __raw_readb(const volatile void __iomem *addr)
{
	return *(const volatile u8 __force *) addr;
}

static inline u16 __raw_readw(const volatile void __iomem *addr)
{
	return *(const volatile u16 __force *) addr;
}

static inline u32 __raw_readl(const volatile void __iomem *addr)
{
	return *(const volatile u32 __force *) addr;
}

#define readb __raw_readb
#define readw(addr) __le16_to_cpu(__raw_readw(addr))
#define readl(addr) __le32_to_cpu(__raw_readl(addr))

readb、readw 和 readl 这三个函数分别对应 8bit、16bit 和 32bit 读操作,参数 addr 就是要读取写内存地址,返回值就是读取到的数据。

接下来,就是编写驱动程序了,具体的寄存器配置就不一一介绍了,这里直接给出驱动源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<linux/module.h>      /* module */
#include<linux/fs.h>          /* file operation*/
#include<asm/uaccess.h>       /* get_user()*/
#include<linux/miscdevice.h>  /* miscdevice*/
#include<asm/io.h>            /* ioctl*/
#include <mach/regs-lradc.h> /* #define*/

#include <linux/kernel.h>
#include <linux/sysdev.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/delay.h>

#include "mach/adc_driver.h"

#define DEBUG_T 1  //调试开关
#define DEVICE_NAME	"imx283_adc"//驱动名称
#define LRADC_BASE  0x80050000 //LRADC基地址 实际物理地址
static void __iomem *adc_base = NULL;//映射后LRADC基地址


static int adc_open(struct inode *inode, struct file *fp)
{
   
   /*SFTRST=0 HSADC退出复位进入正常模式*/
   writel(1<<31,adc_base+0x08);//HW_HSADC_CTRL0_CLR
   
   /*CLKGATE=0 进入正常模式*/
   writel(1<<30,adc_base+0x08);//HW_HSADC_CTRL0_CLR
   
   /*失能ADC通道0/1/6中断和按键中断*/
   writel((1<<28)|(1<<27)|(1<<22)|(1<<17)|(1<<16),adc_base+HW_LRADC_CTRL1_CLR);

   /*清除ADC0/1/6 标志位*/
   writel((1<<6)|(1<<1)|(1<<0),adc_base+HW_LRADC_CTRL1_CLR);
   
   /*使能0/1/2/6通道硬件/2电路      ADC量程为0~3.7V*/ 
   writel((1<<30)|(1<<25)|(1<<24), adc_base + HW_LRADC_CTRL2_SET);
   
   /*丢弃初次ADC转换的前3次采样值           */
   writel(3<<24,adc_base+HW_LRADC_CTRL3_SET);

   /*配置  ADC时钟为4M 采样率=4M/16*/ 
   writel(1<<9,adc_base+HW_LRADC_CTRL3_CLR);
   writel(1<<8,adc_base+HW_LRADC_CTRL3_SET);
   
   /*指定转换ADC通道0~7对应的实际通道*/
   writel(0xFFFFFFFF,adc_base+HW_LRADC_CTRL4_CLR);
   writel(0x76543210,adc_base+HW_LRADC_CTRL4_SET);
   
#if (DEBUG_T)	   
   printk("HW_LRADC_CTRL0=%08X\n",readl(adc_base+HW_LRADC_CTRL0));
   printk("HW_LRADC_CTRL1=%08X\n",readl(adc_base+HW_LRADC_CTRL1));
   printk("HW_LRADC_CTRL2=%08X\n",readl(adc_base+HW_LRADC_CTRL2));
   printk("HW_LRADC_CTRL3=%08X\n",readl(adc_base+HW_LRADC_CTRL3));
   printk("HW_LRADC_CTRL4=%08X\n",readl(adc_base+HW_LRADC_CTRL4));
   printk("HW_LRADC_VERSION=%08X\n",readl(adc_base+HW_LRADC_VERSION));
#endif   

   return 0;
   
}


static int adc_release (struct inode *inode, struct file *fp)
{
     /*失能ADC,让ADC进入复位模式*/
	writel((1<<31)|(1<<30), adc_base + HW_LRADC_CTRL0_SET);	  
	return 0;
}



static int adc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{

   int ch = 0;
   int adc_res = 0;
   if(_IOC_TYPE(cmd)!=ADC_IOC_MAGIC)
   {
     return -ENOTTY;//命令参数错误
   }
   
  /* switch(cmd)
   	{
      case ADC_CH0:
		break;
	  case ADC_CH1:
	   break;
	  case ADC_CH6:
	  	break;
	  default:
		printk("adc control cmd invalid!!\n");
		return -1;
   }*/

   ch = _IOC_NR(cmd);//获取通道号
   
   printk("ch=%d\n",ch);
   
   /*ADC采集不连续 单次模式*/     	
   writel(1<<29,adc_base+HW_LRADC_CHn_CLR(ch));
   
   /*采集样本数为1*/
   writel(0x1f<<24,adc_base+HW_LRADC_CHn_CLR(ch));

   /*清空ADC转换结果寄存器*/
   writel(0x3FFFF<<0,adc_base+HW_LRADC_CHn_CLR(ch));
   
   /*启动ADC开始转换*/
   writel((1<<ch),adc_base+HW_LRADC_CTRL0_SET);
   
   /*等待ADC转换完成*/
   while((readl(adc_base+HW_LRADC_CTRL1)&(1<<ch))==0)
   cpu_relax();
   
   /*清除flag*/
   writel((1<<ch),adc_base+HW_LRADC_CTRL1_CLR);
   
   /*读取ADC转换结果*/
   adc_res = readl(adc_base+HW_LRADC_CHn(ch)) & 0x3FFFF;	
   
   copy_to_user((void *)arg, (void *)(&adc_res), sizeof(int));
   return 0;
}


static struct file_operations adc_fops = 
{
  .owner		= THIS_MODULE,
  .open		    = adc_open,
  .release	    = adc_release,
  .ioctl      	= adc_ioctl,
};

/*定义misc设备*/
static struct miscdevice adc_miscdevice =
{
   .minor	= MISC_DYNAMIC_MINOR, 
   .name    = DEVICE_NAME,
   .fops    =&adc_fops,
};

static int __init adc_init(void)
{
    int ret;
    /*映射物理地址*/
    adc_base = ioremap(LRADC_BASE, 180*4);
	/*注册MISC设备*/
	ret = misc_register(&adc_miscdevice);
	if(ret < 0)
	{
      printk("misc register error %d \n",ret);
	  return ret;
	}
   printk("module init ok\n");
   return 0;

}

static void __exit adc_exit(void)
{
   /*注销设备*/
  misc_deregister(&adc_miscdevice);
   
   /*取消地址映射*/
  iounmap(adc_base);
   
  printk("module exit ok \n");
}

module_init(adc_init);
module_exit(adc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzx2020");

adc_driver.h的内容为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#ifndef __ADC_DRIVER_H
#define __ADC_DRIVER_H

#define ADC_IOC_MAGIC     'j'

#define ADC_CH0     	_IOR(ADC_IOC_MAGIC, 0, int)	/* 通道0			*/
#define ADC_CH1     	_IOR(ADC_IOC_MAGIC, 1, int)	/* 通道1			*/
#define ADC_CH2     	_IOR(ADC_IOC_MAGIC, 2, int)	/* 通道2			*/
#define ADC_CH3   	    _IOR(ADC_IOC_MAGIC, 3, int)	/* 通道3			*/
#define ADC_CH4     	_IOR(ADC_IOC_MAGIC, 4, int)	/* 通道4			*/
#define ADC_CH5     	_IOR(ADC_IOC_MAGIC, 5, int)	/* 通过5			*/
#define ADC_CH6     	_IOR(ADC_IOC_MAGIC, 6, int)	/* 通道6			*/
#define ADC_VBAT     	_IOR(ADC_IOC_MAGIC, 7, int)	/* 测量电池电压			*/

#define ADC_CH0_DIV2     	_IOW(ADC_IOC_MAGIC, 20, int)	/* 通道0,开启除 2		*/
#define ADC_CH1_DIV2     	_IOW(ADC_IOC_MAGIC, 21, int)	/* 通道1,开启除 2		*/
#define ADC_CH2_DIV2     	_IOW(ADC_IOC_MAGIC, 22, int)	/* 通道2,开启除 2		*/
#define ADC_CH3_DIV2   	_IOW(ADC_IOC_MAGIC, 23, int)	/* 通道3,开启除 2		*/
#define ADC_CH4_DIV2     	_IOW(ADC_IOC_MAGIC, 24, int)	/* 通道4,开启除 2		*/
#define ADC_CH5_DIV2     	_IOW(ADC_IOC_MAGIC, 25, int)	/* 通过5,开启除 2		*/
#define ADC_CH6_DIV2     	_IOW(ADC_IOC_MAGIC, 26, int)	/* 通道6,开启除 2		*/
#define ADC_VBAT_DIV4     _IOW(ADC_IOC_MAGIC, 27, int)	/* 测量电池电压,开启除4		*/

#endif

adc_driver.h文件必须和引用的位置保持一致,我这里引用的是#include "mach/adc_driver.h",所以我这个文件放在内核源码

arch/arm/mach-mx28/include/mach下。

接下来我们需要编写一个测试程序,测试程序也需要用到上面的adc_driver.h文件,测试程序如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>	/* using printf()        */
#include<stdlib.h>      /* using sleep()         */
#include<fcntl.h>       /* using file operation  */
#include<sys/ioctl.h>   /* using ioctl()         */
#include <asm/ioctls.h>
#include <unistd.h> //sleep  write read close
#include "adc_driver.h"


int main( int argc, const char * argv [] )
{

   int fd,res,ch;
   if(argc !=2)
   {
     printf("parameter nunmber error \n");
	 return 0;
   }
   ch = atoi(argv[1]);
   fd = open("/dev/imx283_adc",O_RDWR);
   if(fd < 0)
   {
     printf("open imx283_adc error %d\n",fd);
	 close(fd);
	 return 0;
   }

   while(1)
   {
 
      switch(ch) 
      {
      	 case 0:ioctl(fd,ADC_CH0,&res);break;
		 case 1:ioctl(fd,ADC_CH1,&res);break;	 
		 case 6:ioctl(fd,ADC_CH6,&res);break;
		 case 7:ioctl(fd,ADC_VBAT,&res);break;
		 default:ioctl(fd,ADC_CH0,&res);break;
      }
	  printf("ADC_CH%d=%0.2f\n",ch,(double)(res)*3.7/4096);
	  sleep(1);
   }
   close(fd);
   return 0;
}

注意这里的测试程序是需要传入参数的,参数个数为1个,多于或少于1个会提示参数错误。参数说明如下:

传入参数

说明

0

ADC通道0

1

ADC通道1

6

ADC通道6

7

ADC通道7  采集电池电压

其他

ADC通道0

在开发板上加载驱动:

首先我们可以查看/dev下的设备节点:

可以看到imx283_adc的设备节点已经生成,主设备号10,次设备号是55。接下来运行测试APP:

这里通道7电压为2.09V是因为这里计算是按照*2计算的,实际上通道7内部是硬件除4电路,所以这里应该是*4,也就是2.09*2=4.18V。


HSADC(High-Speed ADC)

上面讲到了LRADC,这里就顺便讲下imx283开发板的HSADC,即高速ADC,最高采样率可达2Msps,它的精度最高是12bit。

这部分内容主要讲下HSADC的配置,不涉及太多驱动知识。

高速ADC模块设计用于驱动线性图像扫描仪传感器(例如,东芝TCD1304DG线性图像扫描仪传感器)。它还可以支持以高达2 Msps的数据速率采样模拟源然后将样本数据移动到外部存储器的情况。为了提高灵活性,高速ADC块可以与PWM块协同工作。它可以产生外部设备的驱动信号,例如线性图像扫描仪传感器。PWM还可以产生与高速同步的触发信号启动ADC转换的ADC块。一个APBH-DMA信道连接到高速ADC块,用于在高速ADC块到外部存储器。

注意:HSADC最大量程是0~1.85V

HSADC的配置流程如下:

  配置系统时钟——配置HSADC模块时钟——HSADC复位——配置工作模式及相关参数配置——使能HSADC预充电——启动HSADC ——配置中断、清除标志位——触发HSADC进行转换——等待转换完成——读取转换结果——清除标志位——再次触发进行下一次转换。

下面介绍几个重要的配置:

1.系统时钟配置,寄存器:HW_CLKCTRL_FRAC1

位bit

说明

15

HSADC时钟门控 清0使能HSADC时钟

14

标志位,每次时钟配置生效,该位会自动翻转

13-8

分频系数fhsadcrac   HSADC主时钟=480*(18/分频系数)   系数范围:18-35

2.HSADC 时钟分频器

位bit

说明

30

分频器使能位  置1 分频器正常工作

29-28

分频系数 9/18/36/72可选

 3.HW_HSADC_CTRL2 的bit13和bit0

4.HW_HSADC_CTRL1是配置中断使能和中断标志位。

BIT0是当某个中断发生时会被置位,由于我们是查询方式获取ADC结果,所以我们需要轮询bit0是否被置位,所以这里务必打开至少一个ADC中断,否则这一位永远不会被置位。

其余的配置就没有什么需要特别强调的,按照上面讲的流程,一个个配置即可。下面给出HSADC驱动源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<linux/module.h>      /* module */
#include<linux/fs.h>          /* file operation*/
#include<asm/uaccess.h>       /* get_user()*/
#include<linux/miscdevice.h>  /* miscdevice*/
#include<asm/io.h>            /* ioctl*/

#include <linux/kernel.h>
#include <linux/sysdev.h>
#include <linux/platform_device.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/cdev.h>

#include <../arch/arm/mach-mx28/regs-clkctrl.h>

#define DEBUG_T 0

#define DEVICE_NAME	"imx283_hsadc"//驱动名称
#define HSADC_BASE  0x80002000 //HSADC基地址 实际物理地址
#define HW_CLKCTRL_BASE  0x80040000//时钟控制模块基地址
#define HSADC_CLKCTRL_BASE 0x80040150//HSADC CLK控制寄存器基地址

static void __iomem *hsadc_base = NULL;//映射后HSADC基地址
static void __iomem *hsadc_clk_base = NULL;
static void __iomem *CLKCTRL_FRAC0_BASE=NULL;


static int hsadc_open(struct inode *inode, struct file *fp)
{
    /* 清0分频器配置位  再将       分频值配置为30 主clk=480*18/30=288MHz*/
    writel((0x3F<<8),CLKCTRL_FRAC0_BASE+0x18);//CLR 
    writel((0x1E<<8),CLKCTRL_FRAC0_BASE+0x14);//SET
	
    /*使能HSADC CLOCK*/
	writel(1<<15,CLKCTRL_FRAC0_BASE+0x18);//HW_CLKCTRL_FRAC1_SET

	/*RESETB=1 Divider normal work  FREQDIV=11 Divide by 72*/
    writel((1<<30)|(3<<28),hsadc_clk_base);//hsadc_clk_base

    /*SFTRST=0 HSADC退出复位进入正常模式*/
	writel(1<<31,hsadc_base+0x08);//HW_HSADC_CTRL0_CLR
	
	/*CLKGATE=0 进入正常模式*/
	writel(1<<30,hsadc_base+0x08);//HW_HSADC_CTRL0_CLR

    /*HSADC由软件触发转换*/
	writel(3<<28,hsadc_base+0x08);//HW_HSADC_CTRL0_CLR
    
    /*HSADC忽略初次上电4次采样值 */
    writel(3<<19,hsadc_base+0x04);//HW_HSADC_CTRL0_SET

    /*HSADC输出12bit结果*/
	writel(1<<18, hsadc_base+0x04);//HW_HSADC_CTRL0_SET
	writel(1<<17, hsadc_base+0x08);//HW_HSADC_CTRL0_CLR
	
    /*小端数据模式*/
	writel(1<<16,hsadc_base+0x08);//HW_HSADC_CTRL0_CLR
  
    /*使能ADC_DONE中断*/
    writel((1<<30),hsadc_base+0x14);//HW_HSADC_CTRL1_CLR
	
    /*清除POWER_DOWN位    ADC模块上电*/
	writel(1<<13,hsadc_base+0x28);//HW_HSADC_CTRL2_CLR
	
	/*ADC_PRECHARGE置1 使能HSADC预充电*/
    writel(1<<0,hsadc_base+0x24);//HW_HSADC_CTRL2_SET

	/*选择HSADC0作为ADC输入源*/        
	writel(7<<1,hsadc_base+0x24);//HW_HSADC_CTRL2_SET
	
#if (DEBUG_T)	
	printk("HW_HSADC_CTRL0=%08X\n",readl(hsadc_base+0));
	printk("HW_HSADC_CTRL1=%08X\n",readl(hsadc_base+0X10));
	printk("HW_HSADC_CTRL2=%08X\n",readl(hsadc_base+0x20));
	printk("HW_HSADC_CLK=%08x\n",readl(hsadc_clk_base)); 	  
    printk("HW_HSADC_CLK2=%08x\n",readl(CLKCTRL_FRAC0_BASE+0x10)); 
#endif	
	
	return 0; 
}


static int hsadc_release(struct inode * inode, struct file * file)
{
   //失能HSADC    
   writel((1<<31)|(1<<30),hsadc_base+0x04);//HW_HSADC_CTRL0_SET
   return 0;
}


static int hsadc_read(struct file *filp,  char __user *buf, size_t count,
                loff_t *f_pos)
{
    int ret;
	int hsadc_res;
	unsigned char databuf[2];
    unsigned int timeout = 1000;
	/*清除所有flag*/
	writel(3<<26,hsadc_base+0x14);//HW_HSADC_CTRL1_SET

	/*HSADC_RUN=1  启动HSADC等待触发*/
	writel(1<<0,hsadc_base+0x04);//HW_HSADC_CTRL0_SET

	/*SOFTWARE_TRIGGER=1 软件触发HSADC进行转换*/
    writel(1<<27,hsadc_base+0x04);//HW_HSADC_CTRL0_SET

	while(((readl(hsadc_base+0x10)&0x01)==0)&&(timeout > 0))//HW_HSADC_CTRL1等待HSADC转换完成 
	{
	   timeout--;
	   if(timeout == 0)
	   {    
            printk("timeout! \n");   
			break;
	   }
	   
	   //cpu_relax();
	}
#if (DEBUG_T)	
	printk("HW_HSADC_CTRL0=%08X\n",readl(hsadc_base+0));
	printk("HW_HSADC_CTRL1=%08X\n",readl(hsadc_base+0X10));
	printk("HW_HSADC_CTRL2=%08X\n",readl(hsadc_base+0x20));
#endif	
	/*清除所有flag*/
	writel(3<<26,hsadc_base+0x14);//HW_HSADC_CTRL1_SET 

	/*读取转换结果 12bit*/
	hsadc_res = readl(hsadc_base+0x50) & 0xFFF;//HW_HSADC_FIFO_DATA 读取ADC数据

	printk("hsadc_res= %x\n",hsadc_res);
   	
	databuf[0] = (hsadc_res >> 8) & 0xFF;
	databuf[1] = hsadc_res & 0xFF;
	ret = copy_to_user(buf, databuf, 2);
	if(ret < 0)
	{
      printk("kernel read error \n");
	  return ret;
	}
	return 0;
  
}


static struct file_operations hsadc_fops =
{
  .owner   = THIS_MODULE,
  .open    = hsadc_open,
  .release = hsadc_release,
  .read    = hsadc_read,
};

static struct cdev *hsadc_cdev = NULL;//cdev
static struct class *hsadc_class = NULL;//类
static struct device *hsadc_device = NULL;//设备
static dev_t  dev_id;//设备号
static int major;//主设备号

static int __init hsadc_init(void)
{
  int ret;
  hsadc_clk_base = ioremap(HSADC_CLKCTRL_BASE, 4);
  hsadc_base = ioremap(HSADC_BASE,0xC0*4);//地址映射
  CLKCTRL_FRAC0_BASE = ioremap(HW_CLKCTRL_BASE+HW_CLKCTRL_FRAC0,32);
  
  ret = alloc_chrdev_region(&dev_id, 0, 1, DEVICE_NAME); //申请设备号
  if(ret < 0)
  {
     printk(KERN_ERR "alloc dev_id error %d \n", ret);
	 return ret;
  }
  major = MAJOR(dev_id);//获取主设备号
  hsadc_cdev=cdev_alloc();//申请cdev结构
  if(hsadc_cdev != NULL)
  {
     cdev_init(hsadc_cdev,&hsadc_fops);//初始化hsadc_cdev结构体
	 ret = cdev_add(hsadc_cdev, dev_id, 1);//添加hsadc设备到hsadc_cdev结构体 
	 if(ret != 0)
	 {
       printk("cdev add error %d \n",ret);
	   goto error;
	 }
  }
  else
  {
     printk("cdev_alloc error \n");
     return -1;
  }
  //创建hsadc_class类
  hsadc_class = class_create(THIS_MODULE, "HSADC_CLASS");
  if(hsadc_class !=NULL)
  {
     //在hsadc_class类下面创建1个设备
     hsadc_device=device_create(hsadc_class, NULL, dev_id, NULL, DEVICE_NAME);
  }
	 printk("module init ok\n");
	 return 0;
error:
     cdev_del(hsadc_cdev);//删除hsadc_cdev结构体
     unregister_chrdev_region(dev_id, 1);//释放设备号
     return ret;	 
}


static void __exit hsadc_exit(void)
{
   iounmap(hsadc_base);//取消地址映射
   cdev_del(hsadc_cdev);//删除hsadc_cdev结构体
   unregister_chrdev_region(dev_id, 1);//释放设备号
   device_del(hsadc_device);//删除设备
   class_destroy(hsadc_class);//删除类
   printk("module exit ok\n");
}


module_init(hsadc_init);
module_exit(hsadc_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xzx2020");

以及测试APP:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include<stdio.h>	/* using printf()        */
#include<stdlib.h>      /* using sleep()         */
#include<fcntl.h>       /* using file operation  */
#include<sys/ioctl.h>   /* using ioctl()         */
#include <asm/ioctls.h>
#include <unistd.h> //sleep  write read close

int main(int argc, const char * argv [ ])
{
  int fd;
  int value = 0;
  unsigned char buf[2];
  fd = open("/dev/imx283_hsadc",O_RDWR);
  if(fd < 0)
  {
     printf("open imx283_hsadc error %d\n",fd);
	 return 0;
  }

  while(1)
  {
    read(fd,buf,2);
	value = buf[0]<<8 | buf[1];
    printf("HSADC = %.2f\n",(double)(value)*1.85/4096);
	sleep(1);
  }
  return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-02-19,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
i.MX283开发板按键驱动和GPIO中断
由于手头上的i.MX283开发板没有独立按键,所以只能用一个IO口手动拉高拉低来模拟按键,但是这样会造成一个小问题,这个后面会提到。按键驱动与LED驱动最大的区别就是前者是GPIO输入,后者是GPIO输出,我们只需要读取IO口电平即可,同样的这也是一个字符设备,按照字符设备驱动框架编写驱动即可。
知否知否应是绿肥红瘦
2025/02/19
630
i.MX283开发板按键驱动和GPIO中断
i.MX283开发板I2C驱动——DS2460
i.MX283开发板有两个I2C接口,其中I2C0接了一个DS2460加密芯片,本文介绍Linux下如何编写I2C驱动程序读写DS2460。
知否知否应是绿肥红瘦
2025/02/19
690
i.MX283开发板I2C驱动——DS2460
i.MX283开发板第一个Linux驱动-LED驱动改进
上一个博客i.MX283开发板第一个Linux驱动讲的是最简单的LED驱动的编写,但是其中还有一些不足。
知否知否应是绿肥红瘦
2025/02/19
410
i.MX283开发板第一个Linux驱动-LED驱动改进
Android新增LED设备--从底层到上层理解安卓架构
为了更好的理解安卓的层次关系,本文在RK3399的安卓系统上增加LED灯的外设,并使用APP打开关闭LED灯。以这样一个最简单的实例,来演示从上层到底层的调用过程。首先从最底层的kernel层开始。
Jasonangel
2021/08/26
2.9K0
Android新增LED设备--从底层到上层理解安卓架构
驱动GPIO操作总结
设备驱动程序是软件概念和硬件电路之间的一个抽象层,软件操作硬件的关键就是对寄存器的操作。笔者使用的S5PV210是IO与内存统一编址的,在裸机中直接操作IO端口的物理地址,而在驱动中必须使用虚拟地址。直接基于IO的虚拟地址用指针解引用的方式来读写有两种方式,静态映射和动态映射。除了可以直接将指针解引用的方式,内核中提供了专用的读写接口来读写寄存器。考虑到GPIO作为硬件资源,存在着被多个驱动使用,还有复用的问题,所以内核提供了GPIO驱动gpiolib框架来统一管控GPIO资源,gpiolib在内核中作为一个驱动所实现。
菜菜cc
2022/11/15
9910
Linux笔记(13)| 字符设备驱动基础入门
距离上一次更新有一段时间了,主要是最近更忙一些,一般来说,有时间我会尽量更新,如果比较忙的话就更新慢一些。
飞哥
2020/08/28
2.1K0
Linux笔记(13)| 字符设备驱动基础入门
i.MX283开发板——LED子系统
前面的文章有讲过LED字符设备驱动,用户可以open “/dev/xxxLED”驱动文件,通过write或者ioctl接口去访问LED设备,实际
知否知否应是绿肥红瘦
2025/02/19
1120
i.MX283开发板——LED子系统
Linux笔记(21)| platform总线驱动分析
上次跟大家分享了设备模型的一些东西,包括总线、设备、驱动等的一些概念,还有他们之间的联系。今天要分享的是platform总线驱动,platform总线是总线的一种,这是相对于物理总线来说的,这是一种虚拟的总线。
飞哥
2021/02/02
2.5K0
设备驱动基础学习–platform driver简单实现「建议收藏」
platform是一条虚拟的总线。设备用platform_device表示,驱动用platform_driver进行注册,Linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的)。
全栈程序员站长
2022/09/06
1.5K0
设备驱动基础学习–platform driver简单实现「建议收藏」
linux misc设备驱动《Rice linux 学习开发》
misc(杂项)设备,由于硬件设备的多样化,有一些设备不知道如何归类,所以linux将这些不知道怎么归类的设备归类为misc设备。例如led、watchdog、beep、adc等都可以归纳为misc设备。
Rice加饭
2022/05/09
2.1K0
linux misc设备驱动《Rice linux 学习开发》
【i.MX6ULL】驱动开发4——点亮LED(寄存器版)
上篇文章(【i.MX6ULL】驱动开发3——GPIO寄存器配置原理),介绍了i.MX6ULL芯片的GPIO的工作原理与寄存器配置。
xxpcb
2021/09/29
8640
Linux驱动开发-编写PCF8591(ADC)芯片驱动
PCF8591是一个IIC总线接口的ADC/DAC转换芯片,功能比较强大,这篇文章就介绍在Linux系统里如何编写一个PCF8591的驱动,完成ADC数据采集,DAC数据输出。
DS小龙哥
2022/04/08
2.9K0
Linux驱动开发-编写PCF8591(ADC)芯片驱动
Linux触摸屏驱动分析(6410) -- s3c-ts
static struct s3c_ts_mach_info s3c_ts_platform __initdata = { .delay = 10000, /*转化延迟*/ .presc = 49, /*转化时钟分频*/ .oversampling_shift = 2, /*转化次数 1<<2 == 4次*/ .resol_bit = 12, /*转化进度*/ .s3c_adc_con = ADC_TYPE_2, }; /*s3c_ts所列的资源*/ static
DragonKingZhu
2022/05/08
2K0
Linux驱动开发: 杂项字符设备
杂项设备(misc device)也是在嵌入式系统中用得比较多的一种设备驱动。
DS小龙哥
2022/01/12
4K0
Linux驱动开发: 杂项字符设备
全志模块设备开发之PWM编程基础介绍
​ PWM,英文名Pulse Width Modulation,是脉冲宽度调制缩写,它是通过对一系列脉冲的宽度进行调制,等效出所需要的波形(包含形状以及幅值),对模拟信号电平进行数字编码,也就是说通过调节占空比的变化来调节信号、能量等的变化,占空比就是指在一个周期内,信号处于高电平的时间占据整个信号周期的百分比,例如方波的占空比就是50%。是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。
阿志小管家
2024/08/16
800
全志模块设备开发之PWM编程基础介绍
PCI设备驱动程序「建议收藏」
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huangweiqing80/article/details/83347495
全栈程序员站长
2022/11/18
2.4K0
PCI设备驱动程序「建议收藏」
Linux驱动之Misc子系统剖析
Linux驱动分为字符设备驱动、块设备驱动和网络设备驱动,而字符设备又包括很多种,内核使用主设备号来区分各个字符设备驱动,在include/linux/major.h文件中已经预先定义好了各类字符设备的主设备号,但是即便如此,仍然存在着大量字符设备无法准确归类,对于这些设备,内核提供了一种Misc(杂项)设备来安放它们的去处。
菜菜cc
2022/11/15
1.4K0
Linux驱动开发-编写VS1053芯片音频驱动
VS1053是一款硬件编解码的音频芯片,提供SPI接口和IIS接口两种通信协议,这篇文章是介绍在Linux下如果模拟SPI时序来操作VS1053完成录音、播放音频歌曲功能。但是没有注册标准的音频驱动,没有对接音频框架,只是在驱动层完成VS1053的直接控制,本篇的重点主要是介绍如何初始化开发板的GPIO口,使用Linux的延时函数,模拟SPI时序,代码写了两种版本,一种是直接通过ioremap直接映射GPIO口地址,完成配置,一种是直接调用官方内核提供的库函数接口,完成GPIO口初始化,控制。
DS小龙哥
2022/04/08
3.2K0
Linux驱动开发-编写VS1053芯片音频驱动
i.MX283开发板第一个Linux驱动-LED驱动
字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如我们最常见的点灯、按键、IIC、SPI,LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。
知否知否应是绿肥红瘦
2025/02/19
680
i.MX283开发板第一个Linux驱动-LED驱动
Linux驱动框架与杂项字符设备框架介绍
比如: 温度传感器、湿度传感器、光照度、门锁、LED灯、蜂鸣器 驱动都是使用字符设备框架编写
DS小龙哥
2022/04/08
3.3K0
Linux驱动框架与杂项字符设备框架介绍
相关推荐
i.MX283开发板按键驱动和GPIO中断
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文