PDA

View Full Version : Assembly timing woes! Anyone know DMX?



big_mark
10-30-2006, 06:01 PM
Hi all

I'm in the process of creating a DMX receiver using my propellor kit and I'm having a few problems! http://forums.parallax.com/images/smilies/confused.gif

First of all, a little background information. DMX is a communications protocol used in the entertainment industry to control lighting. It uses RS485 balanced transmission, at 250,000 bits per second. The DMX data is sent in 'packets' of upto 513 bytes. The first byte is the start code and is used to define what sort of data is being transmitted. Currently, only zero has been defined as a valid start code. The remaining bytes are used to define channel levels.

A DMX packet is made up of the following parts :

First of all there is the break. This is *at least* 22 consecutive low bits, but could be longer

Then we have the Mark After Break (MAB), which is *at least* 2 high bits, but again could be longer

After that, we have the data frames. There could be *upto* 513 of them (1 start code and 512 data frames), but there could also be less. Each frame starts with 1 low start bit, then 8 data bits, and finally 2 high stop bits. Between each frame there is what is known as the Mark Time Between Frames (MTBF, yes I know....). This can be anywhere from 0 to 1 second and is indicated by consecutive high bits.

The problem I am having is that I think my timing code is wrong. Heres part of my code :





DAT

'
'
' Assembly program
'
org

asm_entry mov asm_cnt,cnt ' prepare for WAITCNT loop
add asm_cnt,dmx_clock_step

:loop waitcnt asm_cnt,dmx_clock_step ' wait for next CNT value (timing is determinant after WAITCNT)

mov dmx_bit, ina ' Read input port A




Dmx_clock_step is set to 320 because I have the propellor running at 80Mhz and DMX is transmitted at 250,000 bps, so 80,000,000/250,000 = 320.

I have a little test program set up to display things like break size, MAB size, etc and I am getting what I think are very strange values. I get a consistent 780 bits in the break and 22 bits in the MAB. After that my code does not work and it will not read the rest of the packet correctly. It reads the start bit and 8 low bits of the start code, but does not get any valid stop bits. It continues to read the break and MAB though...

I am using an Analog Devices ADM3485E to interface to a Zero88 Linebacker which I am using to generate the DMX signals.


Many Thanks

Mark

BTX
10-30-2006, 07:27 PM
Dear bib_mark.

I've done·DMX receivers in 8052, I used UART in mode 2, I think you must get a driver for UART at 11 bits.

One start bit, 8 data bits (LSB first), and 2 stop bits.

working·with the Propeller serial driver published here, you'll get that DMX reciever work fine and easy !!.

Best regards.

Alberto.

Graham Stabler
10-30-2006, 07:56 PM
the timing code looks fine. Check it by making it loop with a pin toggle and look on the scope.

Bear in mind that although it might be looping at a given frequency you may need to syncronize it or something.

Graham

cgracey
10-31-2006, 01:08 AM
Graham Stabler said...
the timing code looks fine. Check it by making it loop with a pin toggle and look on the scope.

Bear in mind that although it might be looping at a given frequency you may need to syncronize it or something.

Graham
This is certainly a problem. You'll need to wait for an RX edge transition (the start bit), and then initialize your WAITCNT target. Remember that for the first bit, you will need to wait 1.5 bits periods to land in the middle of data bit 0. Then you'll wait 1 bit period to get each of the other bits, through the stop bit (to avoid wrongly retriggering on the next byte). That WAITCNT target initialization must be repeated for each byte. The way you are going to know when the first byte is about to be transmitted is by watching for those extended break conditions.


▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔


Chip Gracey
Parallax, Inc.

big_mark
10-31-2006, 01:38 AM
Heres the code I wrote for my DMX receiver. At the moment it will only receive one particular byte, defined by the variable 'dmx_byte_to_store' (You have no idea how much meaningless variable names drive me up the wall!!!). My ultimate aim is to be able to receive a subset of the dmx packet, store it in main memory, and have other cogs use it for different things e.g. simple relays, two-axis moving mirors, motorised irises etc. Anyway, here is the assembly code. It sits in its own object file. I have written a test program which uses it, along with the vga object which I use to display various debugging figures.




DAT

'
'
' Assembly program
'
org

