最近要实验用到超声波测距,一下子着手驱动设计,简直无从下手。
由于没有接触过裸板程序,arm板底子薄弱,所以很多东西写的不完善,在加上这是自己的第一个驱动程序,写起来也比较吃力。多亏了很多前辈分享的一些代码,参考学习下,写了一个自己的驱动程序,下面特此分享出来。
目前还有很多东西不完善,我的模块通电就开始工作,至于10us的脉冲信号激发,我是没有用上,日后要好好调试一番,下面这个驱动就没有涉及Trig触发信号。
看别人的代码是一件很辛苦的事情,下面我尽可能的把注释写完整。毕竟这也是从许多前辈那借鉴参考过来的。我也懂这个过程的艰辛。
下面声明一下主要用到的东西,啰嗦就啰嗦点吧。
我用的是TQ2440板子,三星S3C2440系列。超声波模块就是某宝4.5元一个的普通传感器。
首先要arm板的GPIO口有一些了解,可以下载S3C2440数据手册查看每个寄存器对应的地址及以后要用到的中断相应的控制寄存器器并查看底板电路图。
主要思想就是设置一个计数器(定时器,板子有相应的定时器,是硬件定时器。),设置引脚的中断触发方式,和中断处理函数,多数和裸板程序一样,只要把相应的寄存器地址映射到内存中(也有相应的库函数,耐心点可以找到)。
之前尝试了边沿触发,但是效果不好,会吧低电平信号时间也会记录下来,无法区分,之后便把传感器输出引脚分了两根线出来分别接在不同的引脚上,一个用来等待上升沿中断,一个用来等待下降沿中断。
下面贴上驱动代码
#include <linux/miscdevice.h> #include <linux/module.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/string.h> #include <linux/list.h> #include <linux/pci.h> #include <linux/poll.h> #include <linux/platform_device.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/clk.h> #include <mach/regs-gpio.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include <asm/unistd.h> #include <plat/regs-timer.h> #include <mach/hardware.h> #include <linux/kernel.h> #include <linux/module.h> #define __IRQT_FALEDGE IRQ_TYPE_EDGE_FALLING #define __IRQT_RISEDGE IRQ_TYPE_EDGE_RISING #define __IRQT_LOWLVL IRQ_TYPE_LEVEL_LOW #define __IRQT_HIGHLVL IRQ_TYPE_LEVEL_HIGH #define IRQT_NOEDGE (0) #define IRQT_RISING (__IRQT_RISEDGE) #define IRQT_FALLING (__IRQT_FALEDGE) #define IRQT_BOTHEDGE (__IRQT_RISEDGE|__IRQT_FALEDGE) #define IRQT_LOW (__IRQT_LOWLVL) #define IRQT_HIGH (__IRQT_HIGHLVL) #define IRQT_PROBE IRQ_TYPE_PROBE volatile unsigned long *gpbcon = NULL; volatile unsigned long *gpbdat = NULL; volatile unsigned long *tcfg0 = NULL;//用来设置预分频 volatile unsigned long *tcfg1 = NULL;//用来设置分频 volatile unsigned long *tcon = NULL;//定时器控制器 volatile unsigned long *tcntb0 = NULL;//计数缓冲寄存器 volatile unsigned long *tcnto0 = NULL;//计数观察寄存器 volatile unsigned long *tcntb1 = NULL;//比较缓冲寄存器 volatile unsigned long *tcnto1 = NULL;//计数观察寄存器 volatile unsigned long *gpfcon = NULL; volatile unsigned long *gpfdat = NULL; volatile unsigned long *intmsk = NULL;//中断控制寄存器 volatile unsigned long *eintmsk = NULL;//外部中断控制寄存器 volatile unsigned long *extint2 = NULL;//外部中断控制寄存器 volatile unsigned long *extint1 = NULL;//外部中断控制寄存器 volatile unsigned long t0=0; volatile unsigned long t1=0; volatile unsigned long distance=0; static irqreturn_t irq__ENTE19(int irq, int dev_id)//上升延触发中断 { t0=*tcnto0; return IRQ_RETVAL(IRQ_HANDLED); } static irqreturn_t irq__ENTE8(int irq, int dev_id)//下降沿触发中断 { t1=*tcnto0; distance= t0- t1; distance=distance*17; printk("%6lu\t",distance); return IRQ_RETVAL(IRQ_HANDLED); } ssize_t drive_open (struct inode *inode, struct file *file) { *intmsk &=(~(1<<5)); //EINT8_23 [5] 0 = 可服务 1 = 屏蔽 *eintmsk &=~(1<<19);//EINT19 [4] 0 = 使能中断 *eintmsk &=~(1<<8);//EINT8使能中断 *extint2 |= (1<<14);//ENT19 11x bits[16:18]上升延触发 x01 1 *extint2 &= ~(1<<13);// 0 *extint1 &= ~(1<<2);//EINT8下降延触发 0 *extint1 |= 1<<1;//1 request_irq(IRQ_EINT19,irq__ENTE19, IRQT_RISING, "EINT19", NULL);//上升延触发 request_irq(IRQ_EINT8,irq__ENTE8, IRQT_FALLING, "EINT8", NULL);//下降延触发 printk("-----------------Drive open.----------------\n"); return 0; } ssize_t drive_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct clk *clk_p; unsigned int pclk; unsigned int tcnt; *tcfg0 &= ~0xff; //定时器0预分配清零 *tcfg0 |= (250 - 1); //预分频 250 *tcfg1 &= ~0xf; //定时器0 mux 输入分频清零 // 0000 mux 分频 2 clk_p = clk_get(NULL, "pclk"); pclk = clk_get_rate(clk_p); tcnt = pclk/250/2;//100 kHZ *tcntb0 &= 0x0000; //16位寄存器 *tcntb0 |=500000;//100us 信号 *tcntb1 &= 0x0000; //16位寄存器 *tcntb1 |=10000000;//100s “计数器” *tcon &= ~0x7ff; //1111 --> 0000清零 前11bits s3c2410_gpio_setpin(S3C2410_GPG0, 1); *tcon |=0xb; // timer0 关闭死区、自动重载、关反相器、手动更新TCNTB0&TCMPB0、启动定时器0 1011 *tcon &= ~2; //清除定时器0的手动更新位 return 0; } ssize_t drive_release (struct inode *inode, struct file *file) { free_irq(IRQ_EINT19, NULL); free_irq(IRQ_EINT8, NULL); printk("-----------------Drive close.----------------\n"); return 0; } static struct file_operations drive_ops = { .owner = THIS_MODULE, .open = drive_open, .ioctl = drive_ioctl, .release = drive_release, }; static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, .name = "mytimer", .fops = &drive_ops, }; static int __init init_drive(void) { int ret; ret = misc_register(&misc); tcfg0 = (volatile unsigned long *)ioremap(0x51000000, 4); //虚拟内存映射 tcfg1 = (volatile unsigned long *)ioremap(0x51000004, 4); tcon = (volatile unsigned long *)ioremap(0x51000008, 4); tcntb0 = (volatile unsigned long *)ioremap(0x5100000c, 4); tcntb1 = (volatile unsigned long *)ioremap(0x51000018, 4); intmsk = (volatile unsigned long *)ioremap(0x4a000008, 4); eintmsk = (volatile unsigned long *)ioremap(0x560000a4,4); tcnto0 = (volatile unsigned long *)ioremap(0x51000014, 4); tcnto1 = (volatile unsigned long *)ioremap(0x51000020, 4); extint1= (volatile unsigned long *)ioremap(0x5600008c,4); extint2= (volatile unsigned long *)ioremap(0x56000090, 4); s3c2410_gpio_cfgpin(S3C2410_GPG0, S3C2410_GPIO_OUTPUT); printk("-----------------Drive init.----------------\n"); return 0; } static void __exit exit_drive(void) { *tcon &= ~1; //关闭timer0 misc_deregister(&misc); printk("-----------------Drive button exit.----------------\n"); } module_init(init_drive); module_exit(exit_drive); MODULE_LICENSE("GPL");
下面是测试代码:
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char **argv) { int fd,ret; fd = open("/dev/mytimer", O_RDWR); if(fd == -1) { printf("can‘t open device mknod %s\n","/dev/mytimer"); return 0; } ioctl(fd, 1); while(1); close(fd); return 0; }
时间: 2024-10-12 02:59:57