Split from: Interrupt Controlled Multi (8) Channel PWM 60Hz

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello guys, 

 

I am trying to do a similar thing. Instead of 60Hz, I am working on 50Hz and 6 channels instead of 8. Also I want to achieve the same but in 3 phase. 

 

 

I tried looking through the code but the page is lost. Can anyone please post the code.

 

I am uploading a schematic. It is based on a couple of Circuits i researched. 

 

Any suggestions or ideas will be great.

 

Thanks,
wurs10

Attachment(s): 

Last Edited: Sat. Jan 27, 2018 - 02:23 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Instead of reviving a 11 year old thread you are probably better of searching on github.

https://github.com/search?utf8=%...

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks for the reply. I read the entire post and was curious to know how the ISR Works. 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found the code.

 

/*
===========================================================================
 Filemane	: main.c
 Project	: DMX_Dimmer
 Author		: Dan Brateris
 Email		: dan@djdanonline.com
 Function	: 8ch DMX Dimmer
 Compiler  	: WinAVR 20090313  (http://sourceforge.net/projects/winavr/)
 MCU       	: ATmega32A
 XTAL      	: 8 MHz
 Creation	: 11/22/2009
 Version	: Version 1.0
 
 History	:
 Date		:
===========================================================================
*/

// Includes
#include "lcd.h"
#include <avr/io.h>
#include <inttypes.h>
#include <avr/eeprom.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// Set Oscillator, Buad Rate UBRR Value
#define FOSC 8000000L		
#define BAUD 250000
#define MYUBRR (FOSC/(16*BAUD))-1

// Type Definitions
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;

// Prototype Functions
void lcd_writedmx(u16 DMX_Address, int x, int y);
void lcd_write_startup_screen();
void lcd_write_running_screen();
void lcd_write_ch_values();
void increment_dmx();
void decrement_dmx();
void init_usart(unsigned int ubrr);

// Volatile char options;
volatile int startdmx;					// The stored start DMX value
volatile int dmxcount;					// Current dmx frame count
volatile char ChannelData[8];			// Valid DMX Channel Data
volatile char PhaseAngleValues[8];		// Phase Angle Modulation(PAM) values
volatile char PhaseAngleCount = 255;	// Software PAM counter
volatile char OutputPortValue = 0x00;	// Temp output port values

// Pointer to store startdmx in EEPROM
const unsigned int startdmx_pointer = 0x36;

/*
Interrupt to Receive DMX Signal
*/
ISR (USART_RXC_vect)
{
	uint8_t status, data;
	status = UCSRA;
	data = UDR;

	// Check for Frame Error, or Data Over Run Error if true reset dmxcount, start over
	if(status & 0x10 || status & 0x08){dmxcount = 0;return;}

	// Check for start condition
	if(dmxcount == 0){
	 	if(data == 0){
			dmxcount++;
			return;
		} 
	}
	if(dmxcount==startdmx)ChannelData[0]=data;
	if(dmxcount==(startdmx+1))ChannelData[1]=data;
	if(dmxcount==(startdmx+2))ChannelData[2]=data;
	if(dmxcount==(startdmx+3))ChannelData[3]=data;
	if(dmxcount==(startdmx+4))ChannelData[4]=data;
	if(dmxcount==(startdmx+5))ChannelData[5]=data;
	if(dmxcount==(startdmx+6))ChannelData[6]=data;
	if(dmxcount==(startdmx+7))ChannelData[7]=data;
	dmxcount++;
}

/*
Mains Zero Corss Sync
*/
ISR (INT0_vect)
{
	OutputPortValue = 0x00; // Clear the temp output variable
	PORTB = 0x00;			// Turn All Triacs Off
	PhaseAngleCount = 0xFF;	// Reset the PhaseAngleCount
	TCNT1 = 0x00;			// Reset the phase angle modulation counter, sync

	// Load the current DMX values into the phase angle control values
	PhaseAngleValues[0] = ChannelData[0];
	PhaseAngleValues[1] = ChannelData[1];
	PhaseAngleValues[2] = ChannelData[2];
	PhaseAngleValues[3] = ChannelData[3];
	PhaseAngleValues[4] = ChannelData[4];
	PhaseAngleValues[5] = ChannelData[5];
	PhaseAngleValues[6] = ChannelData[6];
	PhaseAngleValues[7] = ChannelData[7];
}

/*
Button Pressed
*/
ISR (INT1_vect)
{

}

/*
Timer 1 Compare A Match
Software Phase Angle Modulation
*/
ISR (TIMER1_COMPA_vect)
{
	// Check if a PWM Count has reached a channel value
	// If so, turn that channel on
	if(PhaseAngleValues[0] == PhaseAngleCount)OutputPortValue |= 0b00000001;
	if(PhaseAngleValues[1] == PhaseAngleCount)OutputPortValue |= 0b00000010;
	if(PhaseAngleValues[2] == PhaseAngleCount)OutputPortValue |= 0b00000100;
	if(PhaseAngleValues[3] == PhaseAngleCount)OutputPortValue |= 0b00001000;
	if(PhaseAngleValues[4] == PhaseAngleCount)OutputPortValue |= 0b00010000;
	if(PhaseAngleValues[5] == PhaseAngleCount)OutputPortValue |= 0b00100000;
	if(PhaseAngleValues[6] == PhaseAngleCount)OutputPortValue |= 0b01000000;
	if(PhaseAngleValues[7] == PhaseAngleCount)OutputPortValue |= 0b10000000;

	// Assert the output signals
	PORTB = OutputPortValue;

	//Decrement the software PWMCount
	PhaseAngleCount--;
}

/*
Main Function
*/
int main (void)
{
	// Initialize Microprocessor
	ioinit();
	init_usart(MYUBRR);

	lcd_init(LCD_DISP_ON);		// Initialize LCD
	lcd_write_startup_screen();	// Show Startup Screen

	// Load Stored DMX Address
	startdmx = eeprom_read_word((int*)startdmx_pointer);
	if(startdmx <= 0 || startdmx >= 512){
		startdmx = 1;
	}

	// Show Runing Screen
	lcd_write_running_screen();
	lcd_writedmx(startdmx,13,0);

	// Enable Interrupts
	sei();

	//lcd_clrscr();

	// Main Loop
	while(1)
	{
		// Button Checks
		if(bit_is_clear(PINC,0)){
			_delay_ms(70);
			if(bit_is_clear(PINB,0)){
				//lcd_clrscr();
				//lcd_puts("Switch 1 Pressed");
			}
		}
		if(bit_is_clear(PINC,1)){
			_delay_ms(70);
			if(bit_is_clear(PINB,1)){
				//lcd_clrscr();
				//lcd_puts("Switch 2 Pressed");
			}
		}
		if(bit_is_clear(PINC,6)){
			_delay_ms(70);
			if(bit_is_clear(PINB,6)){
				decrement_dmx();
				lcd_writedmx(startdmx,13,0);
				//lcd_puts("Switch 3 Pressed");
			}
		}
		if(bit_is_clear(PINC,7)){
			_delay_ms(70);
			if(bit_is_clear(PINB,7)){
				increment_dmx();
				lcd_writedmx(startdmx,13,0);
				//lcd_puts("Switch 4 Pressed");
			}
		}
		//lcd_write_ch_values();
	}
	return 1;
}

