CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

[solved] dmx512 receiver?
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
JackB



Joined: 04 Mar 2016
Posts: 32
Location: Netherlands

View user's profile Send private message

[solved] dmx512 receiver?
PostPosted: Fri Mar 04, 2016 1:59 pm     Reply with quote

Hi,
I'm new to this ccs c compiler. I do like its power, but I'm afraid is can also be a limiting factor, especially in receiving dmx signals.

I'm using PIC18F67K90 and like to fetch a dmx512 stream.
From there, I would like to write the received bytes in an array.
Code:
char dmx_in[DMX_SIZE];

dmx_in[0] for 1st byte,
dmx_in[1] for 2nd byte, etc.

I defined the dmx pins like this:
Code:
#use rs232(xmit=PIN_C6,rcv=PIN_C7,BAUD=250000,BITS=8,PARITY=N,STOP=2,ERRORS,stream=DMX)

If I would use this:
Code:
char dmx_value = fgetc(DMX);

But that way I can't determine which byte number comes in, as no break is detected.
I think I need an interrupt for that.

However enabling interrupt for serial receive is tricky, I also like use another USART to communicate with a PC. I defined that as:
Code:
#use rs232(uart2, baud=9600, stream=STD)

How do I generate different interrupts for both serial (dmx/pc) ports?
How do I properly receive a dmx512 stream?

Kind regards,
Jack.


Last edited by JackB on Tue Mar 08, 2016 5:21 am; edited 2 times in total
temtronic



Joined: 01 Jul 2010
Posts: 9226
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Mar 04, 2016 2:13 pm     Reply with quote

PICs are actually very good at DMX ! BTDT years ago ( 20)....

For interrupt driven basics, look at the CCS supplied examples, ex_isr.c and ex_sisr.c.
For DMX, try the 'code library ' here ( in the forum index) and 'search' for 'DMX'. there's probably a few program though I haven't looked.

Also use google 'DMX CCS C code', or similar keywords.

You aren't the first who uses PICs with DMX !!


Jay
JackB



Joined: 04 Mar 2016
Posts: 32
Location: Netherlands

View user's profile Send private message

PostPosted: Fri Mar 04, 2016 3:25 pm     Reply with quote

Hi Jay,

I'm using MPLAB X and ccs c on a Linux Mint laptop.
The examples directory /opt/picc/examples does not contain ex_isr.c.
However it does have ex_sisr.c.

If it says:
Code:
enable_interrupts(int_rda);

how do I know on which stream the interrupts are generated?

Note I have two streams:
Code:
#use rs232(uart2, baud=9600, stream=STD)

#use rs232(xmit=PIN_C6,rcv=PIN_C7,BAUD=250000,BITS=8,PARITY=N,STOP=2,ERRORS,stream=DMX)

So I have to use
Code:
getc(STD);
getc(DMX);

They would have to go in their own respective interrupt routines, I guess.

How could we setup interrupts on those two streams independent?

Regards, Jack.
Ttelmah



Joined: 11 Mar 2010
Posts: 19513

View user's profile Send private message

PostPosted: Fri Mar 04, 2016 3:29 pm     Reply with quote

UART1, develops INT_RDA, and UART2, INT_RDA2. Can't get much simpler than that...

fgetc, not getc, to use streams.

However there is a potential problem. The PIC18 hardware, cannot generate 2 stop bits. Doesn't mind how many stop bits there are for receive.
temtronic



Joined: 01 Jul 2010
Posts: 9226
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Fri Mar 04, 2016 4:04 pm     Reply with quote

yeah, bum finger dropped the s.. reminds me daily that I shouldn't have run 'skil'saw through the ring finger....28 year ago, sigh....

As for generating a 2nd stop bit,once you know the bit time, just manually add it by executing a bit set (or bit clr) for the appropriate time. You need to do the same thing when talking to ASR33s as well......

BTW I did search and found 40 'hits' about DMS here.....hopefully some insight into code...

Jay
gaugeguy



Joined: 05 Apr 2011
Posts: 303

View user's profile Send private message

PostPosted: Fri Mar 04, 2016 4:16 pm     Reply with quote

I haven't worked with DMX512 but it seems that 8N2 could be done using 9 bit data with the 9th bit always set to generate two stop bits.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Fri Mar 04, 2016 8:26 pm     Reply with quote

