Skip to content

Instantly share code, notes, and snippets.

@RickKimball RickKimball/main.cpp
Last active Aug 29, 2015

Embed
What would you like to do?
ws281x driver using USCI SPI, with updated asm code for msp430-elf-gcc
/*
* 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
You can’t perform that action at this time.