Полная версия этой страницы:
АЦП и ATmega48
Дмитрий_Мигачев
May 27 2006, 22:37
Вообщем не могу понять как работать с несколькими каналами АЦП. Юзаю VMLab, он меня всем удовлетворяет, сейчас пишу тут одну вещь и встал...Дело в том что есть какой то пульт управления, и результаты с рычагов должны оцифровываться 50 раз в секунду, вообщем как я думал, по переполнению таймера счетчика, мы уходим в прерывание, где тама, тама производим необходимую настройку ацп, далее разрешаем прерывание по завершению АЦП и так с двумя каналами. Я думал что бы работать с двумя каналами необходимо завести переменную, к примеру в начеле она равна нулю, после первого АЦП она инкрементируется, и далее производиться обработка второго канала, но компилятор выдает ошибком, вообщем мне кажется что я выбрал не правильный путь, подскажите как сделать правильней
Для понятливости того, что я написал приведу пример своего кода:
*************************
*************************
unsigned char adc_chan, b;
unsigned short kanal1, kanal2;
SIGNAL(SIG_OVERFLOW1){
asm("nop");
TCNT1=0xFFEB;
if (adc_chan==0) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS0);
ADMUX=0; //ADC0
ADCSRA|=_BV(ADSC);
asm("sei");
if (adc_chan==1) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS0);
ADMUX=_BV(MUX2)+_BV(MUX1)+_BV(MUX0);//ADC7
ADCSRA|=_BV(ADSC);
asm("sei");
}
}
SIGNAL(SIG_ADC){
if (adc_chan==0){
b=ADCL;
kanal1=(ADCH<<8)+b;
adc_chan++;
}
if (adc_chan==1){
b=ADCL;
kanal2=(ADCH<<8)+b;
adc_chan=0;
}
}
void idle_init(void) {
SMCR=_BV(SE);
asm("sei");
}
void timer1_init(void) {
TCCR1B=_BV(CS12)+_BV(CS10);//CLK/1024
TCNT1=0xFFEB;
TIMSK1=_BV(TOIE1);
}
// ***********************************************************
// Main program
//
int main(void) {
idle_init();
timer1_init();
adc_chan=0;
while(1) {
asm("sleep");
}
}
но он матюгается на этот код, а как сделать правильней?
Дмитрий_Мигачев
May 27 2006, 22:53
Вопрос конешно я задал довольно не понятно, но извините у нас уже просто почти 6 утра)
defunct
May 27 2006, 23:32
пишем функцию
Код
#define MUX_STATE (0 << REFS1)|(0 << REFS0)
#define ADC_STATE (1 << ADEN)|(0 << ADPS2)|(1 << ADPS1)|(1 <<ADPS0)
int ReadAdc(unsigned char ChannelNum)
{
ADMUX = ChannelNum | MUX_STATE;
ADCSRA = (1 << ADSC) | ADC_STATE;
while ( (ADCSRA & ( 1 << ADIF))==0);
return ADCW;
}
и подставляем в нее номер канала как параметр.
Channel0 = ReadAdc( 0 );
Channel2 = ReadAdc( 2 );
Laksus
May 28 2006, 11:29
Я не вникал в суть кода, но, по моему после первой
if() в SIGNAL(SIG_OVERFLOW1) не хватает }.
____________
Александр
2006 05 28
Laksus
May 28 2006, 11:59
И еще парочка мыслей по "косметике".
1- asm("sei"); в конце обработчика прерывания, по моему, совсем не нужны.
Прерывния и так установятся.
2- для того, чтобы считать 16-битное значения в С,
излишне выражение
b=ADCL;
kanal1=(ADCH<<8)+b;
компилятор только запутывается.
Достаточно
kanal1=ADC ;
а компилятор сам разберется с верхними/нижними байтами и сделает в лучшем виде.
_____________
Александр
2006 05 28
Дмитрий_Мигачев
May 29 2006, 03:21
Всем спасибо за ответы, более или менее разобрался, было много недочетов в том коде, поскольку писал на сонную голову, вчера на работе переделывал, но код не рациональный, хотелось бы узнать, как сделать более правильный код:
int My_global;
unsigned char adc_chan, b;
unsigned short kanal1, kanal2;
SIGNAL(SIG_OVERFLOW1){
TCNT1=0xFFEB
if (adc_chan==0) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS1)+_BV(ADPS0);
ADMUX=0;
ADCSRA|=_BV(ADSC);
}
if (adc_chan==1) {
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS1)+_BV(ADPS0);
ADMUX=_BV(MUX2)+_BV(MUX1)+_BV(MUX0);
ADCSRA|=_BV(ADSC);
}
}
SIGNAL(SIG_ADC){
if (adc_chan==0){
b=ADCL;
kanal1=(ADCH<<8)+b;
adc_chan++;
ADCSRA&=~_BV(3);
return;
}
if (adc_chan==1){
b=ADCL;
kanal2=(ADCH<<8)+b;
adc_chan=0;
ADCSRA&=~_BV(3);
}
}
void idle_init(void) {
SMCR=_BV(SE);
asm("sei");
}
void timer1_init(void) {
TCCR1B=_BV(CS12)+_BV(CS10);
TCNT1=0xFFEB;
TIMSK1=_BV(TOIE1);
}
int main(void) {
idle_init();
timer1_init();
adc_chan=0;
while(1) {
asm("sleep");
}
}
haker_fox
May 29 2006, 03:55
Первое, что бы я пожелал: оформляйте код в специальных тегах, вот так:
Код
int My_global;
unsigned char adc_chan;
unsigned short kanal1, kanal2;
SIGNAL(SIG_OVERFLOW1)
{
TCNT1=0xFFEB;
switch(adc_chan)
}
case 0:
ADMUX=0;
break;
case 1:
ADMUX=_BV(MUX2)+_BV(MUX1)+_BV(MUX0);
break;
}
ADCSRA|=_BV(ADSC);
}
SIGNAL(SIG_ADC)
{
switch(adc_chan)
{
case 0:
kanal1=ADC;
adc_chan++;
break;
case 1:
kanal2=ADC;
adc_chan=0;
break;
}
ADCSRA&=~_BV(3);
}
void idle_init(void)
{
SMCR=_BV(SE);
}
void timer1_init(void)
{
TCCR1B=_BV(CS12)+_BV(CS10);
TCNT1=0xFFEB;
TIMSK1=_BV(TOIE1);
}
int main(void)
{
ADCSRA=_BV(ADEN)+_BV(ADIF)+_BV(ADIE)+_BV(ADPS1)+_BV(ADPS0);
idle_init();
timer1_init();
adc_chan=0;
while(1)
{
asm("sleep");
}
}
Ну примерно бы так я офоромил программу, правда еще надо комментарии дописать. Ну и на всякий случай на симуляторе прогнать: нет ли где ошибок)))
Дмитрий_Мигачев
May 29 2006, 04:19
Цитата(haker_fox @ May 29 2006, 10:55)

