Chinaunix首页 | 论坛 | 博客
  • 博客访问: 798983
  • 博文数量: 281
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 2770
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-02 19:45
个人简介

邮箱:zhuimengcanyang@163.com 痴爱嵌入式技术的蜗牛

文章分类
文章存档

2020年(1)

2018年(1)

2017年(56)

2016年(72)

2015年(151)

分类: 嵌入式

2015-09-23 14:49:21

在中断服务例程中使用队列

xQueueSendToFrontFromISR(),xQueueSendToBackFromISR()与xQueueReceiveFromISR()分别是xQueueSendToFront(),xQueueSendToBack()与xQueueReceive()的中断安全版本,专门用于中断服务例程中。
信号量用于事件通信。而队列不仅可以用于事件通信,还可以用来传递数据。

xQueueSendToFrontFromISR()与xQueueSendToBackFromISR() API 函数
函数原型:
portBASE_TYPE xQueueSendToFrontFromISR( xQueueHandle xQueue, void *pvItemToQueue, portBASE_TYPE *pxHigherPriorityTaskWoken );
portBASE_TYPE xQueueSendToBackFromISR( xQueueHandle xQueue, void *pvItemToQueue, portBASE_TYPE *pxHigherPriorityTaskWoken );

xQueue
目标队列的句柄。这个句柄即是调用xQueueCreate()创建该队列时的返回值。
pvItemToQueue
发送数据的指针。其指向将要复制到目标队列中的数据单元。
由于在创建队列时设置了队列中数据单元的长度,所以会从该指针指向的空间复制对应长度的数据到队列的存储区域。
pxHigherPriorityTaskWoken
对某个队列而言,可能有不止一个任务处于阻塞态在等待其数据有效。调用xQueueSendToFrontFromISR()或xQueueSendToBackFromISR()会使得队列数据变为有效,所以会让其中一个等待任务切出阻塞态。如果调用这两个API 函数使得一个任务解除阻塞,并且这个任务的优先级高于当前任务(也就是被中断的任务),那么API 会在函数内部将*pxHigherPriorityTaskWoken 设为pdTRUE。

如果这两个API 函数将此值设为pdTRUE,则在中断退出前应当进行一次上下文切换。这样才能保证中断直接返回到就绪态任务中优先级最高的任务中。
返回值 有两个可能的返回值:
1. pdPASS
返回pdPASS 只会有一种情况,那就是数据被成功发送到队列中。
2. errQUEUE_FULL
如果由于队列已满而无法将数据写入,则将返回errQUEUE_FULL。

有效使用队列
FreeRTOS 的大多数demo 应用程序中都包含一个简单的UART 驱动,其通过队列将字符传递到发送中断例程,也使用队列将字符从接收中断例程中传递出来。发送或接收的每个字符都通过队列单独传递。这些UART 驱动的这种实现方式只是单纯了为了演示如何在中断中使用队列。实际上利用队列传递单个字符是极其低效的,特别是在波特率较高的时后,所以这种方式并不建议用在产品代码中。实际应用中可以采用下述更有效的方式:
(1) 将接收到的字符先缓存到内存中。当接收到一个传输完成消息,或是检测到传输中断后,使用信号量让某个任务解除阻塞,这个任务将对字符缓存进行处理。
(2) 在中断服务中直接解析接收到的字符,然后通过队列将解析后经解码得到的命令发送到处理任务(与图23 中描述的方式类似)。这种技术仅适用于数据流能够快速解析的场合,这样整个数据解析工作才可以放在中断服务中完成。

例子-14:
本例演示在同一个中断服务中使用xQueueSendToBackFromISR() 和xQueueReceiveFromISR()。和之前一样,采用软件中断以方便实现。
硬件平台: stm32F103平台,包含一个按键,一个LED灯。
软件要求:
按下按键,发送5个数组到队列中;在一个timer定时器(100ms)的中断中,读取这个队列中的值,以这个值为字符串数组索引,调用xQueueSendFromISR()发送字符串到另一个队列中。另一个任务将接收从定时器中断服务例程发出的字符串指针。此任务在读队列时被阻塞,直到队列中有消息到来,并将接收到的字符串打印输出。
代码实现:

