Захват ввода с включенным спящим режимом на плате ATM32u4 работает только при каждом втором чтении в спящем режиме.

Вкратце: у меня есть код захвата ввода и код энергосбережения, объединенные в одну программу. Оба адаптированы из примеров Ника Гэммона, которые отлично работают независимо друг от друга. Когда я объединил их в один код, он считывает данные только через раз, когда одиночный импульс отправляется для захвата каждые 20 секунд. Плата спит в течение одного цикла простоя между 20-секундными показаниями, затем просыпается, но пропускает первое чтение, а затем остается в активном состоянии в течение следующего цикла простоя и правильно считывает импульс на 40-секундной отметке для второго показания. И повторяет. Ссылки на эти два кода Ника Гэммона перечислены в программе.

Подробности: Adafruit BlueFruit Feather 32u4 с nrf51 в среде разработки Arduino 1.8.10. Используя пример Ника Гэммона для захвата ввода, внешний входящий импульс длительностью около 800 мкс (микросекунд) измеряется (ICP3) захватом ввода № 3 (переписанным из ICP1) на контакте D13 (PC7) (светодиод удален). Работает хорошо.

Затем у меня была отдельная программа, которая переводила плату в режим глубокого сна и с помощью INT0 (D3) пробуждала ее внешним сигналом прерывания. Поскольку я не смог понять, как использовать входной захват в качестве прерывания ISR для пробуждения платы, я подал тот же внешний импульсный сигнал на контакт D3 (INT0). Таким образом, когда импульс достигает D3 (INT0) и D13 (ICP3) одновременно, и D3 пробуждает плату через INT0, тогда ICP3 может захватить импульс и измерить его. Затем сразу после этого или через несколько секунд можно снова заснуть, пока не придет следующий импульс на D13 (ICP3). Я разнес внешние одиночные импульсы 800 мкс с интервалом в 20 секунд, чтобы облегчить диагностику. Если я удалю строку, переводящую плату в спящий режим "sleep_cpu();" Каждые 20 секунд я получаю хорошее измерение импульса одиночного сигнала. Поскольку я не использую функцию отсоединения и повторного подключения USB, после того, как плата переходит в спящий режим, она отключается, но я отслеживаю энергопотребление с помощью мА-метра Fluke и могу видеть, когда он спит и когда просыпается, а также отслеживаю внешний импульс поступает на D13 (ICP3) и D3 (INT0) на оскопе.

Примечания. Захват ввода указан в списке прерываний, но имеет гораздо более низкий приоритет, чем INTo или INT1. Мне не нужен активный USB во время чтения или сна, потому что для отправки данных используется BLE. В коде, который я опубликовал, часть BLE удалена, что упрощает устранение неполадок. Внешний однократный импульс 800 мкс является устойчивым, как скала, который я показываю на оскопе, с правильной амплитудой (от 0 до 3,3 В) и интервалами. Я пытаюсь заставить процессор спать между чтениями. Может быть сразу после чтения или через несколько секунд. Прерывания должны быть живыми и готовыми к захвату на случай, если 20 секунд превратятся в интервалы в 1 или 2 секунды (в реальной жизни).

// Время интервала между двумя последовательными импульсами. Частотный таймер с использованием ввода //блок захвата №3 на 32u4
// Автор: Ник Гэммон
// Дата: 31 августа 2013 г. http://www.gammon.com.au/timers
// Ссылки: здесь: http://www.gammon.com.au/forum/?id=11504&reply=12#reply12 //и здесь: https://www.gammon.com.au/power Sketch J
// Информация здесь: http://gammon.com.au/interrupts
//Частота (Гц) Per (мкс)
// 8 000 000* 0,125 мкс или 125 нс за такт на частоте 8 МГц


#include "LowPower.h" 
//часть обхода отсутствующей функциональности BOSD в 32u4.
// Не требуется для микроконтроллеров 328p для сна.

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include "avr/io.h"
#include <math.h> 
#include <Arduino.h>


// Вход: контакт D13 (PC7, ICP3) adafruit 32u, который является входным контактом захвата таймера 3, // удален светодиод D13 путем обрезания дорожки или удаления резистора перед светодиодом 13.

const int wakeUpPin = 3;
volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;
volatile unsigned long elapsedTime;

//////ПУСК: ПРОГРАММА ТАЙМЕРА ISR/СЧЕТЧИКА ИМПУЛЬСОВ НА D13 ВХОД-ЗАХВАТ №3 на 32u4 ////
//https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html //http://medesign.seas.upenn.edu/index.php/Guides/MaEvArM-timer3

// таймер переполняется (каждые 65536 отсчетов)
ISR (TIMER3_OVF_vect) //1 3

{
        overflowCount++;
}  // конец TIMER3_OVF_vect

ISR (TIMER3_CAPT_vect)   // 1 3
  {
       // отмена сна в качестве меры предосторожности (добавлено сюда из режима энергосбережения)
      sleep_disable();
    
   // получить значение счетчика до того, как оно изменится
  unsigned int timer3CounterValue;
  timer3CounterValue = ICR3;  
// см. техническое описание, стр. 117 (доступ к 16-битным регистрам)
  unsigned long overflowCopy = overflowCount;
  
  // если только что пропустили переполнение
  if ((TIFR3 & bit (TOV3)) && timer3CounterValue < 0x7FFF)  // 1 3 //http://medesign.seas.upenn.edu/index.php/Guides/MaEvArM-timer3
    overflowCopy++;
  
  // подождем, пока не заметим последний
  if (triggered)
    return;

  if (first)
    {
    startTime = (overflowCopy << 16) + timer3CounterValue;
    first = false;
    return;  
    }
    
  finishTime = (overflowCopy << 16) + timer3CounterValue;
  triggered = true;
  TIMSK3 = 0;    // больше никаких прерываний на данный момент // 1 3 //http://medesign.seas.upenn.edu/index.php/Guides/MaEvArM-timer3
  }  // конец TIMER3_CAPT_vect

/////////////////////////ENF OF: ПРОГРАММА ИЗМЕРЕНИЯ ТАЙМЕРА/СЧЕТЧИКА ИМПУЛЬСОВ/////////////


////////ПРОГРАММА ПОДГОТОВКИ К ПРЕРЫВАНИЯМ ДЛЯ ЗАХВАТА ПО ТАЙМЕРУ/СЧЕТЧИКУ//////////////////

void prepareForInterrupts ()
  {
 
  noInterrupts ();  // защищенный код
  first = true;
  triggered = false;  // перезаряжаем для следующего раза
  // сбросить Таймер 1
  TCCR3A = 0;   // 1 3
  TCCR3B = 0;    // 1 3
  
  TIFR3 = bit (ICF3) | bit (TOV3);  // очищаем флаги, чтобы не было фиктивного прерывания // 1 3
  TCNT3 = 0;          // Счетчик до нуля // 1 3
  overflowCount = 0;  // Поэтому переполнения пока нет
  
  // Таймер 3 - считает тактовые импульсы
  TIMSK3 = bit (TOIE3) | bit (ICIE3);   // прерывание при переполнении Таймера 3 и захвате ввода
  // запускаем Таймер 1, без предделителя
  TCCR3B =  bit (CS30) | bit (ICES3);  

// плюс Input Capture Edge Select (нарастающий на D13)
//TCCR1B &= ~(1<<ICES1);
//ICP настроен на запуск по спаду/нарастанию фронта
// Бит 6 — ICES1: Input Capture Edge select Выбор обнаружения фронта для //функции захвата ввода. 0 = захват по заднему фронту 1 = захват по //нарастающему фронту
  
  interrupts ();
  }  // конец подготовки к прерываниям
  
///КОНЕЦ: ПОДГОТОВКА К ПРЕРЫВАНИЯМ ДЛЯ ТАЙМЕРА/СЧЕТЧИКА/////////////
  
///ЭНЕРГОСБЕРЕЖЕНИЕ - ПРЕРЫВАНИЕ СИГНАЛА WAKEUP ISR НА D3 на 32u4 или D2 на 328p///

