Last active
December 20, 2015 11:19
-
-
Save RickKimball/6122630 to your computer and use it in GitHub Desktop.
lpc8xx lpc810 ws2811/ws2812 driver routines in cortex-m0+ gnu assembler
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@------------------------------------------------------------------------------- | |
@ 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@------------------------------------------------------------------------------- | |
@ 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