Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何在微控制器中实现多任务?_C_Concurrency_Parallel Processing_Embedded - Fatal编程技术网

C 如何在微控制器中实现多任务?

C 如何在微控制器中实现多任务?,c,concurrency,parallel-processing,embedded,C,Concurrency,Parallel Processing,Embedded,我使用嵌入式(C)8051微控制器为手表编写了一个程序。共有6个7段显示器,如下所示: _______________________ | | | | two 7-segments for showing HOURS | HR | MIN | SEC | two 7-segments for showing MINUTES and |______._______._____

我使用嵌入式(C)8051微控制器为手表编写了一个程序。共有6个7段显示器,如下所示:

         _______________________
        |      |       |        |   two 7-segments for showing HOURS
        | HR   | MIN   |   SEC  |   two 7-segments for showing MINUTES and
        |______._______.________|   two 7-segments for showing SECONDS
          7-segment LED display
为了更新小时、分钟和秒,我们对循环使用了3个
。这意味着先更新秒数,然后更新分钟数,然后更新小时数。然后我问我的教授为什么我们不能同时更新(我的意思是一个小时后的小时增量,而不等待分钟更新)。他告诉我,由于指令的顺序执行,我们不能进行并行处理

问题:

数字生日卡,可在LED同时闪烁的同时连续播放音乐。数字闹钟在特定时间会发出嘟嘟声。在它发出声音的同时,时间将继续更新。所以声音和时间增量都是并行运行的。他们是如何通过顺序执行实现这些结果的

如何在微控制器中同时运行多个任务(调度)?

首先,这种顺序执行有什么意义。只有一个内核,一个程序空间,一个计数器。MPU一次执行一条指令,然后依次移动到另一条指令。在这个系统中,没有内在的机制使它停止做一件事而开始做另一件事——这都是一个程序,它完全掌握在程序员的手中——序列是什么,它将做什么;只要MPU在运行,它将不间断地持续,一次一条指令,并且不会发生其他任何事情,除非程序员让它先发生

现在,多任务处理:

通常,操作系统提供多任务处理,具有相当复杂的调度算法

通常,微控制器在没有操作系统的情况下运行

那么,如何在微控制器中实现多任务处理呢

简单的答案是“你没有”。但通常情况下,简单的答案很少涵盖超过5%的案例

你将很难写出一个真正的、先发制人的多任务处理。大多数微控制器都没有这方面的设备,而Intel CPU使用两条特定指令所做的事情需要您编写数英里的代码。最好忘记微控制器的经典多任务处理,除非你真的没有更好的时间

现在,有两种常用的方法被频繁使用,而麻烦要小得多

打断 大多数微控制器有不同的中断源,通常包括定时器。因此,主循环连续运行一个任务,当计时器计数为零时,发出中断。主循环停止,执行跳转到称为“中断向量”的地址。在那里,启动不同的程序,执行不同的一次性任务。一旦完成(如果需要,可能重置计时器),您将从中断返回,主循环将恢复

微控制器通常有几个定时器,您可以为每个定时器分配一个任务,更不用说其他任务,外部中断(例如,按下键盘输入键,或通过RS232到达数据)

虽然这种方法非常有限,但在大多数情况下确实足够了;特别是您的:设置计时器以循环1s,在中断时计算新的小时,更改显示,然后离开中断。在主循环中,等待日期到达生日,以及何时开始播放音乐和闪烁LED

协同多任务 早期就是这样做的。您需要将“任务”编写为子例程,每个子例程中都有一个有限状态机(或循环的单次传递),而“操作系统”是一个简单的循环,按顺序跳转到连续任务

每次跳转后,MPU开始执行给定的任务,并将继续执行,直到任务在首次保存其状态后返回控制权,以便在再次启动时恢复控制权。任务作业的每次传递都应该非常短。在有限状态引擎中,任何延迟循环都必须替换为等待状态(如果条件不满足,则返回。如果满足,则更改状态)。所有更长的循环必须展开为不同的状态(“状态:复制数据块,复制字节N,增加N,N=结束?是:下一个状态,否:返回控制”)