/*
Initialize the Microprocessor IO
*/
void ioinit (void){
// Port Initialization
DDRA = 0xFF;	// PORTA Outputs: 	LCD
DDRB = 0xFF;	// PORTB Outputs: 	Triac Control
DDRC = 0x00;	// PORTC Inputs:	Buttons & JTAG
DDRD = 0x00;	// PORTD Inputs:	RX & Zero Cross Sync

PORTA = 0x00;	// PORTA Zeros
PORTB = 0x00;	// PORTB Zeros: Triacs Off
PORTC = 0xFF;	// PORTC Zeros
PORTD = 0x00;	// PORTD Zeros

// Timer/Counter 1 initialization
TCCR1A = 0x00000000;
TCCR1B = 0x09;
TIMSK = 0x10;
OCR1AH = 0x00;
OCR1AL = 0xDA;

OCR1AH = 0x01;
OCR1AL = 0x0D;

// Analog Comparator initialization
ACSR=0x80;

//External Interrupt initialization
MCUCR = 0x0A;
GICR = 0x40;

// Watchdog Timer Prescaler OSC/2048k
// WDTCR = 0x1F;
// WDTCR = 0x0F;
}

/*
Initialize the USART
*/
void init_usart(unsigned int ubrr) {

	UCSRB = 0x94;	// UCSRB = 0b10010100
	UCSRC = 0x0e;	// UCSRC = 0b00001110
   	UBRRH = (unsigned char)(ubrr>>8);
   	UBRRL = (unsigned char)ubrr;
}

/*
Display the Startup Screen on the LCD
*/
void lcd_write_startup_screen(){
	//////////1234567890123456
	lcd_clrscr();
	lcd_puts("Starting Dimmer");
	lcd_gotoxy(0,1);
	lcd_puts("Firmware V1.0.0");
	_delay_ms(2000);
}

/*
Display the Running Screen on the LCD
*/
void lcd_write_running_screen(){
	//////////1234567890123456
	lcd_clrscr();
	lcd_puts("DMX Address:D");
	lcd_gotoxy(0,1);
	lcd_puts("Menu Enter Up Dn");
}

/*
Write the DMX Address to the LCD
*/
void lcd_writedmx(u16 DMX_Address, int x, int y){
	char buffer[6];
	itoa(DMX_Address, buffer, 10);
	lcd_gotoxy(x,y);
	
	if(DMX_Address >= 100){
		lcd_putc(buffer[0]);
		lcd_putc(buffer[1]);
		lcd_putc(buffer[2]);
	}
	else if(DMX_Address <= 99 && DMX_Address >= 10){
		lcd_putc('0');
		lcd_putc(buffer[0]);
		lcd_putc(buffer[1]);
	}
	else{
		lcd_putc('0');
		lcd_putc('0');
		lcd_putc(buffer[0]);
	}
}

/*
Write all the incoming DMX Channel Values to the LCD
*/
void lcd_write_ch_values(){
	lcd_clrscr();
	_delay_ms(50);
	lcd_writedmx(ChannelData[0],0, 0);
	lcd_writedmx(ChannelData[1],4, 0);
	lcd_writedmx(ChannelData[2],8, 0);
	lcd_writedmx(ChannelData[3],12,0);
	lcd_writedmx(ChannelData[4],0, 1);
	lcd_writedmx(ChannelData[5],4, 1);
	lcd_writedmx(ChannelData[6],8, 1);
	lcd_writedmx(ChannelData[7],12,1);
}

/*
Increment the start dmx address of the dimmer
*/
void increment_dmx(){
	if(startdmx < 255)
		startdmx++;
	else
		startdmx = 255;
}
/*
Decrement the start dmx address of the dimmer
*/
void decrement_dmx(){
	if(startdmx > 1)
		startdmx--;
	else
		startdmx = 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I understand it, a light dimmer (where the light is powered by mains 220VAC/120VAC) determines the zero-cross point of the AC voltage.  Then it turns on the lamp.  At a certain point of the AC cycle, the CPU turns off the mains voltage to the lamp.    I don't understand where PWM would be used in this configuration/format.  I think that a timer that could break the 60Hz mains sine cycle into 60 or so units of time would be a better approach.

 

Could you elaborate more on the possible use of PWM in this application?   Thanks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The term is phase control, not Pwm! Simonetta - you’ve got the process back to front. At some pount into the cycle, you turn on the power by triggering the triac/scr. These devices latch on and turn off when the current drops below their holding value.
I’d suggest using three mega16s -one per phase. I doubt one mega16 would have enough performance to do three phase in one chip. Also note this code is pretty crude - it diesn’t have any filtering so the lights will flicker when you get a mains transient.
Your zero cross circuit needs some attention as well.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The zero cross circuit is based on the PULSAR Dimmer Rackpack of Pulsar light of Cambridge UK. It's a tried and tested circuit. They used to make analog 6ch x 2kw per channel dimmers. Very stable circuit. As for the one mega16 per phase. It is possible to do all the three phases with just one mega16. It has three ext interrupts.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I beg to differ. The specification of the parts is critical. No phase locked recovery. What was your question?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

I doubt one mega16 would have enough performance to do three phase in one chip.

 

Prepare to be undoubted.

 

I'm currently repairing a commercial 6-channel dimmer, with support for three-phase inputs, that runs on a single mega161 running at 8MHz.

 

So it's dealing with 3 zero-crossing inputs, a DMX input, a 44780 based LCD display, 6 test buttons, 4 menu buttons and 6 analogue inputs which it AtoD converts. Which is quite impressive. Especially when you realise that the chip doesn't have an on-board ADC nor does it use an external ADC! How they do it is quite inventive and left as an exercise for the reader.

"This forum helps those that help themselves."

"How have you proved that your chip is running at xxMHz?" - Me

"If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The USD 2 light dimmers usually use phase control with a triac.

The USD 50 dimmers often use phase control with an IGBT. Which is turned on at a zero crossing and switched of somewhere halfway.

The reason for this is that turning a triac on creates very steep flanks = lots of electric noise / heavy filtering.

With an IGBT (or MOSfet) you can control the turn-on / off speed to take the edge of the flanks.

 

The lamp usually runs on AC, but there is a bridge rectum fire in between so the IGBT only switches unipolar voltages.

This setup could also be used for PWM (Smaller filters?) but I don't know how often that's done.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Paulvdh wrote:

The USD 50 dimmers often use phase control with an IGBT. Which is turned on at a zero crossing and switched of somewhere halfway.

 

The search term for these is 'reverse phase control' or 'phase-cut' or 'trailing edge'.

"This forum helps those that help themselves."

"How have you proved that your chip is running at xxMHz?" - Me

"If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suppose it depends on what one considers adequate. The stuff I designed would have 12 bit resolution and 24 bit fade calcs and with a phase locked technique to filter and recover the zero cross that would work with generators and filter out a mains signalling technique called Zellweger. Rene wasn't involved. The code I wrote for a mega162 running at 8MHz consumed most of the cpu - and that was for 4 channels. The isr was written is asm to eek out some performance. It would also detect 50/60Hz automatically.

As for the adc - probably a r/c and comparator. One can use the input schmitt as the comparator.

If the dimmer is for a rock n roll show, fade performance is not required. For stage and architectural, then smooth fades are much nicer. Mind you, when I was designing commercial dimmers, incandescents were still in use. These days, the led inverters might actually smooth the lumpy fade out.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

As for the adc - probably a r/c and comparator. One can use the input schmitt as the comparator.

 

Close with comparator but no.

 

The '161 has an external memory bus so they hung an 8-bit latch on it and used that to drive an R-2R DAC to generate a ramp. That feeds one input of the on-chip comparator; the other input is fed via a 4051 with the analogue inputs. 

 

Kartman wrote:

If the dimmer is for a rock n roll show, fade performance is not required. For stage and architectural,...

 

This is a Strand Lighting unit, circa 2002 so very much aimed at high-end theatre.

 

Unfortunately, the unit and I are 400 miles apart otherwise I'd see if they'd set the fuses.

"This forum helps those that help themselves."

"How have you proved that your chip is running at xxMHz?" - Me

"If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I thought Strand was defunct in the 90’s. Philips ended up with the brand.