嗯,我看到其他答案涵盖了很多领域;所以,希望我最终不会把它变成比我想要的更大的东西。 (TL;DR:拯救女孩!:D)。但是,我确实(我认为)提供了一个非常好的解决方案;所以我希望你能利用它。我对 8051[☆] 的经验很少;尽管我确实在另一个微控制器上工作了大约 3 个月(加上大约 3 个全职),但取得了一定的成功。在此过程中,我几乎完成了这个小东西必须提供的所有功能:串行通信、SPI、PWM 信号、伺服控制、DIO、热电偶等等。当我在研究它时,我很幸运地遇到了一个出色的(IMO)解决方案,用于(合作)“线程”调度,它与一些由 PIC 上的中断完成的少量额外实时工作混合得很好。当然,还有其他设备的其他中断处理程序。
pt_thread: 由 Adam Dunkels(与 Oliver Schmidt)发明(v1.0 于 2005 年 2 月发布),他的网站是对它们的一个很好的介绍,wand 包括从 2006 年 10 月到 v1.4 的下载;我很高兴再次去看,因为我找到了;但是 2009 年 1 月的一篇文章指出,Larry Ruane 使用了事件驱动技术“[用于] [使用 GCC;并使用] 非常好的语法的完整重新实现”,and available on sourceforge。不幸的是,自 2009 年左右以来,两者似乎都没有更新。但 2006 年的版本非常适合我。最后一条新闻(从 2009 年 12 月开始)指出,“Sonic Unleashed”在其手册中指出使用了 protothreads!
我认为 pt_threads 很棒的一件事是它们非常简单;而且,无论更新的(Ruane)版本有什么好处,它肯定更复杂。尽管可能值得一看,但我将在这里坚持使用 Dunkels 的原始实现。他原来的pt_threads“库”包括:五个头文件。而且,真的,这似乎有点夸大其词,因为一旦我缩小了一些宏和其他东西,删除了 doxygen 部分,示例,并将 cmets 剔除到最低限度,我仍然觉得给出了一个解释,它大约在115 行(包括在下面。)
源 tarball 中包含一些示例,他的网站上提供了非常好的 .pdf 文档(或 .html)(上面链接)。但是,让我通过一个快速示例来阐明一些概念。 (不是宏本身,我花了一些时间来理解这些,而且它们并不是仅仅为了使用该功能所必需的。:D)
很遗憾,我今晚没时间了;但我会在明天的某个时候重新开始写一个小例子;无论哪种方式,他的网站上都有 ton 的资源,链接在上面;这是一个相当简单的过程,对我来说棘手的部分(因为我想它与任何协作多线程一样;Win 3.1 有人吗?:D)是确保我对代码进行了正确的循环计数,以免超出时间我需要在产生 pt_thread 之前处理下一件事。
我希望这能给你一个开始;如果您尝试一下,请告诉我结果如何!
文件: pt.h
#ifndef __PT_H__
#define __PT_H__
#包括“lc.h”
// 注意:枚举是我的压缩空间;最初都是#defines
枚举 PT_STATUS_ENUM { PT_WAITING, PT_YIELDED, PT_EXITED, PT_ENDED };
结构 pt { lc_t lc; } // protothread 控制结构(pt_thread)
#define PT_INIT(pt) LC_INIT((pt)->lc) // 使用前初始化pt_thread
// 你可以使用它来声明 pt_thread 函数
#define PT_THREAD(name_args) char name_args
// 注意:看这个,我想我可能会定义自己的宏如下,以免
// 每次都必须重新声明 struct pt *pt。
//#define PT_DECLARE(name, args) char name(struct pt *pt, args)
// 开始/结束 pt_thread (内部实现 fn);必须始终配对
#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);返回 PT_ENDED;}
// {block, yield} 'pt' {until,while} 'c' 为真
#define PT_WAIT_UNTIL(pt,c) 做 { \
LC_SET((pt)->lc); if(!(c)) {return PT_WAITING;} \
} 而(0)
#define PT_WAIT_WHILE(pt, cond) PT_WAIT_UNTIL((pt), !(cond))
#define PT_YIELD_UNTIL(pt, cond) \
做{ PT_YIELD_FLAG = 0; LC_SET((pt)->lc); \
if((PT_YIELD_FLAG == 0) || !(cond)) { return PT_YIELDED; } } 而(0)
// 注意:不存在对应的“YIELD_WHILE”;监督? [贝壳蝴蝶]
//#define PT_YIELD_WHILE(pt,cond) PT_YIELD_UNTIL((pt), !(cond))
// 阻塞 pt_thread 'pt',等待子 'thread' 完成
#define PT_WAIT_THREAD(pt, thread) PT_WAIT_WHILE((pt), PT_SCHEDULE(thread))
// 生成 pt_thread 'ch' 作为 'pt' 的子线程,等待 'thr' 退出
#define PT_SPAWN(pt,ch,thr) 做 { \
PT_INIT((孩子)); PT_WAIT_THREAD((pt),(thread)); } 而(0)
// 阻塞并导致 pt_thread 在其 PT_BEGIN() 处重新开始执行
#define PT_RESTART(pt) 做 { PT_INIT(pt);返回 PT_WAITING; } 而(0)
// 退出 pt_thread;如果是孩子,则父母将取消阻止并运行
#define PT_EXIT(pt) 做 { PT_INIT(pt);返回 PT_EXITED; } 而(0)
// 调度 pt_thread: fn ret != 0 如果 pt 正在运行,或者 0 如果退出
#define PT_SCHEDULE(f) ((f) lc); \
if(PT_YIELD_FLAG == 0) { 返回 PT_YIELDED; } } 而(0)
文件: lc.h
#ifndef __LC_H__
#define __LC_H__
#ifdef LC_INCLUDE
#include LC_INCLUDE
#别的
#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 无符号短 lc_t;
#define LC_INIT(s) s = 0;
#define LC_RESUME(s) switch(s) { case 0:
#define LC_SET(s) s = __LINE__;案例__LINE__:
#define LC_END(s) }
#endif /* __LC_SWITCH_H__ */
文件: lc-addrlabels.h
#ifndef __LC_ADDRLABELS_H__
#define __LC_ADDRLABELS_H__
类型定义无效* lc_t;
#define LC_INIT(s) s = NULL
#define LC_RESUME(s) do { if(s != NULL) { goto *s; } } 而(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) \
做 {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 { 无符号整数计数; };
// 用于初始化、等待和发出 pt_sem 信号量的宏
#define PT_SEM_INIT(s, c) (s)->count = c
#define PT_SEM_WAIT(pt, s) 做\
{ PT_WAIT_UNTIL(pt, (s)->count > 0); -(s)->计数; } 而(0)
#define PT_SEM_SIGNAL(pt, s) ++(s)->count
#endif /* __PT_SEM_H__ */
[☆] *大约一周学习微控制器[†]并在评估期间使用它一周,看看它是否符合我们的要求需要一个小的线路可更换远程 I/O 单元。 (长话短说:没有)
[†] The 8051 Microcontroller, Third Edition *被建议给我作为 8051 编程“圣经”我不知道是不是,但是我当然能够使用它来解决问题。[‡]
[‡] 甚至现在再看一遍,我也没有什么不喜欢的地方。 :) 好吧,我的意思是……我希望我没有买两本;但他们是如此便宜!
许可协议(如适用)
这篇文章包含基于(或取自)“Protothreads 库”(本文及以下简称“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,雪莱蝴蝶,与爱分享!”;或者,如果您愿意,
适用于材料的完全非限制性、仅限署名的许可(例如用于软件的 Apache 2.0;或 CC-BY
文本许可),以便您可以根据需要使用它,使其最适合您的需要。
这篇文章还包含源代码,几乎完全是通过删除解释性材料从原始源创建的,
重新格式化和解释内联文档/cmets,以及我的一些修改/添加
(stackexchange 网络上的 shelleybutterfly)。我可能合法获得的任何 PTLIB 衍生品
版权或其他利益,我特此将所有此类利益和原作品的所有版权利益归还给
遵循本协议的 PTLIB 许可中指定的原始版权所有者。
在任何司法管辖区,无论出于何种原因,上述条款都不可能适用于您,那么,无论出于何种原因
我对材料感兴趣,我特此根据您的任何非限制性、仅归属的许可向您提供
选择;或者,如果这也不可能,那么我允许 stack exchange inc 将其提供给您
您确定在您的管辖范围内可以接受的任何条款。
来自 PTLIB 的所有源代码,以及 PTLIB 的派生代码,上面详述的其他条款未涵盖的所有源代码
根据以下协议,特此提供给“stack exchange inc”和您:
“The Protothreads Library”许可协议
版权所有 (c) 2004-2005,瑞典计算机科学研究所。版权所有。
允许以源代码和二进制形式重新分发和使用,无论是否修改,前提是
满足以下条件:
1. 源代码的再分发必须保留上述版权声明、本条件列表和以下内容
免责声明。
2. 以二进制形式重新分发必须复制上述版权声明、此条件列表和以下内容
随分发提供的文档和/或其他材料中的免责声明。
3. 研究所的名称或其贡献者的姓名均不得用于认可或推广衍生产品
未经事先书面许可,不得使用本软件。
本软件由研究所和贡献者“按原样”提供,并提供任何明示或暗示的保证,包括:
但不限于,对适销性和特定用途适用性的默示保证不予承认。没有
事件机构或贡献者应对任何直接的、间接的、偶然的、特殊的、示例性的或
间接损害(包括但不限于采购替代商品或服务;使用、数据或
利润;或业务中断)无论是由何种原因引起的,以及基于任何责任理论,无论是合同责任、严格责任、
或以任何方式因使用本软件而引起的侵权(包括疏忽或其他),即使已被告知
此类损坏的可能性。
作者:亚当·邓克斯