Генерация серии импульсов за заданное время

space.andy
Offline
Зарегистрирован: 04.05.2016

Добрый день! У меня есть простой код с таймером, который постоянно проверяет значение на "входном" пине и в соответствие с этим решает, что сделать на "выходном".

        unsigned long currentMillis = millis();

        if(currentMillis - previousMillis >= 10) {
            value = digitalRead(in_pin);
            previousMillis = currentMillis;
        }
        if(value == 0) {
            digitalWrite(out_pin, LOW);
        }
        else if(value == 1) {
           // ???? 
        }

Проблема в том, что когда на "входной" пин подается сигнал, на "выходном" должна генерироваться серия импульсов какое-то заданное время, например 3 секунды. Я понимаю, что здесь наверное нужно какое-то прерывание, которое остановит выполнение программы и запустит эту серию импульсов, но реализовать не получается.

Буду благодарен за помощь.

dhog1
Offline
Зарегистрирован: 01.03.2016

После шквала комментариев положу свои 5 копеек.  Ну например при подаче на "входной" пин сигнала, "выходной" пин должен в течение 3-х секунд выдавать серию непонятно каких импульсов. Предположим, например, что это импульсы с периодом 8 мс, почему нет, другого же не сказано.

3 сек / 0,008 сек = 375 импульсов в течение 3 сек после появления сигнала на "входном" пине, с этим уже можно работать. "Выходной" пин удерживать в HIGH 4 мс, потом в LOW 4 мс - всего 8 мс. И так 375 раз, чтобы было ровно 3 сек. При повторной "подаче" сигнала на "входной" пин - повторить. А если сигнал прийдет раньше, чем истекут 3 сек? Ну, например, не потерять эти события, и отработать каждое такое событие новой серией сразу же после завершения "текущих импульсных" секунд. Так интересней.

Вот с примерно такой постановкой задачи можно поговорить о коде. Вариант с комментариями приводится. И прерывание (целых 2) там тоже имеется.

                            // просто удобный набор макросов манипулирования битами
                            // чтобы облегчить чтение, убрать громоздкие конструкции
                            // названия макросов мнемоничны и говорят сами за себя
    #define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT))
    #define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT))
    #define FLIPBIT(ADDRESS,BIT) (ADDRESS ^= (1<<BIT))
    #define CHECKBIT(ADDRESS,BIT) (ADDRESS & (1<<BIT))

    #define VARFROMCOMB(x, y) x
    #define BITFROMCOMB(x, y) y

    #define C_SETBIT(comb) SETBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))
    #define C_CLEARBIT(comb) CLEARBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))
    #define C_FLIPBIT(comb) FLIPBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))
    #define C_CHECKBIT(comb) CHECKBIT(VARFROMCOMB(comb), BITFROMCOMB(comb))

                            // определяем "входной" пин, например D6
#define IN_PIN_PORT     PORTD, PORTD6
#define IN_PIN_DDR      DDRD,  DDD6
#define IN_PIN_PIN      PIND,  PIND6
                            // определяем "выходной" пин, например D7
#define OUT_PIN_PORT    PORTD, PORTD7
#define OUT_PIN_DDR     DDRD,  DDD7
#define OUT_PIN_PIN     PIND,  PIND7
                            // для "заданного" времени и предполагаемых характеристик импульса
                            // подсчитываем кол-во импульсов в заданное время
#define IMP_CNT    375      // 3 sec / 0.008 sec = 375
                            // принимаем решение использовать Timer2, который будет считать с периодом таймера
                            // 4 мс с установленной делителем частотой 65,500 кГц на 16 МГц МК, после чего переполняться
                            // и вызывать прерывание по переполнению Timer2. Чтобы "сформировать" импульс нужно будет
                            // 2 таких переполнения
