|
|
  |
Работа с АЦП в TINY13, Нужны примеры на С |
|
|
|
Aug 10 2009, 14:38
|
Участник

Группа: Участник
Сообщений: 15
Регистрация: 4-09-07
Из: Kiev
Пользователь №: 30 288

|
Сабж, а конкретнее: есть VСС=4.90V, подаю на вход АЦП (PORTB.2) через делитель от 0 до 4900mV согласно правилам. Нужен пример где идет постоянное измерение напряжения, и в результате чтоб была конечная переменная со значением в mV. Не понятно: какой Vref, и какие типы данных следует применять. Супер-мега точность не нужна. В CodeVision выставляю вот это (на картинке). Куски кода которые относятся к АЦП: //9000mV on Batt = 4900mV on ADC (4900*1.84 = 9016mV) kv = 1.84 #define ADC_VREF_TYPE 0x00 //(что значит эта строка?) #define VREF 4900 //mV (не знаю нодо ли это, или как правильно?) unsigned int VCC, kv=1.84; //(какой тип данных следует применять?) // Read the AD conversion result unsigned int read_adc(unsigned char adc_input) { ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); // Delay needed for the stabilization of the ADC input voltage delay_us(10); // Start the AD conversion ADCSRA|=0x40; // Wait for the AD conversion to complete while ((ADCSRA & 0x10)==0); ADCSRA|=0x10; return ADCW; } void BATT_VCC () { //vichislenie BATT_VCC (mV) VCC = read_adc(1); // VСС = 0-1024, 1 - nomer kanala ADC VCC = VCC*VREF/1024; VCC = VCC*kv; //(VСС mV) } BATT_VCC (); Это правильно?
Сообщение отредактировал Bogila Anton - Aug 10 2009, 14:40
Эскизы прикрепленных изображений
|
|
|
|
|
Aug 10 2009, 15:31
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Bogila Anton @ Aug 10 2009, 17:38)  #define ADC_VREF_TYPE 0x00 //(что значит эта строка?) В совокупности со строчкой: Цитата ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); Значит, что VCC used as analog reference. Цитата #define VREF 4900 //mV (не знаю нодо ли это, или как правильно?) Измерить тестером VCC и указать реальное значение VCC в миливольтах. Цитата unsigned int VCC, kv=1.84; //(какой тип данных следует применять?) для констант вида 1.84 - float, но в tiny13 float не влезет, следовательно unsigned short и поменять логику вычислений на целочисленную. Цитата Это правильно? Работать не будет. У вас в кучу смешаны вольты и миливольты, плюс как для измерения VCC выбран неправильный референс. Если требуется измерить Vcc, то подход на тини13 д.б. такой: выбрать Internal Voltage Reference 1.1V (ADMUX bit REFS0 = 1). На вход канала PB2 через делитель 1/5 подавать VCC. Результат АЦП преобразовать в миливольты по нехитрой формуле: VCC = ADC * 43 / 8; или VCC = ADC * 43 >> 3; Как получились цифры 43 и 8: Vbgref = 1.1V (из даташита). Vin (при Vcc=5V) = 1V 5000mv = k * ADC; ADC (при Vcc=5V) = 1023 / 1.1 = 930. k = 5000 / 930 = 5.3763 Чтобы не сильно потерять в точности при целочисленной арифметике, домножаем k на 8 с округлением до целого: K = 8k = 43.010 (43 после округления) С таким K, результат будет в 1/8 миливольта. Приводим его обратно к миливольтам делением на 8 (эквивалентно сдвигу результата вправо на 3 разряда) и имеем то, что имеем: VCC = ADC * 43 >> 3;
|
|
|
|
|
Aug 10 2009, 20:56
|
Участник

Группа: Участник
Сообщений: 15
Регистрация: 4-09-07
Из: Kiev
Пользователь №: 30 288

