PDA

View Full Version : Better processing of a serial bitstream



breesy
- 7th June 2007, 11:34
Hi,

I have a little side task that I have been working on, basically its a string of RGB led's strung together to produce a decorative christmas lighting effect.. Each of the LED's is actually a tiny PCB with a PIC12F629 on board which PWM each colour. These chips run at 4 MHz using their internal RC oscillator, and do not have inbuilt USARTs, using debugin we can achieve a maximum of 9600 baud.

To address each PIC, a common serial line is fed to each of the PIC's and each PIC takes three bytes (one for each colour) out of the byte stream based on its ID (location in the chain). In addition there are two leading bytes of which we don't care about their value, they are merely used to trigger into the interrupt and a trailing byte. To prevent re-entry into the interrupt once we have received our LED values we wait until the sentinel byte is received.

By now your probably starting to see the problem, with 25 PICs we have a string length of 75 + 3 bytes, at 9600 baud this will take somewhere in the region of 80ms per update. During this time the lights are out, and a noticeable flicker is observed. I have considered disabling the interrupt and using a timer to re-enable it later allowing the main PWM loop to continue. However this only fixes the problem near the beginning of the string. Towards the end we are still waiting ~80ms for the data to arrive. Another alternative is to address each PIC individually however this is quite inefficient when changing the entire string at once. Theres always using a chip with PWM or a separate PWM chip, but I already have the PIC's and PCBs, and besides on longer strings the fewer and cheaper the parts the better, currently it is only a PIC, RGB LED and 1uF cap per board. So the question I pose is, is there a better solution that I am missing to spend the least time away from the main loop yet still be able to address all PIC's and update them several times a second?

I guess if I ever make another string I could use the PIC12F615 with an 8 MHz oscillator, but the fundamental problem still exists.

My current code is below:



@ __config _INTRC_OSC_NOCLKOUT & _WDT_OFF & _MCLRE_OFF & _CP_OFF

DEFINE OSC 4
DEFINE OSCCAL_1K 1

DEFINE DEBUGIN_REG GPIO ' Set Debugin pin port
DEFINE DEBUGIN_BIT 2 ' Set Debugin pin bit
DEFINE DEBUG_BAUD 9600 ' Set Debugin baud rate (same as Debug baud)
DEFINE DEBUGIN_MODE 0 ' Set Debugin mode: 0 = true, 1 = inverted

ID CON 1

vRed var Byte
vBlue var Byte
vGreen var Byte
Buffer var Byte
Junk var Byte
Counter var Byte
Shaddow var Byte

'Symbols
Symbol GIE = INTCON.7 ' Global Interrupt Enable
Symbol PEIE = INTCON.6 ' Peripheral Interrupts Enabled
Symbol INTIF = INTCON.1 ' Ext Interrupt Flag
Symbol INTIE = INTCON.4 ' Ext Interrupt Enable
Symbol INTEDG = OPTION_REG.6 ' Interrupt Edge
Symbol TMR1ON = T1CON.0 ' Timer1 On
Symbol TMR1IE = PIE1.0 ' Timer1 Interrupt Enable
Symbol TMR1IF = PIR1.0 ' Timer1 Interrupt Flag


' -------------- Pin Assignments --------------
GP0 var GPIO.0
gRed var GPIO.1
Rx var GPIO.2
GP3 var GPIO.3
gGreen var GPIO.4
gBlue var GPIO.5

Symbol Red = Shaddow.1
Symbol Green = Shaddow.4
Symbol Blue = Shaddow.5

' -------------- Initialisation --------------

Initialisation:
'OSCCAL = %11111111 ' 4 MHz Internal Oscillator
Pause 50 ' Safe Start Up Delay

ANSEL = %00000000 ' Disable Analogue Inputs
CMCON = %00000000 ' Disable Comparator

TRISIO = %00111111

Shaddow = %11111111
GPIO = Shaddow

