Các lớp C ++ cho trừu tượng pin I / O


13

Tôi đang tìm kiếm trừu tượng C ++ cho các điểm I / O phần cứng hoặc chân. Những thứ như in_pin, out_pin, inout_pin, có thể là open_collector_pin, v.v.

Tôi chắc chắn có thể tự mình đưa ra một bộ trừu tượng như vậy, vì vậy tôi không tìm kiếm 'này, bạn có thể làm theo cách này', mà là 'nhìn vào thư viện đã được sử dụng trong này và này dự án này'.

Google không bật lên bất cứ điều gì, có lẽ vì tôi không biết người khác sẽ gọi nó như thế nào.

Mục đích của tôi là xây dựng các thư viện I / O dựa trên các điểm như vậy, nhưng cũng cung cấp các điểm như vậy, vì vậy có thể dễ dàng kết nối HD44780 LCd với các chân IO của chip hoặc I2C (hoặc SPI) Bộ mở rộng I / O hoặc bất kỳ điểm nào khác có thể được kiểm soát bằng cách nào đó mà không có bất kỳ thay đổi nào đối với lớp LCD.

Tôi biết điều này nằm ở cạnh điện tử / phần mềm, xin lỗi nếu nó không thuộc về nơi này.

@leon: hệ thống dây điện Đó là một túi phần mềm lớn, tôi sẽ cần nhìn gần hơn. Nhưng có vẻ như họ không sử dụng trừu tượng pin như tôi muốn. Ví dụ trong việc thực hiện bàn phím tôi thấy

digitalWrite(columnPins[c], LOW);   // Activate the current column.

Điều này ngụ ý rằng có một chức năng (digitalWrite) biết cách ghi vào chân I / O. Điều này làm cho không thể thêm một loại pin I / O mới (ví dụ như pin trên MCP23017, do đó nó phải được ghi qua I2C) mà không cần viết lại hàm digitalWrite.

@Oli: Tôi đã lấy một ví dụ về Arduino IO, nhưng dường như sử dụng cách tiếp cận tương tự như thư viện Wires:

int ledPin = 13;                 // LED connected to digital pin 13
void setup(){
    pinMode(ledPin, OUTPUT);      // sets the digital pin as output
}

Chúng ta đang nói về vi điều khiển nào?
Majenko

Điều đó không liên quan; đối với một vi điều khiển cụ thể, các chân io của uC đó sẽ thực hiện các giao diện thích hợp. Nhưng đây là dành cho C ++, vì vậy hãy nghĩ đến các chip 32 bit như ARM, Cortex và MIPS.
Wouter van Ooijen

1
Tôi chưa bao giờ sử dụng một, nhưng Arduino không trừu tượng tất cả các chân như thế này? Bạn có thể (hoặc có thể không) có được một số thông tin hữu ích khi nhìn vào cách họ đã làm mọi việc.
Oli Glaser

1
Và như để viết lại hàm digitalWrite - hãy xem "nạp chồng" trong C ++. Tôi vừa mới viết một chức năng DigitalWrite quá tải cho một bảng mở rộng IO cho Arduino. Miễn là bạn sử dụng các tham số khác nhau (tôi đã thay thế "int" đầu tiên bằng "struct"), nó sẽ chọn digitalWrite của bạn theo sở thích thành tham số mặc định.
Majenko

1
Tôi đã nói chuyện về việc gặp C ++ ở Berlin về công việc của tôi về chủ đề này. Nó có thể được tìm thấy trên youtube: youtube.com/watch?v=k8sRQMx2qUw Kể từ đó tôi đã chuyển sang một cách tiếp cận hơi khác, nhưng cuộc nói chuyện vẫn có thể thú vị.
Wouter van Ooijen

Câu trả lời:


3

Câu trả lời ngắn gọn: thật đáng buồn, không có thư viện để làm những gì bạn muốn. Tôi đã thực hiện nó nhiều lần nhưng luôn trong các dự án không nguồn mở. Tôi đang xem xét đưa một cái gì đó lên github nhưng tôi không chắc khi nào tôi có thể.

Tại sao lại là C ++?

  1. Trình biên dịch là miễn phí để sử dụng đánh giá biểu thức kích thước từ động. C truyền đến int. Mặt nạ / dịch chuyển byte của bạn có thể được thực hiện nhanh hơn / nhỏ hơn.
  2. Nội tuyến.
  3. Hoạt động tạo khuôn mẫu cho phép bạn thay đổi kích thước từ và các thuộc tính khác, với loại an toàn.

5

Cho phép tôi biết xấu hổ cắm dự án nguồn mở https://Kvasir.io của mình . Phần Kvasir :: Io cung cấp các chức năng thao tác pin. Trước tiên, bạn phải xác định mã pin của mình bằng Kvasir :: Io :: PinLocation như vậy:

constexpr PinLocation<0,4> led1;    //port 0 pin 4
constexpr PinLOcation<0,8> led2;

Lưu ý rằng điều này không thực sự sử dụng RAM vì đây là các biến constexpr.

Trong suốt mã của bạn, bạn có thể sử dụng các vị trí ghim này trong các chức năng của 'nhà máy hành động' như makeOpenDrain, thiết lập, xóa, makeOutput, v.v. Một 'nhà máy hành động' không thực sự thực thi hành động, thay vào đó, nó trả về Kvasir :: Đăng ký :: Hành động có thể được thực thi bằng Kvasir :: Đăng ký :: áp dụng (). Lý do cho điều này là áp dụng () hợp nhất các hành động được truyền cho nó khi chúng hành động trên một và cùng một thanh ghi để có được hiệu quả.

apply(makeOutput(led1),
    makeOutput(led2),
    makeOpenDrain(led1),
    makeOpenDrain(led2));

Vì việc tạo và hợp nhất các hành động được thực hiện tại thời điểm biên dịch, điều này sẽ mang lại mã trình biên dịch mã tương tự như mã hóa tay tương đương:

PORT0DIR |= (1<<4) | (1<<8);
PORT0OD |= (1<<4) | (1<<8);

3

Dự án Wires sử dụng sự trừu tượng như thế:

http://wires.org.co/

và trình biên dịch được viết bằng C ++. Bạn nên tìm thấy nhiều ví dụ trong mã nguồn. Phần mềm Arduino dựa trên hệ thống dây điện.


đã trả lời trong phần câu hỏi
Wouter van Ooijen

2

Trong C ++, có thể viết một lớp để bạn có thể sử dụng các cổng I / O như thể chúng là các biến, ví dụ:

  PORTB = 0x12; / * Ghi vào cổng 8 bit * /
  nếu (RB3) LATB4 = 1; / * Đọc một bit I / O và viết điều kiện khác * /

mà không quan tâm đến việc thực hiện cơ bản. Ví dụ: nếu một người đang sử dụng một nền tảng phần cứng không hỗ trợ các hoạt động ở mức bit nhưng không hỗ trợ các hoạt động của thanh ghi mức byte, thì người ta có thể (có thể với sự trợ giúp của một số macro) xác định một lớp tĩnh IO_PORTS với một ghi đọc nội tuyến các thuộc tính được gọi là bbRB3 và bbLATB4, sao cho câu lệnh cuối cùng ở trên sẽ biến thành

  if (IO_PORTS.bbRB3) IO_PORTS.bbLATB4 = 1;

lần lượt sẽ được xử lý thành một cái gì đó như:

  if (!! (PORTB & 8)) (1? (PORTB | = 16): (PORTB & = ~ 16));

Một trình biên dịch sẽ có thể nhận thấy biểu thức hằng trong toán tử ?: Và chỉ cần bao gồm phần "true". Có thể giảm số lượng thuộc tính được tạo bằng cách mở rộng macro thành một cái gì đó như:

  if (IO_PORTS.ppPORTB [3]) IO_PORTS.ppPORTB [4] = 1;

hoặc là

  if (IO_PORTS.bb (addrPORTB, 3)) IO_PORTS.bbPORTB (addrPORTB, 4) = 1;

nhưng tôi không chắc chắn một trình biên dịch sẽ có thể sắp xếp mã một cách độc đáo.

Tôi không có ý muốn ám chỉ rằng sử dụng các cổng I / O như thể chúng là các biến nhất thiết phải là một ý tưởng tốt, nhưng vì bạn đề cập đến C ++ nên đây là một mẹo hữu ích để biết. Sở thích riêng của tôi trong C hoặc C ++, nếu không yêu cầu khả năng tương thích với mã sử dụng kiểu đã nói ở trên, có thể là xác định một số loại macro cho mỗi bit I / O, sau đó xác định macro cho "readBit", "writeBit", "setBit" và "clearBit" với điều kiện là đối số nhận dạng bit được truyền cho các macro đó phải là tên của một cổng I / O được sử dụng cho các macro đó. Ví dụ trên, ví dụ, sẽ được viết là

  if (readBit (RB3)) setBit (LATB4);

