Function Generator

( 9 Votes )

Nội dung Các bài cần tham khảo trước
  1. Giới Thiệu.
  2. AVR và MCP4822.
Download ví dụ

 

I. Giới thiệu.

     Một trong những “thiếu sót” lớn nhất của AVR (và hầu hết các họ vi điều khiển khác) là không tích hợp bộ chuyển đổi số sang tương tự (DAC: Ditital to Analog Converter). Nếu như các bộ chuyển đổi ADC (Analog to Digital Converter) thường dùng trong các ứng dụng đo lường (đọc cảm biến) thì bộ chuyển đổi DAC lại hữu dụng trong các ứng dụng điều khiển (xuất điện áp hoặc dòng) hoặc trong các ứng dụng tạo dạng tín hiệu analog (function generator). Vì AVR không tích hợp bộ chuyển đổi DAC bên trong, để thực hiện các ứng dụng trên chúng ta cần sử dụng các IC DAC chuyên dụng. Cũng giống như chuyển đổi ADC, các bộ chuyển đổi DAC cũng được đánh giá thông qua “độ phân giải” (resolution). DAC có độ phân giải cao thì tín hiệu analog ngõ ra càng “mịn”. Tuy nhiên, độ phân giải lớn đồng nghĩa cần nhiều đường xuất dữ liệu. Do đó nhiều chip DAC sử dụng phương pháp giao tiếp nối tiếp thay cho giao tiếp song song trực tiếp. Trong bài này tôi sẽ trình bày phương pháp điều khiển chip DAC 12 bits của microchip có tên MCP4288 thông qua chuẩn SPI của AVR.
     MCP4822 là chip chuyển đổi DAC 2 kênh, 12 bits của microchip (nhà sản xuất vi điều khiển PIC) với giao diện SPI tốc độ tối đa 12MHz. MCP4822 tích hợp sẵn mạch tạo điện áp tham chiếu 2.048V bên trong và cho phép chọn hệ số khuyếch đại 1 hoặc 2. Chip này rất dễ sử dụng do mạch điều khiển đơn giản và phương thức giao tiếp cũng đơn giản. Gói PDIP 8 chân của chip được mô tả trong hình 1..

Hình 1. MCP4822.

Các chân của MCP4822 được mô tả như sau:
-    VDD: nguồn dương, có thể cấp nguồn từ 2.7V đến 5.5V.
-    CS: chân chọn chip (Slave) của chuẩn SPI, chân này tích cực âm.
-    SCK: xung giữ nhịp của SPI
-   SDI: đường data của SPI (Serial Data Input), đường này tương đương MOSI trên AVR. Khi dùng AVR điều khiển MCP4822 chúng ta sẽ nối chân MOSI của AVR với chân SDI này.
-    LDAC: chân chốt dữ liệu (tích cực âm). Nếu chân này ở mức cao thì dữ liệu số truyền đến MCP4822 không được chuyển đổi thành và xuất ra chân analog out. Nếu không cần chức năng chốt dữ liệu hãy nối chân này với GND để giảm số lượng đường điều khiển.
-    VOUTB: chân xuất điện áp sau khi chuyển đổi, kênh B.
-    AVSS: chân GDN.
-    VOUTA: chân xuất điện áp sau khi chuyển đổi, kênh A.
Có thể kết nối MCP4822 bằng một mạch điện đơn giản như trong hình 2.

Hình 2. Mạch ứng dụng đơn giản của MCP4822.

     Điều khiển chuyển đổi DAC của MCP4822 rất đơn giản, chỉ cần xuất giá trị và lệnh đúng định dạng theo yêu cầu của chip thông qua chuẩn SPI là có thể tạo ra các tín hiệu analog trên các chân VOUTA và VOUTB. Như thế có 2 việc cần nắm rõ đó là chuẩn giao tiếp SPI và định dạng dữ liệu cần truyền đến SPI (định dạng do MCP4822 định sẵn). Do chúng ta thực hiện điều khiển MCP4822 bằng AVR, để nắm rõ phương pháp giao tiếp SPI người đọc có thể tham khảo bài “Giao tiếp SPI”. Trong bài này tôi chỉ trình bày định dạng dữ liệu của MCP4822 và chương trình mẫu giao tiếp.
     Nếu bạn có trong tay datasheet của chip MCP4822 hãy tham khảo phần “Write Command” trong mục “Serial Interface” (trang 17 trong datasheet tôi có). Phần “Write command” giải thích định dạng một lệnh (Command) mà một SPI master (AVR) cần viết vào MCP4822 để thực hiện một chuyển đổi DAC. Tuy gọi là “lệnh” nhưng thực ra nó bao gồm cả lệnh và dữ liệu. Quan sát hình 3 để biết định dạng 1 lệnh của MCP4822.

Hình 3. Định dạng lệnh cho MCP4822.

      Lệnh gởi cho MCP4822 dài 16 bits. Do việc truyền nhận SPI thực hiện mỗi byte 8 bits nên lệnh 16 bits được chia thành 2 bytes gọi là nửa cao (Upper Half) và nửa thấp (Lower Half). Trong 16 bits đó có 12 bits dữ liệu (gọi là D0 đến D11), 3 bits điều khiển và 1 bits không sử dụng. Nửa thấp chứa 8 bits thấp (D0:7) của phần dữ liệu. 4 bits dữ liệu còn lại (D8:11) chứa trong 4 bits thấp của nửa cao. Các bits điều khiển được bố trí trong các vị trí cao của nửa cao. Ý nghĩa các bits điều khiển được giải thích như sau:
     - A/B: là bit chọn kênh, bit này nằm ở vị trí cao nhất (bit 7) của nửa trên. Do MCP4822 có 2 kênh DAC độc lập A và B, để chọn kênh làm việc chúng ta cần cài đặt bit A/B này. Khi A/B=0 kênh A được chọn, ngược lại A/B=1 khi chúng ta muốn chọn kênh B.
     - GA: là bit 5 trong nửa cao bit này cho phép chọn hệ số khuyếch đại cho tín hiệu analog ngõ ra. Cụ thể, nếu GA=0 thì analog ngõ ra được khuyếch đại 2 lần trong khi nếu GA=1 thì analog ngõ ra không được khuyếch đại (1 lần). Để hiểu cách tính giá trị analog ngõ ra chúng ta cần nhớ đến điện áp tham chiếu và độ phân giải của MCP4822. Nhắc lại chip này có điện áp tham chiếu trong 2.048V và độ phân giải 12 bits (12 bits có thể biểu diễn 4096 số thập phân : 212=4096), điều này có nghĩa là nếu chúng ta xuất cho MCP4822 một giá trị số 4096 thì điện áp ngõ ra analog là 2.096V. Gọi D là giá trị dữ liệu (12 bits) gởi đến MCP4822 trên SPI, trong trường hợp bình thường (không khuyếch đại) điện áp ngõ ra (VOUTA hoặc VOUTB) sẽ được tính Vout=2.048*D/4096. Nếu GA=0 (khuyếch đại 2 lần) thì điện áp ngõ ra là: Vout=2*2.048*D/4096.
     - SHDN: là bit tích cực thấp tắt ngõ ra. Nếu bit này bằng 0 thì ngõ ra analog bị tắt. Vì vậy, khi sử dụng MCP4822 cần set bit này lên 1.
     Tóm lại việc điều khiển MCP4822 được thực hiện thông qua 2 bước. Ở bước 1 chúng ta chuẩn bị sẵn các nửa cao và nửa thấp, đặc biệt là 3 bits điều khiển trong nửa cao. Bước 2 chúng ta gởi lần lượt 2 nửa này đến MCP4822 thông qua giao diện SPI theo thứ tự nửa cao trước, nửa thấp sau.

II. AVR và MCP4822

     Phần này tôi hướng dẫn lập trình điều khiển và giao tiếp với MCP4822 bằng AVR ATmega32, dùng WinAVR. Do MCP4822 hoạt động như một Slave SPI, bạn nhất thiết phải đọc lại “Bài 7 -  Giao tiếp SPI”. Tôi sẽ không đề cập lại toàn bộ giao diện SPI nhưng tóm tắt cách thực hiện với AVR như sau:
     - Giao diện SPI trên AVR gồm 4 chân SCK (xung giữ nhịp), MISO (Master Input, Slave Output), MOSI (Master Output, Slave Input) và SS (Slave Select). Để sử dụng SPI trên AVR đầu tiên cần chọn hướng các chân giao diện này thông qua thanh ghi DDRx của PORT tương ứng (ví dụ trên Atmega32, các chân này nằm ở PORTB).
     -  Chọn chế độ và kích hoạt SPI thông qua thanh ghi điều khiển SPCR.
     -  Muốn gởi 1 byte dữ liệu đến Slave, cần kéo chân chọn chip SS (nối với chân SS hoặc CS của Slave) xuống 0. Xuất dữ liệu ra thanh ghi SPDR và chờ đến khi bit trạng thái SPIF trong thanh ghi trạng thái SPSR được set.
     Chúng ta đã chuẩn bị đầy đủ để giao tiếp với MCP4822. Phần tiếp theo tôi sẽ trình bày chương trình và mô phỏng giao tiếp giữa AVR và DS1307. Hãy vẽ một mạch điện bằng Proteus như trong hình 4. Đây là ví dụ dùng MCP4822 làm function generator, trong đó kênh A xuất 1 hàm sine có tần số 50Hz và biên độ khoảng 4.094V và kênh B xuất 1 hàm “răng cưa” (saw). Do MCP4822 không hỗ trợ giá trị âm nên tín hiệu xuất ra sẽ được dịch chuyển lên phía trên.

Hình 4. Ví dụ giao tiếp AVR – MCP4822.

     Tôi sẽ chia chương trình thành 2 phần, phần giao tiếp với MCP4822 thông qua SPI được viết trong file myDS1307RTC.h và phần ví dụ xuất 2 hàm sine và saw ra MCP4822, hiển thị được viết trong file SPIDAC_test.c
List 1. myDS1307RTC.h

      Phần định nghĩa macro cbi, sbi và vị trí các chân SPI thì đã rất quen thuộc, tôi chỉ nói thêm về 3 chương trình con trong đoạn code trên, đó là: SPI_MasterInit(void), SPI_rByte(uint8_t data) và DAC_write(uint8_t ch, uint8_t gain, uint16_t data).
     - SPI_MasterInit(void): khởi động SPI ở chế độ Master. Việc này thực hiện đơn giản bằng cách định hướng cho các chân SPI (dòng 16) và kích hoạt SPI ở Master mode thông qua thanh ghi SPCR(dòng 17). Bạn cần xem lại bài “Giao tiếp SPI” để hiểu rõ hơn cấu trúc thanh ghi SPCR và điều khiển SPI.
     - SPI_rByte(uint8_t data): truyền một byte đến SPI Slave. Để phát dữ liệu ra các chân SPI, chúng ta cần viết dữ liệu này (biến data) vào thanh ghi SPDR của AVR, quá trình phát dữ liệu tự động xảy ra (tất nhiên trước đó Slave phải được chọn trước thông qua chân SS). Quá trình truyền kết thúc khi bit SPIE trong thanh ghi trạng thái SPSR được set lên 1 (xem dòng 21).
     - DAC_write(uint8_t ch, uint8_t gain, uint16_t data): truyền lệnh 16 bits đến MCP4822. Đây là chương trình con quan trọng nhất trong list 1, thực hiện gởi một lệnh 2 bytes đến chip MCP4822 thông qua chuẩn SPI. Phần quan trọng nhất trong chương trình con này là “chuẩn bị” giá trị của lệnh cần truyền (dòng 28 đến 33). Tham số ch trong đoạn chương trình con này là giá trị chọn kênh (A hoặc B), ch=0 tương ứng kênh A được chọn, ngược lại ch=1 là kênh B. Tham số gain là hệ số khuyếch đại, gain mang 2 giá trị 1 hoặc 2. Dòng 29 chúng ta tính lại giá trị bit G (xem lại phần định dạng lệnh của MCP4822), nếu gain = 1 thì G =1 ngược lại G=0 (hệ số khuyếch đại bằng 1). Dòng 30 là giải thuật dịch bit, kết quả dòng lệnh này biến tạm Hdata sẽ chứa 4 bit cao nhất của giá trị cần chuyển đổi (data) ở 4 vị trí thấp của Hdata (D11-8 nằm trong các bit 3-0 của Hdata). Dòng 32 ghép các bit điều khiển ch, G với 4 bit dữ liệu tạo thành nửa cao của lệnh (Hcmd). Chú ý trong dòng này, bit SHDN phải được set bằng 1 thông qua (1<<4). Ở dòng 33, chúng ta gán 8 bit thấp của data cho nửa thấp Lcmd. Sau cùng là truyền 2 nửa cao và thấp của lệnh ra SPI. Trước khi truyền, cần reset chân SS về 0 (dòng 35) và rồi truyền liên tiếp 2 bytes Hcmd và Lcmd. Kết thúc quá trình truyền lệnh, chân SS được kéo lên 1.
     Như vậy, với DAC_write(uint8_t ch, uint8_t gain, uint16_t data) chúng ta có thể gởi một lệnh gồm giá trị số cần chuyển đổi, kênh và hệ số khuyếch đại đến CMP4822. Hàm này sẽ được sử dụng trực tiếp trong phần ứng dụng tạo các tín hiệu analog.
     Để kiểm tra các hàm giao tiếp MCP4822, hãy tạo 1 Project bằng WinAVR với tên gọi MCP4822, tạo file SPIDAC_Test.c và viết code như trong list 2.