|
Мне нужно измерять напряжение от 0-9V. Делитель токой что при 9V которые надо измерять, на входе АЦП 4.9V, при питании контроллера 4.9V.
Я не совсем понял как задать Vref от VCC контроллера, тоесть 4.9V в моем случае. Это оно: #define ADC_VREF_TYPE 0x00 ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); тогда что такое adc_input? Это вход АЦП тоесть в моем случае ADMUX=1 | (ADC_VREF_TYPE & 0xff);
При Vref от VCC будет ли работать это: //9000mV on Batt = 4900mV on ADC (4900*1.84 = 9016mV) kv = 1.84 #define VREF 4900 void BATT_VCC () { ADC = read_adc(1); VCC = (ADC*VREF/1023)*kv; } //VCC = (0*4900/1023)*1.84 = 0 //VCC = (1023*4900/1023)*1.84 = 9016 mV В теории вроде верно?
Сообщение отредактировал Bogila Anton - Aug 10 2009, 20:59
|
|
|
|
|
Aug 11 2009, 11:13
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Bogila Anton @ Aug 10 2009, 23:56)  Мне нужно измерять напряжение от 0-9V. Делитель токой что при 9V которые надо измерять, на входе АЦП 4.9V, при питании контроллера 4.9V. Тогда Цитата #define ADC_VREF_TYPE 0x00 ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); здесь все верно. Цитата тогда что такое adc_input? Это вход АЦП тоесть в моем случае ADMUX=1 Угу (вход с PB2). Цитата При Vref от VCC будет ли работать это: //9000mV on Batt = 4900mV on ADC (4900*1.84 = 9016mV) kv = 1.84 Не будет. 1.84 округлится до 2. И точность будет хромать это раз. Второе, вам надо приводить не к 4900mV, а сразу к измеряемому напряжению (до делителя). Пусть ADC=1023 ---> 9000mV. Диапазон unsigned short 0..65535. k = 9000mv / ADC = 9000 / 1023 = 8.79765 Vизм = k * ADC Чтобы не сильно потерять в точности при целочисленной арифметике, подбираем K таким, чтобы дробная часть была как можно меньше, и чтобы MAX(ADC) * K уложилось в unsigned short диапазон. K = 4 * k = 35.190 округляем до целого K = 35. Ну и формула примет вид Vизм = 35 * ADC >> 2;
|
|
|
|
|
Aug 12 2009, 10:14
|
Участник

Группа: Участник
Сообщений: 15
Регистрация: 4-09-07
Из: Kiev
Пользователь №: 30 288

|
Спасибо, вычесления понятны /* VСС - ?mV ADC=1023 ---> 9000mV k=9000mV/ADC=9000/1023=8.797 VCC=ADC*k Чтобы не сильно потерять в точности при целочисленной арифметике, подбираем K таким, чтобы дробная часть была как можно меньше, и чтобы MAX(ADC) * K уложилось в unsigned short диапазон. k=k*4=8.797*4=35.188 k=35 VCC=ADC*(35/4) */ и в теории все сходится, а на практике у меня не очень. Хочу уточнить результат ADC это 0-1023 ведь? Вот такой код у меня как пример работы вышел, может что-то где-то не так? А выходит следующие, я указал значение 6100mV и при реальных 6100 +/- там, сходится в теории и в реале, теперь пишу в коде 6000 а срабатывает гораздо ниже где-то на 5000mV. Делитель вроде верный 3300 Ohm к + и 3980 Ohm к земле.
/***************************************************** Port Definition PB0 = OUT (0) (PWM) PB1 = IN (Pull Up) (Switch) PB2 = IN (ADC) PB3 = OUT (1) (RED) PB4 = IN PB5 = IN ****************************************************** Hard info Vmax on Batt = 9000mV V on ATTINY13 = 4900mV Vmax on ADC = 4900mV ****************************************************** Chip type : ATtiny13 AVR Core Clock frequency: 9,600000 MHz Memory model : Tiny External RAM size : 0 Data Stack size : 16 *****************************************************/
#include <tiny13.h> #include <delay.h>
#define ADC_VREF_TYPE 0x00
unsigned int ADC, power_good; unsigned short VCC;
void RED_ON (void) {PORTB.3=0;} void RED_OFF (void) {PORTB.3=1;}
// External Interrupt 0 service routine interrupt [EXT_INT0] void ext_int0_isr(void) {
}
// Read the AD conversion result unsigned int read_adc(unsigned char adc_input) { ADMUX=adc_input | (ADC_VREF_TYPE & 0xff); // Delay needed for the stabilization of the ADC input voltage delay_us(10); // Start the AD conversion ADCSRA|=0x40; // Wait for the AD conversion to complete while ((ADCSRA & 0x10)==0); ADCSRA|=0x10; return ADCW; } void BATT_VCC () { ADC = read_adc(1); // 1 - ADC channel /* VСС - ?mV ADC=1023 ---> 9000mV k=9000mV/ADC=9000/1023=8.797 VCC=ADC*k Чтобы не сильно потерять в точности при целочисленной арифметике, подбираем K таким, чтобы дробная часть была как можно меньше, и чтобы MAX(ADC) * K уложилось в unsigned short диапазон. k=k*4=8.797*4=35.188 k=35 VCC=ADC*35/4 */ VCC=ADC*(35/4); }
// Declare your global variables here
void main(void) { // Declare your local variables here
// Crystal Oscillator division factor: 1 #pragma optsize- CLKPR=0x80; CLKPR=0x00; #ifdef _OPTIMIZE_SIZE_ #pragma optsize+ #endif
// Input/Output Ports initialization // Port B initialization // Func5=In Func4=In Func3=Out Func2=In Func1=In Func0=Out // State5=T State4=T State3=1 State2=T State1=P State0=0 PORTB=0x0A; DDRB=0x09;
// Timer/Counter 0 initialization // Clock source: System Clock // Clock value: 9600,000 kHz // Mode: Phase correct PWM top=FFh // OC0A output: Non-Inverted PWM // OC0B output: Disconnected TCCR0A=0x81; TCCR0B=0x01; TCNT0=0x00; OCR0A=0x00; OCR0B=0x00;
// External Interrupt(s) initialization // INT0: On // INT0 Mode: Low level // Interrupt on any change on pins PCINT0-5: Off GIMSK=0x40; MCUCR=0x00; GIFR=0x40;
// Timer/Counter 0 Interrupt(s) initialization TIMSK0=0x00;
// Analog Comparator initialization // Analog Comparator: Off ACSR=0x80; ADCSRB=0x00;
// ADC initialization // ADC Clock frequency: 75,000 kHz // ADC Bandgap Voltage Reference: Off // ADC Auto Trigger Source: None // Digital input buffers on ADC0: Off, ADC1: Off, ADC2: On, ADC3: Off DIDR0&=0x03; DIDR0|=0x2C; //ADMUX=ADC_VREF_TYPE & 0xff; // dafault generated by CVAVR ADMUX=1|(ADC_VREF_TYPE & 0xff); ADCSRA=0x87;
// Global enable interrupts #asm("sei")
BATT_VCC ();
while (1) {
// power_good chek begin BATT_VCC (); if (VCC>6100) { //mV delay_ms(100); if (VCC>6100) { power_good=0; }; } else { power_good=1; }; // power_good chek end
if (power_good==0) { RED_ON (); } else { RED_OFF (); };
}; }
Сообщение отредактировал Bogila Anton - Aug 12 2009, 10:24
|
|
|
|
|
Aug 13 2009, 23:16
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Bogila Anton @ Aug 12 2009, 13:14)  Хочу уточнить результат ADC это 0-1023 ведь? Да. Цитата и в теории все сходится, а на практике у меня не очень. Дак потому что надо писать не VCC=ADC*(35/4); а VCC = 35 * ADC / 4; иначе Вы режете диапазон. Еще лучше помочь компилятору и вручную заменить деление сдвигом: VCC = 35 * ADC >> 2; PS: почуствуйте разницу в приведенных выражениях: int A = 2; int B = 256; int C = 256; int X = A / B * C; (результ 0) и int X = C * A / B; (результ 2) от порядка следования операций в целочисленной арифметике сильно зависит результат. Если вначале вычислить A / B = 2 / 256 при округлении до целого получим 0. Потом 0 * 256 = 0. Если же вначале выполнить A * C = 512, поделив 512 на B=256 получим "2", что есть правильный результат.
|
|
|
|
|
Aug 14 2009, 06:37
|

Профессионал
    
Группа: Участник
Сообщений: 1 091
Регистрация: 25-07-07
Из: Саратов
Пользователь №: 29 357

|
Цитата(defunct @ Aug 14 2009, 03:16)  Дак потому что надо писать не VCC=ADC*(35/4);
а VCC = 35 * ADC / 4; иначе Вы режете диапазон. При чем тут диапазон? Просто 35/4 - это целочисленное деление, дающее в результате 8 Цитата(defunct @ Aug 14 2009, 03:16)  Еще лучше помочь компилятору и вручную заменить деление сдвигом: VCC = 35 * ADC >> 2; Уж сколько раз твердили миру, что деление и сдвиг не одно и то же! Сравни: -3 / 2 = -1 -3 >> 1 = -2 АЦП может выдавать отрицательный результат, поэтому здесь это особенно важно.
|
|
|
|
|
Aug 14 2009, 07:08
|
Участник

Группа: Участник
Сообщений: 15
Регистрация: 4-09-07
Из: Kiev
Пользователь №: 30 288

|
Цитата(defunct @ Aug 14 2009, 02:16)  Дак потому что надо писать не VCC=ADC*(35/4); а VCC = 35 * ADC / 4; иначе Вы режете диапазон. Еще лучше помочь компилятору и вручную заменить деление сдвигом: VCC = 35 * ADC >> 2; PS: почуствуйте разницу в приведенных выражениях: int A = 2; int B = 256; int C = 256; int X = A / B * C; (результ 0) и int X = C * A / B; (результ 2) от порядка следования операций в целочисленной арифметике сильно зависит результат. Если вначале вычислить A / B = 2 / 256 при округлении до целого получим 0. Потом 0 * 256 = 0. Если же вначале выполнить A * C = 512, поделив 512 на B=256 получим "2", что есть правильный результат. Вы меня запутали, мой калькулятор говорит: VCC = ADC*(35/4) = 1*(35/4) = 8.75 VCC = 35*ADC /4 = 35*1/4 = 8.75 VCC = ADC*(35/4) = 1023*(35/4) = 8951.25 VCC = 35*ADC /4 = 35*1023/4 = 8951.25 также само int A = 2; int B = 256; int C = 256; int X = A/B*C = 2/256*256 = 2 int X = C*A/B = 256*2/256 = 2 В чем разница? Цитата Уж сколько раз твердили миру Так как всетаки правильно?
Сообщение отредактировал Bogila Anton - Aug 14 2009, 07:10
|
|
|
|
|
Aug 14 2009, 13:01
|

кекс
     
Группа: Свой
Сообщений: 3 825
Регистрация: 17-12-05
Из: Киев
Пользователь №: 12 326

|
Цитата(Bogila Anton @ Aug 14 2009, 10:08)  int X = A/B*C = 2/256*256 = 2 int X = C*A/B = 256*2/256 = 2
В чем разница? Разница в том что числа целые, нигде нет дробной части. Все промежуточные результаты тоже целые. Поэтому int X = A/B*C = 2/256*256 = 0 A/B = 2/ 256 = 0 0 * C = 0 * 256 = 0 int X = C*A/B = 256*2/256 = 2 C * A = 256 * 2 = 512 512 / B = 512 / 256 = 2. Цитата(777777 @ Aug 14 2009, 09:37)  При чем тут диапазон? При том что 35 * MAX(ADC) > 8 * MAX(ADC). Цитата -3 / 2 = -1 -3 >> 1 = -2 АЦП может выдавать отрицательный результат, поэтому здесь это особенно важно. Ну во-первых заглянули бы в даташит на t13 перед тем как говорить может АЦП выдавать отрицательный результат или не может. В контексте контретно этой ветки - отрицательного результата быть не может. Во-вторых в Tiny13 всего 1kb памяти программ, деление будет для нее очень тяжеловесной операцией, поэтому ее стоит заменить сдвигом. А для отрицательных чисел - сдвигом с коррекцией. Цитата Сравни: Ну а тут уже о внимательности. 3. Перечитайте ветку, рекомендованный тип здесь - unsigned short. Цитата(Bogila Anton @ Aug 14 2009, 10:08)  Вы меня запутали, мой калькулятор говорит: Просто напишите в программе, именно так VCC = 35 * ADC >> 2; и скажите работает как надо или нет.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|