User Tools

Site Tools


projects:dmx_dimmer:home

DMX dimmer

~~META:description abstract=A DMX512 2 channels dimmer~~

A DMX512 2 channels dimmer.

Features

  • Up to 4A, can drive halogen bulbs
  • Uses 2 DMX channels

Theory of dimming

Bulb voltage from trigger time t to end of period is U(t) = U_eff sqrt{1-{t/10ms}+{sin({2 pi t}/10ms)/{2 pi}}}

Power is P(t) = {U(t)^2} / Z

Power over full wave is P(0) = U_eff^2 / Z and power for value is P(n) = {n / 255} P(0)

Using a quick and dirty solver we find the trigger time for each value (in us) :

0;9331
1;9031
2;8848
3;8709
4;8593
5;8493
6;8403
7;8322
8;8248
9;8179
10;8114
11;8053
12;7995
13;7940
14;7887
15;7836
16;7788
17;7741
18;7695
19;7651
20;7609
21;7567
22;7527
23;7487
24;7449
25;7411
26;7375
27;7339
28;7303
29;7269
30;7235
31;7202
32;7169
33;7137
34;7105
35;7074
36;7043
37;7013
38;6983
39;6953
40;6924
41;6895
42;6867
43;6839
44;6811
45;6783
46;6756
47;6729
48;6703
49;6676
50;6650
51;6624
52;6599
53;6573
54;6548
55;6523
56;6498
57;6474
58;6449
59;6425
60;6401
61;6377
62;6354
63;6330
64;6307
65;6283
66;6260
67;6237
68;6215
69;6192
70;6169
71;6147
72;6124
73;6102
74;6080
75;6058
76;6036
77;6015
78;5993
79;5971
80;5950
81;5928
82;5907
83;5886
84;5865
85;5843
86;5822
87;5802
88;5781
89;5760
90;5739
91;5718
92;5698
93;5677
94;5657
95;5636
96;5616
97;5596
98;5575
99;5555
100;5535
101;5515
102;5495
103;5475
104;5455
105;5435
106;5415
107;5395
108;5375
109;5355
110;5335
111;5315
112;5295
113;5276
114;5256
115;5236
116;5217
117;5197
118;5177
119;5157
120;5138
121;5118
122;5099
123;5079
124;5059
125;5040
126;5020
127;5000
128;4981
129;4961
130;4942
131;4922
132;4902
133;4883
134;4863
135;4844
136;4824
137;4804
138;4784
139;4765
140;4745
141;4725
142;4706
143;4686
144;4666
145;4646
146;4626
147;4606
148;4586
149;4566
150;4546
151;4526
152;4506
153;4486
154;4466
155;4446
156;4426
157;4405
158;4385
159;4365
160;4344
161;4324
162;4303
163;4283
164;4262
165;4241
166;4220
167;4199
168;4179
169;4158
170;4136
171;4115
172;4094
173;4073
174;4051
175;4030
176;4008
177;3986
178;3965
179;3943
180;3921
181;3899
182;3877
183;3854
184;3832
185;3809
186;3786
187;3764
188;3741
189;3718
190;3694
191;3671
192;3647
193;3624
194;3600
195;3576
196;3552
197;3527
198;3503
199;3478
200;3453
201;3428
202;3402
203;3377
204;3351
205;3325
206;3298
207;3272
208;3245
209;3218
210;3190
211;3162
212;3134
213;3106
214;3077
215;3048
216;3018
217;2988
218;2958
219;2927
220;2896
221;2864
222;2832
223;2799
224;2766
225;2732
226;2698
227;2662
228;2626
229;2590
230;2552
231;2514
232;2474
233;2434
234;2392
235;2350
236;2306
237;2260
238;2213
239;2165
240;2114
241;2061
242;2006
243;1948
244;1887
245;1822
246;1753
247;1679
248;1598
249;1508
250;1408
251;1292
252;1153
253;970
254;670
255;0

C array version :

