commit bd6d5d880305d1db9a985a5a188fa1be8a1fc80f
parent abd26239c5bd4a61c5c87e661b60b8ddb6262340
Author: minute <lukas@mntre.com>
Date: Tue, 24 Oct 2023 11:34:29 +0000
Merge branch 'trackpad-improvements' into 'master'
trackpad-fw: Make trackpad more responsive
See merge request reform/reform!49
Diffstat:
6 files changed, 241 insertions(+), 162 deletions(-)
diff --git a/reform2-trackpad-fw/Makefile b/reform2-trackpad-fw/Makefile
@@ -17,7 +17,7 @@ F_CPU = 16000000
F_USB = $(F_CPU)
OPTIMIZATION = s
TARGET = Mouse
-SRC = $(TARGET).c i2cmaster/i2cmaster.S Descriptors.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
+SRC = $(TARGET).c i2cmaster/i2cmaster.S Descriptors.c azoteq.c $(LUFA_SRC_USB) $(LUFA_SRC_USBCLASS)
LUFA_PATH = ./lufa-master/LUFA
CC_FLAGS = -DUSE_LUFA_CONFIG_HEADER -IConfig/
LD_FLAGS =
diff --git a/reform2-trackpad-fw/Mouse.c b/reform2-trackpad-fw/Mouse.c
@@ -33,62 +33,7 @@
#include <stdlib.h>
#include "i2cmaster/i2cmaster.h"
-
-#define output_low(port,pin) port &= ~(1<<pin)
-#define output_high(port,pin) port |= (1<<pin)
-#define set_input(portdir,pin) portdir &= ~(1<<pin)
-#define set_output(portdir,pin) portdir |= (1<<pin)
-
-// Registers
-#define Product_ID 0x00
-#define Revision_ID 0x01
-#define Motion 0x02
-#define Delta_X_L 0x03
-#define Delta_X_H 0x04
-#define Delta_Y_L 0x05
-#define Delta_Y_H 0x06
-#define SQUAL 0x07
-#define Raw_Data_Sum 0x08
-#define Maximum_Raw_data 0x09
-#define Minimum_Raw_data 0x0A
-#define Shutter_Lower 0x0B
-#define Shutter_Upper 0x0C
-#define Control 0x0D
-#define Config1 0x0F
-#define Config2 0x10
-#define Angle_Tune 0x11
-#define Frame_Capture 0x12
-#define SROM_Enable 0x13
-#define Run_Downshift 0x14
-#define Rest1_Rate_Lower 0x15
-#define Rest1_Rate_Upper 0x16
-#define Rest1_Downshift 0x17
-#define Rest2_Rate_Lower 0x18
-#define Rest2_Rate_Upper 0x19
-#define Rest2_Downshift 0x1A
-#define Rest3_Rate_Lower 0x1B
-#define Rest3_Rate_Upper 0x1C
-#define Observation 0x24
-#define Data_Out_Lower 0x25
-#define Data_Out_Upper 0x26
-#define Raw_Data_Dump 0x29
-#define SROM_ID 0x2A
-#define Min_SQ_Run 0x2B
-#define Raw_Data_Threshold 0x2C
-#define Config5 0x2F
-#define Power_Up_Reset 0x3A
-#define Shutdown 0x3B
-#define Inverse_Product_ID 0x3F
-#define LiftCutoff_Tune3 0x41
-#define Angle_Snap 0x42
-#define LiftCutoff_Tune1 0x4A
-#define Motion_Burst 0x50
-#define LiftCutoff_Tune_Timeout 0x58
-#define LiftCutoff_Tune_Min_Length 0x5A
-#define SROM_Load_Burst 0x62
-#define Lift_Config 0x63
-#define Raw_Data_Burst 0x64
-#define LiftCutoff_Tune2 0x65
+#include "azoteq.h"
#include "Mouse.h"
@@ -115,32 +60,24 @@ USB_ClassInfo_HID_Device_t Mouse_HID_Interface =
},
};
-#define ADDR_SENSOR (0x74<<1)
-
uint8_t twi_write_reg[1];
uint8_t twi_write_buf[10];
uint8_t twi_read_buf[10];
void SetupHardware(void)
{
- /* Disable watchdog if enabled by bootloader/fuses */
- MCUSR &= ~(1 << WDRF);
- wdt_disable();
+ /* Disable watchdog if enabled by bootloader/fuses */
+ MCUSR &= ~(1 << WDRF);
+ wdt_disable();
- // Disable clock division
+ // Disable clock division
// this should yield 8Mhz with internal osc
- clock_prescale_set(clock_div_1);
+ clock_prescale_set(clock_div_1);
DDRD = 0b00000000;
DDRB = 0b00000100;
DDRC = 0b00000000;
- //output_high(PORTC, 5);
-
- // no jtag plox
- //MCUCR |=(1<<JTD);
- //MCUCR |=(1<<JTD);
-
i2c_init();
USB_Init();
@@ -158,6 +95,13 @@ void SetupHardware(void)
i2c_write(0); // reset
i2c_stop();
+ // Disable idle mode timeout, so we never enter LP1
+ i2c_start_wait(ADDR_SENSOR|I2C_WRITE);
+ i2c_write(0x05);
+ i2c_write(0x86);
+ i2c_write(0xff); // Idle mode, 255s = never timeout
+ i2c_stop();
+
Delay_MS(100);
}
@@ -174,49 +118,39 @@ void EVENT_USB_Device_Disconnect(void)
/** Event handler for the library USB Configuration Changed event. */
void EVENT_USB_Device_ConfigurationChanged(void)
{
- bool ConfigSuccess = true;
+ bool ConfigSuccess = true;
- ConfigSuccess &= HID_Device_ConfigureEndpoints(&Mouse_HID_Interface);
+ ConfigSuccess &= HID_Device_ConfigureEndpoints(&Mouse_HID_Interface);
- USB_Device_EnableSOFEvents();
+ USB_Device_EnableSOFEvents();
}
/** Event handler for the library USB Control Request reception event. */
void EVENT_USB_Device_ControlRequest(void)
{
- HID_Device_ProcessControlRequest(&Mouse_HID_Interface);
+ HID_Device_ProcessControlRequest(&Mouse_HID_Interface);
}
/** Event handler for the USB device Start Of Frame event. */
void EVENT_USB_Device_StartOfFrame(void)
{
- HID_Device_MillisecondElapsed(&Mouse_HID_Interface);
+ HID_Device_MillisecondElapsed(&Mouse_HID_Interface);
}
-uint8_t addr1 = 0x00;
-uint8_t addr2 = 0x11;
-uint8_t buf[80];
-float dx2 = 0, dy2 = 0;
-float dx3 = 0, dy3 = 0;
-float dx4 = 0, dy4 = 0;
-float dxx = 0, dyy = 0;
float dx = 0, dy = 0;
int16_t lastx = 0, lasty = 0;
int16_t lastx2 = 0, lasty2 = 0;
unsigned int cycle = 0;
int wheeling = 0;
int touched_time = 0;
-int touched_time2 = 0;
int last_num_fingers = 0;
int start_num_fingers = 0;
int report_lift = 0;
+bool g_want_bootloader = false;
#define PRESS_TIME 6
#define MOTION_CLIP 127
-//#define READ_LEN 8
-#define READ_LEN 20
-
/** 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
@@ -278,31 +212,23 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn
return false;
}
- i2c_start_wait(ADDR_SENSOR|I2C_WRITE);
- i2c_write(addr1);
- i2c_write(addr2);
- i2c_rep_start(ADDR_SENSOR|I2C_READ);
-
- for (int i=0; i<READ_LEN; i++) {
- buf[i] = i2c_readAck();
- }
- buf[READ_LEN] = i2c_readNak();
- i2c_stop();
-
- int num_fingers = buf[0];
+ struct azoteq_data sensor = { 0 };
+ read_azoteq_data(&sensor);
// touched?
- if (num_fingers) {
+ if (sensor.num_fingers) {
touched_time++;
- touched_time2++;
wheeling = 0;
- if (num_fingers != last_num_fingers) {
- touched_time2 = 0;
+ if (sensor.num_fingers != last_num_fingers) {
+ lastx2 = 0;
+ lasty2 = 0;
+ lastx = 0;
+ lasty = 0;
}
- if (start_num_fingers < num_fingers) {
- start_num_fingers = num_fingers;
+ if (start_num_fingers < sensor.num_fingers) {
+ start_num_fingers = sensor.num_fingers;
}
if (start_num_fingers == 2) {
@@ -315,45 +241,26 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn
//MouseReport->Button |= 2;
}
- // fingers are 7 bytes apart, up to 5 fingers
- // absolute x and y coordinates are signed 16-bit integers
- int16_t xpos = ((uint16_t)buf[5]<<8)|((uint16_t)buf[6]);
- int16_t ypos = ((uint16_t)buf[7]<<8)|((uint16_t)buf[8]);
- int16_t xpos2 = ((uint16_t)buf[12]<<8)|((uint16_t)buf[13]);
- int16_t ypos2 = ((uint16_t)buf[14]<<8)|((uint16_t)buf[15]);
-
- // skip the first motion reading(s) immediately after
- // touchdown because they often have skips
- if (touched_time2 > 4) {
- if (num_fingers>=2) {
- dx = ((float)xpos2+(float)xpos)/2-((float)lastx2+(float)lastx)/2;
- dy = ((float)ypos2+(float)ypos)/2-((float)lasty2+(float)lasty)/2;
- } else {
- dx = (float)(xpos-lastx);
- dy = (float)(ypos-lasty);
- }
-
- dxx = (dx+dx2+dx3+dx4)/4.0;
- dyy = (dy+dy2+dy3+dy4)/4.0;
-
- //if (dxx>MOTION_CLIP) dxx=MOTION_CLIP;
- //if (dxx<=-MOTION_CLIP) dxx=-MOTION_CLIP;
- //if (dyy>MOTION_CLIP) dyy=MOTION_CLIP;
- //if (dyy<=-MOTION_CLIP) dyy=-MOTION_CLIP;
-
- if (wheeling) {
- // horizontal and vertical scrolling
- MouseReport->Pan = dxx;
- MouseReport->Wheel = -dyy;
- } else {
- // normal movement
- MouseReport->X = dxx;
- MouseReport->Y = dyy;
- }
+ if (sensor.num_fingers >= 2) {
+ dx = lastx && lastx2 ? ((float)sensor.fingers[1].abs_x + (float)sensor.fingers[0].abs_x) / 2.0f - ((float)lastx2 + (float)lastx) / 2.0f : 0.0f;
+ dy = lasty && lasty2 ? ((float)sensor.fingers[1].abs_y + (float)sensor.fingers[0].abs_y) / 2.0f - ((float)lasty2 + (float)lasty) / 2.0f : 0.0f;
+ } else {
+ dx = (float)sensor.relative_x;
+ dy = (float)sensor.relative_y;
}
- lastx = xpos; lasty = ypos;
- lastx2 = xpos2; lasty2 = ypos2;
+ if (wheeling) {
+ // horizontal and vertical scrolling
+ MouseReport->Pan = dx / 4.0f;
+ MouseReport->Wheel = -dy / 4.0f;
+ } else {
+ // normal movement
+ MouseReport->X = dx;
+ MouseReport->Y = dy;
+ }
+
+ lastx = sensor.fingers[0].abs_x; lasty = sensor.fingers[0].abs_y;
+ if (sensor.num_fingers > 1) lastx2 = sensor.fingers[1].abs_x; lasty2 = sensor.fingers[1].abs_y;
} else {
// no (more) touches
@@ -372,26 +279,26 @@ bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDIn
report_lift = 1;
dx = 0;
dy = 0;
+ lastx = 0;
+ lasty = 0;
+ lastx2 = 0;
+ lasty2 = 0;
}
- dx4 = dx3; dy4 = dy3;
- dx3 = dx2; dy3 = dy2;
- dx2 = dx; dy2 = dy;
-
- last_num_fingers = num_fingers;
-
- // end cycle
- i2c_start_wait(ADDR_SENSOR|I2C_WRITE);
- i2c_write(0xee);
- i2c_write(0xee);
- i2c_write(0xff);
- i2c_stop();
+ last_num_fingers = sensor.num_fingers;
*ReportSize = sizeof(USB_WheelMouseReport_Data_t);
return true;
}
+#define BOOTLOADER_START_ADDRESS ((0x8000 - 0x1000) >> 1)
+void jump_to_bootloader(void) {
+ ((void (*)(void))BOOTLOADER_START_ADDRESS)();
+}
+
+#define cmd(_s) (*(uint32_t *)(_s))
+#define CMD_JUMP_TO_BOOTLOADER cmd("JTBL")
/** 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
@@ -406,18 +313,24 @@ void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDI
const void* ReportData,
const uint16_t ReportSize)
{
- // Unused (but mandatory for the HID class driver) in this demo, since there are no Host->Device reports
+ // Unused (but mandatory for the HID class driver) in this demo, since there are no Host->Device reports
+ // if (ReportSize < 4) return;
+ const uint32_t command = *(uint32_t *)ReportData;
+ if (command == CMD_JUMP_TO_BOOTLOADER) {
+ g_want_bootloader = true;
+ }
}
int main(void)
{
- SetupHardware();
- GlobalInterruptEnable();
-
- for (;;)
- {
- HID_Device_USBTask(&Mouse_HID_Interface);
- USB_USBTask();
- }
+ SetupHardware();
+ GlobalInterruptEnable();
+
+ for (;;)
+ {
+ HID_Device_USBTask(&Mouse_HID_Interface);
+ USB_USBTask();
+ if (g_want_bootloader) jump_to_bootloader();
+ }
}
diff --git a/reform2-trackpad-fw/README.md b/reform2-trackpad-fw/README.md
@@ -0,0 +1,37 @@
+# MNT Reform 2.0 Touchpad Firmware
+
+## Code Structure
+
+- `Mouse.c`: Sensor gesture processing + USB HID report generation
+- `azoteq.{c,h}`: Azoteq sensor registers in structs for easy access
+- `Descriptors.c`: USB HID descriptors
+
+### Debian/Ubuntu
+
+`apt install gcc-avr avr-libc dfu-programmer`
+
+### Mac
+
+*TODO: is this correct?*
+
+```
+brew tap osx-cross/avr
+brew install avr-gcc
+brew install dfu-programmer
+```
+
+## Building
+
+Build the firmware by running `make`. The firmware can then be found in
+Mouse.hex.
+
+# Analysis
+
+After building, you can load `Mouse.elf` in `gdb`, and inspect the generated code.
+Try `disas main`, for example.
+
+To flash, run:
+`sudo ./flash.sh`
+
+The script sends the 'xJTBL' command to the trackpad to send it into bootloader mode, and then flashes the new firmware.
+The trackpad should reset briefly after flashing, and start running the new firmware.
diff --git a/reform2-trackpad-fw/azoteq.c b/reform2-trackpad-fw/azoteq.c
@@ -0,0 +1,49 @@
+/*
+ azoteq.c -- Azoteq IQS550 mappings
+ Copyright 2023 Valtteri Koskivuori <vkoskiv@gmail.com>
+ License: GPLv3
+*/
+
+#include "i2cmaster/i2cmaster.h"
+#include "azoteq.h"
+
+static void swap_u16(uint16_t *val) {
+ uint8_t hi = (*val & 0xff00) >> 8;
+ *val <<= 8;
+ *val |= hi;
+}
+
+static void swap_16(int16_t *val) {
+ uint8_t hi = (*val & 0xff00) >> 8;
+ *val <<= 8;
+ *val |= hi;
+}
+
+void read_azoteq_data(struct azoteq_data *data) {
+ i2c_start_wait(ADDR_SENSOR|I2C_WRITE);
+ i2c_write(0x00);
+ i2c_write(0x00);
+ i2c_rep_start(ADDR_SENSOR|I2C_READ);
+
+ for (int i = 0; i < sizeof(*data); ++i) {
+ ((unsigned char *)data)[i] = i2c_readAck();
+ }
+ i2c_readNak();
+ i2c_stop();
+
+ // end cycle
+ i2c_start_wait(ADDR_SENSOR|I2C_WRITE);
+ i2c_write(0xee);
+ i2c_write(0xee);
+ i2c_write(0xff);
+ i2c_stop();
+
+ // Sad, we have to swap byte order for 16-bit quantities
+ for (int i = 0; i < 5; ++i) {
+ swap_u16(&data->fingers[i].abs_x);
+ swap_u16(&data->fingers[i].abs_y);
+ }
+ swap_16(&data->relative_x);
+ swap_16(&data->relative_y);
+}
+
diff --git a/reform2-trackpad-fw/azoteq.h b/reform2-trackpad-fw/azoteq.h
@@ -0,0 +1,76 @@
+/*
+ azoteq.h -- Azoteq IQS550 mappings
+ Copyright 2023 Valtteri Koskivuori <vkoskiv@gmail.com>
+ License: GPLv3
+*/
+
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+struct azoteq_gesture_events {
+ uint8_t ges_unused_0 : 2;
+ bool ges_swipe_y_minus : 1;
+ bool ges_swipe_y_plus : 1;
+ bool ges_swipe_x_plus : 1;
+ bool ges_swipe_x_minus : 1;
+ bool ges_press_and_hold : 1;
+ bool ges_single_tap : 1;
+
+ uint8_t ges_unused_1 : 5;
+ bool ges_zoom: 1;
+ bool ges_scroll: 1;
+ bool ges_2_finger_tap: 1;
+};
+
+struct azoteq_sysinfo {
+ bool sys_show_reset : 1;
+ bool sys_alp_reati_occurred : 1;
+ bool sys_alp_ati_error : 1;
+ bool sys_reati_occurred : 1;
+ bool sys_ati_error : 1;
+ uint8_t sys_charging_mode : 3;
+
+ uint8_t sys_unused: 2;
+ bool sys_switch_state : 1;
+ bool sys_snap_toggle : 1;
+ bool sys_rr_missed : 1;
+ bool sys_too_many_fingers : 1;
+ bool sys_palm_detect : 1;
+ bool sys_tp_movement : 1;
+};
+
+struct azoteq_header {
+ uint16_t product_number;
+ uint16_t project_number;
+ uint8_t major_version;
+ uint8_t minor_version;
+ uint8_t bootloader_status;
+ uint8_t unused_0[4];
+ uint8_t max_touch_column : 4;
+ uint8_t max_touch_row : 4;
+ uint8_t prev_cycle_time_ms;
+ struct azoteq_gesture_events ges_events;
+ struct azoteq_sysinfo sys;
+};
+
+struct azoteq_finger {
+ uint16_t abs_x;
+ uint16_t abs_y;
+ uint16_t touch_strength;
+ uint8_t touch_area;
+};
+
+// Starts at 0x0000, 57 bytes
+struct azoteq_data {
+ struct azoteq_header header;
+ uint8_t num_fingers;
+ int16_t relative_x;
+ int16_t relative_y;
+ struct azoteq_finger fingers[5];
+};
+
+#define ADDR_SENSOR (0x74<<1)
+
+void read_azoteq_data(struct azoteq_data *data);
diff --git a/reform2-trackpad-fw/flash.sh b/reform2-trackpad-fw/flash.sh
@@ -1,4 +1,8 @@
+#!/bin/bash
+# Kick trackpad into bootloader mode, and wait a bit
+echo -ne 'xJTBL' > /dev/hidraw2
+sleep 2
dfu-programmer atmega32u2 erase --suppress-bootloader-mem