Kelas C ++ untuk abstraksi pin I / O

13

Saya mencari abstraksi C ++ untuk perangkat keras I / O poin atau pin. Hal-hal seperti in_pin, out_pin, inout_pin, mungkin open_collector_pin, dll.

Saya pasti bisa membuat serangkaian abstraksi sendiri, jadi saya tidak mencari jenis jawaban 'hei, Anda bisa melakukannya dengan cara ini', tetapi 'lihat perpustakaan ini yang telah digunakan dalam ini dan ini dan proyek ini'.

Google tidak menemukan apa pun, mungkin karena saya tidak tahu bagaimana orang lain menyebutnya.

Tujuan saya adalah untuk membangun perpustakaan I / O yang didasarkan pada titik-titik tersebut, tetapi juga menyediakan titik-titik tersebut, sehingga akan mudah untuk misalnya menghubungkan HD44780 LCd ke pin IO chip, atau I2C (atau SPI) I / O extender, atau titik lain yang entah bagaimana dapat dikontrol, tanpa mengubah kelas LCD.

Saya tahu ini ada di tepi elektronik / perangkat lunak, maaf kalau bukan milik di sini.

@leon: wiring Itu adalah sekumpulan besar perangkat lunak, saya perlu melihat lebih dekat. Tapi sepertinya mereka tidak menggunakan pin abstraksi seperti yang saya inginkan. Misalnya dalam implementasi keypad saya lihat

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

Ini menyiratkan bahwa ada satu fungsi (digitalWrite) yang tahu bagaimana menulis ke pin I / O. Ini membuatnya tidak mungkin untuk menambahkan tipe baru I / O pin (misalnya yang ada di MCP23017, jadi harus ditulis melalui I2C) tanpa menulis ulang fungsi digitalWrite.

@Oli: Saya mencari contoh Arduino IO di Google, tetapi tampaknya menggunakan pendekatan yang sama dengan pustaka Wiring:

int ledPin = 13;                 // LED connected to digital pin 13
void setup(){
    pinMode(ledPin, OUTPUT);      // sets the digital pin as output
}
Wouter van Ooijen
sumber
Mikrokontroler apa yang kita bicarakan di sini?
Majenko
Itu tidak relevan; untuk mikrokontroler tertentu pin io dari uC itu akan mengimplementasikan antarmuka yang sesuai. Tapi ini untuk C ++, jadi pikirkan chip 32-bit seperti ARM, Cortex dan MIPS.
Wouter van Ooijen
1
Saya tidak pernah menggunakan satu, tetapi tidak Arduino abstrak semua pin seperti ini? Anda mungkin (atau mungkin tidak) mendapatkan beberapa info yang berguna dengan melihat cara mereka melakukan sesuatu.
Oli Glaser
1
Dan untuk menulis ulang fungsi digitalWrite - lihat "overloading" di C ++. Saya baru saja menulis fungsi digitalWrite yang kelebihan beban untuk papan expander IO untuk Arduino. Selama Anda menggunakan parameter yang berbeda (saya mengganti "int" pertama dengan "struct"), itu akan memilih digitalWrite Anda daripada preferensi yang standar.
Majenko
1
Saya melakukan pembicaraan pada pertemuan C ++ di Berlin tentang pekerjaan saya tentang hal ini. Itu dapat ditemukan di youtube: youtube.com/watch?v=k8sRQMx2qUw Sejak itu saya beralih ke pendekatan yang sedikit berbeda, tetapi pembicaraannya mungkin masih menarik.
Wouter van Ooijen

Jawaban:

3

Jawaban singkat: sayangnya, tidak ada perpustakaan untuk melakukan apa yang Anda inginkan. Saya sudah melakukannya sendiri berkali-kali tetapi selalu dalam proyek-proyek non-sumber terbuka. Saya sedang mempertimbangkan meletakkan sesuatu di github tetapi saya tidak yakin kapan saya bisa.