const uint16_t timings[256] = {
	9331, 9031, 8848, 8709, 8593, 8493, 8403, 8322,
	8248, 8179, 8114, 8053, 7995, 7940, 7887, 7836,
	7788, 7741, 7695, 7651, 7609, 7567, 7527, 7487,
	7449, 7411, 7375, 7339, 7303, 7269, 7235, 7202,
	7169, 7137, 7105, 7074, 7043, 7013, 6983, 6953,
	6924, 6895, 6867, 6839, 6811, 6783, 6756, 6729,
	6703, 6676, 6650, 6624, 6599, 6573, 6548, 6523,
	6498, 6474, 6449, 6425, 6401, 6377, 6354, 6330,
	6307, 6283, 6260, 6237, 6215, 6192, 6169, 6147,
	6124, 6102, 6080, 6058, 6036, 6015, 5993, 5971,
	5950, 5928, 5907, 5886, 5865, 5843, 5822, 5802,
	5781, 5760, 5739, 5718, 5698, 5677, 5657, 5636,
	5616, 5596, 5575, 5555, 5535, 5515, 5495, 5475,
	5455, 5435, 5415, 5395, 5375, 5355, 5335, 5315,
	5295, 5276, 5256, 5236, 5217, 5197, 5177, 5157,
	5138, 5118, 5099, 5079, 5059, 5040, 5020, 5000,
	4981, 4961, 4942, 4922, 4902, 4883, 4863, 4844,
	4824, 4804, 4784, 4765, 4745, 4725, 4706, 4686,
	4666, 4646, 4626, 4606, 4586, 4566, 4546, 4526,
	4506, 4486, 4466, 4446, 4426, 4405, 4385, 4365,
	4344, 4324, 4303, 4283, 4262, 4241, 4220, 4199,
	4179, 4158, 4136, 4115, 4094, 4073, 4051, 4030,
	4008, 3986, 3965, 3943, 3921, 3899, 3877, 3854,
	3832, 3809, 3786, 3764, 3741, 3718, 3694, 3671,
	3647, 3624, 3600, 3576, 3552, 3527, 3503, 3478,
	3453, 3428, 3402, 3377, 3351, 3325, 3298, 3272,
	3245, 3218, 3190, 3162, 3134, 3106, 3077, 3048,
	3018, 2988, 2958, 2927, 2896, 2864, 2832, 2799,
	2766, 2732, 2698, 2662, 2626, 2590, 2552, 2514,
	2474, 2434, 2392, 2350, 2306, 2260, 2213, 2165,
	2114, 2061, 2006, 1948, 1887, 1822, 1753, 1679,
	1598, 1508, 1408, 1292, 1153, 970, 670, 0
};

The solver :

var nl = {};
 
for(var t=0; t<=0.01; t+= 0.000001) {
  var at = 1 - t/0.01 + Math.sin(2 * Math.PI * t / 0.01) / (2 * Math.PI);
  var n = Math.round(255 * at);
  if(typeof nl['_' + n] == 'undefined') nl['_' + n] = Math.round(1000000 * t);
}
 
var s = [];
for(var i=0; i<256; i++) s.push(i + ';' + nl['_' + i]);
 
console.log(s.join("\n"));

Analysis file with charts comparing linear approach with fitted one : timings.xls

Electronics

schematic.svg

Source code

DMX to power conversion is done through a 16 bit timer with a prescaler of 8 for a clock of 8MHz.

This allows for approx. 10k steps over a single mains half period (zero-crossing sensor time delta should be taken into account here).

Sensor accuracy can be improved by detecting falling edge, reseting timer, then detecting rising edge and adding 1/2 of measured delay after further falling edges by offsetting OCR1x.

main.c
/**
 * @defgroup DMX512 dimmer
 * @type firmware
 * @file main.c
 * @author Etienne Meleard <etienne@yent.eu>
 * @creation 2014-06-20
 * @tabsize 4
 * @mcu ATMEGA8L
 * @fcpu 8MHz
*/
 
 
//============================ WARNING ===============================//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
//                                                                    //
// Fuses must be like SUT1 = 1, SUT0 = 1, CKSEL3 = 1, CKSEL2 = 0,     //
// CKSEL1 = 0 and CKSEL0 = 0 to ensure using the crystal as clock     //
// (otherwise it uses the 8MHz internal clock), full swing mode is    //
// mandatory to acheive 8MHz operation.                               //
//                                                                    //
// CKDIV8 fuse must not be set to avoid clock frequency divided by 8  //
//                                                                    //
// Fuse set or = 1 means checked in ponyprog                          //
//                                                                    //
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!//
//====================================================================//
 
 
// ================= Fuses ================= //
 
