Đã được đăng vào 04/12/2020 @ 08:46
Đo tụ điện bằng Arduino
Cách đo giá trị của tụ điện cỡ nhỏ từ vài pF đến cỡ lớn hàng nghìn uF chỉ bằng một chiếc bo mạch arduino với vài dòng code đơn giản, giúp ích cho các bạn học sinh, sinh viên cũng như các bạn làm nghiên cứu viên và các bạn đôi khi cần sao chép mạch điện tử là nội dung chính của bài viết này.
ngochoangimsat
Xem thêm:
- Đo cuộn cảm và tần số cộng hưởng mạch LC bằng Arduino
- Hack hệ thống CAN bus trên ô tô
- Cách tính giá trị tụ mắc song song, mắc nối tiếp
- Cách đọc trị số và ý nghĩa tụ điện
- Các tham số kỹ thuật đặc trưng của tụ điện và sơ đồ tương đương của tụ
1. Tổng quan
Cũng như đã trình bày trong bài viết “Cách đo cuộn cảm và đo tần số cộng hưởng của mạch LC bằng arduino”
Cùng nằm trong loạt bài viết “Tự làm các dụng cụ đo đạc điện tử bằng Arduino”
Để đo dung lượng của tụ điện có rất nhiều phương pháp khác nhau như: dùng máy hiện sóng, dùng đồng hồ đo điện dung,…
Và lý do vì sao tôi chọn Arduino để sử dụng cũng đã được giải thích trong bài viết trước đây.
Trong bài này sẽ không nhắc lại các vấn đề đó mà đi thẳng vào nội dung cần giải quyết.

Động đến vấn đề đo điện dung của tụ bằng arduino đã có rất nhiều bài trên mạng
Tuy nhiên tôi sẽ đề xuất một cách cực kỳ đơn giản nhưng lại hiệu quả rất lớn đặc biệt là để đo các tụ điện có dung lượng nhỏ trong quá trình sao chép mạch điện có sử dụng tụ điện dán (thường là có dung lượng nhỏ và trên thân tụ hoàn toàn không có số để đọc giá trị như các tụ lớn).
Nếu như các bạn cho rằng chỉ cần thực tế mà không cần lý thuyết thì đây sẽ là câu trả lời cho các bạn thấy rằng, lý thuyết nó mạnh mẽ cỡ nào để đào sâu một vấn đề cần giải quyết.
Hãy bắt đầu với lý thuyết trước đã.
Thành công chính là sự kết hợp giữa lý thuyết và thực tiễn.
Với nhiều cách dãn nhán tụ điện khác nhau, trong đó phần lớn các dụ điện dán SMD cỡ nhỏ đều không có ghi số, việc tìm ra giá trị của tụ điện có thể là một thách thức.
Đặc biệt nếu bạn không có đồng hồ đa năng kỹ thuật số loại xịn để kiểm tra chúng.
Trong hướng dẫn này, tôi sẽ chỉ cho bạn cách xây dựng ba máy đo điện dung khác nhau bằng cách sử dụng Arduino và một vài điện trở.
Sau khi hoàn thành dự án này, bạn sẽ có thể đo tất cả các tụ điện của mình và gắn nhãn chúng để tham khảo trong tương lai.
Về cơ bản hiện nay có 3 phương pháp chính để đo tụ điện sử dụng arduino là:
(1) – Phương pháp 1: Đo tụ có dung lượng trung bình (0,1uF đến 100uF), sai số < 10%
(2) – Phương pháp 2: Đo tụ dung lượng cao (từ 1uF đến 3900uF)
(3) – Phương pháp 3: Đo tụ có dung lượng thấp (từ 1pF đến 1000uF)
Phạm vi ứng dụng của mỗi phương pháp và sai số theo từng miền giá trị đo được tổng hợp trên đồ thị hình 2.

