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

 
 
> Работа с АЦП в 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
 
Start new topic
Ответов
Bogila Anton
сообщение Aug 12 2009, 10:14
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 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
Сообщение #3


кекс
******

Группа: Свой
Сообщений: 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
Bogila Anton
сообщение Aug 14 2009, 07:08
Сообщение #4


Участник
*

Группа: Участник
Сообщений: 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
defunct
сообщение Aug 14 2009, 13:01
Сообщение #5


кекс
******

Группа: Свой
Сообщений: 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 - 23:18
Рейтинг@Mail.ru


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