Обращение к АЦП в обработчике прерывания

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

Собственно вопрос таков - кто-нибудь писал работающие скетчи в которых изменение напряжения происходит внутри функции вызываемой в прерывании?

Пытаюсь сделать измерение среднеквадратичного тока в тиристорном диммере. Само построение диммера стандартное. 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 на свободные цифровые выходы - меандры генерируются. Вот только ток не измеряется.

sadman41
Offline
Зарегистрирован: 19.10.2016

Да, была у меня по первости дурная идея мерять VCC контроллера в прерывании таймера. Меряло что-то там, основную программу тормозило, бипер на ШИМе заикался ))

Technolog
Offline
Зарегистрирован: 19.11.2014

Инициализируйте переменные которые изменяются в прерывании как volatile

volatile byte Var1;

volatile float Var2;  и т.д.

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

Technolog пишет:

Инициализируйте переменные которые изменяются в прерывании как volatile

Именно так они и инициализируются. Это была бы слишком детская ошибка :)

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Alex_Sk, во первых по огрызку программы трудно судить. Где объявления типов данных?  И  что за волшебная чушь в 12 строке?

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

dimax пишет:

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 вместо результата преобразования.

axill
Offline
Зарегистрирован: 05.09.2011

Нужно использовать два обработчика

в обработчике таймера надо запускать измерение АЦП

а считывать готовые данные в прерывании самого АЦП

ваша глупость в том, что внутри обработчика написан цикл ожидания

это как раз то что нужно избегать

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

Ожидание флага завершения преобразования это стандартное решение используемое и в analogRead и в An_Read из CyberLib. И как-то эти функции глупостью никто не считает.

Прерывания таймера идут через 156мкс а преобразование в АЦП при тактировании 1МГц занимает около 40мкс вместе с этим ожиданием. На работу loop() с индикацией времени остается достаточно.

Можно и на прерываниях с АЦП попробовать, возможно, если запускать преобразование в конце обработчика, то завершаться оно будет уже в момент когда других прерываний нет и это сработает.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

1. Измерять из под прерывания можно, если Вы понимаете даташит верно. В этом коде это не так.

2. Запускать измерение АЦП изнутри прерывания тоже в общем-то можно и принимать результат по прерыванию АЦП тоже можно, но надо понимать что когда и где должно правильно завершаться.

3. посмотрите эту тему http://arduino.ru/forum/proekty/samodelnaya-mega2560-128a-s-pamyatyu-512kb и все темки в проектах по ключевому слову "осцилограф". Там можно почерпнуть много полезного. Ну и перечитайте даташит и оцените что не так в вашей программе.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

 Arhat109-2,   Вы, случайно, на EUROBOT завтра не едете?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Alex_Sk пишет:

С типами данных все в порядке.

Ну конечно, у 99 % начинающих с типами даных вечные косяки, но у вас -то всё хорошо, хотя почему-то не работает, не наводит на мысли?

 

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Нет. Мы игрушками в Лего не занимаемся. Какой смысл имитировать "наполнение водонапорной башни" тренируясь забрасывать мячики в коробку? Есть школьный проект "гидропоника", где есть нормальное место нормальному применению Ардуино с вполне реальной системой полива и прочего "робот может использовать природные ресурсы" .. выросли мы уже из игр в лего и давненько. Пусть "взрослые развлекаются". :)

P.S.  В целом, за 2 года наблюдаю бурный рост имитации деятельности в робототехнике с явно отсутствующим результатом. Чем дальше - тем кучерявей.

P.P.S. Зачем в этой теме спрашивать, ежели можно кинуть мыло на общедоступную почту? :)

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Arhat109-2 пишет:

P.P.S. Зачем в этой теме спрашивать, ежели можно кинуть мыло на общедоступную почту? :)

Ну я прошу прощения, модераторы, думаю, потрут.  

 

Logik
Offline
Зарегистрирован: 05.08.2014

Правильно Вам axill написал. Никаких ожиданий в прерывании!

И то что analogRead кривой - давно известно.

Пишем в сетапе для АЦП типа так.

 DIDR0 =  _BV(ADC4D); // отключаем цифровые входы
 ADMUX = _BV(REFS0)  | _BV(ADLAR) | _BV(MUX2);// измеряем на ADC4, используем опорное напр.= 5В
 //ADMUX = _BV(REFS0) | _BV(REFS1) | _BV(ADLAR) | _BV(MUX2);// измеряем на ADC4, используем внутреннее опорное напр.= 1.1В
 ADCSRA = 0x84; // включаем АЦП, разрешаем прерывания, делитель = 16
 ADCSRB = 0x40; 

В прерывании таймера первым делом считываете результат с АЦП, затем запускаете преобразование.

w=ADCH;
ADCSRA|=_BV(ADSC);// Запускаем преобразование 

Т.е. стартуем АЦП для получения результата при следующем прерывании таймера.

Второе прерывание, по завершению работы АЦП, может понадобится если нужна быстрая реакция на сигнал. Например ловим переход через 0. При оцифровке для измерения хватит и того что написал я.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Logik пишет:

хватит и того что написал я.

Если бы ещё и нормально работало - цены б не было.

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

И не пойдет мне так. Не получится запускать преобразование в одном прерывании таймера а результат читать в другом. У меня АЦП еще на двух входах в цикле loop() используется и еще на одном в обработчике внешнего прерывания детектора нуля.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Почитайте про автоматное программирование. И да, мультиплексор АЦП достаточно медленный и требовательный к выходному сопротивлению источника сигнала. Читайте даташит про это внимательней. Запускать АЦП на 1Мгц скорее всего не получится без танцев с бубном.

Logik
Offline
Зарегистрирован: 05.08.2014

Ворота пишет:

Logik пишет:

хватит и того что написал я.

Если бы ещё и нормально работало - цены б не было.

Ты пробовал?

Я копировал с своего работающего приложения.

Logik
Offline
Зарегистрирован: 05.08.2014

Alex_Sk пишет:
И не пойдет мне так. Не получится запускать преобразование в одном прерывании таймера а результат читать в другом. У меня АЦП еще на двух входах в цикле loop() используется и еще на одном в обработчике внешнего прерывания детектора нуля.

Ха! Так у Вас с архитектурой проблемы, а не с работой АЦП. Хоть прерывания запрещены на время ввода из лоопа? Кстати запускать таймер после каждого переход нуля не следует, запустите вначале цикла измерения (у Вас оно вроде 1 сек.), остановите вконце, в течении измерения таймер без перезапуска.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Вообще собирать RMS-метры для 50 Гц сигнала на ресурсах голого М.К. если и есть смысл, то только в образовательных целях. Ибо наши друзья-китайцы давно завалили Али копеечными чипами, типа CS5460  - в одном корпусе два 24-бит  сигма/дельта ацп с аппаратным расчётом RMS. Один на напругу, второй на ток.   И всё это  удовольствие буквально от 20 рублей/штука  :) Для начинающих есть вроде и библа под ардуину.  Собссно на этом чипе и собрано большинство китайских измерителей мощности.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Logik пишет:

Ты пробовал?

А чего тут пробовать? Если матчасть знать, то и так всё видно.

Открываем п. 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», то бишь цену на прошлогодний овёс.

Logik пишет:

Я копировал с своего работающего приложения.

Ну, это, согласись, замечание о качестве твоих приложений, а вовсе не о правильности данного кода.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

dimax пишет:

копеечными чипами, типа CS5460  - в одном корпусе два 24-бит  сигма/дельта ацп 

Я вот смотрел на них, но ... не разбираюсь я  ... рекомендуете попробовать?

Logik
Offline
Зарегистрирован: 05.08.2014

Ворота пишет:

Logik пишет:

Ты пробовал?

А чего тут пробовать? Если матчасть знать, то и так всё видно.

Открываем п. 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», то бишь цену на прошлогодний овёс.

