реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Работа с АЦП в TINY13, Нужны примеры на С
Bogila Anton
сообщение Aug 10 2009, 14:38
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 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
Эскизы прикрепленных изображений
Прикрепленное изображение
 
Go to the top of the page
 
+Quote Post
defunct
сообщение Aug 10 2009, 15:31
Сообщение #2


кекс
******

Группа: Свой
Сообщений: 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;
Go to the top of the page
 
+Quote Post
Bogila Anton
сообщение Aug 10 2009, 20:56
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
defunct
сообщение Aug 11 2009, 11:13
Сообщение #4


кекс
******

Группа: Свой
Сообщений: 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;
Go to the top of the page
 
+Quote Post
Bogila Anton
сообщение Aug 12 2009, 10:14
Сообщение #5


Участник
*

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
defunct
сообщение Aug 13 2009, 23:16
Сообщение #6


кекс
******

Группа: Свой
Сообщений: 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", что есть правильный результат.
Go to the top of the page
 
+Quote Post
777777
сообщение Aug 14 2009, 06:37
Сообщение #7


Профессионал
*****

Группа: Участник
Сообщений: 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
АЦП может выдавать отрицательный результат, поэтому здесь это особенно важно.
Go to the top of the page
 
+Quote Post
Bogila Anton
сообщение Aug 14 2009, 07:08
Сообщение #8


Участник
*

Группа: Участник
Сообщений: 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
Go to the top of the page
 
+Quote Post
AHTOXA
сообщение Aug 14 2009, 07:46
Сообщение #9


фанат дивана
******

Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684



Цитата(Bogila Anton @ Aug 14 2009, 13:08) *
Вы меня запутали, мой калькулятор говорит:


А вы попробуйте переключить калькулятор в HEX smile.gif


--------------------
Если бы я знал, что такое электричество...
Go to the top of the page
 
+Quote Post
defunct
сообщение Aug 14 2009, 13:01
Сообщение #10


кекс
******

Группа: Свой
Сообщений: 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;
и скажите работает как надо или нет.
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 20th July 2025 - 16:24
Рейтинг@Mail.ru


Страница сгенерированна за 0.01452 секунд с 7
ELECTRONIX ©2004-2016