diff --git a/src/TWI_slave.c b/src/TWI_slave.c new file mode 100755 index 0000000..0377308 --- /dev/null +++ b/src/TWI_slave.c @@ -0,0 +1,264 @@ +/***************************************************************************** +* +* Atmel Corporation +* +* File : TWI_Slave.c +* Compiler : IAR EWAAVR 2.28a/3.10c +* Revision : $Revision: 2475 $ +* Date : $Date: 2007-09-20 12:00:43 +0200 (to, 20 sep 2007) $ +* Updated by : $Author: mlarsson $ +* +* Support mail : avr@atmel.com +* +* Supported devices : All devices with a TWI module can be used. +* The example is written for the ATmega16 +* +* AppNote : AVR311 - TWI Slave Implementation +* +* Description : This is sample driver to AVRs TWI module. +* It is interupt driveren. All functionality is controlled through +* passing information to and from functions. Se main.c for samples +* of how to use the driver. +* +****************************************************************************/ +/*! \page MISRA + * + * General disabling of MISRA rules: + * * (MISRA C rule 1) compiler is configured to allow extensions + * * (MISRA C rule 111) bit fields shall only be defined to be of type unsigned int or signed int + * * (MISRA C rule 37) bitwise operations shall not be performed on signed integer types + * As it does not work well with 8bit architecture and/or IAR + + * Other disabled MISRA rules + * * (MISRA C rule 109) use of union - overlapping storage shall not be used + * * (MISRA C rule 61) every non-empty case clause in a switch statement shall be terminated with a break statement +*/ + +#include +#include +#include "TWI_slave.h" + +static unsigned char TWI_buf[TWI_BUFFER_SIZE]; // Transceiver buffer. Set the size in the header file +static unsigned char TWI_msgSize = 0; // Number of bytes to be transmitted. +static unsigned char TWI_state = TWI_NO_STATE; // State byte. Default set to TWI_NO_STATE. + +// This is true when the TWI is in the middle of a transfer +// and set to false when all bytes have been transmitted/received +// Also used to determine how deep we can sleep. +static unsigned char TWI_busy = 0; + +union TWI_statusReg_t TWI_statusReg = {0}; // TWI_statusReg is defined in TWI_Slave.h + +/**************************************************************************** +Call this function to set up the TWI slave to its initial standby state. +Remember to enable interrupts from the main application after initializing the TWI. +Pass both the slave address and the requrements for triggering on a general call in the +same byte. Use e.g. this notation when calling this function: +TWI_Slave_Initialise( (TWI_slaveAddress< +#include + +#define F_CPU 16000000UL // 16Mhz + +#include + +// Setleds for standard RGB +void inline ws2812_setleds(struct cRGB *ledarray, uint16_t leds) +{ + ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin)); +} + +void inline ws2812_setleds_pin(struct cRGB *ledarray, uint16_t leds, uint8_t pinmask) +{ + ws2812_sendarray_mask((uint8_t*)ledarray,leds+leds+leds,pinmask); + _delay_us(ws2812_resettime); +} + +// Setleds for SK6812RGBW +void inline ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t leds) +{ + ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(ws2812_pin)); + _delay_us(ws2812_resettime); +} + +void ws2812_sendarray(uint8_t *data,uint16_t datlen) +{ + ws2812_sendarray_mask(data,datlen,_BV(ws2812_pin)); +} + +/* + This routine writes an array of bytes with RGB values to the Dataout pin + using the fast 800kHz clockless WS2811/2812 protocol. +*/ + +// Timing in ns +#define w_zeropulse 350 +#define w_onepulse 900 +#define w_totalperiod 1250 + +// Fixed cycles used by the inner loop +#define w_fixedlow 2 +#define w_fixedhigh 4 +#define w_fixedtotal 8 + +// Insert NOPs to match the timing, if possible +#define w_zerocycles (((F_CPU/1000)*w_zeropulse )/1000000) +#define w_onecycles (((F_CPU/1000)*w_onepulse +500000)/1000000) +#define w_totalcycles (((F_CPU/1000)*w_totalperiod +500000)/1000000) + +// w1 - nops between rising edge and falling edge - low +#define w1 (w_zerocycles-w_fixedlow) +// w2 nops between fe low and fe high +#define w2 (w_onecycles-w_fixedhigh-w1) +// w3 nops to complete loop +#define w3 (w_totalcycles-w_fixedtotal-w1-w2) + +#if w1>0 + #define w1_nops w1 +#else + #define w1_nops 0 +#endif + +// The only critical timing parameter is the minimum pulse length of the "0" +// Warn or throw error if this timing can not be met with current F_CPU settings. +#define w_lowtime ((w1_nops+w_fixedlow)*1000000)/(F_CPU/1000) +#if w_lowtime>550 + #error "Light_ws2812: Sorry, the clock speed is too low. Did you set F_CPU correctly?" +#elif w_lowtime>450 + #warning "Light_ws2812: The timing is critical and may only work on WS2812B, not on WS2812(S)." + #warning "Please consider a higher clockspeed, if possible" +#endif + +#if w2>0 +#define w2_nops w2 +#else +#define w2_nops 0 +#endif + +#if w3>0 +#define w3_nops w3 +#else +#define w3_nops 0 +#endif + +#define w_nop1 "nop \n\t" +#define w_nop2 "rjmp .+0 \n\t" +#define w_nop4 w_nop2 w_nop2 +#define w_nop8 w_nop4 w_nop4 +#define w_nop16 w_nop8 w_nop8 + +void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi) +{ + uint8_t curbyte,ctr,masklo; + uint8_t sreg_prev; + + ws2812_DDRREG |= maskhi; // Enable output + + masklo =~maskhi&ws2812_PORTREG; + maskhi |= ws2812_PORTREG; + + sreg_prev=SREG; + cli(); + + while (datlen--) { + curbyte=*data++; + + asm volatile( + " ldi %0,8 \n\t" + "loop%=: \n\t" + " out %2,%3 \n\t" // '1' [01] '0' [01] - re +#if (w1_nops&1) +w_nop1 +#endif +#if (w1_nops&2) +w_nop2 +#endif +#if (w1_nops&4) +w_nop4 +#endif +#if (w1_nops&8) +w_nop8 +#endif +#if (w1_nops&16) +w_nop16 +#endif + " sbrs %1,7 \n\t" // '1' [03] '0' [02] + " out %2,%4 \n\t" // '1' [--] '0' [03] - fe-low + " lsl %1 \n\t" // '1' [04] '0' [04] +#if (w2_nops&1) + w_nop1 +#endif +#if (w2_nops&2) + w_nop2 +#endif +#if (w2_nops&4) + w_nop4 +#endif +#if (w2_nops&8) + w_nop8 +#endif +#if (w2_nops&16) + w_nop16 +#endif + " out %2,%4 \n\t" // '1' [+1] '0' [+1] - fe-high +#if (w3_nops&1) +w_nop1 +#endif +#if (w3_nops&2) +w_nop2 +#endif +#if (w3_nops&4) +w_nop4 +#endif +#if (w3_nops&8) +w_nop8 +#endif +#if (w3_nops&16) +w_nop16 +#endif + + " dec %0 \n\t" // '1' [+2] '0' [+2] + " brne loop%=\n\t" // '1' [+3] '0' [+4] + : "=&d" (ctr) + : "r" (curbyte), "I" (_SFR_IO_ADDR(ws2812_PORTREG)), "r" (maskhi), "r" (masklo) + ); + } + + SREG=sreg_prev; +} diff --git a/src/light_ws2812.h b/src/light_ws2812.h new file mode 100644 index 0000000..f94cf97 --- /dev/null +++ b/src/light_ws2812.h @@ -0,0 +1,93 @@ +/* + * light weight WS2812 lib include + * + * Version 2.3 - Nev 29th 2015 + * Author: Tim (cpldcpu@gmail.com) + * + * Please do not change this file! All configuration is handled in "ws2812_config.h" + * + * License: GNU GPL v2+ (see License.txt) + + + */ + +#ifndef LIGHT_WS2812_H_ +#define LIGHT_WS2812_H_ + +#include +#include + +/////////////////////////////////////////////////////////////////////// +// Define Reset time in µs. +// +// This is the time the library spends waiting after writing the data. +// +// WS2813 needs 300 µs reset time +// WS2812 and clones only need 50 µs +// +/////////////////////////////////////////////////////////////////////// +#if !defined(ws2812_resettime) +#define ws2812_resettime 300 +#endif + +/////////////////////////////////////////////////////////////////////// +// Define I/O pin +/////////////////////////////////////////////////////////////////////// +#if !defined(ws2812_port) +#define ws2812_port B // Data port +#endif + +#if !defined(ws2812_pin) +#define ws2812_pin 2 // Data out pin +#endif + +/* + * Structure of the LED array + * + * cRGB: RGB for WS2812S/B/C/D, SK6812, SK6812Mini, SK6812WWA, APA104, APA106 + * cRGBW: RGBW for SK6812RGBW + */ + +struct cRGB { uint8_t g; uint8_t r; uint8_t b; }; +struct cRGBW { uint8_t g; uint8_t r; uint8_t b; uint8_t w;}; + + + +/* User Interface + * + * Input: + * ledarray: An array of GRB data describing the LED colors + * number_of_leds: The number of LEDs to write + * pinmask (optional): Bitmask describing the output bin. e.g. _BV(PB0) + * + * The functions will perform the following actions: + * - Set the data-out pin as output + * - Send out the LED data + * - Wait 50µs to reset the LEDs + */ + +void ws2812_setleds (struct cRGB *ledarray, uint16_t number_of_leds); +void ws2812_setleds_pin (struct cRGB *ledarray, uint16_t number_of_leds,uint8_t pinmask); +void ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t number_of_leds); + +/* + * Old interface / Internal functions + * + * The functions take a byte-array and send to the data output as WS2812 bitstream. + * The length is the number of bytes to send - three per LED. + */ + +void ws2812_sendarray (uint8_t *array,uint16_t length); +void ws2812_sendarray_mask(uint8_t *array,uint16_t length, uint8_t pinmask); + + +/* + * Internal defines + */ + +#define CONCAT(a, b) a ## b +#define CONCAT_EXP(a, b) CONCAT(a, b) + +#define ws2812_PORTREG CONCAT_EXP(PORT,ws2812_port) +#define ws2812_DDRREG CONCAT_EXP(DDR,ws2812_port) + +#endif /* LIGHT_WS2812_H_ */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..91b051d --- /dev/null +++ b/src/main.c @@ -0,0 +1,40 @@ +#define F_CPU 16000000UL // 16Mhz +#define I2C_ADDR 0b1000010 // 0x84h + +#define ws2812_resettime 300 +#define ws2812_port B +#define ws2812_pin 2 + +#define RGBW_COUNT 15 +#define TWI_BUFFER_SIZE RGBW_COUNT * 4 // 15 RGBW leds, 4 byte each + +#include +#include +#include + +#include "TWI_slave.h" +#include "light_ws2812.h" + +int main() +{ + TWI_Slave_Initialise(I2C_ADDR << 1); + TWI_Start_Transceiver(); + DDRB |= _BV(ws2812_pin); + sei(); + + uint8_t rxMessage[TWI_BUFFER_SIZE]; + + while(1) { + if (!TWI_Transceiver_Busy() && TWI_statusReg.RxDataInBuf) + { + TWI_Get_Data_From_Transceiver(rxMessage, TWI_BUFFER_SIZE); + ws2812_sendarray((uint8_t *)rxMessage, TWI_BUFFER_SIZE); + TWI_Start_Transceiver(); + } + + if (TIFR1 & _BV(OCF1A)) + { + TIFR1 = _BV(OCF1A); + } + } +} \ No newline at end of file