commit 569d556b4e964b60833206eef32b4080486dc283 Author: FizzyApple12 Date: Fri Jul 1 04:27:38 2022 -0400 Initial commit with V1 hardware diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b65eef --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +WaccaProtocolTranslator/build/ + +WaccaProtocolTranslator/.vscode/ + +WaccaTouchPanel/build/ + +WaccaTouchPanel/.vscode/ + +/**/OldVersions/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e44c070 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Wacca Controller + +This repo is right now still a work in progress, nothing has been finalized and things will probably be broken, stay tuned as things are finalized though! diff --git a/Wacca Controller/Full Controller.iam b/Wacca Controller/Full Controller.iam new file mode 100644 index 0000000..63e4690 Binary files /dev/null and b/Wacca Controller/Full Controller.iam differ diff --git a/Wacca Controller/Half Ring/Half Ring.iam b/Wacca Controller/Half Ring/Half Ring.iam new file mode 100644 index 0000000..6093ef6 Binary files /dev/null and b/Wacca Controller/Half Ring/Half Ring.iam differ diff --git a/Wacca Controller/Touch Segment/Touch Casing.ipt b/Wacca Controller/Touch Segment/Touch Casing.ipt new file mode 100644 index 0000000..673c11e Binary files /dev/null and b/Wacca Controller/Touch Segment/Touch Casing.ipt differ diff --git a/Wacca Controller/Touch Segment/Touch Fifth.ipt b/Wacca Controller/Touch Segment/Touch Fifth.ipt new file mode 100644 index 0000000..82d732b Binary files /dev/null and b/Wacca Controller/Touch Segment/Touch Fifth.ipt differ diff --git a/Wacca Controller/Touch Segment/Touch Segment Cover.ipt b/Wacca Controller/Touch Segment/Touch Segment Cover.ipt new file mode 100644 index 0000000..a218c5d Binary files /dev/null and b/Wacca Controller/Touch Segment/Touch Segment Cover.ipt differ diff --git a/Wacca Controller/Touch Segment/Touch Segment.iam b/Wacca Controller/Touch Segment/Touch Segment.iam new file mode 100644 index 0000000..5873620 Binary files /dev/null and b/Wacca Controller/Touch Segment/Touch Segment.iam differ diff --git a/WaccaProtocolTranslator/CMakeLists.txt b/WaccaProtocolTranslator/CMakeLists.txt new file mode 100644 index 0000000..e297e2a --- /dev/null +++ b/WaccaProtocolTranslator/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.13) + +set(PICO_SDK_FETCH_FROM_GIT on) + +include(pico_sdk_import.cmake) + +project(WaccaTouchPanel) + +pico_sdk_init() + +add_executable(wacca_protocol_translator + main.c + waccaserial.h + waccaserial.c + queue.h + queue.c +) + +# Add pico_stdlib library which aggregates commonly used features +target_link_libraries(wacca_protocol_translator + pico_stdlib + hardware_i2c + pico_multicore + pico_stdio + pico_stdio_usb +) + +# create map/bin/hex/uf2 file in addition to ELF. +pico_add_extra_outputs(wacca_protocol_translator) + +pico_enable_stdio_usb(wacca_protocol_translator 1) +pico_enable_stdio_uart(wacca_protocol_translator 0) \ No newline at end of file diff --git a/WaccaProtocolTranslator/build.bat b/WaccaProtocolTranslator/build.bat new file mode 100644 index 0000000..ad00791 --- /dev/null +++ b/WaccaProtocolTranslator/build.bat @@ -0,0 +1,5 @@ +if not exist "build" mkdir build +cd build +cmake -G "Unix Makefiles" .. +make wacca_protocol_translator +cd .. \ No newline at end of file diff --git a/WaccaProtocolTranslator/build.sh b/WaccaProtocolTranslator/build.sh new file mode 100644 index 0000000..7fd8e17 --- /dev/null +++ b/WaccaProtocolTranslator/build.sh @@ -0,0 +1,5 @@ +mkdir -p build +cd build +cmake -G "Unix Makefiles" .. +make wacca_protocol_translator +cd .. \ No newline at end of file diff --git a/WaccaProtocolTranslator/main.c b/WaccaProtocolTranslator/main.c new file mode 100644 index 0000000..e8e1b89 --- /dev/null +++ b/WaccaProtocolTranslator/main.c @@ -0,0 +1,85 @@ +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "waccaserial.h" + +#define TOUCH_I2C_PORT i2c0 +#define TOUCH_I2C_SDA 6 +#define TOUCH_I2C_SCL 7 +#define TOUCH_PANEL_0_ADDR 0x11 +#define TOUCH_PANEL_1_ADDR 0x11 //0x12 +#define TOUCH_PANEL_2_ADDR 0x11 //0x13 +#define TOUCH_PANEL_3_ADDR 0x11 //0x14 +#define TOUCH_PANEL_4_ADDR 0x11 //0x15 +#define TOUCH_PANEL_5_ADDR 0x11 //0x16 +#define TOUCH_I2C_FREQ 100000 + +uint32_t touchPanel0Data = 0; +uint32_t touchPanel1Data = 0; +uint32_t touchPanel2Data = 0; +uint32_t touchPanel3Data = 0; +uint32_t touchPanel4Data = 0; +uint32_t touchPanel5Data = 0; + + +uint32_t getTouchPacket(i2c_inst_t *port, uint8_t addr) { + int bytesRead = 0; + uint8_t data[4]; + uint8_t dummyRegister = 0x69; + + i2c_write_blocking(port, addr, &dummyRegister, 1, true); // it's okay to use blocking methods, the main protocol translation is running on a seperate cpu core + bytesRead = i2c_read_blocking(port, addr, data, 4, false); + + if (bytesRead != 4) return 0; + + return (uint32_t) (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24)); +} + +#define PingWaccaPanel(ADDR, final) do { final = getTouchPacket(TOUCH_I2C_PORT, ADDR); } while (final == 0) + +void setupTouchPanels() { + i2c_init(TOUCH_I2C_PORT, TOUCH_I2C_FREQ); + + gpio_set_function(TOUCH_I2C_SDA, GPIO_FUNC_I2C); + gpio_set_function(TOUCH_I2C_SCL, GPIO_FUNC_I2C); + + gpio_pull_up(TOUCH_I2C_SDA); + gpio_pull_up(TOUCH_I2C_SCL); + + PingWaccaPanel(TOUCH_PANEL_0_ADDR, touchPanel0Data); + PingWaccaPanel(TOUCH_PANEL_1_ADDR, touchPanel1Data); + PingWaccaPanel(TOUCH_PANEL_2_ADDR, touchPanel2Data); + PingWaccaPanel(TOUCH_PANEL_3_ADDR, touchPanel3Data); + PingWaccaPanel(TOUCH_PANEL_4_ADDR, touchPanel4Data); + PingWaccaPanel(TOUCH_PANEL_5_ADDR, touchPanel5Data); + + ws_setTouchData(touchPanel0Data, touchPanel1Data, touchPanel2Data, touchPanel3Data, touchPanel4Data, touchPanel5Data); +} + +int main() { + setupTouchPanels(); + + ws_start(); + + while(1) { // note: getting a touch packet might take a bit, use this rolling update to speed up input throughput to the game + touchPanel0Data = getTouchPacket(TOUCH_I2C_PORT, TOUCH_PANEL_0_ADDR); + ws_setTouch0Data(touchPanel0Data); + + touchPanel1Data = getTouchPacket(TOUCH_I2C_PORT, TOUCH_PANEL_1_ADDR); + ws_setTouch1Data(touchPanel1Data); + + touchPanel2Data = getTouchPacket(TOUCH_I2C_PORT, TOUCH_PANEL_2_ADDR); + ws_setTouch2Data(touchPanel2Data); + + touchPanel3Data = getTouchPacket(TOUCH_I2C_PORT, TOUCH_PANEL_3_ADDR); + ws_setTouch3Data(touchPanel3Data); + + touchPanel4Data = getTouchPacket(TOUCH_I2C_PORT, TOUCH_PANEL_4_ADDR); + ws_setTouch4Data(touchPanel4Data); + + touchPanel5Data = getTouchPacket(TOUCH_I2C_PORT, TOUCH_PANEL_5_ADDR); + ws_setTouch5Data(touchPanel5Data); + } + + return 0; +} \ No newline at end of file diff --git a/WaccaProtocolTranslator/pico_sdk_import.cmake b/WaccaProtocolTranslator/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/WaccaProtocolTranslator/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE}) diff --git a/WaccaProtocolTranslator/queue.c b/WaccaProtocolTranslator/queue.c new file mode 100644 index 0000000..b33cc7d --- /dev/null +++ b/WaccaProtocolTranslator/queue.c @@ -0,0 +1,40 @@ +#include +#include +#include "queue.h" + +void enqueue(node_t **head, int val) { + node_t *new_node = malloc(sizeof(node_t)); + if (!new_node) return; + + new_node->val = val; + new_node->next = *head; + + *head = new_node; +} + +int dequeue(node_t **head) { + node_t *current, *prev = NULL; + int retval = -1; + + if (*head == NULL) return -1; + + current = *head; + while (current->next != NULL) { + prev = current; + current = current->next; + } + + retval = current->val; + free(current); + + if (prev) + prev->next = NULL; + else + *head = NULL; + + return retval; +} + +bool isEmpty(node_t **head) { + return *head == NULL; +} \ No newline at end of file diff --git a/WaccaProtocolTranslator/queue.h b/WaccaProtocolTranslator/queue.h new file mode 100644 index 0000000..1a66d07 --- /dev/null +++ b/WaccaProtocolTranslator/queue.h @@ -0,0 +1,18 @@ +#ifndef _QUEUE_ +#define _QUEUE_ + +#include +#include + +typedef struct node { + int val; + struct node *next; +} node_t; + +void enqueue(node_t **head, int val); + +int dequeue(node_t **head); + +bool isEmpty(node_t **head); + +#endif \ No newline at end of file diff --git a/WaccaProtocolTranslator/waccaserial.c b/WaccaProtocolTranslator/waccaserial.c new file mode 100644 index 0000000..e47ba92 --- /dev/null +++ b/WaccaProtocolTranslator/waccaserial.c @@ -0,0 +1,265 @@ +#include "waccaserial.h" +#include "pico.h" +#include "pico/time.h" +#include "pico/multicore.h" +#include "hardware/i2c.h" +#include +#include +#include "queue.h" +#include "pico/stdio_usb.h" + +// Massive thank you to the contributors for the WACVR project <3 + +uint8_t TouchPack[36] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +bool StartUp = false; +uint8_t inByte = 0; + +node_t *touchQueueHead = NULL; + +uint32_t elapsedTime = 0; +uint32_t previousTime = 0; + +bool runningMulticore = false; + +void ws_start() { + stdio_usb_init(); + + previousTime = to_ms_since_boot(get_absolute_time()); +} + +void ws_touchThreadLoop() { + while (true) { + uint32_t currentTime = to_ms_since_boot(get_absolute_time()); + elapsedTime += currentTime - previousTime; + previousTime = currentTime; + + if (elapsedTime >= 1000) { + elapsedTime -= 1000; + enqueue(&touchQueueHead, 1); + } + + if (!isEmpty(&touchQueueHead)) { + dequeue(&touchQueueHead); + ws_sendTouchState(); + } + + if (stdio_usb_connected()) ws_readHead(); + } +} + +void ws_sendTouchState() { + if (StartUp) ws_sendTouch(); +} + +void ws_readHead() { + uint8_t inByte = getchar_timeout_us(0); + + if (inByte != 0) { + uint8_t data = 0; + uint8_t importantData = 0; + int i = 0; + do { + data = getchar_timeout_us(0); + if (i == 2) importantData = data; + i++; + } while (data != 0); + + ws_sendResp(importantData); + } +} + +void ws_sendResp(char importantNextReadData) { + switch (inByte) { + case CMD_GET_SYNC_BOARD_VER: + StartUp = false; + + putchar_raw(inByte); + + for (int i = 0; i < 6; i++) { + putchar_raw(SYNC_BOARD_VER[i]); + } + + putchar_raw(44); + break; + case CMD_NEXT_READ: + StartUp = false; + switch (importantNextReadData) { + case 0x30: + for (int i = 0; i < 80; i++) { + putchar_raw(read1[i]); + } + + putchar_raw(bh_calcChecksum((uint8_t *) read1, 80)); + break; + case 0x31: + for (int i = 0; i < 80; i++) { + putchar_raw(read2[i]); + } + + putchar_raw(bh_calcChecksum((uint8_t *) read2, 80)); + break; + case 0x33: + for (int i = 0; i < 80; i++) { + putchar_raw(read3[i]); + } + + putchar_raw(bh_calcChecksum((uint8_t *) read3, 80)); + break; + default: + break; + } + break; + case CMD_GET_UNIT_BOARD_VER: + putchar_raw(inByte); + + for (int i = 0; i < 6; i++) { + putchar_raw(SYNC_BOARD_VER[i]); + } + + #ifdef RIGHT + putchar_raw('R'); + #endif + #ifndef RIGHT + putchar_raw('L'); + #endif + + for (int i = 0; i < 6; i++) { + for (int i = 0; i < 6; i++) { + putchar_raw(UNIT_BOARD_VER[i]); + } + } + + #ifdef RIGHT + putchar_raw(118); + #endif + #ifndef RIGHT + putchar_raw(104); + #endif + + break; + case CMD_MYSTERY1: + StartUp = false; + + for (int i = 0; i < 7; i++) { + putchar_raw(SettingData_162[i]); + } + break; + case CMD_MYSTERY2: + StartUp = false; + + for (int i = 0; i < 7; i++) { + putchar_raw(SettingData_148[i]); + } + break; + case CMD_START_AUTO_SCAN: + for (int i = 0; i < 7; i++) { + putchar_raw(SettingData_201[i]); + } + + StartUp = true; + + if (!runningMulticore) { + multicore_launch_core1(ws_touchThreadLoop); + runningMulticore = true; + } + break; + case CMD_BEGIN_WRITE: + break; + case CMD_NEXT_WRITE: + break; + case 154: + StartUp = false; + break; + } +} + +void ws_getTouchPack() { + TouchPack[0] = 129; + TouchPack[34] = TouchPack[34] + 1; + TouchPack[35] = 128; + TouchPack[35] = bh_calcChecksum(TouchPack, 36); + if (TouchPack[34] > 127) TouchPack[34] = 0; +} + +void ws_sendTouch() { + if (StartUp) { + ws_getTouchPack(); + + for (int i = 0; i < 36; i++) { + putchar_raw(TouchPack[i]); + } + } +} + +// since i want the wacca panels to be as simple as possible, i need to reverse the input order on the right side to fit with how the game reads data +#ifdef RIGHT +uint8_t reverseAndShift(uint8_t b) { + b = (b & 0xF0) >> 4 | (b & 0x0F) << 4; + b = (b & 0xCC) >> 2 | (b & 0x33) << 2; + b = (b & 0xAA) >> 1 | (b & 0x55) << 1; + return b >> 3; +} +#endif + +// if im not on on the right then no flip needs to happen so we can just return the byte as normal +#ifndef RIGHT +uint8_t reverseAndShift(uint8_t b) { + return b; +} +#endif + +#define getRow1(segment) reverseAndShift((uint8_t) ((segment & 0b00000000000000000000000000011111))) +#define getRow2(segment) reverseAndShift((uint8_t) ((segment & 0b00000000000000000000001111100000) >> 5)) +#define getRow3(segment) reverseAndShift((uint8_t) ((segment & 0b00000000000000000111110000000000) >> 10)) +#define getRow4(segment) reverseAndShift((uint8_t) ((segment & 0b00000000000011111000000000000000) >> 15)) + +void ws_setTouch0Data(uint32_t touchSegment0) { + TouchPack[6] = getRow1(touchSegment0); + TouchPack[12] = getRow2(touchSegment0); + TouchPack[18] = getRow3(touchSegment0); + TouchPack[24] = getRow4(touchSegment0); +} + +void ws_setTouch1Data(uint32_t touchSegment1) { + TouchPack[5] = getRow1(touchSegment1); + TouchPack[11] = getRow2(touchSegment1); + TouchPack[17] = getRow3(touchSegment1); + TouchPack[23] = getRow4(touchSegment1); +} + +void ws_setTouch2Data(uint32_t touchSegment2) { + TouchPack[4] = getRow1(touchSegment2); + TouchPack[10] = getRow2(touchSegment2); + TouchPack[16] = getRow3(touchSegment2); + TouchPack[22] = getRow4(touchSegment2); +} + +void ws_setTouch3Data(uint32_t touchSegment3) { + TouchPack[3] = getRow1(touchSegment3); + TouchPack[9] = getRow2(touchSegment3); + TouchPack[15] = getRow3(touchSegment3); + TouchPack[21] = getRow4(touchSegment3); +} + +void ws_setTouch4Data(uint32_t touchSegment4) { + TouchPack[2] = getRow1(touchSegment4); + TouchPack[8] = getRow2(touchSegment4); + TouchPack[14] = getRow3(touchSegment4); + TouchPack[20] = getRow4(touchSegment4); +} + +void ws_setTouch5Data(uint32_t touchSegment5) { + TouchPack[1] = getRow1(touchSegment5); + TouchPack[7] = getRow2(touchSegment5); + TouchPack[13] = getRow3(touchSegment5); + TouchPack[19] = getRow4(touchSegment5); +} + +void ws_setTouchData(uint32_t touchSegment0, uint32_t touchSegment1, uint32_t touchSegment2, uint32_t touchSegment3, uint32_t touchSegment4, uint32_t touchSegment5) { + ws_setTouch0Data(touchSegment0); + ws_setTouch1Data(touchSegment1); + ws_setTouch2Data(touchSegment2); + ws_setTouch3Data(touchSegment3); + ws_setTouch4Data(touchSegment4); + ws_setTouch5Data(touchSegment5); +} \ No newline at end of file diff --git a/WaccaProtocolTranslator/waccaserial.h b/WaccaProtocolTranslator/waccaserial.h new file mode 100644 index 0000000..4c2a9dd --- /dev/null +++ b/WaccaProtocolTranslator/waccaserial.h @@ -0,0 +1,79 @@ +#ifndef _WACCA_SERIAL_ +#define _WACCA_SERIAL_ + +#include "pico.h" +#include "pico/time.h" +#include "pico/multicore.h" +#include "hardware/i2c.h" +#include +#include +#include "queue.h" +#include "pico/stdio_usb.h" + +// Massive thank you to the contributors for the WACVR project <3 + +//#define RIGHT //uncomment to enable righty flip on interface and sync board data swap + +#define CMD_GET_SYNC_BOARD_VER 0xa0 +#define CMD_NEXT_READ 0x72 +#define CMD_GET_UNIT_BOARD_VER 0xa8 +#define CMD_MYSTERY1 0xa2 +#define CMD_MYSTERY2 0x94 +#define CMD_START_AUTO_SCAN 0xc9 +#define CMD_BEGIN_WRITE 0x77 +#define CMD_NEXT_WRITE 0x20 + +static const uint8_t SYNC_BOARD_VER[6] = "190523"; +static const uint8_t UNIT_BOARD_VER[6] = "190514"; +static const uint8_t read1[80] = " 0 0 1 2 3 4 5 15 15 15 15 15 15 11 11 11"; +static const uint8_t read2[80] = " 11 11 11 128 103 103 115 138 127 103 105 111 126 113 95 100"; +static const uint8_t read3[80] = " 101 115 98 86 76 67 68 48 117 0 82 154 0 6 35 4"; + +static const uint8_t SettingData_160[8] = { 160, 49, 57, 48, 53, 50, 51, 44 }; +static const uint8_t SettingData_162[7] = { 162, 63, 29, 0, 0, 0, 0 }; +static const uint8_t SettingData_148[7] = { 148, 0, 20, 0, 0, 0, 0 }; +static const uint8_t SettingData_201[7] = { 201, 0, 73, 0, 0, 0, 0 }; + +extern uint8_t TouchPack[36]; +extern bool StartUp; +extern uint8_t inByte; + +void ws_start(); //public + +void ws_touchThreadLoop(); + +void ws_sendTouchState(); + +void ws_readHead(); + +void ws_sendResp(char importantNextReadData); + +void ws_getTouchPack(); + +void ws_sendTouch(); + +void ws_setTouch0Data(uint32_t touchSegment0); //public + +void ws_setTouch1Data(uint32_t touchSegment1); //public + +void ws_setTouch2Data(uint32_t touchSegment2); //public + +void ws_setTouch3Data(uint32_t touchSegment3); //public + +void ws_setTouch4Data(uint32_t touchSegment4); //public + +void ws_setTouch5Data(uint32_t touchSegment5); //public + +void ws_setTouchData(uint32_t touchSegment0, uint32_t touchSegment1, uint32_t touchSegment2, uint32_t touchSegment3, uint32_t touchSegment4, uint32_t touchSegment5); //public + +inline uint8_t bh_calcChecksum(uint8_t * bytes, int length) { + uint8_t checkSum = 0x00; + + for (int i = 0; i < length; i++) { + checkSum ^= *(bytes + i); + } + + return checkSum; +} + +#endif \ No newline at end of file diff --git a/WaccaTouchPanel/CMakeLists.txt b/WaccaTouchPanel/CMakeLists.txt new file mode 100644 index 0000000..a3288e4 --- /dev/null +++ b/WaccaTouchPanel/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.13) + +set(PICO_SDK_FETCH_FROM_GIT on) + +include(pico_sdk_import.cmake) + +project(WaccaTouchPanel) + +pico_sdk_init() + +add_executable(wacca_touch_panel + main.c + mpr121.h + mpr121.c +) + +# Add pico_stdlib library which aggregates commonly used features +target_link_libraries(wacca_touch_panel + pico_stdlib + hardware_i2c +) + +# create map/bin/hex/uf2 file in addition to ELF. +pico_add_extra_outputs(wacca_touch_panel) + +pico_enable_stdio_usb(wacca_touch_panel 1) +pico_enable_stdio_uart(wacca_touch_panel 0) \ No newline at end of file diff --git a/WaccaTouchPanel/build.bat b/WaccaTouchPanel/build.bat new file mode 100644 index 0000000..cca1c93 --- /dev/null +++ b/WaccaTouchPanel/build.bat @@ -0,0 +1,5 @@ +if not exist "build" mkdir build +cd build +cmake -G "Unix Makefiles" .. +make wacca_touch_panel +cd .. \ No newline at end of file diff --git a/WaccaTouchPanel/build.sh b/WaccaTouchPanel/build.sh new file mode 100644 index 0000000..903fa2e --- /dev/null +++ b/WaccaTouchPanel/build.sh @@ -0,0 +1,5 @@ +mkdir -p build +cd build +cmake -G "Unix Makefiles" .. +make wacca_touch_panel +cd .. \ No newline at end of file diff --git a/WaccaTouchPanel/main.c b/WaccaTouchPanel/main.c new file mode 100644 index 0000000..b5d7265 --- /dev/null +++ b/WaccaTouchPanel/main.c @@ -0,0 +1,217 @@ +#include +#include +#include "pico/stdlib.h" +#include "hardware/i2c.h" +#include "hardware/irq.h" +#include "mpr121.h" + +#define DEBUG_MODE // uncomment for debug messages (note: this will slow things down!) + +#define MOTHERBOARD_I2C_PORT i2c1 +#define MOTHERBOARD_I2C_HWID 0x10 // 0x1[1-6 depending on which position it's in bottom to top] +#define MOTHERBOARD_I2C_SDA 4 +#define MOTHERBOARD_I2C_SCL 5 +#define MOTHERBOARD_I2C_FREQ 100000 + +#define TOUCH_I2C_PORT i2c0 +#define TOUCH_I2C_SDA 6 +#define TOUCH_I2C_SCL 7 +#define INNER_MPR121_ADDR 0x5A +#define OUTER_MPR121_ADDR 0x5B +#define TOUCH_I2C_FREQ 100000 +#define MPR121_TOUCH_THRESHOLD 16 +#define MPR121_RELEASE_THRESHOLD 10 + +typedef enum i2c_slave_event_t { + I2C_SLAVE_RECEIVE, + I2C_SLAVE_REQUEST, + I2C_SLAVE_FINISH, +} i2c_slave_event_t; + +// Buttons +// [--] [--] [--] [--] [--] +// [--] [--] [--] [--] [--] +// [--] [--] [ ] [ ] [ ] +// [ ] [ ] [ ] [ ] [ ] + +struct mpr121_sensor innerSensor; +uint16_t touchedInner = 0; + +// Buttons +// [ ] [ ] [ ] [ ] [ ] +// [ ] [ ] [ ] [ ] [ ] +// [ ] [ ] [--] [--] [--] +// [--] [--] [--] [--] [--] + +struct mpr121_sensor outerSensor; +uint16_t touchedOuter = 0; + +// Button bits +// [ 0] [ 1] [ 2] [ 3] [ 4] +// [ 5] [ 6] [ 7] [ 8] [ 9] +// [10] [11] [12] [13] [14] +// [15] [16] [17] [18] [19] + +uint32_t previousTouchDataPacket = 0; +bool writingPacket = false; +uint32_t touchDataPacket = 0; + +bool transferInProgress; + +static inline uint8_t i2c_read_byte(i2c_inst_t *i2c) { + i2c_hw_t *hw = i2c_get_hw(i2c); + assert(hw->status & I2C_IC_STATUS_RFNE_BITS); + return (uint8_t) hw->data_cmd; +} + +static inline void i2c_write_byte(i2c_inst_t *i2c, uint8_t value) { + i2c_hw_t *hw = i2c_get_hw(i2c); + assert(hw->status & I2C_IC_STATUS_TFNF_BITS); + hw->data_cmd = value; +} + +static void i2cSlaveHandler(i2c_inst_t *i2c, i2c_slave_event_t event) { + switch (event) { + case I2C_SLAVE_RECEIVE: + break; + case I2C_SLAVE_REQUEST: ; + uint32_t dataToSend = previousTouchDataPacket; + + if (!writingPacket) dataToSend = touchDataPacket; + + #ifdef DEBUG_MODE + printf("Master requested packet, sending [%u]...\n", dataToSend); + #endif + + i2c_write_byte(MOTHERBOARD_I2C_PORT, (uint8_t) dataToSend); + i2c_write_byte(MOTHERBOARD_I2C_PORT, (uint8_t) dataToSend >> 8); + i2c_write_byte(MOTHERBOARD_I2C_PORT, (uint8_t) dataToSend >> 16); + i2c_write_byte(MOTHERBOARD_I2C_PORT, (uint8_t) dataToSend >> 24); + + break; + case I2C_SLAVE_FINISH: + break; + default: + break; + } +} + +static inline void finishTransfer() { + if (transferInProgress) { + i2cSlaveHandler(MOTHERBOARD_I2C_PORT, I2C_SLAVE_FINISH); + transferInProgress = false; + } +} + +static void __not_in_flash_func(i2cSlaveI2CIRQHandler)() { + uint32_t intrStat = MOTHERBOARD_I2C_PORT->hw->intr_stat; + if (intrStat == 0) { + return; + } + if (intrStat & I2C_IC_INTR_STAT_R_TX_ABRT_BITS) { + MOTHERBOARD_I2C_PORT->hw->clr_tx_abrt; + finishTransfer(); + } + if (intrStat & I2C_IC_INTR_STAT_R_START_DET_BITS) { + MOTHERBOARD_I2C_PORT->hw->clr_start_det; + finishTransfer(); + } + if (intrStat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { + MOTHERBOARD_I2C_PORT->hw->clr_stop_det; + finishTransfer(); + } + if (intrStat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) { + transferInProgress = true; + i2cSlaveHandler(MOTHERBOARD_I2C_PORT, I2C_SLAVE_RECEIVE); + } + if (intrStat & I2C_IC_INTR_STAT_R_RD_REQ_BITS) { + MOTHERBOARD_I2C_PORT->hw->clr_rd_req; + transferInProgress = true; + i2cSlaveHandler(MOTHERBOARD_I2C_PORT, I2C_SLAVE_REQUEST); + } +} + + + +void setupTouch() { + i2c_init(TOUCH_I2C_PORT, TOUCH_I2C_FREQ); + + gpio_set_function(TOUCH_I2C_SDA, GPIO_FUNC_I2C); + gpio_set_function(TOUCH_I2C_SCL, GPIO_FUNC_I2C); + + gpio_pull_up(TOUCH_I2C_SDA); + gpio_pull_up(TOUCH_I2C_SCL); + + mpr121_init(TOUCH_I2C_PORT, INNER_MPR121_ADDR, &innerSensor); + mpr121_set_thresholds(MPR121_TOUCH_THRESHOLD, MPR121_RELEASE_THRESHOLD, &innerSensor); + + mpr121_init(TOUCH_I2C_PORT, OUTER_MPR121_ADDR, &outerSensor); + mpr121_set_thresholds(MPR121_TOUCH_THRESHOLD, MPR121_RELEASE_THRESHOLD, &outerSensor); +} + +void setupMotherboard() { + i2c_init(MOTHERBOARD_I2C_PORT, MOTHERBOARD_I2C_FREQ); + + gpio_set_function(MOTHERBOARD_I2C_SDA, GPIO_FUNC_I2C); + gpio_set_function(MOTHERBOARD_I2C_SCL, GPIO_FUNC_I2C); + + gpio_pull_up(MOTHERBOARD_I2C_SDA); + gpio_pull_up(MOTHERBOARD_I2C_SCL); + + MOTHERBOARD_I2C_PORT->hw->enable = 0; + hw_set_bits(&MOTHERBOARD_I2C_PORT->hw->con, I2C_IC_CON_RX_FIFO_FULL_HLD_CTRL_BITS); + MOTHERBOARD_I2C_PORT->hw->enable = 1; + + uint i2cHardwareIndex = i2c_hw_index(MOTHERBOARD_I2C_PORT); + i2c_set_slave_mode(MOTHERBOARD_I2C_PORT, false, MOTHERBOARD_I2C_HWID); + + MOTHERBOARD_I2C_PORT->hw->intr_mask = I2C_IC_INTR_MASK_M_RX_FULL_BITS | I2C_IC_INTR_MASK_M_RD_REQ_BITS | I2C_IC_RAW_INTR_STAT_TX_ABRT_BITS | I2C_IC_INTR_MASK_M_STOP_DET_BITS | I2C_IC_INTR_MASK_M_START_DET_BITS; + + uint IRQNumber = I2C0_IRQ + i2cHardwareIndex; + irq_set_exclusive_handler(IRQNumber, i2cSlaveI2CIRQHandler); + irq_set_enabled(IRQNumber, true); +} + +#ifdef DEBUG_MODE + #define formatButton(data, number) (((data) & (1 << (number))) ? "#" : " ") + #define printMultiButtonRow(data1, data2, n1, n2, n3, n4, n5) printf("[%s] [%s] [%s] [%s] [%s]\n", formatButton(data1, n1), formatButton(data1, n2), formatButton(data2, n3), formatButton(data2, n4), formatButton(data2, n5)) + #define printButtonRow(data, n1, n2, n3, n4, n5) printf("[%s] [%s] [%s] [%s] [%s]\n", formatButton(data, n1), formatButton(data, n2), formatButton(data, n3), formatButton(data, n4), formatButton(data, n5)) +#endif + +int main() { + stdio_init_all(); + printf("Wacca Touch Panel v0.1\n"); + + setupTouch(); + printf("Touch Ready\n"); + + setupMotherboard(); + printf("Communications Ready\n"); + + while(1) { + mpr121_touched(&touchedInner, &innerSensor); + mpr121_touched(&touchedOuter, &outerSensor); + + touchedInner = touchedInner & 0b0000111111111111; + touchedOuter = touchedOuter & 0b0000000011111111; + + writingPacket = true; + + touchDataPacket = ((uint32_t) touchedOuter << 12 | touchedInner) | 0x06900000; + + #ifdef DEBUG_MODE + printf("-------------------\n"); + printButtonRow(touchedInner, 0, 1, 2, 3, 4); + printButtonRow(touchedInner, 5, 6, 7, 8, 9); + printMultiButtonRow(touchedInner, touchedOuter, 10, 11, 0, 1, 2); + printButtonRow(touchedOuter, 3, 4, 5, 6, 7); + printf("-------------------\n"); + #endif + + writingPacket = false; + + sleep_ms(10); + } + + return 0; +} \ No newline at end of file diff --git a/WaccaTouchPanel/mpr121.c b/WaccaTouchPanel/mpr121.c new file mode 100644 index 0000000..7586675 --- /dev/null +++ b/WaccaTouchPanel/mpr121.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2021-2022 Antonio González + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "mpr121.h" + +void mpr121_init(i2c_inst_t *i2c_port, uint8_t i2c_addr, + mpr121_sensor_t *sensor) { + sensor->i2c_port = i2c_port; + sensor->i2c_addr = i2c_addr; + + // Enter stop mode by setting ELEPROX_EN and ELE_EN bits to zero. + // This is needed because register write operations can only take + // place in stop mode. + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); + + // Writing 0x80 (SOFT_RESET) with 0x63 asserts soft reset. + mpr121_write(MPR121_SOFT_RESET_REG, 0x63, sensor); + + // == Capacitance sensing settings (AN2889), Filtering and ========= + // timing settings (AN3890) + + // These settings are configured in two registers: the Filter and + // Global CDC CDT Configuration registers (0x5C, 0x5D). + // + // Charge-discharge current (CDC) and charge-discharge time (CDT) + // can be configured globally or on a per-electrode basis. Here, + // The global CDC and CDT values are set to their defaults, and then + // these values are overriden by independently configuring each + // electrode (auto-configuration). + + // Filter/global CDC configuration register (0x5C) + // + // First filter iterations (FFI), bits 7-6. Number of samples taken + // as input to the first level of filtering. Default is 0b00 (sets + // samples taken to 6) + // + // Charge-discharge current (CDC), bits 5-0. Sets the value of + // charge-discharge current applied to the electrode. Max is 63 µA + // in 1 µA steps. Default is 0b010000 (16 µA) + // + // AFE configuration register default, 0b00010000 = 0x10 + mpr121_write(MPR121_AFE_CONFIG_REG, 0x10, sensor); + + // Filter/global CDC configuration register (0x5D) + // + // Charge discharge time (CDT), bits 7-5. Selects the global value + // of charge time applied to electrode. The maximum is 32 μs, + // programmable as 2^(n-2) μs. Default is 0b001 (time is set to + // 0.5 µs) + // + // Second filter iterations (SFI), bits 4-3. Selects the number of + // samples taken for the second level filter. Default is 0b00 + // (number of samples is set to 4) + // + // Electrode sample interval (ESI), bits 2-0. Controls the sampling + // rate of the device. The maximum is 128 ms, programmable to 2^n + // ms. Decrease this value for better response time, increase to + // save power. Default is 0b100 (period set to 16 ms). + // + // Filter configuration register default, 0b00100100 = 0x24 + // I do not need power saving features but I want fast responses, + // so I set this to 0x20. + mpr121_write(MPR121_FILTER_CONFIG_REG, 0x20, sensor); + + // Auto-configuration + // + // Sets automatically charge current (CDC) and time (CDT) values for + // each electrode. + // + // Autoconfig USL register: the upper limit for the + // auto-configuration. This value (and those that follow below) + // were calculated based on Vdd = 3.3 V and following the equations + // in NXP Application Note AN3889. + // USL = 201 = 0xC9 + mpr121_write(MPR121_AUTOCONFIG_USL_REG, 0xC9, sensor); + + // Autoconfig target level register: the target level for the + // auto-configuration baseline search. + // TL = 181 = 0xB5 + mpr121_write(MPR121_AUTOCONFIG_TARGET_REG, 0xB5, sensor); + + // Autoconfig LSL register: the lower limit for the + // auto-configuration. + // LSL = 131 = 0x83 + mpr121_write(MPR121_AUTOCONFIG_LSL_REG, 0x83, sensor); + + // Autoconfiguration control register. Default value is 0b00001011 = + // 0x0B, where: + // + // First filter iterations (FFI), bits 7-6. Must be the same value + // of FFI as in register MPR121_AFE_CONFIG_REG (0x5C) above; + // default is 0b00. + // + // Retry, bits 5-4. Default is disabled, 0b00. + // + // Baseline value adjust (BVA), bits 3-2. This value must be the + // same as the CL (calibration lock) value in the Electrode + // Configuration Register, below, i.e. 0b10. + // + // Automatic Reconfiguration Enable (ARE), bit 1. Default is 0b1, + // enabled. + // + // Automatic Reconfiguration Enable (ACE), bit 0. Default is 0b1, + // enabled. + mpr121_write(MPR121_AUTOCONFIG_CONTROL_0_REG, 0x0B, sensor); + + // == Baseline system (AN3891) ===================================== + + // Maximum Half Delta (MHD): Determines the largest magnitude of + // variation to pass through the baseline filter. The range of the + // effective value is 1~63. + mpr121_write(MPR121_MAX_HALF_DELTA_RISING_REG, 0x01, sensor); + mpr121_write(MPR121_MAX_HALF_DELTA_FALLING_REG, 0x01, sensor); + + // Noise Half Delta (NHD): Determines the incremental change when + // non-noise drift is detected. The range of the effective value is + // 1~63. + mpr121_write(MPR121_NOISE_HALF_DELTA_RISING_REG, 0x01, sensor); + mpr121_write(MPR121_NOISE_HALF_DELTA_FALLING_REG, 0x01, sensor); + mpr121_write(MPR121_NOISE_HALF_DELTA_TOUCHED_REG, 0x01, sensor); + + // Noise Count Limit (NCL): Determines the number of samples + // consecutively greater than the Max Half Delta value. This is + // necessary to determine that it is not noise. The range of the + // effective value is 0~255. + mpr121_write(MPR121_NOISE_COUNT_LIMIT_RISING_REG, 0x00, sensor); + mpr121_write(MPR121_NOISE_COUNT_LIMIT_FALLING_REG, 0xFF, sensor); + mpr121_write(MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG, 0x00, sensor); + + // Filter Delay Count Limit (FDL): Determines the operation rate of + // the filter. A larger count limit means the filter delay is + // operating more slowly. The range of the effective value is 0~255. + mpr121_write(MPR121_FILTER_DELAY_COUNT_RISING_REG, 0x00, sensor); + mpr121_write(MPR121_FILTER_DELAY_COUNT_FALLING_REG, 0x02, sensor); + mpr121_write(MPR121_FILTER_DELAY_COUNT_TOUCHED_REG, 0x00, sensor); + + // == Debounce and thresholds (AN3892) ============================= + + // Debounce. Value range for each is 0~7. + // Bits 2-0, debounce touch (DT). + // Bits 6-4, debounce release (DR). + mpr121_write(MPR121_DEBOUNCE_REG, 0x00, sensor); + + // Touch and release threshold values for all electrodes. + for (uint8_t i=0; i<12; i++) { + mpr121_write(MPR121_TOUCH_THRESHOLD_REG + i * 2, 0x0F, sensor); + mpr121_write(MPR121_RELEASE_THRESHOLD_REG + i * 2, 0x0A, sensor); + } + + // Electrode Configuration Register (ECR, 0x5E). This must be the + // last register to write to because setting ELEPROX_EN and/or + // ELE_EN to non-zero puts the sensor in Run Mode. + // + // Calibration lock (CL), bits 7-6. The default on reset is 0b00 + // (CL enabled). Here I set this instead to 0b10 because this + // enables baseline tracking with initial baseline value loaded + // with the 5 high bits of the first electrode data value, which + // makes the sensor stabilise sooner. Note that ths value must + // match BVA bits in the Auto-configure Control Register above. + // + // Proximity enable (ELEPROX_EN), bits 5-4. Default, 0b00 + // (proximity detection disabled). + // + // Electrode enabled (ELE_EN), bits 3-0. Default, 0b1100 (enable + // all 12 electrodes). + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x8C, sensor); +} \ No newline at end of file diff --git a/WaccaTouchPanel/mpr121.h b/WaccaTouchPanel/mpr121.h new file mode 100644 index 0000000..f5abf5d --- /dev/null +++ b/WaccaTouchPanel/mpr121.h @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2021-2022 Antonio González + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _MPR121_H_ +#define _MPR121_H_ + +#include "pico.h" +#include "hardware/i2c.h" + +/** \file mpr121.h + * \brief Library for using an MPR121-based touch sensor with the + * Raspberry Pi Pico + * +*/ + +typedef struct mpr121_sensor { + i2c_inst_t *i2c_port; + uint8_t i2c_addr; + // uint8_t i2c_sda; + // uint8_t ic2_scl; +} mpr121_sensor_t; + +/*! \brief MPR121 register map + */ +enum mpr121_register { + MPR121_TOUCH_STATUS_REG = 0x00u, + MPR121_OUT_OF_RANGE_STATUS_0_REG = 0x02u, + MPR121_OUT_OF_RANGE_STATUS_1_REG = 0x03u, + MPR121_ELECTRODE_FILTERED_DATA_REG = 0x04u, + MPR121_BASELINE_VALUE_REG = 0x1Eu, + // Registers 0x2B ~ 0x7F are control and configuration registers + MPR121_MAX_HALF_DELTA_RISING_REG = 0x2Bu, + MPR121_NOISE_HALF_DELTA_RISING_REG = 0x2Cu, + MPR121_NOISE_COUNT_LIMIT_RISING_REG = 0x2Du, + MPR121_FILTER_DELAY_COUNT_RISING_REG = 0x2Eu, + MPR121_MAX_HALF_DELTA_FALLING_REG = 0x2Fu, + MPR121_NOISE_HALF_DELTA_FALLING_REG = 0x30u, + MPR121_NOISE_COUNT_LIMIT_FALLING_REG = 0x31u, + MPR121_FILTER_DELAY_COUNT_FALLING_REG = 0x32u, + MPR121_NOISE_HALF_DELTA_TOUCHED_REG = 0x33u, + MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG = 0x34u, + MPR121_FILTER_DELAY_COUNT_TOUCHED_REG = 0x35u, + // (ELEPROX 0x36 .. 0x40) + MPR121_TOUCH_THRESHOLD_REG = 0x41u, + MPR121_RELEASE_THRESHOLD_REG = 0x42u, + // (ELEPROX 0x59 .. 0x5A) + MPR121_DEBOUNCE_REG = 0x5Bu, + MPR121_AFE_CONFIG_REG = 0x5Cu, + MPR121_FILTER_CONFIG_REG = 0x5Du, + MPR121_ELECTRODE_CONFIG_REG = 0x5Eu, + MPR121_ELECTRODE_CURRENT_REG = 0x5Fu, + MPR121_ELECTRODE_CHARGE_TIME_REG = 0x6Cu, + MPR121_GPIO_CTRL_0_REG = 0x73u, + MPR121_GPIO_CTRL_1_REG = 0x74u, + MPR121_GPIO_DATA_REG = 0x75u, + MPR121_GPIO_DIRECTION_REG = 0x76u, + MPR121_GPIO_ENABLE_REG = 0x77u, + MPR121_GPIO_DATA_SET_REG = 0x78u, + MPR121_GPIO_DATA_CLEAR_REG = 0x79u, + MPR121_GPIO_DATA_TOGGLE_REG = 0x7Au, + MPR121_AUTOCONFIG_CONTROL_0_REG = 0x7Bu, + MPR121_AUTOCONFIG_CONTROL_1_REG = 0x7Cu, + MPR121_AUTOCONFIG_USL_REG = 0x7Du, + MPR121_AUTOCONFIG_LSL_REG = 0x7Eu, + MPR121_AUTOCONFIG_TARGET_REG = 0x7Fu, + MPR121_SOFT_RESET_REG = 0x80u +}; + +/*! \brief Initialise the MPR121 and configure registers + * + * The default parameters used here to configure the sensor are as in + * the MPR121 Quick Start Guide (AN3944). + * + * \param i2c_port The I2C instance, either i2c0 or i2c1 + * \param i2c_addr The I2C address of the MPR121 device + * \param sensor Pointer to the structure that stores the MPR121 info + */ +void mpr121_init(i2c_inst_t *i2c_port, uint8_t i2c_addr, + mpr121_sensor_t *sensor); + +/*! \brief Write a value to the specified register + * + * \param reg The register address + * \param val The value to write + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_write(enum mpr121_register reg, uint8_t val, + mpr121_sensor_t *sensor) { + uint8_t buf[] = {reg, val}; + i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, buf, 2, + false); +} + +/*! \brief Read a byte from the specified register + * + * \param reg The register address + * \param dst Pointer to buffer to receive data + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_read(enum mpr121_register reg, uint8_t *dst, + mpr121_sensor_t *sensor) { + i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, ®, 1, + true); + i2c_read_blocking(sensor->i2c_port, sensor->i2c_addr, dst, 1, + false); +} + +/*! \brief Read a 2-byte value from the specified register + * + * \param reg The register address + * \param dst Pointer to buffer to receive data + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_read16(enum mpr121_register reg, uint16_t *dst, + mpr121_sensor_t *sensor) { + uint8_t vals[2]; + i2c_write_blocking(sensor->i2c_port, sensor->i2c_addr, ®, 1, + true); + i2c_read_blocking(sensor->i2c_port, sensor->i2c_addr, vals, 2, + false); + *dst = vals[1] << 8 | vals[0]; +} + +/*! \brief Set touch and release thresholds + * + * From the MPR121 datasheet (section 5.6): + * > In a typical application, touch threshold is in the range 4--16, + * > and it is several counts larger than the release threshold. This + * > is to provide hysteresis and to prevent noise and jitter. + * + * \param touch Touch threshold in the range 0--255 + * \param release Release threshold in the range 0--255 + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_set_thresholds(uint8_t touch, uint8_t release, + mpr121_sensor_t *sensor) { + uint8_t config; + mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); + if (config != 0){ + // Stop mode + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); + } + + for (uint8_t i=0; i<12; i++) { + mpr121_write(MPR121_TOUCH_THRESHOLD_REG + i * 2, touch, sensor); + mpr121_write(MPR121_RELEASE_THRESHOLD_REG + i * 2, release, + sensor); + } + + if (config != 0){ + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); + } +} + +/*! \brief Enable only the number of electrodes specified + * + * \param nelec Number of electrodes to enable + * \param sensor Pointer to the structure that stores the MPR121 info + * + * E.g. if `nelec` is 3, electrodes 0 to 2 will be enabled; if `nelec` + * is 6, electrodes 0 to 5 will be enabled. From the datasheet: + * "Enabling specific channels will save the scan time and sensing + * field power spent on the unused channels." + */ +static void mpr121_enable_electrodes(uint8_t nelec, + mpr121_sensor_t *sensor){ + uint8_t config; + mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); + + // Clear bits 3-0, which controls the operation of the 12 + // electrodes. + config &= ~0x0f; + + // Set number of electrodes enabled + config |= nelec; + + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); +} + +/*! \brief Read the touch/release status of all 13 input channels + * + * \param dst Pointer to buffer to receive data + * \param sensor Pointer to the structure that stores the MPR121 info + * + * In the value read, bits 11-0 represent electrodes 11 to 0, + * respectively, and bit 12 is the proximity detection channel. Each + * bit represent the status of these channels: 1 if the channel is + * touched, 0 if it is released. + */ +static void mpr121_touched(uint16_t *dst, mpr121_sensor_t *sensor) { + mpr121_read16(MPR121_TOUCH_STATUS_REG, dst, sensor); + *dst &= 0x0fff; +} + +/*! \brief Determine whether an electrode has been touched + * + * \param electrode Electrode number + * \param dst Pointer to buffer to receive data + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_is_touched(uint8_t electrode, bool *dst, + mpr121_sensor_t *sensor){ + uint16_t touched; + mpr121_touched(&touched, sensor); + *dst = (bool) ((touched >> electrode) & 1); +} + +/*! \brief Read an electrode's filtered data value + * + * \param electrode Electrode number + * \param dst Pointer to buffer to receive data + * \param sensor Pointer to the structure that stores the MPR121 info + * + * The data range of the filtered data is 0 to 1024. + * \sa mpr121_baseline_value + */ +static void mpr121_filtered_data(uint8_t electrode, uint16_t *dst, + mpr121_sensor_t *sensor){ + mpr121_read16(MPR121_ELECTRODE_FILTERED_DATA_REG + (electrode * 2), + dst, sensor); + // Filtered data is 10-bit + *dst &= 0x3ff; +} + +/*! \brief Read an electrode's baseline value + * + * \param electrode Electrode number + * \param dst Pointer to buffer to receive data + * \param sensor Pointer to the structure that stores the MPR121 info + * + * From the MPR112 datasheet: + * > Along with the 10-bit electrode filtered data output, each channel + * > also has a 10-bit baseline value. These values are the output of + * > the internal baseline filter operation tracking the slow-voltage + * > variation of the background capacitance change. Touch/release + * > detection is made based on the comparison between the 10-bit + * > electrode filtered data and the 10-bit baseline value. + * + * > Although internally the baseline value is 10-bit, users can only + * > access the 8 MSB of the 10-bit baseline value through the baseline + * > value registers. + * + * \sa mpr121_filtered_data + */ +static void mpr121_baseline_value(uint8_t electrode, uint16_t *dst, + mpr121_sensor_t *sensor){ + uint8_t baseline; + mpr121_read(MPR121_BASELINE_VALUE_REG + electrode, &baseline, + sensor); + // From the datasheet: Although internally the baseline value is + // 10-bit, users can only access the 8 MSB of the 10-bit baseline + // value through the baseline value registers. The read out from the + // baseline register must be left shift two bits before comparing it + // with the 10-bit electrode data. + *dst = baseline << 2; +} + +/*! \brief Set the Max Half Delta + * + * The Max Half Delta determines the largest magnitude of variation to + * pass through the third level filter. See application note MPR121 + * Baseline System (AN3891) for details. + * + * \param rising Value in the range 1~63 + * \param falling Value in the range 1~63 + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_set_max_half_delta(uint8_t rising, uint8_t falling, + mpr121_sensor_t *sensor) { + // Read current configuration then enter stop mode + uint8_t config; + mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); + // Write MHD values + mpr121_write(MPR121_MAX_HALF_DELTA_RISING_REG, rising, sensor); + mpr121_write(MPR121_MAX_HALF_DELTA_FALLING_REG, falling, sensor); + // Re-enable electrodes + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); +} + +/*! \brief Set the Noise Half Delta + * + * The Noise Half Delta determines the incremental change when + * non-noise drift is detected. See application note MPR121 Baseline + * System (AN3891) for details. + * + * \param rising Value in the range 1~63 + * \param falling Value in the range 1~63 + * \param touched Value in the range 1~63 + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_set_noise_half_delta(uint8_t rising, uint8_t falling, + uint8_t touched, mpr121_sensor_t *sensor) { + // Read current configuration then enter stop mode + uint8_t config; + mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); + // Write NHD values + mpr121_write(MPR121_NOISE_HALF_DELTA_RISING_REG, rising, sensor); + mpr121_write(MPR121_NOISE_HALF_DELTA_FALLING_REG, falling, sensor); + mpr121_write(MPR121_NOISE_HALF_DELTA_TOUCHED_REG, touched, sensor); + // Re-enable electrodes + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); +} + +/*! \brief Set the Noise Count Limit + * + * The Noise Count Limit determines the number of samples consecutively + * greater than the Max Half Delta necessary before it can be + * determined that it is non-noise. See application note MPR121 Baseline + * System (AN3891) for details. + * + * \param rising Value in the range 0~255 + * \param falling Value in the range 0~255 + * \param touched Value in the range 0~255 + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_set_noise_count_limit(uint8_t rising, + uint8_t falling, uint8_t touched, mpr121_sensor_t *sensor) { + // Read current configuration then enter stop mode + uint8_t config; + mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); + // Write new NCL values + mpr121_write(MPR121_NOISE_COUNT_LIMIT_RISING_REG, rising, sensor); + mpr121_write(MPR121_NOISE_COUNT_LIMIT_FALLING_REG, falling, sensor); + mpr121_write(MPR121_NOISE_COUNT_LIMIT_TOUCHED_REG, touched, sensor); + // Re-enable electrodes + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); +} + +/*! \brief Set the Filter Delay Limit + * + * The Filter Delay Limit determines the rate of operation of the + * filter. A larger number makes it operate slower. See application + * note MPR121 Baseline System (AN3891) for details. + * + * \param rising Value in the range 0~255 + * \param falling Value in the range 0~255 + * \param touched Value in the range 0~255 + * \param sensor Pointer to the structure that stores the MPR121 info + */ +static void mpr121_set_filter_delay_limit(uint8_t rising, + uint8_t falling, uint8_t touched, mpr121_sensor_t *sensor) { + // Read current configuration then enter stop mode + uint8_t config; + mpr121_read(MPR121_ELECTRODE_CONFIG_REG, &config, sensor); + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, 0x00, sensor); + // Write new FDL values + mpr121_write(MPR121_FILTER_DELAY_COUNT_RISING_REG, rising, sensor); + mpr121_write(MPR121_FILTER_DELAY_COUNT_FALLING_REG, falling, + sensor); + mpr121_write(MPR121_FILTER_DELAY_COUNT_TOUCHED_REG, touched, + sensor); + // Re-enable electrodes + mpr121_write(MPR121_ELECTRODE_CONFIG_REG, config, sensor); +} + +#endif diff --git a/WaccaTouchPanel/pico_sdk_import.cmake b/WaccaTouchPanel/pico_sdk_import.cmake new file mode 100644 index 0000000..65f8a6f --- /dev/null +++ b/WaccaTouchPanel/pico_sdk_import.cmake @@ -0,0 +1,73 @@ +# This is a copy of /external/pico_sdk_import.cmake + +# This can be dropped into an external project to help locate this SDK +# It should be include()ed prior to project() + +if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) + set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) + message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) + set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) + message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") +endif () + +if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) + set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) + message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") +endif () + +set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") +set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") +set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") + +if (NOT PICO_SDK_PATH) + if (PICO_SDK_FETCH_FROM_GIT) + include(FetchContent) + set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) + if (PICO_SDK_FETCH_FROM_GIT_PATH) + get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") + endif () + # GIT_SUBMODULES_RECURSE was added in 3.17 + if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + GIT_SUBMODULES_RECURSE FALSE + ) + else () + FetchContent_Declare( + pico_sdk + GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk + GIT_TAG master + ) + endif () + + if (NOT pico_sdk) + message("Downloading Raspberry Pi Pico SDK") + FetchContent_Populate(pico_sdk) + set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) + endif () + set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) + else () + message(FATAL_ERROR + "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." + ) + endif () +endif () + +get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +if (NOT EXISTS ${PICO_SDK_PATH}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") +endif () + +set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) +if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) + message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") +endif () + +set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) + +include(${PICO_SDK_INIT_CMAKE_FILE})