首页 > 其他分享 >[香橙派开发系列]中断?不!中断!

[香橙派开发系列]中断?不!中断!

时间:2023-12-20 21:38:14浏览次数:23  
标签:系列 单片机 中断 16 香橙 int 线程 pd

目录

前言

中断这个在很多单片机中都是比较常见的,像什么51单片机,stm32单片机都是可以通过自己的设置来实现中断的。在香橙派这种比较高级的单片机上也是有中断的,但是呢,这里的中断和51或者stm的单片机有点不同。

一、什么是中断

中断这个概念老生常谈了,学过单片机的都应该知道这个,也就是说,你在这打游戏,有一个电话过来了,你就得先停止手上的游戏,然后去处理一下那件事,这个就是中断。这里就不细说中断的概念了,毕竟和香橙派上的中断不太一样。

二、普通单片机和系统单片机的区别

这里要划分一下概念,我分为普通单片机和系统单片机两种,普通单片机就是指像51和stm32那种单核单片机,就是按照一定顺序执行的单片机,然后系统单片机就是指这种能在里面运行操作系统的单片机。

这两种类型的单片机有什么不同呢?

做过单片机开发的都知道,这种单片机只能单步执行,比如你写的这些程序,它只能一条一条的执行,没办法引出一些线程或者进程的概念,这种单片机的中断我们可以自己配置的,因为我们可以接触到底层环境来直接配置。

而这种系统单片机,因为有一层系统,所以底层的一些操作都被一层叫做HAL的抽象包给封装了,我们可以使用这个抽象包给的一些接口来对底层进行一些操作,而且这种单片机的芯片都是比较强的,可以支持像什么线程和进程的操作。

三、中断的区别

在普通单片机中,我们设置好中断后,满足了中断的条件了,它就会打断它现在正在执行的操作,转向到中断处理函数中去执行中断,就如同这张图一样

img

就如图一样,每次执行中断的时候就会打断当前执行的内容。

而在系统单片机中就不一样了,因为系统单片机中它是可以支持多线程和多进程的,所以就可以不用打断当前的系统执行,而是分裂出一个进程或者线程来处理这个中断请求,就如下面这张图

img

可以看到这种就是这个过程不会影响着我们现在执行的程序,而我们的香橙派就是用到这种方式的中断。

四、配置香橙派的中断

1.进程版

我们配置香橙派的中断其实就可以不用像配置stm32的那种一样复杂了,直接分裂一个线程一直判断是否按下或者是其他内容即可实现这个过程。

比如在stm32中我们要一个按键按下了后就会执行中断处理函数实现一些内容,这里我们就可以使用一个进程一直去读取这个按键的引脚,当按键按下了,我们就让标志位置为1或者0,然后父进程就读取这个标志位,点亮一个LED灯。

代码就可以这么写:

1.先将引脚进行初始化,这里使用的是PC10作为输入引脚,PC7作为输出引脚,然后进行配置:

#include <wiringPi.h>
int main(){
    wiringPiSetup();      // 初始化
    pinMode(16, INPUT);   // PC10输入
    pinMode(13, OUTPUT);  // PC7作为输入
    pullUpDnControl(16, PUD_UP);    // 上拉输入
    return 0;
}

2.设置完成后就开始创建进程和匿名通道。这里因为要实现进程之间的数据交换,所以使用了一个匿名通道进行信息交换

#include <wiringPi.h>
#include <unistd.h>
int main(){
    int ret;       // 接收返回值
    int pd[2];     // 管道描述符
    int pid;       // 进程ID
    wiringPiSetup();      // 初始化
    pinMode(16, INPUT);   // PC10输入
    pinMode(13, OUTPUT);  // PC7作为输入
    pullUpDnControl(16, PUD_UP);    // 上拉输入
    // 创建匿名管道
    ret = pipe(pd);      // 创建管道
    if (ret < 0){
        // 错误处理
        perror("pipe");
        return -1;     // 退出程序
    }
    // 创建进程
    pid = fort();    // 创建进程
    if (pid < 0){
        // 错误处理
        close(pd[0]);      // 关闭管道读
        close(pd[1]);      // 关闭管道写
        perror("fort");
        return -2;
    }
    if (pid == 0){
        // 子进程处理
        
    }
    else{
        // 父进程处理
    }
    return 0;
}

3.在子进程中写入判断函数并将标志位发送到父进程中,并让LED灯亮起来。