Khi tôi thử nghiệm các máy đo điện dung này, tôi không thể tìm thấy một máy đo nào có thể đo chính xác toàn bộ dải tụ điện thường được sử dụng.
Một máy đo sẽ đo chính xác các giá trị trong phạm vi 1000 μF, nhưng nó sẽ không thành công trong phạm vi nF và pF.
Một máy đo điện dung khác chính xác trong phạm vi nF và pF, nhưng không thành công trong phạm vi μF.
Vì vậy, tôi sẽ chỉ cho bạn ba máy đo khác nhau
Chúng cùng nhau sẽ bao phủ một phạm vi khoảng 1 pF đến 3900 μF.
2. Những thứ cần có
Để đo đạc sử dụng arduino thì thứ cần có đầu tiên là một mạch Arduino
Thường trên mạng các bạn sẽ tìm thấy các hướng dẫn sử dụng arduino uno
Tuy nhiên theo như cách tôi đã đề xuất trong phần cách đo cuộn cảm thì các bạn nên mua mạch Arduino Nano.
Trong phần này chúng ta sẽ sử dụng lại chính con arduino đó để bổ sung tính năng cho nó.

3. Lý thuyết và thuật toán
Tương ứng với từng phần mã nguồn được cung cấp trong mục 5 dưới đây
Lý thuyết căn bản và thuật toán sẽ được tôi tách biệt thành 3 mục.
Trong đó:
Mục 3.1: Lý thuyết và thuật toán đo dung lượng tụ sử dụng hằng số thời gian: Đo được tụ điện từ 1uF đến 3900uF
Mục 3.2: Lý thuyết và thuật toán đo dung lượng tụ sử dụng điện dung ký sinh: Đo được tụ điện có giá trị nhỏ từ 1pF đến 1000pF
Mục 3.3: Lý thuyết và thuật toán đo dung lượng tụ điện bằng điện trở pullup: Đo được tụ điện có giá trị từ 1pF đến 1000000pF (tức là 1000uF).
3.1. Lý thuyết và thuật toán đo dung lượng tụ sử dụng hằng số thời gian
Máy đo điện dung Arduino dựa trên một đặc tính quan trọng của mạch điện trở tụ điện (RC) – đó là hằng số thời gian.
Hằng số thời gian của một đoạn mạch RC được xác định là thời gian để điện áp trên tụ điện đạt 63,2% hiệu điện thế của nó khi được sạc đầy:

Các tụ điện lớn mất nhiều thời gian hơn để sạc và do đó sẽ tạo ra các hằng số thời gian lớn hơn.
Điện dung trong mạch RC liên quan đến hằng số thời gian theo phương trình sau:
Mỗi máy đo điện dung có một đoạn mạch RC với các giá trị điện trở đã biết và một giá trị tụ điện chưa biết.
Arduino sẽ đo điện áp tại tụ điện và ghi lại thời gian cần để đạt được 63,2% điện áp của nó khi được sạc đầy (hằng số thời gian).
Vì giá trị điện trở đã được biết trước, chúng ta có thể sử dụng công thức trong phương trình 2 để toán điện dung chưa biết.
Phương pháp này cho phép đo các tụ điện có điện dung trong khoảng 1uF đến 3900uF.
3.2. Lý thuyết và thuật toán đo dung lượng tụ sử dụng điện dung ký sinh
Trong sơ đồ hình 3 thì CT là tụ điện cần đo.
Chúng ta thấy rằng chỉ cần nối tụ cần đo vào thẳng hai chân A2 và A0 của arduino là đủ, không cần thêm linh kiện nào khác.

A0 và A2 là hai chân của Arduino;
C1 là tụ ký sinh có sẵn trên arduino, không phải tụ chúng ta mắc thêm vào;
CT là tụ điện cần đo điện dung được mắc vào.
Tại thời điểm ban đầu, chúng ta để cả hai tụ điện được xả hết và A2 ở 0V.
Khi chúng ta tăng A2 lên 5 vôn, một dòng điện sẽ chạy qua cả hai tụ điện.
Điện áp trên A0 sẽ đạt 1% giá trị cuối cùng của nó trong vòng 30ns.
Giá trị điện áp mà A0 đạt được sẽ bằng tỷ lệ của CT chia cho tổng điện dung C1 + CT.
Các công thức mà chúng ta sẽ cần là:
Chúng ta sẽ sử dụng ADC để đo VA0.
VA2 thực tế là 5 vôn, vì vậy VA0 sẽ thay đổi từ 0 đến 5 vôn
Nhưng chúng ta có thể sử dụng giá trị ADC để thay thế các phép tính dễ dàng hơn.
Rõ ràng là chúng ta nên sử dụng giá trị ADC lớn nhất (1023) cho VA2.
Các kết quả đọc ADC mà chúng ta có thể mong đợi sẽ dao động từ khoảng 33 đối với CT = 1pF đến khoảng 993 đối với CT = 1nF (1000pF).
Bạn có tự hỏi là khi có được những thứ trên chúng ta đã sẵn để xây dựng mạch và viết mã không?.
Đúng! nhưng không hoàn toàn.
Trong mạch trên, chúng ta đã chỉ định 1 điện dung ký sinh C1 là 30pF.
Nhưng Arduino sẽ có một số điện dung ký sinh khác trên bảng mạch và trong chính bộ vi điều khiển.
Điện dung ký sinh theo khảo sát rơi vào khoảng 30pF.
Chúng ta có thể đơn giản hóa bằng cách loại bỏ C1.
Nhưng để có kết quả đo chính xác, chúng ta cần tìm ra điện dung ký sinh này là bao nhiêu?
Vì vậy chúng ta có thể dựa vào phương trình 5 tính toán khi có một tụ điện CT chuẩn làm mẫu.
Nhưng trước tiên, chúng ta sẽ xây dựng mạch và viết mã (xem chi tiết tại mục 5).
3.3. Lý thuyết và thuật toán đo dung lượng tụ điện bằng điện trở pullup
Một giải pháp khác được đề xuất bằng cách sử dụng điện trở pullup bên trong vi điều khiển để đo được các tụ có điện cao hơn so với phương pháp sử dụng điện dung ký sinh đã trình bày trong mục 3.2 mà vẫn không cần mắc thêm điện trở như phương pháp đã đề cập trong mục 3.1.
Ban đầu, khi tiếp cận với giải pháp đã đề cập trong phần 3.1
Tôi đã loại trừ phương án sử dụng điện trở pullup tích hợp trong vi xử lý vì nghĩ nó sẽ không hoạt động với các tụ điện có giá trị thấp (nhỏ hơn khoảng 1nF).
Nhưng suy nghĩ lại,
Tôi nhận ra rằng phương pháp này có thể được sử dụng để mở rộng phạm vi lên đến giá trị uF.
Bằng cách kết hợp giải pháp sử dụng điện trở pullup với giải pháp đã đề cập trong phần 5.1.
Cuối cùng chúng ta đã có được một máy đo điện dung từ 1pF đến hơn 1000uF mà không cần linh kiện bên ngoài!
Trên thực tế, việc này hoạt động hơi phức tạp hơn tôi nghĩ.
Ý tưởng cơ bản sẽ là:
Kiểm tra tụ điện bằng phương pháp điện dung ký sinh.
Nếu tụ điện nhỏ hơn 1nF thì chúng ta đã hoàn thành.
Nếu không, chúng ta bắt đầu sạc tụ điện bằng điện trở pullup bên trong vi xử lý.
Sau một khoảng thời gian nhất định (có thể là hằng số thời gian RC), tụ điện sẽ đạt đến một hiệu điện thế nhất định.
Sử dụng thời gian sạc và điện áp (hay cụ thể hơn là tỷ số giữa điện áp tụ điện và điện áp sạc) chúng ta có thể tính ra điện dung.
Thời gian để sạc các tụ điện lớn sẽ lên tới hàng mili giây.
Điều này sẽ khá dễ dàng để đo lường với mức độ chính xác hợp lý.
Nhưng hằng số thời gian RC của một tụ điện 1nF với một pullup 30k là khoảng 30us.
Cái này khó đo lường chính xác hơn.
Tôi đã nghĩ đến việc sử dụng ngắt và sử dụng phương pháp chuyển đổi ADC (analogRead mất khoảng 100us – và bạn có thể thực hiện chuyển đổi nhanh hơn nhiều so với cách này)
Nhưng tôi không muốn mã quá phức tạp và tôi muốn tuân thủ các quy trình Arduino tiêu chuẩn.
Vì vậy, tôi đã nghĩ ra đoạn mã tích hợp lý thuyết trong phần 3.1 và lý thuyết trong phần 3.2.
Tôi sẽ giải thích lý do tại sao tôi code theo cách đó…
Tôi đã thử sử dụng analogRead () để xác định thời điểm tụ điện đã sạc đến một giá trị nhất định.
Điều này có thể hoạt động
Nhưng analogRead() rất chậm nên tôi quyết định sử dụng chân input làm chân đầu vào kỹ thuật số thay vì chân analog để bắt đầu.
Khi đầu vào đạt khoảng 2,5V, mức logic sẽ thay đổi từ 0 thành 1 và chúng ta ngừng sạc tụ điện.
Tại thời điểm này, chúng ta thực sự không thể chắc chắn điện áp trên tụ điện là bao nhiêu
Vì vậy chúng ta sử dụng analogRead để có được giá trị chính xác.
Chúng tôi đã lấy dấu thời gian khi bắt đầu và kết thúc quá trình sạc
Vì vậy bây giờ chúng tôi có mọi thứ cần thiết để xác định điện dung.
Các phép toán đằng sau điều này như sau:

Trong đó:
Vc(t) – là giá trị điện áp trên tụ điện tại thời điểm t tính từ lúc bắt đầu nạp;
Vin – là giá trị điện áp đặt vào mạch nạp cho tụ điện (5V)
C – là điện dung của tụ điện;
t – là thời gian (thời lượng) nạp của tụ
4. Sơ đồ mạch điện và mã nguồn
4.1. Máy đo tụ có giá trị từ 1uF đến 3900uF
4.1.1. Sơ đồ mạch điện
Sơ đồ mạch điện này áp dụng lý thuyết và thuật toán được mô tả trong phần 3.1.

Trong đó: CT là tụ cần đo điện dung
4.1.2. Mã nguồn hiển thị lên màn hình serial của arduino (khi không gắn màn hình LCD)
#define analogPin 0 #define chargePin 13 #define dischargePin 8 #define resistorValue 10000.0F unsigned long startTime; unsigned long elapsedTime; float microFarads; float nanoFarads; void setup(){ pinMode(chargePin, OUTPUT); digitalWrite(chargePin, LOW); Serial.begin(9600); } void loop(){ digitalWrite(chargePin, HIGH); startTime = millis(); while(analogRead(analogPin) < 648){ } elapsedTime= millis() - startTime; microFarads = ((float)elapsedTime / resistorValue) * 1000; Serial.print(elapsedTime); Serial.print(" mS "); if (microFarads > 1){ Serial.print((long)microFarads); Serial.println(" microFarads"); } else{ nanoFarads = microFarads * 1000.0; Serial.print((long)nanoFarads); Serial.println(" nanoFarads"); delay(500); } digitalWrite(chargePin, LOW); pinMode(dischargePin, OUTPUT); digitalWrite(dischargePin, LOW); while(analogRead(analogPin) > 0){ } pinMode(dischargePin, INPUT); }

4.1.3. Mã nguồn hiển thị lên màn hình LCD 16×2
#define analogPin 0 #define chargePin 13 #define dischargePin 8 #define resistorValue 10000.0F #include <LiquidCrystal.h> LiquidCrystal lcd(12, 11, 5, 4, 3, 2); unsigned long startTime; unsigned long elapsedTime; float microFarads; float nanoFarads; void setup(){ pinMode(chargePin, OUTPUT); digitalWrite(chargePin, LOW); lcd.begin(16, 2); } void loop(){ digitalWrite(chargePin, HIGH); startTime = millis(); while(analogRead(analogPin) < 648){ } elapsedTime= millis() - startTime; microFarads = ((float)elapsedTime / resistorValue) * 1000; lcd.print(elapsedTime); lcd.print(" mS"); delay(2000); lcd.clear(); delay(500); if (microFarads > 1){ lcd.print(microFarads); lcd.print(" uF"); delay(2000); } else{ nanoFarads = microFarads * 1000.0; lcd.print(nanoFarads); lcd.print(" nF"); delay(2000); } lcd.clear(); digitalWrite(chargePin, LOW); pinMode(dischargePin, OUTPUT); digitalWrite(dischargePin, LOW); while(analogRead(analogPin) > 0){ } pinMode(dischargePin, INPUT); }
4.2. Máy đo tụ có giá trị từ 0,0047uF đến 180uF (4,7nF – 180uF)
4.2.1. Sơ đồ mạch điện
Sơ đồ mạch điện này sử dụng chung lý thuyết và thuật toán được trình bày trong mục 3.1 tương tự sơ đồ mạch đã trình bày trong mục 4.1
Tuy nhiên code arduino và điện trở kết nối có sự thay đổi đáng kể và đây được xem là một máy đo tụ điện độ chính xác cao nhờ sử dụng các vector ngắt và so sánh trong vi điều khiển.

