Bài 8 - Giao tiếp TWI - I2C

( 43 Votes )

Nội dung Các bài cần tham khảo trước
  1. Bạn sẽ đi đến đâu.
  2. Giao diện TWI – I2C.
  3. TWI trên AVR.
  4. Điều khiển AVR TWI.
Download ví dụ

 

I. Bạn sẽ đi đến đâu.

      Bài này giới thiệu cách giao tiếp bằng truyền thông nối tiếp đồng bộ Two-Wire Serial (TWI) tương thích với chuẩn I2C. Trong bài này chúng ta sẽ khảo sát 2 mode truyền và nhận trên chip Master cùng với 2 mode truyền và nhận trên chip Slave. Công cụ chính cũng là 2 bộ phần mềm WinAVR và Proteus. Vi điều khiển ATmega32 sẽ được dùng làm minh họa.

Sau bài này, tôi hy vọng bạn có thể hiểu và thực hiện được:

-         Nguyên lý truyền thông nối tiếp TWI và I2C.
-         Sử dụng module TWI trong AVR ở các chế độ Master.
-         Sử dụng module TWI trong AVR ở các chế độ Slave.
-         Ví dụ giao tiếp giữa các AVR bằng TWI.

II. Giao diện TWI – I2C.

      TWI (Two-Wire Serial Intereafce) là một module truyền thông nối tiếp đồng bộ trên các chip AVR dựa trên chuẩn truyền thông I2C. I2C là viết tắc của từ Inter-Integrated Circuit là một chuẩn truyền thông do hãng điện tử Philips Semiconductor sáng lập và xây dựng thành chuẩn năm 1990. Phiên bản mới nhất của I2C là V3.0 phát hành năm 2007. Để hiểu thêm về I2C  bạn có thể tham khảo các tài liệu “I2C Specification” từ trang web của NXP- http://www.nxp.com (lập bởi Philips).  Trong phạm vi bài học này tôi chỉ giới thiệu giao thức TWI được giới thiệu trong datasheet của các chip AVR từ Atmel. Tuy nhiên, về cơ bản TWI trong AVR hoàn toàn tương thích  I2C, do đó tìm hiểu TWI của AVR không chỉ giúp bạn giao tiếp giữa các AVR với nhau mà có thể dùng TWI để điều khiển bất kỳ một thiết bị nào theo chuẩn I2C (các chip nhớ, bộ chuyển đổi ADC, DCA, đồng hồ thời gian thực…).
      TWI (I2C) là một truyền thông nối tiếp đa chip chủ (tạm dịch của cụm từ multi-master serial computer bus).  Khái niệm “multi-master” (tôi sẽ dùng từ tiếng anh multi-master thay vì dùng “đa chip chủ”) được hiểu là trong trên cùng một bus có thể có nhiều hơn một thiết bị làm Master, đồng thời một Slave có thể trở thành một Master nếu nó có khả năng. Ví dụ trong một mạng TWI của nhiều AVR kết nối với nhau, bất kỳ một AVR nào đều có thể trở thành Master ở một thời điểm nào đó. Tuy nhiên nếu một mạng dùng một AVR điều khiển các chip nhớ (như EEPROM AT24C1024 chẳng hạn) thì khái niệm “multi-master” không tồn tại vì các chip nhớ được thiết kế sẵn là Slave, không có khả năng trở thành master. TWI (I2C) được thực hiện trên 2 đường SDA (Serial DATA) và SCL (Serial Clock) trong đó SDA là đường truyền/nhận dữ liệu và SCL là đường xung nhịp. Căn cứ theo chuẩn I2C, các đường SDA và SCL trên các thiết bị có cấu hình “cực góp mở” (open-drain hoặc open-collector, tham khảo các mạch số dùng transistor để hiểu thêm), nghĩa là cần có các “điện trở kéo lên” (pull-up resistor) cho các đường này. Ở trạng thái nghỉ (Idle), 2 chân SDA và SCL ở mức cao. Hình 1 mô tả một mô hình mạng TWI (I2C) cơ bản.

I2C

Hình 1. Mạng TWI (I2C) với nhiều thiết bị và 2 điện trở kéo lên cho SDA, SCL.

          Tiếp theo chúng ta tìm hiểu một số khái niệm và đặc điểm của TWI. Các khái niệm và đặc điểm tôi đề cập dưới đây được dùng cho cả TWI và I2C, nếu có sự khác biệt tôi sẽ giải thích thêm.
         Master: là chip khởi động quá trình truyền nhận, phát đi địa chỉ của thiết bị cần giao tiếp và tạo xung giữ nhịp trên đường SCL.
        Slave: là chip có một địa chỉ cố định, được gọi bởi Master và phục vụ yêu cầu từ Master.
        SDA- Serial Data: là đường dữ liệu nối tiếp, tất cả các thông tin về địa chỉ hay dữ liệu đều được truyền trên đường này theo thứ tự từng bit một. Chú ý là trong chuẩn I2C, bit có trọng số lớn nhất (MSB) được truyền trước nhất, đặc điểm này ngược lại với chuẩn UART.
        SCL –Serial Clock: là đường giữ nhịp nối tiếp. TWI (I2C) là chuần truyền thông nối tiếp đồng bộ, cần có 1 đường tạo xung giữ nhịp cho quá trình truyền/nhận, cứ mỗi xung trên đường giữ nhịp SCL, một bit dữ liệu trên đường SDA sẽ được lấy mẫu (sample). Dữ liệu nối tiếp trên đường SDA được lấy mẫu khi đường SCL ở mức cao trong một chu kỳ giữ nhịp, vì thế đường SDA không được đổi trạng thái khi SCL ở mức cao (trừ START và STOP condition). Chân SDA có thể được đổi trạng thái khi SCL ở mức thấp.

 sample

        START Condition-Điều kiện bắt đầu: từ trạng thái nghỉ, khi cả SDA và SCL ở mức cao nếu Master muốn thực hiện một “cuộc gọi”, Master sẽ kéo chân SDA xuống thấp trong khi SCL vẫn cao. Trạng thái này gọi là START Condition (chúng ta gọi tắt là S).
        STOP Condition-Điều kiện kết thúc: sau khi thực hiện truyền/nhận dữ liệu, nếu Master muốn kết thúc quá trình nó sẽ tạo ra một STOP condition. STOP condition được Master thực hiện bằng cách kéo chân SDA lên cao khi đường SCL đang ở mức cao. STOP condition chỉ được tạo ra sau khi địa chỉ hoặc dữ liệu đã được truyền/nhận.
        REPEAT START – Bắt đầu lặp lại: khoảng giữa START và STOP condition là khoảng bận của đường truyền, các Master khác không tác động được vào đường truyền trong khoảng này. Trường hợp sau khi kết thúc truyền/nhận mà Master không gởi STOP condition lại gởi thêm 1 START condition gọi là REPEAT START. Khả năng này thường được dùng khi Master muốn lấy dữ liệu liên tiếp từ các Slaves. Hình bên dưới mô tả các Master tạo ra START, STOP và REPEAT START.