if i was doing a DMX driver - and had a PIC with lotsa I/O of the parallel kind - i would not hesitate to use a 16c550 external UART. the driver is non trivial - but the performance at high data rates is exceptionally nice . and the interrupt load on both send and receive using the FIFOs utilizes vastly fewer PIC clock cycles.
and it support 2 stop bits Very Happy Very Happy Very Happy Very Happy
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Fri Mar 04, 2016 11:03 pm     Reply with quote

CCS supplies a DMX driver with the compiler. See the Drivers directory:
Quote:
c:\program files\picc\drivers\dmx.c
c:\program files\picc\drivers\dmx.h
Ttelmah



Joined: 11 Mar 2010
Posts: 19513

View user's profile Send private message

PostPosted: Sat Mar 05, 2016 3:41 am     Reply with quote

The CCS example people are pointing you to, uses _software_ RS232, to allow 2 stop to be sent. :(
Not what you want/need.

There are two ways of doing it properly:

1) Set the UART to bits=8, stop=1. Then just add one bit time delay after sending any byte. Key is though that you must wait for the byte to have sent, then wait. It's fairly easy to encapsulate the transmission routine to do this, but means you can't use interrupt driven TX. Receive doesn't care if there is an extra stop.

2) Second method. The better one. Set the UART to bits=9, stop=1. Then encapsulate as:
Code:

#use rs232(xmit=PIN_C6,rcv=PIN_C7,BAUD=250000,BITS=9,PARITY=N,STOP=1,ERRORS,stream=DMX)

void DMX_putc(int8 chr)
{
   int16 tx_val;
   tx_val=chr | 0x100;
   fputc(DMX);
}

//Then to print to DMX
fprintf(DMX_putc,"What you want to send");

This automatically adds the extra bit on TX.

Even better, if you set it up like this, you can then use interrupts.

On RX, just ensure the values are read as bytes, and the 9th bit will get dropped.

This can then also be used fully interrupt driven. The serial transmit example will merrily work provided you add the same extra bit encapsulation to this, and the RX will work 'as is' (remembering to switch to using fputc and fgetc in the routines).
JackB



Joined: 04 Mar 2016
Posts: 32
Location: Netherlands

View user's profile Send private message

PostPosted: Sat Mar 05, 2016 5:08 pm     Reply with quote

Thank you all for your helpful responses.
The tip to use 9-bits worked best for me.
Pointing to the dmx example for receiving dmx is not usefull.
For sending, that example would waste a lot of cycles too, not a good example.
So after all, I did not like the ccs c compiler, it seems too limiting to use the PIC registers, flags and interrupts. Mad
But it may be just me. Sad

I'm now using the XC8 compiler, and got the dmx receiver working.
It's all interrupt driven, so it takes a minimum of cycles.
Probably, I better understand what happens, and I feel like I have more control with the XC8 compiler.Cool
The PIC sends out pwm over 5 channels (1.25kHz, 0-100%), depending on the dmx input.
That was all I needed. Smile
Since it is xc8 code instead of ccs c code, it would probably make no sense to post it here. Rolling Eyes
temtronic



Joined: 01 Jul 2010
Posts: 9226
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sat Mar 05, 2016 6:14 pm     Reply with quote

With every compiler there is a 'learning curve',same holds true for every microcomputer and programming langauge. I'm first to admit I am NOT a C programmer, got doing it 20+ years ago when I went to a PIC seminar, taught myself assembler (only 30 instructions !), then got a contract for a project far more complicated than I could easily do in assembler. CCS PCM v 2.540 was 'current', cost effective and came with lots of examples. I'm still NOT a C programmer but managed to suceed in business and am now 'retired'. What I do know is that there is a core of truly talented C programmers here and that CCS C can control a huge number of PICs to do a LOT of interesting functions.There's 3 groups here. The 'pros', ones who do make money at programming PICs, the 'hobby guys', who make 'widgets or gizmos' and then the 'students', trying to cram 5 years of study into that many weeks ! I'm too old to learn yet another language or dialic of one so really if you're comfortable with XC8, great. If (when) it lets you down or seems impossible, come on back to CCS. It is 'open' so you can 'diddle bits' if you want or use their functions or create your own.

As for your DMX512 code, it'd be interesting to see it posted, perhaps someone will 'transpose' into CCS C. It's be interesting to compare !!

