Forgot to add new LED driver files

This commit is contained in:
William Toohey
2017-10-06 15:46:00 +10:00
parent e4386bdd4e
commit 1a2947488a
3 changed files with 463 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
#define R 0
#define G 1
#define B 2
#define SK9822_BRIGHTNESS 1
static const uint8_t SK9822_map[] = {0, 2, 4, 6, 7, 5, 3, 1};
void led_init(void) {
// SCLK/MOSI PB1/2
DDRB |= _BV(1)|_BV(2);
// SPI enabled, master mode, CLK/4 speed
SPCR = _BV(SPE)|_BV(MSTR);
// normal speed mode
SPSR = 0;
}
// busy wait
void SPI_write(uint8_t val) {
cli();
SPDR = val;
while(!(SPSR & _BV(SPIF)))
;
SPSR = _BV(SPIF); // clear flag
sei();
}
void led_commit(void) {
SPI_write(0x00); // Start Frame
SPI_write(0x00);
SPI_write(0x00);
SPI_write(0x00);
for (uint8_t i = 0; i < LED_COUNT; i++)
{
SPI_write(0xe0|SK9822_BRIGHTNESS); // Maximum global brightness
uint8_t offset = SK9822_map[i] * 3;
SPI_write(leds[offset+B]);
SPI_write(leds[offset+G]);
SPI_write(leds[offset+R]);
}
// Reset frame - Only needed for SK9822, has no effect on APA102
SPI_write(0x00);
SPI_write(0x00);
SPI_write(0x00);
SPI_write(0x00);
// End frame - 1 for every 16 LEDs
SPI_write(0x00);
}
+128
View File
@@ -0,0 +1,128 @@
#define GND_COUNT 4
// RGB * 2
#define LED_PINS 6
// LED gnd 0-3 are on PC7-4
#define GND_PORT PORTC
#define GND_DDR DDRC
#define GND_MASK 0xF0
#define GND_OFFSET 4 // in bits
// LED power BGR BGR PB2-7
#define LED_PORT PORTB
#define LED_DDR DDRB
#define LED_MASK (0b111111 << 2)
// How many are actually PWM'd, because the chip isn't that quick
// MUST be a multiple of BRIGHTNESS_LEVELS
#define BRIGHTNESS_DOWNSCALE 128
#define BRIGHTNESS_INCREMENT (BRIGHTNESS_LEVELS / BRIGHTNESS_DOWNSCALE)
#define UPDATE_HZ 100
// prescaler is the div8
#define TIMER_COMPARE ((F_CPU / 8 / UPDATE_HZ / GND_COUNT / BRIGHTNESS_DOWNSCALE)-1)
#if TIMER_COMPARE > 255
#error timer compare too large for timer register
#endif
#define R 2
#define G 1
#define B 0
static volatile uint8_t leds_frontbuffer[LED_PHYSICAL_COUNT];
void led_init(void) {
// all GNDs low level for high impedence or gnd
GND_PORT &= ~GND_MASK;
// all GNDs input
GND_DDR &= ~GND_MASK;
// all LEDs off
LED_PORT &= ~LED_MASK;
// all LEDs output
LED_DDR |= LED_MASK;
memset(leds, 0, LED_PHYSICAL_COUNT);
memset((uint8_t*)leds_frontbuffer, 0, LED_PHYSICAL_COUNT);
// 64 light levels * 60Hz update * 4 different GND pins = 15360Hz
// 520 clock cycles for our interrupt handler
// CTC mode
TCCR0A = _BV(WGM01);
// clk/8 prescaler
TCCR0B = _BV(CS01);
OCR0A = TIMER_COMPARE;
// Enable interrupt on OCR0A
TIMSK0 = _BV(OCIE0A);
// Clear interrupt
TIFR0 = _BV(OCF0A);
}
void led_commit(void) {
memcpy((uint8_t*)leds_frontbuffer, leds, LED_PHYSICAL_COUNT);
}
/* Straight voodoo magic, consult the Inline Assembler Cookbook
Equivalent to:
if(*led++ > brightness)
out |= _BV(outPin)
*/
#define LED_PIN_SET(led, outPin) \
__asm__ volatile( \
"ld __tmp_reg__, %a["#led"]+ \n\t\
cp %[bright], __tmp_reg__ \n\t\
brcc skip%= \n\t\
ori %[out], (1 << "#outPin") \n\t\
skip%=:" \
: [out] "+a" (out), [led] "+z" (led) /* outputs */ \
: [bright] "r" (brightness) /* inputs */ )
// This function once took about 279 clock cycles.
// Optimised GND accesses got it to 157
// Optimised variables to static, got it to 100
// Made LED setter assembly, got it to 90
ISR(TIMER0_COMPA_vect) {
/* Why are these static here instead of at the top of file?
The compiler won't optimise 2 consecutive operations to use a register,
and instead will perform a costly lds-sts every time. Making them
static here will cache them in a local register.
*/
// Because we roll over on each loop and want to start at 0 this starts at max
static uint8_t currentGnd = GND_COUNT - 1;
// This saves us doing a costly dynamic _BV()
static uint8_t currentGndMask = 0;
static uint8_t brightness = BRIGHTNESS_LEVELS - BRIGHTNESS_INCREMENT;
static volatile uint8_t* offset = &leds_frontbuffer[0];
uint8_t out = 0;
currentGnd++;
currentGndMask >>= 1;
if(currentGnd >= GND_COUNT) {
currentGnd = 0;
// Because we work backwards start at the high end and shift down
currentGndMask = _BV(7);
offset = &leds_frontbuffer[0];
brightness += BRIGHTNESS_INCREMENT;
// brightness rolls over cleanly due to being a multiple
#if BRIGHTNESS_LEVELS != 256
if(brightness > BRIGHTNESS_MAX)
brightness = 0;
#endif
}
// Faster than loops
// NOTE: ASM MACRO INCREMENTS OFFSET
LED_PIN_SET(offset, 2);
LED_PIN_SET(offset, 3);
LED_PIN_SET(offset, 4);
LED_PIN_SET(offset, 5);
LED_PIN_SET(offset, 6);
LED_PIN_SET(offset, 7);
// Turn off before switch
LED_PORT &= ~LED_MASK;
// Enable new ground
GND_DDR = (GND_DDR & ~GND_MASK) | currentGndMask;
LED_PORT |= out;
}
+283
View File
@@ -0,0 +1,283 @@
#include "PocketVoltex.h"
#include "Config.h"
#include "Encoder.h"
#include "LED.h"
#include "LEDPatterns.h"
#include "Macro.h"
#include <util/delay.h>
#undef SK9822_BRIGHTNESS
#define SK9822_BRIGHTNESS 1
#define LOAD_SWITCH(source, sourceBit, result, resultBit) result |= !((source) & _BV(sourceBit)) << resultBit
// B 0,4,5
#define SWITCH_MASKB 0b00110001
// C 2
#define SWITCH_MASKC 0b00000100
// D 4,5,6,7
#define SWITCH_MASKD 0b11110000
// How long to wait before moving to internal lighting
#define HID_LED_TIMEOUT 2000
// If I add more buttons with the macro key I don't need to care
#if SWITCH_COUNT <= 8
#define SWITCH_BITMASK_UINT uint8_t
#elif SWITCH_COUNT <= 16
#define SWITCH_BITMASK_UINT uint16_t
#else
#error TOO MANY SWITCHES
#endif
typedef struct
{
int8_t X; // VOL-L
int8_t Y; // VOL-R
SWITCH_BITMASK_UINT Buttons; // bitmask
} Joystick_Report_t;
typedef struct
{
uint8_t Modifier; // Keyboard modifier byte indicating pressed modifier keys (\c HID_KEYBOARD_MODIFER_* masks)
uint8_t Reserved; // Reserved for OEM use, always set to 0.
uint8_t KeyCode[SWITCH_COUNT]; // Length determined by the number of keys that can be reported
} Keyboard_Report_t;
typedef struct
{
uint8_t mainLights[LED_PHYSICAL_COUNT];
uint8_t btFx[6];
} LED_Report_t;
static uint8_t updateLEDs = 1;
/** LUFA HID Class driver interface configuration and state information. This structure is
* passed to all HID Class driver functions, so that multiple instances of the same class
* within a device can be differentiated from one another.
*/
USB_ClassInfo_HID_Device_t Inputs_HID_Interface =
{
.Config =
{
.InterfaceNumber = INTERFACE_ID_Inputs,
.ReportINEndpoint =
{
.Address = INPUTS_EPADDR,
.Size = INPUTS_EPSIZE,
.Banks = 1,
},
.PrevReportINBuffer = NULL,
.PrevReportINBufferSize = MAX(MAX(sizeof(Keyboard_Report_t), sizeof(Joystick_Report_t)), sizeof(USB_MouseReport_Data_t)),
},
};
USB_ClassInfo_HID_Device_t LED_HID_Interface =
{
.Config =
{
.InterfaceNumber = INTERFACE_ID_LED,
.ReportINEndpoint =
{
.Address = LED_EPADDR,
.Size = LED_EPSIZE,
.Banks = 1,
},
.PrevReportINBuffer = NULL,
.PrevReportINBufferSize = sizeof(LED_Report_t),
},
};
// Set to max already so we have our init flash
void RebootToBootloader(void) {
/* Disconnect from the host - USB interface will be reset later along with the AVR */
USB_Detach();
// Back to the bootloader
wdt_enable(WDTO_250MS);
while(1);
}
int main(void)
{
GlobalInterruptDisable();
InitConfig();
SetupHardware();
GlobalInterruptEnable();
while(1) {
int8_t x = encoder_get(0);
int8_t y = encoder_get(1);
led_knobs_update(x, y);
encoder_set(0, 0);
encoder_set(1, 0);
if(led_on_frame()) {
updateLEDs = 1;
}
if(updateLEDs) {
updateLEDs = 0;
led_set_all(32,32,32);
//led_pattern_animate();
// knob lights go above all
led_overlay_knobs();
led_commit();
}
_delay_ms(1);
}
for (;;)
{
HID_Device_USBTask(&Inputs_HID_Interface);
HID_Device_USBTask(&LED_HID_Interface);
USB_USBTask();
Endpoint_SelectEndpoint(CONFIG_OUT_EPADDR);
if (Endpoint_IsOUTReceived()) {
uint8_t ReceivedData[COMMAND_BYTES];
Endpoint_Read_Stream_LE(ReceivedData, COMMAND_BYTES, NULL);
Endpoint_ClearOUT();
command_response_t respond = HandleConfig(ReceivedData);
switch(respond) {
// we are returning the requested data
case RESPOND:
Endpoint_SelectEndpoint(CONFIG_IN_EPADDR);
Endpoint_Write_Stream_LE(ReceivedData, COMMAND_BYTES, NULL);
Endpoint_ClearIN();
break;
case REBOOT:
RebootToBootloader();
break;
// no data to return
default:
break;
}
}
if(updateLEDs) {
updateLEDs = 0;
led_set_all(32,32,32);
//led_pattern_animate();
// knob lights go above all
led_overlay_knobs();
led_commit();
}
}
}
/** Configures the board hardware and chip peripherals */
void SetupHardware()
{
/* Disable watchdog if enabled by bootloader/fuses */
MCUSR &= ~_BV(WDRF);
wdt_disable();
// Inputs
DDRB &= ~SWITCH_MASKB;
DDRC &= ~SWITCH_MASKC;
DDRD &= ~SWITCH_MASKD;
// Pullups
PORTB |= SWITCH_MASKB;
PORTC |= SWITCH_MASKC;
PORTD |= SWITCH_MASKD;
/* Hardware Initialization */
encoder_init();
led_init();
led_pattern_init();
//USB_Init();
}
/** HID class driver callback function for the creation of HID reports to the host.
*
* \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced
* \param[in,out] ReportID Report ID requested by the host if non-zero, otherwise callback should set to the generated report ID
* \param[in] ReportType Type of the report to create, either HID_REPORT_ITEM_In or HID_REPORT_ITEM_Feature
* \param[out] ReportData Pointer to a buffer where the created report should be stored
* \param[out] ReportSize Number of bytes written in the report (or zero if no report is to be sent)
*
* \return Boolean \c true to force the sending of the report, \c false to let the library determine if it needs to be sent
*/
bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
uint8_t* const ReportID,
const uint8_t ReportType,
void* ReportData,
uint16_t* const ReportSize)
{
*ReportSize = 0;
return false;
}
/** HID class driver callback function for the processing of HID reports from the host.
*
* \param[in] HIDInterfaceInfo Pointer to the HID class interface configuration structure being referenced
* \param[in] ReportID Report ID of the received report from the host
* \param[in] ReportType The type of report that the host has sent, either HID_REPORT_ITEM_Out or HID_REPORT_ITEM_Feature
* \param[in] ReportData Pointer to a buffer where the received report has been stored
* \param[in] ReportSize Size in bytes of the received HID report
*/
void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
const uint8_t ReportID,
const uint8_t ReportType,
const void* ReportData,
const uint16_t ReportSize)
{
}
/** Event handler for the library USB Connection event. */
void EVENT_USB_Device_Connect(void)
{
}
/** Event handler for the library USB Disconnection event. */
void EVENT_USB_Device_Disconnect(void)
{
led_set_all(0,0,0);
led_commit();
}
/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
Endpoint_ConfigureEndpoint(CONFIG_OUT_EPADDR, EP_TYPE_BULK, CONFIG_EPSIZE, 1);
Endpoint_ConfigureEndpoint(CONFIG_IN_EPADDR, EP_TYPE_BULK, CONFIG_EPSIZE, 1);
HID_Device_ConfigureEndpoints(&Inputs_HID_Interface);
HID_Device_ConfigureEndpoints(&LED_HID_Interface);
USB_Device_EnableSOFEvents();
}
/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
USB_Process_BOS();
HID_Device_ProcessControlRequest(&Inputs_HID_Interface);
HID_Device_ProcessControlRequest(&LED_HID_Interface);
}
/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
HID_Device_MillisecondElapsed(&Inputs_HID_Interface);
HID_Device_MillisecondElapsed(&LED_HID_Interface);
int8_t x = encoder_get(0);
int8_t y = encoder_get(1);
led_knobs_update(x, y);
encoder_set(0, 0);
encoder_set(1, 0);
// we use a sentinel since this is actually inside an interrupt!
// less LED flicker if ran outside
if(led_on_frame()) {
updateLEDs = 1;
}
}