sample

         Address Packet Format – Định dạng gói địa chỉ: trên mạng TWI (I2C), tất cả các thiết bị (chip) đều có thể là Master hay Slave. Mỗi thiết bị có một địa chỉ cố định gọi là Device address. Khi một Master muốn giao tiếp với một Slave nào đó, nó trước hết tạo ra một START condition và tiếp theo là gởi địa chỉ Device address của Slave cần giao tiếp trên đường truyền, vì thế xuất hiện khái niệm “gói địa chỉ” (Address Packet). Gói địa chỉ trong TWI (I2C) có định dạng 9 bits trong đó 7 bit đầu (gọi là SLA, được gởi liền sau START condition) chứa địa chỉ Slave, một bit READ/WRITE và một bit ACK-Ackknowledge (xác nhận).  Do bit địa chỉ có độ dài 7 bits nên về mặt lý thuyết, trên 1 mạng TWI (I2C) có thể tồn tại tối đa 2^7=128 thiết bị có địa chỉ riêng biệt. Tuy nhiên, có một số địa chỉ không được sử dụng như các địa chỉ có định dạng 1111xxx (tức các địa chỉ lớn hơn hoặc bằng 120 không được dùng). Riêng địa chỉ 0 được dùng cho “cuộc gọi chung” (General call). Bit READ/WRITE (R/W) được truyền tiếp sau 7 bit địa chỉ là bit báo cho Slave biết Master muốn “đọc” hay “ghi” vào Slave. Nếu bit này bằng 0 (gọi là W) thì quá trình “Ghi” dữ liệu từ Master đến Slave được yêu cầu, nếu bit này bằng 1 (gọi là R) thì Master muốn “đọc” dữ liệu từ Slave về. Tám bits trên (SLA+R/W) được Master phát ra sau khi phát START condition, nếu một Slave trên mạng nhận ra rằng địa chỉ mà Master yêu cầu trùng khớp với Device address của chính mình, nó sẽ “đáp trả” lại Master bằng cách phát ra 1 tín hiệu “xác nhận” ACK bằng cách kéo chân SDA xuống thấp trong xung thứ 9. Ngược lại, nếu không có Slave đáp ứng lại, chân SDA vẫn ở mức cao trong xung giữ nhịp thứ 9 thì gọi là tín hiệu “không xác nhận” – NOT ACK, lúc này Master cần có những ứng xử phù hợp tùy theo mỗi trường hợp cụ thể, ví dụ Master có thể gởi STOP condition và sau đó phát lại địa chỉ Slave khác…Như vậy, trong 9 bit của gói địa chỉ thì chỉ có 8 bit được gởi bởi Master, bit còn lại là do Slave. Ví dụ Master muốn yêu cầu “đọc” dữ liệu từ Slave có địa chỉ 43,  nó cần phát đi một byte như sau trên đường truyền: (43<<1)+1, trong đó (43<<1) là dịch số 43 về bên trái 1 vị trí vì 7 bit địa chỉ nằm ở các vị trí cao trong gói địa chỉ, sau đó cộng giá trị này với “1” tức là quá trình “đọc” được yêu cầu.

 address

         General call – Cuộc gọi chung: khi Master phát đi gói địa chỉ có dạng 0 (thực chất là 0+W) tức nó muốn thực hiện một cuộc gọi chung đến tất cả các Slave. Tất nhiên, cho phép hay không cho phép cuộc gọi chung là do Slave quyết định. Nếu các Slave được cài đặt cho phép cuộc gọi chung, chúng sẽ đáp lại Master bằng ACK. Cuộc gọi chung thường xảy ra khi Master muốn gởi dữ liệu chung đến các Slaves. Chú ý là cuộc gọi chung có dạng 0+R là vô nghĩa vì không thể có chuyện Master nhận dữ liệu từ tất cả các Slave cùng thời điểm.
         Data Packet Format – Định dạng gói dữ liệu: sau khi địa chỉ đã được phát đi, Slave đã đáp lại Master bằng ACK thì quá trình truyền/nhận dữ liệu sẽ diễn ra giữa cặp Master/Slave này. Tùy vào bit R/W trong gói địa chỉ, dữ liệu có thể được truyền theo hướng từ Master đến Slave hay từ Slave đến Master. Dù di chuyển theo hướng nào, gói dữ liệu luôn bao gồm 9 bits trong đó 8 bits đầu là dữ liệu và 1 bit cuối là bit ACK. Tám bits dữ liệu do thiết bị phát gởi và bit ACK do thiết bị nhận tạo ra. Ví dụ khi Master thực hiện quá trình gởi dữ liệu đến Slave, nó sẽ phát ra 8 bits dữ liệu, Slave nhận và phát lại ACK (kéo SDA xuống 0 ở xung thứ 9), sau đó Master sẽ quyết định gợi tiếp byte dữ liệu khác hay không. Nếu Slave phát tín hiệu NOT ACK (không tác động SDA ở xung thứ 9) sau khi nhận dữ liệu thì Master sẽ kết thúc quá trình gởi bằng cách phát đi STOP condition. Hình bên dưới mô tả định dạng gói dữ liệu trong TWI (I2C).

 data

       Phối hợp gói địa chỉ và dữ liệu: một quá trình truyền/nhận TWI (I2C) thường được bắt đầu từ Master, Master phát đi một START condition sau đó gởi gói địa chỉ SLA+R/W trên đường truyền. Tiếp theo nếu có một Slave đáp ứng lại, dữ liệu có thể truyền/nhận liên tiếp trên đường truyền (1 hoặc nhiều byte liên tiếp). Khung truyền thông thường được mô tả như hình bên dưới.

