commit
7f4c06ae4b
@ -0,0 +1,23 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
echo "Enter unit type identifier (empty to cancel):" |
||||||
|
read x |
||||||
|
|
||||||
|
if [ -e $x ]; then |
||||||
|
exit; |
||||||
|
fi |
||||||
|
|
||||||
|
xl="${x,,}" |
||||||
|
xu="${x^^}" |
||||||
|
|
||||||
|
for f in *.h; do mv -- "$f" "${f//tpl/$xl}"; done |
||||||
|
for f in *.c; do mv -- "$f" "${f//tpl/$xl}"; done |
||||||
|
|
||||||
|
sed "s/tpl/$xl/" -i *.h |
||||||
|
sed "s/TPL/$xu/" -i *.h |
||||||
|
sed "s/tpl/$xl/" -i *.c |
||||||
|
sed "s/TPL/$xu/" -i *.c |
||||||
|
|
||||||
|
echo "Unit $xu set up completed. Removing installer.." |
||||||
|
rm '!README.TXT' |
||||||
|
rm $0 |
@ -0,0 +1,11 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2018/02/03.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit_base.h" |
||||||
|
#include "unit_touch.h" |
||||||
|
|
||||||
|
#define TOUCH_INTERNAL |
||||||
|
#include "_touch_internal.h" |
||||||
|
|
@ -0,0 +1,124 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2018/02/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit_base.h" |
||||||
|
#include "unit_touch.h" |
||||||
|
|
||||||
|
#define TOUCH_INTERNAL |
||||||
|
|
||||||
|
#include "_touch_internal.h" |
||||||
|
|
||||||
|
#define DIS_TIME 1 |
||||||
|
|
||||||
|
static void startNextPhase(struct priv *priv); |
||||||
|
|
||||||
|
void UTOUCH_HandleIrq(void *arg) |
||||||
|
{ |
||||||
|
Unit *unit = arg; |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
if (TSC->ISR & TSC_ISR_MCEF) { |
||||||
|
priv->status = UTSC_STATUS_FAIL; |
||||||
|
dbg_touch("TSC Failure."); |
||||||
|
TSC->ICR = TSC_ICR_EOAIC | TSC_ICR_MCEIC; |
||||||
|
} |
||||||
|
|
||||||
|
if (TSC->ISR & TSC_ISR_EOAF) { |
||||||
|
TSC->ICR = TSC_ICR_EOAIC; |
||||||
|
// assert_param((TSC->IOGCSR>>16) == priv->groups_phase[priv->next_phase]);
|
||||||
|
|
||||||
|
// Store captured data
|
||||||
|
const uint32_t chmask = TSC->IOCCR; |
||||||
|
for (int i = 0; i < 32; i++) { |
||||||
|
if (chmask & (1<<i)) { |
||||||
|
priv->readouts[i] = (uint16_t) (TSC->IOGXCR[i >> 2] & 0x3FFF); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
priv->next_phase++; |
||||||
|
|
||||||
|
if (!priv->cfg.interlaced) { |
||||||
|
// check if we've run out of existing or populated groups
|
||||||
|
if (priv->next_phase == 3 || priv->groups_phase[priv->next_phase] == 0) { |
||||||
|
priv->next_phase = 0; |
||||||
|
priv->status = UTSC_STATUS_READY; |
||||||
|
// we'll stay in READY after the first loop until an error occurs or it's re-inited
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
TSC->CR &= ~TSC_CR_IODEF; // pull low - discharge
|
||||||
|
} |
||||||
|
|
||||||
|
priv->ongoing = false; |
||||||
|
priv->discharge_delay = DIS_TIME; |
||||||
|
} |
||||||
|
|
||||||
|
#if TSC_DEBUG |
||||||
|
static volatile uint32_t xcnt=0; |
||||||
|
#endif |
||||||
|
|
||||||
|
void UTOUCH_updateTick(Unit *unit) |
||||||
|
{ |
||||||
|
#if TSC_DEBUG |
||||||
|
xcnt++; |
||||||
|
#endif |
||||||
|
|
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
if (priv->ongoing) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (priv->discharge_delay > 0) { |
||||||
|
priv->discharge_delay--; |
||||||
|
} else { |
||||||
|
startNextPhase(priv); |
||||||
|
} |
||||||
|
|
||||||
|
#if TSC_DEBUG |
||||||
|
if(xcnt >= 250) { |
||||||
|
xcnt=0; |
||||||
|
PRINTF("> "); |
||||||
|
for (int i = 0; i < 32; i++) { |
||||||
|
if (priv->all_channels_mask & (1<<i)) { |
||||||
|
PRINTF("%d ", (int)priv->readouts[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
PRINTF("\r\n"); |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static void startNextPhase(struct priv *priv) |
||||||
|
{ |
||||||
|
if (priv->all_channels_mask == 0) return; |
||||||
|
|
||||||
|
if (priv->cfg.interlaced) { |
||||||
|
// Find the next non-zero bit, wrap around if needed
|
||||||
|
while ((priv->all_channels_mask & (1<<priv->next_phase))==0) { |
||||||
|
priv->next_phase++; |
||||||
|
if (priv->next_phase == 32) { |
||||||
|
priv->next_phase = 0; |
||||||
|
priv->status = UTSC_STATUS_READY; |
||||||
|
} |
||||||
|
} |
||||||
|
TSC->IOGCSR = (uint32_t) (1 << (priv->next_phase >> 2)); // phase divided by 4
|
||||||
|
TSC->IOCCR = (uint32_t) (1 << priv->next_phase); |
||||||
|
|
||||||
|
// interlaced - float neighbouring electrodes
|
||||||
|
TSC->CR |= TSC_CR_IODEF; |
||||||
|
} else { |
||||||
|
TSC->IOGCSR = priv->groups_phase[priv->next_phase]; |
||||||
|
TSC->IOCCR = priv->channels_phase[priv->next_phase]; |
||||||
|
|
||||||
|
// separate - keep neighbouring electrodes at GND
|
||||||
|
} |
||||||
|
|
||||||
|
TSC->ICR = TSC_ICR_EOAIC | TSC_ICR_MCEIC; |
||||||
|
|
||||||
|
// Go!
|
||||||
|
priv->ongoing = true; |
||||||
|
TSC->CR |= TSC_CR_START; |
||||||
|
} |
@ -0,0 +1,215 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2018/02/03.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit_base.h" |
||||||
|
|
||||||
|
#define TOUCH_INTERNAL |
||||||
|
|
||||||
|
#include "_touch_internal.h" |
||||||
|
|
||||||
|
/** Allocate data structure and set defaults */ |
||||||
|
error_t UTOUCH_preInit(Unit *unit) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv)); |
||||||
|
if (priv == NULL) return E_OUT_OF_MEM; |
||||||
|
|
||||||
|
priv->cfg.charge_time = 2; |
||||||
|
priv->cfg.drain_time = 2; |
||||||
|
priv->cfg.spread_deviation = 0; |
||||||
|
priv->cfg.ss_presc = 1; |
||||||
|
priv->cfg.pg_presc = 32; |
||||||
|
priv->cfg.sense_timeout = 7; |
||||||
|
memset(priv->cfg.group_scaps, 0, 8); |
||||||
|
memset(priv->cfg.group_channels, 0, 8); |
||||||
|
|
||||||
|
return E_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/** Finalize unit set-up */ |
||||||
|
error_t UTOUCH_init(Unit *unit) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
unit->tick_interval = 1; // sample every 1 ms
|
||||||
|
|
||||||
|
TRY(rsc_claim(unit, R_TSC)); |
||||||
|
|
||||||
|
// simple bound checks, just clamp without error
|
||||||
|
if (priv->cfg.charge_time > 16) priv->cfg.charge_time = 16; |
||||||
|
if (priv->cfg.charge_time < 1) priv->cfg.charge_time = 1; |
||||||
|
|
||||||
|
if (priv->cfg.drain_time > 16) priv->cfg.drain_time = 16; |
||||||
|
if (priv->cfg.drain_time < 1) priv->cfg.drain_time = 1; |
||||||
|
|
||||||
|
if (priv->cfg.spread_deviation > 128) priv->cfg.drain_time = 128; |
||||||
|
|
||||||
|
if (priv->cfg.ss_presc > 2) priv->cfg.ss_presc = 2; |
||||||
|
if (priv->cfg.ss_presc < 1) priv->cfg.ss_presc = 1; |
||||||
|
|
||||||
|
if (priv->cfg.sense_timeout > 7) priv->cfg.sense_timeout = 7; |
||||||
|
if (priv->cfg.sense_timeout < 1) priv->cfg.sense_timeout = 1; |
||||||
|
|
||||||
|
uint8_t tmppgpresc = priv->cfg.pg_presc; |
||||||
|
if (tmppgpresc == 0) return E_BAD_CONFIG; |
||||||
|
uint8_t pgpresc_reg = 0; |
||||||
|
while ((tmppgpresc & 1) == 0 && tmppgpresc != 0) { |
||||||
|
pgpresc_reg++; |
||||||
|
tmppgpresc >>= 1; |
||||||
|
} |
||||||
|
if (tmppgpresc != 1 || pgpresc_reg > 7) { |
||||||
|
dbg("Bad pgpresc"); |
||||||
|
return E_BAD_CONFIG; // TODO better reporting
|
||||||
|
} |
||||||
|
|
||||||
|
if ((pgpresc_reg==0 && priv->cfg.drain_time<=2) || (pgpresc_reg==1 && priv->cfg.drain_time==0)) { |
||||||
|
dbg("Illegal PGPSC vs CTPL"); |
||||||
|
return E_BAD_CONFIG; |
||||||
|
} |
||||||
|
|
||||||
|
// enable clock
|
||||||
|
hw_periph_clock_enable(TSC); |
||||||
|
// reset
|
||||||
|
__HAL_RCC_TSC_FORCE_RESET(); |
||||||
|
__HAL_RCC_TSC_RELEASE_RESET(); |
||||||
|
|
||||||
|
priv->all_channels_mask = 0; |
||||||
|
|
||||||
|
for (int gi = 0; gi < 8; gi++) { |
||||||
|
const uint8_t cap = priv->cfg.group_scaps[gi]; |
||||||
|
const uint8_t ch = priv->cfg.group_channels[gi]; |
||||||
|
|
||||||
|
if (cap == 0) { |
||||||
|
if (ch != 0) { |
||||||
|
dbg_touch("TSC group %d has no cap!", (int) (gi + 1)); |
||||||
|
return E_BAD_CONFIG; |
||||||
|
} |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (ch == 0) continue; // if no channels, don't bother setting up anything
|
||||||
|
|
||||||
|
if (cap != 2 && cap != 4 && cap != 8 && cap != 16) { |
||||||
|
dbg_touch("TSC group %d has more than 1 cap!", (int) (gi + 1)); |
||||||
|
return E_BAD_CONFIG; |
||||||
|
} |
||||||
|
|
||||||
|
if (cap & ch) { |
||||||
|
dbg_touch("TSC pin can't be both channel and cap! (gpr %d)", (int) (gi + 1)); |
||||||
|
return E_BAD_CONFIG; |
||||||
|
} |
||||||
|
|
||||||
|
// This is a loop through the pins in a group gi
|
||||||
|
int phasenum = 0; |
||||||
|
for (int pi = 0; pi < 4; pi++) { |
||||||
|
// pin numbers are 1-based in the config
|
||||||
|
const bool iscap = 0 != (cap & (2 << pi)); |
||||||
|
const bool isch = 0 != (ch & (2 << pi)); |
||||||
|
|
||||||
|
if (!iscap && !isch) continue; |
||||||
|
|
||||||
|
Resource r = utouch_group_rscs[gi][pi]; |
||||||
|
TRY(rsc_claim(unit, r)); |
||||||
|
|
||||||
|
GPIO_TypeDef *port; |
||||||
|
uint32_t ll; |
||||||
|
assert_param(hw_pinrsc2ll(r, &port, &ll)); |
||||||
|
LL_GPIO_SetPinOutputType(port, ll, isch ? LL_GPIO_OUTPUT_PUSHPULL : LL_GPIO_OUTPUT_OPENDRAIN); |
||||||
|
|
||||||
|
// 7 and 8 (1-based) use AF1, else AF3
|
||||||
|
TRY(hw_configure_gpiorsc_af(r, gi >= 6 ? LL_GPIO_AF_1 : LL_GPIO_AF_3)); |
||||||
|
|
||||||
|
uint32_t bit = (uint32_t) (1 << (gi * 4 + pi)); |
||||||
|
|
||||||
|
if (iscap) { |
||||||
|
dbg_touch("TSC cap @ %s", rsc_get_name(r)); |
||||||
|
// Sampling cap
|
||||||
|
TSC->IOSCR |= bit; |
||||||
|
|
||||||
|
// Disable pin hysteresis (causes noise)
|
||||||
|
TSC->IOHCR ^= bit; |
||||||
|
} |
||||||
|
else { |
||||||
|
dbg_touch("TSC ch @ %s", rsc_get_name(r)); |
||||||
|
|
||||||
|
if (priv->cfg.interlaced) { |
||||||
|
// interlaced - only update the mask beforehand
|
||||||
|
priv->all_channels_mask |= bit; |
||||||
|
} else { |
||||||
|
// channels are configured individually when read.
|
||||||
|
// we prepare bitmaps to use for the read groups (all can be read in at most 3 steps)
|
||||||
|
priv->channels_phase[phasenum] |= bit; // this is used for the channel selection register
|
||||||
|
priv->groups_phase[phasenum] |= 1 << gi; // this will be used for the group enable register, if all 0, this and any following phases are unused.
|
||||||
|
phasenum++; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// common TSC config
|
||||||
|
TSC->CR = |
||||||
|
((priv->cfg.charge_time - 1) << TSC_CR_CTPH_Pos) | |
||||||
|
((priv->cfg.drain_time - 1) << TSC_CR_CTPL_Pos) | |
||||||
|
((priv->cfg.ss_presc - 1) << TSC_CR_SSPSC_Pos) | |
||||||
|
(pgpresc_reg << TSC_CR_PGPSC_Pos) | |
||||||
|
((priv->cfg.sense_timeout - 1) << TSC_CR_MCV_Pos) | |
||||||
|
TSC_CR_TSCE; |
||||||
|
|
||||||
|
if (priv->cfg.spread_deviation > 0) { |
||||||
|
TSC->CR |= ((priv->cfg.spread_deviation - 1) << TSC_CR_SSD_Pos) | TSC_CR_SSE; |
||||||
|
} |
||||||
|
|
||||||
|
dbg_touch("CR = %08x, ht is %d, lt is %d", (int)TSC->CR, |
||||||
|
(int)priv->cfg.charge_time, |
||||||
|
(int)priv->cfg.drain_time); |
||||||
|
|
||||||
|
// iofloat is used for discharging
|
||||||
|
|
||||||
|
// Enable the interrupts
|
||||||
|
TSC->IER = TSC_IER_EOAIE | TSC_IER_MCEIE; |
||||||
|
irqd_attach(TSC, UTOUCH_HandleIrq, unit); |
||||||
|
|
||||||
|
if (!priv->cfg.interlaced) { |
||||||
|
dbg_touch("TSC phases:"); |
||||||
|
for (int i = 0; i < 3; i++) { |
||||||
|
priv->all_channels_mask |= priv->channels_phase[i]; |
||||||
|
|
||||||
|
dbg_touch(" %d: ch %08"PRIx32", g %02"PRIx32, |
||||||
|
i + 1, |
||||||
|
priv->channels_phase[i], |
||||||
|
(uint32_t) priv->groups_phase[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
priv->status = UTSC_STATUS_BUSY; // first loop ...
|
||||||
|
priv->next_phase = 0; |
||||||
|
|
||||||
|
// starts in the tick callback
|
||||||
|
|
||||||
|
return E_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** Tear down the unit */ |
||||||
|
void UTOUCH_deInit(Unit *unit) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
// de-init peripherals
|
||||||
|
if (unit->status == E_SUCCESS) { |
||||||
|
hw_periph_clock_disable(TSC); |
||||||
|
// clear all registers to their default values
|
||||||
|
__HAL_RCC_TSC_FORCE_RESET(); |
||||||
|
__HAL_RCC_TSC_RELEASE_RESET(); |
||||||
|
|
||||||
|
irqd_detach(TSC, UTOUCH_HandleIrq); |
||||||
|
} |
||||||
|
|
||||||
|
// Release all resources, deinit pins
|
||||||
|
rsc_teardown(unit); |
||||||
|
|
||||||
|
// Free memory
|
||||||
|
free_ck(unit->data); |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2018/02/03.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GEX_F072_TOUCH_INTERNAL_H |
||||||
|
#define GEX_F072_TOUCH_INTERNAL_H |
||||||
|
|
||||||
|
#ifndef TOUCH_INTERNAL |
||||||
|
#error bad include! |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "unit_base.h" |
||||||
|
|
||||||
|
#define TSC_DEBUG 0 |
||||||
|
|
||||||
|
#if TSC_DEBUG |
||||||
|
#define dbg_touch(f,...) dbg(f,##__VA_ARGS__) |
||||||
|
#else |
||||||
|
#define dbg_touch(f,...) do{}while(0) |
||||||
|
#endif |
||||||
|
|
||||||
|
enum utsc_status { |
||||||
|
UTSC_STATUS_BUSY = 0, |
||||||
|
UTSC_STATUS_READY = 1, |
||||||
|
UTSC_STATUS_FAIL = 2 |
||||||
|
}; |
||||||
|
|
||||||
|
/** Private data structure */ |
||||||
|
struct priv { |
||||||
|
// settings
|
||||||
|
struct { |
||||||
|
uint8_t charge_time; // 1-16 -> 0..15
|
||||||
|
uint8_t drain_time; // 1-16 -> 0..15
|
||||||
|
uint8_t spread_deviation; // 1-128, 0=off ... 0-127, 0 sets 0 to SSE
|
||||||
|
uint8_t ss_presc; // 1-2 -> 0..1
|
||||||
|
uint8_t pg_presc; // 1,2,4,8,16,32,64,128 -> 0..7 when writing to the periph
|
||||||
|
uint8_t sense_timeout; // 1-7 -> 0..6 hex when writing to the periph
|
||||||
|
// the schmitts must be disabled on all used channels, restored to 0xFFFF on deinit
|
||||||
|
uint8_t group_scaps[8]; |
||||||
|
uint8_t group_channels[8]; |
||||||
|
bool interlaced; |
||||||
|
} cfg; |
||||||
|
|
||||||
|
uint8_t next_phase; |
||||||
|
uint8_t discharge_delay; |
||||||
|
uint32_t channels_phase[3]; |
||||||
|
uint8_t groups_phase[3]; |
||||||
|
uint16_t readouts[32]; |
||||||
|
uint32_t all_channels_mask; |
||||||
|
bool ongoing; |
||||||
|
|
||||||
|
enum utsc_status status; |
||||||
|
}; |
||||||
|
|
||||||
|
extern const char *utouch_group_labels[8]; |
||||||
|
extern const Resource utouch_group_rscs[8][4]; |
||||||
|
|
||||||
|
/** Allocate data structure and set defaults */ |
||||||
|
error_t UTOUCH_preInit(Unit *unit); |
||||||
|
|
||||||
|
/** Load from a binary buffer stored in Flash */ |
||||||
|
void UTOUCH_loadBinary(Unit *unit, PayloadParser *pp); |
||||||
|
|
||||||
|
/** Write to a binary buffer for storing in Flash */ |
||||||
|
void UTOUCH_writeBinary(Unit *unit, PayloadBuilder *pb); |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Parse a key-value pair from the INI file */ |
||||||
|
error_t UTOUCH_loadIni(Unit *unit, const char *key, const char *value); |
||||||
|
|
||||||
|
/** Generate INI file section for the unit */ |
||||||
|
void UTOUCH_writeIni(Unit *unit, IniWriter *iw); |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Finalize unit set-up */ |
||||||
|
error_t UTOUCH_init(Unit *unit); |
||||||
|
|
||||||
|
/** Tear down the unit */ |
||||||
|
void UTOUCH_deInit(Unit *unit); |
||||||
|
|
||||||
|
void UTOUCH_updateTick(Unit *unit); |
||||||
|
|
||||||
|
void UTOUCH_HandleIrq(void *arg); |
||||||
|
|
||||||
|
#endif //GEX_F072_TOUCH_INTERNAL_H
|
@ -0,0 +1,169 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2018/02/03.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "platform.h" |
||||||
|
#include "unit_base.h" |
||||||
|
|
||||||
|
#define TOUCH_INTERNAL |
||||||
|
#include "_touch_internal.h" |
||||||
|
|
||||||
|
const char *utouch_group_labels[8] = { |
||||||
|
"1:A0, 2:A1, 3:A2, 4:A3", |
||||||
|
"1:A4, 2:A5, 3:A6, 4:A7", |
||||||
|
"1:C5, 2:B0, 3:B1, 4:B2", |
||||||
|
"1:A9, 2:A10, 3:A11, 4:A12", |
||||||
|
"1:B3, 2:B4, 3:B6, 4:B7", |
||||||
|
"1:B11, 2:B12, 3:B13, 4:B14", |
||||||
|
"1:E2, 2:E3, 3:E4, 4:E5", |
||||||
|
"1:D12, 2:D13, 3:D14, 4:D15", |
||||||
|
}; |
||||||
|
|
||||||
|
const Resource utouch_group_rscs[8][4] = { |
||||||
|
{R_PA0, R_PA1, R_PA2, R_PA3}, |
||||||
|
{R_PA4, R_PA5, R_PA6, R_PA7}, |
||||||
|
{R_PC5, R_PB0, R_PB1, R_PB2}, |
||||||
|
{R_PA9, R_PA10, R_PA11, R_PA12}, |
||||||
|
{R_PB3, R_PB4, R_PB6, R_PB7}, |
||||||
|
{R_PB11, R_PB12, R_PB13, R_PB14}, |
||||||
|
{R_PE2, R_PE3, R_PE4, R_PE5}, |
||||||
|
{R_PD12, R_PD13, R_PD14, R_PD15}, |
||||||
|
}; |
||||||
|
|
||||||
|
/** Load from a binary buffer stored in Flash */ |
||||||
|
void UTOUCH_loadBinary(Unit *unit, PayloadParser *pp) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
uint8_t version = pp_u8(pp); |
||||||
|
(void)version; |
||||||
|
|
||||||
|
priv->cfg.charge_time = pp_u8(pp); |
||||||
|
priv->cfg.drain_time = pp_u8(pp); |
||||||
|
priv->cfg.spread_deviation = pp_u8(pp); |
||||||
|
priv->cfg.ss_presc = pp_u8(pp); |
||||||
|
priv->cfg.pg_presc = pp_u8(pp); |
||||||
|
priv->cfg.sense_timeout = pp_u8(pp); |
||||||
|
pp_buf(pp, priv->cfg.group_scaps, 8); |
||||||
|
pp_buf(pp, priv->cfg.group_channels, 8); |
||||||
|
|
||||||
|
if (version >= 1) { |
||||||
|
priv->cfg.interlaced = pp_bool(pp); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** Write to a binary buffer for storing in Flash */ |
||||||
|
void UTOUCH_writeBinary(Unit *unit, PayloadBuilder *pb) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
pb_u8(pb, 1); // version
|
||||||
|
|
||||||
|
pb_u8(pb, priv->cfg.charge_time); |
||||||
|
pb_u8(pb, priv->cfg.drain_time); |
||||||
|
pb_u8(pb, priv->cfg.spread_deviation); |
||||||
|
pb_u8(pb, priv->cfg.ss_presc); |
||||||
|
pb_u8(pb, priv->cfg.pg_presc); |
||||||
|
pb_u8(pb, priv->cfg.sense_timeout); |
||||||
|
pb_buf(pb, priv->cfg.group_scaps, 8); |
||||||
|
pb_buf(pb, priv->cfg.group_channels, 8); |
||||||
|
pb_bool(pb, priv->cfg.interlaced); |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Parse a key-value pair from the INI file */ |
||||||
|
error_t UTOUCH_loadIni(Unit *unit, const char *key, const char *value) |
||||||
|
{ |
||||||
|
bool suc = true; |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
if (streq(key, "charge-time")) { |
||||||
|
priv->cfg.charge_time = cfg_u8_parse(value, &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "drain-time")) { |
||||||
|
priv->cfg.drain_time = cfg_u8_parse(value, &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "ss-deviation")) { |
||||||
|
priv->cfg.spread_deviation = cfg_u8_parse(value, &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "ss-clock-prediv")) { |
||||||
|
priv->cfg.ss_presc = cfg_u8_parse(value, &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "pg-clock-prediv")) { |
||||||
|
priv->cfg.pg_presc = cfg_u8_parse(value, &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "sense-timeout")) { |
||||||
|
priv->cfg.sense_timeout = cfg_u8_parse(value, &suc); |
||||||
|
} |
||||||
|
else if (streq(key, "interlaced-pads")) { |
||||||
|
priv->cfg.interlaced = cfg_bool_parse(value, &suc); |
||||||
|
} |
||||||
|
else { |
||||||
|
volatile char namebuf[10]; // must be volatile or gcc optimizes out the second compare and fucks it up
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++) { // skip 7,8
|
||||||
|
SPRINTF(namebuf, "g%d_cap", i+1); |
||||||
|
if (streq(key, namebuf)) { |
||||||
|
priv->cfg.group_scaps[i] = (uint8_t) cfg_pinmask_parse(value, &suc); |
||||||
|
goto matched; |
||||||
|
} |
||||||
|
|
||||||
|
SPRINTF(namebuf, "g%d_ch", i+1); |
||||||
|
if (streq(key, namebuf)) { |
||||||
|
priv->cfg.group_channels[i] = (uint8_t) cfg_pinmask_parse(value, &suc); |
||||||
|
goto matched; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return E_BAD_KEY; |
||||||
|
} |
||||||
|
|
||||||
|
matched: |
||||||
|
if (!suc) return E_BAD_VALUE; |
||||||
|
return E_SUCCESS; |
||||||
|
} |
||||||
|
|
||||||
|
/** Generate INI file section for the unit */ |
||||||
|
void UTOUCH_writeIni(Unit *unit, IniWriter *iw) |
||||||
|
{ |
||||||
|
struct priv *priv = unit->data; |
||||||
|
|
||||||
|
iw_comment(iw, "This unit utilizes the touch sensing controller."); |
||||||
|
iw_comment(iw, "See the reference manual for details about its function."); |
||||||
|
iw_cmt_newline(iw); |
||||||
|
|
||||||
|
iw_comment(iw, "Pulse generator clock prescaller (1,2,4,...,128)"); |
||||||
|
iw_entry(iw, "pg-clock-prediv", "%d", (int)priv->cfg.pg_presc); |
||||||
|
iw_comment(iw, "Sense pad charging time (1-16)"); |
||||||
|
iw_entry(iw, "charge-time", "%d", (int)priv->cfg.charge_time); |
||||||
|
iw_comment(iw, "Charge transfer time (1-16)"); |
||||||
|
iw_entry(iw, "drain-time", "%d", (int)priv->cfg.drain_time); |
||||||
|
iw_comment(iw, "Measurement timeout (1-7)"); |
||||||
|
iw_entry(iw, "sense-timeout", "%d", (int)priv->cfg.sense_timeout); |
||||||
|
|
||||||
|
iw_cmt_newline(iw); |
||||||
|
iw_comment(iw, "Spread spectrum max deviation (0-128,0=off)"); |
||||||
|
iw_entry(iw, "ss-deviation", "%d", (int)priv->cfg.spread_deviation); |
||||||
|
iw_comment(iw, "Spreading clock prescaller (1,2)"); |
||||||
|
iw_entry(iw, "ss-clock-prediv", "%d", (int)priv->cfg.ss_presc); |
||||||
|
|
||||||
|
iw_cmt_newline(iw); |
||||||
|
iw_comment(iw, "Optimize for interlaced pads (individual sampling with others floating)"); |
||||||
|
iw_entry(iw, "interlaced-pads", str_yn(priv->cfg.interlaced)); |
||||||
|
|
||||||
|
iw_cmt_newline(iw); |
||||||
|
iw_comment(iw, "Each used group must have 1 sampling capacitor and 1-3 channels."); |
||||||
|
iw_comment(iw, "Channels are numbered 1,2,3,4"); |
||||||
|
iw_cmt_newline(iw); |
||||||
|
|
||||||
|
char namebuf[10]; |
||||||
|
for (int i = 0; i < 6; i++) { // skip 7,8
|
||||||
|
iw_commentf(iw, "Group%d - %s", i+1, utouch_group_labels[i]); |
||||||
|
SPRINTF(namebuf, "g%d_cap", i+1); |
||||||
|
iw_entry(iw, namebuf, cfg_pinmask_encode(priv->cfg.group_scaps[i], unit_tmp512, true)); |
||||||
|
SPRINTF(namebuf, "g%d_ch", i+1); |
||||||
|
iw_entry(iw, namebuf, cfg_pinmask_encode(priv->cfg.group_channels[i], unit_tmp512, true)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
@ -0,0 +1,59 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "unit_base.h" |
||||||
|
#include "unit_touch.h" |
||||||
|
|
||||||
|
#define TOUCH_INTERNAL |
||||||
|
#include "_touch_internal.h" |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
enum TouchCmd_ { |
||||||
|
CMD_READ=0 |
||||||
|
}; |
||||||
|
|
||||||
|
/** Handle a request message */ |
||||||
|
static error_t UTOUCH_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) |
||||||
|
{ |
||||||
|
struct priv* priv = unit->data; |
||||||
|
|
||||||
|
switch (command) { |
||||||
|
case CMD_READ: |
||||||
|
if (priv->status == UTSC_STATUS_BUSY) return E_BUSY; |
||||||
|
if (priv->status == UTSC_STATUS_FAIL) return E_HW_TIMEOUT; |
||||||
|
|
||||||
|
PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL); |
||||||
|
for (int i = 0; i < 32; i++) { |
||||||
|
if (priv->all_channels_mask & (1<<i)) { |
||||||
|
pb_u16(&pb, priv->readouts[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
com_respond_pb(frame_id, MSG_SUCCESS, &pb); |
||||||
|
return E_SUCCESS; |
||||||
|
|
||||||
|
default: |
||||||
|
return E_UNKNOWN_COMMAND; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Unit template */ |
||||||
|
const UnitDriver UNIT_TOUCH = { |
||||||
|
.name = "TOUCH", |
||||||
|
.description = "Capacitive touch sensing", |
||||||
|
// Settings
|
||||||
|
.preInit = UTOUCH_preInit, |
||||||
|
.cfgLoadBinary = UTOUCH_loadBinary, |
||||||
|
.cfgWriteBinary = UTOUCH_writeBinary, |
||||||
|
.cfgLoadIni = UTOUCH_loadIni, |
||||||
|
.cfgWriteIni = UTOUCH_writeIni, |
||||||
|
// Init
|
||||||
|
.init = UTOUCH_init, |
||||||
|
.deInit = UTOUCH_deInit, |
||||||
|
// Function
|
||||||
|
.handleRequest = UTOUCH_handleRequest, |
||||||
|
.updateTick = UTOUCH_updateTick, |
||||||
|
}; |
@ -0,0 +1,16 @@ |
|||||||
|
//
|
||||||
|
// Created by MightyPork on 2017/11/25.
|
||||||
|
//
|
||||||
|
// Digital input unit; single or multiple pin read access on one port (A-F)
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef U_TOUCH_H |
||||||
|
#define U_TOUCH_H |
||||||
|
|
||||||
|
#include "unit.h" |
||||||
|
|
||||||
|
extern const UnitDriver UNIT_TOUCH; |
||||||
|
|
||||||
|
// UU_ prototypes
|
||||||
|
|
||||||
|
#endif //U_TOUCH_H
|
Loading…
Reference in new issue