Trong đó:
CT là tụ cần đo điện dung
4.3.2. Mã nguồn hiển thị lên màn hình serial (khi không gắn màn hình LCD)
const byte pulsePin = 8; const unsigned long resistance = 10000; volatile boolean triggered; volatile boolean active; volatile unsigned long startTime; volatile unsigned long duration; ISR (ANALOG_COMP_vect){ unsigned long now = micros (); if (active){ duration = now - startTime; triggered = true; digitalWrite (pulsePin, LOW); } } void setup (){ pinMode(pulsePin, OUTPUT); digitalWrite(pulsePin, LOW); Serial.begin(9600); Serial.println("Started."); ADCSRB = 0; ACSR = _BV (ACI) | _BV (ACIE) | _BV (ACIS0) | _BV (ACIS1); } void loop (){ if (!active){ active = true; triggered = false; digitalWrite (pulsePin, HIGH); startTime = micros (); } if (active && triggered){ active = false; Serial.print ("Capacitance = "); Serial.print (duration * 1000 / resistance); Serial.println (" nF"); triggered = false; delay (3000); } }

4.3.3. Mã nguồn hiển thị lên màn hình LCD 16×2
#include <LiquidCrystal.h> LiquidCrystal lcd(12, 11, 5, 4, 3, 2); const byte pulsePin = 8; const unsigned long resistance = 10000; volatile boolean triggered; volatile boolean active; volatile unsigned long startTime; volatile unsigned long duration; ISR (ANALOG_COMP_vect){ unsigned long now = micros (); if (active){ duration = now - startTime; triggered = true; digitalWrite (pulsePin, LOW); } } void setup (){ pinMode(pulsePin, OUTPUT); digitalWrite(pulsePin, LOW); lcd.begin(16, 2); lcd.print("Starting"); delay(1000); lcd.clear(); ADCSRB = 0; ACSR = _BV (ACI) | _BV (ACIE) | _BV (ACIS0) | _BV (ACIS1); } void loop(){ if (!active){ active = true; triggered = false; digitalWrite (pulsePin, HIGH); startTime = micros (); } if (active && triggered){ active = false; lcd.print("Capacitance = "); lcd.setCursor(0,1); lcd.print(duration * 1000 / resistance); lcd.print(" nF"); triggered = false; delay (3000); lcd.clear(); } }
4.3. Máy đo tụ có giá trị từ 1pF đến 1000uF
4.3.1. Sơ đồ mạch điện
Mạch dễ dàng một cách đáng ngạc nhiên, thực tế là không có mạch điện nào cả.
Chúng ta sẽ sử dụng Arduino như sau:

Trong đó:
CT là tụ cần đo điện dung
4.3.2. Mã nguồn căn bản để dễ hiểu nhưng chỉ đo được tụ có điện dung nhỏ
Mã nguồn này là khái niệm căn bản nhất để các bạn dễ hiểu vấn đề
Nó có nhược điểm duy nhất là chỉ đo được các tụ điện có điện dung nhỏ từ 1pF đến 1000pF (1nF).
Các bạn muốn đo được giải rộng hơn vui lòng tham khảo mục “5.2.
Mã nguồn nâng cao” để biết thêm chi tiết:
const int OUT_PIN = A2; const int IN_PIN = A0; //const float TU_KY_SINH_DEN_GND = 30; //Điện dung ký sinh ban đầu theo thiết kế const float TU_KY_SINH_DEN_GND = 24.48; //Điện dung ký sinh sau khi hiệu chỉnh const float TU_KY_SINH_KHAC = 0.0; const float TONG_TU_KY_SINH = TU_KY_SINH_DEN_GND + TU_KY_SINH_KHAC; const int MAX_ADC_VALUE = 1023; void setup() { pinMode(OUT_PIN, OUTPUT); //digitalWrite(OUT_PIN, LOW); //Đây là trạng thái mặc định nên không cần thực hiện lệnh pinMode(IN_PIN, OUTPUT); //digitalWrite(IN_PIN, LOW); Serial.begin(115200); } void loop() { //Tụ cần đo được nối vào chân OUT_PIN và IN_PIN //Cạnh lên của chân OUT_PIN pinMode(IN_PIN, INPUT); digitalWrite(OUT_PIN, HIGH); int val = analogRead(IN_PIN); //Xả hết tụ để đo chu kỳ tiếp theo digitalWrite(OUT_PIN, LOW); pinMode(IN_PIN, OUTPUT); //Tính toán và in kết quả ra màn hình Serial float diendung = (float)val * TONG_TU_KY_SINH / (float)(MAX_ADC_VALUE - val); Serial.print(F("C = ")); Serial.print(diendung, 3); Serial.print(F(" pF (")); Serial.print(val); Serial.println(F(") ")); while (millis() % 500 != 0); }
Đoạn mã trên lặp lại sau mỗi nửa giây, áp một xung 5V vào tụ điện và đo điện áp ở phía bên kia.
Sau đó, nó in ra điện dung được tính toán (và giá trị ADC thô).
Nếu chúng ta thử nó sẽ không chính xác lắm.
Đó là bởi vì điện dung ký sinh đưa vào tính toán không chính xác là 30pF.
Vì vậy, chúng ta cần phải hiệu chỉnh nó.
Tôi đã làm điều này với một tụ điện 100pF.
Đồng hồ vạn năng của tôi tính toán rằng nó thực sự có giá trị 102pF.
Số đọc tôi nhận được trên màn hình Arduino của mình là 125pF (giá trị ADC thô 825).
Vì vậy, nếu chúng ta đặt VA0 = 825, VA2 = 1023 và CT = 102 vào phương trình thứ hai, điều này cho chúng ta biết rằng C1 là 24,48pF.
Vì vậy, tôi đã thay đổi TU_KY_SINH_DEN_GND thành 24,48 và tải nó lên Arduino.
Lúc này giá trị hiển thị trên màn hình nối tiếp là 102pF (Kết quả đã chính xác!).
Tôi đã thử code trên sau khi hiệu chỉnh TU_KY_SINH_DEN_GND = 24.48 với các tụ điện khác nhau và kết quả như bảng thống kê bên dưới:

Tôi đã tính toán độ phân giải lý thuyết của máy đo điện dung Arduino bằng bảng tính.
Độ phân giải là khả năng phân biệt giữa các giá trị lân cận;
Nó không giống như độ chính xác. Theo một cách nào đó, độ chính xác có thể được xem như một sai số tuyệt đối, trong khi độ phân giải là sai số tương đối.
Tôi bắt đầu với độ phân giải 10%:

Tiếp theo đánh giá ở mức 5%

Sau đó đánh giá ở mức 1%

Cuối cùng là đánh giá ở độ phân giải 0,5%

Không có gì ngạc nhiên khi độ phân giải tốt nhất là khoảng 25pF.
Điều này là do C1 là 24,48pF.
Vì vậy khi CT = C1 thì sai số sẽ ở mức tối thiểu.
Tôi nghĩ 5% là mức hợp lý, vì vậy sẽ đề xuất sử dụng mức này cho các tụ điện từ 0,5pF đến 1,3nF (1300pF).
Các tụ điện lớn hơn có thể được đo bằng cách thêm điện dung bổ sung vào C1
Nhưng cần cẩn thận vì các tụ điện lớn hơn sẽ mất nhiều thời gian hơn để đạt được trạng thái ổn định và hàm delay() trong mã nguồn có thể trở nên cần thiết phải thêm vào.
Độ phân giải giảm xuống khá nhanh dưới 0,5pF
Vì vậy điều này đáng xem xét chi tiết:

Điều này cho thấy rõ ràng rằng bạn có thể nhận được các phép đo hữu ích xuống khoảng 0,1pF
Nhưng trong các thử nghiệm, tôi nhận thấy rằng có một điện dung ký sinh giữa A0 và A2 khá lớn.
Bạn có thể cải thiện độ phân giải bằng cách sử dụng các chân cách xa nhau hơn
– Ví dụ: sử dụng A0 và A4 thay vì A0 và A2. Điều này làm giảm điện dung lạc giữa các chân.
4.3.3. Mã nguồn nâng cao để đo được tụ có điện dung từ 1pF đến 1000uF – hiển thị lên màn hình Serial (không cần lắp màn LCD 16×2)
Mã nguồn nâng cao đề cập thêm một giải pháp khác bằng cách sử dụng điện trở pullup bên trong vi điều khiển để đo được các tụ có điện cao hơn.
Ban đầu, khi tiếp cận với giải pháp đã đề cập trong phần “5.1.
Mã nguồn căn bản” tôi đã loại trừ phương án sử dụng điện trở pullup tích hợp trong vi xử lý vì nghĩ nó sẽ không hoạt động với các tụ điện có giá trị thấp (nhỏ hơn khoảng 1nF).
Nhưng suy nghĩ lại, tôi nhận ra rằng phương pháp này có thể được sử dụng để mở rộng phạm vi lên đến giá trị uF.
Bằng cách kết hợp giải pháp sử dụng điện trở pullup với giải pháp đã đề cập trong phần 5.1.
Cuối cùng chúng ta đã có được một máy đo điện dung từ 1pF đến hơn 1000uF mà không cần linh kiện bên ngoài!
const int OUT_PIN = A2; const int IN_PIN = A0; const float TU_KY_SINH_XUONG_GND = 24.48; //Tụ điện ký sinh trong mạch tính từ chân A0 xuống đến GND const float TU_C1 = TU_KY_SINH_XUONG_GND; //Điện dung của tụ điện C1 const float DIEN_TRO_TREO = 34.8; //Điện trở treo PULL_UP có sẵn trong Atmega328 const int MAX_ADC_VALUE = 1023; //Giá trị cao nhất của ADC khi điện áp chân A0 đạ 5V void setup() { pinMode(OUT_PIN, OUTPUT); pinMode(IN_PIN, OUTPUT); Serial.begin(115200); } void loop() { pinMode(IN_PIN, INPUT); digitalWrite(OUT_PIN, HIGH); int val = analogRead(IN_PIN); digitalWrite(OUT_PIN, LOW); if (val < 1000) { pinMode(IN_PIN, OUTPUT); float diendung = (float)val * TU_C1 / (float)(MAX_ADC_VALUE - val); Serial.print(F("Điện dung = ")); Serial.print(diendung, 3); Serial.print(F(" pF (")); Serial.print(val); Serial.println(F(") ")); } else { //Tụ điện có điện dung lớn, sử dụng phương pháp điện trở pullup pinMode(IN_PIN, OUTPUT); delay(1); pinMode(OUT_PIN, INPUT_PULLUP); unsigned long u1 = micros(); unsigned long t; int digVal; do { digVal = digitalRead(OUT_PIN); unsigned long u2 = micros(); t = u2 > u1 ? u2 - u1 : u1 - u2; } while ((digVal < 1) && (t < 400000L)); pinMode(OUT_PIN, INPUT); val = analogRead(OUT_PIN); digitalWrite(IN_PIN, HIGH); int dischargeTime = (int)(t / 1000L) * 5; delay(dischargeTime); pinMode(OUT_PIN, OUTPUT); digitalWrite(OUT_PIN, LOW); digitalWrite(IN_PIN, LOW); float diendung = -(float)t / DIEN_TRO_TREO / log(1.0 - (float)val / (float)MAX_ADC_VALUE); Serial.print(F("C = ")); if (diendung > 1000.0) { Serial.print(diendung / 1000.0, 2); Serial.print(F(" uF")); } else { Serial.print(diendung, 2); Serial.print(F(" nF")); } Serial.print(F(" (")); Serial.print(digVal == 1 ? F("Binh_thuong") : F("Dung_luong_cao")); Serial.print(F(", t= ")); Serial.print(t); Serial.print(F(" us, ADC= ")); Serial.print(val); Serial.println(F(")")); } while (millis() % 1000 != 0); }
Ban đầu tôi giả định rằng điện trở pullup có giá trị là 30k.
Điều này sẽ không đúng và phải được hiệu chỉnh bằng cách sử dụng một tụ điện có giá trị đã biết.
Một tụ nào đó trong khoảng 100nF – 1uF sẽ cho phép đo tốt (không sử dụng tụ có chất điện phân kiểu tụ hóa hay các tụ phân cực khác – chúng có xu hướng chịu đựng rất kém, và thường có sai số -20% đến + 80%).
Bằng cách đưa những con số này vào công thức cuối cùng, chúng ta có thể xác định điện trở pullup thực tế.
Tôi đã sử dụng một tụ điện 1uF và nhận được kết quả sau:
Giá trị điện dung = 1,00 uF (Binh_thuong, t = 24704 us, ADC = 520)
[Lưu ý rằng tại thời điểm này, điện dung đo được có thể không phải là 1,00uF]
Đưa các số này (C = 1e-6F, t = 0,024704s, Vc = 520, Vin = 1023) vào công thức cuối cùng cho ta kết quả: R = 34,80 kΩ (34800Ω).
Đối với C, chúng ta nên sử dụng giá trị mong đợi (1,00uF) thay vì giá trị đo được, như đã nêu trước đây, có thể sẽ khác cho đến khi chúng ta thực hiện hiệu chuẩn.
Sau đó, tôi đã thử nghiệm điều này với các tụ điện khác nhau.

