Có cách nào để thay thế chức năng lõi Arduino mà không sửa đổi mã Arduino?


7

(Hãy bỏ qua những gợi ý rằng đó là ý tưởng sai và bị gián đoạn trong một thời gian ...).

Có cách nào để thay thế digitalWrite()phương thức (ví dụ) để tôi có thể thêm một cái gì đó nữa để xảy ra trong đó không? Nhưng các quy tắc sau phải được tuân theo:

  • Một cuộc gọi để digitalWrite(pin, value)gọi phương thức mới.
  • Một cuộc gọi đến digitalWrite(pin, value)từ một thư viện khác có trong mã của tôi (ví dụ SD.h) phải gọi phương thức mới.
  • Không có tệp nào trong số các tệp Arduino có thể được sửa đổi (tôi không muốn làm điều đó trong mỗi lần nâng cấp phiên bản Arduino).
  • Mọi thứ phải được thực hiện trong một tệp tiêu đề mà tôi có thể bao gồm để sử dụng chức năng mới.

Tôi đã thử cái này và nó hoạt động, nhưng chỉ trong mã của tôi, không phải trong các thư viện bên ngoài mà tôi đưa vào sau tiêu đề này:

#include <Arduino.h>

class PinsHook {
  public:
    static void digitalWriteHook(uint8_t pin, uint8_t value) {
      digitalWrite(pin, value);
      if (pin == 8) {
        Serial.print("Pin ");
        Serial.print(pin);
        Serial.print(" changed to ");
        Serial.println(value);
      }
    }
};

#define digitalWrite(pin, value) PinsHook::digitalWriteHook(pin, value)

1
Thư viện này dường như có thể làm điều đó bằng cách sử dụng các mẫu.
Gerben

Câu trả lời:


3

Không chắc chắn về Arduino IDE (Tôi chưa cài đặt nó ngay bây giờ) nhưng sử dụng UECIDE thì đơn giản như xác định một digitalWritechức năng mới trong mã của bạn. Khi toàn bộ shebang được liên kết với nhau, hàm trong mã của bạn sẽ ghi đè hàm trong các thư viện.

void setup() {
    pinMode(13, OUTPUT);
}

void loop() {
    digitalWrite(13, HIGH);
    delay(100);
    digitalWrite(13, LOW);
    delay(800);
}

void digitalWrite(int pin, int level) {
    // Not gonna do anything!
}

Nhận xét chức năng DigitalWrite và đèn LED nhấp nháy. Không chú ý đến nó và nó không chớp mắt nữa.


Vâng, tôi nghĩ rằng nó cũng làm việc trong các thư viện bên ngoài! Nhưng theo cách này, tôi không thể gọi bản gốc digitalWrite()từ phương thức này bởi vì phương thức này đang tự gọi chính nó (stack overflow!). Điều này có thể được giải quyết? Nếu phương thức của tôi là phương thức được liên kết, thì tôi nghĩ rằng phương thức ban đầu hoàn toàn không được bao gồm và không thể được gọi - tôi có đúng không?
PW

Bạn nói đúng. Bạn sẽ phải sao chép và dán nội dung của DigitalWrite gốc vào biến thể của bạn để sử dụng nó.
Majenko

Tôi đã thử nó nhưng nó không biên dịch. Một số thứ còn thiếu. Nếu bạn có thể tạo mã ví dụ của phương thức đó, ghi đè và gọi phương thức ban đầu (và thêm nó vào câu trả lời của bạn), thì đó sẽ là câu trả lời tôi đang tìm kiếm.
PW

1
Có thể có một số lượng lớn trong DigitalWrite hiện có mà bạn không cần. Nó được tạo ra để chung chung, nhưng nếu bạn chỉ muốn làm những việc cụ thể với các chân cụ thể, bạn có thể tự mã hóa các cuộc gọi đó đến các thanh ghi PORT.
Majenko

@PW ghi đè một chức năng duy nhất sẽ chỉ hoạt động nếu đó là chức năng duy nhất trong mô-đun. "Đơn vị nhỏ nhất mà trình liên kết biết là mô-đun đối tượng". Vì vậy, nếu trình liên kết tìm thấy một hàm phù hợp trong mã của bạn, nó sẽ đưa mô-đun đó vào và bỏ qua các phương thức còn lại trong mã lib. Tôi tin rằng đó là những gì gây ra lỗi trình biên dịch mà bạn đề cập. Thêm thông tin trong câu trả lời của tôi dưới đây.
RubberDuck

2

Chỉ cần ném một ý tưởng. Không có thời gian để kiểm tra ngay bây giờ.

Chỉnh sửa : Tôi đã thử nghiệm giải pháp bên dưới và nó hoạt động như mong đợi, miễn là cả hai __wrap___real_digitalWrite()được khai báo extern "C".

Nếu bạn có thể thuyết phục IDE thêm các tùy chọn bổ sung vào dòng lệnh biên dịch, bạn có thể:

  1. đặt tên __wrap_digitalWrite()cho việc thực hiện của bạndigitalWrite()
  2. bên trong nó, gọi __real_digitalWrite()khi bạn muốn nhận triển khai từ lõi Arduino
  3. thêm -Wl,--wrap=digitalWritevào lệnh biên dịch của bước liên kết cuối cùng

Ví dụ bao bọc:

extern "C" {
    void __wrap_digitalWrite(uint8_t, uint8_t);
    void __real_digitalWrite(uint8_t, uint8_t);
}

// Calls to digitalWrite() are diverted to this.
void __wrap_digitalWrite(uint8_t pin, uint8_t value)
{
    __real_digitalWrite(pin, value);  // call implementation from Arduino core
    if (pin == 8) {
        Serial.print(F("Pin 8 changed to "));
        Serial.println(value);
    }
}

Cố gắng để điều này trong một lớp học sẽ không giúp đỡ.

Cf trang người đàn ông của gnu ld .


2
Vui lòng giải thích thêm về bước 3: Làm thế nào để chúng tôi "thêm -Wl,--wrap=digitalWritevào lệnh biên dịch của bước liên kết cuối cùng"?
Gabriel Staples

1

Bạn đã thử với macro C chưa?

#define digitalWrite(a, b) \
(pre(), digitalWrite(a, b), post())

điều này đánh giá sự trả thù của post()

bạn có thể bỏ qua một trong hai pre()hoặc post()chức năng / macro.

Điều này cần phải được đặt ở đâu đó được bao gồm bất cứ khi nào tiêu đề cho digitalWrite được bao gồm, để nó có thể ghi đè lên nó.

Sự thay thế duy nhất khác là trở nên sáng tạo với thư viện nhị phân.

Hoặc, chỉ cần hợp lý và sửa đổi mã nguồn. Nó đặc biệt có sẵn để mọi người có thể sửa đổi nó.

Wrt không lặp lại sửa đổi mỗi khi có một bản phát hành Arduino mới, điều đó nên tránh. Nếu bạn xây dựng môi trường từ các nguồn git, bạn có thể duy trì bản vá của mình bằng cách hợp nhất / nổi loạn lên nhánh chính.


Vâng, nhưng đó là phương sách cuối cùng. Macro của bạn hoạt động giống như macro của tôi (xem dòng mã cuối cùng của tôi) - không hoạt động trong các thư viện khác. Tôi đang tìm kiếm một cách không xâm lấn để đưa nó vào các thư viện khác. Có ý kiến ​​gì không?
PW

Bạn đang đi ngược lại mọi nguyên tắc thiết kế được sử dụng trong việc thiết kế các lệnh gọi và thư viện chức năng :-D Điều bạn muốn làm gần với việc hack nhị phân hơn là thay đổi mã nguồn của chương trình. Tùy chọn giả hợp pháp duy nhất còn lại là chơi với trình liên kết. Ví dụ đổi tên hàm ban đầu và sử dụng --wrap để chiếm quyền điều khiển. Kiểm tra xem đây là nguồn phần mềm điểm bắt đầu.org / binutils / docs
Igor Stoppa

Btw, nếu bạn chỉ muốn theo dõi những gì đang diễn ra ở cấp độ pin, bạn có thể sử dụng một bộ phân tích logic - nó rất rẻ và đáng tin cậy hơn một số bản in. Thêm vào đó, nó thêm không chi phí cho chương trình. Xem phần "sigrok và Pulseview" ở giữa câu trả lời cho arduino.stackexchange.com/questions/22898/ Kẻ
Igor Stoppa

Tôi biết. Tôi đang suy nghĩ về việc chuyển hướng chân, không gỡ lỗi. Và bằng cách chuyển hướng tôi có nghĩa là gửi chúng thông qua một bản mở rộng hoặc đăng ký thay đổi, điều mà các thư viện ban đầu không thể làm được. Tôi hết thời và tôi không muốn viết các phiên bản thư viện tiêu chuẩn của riêng mình. Liệu nó có ý nghĩa bây giờ?
PW