这样写比较困难,但解决方案更可靠。在您的情况下,您可能有四项任务:

  • 显示更新
  • 播放声音
  • 闪烁发光二极管
如果没有新的秒到达,时钟将返回控件。如果新的秒到达,时钟将重新计算秒数、分钟数、小时数、日期,然后返回

显示器更新显示值。如果您在8段显示器上的数字上进行多路传输,则每次传输将更新一个数字,下一次传输-下一次传输等

播放声音将在非生日时等待(产生)。如果是生日,则从内存中选取样本值,将其输出到扬声器,产生。如果您在预期输出下一个声音之前被调用,则可以选择产生

闪烁-好,输出正确的状态到LED,产量

非常短的循环——比如说,5行10次迭代——仍然是允许的,但是任何更长的循环都应该转换为有限状态引擎的状态,这个过程就是这样的

现在,如果你感觉很坚强,你可以试着去

先发制人的多任务处理。 每个任务都是一个过程,通常会无限执行,只做自己的事情。正常编写,尽量不占用其他过程的内存,而是使用资源,就好像世界上没有其他任何东西需要它们一样

您的操作系统任务是从计时器中断启动的

在中断启动时,OS任务必须保存最后一个任务寄存器的所有当前易失性状态、中断返回地址(任务应从中恢复)、当前堆栈指针,并将其保存在该任务的记录中