Jay

+++++++++++++++++++++++++
Temtronic,

XC8 is off topic. XC8 code should be posted in the Microchip forum.

- Forum Moderator
+++++++++++++++++++++++++
JackB



Joined: 04 Mar 2016
Posts: 32
Location: Netherlands

View user's profile Send private message

PostPosted: Sun Mar 06, 2016 3:19 am     Reply with quote

Now, I tried to use the xc8 compiler, and that actually works.
It uses rx interrupt and timer interrupt.
It would be nice if ccs c is capable to do this, but I really doubt it, after so much trying.

Code:

void init_timer0(void)
{
    // Set up TIMER0 to tick at 1ms intervals.
    // The oscillator ticks at Fosc/4, so 20Mhz/4 = 4MHz.
    // That is 1/16000000 s per tick, so 1 ms is 16000 ticks.
    T0CONbits.T08BIT = 0;   // Timer0 is configured as a 16-bit timer/counter
    T0CONbits.T0CS = 0;     // Internal clock (F OSC/4)
    T0CONbits.PSA = 1;      // Timer0 prescaler is not assigned; Timer0 clock input bypasses the prescaler
    long tpr = (F_CPU/4)/1000;
    tpr = 0xFFFF - tpr;
    TMR0H = tpr >> 8;
    TMR0L = tpr & 0xFF;

    INTCON2bits.TMR0IP = 0; // TMR0 Overflow Interrupt Priority bit, 0 = Low priority, 1 = High priority

    INTCONbits.T0IF = 0;    // Clear the flag
    INTCONbits.T0IE = 1;    // Enable the interrupt
    T0CONbits.TMR0ON = 1;   // Turn on finally set the period   

    INTCONbits.PEIE = 1;    // Turn on peripheral interrupts
    INTCONbits.GIE = 1;     // Turn on global interrupts
}

void init_dmx(void)
{
    // #use rs232(xmit=PIN_C6,rcv=PIN_C7,BAUD=250000,BITS=8,PARITY=N,STOP=2,ERRORS,stream=DMX)
    // Consider that DMX uses a "long character with an error" to say so,
    // to mark the start of packet, and this usually handled in the MCU by checking Overrun/Framing errors.
    // You should be prepared to handle that and then possibly use that to identify the beginning of a "frame".
       
    TRISCbits.TRISC6   = 0;   // TX1 DMX pin set as output
    TRISCbits.TRISC7   = 1;   // Rx1 DMX pin set as input
    TXSTA1bits.TX9     = 1;     // Selects 9-bit transmission
    TXSTA1bits.TXEN    = 1;     // Transmit is enabled
    TXSTA1bits.SYNC    = 0;     // Asynchronous mode
    TXSTA1bits.BRGH    = 1;     // High speed baudrate
    RCSTA1bits.SPEN    = 1;     // Serial port is enabled
    RCSTA1bits.RX9     = 1;     // Selects 9-bit reception
    RCSTA1bits.CREN    = 1;     // Enables the receiver
    BAUDCON1bits.BRG16 = 1;     // 16-bit baud rate generator
    SPBRG1             = 19;    // SYNC=0, BRG16=1, BRGH=1 16-Bit/Asynchronous  F OSC /[4 (n + 1)]  <-- ((20000000 /38400)/4) ? 1 = 129
    SPBRGH1            = 0;     // n = (20000000/(250000*4)) -1 = 19

    PIR1bits.RC1IF     = 0;     // Clear RC1IF flag
    PIR1bits.TX1IF     = 0;     // Clear TX1IF flag

    PIE1bits.RC1IE     = 1;     // Enable RC interrupt

    INTCONbits.PEIE = 1;    // Turn on peripheral interrupts
    INTCONbits.GIE = 1;     // Turn on global interrupts

    for (int i = 0; i < DMX_SIZE; i++) {
        TX1_Data[i] = 0;
        RX1_Data[i] = 0;
    }
    TX1_Data_Index = 0;
    RX1_Data_Index = 0;
    dmx_break_time = 0;
}

