Attiny85 и прерывание.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Доброго дня други! Предыстория:

Решил собрать автономный диммер на Тиньке с детектором ноля и поэтэссами. Нарыл код, немного допилил - работает. Рулится аналоговым напряжением.


// Voltage controlled dimmer with ATtiny85
//http://forum.arduino.cc/index.php?topic=314773.0

#include <avr/io.h>
#include <avr/interrupt.h>

#define DETECT 2      //zero cross detect, interrupt 0, is physical pin 7
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
#define INSTELPIN 2   // =A2 (digital pin4) is physical pin 3 
const byte averageFactor = 10;   // коэффициент сглаживания показаний (0 = не сглаживать)
// чем выше, тем больше "инерционность"
int sensorValue = 0;

void setup() {
  // set up pins
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control

  // set up Timer1
  TCCR1 = 0;     // stop timer
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  attachInterrupt(0, zeroCrossingInterrupt, FALLING);
}

//Interrupt Service Routines
void zeroCrossingInterrupt() {
  TCNT1 = 0;   //reset timer - count from zero
  TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  digitalWrite(GATE, HIGH); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  digitalWrite(GATE, LOW);  //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop() {    // use analog input to set the dimmer
  int oldsensorValue = sensorValue;
  sensorValue = analogRead(INSTELPIN);
  if (averageFactor > 0)        // усреднение показаний для устранения "скачков"
  {
    sensorValue = (oldsensorValue * (averageFactor - 1) + sensorValue) / averageFactor;
  }
  OCR1A = OCR1A = map(sensorValue, 0, 1023, 125, 2);
}
С ШИМа мерцает, даже с программным сглаживанием. Прикрутил отдельный ЦАП с управлением по i2c - так работает хорошо. 

Дальше естественно захотелось добавить сделать из самого диммера i2c slave девайс. Нашел либу tinyWireS - работает. Обмен данными с Ардуиной без проблем.

Теперь собственно проблема. Диммер и i2c по отдельности работают, а вместе нет. Потому что пин SDA используется и для прерывания под детектор ноля. Переназначить на другой пин у меня не получается. Не заработал ни этот метод, ни другой

Вот код диммера с i2c:


// Voltage controlled dimmer with ATtiny85
//http://forum.arduino.cc/index.php?topic=314773.0

#include <PinChangeInterrupt.h>
#include <avr/io.h>
//#include <avr/interrupt.h>
#include <TinyWireS.h>


#define I2C_SLAVE_ADDR (0x41)
#define DETECT 1      //zero cross detect, interrupt 0, is physical pin 6
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
// I2C pins: PB2 - SCL, PB0 - SDA
volatile byte i2cValue;

void setup() {
  // set up pins
  TinyWireS.begin(I2C_SLAVE_ADDR);
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control

  // set up Timer1

  attachPCINT(DETECT, zeroCrossingInterrupt, FALLING);
  
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  //attachInterrupt(0, zeroCrossingInterrupt, FALLING);
  TinyWireS.onRequest(requestEvent);
}

//Interrupt Service Routines
void zeroCrossingInterrupt() {
  TCNT1 = 0;                //reset timer - count from zero
  TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  digitalWrite(GATE, HIGH); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  digitalWrite(GATE, LOW);  //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop() {    // use analog input to set the dimmer

  if (TinyWireS.available())  i2cValue = TinyWireS.receive();

  OCR1A = OCR1A = map(230, 0, 254, 125, 2);
  TinyWireS_stop_check();
}

void requestEvent() {

  TinyWireS.send(i2cValue);
}

Подскажите куда копать? Регистры и ассемблерные втавки для меня сложновато пока.

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

Umka, вторая ссылка всё правильно рассказывает. Нужно полностью отказаться от функции аттачинтеррапт и сделать прерывание PCINT как там описано для другой ноги. Только вам для сохранения прежней логики нужно в прерывании перечитать нужный вход, если LOW То обрабатывать дальше, если HIGH то выйти из прерывания ничего не делая.

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

Если до выходных не решите проблему, "апните" тему в субботу, чтобы я её увидел, ладно.

ptr
Offline
Зарегистрирован: 28.05.2016

dimax пишет:

нужно в прерывании перечитать нужный вход, если LOW То обрабатывать дальше, если HIGH то выйти из прерывания ничего не делая.

В данном случае, достаточно разрешить прерывания только с одного пина, при помощи PCMSK. Тогда и проверять ничего не нужно будет.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Все понимаю, а немного не понимаю. Ведь в ISR() у нас идет слежение за компаратором и переполнением таймера. Как туда добавить слежение за регистрами этими? В PCINT0_vect

GIMSK = 0b00100000; 
PCMSK = 0b00000010;
 
Прерывание на PB1 нужно. Это понятно. 
dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Umka,

// эту строчку  в сетап:
GIMSK=1<<PCIE; PCMSK=1<<PCINT1;

//Это обработчик прерывания:

ISR (PCINT0_vect){
if (PINB&(1<<PINB1)) { return;}
//сюда вставить то, что было в перывании
}

 

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012
 OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  //attachInterrupt(0, zeroCrossingInterrupt, FALLING);
  TinyWireS.onRequest(requestEvent);
}

//Interrupt Service Routines
void zeroCrossingInterrupt() {
  TCNT1 = 0;                //reset timer - count from zero
  TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  digitalWrite(GATE, HIGH); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  digitalWrite(GATE, LOW);  //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

Вот этот кусок и не понятен. Тут же 2 вызова ISR по разным событиям. Не догоняю пока. Или ISR(TIMER1_COMPA_vect) и ISR(TIMER1_OVF_vect) не зависимо от дрыга пином будут вызываться по своему прерыванию?

Просто добавляем 

TCNT1 = 0;                //reset timer - count from zero

TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
 
в ISR (PCINT0_vect) да?
dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Umka пишет:

 Тут же 2 вызова ISR по разным событиям. Не догоняю пока. Или ISR(TIMER1_COMPA_vect) и ISR(TIMER1_OVF_vect) не зависимо от дрыга пином будут вызываться по своему прерыванию?

Ну да. У вас же рабочий скечт, значит одно прерывание поднимает, другое опускает ногу.

 

Umka пишет:

Просто добавляем TCNT1 = 0;                //reset timer - count from zeroTCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet  в ISR (PCINT0_vect) да?

да,  я же в предыдущем сообщении написал уже, зачем переспрашивать-то?

ptr
Offline
Зарегистрирован: 28.05.2016

Umka пишет:


ISR(TIMER1_COMPA_vect) {   //comparator match
...
ISR(TIMER1_OVF_vect) {      //timer1 overflow

Вот этот кусок и не понятен. Тут же 2 вызова ISR по разным событиям. Не догоняю пока.

Это макрос

#define ISR(vector, ...)            \
    void vector (void) __attribute__ ((signal,__INTR_ATTRS)) __VA_ARGS__; \
    void vector (void)

#define TIMER1_COMPA_vect        _VECTOR(3)

#define _VECTOR(N) __vector_ ## N

Так как параметров нет, то будет развернут как

void TIMER1_COMPA_vect(void) {
...
void TIMER1_OVF_vect(void) {

а в конце концов так:

void __vector_3(void) {
...
void __vector_4(void) {

соответственно

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012
 
C:\Users\836D~1\AppData\Local\Temp\build3461124593286753855.tmp\PinChangeInterrupt\PinChangeInterrupt.cpp.o: In function `__vector_2':
C:\Users\Ђ¤¬Ё­Ёбва в®а\Documents\Arduino\libraries\PinChangeInterrupt\src/PinChangeInterrupt0.cpp:40: multiple definition of `__vector_2'
C:\Users\836D~1\AppData\Local\Temp\build3461124593286753855.tmp\dimmer_i2c.cpp.o:C:\Program Files\Arduino/dimmer_i2c.ino:43: first defined here
collect2.exe: error: ld returned 1 exit status
Ошибка компиляции.
 
В 43 строке вот это:
ISR (PCINT0_vect){
if (PINB&(1<<PINB1)) { return;}
TCNT1 = 0;                //reset timer - count from zero
TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
 
}

Разобрался. Не выключил либу PinChangeInterrupt.h

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

Umka, и чего? Компмилятор вам  говорит что много определений для вектора2.  По русски говоря ISR (PCINT0_vect)  у вас в программе указан более чем 1 раз

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

И прерывания на PB1 не ловит. Диммер не светит. :(

Вот полный код:


// Voltage controlled dimmer with ATtiny85
//http://forum.arduino.cc/index.php?topic=314773.0

//#include <PinChangeInterrupt.h>
#include <avr/io.h>
//#include <avr/interrupt.h>
#include <TinyWireS.h>


#define I2C_SLAVE_ADDR (0x41)
#define DETECT 1      //zero cross detect, interrupt 0, is physical pin 6
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
// I2C pins: PB2 - SCL, PB0 - SDA
volatile byte i2cValue;

void setup() {
  // set up pins
  TinyWireS.begin(I2C_SLAVE_ADDR);
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control
  GIMSK=1<<PCIE;
  PCMSK=1<<PCINT1;
  
  //attachPCINT(DETECT, zeroCrossingInterrupt, FALLING);
  
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  //attachInterrupt(0, zeroCrossingInterrupt, FALLING);
  TinyWireS.onRequest(requestEvent);
}

//Interrupt Service Routines
/* void zeroCrossingInterrupt() {
  TCNT1 = 0;                //reset timer - count from zero
  TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
} */

ISR (PCINT0_vect){
//if (PINB&(1<<PINB1)) { return;}
TCNT1 = 0;                //reset timer - count from zero
TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
 
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  digitalWrite(GATE, HIGH); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  digitalWrite(GATE, LOW);  //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop() {    // use analog input to set the dimmer

  if (TinyWireS.available())  i2cValue = TinyWireS.receive();

  OCR1A = map(i2cValue, 0, 254, 125, 2);
  TinyWireS_stop_check();
}

void requestEvent() {

  TinyWireS.send(i2cValue);
}

 

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

Umka, 43 строку отключать не нужно, если бы она была лишней я бы её не писал. Если не в этом дело, то у вас где-то ошибка, и она не в настроке прерывания.  ищите.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Это я пробовал так и этак. Не работает. Вот так работает:

#include <avr/io.h>
#include <avr/interrupt.h>

#define DETECT 2      //zero cross detect, interrupt 0, is physical pin 7
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
#define INSTELPIN 2   // =A2 (digital pin4) is physical pin 3 

void setup(){
  // set up pins
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control

  // set up Timer1 
  TCCR1 = 0;     // stop timer 
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  attachInterrupt(0,zeroCrossingInterrupt, FALLING);    
}  

//Interrupt Service Routines
void zeroCrossingInterrupt(){   
  TCNT1 = 0;   //reset timer - count from zero
  TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
}

ISR(TIMER1_COMPA_vect){    //comparator match
  digitalWrite(GATE,HIGH); //set triac gate to high
  TCNT1 = 255-PULSE;       //trigger pulse width, when TCNT1=255 timer1 overflows
} 
 
ISR(TIMER1_OVF_vect){       //timer1 overflow
  digitalWrite(GATE,LOW);   //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop(){     // use analog input to set the dimmer
int instelwaarde = analogRead(INSTELPIN);
OCR1A = map(instelwaarde, 0, 1023, 120, 2); 
}

 

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

Umka, вы рано совместили рабочий код с i2c. Верните старый код, и модифицируйте прерывание На нём. Если всё ок, то только тогда можно пробовать организовать i2c.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

// Voltage controlled dimmer with ATtiny85
//http://forum.arduino.cc/index.php?topic=314773.0
// This arduino sketch includes a zero 
// crossing detect function and an opto-isolated triac.
// 
// AC Phase control is accomplished using the internal 
// hardware timer1 in the ATtiny85
//
// Timing Sequence
// * timer is set up but disabled
// * zero crossing detected
// * timer starts counting from zero
// * comparator set to "delay to on" value
// * counter reaches comparator value
// * comparator ISR turns on triac gate
// * counter set to overflow - pulse width
// * counter reaches overflow
// * overflow ISR turns off triac gate
// * triac stops conducting at next zero cross

// The hardware timer runs at 8MHz. 
// A half period of a 50Hz AC signal takes 10 ms is 80000 counts.
// Prescaler set to 1024 gives 78 counts per half period


#include <avr/io.h>
#include <avr/interrupt.h>

#define DETECT 1      //zero cross detect, interrupt 0, is physical pin 6
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
#define INSTELPIN 2   // =A2 (digital pin4) is physical pin 3 

void setup(){
  // set up pins
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control

  // set up Timer1 
  GIMSK=1<<PCIE;
  PCMSK=1<<PCINT1;
  TCCR1 = 0;     // stop timer 
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
 // attachInterrupt(0,zeroCrossingInterrupt, FALLING);    
}  

//Interrupt Service Routines
ISR (PCINT0_vect){
  TCNT1 = 0;   //reset timer - count from zero
  TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
}

ISR(TIMER1_COMPA_vect){    //comparator match
  digitalWrite(GATE,HIGH); //set triac gate to high
  TCNT1 = 255-PULSE;       //trigger pulse width, when TCNT1=255 timer1 overflows
} 
 
ISR(TIMER1_OVF_vect){       //timer1 overflow
  digitalWrite(GATE,LOW);   //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop(){     // use analog input to set the dimmer
int instelwaarde = analogRead(INSTELPIN);
OCR1A = map(instelwaarde, 0, 1023, 120, 2); 
}

Не диммит. И похоже этот метод RISING вызов прерывания не поддерживает. Может в этом загвоздка?

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

Umka, опять вы выбросили условие проверки на единицу. Последний раз говорю -это не лишнее условие, оно определяет алгоритм "Falling" обработки прерывания

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Пардон, пропустил. Но и так не диммит. 

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

Umka,  а сигнал то переключали на B1 ? ) Можно для диагностики внутрь прерывания запихать мигание светодиодом, к примеру если он на PB0, то назначить его выходом в сетапе и:

ISR (PCINT0_vect){
if (PINB&(1<<PINB1)) { return;}
static uint8_t n;
if (++n == 50){ PORTB^=1<<PB0; n=0; }
}

должен мигать 2 раза в секунду.

 

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Стоп. Тут плата клон digispark rev3 с потертым загрузчиком. На 1 пине там как раз светодиод. Может в этом причина. Щас перекину детектор на другой пин.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Ну едрить его в качель! Работает. Надо же таким балбесом быть. Забыл про светодиод. И с i2c все работает. 

Вот финальный код:


// Voltage controlled dimmer with ATtiny85
//http://forum.arduino.cc/index.php?topic=314773.0

//#include <PinChangeInterrupt.h>
//#include <avr/io.h>
//#include <avr/interrupt.h>
#include <TinyWireS.h>


#define I2C_SLAVE_ADDR (0x41)
#define DETECT 1      //zero cross detect, interrupt 0, is physical pin 6
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
// I2C pins: PB2 - SCL, PB0 - SDA
volatile byte i2cValue;

void setup() {
  // set up pins
  TinyWireS.begin(I2C_SLAVE_ADDR);
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control
  GIMSK=1<<PCIE;
  PCMSK=1<<PCINT4;
  
  //attachPCINT(DETECT, zeroCrossingInterrupt, FALLING);
  
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  //attachInterrupt(0, zeroCrossingInterrupt, FALLING);
  TinyWireS.onRequest(requestEvent);
}

//Interrupt Service Routines
/* void zeroCrossingInterrupt() {
  TCNT1 = 0;                //reset timer - count from zero
  TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
} */

ISR (PCINT0_vect){
if (PINB&(1<<PINB1)) { return;}
TCNT1 = 0;                //reset timer - count from zero
TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
 
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  digitalWrite(GATE, HIGH); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  digitalWrite(GATE, LOW);  //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop() {    // use analog input to set the dimmer

  if (TinyWireS.available())  i2cValue = TinyWireS.receive();

  OCR1A = map(i2cValue, 0, 254, 125, 2);
  TinyWireS_stop_check();
}

void requestEvent() {

  TinyWireS.send(i2cValue);
}

Вот код мастера дла Ардуино, он рандомное значение шлет в Тини по i2c и выводит в порт ответ.

#include <Wire.h>
#define device (0x41)

void setup() {
  // put your setup code here, to run once:
  Wire.begin();
  Serial.begin(9600); 
}

void loop() {
  // put your main code here, to run repeatedly:
  
  Wire.beginTransmission(device);
  Wire.write(random(50,240));
  Wire.endTransmission();
  delay(2000);
  Wire.requestFrom(device, 1); 
  while (Wire.available())   // slave may send less than requested
  {
    byte c = Wire.read(); // receive a byte as character
    Serial.println(c);         // print the character
  }
}

Вот схема. Плату могу показать, но буду переделывать.

 

 

Всем спасибо за помощь. Может кому пригодится моя поделка.

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

Umka, в финальном коде ошибка. в 11 и  43 строке измените входной порт на новый.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Да, точно. Но оно почему-то и так работает. Чудо!


// автономный диммер на ATtiny85
// управляется по I2C. По мотивам
//http://forum.arduino.cc/index.php?topic=314773.0

//#include <PinChangeInterrupt.h>
//#include <avr/io.h>
//#include <avr/interrupt.h>
#include <TinyWireS.h>


#define I2C_SLAVE_ADDR (0x41)
#define DETECT 4      //zero cross detect, interrupt 0, is physical pin 6
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
// I2C pins: PB2 - SCL, PB0 - SDA
volatile byte i2cValue;

void setup() {
  // set up pins
  TinyWireS.begin(I2C_SLAVE_ADDR);
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control
  GIMSK=1<<PCIE;
  PCMSK=1<<PCINT4;
 
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  //attachInterrupt(0, zeroCrossingInterrupt, FALLING);
  TinyWireS.onRequest(requestEvent);
}

ISR (PCINT0_vect){
if (PINB&(1<<PINB4)) { return;}
TCNT1 = 0;                //reset timer - count from zero
TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
 
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  digitalWrite(GATE, HIGH); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  digitalWrite(GATE, LOW);  //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop() {    // use analog input to set the dimmer

  if (TinyWireS.available())  i2cValue = TinyWireS.receive();

  OCR1A = map(i2cValue, 0, 254, 125, 2);
  TinyWireS_stop_check();
}

void requestEvent() {

  TinyWireS.send(i2cValue);
}

 

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012
ptr
Offline
Зарегистрирован: 28.05.2016

Мои поздравления!

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Продолжение https://www.youtube.com/watch?v=t1mEsbQYO4Q

И... появилась неприятная фигня. На средних уровнях яркости периодически начинается хаотичное мерцание лампы. Будто помеха какая мешает. Это не ресет, уровень диммирования в целом держит. По ресету сбрасывает. Это не в канале детектора ноля. Осциллограмма ровная. Либо что-то с таймером, либо в железе и монтаже проблема.

Вот видео заболевания https://www.youtube.com/watch?v=07iGUiV968A

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

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Добрые други! У кого есть двухлучевой осциллоскоп дома или на работе? Я могу готовую железку - диммер выслать безвозмездно (то есть даром!) для поимки глюка защелкивания симистора. Такое случается когда управляющий импульс на затворе симистора приходит не в нужный момент и симистор не закрывается. Иногда ровно светит все, а иногда появляется мерцание на средней яркости. Без двух лучей не отловить. 

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

https://goo.gl/photos/s94cSRHdN7XR7X2X9 вот он красвевчик. Управляется по I2C по адресу 41 с чего угодно.

nik182
Offline
Зарегистрирован: 04.05.2015

У меня такая ерунда "иногда появляется мерцание на средней яркости" была, когда импульс открытия симмистора переваливал за 0. 

В вашей программе он может перевалить, когда пропишется неправильное значение в регистр таймера. 

Попробуйте окружить строчку OCR1A = map(i2cValue, 0, 254, 125, 2); запрещением - разрешением прерывания. Хорошобы таймер перед этим останавливать.

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

Я бы даже сказал что некорректно менять OCR1 на ходу. Его можно рассчитать в лупе, положить в временную переменную, а в пррывании детектора ноля переложить в OCR1 .

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Боюсь такое не осилю. Не силен я в таймерах и прерываниях настолько. Как это лучше сделать?

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

Как-то так:



// автономный диммер на ATtiny85
// управляется по I2C. По мотивам
//<a href="http://forum.arduino.cc/index.php?topic=314773.0" rel="nofollow">http://forum.arduino.cc/index.php?topic=314773.0</a>

#include <TinyWireS.h>
#define I2C_SLAVE_ADDR (0x41)
#define DETECT 4      //zero cross detect, interrupt 0, is physical pin 6
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
// I2C pins: PB2 - SCL, PB0 - SDA
volatile byte i2cValue, ocr;

void setup() {
  // set up pins
  TinyWireS.begin(I2C_SLAVE_ADDR);
  pinMode(DETECT, INPUT);      //zero cross detect
  digitalWrite(DETECT, HIGH);  //enable pull-up resistor
  pinMode(GATE, OUTPUT);       //triac gate control
  GIMSK=1<<PCIE;
  PCMSK=1<<PCINT4;
 
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  // set up zero crossing interrupt
  //attachInterrupt(0, zeroCrossingInterrupt, FALLING);
  TinyWireS.onRequest(requestEvent);
}

ISR (PCINT0_vect){
if (PINB&(1<<PINB4)) { return;}
TCNT1 = 0;                //reset timer - count from zero
OCR1A=ocr;
TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
 
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  digitalWrite(GATE, HIGH); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  digitalWrite(GATE, LOW);  //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop() {    // use analog input to set the dimmer

  if (TinyWireS.available())  i2cValue = TinyWireS.receive();

  ocr = map(i2cValue, 0, 254, 125, 2);
  TinyWireS_stop_check();
}

void requestEvent() {

  TinyWireS.send(i2cValue);
}
Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012


// автономный диммер на ATtiny85
// управляется по I2C. По мотивам
//<a href="<a href="http://forum.arduino.cc/index.php?topic=314773.0" rel="nofollow">http://forum.arduino.cc/index.php?topic=314773.0</a>" rel="nofollow"><a href="http://forum.arduino.cc/index.php?topic=314773.0" rel="nofollow">http://forum.arduino.cc/index.php?topic=314773.0</a></a>

#include <TinyWireS.h>
#define I2C_SLAVE_ADDR (0x41)
#define DETECT 4      //zero cross detect, interrupt 0, is physical pin 6
#define GATE 3        //triac gate is physical pin 2
#define PULSE 2       //trigger pulse width (counts)
// I2C pins: PB2 - SCL, PB0 - SDA
volatile byte i2cValue, ocr;

void setup() {
  // set up pins
  TinyWireS.begin(I2C_SLAVE_ADDR);
  DDRB &= ~(1 << DETECT); //zero cross detect
  PORTB |= (1 << DETECT); //enable pull-up resistor
  DDRB |= 1 << GATE; //triac gate control
  GIMSK = 1 << PCIE;
  PCMSK = 1 << PCINT4;
 
  OCR1A = 50;    //initialize the comparator
  TIMSK = _BV(OCIE1A) | _BV(TOIE1);  //interrupt on Compare Match A | enable timer overflow interrupt
  sei();  // enable interrupts
  TinyWireS.onRequest(requestEvent);
}

ISR (PCINT0_vect) {
if (PINB&(1<<PINB4)) { return;}
TCNT1 = 0;                //reset timer - count from zero
OCR1A=ocr;
TCCR1 = B00001011;        // prescaler on 1024, see table 12.5 of the tiny85 datasheet
 
}

ISR(TIMER1_COMPA_vect) {   //comparator match
  PORTB |= (1 << GATE); //set triac gate to high
  TCNT1 = 255 - PULSE;     //trigger pulse width, when TCNT1=255 timer1 overflows
}

ISR(TIMER1_OVF_vect) {      //timer1 overflow
  PORTB &= ~(1 << GATE); //turn off triac gate
  TCCR1 = 0;                //disable timer stop unintended triggers
}

void loop() {    // use analog input to set the dimmer

  if (TinyWireS.available())  i2cValue = TinyWireS.receive();

  ocr = map(i2cValue, 0, 254, 125, 2);
  TinyWireS_stop_check();
}

void requestEvent() {

  TinyWireS.send(i2cValue);
}

Спасибо. Заменил DigitalWritы на запись в порты. Скетч стал компактнее. Диммер пока диммит ровно, будем испытывать.

nik182
Offline
Зарегистрирован: 04.05.2015

тогда уж

void loop() {    // use analog input to set the dimmer

  if (TinyWireS.available())  i2cValue = TinyWireS.receive();
  noInterrupts();
  ocr = map(i2cValue, 0, 254, 125, 2);
  interrupts();
  TinyWireS_stop_check();

}

Оно конечно для байта излишне, но в общем случае полезно.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Вроде лучше, но всеравно подмигивает на 70-80% яркости.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

А с запретом прерывания в лупе не пашет. Пытается лампу зажечь на уровне тления едва заметного и только.

nik182
Offline
Зарегистрирован: 04.05.2015

Библиотека i2c перехватывает прерывания. Приоретета прерывний в тинке нет. Поэтому иногда моргает. Возьмите полностью софтовую библиотеку i2c. Напрмер из темы про тинку тринадцатую.

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Киньте ссылку плиз.

nik182
Offline
Зарегистрирован: 04.05.2015

Во первых. С запретом прерываний очень даже понятно. МАР функция самая затратная по времени и соответственно программа живёт почти всё время в режиме запрета прерываний.

Во вторых. Аппаратного i2c в этом процессоре нет. Софтверного слейва без прерываний я нагуглить не смог. Может быть кто то из товарищей поможет? А с прерываниями, из за отсутствия приорететов, решить проблему со слейвом i2c управления димером без глюков на этом процессоре невозможно. Нужен или аппаратный i2c или вообще процессор, где прерывание можно прервать прерыванием.

Можно попробывать обойтись вообще без прерываний и таймеров. В цикле с помощью millis просто дёргать ноги. 

 

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

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

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Был в исходном тексте к сожалению.

Dimax, у Вас есть осциллограф? Может правда готовый блок выслать? 

I2C тут практически аппаратный,  http://www.atmel.com/images/atmel-2561-using-the-usi-module-as-a-i2c-master_ap-note_avr310.pdf

nik182
Offline
Зарегистрирован: 04.05.2015

Да, я в курсе, сам использую. Вот только слейв без прерываний не работает. Софтверно только мастера в доступе. 

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Ну, значит будем бороть. Оно не всегда и не на всех уровнях проявляется. Для инкубаторов конечно и так сойдет, но хочется до ума довести. Кстати, а многоканального диммера на Меге никому не попадалос? Ну кроме киберлибовского разумеется.

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

Umka пишет:

Dimax, у Вас есть осциллограф? Может правда готовый блок выслать? 

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

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Ну, тогда подарок от Санты под елочку :) 

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

Umka, нужного оптрона для симистора не оказалось, так что повторить полностью эксперимент не вышло. Впрочем это и не нужно, я залил в тиньку скетч того товарища с аруино.сс, в качестве детектора ноля подал 50 герц с генератора, и очень чётко наблюдается глюк. Это даже не совсем глюк, а просто косяк автора, счетный регистр становится меньше чем регистр сравнения, фактически шкала регулировки должна кончаться чуть раньше, чем позволяет сама регулировка. Простейшее решение -ограничить значение OCR1A, это и будет фактический конец шкалы. Но по большому счёту скетч -говно, и лучше бы просто найти другой, написанный более опытным человеком.

nik182
Offline
Зарегистрирован: 04.05.2015

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

nik182
Offline
Зарегистрирован: 04.05.2015

Нашёл схему дактчика пересечения нуля. http://www.fritzler-avr.de/HP/tipps/dimm.php

null3.png

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

nik182, зацепила тема? :-)  Интересно бы посмотреть в протеусе, что за сигнал на выходе оптрона будет, что-то сомнения берут что лучше чем просто два резистора. Но набивать лень.  В идеале нужен прямоугольник с фронтом восхождения точно по нулю.

 Я смотрю и скетчик там алгоритмически точно такой-же как в этом топике. По-моему лучше отказаться от прерывания OVF, а в прерывание COMPA вставить всё что было в прерывании OVF, только перед этим вставить паузу микросекунд на 10. Что б симистр успел открыться.  Эта мера должна предотвратить глюки.

nik182
Offline
Зарегистрирован: 04.05.2015

Давно хотел маленький на стол для всяких нужд. Сейчас мучаю tiny25. Я ей прямо на компаратор 220 подаю. Импульс синхронизации отличный. Тик в тик с пересечением нуля. Эта схема тоже для того что бы тик в тик синхроимпульс выдавать.  

Umka
Umka аватар
Offline
Зарегистрирован: 19.10.2012

Там эта, можно 817 оптрон на вход детектора, с диодным мостом, 817-х много, в любой зарядке для мобилки есть.

Вот бы на Меге8 4-канальный диммер раскачать!