精华内容
下载资源
问答
  • 2,当检测出有信号或中断请求时,都暂停证执行程序而转去执行相应处理程序 3,都处理完毕之后返回到原来断点 4,对信号或中断都可进行屏蔽 信号与中断区别: 1,中断有优先级,而信号没有,所有信号都...

    信号是响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。某些状况就是指某些条件错误,如内存段冲突、浮点处理器错误或者非法指令等。信号是在软件层次上对中断的一种模拟,所以信号也称为是软中断
    信号与中断的相似点:
    1,都采用相同的额异步通信方式
    2,当检测出有信号或中断请求时,都暂停证在执行的程序而转去执行相应的处理程序
    3,都在处理完毕之后返回到原来的断点
    4,对信号或中断都可进行屏蔽

    信号与中断的区别:
    1,中断有优先级,而信号没有,所有的信号都是平等的
    2,信号处理程序是在用户态下运行的,而中断处理程序是在核心态下运行的
    3,中断响应是及时的,而信号响应通常都有较大的事件延迟

    进程对信号的三种响应:
    1,忽略信号,除了SIGKILL,SIGSTOP
    2,捕获并处理信号,内核中断正在执行的代码,转去执行先前注册处
    3,执行默认操作,默认操作通常是终止进程,取决于被发送的信号


    中断一个中断处理程序到系统中__sighandler_t signal(iny signum, __sighandler_t handler);signal是一个带signum和handler两个参数的函数,准备捕捉或屏蔽的信号由signum给出,接收到指定信号时将要调用的函数由handler给出;
    handler这个函数必须有一个int类型的参数,即接收到的信号代码,当然handler也可以是SIG_IGN(屏蔽该信号)、SIG_DFL(恢复默认行为)这两个特殊值

    超简单例子👇
    SIGINT对应键盘ctrl+c发出的信号

        #include<unistd.h>
      2 #include<sys/stat.h>
      3 #include<sys/wait.h>
      4 #include<sys/types.h>
      5 #include<fcntl.h>
      6 
      7 #include<stdlib.h>
      8 #include<stdio.h>
      9 #include<errno.h>
     10 #include<string.h>
     11 #include<signal.h>
     12 
     13 #define ERR_EXIT(m) \
     14     do \
     15     { \
     16         perror(m); \
     17         exit(EXIT_FAILURE); \
     18     }while(0)
     19 
     20 void handler(int sig);
     21 
     22 int main(int argc, char *argv[])
     23 {
     24     signal(SIGINT, handler);//注册一个ctrlc的信号,如果捕捉到了这个信号,就去执行handler函数
     25     for(;;);
     26     return 0;
     27 }
     28 void handler(int sig)
     29 {
     30     printf("recv a sig = %d\n",sig);
     31 }
    
    

    结果👇
    在这里插入图片描述


    信号在内核中的表示、信号阻塞与未决、信号集操作函数、sigprocmask(用来获取或改变进程中的信号屏蔽字)

    1,执行信号的处理动作称为信号递达,信号产生到递达之间的状态称为信号未决,进程可以选择阻塞某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

    信号集操作函数:
    #include<signal.h>

    int sigemptyset(sigset_t* set);
    int sigfillset(sigset_t *set);
    int sigaddset(sigset_t * set, int_signo);
    int sigdelset(sigset_t* set, int signo);
    int sigismember(const sigset_t *set, int signo);

    获取/改变 进程中的信号屏蔽字:int sigprocmask(int how, const sigset_t *set, sigset_t *oset)第三个参数是改变之前的信号屏蔽字的状态,第一个参数改变方式有三种SIG_BLOCKSIG_UNBLOCKSIG_SETMASK

    sigpending(&set)函数可以获取进程中的未决信号集合保存到set中

    例子👇信号从产生到递达的一个过程
    bset将SIG_INIT进行阻塞,但接收到SIG_QUIT即ctrl/时取消阻塞,始终用函数将未决信号集打印出来
    设置阻塞或者取消阻塞都是要用自定义的一个set来进行sigprocmask来设置的

     16 void printsigset(sigset_t *set)//打印未决信号集内容
     17 {
     18         int i;
     19         for(i=1; i<NSIG; i++)
     20         {
     21                 if(sigismember(set, i))
     22                         putchar('1');
     23                 else
     24                         putchar('0');
     25         }
     26         printf("\n");
     27 }
     28 void handler(int sig);
     29 int main(int argc, char*argv[])
     30 {
     31         sigset_t pset;
     32         sigset_t bset;sigemptyset(&bset);sigaddset(&bset, SIGINT);
     33         if(signal(SIGINT, handler) == SIG_ERR)
     34                 ERR_EXIT("signal error");
     35         if(signal(SIGQUIT, handler) == SIG_ERR)//if ctrl/, unblock
     36                 ERR_EXIT("signal error");
     37         sigprocmask(SIG_BLOCK, &bset, NULL);//use bset
     38         for(;;)
     39         {
     40             sigpending(&pset);
     41             printsigset(&pset);
     42             sleep(1);
     43         }
     44 
     45 
     46         return 0;
     47 }
     48 
     49 void handler(int sig)
     50 {
     51         if(sig == SIGINT)
     52                 printf("recv a sig = %d\n", sig);
     53         else if(sig == SIGQUIT)
     54         {
     55                 sigset_t uset;
     56                 sigemptyset(&uset);
     57                 sigaddset(&uset, SIGINT);
     58                 sigprocmask(SIG_UNBLOCK, &uset, NULL);
     59         }
     60 }
    

    sigaction函数

    int sigaction(int signum, const struct sigaction *act, const struct sigaction *old)
    第二个参数最为重要,其中包含了对指定信号的处理、信号所传递的信息、信号处理函数执行过程中应屏蔽掉哪些信号等

    struct sigaction{
    	void (*sa_handler)(int);//1
    	void (*sa_sigaction)(int, siginfo_t*, void*);//2,只能选一个,一般就选1
    	sigset_t sa_mask;//指定信号屏蔽字
    	int sa_flags;//改变信号的一些行为
    	void(*sa_restorer)(void);//废弃
    }
    

    handler包含在sigaction这个结构体中👇

        void handler(int sig);
     29 int main(int argc, char*argv[])
     30 {
     31         struct sigaction act;
     32         act.sa_handler = handler;
     33         sigemptyset(&act.sa_mask);
     34         act.sa_flags = 0;
     35  
     36         if(sigaction(SIGINT, &act, NULL)<0)
     37                 ERR_EXIT("sigaction error\n");
     38         for(;;)
     39                 pause();
     40         return 0;
     41 }
     42 void handler(int sig)
     43 {
     44     printf("recv a sig = %d\n", sig);
     45 }
    
    

    关于sa_mask,指定信号掩码,在一个信号执行过程中,sa_mask中的信号发生了也不会递达,会被阻塞直到真正该执行的信号函数结束
    在sa_mask中加入SIGQUIT信号,那么在SIGINT捕捉到执行handler的过程中,发生sa_mask中的信号也会被屏蔽

     int main(int argc, char*argv[])
     30 {
     31         struct sigaction act;
     32         act.sa_handler = handler;
     33         sigemptyset(&act.sa_mask);
     34         sigaddset(&act.sa_mask, SIGQUIT);//
     35         act.sa_flags = 0;
     36 
     37         if(sigaction(SIGINT, &act, NULL)<0)
     38                 ERR_EXIT("sigaction error\n");
     39         for(;;)
     40                 pause();
     41         return 0;
     42 }
     43 void handler(int sig)
     44 {
     45     printf("recv a sig = %d\n", sig);
     46     sleep(5);
     47 }
    

    sa_flags的用法有需要再补吧233


    时间与定时器相关的操作

    • 三种不同精度的睡眠
    • setitimer
    • getitimer

    三种不同精度的睡眠
    1,unisigned int sleep(uninsigned int seconds);,返回剩余秒数while(n = sleep(n))
    2,int usleep(useconds_t usec);微秒
    3,int nanosleep(const struct timepec *req, struct timespec *rem);纳秒,第二个参数时剩余睡眠时间
    三种不同精度就对应三种时间结构

    getitimer()/setitimer()功能描述:
    获取或设定间歇计时器的值。系统为进程提供三种类型的计时器,每一类以不同的时间域递减其值。当计时器超时,信号被发送到进程,之后计时器重启动。

    用法:

    #include <sys/time.h>
    
    int getitimer(int which, struct itimerval *value);
    int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue);
    

    参数:
    which:间歇计时器类型,有三种选择

    ITIMER_REAL //数值为0,计时器的值实时递减,发送的信号是SIGALRM。
    ITIMER_VIRTUAL //数值为1,进程执行时递减计时器的值,发送的信号是SIGVTALRM。
    ITIMER_PROF //数值为2,进程和系统执行时都递减计时器的值,发送的信号是SIGPROF。

    展开全文
  • 这里所说中断并不是由设备主动地发出一个中断请求,而是由主机保证不大于某个时间间隔内安排一次传输。中断传输通常用数据量不大,但是对时间要求较严格设备中,例如人机接口设备(HID)中鼠标,键盘,轨迹...

        中断传输是一种保证查询频率的传输。中断端点再端点描述符中要报告它的查询间隔,主机会保证在小于这个时间间隔的范围内安排一次传输。

        这里所说的中断并不是由设备主动地发出一个中断请求,而是由主机保证在不大于某个时间间隔内安排一次传输。中断传输通常用在数据量不大,但是对时间要求较严格的设备中,例如人机接口设备(HID)中的鼠标,键盘,轨迹球等。中断传输也可以用来不断地检测某个状态,当条件满足后再使用批量传输来传送大量的数据。

        除了在对端点查询的策略上不一样之外,中断传输和批量传输的结构上基本是一样的,只是中断传输中没有 PING 和 NYET 两种包。中断传输使用中断事务

    展开全文
  • 2.2 linux中信号分析

    2019-10-05 15:31:16
    信号:  信号UNIX系统响应某些状况而产生事件,进程接收到信号时会采取相应行动。  信号因为某些错误条件而产生,比如内存段冲突、浮点处理器错误或者非法... 当检测出有信号或者中断请求时,都...

    信号:

      信号是UNIX系统响应某些状况而产生的事件,进程在接收到信号时会采取相应的行动。

      信号是因为某些错误条件而产生的,比如内存段冲突、浮点处理器错误或者非法指令等。

      信号是在软件层次上对中断的一种模拟,所以通常把它称为是软中断。

      信号和中断的区别:  

        相似点:

          采用了相同的异步通信方式。

          当检测出有信号或者中断请求时,都暂停正在执行的程序而转去执行相应的处理程序。

          都在处理完毕后返回到原来的断点。

          对信号和中断都可以进行屏蔽。

        区别:

          中断有优先级,而信号没有优先级,所有的信号都是平等的。

          信号处理程序都是在用户态下运行的,而中断处理程序是在核心态下运行。

          中断响应是及时的,而信号响应通常都有较大的延迟。

      常用的信号如下所示:

     

    进程对信号的三种响应如下:

    man 7 signal可以查看信号的默认动作和信号的含义。

      signal用于安装一个信号处理函数,原型如下:

      __sighandler_t  signal(int signum, __sighandler_t  handler)

      signal是一个带signum和handler两个参数的函数,准备捕捉或者屏蔽的信号由参数signum给出,接收到指定信号时将要调用的函数由handler给出。

      handler这个函数必须有一个int型参数(即接收到的信号代码),它本身的类型是void。

      handler也可以是下面两个特殊值:

        SIG_IGN  忽略该信号

        SIG_DFL  恢复默认行为

    示例程序如下:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <signal.h>
     4 
     5 
     6 void handler(int num)
     7 {
     8     printf("recv signal num = %d\n", num);
     9 }
    10 
    11 
    12 int main()
    13 {
    14     pid_t pid;
    15     
    16     signal(SIGCHLD, SIG_IGN);
    17     
    18     pid = fork();
    19     
    20     if(pid == -1)
    21     {
    22         perror("fork error");
    23         exit(0);
    24     }
    25     
    26     if(pid == 0)
    27     {
    28         printf("child ... \n");
    29         exit(0);
    30     }
    31     while(1)
    32     {
    33         pause();    
    34     }
    35     
    36     return 0;
    37 }

    程序中,我们注册信号时,表示父进程忽略子进程的退出信号,因此,执行结果如下:

     

    默认情况下(当我们不使用signal信号时),父进程会在退出的时候给子进程收尸(前提是子进程先死),可用如下程序证明:

     

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <signal.h>
     5 
     6 
     7 void handler(int num)
     8 {
     9     printf("recv signal num = %d\n", num);
    10 }
    11 
    12 
    13 int main()
    14 {
    15     pid_t pid;
    16     
    17     //signal(SIGCHLD, SIG_IGN);
    18     
    19     pid = fork();
    20     
    21     if(pid == -1)
    22     {
    23         perror("fork error");
    24         exit(0);
    25     }
    26     
    27     if(pid == 0)
    28     {
    29         printf("child ... \n");
    30         exit(0);
    31     }
    32     
    33     sleep(5);
    34     //while(1)
    35     //{
    36     //    pause();    
    37     //}
    38     
    39     return 0;
    40 }

     

    我们让父进程睡眠5秒,保证子进程先死,当父进程还在睡眠时,我们在另一个中断执行ps -ef,结果如下:

    可以看到,父进程睡眠期间,子进程已经死了,而且处于僵尸状态,但是当父进程也结束时,子进程的僵尸状态消失了,说明父进程给它收尸了。当把17行的注释打开时,就是告诉内核,子进程死的时候让内核给他收尸,因此,我们看不到子进程处于僵尸状态的现象了(内核收尸太快了)。

      下面我们演示注册一个真正的信号处理函数和恢复默认行为的程序:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <signal.h>
     5 
     6 
     7 void handler(int num)
     8 {
     9     printf("recv signal num = %d\n", num);
    10 }
    11 
    12 
    13 int main()
    14 {
    15     pid_t pid;
    16     char c;
    17     
    18     signal(SIGINT, handler);
    19     
    20     while( (c = getchar()) != 'a')
    21     {
    22         pause();
    23     }
    24     
    25     signal(SIGINT, SIG_DFL);
    26     while(1)
    27     {
    28         pause();    
    29     }
    30     
    31     return 0;
    32 }

    SIGINT代表ctrl+c信号,18行将这个信号的处理函数注册为handler,当程序停在20行时,我们按下ctrl+c,handler会得到执行。 输入a使程序执行到26行,这时候SIGINT信号的行为恢复到了默认行为(25行中的SIG_DFL表示恢复默认行为),我们再按下ctrl+c,程序直接退出了(这就是默认行为)。执行现象如下:

       signal函数执行成功时,返回默认的处理函数,执行失败时返回SIG_ERR。我们将上述程序改成以下方式:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <signal.h>
     5 
     6 void handler(int num)
     7 {
     8     printf("recv signal num = %d\n", num);
     9 }
    10 
    11 int main()
    12 {
    13     pid_t pid;
    14     char c;
    15     
    16     __sighandler_t old = signal(SIGINT, handler);
    17     
    18     if(SIG_ERR == old)
    19     {
    20         perror("signal error");
    21     }
    22     
    23     while( (c = getchar()) != 'a')
    24     {
    25         pause();
    26     }
    27     
    28     signal(SIGINT, old);
    29     printf("huan yuan\n");
    30     while(1)
    31     {
    32         pause();    
    33     }
    34     
    35     return 0;
    36 }

    使用signal注册信号处理函数时,将默认的处理函数返回到old中,并在下面进行了错误处理,28行恢复默认行为,执行结果如下:

     信号的分类:可靠与不可靠信号,实时与非实时信号。实时信号都是可靠信号,非实时信号都是不可靠信号。1-31号都是不可靠信号。不可靠信号就是向应用程序发送了多次信号,应用程序可能只接收到了一次。可靠信号就是向应用程序发几次信号都能保证全接收到。早期unix系统每接收到一个信号就将处理程序恢复到默认行为。现在的linux中的不可靠信号主要指信号可能会丢失。

     

    信号发送函数kill和raise,如下所示:

     

    kill可以向指定进程发送信号,raise向自身发送信号。 kill是示例程序如下:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <signal.h>
     5 
     6 void my_handler(int num)
     7 {
     8     if(SIGINT == num)
     9     {
    10         printf("recv signal SIGINT\n");
    11     }
    12     else if(SIGUSR1 == num)
    13     {
    14         printf("recv signal SIGUSR1\n");
    15     }
    16     else
    17     {
    18         printf("recv signal num = %d\n", num);
    19     }
    20 }
    21 
    22 int main()
    23 {
    24     pid_t pid;
    25     
    26     if(signal(SIGINT, my_handler) == SIG_ERR)
    27     {
    28         perror("signal error");
    29     }
    30     
    31     if(signal(SIGUSR1, my_handler) == SIG_ERR)
    32     {
    33         perror("signal error");
    34     }
    35     
    36     pid = fork();
    37     
    38     if(pid == -1)
    39     {
    40         perror("fork error");
    41         exit(0);
    42     }
    43     
    44     if(pid == 0)
    45     {
    46         pid = getppid();
    47         kill(pid, SIGUSR1);
    48         exit(0);
    49     }
    50     
    51     int nsleep = 5;
    52     
    53     do
    54     {
    55         printf("parent process begin sleep\n");
    56         nsleep = sleep(nsleep);
    57         printf("parent process end sleep\n");
    58     }while(nsleep > 0);
    59     
    60     return 0;
    61 }

    子进程通过kill向父进程发信号,父进程在睡眠,当收到信号时就去执行信号处理函数,执行完之后,发现nsleep不为0,也即没有睡够指定的时间,因此再次进入睡眠,直到睡完为止。执行结果如下:

     

    小知识:

      getpgrp()获取进程组id, kill(pid, SIGINT)向进程组发送SIGINT。父进程注册的信号处理函数也会复制给子进程(在fork之前注册信号)。sleep函数返回值是剩余的秒数。sleep能被信号打断,是可中断睡眠,处理信号函数返回以后就不再睡眠了,继续向下执行。wait使进程进入的睡眠也是可中断睡眠。

    pause函数:

      将进程置为可中断睡眠状态,然后它调用内核函数schedule(),使linux进程调度器找到另一个进程来运行。

      pause使调用者进程挂起,直到一个信号被捕获。

    alarm函数:

      设置一个闹钟,延迟发送信号,告诉linux内核n秒钟以后,给本进程发送SIGALRM信号。示例程序如下:

     

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <signal.h>
     5 
     6 
     7 void my_handler(int num)
     8 {
     9     printf("recv signal num = %d\n", num);
    10 }
    11 
    12 int main()
    13 {
    14     if(signal(SIGALRM, my_handler) == SIG_ERR)
    15     {
    16         perror("signal error");
    17         exit(0);
    18     }
    19     
    20     alarm(3);
    21     
    22     pause();
    23     printf("return from pause  \n");
    24     
    25     return 0;
    26 }

     

    执行结果如下所示:

     

    可重入与不可重入:

     

    示例程序如下:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <unistd.h>
     4 #include <signal.h>
     5 
     6 typedef struct _Teacher
     7 {
     8     int age;
     9     int num;
    10 }Teacher;
    11 
    12 Teacher g_t;
    13 
    14 void printGlobalTeacher()
    15 {
    16     printf("g_t.age = %d\n", g_t.age);
    17     printf("g_t.num = %d\n", g_t.num);
    18 }
    19 
    20 void my_handler(int num)
    21 {
    22     printf("recv signal num = %d\n", num);
    23     printGlobalTeacher();
    24     alarm(1);
    25 }
    26 
    27 int main()
    28 {
    29     Teacher t1,t2;
    30     t1.age = 30;
    31     t1.num = 30;
    32     t2.age = 40;
    33     t2.num = 40;
    34     
    35     if(signal(SIGALRM, my_handler) == SIG_ERR)
    36     {
    37         perror("signal error");
    38         exit(0);
    39     }
    40     
    41     alarm(1);
    42     
    43     while(1)
    44     {
    45         g_t = t1;
    46         g_t = t2;
    47         //printf("return from pause  \n");
    48     }
    49     return 0;
    50 }

    上述程序中在信号处理函数中调用printGlobalTeacher函数,这个函数是不可重入的函数,因为函数内部访问了全局变量g_t, 因此打印结果有可能出错。执行结果如下:

    可以看到出现了age和num不相等的情况,这就是出错了。

    转载于:https://www.cnblogs.com/wanmeishenghuo/p/9369048.html

    展开全文
  • STM32的中断很重要,它不同于if、switch等条件检测语句,它由硬件产生程序中,代码默认按顺序执行,当遇到中断触发时,就会打断当前执行代码并且调到中断服务函数中去执行中断任务。 这种情况汇编语言中...

    FreeRTOS中断配置和临界段

    中断简介

    STM32的中断很重要,它不同于if、switch等条件检测语句,它是由硬件产生的。在程序中,代码默认按顺序执行,当遇到中断触发时,就会打断当前执行代码并且调到中断服务函数中去执行中断任务。
    这种情况在汇编语言中的表现形式一般是:

    1. 保存用到的寄存器(保护现场)
    2. 处理终端
    3. 回复用到的寄存器(恢复现场)
    4. 中断返回

    Cortex-M处理器内核提供了一个用于中断管理的嵌套向量中断控制器(NVIC),最多支持240个中断请求(IRQ)、一个不可屏蔽中断(NMI)、一个滴答定时器中断(Systick)和多个系统异常(硬件错误、总线错误、使用错误、PendSV等等)。Cortex-M 处理器有多个用于管理中断和异常的可编程寄存器, 这些寄存器大多数都在 NVIC 和系统控制块(SCB)中。

    中断优先级

    Cortex-M处理器有三个固定优先级和256个可编程优先级,最多有128个抢占优先级,但具体数目需要由芯片厂商自行设定。绝大多数芯片都会进行剪裁,一般优先级个数是8、16和32个。Cortex-M处理器最多有256个可编程优先级,说明控制优先级的寄存器共有8位,这个优先级是MSB(最高位)对齐的,所以一般都是前3或4位是用来表达优先级的。
    例如:

    Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0
    置位 置位 置位 置位 返回0 返回0 返回0 返回0

    STM32就使用了前四位作为优先级表达位,因此STM32就有16个可编程优先级:0x00(最高优先级)、0x10-0xF0。此外中断可以被屏蔽,这时需要用到3个特殊寄存器:PRIMASK、FAULTMASK、BASEPRI。

    PRIMASK 和 FAULTMASK 寄存器

    在需要进行对时序严格要求的任务时,我们需要暂时屏蔽所有中断来保证任务的精确完成,这时可以使用 PRIMASK 寄存器,PRIMASK 用于禁止除复位 、NMI 和 HardFalut 外的所有异常和中断,可以通过汇编语言CPS指令来控制PRIMASK。

    CPSIE I; //清除 PRIMASK(使能中断) 
    CPSID I; //设置 PRIMASK(禁止中断)
    

    以上代码等同于

    MOVS    R0,   #0
    MSR  PRIMASK, R0 ;//将 0 写入 PRIMASK 以使能中断
    
    MOVS    R0,   #1
    MSR  PRIMASK, R0 ;//将 1 写入 PRIMASK 禁止所有中断
    

    BASEPRI 寄存器

    当需要精准控制低于哪些优先级的中断需要关闭时,这时就可以使用BASEPRI寄存器,这个寄存器可以进行更加准确的屏蔽。例如屏蔽优先级0x80及以下的中断:

    MOV         R0,         #0X60 
    MSR         BASEPRI,      R0
    

    解除屏蔽只需要将0写入BASEPRI寄存器即可

    MOV         R0,          #0
    MSR         BASEPRI,      R0
    

    FreeRTOS中断配置宏

    configPRIO_BITS

    优先级表达位,STM32为前4位,所以此宏为4

    configLIBRARY_LOWEST_INTERRUPT_PRIORITY

    此宏表示最低优先级,STM32最低优先级为0~15,所以此宏为15,当处理器为其他芯片时,需要按手册修改。

    configKERNEL_INTERRUPT_PRIORITY

    该宏定义如下:

    #define configKERNEL_INTERRUPT_PRIORITY 		( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
    

    该宏是设置内核中断优先级,内容是将 configLIBRARY_LOWEST_INTERRUPT_PRIORITY左移四位,因为中断表达需要用高4位来表达,所以需要左移4位。除此外,也可以直接写为:

    #define configKERNEL_INTERRUPT_PRIORITY                 0xF0 
    

    宏 configKERNEL_INTERRUPT_PRIORITY 用来设置 PendSV 和滴答定时器的中断优先级, port.c 中有如下定义:

    #define portNVIC_PENDSV_PRI         ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 16UL )
    #define portNVIC_SYSTICK_PRI        ( ( ( uint32_t ) configKERNEL_INTERRUPT_PRIORITY ) << 24UL )
    

    PendSV和SysTcik的中断优先级设置是操作0xE000-0xED20地址的,这样一次写入的是个32位的数据,SysTick和PendSV的优先级寄存器分别对应这个32位数据的最高8位和次高8位,因此是一个左移16位,一个左移24位。

    configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY

    此宏定义了FreeRTOS可管理的最大优先级,程序生成后默认设置的是5,所以优先级高于5(优先级数小于5)的都不归FreeRTOS管。

    configMAX_SYSCALL_INTERRUPT_PRIORITY

    此宏是由configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY左移4位而来的,原因和宏configKERNEL_INTERRUPT_PRIORITY一样。此宏设置好以后,低于此优先级的中断可以安全的调用 FreeRTOS的API函数,高于此优先级的中断FreeRTOS是不能禁止的,中断服务函数也不能调用FreeRTOS的API函数!

    FreeRTOS开关中断

    FreeRTOS开关中断函数为portENABLE_INTERRUPTS()和portDISABLE_INTERRUPTS(),这两个函数其实是宏定义,在portmacro.h中有定义,如下:

    #define portDISABLE_INTERRUPTS() 					vPortRaiseBASEPRI() 
    #define portENABLE_INTERRUPTS()                     vPortSetBASEPRI(0)
    

    函数vPortBASEPRI()传递了一个0,这就对应了上面说到的,开启中断是将0写入BASEPRI寄存器。

    临界段代码

    临界段代码也称为临界区,是指哪些必须完成运行,且不能中途打断的代码,比如某些外设的初始化过程需要遵守严格的时序,因此初始化过程不能被打断,所以我们在进行外设初始化时可以添加临界段代码。
    FreeRTOS与临界段代码保护有关的函数有4个:taskENTER_CRITICAL()、
    和taskEXIT_CRITICAL()、taskENTER_CRITICAL_FROM_ISR()、taskEXIT_CRITICAL_FROM_ISR(),这四个函数其实是宏定义,在task.h文件中有定义。这四个函数的区别是前两个是任务级的临界段代码保护,后两个是中断级的临界段代码保护。

    任务级临界段代码保护

    taskENTER_CRITICAL()和taskEXIT_CRITICAL()是任务级的临界代码保护,一个是进入临界段,一个是退出临界段,这两个函数是成对使用的,这函数的定义如下:

    #define taskENTER_CRITICAL()            portENTER_CRITICAL() 
    #define taskEXIT_CRITICAL()             portEXIT_CRITICAL()
    

    而portENTER_CRITICAL()和portEXIT_CRITICAL()也是宏定义,在文件 portmacro.h中有定义。
    任务级临界代码保护使用方法如下:

    void CriticalTask_TEST(void const * argv)
    {
    	taskENTER_CRITICAL();                                                       //进入临界区
    	total_num += 0.01f;   
    	printf("total_num 的值为: %.4f\r\n",total_num);
    	taskEXIT_CRITICAL();                                                        //退出临界区
    	vTaskDelay(1000);
    }
    

    当进入临界区时,中断被屏蔽,临界区代码无法被打断,只有当所有的临界段代码都退出以后才会使能中断!
    注:当进入临界区时,优先级低于configMAX_SYSCALL_INTERRUPT_PRIORITY 的中断得不到及响应,所以临界区代码一定要精简。

    中断级临界段代码保护

    函数taskENTER_CRITICAL_FROM_ISR()和taskEXIT_CRITICAL_FROM_ISR()中断级别临界段代码保护,是用在中断服务程序中的,而且这个中断的优先级一定要低于configMAX_SYSCALL_INTERRUPT_PRIORITY。这两个函数在文件task.h中有如下定义:

    #define taskENTER_CRITICAL_FROM_ISR()        portSET_INTERRUPT_MASK_FROM_ISR()
    #define taskEXIT_CRITICAL_FROM_ISR(x)        portCLEAR_INTERRUPT_MASK_FROM_ISR(x)
    

    中断级临界代码保护使用方法如下:

    void TIM3_IRQHandler(void) 
    {
    	if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
    	{
    		status_value=taskENTER_CRITICAL_FROM_ISR();            //进入临界区
    		total_num += 1;
    		printf("float_num 的值为: %d\r\n",total_num);
    		taskEXIT_CRITICAL_FROM_ISR(status_value);              //退出临界区
    	}
    	TIM_ClearITPendingBit(TIM3,TIM_IT_Update);   //清除中断标志位
    }
    

    FreeRTOS中断测试实验

    创建工程

    ### 任务级临界段代码保护
    打开串口1,参数默认。
    在这里插入图片描述
    配置是时钟树
    在这里插入图片描述

    配置定时器3和定时器5
    在这里插入图片描述
    在这里插入图片描述
    配置定时器中断优先级
    在这里插入图片描述
    配置FreeRTOS并创建任务
    在这里插入图片描述
    创建工程
    在这里插入图片描述
    在这里插入图片描述

    查看代码

    打开工程可以在stm32f1xx_hal_cortex.c文件中找到定制器中断优先级配置代码。

    void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
    {
      if(tim_baseHandle->Instance==TIM3)
      {
      /* USER CODE BEGIN TIM3_MspInit 0 */
      /* USER CODE END TIM3_MspInit 0 */
        /* TIM3 clock enable */
        __HAL_RCC_TIM3_CLK_ENABLE();
        /* TIM3 interrupt Init */
        HAL_NVIC_SetPriority(TIM3_IRQn, 4, 0);
        HAL_NVIC_EnableIRQ(TIM3_IRQn);
      /* USER CODE BEGIN TIM3_MspInit 1 */
      /* USER CODE END TIM3_MspInit 1 */
      }
      else if(tim_baseHandle->Instance==TIM5)
      {
      /* USER CODE BEGIN TIM5_MspInit 0 */
      /* USER CODE END TIM5_MspInit 0 */
        /* TIM5 clock enable */
        __HAL_RCC_TIM5_CLK_ENABLE();
        /* TIM5 interrupt Init */
        HAL_NVIC_SetPriority(TIM5_IRQn, 5, 0);
        HAL_NVIC_EnableIRQ(TIM5_IRQn);
      /* USER CODE BEGIN TIM5_MspInit 1 */
      /* USER CODE END TIM5_MspInit 1 */
      }
    }
    

    可以从中看出TIM3优先级为4,大于FreeRTOS所能管理的优先级,TIM优先级为5,恰好在FreeRTOS所能管理的优先级之中。
    先在main.c中将定时器回调函数代码补全:

    定时器回调函数代码补全

    打开main.c文件,补全HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)函数中缺少的定时器3和定时器5的代码。

    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
      /* USER CODE BEGIN Callback 0 */
    	if(htim->Instance == TIM3)
    	{
    		printf("TIM3 输出.......\r\n");
    	}
      /* USER CODE END Callback 0 */
      if (htim->Instance == TIM1) {
        HAL_IncTick();
      }
      /* USER CODE BEGIN Callback 1 */
    	if(htim->Instance == TIM5)
    	{
    		printf("TIM5 输出.......\r\n");
    	}
      /* USER CODE END Callback 1 */
    }
    

    编写测试代码

    打开freertos.c文件,找到void extitask_test(void const * argument)函数,添加自己的代码:

    void extitask_test(void const * argument)
    {
    	static uint32_t total_num = 0;
      /* USER CODE BEGIN extitask_test */
      /* Infinite loop */
      for(;;)
      {
    		total_num += 1;
    		if(total_num == 5)
    		{
    			printf("关闭中断.............\r\n");
    			portDISABLE_INTERRUPTS();
    			HAL_Delay(5000);
    			printf("打开中断.............\r\n");
    			portENABLE_INTERRUPTS();
    		}
      }
      /* USER CODE END extitask_test */
    }
    

    查看运行结果

    打开串口助手可以看到当中断关闭时只有定时器3在工作,而定时器5则失效,当中断重新打开时,定时器3和定时器5又重新一起计时了。
    在这里插入图片描述

    小结

    当我们使用FreeRTOS时,要注意有些外设的驱动代码需要严格连续执行,不能被打断,这时我们可以使用临界段代码来保护现场。但是只需要屏蔽某些优先级的中断时,就可以使用中断屏蔽代码。
    本节工程创建的部分步骤有所省略,详细步骤可参考STM32CubeMX+FreeRTOS学习笔记(一)

    展开全文
  • OSIntCtxSw()则是在一定条件下,在中断结束后,原被中断的程序与更高优先级任务之间切换。 掌握五种任务通信与同步方法。对应事件控制块与信号量集标志组结构。并比较这五种任务通信与同步方法...
  • 微程序控制器每执行一条机器指令之后,执行下一条机器指令之前,先转到地址0F处,在条件位P1=1时判断是否有中断请求INTQ。如果没有,则继续正常机器指令执行。若检测中断请求,首先发出关中断信号INTC,保存断点...
  • 执行低速系统系统调用时,期间因捕捉到信号而导致中断,但其返回条件无法实现,导致出错。如: 请求读某些文件,但文件中数据不存在了,导致请求无法实现。 处理办法设置原系统调用自启动,以使系统不...
  • 8086将类型码乘4后得到中断向量表的入口地址,从此地址开始读取4字节的中断处理程序的入口地址,8086从此地址开始执行程序,完成了INTR中断请求的响应过程。 18.什么总线请求?8086最小工作模式下,有关总线...
  • 微机课程设计

    2004-10-15 18:43:28
    中断方式:外设准备好后向CPU发中断请求,CPU满足响应中断的条件下发中断响应信号,暂停当前程序,转去执行中断服务程序,完成数据传送,CPU从中断服务程序返回后,继续执行被中断程序。 特点:CPU与外设大...
  • 你必须知道495个C语言问题

    千次下载 热门讨论 2015-05-08 11:09:25
    5.5 使用非零位模式作为空指针内部表示机器上,NULL如何定义? 5.6 如果NULL定义成#defineNULL((char*)0),不就可以向函数传入不加转换NULL了吗? 5.7 我编译器提供头文件中定义NULL为0L。为...
  • 《你必须知道495个C语言问题》

    热门讨论 2010-03-20 16:41:18
    5.5 使用非零位模式作为空指针内部表示机器上,NULL 如何定义? 56 5.6 如果NULL定义成#define NULL((char *)0) ,不就可以向函数传入不加转换NULL 了吗? 57 5.7 我编译器提供头文件中定义...
  • 5.5 使用非零位模式作为空指针内部表示机器上,NULL 如何定义? 56 5.6 如果NULL定义成#define NULL((char *)0) ,不就可以向函数传入不加转换NULL 了吗? 57 5.7 我编译器提供头文件中定义...
  • 40 为使工作一般完全嵌套方式8259A中断控制器能接受下一个中断请求中断服务程序结束处应( )。 A、直接执行IRET指令 B、先执行POP指令 C、发送普通EOI指令 D、不用任何处理,8259会自动进行有关处理 ...
  • IIS6.0 IIS,互联网信息服务

    热门讨论 2010-08-23 21:20:55
    最初Windows NT版本可选包,随后内置Windows 2000、Windows XP Professional和Windows Server 2003一起发行,但普遍使用Windows XP Home版本上并没有IIS。 添加和运行准备  一、IIS添加 请进入“控制...
  • (2)产生死锁四个必要条件是资源互斥使用、保持和等待、非剥夺性、循环等待。 (3)解决死锁方法 一般有死锁预防,即破坏产生死锁四个必要条件中一个或多个,使系统绝不会进入死锁状态;死锁...
  • PCI.EXPRESS系统体系结构标准教材.pdf

    热门讨论 2013-02-17 16:21:16
    9.2.3 生成msi中断请求的基础 9.2.4 中断处理程序处理时的存储器同步 9.2.5 中断延迟 9.2.6 一些规则、建议等等 9.3 传统的pci中断发送机制 9.3.1 背景知识——pci中断信令 9.3.2 虚拟intx信令 9.4 设备可以同时支持...
  • (2) 中断方式,当采集温度比给定范围过底或过高时能提出随机中断请求,主机应能判别哪一个采集器请求,温度过低或过高。 请拟定该接口中有哪些主要部件(不要求画出完整连线图),并概略说明两 种方式...
  • (2) 中断方式,当采集温度比给定范围过底或过高时能提出随机中断请求,主机应能判别哪一个采集器请求,温度过低或过高。 请拟定该接口中有哪些主要部件(不要求画出完整连线图),并概略说明两 种方式下...
  • RFC中文文档-txt

    2009-09-11 14:56:56
    RFC516 丢失消息的检测 RFC591 NVT ASCII UCSB和在线系统之间的实验输入映象 RFC621 “注意圣诞节的时候要把长袜挂烟囱下面” RFC628 更深的数据语言的设计观念 RFC634 最近的网络图 RFC637 SU-DSL网络地址的...
  • 中文版RFC,共456

    2009-04-19 22:56:29
    RFC516 丢失消息的检测 RFC591 NVT ASCII UCSB和在线系统之间的实验输入映象 RFC621 “注意圣诞节的时候要把长袜挂烟囱下面” RFC628 更深的数据语言的设计观念 RFC634 最近的网络图 RFC637 SU-DSL网络地址的...
  • RFC516 丢失消息的检测 RFC591 NVT ASCII UCSB和在线系统之间的实验输入映象 RFC621 “注意圣诞节的时候要把长袜挂烟囱下面” RFC628 更深的数据语言的设计观念 RFC634 最近的网络图 RFC637 SU-DSL网络...
  • 普遍使用 Android APP 技术架构,往往是在一个界面中存在大量业务逻辑,而业务逻辑中充斥着各种网络请求、数据操作等行为,整个项目中也没有模块概念,只有简单以业务逻辑划分文件夹,并且业务之间也是...
  • TCP_IP详解卷1

    热门讨论 2010-12-29 10:53:54
    描述了属于每一层各个协议以及它们如何不同操作系统中运行。作者用Lawrence Berkeley实验室tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输不同分组。对tcpdump输出研究可以帮助理解不同协议如何工作...
  • TCPIP详解卷[1].part12

    2010-12-29 11:12:15
    描述了属于每一层各个协议以及它们如何不同操作系统中运行。作者用Lawrence Berkeley实验室tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输不同分组。对tcpdump输出研究可以帮助理解不同协议如何工作...
  • TCPIP详解卷[1].part11

    2010-12-29 11:09:56
    描述了属于每一层各个协议以及它们如何不同操作系统中运行。作者用Lawrence Berkeley实验室tcpdump程序来捕获不同操作系统和TCP/IP实现之间传输不同分组。对tcpdump输出研究可以帮助理解不同协议如何工作...

空空如也

空空如也

1 2 3
收藏数 50
精华内容 20
关键字:

中断请求的检测条件是在