Android系统下通过GPIO硬件中断实现Android程序唤醒

Android系统底层基于Linux内核,所以要实现硬件中断唤醒Android程序,需要编写Linux驱动,注册对应GPIO中断函数,然后通过异步通知方式将中断信号发送到用户空间应用程序,用户空间应用程序需要注册信号,再将进程PID注册为异步通知方式,这样就能够接收驱动程序发过来的中断信号了,然后我们在用户空间注册的信号处理函数中唤醒Android程序。
1、GPIO中断驱动程序

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/fs.h>
    #include <linux/cdev.h>
    #include <linux/platform_device.h>
    #include <linux/miscdevice.h>
    #include <linux/fs.h>
    #include <linux/slab.h>
    #include <linux/poll.h>
    #include <linux/sched.h>
    #include <linux/irq.h>
    #include <linux/pinctrl/pinctrl.h>
    #include <linux/interrupt.h>
    #include <linux/gpio.h>
    #include <mach/gpio.h>
     
    #define touch_gpio GPIOA(6)   //这个引脚为中断输入引脚
    struct fasync_struct *fasync_queue; //异步通知队列
     
    //中断处理函数
    static irqreturn_t touch_interrupt(int irq, void *dev_id) {
        /* 在中断服务函数中向应用层发送SIGIO信号-异步通知 */
        kill_fasync(&fasync_queue, SIGIO, POLL_IN);
        printk("touch_interrupt\n");
        return IRQ_RETVAL(IRQ_HANDLED);     
    }
     
    static int touch_open(struct inode *inode, struct file *file)
    {
        int ret;
        int virq;
        printk("touch_open\n");
        if(atomic_read(&file->f_count) != 0)
        {
            /*gpio申请*/
            ret = gpio_request(touch_gpio,"touch_gpio");
            if(ret!=0){
                printk("gpio_request failed.\n");
            }
            /*将gpio端口映射到中断号*/
            virq = gpio_to_irq(touch_gpio);
            if (IS_ERR_VALUE(virq)) {
                printk("map gpio [%d] to virq [%d] failed\n", touch_gpio, virq);
                return -EINVAL;
            }
            /* 申请中断并设置为高电平触发 */
            ret = request_irq(virq, touch_interrupt, IRQF_TRIGGER_RISING, "PA0_EINT", NULL);
            if (IS_ERR_VALUE(ret)) {
                printk("request virq %d failed, errno = %d\n", virq, ret);
                return -EINVAL;
            }
        }
        return 0;
    }
    static int touch_release(struct inode *inode, struct file *file)
    {
        printk("touch_release\n");
        if(atomic_read(&file->f_count) == 0)
        {
            int virq;
            virq = gpio_to_irq(touch_gpio);
            if (IS_ERR_VALUE(virq)) {
                printk("map gpio [%d] to virq [%d] failed\n", touch_gpio, virq);
                return -EINVAL;
            }
            free_irq(virq,NULL);
            /*释放GPIO*/
            gpio_free(touch_gpio);
        }
        return 0;
    }
    static int touch_fasync(int fd, struct file *file, int on)
    {
        /*将该设备登记到fasync_queue队列中去*/
        int ret = fasync_helper(fd, file, on, &fasync_queue);
        if(ret<0){
            printk("failed touch_fasync \n");
            return ret;
        }
        return 0;
    }
    static struct file_operations touch_ops = {
        .owner = THIS_MODULE,
        .open = touch_open,
        .release = touch_release,
        .fasync = touch_fasync,
    };
     
    static struct miscdevice touch_device = {
        .minor    = MISC_DYNAMIC_MINOR,
        .fops    = &touch_ops,
        .name    = "touch_driver", // 杂项设备名称为“touch_driver”
    };
     
    static int touch_init(void)
    {
        int ret;
        printk("touch_init\n");
        ret = misc_register(&touch_device);
        return ret;
    }
     
    static void touch_exit(void)
    {
        printk("touch_exit\n");
        misc_deregister(&touch_device);
    }
     
    MODULE_LICENSE("GPL v2");
    module_init(touch_init);
    module_exit(touch_exit);

怎么编译驱动程序就不说了,我这里直接将驱动编译进Linux内核了。重新编译Android系统镜像,烧写到开发板上,启动系统,使用adb shell进入Android系统后台,通过dmesg | grep touch可以查看内核是否打印了“touch”文本相关信息。

2、若是Linux系统下,可以通过如下代码测试驱动是否正常。

    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <poll.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/stat.h>
     
    //中断处理函数
    void touch_irq(int signum)
    {
        printf("touch_irq called\n");
    }
     
    void int main()
    {
        //设备文件名
        char* dev = "/dev/touch_driver";
        fd = open(dev, O_RDWR);
        if(fd<0){
            printf("can not open \n");
        }
        //注册一个信号, 启动信号驱动机制
        signal(SIGIO, touch_irq);
        //将本应用程序的进程号告诉给内核,最终使得驱动程序可以成功发送信号给应用程序
        fcntl(fd, F_SETOWN, getpid());
        //取得当前文件描述符的状态
        int oflags = fcntl(fd, F_GETFL);
        //设置fasync标记,最终会调用驱动的fasync->fasync_helper
        fcntl(fd, F_SETFL, oflags|FASYNC);
        while(1)
        {
            sleep(1);
        }
        return 0;
     
    }

