reform

MNT Reform: Open Source Portable Computer
Log (Feed) | Files | Refs (Tags) | README

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:
Mreform2-trackpad-fw/Makefile | 2+-
Mreform2-trackpad-fw/Mouse.c | 235+++++++++++++++++++++++++------------------------------------------------------
Areform2-trackpad-fw/README.md | 37+++++++++++++++++++++++++++++++++++++
Areform2-trackpad-fw/azoteq.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Areform2-trackpad-fw/azoteq.h | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mreform2-trackpad-fw/flash.sh | 4++++
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