Kenapa C ++?

  1. Kompiler bebas menggunakan evaluasi ekspresi ukuran kata yang dinamis. C merambat ke int. Masker byte Anda / shift dapat dilakukan lebih cepat / lebih kecil.
  2. Sebaris.
  3. Operasi templatizing memungkinkan Anda memvariasikan ukuran kata dan properti lainnya, dengan keamanan jenis.
Jim Frimmel
sumber
5

Izinkan saya untuk tanpa malu menyambungkan proyek open source saya https://Kvasir.io . Bagian Kvasir :: Io menyediakan fungsi manipulasi pin. Anda harus terlebih dahulu menentukan pin Anda menggunakan Kvasir :: Io :: PinLocation seperti:

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

Perhatikan bahwa ini sebenarnya tidak menggunakan RAM karena ini adalah variabel constexpr.

Sepanjang kode Anda, Anda dapat menggunakan lokasi pin ini dalam fungsi 'pabrik tindakan' seperti makeOpenDrain, set, clear, makeOutput, dan sebagainya. 'Pabrik tindakan' tidak benar-benar menjalankan tindakan, melainkan mengembalikan Kvasir :: Register :: Action yang dapat dieksekusi menggunakan Kvasir :: Register :: apply (). Alasan untuk ini adalah bahwa berlaku () menggabungkan tindakan yang diteruskan ketika mereka bertindak pada satu dan register yang sama sehingga ada keuntungan efisiensi.

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

Karena pembuatan dan penggabungan tindakan dilakukan pada waktu kompilasi, ini harus menghasilkan kode assembler yang sama dengan kode tangan khas yang setara:

PORT0DIR |= (1<<4) | (1<<8);
PORT0OD |= (1<<4) | (1<<8);
odinthenerd
sumber
3

Proyek Pengkabelan menggunakan abstraksi seperti itu:

http://wiring.org.co/

dan kompiler ditulis dalam C ++. Anda harus menemukan banyak contoh dalam kode sumber. Perangkat lunak Arduino didasarkan pada Pengkabelan.

Leon Heller
sumber
dijawab dalam badan pertanyaan
Wouter van Ooijen
2

Dalam C ++, dimungkinkan untuk menulis kelas sehingga Anda dapat menggunakan port I / O seolah-olah mereka variabel, misalnya

  PORTB = 0x12; / * Menulis ke port 8-bit * /
  if (RB3) LATB4 = 1; / * Baca satu I / O sedikit dan kondisikan menulis lain * /

tanpa memperhatikan implementasi yang mendasarinya. Sebagai contoh, jika seseorang menggunakan platform perangkat keras yang tidak mendukung operasi bit-level tetapi mendukung operasi register byte-level, seseorang dapat (mungkin dengan bantuan beberapa makro) mendefinisikan kelas statis IO_PORTS dengan inline read-write properti yang disebut bbRB3 dan bbLATB4, sehingga pernyataan terakhir di atas akan berubah menjadi

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

yang pada gilirannya akan diproses menjadi sesuatu seperti:

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

Kompiler harus dapat melihat ekspresi konstan dalam operator?: Dan cukup menyertakan bagian "true". Dimungkinkan untuk mengurangi jumlah properti yang dibuat dengan membuat makro diperluas menjadi seperti:

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

atau

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

tapi saya tidak yakin kompiler akan dapat in-line kodenya juga.

Saya sama sekali tidak ingin menyiratkan bahwa menggunakan port I / O seolah-olah mereka adalah variabel tentu merupakan ide yang baik, tetapi karena Anda menyebutkan C ++ itu adalah trik yang berguna untuk diketahui. Preferensi saya sendiri dalam C atau C ++, jika kompatibilitas dengan kode yang menggunakan gaya tersebut tidak diperlukan, mungkin akan mendefinisikan beberapa jenis makro untuk setiap bit I / O, dan kemudian mendefinisikan makro untuk "readBit", "writeBit", "setBit", dan "clearBit" dengan ketentuan bahwa argumen pengidentifikasi bit yang diteruskan ke makro tersebut haruslah nama port I / O yang dimaksudkan untuk digunakan dengan makro tersebut. Contoh di atas, misalnya, akan ditulis sebagai

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