3、编写Android应用程序测试(Android下需要使用jni调用本地层程序来注册信号)

编写Java类接口和对应的jni代码实现

    public class TouchSenior {
        public native int open(String filepath); // 打开设备文件
        public native void setState(int fd,boolean isOpen); // 设置异步通知是否开启
        public native void close(int fd); // 关闭设备文件
        static {
            System.loadLibrary("touch"); // 这里将jni实现的本地方法编译为libtouch.so动态库
        }
    }

编写jni实现代码

    #include <jni.h>
     
    #ifdef __cplusplus
    extern "C" {
    #endif
     
    #include <android/log.h>
    #define  LOG_TAG    "v4l2_capture"
    #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
    #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG  , LOG_TAG, __VA_ARGS__)
    #define LOGI(...) __android_log_print(ANDROID_LOG_INFO   , LOG_TAG, __VA_ARGS__)
    #define LOGW(...) __android_log_print(ANDROID_LOG_WARN   , LOG_TAG, __VA_ARGS__)
    #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR  , LOG_TAG, __VA_ARGS__)
     
     
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <fcntl.h>
    #include <poll.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/stat.h>
     
    static int fd;
     
    //中断函数
    void touch_irq(int signum)
    {
        LOGD("touch_irq called\n");
    }
     
    JNIEXPORT jint JNICALL
    Java_com_yobotics_touchtest_TouchSenior_open(JNIEnv *env, jobject instance, jstring filepath_) {
        const char *filepath = (*env)->GetStringUTFChars(env, filepath_, 0);
        fd = open(filepath, O_RDWR);
        if(fd<0){
            LOGD("can not open \n");
        }
        //注册一个信号, 启动信号驱动机制
        signal(SIGIO, touch_irq);
     
        //将本应用程序的进程号告诉给内核,最终使得驱动程序可以成功发送信号给应用程序
        fcntl(fd, F_SETOWN, getpid());
     
        (*env)->ReleaseStringUTFChars(env, filepath_, filepath);
        return fd;
    }
     
    JNIEXPORT void JNICALL
    Java_com_yobotics_touchtest_TouchSenior_setState(JNIEnv *env, jobject instance, jint fd,
                                                     jboolean isOpen) {
        if ( isOpen ){
            int oflags = fcntl(fd, F_GETFL);
            fcntl(fd, F_SETFL, oflags|FASYNC);//设置fasync标记
        } else{
            int oflags = fcntl(fd, F_GETFL);
            fcntl(fd, F_SETFL, (oflags&(!FASYNC))); // 取消fasync标记
        }
    }
     
    JNIEXPORT void JNICALL
    Java_com_yobotics_touchtest_TouchSenior_close(JNIEnv *env, jobject instance, jint fd) {
        close(fd);
    }
     
    #ifdef __cplusplus
    }
    #endif

应用程序调用该接口方法实现注册中断,接受中断

    public class MainActivity extends AppCompatActivity {
     
        TouchSenior touchSenior;
        int fd;
     
        @BindView(R.id.btn_open)
        Button btnOpen;
        @BindView(R.id.btn_close)
        Button btnClose;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
     
            touchSenior = new TouchSenior();
            fd = touchSenior.open("/dev/touch_driver");
            Log.d("TTTTT", "fd:" + fd);
        }
     
        @Override
        protected void onDestroy() {
            touchSenior.close(fd);
            super.onDestroy();
        }
     
        @OnClick(R.id.btn_open)
        public void onBtnOpenClicked() {
            touchSenior.setState(fd,true);
        }
     
        @OnClick(R.id.btn_close)
        public void onBtnCloseClicked() {
            touchSenior.setState(fd,false);
        }
    }

点击打开按钮后,Android应用程序会调用本地方法设置进程接受该中断信号,这样我们就可以在中断函数中执行响应的操作来唤醒Android相关的程序,执行相应的操作了。

4、测试GPIO中断

这里使用GPIOA6引脚,将其作为中断输入引脚,因为Linux驱动里边没有中断上拉下拉的标准接口,所以测试的时候需要先将GPIOA6引脚接一个外部下拉电路,电路图如下:

当我们将开关闭合时,在Android Studio的Logcat中会看到打印日志

01-02 09:23:40.710 2201-2201/com.fox.touchtest D/v4l2_capture: touch_irq called
————————————————
版权声明:本文为CSDN博主「永不做码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a499957739/article/details/104425753/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值