1

Arduino sử dụng trình biên dịch avr để biên dịch thư viện chuẩn của nó thành một core.atệp. Do cách thức hoạt động của trình liên kết avr-gcc và thứ tự mà Arduino chỉ định thứ tự liên kết, bạn có thể "ghi đè" hành vi của các chức năng cốt lõi bằng cách sao chép tệp có chứa phương thức bạn muốn thay đổi vào dự án của bạn.

Trình biên dịch biên dịch một tệp ngôn ngữ cấp cao duy nhất (ví dụ ngôn ngữ C) thành một tệp mô-đun đối tượng. Trình liên kết (ld) chỉ có thể làm việc với các mô-đun đối tượng để liên kết chúng lại với nhau. Các mô-đun đối tượng là đơn vị nhỏ nhất mà trình liên kết làm việc với.

Thông thường, trên dòng lệnh của trình liên kết, bạn sẽ chỉ định một tập hợp các mô-đun đối tượng (đã được biên dịch trước đó) và sau đó là danh sách các thư viện, bao gồm Thư viện C chuẩn. Trình liên kết lấy tập hợp các mô-đun đối tượng mà bạn chỉ định trên dòng lệnh và liên kết chúng lại với nhau. Sau đó có lẽ sẽ có một bộ "tài liệu tham khảo không xác định". Một tài liệu tham khảo về cơ bản là một cuộc gọi chức năng. Tham chiếu không xác định là lệnh gọi hàm, không có hàm xác định để khớp với lệnh gọi.

Trình liên kết sau đó sẽ đi qua các thư viện, theo thứ tự, để khớp các tham chiếu không xác định với các định nghĩa hàm được tìm thấy trong các thư viện. Nếu nó tìm thấy chức năng phù hợp với cuộc gọi, thì trình liên kết sẽ liên kết trong mô-đun đối tượng trong đó chức năng được đặt. Phần này rất quan trọng: các liên kết liên kết trong MODULE MỤC TIÊU ENTIRE trong đó chức năng được đặt. Hãy nhớ rằng, trình liên kết không biết gì về các hàm bên trong của một mô-đun đối tượng, ngoài các tên ký hiệu (chẳng hạn như tên hàm). Đơn vị nhỏ nhất mà trình liên kết làm việc với là các mô đun đối tượng.

Khi không còn tham chiếu không xác định, trình liên kết đã liên kết mọi thứ và được thực hiện và xuất ra ứng dụng cuối cùng.

....

Trình liên kết sẽ tìm kiếm các thư viện theo thứ tự xuất hiện trên dòng lệnh. Bất kỳ chức năng nào được tìm thấy đầu tiên phù hợp với tham chiếu không xác định, nó sẽ được liên kết trong.

Liên kết của Arduino xảy ra theo thứ tự này:

  • Hồ sơ dự án
  • Thư viện
  • core.a (Arduino std lib)

Vì vậy, sao chép tệp từ vị trí cài đặt Arduino ( C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino) vào dự án của bạn và sửa đổi nó sẽ dẫn đến việc sử dụng triển khai sửa đổi của bạn.

Điều này chắc chắn sẽ vượt quá nó cho bất kỳ tệp nào trong dự án của bạn và tôi tin rằng nó cũng sẽ ghi đè lên nó trong bất kỳ thư viện nào bạn đang sử dụng, vì vậy hãy cẩn thận với phương pháp này.


0

Trong mô-đun mã của bạn, xác định chức năng trình bao bọc của riêng bạn và sử dụng toán tử phạm vi toàn cầu để tham chiếu chức năng Arduino:

void pinMode(int pin, int pinmode)
{
  /*use my custom function?*/
  if(pin >= MY_OWN_PIN_CODES_BASE)
  {
    /*todo : my own custom pin access*/
    example_pinmode(pin, pinmode);
  }
  else /*use regular Arduino function*/
  {
    /*use global scope operator :: to call Arduino library version*/
    ::pinMode(pin, pinmode);
  }
}

Hãy nhớ rằng, trình liên kết có các quy tắc mà nó tham chiếu. Toán tử phạm vi toàn cầu buộc trình liên kết chọn cái có sẵn trên toàn cầu, trong khi chức năng trình bao bọc tùy chỉnh được mô-đun xem riêng.


Để đáp ứng các yêu cầu của OP, trình bao bọc phải hoạt động trong phạm vi toàn cầu. Trong tình huống này, kỹ thuật của bạn mang lại một vòng lặp vô hạn.
Edgar Bonet