dan diterjemahkan sebagai

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

Itu akan menjadi pekerjaan yang sedikit lebih untuk preprocessor daripada gaya C ++, tetapi akan lebih sedikit pekerjaan untuk kompiler. Itu juga akan memungkinkan pembuatan kode optimal untuk banyak implementasi I / O, dan implementasi kode yang layak untuk hampir semua.

supercat
sumber
3
Kutipan dari pertanyaan: "Saya tidak mencari tipe jawaban 'hei, Anda bisa melakukannya'" ...
Wouter van Ooijen
Saya kira saya tidak begitu jelas apa yang Anda cari. Tentu saja saya akan berharap bahwa banyak orang yang tertarik pada kelas untuk rekonstruksi pin I / O juga akan tertarik untuk mengetahui bahwa menggunakan properti seseorang dapat membuat kode yang ditulis untuk satu gaya I / O menggunakan hampir semua hal lain. Saya telah menggunakan properti untuk membuat pernyataan seperti "LATB3 = 1;" mengirimkan permintaan I / O ke aliran TCP.
supercat
Saya mencoba menjelaskan pertanyaan saya: Saya ingin dapat mengakomodasi tipe baru pin IO tanpa menulis ulang kode yang menggunakan pin IO. Anda menulis tentang konversi jenis yang ditentukan pengguna dan operator penugasan, yang pasti menarik, saya menggunakannya setiap saat, tetapi bukan solusi untuk masalah saya.
Wouter van Ooijen
@Wouter van Ooijen: "Jenis baru pin I / O" apa yang akan Anda antisipasi? Jika kode sumber ditulis dengan sintaksis seperti "jika (BUTTON_PRESSED) MOTOR_OUT = 1;", saya akan berharap bahwa untuk hampir semua mekanisme yang dengannya prosesor dapat membaca tombol kontrol atau motor yang dapat menulis perpustakaan sehingga sumber di atas kode akan menghidupkan motor jika tombol ditekan. Perpustakaan seperti itu mungkin tidak mewakili cara yang paling efisien untuk menyalakan motor, tetapi itu harus bekerja.
supercat
@Wouter van Ooijen: Seseorang mungkin dapat meningkatkan efisiensi jika seseorang mengharuskan kode sumber meminta UPDATE_IO () atau makro UPDATE_INPUTS () beberapa saat sebelum membaca input apa pun, dan melakukan UPDATE_IO () atau UPDATE_OUTPUTS () beberapa saat setelah output apa pun, dengan semantik yang input dapat disampel baik pada kode yang membacanya, atau pada permintaan UPDATE_INPUTS () / UPDATE_IO () sebelumnya. Demikian juga output dapat terjadi segera atau ditangguhkan. Jika I / O diimplementasikan menggunakan sesuatu seperti register geser, menunda tindakan akan memungkinkan beberapa operasi dikonsolidasikan.
supercat
1

Jika Anda mencari sesuatu yang benar-benar mengagumkan untuk mengabstraksikan perangkat keras, dan Anda yakin dengan keterampilan C ++ Anda, maka Anda harus mencoba pola ini:

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

Saya telah menggunakannya dalam satu upaya untuk abstrak perangkat keras untuk chip Cortex-M0. Saya belum menulis apa-apa tentang pengalaman ini (saya akan melakukannya suatu hari nanti), tetapi percayalah itu sangat berguna karena sifat polimorfik statisnya: metode yang sama untuk chip yang berbeda, tanpa biaya (dibandingkan dengan polimorfisme dinamis).

Francisco Rodríguez
sumber
Pada tahun-tahun sejak posting ini saya stettled pada "kelas" yang terpisah untuk pin_in, pin_out, pin_oc dan pin_in_out. Untuk kinerja optimal (ukuran dan kecepatan) saya menggunakan kelas statis, yang dilewatkan sebagai parameter templat. Saya membicarakan hal ini di Pertemuan C ++ di Berlin
Wouter van Ooijen