Merge branch 'tsc'

remotes/github/bad-doublebuf
Ondřej Hruška 7 years ago
commit 7f4c06ae4b
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 5
      comm/messages.c
  2. 19
      framework/resources.c
  3. 6
      framework/rsc_enum.h
  4. 3
      gex.mk
  5. 37
      platform/hw_utils.c
  6. 3
      platform/hw_utils.h
  7. 11
      platform/irq_dispatcher.c
  8. 2
      platform/plat_compat.h
  9. 8
      platform/platform.c
  10. 16
      platform/status_led.c
  11. 1
      platform/status_led.h
  12. 23
      units/template/install.sh
  13. 11
      units/touch/_touch_api.c
  14. 124
      units/touch/_touch_core.c
  15. 215
      units/touch/_touch_init.c
  16. 87
      units/touch/_touch_internal.h
  17. 169
      units/touch/_touch_settings.c
  18. 59
      units/touch/unit_touch.c
  19. 16
      units/touch/unit_touch.h

@ -2,6 +2,7 @@
// Created by MightyPork on 2017/11/21. // Created by MightyPork on 2017/11/21.
// //
#include <platform/status_led.h>
#include "platform.h" #include "platform.h"
#include "framework/settings.h" #include "framework/settings.h"
#include "utils/ini_parser.h" #include "utils/ini_parser.h"
@ -91,6 +92,7 @@ static TF_Result lst_ini_export(TinyFrame *tf, TF_Msg *msg)
bulk->userdata = NULL; bulk->userdata = NULL;
bulkread_start(tf, bulk); bulkread_start(tf, bulk);
Indicator_Effect(STATUS_DISK_BUSY_SHORT);
return TF_STAY; return TF_STAY;
} }
@ -152,6 +154,8 @@ static TF_Result lst_ini_import(TinyFrame *tf, TF_Msg *msg)
bulkwrite_start(tf, bulk); bulkwrite_start(tf, bulk);
Indicator_Effect(STATUS_DISK_BUSY);
done: done:
return TF_STAY; return TF_STAY;
} }
@ -161,6 +165,7 @@ done:
/** Listener: Save settings to Flash */ /** Listener: Save settings to Flash */
static TF_Result lst_persist_cfg(TinyFrame *tf, TF_Msg *msg) static TF_Result lst_persist_cfg(TinyFrame *tf, TF_Msg *msg)
{ {
Indicator_Effect(STATUS_DISK_REMOVED);
settings_save(); settings_save();
return TF_STAY; return TF_STAY;
} }

@ -33,12 +33,6 @@ const char * rsc_get_name(Resource rsc)
// we assume the returned value is not stored anywhere // we assume the returned value is not stored anywhere
// and is directly used in a sprintf call, hence a static buffer is OK to use // and is directly used in a sprintf call, hence a static buffer is OK to use
if (rsc >= R_EXTI0 && rsc <= R_EXTI15) {
uint8_t index = rsc - R_EXTI0;
SNPRINTF(gpionamebuf, 8, "EXTI%d", index);
return gpionamebuf;
}
// R_PA0 is 0 // R_PA0 is 0
if (rsc <= R_PF15) { if (rsc <= R_PF15) {
// we assume the returned value is not stored anywhere // we assume the returned value is not stored anywhere
@ -48,6 +42,12 @@ const char * rsc_get_name(Resource rsc)
return gpionamebuf; return gpionamebuf;
} }
if (rsc >= R_EXTI0 && rsc <= R_EXTI15) {
uint8_t index = rsc - R_EXTI0;
SNPRINTF(gpionamebuf, 8, "EXTI%d", index);
return gpionamebuf;
}
return rsc_names[rsc - R_EXTI15 - 1]; return rsc_names[rsc - R_EXTI15 - 1];
} }
@ -85,11 +85,12 @@ const char * rsc_get_owner_name(Resource rsc)
void rsc_init_registry(void) void rsc_init_registry(void)
{ {
for(uint32_t i = 0; i < RSCMAP_LEN; i++) { memset(UNIT_PLATFORM.resources, 0xFF, RSCMAP_LEN);
UNIT_PLATFORM.resources[i] = global_rscmap[i] = 0xFF; memset(global_rscmap, 0xFF, RSCMAP_LEN);
}
rsc_initialized = true; rsc_initialized = true;
rsc_dbg("Total %d hw resources, bitmap has %d bytes.", RESOURCE_COUNT, RSCMAP_LEN);
} }

