AskSin++
PhaseCut.cpp
1 //- -----------------------------------------------------------------------------------------------------------------------
2 // AskSin++
3 // 2017-03-29 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
4 // 2019-01-11 scuba82: highly inspired by https://github.com/blackhack/ArduLibraries/tree/master/DimmerOne
5 //- -----------------------------------------------------------------------------------------------------------------------
6 /*
7  Fires an interrupt, each time the zero-cross detection pin changes. Output pin will be held up high (trailing-edge phase cut) for a certain time,
8  correspondig to the called dim value. The pre defined values are calculated for 50Hz mains, 8Mhz CPU frequence and a prescaler of 1024.
9  PHASECUTMODE == 1 -> leading-edge phase cut; PHASECUTMODE == 0 trailing-edge phase cut
10 */
11 
12 #include "PhaseCut.h"
13 namespace as {
14 
15 #if defined(ARDUINO_ARCH_AVR) && ! (defined(ARDUINO_AVR_ATmega32) ||defined(__AVR_ATmega128__))
16  PhaseCut phaseCut;
17 
18  PhaseCut::PhaseCut()
19  {
20  isInit = false;
21  }
22 
23  void PhaseCut::init(uint8_t output_pin)
24  {
25  if (isInit)
26  return;
27  isInit = true;
28  ZERO_CROSS_PIN = ZEROPIN;
29  OUTPUT_PIN = output_pin;
30  _timer = 0;
31  running = false;
32 
33  pinMode(OUTPUT_PIN, OUTPUT);
34  pinMode(ZERO_CROSS_PIN, INPUT);
35 
36  TCCR2A = 0;
37  TCCR2B = 0;
38 
39  uint8_t oldSREG = SREG;
40  cli();
41  SetTimer();
42  SREG = oldSREG;
43 
44  TIMSK2 |= (1 << OCIE2A); // Enable COMPA and COMPB interruptions of TIMER2
45  _valid_zero_crossing = true;
46  }
47 
48  bool PhaseCut::Start()
49  {
50  if (!isInit)
51  return false;
52  uint8_t oldSREG = SREG;
53  cli();
54  SREG = oldSREG;
55  attachInterrupt(digitalPinToInterrupt(ZERO_CROSS_PIN), ZeroCrossEventCaller, CHANGE);
56  running = true;
57  return true;
58  }
59 
60  bool PhaseCut::Stop()
61  {
62  if (!isInit)
63  return false;
64  detachInterrupt(digitalPinToInterrupt(ZERO_CROSS_PIN));
65  digitalWrite(OUTPUT_PIN, LOW);
66  running = false;
67  return false;
68  }
69 
70  bool PhaseCut::isrunning()
71  {
72  return running;
73  }
74  bool PhaseCut::SetDimValue(double value)
75  {
76  if (!isInit)
77  return false;
78  _timer = value;
79 
80  return true;
81  }
82 
83  void PhaseCut::SetTimer()
84  {
85  OCR2A = _timer;
86  }
87 
88  void ZeroCrossEventCaller()
89  {
90  phaseCut.ZeroCrossEvent();
91  }
92 
93 
94  ISR(TIMER2_COMPA_vect)
95  {
96  phaseCut.CmpAEvent();
97  }
98 
99  void PhaseCut::ZeroCrossEvent()
100  {
101  if (!_valid_zero_crossing)
102  return;
103  _valid_zero_crossing = false;
104  phaseCut.Fire();
105  TCNT2 = 0; // Restart counter (no need to call cli() inside an ISR)
106  TCCR2B |= (1 << WGM21) | (1 << CS20) | (1 << CS21) | (1 << CS22) ; // Enable/start CTC and set prescaler to 1024
107  }
108 
109  void PhaseCut::Fire()
110  {
111  #if PHASECUTMODE == 1
112  digitalWrite(OUTPUT_PIN, LOW);
113  #else
114  if ( _timer > 0 ) digitalWrite(OUTPUT_PIN, HIGH);
115  #endif
116 
117  }
118 
119  void PhaseCut::CmpAEvent()
120  {
121  #if PHASECUTMODE == 1
122  if ( _timer < 75 )digitalWrite(OUTPUT_PIN, HIGH);
123  #else
124  digitalWrite(OUTPUT_PIN, LOW);
125  #endif
126 
127  TCCR2B = 0; // Disable/stop CTC
128  SetTimer();
129  _valid_zero_crossing = true;
130  }
131 #endif
132 }