Обсуждаем контроллеры компании Atmel.
Ответить

Re: Программирование ATtiny13

Сб ноя 28, 2020 21:49:19

Насчет "чистого Си" не ведаю...
Однако в адуринье на основе АВРок любое прерывание "перекрывает кислород" функциям контроля времени.
Ведь в прерывании ВЛОЖЕННЫЕ прерывания по умолчанию запрещены.
Там или вложенные прерывания разрешать надо или делать тайм-слоты на основе программного подсчета тактов в командах.
:dont_know:
Может для первичной обучалки адуринка сгодится?
Там и симулятор приличный имеется с полным фаршем внешней аппаратной обвязки...
https://www.sites.google.com/site/unoardusim/
И чего б поднабросать можно (правда не для тиньки - но основа алгоритмов одинакова будет... и просмотр пошаговый удобен...)...
:roll:
Помимо прочего помним о предделителях счетчиков. Ибо те предделители тикают и переполняются сами по себе независимо - работает счетчик или нет.
При запуске интервалометра необходимо предварительно сбрасывать предделитель соответствующего счетчика.
Под ассемблером и с учетом тини13...
Можно использовать цифровые компараторы таймера - заложить в них длительность положительной части измерямного импульса.
С приходом фронта внешнего сигнала запуск таймера
Прерывание по совпадению с одного компаратора - 0 (если не имело место большее по величине) - ставим флаг 0
Прерывание со второго - 1. ставим флаг 1
Общее прерывание по переполнению - ошибка интервала ставим флаг 3 и останов таймера по переполнению
С момента фронта внешнего импульса запускаем программное ожидание спада положительного интервала.
По окончании импульса останов счета и смотрим флаги, а уж по ним принимаем решение о том, что у нас было.
8)
Последний раз редактировалось BOB51 Сб ноя 28, 2020 22:12:56, всего редактировалось 2 раз(а).

Re: Программирование ATtiny13

Сб ноя 28, 2020 22:01:41

BOB51, с ардуинкой я дружу digitalwrite(), digitalread(). Кстати на ардуинке на а328 это все решено у меня с использованием библиотеке типа virtualwave или чтото похоже, ну немало я их перебла. пробовал даже в Тиньку13 прошил bootloader, но там ограниченный набор комманд, а переделка библиотек потребовала большей квалификации. Это все понятно. Но как бы чувствую в себе силы решить эту задачку на чисмтом С. Кажется, что хожу вокруг да олоко, но вот никак не могу нащупать как оно работает.

Re: Программирование ATtiny13

Сб ноя 28, 2020 22:11:05

Попробуйте тот вариант, что я выше предложил...
Под ассемблером сие вполне работоспособно...
:roll:

Re: Программирование ATtiny13

Сб ноя 28, 2020 22:46:57

рабочий код для attiny13 под AS4
Спойлер#include <avr/io.h>
#include <avr/interrupt.h>

#define OUTPORT PORTB //PORTB
#define OUT2 3 //pinout 2
#define OUT1 4 //pinout 3
#define OUT4 2 //pinout 7
#define OUT3 0 //pinout 5

#define PWMPIN PINB //PINB
#define PWM 1 //pinout 6