/*FUSES = { // No success writing fuse this way, command line below ...
	.low = (FUSE_SUT1 & FUSE_SUT0 & FUSE_CKSEL3),
	.high = HFUSE_DEFAULT,
	.extended = EFUSE_DEFAULT,
};*/
// Run avrdude -c usbasp -p m8 -U lfuse:w:0xc7:m -U hfuse:w:0xdf:m at
// first (-F may be needed)
 
 
// ================ Includes =============== //
 
#include <avr/pgmspace.h>
#include <avr/io.h>
#include <sup_avr/io2.h>
#include <avr/interrupt.h>
#include <util/delay.h>
 
 
// ================ Pinouts ================ //
 
#define LED			Bit(PORTD).bit5
 
#define RX			Bit(PORTD).bit0
#define RX_COMPLETE	SIG_USART_RECV
#define RX_INT		Bit(PORTD).bit2
#define RX_SIG		SIG_INTERRUPT0
#define RX_TIMER	TCNT0
#define BREAK_LEN	11
#define MAB_LEN		1
 
#define DIP_0		Bit(PINC).bit0
#define DIP_1		Bit(PINC).bit1
#define DIP_2		Bit(PINC).bit2
#define DIP_3		Bit(PINC).bit3
 
#define DIP_A		Bit(PORTC).bit4
#define DIP_B		Bit(PORTB).bit4
#define DIP_C		Bit(PORTB).bit5
 
#define EXT_ADC		6
#define EXT_INPUT	Bit(PIND).bit3
#define EXT_OUTPUT	Bit(PORTD).bit3
#define EXT_SIG		SIG_INTERRUPT1
 
#define OUT_1		Bit(PORTB).bit1
#define CH_1		OCR1A
#define OUT_2		Bit(PORTB).bit2
#define CH_2		OCR1B
 
 
// ================ Macros ================= //
 
#define BLINK		LED=1; _delay_ms(150); BLINK LED=0; _delay_ms(150)
 
#define RX_START	UCSRB |= 0x10
#define RX_STOP		UCSRB &= 0xef
 
#define ADDR_4		PINC & 0x0f
 
 
// ================ Globals ================ //
 
volatile uint16_t address = 0x0000;
 
volatile uint16_t received = 0;
 
const uint16_t timings[256] = PROGMEM {
	9331, 9031, 8848, 8709, 8593, 8493, 8403, 8322,
	8248, 8179, 8114, 8053, 7995, 7940, 7887, 7836,
	7788, 7741, 7695, 7651, 7609, 7567, 7527, 7487,
	7449, 7411, 7375, 7339, 7303, 7269, 7235, 7202,
	7169, 7137, 7105, 7074, 7043, 7013, 6983, 6953,
	6924, 6895, 6867, 6839, 6811, 6783, 6756, 6729,
	6703, 6676, 6650, 6624, 6599, 6573, 6548, 6523,
	6498, 6474, 6449, 6425, 6401, 6377, 6354, 6330,
	6307, 6283, 6260, 6237, 6215, 6192, 6169, 6147,
	6124, 6102, 6080, 6058, 6036, 6015, 5993, 5971,
	5950, 5928, 5907, 5886, 5865, 5843, 5822, 5802,
	5781, 5760, 5739, 5718, 5698, 5677, 5657, 5636,
	5616, 5596, 5575, 5555, 5535, 5515, 5495, 5475,
	5455, 5435, 5415, 5395, 5375, 5355, 5335, 5315,
	5295, 5276, 5256, 5236, 5217, 5197, 5177, 5157,
	5138, 5118, 5099, 5079, 5059, 5040, 5020, 5000,
	4981, 4961, 4942, 4922, 4902, 4883, 4863, 4844,
	4824, 4804, 4784, 4765, 4745, 4725, 4706, 4686,
	4666, 4646, 4626, 4606, 4586, 4566, 4546, 4526,
	4506, 4486, 4466, 4446, 4426, 4405, 4385, 4365,
	4344, 4324, 4303, 4283, 4262, 4241, 4220, 4199,
	4179, 4158, 4136, 4115, 4094, 4073, 4051, 4030,
	4008, 3986, 3965, 3943, 3921, 3899, 3877, 3854,
	3832, 3809, 3786, 3764, 3741, 3718, 3694, 3671,
	3647, 3624, 3600, 3576, 3552, 3527, 3503, 3478,
	3453, 3428, 3402, 3377, 3351, 3325, 3298, 3272,
	3245, 3218, 3190, 3162, 3134, 3106, 3077, 3048,
	3018, 2988, 2958, 2927, 2896, 2864, 2832, 2799,
	2766, 2732, 2698, 2662, 2626, 2590, 2552, 2514,
	2474, 2434, 2392, 2350, 2306, 2260, 2213, 2165,
	2114, 2061, 2006, 1948, 1887, 1822, 1753, 1679,
	1598, 1508, 1408, 1292, 1153, 970, 670, 0
};
 
 
// ================ Functions ============== //
 