List 2. SPIDAC_Test.c

     Chương trình demo MCP4822 dùng các hàm trong file mySPIDAC.h trước đó, bạn cần copy file này vào cùng thư mục với chương trình demo này. Như đã trình bày, chương trình demo này sẽ điều khiển MCP4822 tạo ra 1 sóng sine và saw trên 2 kênh analog output. Thời gian lấy mẫu được chọn 200us. Cứ sau 200us, giá trị mới được tính và gởi đến MCP4822. Timer 0 được dùng để tạo ngắt tràn cứ mỗi 200us. Do chương trình có sử dụng hàm toán sin, chúng ta cần include header math.h trong phần đầu. Các biến được khai báo với từ khóa volatile vì chúng sẽ được dùng trong trình phục vụ ngắt tràn của Timer0. Chúng ta sẽ viết chương trình theo hướng “Interrupt base” nên nội dung phần chương trình trong main sẽ rất đơn giản. Nó bao gồm các khai báo cho Timer0 (các dòng từ 13 đến 16) và gọi chương trình khởi động SPI ở chế độ Master (dòng 18). Tất cả các việc còn lại do trình phục vụ ngất tràn Timer0 thực hiện.
     Biến time là một biến dạng float lưu thời gian thực thi tính theo giây và dùng trong tính toán các hàm tạo tín hiệu sine, saw. Do ngắt tràn Timer0 xảy ra cứ mỗi 200us, biến time được cộng thêm 0.0002s trong trình phục vụ ngắt (dòng 27). Trong dòng 29 biến ch1 được tính theo một hàm sine có tần số f, giá trị mặc định của f là 50 (xem dòng khai báo 8) và có thể thay đổi. Hàm sine sau đó được nhân với biên độ (2017) và dịch lên trên 2017 giá trị để tránh giá trị âm (dòng 30). Sau đó biến ch1 được xuất ra MCP4822 bằng lệnh DAC_write(0, 2, ch1) ở dòng 31. Tham số 0 chỉ kênh A, tham số 2 là hệ số khuyếch đại và ch1 là giá trị số cần chuyển đổi. Tương tự như thế, biến ch2 được gởi ra kênh B nhưng không được khuyếch đại bằng dòng lệnh DAC_write(1, 1, ch2). Kết quả mô phỏng 2 tín hiệu analog trên 2 kênh A và B được đo bằng Oscilloscope ảo của phần mềm mô phỏng Proteus được thể hiện trong hình 5.

Hình 5. Mô phỏng các tín hiệu analog.

     Khi mô phỏng ví dụ này, bạn cần chú ý chọn xung clock cho chip Atmega32 là 16MHz, đồng thời set tín hiệu các kênh của Oscilloscope là DC (xem hình 5, channel A và B trên Oscilloscope). Quan sát 2 tín hiệu trên Oscilloscope bạn thấy tín hiệu saw (màu vàng, kênh B) có peak (giá trị lớn nhất tính từ 0, 2V trong hình 5) khoảng 1/2 peak của tín hiệu sine (kênh A, khoảng 4V trong hình 5). Nếu xem lại hàm tạo của 2 biến ch1 và ch2 trong trình phục vụ ngắt tràn của Timer0 (dòng 29 và dòng 34) bạn dễ dàng nhận thấy 2 biến này có peak  như nhau, 4094. Như thế, điện áp peak của cả 2 kênh phải là 2.048*4094/4096=2.047V. Nhưng thực tế, peak của hàm sine lớn hơn 4V??? Câu trả lời là tín hiệu analog của kên A đã được khuyếch đại 2 lần khi gọi hàm DAC_write(0, 2, ch1) ở dòng 31.
      Bạn đọc hãy tự phát triển thêm các ứng dụng cho module chuyển đổi DAC MCP4822 dựa vào ví dụ mẫu này.