1.解压开发板上面的linux内核代码
把光盘上面的linux-2.6.35.3.tar.bz2复制到d:\imx280a_exe_folder/
wsl linux子系统终端运行命令,解压linux内核代码
cd /home/dengxm2024
tar xvf /mnt/d/imx280a_exe_folder/linux-2.6.35.3.tar.bz2
现在内核代码文件夹已经解压到/home/dengxm2024,名称是linux-2.6.35.3 ,路径全程:/home/dengxm2024/linux-2.6.35.3
2.复制编译adc驱动示例代码:
~/linuxProgDir/kernelside_code/imx280a_adc_drv
驱动:/home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv/Makefile
第一行添加内核源码的路径
KERNEL_PATH := /home/dengxm2024/linux-2.6.35.3
obj-m:=lradc.o
PWD:=$(shell pwd)
KDIR:=$(KERNEL_PATH)
all:
$(MAKE) -C $(KDIR) M=$(PWD)
clean:
rm -rf *.ko *.order *.symvers *.cmd *.o *.mod.c *.tmp_versions .*.cmd .tmp_versions
/home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv/lradc.c
/*********************************************Copyright (c)***********************************************
** Guangzhou ZLG MCU Technology Co., Ltd.
**
** http://www.zlgmcu.com
**
** 广州周立功单片机科技有限公司所提供的所有服务内容旨在协助客户加速产品的研发进度,在服务过程中所提供
** 的任何程序、文档、测试结果、方案、支持等资料和信息,都仅供参考,客户有权不使用或自行参考修改,本公司不
** 提供任何的完整性、可靠性等保证,若在客户使用过程中因任何原因造成的特别的、偶然的或间接的损失,本公司不
** 承担任何责任。
** ——广州周立功单片机科技有限公司
**
**--------------File Info---------------------------------------------------------------------------------
** File name: lradc.c
** Last modified date: 2013年11月28日15:57:38
** Last version:
** Descriptions:
**
**--------------------------------------------------------------------------------------------------------
** Created by: 周华
** Created date: 2013年11月28日15:57:44
** Version: v1.0
** Descriptions: ADC驱动,读取
**--------------------------------------------------------------------------------------------------------
** Modified by: cxf
** Modified date: 2014-09-22
** Version:
** Descriptions: ADC Vref = Bandgap Reference(channel 14) = 1.85V
** 如果电压大于1.85V,需要开启硬件除2
** ADC为12位
** 每个寄存器都有四个,分别用来 r/w 、set 、clear 、trogger
** 名称类似 HW_LRADC_CTRL0、HW_LRADC_CTRL0_SET、
** HW_LRADC_CTRL0_CLR、HW_LRADC_CTRL0_TOG
** HW_LRADC_CTRL0 SFTRST(31) CLKGATE(30) SCHEDULE(7~0)
** HW_LRADC_CTRL1 CHn_IRQ_EN(23~16) CHn_IRQ(7~0) 主要为触屏、ADC中断
** HW_LRADC_CTRL2 CHn_DIV/2(31~24) 主要为温度控制
** HW_LRADC_CTRL3 DISCARD(25、24) CYCLE_TIME(9、8)HIGH_TIME(5/4)DELAY_CLK(1)INVERT_CLK(0)忽略、时钟
** HW_LRADC_CTRL4 4*8 channels 0-16 channel selector
**
*********************************************************************************************************/
#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/module.h>
#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/irq.h>
#include <linux/delay.h>
#include <mach/hardware.h>
#include <mach/device.h>
#include <mach/regs-lradc.h>
#include <mach/lradc.h>
#include "lradc.h"
#define ZLG_IMX_283 0
#define ZLG_IMX_287 1
#define ZLG_IMX_280 2
extern int zlg_board_type;
static void __iomem *adc_base = NULL;
/*********************************************************************************************************
文件操作接口
*********************************************************************************************************/
static int adc_open (struct inode *inode, struct file *fp)
{
try_module_get(THIS_MODULE);
//modified by cxf 2014-10-22 for imx280 ADCs
if(ZLG_IMX_280 == zlg_board_type) {
writel(BM_LRADC_CTRL0_SFTRST,adc_base + HW_LRADC_CTRL0_SET);
udelay(1);
writel(BM_LRADC_CTRL0_SFTRST,adc_base + HW_LRADC_CTRL0_CLR);
/* Clear the Clock Gate for normal operation */
writel(BM_LRADC_CTRL0_CLKGATE,adc_base + HW_LRADC_CTRL0_CLR);
}
writel(0xC0000000, adc_base + HW_LRADC_CTRL0_CLR); /* 开启CLK,关闭复位模式 */
writel(0x18430000, adc_base + HW_LRADC_CTRL1_CLR); /* 关闭0-1-6通道及按键中断 */
writel(0x30000000, adc_base + HW_LRADC_CTRL3_SET); /* 忽略上电前三次采集数据 */
writel(0x76543210, adc_base + HW_LRADC_CTRL4); /* 设置对应的数据存放位置 */
return 0;
}
static int adc_release (struct inode *inode, struct file *fp)
{
module_put(THIS_MODULE);
return 0;
}
/*********************************************************************************************************
** cmd:
** 不开启除2:
** 10:CH0 11:CH1 12:CH2 13:CH3 14:CH4 15:CH5 16:CH6 17:Vbat(内部除4)
** 开启除2:
** 20:CH0 21:CH1 22:CH2 23:CH3 24:CH4 25:CH5 26:CH6
**
** 被注释部分为3.0以上内核函数定义形式
*********************************************************************************************************/
//static int adc_ioctl(struct file *fp, struct file *flip, unsigned int cmd, unsigned long arg)
static int adc_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg)
{
int iRes;
int iTmp;
/*
if(cmd < 10 || cmd > 27) {
return -1;
}
iTmp = cmd % 10;
//CH2 ~ CH5 only for imx280
if((zlg_board_type != ZLG_IMX_280) && (iTmp > 1) && (iTmp < 6)) {
printk("Not ZLG_IMX_280 board.\n");
return -1;
}
*/
iTmp = _IOC_NR(cmd) % 10;
switch(cmd){
case IMX28_ADC_CH0:
writel(0x01000000, adc_base + HW_LRADC_CTRL2_CLR);
break;
case IMX28_ADC_CH0_DIV2:
writel(0x01000000, adc_base + HW_LRADC_CTRL2_SET);
break;
case IMX28_ADC_CH1:
writel(0x02000000, adc_base + HW_LRADC_CTRL2_CLR);
break;
case IMX28_ADC_CH1_DIV2:
writel(0x02000000, adc_base + HW_LRADC_CTRL2_SET);
break;
case IMX28_ADC_CH2:
writel(0x04000000, adc_base + HW_LRADC_CTRL2_CLR);
break;
case IMX28_ADC_CH2_DIV2:
writel(0x04000000, adc_base + HW_LRADC_CTRL2_SET);
break;
case IMX28_ADC_CH3:
writel(0x08000000, adc_base + HW_LRADC_CTRL2_CLR);
break;
case IMX28_ADC_CH3_DIV2:
writel(0x08000000, adc_base + HW_LRADC_CTRL2_SET);
break;
case IMX28_ADC_CH4:
writel(0x10000000, adc_base + HW_LRADC_CTRL2_CLR);
break;
case IMX28_ADC_CH4_DIV2:
writel(0x10000000, adc_base + HW_LRADC_CTRL2_SET);
break;
case IMX28_ADC_CH5:
writel(0x20000000, adc_base + HW_LRADC_CTRL2_CLR);
break;
case IMX28_ADC_CH5_DIV2:
writel(0x20000000, adc_base + HW_LRADC_CTRL2_SET);
break;
case IMX28_ADC_CH6:
writel(0x40000000, adc_base + HW_LRADC_CTRL2_CLR);
break;
case IMX28_ADC_CH6_DIV2:
writel(0x40000000, adc_base + HW_LRADC_CTRL2_SET);
break;
case IMX28_ADC_VBAT:
case IMX28_ADC_VBAT_DIV4:
break;
default:
printk("adc control cmd invalid!!\n");
return -1;
}
/* Clear the accumulator & NUM_SAMPLES */
__raw_writel(0xFFFFFFFF,
adc_base + HW_LRADC_CHn_CLR(iTmp));
/* Clear the interrupt flag */
__raw_writel(1 << iTmp,
adc_base + HW_LRADC_CTRL1_CLR);
__raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << iTmp),
adc_base + HW_LRADC_CTRL0_SET);
/* wait for completion */
while ((__raw_readl(adc_base + HW_LRADC_CTRL1)
& (1 << iTmp)) != (1 << iTmp))
cpu_relax();
/* Clear the interrupt flag */
__raw_writel(1 << iTmp,
adc_base + HW_LRADC_CTRL1_CLR);
iRes = __raw_readl(adc_base + HW_LRADC_CHn(iTmp)) &
BM_LRADC_CHn_VALUE;
copy_to_user((void *)arg, (void *)(&iRes), sizeof(int));
return 0;
}
/*********************************************************************************************************
Device Struct
*********************************************************************************************************/
struct file_operations adc_fops =
{
.owner = THIS_MODULE,
.open = adc_open,
.release = adc_release,
.ioctl = adc_ioctl,
};
static struct miscdevice adc_miscdev =
{
.minor = MISC_DYNAMIC_MINOR,
.name = "magic-adc",
.fops = &adc_fops,
};
/*********************************************************************************************************
Module Functions
*********************************************************************************************************/
static int __init adcModule_init (void)
{
int iRet=0;
if (ZLG_IMX_283 == zlg_board_type) {
printk("zlg EasyARM-imx283 adc driver up. \n");
} else if(ZLG_IMX_287 == zlg_board_type) {
printk("zlg EasyARM-imx287 adc driver up. \n");
} else if(ZLG_IMX_280 == zlg_board_type) {
//printk("zlg EasyARM-imx280 adc driver up. \n");
}
adc_base = ioremap(0x80050000, 0x180*4);
iRet = misc_register(&adc_miscdev);
if (iRet) {
printk("register failed!\n");
}
return iRet;
}
static void __exit adcModule_exit (void) /* warning:return void */
{
printk("zlg EasyARM-imx28xx adc driver down.\n");
misc_deregister(&adc_miscdev);
}
/*********************************************************************************************************
Driver Definitions
*********************************************************************************************************/
module_init(adcModule_init);
module_exit(adcModule_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("admin@9crk.com");
MODULE_DESCRIPTION("EasyARM283 By zhouhua");
/*********************************************************************************************************
End File
*********************************************************************************************************/
/home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv/lradc.h
#ifndef __LRADC_H
#define __LRADC_H
#define IMX28_ADC_IOC_MAGIC 'j'
#define IMX28_ADC_CH0 _IOW(IMX28_ADC_IOC_MAGIC, 10, int) /* 通道0 */
#define IMX28_ADC_CH1 _IOW(IMX28_ADC_IOC_MAGIC, 11, int) /* 通道1 */
#define IMX28_ADC_CH2 _IOW(IMX28_ADC_IOC_MAGIC, 12, int) /* 通道2 */
#define IMX28_ADC_CH3 _IOW(IMX28_ADC_IOC_MAGIC, 13, int) /* 通道3 */
#define IMX28_ADC_CH4 _IOW(IMX28_ADC_IOC_MAGIC, 14, int) /* 通道4 */
#define IMX28_ADC_CH5 _IOW(IMX28_ADC_IOC_MAGIC, 15, int) /* 通过5 */
#define IMX28_ADC_CH6 _IOW(IMX28_ADC_IOC_MAGIC, 16, int) /* 通道6 */
#define IMX28_ADC_VBAT _IOW(IMX28_ADC_IOC_MAGIC, 17, int) /* 测量电池电压 */
#define IMX28_ADC_CH0_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 20, int) /* 通道0,开启除 2 */
#define IMX28_ADC_CH1_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 21, int) /* 通道1,开启除 2 */
#define IMX28_ADC_CH2_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 22, int) /* 通道2,开启除 2 */
#define IMX28_ADC_CH3_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 23, int) /* 通道3,开启除 2 */
#define IMX28_ADC_CH4_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 24, int) /* 通道4,开启除 2 */
#define IMX28_ADC_CH5_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 25, int) /* 通过5,开启除 2 */
#define IMX28_ADC_CH6_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 26, int) /* 通道6,开启除 2 */
#define IMX28_ADC_VBAT_DIV4 _IOW(IMX28_ADC_IOC_MAGIC, 27, int) /* 测量电池电压,开启除4 */
#endif
源码文件夹下面运行make命令:
dengxm2024@PC-202105142413:~/linuxProgDir/kernelside_code/imx280a_adc_drv$ make
make -C /home/dengxm2024/linux-2.6.35.3 M=/home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv
make[1]: Entering directory '/home/dengxm2024/linux-2.6.35.3'
LD /home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv/built-in.o
CC [M] /home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv/lradc.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv/lradc.mod.o
LD [M] /home/dengxm2024/linuxProgDir/kernelside_code/imx280a_adc_drv/lradc.ko
make[1]: Leaving directory '/home/dengxm2024/linux-2.6.35.3'
复制生成的lradc.ko到web服务器主文件夹:
cp lradc.ko /mnt/d/imx280a_exe_folder/
3.复制编译adc用户侧调用的示例代码:
/home/dengxm2024/linuxProgDir/userside_code/ap-283demo-adc/lradc_test.c
/*********************************************************************************
** ADC参考电压为1.85V,外部输入电压不能超过3.7V,内部带有一个模拟除2电路。
*********************************************************************************/
#include<stdio.h> /* using printf() */
#include<stdlib.h> /* using sleep() */
#include<fcntl.h> /* using file operation */
#include<sys/ioctl.h> /* using ioctl() */
#include "lradc.h"
int main(int argc, char *argv[])
{
int fd;
int iRes;
int time = 50;
double val;
fd = open("/dev/magic-adc", 0);
if(fd < 0){
printf("open error by APP- %d\n",fd);
close(fd);
return 0;
}
while(time--){
sleep(1);
#if 0 /* 对于EasyARM-iMX280A,请设置为1 */
ioctl(fd,IMX28_ADC_CH0_DIV2, &iRes); /* 开启除2 CH0 */
val = (iRes * 3.7) / 4096.0;
printf("CH0:%.2f ", val);
ioctl(fd,IMX28_ADC_CH1, &iRes); /* 不开除2 CH1 */
val = (iRes * 1.85) / 4096.0;
printf("CH1:%.2f ", val);
ioctl(fd,IMX28_ADC_CH2_DIV2, &iRes); /* 开启除2 CH2 */
val = (iRes * 3.7) / 4096.0;
printf("CH2:%.2f ", val);
ioctl(fd, IMX28_ADC_CH3, &iRes); /* 不开除2 CH3 */
val = (iRes * 1.85) / 4096.0;
printf("CH3:%.2f ", val);
ioctl(fd,IMX28_ADC_CH4_DIV2, &iRes); /* 开启除2 CH4 */
val = (iRes * 3.7) / 4096.0;
printf("CH4:%.2f ", val);
ioctl(fd,IMX28_ADC_CH5, &iRes); /* 不开除2 CH5 */
val = (iRes * 1.85) / 4096.0;
printf("CH5:%.2f ", val);
ioctl(fd,IMX28_ADC_CH6_DIV2, &iRes); /* 开启除2 CH6 */
val = (iRes * 3.7) / 4096.0;
printf("CH6:%.2f ", val);
ioctl(fd,IMX28_ADC_VBAT_DIV4, &iRes); /* 电池电压默认除4 */
val = (iRes * 7.4) / 4096.0;
printf("Vbat:%.2f ", val);
#endif
ioctl(fd,IMX28_ADC_CH5, &iRes); /* 不开除2 CH5 */
val = (iRes * 1.85) / 4096.0;
printf("CH5:%.2f ", val);
printf("\n");
}
close(fd);
}
/home/dengxm2024/linuxProgDir/userside_code/ap-283demo-adc/lradc.h
#ifndef __LRADC_H
#define __LRADC_H
#define IMX28_ADC_IOC_MAGIC 'j'
#define IMX28_ADC_CH0 _IOW(IMX28_ADC_IOC_MAGIC, 10, int) /* 通道0 */
#define IMX28_ADC_CH1 _IOW(IMX28_ADC_IOC_MAGIC, 11, int) /* 通道1 */
#define IMX28_ADC_CH2 _IOW(IMX28_ADC_IOC_MAGIC, 12, int) /* 通道2 */
#define IMX28_ADC_CH3 _IOW(IMX28_ADC_IOC_MAGIC, 13, int) /* 通道3 */
#define IMX28_ADC_CH4 _IOW(IMX28_ADC_IOC_MAGIC, 14, int) /* 通道4 */
#define IMX28_ADC_CH5 _IOW(IMX28_ADC_IOC_MAGIC, 15, int) /* 通过5 */
#define IMX28_ADC_CH6 _IOW(IMX28_ADC_IOC_MAGIC, 16, int) /* 通道6 */
#define IMX28_ADC_VBAT _IOW(IMX28_ADC_IOC_MAGIC, 17, int) /* 测量电池电压 */
#define IMX28_ADC_CH0_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 20, int) /* 通道0,开启除 2 */
#define IMX28_ADC_CH1_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 21, int) /* 通道1,开启除 2 */
#define IMX28_ADC_CH2_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 22, int) /* 通道2,开启除 2 */
#define IMX28_ADC_CH3_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 23, int) /* 通道3,开启除 2 */
#define IMX28_ADC_CH4_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 24, int) /* 通道4,开启除 2 */
#define IMX28_ADC_CH5_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 25, int) /* 通过5,开启除 2 */
#define IMX28_ADC_CH6_DIV2 _IOW(IMX28_ADC_IOC_MAGIC, 26, int) /* 通道6,开启除 2 */
#define IMX28_ADC_VBAT_DIV4 _IOW(IMX28_ADC_IOC_MAGIC, 27, int) /* 测量电池电压,开启除4 */
#endif
/home/dengxm2024/linuxProgDir/userside_code/ap-283demo-adc/Makefile
#CROSS_COMPILE是环境变量,是交叉编译工具的前缀,隐式声明编译器
CC = $(CROSS_COMPILE)gcc
CXX = $(CROSS_COMPILE)g++
#显示声明编译器
#CC = arm-fsl-linux-gnueabi-gcc
#CXX = arm-fsl-linux-gnueabi-g++
EXEC = ./bin/lradc_test
OBJS = lradc_test.o
SRC = lradc_test.c
#CC = arm-fsl-linux-gnueabi-gcc
CFLAGS +=
LDFLAGS +=
all:$(EXEC)
$(EXEC):$(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
@rm -vf $(EXEC) *.o *~
执行编译:
dengxm2024@PC-202105142413:~/linuxProgDir/userside_code/ap-283demo-adc$ make
arm-fsl-linux-gnueabi-gcc -c lradc_test.c -o lradc_test.o
arm-fsl-linux-gnueabi-gcc -o bin/lradc_test lradc_test.o
复制编译结果到web服务器文件夹
dengxm2024@PC-202105142413:~/linuxProgDir/userside_code/ap-283demo-adc$ cp ./bin/lradc_test /mnt/d/imx280a_exe_folder/
4.运行测试
转到开发板终端窗口,运行命令,从web服务器获取刚才编译好的执行文件
root@EasyARM-iMX28x ~# wget http://192.168.0.233:443/imx280a_exe_folder/lradc.ko
Connecting to 192.168.0.233:443 (192.168.0.233:443)
lradc.ko 100% |*******************************| 5528 0:00:00 ETA
root@EasyARM-iMX28x ~# wget http://192.168.0.233:443/imx280a_exe_folder/lradc_te
st
Connecting to 192.168.0.233:443 (192.168.0.233:443)
lradc_test 100% |*******************************| 14279 0:00:00 ETA
添加可执行属性
root@EasyARM-iMX28x ~# chmod +x lradc_test
root@EasyARM-iMX28x ~# chmod +x lradc.ko
加载adc驱动模块
root@EasyARM-iMX28x ~# insmod lradc.ko
运行采集程序,转动AP-283demo扩展板上面的电位器,可以看到电压变化:
root@EasyARM-iMX28x ~# ./lradc_test
CH5:0.95
CH5:0.95
CH5:0.95
CH5:0.95
CH5:0.95
CH5:0.94
CH5:0.95
CH5:0.94
CH5:0.97
CH5:0.98
CH5:0.98
CH5:0.98
CH5:0.99
CH5:1.01
CH5:1.02
CH5:1.01
CH5:1.01
CH5:1.02
CH5:1.02
CH5:1.01
CH5:1.02
CH5:1.02
CH5:1.02
CH5:1.04
CH5:1.05
CH5:1.05
为什么是adc的第5通道?
官方给的开发板原理中线号编的不一致,要仔细对比看:
AP-283demo原理图:
找接口原理图,ADC_IN0连接到了图纸1的adc0上面:
再查看i.mx280a的电路原理图:
所以ap-283demo扩展板的电位器采集的电压接入到了adc第5通道。