void interrupt __isr_handler(void)
{
    // Check if the interrupt is caused by RC1
    if(PIE1bits.RC1IE && PIR1bits.RC1IF)
    {
        PIR1bits.RC1IF = 0;             // Clear RC1IF flag
        char Byte = RCREG1;             // Read RX register
        if (RCSTA1bits.FERR == 1)
            RCSTA1bits.FERR = 0;    // Framing Error
        if (RCSTA1bits.OERR == 1)
            RCSTA1bits.OERR = 0;    // Overrun Error
        if (dmx_break_time > 15)
            RX1_Data_Index = 0;     // MARK time after Slot > 15mS, reset index
        RX1_Data[RX1_Data_Index++] = Byte; // Received from RX register
        dmx_break_time = 0;
    }

    // Check if the interrupt is caused by Timer0
    if (INTCONbits.TMR0IE && INTCONbits.T0IF) {
        INTCONbits.T0IF = 0;
        long tpr = (F_CPU/4)/1000;
        tpr = 0xFFFF - tpr;
        TMR0H = tpr >> 8;
        TMR0L = tpr & 0xFF;
        __msecs++;

        // Measure DMX break time
        dmx_break_time++;           // Every msec
    }
}

[/code]
Ttelmah



Joined: 11 Mar 2010
Posts: 19513

View user's profile Send private message

PostPosted: Sun Mar 06, 2016 4:43 am     Reply with quote

The setup, for the serial for example, is one line in CCS. Just select 9bits in the #use RS232, and use an int16 to send/receive the value. Done.
The timer is similar. One line for the setup, one to load the value, and two for the interrupts.

99.9% of the time you never have to touch registers in CCS. This is one of it's great powers. You can if you want. in fact it is now rather nice, with the getenv ability allowing you to use them by name, but nothing you are doing needs you to ever touch a register directly.
JackB



Joined: 04 Mar 2016
Posts: 32
Location: Netherlands

View user's profile Send private message

PostPosted: Sun Mar 06, 2016 9:53 am     Reply with quote

I really appreciate all your help! :-)
OK here it goes, but unfortunately it does not receive anything.
We are almost there!
But for what stream do I enable receive interrupts with this?
enable_interrupts(INT_RDA); // serial receiver (which one? STD or DMX ?)

I am toggling a LED, and I see no interrupts get fired there?

Code:

#use rs232(uart2, baud=9600, stream=STD)

#use rs232(xmit=PIN_C6,rcv=PIN_C7,BAUD=250000,BITS=9,PARITY=N,STOP=2,ERRORS,stream=DMX)

int16 break_time;
int16 dmx_in[128];
char dmx_index;

void main()
{
        dmx_index = 0;
    value = get_tris_d() & 0B01111111;  // outputs: d7
    set_tris_d(value);

    setup_timer_2(T2_DIV_BY_4,249,10);
    enable_interrupts(INT_TIMER1);
    enable_interrupts(INT_RDA);      // serial receiver (which one? STD or DMX ?)
    enable_interrupts(GLOBAL);
    while(1)
    {
        fprintf(STD, "value: %ld\r\n", dmx_in[0]);
        fprintf(STD, "value: %ld\r\n", dmx_in[1]);
        fprintf(STD, "value: %ld\r\n", dmx_in[2]);
        fprintf(STD, "value: %ld\r\n", dmx_in[3]);
        delay_ms(1000);

    }
}

#INT_RDA
void dmx() {
    // Is this a receiver interrupt on the DMX stream? It does not trigger...
    output_toggle(PIN_E1);
    dmx_in[dmx_index++] = fgetc(DMX);

}

#INT_TIMER2
void timer2_isr()
{
    // Timer interrupt every 1 mSec
    if(--int_count==0) {           // this program.
        output_toggle(PIN_C1);
        int_count = 500;
    }
    break_time++;
    if (break_time > 15)
        dmx_index = 0;
}



But now i get a compiler error:
For this line:
dmx_in[dmx_index++] = fgetc(DMX);
A numeric expression must appear here

I have left this line out for now and watched the LED. It does not toggle, so no interrupts...


Last edited by JackB on Sun Mar 06, 2016 11:12 am; edited 2 times in total
temtronic



Joined: 01 Jul 2010
Posts: 9226
Location: Greensville,Ontario

View user's profile Send private message

PostPosted: Sun Mar 06, 2016 10:55 am     Reply with quote

Yes I know XC8 is offtopic but if translated into CCS, it might show that CCS is easier, and we'd get a few more converts ? After all what 'they ' do in several lines of code, CCS can do in one !!

Jay
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group