|
|
View previous topic :: View next topic |
Author |
Message |
JackB
Joined: 04 Mar 2016 Posts: 32 Location: Netherlands
|
[solved] dmx512 receiver? |
Posted: Fri Mar 04, 2016 1:59 pm |
|
|
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: 9246 Location: Greensville,Ontario
|
|
Posted: Fri Mar 04, 2016 2:13 pm |
|
|
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
|
|
Posted: Fri Mar 04, 2016 3:25 pm |
|
|
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: 19552
|
|
Posted: Fri Mar 04, 2016 3:29 pm |
|
|
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: 9246 Location: Greensville,Ontario
|
|
Posted: Fri Mar 04, 2016 4:04 pm |
|
|
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
|
|
Posted: Fri Mar 04, 2016 4:16 pm |
|
|
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
|
|
Posted: Fri Mar 04, 2016 8:26 pm |
|
|
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 |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Fri Mar 04, 2016 11:03 pm |
|
|
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: 19552
|
|
Posted: Sat Mar 05, 2016 3:41 am |
|
|
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
|
|
Posted: Sat Mar 05, 2016 5:08 pm |
|
|
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.
But it may be just me.
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.
The PIC sends out pwm over 5 channels (1.25kHz, 0-100%), depending on the dmx input.
That was all I needed.
Since it is xc8 code instead of ccs c code, it would probably make no sense to post it here. |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9246 Location: Greensville,Ontario
|
|
Posted: Sat Mar 05, 2016 6:14 pm |
|
|
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
|
|
Posted: Sun Mar 06, 2016 3:19 am |
|
|
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: 19552
|
|
Posted: Sun Mar 06, 2016 4:43 am |
|
|
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
|
|
Posted: Sun Mar 06, 2016 9:53 am |
|
|
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: 9246 Location: Greensville,Ontario
|
|
Posted: Sun Mar 06, 2016 10:55 am |
|
|
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 |
|
|
|
|
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
|