极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 22373|回复: 13

求教:如何在arduino库函数包含中断服务程序?

[复制链接]
发表于 2015-4-19 11:01:10 | 显示全部楼层 |阅读模式
本帖最后由 stpanzj 于 2015-4-19 21:35 编辑

想写一个旋转编码器的库函数,希望用中断处理输入的变化。编译的时候出现很多问题,根据提示把ISR用static、ISR中用到的变量也static后,在.cpp中增加申明,编译成功。问题是这样一来,这些static类型只能有一份,也就是说不管你生成多少个object,这些ISR和变量使用的都是同一个存储位置的值,问题是:
1、ISR不用static可以吗?
2、ISR中使用到的变量不用static可以吗?

我把问题简化为编译能通过的版本。请各位大侠指点。
如果去掉static,编译出现的问题是:argument of type 'void (test:()' does not match 'void (*)()'

以下是 test.h

#ifndef test_h
#define test_h
#include "Arduino.h"

class test {
public:
  test(uint8_t);
  void init ();
  
private:
  static void _int0(); // ISR
  static uint8_t _data; // use in ISR
};
#endif

以下是test.cpp
#include <test.h>

uint8_t _data;

test::test(uint8_t a){
        _data = a;
}

void test::_int0(){
  _data++;
}

void test::init(){
        attachInterrupt(4,_int0,LOW);
}

以下是test.ino
#include <test.h>
test a(5);
void setup() {
  a.init();
}
void loop() {
}
回复

使用道具 举报

 楼主| 发表于 2015-4-19 12:38:06 | 显示全部楼层
本帖最后由 stpanzj 于 2015-4-19 21:38 编辑

在库函数wire.h中找到答案了,如果定义为static,需要在cpp中加入这一句:
uint8_t test::_data ;
这样编译就成功了。

但问题来了,如果我
test a(4);
test b(5);
这时a和b用的中断程序是同一个,变量也是同一个。我需要的是a用的ISR和b用的各有一份copy,不知能否class中的ISR不用static?
回复 支持 反对

使用道具 举报

发表于 2015-4-19 12:41:46 | 显示全部楼层
庫是未有實體的,  attachInterrupt(4,_int0,LOW); 當中 _int0 並不存在.
回复 支持 反对

使用道具 举报

发表于 2015-4-19 18:27:48 | 显示全部楼层
谢谢分享学习一下
回复 支持 反对

使用道具 举报

发表于 2015-4-20 00:02:03 | 显示全部楼层

除了 Super169 大神说的 你的 _int0 不存在之外,
要注意  attachInterrupt(4, fun_ISR, mode);
如果做超过一次, 那是以最后 attach 的为准,
因为每次 attach 会把 fun_ISR (是一个函数的 pointer) 复制进去盖掉原先的
除非你 attachInterrupt 到不同的 INT# (或 Pin # for some Arduino board)
回复 支持 反对

使用道具 举报

发表于 2015-4-20 12:32:04 | 显示全部楼层
不知樓主是否明白我說 "_int0 並不存在" 的意思.

我嘗試用一個簡單的比喻, 解釋一下吧.

就以 "水怪" 這個 類 作為例子, 我們可以說 "水怪" 有 "鬼爪".
現在你有一個 "蘋果", 如果你說把 "蘋果" 交給 "鬼爪".
你會知道另個 "蘋果" 要交到那裡嗎?
因為你只知道 "鬼爪" 是屬於 "水怪", 但現在沒有 "水怪" 呢?
這就是我說的 "_int0 並不存在" 的意思了.

如果發現你面前有一隻 "水怪" 叫 "Peter", 現在說 "蘋果" 交給 "鬼爪".
如果世上只有一隻 "水怪", 這是沒有問題的.
但如果 "水怪" 可以有多隻, 就應該說明是 交給 "Peter" 的 "鬼爪".
否則, 你的一個 "蘋果", 怎可以交給所有  "水怪"?

程式亦是一樣, 只有 class (水怪) 的定義時, 鬼爪 (_int0) 是不存在的.
當你建立了一個 variable (Peter) 時 , "Peter" 的 "鬼爪" 就可以用 Peter.鬼爪 去表示了.
如果 水怪 是指主程式, 由於主程式只有一個, 而且亦非以 class 出現.
當你在主程式中定義時, 就可以直接用到了.

希望樓主可以明白吧.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-20 17:11:54 | 显示全部楼层
tsaiwn 发表于 2015-4-20 00:02
除了 Super169 大神说的 你的 _int0 不存在之外,
要注意  attachInterrupt(4, fun_ISR, mode);
如果做 ...

谢谢,我注意到了。今天看了红外遥控器的库程序,里面用CNT2中断作为处理输入的入口,中断服务程序用ISR(TIMER_INTR_NAME)的形式编写,就完全不需要用到static了。
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-20 17:22:21 | 显示全部楼层
Super169 发表于 2015-4-20 12:32
不知樓主是否明白我說 "_int0 並不存在" 的意思.

我嘗試用一個簡單的比喻, 解釋一下吧.

多谢了。前面都可以理解。最后两句“如果 水怪 是指主程式, 由於主程式只有一個, 而且亦非以 class 出現.
當你在主程式中定義時, 就可以直接用到了.”
是否说,如果在主程序中以非Class定义,就可以直接使用?
其实我是在学习,也想写出健壮的程序,所以想用Class来屏蔽非必要的内容,但是用到Interrupt routine就抓瞎了,好像有限制非用static不可。但一用到static,就只有一份copy,多个变量也共用相同的值,就比如定义了水怪,其中的爪是用static定义了,那么当有两个水怪Peter和Jon时,我拿一个苹果给Peter的爪,结果Jon也拿到了。
回复 支持 反对

使用道具 举报

发表于 2015-4-20 17:57:22 | 显示全部楼层
stpanzj 发表于 2015-4-20 17:22
多谢了。前面都可以理解。最后两句“如果 水怪 是指主程式, 由於主程式只有一個, 而且亦非以 class 出現. ...

例子就好像主程式中, 你加入了一個 function, 例如:

  1. void setup() {
  2. :
  3. }

  4. void loop() {
  5. :
  6. }

  7. void int0() {
  8. :
  9. }
复制代码


這時 int0 是在主程式中定義, 並非在庫內定義.  這個 int0 可以看成是直接存在的 (本來 主程式背後也是在一個 class 之中, 但比較複雜, 你就當成是直接存在的吧).

static 的意思沒錯了, 如果水怪的爪是用 static 去定義, 那麼所有水怪都共用同一隻爪了.  這裡要特別小心, 不要胡亂去設定 static 的東西.  

用 class 去 屏蔽非必要的内 是很好的, 建立自己的庫, 一些常用的程序, 就不用重複了.
例如你可以在自己的庫 (e.g myUtil) 做一個 static 既 method 把 數字轉成字串 (e.g n2s).
以後你只要加入自己的庫, 在程式中直接用 myUtil::n2s 就可以了, 不需要設定一個 myUtil 的變數.

回复 支持 反对

使用道具 举报

发表于 2015-4-20 19:02:18 | 显示全部楼层
stpanzj 发表于 2015-4-20 17:11
谢谢,我注意到了。今天看了红外遥控器的库程序,里面用CNT2中断作为处理输入的入口,中断服务程序用ISR( ...

真正的中断程序本来就是用 ISR(TIMER_INTR_NAME)的形式编写,
只是为了大多数写程序者方便,
Arduino 对于 INT0, INT1, [ INT2, ... INT5]
都事先写了对应的 ISR( )
然后,
在 ISR( ) 里面偷放一个类似这:
   void (*Func) (void);
每当你用类似这 attachInterrupt(4, fun_ISR, mode);
  它就做 Func = fun_ISR;

(当然你必须事先写好函数 void fun_ISR( ) { ... } )
然后 ISR( ) 内又写像这:
   if(  Func != 0 ) Func( );  // 这会执行你挂入的 fin_ISR( );

类似这种用法,
你可以参考这 SimpleTimer: (原版本有错, 我进去修改了)
  http://playground.arduino.cc/Main/SimpleTimer

或参考我改写过的这 SuperSimpleTimer:
   http://playground.arduino.cc/Main/SuperSimpleTimer
回复 支持 反对

使用道具 举报

发表于 2015-4-20 19:05:03 | 显示全部楼层
tsaiwn 发表于 2015-4-20 19:02
真正的中断程序本来就是用 ISR(TIMER_INTR_NAME)的形式编写,
只是为了大多数写程序者方便,
Arduino 对 ...

补充一下
相对于刚刚说的
如果你做 detachInterrupt(4);
那它只是做 Func = 0;
这样等于取消了你之前挂载的 attachInterrupt(4, fun_ISR, mode);
回复 支持 反对

使用道具 举报

 楼主| 发表于 2015-4-20 23:06:05 | 显示全部楼层
tsaiwn 发表于 2015-4-20 19:05
补充一下
相对于刚刚说的
如果你做 detachInterrupt(4);

谢谢这么热心的大侠。我的问题是如果不用static,在Class中定义用到中断,有什么办法,比如我的例子中不用static。
回复 支持 反对

使用道具 举报

发表于 2015-4-20 23:25:36 | 显示全部楼层
stpanzj 发表于 2015-4-20 23:06
谢谢这么热心的大侠。我的问题是如果不用static,在Class中定义用到中断,有什么办法,比如我的例子中不用 ...



如同 super169 大神说的,
因为 class 本身是一个 type, 没有实体,
你必须创建实体之后, 才能透过该 class 的实体去使用没有 static 的函数,
所以, 如果你一定要在 class 内使用中断,
有三个方法:
(1)使用 ISR( ) 的, 这其实不属于 class,
   所以严格说来不算从 class 内使用
(2)可以用 class 内的 static 函数,
   因为 static 函数就是即使没有任何该 class 的实体也可以用的函数!
   可是你又说不要, 那只剩第三种方法:
(3)如果你要在 class 内 attachInterrupt( )
   要把你的 ISR 函数写在你的 Sketch 程序内,
   然后透过 constructor 或库类别的其它函数把 ISR 传进去
   这就像你把函数传给 attachInterrupt( ) 那样,
   这方法 super169 大神已经给你范例了
回复 支持 反对

使用道具 举报

发表于 2017-12-25 11:50:34 | 显示全部楼层
对于这个程序,我想改为这种功能,按一下,开始闪烁,闪烁的时间是2秒钟《间隔时间是80ms》,在进行第二次闪烁闪烁的时间是100《间隔时间是100ms》,两次闪烁时间定时总时间是1分30s,  有哪位帮帮忙
const int buttonPin = 12;       //button pin number
const int ledPin =  13;         //LED pin number
int buttonState = 3;            //button state  1,2,3
unsigned long currentMillis;    //record the current time for blinking
long previousMillis = 0;        //store the last time LED was updated
long interval = 1000;           //interval at which to blink (milliseconds)
/////////////////////////////////////////////////////////////////////////////////////////
void setup()
{
  pinMode(ledPin, OUTPUT);      
  pinMode(buttonPin, INPUT);   
  digitalWrite(buttonPin, HIGH); //use internal pull up resistor
}////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////
void loop()
{
if (digitalRead(buttonPin) == LOW)   //Not pressed - HIGH; Pressed - LOW
   {  
    buttonState++;
    if (buttonState > 3) buttonState = 1;  //button State from 1 to 3
    //  
    delay(450);             /////handle debouncing//////////////
   }// end if digitalRead(buttonPin)
///////////////////////////////////
switch (buttonState)
{
  case 1:
         digitalWrite(ledPin, HIGH);
         break;
  case 2:
         currentMillis = millis();                           //start the timer
         if (currentMillis - previousMillis > interval)      //timer moved forward by the preset interval
           {
            previousMillis = currentMillis;   
            digitalWrite( ledPin, digitalRead( ledPin ) ^ 1 );   //read the state of the Led, reverse it and turn it on or off accordingly
           }
         break;
  case 3:
         digitalWrite(ledPin, LOW);  
         break;
}//end switch        
}/////end loop
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-25 14:07 , Processed in 0.042792 second(s), 21 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表