Класи C ++ для абстракції вводу-виводу


13

Я шукаю C ++ абстракції для апаратних точок вводу / виводу або штифтів. Такі речі, як in_pin, out_pin, inout_pin, можливо open_collector_pin тощо.

Я, безумовно, сам можу придумати такий набір абстракцій, тому я не шукаю відповідей типу "ей, ти можеш зробити це так", а швидше "погляду на цю бібліотеку, яка була використана в цьому і цьому та цей проект '.

Google нічого не з'явив, можливо, тому, що я не знаю, як це називали б інші.

Моя мета - створити бібліотеки вводу-виводу, які базуються на таких точках, але також надати такі точки, тому було б легко, наприклад, підключити HD44780 LCd або до IO-штифтів чіпа, або до I2C (або SPI) Розширювач вводу / виводу або будь-яка інша точка, якою можна якось керувати, без будь-яких змін до класу РК.

Я знаю, що це на межі електроніки / програмного забезпечення, вибачте, якщо воно тут не належить.

@leon: проводка Це великий пакет програмного забезпечення, мені потрібно буде придивитись ближче. Але, здається, вони не використовують абстракцію штифтів, як я хочу. Наприклад, в реалізації клавіатури, яку я бачу

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

Це означає, що є одна функція (digitalWrite), яка вміє писати на введення-виводу. Це унеможливлює додавання нового виду вводу-виводу (наприклад, такого, який знаходиться на MCP23017, тому він повинен бути записаний через I2C), не переписуючи функцію digitalWrite.

@Oli: Я переглянув приклад IO Arduino, але, здається, використовую приблизно той самий підхід, що і бібліотека Wiring:

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

Про який мікроконтролер ми говоримо тут?
Маєнко

Це не має значення; для конкретного мікроконтролера, іоні цього UC будуть реалізовувати відповідні інтерфейси. Але це для C ++, тому подумайте про 32-бітні мікросхеми, такі як ARM, Cortex та MIPS.
Wouter van Ooijen

1
Я ніколи не використовував його, але хіба Ардуїно не абстрагує всі шпильки, як це? Ви можете (або не можете) отримати корисну інформацію, дивлячись на те, як вони робили.
Олі Глазер

1
А що стосується переписування функції digitalWrite - подивіться на "перевантаження" в C ++. Я лише кілька моментів тому написав перевантажену функцію digitalWrite для плати розширення IO для Arduino. Поки ви використовуєте різні параметри (я замінив перший "int" на "struct"), він буде вибирати ваш цифровий запис на відміну від типового.
Majenko

1
Я говорив на зустрічі C ++ у Берліні про свою роботу з цього приводу. Його можна знайти на youtube: youtube.com/watch?v=k8sRQMx2qUw З тих пір я перейшов на дещо інший підхід, але розмова може все ще бути цікавою.
Wouter van Ooijen

Відповіді:


3

Коротка відповідь: на жаль, не існує бібліотеки, яка б робила те, що ти хочеш. Я робив це сам неодноразово, але завжди у проектах без відкритого джерела. Я роздумую щось поставити на github, але я не впевнений, коли зможу.

Чому C ++?

  1. Компілятор може використовувати динамічну оцінку вираження розміру слова. C поширюється на int. Вашу байт-маску / зсув можна зробити швидше / менше.
  2. Вкладиш.
  3. Операції з шаблонуванням дозволяють змінювати розмір слова та інші властивості, захищаючи тип.

5

Дозвольте мені безсоромно підключити мій проект з відкритим кодом https://Kvasir.io . Розділ Kvasir :: Io забезпечує функцію маніпулювання штифтами. Спочатку слід визначити свій штифт за допомогою Kvasir :: Io :: PinLocation так:

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

Зауважте, що це насправді не використовує оперативну пам'ять, оскільки це змінні constexpr.

У всьому коді ви можете використовувати ці штифтові місця у функціях "фабрики дій", таких як makeOpenDrain, set, clear, makeOutput тощо. Фабрика дій фактично не виконує дію, скоріше вона повертає Kvasir :: Реєстр :: Дія, яку можна виконати за допомогою Kvasir :: Реєстрація :: apply (). Причиною цього є те, що apply () об'єднує дії, передані йому, коли вони діють на одному і тому ж реєстрі, так що є підвищення ефективності.

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

Оскільки створення та об'єднання дій відбувається під час компіляції, це має отримати той же код асемблера, що і типовий кодований еквівалент рукою:

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

3

Проект проводки використовує абстракцію таким чином:

http://wiring.org.co/

а компілятор написаний на C ++. Ви повинні знайти безліч прикладів у вихідному коді. Програмне забезпечення Arduino засноване на проводці.


відповів в органі запитання
Wouter van Ooijen

2

У C ++ можна записати клас, щоб ви могли використовувати порти вводу / виводу так, ніби вони були змінними, наприклад

  ПОРТБ = 0х12; / * Запис у 8-бітний порт * /
  якщо (RB3) LATB4 = 1; / * Прочитайте один біт вводу-виводу та умовно напишіть інший * /