void wakeUp ()
 {
  // отменяем сон в качестве меры предосторожности
     sleep_disable();
  // в целях предосторожности, пока мы занимаемся другими делами
   detachInterrupt(digitalPinToInterrupt(wakeUpPin));   
//D3 для 32u4 и D2 для 328p (INT0)
 }  // конец пробуждения
////ПРОГРАММА ЭНЕРГОСБЕРЕЖЕНИЯ - WAKEUP ISR-END///////////////////

////////УСТАНОВИТЬ ПУСТОЙ //////////////////////////////////////// ////

void setup(void)
{
 pinMode(wakeUpPin, INPUT); //это контакт D3 (INT0) на 32u4 и D2 (INT0) на 328p
digitalWrite (wakeUpPin, LOW);  
// включить раскрывающийся список на D3 (на 32u4 D3 равен INT0) для пробуждения ПРЕРЫВАНИЕ — ЧАСТЬ //ПРОГРАММЫ ЭНЕРГОСБЕРЕЖЕНИЯ С СИГНАЛОМ INT0 на D3 на 32u4 (или D2 на 328p)

 Serial.begin(115200);
  
  pinMode(13, INPUT);   
//Это входной/измерительный контакт (светодиод отключен или резистор LED13 удален)
   analogWrite(13, LOW);  //должен тянуть вверх или вниз, чтобы остановить шум и плавание

  // настройка для прерываний
  prepareForInterrupts ();   

}   // <= это для НАСТРОЙКИ () ///////

/////////////////////////КОНЕЦ СЕГМЕНТА НАСТРОЙКИ///////////////////


//////НАЧАЛО ГЛАВНОГО ЦИКЛА/////////////

void loop(void)
{

   // ждем, пока у нас будет чтение
if (!triggered) //если "запущен" НЕ ИСТИНА (т.е. НЕ Инициировано), то ОСТАНОВИТЕСЬ ЗДЕСЬ
 return;      
    
unsigned long  elapsedTime = (finishTime - startTime); 
//количество периодов между начальным и конечным временем

Serial.print ("Start time: ");
Serial.print(startTime);
Serial.print (" counts.  ");
Serial.print ("Finish time: ");
Serial.print(finishTime );
Serial.print (" counts.  ");
Serial.print ("Elapsed time: ");
Serial.print (elapsedTime);
Serial.println (" counts.   ");

 // Задержка перед следующим обновлением измерений
 delayMicroseconds(1000) ;

 prepareForInterrupts (); 
// возвращается вне LOOP для условий RESET для следующего INPUT CAPTURE ISR AND //OVERFLOW ISR TIMER/COUNTER чтение, очистка регистров и т.д.


///ЗАПУСК: ПРОГРАММА ЭНЕРГОСБЕРЕЖЕНИЯ С ИСПОЛЬЗОВАНИЕМ D3 (INT0) на 32u4 (D2 — INT0 на 328p) //ПРЕРЫВАНИЕ ОТ ИМПУЛЬСНОГО ИЛИ ПОЛЬЗОВАТЕЛЬСКОГО ПЕРЕКЛЮЧАТЕЛЯ/////////////

//задержкамикросекунд(3000000) ;
// ЗАДЕРЖКА, ПОЭТОМУ ТОЛЬКО СОН МОЖЕТ СЛУЧАТЬСЯ КАЖДЫЕ 3 секунды => 3000000 мкс (микросек)

 delay(500);// ДОЛЖНА быть задержка, чтобы разрешить процесс отключения питания

      // отключить АЦП
      ADCSRA = 0;  
  
      set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
      sleep_enable();

  // Не прерывать до перехода в спящий режим, иначе ISR отсоединит прерывания и мы не проснёмся.
      noInterrupts ();

  // будет вызываться, когда INT0 (вывод D2 для 328p) или D3 для 32u4 переходит в состояние HIGH//
     attachInterrupt(digitalPinToInterrupt(wakeUpPin), wakeUp, RISING);   

// [Внешние прерывания] //3 для 32u4 и 2 для 328p (INT0) Используйте RISING как //триггер для (INT0 на D2 для 328p и) INT0 на D3 на 32u4 от операционного усилителя. Тот же //сигнал, что и у D13 INPUT CAPTURE TIMER от операционного усилителя OUT
     EIFR = bit (INTF0);  // сбросить флаг для прерывания 0
     EIFR = bit (INTF1);  // сбросить флаг для прерывания 1
// добавлено просто на всякий случай, чтобы покрыть контакты D2 и D3 INT
          
// отключаем в программном обеспечении brow-out enable, БОДС не работает с 32u4, только //328p. Обойти это можно с помощью "low-power.h" библиотека, здесь не реализована
// BODS должен быть установлен в единицу, а BODSE должен быть установлен в ноль в течение четырех тактовых циклов
       // MCUCR = бит (BODS) | бита (БОДСЕ);
       // Бит BODS автоматически сбрасывается после трех тактов
       // MCUCR = бит (BODS);
        
//Мы гарантируем, что вызов sleep_cpu будет выполнен, поскольку процессор //выполняет следующую инструкцию после включения прерываний.
        interrupts ();  // один цикл
     // спящий_процессор (); // один цикл //******ОТКЛЮЧЕНО, ПОТОМУ ЧТО ПРОДОЛЖАЕТ УДВОИВАТЬСЯ
                                        //ВРЕМЯ ОТПРАВКИ ДАННЫХ И ПРОБУЖДАЕТСЯ ТОЛЬКО НА ПОЛОВИНУ
                                        //ВРЕМЯ

///////КОНЕЦ ПРОГРАММЫ ЭНЕРГОСБЕРЕЖЕНИЯ///////////////////////////////////////// /////////

} 
////////////////////////КОНЕЦ ГЛАВНОГО ЦИКЛА//////////////////////