#include <wiringPi.h>
#include <unistd.h>
#include <string.h>
int main(){
    int ret;       // 接收返回值
    int pd[2];     // 管道描述符
    int pid;       // 进程ID
    int flag = 1;  // 标志位
    char buf[2];   // 发送的字符串
    wiringPiSetup();      // 初始化
    pinMode(16, INPUT);   // PC10输入
    pinMode(13, OUTPUT);  // PC7作为输入
    pullUpDnControl(16, PUD_UP);    // 上拉输入
    // 创建匿名管道
    ret = pipe(pd);      // 创建管道
    if (ret < 0){
        // 错误处理
        perror("pipe");
        return -1;     // 退出程序
    }
    // 创建进程
    pid = fort();    // 创建进程
    if (pid < 0){
        // 错误处理
        close(pd[0]);      // 关闭管道读
        close(pd[1]);      // 关闭管道写
        perror("fort");
        return -2;
    }
    if (pid == 0){
        // 子进程处理
        close(pd[0]);     // 关闭读端,因为子进程只需要写入数据
        while(1){
            if (digitalRead(16) == 0){
                // 按键按下
                delay(20);
                while(digitalRead(16) == 0);   // 消抖
                flag = !flag;
                sprintf(buf, "%d", flag); // 拼接发送的字符串
                write(pd[1], buf, 2);     // 向管道写入数据
            }
        }
    }
    else{
        // 父进程处理
        close(pd[1]);     // 关闭写端,因为只用读取内容
        while(1){
            read(pd[0], buf, 2);   // 读取数据
            if (strcmp(buf, "1") == 0){
                // 高电平
                digitalWrite(13, 1);
            }
            else{
                // 低电平
                digitalWrite(13, 0);
            }
        }
    }
    return 0;
}

然后我们就可以执行一下查看一下效果了:

img

然后再按下按钮

img

为了让大家看得更清楚这个过程我特意添加了一下输出语句来给大家查看一下效果

img

这种方法就和中断一样,系统的主执行内容不会被影响,毕竟是有另一个进程在读取按键的按下和松开。

2.wiringPi库函数版

这种方法是使用wiringPi中的函数来进行实现的,但是我这一直都搞不了,我还在研究这个方法,为了弄这个我今天买了块3b来进行测试,我看看是不是因为zero的不支持还是什么原因,这里后面再补全,先介绍一下这个方法的代码如何写,这里使用的函数是wiringPiISR,函数的原型:

int wiringPiISR (int pin, int mode, void (*function)(void));

功能:该函数会在指定管脚注册一个中断事件的函数,当指定管脚发生中断事件时,会自动调用该函数。

第一个参数是你要让哪个引脚注册位中断,比如说PC7,那这就填写13。

第二个参数是触发模式

模式 解释
INT_EDGE_FALLING 下降沿触发
INT_EDGE_RISING 上升沿触发
INT_EDGE_BOTH 上升沿或下降沿触发
INT_EDGE_SETUP 不初始化

当使用最后的一种方法时,这个函数不会初始化这个引脚,它会默认是在其他敌法进行了初始化。

第三个参数是中断函数,就是当注册的引脚触发了就会触发这个函数进行执行。

返回值如果不成功就会返回一个小于0的数,可以用这个返回值来判断一下初始化是否成功。

知道了这个我们就可以利用这个函数来实现中断:

#include <wiringPi.h>
int flag = 0;
void myinterrinput(void){
    // 中断处理函数
    flag = !flag;
}

int main(){
    wiringPiSetup();
    pinMode(16, INPUT);
    pinMode(13, OUTPUT);
    pullUpDnControl(16, PUD_UP);    // 上拉输入
    wiringPiISR(16, INT_EDGE_FALLING, &myinterrinput);
    while(1){
        digitalWrite(13, flag);
    }
    return 0;
}

执行后,当按键按下后就会执行中断函数,但是我这一执行后就会出现下面的问题

img

这个问题我还在研究到底是为什么,网上也没找到合适的答案,这里我先空着,等3b到了我再试试。

3.线程版

这个版本就可以分为Linux内核线程和wiringPi库函数版的线程了。

这里先使用Linux内核的线程

3.1 Linux内核的线程

其实这个也不能称为Linux内核线程,因为之前的linux最初开发时,在内核中并不能真正支持线程。但是它的确可以通过 clone() 系统调用将进程作为可调用度的实体。然后就将线程进行了改写,由NPTL进行接手,我们现在用的线程是NPTL。

我们可以通过下面的指令来下载NPTL

sudo apt-get install manpages-posix-posix-dev

通过下面的指令来查看一下NPTL的版本

getconf GNU_LIBPTHREAD_VERSION

img

我这里的版本是2.35,只要有这个即可。

这里不过多介绍线程的概念,如果大家对线程感兴趣我后面会出有关于线程的文章给大家介绍一下的。

现在开始编程

1.初始化需要的引脚

#include <wiringPi.h>
int main(){
    wiringPiSetup();      // 初始化
    pinMode(16, INPUT);   // PC10输入
    pinMode(13, OUTPUT);  // PC7作为输入
    pullUpDnControl(16, PUD_UP);    // 上拉输入
    return 0;
}

2.创建管道和线程,这里线程之间的内容是独立的,和上面的进程一样,需要我们创建一个管道来实现进程间的通讯,所以需要创建一个管道。

在写之前,我们需要了解一下线程的创建函数:

#include <pthread.h>

int pthred_create(pthread_t* thread, const pthread_attr_t* attr, void* (*start_routine)(void*), void* arg);
功能:
    创建一个线程
参数:
    thread:线程标识符地址。
    attr:线程属性结构体地址,通常设置为NULL。
    start_routine:线程函数的入口地址。
    arg:传入线程函数的参数。
返回值:
    成功:0
    失败:非0

然后就可以开始写代码:

#include <wiringPi.h>
#include <pthread.h>
void* myinterrinput(void* arg){
    // 进程执行函数
    int flag = 0;
    char buf[2];
    while(1){
        
    }
    return NULL;
}

int main(){
    int ret;
    pthread_t tid;       // 线程标识符
    int pd[2];
    char buf[2];

    wiringPiSetup();     // 初始化
    pinMode(16, INPUT);
    pinMode(13, OUTPUT);
    pullUpDnControl(16, PUD_UP);

    // 创建管道
    ret = pipe(pd);
    if (ret < 0){
        perror("pipe");
        return -1;
    }

    // 创建线程
    ret = pthread_create(&tid, NULL, &myinterrinput, (void*)&pd[1]);       // 创建进程的函数 这里将管道的描述符传递给线程处理函数
    if (ret < 0){
        close(pd[0]);      // 关闭管道读
        close(pd[1]);      // 关闭管道写
        perror("pthread_create");
        return -2;
    }
    while(1){
        // 主进程处理一些非中断的内容
    }
    return 0;
}

3.然后开始完善处理代码

#include <stdio.h>
#include <pthread.h>
#include <wiringPi.h>
#include <unistd.h>
#include <string.h>

void* myinterrinput(void* arg){
    int flag = 0;
    char buf[2];
    while(1){
        if (digitalRead(16) == 0){
            while(digitalRead(16) == 0);
            flag = !flag;
            sprintf(buf, "%d", flag);
            // 下面的代码需要注意一下,非常重要,用了两次强转,因为传递过来的参数是void类型的,长度不够,所以这里使用了两次强转将长度匹配到4
            if (write(*(int*)(long*)arg, buf, 2) < 0){
                printf("error\n");
            }
        }
    }
    return NULL;
}

int main(){
    int ret;
    pthread_t tid;       // 线程标识符
    int pd[2];
    char buf[2];

    wiringPiSetup();     // 初始化
    pinMode(16, INPUT);
    pinMode(13, OUTPUT);
    pullUpDnControl(16, PUD_UP);

    // 创建管道
    ret = pipe(pd);
    if (ret < 0){
        perror("pipe");
        return -1;
    }

    // 创建线程
    ret = pthread_create(&tid, NULL, &myinterrinput, (void*)&pd[1]);
    if (ret < 0){
        close(pd[0]);      // 关闭管道读
        close(pd[1]);      // 关闭管道写
        perror("pthread_create");
        return -2;
    }
    while(1){
        read(pd[0], buf, 2);
        if (strcmp(buf, "1") == 0){
            digitalWrite(13, 1);
            printf("1");
        }
        else if(strcmp(buf, "0") == 0){
            printf("0");
            digitalWrite(13, 0);
        }
    }
    return 0;
}

然后开始编译,这里编译的命令如下:

gcc threadButton.c -o threadButton -lwiringPi -lpthread

因为pthread是属于外部库,需要使用-l进行连接。

运行后也是一样的效果,我就懒得拍照了。

总结