Logik пишет:

Я копировал с своего работающего приложения.

Ну, это, согласись, замечание о качестве твоих приложений, а вовсе не о правильности данного кода.

Продолжаем читать дальше с места остановки "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." А т.к. от ТС требований по точности измерений не поступало, а точность до тысячной выглядит вполне достаточной как для измерения тока димера, то все ОК. Хотя ты и сам согласен что код рабочий.

axill
Offline
Зарегистрирован: 05.09.2011

Alex_Sk пишет:

Ожидание флага завершения преобразования это стандартное решение используемое и в analogRead и в An_Read из CyberLib. И как-то эти функции глупостью никто не считает.

Прерывания таймера идут через 156мкс а преобразование в АЦП при тактировании 1МГц занимает около 40мкс вместе с этим ожиданием. На работу loop() с индикацией времени остается достаточно.

Можно и на прерываниях с АЦП попробовать, возможно, если запускать преобразование в конце обработчика, то завершаться оно будет уже в момент когда других прерываний нет и это сработает.

anslogRead используется в loop(), там допустимы любые циклы ожидания. В прерываниях циклы ожидания категорически не рекомендуются. 

В любом обработчике рекомендуется делать мииимум операций, взвести нужные флаги и Асю остальную работу делать в основном цикле. Иначе легко попасть в ситуацию когда ожидание одно, результат другой

кстати если просто нужно делать замеры с равными интервалами на одном аналоговом входе то для этих целей есть free running, тогда для замеров таймер совсем не нужен

axill
Offline
Зарегистрирован: 05.09.2011

Logik пишет:

Продолжаем читать дальше с места остановки "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 бит

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Громотеи .. там совсем не этот ограничивающий параметр, а выходное сопротивление источника сигнала, которое .. не должно превышать 10кОм если Вы хотите нечто измерять точно. Дал же ссыль где проводил свои иксперименты с этими АЦП.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

ЕвгенийП пишет:

Я вот смотрел на них, но ... не разбираюсь я  ... рекомендуете попробовать?

Можно и попробовать, для вас, как для любителя крупных микросхем они на платке продаются.

Правда мелкого обвяза всё равно много потребуется. В этом плане удобнее готовый модуль PZEM-0004T, упоминавшийся в какой-то соседней теме. Что-то около 400руб стоит.

Он уже на другом чипе, сливает данные через UART с гальваноразвязкой, и к нему напрямую (без МК) можно какой-то дисплей подключить.

Но с другой стороны если стремиться к упрощению, то следующим шагом будет "а вот всего за 100 руб дороже -и получим полностью готовый прибор в корпусе".   Что полностью убивает вдохновение самодельщика :)

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Спасибо.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Logik пишет:
т.к. от ТС требований по точности измерений не поступало

Само собой! Понижение аккуратности измерений с 2 до 4,5 LSB (в идеальном случае) и в твоём приложении, и в твоём совете ТС, это, разумеется, было продуманное, осмысленное решение, исходящее из существа задачи – понизить точность ради экономии бесценных ресурсов! Никто и не сомневался! Никаких багов – сплошные фичи! Как «вустричные лягушки» у деда Щукаря.

axill
Offline
Зарегистрирован: 05.09.2011

Alex_Sk пишет:
И не пойдет мне так. Не получится запускать преобразование в одном прерывании таймера а результат читать в другом. У меня АЦП еще на двух входах в цикле loop() используется и еще на одном в обработчике внешнего прерывания детектора нуля.

так в этом у вас и проблема. вы в разных частях кода, в loop и в прерываниях обращаетесь к одному эксклюзивному ресурсу - АЦП

в даташите четко написано про то как надо переключать канал ADMUX, чтобы гарантированно знать какой результат мы считываем

я в таких случаях делаю просто - делаю программное сканирование. у микроконтроллеров STM такое сканирование заложено в железе, у AVR-ок в железе только сканирование одного канала (free running)

но можно сделать программное сканирование:

- выбираем первый канал измерений в ADMUX перед этим убеждаемся, что АЦП не занят преобразованием, и запускаем измерение

- в прерывании ловим завершение измерения, смотрим что там в ADMUX и сохраняем результат в нужную переменную

- тут же не выходя из прерывания ставим следующий канал в списке (я использую для выбора switch по кругу) и запускаем новое измерение

все, таким образом можно по кругу сканировать нужное число каналов, при необходимости можно заложить логику разной цикличности для разных каналов

axill
Offline
Зарегистрирован: 05.09.2011

пример моего кода сканирования, здесь он еще с оверсэмплингом который можно убрать если не нужен

#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);
}

 

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

Эксперименты показали, что из обработчика прерывания таймера у меня АЦП вообще не запускается. Пробовал делать по типу как советовал 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)();
}

 

axill
Offline
Зарегистрирован: 05.09.2011

Рекомендую не смешивать обращение к adc в прерываниях и в цикле. Либо там либо там. Если используете в прерываниях то в одном месте

смешивание требует ясного понимания всех нюансов работы МК чтобы гарантированно исключить перекресное обращение. Без опыта с наскока этого не добиться 

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

Перекрестное обращение может возникнуть один раз на 6400 запусков АЦП из прерывания. Но и оно не возникнет т.к. в цикле перед обращением к АЦП прерывания запрещаются а после разрешаются. И здесь как раз проявляется смысл ожидания флага завершения преобразования при запрещенных прерываниях.

Вопрос, по сути, свелся к такому - почему вообще не запускаются преобразования АЦП из обработчика прерываний таймера?

Почему игнорируется установка бита ADSC раз в ADCH остается неизменное старое значение?

Или сама команда установки этого бита в ADCSRA из прерывания таймера не проходит?

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Может потому что надо таки дочитать даташит по этому вопросу? К примеру разрешать прерывания в обработчике после обработки АЦП .. не? ADIE это про что по-вашему? ;)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вы знаете, коллеги пытаются Вам помочь, но Вы их не слышите. Ещё чёрти когда Вам написали

dimax пишет:

Alex_Sk, во первых по огрызку программы трудно судить. 

И, тем не менее, Вашего кода мы так и не увидели. Мы увидели куски из библиотеки, какие-то Ваши глубокие соображения, но Вашего кода нет.

Давайте так, поскольку Вашего кода нет, я не буду пытаться угадать в чём там косяк, а просто расскажу Вам как бы я делал эту задачу (шаг за шагом) и заодно развею некоторые Ваши страхи и заблуждения, типа такого.

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, то-ли всё вместе".

Привыкайте работать с маленькими кодами и жизнь сразу станет легче.

Вы поняли о чём я?

 

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

Arhat109-2 пишет:

Может потому что надо таки дочитать даташит по этому вопросу? К примеру разрешать прерывания в обработчике после обработки АЦП .. не? ADIE это про что по-вашему? ;)

Это первое что я пробовал. И до обращения к АЦП в обработчике, и после. Ничего не дало.

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

ЕвгенийП пишет:

Вы знаете, коллеги пытаются Вам помочь, но Вы их не слышите.

Коллеги тоже не хотят меня услышать. Я ведь говорил, что для контроля делал в обработчиках прерываний XOR на свободных цифровых выходах и меандры на этих выходах с соответствующим периодом генерируются. Т.е. первые два пункта из вашей программы выполняются. Но вы этого почему то не хотите видеть. Не выполняется только третий пункт. Причем если в переменную в которой возвращается из обработчика значение АЦП принудительно записать там же в обработчике какое то значение, то это значение в основном цикле считывается из этой переменной без проблем. Т.е. работает все кроме АЦП в обработчике прерываний.

Я начинал программировать примерно в том же году что и вы. Только занимался по большей части звуком и изображением. Отладке меня учить не надо. С МК балуюсь всего несколько лет и от случая к случаю. Здесь возник вопрос именно специфичный для МК а меня начинают учить программированию....

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Alex_Sk пишет:
работает все кроме АЦП в обработчике прерываний.

А в моём маленьком примере - отлично работает. АЦП в обработчике прерываний - без проблем. Вы этого не заметили? А если заметили, то ни на какие мысли не навело?

Alex_Sk пишет:
Отладке меня учить не надо.
ну, тогда извините.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Alex_Sk пишет:
Отладке меня учить не надо. С МК балуюсь всего несколько лет и от случая к случаю. 

Решили проверить уровень интелекта у человека и обезьяны. Подвесили
вместо люстры банан и завели в комнату обезьяну, обезьяна увидела банан
и начала прыгать безрезультатно пытаясь достать его тут раздается голос
"думай!". Обезьяна остановилась, подумала, огляделась увидела в углу
стол, придвинула стол, залезла на него и достала банан. Следом в ту же
комнату завели мужика, а вместо банана подвесили бутылку водки. Мужик
попрыгал, попрыгал бутылку достать не смог. Тут опять раздается голос
"думай!". Мужик остановился, подумал, огляделся, почесал репу и молвил
"Че тут думать?Думать я умею,а тут прыгать надо, а не думать!"

 

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

ЕвгенийП пишет:

Alex_Sk пишет:
работает все кроме АЦП в обработчике прерываний.

А в моём маленьком примере - отлично работает. АЦП в обработчике прерываний - без проблем. Вы этого не заметили? А если заметили, то ни на какие мысли не навело?

Заметить пока не могу, нет свободной платы под рукой. Та мини про с которой воюю запаяна в устройство и заливать в нее не предназначенный для устройства код может выйти боком. Завтра раздобуду еще платку и буду на ней экспериментировать.

Код таймера использовали тот что я привел или подключали библиотеку? Если последнее, то какой версии?

А мысли такие, ваш код не совсем соответствует тому что не работает. Вот поставьте таймер на 156 мкс и накапливайте в прерывании значения АЦП и число отсчетов. А в основном цикле раз в секунду делите одно на другое и результат в сериал. И на какой плате проверяли?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Alex_Sk пишет:
Вот поставьте таймер на 156 мкс и накапливайте в прерывании значения АЦП и число отсчетов. А в основном цикле раз в секунду делите одно на другое и результат в сериал.

Мне-то это зачем? Это Вы делайте. Вы считали, что АЦП из прерывания не работает -  я Вам показал, что работает. Технику работы "мелкими шагами" показал (хотя Вы и не нуждаетесь), а уж свои проблемы решайте сами.

Не знаю, что у Вас там за плата и какая у неё частота. При частоте 16 МГц, при полной разрядности АЦП, Ваша задача трудно разрешима, а если ещё и в loop надо АЦП независимо дёргать, то неразрешима вовсе.

Преобразование АЦП (кроме первого) требует 104мкс. Если Вы хотите проделывать это каждые 156 мкс, то у Вас на каждом цикле будет оставться только 54мкс на всё остальное. Если Вам за эти 54 мкс нужно ещё успеть из loop за АЦП дёрнуть, то как Вы себе это видите? Как впихнуть 104 в 54? А если из loop не надо, то наверное как-то можно уложиться, хотя программировать надо предельно аккуратно.

Хотя, кому я это рассказываю - при преобразовании видео и звука, там сплощь и рядом подобные проблемы, Вы на них, видимо, не одну собаку съели.

axill
Offline
Зарегистрирован: 05.09.2011

Alex_Sk пишет:

Перекрестное обращение может возникнуть один раз на 6400 запусков АЦП из прерывания. Но и оно не возникнет т.к. в цикле перед обращением к АЦП прерывания запрещаются а после разрешаются. И здесь как раз проявляется смысл ожидания флага завершения преобразования при запрещенных прерываниях.

Вопрос, по сути, свелся к такому - почему вообще не запускаются преобразования АЦП из обработчика прерываний таймера?

Почему игнорируется установка бита ADSC раз в ADCH остается неизменное старое значение?

Или сама команда установки этого бита в ADCSRA из прерывания таймера не проходит?

Вот! Вы подтвердили мое утверждение. Без ясного понимания всех нюансов работы МК обеспечить перекресное обращение не получится) вот у вас тоже не получилось

не работает потому, что вы не понимаете как оно работает. Можете ещё пятьсот сообщений написать в надежде, что заработает именно так как вы думаете, но это не поможет.

рекомендаций здесь уже достаточно прозвучало, попробуйте хоть одну

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

ЕвгенийП пишет:

Преобразование АЦП (кроме первого) требует 104мкс.

Нефиг! Тут один напыщенный неуч выше ставил делитель 16, так у него за 13 мкс всё срабатывало :)

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Не понятно про кого Вы тут пишете, но 592000 замеров в секунду с одного канала - получал на обычных НАНО и МЕГА при их 16Мгц. Это около 1,69 микросекунды. Я что-то делал не так? :)

На делителе 16 (тактовая АЦП=1Мгц) 13.5 тактов = 13,5мксек на замер .. что опять "не так"? Можно даже несколько каналов снимать вполне уверенно с точностью в 8 бит. .. там даже и 9 вылезает вполне нормально... ни понял реплики про "неуча" - откровенно.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Arhat109-2 пишет:

Я что-то делал не так? :)

Видимо, да. Про Мега не знаю, а на Нано - точно что-то делал не так.

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Что именно? ссылку на тему - привел раньше. Что там "не так"? :)

Вполне надежно все работало..

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

В той теме, пост #20 ежели вчё. Это последний достигнутый результат.

Alex_Sk
Offline
Зарегистрирован: 06.01.2015

ЕвгенийП пишет:

Не знаю, что у Вас там за плата и какая у неё частота. При частоте 16 МГц, при полной разрядности АЦП, Ваша задача трудно разрешима, а если ещё и в loop надо АЦП независимо дёргать, то неразрешима вовсе.

Преобразование АЦП (кроме первого) требует 104мкс. Если Вы хотите проделывать это каждые 156 мкс, то у Вас на каждом цикле будет оставться только 54мкс на всё остальное. Если Вам за эти 54 мкс нужно ещё успеть из loop за АЦП дёрнуть, то как Вы себе это видите? Как впихнуть 104 в 54? А если из loop не надо, то наверное как-то можно уложиться, хотя программировать надо предельно аккуратно.

На счет использованных вами кода таймера или версии библиотеки так и не ответили что наводит на мысли...

И если бы посмотрели хотя бы на тот код обработчика прерываний таймера что я в самом начале показал, то увидели бы делитель 16 и 8-бит на выходе АЦП что дает 16-18 мкс на преобразование. При тиках таймера 156 мкс еще остается куча времени на все остальное. Платка Мини Про 5В 16МГц ресурсов для задачи имеет достаточно. Я делал схемы где использовался делитель 8 и непрерывный режим АЦП - вот там было несколько напряжно укладывать действия между прерываниями АЦП, а здесь такого напряга нет.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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. Если Вы обращались ко мне, то я ничего ни про каких неучей не писал.

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Alex_Sk пишет:

На счет использованных вами кода таймера или версии библиотеки так и не ответили что наводит на мысли...

Не знаю, на какие мысли это Вас наводит. Ну, не помню я откуда я скачал этот сайберлиб. Вы мой код запускали? Что, не работает? А если работает, так какие Вам ещё мысли нужны?

Alex_Sk пишет:

если бы посмотрели хотя бы на тот код 

Я не смотрю на куски кодов. Полного никто не давал.

Alex_Sk пишет:

делитель 16 ... что дает 16-18 мкс на преобразование

13 на самом деле

Alex_Sk пишет:

 Я делал схемы где использовался делитель 8 

Это нештатный режим. По даташиту так делать нельзя. Вполне возможно, что и работает, но говорить о надёжности не приходится.