0

"Có thể" sử dụng tính năng "ghi đè" chức năng c ++ có thể giúp

void digitalWrite(uint8_t P, int8_t V, bool _wrap)
{
  Serial.print("wrap digitalwrite");
  digitalWrite(P,V);
}

Vì vậy, nếu bạn gọi cho bạn ứng dụng chính:

digitalWrite(pin_button, 1, 0); 

Phiên bản được bọc sẽ được gọi là

và nếu bạn đã sử dụng điều này:

digitalWrite(pin_button, 1); 

Bản gốc sẽ được gọi


Bạn cũng có thể sử dụng định nghĩa này:

void digitalWrite(double P, int8_t V)
{
  Serial.print("wrap digitalwrite");
  //digitalWrite(P,V); Calling it even with (int) P will cause a recursive loop 
}

Nhưng để sử dụng nó, bạn cần xác định một biến có loại kép:

double pin;
pin = 13;
digitalWrite(13,1);

1
Điều này không thỏa mãn hai yêu cầu đầu tiên của OP.
Edgar Bonet

Tôi biết, tôi chỉ nghĩ rằng điều này sẽ giúp ích trong một số trường hợp (và có thể được tăng cường bởi những bình luận khác).
Yahya Tawil

@EdgarBonet xem bản cập nhật mới nếu bạn quan tâm. Điều này đáp ứng tất cả các yêu cầu.
Yahya Tawil

Ngoại trừ điểm 2, khi gọi từ thư viện hiện có.
Edgar Bonet

0

Thông tin từ Duck Duck là chính xác nhưng không đầy đủ.

Tôi cần một lượng lớn đầu ra PWM kỹ thuật số để điều khiển tàu sở thích.

Vì vậy, tôi đã kết nối 4 x 74HC595 với At Mega của mình, nó phải được cung cấp với mẫu bit 2 kHz thông qua 0,5 ms. gián đoạn.

Hàm dịch chuyển nối tiếp của tôi chuyển 32 bit vào các thanh ghi mất 562 chúng tôi mỗi chu kỳ. Không thể nào.

Tôi đã tạo một phiên bản tối ưu hóa của DigitalWrite và Read, trong đó có 328 chúng tôi. Bây giờ có thể để PWM với 2 kHz. Thủ thuật là chức năng tối ưu hóa của tôi ghi nhớ 2 Ghim cuối cùng bao gồm bitmasks và outports của nó; và tôi bỏ qua kiểm tra Timer. Đây là chủ đề an toàn. Đối với một kỹ thuật số duy nhất, việc này sẽ mất thêm một chút thời gian. Với việc lặp lại ghi vào datapin và đồng hồ, nó cải thiện Arduino kỹ thuật số tiêu chuẩn với khoảng 42%.

Nhân tiện: Với thư viện digitalWriteFast, kết quả là 45,8, một sự cải tiến thực sự to lớn. Nhưng thư viện này xử lý chỉ ghi, nếu số pin được biết (và do đó cố định!) Tại thời gian biên dịch. Đó không phải là trường hợp với thư viện LiquidCrystal của tôi, trong đó 4 hoặc 8 bit song song được ghi vào tấm chắn thông qua 4 chân nối tiếp.

Tôi đã nhận được kỹ thuật số tối ưu hóa của mình. Chỉ hoạt động cho ứng dụng của tôi, bao gồm cả LiquidDisplay khi tôi sao chép thư viện C: \ Users \ Dell \ Tải xuống \ arduino-1.8.5 \ phần cứng ứng dụng, được gọi là

C: \ Users \ Dell \ Google Drive \ '\' .. .. AppFolder \ library .

Khi tôi thay thế trong cả hai thư mục, hệ thống dây điện tử với phiên bản tối ưu hóa của tôi, nó đã hoạt động.

Tôi không thể có được nó tại nơi làm việc, khi tôi chỉ thay thế hệ thống dây điện tử trong thư viện Arduino (LiquidCrystal đã thực hiện chức năng tiêu chuẩn ở một nơi khác).

Chỉ thêm Wired_digital.c trong thư mục con của ứng dụng của tôi đã tạo ra hàng loạt lỗi liên kết.

Do đó, tôi đã sao chép toàn bộ thư mục Arduino (không lớn) vào thư mục con của tôi trong ứng dụng và nó biên dịch mà không gặp vấn đề gì. Không phải là một giải pháp rất thanh lịch, bởi vì có một bản sao của toàn bộ thư viện; nhưng nó đã có tác dụng.

Kết luận để ghi đè chức năng Arduino lõi:

• Đặt (các) chức năng đã thay đổi của bạn (.c và / hoặc .h) trong thư viện Arduino và sao chép hoàn toàn thư viện này vào "thư viện" thư mục con trong thư mục ứng dụng của bạn. Sau đó, tất cả các thư viện khác cũng sẽ sử dụng chức năng thay đổi của riêng bạn.

Kết luận hiệu suất digitalWrite trong dịch chuyển nối tiếp 32 bit thực:

• Một DigitalWrite và Read được tối ưu hóa dễ dàng vượt trội so với tiêu chuẩn với 42% nếu 2 chân được sử dụng lặp lại. • digitalWriteFast (với các chân xác định tại thời gian biên dịch) vượt trội so với DigitalWrite tiêu chuẩn trong ứng dụng dịch chuyển 32 bit thực với 92% (nhanh hơn 12,2 lần).

Hy vọng điều này sẽ giúp những người dùng Arduino khác.

//**********************
void digitalWrite(uint8_t pin, uint8_t val)
//**********************
{
uint8_t 
  timer,
  bit,
  port;

volatile uint8_t *out; // volatile because bitmask may be changed by (higher) interrupt

uint8_t oldSREG;

static uint8_t pin1 = 0,
               pin2 = 0;

static bool    p1   = true; // boolean toggle memory

static uint8_t // Store 2 sets of port/out adresses static

//          timer1, //now exclued. Safety threat?
          bit1,
          *out1,
//          timer2,
          bit2,
          *out2; 

  oldSREG = SREG;  // CLI for thread proof function
  cli();
  if ( (pin == pin1) || (pin == pin2) )
  {
    if (pin == pin1)   //Compiler optimizes this excellent (see ASM listing)
    {
      if (val == LOW) 
      {
        *out1 &= ~bit1;
      } else 
      {
        *out1 |= bit1; 
      } 
    }
    else
    if (pin == pin2)
    {
      if (val == LOW) 
      {
        *out2 &= ~bit2;
      } else 
      {
        *out2 |= bit2; 
      }   
    }

    SREG = oldSREG;    
  }
  else  //normal clumsy digitalWrite operation
  {
    SREG = oldSREG;   //Enable interrupts again
    timer = digitalPinToTimer(pin);
    bit = digitalPinToBitMask(pin);
    port = digitalPinToPort(pin);
    if (port == NOT_A_PIN) return;

    // If the pin that support PWM output, we need to turn it off
    // before doing a digital write.
//    if (timer != NOT_ON_TIMER) turnOffPWM(timer);

    out = portOutputRegister(port);

    oldSREG = SREG;
    cli();

    if (val == LOW) {
      *out &= ~bit;
    } else {
      *out |= bit;
    }

    if (p1)        // Save this port, bit and out, also atomic
    {
      pin1  = pin;
      bit1  = bit;
      out1  = out;  // save the pointer, not the value    
    }
    else
    {
      pin2  = pin;
      bit2  = bit;
      out2  = out;  // save the pointer, not the value    
    }
    p1 = !p1;
    SREG = oldSREG; //enable interrupts
  }
}

//**********************
int digitalRead(uint8_t pin)
//**********************
{

uint8_t 
  oldSREG,
  timer,
  bit,
  port;

static uint8_t pin1 = 0;

bool           readBit;

static uint8_t // Store 2 sets of port/out adresses static

//          timer1, //now exclued. Safety threat?
              port1,
              bit1;

  oldSREG = SREG;
  cli();
  if (pin == pin1)
  {
    readBit = (*portInputRegister(port1) & bit1);
    SREG = oldSREG;
    return readBit;           
  }
  else
  {
    SREG = oldSREG;  
    timer = digitalPinToTimer(pin);
    bit = digitalPinToBitMask(pin);
    port = digitalPinToPort(pin);

    if (port == NOT_A_PIN) return LOW;

    // If the pin that support PWM output, we need to turn it off
    // before getting a digital reading.
  //  if (timer != NOT_ON_TIMER) turnOffPWM(timer);

    oldSREG = SREG;
    cli();
    pin1 = pin;       //Atomic operation pin - bit combi must be correct
    bit1 = bit;
    port1 = port;
    SREG = oldSREG;

    if (*portInputRegister(port) & bit) return HIGH;
    return LOW;
  }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.