combine

      Multi-Master Bus –Đường truyền đa chip chủ: như đã trình bày ở trên, TWI (I2C) là chuẩn truyền thông đa chip chủ, nghĩa là tại một thời điểm có thể có nhiều hơn 1 chip làm Master nếu các chip này phát ra START condition cùng lúc. Nếu các Master có cùng yêu cầu và thao tác đối với Slave thì chúng có thể “cùng tồn tại” và quá trình truyền/nhận có thể thành công. Tuy nhiên, trong đa số trường hợp sẽ có một số Master bị “thất lạc” (lost). Một Master bị lost khi nó truyền/nhận 1 mức cao trên SDA trong khi các Master khác truyền/nhận 1 mức thấp. Truyền thông đa chip chủ tương đối phức tạp và vì thế tôi sẽ không đề cập trường hợp này trong lúc thực hiện ví dụ giao tiếp trong bài học này.
      Nắm được các khái niệm và đặc điểm trên của truyền thông TWI (I2C) là bạn đã sẵn sàng để điều khiển module TWI trên AVR. Phần tiếp theo tôi sẽ hướng dẫn cách thao tác module TWI trên AVR thông qua một ví dụ cụ thể.

III. TWI trên AVR.

      1. Thanh ghi:

      TWI trên AVR được vận hành bởi 5 thanh ghi bao gồm thanh ghi tốc độ giữ nhịp TWBR, thanh ghi điều khiển TWCR , thanh ghi trạng thái TWSR, thanh ghi địa chỉ TWAR và thanh ghi dữ liệu TWDR.

      - TWBR (TWI Bit Rate Register): là 1 thanh ghi 8 bit quy định tốc độ phát xung giữ nhịp trên đường SCL của chip Master.

      TWBR

       Tốc độ phát xung giữ nhịp được tính theo công thức:

rate

       Trong đó CPU Clock frequency là tần số hoạt động chính của AVR, TWBR là giá trị thanh thi TWBR và TWPS là giá trị của 2 bits TWPS1 và TWPS0 nằm trong thanh thi trạng thái TWSR. Hai bits này được gọi là bit prescaler, thông thường người ta hay set TWPS1:0 =00 để chọn Prescaler là 1 (40=1). Bảng 1 tóm tắt tốc độ xung giữ nhịp tạo ra trên SCL đối với các giá trị của tham số:
Bảng 1. Tốc độ xung giữ nhịp tham khảo.

       rate table

       - TWCR (TWI Control Register): là thanh ghi 8 bit điều khiển hoạt động của TWI.

       TWCR 

  • Bit 7- TWINT (TWI Interrupt Flag): là một cờ báo rất quan trọng. TWINT được tự động set lên 1 khi TWI kết thúc một quá trình bất kỳ nào đó (như phát/nhận START, phát nhận địa chỉ…). Chú ý là bit này không tự động được xóa bởi phần cứng như các cờ báo trong các module khác. Vì thế, khi lập trình điều khiển TWI chúng ta luôn phải xóa TWINT trước khi muốn thực hiện một quá trình nào đó. Một điểm quan trọng cần lưu ý là bit TWINT được xóa khi chúng ta viết giá trị 1 vào nó. Trong khi lập trình cho TWI, chúng ta thường xóa TWINT bằng cách viết 1 vào nó, sau đó liên tục kiểm tra TWINT, nếu bit này được set lên 1 thì quá trình đã hoàn thành.
  • Bit 6 – TWEA (TWI Enable Acknowledge Bit): tạm hiểu là bit kích hoạt tín hiệu xác nhận. Đối với  chip Slave, nếu bit này được set thì tín hiệu xác ACK sẽ được gởi trong các trường hợp sau: địa chỉ do Master phát ra trùng khớp với địa chỉ của Slave; một cuộc gọi chung đang xảy ra và Slave này cho phép cuộc gọi chung; dữ liệu đã được Slave nhận từ Master. Như thế, khi set một chip ở chế độ Slave, chúng ta cần set bit này để nó có thể đáp ứng lại Master bất cứ khi nào được gọi. Đối với chip Master, tín hiệu ACK chỉ được phát trong 1 trường hợp duy nhất đó là khi Master nhận dữ liệu từ Slave, Master phát ACK để báo cho Slave là mình đã nhận được và muốn tiếp tục nhận từ Slave.
  • Bit 5 – TWSTA (TWI START Condition Bit): là bit tạo START condition. Khi một chip muốn trở thành Master để thực hiện 1 cuộc gọi, bit này cần được set và một START condition được tạo ra trên đường truyền nếu đường truyền đang rảnh. Nếu đường truyền không rảnh, TWI sẽ chờ cho đến khi nó rảnh (nhận ra 1 STOP condition) và tiếp tục gởi START condition. Chú là là bit nay cần được xóa bởi phần mềm sau khi START condition đã được gởi (viết 0 vào bit này để xóa nó).
  • Bit 4 – TWSTO (TWI STOP Condition Bit): là bit tạo STOP condition cho TWI. Khi Master muốn kết thúc một cuộc gọi, nó sẽ phát STOP condition bằng cách viết giá trị 1 vào bit TWSTO. Slave cũng có thể tác động vào bit này, nếu một cuộc gọi bị lỗi, viết 1 vào TWSTO trên Slave sẽ reset đường truyền về trạng thái rảnh ban đầu.
  • Bit 3 – TWWC (TWI Write Collision Flag): khi cờ TWINT đang ở mức thấp tức TWI đang bận, nếu chúng ta viết dữ liệu vào thanh ghi dữ liệu (TWDR) thì một lỗi xảy ra, khi đó bit TWWC tự động được set lên 1. Vì thế, trong quá trình truyền dữ liệu, bit TWINT cần được giữ mức cao khi ghi dữ liệu vào thanh ghi TWDR và sau đó xóa khi dữ liệu đã sẵn sàng.
  • Bit 2 – TWEN (TWI Enable Bit): bit kích hoạt TWI trên AVR, khi TWEN được set lên 1, TWI sẵn sàng hoạt động.
  • Bit 1 – Reserve: không sử dụng.
  • Bit 0 – TWIE (TWI Interrupt Enable Bit): bit cho phép ngắt TWI, khi bit nay được set bằng 1 đồng thời bit I trong thanh ghi trạng thái chung được set, một ngắt TWI xảy ra khi bit TWINT được set bởi phần cứng. Ngắt TWI có thể xảy ra  sau bất kỳ hoạt động nào liên quan đến TWI.  Do đó cần sử dụng ngắt hợp lý. Thông thường, ngắt chỉ được sử dụng cho Slave, đối với Master ngắt không cần thiết vì Master chủ động khởi động một cuộc gọi. 

       Một điều cần chú ý là các bit trong thanh ghi TWCR không cần được set cùng lúc, tùy vào từng giai đoạn trong quá trình giao tiếp TWI các bit có thể được set riêng lẻ.    

      - TWSR (TWI Status Register): là 1 thanh ghi 8 bit trong đó có 5 bit chứa code trạng thái của TWI  và 2 bit chọn prescaler.

      TWSR

         Có rất nhiều bước, nhiều tình huống xảy ra khi giao tiếp bằng TWI cho cả Master và Slave. Ứng với mỗi trường hợp TWI sẽ tạo ra 1 code trong thanh ghi TWSR . Lập trình cho TWI cần xét code trong 5 bit cao của thanh ghi TWSR và đưa ra các ứng xử hợp lý ứng với từng code.

        - TWDR (TWI Data Register): là thanh ghi dữ liệu chính của TWI. Trong quá trình nhận, dữ liệu nhận về sẽ được lưu trong TWDR. Trong quá trình gởi, dữ liệu chứa trong TWDR sẽ được chuyển ra đường SDA.

        - TWAR (TWI Address Register): là thanh ghi chứa device address của chip Slave. Cấu trúc thanh ghi được trình bày trong hình dưới.

       TWDR

       Nhớ lại địa chỉ Slave được tạo thành từ 7 bits, trên thanh ghi TWAR 7 bits địa chỉ này nằm ở 7 vị trí cao. Trước khi sử dụng TWI như Slave, chúng ta phải gán địa chỉ cho chip, việc viết địa chỉ thường được thực hiện bằng lệnh TWAR = (Device_address<<1)+TWGCE. Trong đó TWGCE (TWI General Call Enable) là bit cho phép cuộc gọi chung. Như tôi đề cập bên trên, Slave co quyền cho phép Master thực hiện cuộc gọi chung với nó hay không. Nếu TWGCE=1, Slave  sẽ đáp ứng lại cuộc gọi chung nếu có, nếu TWGCE=0 thì Slave sẽ bỏ qua cuộc gọi chung. 

         2. Hoạt động của TWI:

         TWI trên  AVR được gọi là byte-oriented (tạm dịch là hướng byte) và interrupt-based (dựa trên ngắt). Bất kỳ một sự kiện nào trong quá trình truyền/nhận TWI cũng có thể gây ra 1 ngắt TWI. TWI trên AVR vì thế hoạt động tương đối độc lập với chip. Tuy nhiên, cần khai thác ngắt trên AVR một cách hơp lý. Ví dụ, đối với Master, chúng ta không cần sử dụng ngắt vì chip này hoàn toàn chủ động trong việc truyền và nhận. Riêng với Slave, sử dụng ngắt để tránh  bỏ lỡ các cuộc gọi là cần thiết.
Tất cả các AVR trên mạng TWI đều có thể là Master hay Slave, cả Master và Slave đều có thể truyền và nhận dữ liệu. Vì thế, có tất cả 4 mode trong hoạt động của TWI trên AVR. Chúng ta sẽ lần lượt khảo sát các mode này như sau: Master Transmitter (chip chủ truyền), Master Receiver (Chip chủ nhận), Slave Reicever (chip tớ nhận) và Slave Transmitter (Chip tớ truyền).
Trước khi khảo sát các chế độ hoạt động của TWI chúng ta qui ước một số ký hiệu thường dùng (đây cũng là các ký hiệu dùng trong datasheet của các chip AVR).

