Bài 6 - Chuyển đổi ADC

( 76 Votes )
There are no translations available.

Nội dung Các bài cần tham khảo trước
  1. Bạn sẽ đi đến đâu.
  2. Chuyển đổi tín hiệu tương tự sang tín hiệu số (ADC).
  3. Bộ chuyển đổi ADC trên AVR.
Download ví dụ

 

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

      Bài học này, như tên của nó, sẽ giới thiệu cách sử dụng bộ chuyển đổi tương tự - số (analog to digital converter - ADC). Công cụ chính cũng là 2 bộ phần mềm quen thuộc WinAVR và Proteus.

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ý chuyển đổi AD.
-         Chuyển đổi ADC đơn kênh trên AVR.       
-         Sử dụng chuyển đổi ADC đơn kênh trên AVR, hiển thị số 4 digit bằng LED 7 đoạn.

II. Chuyển đổi dữ liệu tương tự (analog) sang dữ liệu số (digital).

      Trong các ứng dụng đo lường và điều khiển bằng vi điều khiển bộ chuyển đổi tương tự-số (ADC) là một thành phần rất quan trọng. Dữ liệu trong thế giới của chúng ta là các dữ liệu tương tự (analog). Ví dụ nhiệt độ không khí buổi sáng là 25oC và buổi trưa là 32oC, giữa hai mức giá trị này có vô số các giá trị liên tục mà nhiệt độ phải “đi qua” để có thể đạt mức 32oC từ 25oC, đại lượng nhiệt độ như thế gọi là một đại lượng analog. Trong khi đó, rõ ràng vi điều khiển là một thiết bị số (digital), các giá trị mà một vi điều khiển có thể thao tác là các con số rời rạc vì thực chất chúng được tạo thành từ sự kết hợp của hai mức 0 và 1. Ví dụ chúng ta muốn dùng một thanh ghi 8 bit trong vi điều khiển để lưu lại các giá trị nhiệt độ từ 0oC đến 255 oC, như chúng ta đã biết, một thanh ghi 8 bit có thể chứa tối đa 256 (28) giá trị nguyên từ 0 đến 255, như thế các mức nhiệt độ không nguyên như 28.123 oC sẽ không được ghi lại. Nói cách khác, chúng ta đã “số hóa” (digitalize) một dữ liệu analog thành một dữ liệu digital. Quá trình “số hóa” này thường được thực hiện bởi một thiết bị gọi là “bộ chuyển đổi tương tự - số hay đơn giản là ADC (Analog to Digital Converter).

      Có rất nhiều phương pháp chuyển đổi ADC, tôi không có ý định giải thích cụ thể các nguyên lý chuyển đổi này trong bài học về AVR, tuy nhiên tôi sẽ giới thiệu một cách chuyển đổi rất cơ bản và phổ biến để các bạn phần nào nắm được cách mà một bộ ADC làm việc. Phương pháp chuyển đổi mà tôi nói là phương pháp chuyển đổi trực tiếp (direct converting)  hoặc flash ADC.  Các bộ chuyển đổi ADC theo phương pháp này được cấu thành từ một dãy các bộ so sánh (như  opamp), các bộ so sánh được mắc song song và được kết nối trực tiếp với tín hiệu analog cần chuyển đổi. Một điện áp tham chiếu (reference) và một mạch chia áp được sử dụng để tạo ra các mức điện áp so sánh khác nhau cho mỗi bộ so sánh. Hình 1 mô tả một bộ chuyển đổi flash ADC có 4 bộ so sánh, Vin là tín hiệu analog cần chuyển đổi và giá trị sau chuyển đổi là các con số tạo thành từ sự kết hợp các mức nhị phân trên các chân Vo. Trong hình 1, bạn thấy rằng do anh hưởng của mạch chia áp (các điện trở mắc nối tiếp từ điện áp +15V đến ground), điện áp trên chân âm (chân -) của các bộ so sánh sẽ khác nhau. Trong lúc chuyển đổi, giả sử điện áp Vin lớn hơn điện áp “V-“ của bộ so sánh 1 (opamp ở phía thấp nhất trong mạch) nhưng lại nhỏ hơn điện áp V- của các bộ so sánh khác, khi đó ngõ Vo1 ở mức 1 và các ngõ Vo khác ở mức 0, chúng ta thu được một kết quả số. Một cách tương tự, nếu tăng điện áp Vin ta thu được các tổ hợp số khác nhau. Với mạch điện có 4 bộ so sánh như trong hình 1, sẽ có tất cả 5 trường hợp có thể xảy ra, hay nói theo cách khác điện áp analog Vin được chia thành  5 mức số khác nhau. Tuy nhiên, bạn chú ý là các ngõ Vo không phải là các bit của tín hiệu số ngõ ra, chúng chỉ là đại diện để tổ hợp thành tín hiệu số ngõ ra, dễ hiểu hơn chúng ta không sử dụng được các bit Vo trực tiếp mà cần một bộ giải mã (decoder). Trong bảng 1 tôi trình bày kết quả sau khi giải mã ứng với các tổ hợp của các ngõ Vo.