asm_entry mov asm_cnt,cnt ' prepare for WAITCNT loop
add asm_cnt,dmx_clock_step

:loop waitcnt asm_cnt,dmx_clock_step ' wait for next CNT value (timing is determinant after WAITCNT)

mov dmx_bit, ina ' Read input port A

cmp read_state,state_count_break wz ' Are we counting the break bits?
if_nz jmp #:count_mab ' Jump forward if not...

and dmx_bit, asm_pin_mask wz,nr ' Read the dmx pin. Set Z flag to indicate high or low bit

if_z add low_bit_count,#1 ' If the bit is low, increment low_bit_count
if_z jmp #:loop ' and jump back to the top

cmp low_bit_count,#22 ' If the bit is high, have we had 22 or more low bits beforehand?
if_z_or_nc mov hi_bit_count, #1 ' If we have, we now have a valid 22 bit break and the first MAB hi bit...
if_z_or_nc mov read_state,state_count_mab ' ...so we can start counting the MAB bits
if_z_or_nc jmp #:loop

mov low_bit_count,#0 ' If not, reset low bit count...
jmp #:loop ' ...and jump back to the top



:count_mab cmp read_state,state_count_mab wz ' Are we counting the MAB?
if_nz jmp #:read_byte ' Jump forward if not...

and dmx_bit, asm_pin_mask wz,nr ' Read the dmx pin. Set Z flag to indicate high or low bit

if_nz add hi_bit_count,#1 ' If we have a high bit, increment the high bit count
if_nz jmp #:loop ' and jump back to the start

cmp hi_bit_count, #2 ' If we have a low bit, have we had 2 or more high bits?
if_nz_or_c mov low_bit_count, #1 ' If not, then DMX ERROR! MAB too small, so lets start again
if_nz_or_c mov read_state,state_count_break
if_nz_or_c jmp #:loop

mov low_bit_count,#1 ' Valid MAB, and we've just had a low bit for the start bit
mov dmx_byte,#0 ' Get ready to start reading a packet
mov dmx_byte_no,#0
mov dmx_byte_bit_count,#0
mov read_state,state_read_byte
jmp #:loop


:read_byte cmp read_state,state_read_byte wz ' Are we reading a byte ?
if_nz jmp #:count_stop ' Jump forward if not...

shl dmx_byte,#1 ' Add the bit to the byte. Start by shifting it left one bit..
and dmx_bit, asm_pin_mask wz,nr ' Read the dmx pin. Set Z flag to indicate high or low bit
if_nz add dmx_byte,#1 ' If we have a high bit, add it to the byte
if_z add low_bit_count,#1 ' If the bit is low, increment the low bit count
add dmx_byte_bit_count,#1

cmp dmx_byte_bit_count,#8 wz
if_z mov read_state,state_count_stop ' If we've read 8 bits, lets count the stop bits
if_z mov stop_bit_count,#0

jmp #:loop

:count_stop cmp read_state,state_count_stop wz ' Are we counting the stop bits?
if_nz jmp #:catch_start ' Jump forwards if not...

and dmx_bit, asm_pin_mask wz,nr ' Read the dmx pin. Set Z flag to indicate high or low bit

if_nz jmp #:hi_stop_bit ' Jump forwards if we have a high stop bit

cmp dmx_byte,#0 wz ' If we have a low bit here, it may be an error
if_z cmp stop_bit_count,#0 wz ' If the byte we just read is 0, and we have no stop bits
if_z mov low_bit_count,#10 ' then we have the start of a new break. Set low bit count to 10
' (1 start bit + 8 low bits for the byte + the low bit we just read)
if_nz mov low_bit_count, #1 ' Otherwise, we have a dmx error. Time to start again either way...
mov read_state,state_count_break
jmp #:loop

:hi_stop_bit add stop_bit_count,#1 ' We have a high stop bit, so increment the counter

cmp stop_bit_count,#2 wz ' If we don't have two high stop bits yet...
if_nz jmp #:loop ' ...jump back to the start...
' ...but if we do, we have a valid byte

cmp dmx_byte_no,#0 wz ' The first dmx byte is a special case.
if_nz jmp #:valid_packet ' If we've gone past it, skip forwards

cmp dmx_byte,#0 wz ' If we have the first byte, is it zero?
if_nz mov low_bit_count,#0 ' If it isn't, then we aren't interested in this packet...
if_nz mov read_state,state_count_break '...so lets start again
if_nz jmp #:loop

add dmx_byte_no,#1 ' If the first dmx byte is zero, then we are interested in this packet
mov read_state,state_catch_start ' Prepare to catch the start bit of the next byte
jmp #:loop

:valid_packet cmp dmx_byte_no,dmx_byte_to_store wz ' Is this the byte we are interested in?
if_z wrlong dmx_byte,result_ptr ' If it is, store it in main memory
add dmx_byte_no,#1 ' Either way, increment the dmx byte counter
mov read_state,state_catch_start ' and prepare to catch the start bit of the next byte

cmp dmx_byte_no,full_packet wz ' Have we read 512 dmx bytes?
if_z mov low_bit_count,#0
if_z mov read_state,state_count_break ' If we have, start reading a new packet

jmp #:loop

:catch_start and dmx_bit, asm_pin_mask wz,nr ' Read the dmx pin. Set Z flag to indicate high or low bit
if_nz jmp #:loop ' High bit. The DMX standard allows for Mark Time Between Frames
' which is indicated by continuous high bits
mov low_bit_count,#1 ' Valid stop bit, so set low bit counter...
mov read_state,state_read_byte ' ...and the rest of the variables to prepare for the next byte
mov dmx_byte,#0
mov dmx_byte_bit_count,#0
jmp #:loop

'
'
' Data
'
' At 80MHz the interval between succesive DMX bits is 320 clock ticks (80_000_000 / 250_000)
' since DMX is transmitted at 250khz

dmx_clock_step long 320

asm_pin_mask long 0 ' Pin to read dmx from.
dmx_bit long 0 ' Read the bit into here
low_bit_count long 0
hi_bit_count long 0
read_state long state_count_break
dmx_byte long 0 ' The byte we are currently reading
dmx_byte_bit_count long 0
dmx_byte_no long 0 ' Which byte we are receving
dmx_byte_to_store long 1 ' The byte we are interested in
stop_bit_count long 0
asm_cnt long 0 ' Counter
result_ptr long 0 ' Write the data to this pointer
full_packet long 513

test_byte long 100

' Read states
state_count_break long 0
state_count_mab long 1
state_read_byte long 2
state_count_stop long 3
state_catch_start long 4

Timothy D. Swieter
10-31-2006, 02:53 AM
Big_mark -

Did you see the post I made about DMX? Overall I thought it was fairly simple to create the hardware and software. I have not been back to DMX since I created this post. It would be neat to add RDM support.

http://forums.parallax.com/forums/default.aspx?f=25&m=132052

I used the Propeller manual and example code to cook up my design. I tested it with a handheld DMX unit and a USB DMX unit I have. Things worked great!


Hope this helps,
-Tim

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Timothy D. Swieter
tdswieter.com (http://www.tdswieter.com/)
------------------------------------------------------------
One little spark is all it takes for an idea to explode

big_mark
10-31-2006, 04:21 AM
Timothy -

No, I had not seen you DMX code. In fact I didn't even look for any as I had already designed the code on paper before deciding on which processor I was going to use. I must say though, your code is way more efficient than mine is. You've gone about doing it in a way I would never had thought of. Many thanks.

However, I think I have discovered why my code doesn't work. When I ran your demo using my hardware there were fluctuations in the dmx values. I can only assume that my dmx-ttl interface is at fault. I am using an Analog Devices ADM3485E. I chose it because it is designed to run on only 3.3v, therefore simplifying any circuit I might make. I have connected it directly to my Propellor, though I noticed that in Timothys design he has used a 1k resistor between his chip and his Propellor. His dmx-ttl chip runs on 5 volts though, so is that why he has used a resistor?

Mike Green
10-31-2006, 04:51 AM
The 1K resistor is to protect the Propeller's I/O pins from voltages in excess of 3.3V by limiting protection diode fault current.

big_mark
10-31-2006, 04:36 PM
So my setup should be fine then, because my dmx-ttl recievers logic levels are 0.4 volts for logic 0 and vcc-0.4 volts for logic 1. The only thing left that I could try is to terminate the dmx input by putting a 120 ohm resistor between + and - signals. Beyond doing that, I have no idea why it is not working.