利用 ProtoThreads实现Arduino多线程处理(2)

简介: 转载请注明:@小五义http://www.cnblogs.com/xiaowuyi  QQ群:64770604 感谢小V分享给大家的博文。        我在做产品设计的课题的时候,小五义推荐我使用Protothread这个库来进行编写,研究了之后应用于自己的设计上效果还不错,应小五义的请写了这个Protothread的介绍,谈不上懂,就浅浅谈一谈我的理解,帮助大家应用,如果有错误的,欢迎指教。

转载请注明:@小五义http://www.cnblogs.com/xiaowuyi  QQ群:64770604

感谢小V分享给大家的博文。

       我在做产品设计的课题的时候,小五义推荐我使用Protothread这个库来进行编写,研究了之后应用于自己的设计上效果还不错,应小五义的请写了这个Protothread的介绍,谈不上懂,就浅浅谈一谈我的理解,帮助大家应用,如果有错误的,欢迎指教。

     Protothread(以下简称PT),是牛逼哄哄的来自瑞典皇家啥啥啥的Adam Dunkels大师编写的用于协程的一个库,你说协程是啥,简单的理解就是类似多任务处理器。当你开始做有关硬件C语言的编程之后,你会发现你的核心芯片不是无敌的,资源是有限的,这个时候合理安排资源就极为重要了,对于8位16位的单片机来说,用真正的多任务处理几乎是无法承受的,这种时候就需要PT的出马了。

        举个例子,你有两个触发端,分别是你老妈和你老婆,当你老婆生气的时候,你要跪键盘10秒,你老妈叫你的时候,你就要去给她捶背15秒,问题是你的脑子只有8位或者16位,一个时间点只能做一件事情。现在你老婆生气了,你就跪在键盘上,用最基础的delay函数数数:1秒,2秒,3秒……这时候你老妈叫你给她捶背,可是你听不见,因为你在数数,如果你老妈耐心好还好,等你跪完再叫你给她捶背,如果她老人家耐心差,叫一遍就不叫了,那可就完蛋了,直接跟你老婆开干,当然你唯一收获的就是不用跪了,准备完蛋吧。

        PT的有一个能力就是你跪下之后,不用你数数,它帮你数数,然后你膝盖就保持跪的状态,然后双手给你老妈捶背,10秒到后,它跟你讲:“诶,气管炎,10秒到了。”这个时候你想不想起来看你自己了,反正老婆给的任务完成了。

        当然这只是一个很狭窄的例子,不要狭隘的理解为PT只是给你计数,timer只是PT的一个方便的应用,这是使PT提醒的原因不是计时多久,而是一个条件,条件是数完十秒,所以说PT很灵活,你可以给把触发端给PT,比如你为了向老婆展示你的诚心,就一直跪着键盘,这时候不是叫PT数数,告诉PT,如果老妈叫你,就提醒你去敲背,这样都是可以的。

        原理大致如此,然后上几个常用的宏定义吧:

PT_INIT(pt)   初始化任务变量,只在初始化函数中执行一次就行 
PT_BEGIN(pt)  启动任务处理,放在函数开始处 
PT_END(pt)   结束任务,放在函数的最后 
PT_WAIT_UNTIL(pt, condition) 等待某个条件(条件可以为时钟或其它变量,IO等)成立,否则直接退出本函数,下一次进入本函数就直接跳到这个地方判断 

        想要真正的理解PT,自然是需要看懂源代码的,我不会那么无聊给你一个一个罗列,我就给你看几个精髓的,

#define PT_BEGIN(pt) switch((pt)->lc) {case 0;
#define PT_WAIT_UNTIL(pt,condition)   (pt)->lc = __LINE__;   case __LINE__:                                       if(!(condition))  return  

        啊哈哈哈哈,是不是没看懂!!!我一开始也是这样的!!!如果你看懂了你现在真的可以离开了,我没啥可以告诉你的了。。。

     如果你一头雾水,心生怯意,那我来给你打一级强心针吧!

#define PT_END(pt) }

     怎么样!是不是不敢相信!就一个“}”,所以那些大神跟你讲你PT_BEGIN之后,一定要一个PT_END,知道为啥了吧!因为你看看PT_BEGIN,里面一开始有个“{”。

     其实你懂了PT_WAIT_UNTIL之后,你对PT就算是基本的了解,我们一句一句来,首先pt是啥,pt就是一个struct变量,里面有什么呢?里面就一个lc,lc就是用来记住当前的位置,然后 下次进入函数的时候就直接从当前位置开始的,那又怎么做到从当前位置开始呢!这个时候就是展现Adam牛逼哄哄的时候了,_LINE_闪亮登场,_LINE_的作用就是能取到当前_LINE_所在位置的行数,现在你知道了当前的位置,你怎么到这个位置呢,就用的switch case语句,用static pt记录_LINE_的行数,然后通过switch((pt)->lc)跳到case _LINE_,怎么样,理解了之后是不是感叹Adam机智!

     最后就简单的我下面列一个示例吧,是按照arduino的编译器格式写的,就按照上面说的老妈老婆的例子,我个人不敢给老妈老婆分高下,一个管我一生,一个管我下半身,所以就老婆叫跪就跪10秒,不然不跪,老妈叫敲背就敲背15秒,否则浪费。

#include<pt.h>//声明protothread
#include<PT_timer.h>//声明pt.timer
#define ANGEROFWIFE 1//老婆的愤怒
#define MOTHERCALL 2//老母的呼唤
#define KNEE 3//遭殃的膝盖
#define HAND 4//幸福的双手
static struct pt pt1,pt2;
PT_timer servotimer1;
PT_timer servotimer2;//定义两个计时器
void setup()
{
    pinMode(ANGEROFWIFE,INPUT);
    pinMode(MOTHERCALL,INPUT);
    pinMode(KNEE,OUTPUT);
    pinMode(HAND,OUTPUT);//定义端口输入输出
    PT_INIT(&pt1);
    PT_INIT(&pt2);//两个线程初始化,其实就是pt->lc归零
}
static int mission1(struct pt *pt)
{
    PT_BEGIN(pt);//线程开始
    while(1)
    {
        digitalWrite(KNEE,LOW);//老婆不生气我不跪
        PT_WAIT_UNTIL(pt,digitalRead(ANGEROFWIFE));//等老婆生气
        digitalWrite(KNEE,HIGH);//我擦真生气了,赶紧跪
        servotimer1.setTimer(10000);//设定跪10秒,这里单位1为一微妙
        PT_WAIT_UNTIL(pt,(servotimer1.Expired()));//当时间溢出,这里是10秒就结束
}
PT_END(pt);//线程结束
}
Static int mission2(struct pt *pt)
{
    PT_BEGIN(pt);
    While(1)
{
    digitalWrite(HAND,LOW);
    PT_WAIT_UNTIL(pt,digitalRead(MOTHERCALL));
    digitalWrite(HAND,HIGH);
    servotiemr2.setTimer(15000);
    PT_WAIT_UNTIL(pt,(servotimer2.Expired()));
}
    PT_END(pt);
}

void loop()
{
    mission1(&pt1);
    mission2(&pt2);//循环执行,特步,永不停息
}

        上述只是关于PT一个很简单的例子,有一点需要提醒的是,尽量不要在线程里声明变量,如果要用也要用static变量,不然很容易出错也不易察觉,更多的关于protothread详细的源代码和理解可以参照REFERENCE,TV领进门,修行在个人。多多应用,勇于尝试,你就能熟练应用这一神器。

目录
相关文章
【Arduino基础教程】多线程入门
Arduino多线程库SCoop 多线程是指从软件或者硬件上实现多个线程并发执行的技术。 Arduino开发板借助SCoop即可轻松实现多线程。
2168 0
|
3天前
|
NoSQL Redis
单线程传奇Redis,为何引入多线程?
Redis 4.0 引入多线程支持,主要用于后台对象删除、处理阻塞命令和网络 I/O 等操作,以提高并发性和性能。尽管如此,Redis 仍保留单线程执行模型处理客户端请求,确保高效性和简单性。多线程仅用于优化后台任务,如异步删除过期对象和分担读写操作,从而提升整体性能。
14 1
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
60 1
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
32 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
25 2
|
2月前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
41 2
|
2月前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
47 1
|
2月前
|
安全 Java 开发者
Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用
本文深入解析了Java多线程中的`wait()`、`notify()`和`notifyAll()`方法,探讨了它们在实现线程间通信和同步中的关键作用。通过示例代码展示了如何正确使用这些方法,并分享了最佳实践,帮助开发者避免常见陷阱,提高多线程程序的稳定性和效率。
53 1
|
2月前
|
Java
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。
在Java多线程编程中,`wait()` 和 `notify()/notifyAll()` 方法是线程间通信的核心机制。它们通过基于锁的方式,使线程在条件不满足时进入休眠状态,并在条件成立时被唤醒,从而有效解决数据一致性和同步问题。本文通过对比其他通信机制,展示了 `wait()` 和 `notify()` 的优势,并通过生产者-消费者模型的示例代码,详细说明了其使用方法和重要性。
41 1
|
1月前
|
数据采集 Java Python
爬取小说资源的Python实践:从单线程到多线程的效率飞跃
本文介绍了一种使用Python从笔趣阁网站爬取小说内容的方法,并通过引入多线程技术大幅提高了下载效率。文章首先概述了环境准备,包括所需安装的库,然后详细描述了爬虫程序的设计与实现过程,包括发送HTTP请求、解析HTML文档、提取章节链接及多线程下载等步骤。最后,强调了性能优化的重要性,并提醒读者遵守相关法律法规。
66 0