Sambil melihat sketsa yang ditulis orang lain, saya terkadang menemukan kode yang terlihat seperti ini:
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 34286;
TCCR1B |= (1 << CS12);
TIMSK1 |= (1 << TOIE1);
Yang saya tahu adalah bahwa ada sesuatu dengan timing / timer (saya pikir). Bagaimana saya bisa menguraikan — dan membuat — kode seperti ini? Apa TCCR1A
, TCCR1B
, TCNT1
, CS12
, TIMSK1
, dan TOIE1
?
timers
programming
The Guy with The Hat
sumber
sumber
Jawaban:
Ini tidak terlihat aneh. Seperti apa sebenarnya kode MCU yang sebenarnya.
Apa yang Anda miliki di sini adalah contoh konsep perangkat yang dipetakan dengan memori . Pada dasarnya, perangkat keras MCU memiliki lokasi khusus di ruang alamat SRAM MCU yang ditetapkan untuknya. Jika Anda menulis ke alamat-alamat ini, bit-bit byte yang ditulis untuk mengatasi dan mengontrol perilaku periferal m .
Pada dasarnya, bank memori tertentu secara harfiah memiliki sedikit kabel yang berjalan dari sel SRAM ke perangkat keras. Jika Anda menulis "1" untuk bit ini dalam byte itu, itu mengatur sel SRAM ini ke logis tinggi, yang kemudian menghidupkan beberapa bagian dari perangkat keras.
Jika Anda melihat ke header untuk MCU, ada tabel besar kata kunci <-> pemetaan alamat. Ini adalah bagaimana hal-hal seperti
TCCR1B
dll ... diselesaikan pada waktu kompilasi.Mekanisme pemetaan memori ini sangat luas digunakan dalam MCU. ATmega MCU di arduino menggunakannya, seperti halnya PIC, ARM, MSP430, STM32 dan STM8 seri MCU, serta banyak MCU yang tidak segera saya kenal.
Kode Arduino adalah hal yang aneh, dengan fungsi yang mengakses register kontrol MCU secara tidak langsung. Walaupun ini terlihat "lebih bagus", tetapi juga lebih lambat, dan menggunakan lebih banyak ruang program.
Konstanta misterius semuanya dijelaskan dengan sangat rinci dalam lembar data ATmega328P , yang harus Anda baca jika Anda tertarik untuk melakukan sesuatu lebih dari sesekali mengaktifkan pin pada arduino.
Pilih kutipan dari lembar data yang ditautkan di atas:
Jadi, misalnya,
TIMSK1 |= (1 << TOIE1);
menetapkan bitTOIE1
diTIMSK1
. Ini dicapai dengan menggeser biner 1 (0b00000001
) ke kiri olehTOIE1
bit, denganTOIE1
didefinisikan dalam file header sebagai 0. Ini kemudian bitwise ORed ke nilai saat iniTIMSK1
, yang secara efektif mengatur bit ini tinggi.Melihat dokumentasi untuk bit 0 of
TIMSK1
, kita dapat melihatnya digambarkan sebagaiSemua baris lainnya harus ditafsirkan dengan cara yang sama.
Beberapa catatan:
Anda mungkin juga melihat hal-hal seperti
TIMSK1 |= _BV(TOIE1);
._BV()
adalah makro yang umum digunakan berasal dari implementasi libc AVR ._BV(TOIE1)
secara fungsional identik dengan(1 << TOIE1)
, dengan manfaat keterbacaan yang lebih baik.Anda juga dapat melihat garis-garis seperti:
TIMSK1 &= ~(1 << TOIE1);
atauTIMSK1 &= ~_BV(TOIE1);
. Ini memiliki fungsi kebalikan dariTIMSK1 |= _BV(TOIE1);
, dalam hal ini unsets sedikitTOIE1
diTIMSK1
. Ini dicapai dengan mengambil bit-mask yang dihasilkan oleh_BV(TOIE1)
, melakukan operasi bitwise NOT di atasnya (~
), dan kemudianTIMSK1
ANDing dengan nilai NOTed ini (yaitu 0b11111110).Perhatikan bahwa dalam semua kasus ini, nilai hal-hal seperti
(1 << TOIE1)
atau_BV(TOIE1)
sepenuhnya diselesaikan pada waktu kompilasi , sehingga secara fungsional berkurang menjadi konstanta sederhana, dan oleh karena itu tidak memerlukan waktu eksekusi untuk menghitung pada saat runtime.Kode yang ditulis dengan benar umumnya akan memiliki komentar sesuai dengan kode yang merinci apa yang ditugaskan register untuk dilakukan. Ini adalah soft-SPI rutin yang cukup sederhana yang saya tulis baru-baru ini:
PORTC
adalah register yang mengontrol nilai pin output dalamPORTC
ATmega328P.PINC
adalah register di mana masukan nilai-nilaiPORTC
yang tersedia. Pada dasarnya, hal-hal seperti ini adalah apa yang terjadi secara internal ketika Anda menggunakandigitalWrite
ataudigitalRead
fungsinya. Namun, ada operasi pencarian yang mengubah "nomor pin" Arduino menjadi nomor pin perangkat keras yang sebenarnya, yang mengambil tempat dalam ranah siklus 50 jam. Seperti yang mungkin bisa Anda tebak, jika Anda mencoba melaju kencang, membuang 50 siklus clock pada operasi yang hanya membutuhkan 1 adalah sedikit konyol.Fungsi di atas mungkin membutuhkan tempat di ranah siklus 100-200 jam untuk mentransfer 8 bit. Ini mencakup 24 pin-menulis, dan 8 membaca. Ini banyak, berkali-kali lebih cepat daripada menggunakan
digital{stuff}
fungsi.sumber
TCCR1A
adalah timer / penghitung 1 register kontrol ATCCR1B
adalah timer / penghitung 1 register kontrol BTCNT1
adalah nilai penghitung waktu / penghitung 1CS12
adalah bit pilih jam ke-3 untuk timer / penghitung 1TIMSK1
adalah register topeng interupsi timer / counter 1TOIE1
apakah timer / counter 1 overflow interrupt diaktifkanJadi, kode ini mengaktifkan timer / penghitung 1 pada 62,5 kHz dan menetapkan nilai ke 34286. Kemudian memungkinkan overflow interupsi sehingga ketika mencapai 65535, itu akan memicu fungsi interupsi, kemungkinan besar dilabeli sebagai
ISR(timer0_overflow_vect)
sumber
CS12 memiliki nilai 2 karena mewakili bit 2 dari register TCCR1B.
(1 << CS12) mengambil nilai 1 (0b00000001) dan menggesernya 2 kali untuk mendapatkan (0b00000100). Urutan operasi menentukan bahwa hal-hal dalam () terjadi terlebih dahulu, jadi ini dilakukan sebelum "| =" dievaluasi.
(1 << CS10) mengambil nilai 1 (0b00000001) dan menggesernya 0 kali untuk mendapatkan (0b00000001). Urutan operasi menentukan bahwa hal-hal dalam () terjadi terlebih dahulu, jadi ini dilakukan sebelum "| =" dievaluasi.
Jadi sekarang kita mendapatkan TCCR1B | = 0b00000101, yang sama dengan TCCR1B = TCCR1B | 0b00000101.
Sejak "|" adalah "ATAU", semua bit selain CS12 di TCCR1B tidak terpengaruh.
sumber