Dimulai dengan I2C pada PIC18s

8

Untuk proyek saya ingin tiga PIC (dua budak PIC18F4620, satu master PIC18F46K22) untuk berkomunikasi melalui bus I2C. Kemudian, lebih banyak budak dapat ditambahkan (seperti EEPROM, SRAM, ...). Saya menulis kode untuk PIC ini dalam C menggunakan kompiler C18. Saya sudah sering melihat-lihat di internet, tetapi tidak dapat menemukan perpustakaan untuk menangani perangkat (M) SSP. Saya telah membaca lembar data dari kedua PIC pada periferal (M) SSP dalam mode I2C tetapi tidak dapat menemukan cara antarmuka bus.

Jadi saya perlu perpustakaan master dan slave.

Apa yang kamu sarankan? Apakah Anda memiliki perpustakaan seperti itu di suatu tempat? Apakah sudah ada di dalam kompiler dan jika ya, di mana? Apakah ada tutorial yang bagus di suatu tempat di internet?


sumber
2
Saya memiliki masalah serupa beberapa bulan yang lalu. Anda dapat membacanya di sini . Berikut adalah perpustakaan untuk C18 yang bekerja dengan I ^ 2C, tetapi ada satu hal besar yang hilang: Anda perlu mengatur kecepatan bus secara manual dengan menulis ke register yang sesuai dan yang tidak disebutkan di manapun dalam dokumentasi perpustakaan.
AndrejaKo
Terima kasih, itu sangat membantu! Namun itu hanya bagian master, bukan bagian budak.
Ya, saya tidak perlu bekerja dengan budak saat itu, jadi tidak ada contoh budak. Maaf.
AndrejaKo
2
Tidak, tidak apa-apa, itu berguna untuk bagian master! :-)
harap nonaktifkan analog pada port ANSELC = 0;

Jawaban:

22

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/vx.xx/h/i2c.h
  • Sumber C18: installation_path/vx.xx/src/pmc_common/i2c/
  • Header XC8: installation_path/vx.xx/include/plib/i2c.h
  • Sumber XC8: installation_path/vx.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 OpenI2Cfungsi 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_modeadalah:

  • 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 GOTOinstruksi 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:

  1. Master menulis, byte terakhir adalah alamat
  2. Master menulis, byte terakhir adalah data
  3. Master membaca, byte terakhir adalah alamat
  4. Master membaca, byte terakhir adalah data
  5. NACK: akhir transmisi

Anda dapat memeriksa keadaan Anda saat ini dengan memeriksa bit dalam SSPSTATregister. 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 SSPSTATregister (pertama-tama ANDed 0x2dsehingga 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.

Komunitas
sumber
3

Pertama, saya akan merekomendasikan untuk mengganti ke kompiler XC8 hanya karena itu yang terbaru. Ada perpustakaan periferal yang tersedia, tetapi saya belum pernah menggunakannya. Periksa situs web Microchips untuk perincian dan dokumentasinya.

Oke, saya punya beberapa rutinitas dasar yang sangat lama di sini untuk I2C eeprom comms yang saya gunakan dulu dengan PIC16F dan kompiler mid-range Microhip lama (mungkin yang Hi-Tech), tapi saya pikir mereka bisa bekerja dengan baik dengan PIC18, karena saya pikir perangkatnya sama. Anda akan mengetahuinya dengan sangat cepat jika semuanya berbeda.
Mereka adalah bagian dari file yang lebih besar yang digunakan dengan proyek temperatur logger, jadi saya dengan cepat menghapus semua fungsi yang tidak berhubungan lainnya dan disimpan sebagai file di bawah ini, jadi mungkin saya telah membuat sedikit kekacauan, tapi semoga Anda akan bisa mendapatkan beberapa ide tentang apa yang dibutuhkan (bahkan mungkin hanya berfungsi, Anda tidak pernah tahu ;-))

Anda perlu memastikan file header utama sudah benar, dan periksa / ubah pin untuk memastikan mereka adalah pin periferal I2C yang benar, dan nama register jika berasal dari kompiler Hi-Tech, yang melakukan hal-hal sedikit berbeda tentang konvensi penamaan daftar.

File I2C.c:

#include "I2C.h"
#include "delay.h"
#include <pic.h>

#define _XTAL_FREQ 20000000