@ -13,6 +13,7 @@
X(I2C1) X(I2C2) X(I2C3) \ X(I2C1) X(I2C2) X(I2C3) \
X(ADC1) X(ADC2) X(ADC3) X(ADC4) \ X(ADC1) X(ADC2) X(ADC3) X(ADC4) \
X(DAC1) X(DAC2) \ X(DAC1) X(DAC2) \
X(TSC) \
X(USART1) X(USART2) X(USART3) X(USART4) X(USART5) X(USART6) \ X(USART1) X(USART2) X(USART3) X(USART4) X(USART5) X(USART6) \
X(TIM1) X(TIM2) X(TIM3) X(TIM4) X(TIM5) \ X(TIM1) X(TIM2) X(TIM3) X(TIM4) X(TIM5) \
X(TIM6) X(TIM7) X(TIM8) X(TIM9) X(TIM10) X(TIM11) X(TIM12) X(TIM13) X(TIM14) \ X(TIM6) X(TIM7) X(TIM8) X(TIM9) X(TIM10) X(TIM11) X(TIM12) X(TIM13) X(TIM14) \
@ -24,7 +25,6 @@
// X(I2S1) X(I2S2) X(I2S3) // X(I2S1) X(I2S2) X(I2S3)
// X(OPAMP1) X(OPAMP2) X(OPAMP3) X(OPAMP4) // X(OPAMP1) X(OPAMP2) X(OPAMP3) X(OPAMP4)
// X(CAN1) X(CAN2) // X(CAN1) X(CAN2)
// X(TSC)
// X(DCMI) // X(DCMI)
// X(ETH) // X(ETH)
// X(FSMC) // X(FSMC)
@ -59,8 +59,12 @@ typedef enum hw_resource Resource;
/** Enum of all resources */ /** Enum of all resources */
enum hw_resource { enum hw_resource {
#define X(res_name) R_##res_name, #define X(res_name) R_##res_name,
// GPIO are at the beginning, because some units use the constants in their config to represent
// selected pins and those must not change with adding more stuff to the main list
XX_RESOURCES_GPIO XX_RESOURCES_GPIO
// EXTIs (same like GPIOs) have dynamically generated labels to save rom space. Must be contiguous.
XX_RESOURCES_EXTI XX_RESOURCES_EXTI
// All the rest ...
XX_RESOURCES XX_RESOURCES
#undef X #undef X
R_NONE, R_NONE,

@ -16,6 +16,7 @@ GEX_SRC_DIR = \
User/units/adc \ User/units/adc \
User/units/sipo \ User/units/sipo \
User/units/fcap \ User/units/fcap \
User/units/touch \
User/TinyFrame \ User/TinyFrame \
User/CWPack \ User/CWPack \
User/tasks User/tasks
@ -95,7 +96,7 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \
-DUSE_STACK_MONITOR=1 \ -DUSE_STACK_MONITOR=1 \
-DUSE_DEBUG_UART=1 \ -DUSE_DEBUG_UART=1 \
-DDEBUG_MALLOC=0 \ -DDEBUG_MALLOC=0 \
-DDEBUG_RSC=1 -DDEBUG_RSC=0
endif endif

@ -3,7 +3,6 @@
// //
#include "platform.h" #include "platform.h"
#include "utils/avrlibc.h"
#include "hw_utils.h" #include "hw_utils.h"
/** Convert pin number to LL bitfield */ /** Convert pin number to LL bitfield */
@ -131,6 +130,30 @@ error_t hw_configure_gpio_af(char port_name, uint8_t pin_num, uint32_t ll_af)
return E_SUCCESS; return E_SUCCESS;
} }
/** Configure a pin to alternate function */
error_t hw_configure_gpiorsc_af(Resource rsc, uint32_t ll_af)
{
#if PLAT_NO_AFNUM
trap("Illegal call to hw_configure_gpio_af() on this platform");
#else
bool suc = true;
GPIO_TypeDef *port;
uint32_t ll_pin;
suc = hw_pinrsc2ll(rsc, &port, &ll_pin);
if (!suc) return E_BAD_CONFIG;
if (ll_pin & 0xFF)
LL_GPIO_SetAFPin_0_7(port, ll_pin, ll_af);
else
LL_GPIO_SetAFPin_8_15(port, ll_pin, ll_af);
LL_GPIO_SetPinMode(port, ll_pin, LL_GPIO_MODE_ALTERNATE);
#endif
return E_SUCCESS;
}
/** Configure pins using sparse map */ /** Configure pins using sparse map */
error_t hw_configure_sparse_pins(char port_name, uint16_t mask, GPIO_TypeDef **port_dest, error_t hw_configure_sparse_pins(char port_name, uint16_t mask, GPIO_TypeDef **port_dest,
uint32_t ll_mode, uint32_t ll_otype) uint32_t ll_mode, uint32_t ll_otype)
@ -288,6 +311,12 @@ void hw_periph_clock_enable(void *periph)
#ifdef DAC2 #ifdef DAC2
else if (periph == DAC2) __HAL_RCC_DAC2_CLK_ENABLE(); else if (periph == DAC2) __HAL_RCC_DAC2_CLK_ENABLE();
#endif #endif
// --- TSC ---
#ifdef TSC
else if (periph == TSC) __HAL_RCC_TSC_CLK_ENABLE();
#endif
else { else {
dbg("Periph 0x%p missing in hw clock enable func", periph); dbg("Periph 0x%p missing in hw clock enable func", periph);
trap("BUG"); trap("BUG");
@ -393,6 +422,12 @@ void hw_periph_clock_disable(void *periph)
#ifdef DAC2 #ifdef DAC2
else if (periph == DAC2) __HAL_RCC_DAC2_CLK_DISABLE(); else if (periph == DAC2) __HAL_RCC_DAC2_CLK_DISABLE();
#endif #endif
// --- TSC ---
#ifdef TSC
else if (periph == TSC) __HAL_RCC_TSC_CLK_DISABLE();
#endif
else { else {
dbg("Periph 0x%p missing in hw clock disable func", periph); dbg("Periph 0x%p missing in hw clock disable func", periph);
trap("BUG"); trap("BUG");

@ -88,6 +88,9 @@ void hw_deinit_unit_pins(Unit *unit);
*/ */
error_t hw_configure_gpio_af(char port_name, uint8_t pin_num, uint32_t ll_af) __attribute__((warn_unused_result)); error_t hw_configure_gpio_af(char port_name, uint8_t pin_num, uint32_t ll_af) __attribute__((warn_unused_result));
/** Configure a pin to alternate function via rsc */
error_t hw_configure_gpiorsc_af(Resource rsc, uint32_t ll_af) __attribute__((warn_unused_result));
/** /**
* Configure multiple pins using the bitmap pattern * Configure multiple pins using the bitmap pattern
* *

@ -69,6 +69,7 @@ static struct callbacks_ {
struct cbslot tim16; struct cbslot tim16;
struct cbslot adc1; struct cbslot adc1;
struct cbslot tsc;
// XXX add more callbacks here when needed // XXX add more callbacks here when needed
} callbacks; } callbacks;
@ -92,7 +93,8 @@ void irqd_init(void)
HAL_NVIC_SetPriority(EXTI2_3_IRQn, 2, 0); HAL_NVIC_SetPriority(EXTI2_3_IRQn, 2, 0);
HAL_NVIC_SetPriority(EXTI4_15_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_Channel1_IRQn); /*!< DMA1 Channel 1 Interrupt */
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); /*!< DMA1 Channel 2 and Channel 3 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; else if (periph == TIM16) slot = &callbacks.tim16;
// 17 - used by timebase // 17 - used by timebase
else if (periph == TSC) slot = &callbacks.tsc;
else if (periph == ADC1) slot = &callbacks.adc1; else if (periph == ADC1) slot = &callbacks.adc1;
else if (periph >= EXTIS[0] && periph <= EXTIS[15]) { else if (periph >= EXTIS[0] && periph <= EXTIS[15]) {
@ -352,6 +356,11 @@ void ADC1_COMP_IRQHandler(void)
CALL_IRQ_HANDLER(callbacks.adc1); CALL_IRQ_HANDLER(callbacks.adc1);
} }
void TSC_IRQHandler(void)
{
CALL_IRQ_HANDLER(callbacks.tsc);
}
// other ISRs... // other ISRs...

@ -21,7 +21,7 @@
#endif #endif
// 180 is normally enough if not doing extensive debug logging // 180 is normally enough if not doing extensive debug logging
#define TSK_STACK_MSG 200 // TF message handler task stack size (all unit commands run on this thread) #define TSK_STACK_MSG 220 // TF message handler task stack size (all unit commands run on this thread)
#define TSK_STACK_IDLE 64 //configMINIMAL_STACK_SIZE #define TSK_STACK_IDLE 64 //configMINIMAL_STACK_SIZE
#define TSK_STACK_TIMERS 64 //configTIMER_TASK_STACK_DEPTH #define TSK_STACK_TIMERS 64 //configTIMER_TASK_STACK_DEPTH

@ -2,7 +2,6 @@
// Created by MightyPork on 2017/11/26. // Created by MightyPork on 2017/11/26.
// //
#include <units/fcap/unit_fcap.h>
#include "platform.h" #include "platform.h"
#include "usbd_core.h" #include "usbd_core.h"
#include "USB/usb_device.h" #include "USB/usb_device.h"
@ -19,6 +18,8 @@
#include "units/usart/unit_usart.h" #include "units/usart/unit_usart.h"
#include "units/spi/unit_spi.h" #include "units/spi/unit_spi.h"
#include "units/sipo/unit_sipo.h" #include "units/sipo/unit_sipo.h"
#include "units/fcap/unit_fcap.h"
#include "units/touch/unit_touch.h"
#include "hw_utils.h" #include "hw_utils.h"
void plat_init_resources(void) void plat_init_resources(void)
@ -90,6 +91,7 @@ void plat_init_resources(void)
ureg_add_type(&UNIT_ADC); ureg_add_type(&UNIT_ADC);
ureg_add_type(&UNIT_SIPO); ureg_add_type(&UNIT_SIPO);
ureg_add_type(&UNIT_FCAP); ureg_add_type(&UNIT_FCAP);
ureg_add_type(&UNIT_TOUCH);
// Free all present resources // Free all present resources
{ {
@ -98,7 +100,7 @@ void plat_init_resources(void)
// rsc_free_range(NULL, R_COMP1, R_COMP2); // rsc_free_range(NULL, R_COMP1, R_COMP2);
rsc_free(NULL, R_DAC1); rsc_free(NULL, R_DAC1);
// rsc_free(NULL, R_HDMI_CEC); // rsc_free(NULL, R_HDMI_CEC);
// rsc_free(NULL, R_TSC); rsc_free(NULL, R_TSC);
rsc_free_range(NULL, R_I2C1, R_I2C2); rsc_free_range(NULL, R_I2C1, R_I2C2);
// rsc_free_range(NULL, R_I2S1, R_I2S2); // rsc_free_range(NULL, R_I2S1, R_I2S2);
rsc_free_range(NULL, R_SPI1, R_SPI2); rsc_free_range(NULL, R_SPI1, R_SPI2);
@ -154,7 +156,7 @@ void plat_init_resources(void)
rsc_free_range(NULL, R_TIM1, R_TIM4); rsc_free_range(NULL, R_TIM1, R_TIM4);
rsc_free_range(NULL, R_TIM6, R_TIM8); rsc_free_range(NULL, R_TIM6, R_TIM8);
rsc_free_range(NULL, R_TIM15, R_TIM17); rsc_free_range(NULL, R_TIM15, R_TIM17);
// rsc_free(NULL, R_TSC); rsc_free(NULL, R_TSC);
rsc_free_range(NULL, R_USART1, R_USART5); rsc_free_range(NULL, R_USART1, R_USART5);
rsc_free_range(NULL, R_PA0, R_PA15); rsc_free_range(NULL, R_PA0, R_PA15);

@ -61,6 +61,12 @@ void Indicator_Effect(enum GEX_StatusIndicator indicator)
led_on(); led_on();
} }
// prevent the two disk ops interfering (happens in write-reload)
if (indicator == STATUS_DISK_BUSY && active_effect == STATUS_DISK_BUSY_SHORT) return;
if (indicator == STATUS_DISK_BUSY_SHORT && active_effect == STATUS_DISK_BUSY) return;
// TODO add some better protection against effect overlap?
active_effect = indicator; active_effect = indicator;
effect_time = 0; effect_time = 0;
} }
@ -119,7 +125,15 @@ void Indicator_Tick(void)
active_effect = STATUS_NONE; active_effect = STATUS_NONE;
} }
else if (effect_time % 100 == 0) led_on(); else if (effect_time % 100 == 0) led_on();
else if (effect_time % 100 == 50) led_off(); else if (effect_time % 100 == 20) led_off();
}
else if (active_effect == STATUS_DISK_BUSY_SHORT) {
if (effect_time >= 200) {
led_off();
active_effect = STATUS_NONE;
}
else if (effect_time % 100 == 0) led_on();
else if (effect_time % 100 == 20) led_off();
} }
else if (active_effect == STATUS_WELCOME) { else if (active_effect == STATUS_WELCOME) {
if (effect_time == 0) led_on(); if (effect_time == 0) led_on();

@ -16,6 +16,7 @@ enum GEX_StatusIndicator {
STATUS_NONE = 0, STATUS_NONE = 0,
STATUS_FAULT, STATUS_FAULT,
STATUS_DISK_BUSY, STATUS_DISK_BUSY,
STATUS_DISK_BUSY_SHORT,
STATUS_DISK_ATTACHED, STATUS_DISK_ATTACHED,
STATUS_DISK_REMOVED, STATUS_DISK_REMOVED,
STATUS_WELCOME, STATUS_WELCOME,

@ -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…
Cancel
Save