MagicWSmoke
09.05.2013, 21:08
Nun, ich dachte mir: machst mal 'nen Crosspost :D
Nachdem ein Mitglied im MCS-Forum die Ansteuerung eines WS2811 über einen NOP-Schlangen-Code versuchte, dieses nach Unterstützung durchaus erfolgreich, erschien mir der Code doch ein wenig ineffektiv und auch ein bisserl hässlich.
Also hab' ich einen weiteren Code speziell für den ATXMega und dessen Fähigkeiten geschrieben.
Die Funktionsweise ist dergestalt, dass zwei DMA-Kanäle aus einem Array, welches die Helligkeitseinstellungen der einzelnen Leds enthält, einen Doppelpuffer befüllen und diese zwei Puffer abwechselnd in ein als SPI verwendetes UART schreiben. Die Form der in das UART geschrieben Daten sorgt dann für die Erzeugung der Low/High-Bits am TXD-Pin des ATXMega. Sobald ein Puffer leer-geschrieben ist, übernimmt der zweite, während der erste Puffer wieder über eine ISR befüllt wird. Ein Puffer enthält immer einen Frame aus 24 Bit, dieser wieder besteht aus 3 x 8 Bit, welche die Helligkeit der einzelnen Leds steuern, ledarr(0) = grün, ledarr(1) = rot, ledarr(2) = blau, usw.
Jedes Bit des Frames ist im Puffer als Nibble (4Bit) abgelegt, daraus ergeben sich die 12 Byte großen Puffer, welche per DMA in's UART befördert werden.
Der Prozess wird im Hintergrund ausgeführt und verbraucht einen relativ kleinen Teil der vorhandenen Rechenleistung.
Da bei mir kein WS2811-Strip vorhanden ist, jedoch das Mitglied im MCS-Forum das Funktionieren meines Codes bestätigt hat, würd' ich sagen dass es in Ordnung ist, den hier einzustellen.
Denn brauchen kann den sicherlich irgendwer. ;-)
$regfile = "xm128a1def.dat"
$crystal = 32000000
$hwstack = 64
$swstack = 40
$framesize = 40
'$noramclear
'$sim
Config Osc = Enabled , 32mhzosc = Enabled
Config Sysclock = 32mhz
Config Base = 0
'### user configurable ###
Const RGBunits = 3 ' total count of RGB units
Const rstto = 2 ' reset timeout, 1 equals one empty frame, 24 bits * 1,25µs = 30µs
'### user configurable ###
Const bytesperRGBunit = 3 ' every RGB-unit has 3 leds, every led is controlled by 1 byte
Const ledcnt = RGBunits * bytesperRGBunit ' total amount of leds
Const dbfsize = 12 ' one nibble per sent bit, 24 bit per RGB unit, 24 nibbles = 12 bytes
Const USART_TXEN = 3
Const USART_CMOD0 = 6
Const USART_CMOD1 = 7
Const USART_UCPHA = 1
Const USART_UDORD = 2
Const DMA_CH0TRNIF = 0
Const DMA_CH1TRNIF = 1
Const SPItmpl = &b10001000
Const SPIhnib = &b01000000 ' SPI_Out_0 = &b1000, 1.25µs/4*1 = H0.312µs, L0.938µs
Const SPIlnib = &b00000100 ' SPI_Out_1 = &b1100, 1.25µs/4*2 = H0.625µs, L0.625µs
Dim dblbuf_0(dbfsize) As Byte
Dim dblbuf_1(dbfsize) As Byte
Dim ledarr(ledcnt) As Byte ' ledarr(0) is first led of strip
Dim ledptr As Word ' points to actual shifted out RGB data
Dim toctr As Byte
ledptr = 0
ledarr(0) = 128
ledarr(1) = 170
ledarr(2) = 85
ledarr(3) = 1
ledarr(4) = 4
ledarr(5) = 32
On Dma_ch0 Dma_ch0_int NOSAVE
On Dma_ch1 Dma_ch1_int NOSAVE
'UART0 in SPI Mode, TXD --> PD3, XCK --> PD1 (unused)
Config PORTD.3 = Output ' set SPI TXD to output
USARTD0_BAUDCTRLA = 4 ' BSEL = (32MHz/(2*3.2MBaud))-1, 1/(1.25µs/4bit) = 3.2MBaud
USARTD0_BAUDCTRLB = 0 ' BSCALE = 0
USARTD0_CTRLA = 0
USARTD0_CTRLB = Bits(USART_TXEN) ' enable transmitter
USARTD0_CTRLC = Bits(USART_CMOD1 , USART_CMOD0) ' enable UART SPI-mode, MSB first, sample rising edge
Config Dma = Enabled , Doublebuf = CH01 , Cpm = CH0123
'Trigger Base Value = &H6B + Data register empty (DRE) &H01 --> &H6C
Config Dmach0 = Enabled , Burstlen = 1 , Chanrpt = Enabled , Tci = Lo , Eil = OFF , Singleshot = Enabled , _
Sar = BLOCK , Sam = INC , Dar = NONE , Dam = FIXED , Trigger = &H6C , Btc = dbfsize , Repeat = 0 , Sadr = Varptr(dblbuf_0(0)) , Dadr = Varptr(usartD0_data)
Config Dmach1 = Enabled , Burstlen = 1 , Chanrpt = Enabled , Tci = Lo , Eil = OFF , Singleshot = Enabled , _
Sar = BLOCK , Sam = INC , Dar = NONE , Dam = FIXED , Trigger = &H6C , Btc = dbfsize , Repeat = 0 , Sadr = Varptr(dblbuf_1(0)) , Dadr = Varptr(usartD0_data)
Config Priority = Static , Vector = Application , Lo = Enabled , Med = Enabled
Enable Interrupts
Config PORTE.0 = Output ' I'm alive led0 on Atmel XPlained ATMega128A1
Config PORTD.2 = Output
Dim A As Word
Dim C As Word
Const testspeed = 0
#IF testspeed = 0
Do
Waitms 500
Toggle PortE.0
Loop
#ELSE
Do
Toggle PortD.2
For A = 0 To 65535 ' 91ms for this loop with disabled interrupts (no DMA)
C = A ' 108ms with interrupts (with DMA), CPU 84% App <-> 16% ISR/DMA
Next A
Loop
#ENDIF
End
Dma_ch0_int:
!PUSH ZL
!IN ZL, SREG
!PUSH ZL
!PUSH ZH
Loadadr dblbuf_0(0) , Z
!RCALL dblbuff
!LDI ZL, 2^DMA_CH0TRNIF
!STS DMA_INTFLAGS, ZL
!POP ZH
!POP ZL
!OUT SREG, ZL
!POP ZL
Return
Dma_ch1_int:
!PUSH ZL
!IN ZL, SREG
!PUSH ZL
!PUSH ZH
Loadadr dblbuf_1(0) , Z
!RCALL dblbuff
!LDI ZL, 2^DMA_CH1TRNIF
!STS DMA_INTFLAGS, ZL
!POP ZH
!POP ZL
!OUT SREG, ZL
!POP ZL
Return
!dblbuff:
!PUSH r16
!PUSH r17
!PUSH r24
!PUSH r25
!PUSH XL
!PUSH XH
!LDI r16, bytesperRGBunit
!LDS r24, {toctr}
!TST r24
!BREQ filldb
!LDI r16, dbfsize
!CLR r17
!clrdblbuf:
!ST Z+, r17
!DEC r16
!BRNE clrdblbuf
!DEC R24
!STS {toctr}, r24
!RJMP fillend
!filldb:
Loadadr ledarr(0) , X
!LDS r24, {ledptr}
!LDS r25, {ledptr+1}
!ADD XL, r24
!ADC XH, r25
!ADIW r24, bytesperRGBunit
!LDI r17, hbyte(ledcnt)
!CPI r24, lbyte(ledcnt)
!CPC r25, r17
!BRCS avail
!CLR r24
!CLR r25
!LDI r17, rstto
!STS {toctr}, r17
!avail:
!STS {ledptr}, r24
!STS {ledptr+1}, r25
!byteloop:
!LD r24, X+
!LDI r17, SPItmpl
!BST r24, 7
!BLD r17, 6
!BST r24, 6
!BLD r17, 2
!ST Z+, r17
!LDI r17, SPItmpl
!BST r24, 5
!BLD r17, 6
!BST r24, 4
!BLD r17, 2
!ST Z+, r17
!LDI r17, SPItmpl
!BST r24, 3
!BLD r17, 6
!BST r24, 2
!BLD r17, 2
!ST Z+, r17
!LDI r17, SPItmpl
!BST r24, 1
!BLD r17, 6
!BST r24, 0
!BLD r17, 2
!ST Z+, r17
!DEC r16
!BRNE byteloop
!fillend:
!POP XH
!POP XL
!POP r25
!POP r24
!POP r17
!POP r16
Return
Nachdem ein Mitglied im MCS-Forum die Ansteuerung eines WS2811 über einen NOP-Schlangen-Code versuchte, dieses nach Unterstützung durchaus erfolgreich, erschien mir der Code doch ein wenig ineffektiv und auch ein bisserl hässlich.
Also hab' ich einen weiteren Code speziell für den ATXMega und dessen Fähigkeiten geschrieben.
Die Funktionsweise ist dergestalt, dass zwei DMA-Kanäle aus einem Array, welches die Helligkeitseinstellungen der einzelnen Leds enthält, einen Doppelpuffer befüllen und diese zwei Puffer abwechselnd in ein als SPI verwendetes UART schreiben. Die Form der in das UART geschrieben Daten sorgt dann für die Erzeugung der Low/High-Bits am TXD-Pin des ATXMega. Sobald ein Puffer leer-geschrieben ist, übernimmt der zweite, während der erste Puffer wieder über eine ISR befüllt wird. Ein Puffer enthält immer einen Frame aus 24 Bit, dieser wieder besteht aus 3 x 8 Bit, welche die Helligkeit der einzelnen Leds steuern, ledarr(0) = grün, ledarr(1) = rot, ledarr(2) = blau, usw.
Jedes Bit des Frames ist im Puffer als Nibble (4Bit) abgelegt, daraus ergeben sich die 12 Byte großen Puffer, welche per DMA in's UART befördert werden.
Der Prozess wird im Hintergrund ausgeführt und verbraucht einen relativ kleinen Teil der vorhandenen Rechenleistung.
Da bei mir kein WS2811-Strip vorhanden ist, jedoch das Mitglied im MCS-Forum das Funktionieren meines Codes bestätigt hat, würd' ich sagen dass es in Ordnung ist, den hier einzustellen.
Denn brauchen kann den sicherlich irgendwer. ;-)
$regfile = "xm128a1def.dat"
$crystal = 32000000
$hwstack = 64
$swstack = 40
$framesize = 40
'$noramclear
'$sim
Config Osc = Enabled , 32mhzosc = Enabled
Config Sysclock = 32mhz
Config Base = 0
'### user configurable ###
Const RGBunits = 3 ' total count of RGB units
Const rstto = 2 ' reset timeout, 1 equals one empty frame, 24 bits * 1,25µs = 30µs
'### user configurable ###
Const bytesperRGBunit = 3 ' every RGB-unit has 3 leds, every led is controlled by 1 byte
Const ledcnt = RGBunits * bytesperRGBunit ' total amount of leds
Const dbfsize = 12 ' one nibble per sent bit, 24 bit per RGB unit, 24 nibbles = 12 bytes
Const USART_TXEN = 3
Const USART_CMOD0 = 6
Const USART_CMOD1 = 7
Const USART_UCPHA = 1
Const USART_UDORD = 2
Const DMA_CH0TRNIF = 0
Const DMA_CH1TRNIF = 1
Const SPItmpl = &b10001000
Const SPIhnib = &b01000000 ' SPI_Out_0 = &b1000, 1.25µs/4*1 = H0.312µs, L0.938µs
Const SPIlnib = &b00000100 ' SPI_Out_1 = &b1100, 1.25µs/4*2 = H0.625µs, L0.625µs
Dim dblbuf_0(dbfsize) As Byte
Dim dblbuf_1(dbfsize) As Byte
Dim ledarr(ledcnt) As Byte ' ledarr(0) is first led of strip
Dim ledptr As Word ' points to actual shifted out RGB data
Dim toctr As Byte
ledptr = 0
ledarr(0) = 128
ledarr(1) = 170
ledarr(2) = 85
ledarr(3) = 1
ledarr(4) = 4
ledarr(5) = 32
On Dma_ch0 Dma_ch0_int NOSAVE
On Dma_ch1 Dma_ch1_int NOSAVE
'UART0 in SPI Mode, TXD --> PD3, XCK --> PD1 (unused)
Config PORTD.3 = Output ' set SPI TXD to output
USARTD0_BAUDCTRLA = 4 ' BSEL = (32MHz/(2*3.2MBaud))-1, 1/(1.25µs/4bit) = 3.2MBaud
USARTD0_BAUDCTRLB = 0 ' BSCALE = 0
USARTD0_CTRLA = 0
USARTD0_CTRLB = Bits(USART_TXEN) ' enable transmitter
USARTD0_CTRLC = Bits(USART_CMOD1 , USART_CMOD0) ' enable UART SPI-mode, MSB first, sample rising edge
Config Dma = Enabled , Doublebuf = CH01 , Cpm = CH0123
'Trigger Base Value = &H6B + Data register empty (DRE) &H01 --> &H6C
Config Dmach0 = Enabled , Burstlen = 1 , Chanrpt = Enabled , Tci = Lo , Eil = OFF , Singleshot = Enabled , _
Sar = BLOCK , Sam = INC , Dar = NONE , Dam = FIXED , Trigger = &H6C , Btc = dbfsize , Repeat = 0 , Sadr = Varptr(dblbuf_0(0)) , Dadr = Varptr(usartD0_data)
Config Dmach1 = Enabled , Burstlen = 1 , Chanrpt = Enabled , Tci = Lo , Eil = OFF , Singleshot = Enabled , _
Sar = BLOCK , Sam = INC , Dar = NONE , Dam = FIXED , Trigger = &H6C , Btc = dbfsize , Repeat = 0 , Sadr = Varptr(dblbuf_1(0)) , Dadr = Varptr(usartD0_data)
Config Priority = Static , Vector = Application , Lo = Enabled , Med = Enabled
Enable Interrupts
Config PORTE.0 = Output ' I'm alive led0 on Atmel XPlained ATMega128A1
Config PORTD.2 = Output
Dim A As Word
Dim C As Word
Const testspeed = 0
#IF testspeed = 0
Do
Waitms 500
Toggle PortE.0
Loop
#ELSE
Do
Toggle PortD.2
For A = 0 To 65535 ' 91ms for this loop with disabled interrupts (no DMA)
C = A ' 108ms with interrupts (with DMA), CPU 84% App <-> 16% ISR/DMA
Next A
Loop
#ENDIF
End
Dma_ch0_int:
!PUSH ZL
!IN ZL, SREG
!PUSH ZL
!PUSH ZH
Loadadr dblbuf_0(0) , Z
!RCALL dblbuff
!LDI ZL, 2^DMA_CH0TRNIF
!STS DMA_INTFLAGS, ZL
!POP ZH
!POP ZL
!OUT SREG, ZL
!POP ZL
Return
Dma_ch1_int:
!PUSH ZL
!IN ZL, SREG
!PUSH ZL
!PUSH ZH
Loadadr dblbuf_1(0) , Z
!RCALL dblbuff
!LDI ZL, 2^DMA_CH1TRNIF
!STS DMA_INTFLAGS, ZL
!POP ZH
!POP ZL
!OUT SREG, ZL
!POP ZL
Return
!dblbuff:
!PUSH r16
!PUSH r17
!PUSH r24
!PUSH r25
!PUSH XL
!PUSH XH
!LDI r16, bytesperRGBunit
!LDS r24, {toctr}
!TST r24
!BREQ filldb
!LDI r16, dbfsize
!CLR r17
!clrdblbuf:
!ST Z+, r17
!DEC r16
!BRNE clrdblbuf
!DEC R24
!STS {toctr}, r24
!RJMP fillend
!filldb:
Loadadr ledarr(0) , X
!LDS r24, {ledptr}
!LDS r25, {ledptr+1}
!ADD XL, r24
!ADC XH, r25
!ADIW r24, bytesperRGBunit
!LDI r17, hbyte(ledcnt)
!CPI r24, lbyte(ledcnt)
!CPC r25, r17
!BRCS avail
!CLR r24
!CLR r25
!LDI r17, rstto
!STS {toctr}, r17
!avail:
!STS {ledptr}, r24
!STS {ledptr+1}, r25
!byteloop:
!LD r24, X+
!LDI r17, SPItmpl
!BST r24, 7
!BLD r17, 6
!BST r24, 6
!BLD r17, 2
!ST Z+, r17
!LDI r17, SPItmpl
!BST r24, 5
!BLD r17, 6
!BST r24, 4
!BLD r17, 2
!ST Z+, r17
!LDI r17, SPItmpl
!BST r24, 3
!BLD r17, 6
!BST r24, 2
!BLD r17, 2
!ST Z+, r17
!LDI r17, SPItmpl
!BST r24, 1
!BLD r17, 6
!BST r24, 0
!BLD r17, 2
!ST Z+, r17
!DEC r16
!BRNE byteloop
!fillend:
!POP XH
!POP XL
!POP r25
!POP r24
!POP r17
!POP r16
Return