点击(此处)折叠或打开

  1. #include "led.h"
  2. #include "key.h"
  3. #include "exti.h"
  4. #include "delay.h"
  5. #include "sys.h"
  6. #include "usart.h"
  7. #include "timer.h"

  8. // FreeRTOS head file, add here.
  9. #include "FreeRTOS.h"
  10. #include "task.h"
  11. #include "queue.h"
  12. #include "list.h"
  13. #include "portable.h"
  14. #include "FreeRTOSConfig.h"
  15. #include "semphr.h"


  16. xQueueHandle xIntegerQueue;
  17. xQueueHandle xStringQueue;

  18. static const char *pcString[] ={
  19.     
  20.     "String 0 \r\n",
  21.     "String 1 \r\n",
  22.     "String 2 \r\n",
  23.     "String 3 \r\n",
  24. };


  25. void board_Init(void)
  26. {
  27.     LED_Init();    
  28.     KEY_Init();
  29.     //EXTIX_Init();
  30.     uart_init(115200);
  31.     TIM3_Int_Init(10,7200);// 10kHZ, 1ms interrupt.
  32.     
  33.     // interrupt initialize
  34.     NVIC_Configuration();//
  35. }


  36. void keyScan_Task(void *pvParameters)
  37. {
  38.     char key = 0x00;
  39.     char i =0, sendInt = 0;
  40.     
  41.     while(1)
  42.     {
  43.         // add your key scan code here.
  44.         keyScan();
  45.         if((key = keyScan_readBuff()) != 0)
  46.         {
  47.             switch(key)
  48.             {
  49.                 case ( KEY_CODE + SHORT_KEY):
  50.                     
  51.                     printf("send Int: ");
  52.                     for(i=0; i<5; i++)
  53.                     {
  54.                         printf("%d ", sendInt);
  55.                         xQueueSendToBack(xIntegerQueue, &sendInt, 0); // send integer: 1, 2, 3, 4, 5;
  56.                         
  57.                         sendInt ++;
  58.                     }
  59.                     printf("\r\n");
  60.                 
  61.                 break;
  62.                 
  63.                 case ( KEY_CODE+FIRSTLONG_KEY_CODE):
  64.                     printf("long first pressed \r\n");
  65.                 break;
  66.                 
  67.                 case ( KEY_CODE+AFTERLONG_KEY_CODE):
  68.                     printf("long after pressed \r\n");
  69.                 break;
  70.             }
  71.         }
  72.         
  73.         vTaskDelay(10/portTICK_RATE_MS);
  74.     }
  75. }

  76. void LED_task(void *pvParameters)
  77. {
  78.     char state = 0;
  79.     
  80.     while(1)
  81.     {
  82.         ((state = !state) == 1) ? GREEN_ON() : GREEN_OFF();
  83.         vTaskDelay(1000 / portTICK_RATE_MS);
  84.     }
  85. }


  86. void PrintTask(void *pvParameters)
  87. {
  88.     char *pcStr;
  89.     
  90.     while(1)
  91.     {
  92.         // 注意:这里传送的是指针 pcStr
  93.         xQueueReceive(xStringQueue, &pcStr, portMAX_DELAY);
  94.         printf("%s", pcStr);
  95.     }
  96. }


  97. int main(void)
  98. {
  99.     // board initialize.
  100.     board_Init();
  101.     printf("board initialize finish. \r\n");
  102.     
  103.     // create two queues
  104.     xIntegerQueue = xQueueCreate(10, sizeof(unsigned long));
  105.     xStringQueue = xQueueCreate(10, sizeof(char *)); // 这里数据单元是“指针”,指向字符串。

  106.     // ajust crate queue whether successfully
  107.     if((xIntegerQueue != NULL) && (xStringQueue != NULL))
  108.     {
  109.         xTaskCreate(keyScan_Task, "keyScanTask", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
  110.         xTaskCreate(LED_task, "LED_task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
  111.         xTaskCreate(PrintTask, "print_task", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
  112.         
  113.         // start scheduler now
  114.         vTaskStartScheduler();
  115.     }
  116.     else
  117.     {
  118.         // semaphore create unsuccessful here. add your code.
  119.         printf("semaphore create failed \r\n");
  120.     }

  121.     return 0;
  122. }


  123. void TIM3_IRQHandler(void) //TIM3 interrupt, 1ms
  124. {
  125.     static char state = 0;
  126.     static int cnt = 0;
  127.     unsigned long value;
  128.     
  129.     BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  130.  
  131.     if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
  132.     {
  133.         TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // clear timer interrupt flag
  134.         
  135.         if(++cnt >= 100) // per 100ms interrupt here.
  136.         {
  137.             cnt = 0;
  138.             
  139.             // read string queue, till read empty.
  140.             while(xQueueReceiveFromISR(xIntegerQueue, &value, &xHigherPriorityTaskWoken) != errQUEUE_EMPTY)
  141.             {
  142.                 printf("read value: %ld \r\n", value);
  143.                 value &= 0x03;
  144.                 
  145.                 // send string queue.
  146.                 xQueueSendToBackFromISR(xStringQueue, &pcString[value], &xHigherPriorityTaskWoken);
  147.             }
  148.                   
  149.             ((state = !state) == 1) ? RED_ON() : RED_OFF();
  150.         }
  151.     }
  152. }

打印结果:


执行流程:
如果没有按键按下,当定时器中断,执行中断程序,读取队列时为空,导致字符串队列也为空,导致最高优先级的打印字符串任务进入阻塞态。
如果按键按下,则写入5个数值,使得定时器中断程序读到队列的数据,发送字符串到字符串队列,唤醒打印任务,打印队列的字符串。


中断嵌套
最新的FreeRTOS 移植中允许中断嵌套。中断嵌套需要在FreeRTOSConfig.h 中定义下表详细列出的一个或两个常量。
常量
描述
configKERNEL_INTERRUPT_PRIORITY
设置系统心跳时钟的中断优先级。
如果在移植中没有使用常量configMAX_SYSCALL_INTERRUPT_PRIORITY,那么需要调用中断安全版本FreeRTOS API
的中断都必须运行在此优先级上。
configMAX_SYSCALL_INTERRUPT_PRIORITY
设置中断安全版本FreeRTOS API 可以运行的最高中断优先级。

建立一个全面的中断嵌套模型需要设置configMAX_SYSCALL_INTERRUPT_PRIRITY为比configKERNEL_INTERRUPT_PRIORITY更高的优先级。这种模型在下图中有所展示。下图所示的情形假定常量configMAX_SYSCALL_INTERRUPT_PRIRITY 设置为3,configKERNEL_INTERRUPT_PRIORITY 设置为1。同时也假定这种情形基于一个具有七个不同中断优先及的微控制器。这里的七个优先级仅仅是本例的一种假定,并非对应于任何一种特定的微控制器架构。

在任务优先级和中断优先级之间常常会产生一些混淆。下图所示的中断优先级是由微控制器架构体系所定义的。中断优先级是硬件控制的优先级,中断服务例程的执行会与之关联。任务并非运行在中断服务中,所以赋予任务的软件优先级与赋予中断源的硬件优先级之间没有任何关系。



上图说明:

(1)处于中断优先级1 到3(含)的中断会被内核或处于临界区的应用程序阻塞执行,但是它们可以调用中断安全版本的FreeRTOS API 函数

(2)处于中断优先级4 及以上的中断不受临界区影响,所以其不会被内核的任何行为阻塞,可以立即得到执行——这是由微控制器本身对中断优先级的限定所决定的。通常需要严格时间精度的功能( 如电机控制) 会使用高于configMAX_SYSCALL_INTERRUPT_PRIRITY 的优先级,以保证调度器不会对其中断响应时间造成抖动。
(3)不需要调用任何FreeRTOS API 函数的中断,可以自由地使用任意优先级。




对ARM Cortex M3 用户的一点提示:
Cortex M3 使用低优先级号数值表示逻辑上的高优先级中断。这显得不是那么直观,所以很容易被忘记。如果你想对某个中断赋予低优先级,则必须使用一个高优先级
号数值。千万不要给它指定优先级号0(或是其它低优先级号数值),因为这将会使得这个中断在系统中拥有最高优先级— — 如果这个优先级高于
configMAX_SYSCALL_INTERRUPT_PRIRITY,将很可能导致系统崩溃。

Cortex M3 内核的最低优先级为255,但是不同的Cortex M3 处理器厂商实现的优先级位数不尽相同,而各自的配套库函数也使用了不同的方式来支持中断优先级。比如STM32,ST 的驱动库中将最低优先级指定为15,而最高优先级指定为0。




关于中断这块看后一篇介绍更详细:
学习FreeRTOS之中断管理_4
阅读(3218) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~