Lập trình giao tiếp I2C trên vi điều khiển pic16f877a

Giao tiếp i2c trên vi điều khiển pic như thế nào?

Trước tiên chúng ta tìm hiểu về khái niệm về I2C và sau đó là một project về “Giao tiếp i2c trên vi điều khiển pic”: I2C là một thuật ngữ được viết tắt của cụm từ Inter-Integrated Circuit, là một loại Bus nối tiếp, được phát triển bởi công ty điện tử Philips. Thời gian đầu, I2C chỉ được dùng trong các thiết bị điện tử của philip, về sau do tính tiện dụng và đơn giản nên nó được sử dụng rộng rãi trong các lĩnh vực điện tử.

Để giao tiếp I2C giữa các linh kiện điện tử (có hỗ trợ I2C) ta sử dụng 2 đường truyền tín hiệu: một là đường xung nhịp đồng hồ(SCL) và một đường dữ liệu(SDA). SCL và SDA luôn được kéo lên nguồn bằng một điện trở kéo lên có giá trị xấp xỉ 4,7 KOhm.

Các chế độ hoạt động của I²C bao gồm:

  • Chế độ chuẩn (standard mode) hoạt động ở tốc độ 100 Kbit/s.
  • Chế độ tốc độ thấp (low-speed mode) hoạt động ở tốc độ 10 Kbit/s.

I2C sử dụng thanh ghi 7 bit để định địa chỉ cho các thiết bị ngoại vi, do đó nó có thể quản lí được số lượng lên đến 112 Slave và 16 địa chỉ còn lại sử dụng vào các mục đích riêng.

Mô tả project: trong project này ta sử dụng vi điều khiển Pic 16F877A để điều khiển ghi, đọc dữ liệu với EEPROM FM24C02. Bạn sử dụng Button (+), (-) và hệ thống hiển thị 7 segment để chọn giá trị cần ghi vào eeprom, việc ghi dữ liệu và đọc chúng thông qua 2 button là Write và Read. Kết quả đọc từ địa chỉ 0x01 của eeprom sẽ được hiển thị qua portd của vi điều khiển 16f877A.

Chúng ta tìm hiểu sơ qua  eeprom FM24C02 để trong quá trình lập trình bạn dễ hiểu hơn:

  • 24C20 được đóng gói dạng DIP 8 Pin.
  • 2 K EEPROM (256 x 8bit)
  • Supply voltage 2.5V – 5.5v
  • Công nghệ CMOS, có thể erase/write 1000000 lần, thời gian dữ liệu tồn tại lên đến 40 năm.

Tên và chức năng các chân của EEPROM 24C02, hãng ST

Device select code của 24c02

Bạn cần chú ý vào thanh ghi này để điều khiển quá trình đọc /ghi theo giao thức I2C. Bạn thấy 4 bit cao từ b4 đến b7 là cố định, đây là Device code không đổi. Các bit từ b1 đến b3 (E0, E1, E2) là bit cấu hình cho eeprom (chip enable), bit b0 dùng để chọn mode Read/Write (o là write, 1 là read). Ví dụ ta cấu hình như sau: 10100010, như vậy địa chỉ write của eeprom này là A2, trường hợp Read thì có địa chỉ như sau: 10100011 (A3).

Thực hiện project:

Các hàm I2C ta sử dụng trong project này gồm:

  • I2C1_Init(clock): khởi tạo i2c với tần số hoạt động của eeprom, mặc định là 100000, cần gọi hàm này trước khi sử dụng các hàm khác.
  • I2C1_Start(): xác định nếu i2c bus free, gởi đi một tín hiệu bắt đầu.
  • I2C1_Wr(data): gởi 1 byte dữ liệu qua i2c bus
  • I2c1_Rd(ack): đọc 1 byte từ slave và gởi một tín hiệu not acknowledge nếu tham số ack là 0, nếu không sẽ gởi xác nhận.
  • I2C1_Repeated_Start(): gởi lại tín hiệu start.
  • I2C1_Stop(): gởi tín hiệu stop.

Các bài viết sau có thể giúp bạn dễ hiểu các phần trong project này:

Bạn cần chú ý 2 đoạn code sau:
Chương trình con đọc dữ liệu từ 24c02:

void write_24c02(unsigned int address, unsigned int dat){
         I2C1_Start();              // issue I2C start signal
         I2C1_Wr(0xA2);             // send byte via I2C  (device address + W)
         I2C1_Wr(address);                // send byte (address of EEPROM location)
         I2C1_Wr(dat);             // send data (data to be written)
         I2C1_Stop();               // issue I2C stop signal
         }

Chương trình con ghi dữ liệu đến eeprom 24c02:

unsigned int read_24c02(unsigned int address){
         unsigned int kq;
         I2C1_Start();              // issue I2C start signal
         I2C1_Wr(0xA2);             // send byte via I2C  (device address + W)
         I2C1_Wr(address);                // send byte (data address)
         I2C1_Repeated_Start();     // issue I2C signal repeated start
         I2C1_Wr(0xA3);             // send byte (device address + R)
         kq = I2C1_Rd(0u);       // Read the data (NO acknowledge)
         I2C1_Stop();               // issue I2C stop signal
         return kq;
         }

Code hoàn chỉnh của project i2c:

/*
 *Project name: I2C
 *CopyRight:
            (c) localhost/dientudieukhien.net, 2015
 *Description:
            Su dung pic 16f877A dieu khien ghi, doc du lieu eeprom 24c02 bang giao
            thuc I2C. Gia tri duoc chon qua he thong button va led 7 segment, gia
            tri doc duoc se hien thi qua portd de ta kiem tra
 *Configuration:
            Mivrocontroller: PIC 16F877A
            Oscillator: HS, 08.000 MHz
            Device: proteus diagram
            Compiler: mikroC Pro for Pic ver 6.6.2
 *Website: localhost/dientudieukhien.net
*/
/*Tao ma hex cho led 7 doan CA*/
unsigned short mask(unsigned short num) {
         switch (num) {
         case 0 : return 0xc0;
         case 1 : return 0xf9;
         case 2 : return 0xa4;
         case 3 : return 0xb0;
         case 4 : return 0x99;
         case 5 : return 0x92;
         case 6 : return 0x82;
         case 7 : return 0xf8;
         case 8 : return 0x80;
         case 9 : return 0x90;
                         }
                                        }

unsigned short mask(unsigned short num);
unsigned short digit_no, digit10, digit1, digit,i,time,k;
/*Ngat interrupt de quet 2 led*/
void interrupt() {
     if (digit_no==0) {
        PORTA = 0; // Turn off both displays
        PORTB = digit1; // Set mask for displaying ones on PORTD
        PORTA = 1; // Turn on display for ones (LSD)
        digit_no = 1;
        } else {
        PORTA = 0; // Turn off both displays
        PORTB = digit10; // Set mask for displaying tens on PORTD
        PORTA = 2; // Turn on display for tens (MSD)
        digit_no = 0;
        }
TMR0 = 0; // Reset counter TMRO
INTCON = 0x20; // Bit T0IF=0, T0IE=1
}
/*Ham con de chuyen doi, hien thi so*/
void display(){
     digit = i % 10u;
     digit1 = mask(digit); // Prepare mask for displaying ones
     digit = (char)(i / 10u) % 10u;
     digit10 = mask(digit); // Prepare mask for displaying tens
}


void write_24c02(unsigned int address, unsigned int dat){
         I2C1_Start();              // issue I2C start signal
         I2C1_Wr(0xA2);             // send byte via I2C  (device address + W)
         I2C1_Wr(address);                // send byte (address of EEPROM location)
         I2C1_Wr(dat);             // send data (data to be written)
         I2C1_Stop();               // issue I2C stop signal
         }
unsigned int read_24c02(unsigned int address){
         unsigned int kq;
         I2C1_Start();              // issue I2C start signal
         I2C1_Wr(0xA2);             // send byte via I2C  (device address + W)
         I2C1_Wr(address);                // send byte (data address)
         I2C1_Repeated_Start();     // issue I2C signal repeated start
         I2C1_Wr(0xA3);             // send byte (device address + R)
         kq = I2C1_Rd(0u);       // Read the data (NO acknowledge)
         I2C1_Stop();               // issue I2C stop signal
         return kq;
         }

void main(){
  OPTION_REG = 0x80; // Set RBPU disable
  TMR0 = 0;          //Clear TMR0
  INTCON = 0XA8;     // Enable GIE, TMR0IE, RBIE
  CMCON = 0X07;     // Disable Compare
  ADCON1 = 0X06;    //Set porta as digital
  TRISA=0b00111100; //RA0, RA1 as output; RA2,RA3,RA4,RA5 as input
  PORTA=0;
  TRISB=0X00;
  PORTB=0;
  TRISD = 0x00;
  PORTD = 0;
  i=0; 
  time=0;
  display();
  I2C1_Init(100000);         // initialize I2C communication
do{
          if(Button(&porta,2,1,0)){ //Giam mot don vi
                                    time=time-1;
                                    i=time;
                                    display();
                                    delay_ms(200);
                     }
           if(Button(&porta,3,1,0)){ //Tang 1 don vi
                                    time=time++;
                                    i=time;
                                    display();
                                    delay_ms(200);
                     }
           if(Button(&porta,4,1,0)){  //Ghi gia tri cua bien time vao dia chi 1 cua eeprom
                                    write_24c02(1,time);
                                    delay_ms(100);
                                    }
           if(Button(&porta,5,1,0)){ //Doc du lieu tu dia chi 1 cua eeprom
                                    read_24c02(1);
                                    PORTD=time;
                                    delay_ms(100);
                                    }

}while(1);
}