S:                START condition – điều kiện bắt đầu
Rs:              REPEAT START – bắt đầu lặp lại
R:                READ Bit, bit này bằng 1 được gởi kèm với gói địa chỉ
W:              WRITE Bit, bit này mang giá trị 0, gởi kèm gói địa chỉ
ACK:          Ackowledge, bit xác nhận, chân SDA được kéo xuống 0 ở xung thứ 9
NACK:        Not Acknowledge, không xác nhận, SDA ở mức cao ở bit thứ 9
Data:           8 bits dữ liệu
P:               STOP condition – điều kiện kết thúc.
SLA:          Slave address, địa chỉ của Slave cần giao tiếp.

         A. Master Transmitter mode – Master truyền dữ liệu:

        Trong chế độ này, Master truyền 1 hoặc một số byte dữ liệu đến một hoặc các Slave. Để bắt đầu, Master tạo ra một START condition trên đường SDA, nếu đường truyền đang rảnh, Master sẽ tiếp tục phát đi địa chỉ của Slave cần giao tiếp cùng với bit W (ghi) theo định dạng như sau: SLA+W. Nếu Slave đáp lại bằng một ACK trong xung giữ nhịp thứ 9, Master sẽ tiếp tục gởi 1 hoặc liên tiếp các byte dữ liệu trên SDA. Cứ sau mỗi byte dữ liệu, Master sẽ kiểm tra ACK từ Slave. Nếu Slave gởi một NACK hoặc Master không muốn gởi thêm dữ liệu đến Slave nó sẽ phát đi một STOP condition hoặc một REPEAT START (Rs). Nếu STOP được phát, cuộc gọi kết thúc, nếu Rs được phát, một cuộc gọi mới bắt đầu, sau Rs là địa chỉ của Slave mới…Đó là về mặt lý thuyết, trên thực tế làm sao để kiểm tra môt START condition có được gởi chưa? làm sao biết có nhận được ACK sau khi phát địa chỉ hoặc dữ liệu? Tất cả được TWI mã hóa thành các code chứa trong thanh ghi TWSR (chỉ 5 bit cao). Chúng ta chỉ thanh ghi này và đối chiếu với bảng code quy định sẵn để biết trạng thái đường truyền và đưa ra quyết định tiếp theo. Hình 2 mô tả một quá trình Master truyền dữ liệu, các khả năng có thể xảy ra và giá trị tương ứng của thanh ghi TWSR. Ý nghĩa các code trong thanh ghi TWSR trong lúc Master truyền dữ liệu có thể tham khảo thêm datasheet của chip.

MT

Hình 2. Master truyền dữ liệu.

        Từ hình 2, chúng ta nhận thấy khi Master truyền dữ liệu, dãy code 0x08 -> 0x18 -> 0x28 ->… -> 0x28 (-> 0x30) là dãy code thành công nhất. Code 0x08 báo rằng START codition được truyền thành công, code 0x18 báo địa chỉ truyền thành công và đã có Slave xác nhận bằng ACK, code 0x28 tức dữ liệu được Master truyền thành công và Slave đã nhận được, báo ACK lại cho Master, code 0x30 tức dữ liệu đã được truyền nhưng Slave không xác nhận lại, lúc này Master có thể phát đi một STOP codition sau code 0x30. Ngoài ra còn một số code khác tương ứng với các trường hợp khác như gởi địa chỉ thất bại (code 0x20), Master bị lost (code 0x38)…Đối với mỗi loại ứng dụng, cách “hành xử” sẽ khác nhau đối với các trường hợp thất bại này. Trong bài này, tôi sẽ bỏ qua tất cả các trường hợp thất bại, nếu một trong các code thất bại xảy ra chúng ta sẽ thoát khỏi cuộc gọi và đưa đường truyền về trạng thái nghỉ.

       B. Master Receiver mode – Master nhận dữ liệu: 

      Trong chế độ này, Master nhận một hoặc một số byte dữ liệu từ một Slave. Để bắt đầu, Master tạo ra một START condition trên đường SDA, nếu đường truyền đang rảnh, Master sẽ tiếp tục phát đi địa chỉ của Slave cần giao tiếp cùng với bit R (đọc) theo định dạng như sau: SLA+R. Nếu Slave đáp lại bằng một ACK trong xung giữ nhịp thứ 9, Master sẽ bắt đầu sample dữ liệu trên SDA. Cứ sau mỗi byte dữ liệu, nếu Master muốn nhận tiếp byte khác nó phải phát ra 1 ACK ở xung thứ 9 báo cho Slave. Khi Master muốn kết thúc quá trình nhận nó sẽ phát một NOT ACK sau khi nhận dữ liệu, liền sau đó Master phát STOP để kết thúc cuộc gọi hoặc phát đi một REPEAT START nếu nó muốn tiếp tục gọi các Slaves khác. Hình 3 mô tả một quá trình Master nhận dữ liệu, các khả năng có thể xảy ra và giá trị code tương ứng của thanh ghi TWSR. Ý nghĩa các code trong thanh ghi TWSR trong lúc Master truyền dữ liệu có thể tham khảo thêm datasheet của chip.

MR

