AVR · Raspberry Pi

Communication between RPi 3 and ATmega MCU over I2C

Xin chào, ban đầu mình muốn thực hiện giao tiếp I2C giữa Raspberry Pi 3 (RPi 3) và ATtiny2313 nhưng không thành công do RPi 3 có một Hardware bug, nói đơn giản là RPi 3 không hỗ trợ “clock stretching” khi thiết bị Slave giữ SCL ở mức LOW để trao đổi dữ liệu, “clock stretching” chỉ xảy ra ở các Slave có tốc độ xử lý thấp hoặc không có module I2C.

Vì lý do trên mà mình đã thực hiện giao tiếp I2C giữa RPi 3 và MCU ATmega328P và kết quả rất khả quan.


CHUẨN BỊ

  1. Raspberry Pi 3.
  2. MCU ATmega328P.
  3. Vài sợi dây dẫn và breadboard.

SCHEMATIC

Trong kết nối I2C, RPi luôn đóng vai trò Master, các thiết bị khác sẽ là Slave.

rpiatmegai2c

Figure 1 – Sơ đồ kết nối Raspberry Pi 3 và MCU ATmega328P


RASPBERRY PI SIDE

Đầu tiên chúng ta tải thư viện smbus2:

pip3 install smbus2

Mình sẽ viết code cho RPi trên Python 3:

i2cdemo.py

from smbus2 import SMBusWrapper, i2c_msg
import time

SLAVE_ADDRESS = 0x10
while True:
    with SMBusWrapper(1) as bus:
        try:
            # Transmit some bytes to Slave
            msg = i2c_msg.write(SLAVE_ADDRESS, map(int, input('Input:').split()))
            bus.i2c_rdwr(msg)
            # delay 0.1 second
            time.sleep(0.1)
            # Request 8 bytes from Slave
            msg = i2c_msg.read(SLAVE_ADDRESS, 8)
            bus.i2c_rdwr(msg)
            print(list(msg))
        except:
            print("Slave not found!")

CHANGE I2C SPEED

Để thay đổi baudrate của I2C, chúng ta mở Terminal và gõ lệnh:

$ sudo nano /boot/config.txt

Sau đó thêm vào phần dtparam dòng:

dtparam=i2c_arm_baudrate=50000 (chúng ta có thể baudrate tùy ý, 50kbps, 100kbps hoặc 400kbps). Xem Figure 2.

i2c

Figure 2 – Change i2c baudrate


MCU ATMEGA328P SIDE

Đầu tiên chúng ta sẽ clone thư viện I2C từ Github về và include vào project trong AVR Studio.

MCU ATmega328P sẽ chạy với bộ dao động Int. RC Osc. 8 MHz, fuse bits khi nạp code như sau: -U lfuse:w:0xE2:m -U hfuse:w:0xD9:m -U efuse:w:0xFF:m

Dưới đây là code của MCU ATmega328P mình viết trên AVR Studio.

#define F_CPU 8000000UL
#include "avr/io.h"
#include "util/delay.h"
#include "avr/interrupt.h"
#include "I2C.h"

int main() {
	DDRB |= (1 << PB1);
	DDRD |= (1 << PD5) | (1 << PD6);

	I2C_slave_init(0x10);
	sei();

	for (uint8_t i = 0; i < I2CSLAVEBUFFER; i++) {
		SLAVE_RecBuffer[i] = 0;
		SLAVE_TranBuffer[i] = i+'A';
	}

	while (1) {
		PORTB ^= (1 << PB1);

		if (I2Cstate == 1) {
			I2Cstate = 0;
			PORTD = (SLAVE_RecBuffer[0] << PD5) | (SLAVE_RecBuffer[1] << PD6);
		}
		_delay_ms(500);
	}
}
/*
ISR(TWI_vect) {
	uint8_t status = TWSR;
	switch (status) {
		case SLAVE_REC_ADDRESS_MATCH:
										TWCR |= (1 << TWINT) | (1 << TWEA);
										recCount = 0;
										break;
		case SLAVE_REC_DATA_ACK:
		case SLAVE_REC_DATA_NACK:
										SLAVE_RecBuffer[recCount++] = TWDR;
										TWCR |= (1 << TWINT) | (1 << TWEA);
										break;
		case SLAVE_REC_STOP:
										I2Cstate = 1;
										recCount = 0;
										TWCR |= (1 << TWINT) | (1 << TWEA);

										break;

		case SLAVE_TRS_ADDRESS_MATCH:	tranCount = 0;
										TWDR = SLAVE_TranBuffer[tranCount++];
										TWCR |= (1 << TWINT) | (1 << TWEA);
										break;
		case SLAVE_TRS_DATA_ACK:		TWDR = SLAVE_TranBuffer[tranCount++];
										TWCR |= (1 << TWINT) | (1 << TWEA);
										break;
		case SLAVE_TRS_DATA_NACK:
		case SLAVE_TRS_LAST:		 	TWCR |= (1 << TWINT) | (1 << TWEA);
										break;
		default: break;

	}
}
*/

Chú ý: phần ISR (TWI_vect) đã có sẵn trong library, mình thêm vào ở đây chỉ để các bạn dễ theo dõi.


TESTING

Sau khi nạp code cho MCU ATmega328P, trên RPi, các bạn sử dụng lệnh i2cdetect -y 1 để kiểm tra xem Slave đã được kết nối vào I2C bus chưa. Nếu địa chỉ của Slave xuất hiện (ở ví dụ trên là 0x10) thì kết nối đã thành công.

i2cdetect

Figure 3 – Kiểm tra kết nối giữa Master và Slave

Chạy i2cdemo.py script trên RPi, lần lượt nhập vào “1 0”, “0 1”, “1 1”, “0 0” khi xuất hiện prompt “Input:” và chú ý kết quả trên RPi terminal và LEDs nhé.

demo

Figure 4 – Kết quả trên RPi

Dựa vào ví dụ trên các bạn hãy khám phá và sáng tạo thỏa thích!

Thân ái và quyết thắng.

Reference:
[1] smbus2 python library.
[2] Change Raspberry Pi I2C Bus Speed.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s