volatile unsigned char time; //текущее значение счетчика
volatile unsigned char time_begin; //время начала PWM импульса
volatile unsigned char ch; //длительность PWM импульса
volatile int tick4; //счетчик переполнений от последнего изменения на входе PWM
volatile unsigned char timer_mig; //таймер для мигания
//----------
// обработка прерывания от переполнения счетчика
ISR(TIM0_OVF_vect) //прерывание переполнения счетчика
{
tick4++;
timer_mig++; //3.413*256 = 876 ms
}
//----------
// обработка прерывания от изменения на входе PWM
ISR(PCINT0_vect)
{
time=TCNT0; //сохраняем текущее время

if ((PWMPIN & _BV(PWM))==0)
{
ch= time-time_begin; // это спад, определяем длительность импульса PWM
}
else
{
time_begin=time; // это фронт, запоминаем текущее время
}
tick4=0; //сброс счетчика переполнений
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// ГЛАВНАЯ ПРОГРАММА
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int main(void)
{
CLKPR=0x80; //частота 4 800 000 гц
CLKPR=0x00;

DDRB = _BV(OUT1) | _BV(OUT2) | _BV(OUT3) | _BV(OUT4) ; //настроить выходы на вывод

// Настройка Timer0
TCCR0B= _BV(CS01) | _BV(CS00); // Частота счетчика: 4 800 000/256 = 75 000 Hz или 13.3mks
// 13.33*256 = 3,413 ms максимальная длина,которую можно измерить
// Настройка прерывания
GIMSK= _BV(PCIE); // прерывание по изменению

PCMSK=_BV(PWM); //маска - какой пин отслеживаем
GIFR= _BV(INTF0) | _BV(PCIF); //очистить все флаги прерываний

TIMSK0= _BV(TOIE0); //разрешаем прерывания по переполнению
// Global enable interrupts
sei();
// === ГЛАВНЫЙ цикл ========
while (1)
{
//1000/13.33=75
//1200/13.33=90
//1400/13.33=105
//1500/13.33=112.5
//1600/13.33=120
//1800/13.33=135
//2000/13.33=150
//.....................
if (tick4 > 300)
{
OUTPORT &= ~_BV(OUT1); //если счетчик переполнений больше чем 300*3.4 ~ 1сек
OUTPORT &= ~_BV(OUT2); //выключаем оба выхода
OUTPORT &= ~_BV(OUT3); //выключаем оба выхода
OUTPORT &= ~_BV(OUT4); //выключаем оба выхода
}
else
{
//включить-выключить
if (ch <76)
{
OUTPORT &= ~_BV(OUT1); //1-выключить
OUTPORT &= ~_BV(OUT2); //2-выключить
OUTPORT &= ~_BV(OUT3); //3-выключить
OUTPORT &= ~_BV(OUT4); //4-выключить
}
//.......
if ((ch > 78) && (ch <106))
{
OUTPORT |= _BV(OUT1); //1-включить
OUTPORT &= ~_BV(OUT2); //2-выключить
OUTPORT &= ~_BV(OUT3); //3-выключить
OUTPORT &= ~_BV(OUT4); //4-выключить
}
//.......
if (ch > 108)
{
OUTPORT |= _BV(OUT1); //1-включить
OUTPORT |= _BV(OUT2); //2-включить
if((timer_mig & 0x80) == 0)
{
if ((timer_mig & 0x68) == 0) OUTPORT |= _BV(OUT3);
else OUTPORT &= ~_BV(OUT3);
}
else
{
if ((timer_mig & 0x68) == 0) OUTPORT |= _BV(OUT4);
else OUTPORT &= ~_BV(OUT4);
}

}
}
}
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Re: Программирование ATtiny13

Вс ноя 29, 2020 09:53:37

А тинька точно тактируется 1,2 МГц? Может, 9,6 (это как раз в 8 раз, как у Вас)? Тогда фьюзы.

Re: Программирование ATtiny13

Вс ноя 29, 2020 10:09:56

9,6/8=1,2 фузы и предделитель "по умолчанию"при выпуске изделия.
8)

Re: Программирование ATtiny13

Вс ноя 29, 2020 11:40:01

А помигать светиком да сравнить расчётную частоту с наблюдаемой? А может, все- таки 9,6 ?

Re: Программирование ATtiny13

Вс ноя 29, 2020 12:34:57

ок, а зачем мне конролить переполнение есил я TTCR0B=0x05 делитель на 1024

В реальности всегда что-то может пойти не так. По какой-то причине "залипнет" импульс - получите непредсказуемый эффект. По-хорошему надо всегда предусматривать внештатные ситуации.

например если я подаю на вход частоту 50гц -это период 20мс. Это 2 длительности по 10м (high и low)
счет 8 битный -256 значений (0-255) испльзуя делитель на 1024 =имею 1172 кгц. ОДин оборот счетчика даст мне вычислить длительность 0.000853*256=0.218с =218мс. Значит ожидаемо,что 256*10мс/0.218= примерно 11-12 единиц счетчика. А я получаю 92. Т.е 92*0.000853= пример 80 мс. Откуда эти 80мс хоть стрэляй не пойму.
Кроче, что то не так а где концы искать по ка не знаю.

Такое впечатление, что у вас и правда CKDIV8 снят и тактовая просто 9.6 МГц, а не 1.2 МГц. Проверьте фьюзы. Раньше возможно прошивали их уже в этот МК и сняли этот бит.

Re: Программирование ATtiny13

Вс ноя 29, 2020 13:32:23

emax писал(а):рабочий код для attiny13 под AS4
Спойлер#include <avr/io.h>
#include <avr/interrupt.h>

#define OUTPORT PORTB //PORTB
#define OUT2 3 //pinout 2
#define OUT1 4 //pinout 3
#define OUT4 2 //pinout 7
#define OUT3 0 //pinout 5

#define PWMPIN PINB //PINB
#define PWM 1 //pinout 6

volatile unsigned char time; //текущее значение счетчика
volatile unsigned char time_begin; //время начала PWM импульса
volatile unsigned char ch; //длительность PWM импульса
volatile int tick4; //счетчик переполнений от последнего изменения на входе PWM
volatile unsigned char timer_mig; //таймер для мигания
//----------
// обработка прерывания от переполнения счетчика
ISR(TIM0_OVF_vect) //прерывание переполнения счетчика
{
tick4++;
timer_mig++; //3.413*256 = 876 ms
}
//----------
// обработка прерывания от изменения на входе PWM
ISR(PCINT0_vect)
{
time=TCNT0; //сохраняем текущее время

if ((PWMPIN & _BV(PWM))==0)
{
ch= time-time_begin; // это спад, определяем длительность импульса PWM
}
else
{
time_begin=time; // это фронт, запоминаем текущее время
}
tick4=0; //сброс счетчика переполнений
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// ГЛАВНАЯ ПРОГРАММА
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
int main(void)
{
CLKPR=0x80; //частота 4 800 000 гц
CLKPR=0x00;

DDRB = _BV(OUT1) | _BV(OUT2) | _BV(OUT3) | _BV(OUT4) ; //настроить выходы на вывод

// Настройка Timer0
TCCR0B= _BV(CS01) | _BV(CS00); // Частота счетчика: 4 800 000/256 = 75 000 Hz или 13.3mks
// 13.33*256 = 3,413 ms максимальная длина,которую можно измерить
// Настройка прерывания
GIMSK= _BV(PCIE); // прерывание по изменению

PCMSK=_BV(PWM); //маска - какой пин отслеживаем
GIFR= _BV(INTF0) | _BV(PCIF); //очистить все флаги прерываний

TIMSK0= _BV(TOIE0); //разрешаем прерывания по переполнению
// Global enable interrupts
sei();
// === ГЛАВНЫЙ цикл ========
while (1)
{
//1000/13.33=75
//1200/13.33=90
//1400/13.33=105
//1500/13.33=112.5
//1600/13.33=120
//1800/13.33=135
//2000/13.33=150
//...
if (tick4 > 300)
{
OUTPORT &= ~_BV(OUT1); //если счетчик переполнений больше чем 300*3.4 ~ 1сек
OUTPORT &= ~_BV(OUT2); //выключаем оба выхода
OUTPORT &= ~_BV(OUT3); //выключаем оба выхода
OUTPORT &= ~_BV(OUT4); //выключаем оба выхода
}
else
{
//включить-выключить
if (ch <76)
{
OUTPORT &= ~_BV(OUT1); //1-выключить
OUTPORT &= ~_BV(OUT2); //2-выключить
OUTPORT &= ~_BV(OUT3); //3-выключить
OUTPORT &= ~_BV(OUT4); //4-выключить
}
//.......
if ((ch > 78) && (ch <106))
{
OUTPORT |= _BV(OUT1); //1-включить
OUTPORT &= ~_BV(OUT2); //2-выключить
OUTPORT &= ~_BV(OUT3); //3-выключить
OUTPORT &= ~_BV(OUT4); //4-выключить
}
//.......
if (ch > 108)
{
OUTPORT |= _BV(OUT1); //1-включить
OUTPORT |= _BV(OUT2); //2-включить
if((timer_mig & 0x80) == 0)
{
if ((timer_mig & 0x68) == 0) OUTPORT |= _BV(OUT3);
else OUTPORT &= ~_BV(OUT3);
}
else
{
if ((timer_mig & 0x68) == 0) OUTPORT |= _BV(OUT4);
else OUTPORT &= ~_BV(OUT4);
}

}
}
}
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@


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

Код:
CLKPR=0x80; //частота 4 800 000 гц
CLKPR=0x00;

DDRB = _BV(OUT1) | _BV(OUT2) | _BV(OUT3) | _BV(OUT4) ; //настроить выходы на вывод

// Настройка Timer0
TCCR0B= _BV(CS01) | _BV(CS00); // Частота счетчика: 4 800 000/256 = 75 000 Hz или 13.3mks
// 13.33*256 = 3,413 ms максимальная длина,которую можно измерить

и начал думать

BOB51, dgrett, NStorm,
dgrett писал(а):А помигать светиком да сравнить расчётную частоту с наблюдаемой? А может, все- таки 9,6 ?


да, так и сделал. Настроил счетчик, подключил осцилограф и начал считать клеточки.

в итого пришел сюда
Изображение

тут стояло 9.6. Я поставил 4.8. И все у меня получилось -47 условных единиц счетчика на 10мс
т.е 4.8/1024=4687.5 кгц или 21.3мкс. Это измеряемая длительность 256*21.3=54.6мс.
Я измеряю 10мс, то должен получить на счетчике 256*10/54.6=46.8
Как в аптеке!

Такое впечатление, что у вас и правда CKDIV8 снят и тактовая просто 9.6 МГц, а не 1.2 МГц. Проверьте фьюзы. Раньше возможно прошивали их уже в этот МК и сняли этот бит.

это все пока в Протеусе.

устроил сразу небольшой брутфорс. Проверяю 3 имупльса на длительность. Если все 3 соответствуют пеерключаю лампочку.
Код:
ISR(PCINT0_vect)
     
{
  // проверяю импульсы по  LOw
     if ( !(PINB & (1<<BUTTON2)) == 0  ) // если встретился high  То нужно сбросить счетчик что бы не учитывть длительность
  {
    statHigh=TCNT0;
    TCNT0=0x00;
     }
   
  if (ii==0)
  {
   if ( (PINB & (1<<BUTTON2)) == 0) // если LOW
      {
     statLow=TCNT0;
     if (statLow==47){ii=ii++;} // измерим и получим первый имупльс ii=1;
     TCNT0=0x00;
     }
  }
     
     
   if (ii==1)   
   {
      if ( (PINB & (1<<BUTTON2)) == 0) // если LOW
      {    
     statLow=TCNT0;
     if (statLow==47){ii=ii++;} // измерим и получим второй имупльс ii=2;
     TCNT0=0x00;
     }
  }
     
   if (ii==2)   
   {
      if ( (PINB & (1<<BUTTON2)) == 0) // если LOW
      {    
     statLow=TCNT0;
     if (statLow==47){ii=ii++;} // измерим и получим второй имупльс ii=2;
     TCNT0=0x00;
     }
  }
     
     
     
     
     if (ii==3) // Набралось 3 "правильных" импульса. Можно включать лампочку
     {
   
    PORTB ^= (1<<LED); //переключаем состояние светодиода (вкл./выкл.)
   ii=0; // Обнулим счетчик "правильных" импульсов. Ждем следующую комманду.
   }
 

}


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

Re: Программирование ATtiny13

Вс ноя 29, 2020 14:02:01

Код:
ii=ii++;

Ох так не стоит делать. Просто ii++; - это уже увеличит значение ii на 1. А то неровен час до вот этого дойти.

В реальном железе вместо ровно 47 проверку стоит проводить с допущением некоего отклонения. Хотя у вас довольно-таки долгие импульсы, всё-равно на +-1 стоит хотя бы допущение сделать.

Счетчик "правильных" импульсов стоит обнулять не только когда приходят 3 правильных, но и когда приходит любой "неправильный". Иначе из-за накопления какой-нибудь ошибки/помех и т.п. может опять не так как надо работать. Просто при проверке statLow добавить else ii = 0;

Добавлено after 7 minutes 15 seconds:
И как я уже говорил - всегда предусматривайте внештатные ситуации и их обработку. В реальности всё не бывает так идеально, как в симуляторах. Еще стоит добавить прерывание по переполнению таймера, где как минимум сбрасывать ii тоже.
В реальности у вас будет не идеальный генератор, а радио модуль. Там и эфирные помехи и чужой эфир может быть и т.д. Модуль может затупить, зависнуть. Много вариантов может быть. Их стоит предусматривать.

Re: Программирование ATtiny13

Вс ноя 29, 2020 19:59:11

NStorm, счас подергал еще тот код что я выше написал и нашел ошибку, обнуление счетчика нужно только когда попадаю на ВЫсокий уровень.


Код:
ISR(PCINT0_vect)
     //|| (PINB & (1<<BUTTON2)) == 0
{
     jj=jj+1;
     if ( !(PINB & (1<<BUTTON2)) == 0  ) // если high
      {
    statHigh=TCNT0;
    TCNT0=0x00;
      }
    if ( ((PINB & (1<<BUTTON2)) == 0) & (jj==2) & (TCNT0==66)) // если LOW
      {
         stat1=TCNT0; t1=1;
   }
   
      if ( ((PINB & (1<<BUTTON2)) == 0 )&  (jj==4) & (TCNT0==57) ) // если LOW
       {    
          stat2=TCNT0; t2=1;
         }
      if ( ((PINB & (1<<BUTTON2)) == 0 ) & (jj==6)  & (TCNT0==52)) // если LOW
           {    
           stat3=TCNT0; t3=1;
           }
     if (t1==t2==t3)
     {
   PORTB ^= (1<<LED); //переключаем состояние светодиода (вкл./выкл.)
   //ii=5;
   }
}


ну вот с этого варианта можно дальше плясать.

Re: Программирование ATtiny13

Пт дек 04, 2020 11:16:52

Вот есть такая запись
IF ( (PINB & (1<<PB0))==0) {}
Она, как я понимаю указывает на то , что на ножке PB0 низкий уровень

т.е проверяем состояние порта PB0 используя регистр PINB

Не могу раскусить что значит ==0

Например у нас есть регистр PINB, он равен 0b0000 0000
есть PB0 равное 0
делаю 1<<PB0 , 0b0000 00001<<0, получаю 0b0000 0001

делаю операцию И ((PINB & 0b0000 0001)) между 0b0000 0000 И 0b0000 0001, получаю 0b0000 0001

А в каком месте тут должно быть ==0

Re: Программирование ATtiny13

Пт дек 04, 2020 11:26:45

olegue писал(а):делаю операцию И ((PINB & 0b0000 0001)) между 0b0000 0000 И 0b0000 0001, получаю 0b0000 0001
изучите битовые операции хорошенько

1 & 0 = 0, а не 1, как вы думаете

Re: Программирование ATtiny13

Пт дек 04, 2020 11:45:03

изучите битовые операции хорошенько

1 & 0 = 0, а не 1, как вы думаете


да, верно . Я долго писал этот пост, отходил по пять раз .
т.е получаем в данном конкретном случает 0b0000 0000

а к чему тогда ==0, к чему этот 0 относится?

К конкретному пину? PB0?

Re: Программирование ATtiny13

Пт дек 04, 2020 12:44:30

olegue, ко всему выражению. Вместо инверсии.
Вы ведь пишете:
т.е получаем в данном конкретном случает 0b0000 0000

Вот к этому и относится. 0b00000000 = 0, поэтому выражение будет истиной.
Если бы пин был в высоком уровне, получили бы:
0b00000001 & 1 = 1
Что уже НЕ РАВНО 0, поэтому условие IF не выполняется.

На самом деле лучше писать
Код:
if (!(PINB & (1<<PB0))) {}

когда надо проверить, что бит НЕ установлен и тоже самое, без !, когда надо проверить, что бит установлен.
Потому что с ==0 еще будет работать, а когда захотите проверить что бит установлен и напишите == 1 оно будет работать только для PB0, где-то прям в этой теме я уже по-моему вам об этом писал.

Re: Программирование ATtiny13

Пт дек 04, 2020 13:59:57

для avr-gcc всех версий есть "стандартный" макрос bit_is_set (или его антогонист - bit_is_clear), который по меньшей мере при прочтении не введет в заблуждение:
Код:
if(bit_is_set(PINB, PB0)){
}

if(bit_is_clear(PINB, PB3){
}
понятно без комментариев каждому, кто знает 25 английских слов...

Re: Программирование ATtiny13

Пт дек 04, 2020 14:38:43

NStorm, перечитал много раз. Ну не понимаю я

пускай не PB0 , а PB2

(1<<PB2) - это 0b0000 0100

пускай состояние PINB - 0b1111 1111

и мне нужно узнать состояние пина PB2 (3й бит)

0b1111 1111 И 0b0000 0100 =0b0000 0100, т.е не 0, т.е в этом байте есть хотя бы одна единица это не ==0

если состояние PINB - 0b1111 1001

0b1111 1001 И 0b0000 0100 =0b0000 0000, т.е все 0. т.е ==0

Дошло!!! Полезно писать посты. Позволяет собрать мысли до кучи.

Добавлено after 1 minute 54 seconds:
NStorm писал(а):а когда захотите проверить что бит установлен и напишите == 1 оно будет работать только для PB0,


0b1111 1111 И 0b0000 0100 =0b0000 0100, т.е не 0, т.е в этом байте есть хотя бы одна единица это не ==0


работает и для PB2 , разве нет?

или нужна именно единица младшего бита что бы приравнять к 1 (==1)?

Re: Программирование ATtiny13

Пт дек 04, 2020 15:01:34

или нужна именно единица младшего бита что бы приравнять к 1 (==1)?

Ну а вы как думаете? Разве 0b0000 0100 равно 1?

Re: Программирование ATtiny13

Пт дек 04, 2020 16:39:27

NStorm писал(а):Разве 0b0000 0100 равно 1?

001
010
011
100

это походу 100=4. Вот оно как.

Re: Программирование ATtiny13

Сб дек 05, 2020 11:22:52

У адуринки есть другой способ побитовых проверок/операций с битами:
Код:
Bits and Bytes

lowByte()
highByte()
bitRead()
bitWrite()
bitSet()
bitClear()
bit()

:roll:
Ответить