然后,使用调度程序算法,它选择 FILE: pt.h #ifndef __PT_H__ #define __PT_H__ #include "lc.h" // NOTE: the enums are mine to compress space; originally all were #defines enum PT_STATUS_ENUM { PT_WAITING, PT_YIELDED, PT_EXITED, PT_ENDED }; struct pt { lc_t lc; } // protothread control structure (pt_thread) #define PT_INIT(pt) LC_INIT((pt)->lc) // initializes pt_thread prior to use // you can use this to declare pt_thread functions #define PT_THREAD(name_args) char name_args // NOTE: looking at this, I think I might define my own macro as follows, so as not // to have to redclare the struct pt *pt every time. //#define PT_DECLARE(name, args) char name(struct pt *pt, args) // start/end pt_thread (inside implementation fn); must always be paired #define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc) #define PT_END(pt) LC_END((pt)->lc);PT_YIELD_FLAG = 0;PT_INIT(pt);return PT_ENDED;} // {block, yield} 'pt' {until,while} 'c' is true #define PT_WAIT_UNTIL(pt,c) do { \ LC_SET((pt)->lc); if(!(c)) {return PT_WAITING;} \ } while(0) #define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond)) #define PT_YIELD_UNTIL(pt, cond) \ do { PT_YIELD_FLAG = 0; LC_SET((pt)->lc); \ if((PT_YIELD_FLAG == 0) || !(cond)) { return PT_YIELDED; } } while(0) // NOTE: no corresponding "YIELD_WHILE" exists; oversight? [shelleybutterfly] //#define PT_YIELD_WHILE(pt,cond) PT_YIELD_UNTIL((pt), !(cond)) // block pt_thread 'pt', waiting for child 'thread' to complete #define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread)) // spawn pt_thread 'ch' as child of 'pt', waiting until 'thr' exits #define PT_SPAWN(pt,ch,thr) do { \ PT_INIT((child)); PT_WAIT_THREAD((pt),(thread)); } while(0) // block and cause pt_thread to restart its execution at its PT_BEGIN() #define PT_RESTART(pt) do { PT_INIT(pt); return PT_WAITING; } while(0) // exit the pt_thread; if a child, then parent will unblock and run #define PT_EXIT(pt) do { PT_INIT(pt); return PT_EXITED; } while(0) // schedule pt_thread: fn ret != 0 if pt is running, or 0 if exited #define PT_SCHEDULE(f) ((f) lc); \ if(PT_YIELD_FLAG == 0) { return PT_YIELDED; } } while(0) FILE: lc.h #ifndef __LC_H__ #define __LC_H__ #ifdef LC_INCLUDE #include LC_INCLUDE #else #include "lc-switch.h" #endif /* LC_INCLUDE */ #endif /* __LC_H__ */ FILE: lc-switch.h // WARNING: implementation using switch() won't work with an LC_SET() inside a switch()! #ifndef __LC_SWITCH_H__ #define __LC_SWITCH_H__ typedef unsigned short lc_t; #define LC_INIT(s) s = 0; #define LC_RESUME(s) switch(s) { case 0: #define LC_SET(s) s = __LINE__; case __LINE__: #define LC_END(s) } #endif /* __LC_SWITCH_H__ */ FILE: lc-addrlabels.h #ifndef __LC_ADDRLABELS_H__ #define __LC_ADDRLABELS_H__ typedef void * lc_t; #define LC_INIT(s) s = NULL #define LC_RESUME(s) do { if(s != NULL) { goto *s; } } while(0) #define LC_CONCAT2(s1, s2) s1##s2 #define LC_CONCAT(s1, s2) LC_CONCAT2(s1, s2) #define LC_END(s) #define LC_SET(s) \ do {LC_CONCAT(LC_LABEL, __LINE__):(s)=&&LC_CONCAT(LC_LABEL,__LINE__);} while(0) #endif /* __LC_ADDRLABELS_H__ */ FILE: pt-sem.h #ifndef __PT_SEM_H__ #define __PT_SEM_H__ #include "pt.h" struct pt_sem { unsigned int count; }; // macros to initiaize, await, and signal a pt_sem semaphore #define PT_SEM_INIT(s, c) (s)->count = c #define PT_SEM_WAIT(pt, s) do \ { PT_WAIT_UNTIL(pt, (s)->count > 0); -(s)->count; } while(0) #define PT_SEM_SIGNAL(pt, s) ++(s)->count #endif /* __PT_SEM_H__ */ LICENSE AGREEMENT (where applicable)
This post contains code based on (or taken from) 'The Protothreads Library' (referred to herein and henceforth as "PTLIB"; including v1.4 and earlier revisions) relying extensively on the source code as well as the documentation for PTLIB. PTLIB original source code and documentation was received from, and freely available for download at the author's PTLIB site 'http://dunkels.com/adam/pt/', available through a link on the downloads page at 'http://dunkels.com/adam/pt/download.html' or directly via 'http://dunkels.com/adam/download/pt-1.4.tar.gz'. This post consists of original text, for which I hereby give to you (with love!) under a full waiver of whatever copyright interest I may have, under the following terms: "copyheart ♥ 2014, shelleybutterfly, share with love!"; or, if you prefer, a fully non-restrictive, attribution-only license appropriate to the material (such as Apache 2.0 for software; or CC-BY license for text) so that you may use it as you see fit, so that it may best suit your needs. This post also contains source code, almost entirely created from the original source by removing explanatory material, reformatting, and paraphrasing the in-line documentation/comments, as well as a few modifications/additions by me (shelleybutterfly on the stackexchange network). Anything derivative of PTLIB for which I may have, legally, gained any copyright or other interest, I hereby cede all such interest back to and all copyright interest in the original work to the original copyright holder, as specified in the license from PTLIB, which follows this agreement. In any jurisdiction where it is not possible for the terms above to apply to you for whatever reason, then, for whatever interest I have in the material, I hereby offer it to you under any non-restrictive, attribution-only, license of your choosing; or, should this also not be possible, then I give permission to stack exchange inc to provide it to you under whatever terms the y determine to be acceptable in your jurisdiction. All source code from PTLIB, and that which is derivative of PTLIB, that is not covered under other terms detailed above hereby provided to 'stack exchange inc' and to you under the following agreement: LICENSE AGREEMENT for "The Protothreads Library"
Copyright (c) 2004-2005, Swedish Institute of Computer Science. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the Institute nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS `AS IS' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Author: Adam Dunkels