如何编写基于ARM的裸机程序和基于Linux的驱动程序?
int main (void)
{
unsigned char bit4,bit3,bit2,bit1;
unsigned int temp = 0;
uart_init();
adc_init(temp);
puts("开始转换");
while(1)
{
while(!(ADCCON & 0x8000));
temp = ADCDAT & 0xfff;
printf("U = %d",temp);
temp = 1.8 * 1000 * temp/0xfff;
bit4 = temp /1000;
putc(table[bit4]);
bit3 = (temp % 1000)/100?;
putc(table[bit3]);
bit2 = ((temp % 1000)%100)/10;
putc(table[bit2]);
bit1 = ((temp % 1000)%100)%10;
putc(table[bit1]);
puts("mV");
putc('');
mydelay_ms(1000);
}
return 0;
}
中断模式
中断模式读取数据步骤如下:
1.要读取数据首先向ADC寄存器ADCCON的bit:0写1,发送转换命令;2.当ADC控制器转换完毕会通过中断线向CPU发送中断信号;3.在中断处理函数中,读走数据,并清中断.
注:中断对应寄存器的设置,后续会更新对应的文档。
void do_irq(void)
{
int irq_num;
irq_num = CPU0.ICCIAR &0x3ff;
switch(irq_num)
{
case 42:
adc_num = ADCDAT&0xfff;
printf("adc = %d",adc_num);
CLRINTADC = 0;
// IECR2 = IECR2 | (1 << 19); 打开的话只能读取一次,
//42/32
ICDICPR.ICDICPR1 = ICDICPR.ICDICPR1 | (1 << 10);【清GIC中断标志位类似于 ICDISER】
break;
}
CPU0.ICCEOIR = CPU0.ICCEOIR & (~0x3ff) | irq_num;
}
void adc_init(void)
{ //12bit 使能分频 分频值 手动
ADCCON = (1 << 16) | (1 << 14) | (0xff << 6) | (1 << 0);
ADCMUX = 3;
}
void adcint_init(void)
{
IESR2 = IESR2 | (1 << 19);
ICDDCR = 1; //使能分配器
//42/32
ICDISER.ICDISER1 = ICDISER.ICDISER1 | (1 << 10);//使能相应中断到分配器
ICDIPTR.ICDIPTR10 = ICDIPTR.ICDIPTR10 &(~(0xff << 16)) | (0x1 << 16);//发送到相应CPU接口
CPU0.ICCPMR = 255;//设置中断屏蔽优先级
CPU0.ICCICR = 1; //全局使能开关
}
int main (void)
{
adc_init();
adcint_init();
while(1)
{
ADCCON = ADCCON | 1;
delay_ms(1000);
}
return 0;
}
基于Linux驱动编写设备树
编写基于Linux的ADC外设驱动,首先需要编写设备树节点信息,在裸机程序中,我们只用到了寄存器地址,而编写基于Linux的驱动,我们需要用到中断功能。所以编写设备树节点需要知道ADC要用到的硬件资源主要包括:寄存器资源和中断资源。
关于中断的使用我们在后续文章中会继续分析,现在我们只需要知道中断信息如何填写即可。
ADC寄存器信息填写
在这里插入图片描述
由上可知,寄存器基地址为0x126c0000,其他寄存器只需要根据基地址做偏移即可获取,所以设备树的reg属性信息如下:
reg = <0x126C0000 0x20>;
ADC中断信息填写
描述中断连接需要四个属性:父节点提供以下信息
interrupt-controller - 一个空的属性定义该节点作为一个接收中断信号的设备。
interrupt-cells - 这是一个中断控制器节点的属性。它声明了该中断控制器的中断指示符中【interrupts】 cell 的个数(类似于 #address-cells 和 #size-cells)。
子节点描述信息
interrupt-parent - 这是一个设备节点的属性,包含一个指向该设备连接的中断控制器的 phandle。那些没有 interrupt-parent 的节点则从它们的父节点中继承该属性。
interrupts - 一个设备节点属性,包含一个中断指示符的列表,对应于该设备上的每个中断输出信号。【设备的中断信息放在该属性中】
父节点
首先我们必须知道ADC控制器的中断线的父节点:
由上图可知ADC控制器位于soc内,4个ADC通道公用一根中断线,该中断线连接在combiner上,所以我们需要查找到combiner这个父节点的说明:
进入设备树文件所在目录:archrmootdts
grep combiner *.* -n
经过筛选得到以下信息,
因为我们使用的板子是exynos4412,而exynos系列通用的平台设备树文件是exynos4.dtsi,查看该文件:
上图列举了combiner控制器的详细信息:
interrupt-cells ;
interrupt-cells =<2>;
所以ADC控制器中断控制器的interrupts属性应该有两个cell。
interrupts属性填写
而设备的中断信息填写方式由内核的以下文档提供:
Documentationdevicetreeindingsinterrupt-controllerinterrupts.txt
69. b) two cells
70. ------------
71. The #interrupt-cells property is set to 2 and the first cell 72. defines the
73. index of the interrupt within the controller, while the second cell is used
74. to specify any of the following flags:
75. - bits[3:0] trigger type and level flags
76. 1 = low-to-high edge triggered
77. 2 = high-to-low edge triggered
78. 4 = active high level-sensitive
79. 8 = active low level-sensitive
由以上信息可知,中断的第一个cell是该中断源所在中断控制器的index,第二个cell表示中断的触发方式
*. 1:上升沿触发*. 2:下降沿触发*. 3:高电平触发*. 4:低电平触发
那么index应该是多少呢?
详见datasheet的9.2.2 GIC Interrupt Table 节:
此处我们应该是填写左侧的SPI ID:10 还是填写INTERRUPT ID:42呢?
此处我们可以参考LCD节点的interrupts填写方法:
通过查找父节点为combiner的设备信息。
继续grep combiner . -n
由此可见lcd这个设备的interrupts属性index值是11,所以可知ADC控制器中断线的index是10。中断信息如下:
interrupt-parent = <&combiner>;
interrupts = <10 3>;
ADC外设设备树信息
fs4412-adc{
compatible = "fs4412,adc";
reg = <0x126C0000 0x20>;
interrupt-parent = <&combiner>;
interrupts = <10 3>;
};
本文默认大家会使用设备树,不知道如何使用设备树的朋友,后续会开一篇单独讲解设备树。【注意】在不支持设备树内核中,以Cortex-A8为例,中断信息填写在以下文件中
内部中断,Irqs.h (archrmmach-s5pc100includemach)
外部中断在Irqs.h (archrmplat-s5pincludeplat)
ADC属于内部中断,位于archrmmach-s5pc100includemachIrqs.h中。
寄存器信息填写在以下位置:
archrmmach-s5pc100Mach-smdkc100.c
static struct platform_device *smdkc100_devices[] __initdata = {
&s3c_device_adc,
&s3c_device_cfcon,
&s3c_device_i2c0,
&s3c_device_i2c1,
&s3c_device_fb,
&s3c_device_hsmmc0,
&s3c_device_hsmmc1,
&s3c_device_hsmmc2,
&samsung_device_pwm,
&s3c_device_ts,
&s3c_device_wdt,
&smdkc100_lcd_powerdev,
&s5pc100_device_iis0,
&samsung_device_keypad,
&s5pc100_device_ac97,
&s3c_device_rtc,
&s5p_device_fimc0,
&s5p_device_fimc1,
&s5p_device_fimc2,
&s5pc100_device_spdif,
};
结构体s3c_device_adc定义在以下文件:
rchrmplat-samsungDevs.c
#ifdef CONFIG_PLAT_S3C24XX
static struct resource s3c_adc_resource[] = {
[0] = DEFINE_RES_MEM(S3C24XX_PA_ADC, S3C24XX_SZ_ADC),
[1] = DEFINE_RES_IRQ(IRQ_TC),
[2] = DEFINE_RES_IRQ(IRQ_ADC),
};
struct platform_device s3c_device_adc = {
.name = "s3c24xx-adc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_adc_resource),
.resource = s3c_adc_resource,
};
#endif CONFIG_PLAT_S3C24XX
#if defined(CONFIG_SAMSUNG_DEV_ADC)
static struct resource s3c_adc_resource[] = {
[0] = DEFINE_RES_MEM(SAMSUNG_PA_ADC, SZ_256),
[1] = DEFINE_RES_IRQ(IRQ_TC),
[2] = DEFINE_RES_IRQ(IRQ_ADC),
};
struct platform_device s3c_device_adc = {
.name = "samsung-adc",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_adc_resource),
.resource = s3c_adc_resource,
};
#endif CONFIG_SAMSUNG_DEV_ADC
由代码可知,平台驱动对应的platform_device具体内容由宏CONFIG_PLAT_S3C24XX、CONFIG_SAMSUNG_DEV_ADC来控制。驱动编写架构和流程如下
read()
{
1、向adc设备发送要读取的命令
ADCCON 1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6
2、读取不到数据就休眠
wait_event_interruptible();
3、等待被唤醒读数据
havedata = 0;
}
adc_handler()
{
1、清中断 ADC使用中断来通知转换数据完毕的
2、状态位置位;
havedata=1;
3、唤醒阻塞进程
wake_up()
}
probe()
{
1、读取中断号,注册中断处理函数
2、读取寄存器的地址,ioremap
3、字符设备的操作
}
驱动需要首先捕获中断信号后再去寄存器读取相应的数据,在ADC控制器没有准备好数据之前,应用层需要阻塞读取数据,所以在读取数据的函数中,需要借助等待队列来实现驱动对应用进程的阻塞。驱动程序
驱动程序对寄存器的操作参考裸机程序,只是基地址需要通过ioremap()做映射,对寄存器的读写操作需要用readl、writel。
driver.c
#include <linux/module.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <asm/io.h>
static int major = 250;
static wait_queue_head_t wq;
static int have_data = 0;
static int adc;
static struct resource *res1;
static struct resource *res2;
static void *adc_base;
#define ADCCON 0x0000
#define ADCDLY 0x0008
#define ADCDAT 0x000C
#define CLRINTADC 0x0018
#define ADCMUX 0x001C
static irqreturn_t adc_handler(int irqno, void *dev)
{
have_data = 1;
printk("11111");
清中断
writel(0x12,adc_base + CLRINTADC);
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
static int adc_open (struct inode *inod, struct file *filep)
{
return 0;
}
static ssize_t adc_read(struct file *filep, char __user *buf, size_t len, loff_t *pos)
{
writel(0x3,adc_base + ADCMUX);
writel(1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6 ,adc_base +ADCCON );
wait_event_interruptible(wq, have_data==1);
read data
adc = readl(adc_base+ADCDAT)&0xfff;
if(copy_to_user(buf,&adc,sizeof(int)))
{
return -EFAULT;
}
have_data = 0;
return len;
}
static int adc_release(struct inode *inode, struct file *filep)
{
return 0;
}
static struct file_operations adc_ops =
{
.open = adc_open,
.release = adc_release,
.read = adc_read,
};
static int hello_probe(struct platform_device *pdev)
{
int ret;
printk("match 0k ");
res1 = platform_get_resource(pdev,IORESOURCE_IRQ, 0);
res2 = platform_get_resource(pdev,IORESOURCE_MEM, 0);
ret = request_irq(res1->start,adc_handler,IRQF_DISABLED,"adc1",NULL);
adc_base = ioremap(res2->start,res2->end-res2->start);
register_chrdev( major, "adc", &adc_ops);
init_waitqueue_head(&wq);
return 0;
}
static int hello_remove(struct platform_device *pdev)
{
free_irq(res1->start,NULL);
free_irq(res2->start,NULL);
unregister_chrdev( major, "adc");
return 0;
}
static struct of_device_id adc_id[]=
{
{.compatible = "fs4412,adc" },
};
static struct platform_driver hello_driver=
{
.probe = hello_probe,
.remove = hello_remove,
.driver ={
.name = "bigbang",
.of_match_table = adc_id,
},
};
static int hello_init(void)
{
printk("hello_init");
return platform_driver_register(&hello_driver);
}
static void hello_exit(void)
{
platform_driver_unregister(&hello_driver);
printk("hello_exit ");
return;
}
MODULE_LICENSE("GPL");
module_init(hello_init);
module_exit(hello_exit);
测试程序
test.c
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
main()
{
int fd,len;
int adc;
fd = open("/dev/hello",O_RDWR);
if(fd<0)
{
perror("open fail ");
return ;
}
while(1)
{
read(fd,&adc,4);
printf("adc%0.2f V ",(1.8*adc)/4096);
}
close(fd);
}
图片新闻
最新活动更多
-
11月22日立即报名>> 【线下论坛】华邦电子与莱迪思联合技术论坛
-
即日-11.30免费预约申请>>> 燧石技术-红外热成像系列产品试用活动
-
11月30日立即试用>> 【有奖试用】爱德克IDEC-九大王牌安全产品
-
即日-12.26火热报名中>> OFweek2024中国智造CIO在线峰会
-
限时免费下载立即下载 >>> 2024“机器人+”行业应用创新发展蓝皮书
-
即日-2025.8.1立即下载>> 《2024智能制造产业高端化、智能化、绿色化发展蓝皮书》
推荐专题
- 1 同源共创 模式革新 | 华天软件皇冠CAD(CrownCAD)2025新品发布会圆满举行
- 2 上海国际嵌入式展暨大会(embedded world China )与多家国际知名项目达成合作
- 3 史上首次,大众终于熬不住开启40亿降本计划!关3个工厂,裁员万名...
- 4 iEi威强电新品丨IMBA-AM5:工业计算的强劲引擎
- 5 守护绿色学习空间,EK超低温热泵服务对外经济贸易大学图书馆
- 6 颜值高 有“门”道|贝特威汽车门板内饰AI视觉检测解决方案
- 7 观众登记启动 优解制造未来,锁定2025 ITES深圳工业展
- 8 “秸”尽全力,防患未“燃” | 秸秆焚烧智能监控解决方案
- 9 揭秘:「全球知名跨境电商」构建核心竞争力的“独门绝技”是?
- 10 3大场景解读 | 红外热像仪赋能科研智造创新应用
发表评论
请输入评论内容...
请输入评论/评论长度6~500个字
暂无评论
暂无评论