Microchip menulis catatan aplikasi tentang ini:
- AN734 tentang penerapan budak I2C
- AN735 tentang penerapan master I2C
- Ada juga AN736 yang lebih teoritis tentang pengaturan protokol jaringan untuk pemantauan lingkungan, tetapi tidak diperlukan untuk proyek ini.
Catatan aplikasi bekerja dengan ASM tetapi itu dapat porting ke C dengan mudah.
Kompiler C18 dan XC8 gratis dari Microchip memiliki fungsi I2C. Anda dapat membaca lebih lanjut tentang mereka di dokumentasi perpustakaan kompiler , bagian 2.4. Inilah beberapa info mulai cepat:
Pengaturan
Anda sudah memiliki kompiler C18 atau XC8 dari Microchip. Keduanya memiliki fungsi I2C bawaan. Untuk menggunakannya, Anda harus memasukkan i2c.h
:
#include i2c.h
Jika Anda ingin melihat kode sumbernya, Anda dapat menemukannya di sini:
- Header C18:
installation_path
/v
x.xx
/h/i2c.h
- Sumber C18:
installation_path
/v
x.xx
/src/pmc_common/i2c/
- Header XC8:
installation_path
/v
x.xx
/include/plib/i2c.h
- Sumber XC8:
installation_path
/v
x.xx
/sources/pic18/plib/i2c/
Dalam dokumentasi, Anda dapat menemukan di file mana dalam /i2c/
folder suatu fungsi berada.
Membuka koneksi
Jika Anda terbiasa dengan modul MSSP Microchip, Anda akan tahu mereka harus diinisialisasi terlebih dahulu. Anda dapat membuka koneksi I2C pada port MSSP menggunakan OpenI2C
fungsi ini. Inilah yang didefinisikan:
void OpenI2C (unsigned char sync_mode, unsigned char slew);
Dengan sync_mode
, Anda dapat memilih apakah perangkat itu master atau slave, dan, jika itu adalah slave, apakah itu harus menggunakan alamat 10-bit atau 7-bit. Sebagian besar waktu, 7-bit digunakan, terutama dalam aplikasi kecil. Opsi untuk sync_mode
adalah:
SLAVE_7
- Mode Slave, alamat 7-bit
SLAVE_10
- Mode Slave, alamat 10-bit
MASTER
- Mode Master
Dengan slew
, Anda dapat memilih apakah perangkat harus menggunakan laju perubahan tegangan. Lebih lanjut tentang apa yang ada di sini: Berapa laju perubahan tegangan untuk I2C?
Dua modul MSSP
Ada sesuatu yang istimewa tentang perangkat dengan dua modul MSSP, seperti PIC18F46K22 . Mereka memiliki dua set fungsi, satu untuk modul 1 dan satu untuk modul 2. Misalnya, alih-alih OpenI2C()
, mereka memiliki OpenI2C1()
dan openI2C2()
.
Oke, jadi Anda sudah mengatur semuanya dan membuka koneksi. Sekarang mari kita lakukan beberapa contoh:
Contohnya
Tuan tulis contoh
Jika Anda terbiasa dengan protokol I2C, Anda akan tahu urutan penulisan master khas terlihat seperti ini:
Master : START | ADDR+W | | DATA | | DATA | | ... | DATA | | STOP
Slave : | | ACK | | ACK | | ACK | ... | | ACK |
Pada awalnya, kami mengirim kondisi MULAI. Pertimbangkan ini mengangkat telepon. Kemudian, alamat dengan bit Tulis - memanggil nomor tersebut. Pada titik ini, budak dengan alamat yang dikirim tahu dia dipanggil. Dia mengirimkan Pengakuan ("Halo"). Sekarang, perangkat master dapat mengirim data - dia mulai berbicara. Dia mengirimkan jumlah byte. Setelah setiap byte, budak harus ACK data yang diterima ("ya, saya mendengar Anda"). Ketika perangkat master selesai berbicara, dia menutup dengan kondisi STOP.
Di C, urutan penulisan master akan terlihat seperti ini untuk master:
IdleI2C(); // Wait until the bus is idle
StartI2C(); // Send START condition
IdleI2C(); // Wait for the end of the START condition
WriteI2C( slave_address & 0xfe ); // Send address with R/W cleared for write
IdleI2C(); // Wait for ACK
WriteI2C( data[0] ); // Write first byte of data
IdleI2C(); // Wait for ACK
// ...
WriteI2C( data[n] ); // Write nth byte of data
IdleI2C(); // Wait for ACK
StopI2C(); // Hang up, send STOP condition
Tuan baca contoh
Urutan baca master sedikit berbeda dari urutan tulis:
Master : START | ADDR+R | | | ACK | | ACK | ... | | NACK | STOP
Slave : | | ACK | DATA | | DATA | | ... | DATA | |
Sekali lagi, master memulai panggilan dan memanggil nomor tersebut. Namun, dia sekarang ingin mendapatkan informasi. Budak pertama menjawab panggilan, kemudian mulai berbicara (mengirim data). Master mengakui setiap byte sampai ia memiliki informasi yang cukup. Kemudian ia mengirim Not-ACK dan menutup dengan kondisi STOP.
Dalam C, ini akan terlihat seperti ini untuk bagian master:
IdleI2C(); // Wait until the bus is idle
StartI2C(); // Send START condition
IdleI2C(); // Wait for the end of the START condition
WriteI2C( slave_address | 0x01 ); // Send address with R/W set for read
IdleI2C(); // Wait for ACK
data[0] = ReadI2C(); // Read first byte of data
AckI2C(); // Send ACK
// ...
data[n] = ReadI2C(); // Read nth byte of data
NotAckI2C(); // Send NACK
StopI2C(); // Hang up, send STOP condition
Kode budak
Untuk budak, yang terbaik adalah menggunakan Interrupt Service Routine atau ISR. Anda dapat mengatur mikrokontroler Anda untuk menerima interupsi ketika alamat Anda dipanggil. Dengan begitu Anda tidak perlu memeriksa bus terus-menerus.
Pertama, mari kita buat dasar-dasar untuk interupsi. Anda harus mengaktifkan interupsi, dan menambahkan ISR. Penting bahwa PIC18 memiliki dua tingkat interupsi: tinggi dan rendah. Kita akan mengatur I2C sebagai interupsi prioritas tinggi, karena itu sangat penting untuk membalas panggilan I2C. Apa yang akan kita lakukan adalah sebagai berikut:
- Tulis SSP ISR, karena ketika interupsi adalah interupsi SSP (dan bukan interupsi lain)
- Tulis ISR prioritas tinggi umum, ketika interupsi prioritas tinggi. Fungsi ini harus memeriksa gangguan apa yang dipancarkan, dan memanggil sub-ISR yang tepat (misalnya, SSP ISR)
- Tambahkan
GOTO
instruksi ke ISR umum pada vektor interupsi prioritas tinggi. Kami tidak dapat meletakkan ISR umum langsung pada vektor karena terlalu besar dalam banyak kasus.
Berikut contoh kode:
// Function prototypes for the high priority ISRs
void highPriorityISR(void);
// Function prototype for the SSP ISR
void SSPISR(void);
// This is the code for at the high priority vector
#pragma code high_vector=0x08
void interrupt_at_high_vector(void) { _asm GOTO highPriorityISR _endasm }
#pragma code
// The actual high priority ISR
#pragma interrupt highPriorityISR
void highPriorityISR() {
if (PIR1bits.SSPIF) { // Check for SSP interrupt
SSPISR(); // It is an SSP interrupt, call the SSP ISR
PIR1bits.SSPIF = 0; // Clear the interrupt flag
}
return;
}
// This is the actual SSP ISR
void SSPISR(void) {
// We'll add code later on
}
Hal berikutnya yang harus dilakukan adalah mengaktifkan interupsi prioritas tinggi ketika chip menginisialisasi. Ini dapat dilakukan dengan beberapa manipulasi register sederhana:
RCONbits.IPEN = 1; // Enable interrupt priorities
INTCON &= 0x3f; // Globally enable interrupts
PIE1bits.SSPIE = 1; // Enable SSP interrupt
IPR1bits.SSPIP = 1; // Set SSP interrupt priority to high
Sekarang, kami memiliki interupsi yang berfungsi. Jika Anda menerapkan ini, saya akan memeriksanya sekarang. Tulis dasar SSPISR()
untuk mulai berkedip LED ketika gangguan SSP terjadi.
Oke, jadi interupsi Anda berfungsi. Sekarang mari kita menulis beberapa kode nyata untuk SSPISR()
fungsi ini. Tetapi pertama-tama beberapa teori. Kami membedakan lima jenis interupsi I2C yang berbeda:
- Master menulis, byte terakhir adalah alamat
- Master menulis, byte terakhir adalah data
- Master membaca, byte terakhir adalah alamat
- Master membaca, byte terakhir adalah data
- NACK: akhir transmisi
Anda dapat memeriksa keadaan Anda saat ini dengan memeriksa bit dalam SSPSTAT
register. Daftar ini adalah sebagai berikut dalam mode I2C (bit yang tidak digunakan atau tidak relevan dihilangkan):
- Bit 5: D / BUKAN A: Data / Tidak alamat: atur jika byte terakhir adalah data, dihapus jika byte terakhir adalah alamat
- Bit 4: P: Stop bit: setel jika kondisi STOP terjadi terakhir (tidak ada operasi aktif)
- Bit 3: S: Start bit: setel jika kondisi MULAI terjadi terakhir (ada operasi aktif)
- Bit 2: R / BUKAN W: Baca / Tidak tulis: setel jika operasi adalah Master Baca, dihapus jika operasi adalah Master Write
- Bit 0: BF: Buffer Full: atur jika ada data dalam register SSPBUFF, dihapus jika tidak
Dengan data ini, mudah untuk melihat bagaimana melihat status modul I2C:
State | Operation | Last byte | Bit 5 | Bit 4 | Bit 3 | Bit 2 | Bit 0
------+-----------+-----------+-------+-------+-------+-------+-------
1 | M write | address | 0 | 0 | 1 | 0 | 1
2 | M write | data | 1 | 0 | 1 | 0 | 1
3 | M read | address | 0 | 0 | 1 | 1 | 0
4 | M read | data | 1 | 0 | 1 | 1 | 0
5 | none | - | ? | ? | ? | ? | ?
Dalam perangkat lunak, yang terbaik adalah menggunakan state 5 sebagai default, yang diasumsikan ketika persyaratan untuk negara-negara lain tidak terpenuhi. Dengan begitu, Anda tidak menjawab ketika Anda tidak tahu apa yang terjadi, karena budak tidak menanggapi NACK.
Bagaimanapun, mari kita lihat kodenya:
void SSPISR(void) {
unsigned char temp, data;
temp = SSPSTAT & 0x2d;
if ((temp ^ 0x09) == 0x00) { // 1: write operation, last byte was address
data = ReadI2C();
// Do something with data, or just return
} else if ((temp ^ 0x29) == 0x00) { // 2: write operation, last byte was data
data = ReadI2C();
// Do something with data, or just return
} else if ((temp ^ 0x0c) == 0x00) { // 3: read operation, last byte was address
// Do something, then write something to I2C
WriteI2C(0x00);
} else if ((temp ^ 0x2c) == 0x00) { // 4: read operation, last byte was data
// Do something, then write something to I2C
WriteI2C(0x00);
} else { // 5: slave logic reset by NACK from master
// Don't do anything, clear a buffer, reset, whatever
}
}
Anda dapat melihat bagaimana Anda dapat memeriksa SSPSTAT
register (pertama-tama ANDed 0x2d
sehingga kami hanya memiliki bit yang berguna) menggunakan bitmask untuk melihat jenis interupsi yang kami miliki.
Adalah tugas Anda untuk mengetahui apa yang harus Anda kirim atau lakukan ketika Anda merespons interupsi: tergantung pada aplikasi Anda.
Referensi
Sekali lagi, saya ingin menyebutkan catatan aplikasi yang ditulis Microchip tentang I2C:
- AN734 tentang penerapan budak I2C
- AN735 tentang penerapan master I2C
- AN736 tentang pengaturan protokol jaringan untuk pemantauan lingkungan
Ada dokumentasi untuk pustaka kompilator: Kompilasi pustaka dokumentasi
Saat mengatur sesuatu sendiri, periksa lembar data chip Anda pada bagian (M) SSP untuk komunikasi I2C. Saya menggunakan PIC18F46K22 untuk bagian master dan PIC18F4620 untuk bagian budak.