好的,我看到其他答案已经涵盖了很多内容;希望我不会把这变成比我意图更大的事情。(TL;DR: 女孩来拯救! :D)但是,我有一个(我认为是)非常好的解决方案可以提供;所以我希望您能好好利用它。我只有一点8051
[☆]的经验;尽管我曾在另一款微控制器上工作了约3个月(加上约3个全职),取得了一定的成功。在这个过程中,我几乎做了所有小东西要提供的东西:串行通信、SPI、PWM信号、伺服控制、DIO、热电偶等等。当我在工作时,我走运地遇到了一个优秀(依我之见)的解决方案,用于(协作式)“线程”调度,这与从PIC中断中进行的一些额外的实时处理混合得很好。当然,还有其他设备的中断处理程序。
pt_thread:由Adam Dunkels(与Oliver Schmidt)发明(v1.0于2005年2月发布),他的网站是它们的一个很好的介绍,并包括从2006年10月开始的v1.4下载;我很高兴再次去看,因为我发现;但是有一个来自2009年1月的项目说明,Larry Ruane使用了事件驱动技术“[进行]完整的重新实现[使用GCC;并且具有]非常好的语法”,
并在sourceforge上提供。不幸的是,自2009年左右以来,似乎没有对任何一个版本进行更新;但是2006年的版本为我服务得非常好。最后一条新闻(来自2009年12月)指出,“索尼克:失控”在其手册中指出使用了protothreads!
我认为pt_threads的一件令人惊叹的事情是它们非常简单;而且,无论新版本(Ruane)的好处如何,它肯定更复杂。虽然可能值得一看,但我要在这里坚持Dunkels的原始实现。他的原始
pt_threads
“库”包括:五个头文件。而且,真的,这似乎是一个夸张,因为一旦我缩小了一些宏和其他东西,删除了doxygen部分、示例,并将注释减少到我仍然觉得给出了解释的最少量,它只有大约115行(以下包括)。源tarball中包含了示例,他的网站上还提供了非常好的.pdf文档(或.html)。但是,让我通过一个快速的示例来阐明一些概念。(不是宏本身,我花了一段时间才理解它们,而且它们并不是使用功能所必需的。:D)
很抱歉,今晚的时间已经用尽了;但是我会尽量在明天某个时候回来写一个小例子;无论如何,他的网站上有大量的资源链接;这是一个相当简单的过程,对我来说棘手的部分(我想对于任何合作多线程来说都是如此吧?Win 3.1呢?:D)是确保我已经正确地进行了代码循环计数,以便在产生 pt_thread 前不会超出我处理下一件事情所需的时间。
我希望这能给你一个开始;如果你尝试它,请让我知道它的进展情况!
文件: 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)
文件: 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__ */
文件: lc-switch.h
// 警告:使用 switch() 实现的代码无法在 switch() 中使用 LC_SET()!
#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__ */
文件: 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__ */
文件: pt-sem.h
#ifndef __PT_SEM_H__
#define __PT_SEM_H__
#include "pt.h"
struct pt_sem { unsigned int count; };
// 宏用于初始化、等待和信号化 pt_sem 信号量
#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__ */
[☆] *花费了一周的时间学习微控制器[†],并在评估期间使用它进行了一周的实验,以查看它是否能够满足我们对小型线可替换远程 I/O 单元的需求。 (长话短说:不行)
[†] 《The 8051 Microcontroller, Third Edition》被推荐为8051编程的“圣经”,我不知道它是否确实如此,但我确实能够使用它理解这些东西。[‡]
[‡] 现在再看一遍,我也没有发现什么不喜欢的地方:)嗯,我的意思是……我希望我没有买两本;但它们真的很便宜!
LICENSE AGREEMENT (where applicable)
本文包含基于(或取自)“Protothreads Library”(以下简称“PTLIB”,包括v1.4和早期版本)的代码,大量依赖PTLIB的源代码和文档。
PTLIB的原始源代码和文档来自作者的PTLIB网站'http://dunkels.com/adam/pt/',可通过'http://dunkels.com/adam/pt/download.html'上的下载页面链接或直接通过'http://dunkels.com/adam/download/pt-1.4.tar.gz'下载。
本文由原创文本组成,我在此免除我可能拥有的任何版权利益,并按照以下条款授权您使用:“copyheart ♥ 2014,shelleybutterfly,分享爱!”;或者,如果您愿意,适用于此材料的完全非限制性的仅署名许可证(例如Apache 2.0用于软件;或CC-BY用于文本),以便您根据需要使用它。
本文还包含源代码,几乎完全是通过删除解释性材料、重新格式化和释义行内文档/注释以及我(堆栈交换网络上的shelleybutterfly)进行一些修改/添加而创建的。对于我可能在法律上获得的任何PTLIB衍生物,我在此放弃所有这种利益,并将原始作品的所有版权利益归还给原始版权持有人,如PTLIB的许可证所规定的那样。在任何不可能出于任何原因适用于您的条款的司法管辖区域中,对于我拥有的任何材料利益,我在此授权您根据您选择的任何非限制性、仅署名的许可证使用它;或者,如果这也不可能,那么我允许堆栈交换公司根据他们确定的在您的司法管辖区域内可接受的任何条款向您提供它。
来自PTLIB和PTLIB衍生物的所有源代码,未被涵盖在上述其他条款中的,均根据以下协议提供给“堆栈交换公司”和您:
《Protothreads Library》授权协议
版权所有 (c) 2004-2005,瑞典计算机科学研究所。保留所有权利。
允许在源代码和二进制形式下自由分发、使用和修改,前提是满足以下条件:
1. 在分发源代码时必须包含上述版权声明、本条件列表和下列免责声明。
2. 在分发二进制文件时必须在文档和/或其他提供的材料中重现上述版权声明、本条件列表和下列免责声明。
3. 未得到特定事先书面许可,不得使用机构的名称或其贡献者的名称来认可或推广从本软件派生的产品。
本软件由瑞典计算机科学研究所和贡献者按原样提供,不提供任何明示或暗示担保,包括但不限于适销性和适用于特定目的的暗示担保。无论因何种原因,在任何情况下,瑞典计算机科学研究所和贡献者均不对直接、间接、附带、特殊、惩罚性或后果性损害(包括但不限于替代商品或服务的采购、使用、数据或利润损失或业务中断)负责,即使已被告知此类损害的可能性。
作者: Adam Dunkels