212 lines
5.4 KiB
C
212 lines
5.4 KiB
C
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "ch.h"
|
|
#include "hal.h"
|
|
|
|
#include "usbcfg.h"
|
|
#include "led.h"
|
|
#include "log.h"
|
|
#include "cmd.h"
|
|
|
|
/*
|
|
* HID report sizes (data only, excluding the report ID byte prepended by the
|
|
* USB host). These match the LampArray HID descriptor in usbcfg.c.
|
|
*/
|
|
#define RPT1_LEN 22 /* LampArrayAttributesReport */
|
|
#define RPT3_LEN 28 /* LampAttributesResponseReport */
|
|
#define RPT6_LEN 1 /* LampArrayControlReport */
|
|
|
|
/*
|
|
* Called from ISR context — fill buf with the response for report `id`.
|
|
* buf[0] must be set to the report ID; *len must be set to the total length.
|
|
* Return false to stall (unknown ID).
|
|
*/
|
|
bool isr_hid_get_report(uint8_t id, uint8_t *buf, size_t *len) {
|
|
switch (id) {
|
|
case 1: /* LampArrayAttributesReport */
|
|
*len = 1 + RPT1_LEN;
|
|
memset(buf, 0, *len);
|
|
buf[0] = 1;
|
|
/* LampCount, BoundingBox*, LampArrayKind, MinUpdateInterval all zero */
|
|
return true;
|
|
|
|
case 3: /* LampAttributesResponseReport */
|
|
*len = 1 + RPT3_LEN;
|
|
memset(buf, 0, *len);
|
|
buf[0] = 3;
|
|
/* All lamp attribute fields zero until real lamp state is added */
|
|
return true;
|
|
|
|
case 6: /* LampArrayControlReport */
|
|
*len = 1 + RPT6_LEN;
|
|
buf[0] = 6;
|
|
buf[1] = 0; /* AutonomousMode = 0 (host-controlled) */
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Called from ISR context — process an incoming SET_REPORT payload.
|
|
* buf[0] is the report ID; len includes that byte.
|
|
*/
|
|
void isr_hid_set_report(uint8_t id, const uint8_t *buf, size_t len) {
|
|
(void)buf;
|
|
(void)len;
|
|
switch (id) {
|
|
case 2: /* LampAttributesRequestReport — which lamp to query next */
|
|
break;
|
|
case 4: /* LampMultiUpdateReport — set up to 8 lamps */
|
|
break;
|
|
case 5: /* LampRangeUpdateReport — set a range of lamps */
|
|
break;
|
|
case 6: /* LampArrayControlReport — autonomous mode flag */
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void serial_write(const uint8_t *data, size_t len) {
|
|
chnWrite((BaseChannel *)&SDU1, data, len);
|
|
}
|
|
|
|
static void serial_writes(const char *data) {
|
|
chnWrite((BaseChannel *)&SDU1, (uint8_t *)data, strlen(data));
|
|
}
|
|
|
|
static void cmd_resize(int argc, char **argv) {
|
|
if (argc < 3) {
|
|
serial_writes("usage: resize <driver> <count>\r\n");
|
|
return;
|
|
}
|
|
uint8_t driver = (uint8_t)atoi(argv[1]);
|
|
uint8_t count = (uint8_t)atoi(argv[2]);
|
|
|
|
if(driver >= NUM_DRIVERS) {
|
|
serial_writes("invalid driver: ");
|
|
serial_writes(argv[1]);
|
|
serial_writes("\r\n");
|
|
return;
|
|
}
|
|
|
|
led_resize(driver, count);
|
|
serial_writes("ok\r\n");
|
|
}
|
|
|
|
static void cmd_set(int argc, char **argv) {
|
|
if (argc < 3) {
|
|
serial_writes("usage: set <driver> <count>\r\n");
|
|
return;
|
|
}
|
|
|
|
uint8_t driver = (uint8_t)atoi(argv[1]);
|
|
uint8_t color = (uint8_t)atoi(argv[2]);
|
|
|
|
if(driver >= NUM_DRIVERS) {
|
|
serial_writes("invalid driver: ");
|
|
serial_writes(argv[1]);
|
|
serial_writes("\r\n");
|
|
return;
|
|
}
|
|
|
|
if(color >= C_PALETTE_SIZE) {
|
|
serial_writes("invalid color: ");
|
|
serial_writes(argv[2]);
|
|
serial_writes("\r\n");
|
|
return;
|
|
}
|
|
memset(led_drivers[driver].framebuffer, color, led_drivers[driver].num_leds);
|
|
}
|
|
|
|
#define SERIAL_LINE_BUF 128
|
|
|
|
static THD_WORKING_AREA(waSerial, 512);
|
|
static THD_FUNCTION(SerialThread, arg) {
|
|
(void)arg;
|
|
chRegSetThreadName("serial");
|
|
|
|
char buf[SERIAL_LINE_BUF];
|
|
int len;
|
|
|
|
while (true) {
|
|
while (SDU1.config->usbp->state != USB_ACTIVE || !cdc_dtr)
|
|
chThdSleepMilliseconds(100);
|
|
|
|
len = 0;
|
|
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"> ", 2);
|
|
|
|
while (SDU1.config->usbp->state == USB_ACTIVE && cdc_dtr) {
|
|
msg_t c = chnGetTimeout((BaseChannel *)&SDU1, TIME_MS2I(100));
|
|
if (c < MSG_OK)
|
|
continue;
|
|
|
|
uint8_t ch = (uint8_t)c;
|
|
|
|
if (ch == '\r' || ch == '\n') {
|
|
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"\r\n", 2);
|
|
if (len > 0) {
|
|
buf[len] = '\0';
|
|
cmd_dispatch(buf);
|
|
len = 0;
|
|
}
|
|
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"> ", 2);
|
|
} else if (ch == 0x08 || ch == 0x7F) { /* backspace / DEL */
|
|
if (len > 0) {
|
|
len--;
|
|
chnWrite((BaseChannel *)&SDU1, (uint8_t *)"\b \b", 3);
|
|
}
|
|
} else if (ch >= 0x20 && ch < 0x7F) { /* printable ASCII */
|
|
if (len < SERIAL_LINE_BUF - 1) {
|
|
buf[len++] = (char)ch;
|
|
chnWrite((BaseChannel *)&SDU1, &ch, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int main(void) {
|
|
halInit();
|
|
chSysInit();
|
|
|
|
sduObjectInit(&SDU1);
|
|
sduStart(&SDU1, &serusbcfg);
|
|
|
|
usbDisconnectBus(&USBD1);
|
|
chThdSleepMilliseconds(1500);
|
|
usbStart(&USBD1, &usbcfg);
|
|
usbConnectBus(&USBD1);
|
|
|
|
chThdCreateStatic(waSerial, sizeof(waSerial), NORMALPRIO-1, SerialThread, NULL);
|
|
|
|
led_init(chThdGetSelfX());
|
|
|
|
for (uint32_t i = 0; i < NUM_DRIVERS; i++) {
|
|
chEvtRegisterMask(&led_drivers[i].event_source, &event_listeners[i], EVENT_MASK(i));
|
|
}
|
|
|
|
log_init(serial_write);
|
|
cmd_init(serial_write);
|
|
cmd_register("resize", cmd_resize);
|
|
cmd_register("set", cmd_set);
|
|
chThdSleepMilliseconds(3000);
|
|
|
|
#define ANIM_FRAMERATE 60
|
|
systime_t prev_frame = chVTGetSystemTimeX();
|
|
systime_t next_frame = chVTGetSystemTimeX();
|
|
while (true) {
|
|
prev_frame = next_frame;
|
|
next_frame = chTimeAddX(prev_frame, TIME_US2I(1000000/ANIM_FRAMERATE));
|
|
//update animations
|
|
led_draw(event_listeners);
|
|
chThdSleepUntilWindowed(prev_frame, next_frame);
|
|
}
|
|
chThdSleepMilliseconds(TIME_INFINITE);
|
|
}
|