Junk = 2 + 3 * (ID - 1)
vRed = 0
vGreen = 0
vBlue = 0

Input Rx

Output gRed
High gRed

Output gGreen
High gGreen

Output gBlue
High gBlue

'Enable Interrupts
GIE = 1
PEIE = 1
INTEDG = 0
INTIE = 1
INTIF = 0

ON INTERRUPT GOTO Int_Handle

'-------------- Main Loop --------------
Iterate:

DISABLE
If Counter = 0 Then Shaddow = %11001101

If Counter = vRed Then Red = 1
If Counter = vGreen Then Green = 1
If Counter = vBlue Then Blue = 1
GPIO = Shaddow

Counter = Counter + 1
ENABLE

Goto Iterate



DISABLE
' -------------- Interrupts --------------
Int_Handle:

GPIO = %11111111

DebugIn 1, Int_Exit, [skip Junk, vRed, vGreen, vBlue, wait (255)]
vRed = vRed
vGreen = vGreen
vBlue = vBlue

Counter = 0

Int_Exit:
INTIF = 0
Resume

ENABLE

end



All the Best,
Daniel

skimask
- 7th June 2007, 15:19
I did the same thing awhile back, use an 8 pin PIC without a UART, ran RGB LEDs using PWM, tried to control/run them using serial input.
Problem is, it takes X amount of time to receive serial bytes using the software 'bit-bang' method, which invariably interrupts your PWM for the LEDs, causing the flickering.

My solution (and I'm sorry I can't give you the code I used 'cause I got paid a fair amount of money for it and the owner of that code is a tinkerer himself), using a 12F683, was to use the built in 8Mhz oscillator to run the TMR0 and use the overflow interrupt, this gave me a 7812.5Hz loop, which you could use to sample a serial input line using a 7812.5 baud rate. Well, that doesn't work too well because the bits have to be perfectly centered and everything.
So, I use a bit of multi-sampling, over-clocking, majority logic, whatever you want to call it, whatever it's really called...I don't know.
I knocked my transmitter baud rate down to 488.28125 (or at least as close to that as I could get it), and sampled the receive bit 16 times and I'd count the amount of time the bit was high and/or low and pick the higher number of the two.
The one special case is the initial 'start' bit. If all 8 bits ended up being a '1', then I'd continue to look for another 4 bits of high without any low in there anywhere. Same thing for the stop bits.
Basically, I made it up as I went along... It's kinda like bit-banging a serial port, but the program keeps running in the background.

Oh, and at the same time the TMR0 overflow would interrupt, after checking the serial input, I'd update the RGB PWM for the LEDs.
Worked out kinda slick...

Hope that gives you some ideas...

SteveB
- 8th June 2007, 00:13
How often do you need to send each slave a seperate RBG value? If most of your transmissions will have the same values for many of the slaves, you might try something along these lines. Use a 4 byte + 3 Byte string. The 4 bytes is used to address the slaves, each with their own individual control bit. The 3 bytes are the RGB values. Any slave whose bit is set in the 4 bytes will use the RGB values, others will ignore the data.

This would allow you to send a much smaller data packet, and the slave processing overhead would still be pretty minimal. Also, you could use the left over 7 bits (from the 4 byte group) to have a bunch of "commands" preprogrammed that the slaves could respond to accordingly.

The obvious downside is that if you need to have individual values for all 75 slaves it is much more data then what you've got.

Also, you might be able to dump the interrupt and just poll the RX pin. The master could hold this low (or high) for a period of time and the slaves can poll it. If it is in the correct state for a loop or two of the main loop, the slave jumps to the recieve and waits for the data. This would rid you of the need for the "junk" data. You would have to tinker with the timing to get all the slaves to respond reliably, but it should work well. With the above technique, you looking at about 7.3ms per data packet. 25 of them is 183ms, so a bit high if you have to update all of the slaves individually.

Just a couple of thoughts,
SteveB