без урахування основної реалізації. Наприклад, якщо використовується апаратна платформа, яка не підтримує операції на рівні бітів, але підтримує операції реєстру байтового рівня, можна (можливо, за допомогою деяких макросів) визначити статичний клас IO_PORTS з вбудованим читанням-записом властивості, що називаються bbRB3 та bbLATB4, таким чином, що остання заява вище перетвориться на

  якщо (IO_PORTS.bbRB3) IO_PORTS.bbLATB4 = 1;

що в свою чергу переробляється на щось на зразок:

  якщо (!! (PORTB & 8)) (1? (PORTB | = 16): (PORTB & = ~ 16));

Компілятор повинен мати можливість помітити постійний вираз в операторі?: Та просто включити "справжню" частину. Можна зменшити кількість створених властивостей шляхом розширення макросів на щось на зразок:

  якщо (IO_PORTS.ppPORTB [3]) IO_PORTS.ppPORTB [4] = 1;

або

  якщо (IO_PORTS.bb (addrPORTB, 3)) IO_PORTS.bbPORTB (addrPORTB, 4) = 1;

але я не впевнений, що компілятор зміг би ввімкнути код так добре.

Я ні в якому разі не хочу натякати на те, що використання портів вводу-виводу, як ніби вони є змінними, - це обов'язково хороша ідея, але оскільки ви згадуєте C ++, це корисний трюк. Моє власне уподобання в C або C ++, якщо сумісність з кодом, який використовує вищезгаданий стиль, не потрібна, можливо, було б визначити якийсь тип макросу для кожного біта вводу-виводу, а потім визначити макроси для "readBit", "writeBit", "setBit" і "clearBit" за умови, що аргумент, що ідентифікує біт, переданий цим макросам, повинен мати ім'я порту вводу / виводу, призначеного для використання з такими макросами. Наведений вище приклад, наприклад, буде записаний як

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

і перекладено як

  якщо (!! (_ PORT_RB3 & _BITMASK_RB3)) _PORT_LATB4 | = _BITMASK_LATB4;

Це буде трохи більше роботи для препроцесора, ніж стиль C ++, але для компілятора буде менше роботи. Це також дозволило б оптимізувати генерацію коду для багатьох реалізацій вводу-виводу та пристойну реалізацію коду майже для всіх.


3
Цитата з питання: "Я не шукаю" ей, ти можеш зробити це так "тип відповідей" ...
Wouter van Ooijen

Я думаю, мені не зовсім зрозуміло, що ти шукаєш. Звичайно, я б очікував, що багатьом людям, які цікавляться заняттями з реконструкції контактів вводу-виводу, також буде цікаво знати, що за допомогою властивостей можна зробити код, написаний для одного стилю вводу-виводу, використовувати майже все інше. Я використовував властивості, щоб зробити заяву типу "LATB3 = 1;" надіслати запит вводу / виводу потоку TCP.
supercat

Я намагався бути зрозумілим у своєму запитанні: я хочу мати можливість розміщувати нові типи штифтів IO, не переписуючи код, який використовує штифти IO. Ви пишете про визначені користувачем типи перетворень та операторів присвоєння, що, безумовно, цікаво, я постійно їх використовую, але це не є рішенням моєї проблеми.
Wouter van Ooijen

@Wouter van Ooijen: Які "нові типи шпильок вводу / виводу" ви б передбачили? Якщо вихідний код написаний із синтаксисом на кшталт "if (BUTTON_PRESSED) MOTOR_OUT = 1;", я б очікував, що майже для будь-якого механізму, за допомогою якого процесор може прочитати управління кнопками або двигун, може написати бібліотеку, так що вищенаведене джерело код увімкне мотор, якщо натиснути кнопку. Така бібліотека може не представляти собою найефективніший спосіб включення мотора, але це має працювати.
supercat

@Wouter van Ooijen: Можна, можливо, підвищити ефективність, якщо потрібно, щоб вихідний код викликав макрос UPDATE_IO () або UPDATE_INPUTS () десь перед тим, як прочитати будь-який ввід, і виконати UPDATE_IO () або UPDATE_OUTPUTS () деякий час після будь-якого виводу, за допомогою семантика, яку можна ввести в вибірку або за кодом, який їх читає, або за попереднім викликом UPDATE_INPUTS () / UPDATE_IO (). Так само виходи можуть або відбуватися негайно, або бути відкладеними. Якщо введення / виведення реалізовано за допомогою чогось подібного реєстру зрушень, дії відстрочки дозволять консолідувати кілька операцій.
supercat

1

Якщо ви шукаєте щось по-справжньому приголомшливо для того, щоб абстрагувати апаратне забезпечення, і ви впевнені у своїх навичках C ++, то варто спробувати цю схему:

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

Я використовував це в одній спробі абстрагувати апаратне забезпечення для мікросхема Cortex-M0. Я ще нічого не писав про цей досвід (зроблю це колись), але повірте, він був дуже корисним через його статичну поліморфність: той самий метод для різних фішок, без будь-яких витрат (порівняно з динамічним поліморфізмом).


За роки, що минули з цієї посади, я зупинився на окремих "класах" для pin_in, pin_out, pin_oc та pin_in_out. Для оптимальної продуктивності (розміру та швидкості) я використовую статичні класи, передані як параметри шаблону. Я говорив про це на зустрічі C ++ у Берліні
Wouter van Ooijen
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.