АЦП - выбор лапки
-
kobzar
- Грызет канифоль
- Сообщения: 278
- Зарегистрирован: Вт дек 03, 2013 11:04:04
- Откуда: Киев
- Контактная информация:
Re: АЦП - выбор лапки
Так а и сказал что благодарю за мысль - по аналогии я уже накручу то надо!!!
Влодение рускай арфаграфией - это как владение кунг-фу: настаящие мастира не преминяют ево бес ниабхадимости
- Реклама
- zero648
- Вымогатель припоя
- Сообщения: 650
- Зарегистрирован: Пн июн 18, 2012 12:01:04
- Откуда: Челябинская область, Копейск
Re: АЦП - выбор лапки
А у нас что, биты мультиплексора теперь раскиданы по всему регистру, что-ли? Каким местом здесь не тру?
Можно, конечно, и так, что тоже самое
(0<<MUX0) //ADC0
(1<<MUX0) //ADC1
(2<<MUX0) //ADC2
- pyzhman
- Друг Кота
- Сообщения: 7016
- Зарегистрирован: Вс июл 12, 2009 19:15:29
- Откуда: Ижевск
- Контактная информация:
Re: АЦП - выбор лапки
Это не по поводу вашего кода. Это по поводу:

kobzar писал(а):Ето не трувей которым пользуются только КВАВР-щеки!
Docendo discimus
- zero648
- Вымогатель припоя
- Сообщения: 650
- Зарегистрирован: Пн июн 18, 2012 12:01:04
- Откуда: Челябинская область, Копейск
Re: АЦП - выбор лапки
Действительно, АСМ-щеки тоже этим пользуются 
- pyzhman
- Друг Кота
- Сообщения: 7016
- Зарегистрирован: Вс июл 12, 2009 19:15:29
- Откуда: Ижевск
- Контактная информация:
Re: АЦП - выбор лапки
Согласен. Вообще говоря, интереснее с машиной говорить на ее языке, а не на абракадабрском десятичной системы. Равно как и с людьми. Впрочем, это уже офтоп; не обижусь, если удалят.
Docendo discimus
- Реклама
Re: АЦП - выбор лапки
Коты, помогите. Мне нужно опрашивать три АЦП лапы, но при опросе, все лапы при опросе имеют значения 1023 1022 1021 (эти значения меняются на 1 или 2). Пользуюсь ATMega 328P 20мГц. Прочитал справочную литературу, сделал сначала программу по опросу одной лапки, все работает замечательно. Если программа опрашивает один из портов и не опрашивает другие, значения получаются правильные. Если же я отправляю опрос трех лапок в цикле, то получаются неправильные значения. Я выложу кусок кода из программы, если вам не тяжело, покажите мою ошибку.
init_adc.h
init_adc.c
main.c
}
Но в итоге переменные vuntADCdata00 vuntADCdata01 и vuntADCdata02 содержат ересь в виде 1023 1021 1022, помогите!
init_adc.h
Код: Выделить всё
#ifndef INIT_ADC_H_
#define INIT_ADC_H_
volatile unsigned int vuntADCdata00;
volatile unsigned int vuntADCdata01;
volatile unsigned int vuntADCdata02;
volatile unsigned char uchChangeADC;
void init_adc (void); //Функция инициализации АЦП
void change_adc(void); //Функция изменения АЦП
#endif /* INIT_ADC_H_ */init_adc.c
Код: Выделить всё
#include <avr/io.h>
#include "init_adc.h"
#include <avr/interrupt.h>
ISR (ADC_vect)
{
if (uchChangeADC == 0)
vuntADCdata00 = (ADCL | ADCH << 8); // Считываем ADC PC0
else
if (uchChangeADC == 1)
vuntADCdata01 = (ADCL | ADCH << 8); // Считываем ADC PC1
else
vuntADCdata02 = (ADCL | ADCH << 8); // Считываем ADC PC2
}
void init_adc (void) //Функция инициализации АЦП
{
ADMUX |=(0 << REFS1) | (1 << REFS0) // Напряжение питания AVcc
| (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); //вход PC0
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);//20000мГц
ADCSRA |= (1 << ADEN) //Включение АЦП
| (1 << ADSC) //запуск преобразования
| (0 << ADATE) // 0 - Одиночное преобразование, 1 - настраеваемое преобразование (регистр ADCSRB (биты ADTS0-ADTS2))
| (1 << ADIE); //Разрешение прерывания от компаратора
uchChangeADC = 0;
}
void change_adc(void)//Функция изменения АЦП
{
if (uchChangeADC == 0)
{
ADMUX |=(0 << REFS1) | (1 << REFS0) // Напряжение питания AVcc
|(0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (1 << MUX0); //вход PC1
uchChangeADC = 1;
}
else
{
if (uchChangeADC == 1)
{
ADMUX |=(0 << REFS1) | (1 << REFS0) // Напряжение питания AVcc
| (0 << MUX3) | (0 << MUX2) | (1 << MUX1) | (0 << MUX0); //вход PC2
uchChangeADC = 2;
}
else
{
ADMUX |=(0 << REFS1) | (1 << REFS0) // Напряжение питания AVcc
| (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); //вход PC0
uchChangeADC = 0;
}
}
ADCSRA |=(1 << ADSC); //запуск преобразования
}main.c
Код: Выделить всё
void main (void)
{
init_adc();//инициализация ацп
while(1)
{
change_adc();//Функция должна менять лапку АЦП а я потом считываю значения из переменных vuntADCdata00 vuntADCdata01 и vuntADCdata02
}}
Но в итоге переменные vuntADCdata00 vuntADCdata01 и vuntADCdata02 содержат ересь в виде 1023 1021 1022, помогите!
Кот должен прожить жизнь без сожаления.
- zero648
- Вымогатель припоя
- Сообщения: 650
- Зарегистрирован: Пн июн 18, 2012 12:01:04
- Откуда: Челябинская область, Копейск
Re: АЦП - выбор лапки
А если функцию так сделать:
Код: Выделить всё
void change_adc(void)//Функция изменения АЦП
{
uchChangeADC++;
if (uchChangeADC == 3) {uchChangeADC=0;}
ADMUX |=(0 << REFS1) | (1 << REFS0) | (uchChangeADC << MUX0); // Напряжение питания AVcc
ADCSRA |=(1 << ADSC); //запуск преобразования
}-
kobzar
- Грызет канифоль
- Сообщения: 278
- Зарегистрирован: Вт дек 03, 2013 11:04:04
- Откуда: Киев
- Контактная информация:
Re: АЦП - выбор лапки
Вобщем и целом, благодаря вашим советам получилось сделать выбор канала через свиг регистра!
Другими словами - правильно и без всяких там разных систем счисления! Я не стану утверждать что мой метод более или менее правильный чем предложенные ранее, просто я педант - и в процессе изучения Си для АВР-ок! Поставленная перед самим собой задача, была сделать и понять так как "Правильно" а не "Будет работать". Конечно я понимаю что со временем обьем полученных знаний умноженный на уровень лени приведет меня к тому что я буду на уровне подсознания оперировать двиочными и десятичными числам автоматом переводя их в названия выставленных битов ... но пока что - я вижу точно что я выставил и зачем! Кроме того меня спрашивали зачем заморачиваться с циклом? Ответ банален - сделав оди раз цыкл - мне больше не надо дописывать строки кода если я допустим захочу использовать 3,5,7 входов ацп! Мне достаточно будет просто изменить длину цикла
Примитивная функция для опроса трех ног выглядит так (конечно при использовании более чем первые три входа функцию нужно будет модифицировать но ето выходит за рамки моей задачи)
И ее использование для вывода данных например на ЛЦД 3310 может иметь примитивный вид
Ну а дальше добавим несколько замеров и вывод средне арифметического и так далее - но это уже совершенно другая сказка ....
Другими словами - правильно и без всяких там разных систем счисления! Я не стану утверждать что мой метод более или менее правильный чем предложенные ранее, просто я педант - и в процессе изучения Си для АВР-ок! Поставленная перед самим собой задача, была сделать и понять так как "Правильно" а не "Будет работать". Конечно я понимаю что со временем обьем полученных знаний умноженный на уровень лени приведет меня к тому что я буду на уровне подсознания оперировать двиочными и десятичными числам автоматом переводя их в названия выставленных битов ... но пока что - я вижу точно что я выставил и зачем! Кроме того меня спрашивали зачем заморачиваться с циклом? Ответ банален - сделав оди раз цыкл - мне больше не надо дописывать строки кода если я допустим захочу использовать 3,5,7 входов ацп! Мне достаточно будет просто изменить длину цикла
Примитивная функция для опроса трех ног выглядит так (конечно при использовании более чем первые три входа функцию нужно будет модифицировать но ето выходит за рамки моей задачи)
Код: Выделить всё
int readADC(int ch)
{
unsigned char set_admux = ADMUX;
set_admux &= ~((1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0));
if (ch != 0)
{
set_admux |= (1<<ch-1);
}
ADMUX = set_admux;
char buffer[12];
_delay_ms(20);
ADCSRA |= (1 << ADSC); // Запускаем преобразование
while((ADCSRA & (1 << ADSC))); // Ждем окончания преобразования
int val = ADC;
itoa(val, buffer,10);
return buffer; // Возвращаем значение АЦП
}И ее использование для вывода данных например на ЛЦД 3310 может иметь примитивный вид
Код: Выделить всё
LcdClear();
for (int i=0;i<=2;i++)
{
LcdGotoXYFont(1,i+1);
LcdStr(FONT_1X,readADC(i));
}
LcdUpdate();
_delay_ms(1000);Ну а дальше добавим несколько замеров и вывод средне арифметического и так далее - но это уже совершенно другая сказка ....
Влодение рускай арфаграфией - это как владение кунг-фу: настаящие мастира не преминяют ево бес ниабхадимости
Re: АЦП - выбор лапки
zero648 писал(а):А если функцию так сделать:Код: Выделить всё
void change_adc(void)//Функция изменения АЦП
{
uchChangeADC++;
if (uchChangeADC == 3) {uchChangeADC=0;}
ADMUX |=(0 << REFS1) | (1 << REFS0) | (uchChangeADC << MUX0); // Напряжение питания AVcc
ADCSRA |=(1 << ADSC); //запуск преобразования
}
Мысль понятна. Я подобную манипуляцию с ADMUX не пробовал делать. Через 7 часов я доберусь до МК и программатора и обязательно попробую и отпишусь о результатах. Спасибо за помощь!
Кот должен прожить жизнь без сожаления.
Re: АЦП - выбор лапки
DruidCat писал(а):zero648 писал(а):А если функцию так сделать:Код: Выделить всё
void change_adc(void)//Функция изменения АЦП
{
uchChangeADC++;
if (uchChangeADC == 3) {uchChangeADC=0;}
ADMUX |=(0 << REFS1) | (1 << REFS0) | (uchChangeADC << MUX0); // Напряжение питания AVcc
ADCSRA |=(1 << ADSC); //запуск преобразования
}
Мысль понятна. Я подобную манипуляцию с ADMUX не пробовал делать. Через 7 часов я доберусь до МК и программатора и обязательно попробую и отпишусь о результатах. Спасибо за помощь!
Не, не работает такой способ записи.
А вот так заработало.
Код: Выделить всё
#include <avr/io.h>
#include "init_adc.h"
#include <avr/interrupt.h>
ISR (ADC_vect)
{
if (uchChangeADC == 0)
vuntADCdata00 = (ADCL | ADCH << 8); // Считываем ADC PC0
else
if (uchChangeADC == 1)
vuntADCdata01 = (ADCL | ADCH << 8); // Считываем ADC PC1
else
vuntADCdata02 = (ADCL | ADCH << 8); // Считываем ADC PC2
}
void init_adc (void) //Функция инициализации АЦП
{
// ADMUX |=(0 << REFS1) | (1 << REFS0) // Напряжение питания AVcc (Нет необходимости в этой записи)
// | (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); //вход PC0 (Нет необходимости в этой записи)
ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);//20000мГц
ADCSRA |= (1 << ADEN) //Включение АЦП
// | (1 << ADSC) //запуск преобразования (Нет необходимости в этой записи)
| (0 << ADATE) // 0 - Одиночное преобразование, 1 - настраевоемое преобразование (регистр ADCSRB (биты ADTS0-ADTS2))
| (1 << ADIE); //Разрешение прерывания от компаратора
uchChangeADC = 0;
}
void change_adc(void)//Функция изменения АЦП
{
if (uchChangeADC == 0)
{
ADMUX = 0х41; //вход PC1
uchChangeADC = 1;
}
else
{
if (uchChangeADC == 1)
{
ADMUX = 0x42; //вход PC2
uchChangeADC = 2;
}
else
{
ADMUX = 0x40; //вход PC0
uchChangeADC = 0;
}
}
[b] _delay_ms(1);[/b]//Без паузы не работает, у меня программа большая, поэтому 1 мс достаточно
ADCSRA |=(1 << ADSC); //запуск преобразования
}Но вот возникла другая проблема, снятие показаний с канала не совпадают, с логикой. Получается, если я выбираю канал РС0, то данные снимаются с РС2, если РС1, то данные с РС0 и если РС2 то данные снимаются с РС1. Коты, не подскажете почету так? И я не понял, почему заработала запись активизации канала только такой записью ADMUX = 0x40? Еще заработал выбор АЦП, с записью регистра ADMUX предложенный kobzar. По чему так, я не могу понять
Кот должен прожить жизнь без сожаления.
Re: АЦП - выбор лапки
kobzar писал(а):Вобщем и целом, благодаря вашим советам получилось сделать выбор канала через свиг регистра!
Другими словами - правильно и без всяких там разных систем счисления! Я не стану утверждать что мой метод более или менее правильный чем предложенные ранее, просто я педант - и в процессе изучения Си для АВР-ок! Поставленная перед самим собой задача, была сделать и понять так как "Правильно" а не "Будет работать". Конечно я понимаю что со временем обьем полученных знаний умноженный на уровень лени приведет меня к тому что я буду на уровне подсознания оперировать двиочными и десятичными числам автоматом переводя их в названия выставленных битов ... но пока что - я вижу точно что я выставил и зачем! Кроме того меня спрашивали зачем заморачиваться с циклом? Ответ банален - сделав оди раз цыкл - мне больше не надо дописывать строки кода если я допустим захочу использовать 3,5,7 входов ацп! Мне достаточно будет просто изменить длину цикла![]()
Примитивная функция для опроса трех ног выглядит так (конечно при использовании более чем первые три входа функцию нужно будет модифицировать но ето выходит за рамки моей задачи)Код: Выделить всё
int readADC(int ch)
{
unsigned char set_admux = ADMUX;
set_admux &= ~((1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0));
if (ch != 0)
{
set_admux |= (1<<ch-1);
}
ADMUX = set_admux;
char buffer[12];
_delay_ms(20);
ADCSRA |= (1 << ADSC); // Запускаем преобразование
while((ADCSRA & (1 << ADSC))); // Ждем окончания преобразования
int val = ADC;
itoa(val, buffer,10);
return buffer; // Возвращаем значение АЦП
}
И ее использование для вывода данных например на ЛЦД 3310 может иметь примитивный видКод: Выделить всё
LcdClear();
for (int i=0;i<=2;i++)
{
LcdGotoXYFont(1,i+1);
LcdStr(FONT_1X,readADC(i));
}
LcdUpdate();
_delay_ms(1000);
Ну а дальше добавим несколько замеров и вывод средне арифметического и так далее - но это уже совершенно другая сказка ....
А если вот так тебе сделать?
Код: Выделить всё
unsigned int readADC(int ch)
{
unsigned char set_admux = ADMUX;
set_admux &= ~((1<<MUX3)|(1<<MUX2)|(1<<MUX1)|(1<<MUX0));
if (ch != 0)
{
set_admux |= (1<<ch-1);
}
ADMUX = set_admux;
_delay_ms(20);
ADCSRA |= (1 << ADSC); // Запускаем преобразование
while((ADCSRA & (1 << ADSC))); // Ждем окончания преобразования
return (ADCL | ADCH << 8); // Возвращаем значение АЦП
}АЦП 10 битный, сделай возврат данных функции unsigned int (16 бит от 0 )
И зачем городить с itoa? Я так и не понял, возвращай сразу 10 битное значение АЦП функции.
Кот должен прожить жизнь без сожаления.
-
Alexeyslav
- Друг Кота
- Сообщения: 4550
- Зарегистрирован: Чт май 05, 2011 21:26:34
- Откуда: Украина, Славутич
- Контактная информация:
Re: АЦП - выбор лапки
Предыдущее значение получаешь потому что мультиплексор фиксируется не в тот момент когда начинается преобразование - чтобы этого не было, после окончания преобразования АЦП нужно выдержать некоторое время(1-2 такта на частоте АЦП) после переключения канала, потом стартовать преобразование, тогда будет измерен правильный канал.
В догонку, посоветую "правильный" способ переключения каналов в цикле - заносишь в массив констант номера нужных каналов, и в цикле по индексу с массива присваиваешь значение, не забывая OR-ить нужные настройки источника опорного напряжения. Таким образом у тебя имеется единственное место определяющее номера каналов и последовательность их выбора - массив с номерами каналов. Размер массива можно использовать как параметр цикла...
В догонку, посоветую "правильный" способ переключения каналов в цикле - заносишь в массив констант номера нужных каналов, и в цикле по индексу с массива присваиваешь значение, не забывая OR-ить нужные настройки источника опорного напряжения. Таким образом у тебя имеется единственное место определяющее номера каналов и последовательность их выбора - массив с номерами каналов. Размер массива можно использовать как параметр цикла...
Re: АЦП - выбор лапки
Большое спасибо за помощь! По второму пункту ответа все предельно ясно. А вот по первому я слегка запутался. У меня программа работает по такой логике:
1) Выбор канала.
2) Пауза.
3) Запуск преобразования.
4) Прерываение.
5) Съем показаний.
Пожалуйста, покажите мне, где нужно сделать паузу в 2 такта, чтоб съем показаний был корректный. И если не затруднит, подскажите как лучше эту паузу сделать. _delay_ms - это бяка, каких свет не видывал.
1) Выбор канала.
2) Пауза.
3) Запуск преобразования.
4) Прерываение.
5) Съем показаний.
Пожалуйста, покажите мне, где нужно сделать паузу в 2 такта, чтоб съем показаний был корректный. И если не затруднит, подскажите как лучше эту паузу сделать. _delay_ms - это бяка, каких свет не видывал.
Кот должен прожить жизнь без сожаления.
-
Alexeyslav
- Друг Кота
- Сообщения: 4550
- Зарегистрирован: Чт май 05, 2011 21:26:34
- Откуда: Украина, Славутич
- Контактная информация:
Re: АЦП - выбор лапки
А попробуйте запустить преобразование вручную, т.е. константой задать номер канала, измерить и так несколько каналов. Может, просто с циклом что-то не то и номер канала подставляется не тот?
Вот странно, посмотрел у себя(mega48PA) - устанавливаю номер канала и сразу же за ним команда начала преобразования, измерение происходит ровно на заданном канале.
В даташите читал какие-то предостережения на счет переключения канала при измерении, но помню что в одном проекте на mega8 тоже была такая фигня, получал измерение предыдущего канала. Не помню уже как проблему разрешил...
Тут походу с кондачка не решится, и надо таки покурить даташит и программу. Что-то мне кажется, проблема в программе.
Сделай тестовую функцию без прерываний - задаешь канал, получаешь результат. И запусти подряд несколько измерений, потом по UART передать на терминал в протеусе будет наглядно видно.
Вот странно, посмотрел у себя(mega48PA) - устанавливаю номер канала и сразу же за ним команда начала преобразования, измерение происходит ровно на заданном канале.
В даташите читал какие-то предостережения на счет переключения канала при измерении, но помню что в одном проекте на mega8 тоже была такая фигня, получал измерение предыдущего канала. Не помню уже как проблему разрешил...
Тут походу с кондачка не решится, и надо таки покурить даташит и программу. Что-то мне кажется, проблема в программе.
Сделай тестовую функцию без прерываний - задаешь канал, получаешь результат. И запусти подряд несколько измерений, потом по UART передать на терминал в протеусе будет наглядно видно.
Re: АЦП - выбор лапки
Alexeyslav писал(а):Сделай тестовую функцию без прерываний - задаешь канал, получаешь результат. И запусти подряд несколько измерений, потом по UART передать на терминал в протеусе будет наглядно видно.
Сделал, работает нормально. Задаю канал, делаю паузу на установку канала. Потом жду завершения преобразования. Снимаю показания. У меня глупый вопрос: если происходит прерывание, это ведь означает окончание преобразования? В справочнике написанно что Да.
Одним словом я написал драйвер АЦП работающий на другом принципе. В виде функции, которая получает номер канала и возвращает результат. Ее особенность в том, что она не использует пауз, таймеров и прерываний. И работает онлайн, код этой функции можно встроить в большую программу, и она не будет тормозить основную программу. Сейчас я ее красиво оформлю и выложу на форуме, вдруг кому пригодится.
Кот должен прожить жизнь без сожаления.
-
Alexeyslav
- Друг Кота
- Сообщения: 4550
- Зарегистрирован: Чт май 05, 2011 21:26:34
- Откуда: Украина, Славутич
- Контактная информация:
Re: АЦП - выбор лапки
Погоди ка, а как эта функция вообще работает? Ведь чтобы вернуть результат надо дождаться конца измерения - а значит внутри функции нужно ЖДАТЬ. Либо это вовсе не функция, а просто синоним команды запуска преобразования(куда там больше?) которая ничего не возвращает, а результат ложится и обрабатывается совсем в другом месте.


