Bài 16 : Giao tiếp I2C – DS1307
16.8 Giao thức I2C và RTC DS1307
16.8.1 Kết nối phần cứng
Gạt switch 1 và 2 lên ON để kích hoạt P1 và P3. SDA được nối với P1.0.
SCL được nối với P1.1.
16.8.2 Start và Stop truyền dữ liệu
Việc truyền dữ liệu được bắt đầu bởi một tín hiệu Start và kết thúc việc
truyền bằng một tín hiệu Stop. Số byte dữ liệu truyền giữa 2 tín hiệu Start và Stop là không hạn chế và được xác định bởi master.
Dựa vào giản đồ trên ta hiện thực hàm để Start I2C như sau : void start_I2C() { SCL = 1; SDA = 1; nop();nop(); SDA = 0; SCL = 0; nop();nop(); }
Tốc độ clock chuẩn của giao thức I2C là 100KHz. Khi truyền ở tốc độ cao có thể hoạt động ở clock 1MHz. Tuy nhiên bạn nên delay vài uS để đảm bảo tính đúng đắn của dữ liệu.
Giản đồ xung cho điều kiện Stop như sau
Hàm để Stop I2C được hiện thực như sau:
void stop_I2C() { SCL = 1; SDA = 0; nop();nop(); SDA = 1; }
BKIT HARDWARE CLUB www.bkit4u.com 107
16.8.3 Truyền 1 byte dữ liệu
Khi truyền 1 byte dữ liệu, bit có trọng số cao nhất (bit 7) sẽ được truyền trước. Khi bit cuối cùng (bit 0) được truyền, sẽ có thêm bit ACK báo hiệu kết thúc 1 byte dữ liệu.
Truyền từ master xuống slave:
Ở chế độ này, master sẽ gửi 8 bit dữ liệu, sau khi nhận xong 8 bit này, slave sẽ tự động gửi lại 1 bit ACK và master phải tạo ra thêm 1 clock để nhận bit ACK này.
//write I2C
void write_I2C(unsigned char data2send) {
int i;
for (i=0;i<8;i++) {
SDA = (data2send & 0x80) ? 1:0; SCL=1; nop(); SCL=0; data2send<<=1; nop(); }
//clock to receive ACK from slave SCL = 1;
nop();nop(); SCL = 0; }
Truyền từ slave lên master
Ở chế độ này, master sẽ nhận vào 8 bit dữ liệu, và sau khi nhận xong, master phải gửi 1 bit ACK xuống slave. Trong quá trình đọc 1 chuỗi byte từ slave, master gửi bit ACK. Đối với byte cuối cùng, master sẽ gửi bit NO ACK và sau đó gửi tín hiệu stop. Hàm đọc 1 byte sau đây có tham số là ACK_Bit, dùng để phân biệt ACK và NACK.
unsigned char read_I2C(bit ACK_Bit) {
unsigned char Data=0; int i; SDA = 1; for (i=0;i<8;i++) { SCL = 1; Data<<= 1;
Data = (Data | SDA); SCL = 0;
nop(); }
if (ACK_Bit == 1)
SDA = 0; // Send ACK else
SDA = 1; // Send NO ACK nop();nop();
//clock to send (N)ACK SCL = 1;
nop();nop(); SCL = 0; return Data; }
16.8.4 Giao tiếp với DS1307
Ghi dữ liệu vào DS1307
Đây là quá trình truyền dữ liệu từ master xuống slave. Khi master gửi xong 1 byte, slave sẽ gửi lại bit ACK. Quá trình giao tiếp như sau:
v Master gửi tín hiệu Start.
v Master gửi địa chỉ của DS1307 (1101 000) và bit R/W, trong trường hợp
này là 0. Byte đầu tiên mà master gửi xuống sau khi start là D0.
v Master gửi địa chỉ pointer dữ liệu cần ghi, chẳng hạn là 0x00 (register
pointer, word address)
v Master gửi các byte data cần ghi. v Master gửi tín hiện stop.
Code hiện thực cho quá trình này như sau
void write_RTC(unsigned char *buff) {
start_I2C(); write_I2C(0xD0); write_I2C(0x00); write_I2C(*(buff+0));
BKIT HARDWARE CLUB www.bkit4u.com 109 write_I2C(*(buff+3)); write_I2C(*(buff+4)); write_I2C(*(buff+5)); write_I2C(*(buff+6)); stop_I2C(); }
buff là 1 mảng có 7 phần tử, tương ứng với các giá trị giây, phút, giờ, thứ, ngày,
tháng và năm.
Đọc dữ liệu từ DS1307
Đây là quá trình truyền dữ liệu từ slave lên master. Như đã trình bày ở phần trước, khi gửi nhận từng byte, sẽ có bit ACK đi kèm ngoại trừ byte cuối cùng trước khi stop.
Để có thể đọc chính xác giá trị mong muốn, thông thường ta phải ghi vào thanh ghi địa con trỏ dữ liệu (register pointer). Q trình này chính là trình truyền dữ liệu từ master xuống slave nên R/W bit sẽ là 0. Sau khi ghi dữ liệu và register pointer xong, quá trình đọc dữ liệu mới bắt đầu, và bit R/W sẽ là 1.
Từng bước đọc dữ liệu từ DS1307 như sau:
v Master gửi tín hiệu start.
v Master gửi địa chỉ DS1307 + R/W = 0 : 0xD0. v Master gửi byte ghi vào register pointer : 0x00. v Master gửi tín hiệu start
v Master gửi địa chỉ DS1307 + R/W = 1 : 0xD1.
v Master đọc các byte dữ liệu và gửi bit ACK. Byte cuối cùng trước khi stop,
master gửi bit NACK.
v Master gửi tín hiện stop.
Code hiện thực cho quá trình này như sau
void read_RTC(unsigned char * buff) {
//send address to slave and reset pointer start_I2C();
write_I2C(0xD0); //address + direction write_I2C(0x00); //pointer data
//start read operation start_I2C(); write_I2C(0xD1); *(buff+0)=read_I2C(ACK); // Second *(buff+1)=read_I2C(ACK); // Minute *(buff+2)=read_I2C(ACK); // hour *(buff+3)=read_I2C(ACK); // Day
*(buff+4)=read_I2C(ACK); // date *(buff+5)=read_I2C(ACK); // month *(buff+6)=read_I2C(NO_ACK); // year stop_I2C();
}
Hàm main() dưới đây minh hoạ cho việc sử dụng các hàm trong module RTC DS1307: void main() { P1 = P3 = 0x00; RTC_ARR[0] = 0x12; //second = 12 RTC_ARR[1] = 0x55; // minute = 55
RTC_ARR[2] = 0x05; // hour = 05 ,24-hour mode(bit 6=0) RTC_ARR[3] = 0x01; // Day = 1 or sunday
RTC_ARR[4] = 0x01; // Date = 01 RTC_ARR[5] = 0x08; // month = August RTC_ARR[6] = 0x05; // year = 05 or 2005 write_RTC(&RTC_ARR[0]); // Set RTC
while(1) {
read_RTC(&RTC_ARR[0]); //read 7 bytes RTC P3=RTC_ARR[0]; //out second to P3
delay();; // delay about 1 second }