void write_ext_eeprom(unsigned int address, unsigned char data)
 {
    unsigned char a0 = ((address & 0x8000) >> 14);  
    unsigned char msb = (address >> 8);
    unsigned char lsb = (address & 0x00FF);


   i2c_start();
   i2c_write(0xa0 | a0);
   i2c_write(msb);
   i2c_write(lsb);
   i2c_write(data);
   i2c_stop();
   DelayMs(11);
}

/******************************************************************************************/

unsigned char read_ext_eeprom(unsigned int address)
{
   unsigned char a0 = ((address & 0x8000) >> 14);  
   unsigned char data;
   unsigned char msb = (address >> 8);
   unsigned char lsb = (address & 0x00FF);

   i2c_start();
   i2c_write(0xa0 | a0);
   i2c_write(msb);
   i2c_write(lsb);
   i2c_repStart();
   i2c_write(0xa1 | a0);
   data=i2c_read(0);
   i2c_stop();
   return(data);
}

void i2c_init()
{
 TRISC3=1;           // set SCL and SDA pins as inputs
 TRISC4=1;

 SSPCON = 0x38;      // set I2C master mode
 SSPCON2 = 0x00;

 //SSPADD = 9;          // 500kHz bus with 20MHz xtal 
 SSPADD = 49;           // 100kHz bus with 20Mhz xtal

 CKE=0;     // use I2C levels      worked also with '0'
 SMP=1;     // disable slew rate control  worked also with '0'

 PSPIF=0;      // clear SSPIF interrupt flag
 BCLIF=0;      // clear bus collision flag
}

/******************************************************************************************/

void i2c_waitForIdle()
{
 while (( SSPCON2 & 0x1F ) | RW ) {}; // wait for idle and not writing
}

/******************************************************************************************/

void i2c_start()
{
 i2c_waitForIdle();
 SEN=1;
}

/******************************************************************************************/

void i2c_repStart()
{
 i2c_waitForIdle();
 RSEN=1;
}

/******************************************************************************************/

void i2c_stop()
{
 i2c_waitForIdle();
 PEN=1;
}

/******************************************************************************************/

int i2c_read( unsigned char ack )
{
 unsigned char i2cReadData;

 i2c_waitForIdle();

 RCEN=1;

 i2c_waitForIdle();

 i2cReadData = SSPBUF;

 i2c_waitForIdle();

 if ( ack )
  {
  ACKDT=0;
  }
 else
  {
  ACKDT=1;
  }
  ACKEN=1;               // send acknowledge sequence

 return( i2cReadData );
}

/******************************************************************************************/

unsigned char i2c_write( unsigned char i2cWriteData )
{
 i2c_waitForIdle();
 SSPBUF = i2cWriteData;
//if(ACKSTAT)
{
//while(ACKSTAT);
}
 return ( ! ACKSTAT  ); // function returns '1' if transmission is acknowledged
}

File Header I2C.h:

extern void i2c_init();
extern void i2c_waitForIdle();
extern void i2c_start();
extern void i2c_repStart();
extern void i2c_stop();
extern int i2c_read( unsigned char ack );
extern unsigned char i2c_write( unsigned char i2cWriteData );
Oli Glaser
sumber
Terima kasih, itu adalah bagian utama dan memang mungkin sama dengan PIC18. Juga terima kasih atas catatan kompiler :-) Bertanya sedikit memberi saya beberapa catatan aplikasi, jadi saya akan menambahkannya sebagai jawaban sendiri.
Anda harus mempertimbangkan untuk menambahkan bagian tentang pengaturan generator baud rate. Kode biasanya terlihat seperti SSP1ADD = ((_XTAL_FREQ/100000)/4)-1;1KHz, dll.
Jesse Craig
1

Kompiler XC8 dan XC16 menyertakan pustaka untuk I2C.

Masalah yang saya temui adalah bahwa dokumentasi tidak terlalu bagus! Jika Anda menggunakan contoh-contoh dari dokumentasi Microchip, Anda kurang beruntung. Bahkan dukungan Microchip tidak dapat membantu Anda. Saya telah berada di sana sendiri.

Beberapa waktu yang lalu saya bekerja dengan mikrokontroler seri PIC24EP512GP dan perpustakaan tidak bekerja untuk saya seperti yang didokumentasikan oleh Microchip.

Chetan Bhargava
sumber
Ah, itu memalukan! Jadi apa yang kamu lakukan?
Sayangnya saya diperbaiki sendiri.
Chetan Bhargava
1
Apakah mereka bermanfaat untuk orang lain juga? Saya ingin melihat mereka!