Обращение к АЦП в обработчике прерывания
- Войдите на сайт для отправки комментариев
Собственно вопрос таков - кто-нибудь писал работающие скетчи в которых изменение напряжения происходит внутри функции вызываемой в прерывании?
Пытаюсь сделать измерение среднеквадратичного тока в тиристорном диммере. Само построение диммера стандартное. Atmega328. Детектор нуля по прерыванию сбрасывает счетчик и запускает таймер. Таймер генерирует прерывания с интервалом 156мкс что дает 64 шага на полупериоде 50Гц. В обработчике прерываний таймера формируется управляющий сигнал на тиристор и измеряется протекающий через тиристор ток на каждом тике таймера. Квадраты этого тока накапливаются в переменной. В другой подсчитывается число измерений. В основном цикле раз в секунду накопленная сумма делится на число измерений, извлекается квадраный корень и выводится на индикацию. Сумма и счетчик обнуляются.
Проблема в том, что в переменной где должна накапливаться сумма квадратов тока всегда ноль. В счетчике числа измерений число правильное. Получается, что АЦП не срабатывает. Или в обработчике прерываний он и не должен работать?
Вот код обработчика:
void halfcycle() //прерывания таймера
{
if (tic < 63) {
tic++; //счетчик тактов ШИМ
if ( Dimmer1 < tic ) { //определение момента включения тиристора
PORTD |= B00010000; //D4_High; //переключаем в 1 выход управления если счетчик досчитал до заданного порога
ADMUX = B11100000; //используем вход А0 и внутренний источник опорного напряжения 1.1В
ADCSRA = B11000100; //Тактовая АЦП 1МГц
while (ADCSRA & (1 << ADSC));
I = ADCH; //измеряем мгновенное значение тока
for (uint8_t k = 0; k < I; k++) { I += I; } //возведение в квадрат
I_rms += I; //накопление квадратичного значения тока
cnt1 ++; //счетчик измерений
}
}
else {
PORTD &= B11101111; //D4_Low; //выключаем управление тиристором на случай если детектор нуля не сработает
}
}
Прерывания работают, ток нагрузки регулируется. Для контроля вешал в прерываниях XOR на свободные цифровые выходы - меандры генерируются. Вот только ток не измеряется.
Да, была у меня по первости дурная идея мерять VCC контроллера в прерывании таймера. Меряло что-то там, основную программу тормозило, бипер на ШИМе заикался ))
Инициализируйте переменные которые изменяются в прерывании как volatile
volatile byte Var1;
volatile float Var2; и т.д.
Инициализируйте переменные которые изменяются в прерывании как volatile
Именно так они и инициализируются. Это была бы слишком детская ошибка :)
Alex_Sk, во первых по огрызку программы трудно судить. Где объявления типов данных? И что за волшебная чушь в 12 строке?
Alex_Sk, во первых по огрызку программы трудно судить. Где объявления типов данных? И что за волшебная чушь в 12 строке?
Прошу прощения за 12 строку. При выдирании куска кода просмотрел.
Там вот так на самом деле:
uint16_t I_tmp = 0; for (uint8_t k = 0; k < I; k++) { I_tmp += I; } I_rms += I_tmp; //накопление среднеквадратичного значения токаТак быстрее вычисляется квадрат числа чем умножением в данном случае.
С типами данных все в порядке.
Я попробовал копировать в I_rms просто значение получаемое от АЦП без всяких вычислений. И на индикацию значения в диапазоне 0..255 выводятся пропорционально положению регулятора тока, но выводятся раз в 15-60 сек, интервал случаен, а в остальное время 0. Т.е. проблема именно в получении значения из прерывания.
В самом цикле loop() на индикатор выводится еще порог напряжения считываемый с потенциометра в этом же цикле. Выводится правильно, младший разряд после запятой на единичку скачет туда-обратно раз в секунду как и работает цикл. Значит прерывания индикации не мешают. По всему выходит, что в регистре ADCH АЦП чаще всего оказывается 0 вместо результата преобразования.
Нужно использовать два обработчика
в обработчике таймера надо запускать измерение АЦП
а считывать готовые данные в прерывании самого АЦП
ваша глупость в том, что внутри обработчика написан цикл ожидания
это как раз то что нужно избегать
Ожидание флага завершения преобразования это стандартное решение используемое и в analogRead и в An_Read из CyberLib. И как-то эти функции глупостью никто не считает.
Прерывания таймера идут через 156мкс а преобразование в АЦП при тактировании 1МГц занимает около 40мкс вместе с этим ожиданием. На работу loop() с индикацией времени остается достаточно.
Можно и на прерываниях с АЦП попробовать, возможно, если запускать преобразование в конце обработчика, то завершаться оно будет уже в момент когда других прерываний нет и это сработает.
1. Измерять из под прерывания можно, если Вы понимаете даташит верно. В этом коде это не так.
2. Запускать измерение АЦП изнутри прерывания тоже в общем-то можно и принимать результат по прерыванию АЦП тоже можно, но надо понимать что когда и где должно правильно завершаться.
3. посмотрите эту тему http://arduino.ru/forum/proekty/samodelnaya-mega2560-128a-s-pamyatyu-512kb и все темки в проектах по ключевому слову "осцилограф". Там можно почерпнуть много полезного. Ну и перечитайте даташит и оцените что не так в вашей программе.
Arhat109-2, Вы, случайно, на EUROBOT завтра не едете?
С типами данных все в порядке.
Ну конечно, у 99 % начинающих с типами даных вечные косяки, но у вас -то всё хорошо, хотя почему-то не работает, не наводит на мысли?
Нет. Мы игрушками в Лего не занимаемся. Какой смысл имитировать "наполнение водонапорной башни" тренируясь забрасывать мячики в коробку? Есть школьный проект "гидропоника", где есть нормальное место нормальному применению Ардуино с вполне реальной системой полива и прочего "робот может использовать природные ресурсы" .. выросли мы уже из игр в лего и давненько. Пусть "взрослые развлекаются". :)
P.S. В целом, за 2 года наблюдаю бурный рост имитации деятельности в робототехнике с явно отсутствующим результатом. Чем дальше - тем кучерявей.
P.P.S. Зачем в этой теме спрашивать, ежели можно кинуть мыло на общедоступную почту? :)
P.P.S. Зачем в этой теме спрашивать, ежели можно кинуть мыло на общедоступную почту? :)
Ну я прошу прощения, модераторы, думаю, потрут.
Правильно Вам axill написал. Никаких ожиданий в прерывании!
И то что analogRead кривой - давно известно.
Пишем в сетапе для АЦП типа так.
В прерывании таймера первым делом считываете результат с АЦП, затем запускаете преобразование.
Т.е. стартуем АЦП для получения результата при следующем прерывании таймера.
Второе прерывание, по завершению работы АЦП, может понадобится если нужна быстрая реакция на сигнал. Например ловим переход через 0. При оцифровке для измерения хватит и того что написал я.
хватит и того что написал я.
Если бы ещё и нормально работало - цены б не было.
И не пойдет мне так. Не получится запускать преобразование в одном прерывании таймера а результат читать в другом. У меня АЦП еще на двух входах в цикле loop() используется и еще на одном в обработчике внешнего прерывания детектора нуля.
Почитайте про автоматное программирование. И да, мультиплексор АЦП достаточно медленный и требовательный к выходному сопротивлению источника сигнала. Читайте даташит про это внимательней. Запускать АЦП на 1Мгц скорее всего не получится без танцев с бубном.
хватит и того что написал я.
Если бы ещё и нормально работало - цены б не было.
Ты пробовал?
Я копировал с своего работающего приложения.
Ха! Так у Вас с архитектурой проблемы, а не с работой АЦП. Хоть прерывания запрещены на время ввода из лоопа? Кстати запускать таймер после каждого переход нуля не следует, запустите вначале цикла измерения (у Вас оно вроде 1 сек.), остановите вконце, в течении измерения таймер без перезапуска.
Вообще собирать RMS-метры для 50 Гц сигнала на ресурсах голого М.К. если и есть смысл, то только в образовательных целях. Ибо наши друзья-китайцы давно завалили Али копеечными чипами, типа CS5460 - в одном корпусе два 24-бит сигма/дельта ацп с аппаратным расчётом RMS. Один на напругу, второй на ток. И всё это удовольствие буквально от 20 рублей/штука :) Для начинающих есть вроде и библа под ардуину. Собссно на этом чипе и собрано большинство китайских измерителей мощности.
Ты пробовал?
А чего тут пробовать? Если матчасть знать, то и так всё видно.
Открываем п. 24.4 даташита и читаем (если умеешь): «the successive approximation circuitry requires an input clock frequency between 50kHz and 200kHz to get maximum resolution». С твоим делителем 16 частота получается 1МГц, если тактовая 16, или 500кГц, если тактовая – 8. Так что работать-то оно работает, только измеряет с «lower resolution», то бишь цену на прошлогодний овёс.
Я копировал с своего работающего приложения.
Ну, это, согласись, замечание о качестве твоих приложений, а вовсе не о правильности данного кода.
копеечными чипами, типа CS5460 - в одном корпусе два 24-бит сигма/дельта ацп
Я вот смотрел на них, но ... не разбираюсь я ... рекомендуете попробовать?
Ты пробовал?
А чего тут пробовать? Если матчасть знать, то и так всё видно.
Открываем п. 24.4 даташита и читаем (если умеешь): «the successive approximation circuitry requires an input clock frequency between 50kHz and 200kHz to get maximum resolution». С твоим делителем 16 частота получается 1МГц, если тактовая 16, или 500кГц, если тактовая – 8. Так что работать-то оно работает, только измеряет с «lower resolution», то бишь цену на прошлогодний овёс.
Я копировал с своего работающего приложения.
Ну, это, согласись, замечание о качестве твоих приложений, а вовсе не о правильности данного кода.
Продолжаем читать дальше с места остановки "If a lower resolution than 10 bits is needed, the input clock frequency to the ADC can be higher than 200kHz to get a higher sample rate." А т.к. от ТС требований по точности измерений не поступало, а точность до тысячной выглядит вполне достаточной как для измерения тока димера, то все ОК. Хотя ты и сам согласен что код рабочий.
Ожидание флага завершения преобразования это стандартное решение используемое и в analogRead и в An_Read из CyberLib. И как-то эти функции глупостью никто не считает.
Прерывания таймера идут через 156мкс а преобразование в АЦП при тактировании 1МГц занимает около 40мкс вместе с этим ожиданием. На работу loop() с индикацией времени остается достаточно.
Можно и на прерываниях с АЦП попробовать, возможно, если запускать преобразование в конце обработчика, то завершаться оно будет уже в момент когда других прерываний нет и это сработает.
anslogRead используется в loop(), там допустимы любые циклы ожидания. В прерываниях циклы ожидания категорически не рекомендуются.
В любом обработчике рекомендуется делать мииимум операций, взвести нужные флаги и Асю остальную работу делать в основном цикле. Иначе легко попасть в ситуацию когда ожидание одно, результат другой
кстати если просто нужно делать замеры с равными интервалами на одном аналоговом входе то для этих целей есть free running, тогда для замеров таймер совсем не нужен
Продолжаем читать дальше с места остановки "If a lower resolution than 10 bits is needed, the input clock frequency to the ADC can be higher than 200kHz to get a higher sample rate." А т.к. от ТС требований по точности измерений не поступало, а точность до тысячной выглядит вполне достаточной как для измерения тока димера, то все ОК. Хотя ты и сам согласен что код рабочий.
точность до тысячной это как раз 10 бит, но для 10 бит нужна частота ниже 200кгц о чем в даташите написано явно. при частоте выше расчитывать можно не больше чем на 8 бит и как я понимаю нужно переключаться в режим 8 бит
Громотеи .. там совсем не этот ограничивающий параметр, а выходное сопротивление источника сигнала, которое .. не должно превышать 10кОм если Вы хотите нечто измерять точно. Дал же ссыль где проводил свои иксперименты с этими АЦП.
Я вот смотрел на них, но ... не разбираюсь я ... рекомендуете попробовать?
Можно и попробовать, для вас, как для любителя крупных микросхем они на платке продаются.
Правда мелкого обвяза всё равно много потребуется. В этом плане удобнее готовый модуль PZEM-0004T, упоминавшийся в какой-то соседней теме. Что-то около 400руб стоит.
Он уже на другом чипе, сливает данные через UART с гальваноразвязкой, и к нему напрямую (без МК) можно какой-то дисплей подключить.
Но с другой стороны если стремиться к упрощению, то следующим шагом будет "а вот всего за 100 руб дороже -и получим полностью готовый прибор в корпусе". Что полностью убивает вдохновение самодельщика :)
Спасибо.
Само собой! Понижение аккуратности измерений с 2 до 4,5 LSB (в идеальном случае) и в твоём приложении, и в твоём совете ТС, это, разумеется, было продуманное, осмысленное решение, исходящее из существа задачи – понизить точность ради экономии бесценных ресурсов! Никто и не сомневался! Никаких багов – сплошные фичи! Как «вустричные лягушки» у деда Щукаря.
так в этом у вас и проблема. вы в разных частях кода, в loop и в прерываниях обращаетесь к одному эксклюзивному ресурсу - АЦП
в даташите четко написано про то как надо переключать канал ADMUX, чтобы гарантированно знать какой результат мы считываем
я в таких случаях делаю просто - делаю программное сканирование. у микроконтроллеров STM такое сканирование заложено в железе, у AVR-ок в железе только сканирование одного канала (free running)
но можно сделать программное сканирование:
- выбираем первый канал измерений в ADMUX перед этим убеждаемся, что АЦП не занят преобразованием, и запускаем измерение
- в прерывании ловим завершение измерения, смотрим что там в ADMUX и сохраняем результат в нужную переменную
- тут же не выходя из прерывания ставим следующий канал в списке (я использую для выбора switch по кругу) и запускаем новое измерение
все, таким образом можно по кругу сканировать нужное число каналов, при необходимости можно заложить логику разной цикличности для разных каналов
пример моего кода сканирования, здесь он еще с оверсэмплингом который можно убрать если не нужен
#define ADC_DIV 64 ISR(ADC_vect) { var.adc.sum += ADCW; if(++var.adc.cnt == ADC_DIV) { switch(ADMUX & 0xf) { case TEMP_IN: var.adc.temp_in = calc_temperature(var.adc.sum); ADMUX = (ADMUX & 0xf0) | TEMP_OUT; break; case TEMP_OUT: var.adc.temp_out = calc_temperature(var.adc.sum); ADMUX = (ADMUX & 0xf0) | TEMP_WATER; break; case TEMP_WATER: var.adc.temp_water = calc_temperature(var.adc.sum); ADMUX = (ADMUX & 0xf0) | TEMP_IN; break; } var.adc.sum = 0; var.adc.cnt = 0; } ADCSRA |= (1 << ADSC); }Эксперименты показали, что из обработчика прерывания таймера у меня АЦП вообще не запускается. Пробовал делать по типу как советовал Logik. Оставил только два аналоговых входа - один для потенциометра задающего ток, второй для измерения тока. Потенциометр считывался в loop() после этого там же в ADMUX выставлялся нужный вход и выравнивание влево. В обработчике прерывания таймера сначала считывался регистр ADCH, потом выполнялся код диммера, а последней командой запускалось новое преобразование. Цикл loop крутится с задержкой в 1 сек, значит за время до следующего считывания потенциометра должно произойти 100Гц*64=6400 измерений тока. НО ОНИ НЕ ПРОИСХОДЯТ! В ADCH продолжает висеть выровненное влево, как и просили, значение от измерения со входа потенциометра. Все 6400 команд запуска измерений просто проигнорированы.
Может дело в том, как запускается таймер? Я использую код Timer1 выдернутый из CyberLib. Сами то прерывания по таймеру работают.
void (*func)(); volatile uint16_t dub_tcnt1; void StartTimer1(void (*isr)(), uint32_t set_us) { // cli(); TIMSK1 &= ~(1<<TOIE1);//запретить прерывания по таймеру1 func = *isr; //указатель на функцию TCCR1A = 0; //timer1 off TCCR1B = 0; //prescaler off (1<<CTC1)-3й бит //uint8_t oldSREG = SREG; //if(set_us < 6) set_us = 6; //min //if(set_us > 4194304) set_us = 4194303; //max if(set_us > 5 && set_us < 4096) { set_us = 65584 - (set_us << 4); DIV_0;} else if(set_us > 4095 && set_us < 32768) { set_us = 65542 - (set_us << 1); DIV_8; } else if(set_us > 32767 && set_us < 262144) { set_us = 65536 - (set_us >> 2); DIV_64;} else if(set_us > 262143 && set_us < 1048576) { set_us = 65536 - (set_us >> 4); DIV_256; } else if(set_us > 1048575 && set_us < 4194304) { set_us = 65536 - (set_us >> 6); DIV_1024;} else TCCR1B = 1; dub_tcnt1 = set_us; TCNT1 = 0;//dub_tcnt1; // выставляем начальное значение TCNT1 //OCR1A = dub_tcnt1; //TCNT1H=0;//обнуляем регистр TCNT1 //TCNT1L=0; TIMSK1 |= (1 << TOIE1); // разрешаем прерывание по переполнению таймера sei(); } void StopTimer1(void) { TIMSK1 &= ~(1<<TOIE1); //запретить прерывания по таймеру1 } void ResumeTimer1(void) { TIMSK1 |= (1<<TOIE1); //Продолжить отсчет, (разрешить прерывания по таймеру1) } void RestartTimer1(void) { TCNT1 = dub_tcnt1; TIMSK1 |= (1<<TOIE1); //разрешить прерывания по таймеру1 } ISR(TIMER1_OVF_vect) { TCNT1 = dub_tcnt1; (*func)(); }Рекомендую не смешивать обращение к adc в прерываниях и в цикле. Либо там либо там. Если используете в прерываниях то в одном месте
смешивание требует ясного понимания всех нюансов работы МК чтобы гарантированно исключить перекресное обращение. Без опыта с наскока этого не добиться
Перекрестное обращение может возникнуть один раз на 6400 запусков АЦП из прерывания. Но и оно не возникнет т.к. в цикле перед обращением к АЦП прерывания запрещаются а после разрешаются. И здесь как раз проявляется смысл ожидания флага завершения преобразования при запрещенных прерываниях.
Вопрос, по сути, свелся к такому - почему вообще не запускаются преобразования АЦП из обработчика прерываний таймера?
Почему игнорируется установка бита ADSC раз в ADCH остается неизменное старое значение?
Или сама команда установки этого бита в ADCSRA из прерывания таймера не проходит?
Может потому что надо таки дочитать даташит по этому вопросу? К примеру разрешать прерывания в обработчике после обработки АЦП .. не? ADIE это про что по-вашему? ;)
Вы знаете, коллеги пытаются Вам помочь, но Вы их не слышите. Ещё чёрти когда Вам написали
Alex_Sk, во первых по огрызку программы трудно судить.
И, тем не менее, Вашего кода мы так и не увидели. Мы увидели куски из библиотеки, какие-то Ваши глубокие соображения, но Вашего кода нет.
Давайте так, поскольку Вашего кода нет, я не буду пытаться угадать в чём там косяк, а просто расскажу Вам как бы я делал эту задачу (шаг за шагом) и заодно развею некоторые Ваши страхи и заблуждения, типа такого.
Первое, что Вы должны понять: никогда не делайте программу в которой более, чем одно не до конца ясное для Вас место. Делайте шаг за шагом, крохотные программки, так, чтобы на каждом шаге Вы были уверены, что у Вас всё предыдущее работает, а не работает только последний шаг. Так Вам будет неизмеримо легче отлаживаться.
Вот, смотрите, я программирую с 1979 года, прожил в программировании целую жизнь, тем не менее, я бы никогда не стал, как Вы, сразу писать программу в которой присутствует незнакомая (или подзабытая) библиотека и незнакомый ADC (или подзабытый) – никогда, т.к. для меня это сложно! А Вам новичкам – всё просто, фигачите по тыщще строк, а потом не знаете за что хвататься.
Итак, на Вашем месте, я бы делал такие шаги.
Шаг №1. Проверить правильно ли я работаю с указателем на функцию. Пишем крохотный код
void (*func)(void); void kaka(void) { Serial.println("kaka called!"); } void setup(void) { Serial.begin(57600); Serial.println("Fun begins"); func = kaka; func(); } void loop(void) {}Запускаем. Видим в мониторе текст "kaka called!". Т.е. мы убедились, что с указателем у нас порядок.
Шаг №2. Проверить правильно ли мы используем сайберлибовский таймер. Дополняем наш код.
#include <CyberLib.h> void kaka(void) { Serial.println("kaka called!"); } void setup(void) { Serial.begin(57600); Serial.println("Fun begins"); StartTimer1(kaka, 1000000); } void loop(void) {}Запускаем и видим, что текст "kaka called!" исправно появляется раз в секунду. Значит, таймер работает.
Шаг №3. Проверить вызывается ли ADC из таймерного прерывания. Подключаем к аналоговому пину №0 потенциометр и дополняем наш код.
#include <CyberLib.h> void kaka(void) { Serial.println(analogRead(0)); } void setup(void) { Serial.begin(57600); Serial.println("Fun begins"); StartTimer1(kaka, 1000000); } void loop(void) {}Запускаем, крутим потенциометр и убеждаемся, что показания в мониторе порта исправно меняются, значит ADC из таймерного прерывания вызывается вполне успешно.
Далее, я не знаю Вашей задачи, но продолжайте идти такими же крохотными шагами, и переходите к целиковой программе только тогда, когда у Вас не останется сомнений, что вся низкоуровневая подложка работает нормально. Тогда на каждом шаге, если что-то не так, у Вас есть тыл, в котором Вы уверены. И Вы всегда понимаете, где искать беду, а не так как сейчас: "то-ли таймер глючит, то-ли ADC, то-ли всё вместе".
Привыкайте работать с маленькими кодами и жизнь сразу станет легче.
Вы поняли о чём я?
Может потому что надо таки дочитать даташит по этому вопросу? К примеру разрешать прерывания в обработчике после обработки АЦП .. не? ADIE это про что по-вашему? ;)
Это первое что я пробовал. И до обращения к АЦП в обработчике, и после. Ничего не дало.
Вы знаете, коллеги пытаются Вам помочь, но Вы их не слышите.
Коллеги тоже не хотят меня услышать. Я ведь говорил, что для контроля делал в обработчиках прерываний XOR на свободных цифровых выходах и меандры на этих выходах с соответствующим периодом генерируются. Т.е. первые два пункта из вашей программы выполняются. Но вы этого почему то не хотите видеть. Не выполняется только третий пункт. Причем если в переменную в которой возвращается из обработчика значение АЦП принудительно записать там же в обработчике какое то значение, то это значение в основном цикле считывается из этой переменной без проблем. Т.е. работает все кроме АЦП в обработчике прерываний.
Я начинал программировать примерно в том же году что и вы. Только занимался по большей части звуком и изображением. Отладке меня учить не надо. С МК балуюсь всего несколько лет и от случая к случаю. Здесь возник вопрос именно специфичный для МК а меня начинают учить программированию....
А в моём маленьком примере - отлично работает. АЦП в обработчике прерываний - без проблем. Вы этого не заметили? А если заметили, то ни на какие мысли не навело?
А в моём маленьком примере - отлично работает. АЦП в обработчике прерываний - без проблем. Вы этого не заметили? А если заметили, то ни на какие мысли не навело?
Заметить пока не могу, нет свободной платы под рукой. Та мини про с которой воюю запаяна в устройство и заливать в нее не предназначенный для устройства код может выйти боком. Завтра раздобуду еще платку и буду на ней экспериментировать.
Код таймера использовали тот что я привел или подключали библиотеку? Если последнее, то какой версии?
А мысли такие, ваш код не совсем соответствует тому что не работает. Вот поставьте таймер на 156 мкс и накапливайте в прерывании значения АЦП и число отсчетов. А в основном цикле раз в секунду делите одно на другое и результат в сериал. И на какой плате проверяли?
Мне-то это зачем? Это Вы делайте. Вы считали, что АЦП из прерывания не работает - я Вам показал, что работает. Технику работы "мелкими шагами" показал (хотя Вы и не нуждаетесь), а уж свои проблемы решайте сами.
Не знаю, что у Вас там за плата и какая у неё частота. При частоте 16 МГц, при полной разрядности АЦП, Ваша задача трудно разрешима, а если ещё и в loop надо АЦП независимо дёргать, то неразрешима вовсе.
Преобразование АЦП (кроме первого) требует 104мкс. Если Вы хотите проделывать это каждые 156 мкс, то у Вас на каждом цикле будет оставться только 54мкс на всё остальное. Если Вам за эти 54 мкс нужно ещё успеть из loop за АЦП дёрнуть, то как Вы себе это видите? Как впихнуть 104 в 54? А если из loop не надо, то наверное как-то можно уложиться, хотя программировать надо предельно аккуратно.
Хотя, кому я это рассказываю - при преобразовании видео и звука, там сплощь и рядом подобные проблемы, Вы на них, видимо, не одну собаку съели.
Перекрестное обращение может возникнуть один раз на 6400 запусков АЦП из прерывания. Но и оно не возникнет т.к. в цикле перед обращением к АЦП прерывания запрещаются а после разрешаются. И здесь как раз проявляется смысл ожидания флага завершения преобразования при запрещенных прерываниях.
Вопрос, по сути, свелся к такому - почему вообще не запускаются преобразования АЦП из обработчика прерываний таймера?
Почему игнорируется установка бита ADSC раз в ADCH остается неизменное старое значение?
Или сама команда установки этого бита в ADCSRA из прерывания таймера не проходит?
Вот! Вы подтвердили мое утверждение. Без ясного понимания всех нюансов работы МК обеспечить перекресное обращение не получится) вот у вас тоже не получилось
не работает потому, что вы не понимаете как оно работает. Можете ещё пятьсот сообщений написать в надежде, что заработает именно так как вы думаете, но это не поможет.
рекомендаций здесь уже достаточно прозвучало, попробуйте хоть одну
Преобразование АЦП (кроме первого) требует 104мкс.
Нефиг! Тут один напыщенный неуч выше ставил делитель 16, так у него за 13 мкс всё срабатывало :)
Не понятно про кого Вы тут пишете, но 592000 замеров в секунду с одного канала - получал на обычных НАНО и МЕГА при их 16Мгц. Это около 1,69 микросекунды. Я что-то делал не так? :)
На делителе 16 (тактовая АЦП=1Мгц) 13.5 тактов = 13,5мксек на замер .. что опять "не так"? Можно даже несколько каналов снимать вполне уверенно с точностью в 8 бит. .. там даже и 9 вылезает вполне нормально... ни понял реплики про "неуча" - откровенно.
Я что-то делал не так? :)
Видимо, да. Про Мега не знаю, а на Нано - точно что-то делал не так.
Что именно? ссылку на тему - привел раньше. Что там "не так"? :)
Вполне надежно все работало..
В той теме, пост #20 ежели вчё. Это последний достигнутый результат.
Не знаю, что у Вас там за плата и какая у неё частота. При частоте 16 МГц, при полной разрядности АЦП, Ваша задача трудно разрешима, а если ещё и в loop надо АЦП независимо дёргать, то неразрешима вовсе.
Преобразование АЦП (кроме первого) требует 104мкс. Если Вы хотите проделывать это каждые 156 мкс, то у Вас на каждом цикле будет оставться только 54мкс на всё остальное. Если Вам за эти 54 мкс нужно ещё успеть из loop за АЦП дёрнуть, то как Вы себе это видите? Как впихнуть 104 в 54? А если из loop не надо, то наверное как-то можно уложиться, хотя программировать надо предельно аккуратно.
На счет использованных вами кода таймера или версии библиотеки так и не ответили что наводит на мысли...
И если бы посмотрели хотя бы на тот код обработчика прерываний таймера что я в самом начале показал, то увидели бы делитель 16 и 8-бит на выходе АЦП что дает 16-18 мкс на преобразование. При тиках таймера 156 мкс еще остается куча времени на все остальное. Платка Мини Про 5В 16МГц ресурсов для задачи имеет достаточно. Я делал схемы где использовался делитель 8 и непрерывный режим АЦП - вот там было несколько напряжно укладывать действия между прерываниями АЦП, а здесь такого напряга нет.
Arhat109-2
Открываем даташит (328-ой) и читаем
раздел 24.4: "the successive approximation circuitry requires an input clock frequency between 50kHz and 200kHz to get maximum resolution"
Таблица 24-1: "Conversion Time: (Cycles) - 13"
раздел 24.9.1: делители частоты бывают 2, 4, 8, 16, 32, 64, 128
теперь считаем, для тактовой 16МГц, при делителе 128, частота будет 125кГц, а при 64 - уже 250 кГц. Стало быть, чтобы удержаться в указанном в п. 24.4 диапазоне чатот, делитель должен быть 128.
Считаем дальше, при тактовой 16МГц, один такт - 1/16 мкс., умножаем на делитель 128 и на количество "Cycles" 13, получаем 128*13/16 = 104мкс.
Таким образом. 104 мкс - это время работы АЦП при максимальном разрешении.
Теперь предположим, что мы готовы пожертововать разрешением и увеличить частоту (выше, чем указано в п. 24.4). Опять же открываем даташит и читаем (Table 29-15) - максимальная частота часов ADC - 1000кГц. Значит, самый маленький делитель, который мы в принципе можем использовать - 16. При этом мы потеряем в точности, но ADC будет работать. Время конверссии при этом получается 104/8 = 13 мкс.
Быстрее - никак.
Как у Вас получилось 1,69 мкс, я не знаю. Скорее всего Вы либо что-то подзабыли и перепутали, либо ошиблись при измерениях - подсчётах.
P.S. Если Вы обращались ко мне, то я ничего ни про каких неучей не писал.
На счет использованных вами кода таймера или версии библиотеки так и не ответили что наводит на мысли...
Не знаю, на какие мысли это Вас наводит. Ну, не помню я откуда я скачал этот сайберлиб. Вы мой код запускали? Что, не работает? А если работает, так какие Вам ещё мысли нужны?
если бы посмотрели хотя бы на тот код
Я не смотрю на куски кодов. Полного никто не давал.
делитель 16 ... что дает 16-18 мкс на преобразование
13 на самом деле
Я делал схемы где использовался делитель 8
Это нештатный режим. По даташиту так делать нельзя. Вполне возможно, что и работает, но говорить о надёжности не приходится.