mirror of
https://github.com/mon/PocketVoltex.git
synced 2026-09-22 22:57:58 +03:00
Forgot to add new LED driver files
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user