Hàm này chỉ đơn giản là gửi lệnh clear màn hình lcd (xem thêm trong bảng lệnh của LCD). void lcd_clear() { lcd_write_cmd(0x01); lcd_goto_xy(0, 0); } 15.8.4 Thiết lập vị trí con trỏ
Hàm này thiết lập vị trí bắt đầu xuất dữ liệu trên màn hình LCD 2 hàng 16 cột. Để hiện thực hàm này ta phải tính được địa chỉ tương ứng với toạ độ (row,col) và dùng lệnh SET DDRAM ADDRESS (bit 7 của lệnh này bằng 1).
char lcd_goto_xy(unsigned char row, unsigned char col) { unsigned char addr = 0x00;
if(col >= 20 || row >= 4) return FALSE; if(row < 2) {
addr = (row * 0x40) + col; addr = 0x80 | (addr & 0x7F);
} else {
addr = (row * 0x40) + col; addr = 0x94 | (addr & 0x7F); } lcd_write_cmd(addr); current_row = row; current_col = col; return TRUE; } 15.8.5 In kí tự ra màn hình
Hàm này nhận thông số là 1 kí tự và hiển thị kí tự đó ra màn hình LCD. Việc hiện thực hàm này khá đơn giản, ta chỉ cần kéo chân RS xuống 0 là LCD sẽ hiểu các bit D7- D4 là dữ liệu.
void lcd_print_char(unsigned char dat) {
lcd_wait_busy(); //find next position
if(current_row == 0 && current_col == 16) lcd_goto_xy(1,0);
if(current_row == 1 && current_col ==16) lcd_goto_xy(0,0);
RS(DAT); //RS = 0 lcd_write_4bits(dat); lcd_write_4bits(dat << 4);
current_col ++; //update new position
}
Từ những hàm cơ bản này, bạn có thể hiện thực thêm các hàm để xuất 1 string hay 1 giá trị số ra màn hình LCD. Code chi tiết có thể xem thêm trong thư mục Bài 15.
BKIT HARDWARE CLUB www.bkit4u.com 101
Bài 16 : Giao tiếp I2C – DS1307
Mục đích:
Nắm vững giao tiếp I2C
Yêu cầu:
Xây dựng ứng dụng giao tiếp với DS1307 để lấy dữ liệu ngày tháng năm, giờ phút giây.
16.1 Các đặc điểm của DS1307
Real time clock đếm giờ, phút, giây, tháng, ngày của
tháng, ngày của tuần, năm kể cả năm nhuận (đến năm 2100). 56 byte Ram để lưu trữ dữ liệu, nhưng dữ liệu không bị
mất khi tắt nguồn.
Sử dụng 2 dây tín hiệu để truyền dữ liệu theo giao thức
I2C.
Có thể lập trình được để xuất tín hiệu xung vuông.
Tự động phát hiện ra nguồn cung cấp bị lỗi (ngắt nguồn) và chuyển qua
mạch bảo vệ sử dùng nguồn pin dự trữ.
16.2 Nguyên lý hoạt động
DS1307 hoạt động như một slaver trên bus dữ liệu nối tiếp. Để truy xuất
nội dung ta phải thiết lập một điều kiện Start và cung cấp mã nhận dạng của IC
(Device Identification Code) theo sau bởi thanh ghi địa chỉ. Các thanh ghi theo sau
được truy xuất tuần tự cho đến khi gặp tín hiệu Stop.
Khi VCC = 1.25Vbat thì DS1307 sẽ kết thúc việc truy xuất và reset lại bộ đếm địa chỉ. Các Input sẽ không được nhận ra tại thời điểm này để ngăn ngừa một
số lượng lớn dữ liệu được ghi tới DS1307 từ hệ thống bên ngoài. Khi VCC < Vbat thì ic này sẽ chuyển sang mode sử dụng pin dự trữ. Khi nguồn chính được bật lên thì IC này sẽ chuyển từ dùng nguồn pin sang dùng nguồn chính. Hình sau mô tả
16.3 Các tín hiệu Input và Output
VCC, GND : Nguồn DC được cung cấp cho IC qua những chân này. Khi gắn vào nguồn 5V thì IC này có thể đọc ghi bình thường. Nhưng khi nguồn giảm
xuống còn 3V thì việc đọc ghi sẽ không được phép. Tuy nhiên, các chức năng của
timer vẫn tiếp tục với nguồn cung cấp thấp. Khi Vcc giảm xuống dưới VBAT thì
RAM và timekeeper được chuyển qua sử dụng nguồn cung cấp tại VBAT.
VBAT : Cung cấp nguồn dữ trữ 3V. Để hoạt động ở chế độ sử dụng nguồn
Vbat thì 2.0V < Vbat < 3.5V. Khi VCC gần bằng 1.25VBAT thì chúng ta sẽ không được phép truy xuất vào RTC (Real time clock) và Ram bên trong của IC.
SCL (Serial Clock Input) : SCL được dùng để đồng bộ dữ liệu trên đường
truyền nối tiếp.
SDA (Serial Data Input/Output) : SDA là chân I/O. SDA là chân Open drain nên cần có điện trở kéo lên ở bên ngoài.
BKIT HARDWARE CLUB www.bkit4u.com 103
32khz. Chân này cũng là chân Open drain nên cũng yêu cầu có điện trở kéo lên nguồn ở bên ngoài. SQW/OUT sẽ hoạt động khi có nguồn cung cấp vào cho dù đó
là nguồn VCC hay là VBAT.
X1, X2 : Kết nối với thạch anh 32.768Khz. Mạch tạo xung bên trong được
thiết kế để hoạt động với thạch anh và tụ CL = 12.5 pF.
16.4 RTC và sơ đồ địa chỉ Ram
Sơ đồ địa chỉ của RTC và các thanh ghi Ram của DS1307 như ở hình dưới.
Các thanh ghi RTC được định địa chỉ từ 00h đến 07h. Các thanh ghi Ram được định địa chỉ tiếp theo sau đó và từ 08h đến 3fh. Trong khi truy suất nhiều byte và khi con trỏ địa chỉ chỉ tới ô 3fh, vị trí cuối của vùng nhớ Ram, thì nó sẽ quay lại địa chỉ 00h để truy xuất tiếp.
16.5 Thông tin thời gian và lịch
Thông tin thời gian và lịch được chứa trong trong các thanh ghi tương ứng. Các thanh ghi RTC như ở hình trên. Thời gian và lịch được set hoặc khởi tạo bằng cách ghi ra các byte thanh khi tương ứng. Nội dung của các thanh ghi thời gian và lịch được định dạng theo kiểu BCD. Bit 7 của thanh ghi 0 là clock halt bit (CH).
Khi bít này được set lên 1 thì mạch dao động sẽ bị ẩn không được sử dụng nữa,
khi clear xuống 0 thì mạch dao động sẽ được kích hoạt trở lại.
DS1307 có thể chạy ở chế độ 12h hay 24h. Bít thứ 6 của thanh ghi hours được định nghĩa để set xem sử dụng IC này ở chế độ nào. Khi bit này bằng 1 thì chế độ 12h được chọn. Trong chế độ 12h thì bit 5 chỉ AM/PM (PM khi bit này là 1). Trong chế độ 24h, thì bít 5 là bít thứ 2 của 10hour (20:23).
16.6 Thanh ghi điều khiển (Control Register)
Thanh ghi điều khiển của DS1307 được sử dụng để điều khiển hoạt động
của chân SQW/OUT.
Out (Output control) : Bít này điều khiển mức logic xuất ra trên chân SQW/OUT khi mà sóng vuông không được kích hoạt. Nếu SQWE = 0, thì mức
logic trên chân SQW/OUT là 1 nếu OUT = 1, và là 0 nếu OUT = 0.
SQWE (Square Wave Enabel) : Bít này khi được set lên mức 1 thì sẽ kích
hoạt mạch dao động xuất ra ngoài. Tần số của sóng vuông phụ thuộc vào giá trị ở
bít RS0 và RS1. Với sóng vuông xuất ra 1Hz thì thanh ghi clock sẽ cập nhập dữ
liệu khi có cạnh xuống của xung vuông.
RS (Rate select) : Những bit này điều khiển tần số của sóng vuông được
xuất ra trên chân SQW/OUT. Bảng sau liệt kê ra các tần số có thể được chọn bởi 2
bit RS này.
16.7 Bus dữ liệu nối tiếp.
DS1307 hỗ trợ truyền dữ liệu 2 chiều và giao thức truyền dữ liệu I2C trên 2 dây này. Thiết bị gởi dữ liệu trên bus gọi là transmitter và thiết bị nhận dữ liệu gọi
là receiver. Thiết bị điều khiển các message gọi là master. Thiết bị được điều
khiển bởi master thì gọi là slaver.
Bus dữ liệu được điều khiển bởi master. Bên cạnh đó nó cũng có nhiệm vụ
tạo xung clock trên đường tín hiệu SCL, điều khiển truy xuất bus, và tạo các tín
BKIT HARDWARE CLUB www.bkit4u.com 105
Các trạng thái của bus:
Bus không bận : khi cả 2 đường giữ tín hiệu ở mức high.
Bắt đầu truyền dữ liệu (start condition) : Thay đổi trạng thái trên đường dữ
liệu từ High xuống Low, trong khi đường clock ổn định ở mức high được định
nghĩa là một tín hiệu Start.
Kết thúc truyền dữ liệu (stop condition) : Thay đổi trạng thái trên đường dữ
liệu từ Low lên High, trong khi đường clock ổn định ở mức high thì được định
nghĩa là một tín hiệu Stop.
Dữ liệu hợp lệ : Trạng thái của đường dữ liệu biểu diễn dữ liệu hợp lệ khi
theo sau bởi tín hiệu START, đường dữ liệu ổn định trong khoảng thời gian mà tín hiệu clock ở mức High. Dữ liệu trên đường dữ liệu phải được thay đổi trong
khoảng thời gian mà tín hiệu clock ở mức Low.
Mỗi khi truyền dữ liệu điề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. Thông tin được truyền và mỗi
lần truyền receiver gởi thêm ack ở bít thứ 9. Để cho biết là đã nhận xong một byte
dữ liệu.
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:
vMaster gửi tín hiệu Start.
vMaster 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.
vMaster gửi địa chỉ pointer dữ liệu cần ghi, chẳng hạn là 0x00 (register pointer, word address)
vMaster gửi các byte data cần ghi.
vMaster 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). Quá 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:
vMaster gửi tín hiệu start.
vMaster gửi địa chỉ DS1307 + R/W = 0 : 0xD0.
vMaster gửi byte ghi vào register pointer : 0x00.
vMaster gửi tín hiệu start
vMaster gửi địa chỉ DS1307 + R/W = 1 : 0xD1.
vMaster đọ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.
vMaster 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 }