Hình 3. Master nhận dữ liệu.

       Từ hình 3, trong quá trình  Master nhận dữ liệu, dãy code 0x08 -> 0x40 -> 0x50 ->… -> 0x58 là dãy code thành công nhất. Code 0x08 báo rằng START codition được truyền thành công, code 0x40 báo địa chỉ + R đã được truyền thành công và đã có Slave xác nhận bằng ACK, code 0x50 báo dữ liệu được Master nhận thành công và Master cũng đã phát một ACK bit sau khi nhận, code 0x58 xảy ra khi Master nhận dữ liệu thành công nhưng nó không phát ACK mà phát NOT ACK, báo cho Slave rằng Master không muốn nhận thêm dữ liệu, tiếp theo Master sẽ phát một STOP condition hoặc một REPEAT START. Các trường hợp khác chúng ta không khảo sát.

      C. Slave Receiver mode – Slave nhận dữ liệu: 

       Hình 4 mô tả một quá trình Slave nhận dữ liệu, các khả năng có thể xảy ra và giá trị code tương ứng của thanh ghi TWSR. Chế độ Slave nhận dữ liệu xảy ra khi Master thực hiện một cuộc gọi phát dữ liệu (SLA+W). Như quan sát trong hình 4, Slave chỉ nhận ra cuộc gọi này khi địa chỉ của nó trùng với địa chỉ của Master (Own address mode) hoặc khi Master thực hiện một cuộc gọi chung. Khi đó, bit TWINT của Slave sẽ được set lên 1. Nếu Slave cho phép ngắt TWI (bit TWIE trong thanh ghi TWCR được set từ lúc đầu) thì một ngắt xảy ra báo có một sự kiện TWI. Nếu code trong thanh ghi TWSR là 0x60 thì một cuộc gọi địa chỉ riêng được yêu cầu và Slave cũng đã đáp ứng lại Master bằng một ACK, Slave sau đó bắt đầu nhận dữ liệu từ đường SDA. Cứ sau một byte dữ liệu Slave phải xác nhận một ACK nếu nó còn muốn tiếp tục nhận. Nếu vì một lý do nào đó mà Slave không thể tiếp tục nhận nó có thể phát một NOT ACK sau một byte dữ liệu. Cuộc gọi kết thúc khi Slave nhận được STOP condition, tương ứng code 0xA0. Cuộc gọi chung cũng diễn ra hoàn toàn tương tự cuộc gọi địa chỉ riêng nhưng code có giá trị khác. Khi viết chương trình cho Slave trong chế độ nhận dữ liệu, chúng ta cần xét cả 2 trường hợp cuộc gọi địa chỉ riêng và cuộc gọi chung.

SR

Hình 4. Slave nhận dữ liệu.

      D. Slave Transmitter mode – Slave truyền dữ liệu: 

      Đây là chế độ cuối cùng trong 4 chế độ của AVR TWI. Hình 5 mô tả một quá trình Slave truyền dữ liệu, các khả năng có thể xảy ra và giá trị code tương ứng của thanh ghi TWSR. Chế độ Slave phát dữ liệu xảy ra khi Master muốn nhận dữ liệu từ Slave, Master thực hiện một cuộc gọi nhận dữ liệu (SLA+R). Như quan sát trong hình 5, Slave chỉ nhận ra cuộc gọi này khi địa chỉ của nó trùng với địa chỉ của Master (Own address mode). Khi đó, bit TWINT của Slave sẽ được set lên 1. Nếu Slave đáp lại bằng một ACK ở xung nhịp thứ 9, code trong thanh ghi TWSR sẽ là 0xA8, Slave sau đó bắt đầu phát dữ liệu lên đường SDA. Cứ sau mỗi byte dữ liệu, Master sẽ xác nhận một ACK nếu nó còn muốn tiếp tục nhận, code 0xB8 sẽ xuất hiện trong trường hợp này. Nếu Master không muốn tiếp tục nhận dữ liệu từ Slave, một NOT ACK sẽ được phát và code 0xC0 xuất hiện, Slave kết thúc quá trình phát dữ liệu. Một trường hợp đặc biệt khi bit TWEA (bit ACK) trong thanh ghi TWCR của Slave được reset về 0 trước khi Slave truyền dữ liệu, trường hợp Slave muốn báo rằng nó đã hết dữ liệu để truyền, byte tiếp theo cũng là byte cuối cùng. Sau khi Master nhận byte này, nó có thể xác nhận 1 ACK cho Slave (vì thật ra Master không hề biết Slave đang truyền byte cuối), code trên Slave trong trường hợp này là 0xC8 và Slave sẽ tự hết thúc quá trình truyền mà không cần chờ Master. Khi lập trình cho Slave trong chế độ phát, cần phải có sự “thỏa hiệp” với Master trước để tránh code 0xC8 vì code này không có nhiều ý nghĩa.

 ST

Hình 5. Slave truyền dữ liệu.

        Kỹ thuật chính dùng cho Master khi truyền hay nhận cuộc gọi là hỏi vòng và chờ (polling and waiting). Ứng với mỗi code nhận về từ thanh ghi TWSR (hay ứng với mỗi trạng thái của cuộc gọi) mà Master set các bit tương ứng trong thanh ghi điều khiển TWCR và sau đó chờ bit TWINT được set (quá trình kết thúc) để tiếp tục đọc và xét code TWSR. Quá trình chờ và xét này lặp lại cho đến khi Master kết thúc cuộc gọi bằng STOP condition. Tuy nhiên Slave thì khác, Slave không chủ động thực hiện cuộc gọi mà nó phải chờ yêu cầu từ Master để phục vụ. Vì thế, nếu dùng “hỏi vòng” cho Slave thì sẽ tốn thời gian chờ vô ích và đôi khi còn bỏ lỡ các cuộc gọi. Đối  với Slave, ngắt là phương pháp bắt cuộc gọi tối ưu nhất. Trong bài học này, việc truyền và nhận của Slave sẽ được thực hiện trong các trình phục vụ ngắt TWI.

 IV. Điều khiển AVR TWI.

        Phần này tôi hướng dẫn lập trình điều khiển module TWI AVR bằng WinAVR. Các hình 2, 3, 4 và 5 cần được tham khảo kèm kỹ vì code trong phần này được phát triển từ các hình này. Để đơn giản, chúng ta sẽ viết các hàm giao tiếp TWI trong 1 file riêng gọi là “myTWI.h”, đây có thể coi là thư viện cho TWI dùng trong trang web này. Như đã trình bày, chuẩn I2C thì duy nhất nhưng cách sắp xếp dữ liệu của các chip I2C thì rất đa dạng. Vì thế, khi muốn giao tiếp với một chip I2C nào bạn nhất thiết phải đọc datasheet của chip đó để hiểu định dạng dữ liệu. Các hàm trong thư viện myTWI chỉ phục vụ giao tiếp giữa các AVR với nhau, nếu muốn sử dụng chúng giao chip với một chip EEPROM 24C1004 chẳng hạn, bạn phải viết thêm các hàm mở rộng khác dựa trên các hàm này.
Nội dung file myTWI.h được chia thành 3 phần, phần đầu là các định nghĩa biến, tham số chung, phần 2 gồm các hàm truyền/nhận cho Master và phần 3 là trình phục vụ ngắt TWI cho Slave. List 1 trình bày các định nghĩa chung trong file “myTWI.h”.
List 1. Định nghĩa chung.

list1

       Phần này chủ yếu định nghĩa các code trạng thái trong quá trình thao tác TWI trên AVR mà chúng ta đã biết khi khảo sát các chế độ hoạt động của TWI. Thật ra bạn có thể tham khảo các hình 2-5 và các bảng code trong datasheet của AVR và sử dụng các code trạng thái trực tiếp trong lúc lập trình, tôi định nghĩa như trên chỉ để tiện theo dõi trong lúc lập trình. Các dòng từ 12 đến 25 định nghĩa các code trạng thái cho Slave (cả truyền và  nhận). Chúng ta cũng định nghĩa một số biến toàn cục dùng cho Slave, biến SLAVE_wData[100] là một mảng 100 phần tử dùng chứa dữ liệu mà Slave sẽ truyền, biến Tran_Num là chỉ số của phần tử trong mảng SLAVE_wData sẽ được truyền đi. Biến SLAVE_buff[100] là dữ liệu nhận về từ TWI và Rec_Num là chỉ số của dữ liệu sau cùng do TWI nhận về (dữ liệu SLAVE_buff[Rec_Num]). Biến Device_Addr chứa địa chỉ mà khi là Slave của chính AVR chúng ta đang lập trình. Tương tự, các dòng từ 47 đến 57 định nghĩa code trạng thái cho Master mode. Trước đó, chúng ta cũng định nghĩa các giá trị tốc độ phát xung giữ nhịp sẽ gán cho thanh ghi TWBR (dòng 37, 38). Hai biến TWI_R và TWI_W đại diện cho 2 bit R/W được truyền trong gói địa chỉ (báo cho Slave biết Master muốn truyền hay nhận dữ liệu). Một số macro trong các dòng  42 đến 45 bao gồm START, STOP condition và xóa bit TWINT bằng cách gán các giá trị tương ứng cho thanh ghi điều khiển TWI.
        Cuối cùng là chương trình con void TWI_Init(void) khởi động TWI. Quá trình khởi động bao gồm set tốc độ xung giữ nhịp cho Master (dòng 61, 62), gán địa chỉ device (dòng 63) và xác lập TWI sẵn sàng ở chế độ Slave. Xem lại thanh ghi TWAR, do 7 bit địa chỉ nằm ở vị trí cao nên chúng ta cần phải dịch trái địa chỉ 1 vị trí trước khi gán cho TWAR (Device_Addr <<1), đồng thời set bit 0 trong TWAR để cho phép nhận cuộc gọi chung khi được yêu cầu. Dòng 64 khởi động TWI với bit ACK sẵn sàng và cho phép xảy ra ngắt TWI. Như thế, sau khi khởi động TWI sẵn sàng ở chế độ Slave.

List 2. Code cho Master.

list2

        Hàm TWI_Master_Send_array(uint8_t Addr, uint8_t Data[], uint8_t len) thực hiện truyền 1 dãy các byte dữ liệu trong mode Master. Tham số Addr là địa chỉ của Slave cần giao tiếp, Data[] là mảng dữ liệu và len là chiều dài (số byte) của dữ liệu cần truyền. Việc đầu tiên khi chúng ta vào Master mode là “tắt” ngắt TWI bằng cách xóa bit  TWIE (dòng 3). Trình tự Master truyền dữ liệu hoàn toàn tương tự trình tự trong hình 2. Dòng 5, TWCR=TWI_START, Master bắt đầu phát 1 START condition. Nếu xem lại định nghĩa của macro TWI_START trong list 1 bạn sẽ thấy dòng TWCR=TWI_START tương đương TWCR=(1<<TWINT)|(1<<TWSTA)|(1<<TWEN) tức chúng ta thực hiện xóa bit TWINT (bit này phải luôn được xóa trước khi muốn thực hiện viêc gì) bằng cách ghi 1 vào TWINT, set bit START (bit TWSTA) và cho phép TWI hoạt động bằng bit TWEN. Dòng code 6 chờ cho đến khi bit TWINT được phần cứng set lên 1 (kết thúc), sau đó chúng ta kiểm tra code trong thanh ghi trạng thái TWSR. Chú ý là chỉ có 5 bit cao trong thanh ghi TWSR chứa trạng thái nên chúng ta cần dùng giải thuật mặt nạ che các bit thấp lại, TWSR & 0xF8 chính là cách để che 3 bit thấp của TWSR. So sánh code đọc được với code tương ứng trong hình 1, trong trường hợp này chúng ta so sánh với _START_Sent, chính là so sánh với 0x80 (xem lại định nghĩa của _START_Sent trong list 1). Nếu các code không trùng nhau, một lỗi truyền xảy ra và chúng ta sẽ thoát khỏi chương trình truyền, giá trị trả về chính là code có lỗi (xem dòng code 7). Các dòng code từ 10 đến 13 thực hiện truyền địa chỉ + W, chú ý trong lúc phát, dữ liệu cần phát phải được ghi sẵn vao thanh ghi dữ liệu  TWDR trước khi xóa bit TWINT (dòng 10 và 11). Sau khi truyền địa chỉ chúng ta truyền mảng dữ liệu liên tiếp và cuối cùng là phát STOP condition, TWCR=TWI_STOP tương đương TWCR=(1<<TWINT)|(1<<TWSTO)|(1<<TWEN). Cần khởi động lại TWI để đưa nó về chế độ Slave trước khi thoát khỏi chương trình con truyền dữ liệu của chế độ Master (dòng 24).
        Hàm TWI_Master_Read_array(uint8_t Addr, uint8_t Data[], uint8_t len) thực hiện nhận dữ liệu về Master. Cách giải thích cho hàm này không khác nhiều so với hàm đọc dữ liệu nên bạn đọc tự tìm hiểu. Một điểm cần chú ý là khi nhận 1 dãy byte chúng ta nên đọc n-1 byte đầu bình thường, có trả ACK cho Slave và byte cuối cùng sẽ được nhận riêng, trả NOT ACK để báo cho Slave rằng Master không muốn nhận thêm(đoạn code từ dòng 55 đến 59 dùng đọc byte cuối cùng).

