Lập trình ADC - Đo nhiệt độ dùng LM35 hiển thị LCD với AVR
1. Giới thiệu bộ ADC trên AVR:
ADC (Analog to Digital Converter) Chuyển đổi tín hiệu tương tự sang kỹ thuật số. Trên AVR được tích hợp 8 kênh ADC với độ phân giải 10bit, tức điện áp sẽ được chuyển đổi thành 1024 mức level dạng bậc thang.
ADC được ứng dụng rất nhiều như đo nhiệt độ, đọc giá trị điện áp, tính dòng điện, đọc phím nhấn theo thang điện trở (trong tivi...), biến đổi tín hiệu âm thanh sang kỹ thuật số để lưu trữ,.... ADC có độ phân giải càng cao thì càng khó chế tạo và từ đó giá thành cũng tỉ lệ thuận, nhưng với AVR bạn khỏi phải no chuyện giá thành của các chip ADC, nó đã được tích hợp bên trong tất cả các chip AVR kể cả dòng nhỏ gọn như ATtiny.
2. Thiết kế nguồn cho bộ ADC:
Chân AVCC là chân cấp nguồn cho bộ ADC của AVR, riêng không chung với VCC của chip nha chú ý điều này.
Chân AREF là chân chọn điện áp tham chiếu ngoài của bộ ADC (AREF Max=5V) nó sẽ so sánh áp tham chiếu để cho ra các mức logic tương ứng kiểu tỷ lệ %. VD: AREF=5V, ADC input =2.5V tức bằng 50% AREF thì giá trị ADC là 1024/2=512.
Thiết kế nguồn như trên là lý tưởng cho các bộ ADC của AVR, nhưng trong thực tế nhằm giảm độ cồng kềnh và đã có các mạch nguồn ổn định thì ta chỉ cần có một con tụ lọc 104 là đủ
3. Công thức tính giá trị điện áp chuyển đổi:
ADC 10bit: Vin = (Vref*ADC)/1024
ADC 8bit: Vin = (Vref*ADC)/256
Trong đó:
Vref là điện áp tham chiếu (Đơn vị: V)
ADC là giá trị sau chuyển đổi.
4. Các thanh ghi tham gia điều khiển ADC:
- ADMUX (ADC Multiplexer Selection Register) Là thanh ghi chọn điện áp tham chiếu và chọn kênh ADC
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
REFS1 | REFS0 | ADLAR | - | MUX3 | MUX2 | MUX1 | MUX0 |
Trong đó:
REFS1 và REFS0 Là 2bit chọn điện áp tham chiếu để so sánh, ta có bảng sau:
REFS1 | REFS0 | Giá trị điện áp |
0 | 0 | Bằng AREF đưa vào, mã HEX 0x00 |
0 | 1 | Bằng AVCC đưa vào, mã HEX 0x40 |
1 | 0 | Dự trữ - Không sử dụng |
1 | 1 | 2.56V onchip, mã HEX 0xC0 |
Tại sao lại có phần mã HEX trên thì ta sẽ xét trong phần lập trình.... Giá trị này là khi ADLAR=0
ADLAR là bit sắp xếp 2 thanh ghi ADCH và ADCL, do bộ chuyển đổi ADC là 10bit nên ta phải có 2 thanh ghi 8bit để lưu giá trị chuyển đổi, việc sắp xếp này sẽ tùy vào sở thích của lập trình viên.
MUX3:0 Là 4bit chọn kênh ADC có giá trị thập phân từ 0-7 tương ứng với kênh ADC0-ADC7. MUX3:0=1110: VBG=1.3V, MUX3:0=1111: 0V GND
- ADCSRA Là thanh ghi điều khiển bộ ADC:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
ADEN | ADSC | ADFR | ADIF | ADIE | ADPS2 | ADPS1 | ADPS0 |
Trong đó:
ADEN: Là bit cho phép bộ ADC hoạt động, nếu ta không set bit này lên 1 thì bộ ADC không hoạt động.
ADSC là bit cho phép bộ ADC chuyển đổi, khi quá trình chuyển đổi hoàn tất bit này sẽ tự động được xóa về 0 và ta muốn bộ ADC chuyển đổi tiếp ta bắt buộc phải set lại bit này.
ADFR Là bit cho chế độ liên tục chuyển đổi, Nếu set bit này thì bộ ADC sẽ liên tục chuyển đổi.
ADIF là bit cờ ngắt, khi quá trình chuyển đổi hoàn tất thì bit này tự động được set lên 1, ta sẽ kiểm tra bit này để đảm bảo quá trình đọc ADC không bị lỗi.
ADIE là bit cho phép ngắt bộ ADC. Khi chuyển đổi hoàn tất sẽ có một ngắt xảy ra nếu cho phép ngắt ADC
ADPS2:0 là 3bit chọn xung nhịp cho bộ ADC, tốc độ chuyển đổi phụ thuộc vào 3 bit này. Chú ý chế độ 10bit sẽ chuyển đổi chậm hơn chế độ 8bit. Ta có bảng sau:
ADPS2 | ADPS1 | ADPS0 | Xung nhịp |
0 | 0 | 0 | Fosc/2 |
0 | 0 | 1 | Fosc/2 |
0 | 1 | 0 | Fosc/4 |
0 | 1 | 1 | Fosc/8 |
1 | 0 | 0 | Fosc/16 |
1 | 0 | 1 | Fosc/32 |
1 | 1 | 0 | Fosc/64 |
1 | 1 | 1 | Fosc/128 |
4. Lập trình đọc ADC theo kênh:
Khai báo thay thế đầu chương trình:
#define ADC_VREF 0x00 //Lấy điện áp AREF để so sánh
Khởi tạo trong hàm main:
ADMUX=ADC_VREF; //Đặt áp tham chiếu ban đầu
ADCSRA=0x85; //Khởi tạo ADC, xung clock Fosc/32
Chương trình con đọc ADC đơn kênh 10bit:
unsigned int read_adc(unsigned char adc_input)
{
ADMUX=adc_input | ADC_VREF; //Chọn kênh ADC và điện áp so sánh
ADCSRA|=0x40; //Start ADC
while (!(ADCSRA & 0x10)); //Kiểm tra cờ ADC
ADCSRA|=0x10;
return ADCW; //Giá trị ADC 10bit
}
Chương trình con đọc ADC đơn kênh 8bit:
unsigned char read_adc(unsigned char adc_input)
{
ADMUX=adc_input | ADC_VREF; //Chọn kênh ADC và điện áp so sánh
ADCSRA|=0x40; //Start ADC
while (!(ADCSRA & 0x10)); //Kiểm tra cờ ADC
ADCSRA|=0x10;
return ADCH; //Giá trị ADC 8bit
}
Sử dụng hàm: adc=read_adc(0); //abc chứa giá trị ADC kênh 0
5. Lập trình đọc ADC sử dụng ngắt:
Khi ứng dụng của bạn đòi hỏi tốc độ mà nó lại bị giới hạn ở vòng lặp kiểm tra cờ ADC (vì quá trình chuyển đổi ADC sẽ mất một khoảng thời gian 13-260us) thì giải pháp là sử dụng ngắt, ngắt sẽ cho VXL hoạt động như đang chạy đa nhiệm. Khi nào chuyển đổi xong ta mới đọc giá trị, nó sẽ cải thiệt rất nhiều khi phải chờ vài trăm us để đọc dc ADC, khoảng thời gian đó ta có thể làm được rất nhiều việc khác vì AVR có tốc độ rất cao với chu kỳ lệnh T=1/Fosc khoảng vài chục ns (nano giây) tùy vào thạch anh mắc vào chip từ 1-16Mhz.
Việc sử dụng ngắt rất đơn giản ta chỉ việc khởi động ADC, chọn kênh, điện áp so sánh, set bit ngắt và Run. Chương trình ngắt sẽ như sau:
Khai báo thay thế đầu chương trình:
#define ADC_VREF 0xC0 //Lấy điện áp 2.56V onchip để so sánh
Khởi tạo trong hàm main:
ADMUX = 1 | ADC_VREF; //Chọn kênh ADC1 và điện áp so sánh
ADCSRA = 0xCD; //Start ADC, Fosc/32, Cho phép ngắt
#asm("sei") //Ngắt cục bộ
Chương trình ngắt:
unsigned int adc_data; //Biến lưu giá trị ADC
interrupt [ADC_INT] void adc_isr(void)
{
adc_data=ADCW; //Lấy giá trị ADC 10bit
ADCSRA|=0x40; //Tiếp tục chuyển đổi
}
6. Chế độ Auto Scan ADC - Các bạn tìm hiểu trong phần mềm CodeVisionAVR
7. Lập trình đọc nhiệt độ từ cảm biến nhiệt độ LM35 hiển thị lên LCD16x2
LM35 là cảm biến nhiệt độ đầu ra là tín hiệu analog, cứ 10mV tương ứng với 1 độ C, VD: 250mV là 25 độ C
Việc đọc giá trị từ cảm biến này rất đơn giản do nó có áp OUTPUT MAX nhỏ hơn 2.56V lên ta sử dụng luôn áp nội của AVR để so sánh sẽ chính xác hơn là dùng ổn áp ngoài cấp vào AREF.
Với áp nội thì chúng ta không phải tính toán nhiều mà ở chế độ ADC8bit có 256 thang điện áp tương ứng 0-2.55V, từ đó suy ra cứ một giá trị ADC là ứng với 10mV. VD ADC=25 ứng với 25oC của LM35.
Ta sẽ có Code như sau:
#include
#include <delay.h>
#include <alcd.h>
#define ADC_VREF 0xe0 //Lay ap 2.56V, ADLAR=1
unsigned char read_adc(unsigned char adc_input){
ADMUX=adc_input | ADC_VREF; //Chon kenh ADC va ap so sanh
ADCSRA|=0x40; //Start ADC
while (!(ADCSRA & 0x10)); //Kiem tra co ADC
ADCSRA|=0x10;
return ADCH; //ADC 8bit
}
void temp(unsigned char z){ //Chuyen doi hien thi
lcd_puts("Nhiet do: ");
lcd_putchar((z/100)+48);
lcd_putchar((z%100/10)+48);
lcd_putchar((z%10)+48);
lcd_puts("oC");
}
void main(void){
ADMUX=ADC_VREF;
ADCSRA=0x86; //Fosc/64
lcd_init(16);
lcd_puts("Do nhiet do...");
delay_ms(2000);
lcd_clear();
lcd_puts("SangTaoClub.Net");
while (1){
lcd_gotoxy(0,1);
temp(read_adc(0)); //Show Temp
}
}
Mạch mô phỏng:
Tải Code và mô phỏng ở cuối bài viết, trong phần đính kèm....
Chúc các bạn học tốt!
Tải về đính kèm: