Professional Documents
Culture Documents
Hình 1.1. Một số chuẩn giao tiếp nối tiếp phổ thông.(Nguồn DesignCon 2003)
6
Bảng 1.1. Thông số của một chuẩn giao tiếp nối tiếp (Nguồn DesignCon 2003)
7
Hình 1.2. Kiến trúc của bus I2C
Trong bus I2C có nhiều thiết bị cùng được kết nối, mỗi thiết bị sẽ có một địa chỉ
duy nhất. Một thiết bị khi kết nối vào bus I2C ngoài một địa chỉ duy nhất để phân biệt,
nó còn được cấu hình là thiết bị chủ (Master) hoặc tớ (Slave). Trong giao tiếp I2C
quyền điều khiển thuộc về thiết bị chủ. Thiết bị chủ nắm vai trò tạo xung đồng hồ cho
hệ thống, khi thiết bị chủ giao tiếp với thiết bị tớ, thiết bị chủ có nhiệm vụ tao xung
đồng hồ và quản lý địa chỉ của thiết bị tớ trong suốt quá trình giao tiếp. Thiết bị chủ
giữ vai trò chủ động còn thiết bị tớ giữ vai trò bị động trong khi giao tiếp.
1.2. Module I2C trên vi điều khiển PIC18F4520
PIC18F4520 được trang bị module I2C với 02 chế độ hoạt động Master và Slave.
Các đường dây SDA và SCL tương ứng với các chân RC4/SDI và RC3/SCL/SCK
trên vi điều khiển (hình 1.3)
8
Hình 1.3. Sơ đồ khối I2C tích hợp trên PIC18F4520
1.2.1.Các thanh ghi.
Khối MSSP (Master Synchronous Serial Port) có 6 thanh ghi để điều khiển chế
độ I2C, bao gồm:
+ Thanh ghi điều khiển MSSP1 (SSPCON1)
+ Thanh ghi điều khiển MSSP2 (SSPCON2)
+ Thanh ghi trạng thái MSSP (SSPSTAT)
+ Thanh ghi đệm truyền nhận nối tiếp (SSPBUF)
+ Thanh ghi dịch MSSP (SSPSR).
+ Thanh ghi địa chỉ MSSP (SSPADD)
Thanh ghi SSPBUF chứa dữ liệu sẽ được truyền đi hoặc nhận được và đóng vai
trò như một thanh ghi đệm cho thanh ghi dịch dữ liệu SSPSR. Thanh ghi SSPSR
không thể truy xuất trực tiếp được, muốn truy xuất nó ta phải thông qua thanh ghi
SSPBUF.
Thanh ghi SSPADD chứa địa chỉ của thiết bị ngoại vi cần truy xuất dữ liệu của I2C
khi hoạt động ở chế độ tớ. Khi hoạt động ở chế độ master, thanh ghi SSPADD chứa
giá trị tạo ra tốc độ baud cho xung clock dùng để truyền nhận dữ liệu.
9
Trong quá trình nhận dữ liệu, cả 2 thanh ghi SSPSR và SSPBUF tạo thành bộ đệm
nhận dữ liệu 2 lớp. khi SSPSR thực hiện nhận 1 byte hoàn tất, nó sẽ ghi tới thanh ghi
SSPBUF và cờ ngắt SSPIF được đặt.
Trong quá trình truyền, chúng không đóng vai trò là bộ đệm dữ liệu 2 lớp. Khi ghi
tới SSPBUF lúc này sẽ ghi tới cả SSPBUF và SSPSR.
Thanh ghi SSPSTAT : Thanh ghi trạng thái MSSP(chế độ I2C)
10
0= ghi.
Trong chế độ master :
1= cho phép truyền.
0= không cho phép truyền.
Bit 1 UA : bit cập nhật địa chỉ ( chế độ tớ 10 bit )
1= cập nhật địa chỉ vào thanh ghi SSPADD
0= không cập nhật địa chỉ
Bit 0 BF : bit báo trạng thái đầy bộ đệm
Trong chế độ truyền :
1= SSPBUF đầy.
0= SSPBUF rỗng.
Trong chế độ nhận :
1= SSPBUF đầy ( không bao gồm ACK và bit stop ).
0= SSPBUF rỗng ( không bao gồm ACK và bit stop ).
Thanh ghi SSPCON1 : Thanh ghi điều khiển 1 (chế độ I2C)
11
1= một byte dữ liệu mới được nhận trong khi dữ liệu cũ vẫn còn trong thanh ghi
SSPBUF.
0= không có tràn.
Chế độ truyền :
Không sử dụng đến bit này.
Bit 5 SSPEN : bit cho phép cổng nối tiếp
1= cho phép cổng nối tiếp và thiết lập SDA và SCL là các chân của cổng nối tiếp.
0= không cho phép cổng nối tiếp và thiết lập chúng như các chân vào ra.
Bit 4 CKP : SCK : bit điều khiển tác động
Chế độ tớ :
1= cho xung clock tác động
0= giữ xung clock ở mức thấp (để đảm bảo thời gian thiết lập dữ liệu)
Chế độ master :
Không sử dụng trong chế độ này.
Bit 3-0 SSPM3 : SSPM0 : các bit lựa chọn chế độ hoạt động cổng nối tiếp
1111 : I2C chế độ tớ, 10 bit địa chỉ, cho phép ngắt khi phát hiện bit start và bit
stop.
1110 : I2C chế độ tớ, 7 bit địa chỉ, cho phép ngắt khi phát hiện bit start và bit
stop.
1011 : dùng phần mềm điều khiển chế độ master (chế độ tớ không dùng)
1000 : I2C chế độ master, clock = Fosc/(4*(SSPADD+1))
0111 : I2C chế độ tớ , 10 bit địa chỉ.
0110 : I2C chế độ tớ, 7 bit địa chỉ.
Thanh ghi SSPCON2 : Thanh ghi điều khiển MSSP 2(chế độ I2C)
Bit 7 GCEN : bit cho phép gọi chung. (duy nhất trong chế độ tớ )
1= cho phép ngắt khi địa chỉ 0000h được nhận vào trong SSPSR.
0= không cho phép gọi địa chỉ trên.
Bit 6 ACKSTAT : bit trạng thái ACK (duy nhất trong chế độ master:
truyền dữ liệu )
12
1= chưa nhận được xung ACK từ tớ
0= nhận được xung từ tớ.
Bit 5 ACKDT : bit dữ liệu ACK (chỉ có trong chế độ master : nhận dữ liệu )
1= không có xung ACK
0= có xung ACK
Bit 4 ACKEN : bit cho phép xung ACK.(chỉ có trong chế độ master: nhận dữ liệu )
1= khởi tạo xung ACK trên các chân SDA và SCL. Bit dữ liệu truyền ACKDT ).
Tự động xóa bằng phần cứng.
0= không cho phép xung ACK.
Bit 3 RCEN : bit cho phép nhận (duy nhất trong chế độ master )
1= cho phép nhận dữ liệu.
0= không cho phép nhận dữ liệu.
Bit 2 PEN : bit cho phép điều kiện stop.
1= khởi tạo điều kiện stop trên các chân SDA và SCL. Tự động xóa bằng phần
cứng.
0= không cho phép điều kiện stop.
Bit 1 RSEN : bit cho phép điều kiện start (duy nhất trong chế độ master )
1= khởi tạo điều kiện start trên các chân SDA và SCL. Tự động xóa bằng phần
cứng.
0= không cho phép điều kiện start.
Bit 0 SEN : bit cho phép điều kiện start hoặc cho phép khóa.
Chế độ master :
1= khởi tạo điều kiện start trên các chân SDA và SCL. Tự động xóa bằng phần
cứng.
0= không cho phép điều kiện start.
Chế độ tớ :
1= cho phép khóa xung clock từ chế độ tớ truyền và nhận.
0= không cho phép khóa xung clock.
13
Chế độ master được xác lập bằng cách đưa các giá trị thích hợp vào các bit
SSPM của thanh ghi SSPCON và set bit SSPEN. Ở chế độ Master, các chân SCK và
SDA sẽ được điều khiển bởi phần cứng của MSSP.
14
Hình 1.5. Sơ đồ khối BRG của I2C Chế độ master.
Các giá trị cụ thể của tần số xung nối tiếp do BRG tạo ra được cho như sau:
Trong đó giá trị BRG là giá trị được lấy từ 7 bit thấp của thanh ghi SSPADD. Do
I2C ở chế độ Chế độ master, thanh ghi SSPADD sẽ không được sử dụng để chứa địa
chỉ, thay vào đó chức năng của SSPADD là thanh ghi chứa giá trị của BRG.
Để tạo được điều kiện Start, trước hết cần đưa hai chân SCL và SDA lên mức
logic cao và bit SEN (SSPCON2<0>) phải được set . Khi đó BRG sẽ tự động đọc giá
trị 7 bit thấp của thanh ghi SSPADD và bắt đầu đếm. Sau khoảng thời gian TBRG, chân
SDA được đưa xuống mức logic thấp. Trạng thái chân SDA ở mức logic thấp và chân
SCL ở mức logic cao chính là điều kiện Start của I2C Chế độ master. Khi đó bit S
(SSPSTAT<3>) sẽ được set. Tiếp theo BRG tiếp tục lấy giá trị từ thanh ghi SSPADD
để tiếp tục quá trình đếm, bit SEN được tự động xóa và cờ ngắt SSPIF được đặt bằng
một. Trong trường hợp chân SCL và SDA ở trạng thái logic thấp, hoặc là trong quá
trình tạo điều kiện Start, chân SCL được đưa về trạng thái logic thấp trước khi chân
SDA được đưa về trang thái logic thấp, điều kiện Start sẽ không được hình thành, cờ
ngắt BCLIF sẽ được đặt bằng một và I2C sẽ ở trạng thái tạm ngưng hoạt động (Idle).
15
Hình 1.6. Giản đồ xung I2C Chế độ master trong quá trình tạo điều kiện Start
Tín hiệu Stop sẽ được đưa ra chân SDA khi kết thức dữ liệu bằng cách đặt bằng
một bit PEN (SSPCON2<2>). Sau cạnh xuống của xung clock thứ 9 và với tác động
của bit điều khiển PEN, chân SDA cũng được đưa xuống mức thấp, BRG lại bắt đầu
quá trình đếm. Sau một khoảng thời gian TBRG, chân SCL được đưa lên mức logic cao
và sau một khoảng thời gian TBRG nữa chân SDA cũng được đưa lên mức cao. Ngay
tại thời điểm đó bit P (SSPSTAT<4>) được đặt bằng 1, nghĩa là điều kiện Stop đã
được tạo ra. Sau một khoảng thời gian TBRG nữa, bit PEN tự động được xóa và cờ ngắt
SSPIF được đặt bằng 1.
Hình 1.7. Giản đồ xung I2C Chế độ master trong quá trình tạo điều kiện Stop.
Để tạo được điều kiện Start lặp lại liên tục trong quá trình truyền dữ liệu, trước
hết cần đặt bằng một bit RSEN (SSPCON2<1>). Sau khi đặt bằng một bit RSEN, chân
SCL được đưa xuống mức logic thấp, chân SDA được đưa lên mức logic cao, BRG lấy
giá trị từ thanh ghi SSPADD vào để bắt đầu quá trình đếm. Sau khoảng thời gian
TBRG, chân SCL cũng được đưa lên mức logic cao trong khoảng thời gian TBRG tiếp
theo. Trong khoảng thời gian TBRG kế tiếp, chân SDA lại được đưa xuống mức logic
16
thấp trong khi SCL vẫn được giữ ở mức logic cao. Ngay thời điểm đó bit S
(SSPSTAT<3>) được đặt bằng 1 để báo hiệu điều kiện Start được hình thành, bit
RSEN tự động được xóa và cờ ngắt SSPIF sẽ được đặt bằng một sau một khoảng thời
gian TBRG nữa. Lúc này địa chỉ của I2C Slave có thể được đưa vào thanh ghi
SSPBUF, sau đó ta chỉ việc đưa tiếp địa chỉ hoặc dữ liệu tiếp theo vào thanh ghi
SSPBUF mỗi khi nhận được tín hiệu ACK từ I2C Slave, I2C Master sẽ tự động tạo tín
hiệu Start lặp lại liên tục cho quá trình truyền dữ liệu liên tục. Cần chú ý là bất cứ một
trình tự nào sai trong quá trình tạo điều kiện Start lặp lại sẽ làm cho bit BCLIF được
đặt bằng một và I2C được đưa về trạng thái “Idle”.
Hình 1.8. Giản đồ xung I2C Chế độ master trong quá trình tạo điều kiện Start
liên tục.
Xét quá trình truyền dữ liệu, xung clock sẽ được đưa ra từ chân SCL và dữ liệu
được đưa ra từ chân SDA. Byte dữ liệu đầu tiên phải là byte địa chỉ xác định I2C Slave
cần giao tiếp và bit R/W (trong trường hợp này R/W = 0). Đầu tiên các giá trị địa chỉ
sẽ được đưa vào thanh ghi SSPBUF, bit BF tự động được đặt bằng một lên 1 và bộ
đếm tạo xung clock nối tiếp BRG (Baud Rate Generator) bắt đầu hoạt động. Khi đó
từng bit dữ liệu (hoặc địa chỉ và bit ACK) sẽ được dịch ra ngoài theo từng cạnh xuống
của xung clock sau khi cạnh xuống đầu tiên của chân SCL được nhận diện (điều kiện
Start), BRG bắt đầu đếm ngược về 0. Khi tất cả các bit của byte dữ liệu được đã được
đưa ra ngoài, bộ đếm BRG mang giá trị 0. Sau đó, tại cạnh xuống của xung clock thứ 8,
I2C Master sẽ ngưng tác động lên chân SDA để chờ đợi tín hiệu từ I2C Slave . Tại
17
cạnh xuống của xung clock thứ 9, I2C Master sẽ lấy mẫu tín hiệu từ chân SDA để
kiểm tra xem địa chỉ đã được I2C Slave nhận dạng chưa, trạng thái được đưa vào bit
ACKSTAT (SSPCON2<6>). Cũng tại thời điểm cạnh xuống của xung clock thứ 9, bit
BF được tự động clear, cờ ngắt SSPIF được đặt bằng một và BRG tạm ngưng hoạt
động cho tới khi dữ liệu hoặc địa chỉ tiếp theo được đưa vào thanh ghi SSPBUF, dữ
liệu hoặc địa chỉ sẽ tiếp tục được truyền đi tại cạnh xuống của xung clock tiếp theo.
Hình 1.9. Giản đồ xung I2C Chế độ master trong quá trình truyền dữ liệu.
Xét quá trình nhận dữ liệu ở chế độ I2C Chế độ master. Trước tiên ta cần đặt
bằng một bit cho phép nhận dữ liệu RCEN (SSPCON2<3>). Khi đó BRG bắt đầu quá
trình đếm, dữ liệu sẽ được dịch vào I2C Master qua chân SDA tại cạnh xuống của
chân SCL. Tại cạnh xuống của xung clock thứ 8, bit cờ hiệu cho phép nhận RCEN tự
động được xóa, dữ liệu trong thanh ghi SSPSR được đưa vào thanh ghi SSPBUF, cờ
hiệu BF được đặt bằng một, cờ ngắt SSPIF được đặt bằng một, BRG ngưng đếm và
chân SCL được đưa về mức logic thấp. Khi đó MSSP ở trạng thái tạm ngưng hoạt
động để chờ đợi lệnh tiếp theo. Sau khi đọc giá trị thanh ghi SSPBUF, cờ hiệu BF tự
động được xóa về 0.
18
Hình 1.10. Giản đồ xung I2C Chế độ master trong quá trình nhận dữ liệu.
19
1.2.2.2. Chế độ slave
Để hoạt động được ở chế độ này, trước tiên là phải đăt các chân SCL và SDA
thành chiều vào. I2C của vi điều khiển sẽ được điều khiển bởi một vi điều khiển hoặc
một thiết bị ngoại vi khác thông qua các địa chỉ. Khi địa chỉ này chỉ đến vi điều khiển,
thì tại thời điểm này và tại thời điểm dữ liệu đã được truyền nhận xong sau đó, vi điều
khiển sẽ tạo ra xung để báo hiệu kết thúc dữ liệu, giá trị trong thanh ghi SSPSR sẽ
được đưa vào thanh ghi SSPBUF. Tuy nhiên xung sẽ không được tạo ra nếu một trong
các trường hợp sau xảy ra:
- Bit BF (SSPSTAT<0>) báo hiệu buffer đầy đã được đặt bằng 1 trước khi quá
trình truyền nhận xảy ra.
- Bit SSPOV (SSPCON<6>) được đặt bằng 1 trước khi quá trình truyền nhận xảy
ra (SSPOV được đặt bằng 1 trong trường hợp khi một byte khác được nhận vào trong
khi dữ liệu trong thanh ghi SSPBUF trước đó vẫn chưa được lấy ra).
Trong các trường hợp trên, thanh ghi SSPSR sẽ không đưa giá trị vào thanh ghi
SSPBUF, nhưng bit SSPIF (PIR1<3>) sẽ được đặt bằng 1. Để quá trình truyền nhận
dữ liệu được tiếp tục, cần đọc dữ liệu từ thanh ghi SSPBUF vào trước, khi đó bit BF sẽ
tự động được xóa, còn bit SSPOV phải được xóa bằng chương trình.
Khi MSSP được kích hoạt, nó sẽ chờ tín hiệu để bắt đầu hoạt động. Sau khi nhận
được tín hiệu bắt đầu hoạt động (cạnh xuống đầu tiên của pin SDA), dữ liệu 8 bit sẽ
được dịch vào thanh ghi SSPSR. Các bit đưa vào sẽ được lấy mẫu tại cạnh lên của
xung clock. Giá trị nhận được từ thanh ghi SSPSR sẽ được so sánh với giá trị trong
thanh ghi SSPADD tại cạnh xuống của xung clock thứ 8. Nếu kết quả so sánh bằng
nhau, tức là I2C Master chỉ định đối tượng giao tiếp là vi điều khiển đang ở chế độ
Slave mode (ta gọi hiện tượng này là address match), bit BF và SSPOV sẽ được xóa
về 0 và gây ra các tác động sau:
- Giá trị trong thanh ghi SSPSR được đưa vào thanh ghi SSPBUF.
- Bit BF tự động được đặt bằng 1.
- Một xung ACK được tạo ra.
- Cờ ngắt SSPIF được đặt bằng 1 (ngắt được kích hoạt nếu được cho phép trước
đó) tại cạnh xuống của xung clock thứ 9.
Qúa trình nhận dạng địa chỉ của MSSP ở chế độ I2C Slave mode 10 bit địa chỉ
như sau:
- 2 bit MSB của 10 bit địa chỉ được nhận trước, bit SSPIF, BF và UA
(SSPSTAT<1>) được đặt bằng 1.
(byte địa chỉ đầu tiên có định dạng là ‘11110 A9 A8 0’) .
20
- Cập nhật vào 8 bit địa chỉ thấp của thanh ghi SSPADD, bit UA sẽ được xóa bởi
vi điều khiển để khởi tạo xung clock ở chân SCL sau khi quá trình cập nhật hoàn tất.
- Đọc giá trị thanh ghi SSPBUF (bit BF sẽ được xóa về 0) và xóa cờ ngắt SSPIF.
- Nhận 8 bit địa chỉ cao, bit SSPIF, BF và UA được đặt bằng 1.
- Cập nhật 8 bit địa chỉ đã nhận được vào 8 bit địa chỉ cao của thanh ghi
SSPADD, nếu địa chỉ nhận được là đúng (address match), xung clock ở chân SCL
được khởi tạo và bit UA được đặt bằng 1.
- Đọc giá trị thanh ghi SSPBUF (bit BF sẽ được xóa về 0) và xóa cờ ngắt SSPIF.
- Nhận tín hiệu Start.
- Nhận byte địa chỉ cao (bit SSPIF và BF được đặt bằng 1).
- Đọc giá trị thanh ghi SSPBUF (bit BF được xóa về 0) và xóa cờ ngắt SSPIF.
21
Hình 1.12. Nhận dữ liệu ở chế độ 7bit địa chỉ
1.3. Các hàm trong thư viện i2c.h
Thư viện i2c.h cung cấp một số hàm cho phép người dùng có thể đặt các thông
số cho module I2C cũng như truyền/nhận dữ liệu mà không cần truy xuất các thanh
ghi (bảng 1.2).
Bảng 1.2. Các hàm cơ bản sử dung cho giao tiếp I2C
Tên hàm Mô tả
AckI2C Tạo bit Ack
CloseI2C Đóng (cấm) chức năng của module I2C trên vi điều khiển.
DataRdyI2C Kiểm tra byte dữ liệu mới đã có trong bộ bộ đệm?
getsI2C Đọc/nhận một chuỗi các byte dữ liệu.
IdleI2C Hàm chờ cho đến khi Bus I2C hết bận.
NotAckI2C Tạo bit NotAcK
OpenI2C Khởi tạo module SPI
putsI2C Truyền một chuỗi các byte dữ liệu.
ReadI2C Đọc/nhận một byte dữ liệu.
RestartI2C Khởi tạo lại giao tiếp
StartI2C Khởi tạo giao tiếp
StopI2C Kết thúc giao tiếp
WriteI2C Truyền một byte dữ liệu.
22
• Hàm AckI2C
Chức năng : Tạo xung phản hồi Ack
Nguyên mẫu : void AckI2C( void );
• Hàm DataRdyI2C
Chức năng : Kiểm tra bộ đệm (SSPBUF) có chứa byte dữ liệu hay không.
Nguyên mẫu : void DataRdyI2C ( void );
Chú thích : Hàm này được sử dụng để kiểm tra trước khi đọc một byte dữ liệu
từ bộ đệm.
Giá trị trả về : Bằng 1 nếu bộ đệm chứa một byte dữ liệu.
Bằng 0 nếu bộ đệm không chứa một byte dữ liệu (chưa nhận về).
23
SLAVE_10 Chế độ slave, 10 bit địa chỉ.
MASTER Chế độ master.
slew: Biến quy định tốc độ truyền dữ liệu của Bus I2C:
SLEW_OFF Chế đố tốc độ truyền tiêu chuẩn 100kHz
SLEW_OFF Chế đố tốc độ truyền nhanh 400kHz
Ví dụ: OpenI2C(MASTER, SLEW_OFF);
Sẽ khởi tạo module I2C ở chế độ master, tốc độ truyền dữ liệu 100kHz.
• Hàm WriteI2C
Chức năng : Ghi (truyền) một byte dữ liệu lên bus I2C.
Nguyên mẫu : unsigned char WriteI2C(
unsigned char data_out );
Ví dụ: WriteI2C(0x41);
• Hàm putsI2C
Chức năng : Đọc một chuỗi các byte dữ liệu trên bus SPI.
Nguyên mẫu : void putsI2C( unsigned char *wrptr );
Trong đó: wrptr là con trỏ chỉ tới các byte dữ liệu cần truyền.
Ví dụ: unsigned char wrptr[] = “Hello!”;
putsI2C(wrptr);
1.4. Thiết kế ứng dụng sử dụng I2C
24
Hình 1.13. Mạch điện giao tiếp DS1307 với vi điều khiển
Người dùng có thể cài đặt thời gian thực cho vi mạch bằng cách ghi thông tin
thời gian vào các thanh ghi tương ứng của DS1307 (hình 1.14). Sau khi được cài đặt,
DS1307 sẽ trở thành một đồng hồ thời gian thực (real time clock). Thông tin về thời
gian được đọc bằng cách truy xuất các thanh ghi tương ứng.
29
Chuyển từ số nguyên sang BCD:
int int_bcd(int x)
{
char
N[10]={0X00,0X01,0X02,0X3,0X4,0X05,0X06,0X07,0X08,0X09};
int a,b;
a=x/10;
b=x%10;
return ((N[a]<<4)&0xf0)+N[b] ;
}
Ví dụ 1. Cho sơ mạch điện như hình 1.17. Viết chương trình đọc các thanh ghi
giây (địa chỉ 0x00), phút, giờ và hiển thị trên LCD.
U3 DS1307 VCC
SCL 6 8
SCL VCC
SDA 5 3
SDA VBAT
1 Y1 1
X1
2 7 1
X2 SQW/OUT PIN
2 4 3V
GND
32.768KHz C7 2
VCC
104
31
lcd_write(*str);
str++;
}
Delay1KTCYx(10);
}
/******************************************************/
//Cac ham chuyen doi so nguyen, BCD
char bcd_int(int x)
{
return (((x>>4)&0x0f)*10)+(x&0x0f);
}
int int_bcd(int x)
{
char
N[10]={0X00,0X01,0X02,0X3,0X4,0X05,0X06,0X07,0X08,0X09};
int a,b;
a=x/10;
b=x%10;
return ((N[a]<<4)&0xf0)+N[b] ;
}
/******************************************************/
void main(void)
{
unsigned int a;
TRISA=0X00;
TRISB=0X0F;
TRISC=0X00;
TRISD=0X00;
TRISE=0X00;
ADCON1=0X0F;
OpenI2C(MASTER,SLEW_OFF); //master mode, clock=100Khz
lcd_int();
while(1)
{
StartI2C();
IdleI2C();
WriteI2C(0xd0);
IdleI2C();
32
WriteI2C(0x00);
IdleI2C();
RestartI2C();
IdleI2C();
WriteI2C(0xd1);
IdleI2C();
s=ReadI2C();
IdleI2C();
AckI2C();
IdleI2C();
m=ReadI2C();
IdleI2C();
AckI2C();
IdleI2C();
h=ReadI2C();
IdleI2C();
NotAckI2C();
IdleI2C();
StopI2C();
s=bcd_int(s);
m=bcd_int(m);
h=bcd_int(h);
lcd_cmd(0x80);
sprintf(&M[0],"TIME:
%d%d:%d%d:%d%d",h/10,h%10,m/10,m%10,s/10,s%10);
lcd_str(&M[0]);
}
}
Ví dụ 2. Đặt thời gian cho DS1307
Để thuận tiện cho quá trình đặt thời gian, chúng ta xây dựng hàm set_time dùng
để đặt giờ, phút, giây và ngày, tháng, năm. Lưu ý: Thanh ghi năm (year) chỉ gồm 8bit
33
nên chỉ có thể đặt 2 số hàng chục và hàng đơn vị của năm hiện tại. Ví dụ: năm 2015 có
thể ghi số 15 vào thanh ghi này.
void set_time(unsigned int s,unsigned int m,unsigned int
h,unsigned int dd,unsigned int mm,unsigned int yy )
{
s=int_bcd(s);
m=int_bcd(m);
h=int_bcd(h);
dd=int_bcd(dd);
mm=int_bcd(mm);
yy=int_bcd(yy);
StartI2C();
IdleI2C();
WriteI2C(0xD0);
IdleI2C();
WriteI2C(0x00);
IdleI2C();
WriteI2C(s);
IdleI2C();
WriteI2C(m);
IdleI2C();
WriteI2C(h);
IdleI2C();
StopI2C();
//bat dau lai qua trinh ghi
StartI2C();
IdleI2C();
WriteI2C(0xD0);
IdleI2C();
34
WriteI2C(dd);
IdleI2C();
WriteI2C(mm);
IdleI2C();
WriteI2C(yy);
IdleI2C();
StopI2C();
}
Tương tự, chúng ta có thể xây dựng hàm đọc thời gian:
void get_time(void)
{
StartI2C();
IdleI2C();
WriteI2C(0xd0);
IdleI2C();
WriteI2C(0x00);
IdleI2C();
RestartI2C();
IdleI2C();
WriteI2C(0xd1);
IdleI2C();
s=ReadI2C();
IdleI2C();
AckI2C();
IdleI2C();
m=ReadI2C();
IdleI2C();
AckI2C();
IdleI2C();
h=ReadI2C();
IdleI2C();
35
NotAckI2C();
IdleI2C();
StopI2C();
//bat dau lai qua trinh doc
StartI2C();
IdleI2C();
WriteI2C(0xd0);
IdleI2C();
RestartI2C();
IdleI2C();
WriteI2C(0xd1);
IdleI2C();
NotAckI2C();
IdleI2C();
StopI2C();
}
Với các hàm đã xây dựng, chúng ta có thể tùy ý đặt/đọc thời gian thực, dưới đây
là chương trình minh họa:
void main(void)
36
{
//cac lenh khoi tao
…
//dat thoi gian:
//gio:phut:giay=06:06:06
//ngay/thang/nam=23/12/15
set_time(6,6,6,23,12,15);
while(1)
{
get_time();
s=bcd_int(s);
m=bcd_int(m);
h=bcd_int(h);
dd=bcd_int(dd);
mm=bcd_int(mm);
yy=bcd_int(yy);
//hien thi gio, phut, giay tren dong thu nhat
lcd_cmd(0x80);
sprintf(&M[0],"TIME:
%d%d:%d%d:%d%d",h/10,h%10,m/10,m%10,s/10,s%10);
lcd_str(&M[0]);
//hien thi ngay, thang, nam tren dong thu hai
lcd_cmd(0xc0);
sprintf(&M[0],"DATE:
%d%d/%d%d/20%d%d",dd/10,dd%10,mm/10,mm%10,((yy%1000)%100)/
10,((yy%1000)%100)%10);
lcd_str(&M[0]);
}
}
Chương trình chạy cho ra kết quả trên LCD như sau:
TIME: 06:06:06
DATE: 23/12/2015
37
- Có thể hoạt động ở dải điện áp rộng (2,7÷5,5V hoặc 1,8÷3,6V)
- Có thể hoạt động với xung clock ở 03 tần số: 1Mhz, 400Khz và 100Khz.
- Có chức năng bảo vệ dữ liệu bằng cả phần cứng và phần mềm. Dữ liệu được
ghi có thể lưu được trong 40 năm.
39
Hình 1.20: Thao tác page write
40
WriteI2C(0b10100001);
IdleI2C();
Đọc byte dữ liệu từ ô nhớ tương ứng
a=ReadI2C(); //byte dữ liệu chứa trong biến a.
IdleI2C();
Tạo tín hiệu NotAck, stop:
NotAckI2C();
IdleI2C();
StopI2C();
Khi cần đọc dữ liệu tại ô nhớ có địa chỉ trùng với địa chỉ trong word address
(current address read) có thể bỏ qua bước 1,2,3 (hình 1.22). Khi cần đọc liên tiếp các
ô nhớ (Sequential Read) có thể thực hiện các thao tác đọc ngay sau bước 6 (
41
TRISA=0X00;
TRISB=0X0F;
TRISC=0X00;
TRISD=0X00;
TRISE=0X00;
ADCON1=0X0F;
OpenI2C(MASTER,SLEW_OFF);
lcd_int();
//ghi vao ROM (byte write)
//(1)
StartI2C();
IdleI2C();
//(2)
WriteI2C(0b10100000); //A0A1=00, R/W=0
IdleI2C();
//(3)
WriteI2C(0x00);
IdleI2C();
WriteI2C(0x06);
IdleI2C();
//(4)
WriteI2C(0x41); //ma ky tu ‘A’
IdleI2C();
//(5)
StopI2C();
while(1)
{
//doc tu ROM
//(1)
StartI2C();
IdleI2C();
//(2)
WriteI2C(0b10100000); //A0A1=00, R/W=0
IdleI2C();
//(3)
WriteI2C(0x00);
IdleI2C();
WriteI2C(0x06);
42
IdleI2C();
//(4)
RestartI2C();
IdleI2C();
//(5)
WriteI2C(0b10100001); //A0A1=00, R/W=1
IdleI2C();
//(6)Lưu ý, biến a là biến kiểu char
a=ReadI2C();
IdleI2C();
//(7)
NotAckI2C();
IdleI2C();
StopI2C();
//hien thi
lcd_cmd(0x80);
lcd_write(a);
}
}
Ví dụ 4. Ghi/đọc nhiều byte.
Để thuận tiện cho quá trình ghi/đọc EEPROM, chúng ta xây dựng các hàm:
- byte_write: Ghi từng byte
- page_write: ghi liên tiếp 64 byte
- byte_read: đọc
//Hàm ghi 1 byte vào một địa chỉ bất kỳ trên EEPROM
void byte_write(unsigned char add_high, unsigned char
add_low ,unsigned char data)
{
StartI2C();
IdleI2C();
WriteI2C(0b10100000);
IdleI2C();
WriteI2C(add_high);
IdleI2C();
WriteI2C(add_low);
IdleI2C();
WriteI2C(data);
IdleI2C();
43
StopI2C();
}
//Hàm ghi 1 page (64 byte) bắt đầu từ một địa chỉ bất kỳ trên EEPROM
void page_write(unsigned char add_high,unsigned char
add_low,unsigned char *data)
{
unsigned int i;
StartI2C();
IdleI2C();
WriteI2C(0b10100000);
IdleI2C();
WriteI2C(add_high);
IdleI2C();
WriteI2C(add_low);
IdleI2C();
for(i=0;i<=63;++i)
{
WriteI2C(*data);
IdleI2C();
data++;
}
StopI2C();
}
//Hàm đọc 1 byte từ một địa chỉ bất kỳ trên EEPROM
char byte_read(unsigned char add_high,unsigned char
add_low)
{
char a;
StartI2C();
IdleI2C();
WriteI2C(0b10100000);
IdleI2C();
WriteI2C(add_high);
IdleI2C();
WriteI2C(add_low);
IdleI2C();
RestartI2C();
IdleI2C();
44
WriteI2C(0b10100001);
IdleI2C();
a=ReadI2C();
IdleI2C();
NotAckI2C();
IdleI2C();
StopI2C();
return a;
}
Chương trình dưới đây sẽ ghi một mảng gồm 64 phần tử (từ 0 đến 63) vào
EEPROM và đọc ra 2 phần tử bất kỳ. (Khi chạy, trên LCD sẽ hiển thị số 16 (phần tử
có chỉ số bằng 16 trong mảng N) ở hàng trên và số 17 ở hàng dưới)
void main(void)
{
unsigned int a,b;
unsigned char
N[]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,4
0,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59
,60,61,62,63,64,65,66};
TRISA=0X00;
TRISB=0X0F;
TRISC=0X00;
TRISD=0X00;
TRISE=0X00;
ADCON1=0X0F;
led1=led2=led3=1;
OpenI2C(MASTER,SLEW_OFF);
lcd_int();
//ghi vào page cuối cùng trong không gian 0000÷3FFF
page_write(0x3f,0xc0,&N[0]);
while(1)
{
//đọc ra byte thứ 17 được ghi vào
a=byte_read(0x3f,0xd0);
lcd_cmd(0x80);
sprintf(&M[0],"%d",a);
lcd_str(&M[0]);
45
//đọc ra byte thứ 18 được ghi vào
b=byte_read(0x3f,0xd1);
lcd_cmd(0xc0);
sprintf(&M[0],"%d",b);
lcd_str(&M[0]);
}
}
46