List 3. Code cho Slave.

list3

        Như tôi đã trình bày, toàn bộ quá trình truyền và nhận của Slave được thưc hiện trong chương trình phục vụ ngắt TWI. Khi ngắt TWI xảy ra, trình phục vụ ngắt sẽ đọc và kiểm tra code trong thanh ghi TWSR để thực hiện các công việc phù hợp. Bạn đọc lại tham khảo thêm hình 4 và hình 5 cùng với các code trong những “case” tương ứng của List 3 để hiểu đoạn chương trình này. Điểm lưu ý lớn nhất mà tôi muốn nói là các biến được dùng cho chế độ Slave truyền và nhận. Tôi dùng 2 mảng SLAVE_wData và SLAVE_buff để chứa biến truyền và nhân. Hai biến Tran_Num và Rec_Num là chỉ số của byte hiện hành. Vì thế SLAVE_wData[Tran_Num] chính là byte tiếp theo sẽ được truyền đi nếu Slave được yêu cầu truyền, và SLAVE_buff[Rec_Num] là byte cuối cùng mà Slave nhận về trong chế độ Slave nhận dữ liệu. Hãy khái thác các biến này trong các chương trình ứng dụng.
         Để minh họa cho các sử dụng các hàm trong thư viện myTWI, tôi thực hiện một mạch điện mô phỏng mạng TWI gồm 3 chip ATmega32. Chip thứ nhất là Master, 2 chip còn lại là Slaves. Tôi tạo 2 Project, một cho Master và một cho 2 Slaves dùng chung. PORTD được set input có điện trở kéo lên. Tôi dùng 2 chân PD6 và PD7 để chọn địa chỉ cho 2 Slaves, Slave thứ nhất tôi nối chân PD6 xuống GND, do đó chip này có địa chỉ Device_Addr là  PD7:PD6=10=2 (thập phân). Slave còn lại tôi để 2 chân PD6 và PD7 trống nên địa chỉ của nó là PD7:PD6=11=3. Trong chương trình của Slave có phần đọc 2 chân PD6:PD7 và gán cho biến Device_Addr mà chúng ta đã khai báo trong List 1, như vậy có thể dùng các này để set địa chỉ cho Slaves mà chúng ta gọi là “set địa chỉ cứng”. Trên chip Master, một swich được nối với chân PD0 để chọn Slave cần giao tiếp, nếu switch đóng thì SLAVE có địa chỉ 2 được chọn, nếu switch mở thì SLAVE có địa chỉ 3 được chọn để giao tiếp. Một nút nhấn được nối với ngắt INT0 của chip Master, khi nhấn nút này chương trình còn đọc dữ liệu từ Slave được gọi, tùy theo switch đóng hay mở mà Slave tương ứng được gọi để gởi dữ liệu cho Master. Dữ liệu nhận về sẽ hiển thị trên 1 Character LCD. Hình 6 là sơ đồ mạch điện mô phỏng bằng phần mềm Proteus và List 4, List 5 lần lượt trình bày đoạn code cho chương trình chính của Master và Slave.

demo

Hình 6. Demo TWI.

List 4. Chương trình chính cho Master.

list4

        Ví dụ của Master minh họa cách dùng 2 hàm Master truyền và nhận mảng dữ liệu.  Ở dòng 27 tôi dùng hàm TWI_Master_Send_array để gởi 40 phần tử của mảng Data đến Slave có địa chỉ 2, TWI_Master_Send_array(2,Data,40). Tương tự, dòng 31 gởi 50 phần tử của mảng Data đến Slave có địa chỉ 3. Khi button trên mạch mô phỏng được nhấn, ngắt INT0 xảy ra, trong trình phục vụ ngắt INT0 chúng ta dùng hàm TWI_Master_Read_array để đọc dữ liệu từ một trong 2 Slaves, xem dòng code 43: TWI_Master_Read_array(Slave_Addr,rData,1). Địa chỉ của Slave cần đọc sẽ do switch nối với chân PD0 quyết định (xem dòng 42). Địa chỉ của Slave đang giao tiếp sẽ hiển thị trên dòng 1 của LCD, dữ liệu được hiển thị trên dòng 2.

List 5. Chương trình chính cho Slaves.

list5

       Chương trình demo của Slaves minh họa các chế độ Slave truyền và nhận dữ liệu. Tuy nhiên do các quá trình truyền và nhận dữ liệu của Slave được thực hiện trong trình phục vụ ngắt TWI được viết sẵn trong file myTWI.h. trong chương trình chính của Slave chúng ta không cần phải gọi bất kỳ hàm nào trong myTWI. Công việc cần làm trong chương trình demo cho Slave là khởi động TWI sau đó gán giá trị cho các biến toàn cục của Slave (dòng 21 gán giá trị cho mảng SLAVE_wData).
      Tôi có đính kèm ví dụ demo cho TWI, tôi thực hiện 2 Projetc trong 2 thư mục: TWI1 cho AMster và TWI2 cho Slave. Để chạy demo, chạy file TWI bằng Proteus, dùng switch SW1 để chọn Slave cần giao tiếp, nhấn button để nhận dữ liệu từ Slave. Thay đổi vị trí switch và kiểm tra kết quả.