From ae746b27b216466e353e94ff436ed68d7f36332e Mon Sep 17 00:00:00 2001 From: Raphael Randschau Date: Thu, 11 Oct 2018 21:54:23 -0700 Subject: [PATCH] add atmega168 i2c source the atmega168 acts as the FPGA used in the original ErgoDox EZ shine. It reads the RGBW data via I2C and sets it to the LEDs once all data is received. --- src/TWI_slave.c | 264 +++++++++++++++++++++++++++++++++++++++++++++ src/TWI_slave.h | 124 +++++++++++++++++++++ src/light_ws2812.c | 184 +++++++++++++++++++++++++++++++ src/light_ws2812.h | 93 ++++++++++++++++ src/main.c | 40 +++++++ 5 files changed, 705 insertions(+) create mode 100755 src/TWI_slave.c create mode 100755 src/TWI_slave.h create mode 100644 src/light_ws2812.c create mode 100644 src/light_ws2812.h create mode 100644 src/main.c 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