Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Last active December 20, 2015 11:19
Show Gist options
  • Save RickKimball/6122630 to your computer and use it in GitHub Desktop.
Save RickKimball/6122630 to your computer and use it in GitHub Desktop.
lpc8xx lpc810 ws2811/ws2812 driver routines in cortex-m0+ gnu assembler
@-------------------------------------------------------------------------------
@ ws2811_utils.S - write pixel data to ws2811 leds
@-------------------------------------------------------------------------------
@
@ lpc810 cortex-m0+ ARM ASM routine for streaming pixels data
@
@ assumes interrupts are disabled when called
@ assumes 24MHz system clock, flash wait state set to 0
@ assumes P0_2 configured as OUTPUT connected to ws2811 DIN pin
@
@ Author: rick@kimballsoftware.com
@ Date: 21-Jul-2013
@ Desc: this code is part of the fabooh framework
@
@ Version 1.03 - fix for problem with high bit set on 2nd and subsequent bytes
@ Version 1.02 - reduced flash footprint by replacing nops with br .+2
@ Version 1.01 - update timing for 24MHz clock and high speed (800k)
@
.cpu cortex-m0plus
.thumb
.fpu softvfp
.syntax unified
.file "ws2811_utils.S"
@-------------------------------------------------------------------------------
@ void ws2811_write(uint8_t *pixel_data, unsigned byte_cnt)
@
@ Desc: Stream Green Red Blue (GRB) data to a ws2811 chip with timing based
@ on cycle counting. See ws2811 data sheet for timing details. NOPS
@ are used to fill the time between turning P0_2 on and off to
@ achieve precise timings.
@
@ The ws2811 reshapes the incoming signal and outputs a pulse of
@ 340ns for a 0 code and a 675ns pulse for the 1 code. A total cycle
@ period of 1250ns (800k) is used. This code using a 24MHz clock
@ produces a 333.333 ns 0 code pulse and a 666.666 ns 1 code pulse.
@
@ Example:
@ // 3 color bytes for each ws2811 ic chip
@ // light up 4 ws2811 leds pixels
@ static const uint8_t pixel_data[] = {
@ 0x00,0x3F,0x00, // red
@ 0x3F,0x3F,0x3F, // white
@ 0x00,0x00,0x3F, // blue
@ 0x3F,0x00,0x00, // green
@ };
@
@ ws2811_write((uint8_t *)&pixel_data[0],12);
@ delay_usec(50);
@
@
.text
.align 1
.global ws2811_write
.thumb_func
.type ws2811_write, %function
ws2811_write:
push {r4, r5, r6, lr} @ save off registers we affect
movs r2, #0b100 @ set led pin mask to P0_2
ldr r4, =0xA0002200 @ load r4 = &LPC_GPIO_PORT->SET0
ldr r5, =0xA0002280 @ load r5 = &LPC_GPIO_PORT->CLR0
movs r6, #0x80 @ pixel_mask, start at most significant bit
@ (30 cycle loop / 24MHz == 1250 ns)
.L_loop: @ send each pixel_data byte a bit at a time MSB->LSB
str r2, [r4, #0] @ 1 set P0_2 high - start of ON cycle
ldrb r3, [r0, #0] @ 2, 3 load r3 with pixel_byte = pixel_data[0]
b .+2 @ 4, 5 2 cycle delay
tst r3, r6 @ 6 check if next pixel bit is high or low
bne .L_code1pulse @ 7, (8 if branch taken)
.L_code0pulse: @--- 0 code bit is 333.333ns hi and 916.666ns low
nop @ 8
str r2, [r5,#0] @ 9 set P0_2 low, begin OFF cycle for a 0 code
b .+2 @ 10, 11 2 cycle delay
b .+2 @ 12, 13
b .+2 @ 14, 15
b .L2 @ 16, 17
.L_code1pulse: @--- 1 code bit is 666.666ns hi and 583.333ns low
b .+2 @ 9, 10 2 cycle delay
b .+2 @ 11, 12
b .+2 @ 13, 14
b .+2 @ 15, 16
str r2, [r5,#0] @ 17 set P0_2 low, begin OFF cycle for a 1 code
.L2:
b .+2 @ 18, 19 2 cycle delay
b .+2 @ 20, 21
nop @ 22
lsrs r6, r6, #1 @ 23 pin_mask >>= 1
beq .L3_getnextbyte @ 24, (25 if all bits sent)
.L3_morebits:
b .+2 @ 25, 26 2 cycle delays
b .+2 @ 27, 28
b .L_loop @ 29, 30
.L3_getnextbyte:
movs r6, #0x80 @ 26 start again at most significant bit of pixel
adds r0, #1 @ 27 pixel_data++
subs r1, #1 @ 28 cnt--
bne .L_loop @ 29, 30
.L_done:
pop {r4, r5, r6, pc} @ restore registers and return to caller
.size ws2811_write, .-ws2811_write
@-------------------------------------------------------------------------------
@ ws2812_utils.S - write pixel data to ws2811/2 leds
@-------------------------------------------------------------------------------
@
@ lpc810 cortex-m0+ ARM ASM routine for streaming pixels data
@
@ assumes interrupts are disabled when called
@ assumes 12MHz system clock, flash wait state set to 0
@ assumes P0_2 configured as OUTPUT connected to ws2811 DIN pin
@
@ Author: rick@kimballsoftware.com
@ Date: 21-Jul-2013
@ Desc: this code is part of the fabooh framework
@
@ Version 1.04 - 800k high speed version for 12MHz, +smaller code size 56 bytes
@ Version 1.03 - fix for problem with high bit set on 2nd and subsequent bytes
@ Version 1.02 - reduced flash footprint by replacing nops with br .+2
@ Version 1.01 - update timing for 24MHz clock and high speed (800k)
@
.cpu cortex-m0plus
.thumb
.fpu softvfp
.syntax unified
.file "ws2812_utils.S"
@-------------------------------------------------------------------------------
@ void ws2812_write(uint8_t *pixel_data, unsigned byte_cnt)
@
@ Desc: Stream Green Red Blue (GRB) data to a ws2811/2 chip with timing based
@ on cycle counting. See ws2811 data sheet for timing details. NOPS
@ are used to fill the time between turning P0_2 on and off to
@ achieve precise timings.
@
@ The ws2811 reshapes the incoming signal and outputs a pulse of
@ 340ns for a 0 code and a 675ns pulse for the 1 code. A total cycle
@ period of 1461ns (800k) is used. This code using a 12MHz clock
@ produces a 333.333 ns 0 code pulse and a 666.666 ns 1 code pulse.
@
@ Example:
@ // 3 color bytes for each ws2811 ic chip
@ // light up 4 ws2811 leds pixels
@ static const uint8_t pixel_data[] = {
@ 0x00,0x3F,0x00, // red
@ 0x3F,0x3F,0x3F, // white
@ 0x00,0x00,0x3F, // blue
@ 0x3F,0x00,0x00, // green
@ };
@
@ ws2812_write_hs((uint8_t *)&pixel_data[0],12);
@ delay_usec(50);
@
@
.text
.align 1
.global ws2812_write_hs
.thumb_func
.type ws2812_write_hs, %function
ws2812_write_hs:
push {r4, r5, r6, lr}
@ r0 contains &led_data[0]
@ r1 contains led_data_cnt
movs r2, #0b100 @ r2 set to P0_2 pin bitmask
ldr r3, =0xa0002200 @ &LPC_GPIO->SET0
ldr r4, =0xa0002280 @ &LPC_GPIO->CLR0
add r1, r1, r0 @ &led_data += led_cnt
1: @ 17 cycle loop (data transfer time 1416.66ns)
ldrb r5, [r0, #0] @ T15,16 load r5 with color byte from array
movs r6, #0x80 @ T17 set pixel bit mask to MSB
2:
str r2, [r3, #0] @ T1 set P0_2 high
tst r5, r6 @ T2 test bit to use code 0 timing
nop @ T3 1 cycle delay
bne 3f @ T4 if 1 code then wait to go low
str r2, [r4, #0] @ T5 set P0_2 low at 333.33ns T0H
3:
lsrs r6, r6, #1 @ T6 shift pixel mask right & set flag
b .+2 @ T7,8 2 cycle delay
str r2, [r4, #0] @ T9 set P0_2 low at 666.66ns T1H
bne 4f @ T10 check Z flag to send 8 bits
adds r0, #1 @ T11 get next led byte, led_data++
cmp r0, r1 @ T12 all leds done?
bne 1b @ T13,14 do another byte if not at end
@ beq? fall through and exit
4:
b .+2 @ T12,13 delays to match byte load time
b .+2 @ T14,15 2 cycle nop
bne 2b @ T16,17 do another bit we haven't done 8
pop {r4, r5, r6, pc}
.size ws2812_write_hs, .-ws2812_write_hs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment