diff --git a/light_ws2812.c b/light_ws2812.c index 0542ec9..de96eda 100644 --- a/light_ws2812.c +++ b/light_ws2812.c @@ -92,19 +92,9 @@ void ws2812_sendarray(uint8_t *data,uint16_t datlen) #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; - - masklo =~maskhi&ws2812_PORTREG; - maskhi |= ws2812_PORTREG; - sreg_prev=SREG; - cli(); +inline void send_byte(uint8_t curbyte, uint8_t maskhi, uint8_t masklo){ + uint8_t ctr; - while (datlen--) { - curbyte=*data++; - asm volatile( " ldi %0,8 \n\t" "loop%=: \n\t" @@ -164,6 +154,57 @@ w_nop16 : "=&d" (ctr) : "r" (curbyte), "I" (_SFR_IO_ADDR(ws2812_PORTREG)), "r" (maskhi), "r" (masklo) ); +} + +void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi) +{ + uint8_t curbyte, masklo; + uint8_t sreg_prev; + + masklo =~maskhi&ws2812_PORTREG; + maskhi |= ws2812_PORTREG; + sreg_prev=SREG; + cli(); + + while (datlen--) { + curbyte=*data++; + send_byte(curbyte, maskhi, masklo); + } + + SREG=sreg_prev; +} + +void inline ws2812_sendarraylow_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi) +{ + uint8_t curbyte, masklo; + uint8_t sreg_prev; + + masklo =~maskhi&ws2812_PORTREG; + maskhi |= ws2812_PORTREG; + sreg_prev=SREG; + cli(); + + while (datlen--) { + curbyte=*data++; + send_byte(curbyte<<4, maskhi, masklo); + } + + SREG=sreg_prev; +} + +void inline ws2812_sendarrayhigh_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi) +{ + uint8_t curbyte, masklo; + uint8_t sreg_prev; + + masklo =~maskhi&ws2812_PORTREG; + maskhi |= ws2812_PORTREG; + sreg_prev=SREG; + cli(); + + while (datlen--) { + curbyte=*data++; + send_byte(curbyte&240, maskhi, masklo); } SREG=sreg_prev; diff --git a/light_ws2812.h b/light_ws2812.h index bc2c4e8..4910da3 100644 --- a/light_ws2812.h +++ b/light_ws2812.h @@ -48,6 +48,8 @@ void ws2812_setleds_pin(struct cRGB *ledarray, uint16_t number_of_leds,uint8_t p void ws2812_sendarray (uint8_t *array,uint16_t length); void ws2812_sendarray_mask(uint8_t *array,uint16_t length, uint8_t pinmask); +void ws2812_sendarraylow_mask(uint8_t *array,uint16_t length, uint8_t pinmask); +void ws2812_sendarrayhigh_mask(uint8_t *array,uint16_t length, uint8_t pinmask); /* diff --git a/main.cpp b/main.cpp index 07455be..053fe78 100644 --- a/main.cpp +++ b/main.cpp @@ -20,6 +20,7 @@ #define MODE_RGB 0 #define MODE_RGB_INVERSE 1 #define MODE_WS2812 2 +#define MODE_WS2812_12BIT 3 #define TASK_NONE 0 #define TASK_SEND_DATA 1 @@ -42,13 +43,13 @@ extern "C" /* ----------------------------- LED interface ----------------------------- */ /* ------------------------------------------------------------------------- */ -#define MAX_LEDS 64 +#define MAX_LEDS 128 #define MIN_LED_FRAME 8 * 3 #define DELAY_CYCLES 64 static uint8_t led[MAX_LEDS * 3]; -const PROGMEM uint16_t ledDataCount[] = {MIN_LED_FRAME, MIN_LED_FRAME * 2, MIN_LED_FRAME * 4, MIN_LED_FRAME * 8 }; +const PROGMEM uint16_t ledDataCount[] = {MIN_LED_FRAME, MIN_LED_FRAME * 2, MIN_LED_FRAME * 4, MIN_LED_FRAME * 8, MAX_LEDS * 3}; /* Reports: @@ -61,6 +62,8 @@ const PROGMEM uint16_t ledDataCount[] = {MIN_LED_FRAME, MIN_LED_FRAME * 2, MIN_L 7: LED Frame [Channel, [G, R, B][0..15]] 8: LED Frame [Channel, [G, R, B][0..31]] 9: LED Frame [Channel, [G, R, B][0..63]] + 10: LED Frame [Channel, [G, R, B][0..127]] + 20: Frame repeat [repetitions] Memory Map: 00 : Oscillator calibration value @@ -80,62 +83,69 @@ const PROGMEM uint16_t ledDataCount[] = {MIN_LED_FRAME, MIN_LED_FRAME * 2, MIN_L const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */ - 0x06, 0x00, 0xff, // USAGE_PAGE (Generic Desktop) - 0x09, 0x01, // USAGE (Vendor Usage 1) - 0xa1, 0x01, // COLLECTION (Application) - 0x15, 0x00, // LOGICAL_MINIMUM (0) - 0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255) - 0x75, 0x08, // REPORT_SIZE (8) - 0x85, 0x01, // REPORT_ID (1) - 0x95, 0x03, // REPORT_COUNT (3) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x02, // REPORT_ID (2) - 0x95, 0x20, // REPORT_COUNT (32) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x03, // REPORT_ID (3) - 0x95, 0x20, // REPORT_COUNT (32) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x04, // REPORT_ID (4) - 0x95, 0x01, // REPORT_COUNT (1) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x05, // REPORT_ID (5) - 0x95, 0x05, // REPORT_COUNT (5) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x06, // REPORT_ID (6) - 0x95, MIN_LED_FRAME + 1, // REPORT_COUNT (25) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x07, // REPORT_ID (7) - 0x95, MIN_LED_FRAME * 2 + 1, // REPORT_COUNT (49) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x08, // REPORT_ID (8) - 0x95, MIN_LED_FRAME * 4 + 1, // REPORT_COUNT (97) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0x85, 0x09, // REPORT_ID (9) - 0x95, MIN_LED_FRAME * 8 + 1, // REPORT_COUNT (193) - 0x09, 0x00, // USAGE (Undefined) - 0xb2, 0x02, 0x01, // FEATURE (Data,Var,Abs,Buf) - 0xc0 // END_COLLECTION + '\x06', '\x00', '\xff', // USAGE_PAGE (Generic Desktop) + '\x09', '\x01', // USAGE (Vendor Usage 1) + '\xa1', '\x01', // COLLECTION (Application) + '\x15', '\x00', // LOGICAL_MINIMUM (0) + '\x26', '\xff', '\x00', // LOGICAL_MAXIMUM (255) + '\x75', '\x08', // REPORT_SIZE (8) + '\x85', '\x01', // REPORT_ID (1) + '\x95', '\x03', // REPORT_COUNT (3) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x02', // REPORT_ID (2) + '\x95', '\x20', // REPORT_COUNT (32) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x03', // REPORT_ID (3) + '\x95', '\x20', // REPORT_COUNT (32) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x04', // REPORT_ID (4) + '\x95', '\x01', // REPORT_COUNT (1) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x05', // REPORT_ID (5) + '\x95', '\x05', // REPORT_COUNT (5) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x06', // REPORT_ID (6) + '\x95', '\x19', // REPORT_COUNT (25) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x07', // REPORT_ID (7) + '\x95', '\x31', // REPORT_COUNT (49) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x08', // REPORT_ID (8) + '\x95', '\x61', // REPORT_COUNT (97) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x09', // REPORT_ID (9) + '\x95', '\xc1', // REPORT_COUNT (193) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x0a', // REPORT_ID (10) + '\x96', '\x81', '\x01', // REPORT_COUNT (385) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\x85', '\x14', // REPORT_ID (20) + '\x95', '\x01', // REPORT_COUNT (1) + '\x09', '\x00', // USAGE (Undefined) + '\xb2', '\x02', '\x01', // FEATURE (Data,Var,Abs,Buf) + '\xc0' // END_COLLECTION }; -static uchar currentAddress; -static uchar addressOffset; -static uchar bytesRemaining; +static uint16_t currentAddress; +static uint16_t bytesRemaining; static uchar reportId = 0; static uchar channel; static uint8_t mode; +static uint8_t repeat = 1; static uint8_t task = 0; static uint16_t ledCount = 0; -static uint16_t ledIndex = 0; -static uint16_t delayCycles = 0; +static uint8_t delayCycles = 0; /* ------------------------------------------------------------------------- */ /* ----------------------------- Helper Functions--------------------------- */ @@ -185,11 +195,12 @@ uchar usbFunctionRead(uchar *data, uchar len) if(len > bytesRemaining) len = bytesRemaining; + uint8_t addressOffset = reportId==2 ? 32 : 64; //Ignore the first byte of data as it's report id if (currentAddress == 0) { data[0] = reportId; - eeprom_read_block(&data[1], (uchar *)0 + currentAddress + addressOffset, len - 1); + eeprom_read_block(&data[1], (uchar *)0 + addressOffset, len - 1); currentAddress += len - 1; bytesRemaining -= (len - 1); } @@ -219,7 +230,7 @@ uchar usbFunctionRead(uchar *data, uchar len) return 6; } - else if (reportId >= 6 && reportId <= 9) // Serial data for LEDs + else if (reportId >= 6 && reportId <= 10) // Serial data for LEDs { if (bytesRemaining == 0) { @@ -237,7 +248,7 @@ uchar usbFunctionRead(uchar *data, uchar len) for (int i = 2; i < len; i++) { - data[i] = led[addressOffset + currentAddress + i - 2]; + data[i] = led[currentAddress + i - 2]; } currentAddress += len - 2; @@ -247,7 +258,7 @@ uchar usbFunctionRead(uchar *data, uchar len) { for (int i = 0; i < len; i++) { - data[i] = led[addressOffset + currentAddress + i]; + data[i] = led[currentAddress + i]; } currentAddress += len; @@ -256,7 +267,12 @@ uchar usbFunctionRead(uchar *data, uchar len) return len; } - + else if (reportId == 20) + { + data[0] = 20; + data[1] = repeat; + return 2; + } else { return 0; @@ -289,7 +305,7 @@ uchar usbFunctionWrite(uchar *data, uchar len) //Set PWM values setRGBPWM(led[1], led[0], led[2]); } - else if (mode == MODE_WS2812) + else if (mode == MODE_WS2812 || mode == MODE_WS2812_12BIT) { led[0] = data[2]; led[1] = data[1]; @@ -297,7 +313,14 @@ uchar usbFunctionWrite(uchar *data, uchar len) //Set only the first LED on the first channel cli(); //Disable interrupts - ws2812_sendarray_mask(&led[0], 3, channelToPin(0)); + if(mode == MODE_WS2812_12BIT) + { + ws2812_sendarraylow_mask(&led[0], 3, channelToPin(0)); + } + else + { + ws2812_sendarray_mask(&led[0], 3, channelToPin(0)); + } sei(); //Enable interrupts } @@ -311,10 +334,11 @@ uchar usbFunctionWrite(uchar *data, uchar len) if(len > bytesRemaining) len = bytesRemaining; + uint8_t addressOffset = reportId==2 ? 32 : 64; //Ignore the first byte of data as it's report id if (currentAddress == 0) { - eeprom_write_block(&data[1], (uchar *)0 + currentAddress + addressOffset, len); + eeprom_write_block(&data[1], (uchar *)0 + addressOffset, len); currentAddress += len - 1; bytesRemaining -= (len - 1); } @@ -341,7 +365,7 @@ uchar usbFunctionWrite(uchar *data, uchar len) } else if (reportId == 5) { - if (mode != MODE_WS2812) + if (mode != MODE_WS2812 && mode != MODE_WS2812_12BIT) { return 1; } @@ -356,7 +380,6 @@ uchar usbFunctionWrite(uchar *data, uchar len) //Prepare to send the data simultaneously together with USB polling task = TASK_SEND_DATA; ledCount = (index + 1) * 3; - ledIndex = 0; delayCycles = 0; //Disable any USB requests while sending data to LED Strip @@ -364,26 +387,22 @@ uchar usbFunctionWrite(uchar *data, uchar len) return 1; } - else if (reportId >= 6 && reportId <= 9) // Serial data for LEDs + else if (reportId >= 6 && reportId <= 10) // Serial data for LEDs { - if (mode != MODE_WS2812) + if (mode != MODE_WS2812 && mode != MODE_WS2812_12BIT) { return 1; } if (bytesRemaining == 0) { - if (reportId != 10) - { - //Prepare to send the data simultaneously together with USB polling - task = TASK_SEND_DATA; - ledCount = pgm_read_word_near(&ledDataCount[reportId - 6]); - ledIndex = 0; - delayCycles = 0; - - //Disable any USB requests while sending data to LED Strip - usbDisableAllRequests(); - } + //Prepare to send the data simultaneously together with USB polling + task = TASK_SEND_DATA; + ledCount = pgm_read_word_near(&ledDataCount[reportId - 6]); + delayCycles = 0; + + //Disable any USB requests while sending data to LED Strip + usbDisableAllRequests(); return 1; // end of transfer } @@ -398,7 +417,7 @@ uchar usbFunctionWrite(uchar *data, uchar len) for (int i = 2; i < len; i++) { - led[addressOffset + currentAddress + i - 2] = data[i]; + led[currentAddress + i - 2] = data[i]; } currentAddress += len - 2; @@ -408,19 +427,18 @@ uchar usbFunctionWrite(uchar *data, uchar len) { for (int i = 0; i < len; i++) { - led[addressOffset + currentAddress + i] = data[i]; + led[currentAddress + i] = data[i]; } currentAddress += len; bytesRemaining -= len; } - if (bytesRemaining <= 0 && reportId != 10) + if (bytesRemaining <= 0) { //Prepare to send the data simultaneously together with USB polling task = TASK_SEND_DATA; ledCount = pgm_read_word_near(&ledDataCount[reportId - 6]); - ledIndex = 0; delayCycles = 0; //Disable any USB requests while sending data to LED Strip @@ -429,6 +447,11 @@ uchar usbFunctionWrite(uchar *data, uchar len) return bytesRemaining == 0; // return 1 if this was the last chunk } + else if (reportId == 20) + { + repeat = data[1]; + return 1; + } else { return 1; @@ -471,7 +494,7 @@ static void SetMode(void) { mode = eeprom_read_byte((uchar *)0 + 1 + 12); - if (mode > 2) + if (mode > 3) mode = 0; } @@ -489,17 +512,11 @@ extern "C" usbMsgLen_t usbFunctionSetup(uchar data[8]) else if(reportId == 2){ // Name of the device bytesRemaining = 33; currentAddress = 0; - - addressOffset = 32; - return USB_NO_MSG; } else if(reportId == 3){ // Data of the device bytesRemaining = 33; currentAddress = 0; - - addressOffset = 64; - return USB_NO_MSG; } else if(reportId == 4){ // Report device mode @@ -508,13 +525,10 @@ extern "C" usbMsgLen_t usbFunctionSetup(uchar data[8]) else if(reportId == 5){ // Indexed LED data^M currentAddress = 0; bytesRemaining = 5; - - addressOffset = 0; return USB_NO_MSG; } - else if (reportId >= 6 && reportId <= 9) { // Serial data for LEDs + else if (reportId >= 6 && reportId <= 10) { // Serial data for LEDs currentAddress = 0; - addressOffset = 0; switch (reportId) { case 6: @@ -529,11 +543,17 @@ extern "C" usbMsgLen_t usbFunctionSetup(uchar data[8]) case 9: bytesRemaining = MIN_LED_FRAME * 8 + 1; break; + case 10: + bytesRemaining = MAX_LEDS*3 + 1; + break; } return USB_NO_MSG; /* use usbFunctionWrite() to receive data from host */ } + else if(reportId == 20){ // Set repeat + return USB_NO_MSG; + } return 0; @@ -547,34 +567,25 @@ extern "C" usbMsgLen_t usbFunctionSetup(uchar data[8]) else if(reportId == 2){ // Name of the device currentAddress = 0; bytesRemaining = 32; - - addressOffset = 32; return USB_NO_MSG; } else if(reportId == 3){ // Name of the device currentAddress = 0; bytesRemaining = 32; - - addressOffset = 64; return USB_NO_MSG; } else if(reportId == 4){ // Mode currentAddress = 0; bytesRemaining = 1; - - addressOffset = 0; return USB_NO_MSG; } else if(reportId == 5){ // Indexed LED data currentAddress = 0; bytesRemaining = 5; - - addressOffset = 0; return USB_NO_MSG; } - else if (reportId >= 6 && reportId <= 9) { // Serial data for LEDs + else if (reportId >= 6 && reportId <= 10) { // Serial data for LEDs currentAddress = 0; - addressOffset = 0; switch (reportId) { case 6: @@ -589,11 +600,19 @@ extern "C" usbMsgLen_t usbFunctionSetup(uchar data[8]) case 9: bytesRemaining = MIN_LED_FRAME * 8 + 1; break; + case 10: + bytesRemaining = MAX_LEDS*3 + 1; + break; } return USB_NO_MSG; /* use usbFunctionWrite() to receive data from host */ } + else if(reportId == 20){ + currentAddress = 0; + bytesRemaining = 1; + return USB_NO_MSG; + } return 0; } @@ -667,7 +686,7 @@ void ApplyMode(void) setRGBPWM(0, 0, 0); } } - else if (mode == MODE_WS2812) + else if (mode == MODE_WS2812 || mode == MODE_WS2812_12BIT) { //Turn off PWM setRGBPWM(0, 0, 0); @@ -708,24 +727,31 @@ void ledTransfer() { if (task == TASK_SEND_DATA) { //send all data at the same time, asume there is going to be no communication over USB during this time - uint16_t len = 3 * 64; + uint16_t len = 3 * MAX_LEDS; - if (ledIndex + len >= ledCount) + if (len > ledCount) { - len = ledCount - ledIndex; + len = ledCount; } cli(); //Disable interrupts - ws2812_sendarray_mask(&led[ledIndex], len, channelToPin(channel)); + if(mode == MODE_WS2812_12BIT) + { + for(uint8_t i=0; i= ledCount - 1) - { - task = TASK_NONE; - usbEnableAllRequests(); - } + task = TASK_NONE; + usbEnableAllRequests(); } else if (task == TASK_SET_MODE) { @@ -743,8 +769,6 @@ void ledTransfer() { int main(void) { - uchar i; - wdt_enable(WDTO_1S); SetSerial(); @@ -760,9 +784,8 @@ int main(void) usbInit(); usbDeviceDisconnect(); /* enforce re-enumeration, do this while interrupts are disabled! */ - i = 0; - while(--i){ /* fake USB disconnect for > 250 ms */ - wdt_reset(); + for(uint8_t i=255; i!=0; i--){ /* fake USB disconnect for > 250 ms */ + wdt_reset(); _delay_ms(1); } usbDeviceConnect(); diff --git a/usbconfig.h b/usbconfig.h index 7e00c31..aca1719 100644 --- a/usbconfig.h +++ b/usbconfig.h @@ -256,7 +256,7 @@ section at the end of this file). * HID class is 3, no subclass and protocol required (but may be useful!) * CDC class is 2, use subclass 2 and protocol 1 for ACM */ -#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 96 +#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 115 /* Define this to the length of the HID report descriptor, if you implement * an HID device. Otherwise don't define it or define it to 0. * If you use this define, you must add a PROGMEM character array named