Attiny13 ИК приёмник и периодические затыки.

Fenix_spb

✩✩✩✩✩✩✩
20 Окт 2022
14
0
Сделал светильник с светодиодной лентой на Attiny13 c управлением яркостью с пульта. В протеусе работает как часы)) Спаял и получил небольшие затыки.
Управление яркостью осуществляется через транзисторный модуль с опторазвязкой.
На ножку PB3 подключен ИК приёмник 1838 с обвязкой. С ИК приёмом проблем нет. Потому что если воспроизвести код вот отсюда
команды проходят чётко и без пропусков
Я сделал свой вариант в котором есть миллисекундный таймер и PWM c регулируемой скважностью с частотой примерно 1кГц и обработку повторных нажатий
Но как только я меняю код на свой Начинаются небольшие затыки. Проявляются в том что пульт иногда не реагирует на кнопку, как бы пропускает её. Может 20 раз среагировать, а потом несколько пропустить. Или один пропуск на 10 нажатий. Ну и далее в этом же духе. Проявляется не часто. Мне кажется проблема с двумя прерываниями и переменными с которыми работаю из двух прерываний. В исходном коде работает 1 прерывание и самый длительный интервал ИК 9000 мкр сек помещается между минимальным и максимальным значением TCNT0. А у меня частота гораздо выше из-за PWM и счетчика миллисекунд. Подскажите как исправить этот баг и при этом сохранить приемлемую частоту PWM ~1K?
PS атинька не перезагружается и не зависает, у меня задана начальная скважность и если лента горит в половину яркости, то при пропусках команд яркость не падает. И после пропуска нажатия атинька продолжает обрабатывать нажатия далее.
C:
#include <avr/io.h>
#include <avr/interrupt.h>

#define IR_OUT PB3
#define IR_WAIT_HIGH()  while(~PINB & (1<<IR_OUT))
#define IR_WAIT_LOW()   while( PINB & (1<<IR_OUT))
#define IR_9000us 1350 //9000us*9.6Mhz/64
#define IR_4500us 675
#define IR_2250us 337
#define IR_1687us 253
#define IR_562us 84
#define IR_FAIL 0
#define IR_NEC 1
#define Pwm_start 105

volatile uint16_t milliseconds;
volatile uint16_t IR_duration;
volatile uint16_t duration;
volatile uint8_t c2;

uint16_t addr;
uint8_t  cmd;
uint8_t flag=1;
uint16_t last_milliseconds;

uint8_t IR_checkDur(uint16_t dur)
{
    uint16_t error = dur >> 3;
    //if(error < 6) error = 6;
    if(IR_duration > dur)
    {
        return((IR_duration - dur) < error);
    }
    return((dur - IR_duration) < error);
}

ISR(PCINT0_vect)
{
    IR_duration=duration+TCNT0-c2;
    c2=TCNT0;
    duration=0;
}

ISR(TIM0_OVF_vect)
{
    TCNT0=Pwm_start;
    duration+=150;
    milliseconds++;
}

void timer1_init()
{
    TCCR0A|=(1<<COM0A1)|(1<<WGM01)|(1<<WGM00);
    TCCR0B|=(1<<CS00)|(1<<CS01);
    OCR0A=Pwm_start+1;
    TCNT0=Pwm_start;
    TIMSK0|=(1<<TOIE0);
    asm("sei");
}

void pcint_init()
{
    PCMSK|=(1<<PCINT3);
}

uint8_t IR_readNEC(void)
{
    uint32_t data;
    IR_WAIT_LOW();
    if(IR_checkDur(IR_2250us))
    {
        IR_WAIT_HIGH();
        if(IR_checkDur(IR_562us))
        {
            //repeat code
            if(cmd!=0)
            {
                return IR_NEC;
            }
        }
    }
    if(!IR_checkDur(IR_4500us)) return 0;
 
    for(uint8_t i=32; i; i--)
    {
        data >>= 1;
        IR_WAIT_HIGH();
        if(!IR_checkDur(IR_562us)) return 0;
        IR_WAIT_LOW();
        if(IR_checkDur(IR_1687us)) data |= 0x80000000;
        else if(!IR_checkDur(IR_562us)) return 0;
    }
    IR_WAIT_HIGH();
    if (!IR_checkDur(IR_562us)) return 0;
    uint8_t addr1 = data;                
    uint8_t addr2 = data >> 8;          
    uint8_t cmd1  = data >> 16;          
    uint8_t cmd2  = data >> 24;          
    if((cmd1 + cmd2) < 255) return 0;
    cmd = cmd1;
    if((addr1 + addr2) == 255) addr = addr1;
    else addr = data;
    return IR_NEC;
}

uint8_t IR_Read()
{
    uint8_t protocol = 0;
    GIMSK|=(1<<PCIE);
    do
    {
            IR_WAIT_HIGH();                              
            IR_WAIT_LOW();                              
            IR_WAIT_HIGH();                
            if(IR_checkDur(IR_9000us))
            {            
                protocol = IR_readNEC();
            }
    } while (!protocol);
    GIMSK &= ~(1<<PCIE);
    return protocol;
}

int main(void)
{
    DDRB|=(1<<PB0);
    DDRB  &= ~(1<<IR_OUT);    
    timer1_init();
    pcint_init();
    while (1)
    {
        IR_Read();
        if(addr!=0) continue;     
        if( cmd == 0x40)
        {         
            if(flag)
            {
                TCCR0A&=~(1<<COM0A1);
                PORTB|=(0<<PB0);
            }
            else
            {
                TCCR0A|=(1<<COM0A1);
                PORTB|=(0<<PB0);
            }         
            flag=!flag;
            cmd=0;
            last_milliseconds=milliseconds;
        }
        else if(cmd == 0x44)
        {
            if(OCR0A>Pwm_start+1)
            {
                OCR0A--;
            }
            last_milliseconds=milliseconds;
        }
        else if(cmd == 0x43)
        {
            if(OCR0A<0xFF)
            {
                OCR0A++;
            }
            last_milliseconds=milliseconds;
        }     
        if(milliseconds-last_milliseconds>=250)
        {
            cmd=0;
            IR_duration=0;
        }     
    }
}
 
Изменено:

poty

★★★★★★✩
19 Фев 2020
3,261
948
Перевести на аппаратный PWM и убрать второе прерывание.
 

Fenix_spb

✩✩✩✩✩✩✩
20 Окт 2022
14
0
@Fenix_spb, между первыми прерываниями чего? Опишите проблему.
в первом прерывании PCINT0_vect я считаю изменение уровня на ноже с ИК приёмником. мне нужно знать сколько прошло времени между сменами состояния на PB3. Для этого я использую счётчик который инкрементируется во втором прерывании TIM0_OVF_vect