uint16_t getTiming(uint8_t value) {
	return pgm_read_byte(&timings[255 - value]);	// Opposite offset required to emulate going-down PWM slope
}
 
void init(void) {
	// Led as output
	DDRD |= 1 << 5;
 
	// USART
	UCSRA = 0x00;	// No doubling of frequency
	UCSRB = 0x80;	// Rx disabled, Rx irq enabled, 8bit mode
	UCSRC = 0x8e;	// Async, no parity, 2 stops, 8 bits
	UBRR = 103;		// 250kbds
 
	// Detection timer (timer 0)
	TCCR0 = 0x03;	// 64 prescaler, increment every 8us
 
	// Dip inputs
	DDRC &= 0xf0;
 
	// Dip outputs
	DDRC |= 0x10;
	DDRB |= 0x30;
	DIP_A = 0;
	DIP_B = 0;
	DIP_C = 0;
 
	// Channels timer (timer 1)
	TCCR1A = 0x82;		// 16 bits, fast, inverting PWM on OC1A and OC1B
	TCCR1B = 0x1a;		// with 8 prescaler ~ 1MHz tick period
	ICR1 = 0xffff;		// Timer max to 65535
	CH_1 = getTiming(0);	// Channel 1 to 0
	CH_2 = getTiming(0);	// Channel 2 to 0
 
	// B timer (timer 2)
	TCCR2 = 0x6e;	// 8 bits, fast, non-inverting PWM on OC2
	BLUE_CH = 0x00;	// Blue channel to 0
}
 
void getAddress(void) {
	uint16_t buffer = 0x0000;
 
	DIP_C = 1;
	buffer += ADDR_4;
	DIP_C = 0;
	buffer <<= 4;
 
	DIP_B = 1;
	buffer += ADDR_4;
	DIP_B = 0;
	buffer <<= 4;
 
	DIP_A = 1;
	buffer += ADDR_4;
	DIP_A = 0;
 
	address = buffer;
}
 
uint8_t getFrameStart(void) {
	while(RX);
 
	RX_TIMER = 0;
	while(!RX && RX_TIMER < BREAK_LEN);
	if(RX_TIMER < BREAK_LEN) return 0; // Error, break too short
 
	RX_STOP;
 
	RX_TIMER = 0;
	while(RX && RX_TIMER < MAB_LEN);
	if(RX_TIMER < MAB_LEN) return 0; // Error, break too short
 
	RX_START;
	received = 0;
 
	return 1;
}
 
// =============== Interrupts ============== //
 
SIGNAL(RX_COMPLETE) {
	if(!received && UDR) { // First slot is not 0x00, not a setting packet
		RX_STOP;
		return;
	}
 
	if(received < address + 1) return; // Address not reached
 
	if(received > address + 2) { // Received enough packets, stop receiving and wait for next frame
		RX_STOP;
		return;
	}
 
	// Got channel, set it
	switch(received - (address + 1)) {
		case 0: CH_1 = UDR;	break;
		case 1: CH_2 = UDR;	break;
	}
 
	received++;
}
 
 
// ================== Main ================= //
 
int main(void) {
	uint16_t time_without_start = 0;
 
	cli();		// WatchDog disabled AT ALL TIMES !
	init();
	sei();
 
	BLINK;
	BLINK;
 
	getAddress();
 
	while(1) {
		if(getFrameStart()) {
			LED = 0;
			time_without_start = 0;
		}
 
		_delay_us(100);
		time_without_start++;
		if(time_without_start > 10000) { // 1s without start
			LED = 1; // Error
		}
	}
 
	return 0;
}

How to

Manual

microcontroller lighting dmx

projects/dmx_dimmer/home.txt · Last modified: 2022/06/30 21:13 by 127.0.0.1