Flash ADC

Hình 1. Mạch flash ADC với 4 bộ so sánh.

Bảng 1 Giá trị số ngõ ra sau khi giải mã.

      Độ phân giải (resolution): như trong ví dụ trên, nếu mạch điện có 4 bộ so sánh, ngõ ra digital sẽ có 5 mức giá trị. Tương tự nếu mạch điện có 7 bộ so sánh thì sẽ có 8 mức giá trị có thể ở ngõ ra digital, khoảng cách giữa các mức tín hiệu  trong trường hợp 8 mức sẽ nhỏ hơn trường hợp 4 mức. Nói cách khác, mạch chuyển đổi với 7 bộ so sánh có giá trị digital ngõ ra “mịn” hơn khi chỉ có 4 bộ, độ “mịn” càng cao  tức độ phân giải (resolution) càng lớn. Khái niệm độ phân giải được dùng để chỉ số bit cần thiết để chứa hết các mức giá trị digital ngõ ra. Trong trường hợp có 8 mức giá trị ngõ ra, chúng ta cần 3 bit nhị phân để mã hóa hết các giá trị này, vì thế mạch chuyển đổi ADC với 7 bộ so sánh sẽ có độ phân giải là 3 bit. Một cách tổng quát, nếu một mạch chuyển đổi ADC có độ phân giải n bit thì sẽ có 2n mức giá trị có thể có ở ngõ ra digital. Để tạo ra một mạch chuyển đổi flash ADC có độ phân giải n bit, chúng ta cần đến 2n-1 bộ so sánh, giá trị này rất lớn khi thiết kế bộ chuyển đổi ADC có độ phân giải cao, vì thế các bộ chuyển đổi flash ADC thường có độ phân giải ít hơn 8 bit. Độ phân giải liên quan mật thiết đến chất lượng chuyển đổi ADC, việc lựa chọn độ phân giải phải phù hợp với độ chính xác yêu cầu và khả năng xử lý của bô điều khiển. Trong 2 mô tả một ví dụ “số hóa” một hàm sin analog thành dạng digital.

sine

