ARM-Linux定时器、中断驱动--超声波测距初试

最近要实验用到超声波测距,一下子着手驱动设计,简直无从下手。

由于没有接触过裸板程序,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

ARM-Linux定时器、中断驱动--超声波测距初试的相关文章

[自娱自乐] 3、超声波测距模块DIY笔记(三)

前言 上一节我们已经研究了超声波接收模块并自己设计了一个超声波接收模块,在此基础上又尝试用单片机加反相器构成生成40KHz的超声波发射电路,可是发现采用这种设计的发射电路存在严重的发射功率太低问题,对齐的情况下最多只有10CM.本节主要介绍并制造一种大功率超声波发射装置~ 目录 一.浪里淘金,寻找最简超声波功率提高方案 1.1.优化波形发生程序 1.2.尝试各种其他超声模块方案 1.3.用三极管放大信号 1.4.MAX232放大信号方案 二.步步为营,打造高效准确超声测距算法 2.1.接收MCU

[自娱自乐] 2、超声波测距模块DIY笔记(二)

前言 上一节我们已经大致浏览下目前销售的超声波测距模块同时设计了自己的分析电路,这次由于我买的电子元件都到了,所以就动手实验了下!至写该笔记时已经设计出超声波接收模块和超声波发射模块,同时存在超声波发射模块功率太小的问题,下面主要做该过程的总结! 一.尝试找出一个简单的超声波接收电路: >_<" 首先根据我现有的电子元件,最终找到一个比较适合的简单设计方法:这里用一个芯片CX20106A也就是上一节我说的这种方案简单但是不利于理解超声波接收部分的具体细节!但是为了方便设计,我还是选择

[自娱自乐] 4、超声波测距模块DIY笔记(四)——终结篇&#183;基于C#上位机软件开发

前言 上一节我们已经基本上把超声波硬件的发射和接收模块全部做好了,接下来我们着手开发一个软硬结合的基于C#的平面定位软件! 目录 一.整体思路 二.效果提前展示 2-1.软件部分展示 2-2.硬件部分展示 三.基于C#的客户端软件说明 3-1.整体框架介绍: 3-2.部分技术细节介绍 3-2-1.串口操作 3-2-2.JiSuan函数说明及核心算法介绍 四.阶段小结 五.相关链接 一.整体思路 >_<" 如下图,利用我们上三节开发的超声波发射与接收设备构成一个:2固定接收头+1可移动

传感器之超声波测距HC-SR04

一.前言 HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm:模块包括超声波发射器.接收器与控制电路.像智能小车的测距以及转向,或是一些项目中,常常会用到.智能小车测距可以及时发现前方的障碍物,使智能小车可以及时转向,避开障碍物,所以,我们今天就来学习一下这个传感器. 二.工作原理 1.给超声波模块接入电源和地.      2.给脉冲触发引脚(trig)输入一个长为20us的高电平方波 3.输入方波后,模块会自动发射8个40KHz的声波,与此同时回

基于K60/K66的HC-SR04超声波测距

连接单片机引脚:VCC(5V).IO口.IO口.GND 步骤: (1)给脉冲触发IO口引脚(trig)输入一个持续时间>10us的高电平: (2)输入方波,模块自动发送8个40Khz的声波: (3)同时,回波引脚(Ecoh)引脚电平由0变为1(输出高电平),此时应该启动定时器: (4)当超声波返回并被接收模块(ecoh)接收到时,接收端电平由1变为0(变为低电平),此时关闭定时器: (5)定时器的持续时间即为超声波往返的时间: *测公式:距离=(高电平时间*声速)/2: 调试经验 1.注意超声波

arm linux 启动之二:start_kernel到创建1号进程

本文介绍arm linux启动的第二部分,C语言编写,主要讲述start_kernel到1号进程的创建.主要讲述大概过程,以后再对子函数进行讲解. 一.start_kernel start_kernel位于init/main.c,主要完成linux一些子系统的初始化. 1)smp_setup_processor_id() 单CPU位为空. 2)lock_kernel() 锁CPU,linux是支持抢占的,多CPU时调用这个函数防止其他CPU抢占.3)tick_init() 时间相关初始化4)bo

ARM Linux从Bootloader、kernel到filesystem启动流程

转自:http://www.veryarm.com/1491.html ARM Linux启动流程大致为:bootloader ---->kernel---->root filesystem.bootloader 是一上电就拿到cpu 的控制权的,而bootloader实现了硬件的初始化.bootloader俨然就成了Power on 之后”第一个吃螃蟹”的代码. 谈到这就得想到硬件机制是如何满足这个功能的了.CPU内部一般都集成小容量的SRAM (又叫stapping stone,垫脚石),

ARM Linux 大小核切换 ——cortex-A7 big.LITTLE 大小核 切换代码分析

ARM Linux 大小核切换——cortex-A7 big.LITTLE 大小切换代码分析 8核CPU或者是更多核的处理器,这些CPU有可能不完全对称.有的是4个A15和4个A7,或者是4个A57和4个A53,甚至像海思麒麟935处理器(4核A53 2.2 GHz + 4核A53 1.5 GHz),这8个核的频率可能不一样,则使用过程中需要大小核切换(频率高的是大核,频率低的是小核).本文以ARM cortex-A7为例,分析大小核切换的代码,着重于分析实现切换的代码,对于为什么要这样切换.以

ARM linux电源管理——Cortex A系列CPU(32位)睡眠和唤醒的底层汇编实现

ARM linux电源管理——Cortex A系列CPU(32位)睡眠和唤醒的底层汇编实现 承接 http://www.wowotech.net/pm_subsystem/suspend_and_resume.html Linux电源管理(6)_Generic PM之Suspend功能一文中的下图. 本文主要分析平台相关的CPU睡眠和唤醒,即下电和上电流程,以及ARM底层汇编代码实现. 内核版本:3.1.0               CPU:ARM Cortex-A7 1 平台相关函数执行流程