diff --git a/gex.mk b/gex.mk index 2066ea0..46d79b7 100644 --- a/gex.mk +++ b/gex.mk @@ -96,7 +96,7 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \ -DUSE_STACK_MONITOR=1 \ -DUSE_DEBUG_UART=1 \ -DDEBUG_MALLOC=0 \ - -DDEBUG_RSC=1 + -DDEBUG_RSC=0 endif diff --git a/platform/irq_dispatcher.c b/platform/irq_dispatcher.c index 93ff1cf..01c357f 100644 --- a/platform/irq_dispatcher.c +++ b/platform/irq_dispatcher.c @@ -69,6 +69,7 @@ static struct callbacks_ { struct cbslot tim16; struct cbslot adc1; + struct cbslot tsc; // XXX add more callbacks here when needed } callbacks; @@ -92,7 +93,8 @@ void irqd_init(void) HAL_NVIC_SetPriority(EXTI2_3_IRQn, 2, 0); HAL_NVIC_SetPriority(EXTI4_15_IRQn, 2, 0); -// NVIC_EnableIRQ(TSC_IRQn); /*!< Touch Sensing Controller Interrupts */ + NVIC_EnableIRQ(TSC_IRQn); /*!< Touch Sensing Controller Interrupts */ + HAL_NVIC_SetPriority(TSC_IRQn, 2, 0); NVIC_EnableIRQ(DMA1_Channel1_IRQn); /*!< DMA1 Channel 1 Interrupt */ NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); /*!< DMA1 Channel 2 and Channel 3 Interrupt */ @@ -178,6 +180,8 @@ static struct cbslot *get_slot_for_periph(void *periph) else if (periph == TIM16) slot = &callbacks.tim16; // 17 - used by timebase + else if (periph == TSC) slot = &callbacks.tsc; + else if (periph == ADC1) slot = &callbacks.adc1; else if (periph >= EXTIS[0] && periph <= EXTIS[15]) { @@ -352,6 +356,11 @@ void ADC1_COMP_IRQHandler(void) CALL_IRQ_HANDLER(callbacks.adc1); } +void TSC_IRQHandler(void) +{ + CALL_IRQ_HANDLER(callbacks.tsc); +} + // other ISRs... diff --git a/units/touch/_touch_core.c b/units/touch/_touch_core.c new file mode 100644 index 0000000..8c1cf7b --- /dev/null +++ b/units/touch/_touch_core.c @@ -0,0 +1,111 @@ +// +// 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 = priv->channels_phase[priv->next_phase]; + for (int i = 0; i < 32; i++) { + if (chmask & (1<readouts[i] = (uint16_t) (TSC->IOGXCR[i >> 2] & 0x3FFF); + } + } + + priv->next_phase++; + // 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<readouts[i]); + } + } + PRINTF("\r\n"); + } +#endif +} + +static void startNextPhase(struct priv *priv) +{ + if (priv->next_phase == 0 && priv->groups_phase[0] == 0) { + // no groups are configured + return; + } + + TSC->IOGCSR = priv->groups_phase[priv->next_phase]; + TSC->IOCCR = priv->channels_phase[priv->next_phase]; + TSC->ICR = TSC_ICR_EOAIC | TSC_ICR_MCEIC; + + if (priv->cfg.interlaced) { + TSC->CR |= TSC_CR_IODEF; + // floaty (must be used for interlaced pads) + // note: interlaced pads also need to be sampled individually + } + + // Go! + priv->ongoing = true; + TSC->CR |= TSC_CR_START; +} diff --git a/units/touch/_touch_init.c b/units/touch/_touch_init.c index 51ee6f0..965d227 100644 --- a/units/touch/_touch_init.c +++ b/units/touch/_touch_init.c @@ -2,11 +2,11 @@ // Created by MightyPork on 2018/02/03. // -#include #include "platform.h" #include "unit_base.h" #define TOUCH_INTERNAL + #include "_touch_internal.h" /** Allocate data structure and set defaults */ @@ -33,10 +33,47 @@ 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(); for (int gi = 0; gi < 8; gi++) { const uint8_t cap = priv->cfg.group_scaps[gi]; @@ -44,7 +81,7 @@ error_t UTOUCH_init(Unit *unit) if (cap == 0) { if (ch != 0) { - dbg_touch("TSC group %d has no cap!", (int)(gi+1)); + dbg_touch("TSC group %d has no cap!", (int) (gi + 1)); return E_BAD_CONFIG; } continue; @@ -53,51 +90,89 @@ error_t UTOUCH_init(Unit *unit) 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)); + 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)); + dbg_touch("TSC pin can't be both channel and cap! (gpr %d)", (int) (gi + 1)); return E_BAD_CONFIG; } - int chnum = 0; + // 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) { - Resource r = utouch_group_rscs[gi][pi]; - TRY(rsc_claim(unit, r)); - // 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)); + if (!iscap && !isch) continue; - if (iscap) dbg_touch("TSC *cap @ %s", rsc_get_name(r)); - else dbg_touch("TSC -ch @ %s", rsc_get_name(r)); - } + 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)); - // Sampling cap if (iscap) { - TSC->IOSCR |= (cap>>1) << (gi*4); - } + dbg_touch("TSC cap @ %s", rsc_get_name(r)); + // Sampling cap + TSC->IOSCR |= bit; - // channels are configured individually when read. - // we prepare bitmaps to use for the read groups (all can be read in at most 3 steps) - if (isch) { - priv->channels_phase[chnum] |= 1 << (gi*4+pi); - priv->pgen_phase[chnum] |= 1 << gi; - chnum++; + // Disable pin hysteresis (causes noise) + TSC->IOHCR ^= bit; + } + else { + dbg_touch("TSC ch @ %s", rsc_get_name(r)); + + // 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", TSC->CR, priv->cfg.charge_time, 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); + dbg_touch("TSC phases:"); + priv->all_channels_mask = 0; for (int i = 0; i < 3; i++) { - dbg_touch(" %d: ch %08"PRIx32", g %02"PRIx32, i+1, priv->channels_phase[i], (uint32_t)priv->pgen_phase[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; + UTOUCH_updateTick(unit); + return E_SUCCESS; } @@ -108,11 +183,13 @@ void UTOUCH_deInit(Unit *unit) struct priv *priv = unit->data; // de-init peripherals - if (unit->status == E_SUCCESS ) { + 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 diff --git a/units/touch/_touch_internal.h b/units/touch/_touch_internal.h index 0a1bbca..056fe11 100644 --- a/units/touch/_touch_internal.h +++ b/units/touch/_touch_internal.h @@ -11,14 +11,20 @@ #include "unit_base.h" -#define TSC_DEBUG 0 +#define TSC_DEBUG 1 #if TSC_DEBUG -#define dbg_touch(...) dbg(...) +#define dbg_touch(f,...) dbg(f,##__VA_ARGS__) #else -#define dbg_touch(...) do{}while(0) +#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 @@ -32,12 +38,18 @@ struct priv { // 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 pgen_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]; @@ -68,4 +80,8 @@ 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 diff --git a/units/touch/_touch_settings.c b/units/touch/_touch_settings.c index def2f96..3340c06 100644 --- a/units/touch/_touch_settings.c +++ b/units/touch/_touch_settings.c @@ -46,6 +46,10 @@ void UTOUCH_loadBinary(Unit *unit, PayloadParser *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 */ @@ -53,7 +57,7 @@ void UTOUCH_writeBinary(Unit *unit, PayloadBuilder *pb) { struct priv *priv = unit->data; - pb_u8(pb, 0); // version + pb_u8(pb, 1); // version pb_u8(pb, priv->cfg.charge_time); pb_u8(pb, priv->cfg.drain_time); @@ -63,6 +67,7 @@ void UTOUCH_writeBinary(Unit *unit, PayloadBuilder *pb) 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); } // ------------------------------------------------------------------------ @@ -91,6 +96,9 @@ error_t UTOUCH_loadIni(Unit *unit, const char *key, const char *value) 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 @@ -126,17 +134,23 @@ void UTOUCH_writeIni(Unit *unit, IniWriter *iw) 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.ss_presc); + 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_comment(iw, "Measurement timeout (1-7)"); - iw_entry(iw, "sense-timeout", "%d", (int)priv->cfg.sense_timeout); + + iw_cmt_newline(iw); + iw_comment(iw, "Optimize for interlaced pads"); + 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."); diff --git a/units/touch/unit_touch.c b/units/touch/unit_touch.c index 0634244..dd42aee 100644 --- a/units/touch/unit_touch.c +++ b/units/touch/unit_touch.c @@ -11,13 +11,28 @@ // ------------------------------------------------------------------------ enum TouchCmd_ { - DUMMY=0 + 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<readouts[i]); + } + } + com_respond_pb(frame_id, MSG_SUCCESS, &pb); + return E_SUCCESS; + default: return E_UNKNOWN_COMMAND; } @@ -25,18 +40,6 @@ static error_t UTOUCH_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, // ------------------------------------------------------------------------ -/** - * Handle update-tick (if configured in init) - * - * @param unit - */ -static void UTOUCH_updateTick(Unit *unit) -{ - // -} - -// ------------------------------------------------------------------------ - /** Unit template */ const UnitDriver UNIT_TOUCH = { .name = "TOUCH",