Первое, что бы я пожелал: оформляйте код в специальных тегах, вот так:
...
Ну я понял, что вы изменили, спасибо, но вопрос другой, это оптимальный вариант работы с АЦП (именно в моем случае с двумя каналами), или можно найти другие пути, обязательно наличие следящей переменной adc_chan?
haker_fox
May 29 2006, 04:38
Цитата(Дмитрий_Мигачев @ May 29 2006, 13:19)

Цитата(haker_fox @ May 29 2006, 10:55)

Первое, что бы я пожелал: оформляйте код в специальных тегах, вот так:
...
Ну я понял, что вы изменили, спасибо, но вопрос другой, это оптимальный вариант работы с АЦП (именно в моем случае с двумя каналами), или можно найти другие пути, обязательно наличие следящей переменной adc_chan?
Нет предела совершенству, конечно можно найти еще множество путей. Наличие переменной adc_chan не обязательно, можно использовать сразу регистр ADMUX - ведь в нем хранится номер канала.
SasaVitebsk
May 29 2006, 09:50
Цитата(Дмитрий_Мигачев @ May 29 2006, 07:19)

Ну я понял, что вы изменили, спасибо, но вопрос другой, это оптимальный вариант работы с АЦП (именно в моем случае с двумя каналами), или можно найти другие пути, обязательно наличие следящей переменной adc_chan?
Необязательно ожидать завершения операции от АЦП. Особенно в Вашем случае (50 раз в секунду).
Алгоритм работы такой:
1) АЦП запускается в "автоматическом" режиме без генерации прерываний.
2) Организуется прерывание по таймеру.
3) В данном прерывании:
a) Читается текущее значение
б) Пререключается слудующий канал.
ВНИМАНИЕ! Не наоборот!
Дмитрий_Мигачев
May 29 2006, 14:26
Вообще я хотел сделать так, чтобы оцифровка каналов, начиналась каждые 50 секунд, мне так не обходимо.
Я думал надо делать так:
1) Организуется прерывание по таймеру(50 раз в сек)
2) В данном прерывании устанавливается канал для оцифровки
3) Запускаем АЦП
4) По завершению АЦП возникает прерывание в котором, я сохраняю всю информацию за исключением двух младших равзрядов
defunct
May 29 2006, 14:43
Цитата(Дмитрий_Мигачев @ May 29 2006, 17:26)