#define T2_INIT    0x06     // определились с параметрами Timer2, а это его начальное значение, чтобы считал
                            // точно 4 мс и потом переполнялся
                            
                            // удобные макросы с говорящими названиями, чтобы легче читать код было
#define START_T2   { TCNT2 = T2_INIT; TCCR2B |= (1<<CS22) | (1<<CS21); }
#define STOP_T2    { TCCR2B &= ~((1<<CS22) | (1<<CS21)); }


volatile unsigned int imp_cnt;    // эта переменная - счетчик оставшихся манипуляций HIGH и LOW с "выходным" пином
volatile byte series_cnt;         // а эта не даст пропасть "входному" событию, если оно поступило до того, как 3 сек закончились


ISR(TIMER2_OVF_vect) {           // прерывание по переполнению Timer2, здесь каждый раз формируется "половинка" импульса

    TCNT2 = T2_INIT;                        // переинициализируем счетчик
    if (imp_cnt--) C_FLIPBIT(OUT_PIN_PIN);  // если еще нужны импульсы, меняем состояние "выходного" пина (LOW <--> HIGH)
    else {                                  // готовимся к следующей серии маниипуляции с "выходным" пином
        imp_cnt = IMP_CNT << 1;             // а это число импульсов умноженное на 2
        if (!(--series_cnt)) STOP_T2;       // если нет пропущенных событий, завершаем генерацию импульсов
    }                                       // и останавливаем Timer2, он пока не нужен
}


ISR(PCINT2_vect) {               // прерывание по изменению состояния "входного" пина

    if (C_CHECKBIT(IN_PIN_PIN)) {          // если на "входном" пине HIGH
        series_cnt++;                      // регистрируем необходимость "генерации импульсов"
        if (!(TCCR2B & (1<<CS21))) START_T2;  // и для этого запускаем Timer2, если еще не запущен
    }

}


void setup() {                 

    C_CLEARBIT(IN_PIN_DDR);      // "входной" пин как INPUT
    C_CLEARBIT(IN_PIN_PORT);     // и LOW  (зависит от подключения; лучше, конечно, ставить в tri-state (PULL_UP))
    
    C_SETBIT(OUT_PIN_DDR);       // "выходной" пин как OUTPUT
    C_CLEARBIT(OUT_PIN_PORT);    // и LOW
    
    TIMSK2 = (1<<TOIE2);         // разрешаем прерывание по переполнению Timer2
                                 // настраиваем "входной" пин на регистрацию поступающего сигнала
    PCICR  = (1<<PCIE2);         // в примере для D6 это группа PCINT прерываний PCINT2
    PCMSK2 = (1<<PCINT22);       // в примере D6 имеет маску прерывания пина PCINT22, разрешить прерывание на D6
    PCIFR  = (1<<PCIF2);         // аккуратность требует перед "употреблением" очистить "случайный" флаг прерывания
    
    imp_cnt    = IMP_CNT << 1;   // для формирования импульсов требуется IMP_CNT раз сделать HIGH и столько же LOW
    series_cnt = 0;              // запрос на формирование импульсов пока не поступал

}


void loop() {
                                 // здесь занимаемся своими делами
}

 

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

Зачем так сложно? Как понимаю, у автора задачка на изучение свойств макроса everyMillis(wait, code); в его развернутом виде. Создавать "пачки импульсов" удобно через ШИМ, функцией tone(), но и отключать тогда надо не digitalWrite(), а выключением ШИМ turnOffPWM() или как она там у Wiring.

По сути задачка перефразируется так:

Постоянно (everyMillis) проверять состояние "кнопки", и как только она включилась надо включить флаг "кнопка включена" и запомнить значение millis() для интервала генерации, во второй части loop(), в которой:

Если "кнопка включена", то:

а) выдаем через функцию tone() прямоугольные импульсы;

б) проверяем временной интервал генерации (everyMillis) и если он кончился, то сбрасываем флаг "кнопка включена" и выключаем tone()

Фсё.

P.S.

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