Shop OBEX P1 Docs P2 Docs Learn Events
Assembly timing woes! Anyone know DMX? — Parallax Forums

Assembly timing woes! Anyone know DMX?

big_markbig_mark Posts: 49
edited 2006-10-31 09:36 in Propeller 1
Hi all

I'm in the process of creating a DMX receiver using my propellor kit and I'm having a few problems! 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

Comments

  • BTXBTX Posts: 674
    edited 2006-10-30 12:27
    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 StablerGraham Stabler Posts: 2,510
    edited 2006-10-30 12:56
    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
  • cgraceycgracey Posts: 14,206
    edited 2006-10-30 18:08
    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_markbig_mark Posts: 49
    edited 2006-10-30 18:38
    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. SwieterTimothy D. Swieter Posts: 1,613
    edited 2006-10-30 19:53
    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
    One little spark is all it takes for an idea to explode
  • big_markbig_mark Posts: 49
    edited 2006-10-30 21:21
    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 GreenMike Green Posts: 23,101
    edited 2006-10-30 21:51
    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_markbig_mark Posts: 49
    edited 2006-10-31 09:36
    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.
Sign In or Register to comment.