Hình 2. Analog và digital của hàm sin.

      Điện áp tham chiếu (reference voltage): Cùng một bộ chuyển đổi ADC nhưng có người muốn dùng cho các mức điện áp khác nhau, ví dụ người A muốn chuyển đổi điện áp trong khoảng 0-1V trong khi người B muốn dùng cho điện áp từ 0V đến 5V. Rõ ràng nếu hai người này dùng 2 bộ chuyển đổi ADC đều có khả năng chuyển đổi đến điện áp 5V thì người A đang “phí phạm” tính chính xác của thiết bị. Vấn đề sẽ được giải quyết bằng một đại lượng gọi là điện áp tham chiếu - Vref (reference voltage). Điện áp tham chiếu thường là giá trị điện áp lớn nhất mà bộ ADC có thể chuyển đổi. Trong các bộ ADC, Vref thường là thông số được đặt bởi người dùng, nó là điện áp lớn nhất mà thiết bị có thể chuyển đổi. Ví dụ, một bộ ADC 10 bit (độ phân giải) có Vref=3V, nếu điện áp ở ngõ vào là 1V thì giá trị số thu được sau khi chuyển đổi sẽ là: 1023x(1/3)=314. Trong đó 1023 là giá trị lớn nhất mà một bộ ADC 10 bit có thể tạo ra (1023=210-1). Vì điện áp tham chiếu ảnh hưởng đến độ chính xác của quá trình  chuyển đổi, chúng ta cần tính toán để chọn 1 điện áp tham chiếu phù hợp, không được nhỏ hơn giá trị lớn nhất của input nhưng cũng đừng quá lớn.

II. Chuyển đổi ADC trên AVR.

      Chip AVR ATmega32 của Atmel có tích hợp sẵn các bộ chuyển đổi ADC với độ phân giải 10 bit. Có tất cả 8 kênh đơn (các chân ADC0 đến ADC7), 16 tổ hợp chuyển đổi dạng so sánh, trong đó có 2 kênh so sánh có thể khuyếch đại. Bộ chuyển đổi ADC trên AVR không hoạt động theo nguyên lý flash ADC mà tôi đề cập ở phần trên, ADC trong AVR là loại chuyển đổi xấp xỉ lần lượt (successive approximation ADC).

      ADC trên AVR cần được “nuôi” bằng nguồn điện áp riêng ở chân AVCC, giá trị điện áp cấp cho AVCC không được khác nguồn nuôi chip (VCC) quá +/-0.3V. Nhiễu (noise) là vấn đề rất quan trọng khi sử dụng các bộ ADC, để giảm thiểu sai số chuyển đổi do nhiễu, nguồn cấp cho ADC cần phải được “lọc” (filter) kỹ càng. Một cách đơn giản để tạo nguồn AVCC là dùng một mạch LC kết nối từ nguồn VCC của chip như minh họa trong hình 3, đây là cách được gợi ý bởi nhà sản xuất AVR.

Source

Hình 3. Tạo nguồn AVCC từ VCC.

      Điện áp tham chiếu cho ADC trên AVR có thể được tạo bởi 3 nguồn: dùng điện áp tham chiếu nội 2.56V (cố định), dùng điện áp AVCC hoặc điện áp ngoài đặt trên chân VREF. Một lần nữa, bạn cần chú ý đến noise khi đặt điện áp tham chiếu, nếu dùng điện áp ngoài đặt trên chân VREF thì điện áp này phải được lọc thật tốt, nếu dùng điện áp tham chiếu nội  2.56V hoặc AVCC thì chân VREF cần được nối với một tụ điện. Việc chọn điện áp tham chiếu sẽ được đề cập chi tiết trong phần sử dụng ADC.
      Các chân trên PORTA của chip ATmega32 được dùng cho bộ ADC, chân PA0 tương ứng kênh ADC0 và chân PA7 tương ứng với kênh ADC7.

1. Thanh ghi.

      Có 4 thanh trong bộ ADC trên AVR trong đó có 2 thanh ghi data chứa dữ liệu sau khi chuyển đổi, 2 thanh ghi điều khiển và chứa trạng thái của ADC.

      - ADMUX (ADC Multiplexer Selection Register): là 1 thanh ghi 8 bit điều khiển việc chọn điện áp tham chiếu, kênh và chế độ hoạt động của ADC. Chức năng của từng bit trên thanh ghi này sẽ được trình bày cụ thể như sau:

ADMUX

  • Bit 7:6- REFS1:0 (Reference Selection Bits): là các bit chọn điện áp tham chiếu cho ADC, 1 trong 3 nguồn điện áp tham chiếu có thể được chọn là: điện áp ngoài từ chân VREF, điện áp tham chiếu nội 2.56V hoặc điện áp AVCC. Bảng 2 tóm tắt giá trị các bit và điện áp tham chiếu tương ứng.

Bảng 2: Chọn điện áp tham chiếu

ADMUX
  • Bit 5-ADLAR (ADC Left Adjust Result): là bit cho phép hiệu chỉnh trái kết quả chuyển đổi. Sở dĩ có bit này là vì ADC trên AVR có độ phân giải 10 bit, nghĩa là kết quả thu được sau chuyển đổi là 1 số có độ dài 10 bit (tối đa 1023), AVR bố trí 2 thanh ghi data 8 bit để chứa giá trị sau chuyển đổi. Như thế giá trị chuyển đổi sẽ không lắp đầy 2 thanh ghi  data, trong một số trường hợp người dùng muốn 10 bit kết quả nằm lệch về phía trái trong khi cũng có trường hợp người dùng muốn kết quả nằm về phía phải. Bit ADLAR sẽ quyết định vị trí của 10 bit kết quả trong 16 bit của 2 thanh ghi data. Nếu ADLAR=0 kết quả sẽ được hiệu chỉnh về phía phải (thanh ghi ADCL chứa trọn 8 bit thấp và thanh ghi ADCH chứa 2 bit cao trong 10 bit kết quả), và nếu ADLAR=1 thì kết quả được hiệu chỉnh trái (thanh ghi ADCH chứa trọn 8 bit cao nhất, các bit từ 9 đến 2, và thanh ADCL chứa 2 bit thấp nhất trong 10 bit kết quả (bạn xem hình cách bố trí 2 thanh ghi ADCL và ADCH bên dưới để hiểu rõ hơn).
  • Bits 4:0-MUX4:0 (Analog Channel and Gain Selection Bits): là 5 bit cho phép chọn kênh, chế độ và cả hệ số khuyếch đại cho ADC. Do bộ ADC trên AVR có nhiều kênh và cho phép thực hiện chuyển đổi ADC kiểu so sánh (so sánh điện áp giữa 2 chân analog) nên trước khi thực hiện chuyển đổi, chúng ta cần set các bit MUX để chọn kênh và chế độ cần sử dụng. Bảng 3 tóm tắt các chế độ hoạt động của ADC thông qua các giá trị của các bit MUX. Trong bảng này, ứng với các giá trị từ 00000 đến 00111 (nhị phân), các kênh ADC được chọn ở chế độ đơn kênh (tín hiệu input lấy trực tiếp từ các chân analog và so sánh với 0V), giá trị từ 01000 đến 11101 tương ứng với chế độ chuyển đổi so sánh.

Bảng 3: Chọn chế độ chuyển đổi.

mode1
mode2

      - ADCSRA (ADC Control and Status RegisterA): là thanh ghi chính điều khiển hoạt động và chứa trạng thái của module ADC.

ADCSRA

      Từng bit của thanh ghi ADCSRA được mô tả như bên dưới:

  • Bit 7 - ADEN(ADC Enable): viết giá trị 1 vào bit này tức bạn đã cho phép module ADC được sử dụng. Tuy nhiên khi ADEN=1 không có nghĩa là ADC đã hoạt động ngay, bạn cần set một bit khác lên 1 để bắt đầu quá trình chuyển đổi, đó là bit ADSC.
  • Bit 6 - ADSC(ADC Start Conversion): set bit này lên 1 là bắt đầu khởi động quá trình chuyển đổi. Trong suốt quá trình chuyển đổi, bit ADSC sẽ được giữ nguyên giá trị 1, khi quá trình chuyển đổi kết thúc (tự động), bit này sẽ được trả về 0. Vì vậy bạn không cần và cũng không nên viết giá trị 0 vào bit này ở bất kỳ tình huống nào. Để thực hiện một chuyển đổi, thông thường chúng ta sẽ set bit ADEN=1 trước và sau đó set ADSC=1.
  • Bit 4 – ADIF(ADC Interrupt Flag): cờ báo ngắt. Khi một chuyển đổi kết thúc, bit này tự động được set lên 1, vì thế người dùng cần kiểm tra giá trị bit này trước khi thực hiện đọc giá trị chuyển đổi để đảm bảo quá trình chuyển đổi đã thực sự hoàn tất.
  • Bit 3 – ADIE(ADC Interrupt Enable): bit cho phép ngắt, nếu bit này được set bằng 1 và bit cho phép ngắt toàn cục (bit I trong thanh ghi trạng thái của chip) được set, một ngắt sẽ xảy ra khi một quá trình chuyển đổi ADC kết thúc và các giá trị chuyển đổi đã được cập nhật (các giá trị chuyển đổi chứa trong 2 thanh ghi ADCL và ADCH).
  • Bit 2:0 – ADPS2:0(ADC Prescaler Select Bits): các bit chọn hệ số chia xung nhịp cho ADC. ADC, cũng như tất cả các module khác trên AVR, cần được giữ nhịp bằng một nguồn xung clock. Xung nhịp này được lấy từ nguồn xung chính của chip thông qua một hệ số chia. Các bit ADPS cho phép người dùng chọn hệ số chia từ nguồn clock chính đến ADC. Tham khảo bảng 4 để biết cách chọn hệ số chia.

Bảng 4: Hệ số chia xung nhịp cho ADC.

division

      - ADCL và ADCH (ADC Data Register): 2 thanh ghi chứa giá trị của quá trình chuyển đổi. Do  module ADC trên AVR có độ phân giải tối đa 10 bits nên cần 2 thanh ghi để chứa giá trị chuyển đổi. Tuy nhiên tổng số bít của 2 thanh ghi 8 bit là 16, con số này nhiều hơn 10 bit của kết quả chuyển đổi, vì thế chúng ta được phép chọn cách ghi 10 bit  kết quả vào 2 thanh ghi này. Bit ADLAR trong thanh ghi ADMUX quy định cách mà kết quả được ghi vào.

ADLAR=0:

ADLAR0

ADLAR=1:

ADLAR1

      Thông thường, 2 thanh ghi data được sắp xếp theo định dạng ADLAR=0, ADCL chứa 8 bit thấp và 2 bit thấp của ADCH chứa 2 bit cao nhất của giá trị thu được. Chú ý thứ tự đọc giá trị từ 2 thanh ghi này, để tránh đọc sai kết quả, bạn cần đọc thanh ghi ADCL trước và ADCH sau, vì sau khi ADCH được đọc, các thanh ghi data có thể được cập nhật giá trị tiếp theo.

      - SFIOR(Special FunctionIO Register C): thanh ghi chức năng đặc biệt, 3 bit cao trong thanh ghi này quy định nguồn kích ADC nếu chế độ Auto Trigger được sử dụng. Đó là các bit ADTS2:0 (Auto Trigger Source 2:0). Các loại nguồn kích được trình báy trong bảng 5.

SFIOR

Bảng 5: Nguồn kích ADC trong chế độ Auto Trigger.

Trigger source

2. Sử dụng ADC- Chuyển đổi đơn kênh.

      Khái niệm đơn kênh được hiểu là đại lượng cần chuyển đổi là các điện áp đặt trực tiếp trên các chân analog của chip, giá trị điện áp này được so sánh với 0V của chip, hay nói một cách khác, điện áp cần chuyển đổi và chip AVR có “mass chung”. Chúng ta sẽ minh họa cách sử dụng ADC trên AVR ở chế độ đơn kênh bằng ví dụ đọc và hiển thị giá trị ADC trên các LED 7 đoạn. Như minh họa trong hình 4, chúng ta sẽ dùng 4 LED để hiển thị 4 chữ số của kết quả, do chúng ta đều biết ADC trên AVR có độ phân giải 10 bit nên kết quả chuyển đổi tối đa là 1023, 4 LED là đủ để hiển thị kết quả này. 4 chip 7447 được dùng để điều khiển 4 LED, chúng ta cần 16 đường để xuất dữ liệu hiển thị lên 4 LED vì thế PORTB và PORTC sẽ được dùng cho mục đích này. 4 bit cao của PORTC(PC4:7) chứa chữ số hàng nghìn của kết quả, 4 bit thấp PC0:3 chứa chữ số hàng trăm, 4 bit cao của PORTB(PB4:7) dùng xuất chữ số hàng chục và 4 bit PB0:3 dành cho chữ số hàng đơn vị. Đại lượng cần chuyển đổi là điện áp trên chân ADC0 (kênh 0 của ADC, chân 0 trong PORTA chip ATmega32), điện áp được tạo ra bằng một biến trở RV1. Thay đổi giá trị biến trở, điện áp rơi trên ADC0 thay đổi và được cập nhật trực tiếp trên các LED. Giá trị hiển thị trên LED không phải là giá trị điện áp mà là giá trị tương đối sau khi chuyển đổi. Trong ví dụ này, tôi sẽ trình bày dạng tổng quát, việc đọc ADC và hiển thị LED được viết trong các chương trình con tương ứng. Bằng cách này, các bạn có thể dễ dàng sửa đổi và mở rộng ví dụ sau này.

demo

Hình 4. Đọc ADC đơn kênh.

      List 1 trình bày đoạn code minh họa đọc ADC đơn kênh và hiển thị kết quả trên LED 7 đoạn.

List 1. Đọc ADC đơn kênh và hiển thị bằng LED 7 đoạn.
single ADC
        
Tôi tạm thời chia đoạn chương trình thành 4 phần, phần 1 là các định nghĩa (dòng 4 đến 7), phần 2 là chương trình con đọc ADC đơn kênh (dòng 10 đến 14), phần 3 là chương trình con hiển thị môt giá trị 4 chữ số lên 4 LED 7 đoạn (từ dòng 17 đến 30) và phần 4 là chương trình chính. Chúng ta sẽ tìm hiểu theo từng phần.

      - Phần 1: ba dòng 4, 5 và 6 chúng ta định nghĩa 3 biến đại diện tên của 3 mode điện áp tham chiếu có thể dùng cho ADC. Xem lại bảng 2 chúng ta biết rằng điện áp tham chiếu được chọn thông qua 2 bit REFS trong thanh ghi ADMUX, có 3 loại điện áp có thể được chọn. Biến AREF_MODE tương ứng với trường hợp chúng ta muốn lấy điện áp trên chân AREF  làm điện áp tham chiếu, đối chiếu bảng 2 chúng ta cần set 2 bit REFS bằng 0, và dòng 4 “ #define AREF_MODE          0”  thực hiện việc này. Tương tự, biến INT_MODE đại diện cho trường hợp điện áp tham chiếu nội 2.56V và được định nghĩa cho phép set 1 bit REFS lên 1  “#define  INT_MODE       (1<<REFS1)|(1<<REFS0)”. Biến AVCC_MODE đại diện trường hợp điện áp tham chiếu lấy từ chân AVCC. Cuối cùng, biến ADC_VREF_TYPE được định nghĩa  là biến chọn mode mà chúng ta thực sự  muốn dùng cho ADC, trong ví dụ này tôi chọn điện áp tham chiếu lấy từ chân AVCC vì thế tôi định nghĩa “#define ADC_VREF_TYPE AVCC_MODE”. Bit ADC_VREF_TYPE sẽ được gán cho thanh ghi ADMUX khi khởi động ADC trong chương trình chính.

      - Phần 2-chương trình con đọc ADC đơn kênh “uint16_t  read_adc(unsigned char adc_channel)”: tên chương trình là read_adc và adc_channel là tham số cần truyền cho chương trình con, tham số này là chỉ số kênh muốn đọc (từ kênh 0 đến kênh 7). Giá trị trả về là một số nguyên không dấu 16 bit (kiểu unsigned int của C), tuy nhiên trong ví dụ này tôi dùng kiểu dữ liệu uint16_t thay cho unsigned int, uint16_t là một cách định nghĩa kiểu dữ liệu nguyên không dấu 16 bit của riêng thư viện gcc-avr. Dòng đầu tiên của đoạn chương trình con (dòng 11) là khai báo kênh muốn đọc bằng cách ghép giá trị kênh cho thanh ghi ADMUX “ADMUX =adc_channel | ADC_VREF_TYPE ;”. Xem lại cấu trúc thanh ghi ADMUX, trong thanh ghi này, ngoài các bit chọn nguồn điện áp tham chiếu REFS thì 5 bit thấp MUX4:0 cho phép chọn kênh ADC cần đọc. Tham khảo thêm bảng 3 chúng ta thấy rằng 8 giá trị đầu tiên của các bit MUX4:0 (từ 00000 đến 00111 nhị phân) tương ứng với 8 kênh đơn ADC0:7. Chính sự sắp xếp này cho phép chúng ta ghép trực tiếp giá trị kênh muốn đọc vào thanh ghi ADMUX thông qua dòng lệnh ADMUX =adc_channel | ADC_VREF_TYPE. Chúng ta dùng phép OR "|" để ghép giá trị kênh muốn đọc và chế độ tham chiếu của ADC trước khi gán cho thanh ghi ADMUX. Một chú ý quan trọng là giá trị của tham số adc_channel chỉ trong khoảng từ 0 đến 7 tương ứng với 8 chế độ đọc đơn kênh ADC trong bảng 3. Sau khi kênh đã được chọn, dòng 12 set bit ADCS trong thanh ghi ADCSRA để bắt đầu quá trình chuyển đổi “ADCSRA|=(1<<ADSC);”. Như đã đề cập trong khi khảo sát chức năng của bit ADIF trong thanh ghi ADCSRA, sau khi quá trình chuyển đổi kết thúc bit ADIF sẽ được tự động set lên 1, vì thế dòng code 13 được dùng để chờ cho bit này lên 1, tức chờ cho quá trình chuyển đổi kết thúc. Câu lệnh “loop_until_bit_is_set(ADCSRA,ADIF);” được hiểu là lặp cho đến khi bit ADIF trong thanh ghi ADCSRA được set lên 1, lệnh “loop_until_bit_is_set”  này được định nghĩa sẵn trong thư viện gcc-avr. Nếu quá trình chuyển đỗi đã kết thúc, kết quả chuyển đổi sẽ được chứa trong 2 thanh ghi ADCL và ADCH, 2 thanh ghi này được tự động gọp thành thanh ghi 16 bit ADCW (ADC WORD), dòng 14 “return ADCW” trả về kết quả chuyển đổi.

      - Phần 3-chương trình con hiển thị số có 4 chữ số lên 4 LED 7 đoạn “void LED7_out(uint16_t val)” :  val là số cần hiển thị, chúng ta khai báo 4 biến tạm “dvi, chuc, tram, nghin” đại diện cho các chữ số đơn vị, chục, trăm và nghìn ở dòng 18. Đồng thời, một biến tạm temp_val được dùng để lưu giá trị tạm thời của số val như trong dòng 19 “temp_val=val;”, cách làm này nhằm tránh thay đổi giá trị của bản thân val trong quá trình thao tác. Các dòng code từ 21 đến 26 thực hiện quá trình tách số val ra thành 4 các chữ số hàng đơn vị, chục, trăm và nghìn. Đây chỉ là phương pháp đại số thông thường nên tôi sẽ không giải thích thêm cho đoạn này. Hai dòng 28 và 29 xuất giá trị ra 4 LED 7 đoạn. Bốn LED 7 đoạn được điều khiển bởi các IC chuyển mã 7447, giá trị input choc các IC 7447 là các số BCD 4 bit. Vì thế, để xuất 4 chữ số ra 4 LED thông qua 7447 chúng ta cần 4x4=16 bit, trong ví dụ này tôi dùng PORTB và PORTC cho nhiệm vụ này. Bốn bit cao của PORTC sẽ chứa chữ số hàng nghìn, bốn bit thấp chứa chữ số hàng trăm, bốn bit cao của PORTB chứa chữ số hàng chục và bốn bit thấp PORTB chứa số đơn vị. Dòng code 28 “PORTB=(chuc<<4)+dvi;” xuất 2 chữ số chục và đơn vị ra PORTB, trong đó hàm “chuc<<4” nghĩa là dịch chữ số hàng chục sang trái 4 vị trí để đưa chữ số này lên 4 bit cao của PORTB, sau đó cộng chữ số đơn vị vào 4 bit thấp và cuối cùng là xuất ra PORTB. Tương tự chúng ta có thể xuất 2 chữ số hàng nghìn và hàng trăm ra PORTC thông qua dòng code 29 “PORTC=(nghin<<4)+tram”.

      - Phần 4-chương trình chính: do hầu hết các nhiệm vụ đã được thực hiện trong các đoạn chương trình con nên chương trình chính trong ví dụ này khá đơn giản. Hai dòng code 32 và 33 set các thông số cho ADC, dòng 32 “ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS0);” set các bit trong thanh ghi điều khiển ADCSRA, ADC được cho phép hoạt động bởi bit ADEN, các bit ADPS2:0 để chọn prescaler xung clock (xem lại phần mô tả thanh ghi ADCSRA), trong ví dụ này tôi chọn prescaler = 32 (bạn có thể chọn giá trị khác). Dòng 33 “ADMUX=ADC_VREF_TYPE;” cho phép chọn điện áp tham chiếu bằng cách gán biến ADC_VREF_TYPE mà chúng ta đã định nghĩa trong dòng code 7 cho thanh ghi ADMUX. Bạn cần chú ý là sau khi thực hiện 2 dòng code này, ADC chỉ mới ở tư thế “sẵn sàng” nhưng vẫn chưa hoạt động, ADC sẽ hoạt động khi chúng ta gọi chương trình con đọc adc. Trong vòng lặp while của chương trình chính chúng ta lần lượt đọc giá trị ADC ở kênh 0 bằng cách gọi chương trình con “read_adc(0)” ở dòng lệnh 39 “ADC_val=read_adc(0);” sau đó hiển thị ra LED 7 đoạn ở dòng 40 “LED7_out(ADC_val);” và cuối cùng là delay 1 khoảng thời gian nhỏ (100ms) trước khi lặp lại quá trình đọc và hiển thị.