Я думал надо делать так:
1) Организуется прерывание по таймеру(50 раз в сек)
2) В данном прерывании устанавливается канал для оцифровки
3) Запускаем АЦП
4) По завершению АЦП возникает прерывание в котором, я сохраняю всю информацию за исключением двух младших равзрядов
Просто в прерывании таймера запускайте одиночное преобразование АЦП и считывайте показания.
Не думаю, что для решения вашей задачи нужно вообще шаманить с прерываниями от АЦП.
Дмитрий_Мигачев
May 29 2006, 14:48
Вообще это часть моей задачи. Я хочу сделать радиоуправляемую модель, с 2 пропорциональными и 3 дискретными каналами. Приемопередающий модуль DP1203. Вот и изучаю, как правильней с этим работать
defunct
May 29 2006, 15:12
Цитата(Дмитрий_Мигачев @ May 29 2006, 17:48)

Вот и изучаю, как правильней с этим работать
тут много путей в равной степени правильных.
Однозначно сказать, что такой-то путь самый правильный нельзя..
Простой путь я вам описал. (в обработчике прерывания таймера осуществлять одиночное преобразование и сразу считывать результат).
Чуть более эффективный - ваш (с двумя прерываниями).
Еще более эффективный - настроить АЦП на работу в автоматическом режиме с запуском от таймера и генерацией прерывания по окончанию преобразования. (выбросить прерывание таймера).
Вопрос только в том, нужно ли усложнять жизнь если производительности МК хватает для реализации даже самого простого из правильных путей?
SasaVitebsk
May 29 2006, 15:56
Цитата(Дмитрий_Мигачев @ May 29 2006, 17:26)

Вообще я хотел сделать так, чтобы оцифровка каналов, начиналась каждые 50 секунд, мне так не обходимо.
Я думал надо делать так:
1) Организуется прерывание по таймеру(50 раз в сек)
2) В данном прерывании устанавливается канал для оцифровки
3) Запускаем АЦП
4) По завершению АЦП возникает прерывание в котором, я сохраняю всю информацию за исключением двух младших равзрядов
Перечитайте ещё раз мой пост выше. Мой алгоритм эффективней т.к не требует ожидания от ацп!
Поясню детальнее. Напимер у Вас 10 каналов. Инициализируем таймер на 50/10=5сек. В прерывании алгоритм такой:
1) Считываем значение предыдущего канала
2) Изменяем канал
3) выходим
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.