Page 1 of 3 123 LastLast
Results 1 to 10 of 26

Thread: Phil's Dimmer

  1. #1
    Join Date
    May 2007
    Posts
    5,571
    Post Thanks / Like

    Default Phil's Dimmer

    I've been trolling along on the design of a library for multi-channel AC dimming using tables for determining the turn-on timing for the triacs/scrs in the external solid-state relays (SSRs). At the moment I have the interrupts and timers working to my satisfaction on an Arduino uno smd board (verified by observing the waveforms with an oscilloscope). There's still a lot of work, though, such as expanding it to multiple channels, adding the dimming table support, determining which Arduino boards to support, configuring the which pins are going to drive SSRs, possibly converting the timer ISR to assembly language, etc. And figuring out how to avoid offending the various Arduino sensibilities. It's a long ways from being ready for prime-time, but getting the timers and ISRs to work is a significant milestone imo. The rest is mostly just grinding out c++ code.
    Phil

  2. #2
    Join Date
    Dec 2011
    Location
    UK S80 postcode
    Posts
    1,535
    Post Thanks / Like

    Default Re: Phil's Dimmer

    Hi Phil, I found the manual for the ATmega's quite good, once you step past all the macros that are in common use in the IDE and get to the bare bones you can get their full potential (or at least in my case the best my limited programming can extract).

  3. #3
    Join Date
    May 2007
    Posts
    5,571
    Post Thanks / Like

    Default Re: Phil's Dimmer

    I've been using that a lot. It's fine, although I find it a bit more difficult to use than the PIC datasheets. Just familiarity, I suppose. In any case I'm beyond dealing with the hardware.
    Phil

  4. #4
    Join Date
    Dec 2011
    Location
    UK S80 postcode
    Posts
    1,535
    Post Thanks / Like

    Default Re: Phil's Dimmer

    Hi Phil, I know sometimes it helps to see other peoples work at least it does for me.

    Code:
      //********* PORTS *********
      DDRD &= ~B00101000; // set PD3 & PD5 as inputs.
      DDRB |= B00000110; // set PB1 & PB2 as outputs.
      PORTB &= ~B00000110; // set PB1 & PB2 low.
      
      //********* INTERRUPT 1 *********
      EICRA = B00000000; // clear EICRA register.
      EICRA |= (1 << ISC10) | (1 << ISC11); // set interrupt on rising.
      EIMSK |= (1 << INT1); // enable interrupt.
      OCR2A = 50; // set OCR2A (count to) value about 40uS inc int.
      
      //********* CLEAR TIMER INTERRUPTS *********
      TIMSK0 = B00000000; // clear any T0 interrupts.
      TIMSK1 = B00000000; // clear any T1 interrupts.
      TIMSK2 = B00000000; // clear any T2 interrupts.
      
      //********* TIMER 2 *********
      TIMSK2 = (1 << OCIE2A); // enable interrupt.
      TCCR2A = B00000000; // clear TCCR1A register.
      
      //********* TIMER 1 *********
      TCCR1A = B00000000; // clear TCCR1A register.
      TCCR1A |= (1 << COM1B0) | (1 << COM1B1) | (1 << COM1A0) | (1 << COM1A1); // set OCR1A\B WGM to set high on compare match.
      TCCR1B = B00000000; // clear TCCR1B register.
      TCCR1B |= (1 << CS11) | (1 << CS12) | (1 << WGM12); // set ext input, CTC mode &  falling trigger.
      TCNT1 = 0; // set TCNT1 counter register to zero.
      OCR1B = count_A; // set OCR1B (1st count to value).
      OCR1A = count_B; // set OCR1A (2nd count to value).
    }
    //********* ISR interrupt 1 ********
    ISR (INT1_vect) {
      TCCR2B |= (1 << CS21); // start timer 2 & set prescale to 1/8 clock.
      TCNT2 = 0; // reset timer 2 counter to zero.
      OCR2A = 50; // set OCR2A (count to value) about 40uS including interrupt time.
    }
    //********* ISR timer 2 *********
    ISR (TIMER2_COMPA_vect) {
      TCCR2B = B00000000; // stop timer 2.
      TCNT1 = 0; // reset timer 1 counter to zero.
      TCCR1A &= ~(1 << COM1B0) & ~(1 << COM1A0); // timer 1 WGM to set low on compare match.
      TCCR1C |= (1 << FOC1A) | (1 << FOC1B); // timer 1 force compare match (set outputs low).
      TCCR1A |= (1 << COM1B0) | (1 << COM1A0); // timer 1 WGM to set high on compare match.

  5. #5
    Join Date
    May 2007
    Posts
    5,571
    Post Thanks / Like

    Default Re: Phil's Dimmer

    Here's the top-level header file for the dimmer library that I'm constructing:

    Code:
    #ifndef PSDIMMER_H
    #define PSDIMMER_H
    
    // dimmer_table_t values are used when specifying a dimmer curve for a channel
    enum dimmer_table_t {UNIFORM_DELAY,LED_CURVE,INCAND_CURVE};
    
    // AC_sense_mode_t is used internally within the library, and is not accessed
    //   by the sketch using this library.
    enum AC_sense_mode_t {AC_PULSE_HIGH, AC_PULSE_LOW, AC_LEVEL};
    
    class PSDimmer {
     public:
      PSDimmer(unsigned _AC_sense_pin, // the zero-crossing input pin
    	   byte *flag,		   // bit 0 set when ZC changes
    	   unsigned _test_pin);	   // used by the library developer
      void assign_pin_to_channel(unsigned channel,unsigned pin);
      void stop_output();
      void start_output();
      void dimmer_table_init(unsigned n, // 1 table is supported now
    			 dimmer_table_t t, // only UNIFORM_DELAY supported now
    			 unsigned percent_low,
    			 unsigned percent_high,
    			 float quasi_gamma); // for other dimmer tables
      
      void setPower(byte channel, byte powerLevel);
      void activatePowerLevelChanges();
      
    private:
    				// stuff that the sketch doesn't access directly
      unsigned int AC_period;	// in units of uS in units of 1/2 uS
      AC_sense_mode_t AC_sense_mode; // auto-detected in constructor
      byte AC_sense_pin;
    
    };
    #endif
    At the moment there is only one dimmer curve supported at a time because there isn't enough RAM in the '328 for more than that, although I intend to provide a method for using the program FLASH for more than one table. The tables will be constructed in the Arduino based on parameters passed to the dimmer_table_init function. Several different types of tables would be supported, with parameters to further customize each table (e.g. different percent_lo and percent_high parameters for different color strings of a given type, and perhaps quasi_gamma to allow some tailoring of the curves to fit human eye response). This is still a work in progress, but this is where I'm heading.
    Phil

  6. #6
    Join Date
    Dec 2011
    Location
    UK S80 postcode
    Posts
    1,535
    Post Thanks / Like

    Default Re: Phil's Dimmer

    Hi Phil, using flash works a treat and is quite simple.

    Store data as an array called 'lookup' in flash:
    Code:
     const byte PROGMEM lookup[256][13] {  // comment after line = 8 bit value, 12 bit gamma corrected value
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, //0,0
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //1,1
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //2,1
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //3,1
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //4,2
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0xF0}, //5,2
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0xF0}, //6,2
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F}, //7,3
    
    Through to
    
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0xF0}, //247,3842
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //248,3873
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, //249,3905
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //250,3936
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //251,3968
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //252,3999
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //253,4031
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0}, //254,4063
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}  //255,4095
    A total of 256 lines each containing 13 bytes of data.

    Then read it back:
    Code:
        // output TLS data
        while (true) {  // loop
          pix_val = data[m];
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][0]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][1]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][2]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][3]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][4]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][5]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][6]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][7]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][8]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][9]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][10]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][11]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][12]));  // put data in uart Tx buffer
    
          if (!(m < 512)) break;  // drop out of loop if all data tranfered
          m++;
        }
    In this case UDR0 is the register of the serial output buffer that the data is read into.
    UDRE0 is the bit in the UCSR0A register that shows weather the buffer is empty - if not you overwrite unsent data.

    For arrays the following applies and in the case above:

    pgm_read_byte_near(&(lookup[pix_val][12]))

    or

    pgm_read_byte_near(&(<name of file stored in flash>[<line number of array>][<index number of the line>]))

  7. #7
    Join Date
    Dec 2011
    Location
    UK S80 postcode
    Posts
    1,535
    Post Thanks / Like

    Default Re: Phil's Dimmer

    Hi Phil, using flash works a treat and is quite simple.

    Store data as an array called 'lookup' in flash:
    Code:
     const byte PROGMEM lookup[256][13] {  // comment after line = 8 bit value, 12 bit gamma corrected value
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, //0,0
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //1,1
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //2,1
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //3,1
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //4,2
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0xF0}, //5,2
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0xF0}, //6,2
      {0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0x0F}, //7,3
    
    Through to
    
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F, 0xF0}, //247,3842
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0x0F}, //248,3873
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0}, //249,3905
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //250,3936
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //251,3968
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //252,3999
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}, //253,4031
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0xF0}, //254,4063
      {0xF0, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F}  //255,4095
    A total of 256 lines each containing 13 bytes of data.

    Then read it back:
    Code:
        // output TLS data
        while (true) {  // loop
          pix_val = data[m];
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][0]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][1]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][2]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][3]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][4]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][5]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][6]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][7]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][8]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][9]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][10]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][11]));  // put data in uart Tx buffer
          while ( ! (UCSR0A & (1 << UDRE0)) ); // Wait for buffer to empty
          UDR0 = pgm_read_byte_near(&(lookup[pix_val][12]));  // put data in uart Tx buffer
    
          if (!(m < 512)) break;  // drop out of loop if all data tranfered
          m++;
        }
    In this case UDR0 is the register of the serial output buffer that the data is read into.
    UDRE0 is the bit in the UCSR0A register that shows weather the buffer is empty - if not you overwrite unsent data.

    For arrays the following applies and in the case above:

    pgm_read_byte_near(&(lookup[pix_val][12]))

    or

    pgm_read_byte_near(&(<name of array stored in flash>[<line number of array>][<index number of the line>]))

  8. #8
    Join Date
    May 2007
    Posts
    5,571
    Post Thanks / Like

    Default Re: Phil's Dimmer

    Still trolling away on this, with most of my recent attention focused on porting red-black binary balanced tree code from an old c program that I wrote many years ago to an arduino library implementation specific to current project. It now appears to be working, but still needs more testing. This library would be used when changing an output powerLevel. The dimmer timer interrupt service routine (ISR) needs a list of update times sorted in increasing activation-time order, and the most naive way of implementing lhis (a linked list) could result in traversing an entire 32-element list (for 16 channels) or a 64-element list (for 32 channels) in the worst case.

    Now it's mostly a matter of stitching the (test) sketch and the various libraries together and doing more testing, as well as a fair bit of effort to implement the logic for user-configuring the dimmer tables (and a lot of documentation effort if this library is ever released). Some of the testing can occur in the next day or two with just the Arduino, but some will have to wait until I get home (likely over the weekend) and have access to some SSRs and more LED strings there.

    The motivation for continuing this project is waning, however, due to the other solutions that Angus has located and implemented and appears to be satisfactory for his flood setup.
    Phil

  9. #9
    Join Date
    Dec 2011
    Posts
    7,089
    Post Thanks / Like

    Default Re: Phil's Dimmer

    HI Phil I'm glued to your thread and progress .

    Waning ? You are the only solution !

    I have not found a solution for this FLood yet .
    What I have done to this point is gained some AC experience via Wifi control using the Esp module and in a very crude method .

    Mini incandescent strings I am able to dim from full brightness to off
    Standard led strings also.
    5w incandescent can be dimmed but not turned completely off
    The Floods can be controlled only with a parallel load .

    That said , There is no complete range control (0 - 255) with this method and seems
    useless for e1.31

    Many sad/long faces as you can see . So I hope this restores your motivation .
    Certainly you can see there is no solution available for this hobby yet ,not to mention stand alone .
    Yes there is a few lib's out there and a lot of hot air really when when it comes to AC control.

  10. #10
    Join Date
    May 2007
    Posts
    5,571
    Post Thanks / Like

    Default Re: Phil's Dimmer

    Despite waning motivation (it's got a ways to go before it hits 0), I'm continuing on the project.

    Even though it's somewhat changing to topic of this thread (and is thus likely to be get lost in the sands of time), describe what is happening with the 5W incandescents. First, what are they? Night-lights, a single C7 light on an AC string, or what? I'm a bit surprised that you aren't getting the same results as with the incandescent mini-light strings.

    As far as your flood lights go, they are still an uncertain target, because I don't know how those PM2014 chips work and what sort of setup that they would need to work in any situation where their AC power is turned on and off over the course of an AC half-cycle.
    Phil

Page 1 of 3 123 LastLast

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •