Commit 3bc9e0cc authored by Robey Pointer's avatar Robey Pointer
Browse files

split out a few more modules: hid_report, controller, ux

parent 6ca0f3ca
......@@ -32,14 +32,17 @@
this software.
*/
#include "Config/LUFAConfig.h"
#include "Keyboard.h"
#include <stdlib.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include "Config/LUFAConfig.h"
#include "LUFA/Drivers/Peripheral/Serial.h"
#include "ssd1306.h"
#include "scancodes.h"
#include <stdlib.h>
#include <avr/sleep.h>
#include "controller.h"
#include "hid_report.h"
#include "ux.h"
#include "Keyboard.h"
#define KBD_FW_REV "R1 20210815"
//#define KBD_VARIANT_STANDALONE
......@@ -49,6 +52,8 @@
/** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)];
text_display_t display;
/** 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.
......@@ -211,72 +216,6 @@ void insert_bat_icon(char* str, int x, float v) {
str[x+1] = 4*32+icon+1;
}
void remote_get_voltages(void) {
empty_serial();
term_x = 0;
term_y = 0;
float bat_volts = 0;
float bat_amps = 0;
char bat_gauge[5] = {0,0,0,0,0};
Serial_SendByte('c');
Serial_SendByte('\r');
Delay_MS(1);
remote_receive_string(0);
// lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???%
// | | | | | | | | | | | |
// 0 3 6 9 12 15 18 21 24| | |
// 26 33 39
// |
// `- can be a minus
float sum_volts = 0;
for (int i=0; i<8; i++) {
voltages[i] = ((float)((response[i*3]-'0')*10 + (response[i*3+1]-'0')))/10.0;
if (voltages[i]<0) voltages[i]=0;
if (voltages[i]>=10) voltages[i]=9.9;
sum_volts += voltages[i];
}
int amps_offset = 3*8+2;
// cut off string
response[amps_offset+5]=0;
bat_amps = ((float)atoi(&response[amps_offset]))/1000.0;
int volts_offset = amps_offset+5+2;
response[volts_offset+5]=0;
bat_volts = ((float)atoi(&response[volts_offset]))/1000.0;
int gauge_offset = volts_offset+5+1;
strncpy(bat_gauge, &response[gauge_offset], 4);
// plot
gfx_clear();
char str[32];
sprintf(str,"[] %.1f [] %.1f %s",voltages[0],voltages[4],bat_gauge);
insert_bat_icon(str,0,voltages[0]);
insert_bat_icon(str,8,voltages[4]);
gfx_poke_str(0,0,str);
sprintf(str,"[] %.1f [] %.1f ",voltages[1],voltages[5]);
insert_bat_icon(str,0,voltages[1]);
insert_bat_icon(str,8,voltages[5]);
gfx_poke_str(0,1,str);
sprintf(str,"[] %.1f [] %.1f %2.2fA",voltages[2],voltages[6],bat_amps);
insert_bat_icon(str,0,voltages[2]);
insert_bat_icon(str,8,voltages[6]);
gfx_poke_str(0,2,str);
sprintf(str,"[] %.1f [] %.1f %2.2fV",voltages[3],voltages[7],bat_volts);
insert_bat_icon(str,0,voltages[3]);
insert_bat_icon(str,8,voltages[7]);
gfx_poke_str(0,3,str);
gfx_flush();
}
int low_battery_alert = 0;
void remote_check_for_low_battery(void) {
......@@ -456,16 +395,6 @@ void remote_turn_on_aux(void) {
empty_serial();
}
void remote_report_voltages(void) {
empty_serial();
Serial_SendByte('0');
Serial_SendByte('c');
Serial_SendByte('\r');
Delay_MS(1);
empty_serial();
}
void remote_enable_som_uart(void) {
empty_serial();
......@@ -563,7 +492,7 @@ int execute_meta_function(int keycode) {
remote_turn_off_aux();
}*/
else if (keycode == KEY_B) {
remote_get_voltages();
ux_show_battery(&display);
return 0;
}
else if (keycode == KEY_S) {
......@@ -800,12 +729,14 @@ void SetupHardware()
kbd_brightness_init();
oled_init();
text_display_init(&display, &font_oranj);
gfx_clear();
gfx_flush();
hid_report_init();
anim_hello();
Serial_Init(57600, false);
controller_init();
USB_Init();
}
......@@ -821,7 +752,7 @@ void SetupHardware()
void EnterPowerOff(void)
{
USB_Disable(); // Stop USB stack so it doesn't wake us up
kbd_brightness_set(0);
// Turn off OLED to save power
gfx_clear_screen();
......@@ -829,11 +760,11 @@ void EnterPowerOff(void)
// Disable ADC to save even more power
ADCSRA=0;
cli(); // No interrupts
cli(); // No interrupts
// Set all ports not floating if possible, leaving pullups alone
PORTB=0x3F; // Leave pull-up on all the columns on PB0-3, drive rows 2-3 high, 1-low
PORTC=0xC0;
PORTC=0xC0;
PORTD=0xF0; // Keep pullup on PD5 like setup did, drive rows 4,5,6 high
PORTE=0x40; // Pullup on PE6
PORTF=0xFF; // Pullups on PF (columns)
......@@ -857,11 +788,11 @@ void EnterPowerOff(void)
sleep_cpu(); // Actually go to sleep
// Zzzzzz
sleep_disable(); // We've woken up
sei();
sei();
// Check if circle key has been pressed (active-low)
// If not reset the watchdog and try again
} while(PINC&(1<<6));
// Resume and reinitialize hardware
SetupHardware();
}
......@@ -930,6 +861,9 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn
return false;
}
#if 0
#include "text_display.h"
/** 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
......@@ -947,6 +881,27 @@ void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDI
uint8_t* data = (uint8_t*)ReportData;
if (ReportSize<4) return;
if (memcmp(data, "SIZE", 4) == 0) {
gfx_on();
gfx_poke(0, 0, '0' + (ReportSize / 1000));
gfx_poke(1, 0, '0' + ((ReportSize / 100) % 10));
gfx_poke(2, 0, '0' + ((ReportSize / 10) % 10));
gfx_poke(3, 0, '0' + ((ReportSize) % 10));
gfx_flush();
}
if (memcmp(data, "test", 4) == 0) {
text_display_t display;
extern monospace_font_t font_oranj;
text_display_init(&display, &font_oranj);
text_display_write(&display, "This is a test of the\noranj font on a tiny\nOLED.\n");
text_display_putc(&display, 0);
text_display_putc(&display, 6);
text_display_putc(&display, ' ');
text_display_putc(&display, 5);
text_display_putc(&display, 7);
text_display_flush(&display);
}
if (data[0]=='O' && data[1]=='L' && data[2]=='E' && data[3]=='D') {
// OLED: write characters on display
gfx_on();
......@@ -993,3 +948,5 @@ void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDI
remote_report_voltages();
}
}
#endif
......@@ -18,7 +18,7 @@ F_CPU = 16000000
F_USB = $(F_CPU)
OPTIMIZATION = s
TARGET = Keyboard
SRC = $(TARGET).c Descriptors.c i2c.c ssd1306.c text_display.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
SRC = $(TARGET).c controller.c Descriptors.c hid_report.c i2c.c ssd1306.c text_display.c ux.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
LUFA_PATH = ./lufa-master/LUFA
CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER $(REFORM_KBD_OPTIONS) -IConfig/
LD_FLAGS = -Wl,-u,vfprintf -lprintf_flt
......
/*
MNT Reform 2.0 Keyboard Firmware
Copyright 2019-2021 Lukas F. Hartmann / MNT Research GmbH, Berlin
lukas@mntre.com
Additional work: Robey Pointer robey@lag.net
*/
// This file contains all the code for communicating with the system
// controller, which has things like battery status.
#include <stdbool.h>
#include "LUFA/Drivers/Peripheral/Serial.h"
#include "controller.h"
// how long do we wait (busy loops) before giving up on a response
#define TIMEOUT (500000)
// anything received from the last command
static uint8_t recv_buffer[64];
void controller_init(void) {
Serial_Init(57600, false);
recv_buffer[0] = 0;
}
// wait for the serial port to be quiet.
static void flush(void) {
for (int i = 0; i < 100 && Serial_ReceiveByte() >= 0; i++);
}
// returns true on success, false on timeout.
static bool receive_response(void) {
int n = 0;
while (true) {
int16_t c = -1;
for (int i = 0; i < TIMEOUT && c <= 0; i++) c = Serial_ReceiveByte();
if (c <= 0) {
flush();
return false;
}
if (c == '\r') {
recv_buffer[n] = 0;
flush();
return true;
}
recv_buffer[n++] = c;
if (n >= 64) n = 0;
}
}
const char *controller_request(const char *command) {
flush();
while (*command) Serial_SendByte(*command++);
Serial_SendByte('\r');
Delay_MS(1);
if (receive_response()) {
return (const char *)recv_buffer;
} else {
return NULL;
}
}
/*
MNT Reform 2.0 Keyboard Firmware
Copyright 2019-2021 Lukas F. Hartmann / MNT Research GmbH, Berlin
lukas@mntre.com
Additional work: Robey Pointer robey@lag.net
*/
#pragma once
void controller_init(void);
const char *controller_request(const char *command);
/*
MNT Reform 2.0 Keyboard Firmware
Copyright 2019-2021 Lukas F. Hartmann / MNT Research GmbH, Berlin
lukas@mntre.com
Additional work: Robey Pointer robey@lag.net
*/
#include "Config/LUFAConfig.h"
#include <LUFA/Drivers/USB/USB.h>
#include <LUFA/Platform/Platform.h>
#include "controller.h"
#include "text_display.h"
#include "ux.h"
extern text_display_t display;
// hid commands are all 4-letter
#define cmd(_s) (*(uint32_t *)(_s))
#define CMD_TEXT_FRAME cmd("OLED") // fill the screen with a single wall of text
#define CMD_REPORT_POWER cmd("RPRT") // ask for power stats
#define CMD_UX_POWER cmd("UXPW") // display detailed power screen as if user hit circle-B
void hid_report_init(void) {
// pass
}
/** 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* report_data,
const uint16_t report_size
) {
const uint8_t *data = (uint8_t *)report_data;
if (report_size < 4) return;
const uint32_t command = *(uint32_t *)data;
if (command == CMD_TEXT_FRAME) {
text_display_clear(&display);
for (int i = 4; i < report_size; i++) text_display_putc(&display, data[i]);
text_display_flush(&display);
} else if (command == CMD_REPORT_POWER) {
const char *report = controller_request("0c");
if (report) text_display_write(&display, report);
text_display_putc(&display, '@');
text_display_flush(&display);
} else if (command == CMD_UX_POWER) {
ux_show_battery(&display);
}
#if 0
if (memcmp(data, "SIZE", 4) == 0) {
gfx_on();
gfx_poke(0, 0, '0' + (ReportSize / 1000));
gfx_poke(1, 0, '0' + ((ReportSize / 100) % 10));
gfx_poke(2, 0, '0' + ((ReportSize / 10) % 10));
gfx_poke(3, 0, '0' + ((ReportSize) % 10));
gfx_flush();
}
if (memcmp(data, "test", 4) == 0) {
text_display_write(&display, "This is a test of\nthe oranj font on a\ntiny OLED.\n");
text_display_putc(&display, 0);
text_display_putc(&display, 6);
text_display_putc(&display, ' ');
text_display_putc(&display, 5);
text_display_putc(&display, 7);
text_display_flush(&display);
}
if (data[0]=='O' && data[1]=='I' && data[2]=='N' && data[3]=='V') {
gfx_clear_invert();
gfx_invert_row(data[4]-'0');
}
else if (data[0]=='L' && data[1]=='I' && data[2]=='T' && data[3]=='E') {
char brite = data[4]-'0';
brite++;
if (brite<=1) brite=0;
if (brite>9) brite=9;
kbd_brightness_set(brite);
}
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='0') {
// PWR0: shutdown (turn off power rails)
remote_turn_off_som();
}
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='3') {
// PWR3: aux power off
remote_turn_off_aux();
}
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='4') {
// PWR4: aux power on
remote_turn_on_aux();
}
else if (data[0]=='U' && data[1]=='A' && data[2]=='R' && data[3]=='1') {
// UAR1: UART reporting on
remote_enable_som_uart();
}
else if (data[0]=='U' && data[1]=='A' && data[2]=='R' && data[3]=='0') {
// UAR0: UART reporting off
remote_disable_som_uart();
}
#endif
}
/*
MNT Reform 2.0 Keyboard Firmware
Copyright 2019-2021 Lukas F. Hartmann / MNT Research GmbH, Berlin
lukas@mntre.com
Additional work: Robey Pointer robey@lag.net
*/
#pragma once
void hid_report_init(void);
......@@ -106,6 +106,8 @@ void oled_finish_render(void) {
struct CharacterMatrix display;
bool gfx_off(void) {
bool success = false;
......
#ifndef _sdd1306_h
#define _sdd1306_h
/*
MNT Reform 2.0 Keyboard Firmware
Copyright 2019-2021 Lukas F. Hartmann / MNT Research GmbH, Berlin
lukas@mntre.com
Additional work: Robey Pointer robey@lag.net
*/
#pragma once
#include <stdbool.h>
#include <stdio.h>
......@@ -67,6 +73,7 @@ struct CharacterMatrix {
bool dirty;
};
void oled_init(void);
void oled_clear(void);
void oled_off(void);
void oled_on(void);
......@@ -78,7 +85,6 @@ void oled_finish_render(void);
struct CharacterMatrix display;
void gfx_poke(uint8_t x, uint8_t y, uint8_t c);
void gfx_poke_str(uint8_t x, uint8_t y, char* str);
......@@ -103,7 +109,3 @@ void matrix_write(struct CharacterMatrix *matrix, const char *data);
void matrix_write_ln(struct CharacterMatrix *matrix, const char *data);
void matrix_write_P(struct CharacterMatrix *matrix, const char *data);
void matrix_render(struct CharacterMatrix *matrix);
//bool process_record_gfx(uint16_t keycode, keyrecord_t *record);
#endif // _sdd1306_h
/*
MNT Reform 2.0 Keyboard Firmware
Copyright 2019-2021 Lukas F. Hartmann / MNT Research GmbH, Berlin
lukas@mntre.com
Additional work: Robey Pointer robey@lag.net
*/
#include <stdio.h>
#include <stdlib.h>
#include "controller.h"
#include "text_display.h"
#include "ux.h"
// battery fullness icons: 0-5 are left, 6-11 are right
// these macros assume 0 - 100:
#define BATTERY_LEFT(_n) ((_n) >= 45 ? 5 : (_n) < 5 ? 0 : ((_n) - 5) / 10 + 1)
#define BATTERY_RIGHT(_n) ((_n) < 55 ? 6 : (_n) >= 95 ? 11 : ((_n) - 55) / 10 + 7)
static uint8_t decivolts_to_percent(uint8_t decivolts) {
if (decivolts >= 33) return 100;
if (decivolts >= 31) return 75;
if (decivolts >= 30) return 50;
if (decivolts >= 29) return 25;
return 0;
}
static const char *show_one_battery(uint8_t decivolts) {
static char buffer[7];
uint8_t percent = decivolts_to_percent(decivolts);
buffer[0] = BATTERY_LEFT(percent);
buffer[1] = BATTERY_RIGHT(percent);
buffer[2] = ' ';
buffer[3] = '0' + (decivolts / 10);
buffer[4] = '.';
buffer[5] = '0' + (decivolts % 10);
buffer[6] = 0;
return buffer;
}
void ux_show_battery(text_display_t *display) {
const char *response = controller_request("c");
// lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???%
// | | | | | | | | | | | |
// 0 3 6 9 12 15 18 21 24| | |
// 26 33 39
// |
// `- can be a minus
#define amps_offset (3 * 8 + 2)
#define volts_offset (amps_offset + 5 + 2)
#define battery_offset (volts_offset + 6)
uint8_t decivolts[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < 8; i++) {
decivolts[i] = (response[i * 3] - '0') * 10 + response[i * 3 + 1] - '0';
if (decivolts[i] > 99) decivolts[i] = 0; // garbage data.
}
const char *bat_gauge = &response[battery_offset];
float bat_amps = ((float)atoi(&response[amps_offset])) / 1000.0;
float bat_volts = ((float)atoi(&response[volts_offset])) / 1000.0;
// plot, using oranj (6x8) so all the info will fit
text_display_init(display, &font_oranj);
char str[32];
sprintf(str, "%s %s %.4s\n", show_one_battery(decivolts[0]), show_one_battery(decivolts[4]), bat_gauge);
text_display_write(display, str);
sprintf(str, "%s %s \n", show_one_battery(decivolts[1]), show_one_battery(decivolts[5]));
text_display_write(display, str);
sprintf(str, "%s %s %2.2fA\n", show_one_battery(decivolts[2]), show_one_battery(decivolts[6]), bat_amps);
text_display_write(display, str);
sprintf(str, "%s %s %2.2fV\n", show_one_battery(decivolts[3]), show_one_battery(decivolts[7]), bat_volts);
text_display_write(display, str);
text_display_flush(display);
}
/*
MNT Reform 2.0 Keyboard Firmware
Copyright 2019-2021 Lukas F. Hartmann / MNT Research GmbH, Berlin
lukas@mntre.com
Additional work: Robey Pointer robey@lag.net
*/
#pragma once
#include "text_display.h"
void ux_show_battery(text_display_t *display);
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment