Moin!
Habe nach langer Zeit mal mein STK500 wieder angekorkt und n bissen mit den o.a. Led's rumgespielt.
Da ich von Assembler kein' Plan habe, wollte ich mal sehen, ob ich die Timings nicht auch per SPI geregelt kriege.
Das ganze funzt auch, allerdings ist die ISR sehr unschön, mal abgesehen von der unglaublichen Verschwendung von SRAM, aber egal.
Vielleicht ist hier jemand so nett und kann sich den Code mal ansehen oder ihn einfach sonst irgendwie gebrauchen. Wäre schön, wenn die ISR malin Assembler getestet werden würde (die For-Next-Schleife), dazu fehlt mir einfach das Know-How.
Danke und viele Grüße
Jan
Code
$regfile = "m32def.dat"
$hwstack = 128
$swstack = 128
$framesize = 128
$baud = 57600
'We need an 12,8MHz-Crystal to achieve the desired SPI-Speed. Using an TXCO from funkamateur-shop.
$crystal = 12800000
'Have had some serious trouble using zero-based array's, so leave default array-numbering.
'Config Base = 0
'(
The WS2811/2 protocol is as follows:
Send 24 bits (Green, Red, Blue) for each chip in the strip, all stuck together in one shot.
There are two possible speeds to do so: 400Khz and 800Khz, which is decided by the way the chips are hard-wired.
If you have got a 800Khz strip, you won't be able to address it using the 400Khz timings.
The logic 0, on the 800Khz strip, should be sent using a 0,25us physical 1, followed by a 1 uS physical 0, +- 0,075uS
The logic 1, on the 800Khz strip, should be sent using a 0,6 us physical 1, followed by a 0,65uS physical 0, +- 0,075uS
To reset the strip (go back to the first led), send a minimum 50uS 0 physical value on the line.
If you have a 400Khz version, double all numbers!
To achieve those timings using SPI (for the 800Khz), we select a clock speed of 3.2Mb/s, giving a 0,3125 bit time.
For the logical 0, we send 4 bits: 1000, giving 0,3125uS physical 1, followed by 0,9375uS physical 0, both within allowed error margins.
For the logical 1, we send 4 bits: 1100, giving 0,625uS physical 1, followed by 0,625uS physical 0, both within allowed error margins.
Therefore, we send 2 logical bits with each physical SPI byte. So to address a single color (value 0 to 255), which consists of
8 bits, we need 4 SPI bytes (8 bits color / 2 logical bits per SPI byte = 4 SPI bytes).
So to send the whole 24 bits GRB sequence, we need to send 12 SPI bytes.
If you have the 400Khz kind, just divide the SPI clock by two, to 1.6Mb/s. To achieve this speed, use an xMega-processor.
')
'The pin where the led's are connected. Must be MOSI.
di Alias PortB.5
Declare Sub SetLED(Byval index As Integer , Byval red As Byte , Byval green As Byte , Byval blue As Byte)
'Make sure to use the hardware SPI with a clock divider of 4.
Config Spi = Hard , Interrupt = Off , Data Order = msb , Master = Yes , Polarity = high , Phase = 1 , Clockrate = 4 , Noss = 1 , Spiin = 0
'Initialize the SPI-Bus.
Spiinit
'Configure the timer to periodically transmit the buffer to the led's.
'Don't need to preload a value, giving us 20.48 ms at 12.8MHz. You can even use a 16-bit timer to further increase the interrupt time.
On Timer0 Transmit
Config Timer0 = Timer , Prescale = 1024
Enable Timer0
Enable Interrupts
'Define some constants and calculate the needed buffer-size.
'It's a waste of space, but hey, it's only a proof of concept.
Const NumberOfLEDs = 128
Const BufferSize = NumberOfLEDs * 12
'Dimension the led-buffer.
Dim ledBuffer(BufferSize) As Byte
Dim a As Integer , x As Integer
'We need to preload the buffer with zeroes => color values (GRB = 0/0/0)
For a = 1 To BufferSize
ledBuffer(a) = &B10001000
Next
call setled(0 , 25 , 45 , 70)
call SetLED(127 , 1 , 1 , 1)
Do
Loop
'This method takes an index and RGB color-values to light up the desired led.
'It calculates the correct start-position in the buffer-array and sets the correct bits.
'Absolutely not nice, but it's working. Feel free to improve as you like.
Sub SetLED(Byval index As Integer , Byval red As Byte , Byval green As Byte , Byval blue As Byte)
Local x As Byte
Local startIndex As Integer
startIndex = index * 12
Incr startIndex
For x = 7 To 0 Step -1
Select Case x
Case 7
ledBuffer(startIndex).6 = green.x
ledBuffer(startIndex + 4).6 = red.x
ledBuffer(startIndex + 8).6 = blue.x
Case 6
ledBuffer(startIndex).2 = green.x
ledBuffer(startIndex + 4).2 = red.x
ledBuffer(startIndex + 8).2 = blue.x
Case 5
ledBuffer(startIndex + 1).6 = green.x
ledBuffer(startIndex + 5).6 = red.x
ledBuffer(startIndex + 9).6 = blue.x
Case 4
ledBuffer(startIndex + 1).2 = green.x
ledBuffer(startIndex + 5).2 = red.x
ledBuffer(startIndex + 9).2 = blue.x
Case 3
ledBuffer(startIndex + 2).6 = green.x
ledBuffer(startIndex + 6).6 = red.x
ledBuffer(startIndex + 10).6 = blue.x
Case 2
ledBuffer(startIndex + 2).2 = green.x
ledBuffer(startIndex + 6).2 = red.x
ledBuffer(startIndex + 10).2 = blue.x
Case 1
ledBuffer(startIndex + 3).6 = green.x
ledBuffer(startIndex + 7).6 = red.x
ledBuffer(startIndex + 11).6 = blue.x
Case 0
ledBuffer(startIndex + 3).2 = green.x
ledBuffer(startIndex + 7).2 = red.x
ledBuffer(startIndex + 11).2 = blue.x
End Select
Next
End Sub
'ISR to transmit the whole buffer to the led's.
'This is by far the crappiest part. Can't use a simple For-Next-Loop since it's too slow.
'Maybe a little assembler might help here, but thats out of my scope.
'Just pump the buffer out using multiple SPI-Writes.
Transmit:
'Reset the pointer to the first led.
Reset di
Waitus 60
Set di
'Pump the buffer to the bus. Using 240 Bytes each, therefore addressing 20 led's.
SPIOut ledBuffer(1) , 240
SPIOut ledBuffer(241) , 240
SPIOut ledBuffer(481) , 240
SPIOut ledBuffer(721) , 240
SPIOut ledBuffer(961) , 240
SPIOut ledBuffer(1201) , 240
SPIOut ledBuffer(1441) , 95
'Would like to use this, to make it much nicer, but it's too slow.
'Results in some weird flickering.
'Feel free to try it using asm.
'For x = 1 To BufferSize Step 12
' SPIOut ledBuffer(x) , 12
'Next
Return
End
Alles anzeigen