, 👍1


1 ответ


Лучший ответ:

2

Мне кажется, ваша проблема здесь:

set_sleep_mode (SLEEP_MODE_PWR_DOWN);

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

Чтобы таймер продолжал работать, необходимо использовать SLEEP_MODE_IDLE, который в любом случае является спящим режимом по умолчанию. Этот режим переводит процессор в спящий режим. при сохранении работы периферийных устройств. Если вы боитесь, что это может иметь слишком высокое энергопотребление, вы можете выборочно отключить все периферийные устройства, которые вам не нужны. Обратите внимание, что вы не можете выборочно включить периферийные устройства в других спящих режимах.

,

Спасибо за предложение и ссылку. Видел пример для режима IDLE. Будет проверять, снижается ли отключение неиспользуемых периферийных устройств примерно так же, как потребление, как в SLEEP_MODE_POWERDOWN. Мое предположение заключалось в том, что использование прерывания типа INT0 разбудит ЦП, чтобы ТАЙМЕР ЗАХВАТА ВХОДА был полностью готов. Работает в половине случаев, на каждом втором внешнем импульсе, поэтому я подумал, что в моем коде есть какой-то конфликт последовательности/приоритета прерывания, или некоторые из флагов (которые все еще неясны для меня) не установлены должным образом как результат двух объединенных кодов. Я подозреваю, что «если !triggered» и «return»., @TommyS

Эдгар Боне. Реализовано и работает с IDLE! Гораздо меньше мА. Тем не менее, может быть кодовое решение проблемы, описанной в моем посте. Убежден, что, когда я объединил два примера г-на Ника Гаммона «InputCaptureCounter/Timer» и «Deep Sleep», проблема заключается в Void Loop=> if (!triggered) return ; Это останавливает программу до тех пор, пока флаг внутри ISR Capture не станет «истинным». Когда присоединение Interrupt на INT0/D3 срабатывает одновременно с чтением ISR Capture на D13, программа может не пройти мимо WakeUp ISR, добавленного мной. Как следствие, ISR Capture & Overflow не могут прочитать импульс на D13 (PC7 на 32u4)., @TommyS