Skip to content

Commit

Permalink
version bump for 1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
AJMansfield committed Jun 7, 2019
2 parents e057486 + 87fe03e commit 4aaf8ae
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 44 deletions.
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,41 @@ The phase offsets are calculated based on the _measured_ mains frequency,
so this code will work regardless of 50/60Hz or any other frequency.
This includes correcting for any inaccuracies in the arduino's oscillator or the mains frequency.

This library was developed specifically to control a Krida 2 CH Dimmer
This library was developed specifically for the Krida 2 CH Dimmer
([amazon](https://www.amazon.com/Dimmer-Module-Controller-Arduino-Raspberry/dp/B06Y1GVG26),
[alibaba](https://mdwdz.en.alibaba.com/product/60670737878-804998378/2CH_AC_LED_Light_Dimmer_Module_Controller_Board.html),
[inmojo](http://www.inmojo.com/store/krida-electronics/item/2-channel-ac-led-bulb-dimmer-module-v2/)),
but it should work with other phase-control dimming circuits that output a positive edge on their sync signal.
and has been tested to work with the RobotDyn AC Dimmer
([robotdyn](https://robotdyn.com/ac-light-dimmer-module-1-channel-3-3v-5v-logic-ac-50-60hz-220v-110v.html)),
and should work fine with other phase-control dimming circuits that output a positive edge on their sync signal.

See [the example](examples/basic_example/basic_example.ino) for an example of how to use the library.
The library methods themselves are documented in [the library header](src/TriacDimmer.h).

This library **requires** the use of pins 8, 9, and 10.
Pin 8 _must_ be used as the sync input, and pins 9 and 10 _must_ be used as the channel outputs.
This library **requires** the use of certain pins.
Pin 8 _must_ be used as the sync input, and pins 9 and 10 are the only pins that can be used as channel outputs.
This library _will not work_ on any other pins, period.

![fritzing diagram](fritzing.png)
![fritzing diagram](fritzing.png)

## Flickering, and How to Fix It

If you experience issues with flickering, there are a handful of parameters you can pass to `begin` that can be adjusted depending on what sort of flickering you have.
By default, with no arguments the library uses these values as defaults:

```cpp
TriacDimmer::begin(pulse_length = 20, min_trigger = 2000, on_thresh = 2, off_thresh = 0.01)
```
First off, if you experience flickering regardless of the brightness value you set, increase `pulse_length` from the default `20` to a larger value like `50` or `100` until `TriacDimmer::setBrightness(pin, 0.5);` results in a stable glow.
Once it's stable at 0.5, set the brightness 1.0 (`TriacDimmer::setBrightness(pin, 1.0);`) and check for flickering.
There shouldn't be any, but if there is you can increase `min_trigger` from the default `2000` to perhaps `3000` or `4000` until the flickering stops.
If you're still experiencing flickering no matter how large `min_trigger` is, you can also try setting `on_thresh` to below the highest brightness level that causes flickering. This shouldn't normally be necessary though, as adjusting `min_trigger` should normally be enough.
The last step is to figure out the lowest brightness value can sustain without flickering.
By default the library is set to cut off completely for brightness values smaller than `0.01`, but if you still see flickering at `0.015` or `0.02` you can try setting `off_thresh` to a value that's larger than that.
If you've tried all of these steps and you still get flickering, consider opening an issue.
Make sure to include as much information about your setup as you can, including the specific dimmer board you're using.
Also, if you have access to an oscilliscope, screenshots are always helpful in trying to diagnose flickering.
20 changes: 10 additions & 10 deletions examples/basic_example/basic_example.ino
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,31 @@ unsigned char channel_1 = 9; // channel 1 pin
unsigned char channel_2 = 10; // channel 2 pin

void setup() {
//initialize the dimmer library. We want
// initialize the dimmer library.
TriacDimmer::begin();
}

void loop() {
//gradually increase brightness over time
for(float brightness = 0.01; brightness < 0.99; brightness += 0.01){
// gradually increase brightness over time
for(float brightness = 0.00; brightness < 1.00; brightness += 0.01){

//set channel 1 to the brightness value:
// set channel 1 to the brightness value:
TriacDimmer::setBrightness(channel_1, brightness);

//invert brightness for channel 2:
// invert brightness for channel 2:
TriacDimmer::setBrightness(channel_2, 1 - brightness);

delay(20);
}

//and back down - decrease brightness over time
for(float brightness = 0.99; brightness > 0.01; brightness -= 0.01){
// and back down - decrease brightness over time
for(float brightness = 1.00; brightness > 0.00; brightness -= 0.01){

//set channel 1 to the brightness value:
// set channel 1 to the brightness value:
TriacDimmer::setBrightness(channel_1, brightness);

//invert brightness for channel 2:
TriacDimmer::setBrightness(channel_2, brightness);
// invert brightness for channel 2:
TriacDimmer::setBrightness(channel_2, 1 - brightness);

delay(20);
}
Expand Down
1 change: 1 addition & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ begin KEYWORD2
end KEYWORD2
setBrightness KEYWORD2
getCurrentBrightness KEYWORD2
disable KEYWORD2

######################################
# Constants/defines (LITERAL1)
Expand Down
4 changes: 2 additions & 2 deletions library.properties
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name=TriacDimmer
version=1.0.3
version=1.1.0
author=Anson Mansfield <[email protected]>
maintainer=Anson Mansfield <[email protected]>
sentence=A library for controlling a triac dimmer.
paragraph=Abuses the PWM capabilities of Timer 1 to offload all timing code from the CPU.
paragraph=Uses the advanced capabilities of the Timer 1 perhipheral to offload all timing code from the CPU, resulting in much more accurate timing than is possible normally.
category=Device Control
url=https://github.com/AJMansfield/TriacDimmer
architectures=avr
Expand Down
121 changes: 99 additions & 22 deletions src/TriacDimmer.cpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,40 @@
#include "TriacDimmer.h"
#include <Arduino.h>
#include <assert.h>
#include <util/atomic.h>

volatile uint16_t TriacDimmer::detail::pulse_length;
volatile uint16_t TriacDimmer::detail::period = 16667;
uint16_t TriacDimmer::detail::pulse_length;
uint16_t TriacDimmer::detail::min_trigger;
float TriacDimmer::detail::on_thresh;
float TriacDimmer::detail::off_thresh;

volatile bool TriacDimmer::detail::ch_A_en;
volatile uint16_t TriacDimmer::detail::ch_A_up;
volatile uint16_t TriacDimmer::detail::ch_A_dn;
volatile uint16_t ch_A_dn_buf;

volatile bool TriacDimmer::detail::ch_B_en;
volatile uint16_t TriacDimmer::detail::ch_B_up;
volatile uint16_t TriacDimmer::detail::ch_B_dn;
volatile uint16_t ch_B_dn_buf;

uint16_t last_icr = 0;
volatile uint16_t TriacDimmer::detail::period = 16667;


void TriacDimmer::begin(uint16_t pulse_length){
void TriacDimmer::begin(uint16_t pulse_length, uint16_t min_trigger, float on_thresh, float off_thresh){
// Don't need atomic writes since the ISRs haven't started yet.
TriacDimmer::detail::pulse_length = pulse_length;
TriacDimmer::detail::min_trigger = min_trigger;
TriacDimmer::detail::on_thresh = on_thresh;
TriacDimmer::detail::off_thresh = off_thresh;

TCCR1A = 0;
TCCR1B = _BV(ICNC1) //input capture noise cancel
| _BV(ICES1) //positive edge
| _BV(CS11); // /8 prescaler

pinMode(8, INPUT);
pinMode(9, OUTPUT);
pinMode(10, OUTPUT);

TIFR1 = _BV(ICF1); //clear IC interrupt flag
TIMSK1 = _BV(ICIE1); //enable input capture interrupt
Expand All @@ -35,16 +50,37 @@ void TriacDimmer::end(){
void TriacDimmer::setBrightness(uint8_t pin, float value){
assert(pin == 9 || pin == 10);

if (value > TriacDimmer::detail::on_thresh) {
digitalWrite(pin, HIGH);
TriacDimmer::disable(pin);
} else if (value < TriacDimmer::detail::off_thresh) {
digitalWrite(pin, LOW);
TriacDimmer::disable(pin);
} else {
if ((pin & 0x01) == 0x01){ // if (pin == 9){
TriacDimmer::detail::setChannelA(1 - value);
TriacDimmer::detail::ch_A_en = true;
} else { // if (pin == 10){
TriacDimmer::detail::setChannelB(1 - value);
TriacDimmer::detail::ch_B_en = true;
}
}
pinMode(pin, OUTPUT); // only set to output once configured.
}

void TriacDimmer::disable(uint8_t pin) {
assert(pin == 9 || pin == 10);

if ((pin & 0x01) == 0x01){ // if (pin == 9){
TriacDimmer::detail::setChannelA(1 - value);
TriacDimmer::detail::disableChannelA();
} else { // if (pin == 10){
TriacDimmer::detail::setChannelB(1 - value);
TriacDimmer::detail::disableChannelB();
}
}

float TriacDimmer::getCurrentBrightness(uint8_t pin){
assert(pin == 9 || pin == 10);

if ((pin & 0x01) == 0x01){ // if (pin == 9){
return 1 - TriacDimmer::detail::getChannelA();
} else { // if (pin == 10){
Expand All @@ -53,36 +89,77 @@ float TriacDimmer::getCurrentBrightness(uint8_t pin){
}

void TriacDimmer::detail::setChannelA(float value){
TriacDimmer::detail::ch_A_up = TriacDimmer::detail::period * value;
TriacDimmer::detail::ch_A_dn = TriacDimmer::detail::ch_A_up + TriacDimmer::detail::pulse_length;
uint16_t p, u, d;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// Don't need atomic read for pulse_length or min_trigger since they're not updated from an ISR.
p = TriacDimmer::detail::period;
}
u = p * value;
d = constrain(u + TriacDimmer::detail::pulse_length, TriacDimmer::detail::min_trigger, p);
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
TriacDimmer::detail::ch_A_up = u;
TriacDimmer::detail::ch_A_dn = d;
}
}

void TriacDimmer::detail::setChannelB(float value){
TriacDimmer::detail::ch_B_up = TriacDimmer::detail::period * value;
TriacDimmer::detail::ch_B_dn = TriacDimmer::detail::ch_B_up + TriacDimmer::detail::pulse_length;
uint16_t p, u, d;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
// Don't need atomic read for pulse_length or min_trigger since they're not updated from an ISR.
p = TriacDimmer::detail::period;
}
u = p * value;
d = constrain(u + TriacDimmer::detail::pulse_length, TriacDimmer::detail::min_trigger, p);
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
TriacDimmer::detail::ch_B_up = u;
TriacDimmer::detail::ch_B_dn = d;
}
}

float TriacDimmer::detail::getChannelA(){
return (float)TriacDimmer::detail::ch_A_up / TriacDimmer::detail::period;
uint16_t p, u;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
p = TriacDimmer::detail::period;
u = TriacDimmer::detail::ch_A_up;
}
return (float)p / u;
}

float TriacDimmer::detail::getChannelB(){
return (float)TriacDimmer::detail::ch_B_up / TriacDimmer::detail::period;
uint16_t p, u;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
p = TriacDimmer::detail::period;
u = TriacDimmer::detail::ch_B_up;
}
return (float)p / u;
}

void TriacDimmer::detail::disableChannelA(){
TriacDimmer::detail::ch_A_en = false;
TCCR1A &=~ _BV(COM1A0) | _BV(COM1A1);
}

void TriacDimmer::detail::disableChannelB(){
TriacDimmer::detail::ch_B_en = false;
TCCR1A &=~ _BV(COM1B0) | _BV(COM1B1);
}

ISR(TIMER1_CAPT_vect){
TIMSK1 &=~ (_BV(OCIE1A) | _BV(OCIE1B)); //clear interrupts, in case they haven't run yet
TCCR1A &=~ (_BV(COM1A1) | _BV(COM1B1));
TCCR1A &=~ (_BV(COM1A0) | _BV(COM1B0));
TCCR1C = _BV(FOC1A) | _BV(FOC1B); //ensure outputs are properly cleared

OCR1A = ICR1 + TriacDimmer::detail::ch_A_up;
OCR1B = ICR1 + TriacDimmer::detail::ch_B_up;
ch_A_dn_buf = TriacDimmer::detail::ch_A_dn;
ch_B_dn_buf = TriacDimmer::detail::ch_B_dn;

TCCR1A |= _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1); //set OC1x on compare match
TCCR1A |=
( TriacDimmer::detail::ch_A_en ? _BV(COM1A0) | _BV(COM1A1) : 0 ) |
( TriacDimmer::detail::ch_B_en ? _BV(COM1B0) | _BV(COM1B1) : 0 ); //set OC1x on compare match, only if enabled
TIFR1 = _BV(OCF1A) | _BV(OCF1B); //clear compare match flags
TIMSK1 |= _BV(OCIE1A) | _BV(OCIE1B); //enable input capture and compare match interrupts


static uint16_t last_icr = 0;
TriacDimmer::detail::period = ICR1 - last_icr;
last_icr = ICR1;

Expand All @@ -97,9 +174,9 @@ ISR(TIMER1_CAPT_vect){

ISR(TIMER1_COMPA_vect){
TIMSK1 &=~ _BV(OCIE1A); //disable match interrupt
TCCR1A |= _BV(COM1A1); //clear OC1x on compare match
TCCR1A &=~ _BV(COM1A0); //clear OC1x on compare match

OCR1A = ICR1 + TriacDimmer::detail::ch_A_dn;
OCR1A = last_icr + ch_A_dn_buf;

if((signed)(TCNT1 - OCR1A) >= 0){
TCCR1C = _BV(FOC1A); //interrupt ran late, trigger match manually
Expand All @@ -109,9 +186,9 @@ ISR(TIMER1_COMPA_vect){

ISR(TIMER1_COMPB_vect){
TIMSK1 &=~ _BV(OCIE1B); //disable match interrupt
TCCR1A |= _BV(COM1B1); //clear OC1x on compare match
TCCR1A &=~ _BV(COM1B0); //clear OC1x on compare match

OCR1B = ICR1 + TriacDimmer::detail::ch_B_dn;
OCR1B = last_icr + ch_B_dn_buf;

if((signed)(TCNT1 - OCR1B) >= 0){
TCCR1C = _BV(FOC1B); //interrupt ran late, trigger match manually
Expand Down
Loading

0 comments on commit 4aaf8ae

Please sign in to comment.