firmware/stm32/COLDCARD/modtouch.c

854 lines
25 KiB
C

/*
* (c) Copyright 2018 by Coinkite Inc. This file is part of Coldcard <coldcardwallet.com>
* and is covered by GPLv3 license found in COPYING.
*/
/* NOTES
- this is controlling the TSC - Tcouh Sensing Controler
- see lib/stm32lib/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal_tsc.h
- probably-singleton style master object, handles all pins (up to 24)
- there are (up to?) 8 groups, each with 4 pins associated
- all groups can sample at once, but only a singe pin in each
- one pin in each group is lost to a sense cap, so really 3 touch sensors per group
- unused pins in the group will be floated or pulled low during acquision (caution)
- in their terminlogy "sheild io" is the sense cap line.
- example code: <https://github.com/eleciawhite/STM32Cube/blob/master/STM32Cube_FW_F3_V1.3.0/Projects/STM32373C_EVAL/Examples/TSC/TSC_BasicAcquisition_Interrupt/Src/main.c>
*/
#include <stdio.h>
#include <string.h>
#include "modtouch.h"
#include "rng.h"
#include "flash.h"
#include "py/gc.h"
#include "py/runtime.h"
#include "py/mphal.h"
#include "py/mperrno.h"
typedef struct _modtouch_obj_t {
mp_obj_base_t base;
// big object containing both init values and current state
TSC_HandleTypeDef hw;
bool initialized;
// using during interrupt only
int8_t group1, group2;
uint16_t result1, result2;
mp_obj_t handler;
} modtouch_obj_t;
// one instance can have an interrupt pending. "please" don't delete/free during that time.
static modtouch_obj_t *irq_self;
// forward refs
const mp_obj_type_t touch_class_type;
STATIC mp_obj_t modtouch_init_helper(modtouch_obj_t *self, size_t n_args,
const mp_obj_t *pos_args, mp_map_t *kw_args);
// TSC_IRQHandler()
//
// This symbol is declared weak in startup_stm32.S and is put into the interrupt
// vector table there.
//
void
TSC_IRQHandler(void)
{
IRQ_ENTER(TSC_IRQn);
//assert(irq_self);
if(!irq_self) {
// seems to happen during debug
goto limp;
}
// Call HAL code, which will call either
// HAL_TSC_ErrorCallback or HAL_TSC_ConvCpltCallback
// ... always; no cases w/o that.
HAL_TSC_IRQHandler(&irq_self->hw);
if(irq_self->handler) {
mp_sched_schedule(irq_self->handler, irq_self);
}
HAL_TSC_Stop_IT(&irq_self->hw);
irq_self = NULL;
limp:
IRQ_EXIT(TSC_IRQn);
}
// HAL_TSC_ConvCpltCallback()
//
void
HAL_TSC_ConvCpltCallback(TSC_HandleTypeDef* htsc)
{
// capture the results.
irq_self->result1 = HAL_TSC_GroupGetValue(htsc, irq_self->group1);
if(irq_self->group2 != -1) {
irq_self->result2 = HAL_TSC_GroupGetValue(htsc, irq_self->group2);
} else {
irq_self->result2 = 0;
}
}
// HAL_TSC_ErrorCallback()
//
void
HAL_TSC_ErrorCallback(TSC_HandleTypeDef* htsc)
{
// note the error
irq_self->result1 = 0;
irq_self->result2 = 0;
}
STATIC mp_obj_t modtouch_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
// no args, except keywords
mp_arg_check_num(n_args, n_kw, 0, 0, true);
modtouch_obj_t *self = m_new_obj(modtouch_obj_t);
memset(self, 0, sizeof(modtouch_obj_t));
self->base.type = &touch_class_type;
self->hw.Instance = TSC;
self->initialized = false;
self->group1 = self->group2 = -1;
self->result1 = self->result2 = 0;
if (n_kw > 0) {
// configure and start it too
mp_map_t kw_args;
mp_map_init_fixed_table(&kw_args, n_kw, args + n_args);
modtouch_init_helper(self, 0, 0, &kw_args);
}
return MP_OBJ_FROM_PTR(self);
}
STATIC void touch_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
modtouch_obj_t *self = self_in;
if(!self->initialized) {
mp_printf(print, "Touch(@%p)", self);
return;
}
mp_printf(print, "Touch(@%p, ", self);
TSC_InitTypeDef *init = &self->hw.Init;
mp_printf(print, "channels=0x%08x, caps=0x%08x, ", init->ChannelIOs, init->ShieldIOs);
mp_printf(print, "CTPH=%d, CTPL=%d, spread=%d, ",
((init->CTPulseHighLength & TSC_CR_CTPH_Msk) >> TSC_CR_CTPH_Pos)+1,
((init->CTPulseLowLength & TSC_CR_CTPL_Msk) >> TSC_CR_CTPL_Pos)+1,
(init->SpreadSpectrumDeviation & TSC_CR_SSD_Msk) >> TSC_CR_SSD_Pos
);
mp_printf(print, "pulse_prescale=%d, max_count=%d, ",
1<<((init->PulseGeneratorPrescaler&TSC_CR_PGPSC_Msk) >> TSC_CR_PGPSC_Pos),
(256 << ((init->MaxCountValue & TSC_CR_MCV_Msk) >> TSC_CR_MCV_Pos))-1);
mp_printf(print, "float_unused=%d, handler=%c",
(init->IODefaultMode == TSC_IODEF_IN_FLOAT),
(self->handler ? 'Y' : 'N')
);
#if 0
if(irq_self) {
mp_printf(print, ", busy=%s", (irq_self == self) ? "ME" : "other");
}
#endif
mp_printf(print, ")");
}
// remap_tsc_pin()
//
// Map port/pin to group+channel in TSC terms.
//
STATIC int
remap_tsc_pin(const pin_obj_t *pin, GPIO_TypeDef **port)
{
#ifdef STM32L4
/*
Table 1. IOs for the STM32L4xx devices
+--------------------------------+
| IOs | TSC functions |
|--------------|-----------------|
| PB12 (AF) | TSC_G1_IO1 |
| PB13 (AF) | TSC_G1_IO2 |
| PB14 (AF) | TSC_G1_IO3 |
| PB15 (AF) | TSC_G1_IO4 |
|--------------|-----------------|
| PB4 (AF) | TSC_G2_IO1 |
| PB5 (AF) | TSC_G2_IO2 |
| PB6 (AF) | TSC_G2_IO3 |
| PB7 (AF) | TSC_G2_IO4 |
|--------------|-----------------|
| PA15 (AF) | TSC_G3_IO1 |
| PC10 (AF) | TSC_G3_IO2 |
| PC11 (AF) | TSC_G3_IO3 |
| PC12 (AF) | TSC_G3_IO4 |
|--------------|-----------------|
| PC6 (AF) | TSC_G4_IO1 |
| PC7 (AF) | TSC_G4_IO2 |
| PC8 (AF) | TSC_G4_IO3 |
| PC9 (AF) | TSC_G4_IO4 |
|--------------|-----------------|
| PE10 (AF) | TSC_G5_IO1 |
| PE11 (AF) | TSC_G5_IO2 |
| PE12 (AF) | TSC_G5_IO3 |
| PE13 (AF) | TSC_G5_IO4 |
|--------------|-----------------|
| PD10 (AF) | TSC_G6_IO1 |
| PD11 (AF) | TSC_G6_IO2 |
| PD12 (AF) | TSC_G6_IO3 |
| PD13 (AF) | TSC_G6_IO4 |
|--------------|-----------------|
| PE2 (AF) | TSC_G7_IO1 |
| PE3 (AF) | TSC_G7_IO2 |
| PE4 (AF) | TSC_G7_IO3 |
| PE5 (AF) | TSC_G7_IO4 |
|--------------|-----------------|
| PF14 (AF) | TSC_G8_IO1 |
| PF15 (AF) | TSC_G8_IO2 |
| PG0 (AF) | TSC_G8_IO3 |
| PG1 (AF) | TSC_G8_IO4 |
|--------------|-----------------|
| PB10 (AF) | TSC_SYNC |
| PD2 (AF) | |
+--------------------------------+
*/
*port = NULL;
switch(pin->port) {
case 0: // Port A: just one
*port = GPIOA;
if(pin->pin == 15) return TSC_GROUP3_IO1;
return -1;
case 1: // Port B: PB1..
*port = GPIOB;
switch(pin->pin) {
case 12: return TSC_GROUP1_IO1;
case 13: return TSC_GROUP1_IO2;
case 14: return TSC_GROUP1_IO3;
case 15: return TSC_GROUP1_IO4;
case 4: return TSC_GROUP2_IO1;
case 5: return TSC_GROUP2_IO2;
case 6: return TSC_GROUP2_IO3;
case 7: return TSC_GROUP2_IO4;
default: return -1;
}
break;
case 2: // Port C: PC1..
*port = GPIOC;
switch(pin->pin) {
case 10: return TSC_GROUP3_IO2;
case 11: return TSC_GROUP3_IO3;
case 12: return TSC_GROUP3_IO4;
case 6: return TSC_GROUP4_IO1;
case 7: return TSC_GROUP4_IO2;
case 8: return TSC_GROUP4_IO3;
case 9: return TSC_GROUP4_IO4;
default: return -1;
}
break;
case 3: // Port D: PD1..
*port = GPIOD;
switch(pin->pin) {
case 10: return TSC_GROUP6_IO1;
case 11: return TSC_GROUP6_IO2;
case 12: return TSC_GROUP6_IO3;
case 13: return TSC_GROUP6_IO4;
default: return -1;
}
break;
case 4: // Port E: PE1..
*port = GPIOE;
switch(pin->pin) {
case 10: return TSC_GROUP5_IO1;
case 11: return TSC_GROUP5_IO2;
case 12: return TSC_GROUP5_IO3;
case 13: return TSC_GROUP5_IO4;
case 2: return TSC_GROUP7_IO1;
case 3: return TSC_GROUP7_IO2;
case 4: return TSC_GROUP7_IO3;
case 5: return TSC_GROUP7_IO4;
default: return -1;
}
break;
case 5: // Port F: PF1..
*port = GPIOF;
switch(pin->pin) {
case 14: return TSC_GROUP8_IO1;
case 15: return TSC_GROUP8_IO2;
default: return -1;
}
break;
case 6: // Port G: PG0..
*port = GPIOG;
switch(pin->pin) {
case 0: return TSC_GROUP8_IO3;
case 1: return TSC_GROUP8_IO4;
default: return -1;
}
break;
default: return -1;
}
#else
#error "need a mapping here"
#endif
}
// tsc_group_for_pin()
//
STATIC int
tsc_group_for_pin(uint32_t mask)
{
for(int i=0; i<TSC_NB_OF_GROUPS; i++) {
if((mask & ((uint32_t)0x0F << (i * 4)))) {
return i;
}
}
return -1;
}
// pin_list_to_mask()
//
STATIC uint32_t
pin_list_to_mask(mp_obj_t *pin_list, bool claim, bool is_cap)
{
uint32_t rv = 0;
size_t len;
mp_obj_t *array;
mp_obj_get_array(pin_list, &len, &array);
for(int i=0; i<len; i++) {
const pin_obj_t *pin = pin_find(array[i]);
if(!pin) {
// error handle? Exception would have happened tho
continue;
}
// remap to channel group / IO number for TSC
GPIO_TypeDef *port = NULL;
int mask = remap_tsc_pin(pin, &port);
if(mask < 0) {
nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
"Pin(%q) not on TSC", pin->name));
}
rv |= mask;
/*
we're supposed to do this:
(#) GPIO pins configuration
(++) Enable the clock for the TSC GPIOs using __HAL_RCC_GPIOx_CLK_ENABLE() macro.
(++) Configure the TSC pins used as sampling IOs in alternate function output Open-Drain mode,
and TSC pins used as channel/shield IOs in alternate function output Push-Pull mode
using HAL_GPIO_Init() function.
*/
if(claim) {
// configure for our usage
// Not clear if mp_hal_pin_config() can't do this stuff, so do ourselves.
mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ALT,
is_cap ? MP_HAL_PIN_MODE_OPEN_DRAIN : MP_HAL_PIN_MODE_OUTPUT,
GPIO_AF9_TSC);
// GPIO_MODE_AF_PP
// GPIO_MODE_AF_OD
GPIO_InitTypeDef cfg = {
.Pin = (1 << pin->pin),
.Mode = is_cap ? GPIO_MODE_AF_PP : GPIO_MODE_AF_OD,
.Pull = GPIO_NOPULL,
.Speed = GPIO_SPEED_FREQ_VERY_HIGH, // ??
.Alternate = GPIO_AF9_TSC
};
HAL_GPIO_Init(port, &cfg);
}
}
return rv;
}
// Touch.init()
//
STATIC mp_obj_t modtouch_init_helper(modtouch_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
TSC_InitTypeDef *init = &self->hw.Init;
mp_arg_t allowed_args[] = {
{ MP_QSTR_channels, MP_ARG_REQUIRED|MP_ARG_OBJ }, // list of pins
{ MP_QSTR_caps, MP_ARG_REQUIRED|MP_ARG_OBJ }, // list of pins
{ MP_QSTR_CTPL, MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_CTPH, MP_ARG_INT, {.u_int = 1} },
{ MP_QSTR_spread, MP_ARG_INT, {.u_int = 127} },
{ MP_QSTR_pulse_prescale, MP_ARG_INT, {.u_int = 64} },
{ MP_QSTR_max_count, MP_ARG_INT, {.u_int = 8191} },
{ MP_QSTR_float_unused, MP_ARG_BOOL, {.u_bool = true} },
{ MP_QSTR_handler, MP_ARG_OBJ, {.u_obj = NULL} },
{ MP_QSTR_queue, MP_ARG_OBJ, {.u_obj = NULL} }, // unused, simulator compat
};
if(self->initialized && init->ChannelIOs) {
// chan/caps args are optional if we've been here before
allowed_args[0].flags &= ~MP_ARG_REQUIRED;
allowed_args[1].flags &= ~MP_ARG_REQUIRED;
}
// parse args
struct {
// order must match allowed_args above
mp_arg_val_t channels, caps, CTPL, CTPH, spread, pulse_prescale;
mp_arg_val_t max_count, float_unused, handler, queue;
} args;
mp_arg_parse_all(n_args, pos_args, kw_args,
MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t*)&args);
if(args.handler.u_obj) {
if(!MP_OBJ_IS_FUN(args.handler.u_obj)) {
mp_raise_TypeError("handler");
}
self->handler = args.handler.u_obj;
} else {
self->handler = NULL;
}
if(args.channels.u_obj) {
// read list of pins.
uint32_t channel_mask = pin_list_to_mask(args.channels.u_obj, true, false);
uint32_t cap_mask = pin_list_to_mask(args.caps.u_obj, true, true);
if((channel_mask & cap_mask) != 0) {
mp_raise_ValueError("Same pin cannot be both channel and cap");
}
// set all configuration values
// not really; we're not sampling yet
init->ShieldIOs = 0;
init->ChannelIOs = channel_mask;
init->SamplingIOs = cap_mask;
}
if( (args.CTPH.u_int < 1) || (args.CTPH.u_int > 16)
|| (args.CTPL.u_int < 1) || (args.CTPL.u_int > 16)
) {
mp_raise_ValueError("CTPH & CTPL must be 1..16");
}
init->CTPulseHighLength = (args.CTPH.u_int-1) << TSC_CR_CTPH_Pos;
init->CTPulseLowLength = (args.CTPL.u_int-1) << TSC_CR_CTPL_Pos;
if(args.spread.u_int > 0) {
init->SpreadSpectrum = TSC_CR_SSE;
init->SpreadSpectrumDeviation = (args.spread.u_int << TSC_CR_SSD_Pos) & TSC_CR_SSD_Msk;
init->SpreadSpectrumPrescaler = TSC_SS_PRESC_DIV1;
} else {
// disable spread-spectrum
init->SpreadSpectrum = 0;
init->SpreadSpectrumDeviation = 0;
init->SpreadSpectrumPrescaler = 0;
}
switch(args.pulse_prescale.u_int) {
case 1: init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV1; break;
case 2: init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV2; break;
case 4: init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV4; break;
case 8: init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV8; break;
case 16: init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV16; break;
case 32: init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV32; break;
case 64: init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV64; break;
case 128:init->PulseGeneratorPrescaler = TSC_PG_PRESC_DIV128; break;
default:
mp_raise_ValueError("pulse_prescale");
}
switch(args.max_count.u_int) {
case 255: init->MaxCountValue = TSC_MCV_255; break;
case 511: init->MaxCountValue = TSC_MCV_511; break;
case 1023: init->MaxCountValue = TSC_MCV_1023; break;
case 2047: init->MaxCountValue = TSC_MCV_2047; break;
case 8191: init->MaxCountValue = TSC_MCV_8191; break;
case 16383: init->MaxCountValue = TSC_MCV_16383; break;
default:
mp_raise_ValueError("max_count");
}
init->IODefaultMode = args.float_unused.u_bool ? TSC_IODEF_IN_FLOAT : TSC_IODEF_OUT_PP_LOW;
// no support for sync mode here
init->AcquisitionMode = TSC_ACQ_MODE_NORMAL;
init->SynchroPinPolarity = TSC_SYNC_POLARITY_FALLING;
init->MaxCountInterrupt = ENABLE;
// enable clock to TSC
__HAL_RCC_TSC_CLK_ENABLE();
// no need for __HAL_RCC_GPIOA_CLK_ENABLE() .. etc, as done by mp_hal_pin_config()
// call the real setup code.
HAL_StatusTypeDef x = HAL_TSC_Init(&self->hw);
if(x != HAL_OK) {
mp_raise_ValueError("HAL_TSC_Init");
}
self->initialized = true;
return mp_const_none;
}
STATIC mp_obj_t modtouch_init(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
return modtouch_init_helper(args[0], n_args - 1, args + 1, kw_args);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(modtouch_init_obj, 1, modtouch_init);
STATIC mp_obj_t modtouch_discharge(mp_obj_t self_in) {
modtouch_obj_t *self = self_in;
HAL_StatusTypeDef x = HAL_TSC_IODischarge(&self->hw, ENABLE);
if(x != HAL_OK) {
mp_raise_ValueError("HAL_TSC_IODischarge");
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(modtouch_discharge_obj, modtouch_discharge);
// sample SINGLE pin
//
// - takes about 12ms to complete (with default settings)
// - timeout based on max_count value
// - requires caller to do discharge step and 1ms delay
//
STATIC mp_obj_t modtouch_sample(mp_obj_t self_in, mp_obj_t which_pin)
{
modtouch_obj_t *self = self_in;
HAL_StatusTypeDef x;
const pin_obj_t *pin = pin_find(which_pin);
GPIO_TypeDef *port = NULL;
uint32_t mask = remap_tsc_pin(pin, &port);
int group = tsc_group_for_pin(mask);
if((mask & self->hw.Init.ChannelIOs) == 0) {
mp_raise_ValueError("not setup to sample that pin");
}
assert(group >= 0);
assert(group < 8);
// configure to sample specific pin
TSC_IOConfigTypeDef config = {
//.ChannelIOs = self->hw.Init.ChannelIOs,
.ChannelIOs = mask,
.ShieldIOs = self->hw.Init.ShieldIOs,
.SamplingIOs = self->hw.Init.SamplingIOs,
};
x = HAL_TSC_IOConfig(&self->hw, &config);
if(x != HAL_OK) {
mp_raise_ValueError("HAL_TSC_IOConfig");
}
// NOTE: discharge must be done before we are called
#if 0
HAL_TSC_IODischarge(&self->hw, ENABLE);
mp_hal_delay_ms(1);
#endif
x = HAL_TSC_Start(&self->hw);
if(x != HAL_OK) {
mp_raise_ValueError("HAL_TSC_Start");
}
while(HAL_TSC_GetState(&self->hw) == HAL_TSC_STATE_BUSY) {
// busy wait
}
x = HAL_TSC_GetState(&self->hw);
uint32_t count = 0;
switch(x) {
case HAL_TSC_STATE_READY:
count = HAL_TSC_GroupGetValue(&self->hw, group);
break;
case HAL_TSC_STATE_ERROR:
// acquisition is completed with max count error
count = 0;
break;
default:
mp_raise_ValueError("TSC State");
break;
}
HAL_TSC_Stop(&self->hw);
return MP_OBJ_NEW_SMALL_INT(count);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(modtouch_sample_obj, modtouch_sample);
// sample TWO pins at same time
//
// unused?
//
STATIC mp_obj_t modtouch_sample_two(mp_obj_t self_in, mp_obj_t pin1_in, mp_obj_t pin2_in)
{
modtouch_obj_t *self = self_in;
HAL_StatusTypeDef x;
GPIO_TypeDef *port = NULL;
uint32_t mask = remap_tsc_pin(pin_find(pin1_in), &port);
int group1 = tsc_group_for_pin(mask);
uint32_t mask2 = remap_tsc_pin(pin_find(pin2_in), &port);
int group2 = tsc_group_for_pin(mask2);
if(group1 == group2) {
mp_raise_ValueError("pins must be in different groups");
}
mask |= mask2;
if((mask & self->hw.Init.ChannelIOs) == 0) {
mp_raise_ValueError("not setup to sample that pin");
}
// configure to sample specific pin
TSC_IOConfigTypeDef config = {
.ChannelIOs = mask,
.ShieldIOs = self->hw.Init.ShieldIOs,
.SamplingIOs = self->hw.Init.SamplingIOs,
};
x = HAL_TSC_IOConfig(&self->hw, &config);
if(x != HAL_OK) {
mp_raise_ValueError("HAL_TSC_IOConfig");
}
// NOTE: discharge must be done before we are called
x = HAL_TSC_Start(&self->hw);
if(x != HAL_OK) {
mp_raise_ValueError("HAL_TSC_Start");
}
while(HAL_TSC_GetState(&self->hw) == HAL_TSC_STATE_BUSY) {
// busy wait
}
x = HAL_TSC_GetState(&self->hw);
uint32_t count1 = 0, count2 = 0;
switch(x) {
case HAL_TSC_STATE_READY:
count1 = HAL_TSC_GroupGetValue(&self->hw, group1);
count2 = HAL_TSC_GroupGetValue(&self->hw, group2);
break;
case HAL_TSC_STATE_ERROR:
// acquisition is completed with max count error; return zeros
break;
default:
mp_raise_ValueError("TSC State");
break;
}
HAL_TSC_Stop(&self->hw);
mp_obj_t tuple[2] = {
MP_OBJ_NEW_SMALL_INT(count1),
MP_OBJ_NEW_SMALL_INT(count2),
};
return mp_obj_new_tuple(2, tuple);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_3(modtouch_sample_two_obj, modtouch_sample_two);
// Interrupt-based touch sampling of upto TWO pins at same time
//
STATIC mp_obj_t modtouch_start_sample(size_t n_args, const mp_obj_t *args)
{
modtouch_obj_t *self = args[0];
HAL_StatusTypeDef x;
GPIO_TypeDef *port = NULL;
uint32_t mask = remap_tsc_pin(pin_find(args[1]), &port);
self->group1 = tsc_group_for_pin(mask);
if(n_args >= 3) {
const pin_obj_t *pin2 = pin_find(args[2]);
uint32_t mask2 = remap_tsc_pin(pin2, &port);
self->group2 = tsc_group_for_pin(mask2);
if(self->group1 == self->group2) {
mp_raise_ValueError("pins must be in different groups");
}
mask |= mask2;
} else {
self->group2 = -1;
}
if((mask & self->hw.Init.ChannelIOs) == 0) {
mp_raise_ValueError("not setup to sample that pin");
}
// Commit to being the handler. Be sure to clear
// any error cases, before getting to this point.
// This is atomic test and set.
if(!__sync_bool_compare_and_swap(&irq_self, NULL, self)) {
// this used to raise but since usually caused by
// a race, it's better to return
return mp_const_true;
}
// configure to sample specific pin now
TSC_IOConfigTypeDef config = {
.ChannelIOs = mask,
.ShieldIOs = self->hw.Init.ShieldIOs,
.SamplingIOs = self->hw.Init.SamplingIOs,
};
x = HAL_TSC_IOConfig(&self->hw, &config);
if(x != HAL_OK) {
irq_self = NULL;
mp_raise_ValueError("HAL_TSC_IOConfig");
}
HAL_NVIC_SetPriority(TSC_IRQn, IRQ_PRI_CAN, 0);
HAL_NVIC_EnableIRQ(TSC_IRQn);
x = HAL_TSC_Start_IT(&self->hw);
if(x != HAL_OK) {
irq_self = NULL;
mp_raise_ValueError("HAL_TSC_Start_IT");
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(modtouch_start_sample_obj, 2, 3, modtouch_start_sample);
// readback results from interrupt handler, or
// - None if not finished yet
// - (a,b) if dual sample
// - N if single sample
//
STATIC mp_obj_t modtouch_finished(mp_obj_t self_in)
{
modtouch_obj_t *self = self_in;
if(irq_self) {
// not done interrupt handling yet
return mp_const_none;
}
if(self->group2 == -1) {
return MP_OBJ_NEW_SMALL_INT(self->result1);
} else {
mp_obj_t tuple[2] = {
MP_OBJ_NEW_SMALL_INT(self->result1),
MP_OBJ_NEW_SMALL_INT(self->result2),
};
return mp_obj_new_tuple(2, tuple);
}
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(modtouch_finished_obj, modtouch_finished);
STATIC mp_obj_t pins_to_mask_helper(mp_obj_t pin_list)
{
uint32_t mask = pin_list_to_mask(pin_list, false, false);
return MP_OBJ_NEW_SMALL_INT(mask);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pins_to_mask_obj, pins_to_mask_helper);
STATIC mp_obj_t pin_to_group_helper(mp_obj_t pin_name)
{
const pin_obj_t *pin = pin_find(pin_name);
GPIO_TypeDef *port = NULL;
uint32_t mask = remap_tsc_pin(pin, &port);
int group = tsc_group_for_pin(mask);
return MP_OBJ_NEW_SMALL_INT(group);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(pin_to_group_obj, pin_to_group_helper);
STATIC const mp_rom_map_elem_t modtouch_locals_dict_table[] = {
// instance methods
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&modtouch_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_discharge), MP_ROM_PTR(&modtouch_discharge_obj) },
{ MP_ROM_QSTR(MP_QSTR_sample), MP_ROM_PTR(&modtouch_sample_obj) },
{ MP_ROM_QSTR(MP_QSTR_sample_two), MP_ROM_PTR(&modtouch_sample_two_obj) },
{ MP_ROM_QSTR(MP_QSTR_start_sample), MP_ROM_PTR(&modtouch_start_sample_obj) },
{ MP_ROM_QSTR(MP_QSTR_finished), MP_ROM_PTR(&modtouch_finished_obj) },
//{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&modtouch_deinit_obj) },
};
STATIC MP_DEFINE_CONST_DICT(modtouch_locals_dict, modtouch_locals_dict_table);
const mp_obj_type_t touch_class_type = {
{ &mp_type_type },
.name = MP_QSTR_Touch,
.make_new = modtouch_make_new,
.print = touch_print,
//.protocol = &mp_machine_soft_i2c_p,
.locals_dict = (mp_obj_dict_t*)&modtouch_locals_dict,
};
STATIC const mp_rom_map_elem_t touch_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_touch) },
{ MP_ROM_QSTR(MP_QSTR_Touch), MP_ROM_PTR(&touch_class_type) },
{ MP_ROM_QSTR(MP_QSTR_pins_to_mask), MP_ROM_PTR(&pins_to_mask_obj) },
{ MP_ROM_QSTR(MP_QSTR_pin_to_group), MP_ROM_PTR(&pin_to_group_obj) },
};
STATIC MP_DEFINE_CONST_DICT(touch_module_globals, touch_module_globals_table);
const mp_obj_module_t touch_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t*)&touch_module_globals,
};