Khi đo các tụ phân cực hãy chắc chắn rằng bạn kết nối chân + của tụ điện vào chân A2 và chân – của tụ điện vào chân A0.
4.3.4. Mã nguồn nâng cao, hiển thị lên màn hình LCD 16×2
#include <LiquidCrystal.h> LiquidCrystal lcd(12, 11, 5, 4, 3, 2); const int OUT_PIN = A2; const int IN_PIN = A0; const float TU_KY_SINH_DEN_GND = 24.48; const float TU_C1 = TU_KY_SINH_DEN_GND; const float DIEN_TRO_TREO = 34.8; const int MAX_ADC_VALUE = 1023; void setup() { pinMode(OUT_PIN, OUTPUT); pinMode(IN_PIN, OUTPUT); lcd.begin(16, 2); } void loop() { pinMode(IN_PIN, INPUT); digitalWrite(OUT_PIN, HIGH); int val = analogRead(IN_PIN); digitalWrite(OUT_PIN, LOW); if (val < 1000) { pinMode(IN_PIN, OUTPUT); float diendung = (float)val * TU_C1 / (float)(MAX_ADC_VALUE - val); lcd.setCursor(0, 0); lcd.print(F("C = ")); lcd.setCursor(0, 1); lcd.print(diendung, 3); lcd.print(F("pF ")); lcd.print(val); lcd.print("mS"); } else { pinMode(IN_PIN, OUTPUT); delay(1); pinMode(OUT_PIN, INPUT_PULLUP); unsigned long u1 = micros(); unsigned long t; int digVal; do { digVal = digitalRead(OUT_PIN); unsigned long u2 = micros(); t = u2 > u1 ? u2 - u1 : u1 - u2; } while ((digVal < 1) && (t < 400000L)); pinMode(OUT_PIN, INPUT); val = analogRead(OUT_PIN); digitalWrite(IN_PIN, HIGH); int dischargeTime = (int)(t / 1000L) * 5; delay(dischargeTime); pinMode(OUT_PIN, OUTPUT); digitalWrite(OUT_PIN, LOW); digitalWrite(IN_PIN, LOW); float diendung = -(float)t / DIEN_TRO_TREO / log(1.0 - (float)val / (float)MAX_ADC_VALUE); lcd.setCursor(0, 0); lcd.print(F("C = ")); if (diendung > 1000.0) { lcd.setCursor(0, 1); lcd.print(diendung / 1000.0, 2); lcd.print(F("uF ")); lcd.print(val); lcd.print("mS"); } else { lcd.setCursor(0, 1); lcd.print(diendung, 2); lcd.print(F("nF ")); lcd.print(val); lcd.print("mS"); } } while (millis() % 1000 != 0); }
Để lại một bình luận