Mô phỏng ví dụ: Tạo 1 project bằng Programmer Notepad và type đoạn code trên vào file source (xem phần tạo Project với WinAVR). Biên dịch và chạy mô phỏng với mạch điện trong hình 4. Điều chỉnh giá trị biến trở RV1 để thay đổi giá trị điện áp input của ADC kênh 0 và xem giá trị hiển thị trên các LED 7 đoạn. Hãy thay đổi giá trị biến ADC_VREF_TYPE trong dòng code 7 sang các mode khác như INT_MODE, biên dịch và mô phỏng lại chương trình, quan sát và so sánh sự khác nhau giữa các mode điện áp tham chiếu. Bạn sẽ dễ dàng nhận thấy rằng khi chọn điện áp tham chiếu nội 2.56V, khi tăng biến trở đến khoảng giữa thì kết quả chuyển đổi sẽ là 1023(giá trị lớn nhất của số 10 bit) và nếu tiếp tục tăng biến trở giá trị này sẽ không thay đổi. Điều này có nghĩa là nếu điện áp input lớn hơn điện áp tham chiếu thì kết quả chuyển đổi sẽ là 1023.

      Phần chuyển đổi ADC ở chế độ so sánh sẽ được trình bày trong 1 dịp khác ở phần ứng dụng.