中断其实很有用的,但是对于这种多线程的开发板可以用一些其他的方式来实现这个方法,比如软中断,后面我研究一下如何通过底层实现外中断,毕竟上面的这些方法消耗的资源有点大,而且反应比直接中断要慢。

标签:系列,单片机,中断,16,香橙,int,线程,pd
From: https://www.cnblogs.com/Lavender-edgar/p/17917599.html

相关文章

  • 【愚公系列】2023年12月 通用职责分配原则(九)-受保护变量原则(Protected Variations
    ......
  • C++系列三:QT代码库
    目录前言QT小记1.菜单栏、工具栏、状态栏2.自定义的对话框3.任务管理器4.链接数据库mysql,sqlite5.WidgetsGalleryExample代码学习:999.ControlsQT-For-Python1.DemoQT-Quick1.HelloWorld2.简单表单前言记录有关qt的案例。QT小记知识点:1.ui_widget.h:类的名称是Ui......
  • Unity 3D定点数物理引擎实战系列1.1BEPUphysicsint 3D定点数物理引擎介绍
    1.1BEPUphysicsint3D定点数物理引擎介绍对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀帧同步的游戏中如果用物理引擎,为了保证不同设备上的结果一致,需要采用定点数来计算迭代游戏过程中的物理运算。也就是我们通常说的定点数物理引擎(确定性物理......
  • RTSP流媒体视频平台LiteNVR播放HLS流,出现中断且无法自动恢复的原因排查
    有用户反映,在使用安防视频LiteNVR平台时,取平台分发的hls地址在移动端播放一段时间就会停止,且无法自动恢复播放。今天我们来介绍下该问题的排查与解决方法。LiteNVR是基于RTSP/Onvif协议推出的安防视频监控管理平台,它可实现设备接入、实时直播、录像、检索与回放、存储、视频分发......
  • 最高法--票据权利时效均系可中断时间,对票据时效起诉后再撤诉也应当视为中断。
    1.(2022)最高法民申727号  陕西能源凉水井矿业有限责任公司、陕西华山创业有限公司等票据追索权纠纷民事申请再审审查民事裁定书申请人主张:凉水井公司、华山创业公司、陕西能源公司依据《中华人民共和国民事诉讼法》第二百零七条规定申请再审,请求:1.裁定中止本案原审判决执行;2.撤销......
  • [Unraid 系列 v6.10+] 9 安装 qbittorrent 容器
    说明Unraid建议使用ComposeSTACK进行管理。初始创建docker-compose.yml:version:"3"services:qbittorrent:image:linuxserver/qbittorrentcontainer_name:qbittorrentenvironment:-PUID=99-PGID=100-TZ=Asia/Sh......
  • 任务调度处理系列之 Spring源码分析-【SchedulingConfigurer实现原理】转
     一、可能的场景在做业务平台的时候我们经常会遇到,某些跟时间打交道的需要修改状态,比如说在时间区间之前,属于未生效状态,区间之内属于有效期,区间之后,属于过期,或者需要每天每周每月,甚至是年为单位的做一些固定的操作。通过定时任务可以通过开启定时任务来完成这些需求。我做合......
  • 模拟集成电路设计系列博客——4.3.1 有源RC滤波器
    4.3.1有源RC滤波器除了Gm-C滤波器外,另一种实现模拟集成滤波器的方案是有源RC滤波器或者MOSFET-C滤波器。在这两个技术中,电流的积分都是通过反馈连接在一个高增益放大器的电容上实现的,这与将电流积分电容连接到地的Gm-C滤波器方案不同。有时这种方案被叫做米勒积分,因为就像两级放......
  • RISC-V系列单片机快速入门指南
     如何获取芯片开发资料方法一:按型号选择我们更推荐采用按型号选择的方法,获取所对应型号芯片的开发资料,这能有效降低错误使用资料的风险!沁恒官网首页的产品中心,点击青稞RISC-V通用系列,可跳转至CH32V系列单片机的产品选型表。 以CH32V203C8T6为例,点击红色方框中的芯......
  • Kernel Memory 入门系列: Embedding 简介
    KernelMemory入门系列:Embedding简介在RAG模式其实留了一个问题。我们对于的用户问题的理解和文档的检索并没有提供合适的方法。当然我们可以通过相对比较传统的方法。例如对用户的问题进行关键词提取,然后通过关键词检索文档。这样的话,就需要我们提前对文档做好相关关键......