Initial commit with V1 hardware

This commit is contained in:
FizzyApple12
2022-07-01 04:27:38 -04:00
commit 569d556b4e
24 changed files with 1474 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
WaccaProtocolTranslator/build/
WaccaProtocolTranslator/.vscode/
WaccaTouchPanel/build/
WaccaTouchPanel/.vscode/
/**/OldVersions/
+3
View File
@@ -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!
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+32
View File
@@ -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)
+5
View File
@@ -0,0 +1,5 @@
if not exist "build" mkdir build
cd build
cmake -G "Unix Makefiles" ..
make wacca_protocol_translator
cd ..
+5
View File
@@ -0,0 +1,5 @@
mkdir -p build
cd build
cmake -G "Unix Makefiles" ..
make wacca_protocol_translator
cd ..
+85
View File
@@ -0,0 +1,85 @@
#include <stdbool.h>
#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;
}
@@ -0,0 +1,73 @@
# This is a copy of <PICO_SDK_PATH>/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})
+40
View File
@@ -0,0 +1,40 @@
#include <stdlib.h>
#include <stdbool.h>
#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;
}
+18
View File
@@ -0,0 +1,18 @@
#ifndef _QUEUE_
#define _QUEUE_
#include <stdlib.h>
#include <stdbool.h>
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
+265
View File
@@ -0,0 +1,265 @@
#include "waccaserial.h"
#include "pico.h"
#include "pico/time.h"
#include "pico/multicore.h"
#include "hardware/i2c.h"
#include <stdbool.h>
#include <string.h>
#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);
}
+79
View File
@@ -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 <stdbool.h>
#include <string.h>
#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
+27
View File
@@ -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)
+5
View File
@@ -0,0 +1,5 @@
if not exist "build" mkdir build
cd build
cmake -G "Unix Makefiles" ..
make wacca_touch_panel
cd ..
+5
View File
@@ -0,0 +1,5 @@
mkdir -p build
cd build
cmake -G "Unix Makefiles" ..
make wacca_touch_panel
cd ..
+217
View File
@@ -0,0 +1,217 @@
#include <stdio.h>
#include <stdbool.h>
#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;
}
+170
View File
@@ -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);
}
+363
View File
@@ -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, &reg, 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, &reg, 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
+73
View File
@@ -0,0 +1,73 @@
# This is a copy of <PICO_SDK_PATH>/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})