và được dịch là

  if (!! (_ PORT_RB3 & _BITMASK_RB3)) _PORT_LATB4 | = _BITMASK_LATB4;

Đó sẽ là một công việc nhiều hơn cho bộ tiền xử lý so với kiểu C ++, nhưng nó sẽ làm việc ít hơn cho trình biên dịch. Nó cũng sẽ cho phép tạo mã tối ưu cho nhiều triển khai I / O và thực hiện mã tốt cho hầu hết tất cả.


3
Một câu trích dẫn từ câu hỏi: "Tôi không tìm kiếm 'này, bạn có thể làm theo cách này" loại câu trả lời "...
Wouter van Ooijen

Tôi đoán tôi không rõ ràng những gì bạn đang tìm kiếm. Chắc chắn tôi sẽ mong đợi rằng nhiều người quan tâm đến các lớp để tái cấu trúc pin I / O cũng sẽ thích thú khi biết rằng việc sử dụng các thuộc tính có thể tạo mã được viết cho một kiểu I / O sử dụng bất cứ thứ gì khác. Tôi đã sử dụng các thuộc tính để đưa ra tuyên bố như "LATB3 = 1;" gửi yêu cầu I / O đến luồng TCP.
supercat

Tôi đã cố gắng rõ ràng trong câu hỏi của mình: Tôi muốn có thể chứa các loại chân IO mới mà không cần viết lại mã sử dụng chân IO. Bạn viết về các toán tử chuyển đổi và chuyển đổi loại do người dùng định nghĩa, điều này chắc chắn rất thú vị, tôi sử dụng chúng mọi lúc, nhưng không phải là giải pháp cho vấn đề của tôi.
Wouter van Ooijen

@Wouter van Ooijen: "Loại chân I / O mới" nào bạn sẽ dự đoán? Nếu mã nguồn được viết với cú pháp như "if (BUTTON_PRESSED) MOTOR_OUT = 1;", tôi sẽ hy vọng rằng đối với bất kỳ cơ chế nào mà bộ xử lý có thể đọc điều khiển nút hoặc động cơ có thể viết thư viện để nguồn trên mã sẽ bật động cơ nếu nhấn nút. Một thư viện như vậy có thể không đại diện cho cách bật động cơ hiệu quả nhất, nhưng nó sẽ hoạt động.
supercat

@Wouter van Ooijen: Có lẽ người ta có thể cải thiện hiệu quả nếu người ta yêu cầu mã nguồn gọi macro UPDATE_IO () hoặc UPDATE_INPUTS () trước khi đọc bất kỳ đầu vào nào và thực hiện UPDATE_IO () hoặc UPDATE_OUTPUTS () một thời gian sau bất kỳ đầu ra nào, với các ngữ nghĩa mà các đầu vào có thể được lấy mẫu tại mã đọc chúng hoặc tại lệnh gọi UPDATE_INPUTS () / UPDATE_IO () trước đó. Tương tự đầu ra có thể xảy ra ngay lập tức hoặc bị hoãn lại. Nếu một I / O được triển khai bằng cách sử dụng một cái gì đó như một thanh ghi thay đổi, các hành động trì hoãn sẽ cho phép nhiều hoạt động được hợp nhất.
supercat

1

Nếu bạn đang tìm kiếm thứ gì đó thực sự tuyệt vời để trừu tượng hóa phần cứng và bạn tự tin vào các kỹ năng C ++ của mình, thì bạn nên thử mẫu này:

https://en.wikipedia.org/wiki/Cantlyly_recurring_template_potype

Tôi đã sử dụng nó trong một lần thử để trừu tượng phần cứng cho chip Cortex-M0. Tôi chưa viết bất cứ điều gì về trải nghiệm này (tôi sẽ thực hiện vào một ngày nào đó), nhưng tôi tin rằng nó rất hữu ích vì bản chất đa hình tĩnh của nó: cùng một phương pháp cho các chip khác nhau, miễn phí (so với đa hình động).


Trong những năm kể từ bài đăng này, tôi đã đặt ra các "lớp" riêng biệt cho pin_in, pin thừng, pin_oc và pin_in thừng. Để có hiệu suất tối ưu (kích thước và tốc độ) tôi sử dụng các lớp tĩnh, được truyền dưới dạng tham số mẫu. Tôi đã nói về điều này trong Cuộc họp C ++ ở Berlin
Wouter van Ooijen
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.