Last active
August 29, 2015 13:57
-
-
Save RickKimball/9792143 to your computer and use it in GitHub Desktop.
ws281x driver using USCI SPI, with updated asm code for msp430-elf-gcc
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
/* | |
* main.cpp - msp430-elf-gcc ws281x USCI SPI driver using inline asm | |
* | |
* Author: kimballr | |
* Date: Dec 28, 2012 | |
* Version: 0.0002 | |
* | |
* 2014-09-29 rrk - updated for both msp430-elf-gcc and msp430-gcc | |
* P1.0 - CS - N/A not used, however it is still useful as an oscilloscope trigger | |
* P1.7 - MOSI -> WS281x DIN | |
*/ | |
#include <msp430.h> | |
#include <stdint.h> | |
typedef struct { | |
uint8_t g; | |
uint8_t r; | |
uint8_t b; | |
} RGBLED; | |
#define DATA_OUT_PIN BIT7 /* MOSI */ | |
/* macro to switch from RBG->GRB */ | |
#define RGB(x) ((unsigned long)(x)>>8)&0xff, ((unsigned long)(x)>>16)&0xff, (x)&0xff | |
static inline | |
void sendRGB(uint8_t * led_data, unsigned led_data_len); | |
int main(void) { | |
WDTCTL = WDTPW | WDTHOLD; | |
P1DIR |= BIT4; P1SEL |= BIT4; | |
P1DIR |= BIT0; P1OUT &= ~BIT0; | |
BCSCTL1 = CALBC1_16MHZ; | |
DCOCTL = CALDCO_16MHZ; | |
__delay_cycles(128); | |
DCOCTL = (DCOCTL+32) & (0b11100000); // set to next non-modulated value > 16MHz | |
__delay_cycles(8000000); | |
// configure USCI B0 as 800kHz per bit | |
UCB0CTL1 = UCSWRST; // put in reset mode | |
UCB0CTL0 = (UCMST | UCSYNC | UCMSB); // 8-bit SPI Master, MSB first data | |
P1SEL |= DATA_OUT_PIN; | |
P1SEL2 |= DATA_OUT_PIN; | |
UCB0CTL1 = UCSSEL_2; // SMCLK and take out of reset mode | |
#if 1 /* goofing around */ | |
__asm__ __volatile("mov #3, %[UCB0BR]" : : [UCB0BR] "m" (UCB0BR0)); | |
#else | |
UCB0BR0 = 0x03; // 1/(16MHz/3) = ~0.1875us per bit | |
UCB0BR1 = 0; | |
#endif | |
UCB0CTL1 &= ~UCSWRST; | |
{ | |
static const uint8_t pixel_data[] = { | |
RGB(0x0F0000), // red - all colors at 50% | |
RGB(0x000F00), // green | |
RGB(0x00000F), // blue | |
0x0F, 0x0F, 0x0F, // white | |
0x00, 0x0F, 0x00, // red | |
0x0F, 0x00, 0x00, // green | |
0x00, 0x00, 0x0F, // blue | |
}; | |
unsigned color_indx=0; | |
while(1) { | |
__delay_cycles(16000000/2); | |
P1OUT ^= BIT0; // toggle CS | |
#if 1 | |
sendRGB((uint8_t *) &pixel_data[color_indx], 12); // target 4 leds*(24 bit color) | |
color_indx = ( color_indx == 9 ) ? 0 : color_indx += 3; | |
#else | |
static const uint8_t test_leds[] = { | |
0b10110011, 0b00001111, 0b11110000 | |
}; | |
sendRGB(const_cast<uint8_t *>(test_leds), 3); | |
#endif | |
P1OUT ^= BIT0; | |
} | |
}; | |
return 0; | |
} | |
#if 0 | |
/* | |
* straight c version | |
*/ | |
void sendRGB(uint8_t * led_data, unsigned led_data_len) { | |
uint8_t *colorbits = &led_data[0]; | |
do { | |
unsigned color = *colorbits++; | |
unsigned colorpixel = 0x80; // shift out bits in MSB order | |
do { | |
while (!(IFG2 & UCB0TXIFG)); // wait for the tx buffer to be empty | |
(color & colorpixel) ? (UCB0TXBUF = 0xF0) : (UCB0TXBUF = 0xC0); | |
colorpixel >>= 1; | |
} while (colorpixel); | |
} while (--led_data_len); | |
while(UCB0STAT & UCBUSY); // wait for all bits to be transmitted | |
//__delay_cycles(800); // delay 50us to latch data, not be necessary as main loop waits | |
} | |
#else | |
/* | |
* inline msp430-elf-gcc asm version | |
*/ | |
void sendRGB(uint8_t * led_data, unsigned led_data_len) { | |
unsigned color, bit_cnt; | |
// shift out bits in MSB order | |
__asm__ __volatile__ | |
( | |
"1:\n" | |
" mov.b @%[led_data]+, %[color] ; 2 cycles\n" | |
" mov.b #8, %[bit_cnt] ; 1 cycle\n" | |
"2:\n" | |
" and.b %[TXIFG], %[IFG2] ; 4 cycles\n" | |
" jz 2b ; 2 cycles\n" | |
" rlc.b %[color] ; 1 cycle\n" | |
" jc 3f ; 2 cycles\n" | |
" mov.b %[OFF_BITS], %[TXBUF] ; 4 cycles\n" | |
" jmp 4f ; 2 cycles\n" | |
"3:\n" | |
" mov.b %[ON_BITS], %[TXBUF] ; 4 cycles\n" | |
" jmp .+2 ; 2 cycles\n" | |
"4:\n" | |
" dec %[bit_cnt] ; 1 cycle\n" | |
" jnz 2b ; 2 cycles\n" | |
" dec %[led_data_len] ; 1 cycle\n" | |
" jnz 1b ; 2 cycles\n" | |
"5:\n" | |
" and.b %[UCBUSY_], %[STAT] ; 4 cycles\n" | |
" jnz 5b ; 2 cycles\n" | |
:[led_data] "+&r" (led_data) | |
,[led_data_len] "+&r" (led_data_len) | |
,[color] "=&r" (color) | |
,[bit_cnt] "=&r" (bit_cnt) | |
:[ON_BITS] "r" (0b11110000) | |
,[OFF_BITS] "r" (0b11000000) | |
,[TXBUF] "m" (UCB0TXBUF) | |
,[IFG2] "m" (IFG2) | |
,[STAT] "m" (UCB0STAT) | |
,[TXIFG] "i" (UCB0TXIFG) | |
,[UCBUSY_] "i" (UCBUSY) | |
: "cc" | |
); | |
// NOTE: caller must delay 50us to latch data | |
} | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment