From 538a4876b2a030ced10351f8dde60444a03d5976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 3 Feb 2018 11:59:59 +0100 Subject: [PATCH 01/20] ADC skeleton --- units/adc/_adc_api.c | 11 +++++++ units/adc/_adc_init.c | 49 ++++++++++++++++++++++++++++ units/adc/_adc_internal.h | 46 +++++++++++++++++++++++++++ units/adc/_adc_settings.c | 58 ++++++++++++++++++++++++++++++++++ units/adc/unit_adc.c | 43 +++++++++++++++++++++++++ units/adc/unit_adc.h | 16 ++++++++++ units/digital_in/_din_exti.c | 1 - units/i2c/unit_i2c.c | 2 -- units/spi/unit_spi.c | 4 +-- units/template/_tpl_init.c | 6 ++-- units/template/_tpl_internal.h | 14 ++++---- units/template/_tpl_settings.c | 8 ++--- units/template/unit_tpl.c | 23 +++++++------- units/template/unit_tpl.h | 36 +++------------------ 14 files changed, 256 insertions(+), 61 deletions(-) create mode 100644 units/adc/_adc_api.c create mode 100644 units/adc/_adc_init.c create mode 100644 units/adc/_adc_internal.h create mode 100644 units/adc/_adc_settings.c create mode 100644 units/adc/unit_adc.c create mode 100644 units/adc/unit_adc.h diff --git a/units/adc/_adc_api.c b/units/adc/_adc_api.c new file mode 100644 index 0000000..872d3a1 --- /dev/null +++ b/units/adc/_adc_api.c @@ -0,0 +1,11 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#include "platform.h" +#include "unit_base.h" +#include "unit_adc.h" + +#define ADC_INTERNAL +#include "_adc_internal.h" + diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c new file mode 100644 index 0000000..cc228d0 --- /dev/null +++ b/units/adc/_adc_init.c @@ -0,0 +1,49 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#include "platform.h" +#include "unit_base.h" + +#define ADC_INTERNAL +#include "_adc_internal.h" + +/** Allocate data structure and set defaults */ +error_t UADC_preInit(Unit *unit) +{ + struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv)); + if (priv == NULL) return E_OUT_OF_MEM; + + // + + return E_SUCCESS; +} + +/** Finalize unit set-up */ +error_t UADC_init(Unit *unit) +{ + bool suc = true; + struct priv *priv = unit->data; + + // + + return E_SUCCESS; +} + + +/** Tear down the unit */ +void UADC_deInit(Unit *unit) +{ + struct priv *priv = unit->data; + + // de-init peripherals + if (unit->status == E_SUCCESS ) { + // + } + + // Release all resources, deinit pins + rsc_teardown(unit); + + // Free memory + free_ck(unit->data); +} diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h new file mode 100644 index 0000000..c6c7225 --- /dev/null +++ b/units/adc/_adc_internal.h @@ -0,0 +1,46 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#ifndef GEX_F072_ADC_INTERNAL_H +#define GEX_F072_ADC_INTERNAL_H + +#ifndef ADC_INTERNAL +#error bad include! +#endif + +#include "unit_base.h" + +/** Private data structure */ +struct priv { + // settings + + // internal state +}; + +/** Allocate data structure and set defaults */ +error_t UADC_preInit(Unit *unit); + +/** Load from a binary buffer stored in Flash */ +void UADC_loadBinary(Unit *unit, PayloadParser *pp); + +/** Write to a binary buffer for storing in Flash */ +void UADC_writeBinary(Unit *unit, PayloadBuilder *pb); + +// ------------------------------------------------------------------------ + +/** Parse a key-value pair from the INI file */ +error_t UADC_loadIni(Unit *unit, const char *key, const char *value); + +/** Generate INI file section for the unit */ +void UADC_writeIni(Unit *unit, IniWriter *iw); + +// ------------------------------------------------------------------------ + +/** Finalize unit set-up */ +error_t UADC_init(Unit *unit); + +/** Tear down the unit */ +void UADC_deInit(Unit *unit); + +#endif //GEX_F072_ADC_INTERNAL_H diff --git a/units/adc/_adc_settings.c b/units/adc/_adc_settings.c new file mode 100644 index 0000000..79101cd --- /dev/null +++ b/units/adc/_adc_settings.c @@ -0,0 +1,58 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#include "platform.h" +#include "unit_base.h" + +#define ADC_INTERNAL +#include "_adc_internal.h" + +/** Load from a binary buffer stored in Flash */ +void UADC_loadBinary(Unit *unit, PayloadParser *pp) +{ + struct priv *priv = unit->data; + + uint8_t version = pp_u8(pp); + (void)version; + + // +} + +/** Write to a binary buffer for storing in Flash */ +void UADC_writeBinary(Unit *unit, PayloadBuilder *pb) +{ + struct priv *priv = unit->data; + + pb_u8(pb, 0); // version + + // +} + +// ------------------------------------------------------------------------ + +/** Parse a key-value pair from the INI file */ +error_t UADC_loadIni(Unit *unit, const char *key, const char *value) +{ + bool suc = true; + struct priv *priv = unit->data; + + if (false) { + // + } + else { + return E_BAD_KEY; + } + + if (!suc) return E_BAD_VALUE; + return E_SUCCESS; +} + +/** Generate INI file section for the unit */ +void UADC_writeIni(Unit *unit, IniWriter *iw) +{ + struct priv *priv = unit->data; + + // +} + diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c new file mode 100644 index 0000000..e966b18 --- /dev/null +++ b/units/adc/unit_adc.c @@ -0,0 +1,43 @@ +// +// Created by MightyPork on 2017/11/25. +// + +#include "unit_base.h" +#include "unit_adc.h" + +#define ADC_INTERNAL +#include "_adc_internal.h" + +// ------------------------------------------------------------------------ + +enum TplCmd_ { + // +}; + +/** Handle a request message */ +static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) +{ + switch (command) { + default: + return E_UNKNOWN_COMMAND; + } +} + +// ------------------------------------------------------------------------ + +/** Unit template */ +const UnitDriver UNIT_ADC = { + .name = "ADC", + .description = "Template unit", + // Settings + .preInit = UADC_preInit, + .cfgLoadBinary = UADC_loadBinary, + .cfgWriteBinary = UADC_writeBinary, + .cfgLoadIni = UADC_loadIni, + .cfgWriteIni = UADC_writeIni, + // Init + .init = UADC_init, + .deInit = UADC_deInit, + // Function + .handleRequest = UADC_handleRequest, +}; diff --git a/units/adc/unit_adc.h b/units/adc/unit_adc.h new file mode 100644 index 0000000..84d48ba --- /dev/null +++ b/units/adc/unit_adc.h @@ -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_TPL_H +#define U_TPL_H + +#include "unit.h" + +extern const UnitDriver UNIT_ADC; + +// UU_ prototypes + +#endif //U_TPL_H diff --git a/units/digital_in/_din_exti.c b/units/digital_in/_din_exti.c index 0f57e38..34a9bd5 100644 --- a/units/digital_in/_din_exti.c +++ b/units/digital_in/_din_exti.c @@ -8,7 +8,6 @@ #define DIN_INTERNAL #include "_din_internal.h" -#include "_din_exti.h" /** * Send a trigger event to master (called on the message queue thread). diff --git a/units/i2c/unit_i2c.c b/units/i2c/unit_i2c.c index 97fbb00..7176578 100644 --- a/units/i2c/unit_i2c.c +++ b/units/i2c/unit_i2c.c @@ -10,8 +10,6 @@ // I2C master #define I2C_INTERNAL #include "_i2c_internal.h" -#include "_i2c_settings.h" -#include "_i2c_init.h" enum PinCmd_ { CMD_TEST = 0, diff --git a/units/spi/unit_spi.c b/units/spi/unit_spi.c index 2f77209..c05c771 100644 --- a/units/spi/unit_spi.c +++ b/units/spi/unit_spi.c @@ -20,7 +20,7 @@ enum PinCmd_ { }; /** Handle a request message */ -static error_t SPI_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) +static error_t USPI_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) { uint8_t slave; uint16_t slaves; @@ -80,5 +80,5 @@ const UnitDriver UNIT_SPI = { .init = USPI_init, .deInit = USPI_deInit, // Function - .handleRequest = SPI_handleRequest, + .handleRequest = USPI_handleRequest, }; diff --git a/units/template/_tpl_init.c b/units/template/_tpl_init.c index 355efd7..ae9e1e8 100644 --- a/units/template/_tpl_init.c +++ b/units/template/_tpl_init.c @@ -9,7 +9,7 @@ #include "_tpl_internal.h" /** Allocate data structure and set defaults */ -error_t TPL_preInit(Unit *unit) +error_t UTPL_preInit(Unit *unit) { struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv)); if (priv == NULL) return E_OUT_OF_MEM; @@ -20,7 +20,7 @@ error_t TPL_preInit(Unit *unit) } /** Finalize unit set-up */ -error_t TPL_init(Unit *unit) +error_t UTPL_init(Unit *unit) { bool suc = true; struct priv *priv = unit->data; @@ -32,7 +32,7 @@ error_t TPL_init(Unit *unit) /** Tear down the unit */ -void TPL_deInit(Unit *unit) +void UTPL_deInit(Unit *unit) { struct priv *priv = unit->data; diff --git a/units/template/_tpl_internal.h b/units/template/_tpl_internal.h index a8a72d3..b94d7b1 100644 --- a/units/template/_tpl_internal.h +++ b/units/template/_tpl_internal.h @@ -19,28 +19,28 @@ struct priv { }; /** Allocate data structure and set defaults */ -error_t TPL_preInit(Unit *unit); +error_t UTPL_preInit(Unit *unit); /** Load from a binary buffer stored in Flash */ -void TPL_loadBinary(Unit *unit, PayloadParser *pp); +void UTPL_loadBinary(Unit *unit, PayloadParser *pp); /** Write to a binary buffer for storing in Flash */ -void TPL_writeBinary(Unit *unit, PayloadBuilder *pb); +void UTPL_writeBinary(Unit *unit, PayloadBuilder *pb); // ------------------------------------------------------------------------ /** Parse a key-value pair from the INI file */ -error_t TPL_loadIni(Unit *unit, const char *key, const char *value); +error_t UTPL_loadIni(Unit *unit, const char *key, const char *value); /** Generate INI file section for the unit */ -void TPL_writeIni(Unit *unit, IniWriter *iw); +void UTPL_writeIni(Unit *unit, IniWriter *iw); // ------------------------------------------------------------------------ /** Finalize unit set-up */ -error_t TPL_init(Unit *unit); +error_t UTPL_init(Unit *unit); /** Tear down the unit */ -void TPL_deInit(Unit *unit); +void UTPL_deInit(Unit *unit); #endif //GEX_F072_TPL_INTERNAL_H diff --git a/units/template/_tpl_settings.c b/units/template/_tpl_settings.c index 35ae7c2..5d71a7e 100644 --- a/units/template/_tpl_settings.c +++ b/units/template/_tpl_settings.c @@ -9,7 +9,7 @@ #include "_tpl_internal.h" /** Load from a binary buffer stored in Flash */ -void TPL_loadBinary(Unit *unit, PayloadParser *pp) +void UTPL_loadBinary(Unit *unit, PayloadParser *pp) { struct priv *priv = unit->data; @@ -20,7 +20,7 @@ void TPL_loadBinary(Unit *unit, PayloadParser *pp) } /** Write to a binary buffer for storing in Flash */ -void TPL_writeBinary(Unit *unit, PayloadBuilder *pb) +void UTPL_writeBinary(Unit *unit, PayloadBuilder *pb) { struct priv *priv = unit->data; @@ -32,7 +32,7 @@ void TPL_writeBinary(Unit *unit, PayloadBuilder *pb) // ------------------------------------------------------------------------ /** Parse a key-value pair from the INI file */ -error_t TPL_loadIni(Unit *unit, const char *key, const char *value) +error_t UTPL_loadIni(Unit *unit, const char *key, const char *value) { bool suc = true; struct priv *priv = unit->data; @@ -49,7 +49,7 @@ error_t TPL_loadIni(Unit *unit, const char *key, const char *value) } /** Generate INI file section for the unit */ -void TPL_writeIni(Unit *unit, IniWriter *iw) +void UTPL_writeIni(Unit *unit, IniWriter *iw) { struct priv *priv = unit->data; diff --git a/units/template/unit_tpl.c b/units/template/unit_tpl.c index 8770354..30ce290 100644 --- a/units/template/unit_tpl.c +++ b/units/template/unit_tpl.c @@ -15,7 +15,8 @@ enum TplCmd_ { }; /** Handle a request message */ -static error_t TPL_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) +static error_t UTPL_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, + PayloadParser *pp) { switch (command) { default: @@ -30,7 +31,7 @@ static error_t TPL_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Pa * * @param unit */ -static void TPL_updateTick(Unit *unit) +static void UTPL_updateTick(Unit *unit) { // } @@ -42,15 +43,15 @@ const UnitDriver UNIT_TPL = { .name = "TPL", .description = "Template unit", // Settings - .preInit = TPL_preInit, - .cfgLoadBinary = TPL_loadBinary, - .cfgWriteBinary = TPL_writeBinary, - .cfgLoadIni = TPL_loadIni, - .cfgWriteIni = TPL_writeIni, + .preInit = UTPL_preInit, + .cfgLoadBinary = UTPL_loadBinary, + .cfgWriteBinary = UTPL_writeBinary, + .cfgLoadIni = UTPL_loadIni, + .cfgWriteIni = UTPL_writeIni, // Init - .init = TPL_init, - .deInit = TPL_deInit, + .init = UTPL_init, + .deInit = UTPL_deInit, // Function - .handleRequest = TPL_handleRequest, - .updateTick = TPL_updateTick, + .handleRequest = UTPL_handleRequest, + .updateTick = UTPL_updateTick, }; diff --git a/units/template/unit_tpl.h b/units/template/unit_tpl.h index 76d4f10..a0865a5 100644 --- a/units/template/unit_tpl.h +++ b/units/template/unit_tpl.h @@ -4,39 +4,13 @@ // Digital input unit; single or multiple pin read access on one port (A-F) // -#ifndef U_DIN_H -#define U_DIN_H +#ifndef U_TPL_H +#define U_TPL_H #include "unit.h" -extern const UnitDriver UNIT_DIN; +extern const UnitDriver UNIT_TPL; -/** - * Read pins - * - * @param unit - unit instance - * @param packed - output; the packed (right aligned) bits representing the pins, highest to lowest, are written here. - * @return success - */ -error_t UU_DI_Read(Unit *unit, uint16_t *packed); +// UU_ prototypes -/** - * Arm pins for trigger generation - * - * @param unit - unit instance - * @param arm_single_packed - packed bit map of pins to arm for single trigger - * @param arm_auto_packed - packed bit map of pins to arm for auto trigger (repeated) - * @return success - */ -error_t UU_DI_Arm(Unit *unit, uint16_t arm_single_packed, uint16_t arm_auto_packed); - -/** - * Dis-arm pins to not generate events - * - * @param unit - unit instance - * @param disarm_packed - packed bit map of pins to dis-arm - * @return success - */ -error_t UU_DI_DisArm(Unit *unit, uint16_t disarm_packed); - -#endif //U_DIN_H +#endif //U_TPL_H From 3ebc19fc2dfbc3e7070584e49a0f79c19c8b47b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 3 Feb 2018 22:06:31 +0100 Subject: [PATCH 02/20] fixes and start of ADC. Not really working yet --- FreeRTOSConfig.h | 2 +- framework/resources.h | 8 +- framework/unit_registry.c | 2 +- freertos.c | 2 + gex.mk | 1 + platform/debug_uart.c | 5 +- platform/hw_utils.c | 87 ++++++++++- platform/hw_utils.h | 31 +++- platform/irq_dispatcher.c | 69 +++++++-- platform/platform.c | 2 + platform/platform.h | 1 + platform/timebase.c | 2 + units/1wire/_ow_init.c | 1 - units/1wire/_ow_internal.h | 3 - units/1wire/_ow_search.c | 1 - units/1wire/_ow_settings.c | 1 - units/1wire/unit_1wire.c | 2 - units/adc/_adc_init.c | 225 ++++++++++++++++++++++++++++- units/adc/_adc_internal.h | 22 +++ units/adc/_adc_settings.c | 45 +++++- units/adc/unit_adc.c | 4 +- units/digital_in/_din_init.c | 2 - units/digital_in/_din_settings.c | 1 - units/digital_in/unit_din.c | 2 - units/digital_out/_dout_init.c | 1 - units/digital_out/_dout_settings.c | 1 - units/i2c/_i2c_init.c | 4 +- units/i2c/_i2c_settings.c | 1 - units/neopixel/_npx_init.c | 6 +- units/spi/_spi_init.c | 6 +- units/test/unit_test.c | 2 +- units/usart/_usart_dmas.c | 4 +- units/usart/_usart_init.c | 11 +- units/usart/_usart_internal.h | 4 + units/usart/_usart_settings.c | 6 +- utils/malloc_safe.h | 8 +- utils/stacksmon.h | 2 +- 37 files changed, 503 insertions(+), 74 deletions(-) diff --git a/FreeRTOSConfig.h b/FreeRTOSConfig.h index 51bc917..2be418b 100644 --- a/FreeRTOSConfig.h +++ b/FreeRTOSConfig.h @@ -153,7 +153,7 @@ to exclude the API function. */ #elif defined(GEX_PLAT_F072_DISCOVERY) // This is for F072 #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 3 - #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 3 + #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 1 #define configPRIO_BITS 2 diff --git a/framework/resources.h b/framework/resources.h index 2fec7a3..7ee9f4f 100644 --- a/framework/resources.h +++ b/framework/resources.h @@ -38,7 +38,7 @@ void rsc_init_registry(void); * @param pin - pin number 0-15 * @return success */ -error_t rsc_claim_pin(Unit *unit, char port_name, uint8_t pin); +error_t rsc_claim_pin(Unit *unit, char port_name, uint8_t pin) __attribute__((warn_unused_result)); /** * Claim a resource by the Resource enum @@ -47,7 +47,7 @@ error_t rsc_claim_pin(Unit *unit, char port_name, uint8_t pin); * @param rsc - resource to claim * @return success */ -error_t rsc_claim(Unit *unit, Resource rsc); +error_t rsc_claim(Unit *unit, Resource rsc) __attribute__((warn_unused_result)); /** * Claim a range of resources (use for resources of the same type, e.g. USART1-5) @@ -57,7 +57,7 @@ error_t rsc_claim(Unit *unit, Resource rsc); * @param rsc1 - last resource to claim * @return success (E_SUCCESS = complete claim) */ -error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1); +error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1) __attribute__((warn_unused_result)); /** * Claim GPIOs by bitmask and port name, atomically. @@ -68,7 +68,7 @@ error_t rsc_claim_range(Unit *unit, Resource rsc0, Resource rsc1); * @param pins - pins, bitmask * @return success (E_SUCCESS = complete claim) */ -error_t rsc_claim_gpios(Unit *unit, char port_name, uint16_t pins); +error_t rsc_claim_gpios(Unit *unit, char port_name, uint16_t pins) __attribute__((warn_unused_result)); /** * Release all resources held by a unit, de-init held GPIOs diff --git a/framework/unit_registry.c b/framework/unit_registry.c index f0203fb..482dda8 100644 --- a/framework/unit_registry.c +++ b/framework/unit_registry.c @@ -606,7 +606,7 @@ void ureg_tick_units(void) UlistEntry *li = ulist_head; while (li != NULL) { Unit *const pUnit = &li->unit; - if (pUnit->status == E_SUCCESS && pUnit->tick_interval > 0) { + if (pUnit && pUnit->data && pUnit->status == E_SUCCESS && pUnit->tick_interval > 0) { if (pUnit->_tick_cnt == 0) { if (pUnit->driver->updateTick) { pUnit->driver->updateTick(pUnit); diff --git a/freertos.c b/freertos.c index 045e3e0..3c69f35 100644 --- a/freertos.c +++ b/freertos.c @@ -146,6 +146,8 @@ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ stackmon_register("Main", mainTaskStack, sizeof(mainTaskStack)); stackmon_register("Job+Msg", msgJobQueTaskStack, sizeof(msgJobQueTaskStack)); + stackmon_register("Idle", xIdleStack, sizeof(xIdleStack)); + stackmon_register("Timers", xTimersStack, sizeof(xTimersStack)); /* USER CODE END Init */ /* Create the mutex(es) */ diff --git a/gex.mk b/gex.mk index ee4b54c..94a9543 100644 --- a/gex.mk +++ b/gex.mk @@ -13,6 +13,7 @@ GEX_SRC_DIR = \ User/units/1wire \ User/units/i2c \ User/units/spi \ + User/units/adc \ User/TinyFrame \ User/CWPack \ User/tasks diff --git a/platform/debug_uart.c b/platform/debug_uart.c index ce28d75..f572bc2 100644 --- a/platform/debug_uart.c +++ b/platform/debug_uart.c @@ -66,7 +66,10 @@ void DebugUart_PreInit(void) { // configure AF only if platform uses AF numbers #if !PLAT_NO_AFNUM - hw_configure_gpio_af(DEBUG_USART_PORT, DEBUG_USART_PIN, DEBUG_USART_AF); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" + (void) hw_configure_gpio_af(DEBUG_USART_PORT, DEBUG_USART_PIN, DEBUG_USART_AF); +#pragma GCC diagnostic pop #endif hw_periph_clock_enable(DEBUG_USART); diff --git a/platform/hw_utils.c b/platform/hw_utils.c index a14c33b..b395d44 100644 --- a/platform/hw_utils.c +++ b/platform/hw_utils.c @@ -4,6 +4,7 @@ #include "platform.h" #include +#include #include "hw_utils.h" #include "macro.h" @@ -263,16 +264,64 @@ char * pinmask2str(uint16_t pins, char *buffer) if (!first) { b += SPRINTF(b, ", "); } + if (start == (uint32_t)(i+1)) { b += SPRINTF(b, "%"PRIu32, start); } -// else if (start == (uint32_t)(i+2)) { -// // exception for 2-long ranges - don't show as range -// b += SPRINTF(b, "%"PRIu32",%"PRIu32, start, i + 1); -// } else { b += SPRINTF(b, "%"PRIu32"-%"PRIu32, start, i + 1); } + + first = false; + on = false; + } + } + } + + return buffer; +} + +char * pinmask2str_up(uint16_t pins, char *buffer) +{ + char *b = buffer; + uint32_t start = 0; + bool on = false; + bool first = true; + + // shortcut if none are set + if (pins == 0) { + buffer[0] = 0; + return buffer; + } + + for (int32_t i = 0; i <= 16; i++) { + bool bit; + + if (i == 16) { + bit = false; + } else { + bit = 0 != (pins & 1); + pins >>= 1; + } + + if (bit) { + if (!on) { + start = (uint32_t) i; + on = true; + } + } else { + if (on) { + if (!first) { + b += SPRINTF(b, ", "); + } + + if (start == (uint32_t)(i-1)) { + b += SPRINTF(b, "%"PRIu32, start); + } + else { + b += SPRINTF(b, "%"PRIu32"-%"PRIu32, start, i - 1); + } + first = false; on = false; } @@ -387,6 +436,36 @@ error_t hw_configure_sparse_pins(char port_name, uint16_t mask, GPIO_TypeDef **p return E_SUCCESS; } +/** Solve a timer/counter's count and prescaller value */ +bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit, + uint16_t *presc, uint32_t *count, float *real_freq) +{ + if (required_freq == 0) return false; + const float fPresc = base_freq / required_freq; + uint32_t wCount = (uint32_t) lrintf(fPresc); + + const uint32_t ceil = is16bit ? UINT16_MAX : UINT32_MAX; + + uint32_t wPresc = 1; + while (wCount > ceil) { + wPresc <<= 1; + wCount >>= 1; + } + + if (wPresc > ceil || count == 0) { + return false; + } + + *count = wCount; + *presc = (uint16_t) wPresc; + + if (wPresc * wCount == 0) return false; + *real_freq = (base_freq / (wPresc * wCount)); + + return true; +} + + void hw_periph_clock_enable(void *periph) { // GPIOs are enabled by default on start-up diff --git a/platform/hw_utils.h b/platform/hw_utils.h index 15baecd..240d577 100644 --- a/platform/hw_utils.h +++ b/platform/hw_utils.h @@ -78,6 +78,7 @@ uint16_t parse_pinmask(const char *value, bool *suc); /** * Convert a pin bitmap to the ASCII format understood by str_parse_pinmask() + * This is the downto variant (15..0) * * @param pins - sparse pin map * @param buffer - output string buffer @@ -85,6 +86,16 @@ uint16_t parse_pinmask(const char *value, bool *suc); */ char * pinmask2str(uint16_t pins, char *buffer); +/** + * Convert a pin bitmap to the ASCII format understood by str_parse_pinmask() + * This is the ascending variant (0..15) + * + * @param pins - sparse pin map + * @param buffer - output string buffer + * @return the output buffer + */ +char * pinmask2str_up(uint16_t pins, char *buffer); + /** * Spread packed port pins using a mask * @@ -126,7 +137,7 @@ void hw_deinit_unit_pins(Unit *unit); * @param ll_af - LL alternate function constant * @return success */ -error_t hw_configure_gpio_af(char port_name, uint8_t pin_num, uint32_t ll_af); +error_t hw_configure_gpio_af(char port_name, uint8_t pin_num, uint32_t ll_af) __attribute__((warn_unused_result)); /** * Configure multiple pins using the bitmap pattern @@ -140,7 +151,7 @@ error_t hw_configure_gpio_af(char port_name, uint8_t pin_num, uint32_t ll_af); */ 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) __attribute__((warn_unused_result)); /** Helper struct for defining alternate mappings */ struct PinAF { @@ -161,6 +172,22 @@ void hw_periph_clock_enable(void *periph); */ void hw_periph_clock_disable(void *periph); +/** + * Solve a timer/counter's count and prescaller value to meet the desired + * overflow frequency. The resulting values are the dividing factors; + * subtract 1 before writing them into the peripheral registers. + * + * @param[in] base_freq - the counter's input clock frequency in Hz + * @param[in] required_freq - desired overflow frequency + * @param[in] is16bit - limit counter to 16 bits (prescaller is always 16-bit) + * @param[out] presc - field for storing the computed prescaller value + * @param[out] count - field for storing the computed counter value + * @param[out] real_freq - field for storing the computed real frequency + * @return true on success + */ +bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit, + uint16_t *presc, uint32_t *count, float *real_freq); + // ---------- LL extras ------------ static inline bool LL_DMA_IsActiveFlag_G(uint32_t isr_snapshot, uint8_t channel) diff --git a/platform/irq_dispatcher.c b/platform/irq_dispatcher.c index 2908254..13e18a0 100644 --- a/platform/irq_dispatcher.c +++ b/platform/irq_dispatcher.c @@ -61,6 +61,10 @@ static struct callbacks_ { struct cbslot dma2_7; struct cbslot dma2_8; + struct cbslot tim6; + struct cbslot tim7; + struct cbslot tim15; + // XXX add more callbacks here when needed } callbacks; @@ -68,41 +72,62 @@ void irqd_init(void) { memset(&callbacks, 0, sizeof(callbacks)); + // TODO move the priorities to some define + // NVIC_EnableIRQ(WWDG_IRQn); /*!< Window WatchDog Interrupt */ // NVIC_EnableIRQ(PVD_VDDIO2_IRQn); /*!< PVD & VDDIO2 Interrupt through EXTI Lines 16 and 31 */ // NVIC_EnableIRQ(RTC_IRQn); /*!< RTC Interrupt through EXTI Lines 17, 19 and 20 */ // NVIC_EnableIRQ(FLASH_IRQn); /*!< FLASH global Interrupt */ // NVIC_EnableIRQ(RCC_CRS_IRQn); /*!< RCC & CRS global Interrupt */ + NVIC_EnableIRQ(EXTI0_1_IRQn); /*!< EXTI Line 0 and 1 Interrupt */ NVIC_EnableIRQ(EXTI2_3_IRQn); /*!< EXTI Line 2 and 3 Interrupt */ NVIC_EnableIRQ(EXTI4_15_IRQn); /*!< EXTI Line 4 to 15 Interrupt */ + HAL_NVIC_SetPriority(EXTI0_1_IRQn, 2, 0); + 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(DMA1_Channel1_IRQn); /*!< DMA1 Channel 1 Interrupt */ NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); /*!< DMA1 Channel 2 and Channel 3 Interrupt */ NVIC_EnableIRQ(DMA1_Channel4_5_6_7_IRQn); /*!< DMA1 Channel 4 to Channel 7 Interrupt */ - HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 3, 0); - HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 3, 0); - HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 3, 0); + HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 2, 0); + HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2, 0); + HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 2, 0); + // NVIC_EnableIRQ(ADC1_COMP_IRQn); /*!< ADC1 and COMP interrupts (ADC interrupt combined with EXTI Lines 21 and 22 */ // NVIC_EnableIRQ(TIM1_IRQn); /*!< TIM1 global Interrupt */ // NVIC_EnableIRQ(TIM2_IRQn); /*!< TIM2 global Interrupt */ // NVIC_EnableIRQ(TIM3_IRQn); /*!< TIM3 global Interrupt */ -// NVIC_EnableIRQ(TIM6_DAC_IRQn); /*!< TIM6 global and DAC channel underrun error Interrupt */ -// NVIC_EnableIRQ(TIM7_IRQn); /*!< TIM7 global Interrupt */ -// --used internally-- NVIC_EnableIRQ(TIM14_IRQn); /*!< TIM14 global Interrupt */ -// NVIC_EnableIRQ(TIM15_IRQn); /*!< TIM15 global Interrupt */ + + NVIC_EnableIRQ(TIM6_DAC_IRQn); /*!< TIM6 global and DAC channel underrun error Interrupt */ + HAL_NVIC_SetPriority(TIM7_IRQn, 2, 0); // Used for DAC timing + + NVIC_EnableIRQ(TIM7_IRQn); /*!< TIM7 global Interrupt */ + HAL_NVIC_SetPriority(TIM7_IRQn, 2, 0); + + /* Tim14 is used for HAL timebase, because SysTick is used to time FreeRTOS and has the lowest priority. */ + /* Tim14's priority is set to 0 in the init routine, which runs early in the startup sequence */ + // NVIC_EnableIRQ(TIM14_IRQn); /*!< TIM14 global Interrupt */ + + NVIC_EnableIRQ(TIM15_IRQn); /*!< TIM15 global Interrupt */ + HAL_NVIC_SetPriority(TIM15_IRQn, 2, 0); + // NVIC_EnableIRQ(TIM16_IRQn); /*!< TIM16 global Interrupt */ // NVIC_EnableIRQ(TIM17_IRQn); /*!< TIM17 global Interrupt */ // NVIC_EnableIRQ(I2C1_IRQn); /*!< I2C1 Event Interrupt & EXTI Line23 Interrupt (I2C1 wakeup) */ // NVIC_EnableIRQ(I2C2_IRQn); /*!< I2C2 Event Interrupt */ // NVIC_EnableIRQ(SPI1_IRQn); /*!< SPI1 global Interrupt */ // NVIC_EnableIRQ(SPI2_IRQn); /*!< SPI2 global Interrupt */ + NVIC_EnableIRQ(USART1_IRQn); /*!< USART1 global Interrupt & EXTI Line25 Interrupt (USART1 wakeup) */ NVIC_EnableIRQ(USART2_IRQn); /*!< USART2 global Interrupt & EXTI Line26 Interrupt (USART2 wakeup) */ NVIC_EnableIRQ(USART3_4_IRQn); /*!< USART3 and USART4 global Interrupt */ - HAL_NVIC_SetPriority(USART1_IRQn, 3, 0); - HAL_NVIC_SetPriority(USART2_IRQn, 3, 0); - HAL_NVIC_SetPriority(USART3_4_IRQn, 3, 0); + HAL_NVIC_SetPriority(USART1_IRQn, 2, 0); + HAL_NVIC_SetPriority(USART2_IRQn, 2, 0); + HAL_NVIC_SetPriority(USART3_4_IRQn, 2, 0); + // NVIC_EnableIRQ(CEC_CAN_IRQn); /*!< CEC and CAN global Interrupts & EXTI Line27 Interrupt */ // NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); /*!< TIM1 Break, Update, Trigger and Commutation Interrupt */ // - handled by hal msp init @@ -130,6 +155,10 @@ static struct cbslot *get_slot_for_periph(void *periph) else if (periph == USART5) slot = &callbacks.usart5; #endif + else if (periph == TIM6) slot = &callbacks.tim6; + else if (periph == TIM7) slot = &callbacks.tim7; + else if (periph == TIM15) slot = &callbacks.tim15; + else if (periph >= EXTIS[0] && periph <= EXTIS[15]) { slot = &callbacks.exti[periph - EXTIS[0]]; } @@ -265,6 +294,26 @@ void EXTI4_15_IRQHandler(void) } +// ------------ INTERRUPTS ------------- + +// TIM14 is used to generate HAL timebase and its handler is in the file "timebase.c" + +void TIM6_DAC_IRQHandler(void) +{ + CALL_IRQ_HANDLER(callbacks.tim6); +} + +void TIM7_IRQHandler(void) +{ + CALL_IRQ_HANDLER(callbacks.tim7); +} + +void TIM15_IRQHandler(void) +{ + CALL_IRQ_HANDLER(callbacks.tim15); +} + + // other ISRs... #if 0 diff --git a/platform/platform.c b/platform/platform.c index e1eff83..cead5c4 100644 --- a/platform/platform.c +++ b/platform/platform.c @@ -13,6 +13,7 @@ #include "units/neopixel/unit_neopixel.h" #include "units/i2c/unit_i2c.h" #include "units/1wire/unit_1wire.h" +#include "units/adc/unit_adc.h" #include "units/test/unit_test.h" #include "units/usart/unit_usart.h" #include "units/spi/unit_spi.h" @@ -84,6 +85,7 @@ void plat_init_resources(void) ureg_add_type(&UNIT_SPI); ureg_add_type(&UNIT_USART); ureg_add_type(&UNIT_1WIRE); + ureg_add_type(&UNIT_ADC); // Free all present resources { diff --git a/platform/platform.h b/platform/platform.h index f9199df..1b52749 100644 --- a/platform/platform.h +++ b/platform/platform.h @@ -13,6 +13,7 @@ #include #include #include +#include // FreeRTOS includes #include diff --git a/platform/timebase.c b/platform/timebase.c index 932c92a..958f8cd 100644 --- a/platform/timebase.c +++ b/platform/timebase.c @@ -15,6 +15,8 @@ HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) // This makes it a good choice for the timebase generation. We set it to generate // an interrupt every 1 ms + assert_param(TickPriority == 0); // any other setting can lead to crashes + // - TIM14 is always up-counting // - using APB1 clock __HAL_RCC_TIM14_CLK_ENABLE(); diff --git a/units/1wire/_ow_init.c b/units/1wire/_ow_init.c index 10f21f7..0893d10 100644 --- a/units/1wire/_ow_init.c +++ b/units/1wire/_ow_init.c @@ -7,7 +7,6 @@ #define OW_INTERNAL #include "_ow_internal.h" -#include "_ow_init.h" /** Allocate data structure and set defaults */ error_t OW_preInit(Unit *unit) diff --git a/units/1wire/_ow_internal.h b/units/1wire/_ow_internal.h index 7e00a6d..436dd93 100644 --- a/units/1wire/_ow_internal.h +++ b/units/1wire/_ow_internal.h @@ -27,9 +27,6 @@ struct priv { struct ow_search_state searchState; }; -/** Allocate data structure and set defaults */ -error_t OW_preInit(Unit *unit); - /** Load from a binary buffer stored in Flash */ void OW_loadBinary(Unit *unit, PayloadParser *pp); diff --git a/units/1wire/_ow_search.c b/units/1wire/_ow_search.c index 5a5a978..50cbe79 100644 --- a/units/1wire/_ow_search.c +++ b/units/1wire/_ow_search.c @@ -9,7 +9,6 @@ #include "_ow_search.h" #include "_ow_internal.h" #include "_ow_low_level.h" -#include "_ow_checksum.h" #include "_ow_commands.h" void ow_search_init(Unit *unit, uint8_t command, bool test_checksums) diff --git a/units/1wire/_ow_settings.c b/units/1wire/_ow_settings.c index c879c9c..c43b3bf 100644 --- a/units/1wire/_ow_settings.c +++ b/units/1wire/_ow_settings.c @@ -7,7 +7,6 @@ #define OW_INTERNAL #include "_ow_internal.h" -#include "_ow_settings.h" /** Load from a binary buffer stored in Flash */ void OW_loadBinary(Unit *unit, PayloadParser *pp) diff --git a/units/1wire/unit_1wire.c b/units/1wire/unit_1wire.c index 84a187a..194c391 100644 --- a/units/1wire/unit_1wire.c +++ b/units/1wire/unit_1wire.c @@ -9,8 +9,6 @@ // 1WIRE master #define OW_INTERNAL #include "_ow_internal.h" -#include "_ow_init.h" -#include "_ow_settings.h" #include "_ow_low_level.h" /** diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index cc228d0..5f2d9e4 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -8,29 +8,241 @@ #define ADC_INTERNAL #include "_adc_internal.h" +const uint32_t LL_ADC_SAMPLETIMES[] = { + LL_ADC_SAMPLINGTIME_1CYCLE_5, + LL_ADC_SAMPLINGTIME_7CYCLES_5, + LL_ADC_SAMPLINGTIME_13CYCLES_5, + LL_ADC_SAMPLINGTIME_28CYCLES_5, + LL_ADC_SAMPLINGTIME_41CYCLES_5, + LL_ADC_SAMPLINGTIME_55CYCLES_5, + LL_ADC_SAMPLINGTIME_71CYCLES_5, + LL_ADC_SAMPLINGTIME_239CYCLES_5, +}; + + /** Allocate data structure and set defaults */ error_t UADC_preInit(Unit *unit) { struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv)); if (priv == NULL) return E_OUT_OF_MEM; - // + priv->channels = 1; // PA0 + priv->enable_tsense = false; + priv->enable_vref = false; + priv->sample_time = 0b010; // 13.5c + priv->frequency = 4; //1000 return E_SUCCESS; } +static void UADC_DMA_Handler(void *arg) +{ + Unit *unit = arg; + + dbg("ADC DMA ISR hit"); + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + const uint32_t isrsnapshot = priv->DMAx->ISR; + + if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) { + bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_chnum); + bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_chnum); + + // Here we have to either copy it somewhere else, or notify another thread (queue?) + // that the data is ready for reading + + if (ht) { + uint16_t start = 0; + uint16_t end = (uint16_t) (priv->dma_buffer_size / 2); + // TODO handle first half + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + } + + if (tc) { + uint16_t start = (uint16_t) (priv->dma_buffer_size / 2); + uint16_t end = (uint16_t) priv->dma_buffer_size; + // TODO handle second half + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + } + + if (LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum)) { + // this shouldn't happen - error + dbg("ADC DMA TE!"); + LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum); + } + } +} + /** Finalize unit set-up */ error_t UADC_init(Unit *unit) { bool suc = true; struct priv *priv = unit->data; - // + // Written for F072 which has only one ADC + + TRY(rsc_claim(unit, R_ADC1)); + TRY(rsc_claim(unit, R_DMA1_1)); + TRY(rsc_claim(unit, R_TIM15)); + + priv->DMAx = DMA1; + priv->DMA_CHx = DMA1_Channel1; + priv->dma_chnum = 1; + priv->ADCx = ADC1; + priv->ADCx_Common = ADC1_COMMON; + priv->TIMx = TIM15; + + // ----------------------- CONFIGURE PINS -------------------------- + { + // Claim and configure all analog pins + priv->nb_channels = 0; + for (uint8_t i = 0; i < 16; i++) { + if (priv->channels & (1 << i)) { + char c; + uint8_t num; + if (i <= 7) { + c = 'A'; + num = i; + } + else if (i <= 9) { + c = 'B'; + num = (uint8_t) (i - 8); + } + else { + c = 'C'; + num = (uint8_t) (i - 10); + } + + TRY(rsc_claim_pin(unit, c, num)); + uint32_t ll_pin = hw_pin2ll(num, &suc); + GPIO_TypeDef *port = hw_port2periph(c, &suc); + assert_param(suc); + + LL_GPIO_SetPinPull(port, ll_pin, LL_GPIO_PULL_NO); + LL_GPIO_SetPinMode(port, ll_pin, LL_GPIO_MODE_ANALOG); + priv->nb_channels++; + } + } + if (priv->enable_tsense) priv->nb_channels++; + if (priv->enable_vref) priv->nb_channels++; + + if (priv->nb_channels == 0) { + dbg("!! Need at least 1 channel"); + return E_BAD_CONFIG; + } + } + + // ------------------- ENABLE CLOCKS -------------------------- + { + // enable peripherals clock + hw_periph_clock_enable(priv->ADCx); + hw_periph_clock_enable(priv->TIMx); + } + + // ------------------- CONFIGURE THE TIMER -------------------------- + dbg("Setting up TIMER"); + { + // Find suitable timer values + uint16_t presc; + uint32_t count; + float real_freq; + if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, + &real_freq)) { + dbg("Failed to resolve timer params."); + return E_BAD_VALUE; + } + dbg("Frequency error %d ppm, presc %d, count %d", + (int) lrintf(1000000.0f * ((real_freq - priv->frequency) / (float)priv->frequency)), (int) presc, (int) count); + + LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1)); + LL_TIM_SetAutoReload(priv->TIMx, count - 1); + LL_TIM_EnableARRPreload(priv->TIMx); + LL_TIM_EnableUpdateEvent(priv->TIMx); + LL_TIM_GenerateEvent_UPDATE(priv->TIMx); // load the prescaller value + } + + // --------------------- CONFIGURE THE ADC --------------------------- + dbg("Setting up ADC"); + { + // Calibrate the ADC + dbg("Wait for calib"); + LL_ADC_StartCalibration(priv->ADCx); + while (LL_ADC_IsCalibrationOnGoing(priv->ADCx)) {} + dbg("ADC calibrated."); + + uint32_t mask = 0; + if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT; + if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR; + LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask); + LL_ADC_SetDataAlignment(priv->ADCx, LL_ADC_DATA_ALIGN_RIGHT); + LL_ADC_SetResolution(priv->ADCx, LL_ADC_RESOLUTION_12B); + LL_ADC_REG_SetDMATransfer(priv->ADCx, LL_ADC_REG_DMA_TRANSFER_UNLIMITED); + + // configure channels + LL_ADC_REG_SetSequencerChannels(priv->ADCx, priv->channels); + if (priv->enable_tsense) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_TEMPSENSOR); + if (priv->enable_vref) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_VREFINT); + + LL_ADC_REG_SetTriggerSource(priv->ADCx, LL_ADC_REG_TRIG_EXT_TIM15_TRGO); + + LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->sample_time]); + + LL_ADC_Enable(priv->ADCx); + } + + // --------------------- CONFIGURE DMA ------------------------------- + dbg("Setting up DMA"); + { + // The length must be a 2*multiple of the number of channels + // this is a horrible way to do it but will work + uint16_t itemcount = (uint16_t) (priv->nb_channels * (uint16_t) (UADC_DMA_MAX_BUF_LEN / (2 * priv->nb_channels))); + priv->dma_buffer_size = (uint16_t) (itemcount * 2); + dbg("DMA item count is %d (%d bytes)", itemcount, priv->dma_buffer_size); + + priv->dma_buffer = malloc_ck(priv->dma_buffer_size); + if (NULL == priv->dma_buffer) return E_OUT_OF_MEM; + assert_param(((uint32_t) priv->dma_buffer & 3) == 0); // must be aligned + + { + LL_DMA_InitTypeDef init; + LL_DMA_StructInit(&init); + init.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + + init.Mode = LL_DMA_MODE_CIRCULAR; + init.NbData = itemcount; + + init.PeriphOrM2MSrcAddress = (uint32_t) &priv->ADCx->DR; + init.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD; + init.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + + init.MemoryOrM2MDstAddress = (uint32_t) priv->dma_buffer; + init.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD; + init.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + + assert_param(SUCCESS == LL_DMA_Init(priv->DMAx, priv->dma_chnum, &init)); + + irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit); + // Interrupt on transfer 1/2 and complete + // We will capture the first and second half and send it while the other half is being filled. + LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); + } + + LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); + } + + dbg("ADC inited, starting the timer ..."); + + // FIXME - temporary demo - counter start... + LL_TIM_EnableCounter(priv->TIMx); return E_SUCCESS; } + /** Tear down the unit */ void UADC_deInit(Unit *unit) { @@ -38,7 +250,14 @@ void UADC_deInit(Unit *unit) // de-init peripherals if (unit->status == E_SUCCESS ) { - // + //LL_ADC_DeInit(priv->ADCx); + LL_ADC_CommonDeInit(priv->ADCx_Common); + LL_TIM_DeInit(priv->TIMx); + + irqd_detach(priv->DMAx, UADC_DMA_Handler); + LL_DMA_DeInit(priv->DMAx, priv->dma_chnum); + + free_ck(priv->dma_buffer); } // Release all resources, deinit pins diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index c6c7225..319f218 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -14,10 +14,32 @@ /** Private data structure */ struct priv { // settings + uint16_t channels; //!< bit flags (will be recorded in order 0-15) + bool enable_tsense; //!< append a signal from the temperature channel (voltage proportional to Tj) + bool enable_vref; //!< append a signal from the internal voltage reference + uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge + uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately + + // TODO averaging (maybe a separate component?) + // TODO threshold watchdog with hysteresis (maybe a separate component?) + // TODO trigger level, edge direction, hold-off, pre-trigger buffer (extract from the DMA buffer) // internal state + ADC_TypeDef *ADCx; + ADC_Common_TypeDef *ADCx_Common; + TIM_TypeDef *TIMx; + DMA_TypeDef *DMAx; + uint8_t dma_chnum; + DMA_Channel_TypeDef *DMA_CHx; + uint16_t *dma_buffer; + uint8_t nb_channels; // nr of enabled adc channels + uint16_t dma_buffer_size; // real number of bytes }; +// max size of the DMA buffer. The actual buffer size will be adjusted to accommodate +// an even number of sample groups (sets of channels) +#define UADC_DMA_MAX_BUF_LEN 512 + /** Allocate data structure and set defaults */ error_t UADC_preInit(Unit *unit); diff --git a/units/adc/_adc_settings.c b/units/adc/_adc_settings.c index 79101cd..e35ec86 100644 --- a/units/adc/_adc_settings.c +++ b/units/adc/_adc_settings.c @@ -16,7 +16,11 @@ void UADC_loadBinary(Unit *unit, PayloadParser *pp) uint8_t version = pp_u8(pp); (void)version; - // + priv->channels = pp_u16(pp); + priv->enable_tsense = pp_bool(pp); + priv->enable_vref = pp_bool(pp); + priv->sample_time = pp_u8(pp); + priv->frequency = pp_u32(pp); } /** Write to a binary buffer for storing in Flash */ @@ -26,7 +30,11 @@ void UADC_writeBinary(Unit *unit, PayloadBuilder *pb) pb_u8(pb, 0); // version - // + pb_u16(pb, priv->channels); + pb_bool(pb, priv->enable_tsense); + pb_bool(pb, priv->enable_vref); + pb_u8(pb, priv->sample_time); + pb_u32(pb, priv->frequency); } // ------------------------------------------------------------------------ @@ -37,8 +45,21 @@ error_t UADC_loadIni(Unit *unit, const char *key, const char *value) bool suc = true; struct priv *priv = unit->data; - if (false) { - // + if (streq(key, "channels")) { + priv->channels = parse_pinmask(value, &suc); + } + else if (streq(key, "enable_tsense")) { + priv->enable_tsense = str_parse_yn(value, &suc); + } + else if (streq(key, "enable_vref")) { + priv->enable_vref = str_parse_yn(value, &suc); + } + else if (streq(key, "sample_time")) { + priv->sample_time = (uint8_t) avr_atoi(value); + if (priv->sample_time > 7) return E_BAD_VALUE; + } + else if (streq(key, "frequency")) { + priv->frequency = (uint32_t) avr_atoi(value); } else { return E_BAD_KEY; @@ -53,6 +74,20 @@ void UADC_writeIni(Unit *unit, IniWriter *iw) { struct priv *priv = unit->data; - // + iw_comment(iw, "Enabled channels, comma separated"); + iw_comment(iw, "0-7 = A0-A7, 8-9 = B0-B1, 10-15 = C0-C5"); + iw_entry(iw, "channels", "%s", pinmask2str_up(priv->channels, unit_tmp512)); + + iw_comment(iw, "Enable Tsense channel"); + iw_entry(iw, "enable_tsense", str_yn(priv->enable_tsense)); + + iw_comment(iw, "Enable Vref channel"); + iw_entry(iw, "enable_vref", str_yn(priv->enable_tsense)); + + iw_comment(iw, "Sampling time (0-7)"); + iw_entry(iw, "sample_time", "%d", (int)priv->sample_time); + + iw_comment(iw, "Sampling frequency (Hz)"); + iw_entry(iw, "frequency", "%d", (int)priv->frequency); } diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index e966b18..c1a3e73 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -11,7 +11,7 @@ // ------------------------------------------------------------------------ enum TplCmd_ { - // + CMD_DUMMY, }; /** Handle a request message */ @@ -28,7 +28,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P /** Unit template */ const UnitDriver UNIT_ADC = { .name = "ADC", - .description = "Template unit", + .description = "Analog inputs", // Settings .preInit = UADC_preInit, .cfgLoadBinary = UADC_loadBinary, diff --git a/units/digital_in/_din_init.c b/units/digital_in/_din_init.c index ab7be55..7d501ce 100644 --- a/units/digital_in/_din_init.c +++ b/units/digital_in/_din_init.c @@ -7,8 +7,6 @@ #define DIN_INTERNAL #include "_din_internal.h" -#include "_din_init.h" -#include "_din_exti.h" /** Allocate data structure and set defaults */ error_t DIn_preInit(Unit *unit) diff --git a/units/digital_in/_din_settings.c b/units/digital_in/_din_settings.c index 699a719..5c5f235 100644 --- a/units/digital_in/_din_settings.c +++ b/units/digital_in/_din_settings.c @@ -7,7 +7,6 @@ #define DIN_INTERNAL #include "_din_internal.h" -#include "_din_settings.h" /** Load from a binary buffer stored in Flash */ void DIn_loadBinary(Unit *unit, PayloadParser *pp) diff --git a/units/digital_in/unit_din.c b/units/digital_in/unit_din.c index 73b9f05..0e66784 100644 --- a/units/digital_in/unit_din.c +++ b/units/digital_in/unit_din.c @@ -7,8 +7,6 @@ #define DIN_INTERNAL #include "_din_internal.h" -#include "_din_settings.h" -#include "_din_init.h" // ------------------------------------------------------------------------ diff --git a/units/digital_out/_dout_init.c b/units/digital_out/_dout_init.c index 55856cf..8ee2a0c 100644 --- a/units/digital_out/_dout_init.c +++ b/units/digital_out/_dout_init.c @@ -7,7 +7,6 @@ #define DOUT_INTERNAL #include "_dout_internal.h" -#include "_dout_init.h" /** Allocate data structure and set defaults */ error_t DOut_preInit(Unit *unit) diff --git a/units/digital_out/_dout_settings.c b/units/digital_out/_dout_settings.c index b262b6b..f275504 100644 --- a/units/digital_out/_dout_settings.c +++ b/units/digital_out/_dout_settings.c @@ -7,7 +7,6 @@ #define DOUT_INTERNAL #include "_dout_internal.h" -#include "_dout_settings.h" /** Load from a binary buffer stored in Flash */ void DOut_loadBinary(Unit *unit, PayloadParser *pp) diff --git a/units/i2c/_i2c_init.c b/units/i2c/_i2c_init.c index 2b696fa..a2bde44 100644 --- a/units/i2c/_i2c_init.c +++ b/units/i2c/_i2c_init.c @@ -118,8 +118,8 @@ error_t UI2C_init(Unit *unit) TRY(rsc_claim_pin(unit, portname, pin_sda)); TRY(rsc_claim_pin(unit, portname, pin_scl)); - hw_configure_gpio_af(portname, pin_sda, af_i2c); - hw_configure_gpio_af(portname, pin_scl, af_i2c); + TRY(hw_configure_gpio_af(portname, pin_sda, af_i2c)); + TRY(hw_configure_gpio_af(portname, pin_scl, af_i2c)); hw_periph_clock_enable(priv->periph); diff --git a/units/i2c/_i2c_settings.c b/units/i2c/_i2c_settings.c index 348e756..72ec502 100644 --- a/units/i2c/_i2c_settings.c +++ b/units/i2c/_i2c_settings.c @@ -7,7 +7,6 @@ #define I2C_INTERNAL #include "_i2c_internal.h" -#include "_i2c_settings.h" /** Load from a binary buffer stored in Flash */ void UI2C_loadBinary(Unit *unit, PayloadParser *pp) diff --git a/units/neopixel/_npx_init.c b/units/neopixel/_npx_init.c index 471aab0..f27a0f0 100644 --- a/units/neopixel/_npx_init.c +++ b/units/neopixel/_npx_init.c @@ -13,10 +13,10 @@ error_t Npx_preInit(Unit *unit) { struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv)); - if (priv == NULL) + if (priv == NULL) return E_OUT_OF_MEM; - // some defaults - priv->pin_number = 0; + // some defaults + priv->pin_number = 0; priv->port_name = 'A'; priv->pixels = 1; diff --git a/units/spi/_spi_init.c b/units/spi/_spi_init.c index 3a779fd..a76bf08 100644 --- a/units/spi/_spi_init.c +++ b/units/spi/_spi_init.c @@ -132,9 +132,9 @@ error_t USPI_init(Unit *unit) TRY(rsc_claim_pin(unit, spi_portname, pin_miso)); TRY(rsc_claim_pin(unit, spi_portname, pin_sck)); - hw_configure_gpio_af(spi_portname, pin_mosi, af_spi); - hw_configure_gpio_af(spi_portname, pin_miso, af_spi); - hw_configure_gpio_af(spi_portname, pin_sck, af_spi); + TRY(hw_configure_gpio_af(spi_portname, pin_mosi, af_spi)); + TRY(hw_configure_gpio_af(spi_portname, pin_miso, af_spi)); + TRY(hw_configure_gpio_af(spi_portname, pin_sck, af_spi)); // configure SSN GPIOs { diff --git a/units/test/unit_test.c b/units/test/unit_test.c index 322daf6..c551f51 100644 --- a/units/test/unit_test.c +++ b/units/test/unit_test.c @@ -112,7 +112,7 @@ static void bw_dump(struct bulk_write *bulk, const uint8_t *chunk, uint32_t len) } dbg("\r\nBulk write at %d, len %d", (int)bulk->offset, (int)len); - PUTSN((const char *) chunk, len); + PUTSN((const char *) chunk, (uint16_t) len); PUTS("\r\n"); } diff --git a/units/usart/_usart_dmas.c b/units/usart/_usart_dmas.c index 1723832..39de18e 100644 --- a/units/usart/_usart_dmas.c +++ b/units/usart/_usart_dmas.c @@ -190,7 +190,6 @@ error_t UUSART_SetupDMAs(Unit *unit) LL_DMA_EnableChannel(priv->dma, priv->dma_rx_chnum); LL_DMA_EnableChannel(priv->dma, priv->dma_tx_chnum); - // TODO also set up usart timeout interrupt that grabs whatever is in the DMA buffer and sends it return E_SUCCESS; } @@ -205,7 +204,7 @@ static void UUSART_DMA_RxHandler(void *arg) struct priv *priv = unit->data; assert_param(priv); - uint32_t isrsnapshot = priv->dma->ISR; + const uint32_t isrsnapshot = priv->dma->ISR; if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_rx_chnum)) { bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_rx_chnum); @@ -228,6 +227,7 @@ static void UUSART_DMA_RxHandler(void *arg) if (LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_rx_chnum)) { // this shouldn't happen + dbg("USART DMA TE!"); LL_DMA_ClearFlag_TE(priv->dma, priv->dma_rx_chnum); } } diff --git a/units/usart/_usart_init.c b/units/usart/_usart_init.c index 87a733e..6a8d00f 100644 --- a/units/usart/_usart_init.c +++ b/units/usart/_usart_init.c @@ -1,7 +1,6 @@ // // Created by MightyPork on 2018/01/14. // -#include #include "platform.h" #include "unit_base.h" @@ -25,7 +24,7 @@ error_t UUSART_preInit(Unit *unit) priv->baudrate = 115200; priv->parity = 0; //!< 0-none, 1-odd, 2-even priv->stopbits = 1; //!< 0-half, 1-one, 2-1.5, 3-two - priv->direction = 3; // RXTX + priv->direction = UUSART_DIRECTION_RXTX; // RXTX priv->hw_flow_control = false; priv->clock_output = false; @@ -205,7 +204,7 @@ static inline error_t UUSART_configPins(Unit *unit) if (pins_wanted[i]) { if (mappings[i].port == 0) return E_BAD_CONFIG; TRY(rsc_claim_pin(unit, mappings[i].port, mappings[i].pin)); - hw_configure_gpio_af(mappings[i].port, mappings[i].pin, mappings[i].af); + TRY(hw_configure_gpio_af(mappings[i].port, mappings[i].pin, mappings[i].af)); } } @@ -252,9 +251,9 @@ error_t UUSART_init(Unit *unit) : LL_USART_STOPBITS_2); LL_USART_SetTransferDirection(priv->periph, - priv->direction == 1 ? LL_USART_DIRECTION_RX : - priv->direction == 2 ? LL_USART_DIRECTION_TX - : LL_USART_DIRECTION_TX_RX); + (priv->direction == UUSART_DIRECTION_RX) ? LL_USART_DIRECTION_RX : + (priv->direction == UUSART_DIRECTION_TX) ? LL_USART_DIRECTION_TX + : LL_USART_DIRECTION_TX_RX); LL_USART_SetHWFlowCtrl(priv->periph, priv->hw_flow_control == 0 ? LL_USART_HWCONTROL_NONE : diff --git a/units/usart/_usart_internal.h b/units/usart/_usart_internal.h index 243651e..7b4584a 100644 --- a/units/usart/_usart_internal.h +++ b/units/usart/_usart_internal.h @@ -17,6 +17,10 @@ #define UUSART_RXBUF_LEN 128 #define UUSART_TXBUF_LEN 128 +#define UUSART_DIRECTION_RX 1 +#define UUSART_DIRECTION_TX 2 +#define UUSART_DIRECTION_RXTX 3 + /** Private data structure */ struct priv { uint8_t periph_num; //!< 1-6 diff --git a/units/usart/_usart_settings.c b/units/usart/_usart_settings.c index 4e17273..c827762 100644 --- a/units/usart/_usart_settings.c +++ b/units/usart/_usart_settings.c @@ -104,9 +104,9 @@ error_t UUSART_loadIni(Unit *unit, const char *key, const char *value) } else if (streq(key, "direction")) { priv->direction = (uint8_t) str_parse_3(value, - "RX", 1, - "TX", 2, - "RXTX", 3, &suc); + "RX", UUSART_DIRECTION_RX, + "TX", UUSART_DIRECTION_TX, + "RXTX", UUSART_DIRECTION_RXTX, &suc); } else if (streq(key, "hw-flow-control")) { priv->hw_flow_control = (uint8_t) str_parse_4(value, diff --git a/utils/malloc_safe.h b/utils/malloc_safe.h index 5f9d372..bbb1bb1 100644 --- a/utils/malloc_safe.h +++ b/utils/malloc_safe.h @@ -15,10 +15,10 @@ #include #include -void *malloc_ck_do(size_t size, const char* file, uint32_t line) __attribute__((malloc)); -void *calloc_ck_do(size_t nmemb, size_t size, const char* file, uint32_t line) __attribute__((malloc)); -char *strdup_ck_do(const char *s, const char* file, uint32_t line) __attribute__((malloc)); -char *strndup_ck_do(const char *s, uint32_t len, const char* file, uint32_t line) __attribute__((malloc)); +void *malloc_ck_do(size_t size, const char* file, uint32_t line) __attribute__((malloc,warn_unused_result)); +void *calloc_ck_do(size_t nmemb, size_t size, const char* file, uint32_t line) __attribute__((malloc,warn_unused_result)); +char *strdup_ck_do(const char *s, const char* file, uint32_t line) __attribute__((malloc,warn_unused_result)); +char *strndup_ck_do(const char *s, uint32_t len, const char* file, uint32_t line) __attribute__((malloc,warn_unused_result)); #if DEBUG_MALLOC diff --git a/utils/stacksmon.h b/utils/stacksmon.h index cddc53b..cdeeb56 100644 --- a/utils/stacksmon.h +++ b/utils/stacksmon.h @@ -12,7 +12,7 @@ #if USE_STACK_MONITOR /** Number of tracked stacks, max */ -#define STACK_NUM 3 +#define STACK_NUM 4 /** * Check canaries and trap if they're dead From d8933cbf819fabe5dcdf3000f6585aefde86da07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 3 Feb 2018 22:45:40 +0100 Subject: [PATCH 03/20] fixed buffer sizing and added buf size to config --- units/adc/_adc_init.c | 13 ++++++++----- units/adc/_adc_internal.h | 1 + units/adc/_adc_settings.c | 16 +++++++++++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 5f2d9e4..6ce4b31 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -30,7 +30,8 @@ error_t UADC_preInit(Unit *unit) priv->enable_tsense = false; priv->enable_vref = false; priv->sample_time = 0b010; // 13.5c - priv->frequency = 4; //1000 + priv->frequency = 1000; + priv->buffer_size = 512; return E_SUCCESS; } @@ -160,6 +161,7 @@ error_t UADC_init(Unit *unit) LL_TIM_SetAutoReload(priv->TIMx, count - 1); LL_TIM_EnableARRPreload(priv->TIMx); LL_TIM_EnableUpdateEvent(priv->TIMx); + LL_TIM_SetTriggerOutput(priv->TIMx, LL_TIM_TRGO_UPDATE); LL_TIM_GenerateEvent_UPDATE(priv->TIMx); // load the prescaller value } @@ -195,9 +197,9 @@ error_t UADC_init(Unit *unit) // --------------------- CONFIGURE DMA ------------------------------- dbg("Setting up DMA"); { - // The length must be a 2*multiple of the number of channels - // this is a horrible way to do it but will work - uint16_t itemcount = (uint16_t) (priv->nb_channels * (uint16_t) (UADC_DMA_MAX_BUF_LEN / (2 * priv->nb_channels))); + // The length must be a 2*multiple of the number of channels, in bytes + uint16_t itemcount = (uint16_t) ((priv->nb_channels) * (uint16_t) (priv->buffer_size / (2 * priv->nb_channels))); + if (itemcount % 2 == 1) itemcount -= priv->nb_channels; priv->dma_buffer_size = (uint16_t) (itemcount * 2); dbg("DMA item count is %d (%d bytes)", itemcount, priv->dma_buffer_size); @@ -236,6 +238,7 @@ error_t UADC_init(Unit *unit) dbg("ADC inited, starting the timer ..."); // FIXME - temporary demo - counter start... + LL_ADC_REG_StartConversion(priv->ADCx); // the first conversion must be started manually LL_TIM_EnableCounter(priv->TIMx); return E_SUCCESS; @@ -254,7 +257,7 @@ void UADC_deInit(Unit *unit) LL_ADC_CommonDeInit(priv->ADCx_Common); LL_TIM_DeInit(priv->TIMx); - irqd_detach(priv->DMAx, UADC_DMA_Handler); + irqd_detach(priv->DMA_CHx, UADC_DMA_Handler); LL_DMA_DeInit(priv->DMAx, priv->dma_chnum); free_ck(priv->dma_buffer); diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index 319f218..e3d0cdf 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -19,6 +19,7 @@ struct priv { bool enable_vref; //!< append a signal from the internal voltage reference uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately + uint16_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer // TODO averaging (maybe a separate component?) // TODO threshold watchdog with hysteresis (maybe a separate component?) diff --git a/units/adc/_adc_settings.c b/units/adc/_adc_settings.c index e35ec86..e44b457 100644 --- a/units/adc/_adc_settings.c +++ b/units/adc/_adc_settings.c @@ -21,6 +21,10 @@ void UADC_loadBinary(Unit *unit, PayloadParser *pp) priv->enable_vref = pp_bool(pp); priv->sample_time = pp_u8(pp); priv->frequency = pp_u32(pp); + + if (version >= 1) { + priv->buffer_size = pp_u16(pp); + } } /** Write to a binary buffer for storing in Flash */ @@ -28,13 +32,14 @@ void UADC_writeBinary(Unit *unit, PayloadBuilder *pb) { struct priv *priv = unit->data; - pb_u8(pb, 0); // version + pb_u8(pb, 1); // version pb_u16(pb, priv->channels); pb_bool(pb, priv->enable_tsense); pb_bool(pb, priv->enable_vref); pb_u8(pb, priv->sample_time); pb_u32(pb, priv->frequency); + pb_u16(pb, priv->buffer_size); } // ------------------------------------------------------------------------ @@ -61,6 +66,9 @@ error_t UADC_loadIni(Unit *unit, const char *key, const char *value) else if (streq(key, "frequency")) { priv->frequency = (uint32_t) avr_atoi(value); } + else if (streq(key, "buffer_size")) { + priv->buffer_size = (uint16_t) avr_atoi(value); + } else { return E_BAD_KEY; } @@ -89,5 +97,11 @@ void UADC_writeIni(Unit *unit, IniWriter *iw) iw_comment(iw, "Sampling frequency (Hz)"); iw_entry(iw, "frequency", "%d", (int)priv->frequency); + + iw_comment(iw, "Sample buffer size (bytes, 2 per channels per sample)"); + iw_comment(iw, "- a report is sent when 1/2 of the circular buffer is filled"); + iw_comment(iw, "- the buffer is shared by all channels"); + iw_comment(iw, "- insufficient buffer size can lead to data loss"); + iw_entry(iw, "buffer_size", "%d", (int)priv->buffer_size); } From 10cc13bc82e3d58373af80f7602b9c0c46aa1c47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 3 Feb 2018 22:47:50 +0100 Subject: [PATCH 04/20] added a buffer size check --- units/adc/_adc_init.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 6ce4b31..98aad8b 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -133,6 +133,11 @@ error_t UADC_init(Unit *unit) dbg("!! Need at least 1 channel"); return E_BAD_CONFIG; } + + if (priv->dma_buffer_size < priv->nb_channels*2*2) { + dbg("Insufficient buf size"); + return E_BAD_CONFIG; + } } // ------------------- ENABLE CLOCKS -------------------------- From 0a335514740097bf5cd346b5f4d873404c7cceb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sat, 3 Feb 2018 22:49:09 +0100 Subject: [PATCH 05/20] oops fix bug --- units/adc/_adc_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 98aad8b..5463b09 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -134,7 +134,7 @@ error_t UADC_init(Unit *unit) return E_BAD_CONFIG; } - if (priv->dma_buffer_size < priv->nb_channels*2*2) { + if (priv->buffer_size < priv->nb_channels*2*2) { dbg("Insufficient buf size"); return E_BAD_CONFIG; } From c5f41819b10b8ed0b733da28ea16eee108acb03e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 4 Feb 2018 10:30:26 +0100 Subject: [PATCH 06/20] move the 1wire report to a job --- units/1wire/unit_1wire.c | 57 ++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 14 deletions(-) diff --git a/units/1wire/unit_1wire.c b/units/1wire/unit_1wire.c index 194c391..6dead85 100644 --- a/units/1wire/unit_1wire.c +++ b/units/1wire/unit_1wire.c @@ -11,6 +11,23 @@ #include "_ow_internal.h" #include "_ow_low_level.h" +/** Callback for sending a poll_ready success / failure report */ +static void OW_TimerRespCb(Job *job) +{ + Unit *unit = job->unit; + assert_param(unit); + struct priv *priv = unit->data; + + bool success = (bool) job->data1; + + if (success) { + com_respond_ok(priv->busyRequestId); + } else { + com_respond_error(priv->busyRequestId, E_HW_TIMEOUT); + } + priv->busy = false; +} + /** * 1-Wire timer callback, used for the 'wait_ready' function. * @@ -28,11 +45,6 @@ void OW_TimerCb(TimerHandle_t xTimer) struct priv *priv = unit->data; assert_param(priv->busy); - // XXX Possible bug - // This is run on the timers thread. The TF write functions block on a semaphore. - // The FreeRTOS documentation warns against blocking in the timers thread, - // but does not say why. This can be solved by scheduling the response using the job queue. - if (priv->parasitic) { // this is the end of the 750ms measurement time goto halt_ok; @@ -45,16 +57,26 @@ void OW_TimerCb(TimerHandle_t xTimer) uint32_t time = PTIM_GetTime(); if (time - priv->busyStart > 1000) { xTimerStop(xTimer, 100); - com_respond_error(priv->busyRequestId, E_HW_TIMEOUT); - priv->busy = false; + + Job j = { + .unit = unit, + .data1 = 0, // failure + .cb = OW_TimerRespCb, + }; + scheduleJob(&j); } } return; halt_ok: xTimerStop(xTimer, 100); - com_respond_ok(priv->busyRequestId); - priv->busy = false; + + Job j = { + .unit = unit, + .data1 = 1, // success + .cb = OW_TimerRespCb, + }; + scheduleJob(&j); } @@ -110,15 +132,22 @@ static error_t OW_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Pay priv->busyRequestId = frame_id; return E_SUCCESS; // We will respond when the timer expires + /** Search devices with alarm. No payload, restarts the search. */ case CMD_SEARCH_ALARM: with_alarm = true; // fall-through + /** Search all devices. No payload, restarts the search. */ case CMD_SEARCH_ADDR: search_reset = true; // fall-through + /** Continue a previously begun search. */ case CMD_SEARCH_CONTINUE:; uint32_t found_count = 0; bool have_more = false; + if (!search_reset && priv->searchState.status != OW_SEARCH_MORE) { + dbg("Search not ongoing!"); + return E_PROTOCOL_BREACH; + } TRY(UU_1WIRE_Search(unit, with_alarm, search_reset, (void *) unit_tmp512, UNIT_TMP_LEN/8, &found_count, @@ -160,8 +189,8 @@ static error_t OW_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Pay * Write payload to the bus, no confirmation (unless requested). * * Payload: - * - Match variant: addr:u64, rest:write_data - * - Skip variant: all:write_data + * addr:u64, rest:write_data + * if addr is 0, use SKIP_ROM */ case CMD_WRITE: addr = pp_u64(pp); @@ -173,10 +202,10 @@ static error_t OW_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Pay * Write and read. * * Payload: - * - Match variant: addr:u64, read_len:u16, rest:write_data - * - Skip variant: read_len:u16, rest:write_data + * addr:u64, read_len:u16, rest:write_data + * if addr is 0, use SKIP_ROM */ - case CMD_READ:; + case CMD_READ: addr = pp_u64(pp); uint16_t rcount = pp_u16(pp); bool test_crc = pp_bool(pp); From 300a6a6e901b2b0f028267593279d9793eeeead4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 4 Feb 2018 10:40:11 +0100 Subject: [PATCH 07/20] split ll extensions from hw_utils to own file --- platform/hw_utils.c | 96 +---------------------------------- platform/hw_utils.h | 49 +----------------- platform/ll_extension.c | 108 ++++++++++++++++++++++++++++++++++++++++ platform/ll_extension.h | 59 ++++++++++++++++++++++ units/adc/_adc_core.c | 4 ++ units/adc/_adc_init.c | 12 ----- 6 files changed, 174 insertions(+), 154 deletions(-) create mode 100644 platform/ll_extension.c create mode 100644 platform/ll_extension.h create mode 100644 units/adc/_adc_core.c diff --git a/platform/hw_utils.c b/platform/hw_utils.c index b395d44..9f08f31 100644 --- a/platform/hw_utils.c +++ b/platform/hw_utils.c @@ -3,101 +3,9 @@ // #include "platform.h" -#include -#include +#include "utils/avrlibc.h" #include "hw_utils.h" -#include "macro.h" - -const uint32_t LL_SYSCFG_EXTI_PORTS[PORTS_COUNT] = { - LL_SYSCFG_EXTI_PORTA, - LL_SYSCFG_EXTI_PORTB, - LL_SYSCFG_EXTI_PORTC, - LL_SYSCFG_EXTI_PORTD, - LL_SYSCFG_EXTI_PORTE, -#if PORTS_COUNT>5 - LL_SYSCFG_EXTI_PORTF, -#endif -#if PORTS_COUNT>6 - LL_SYSCFG_EXTI_PORTG, -#endif -}; - -const uint32_t LL_SYSCFG_EXTI_LINES[16] = { - LL_SYSCFG_EXTI_LINE0, - LL_SYSCFG_EXTI_LINE1, - LL_SYSCFG_EXTI_LINE2, - LL_SYSCFG_EXTI_LINE3, - LL_SYSCFG_EXTI_LINE4, - LL_SYSCFG_EXTI_LINE5, - LL_SYSCFG_EXTI_LINE6, - LL_SYSCFG_EXTI_LINE7, - LL_SYSCFG_EXTI_LINE8, - LL_SYSCFG_EXTI_LINE9, - LL_SYSCFG_EXTI_LINE10, - LL_SYSCFG_EXTI_LINE11, - LL_SYSCFG_EXTI_LINE12, - LL_SYSCFG_EXTI_LINE13, - LL_SYSCFG_EXTI_LINE14, - LL_SYSCFG_EXTI_LINE15, -}; -COMPILER_ASSERT(16 == ELEMENTS_IN_ARRAY(LL_SYSCFG_EXTI_LINES)); - -const uint32_t LL_EXTI_LINES[16] = { - LL_EXTI_LINE_0, - LL_EXTI_LINE_1, - LL_EXTI_LINE_2, - LL_EXTI_LINE_3, - LL_EXTI_LINE_4, - LL_EXTI_LINE_5, - LL_EXTI_LINE_6, - LL_EXTI_LINE_7, - LL_EXTI_LINE_8, - LL_EXTI_LINE_9, - LL_EXTI_LINE_10, - LL_EXTI_LINE_11, - LL_EXTI_LINE_12, - LL_EXTI_LINE_13, - LL_EXTI_LINE_14, - LL_EXTI_LINE_15, -}; -COMPILER_ASSERT(16 == ELEMENTS_IN_ARRAY(LL_EXTI_LINES)); - -/** Pin number to LL bitfield mapping */ -const uint32_t LL_GPIO_PINS[16] = { - LL_GPIO_PIN_0, - LL_GPIO_PIN_1, - LL_GPIO_PIN_2, - LL_GPIO_PIN_3, - LL_GPIO_PIN_4, - LL_GPIO_PIN_5, - LL_GPIO_PIN_6, - LL_GPIO_PIN_7, - LL_GPIO_PIN_8, - LL_GPIO_PIN_9, - LL_GPIO_PIN_10, - LL_GPIO_PIN_11, - LL_GPIO_PIN_12, - LL_GPIO_PIN_13, - LL_GPIO_PIN_14, - LL_GPIO_PIN_15, -}; -COMPILER_ASSERT(16 == ELEMENTS_IN_ARRAY(LL_GPIO_PINS)); - -/** Port number (A=0) to config struct pointer mapping */ -GPIO_TypeDef * const GPIO_PERIPHS[PORTS_COUNT] = { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, -#if PORTS_COUNT>5 - GPIOF, -#endif -#if PORTS_COUNT>6 - GPIOG, -#endif -}; -COMPILER_ASSERT(PORTS_COUNT == ELEMENTS_IN_ARRAY(GPIO_PERIPHS)); + /** Convert pin number to LL bitfield */ uint32_t hw_pin2ll(uint8_t pin_number, bool *suc) diff --git a/platform/hw_utils.h b/platform/hw_utils.h index 240d577..d89a285 100644 --- a/platform/hw_utils.h +++ b/platform/hw_utils.h @@ -10,12 +10,7 @@ #include "platform.h" #include "resources.h" - -extern const uint32_t LL_SYSCFG_EXTI_PORTS[PORTS_COUNT]; -extern const uint32_t LL_SYSCFG_EXTI_LINES[16]; -extern GPIO_TypeDef * const GPIO_PERIPHS[PORTS_COUNT]; -extern const uint32_t LL_GPIO_PINS[16]; -extern const uint32_t LL_EXTI_LINES[16]; +#include "ll_extension.h" /** * Convert pin number to LL driver bitfield for working with the pin. @@ -188,46 +183,4 @@ void hw_periph_clock_disable(void *periph); bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit, uint16_t *presc, uint32_t *count, float *real_freq); -// ---------- LL extras ------------ - -static inline bool LL_DMA_IsActiveFlag_G(uint32_t isr_snapshot, uint8_t channel) -{ - return 0 != (isr_snapshot & (DMA_ISR_GIF1 << (uint32_t)((channel-1) * 4))); -} - -static inline bool LL_DMA_IsActiveFlag_TC(uint32_t isr_snapshot, uint8_t channel) -{ - return 0 != (isr_snapshot & (DMA_ISR_TCIF1 << (uint32_t)((channel-1) * 4))); -} - -static inline bool LL_DMA_IsActiveFlag_HT(uint32_t isr_snapshot, uint8_t channel) -{ - return 0 != (isr_snapshot & (DMA_ISR_HTIF1 << (uint32_t)((channel-1) * 4))); -} - -static inline bool LL_DMA_IsActiveFlag_TE(uint32_t isr_snapshot, uint8_t channel) -{ - return 0 != (isr_snapshot & (DMA_ISR_TEIF1 << (uint32_t)((channel-1) * 4))); -} - -static inline void LL_DMA_ClearFlag_HT(DMA_TypeDef *DMAx, uint8_t channel) -{ - DMAx->IFCR = (DMA_IFCR_CHTIF1 << (uint32_t)((channel-1) * 4)); -} - -static inline void LL_DMA_ClearFlag_TC(DMA_TypeDef *DMAx, uint8_t channel) -{ - DMAx->IFCR = (DMA_IFCR_CTCIF1 << (uint32_t)((channel-1) * 4)); -} - -static inline void LL_DMA_ClearFlag_TE(DMA_TypeDef *DMAx, uint8_t channel) -{ - DMAx->IFCR = (DMA_IFCR_CTEIF1 << (uint32_t)((channel-1) * 4)); -} - -static inline void LL_DMA_ClearFlags(DMA_TypeDef *DMAx, uint8_t channel) -{ - DMAx->IFCR = (DMA_IFCR_CGIF1 << (uint32_t)((channel-1) * 4)); -} - #endif //GEX_PIN_UTILS_H diff --git a/platform/ll_extension.c b/platform/ll_extension.c new file mode 100644 index 0000000..cec9d9f --- /dev/null +++ b/platform/ll_extension.c @@ -0,0 +1,108 @@ +// +// Created by MightyPork on 2018/02/04. +// + +#include "platform.h" +#include "ll_extension.h" + +const uint32_t LL_SYSCFG_EXTI_PORTS[PORTS_COUNT] = { + LL_SYSCFG_EXTI_PORTA, + LL_SYSCFG_EXTI_PORTB, + LL_SYSCFG_EXTI_PORTC, + LL_SYSCFG_EXTI_PORTD, + LL_SYSCFG_EXTI_PORTE, +#if PORTS_COUNT>5 + LL_SYSCFG_EXTI_PORTF, +#endif +#if PORTS_COUNT>6 + LL_SYSCFG_EXTI_PORTG, +#endif +}; + +const uint32_t LL_SYSCFG_EXTI_LINES[16] = { + LL_SYSCFG_EXTI_LINE0, + LL_SYSCFG_EXTI_LINE1, + LL_SYSCFG_EXTI_LINE2, + LL_SYSCFG_EXTI_LINE3, + LL_SYSCFG_EXTI_LINE4, + LL_SYSCFG_EXTI_LINE5, + LL_SYSCFG_EXTI_LINE6, + LL_SYSCFG_EXTI_LINE7, + LL_SYSCFG_EXTI_LINE8, + LL_SYSCFG_EXTI_LINE9, + LL_SYSCFG_EXTI_LINE10, + LL_SYSCFG_EXTI_LINE11, + LL_SYSCFG_EXTI_LINE12, + LL_SYSCFG_EXTI_LINE13, + LL_SYSCFG_EXTI_LINE14, + LL_SYSCFG_EXTI_LINE15, +}; +COMPILER_ASSERT(16 == ELEMENTS_IN_ARRAY(LL_SYSCFG_EXTI_LINES)); + +const uint32_t LL_EXTI_LINES[16] = { + LL_EXTI_LINE_0, + LL_EXTI_LINE_1, + LL_EXTI_LINE_2, + LL_EXTI_LINE_3, + LL_EXTI_LINE_4, + LL_EXTI_LINE_5, + LL_EXTI_LINE_6, + LL_EXTI_LINE_7, + LL_EXTI_LINE_8, + LL_EXTI_LINE_9, + LL_EXTI_LINE_10, + LL_EXTI_LINE_11, + LL_EXTI_LINE_12, + LL_EXTI_LINE_13, + LL_EXTI_LINE_14, + LL_EXTI_LINE_15, +}; +COMPILER_ASSERT(16 == ELEMENTS_IN_ARRAY(LL_EXTI_LINES)); + +/** Pin number to LL bitfield mapping */ +const uint32_t LL_GPIO_PINS[16] = { + LL_GPIO_PIN_0, + LL_GPIO_PIN_1, + LL_GPIO_PIN_2, + LL_GPIO_PIN_3, + LL_GPIO_PIN_4, + LL_GPIO_PIN_5, + LL_GPIO_PIN_6, + LL_GPIO_PIN_7, + LL_GPIO_PIN_8, + LL_GPIO_PIN_9, + LL_GPIO_PIN_10, + LL_GPIO_PIN_11, + LL_GPIO_PIN_12, + LL_GPIO_PIN_13, + LL_GPIO_PIN_14, + LL_GPIO_PIN_15, +}; +COMPILER_ASSERT(16 == ELEMENTS_IN_ARRAY(LL_GPIO_PINS)); + +/** Port number (A=0) to config struct pointer mapping */ +GPIO_TypeDef * const GPIO_PERIPHS[PORTS_COUNT] = { + GPIOA, + GPIOB, + GPIOC, + GPIOD, + GPIOE, +#if PORTS_COUNT>5 + GPIOF, +#endif +#if PORTS_COUNT>6 + GPIOG, +#endif +}; +COMPILER_ASSERT(PORTS_COUNT == ELEMENTS_IN_ARRAY(GPIO_PERIPHS)); + +const uint32_t LL_ADC_SAMPLETIMES[8] = { + LL_ADC_SAMPLINGTIME_1CYCLE_5, + LL_ADC_SAMPLINGTIME_7CYCLES_5, + LL_ADC_SAMPLINGTIME_13CYCLES_5, + LL_ADC_SAMPLINGTIME_28CYCLES_5, + LL_ADC_SAMPLINGTIME_41CYCLES_5, + LL_ADC_SAMPLINGTIME_55CYCLES_5, + LL_ADC_SAMPLINGTIME_71CYCLES_5, + LL_ADC_SAMPLINGTIME_239CYCLES_5, +}; diff --git a/platform/ll_extension.h b/platform/ll_extension.h new file mode 100644 index 0000000..ddb85af --- /dev/null +++ b/platform/ll_extension.h @@ -0,0 +1,59 @@ +// +// Created by MightyPork on 2018/02/04. +// + +#ifndef GEX_F072_LL_EXTENSION_H +#define GEX_F072_LL_EXTENSION_H + +#include "platform.h" + +extern const uint32_t LL_SYSCFG_EXTI_PORTS[PORTS_COUNT]; +extern const uint32_t LL_SYSCFG_EXTI_LINES[16]; +extern GPIO_TypeDef * const GPIO_PERIPHS[PORTS_COUNT]; +extern const uint32_t LL_GPIO_PINS[16]; +extern const uint32_t LL_EXTI_LINES[16]; +extern const uint32_t LL_ADC_SAMPLETIMES[8]; + + +static inline bool LL_DMA_IsActiveFlag_G(uint32_t isr_snapshot, uint8_t channel) +{ + return 0 != (isr_snapshot & (DMA_ISR_GIF1 << (uint32_t)((channel-1) * 4))); +} + +static inline bool LL_DMA_IsActiveFlag_TC(uint32_t isr_snapshot, uint8_t channel) +{ + return 0 != (isr_snapshot & (DMA_ISR_TCIF1 << (uint32_t)((channel-1) * 4))); +} + +static inline bool LL_DMA_IsActiveFlag_HT(uint32_t isr_snapshot, uint8_t channel) +{ + return 0 != (isr_snapshot & (DMA_ISR_HTIF1 << (uint32_t)((channel-1) * 4))); +} + +static inline bool LL_DMA_IsActiveFlag_TE(uint32_t isr_snapshot, uint8_t channel) +{ + return 0 != (isr_snapshot & (DMA_ISR_TEIF1 << (uint32_t)((channel-1) * 4))); +} + +static inline void LL_DMA_ClearFlag_HT(DMA_TypeDef *DMAx, uint8_t channel) +{ + DMAx->IFCR = (DMA_IFCR_CHTIF1 << (uint32_t)((channel-1) * 4)); +} + +static inline void LL_DMA_ClearFlag_TC(DMA_TypeDef *DMAx, uint8_t channel) +{ + DMAx->IFCR = (DMA_IFCR_CTCIF1 << (uint32_t)((channel-1) * 4)); +} + +static inline void LL_DMA_ClearFlag_TE(DMA_TypeDef *DMAx, uint8_t channel) +{ + DMAx->IFCR = (DMA_IFCR_CTEIF1 << (uint32_t)((channel-1) * 4)); +} + +static inline void LL_DMA_ClearFlags(DMA_TypeDef *DMAx, uint8_t channel) +{ + DMAx->IFCR = (DMA_IFCR_CGIF1 << (uint32_t)((channel-1) * 4)); +} + + +#endif //GEX_F072_LL_EXTENSION_H diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c new file mode 100644 index 0000000..a2bcd11 --- /dev/null +++ b/units/adc/_adc_core.c @@ -0,0 +1,4 @@ +// +// Created by MightyPork on 2018/02/04. +// + diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 5463b09..1dd0a2d 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -8,18 +8,6 @@ #define ADC_INTERNAL #include "_adc_internal.h" -const uint32_t LL_ADC_SAMPLETIMES[] = { - LL_ADC_SAMPLINGTIME_1CYCLE_5, - LL_ADC_SAMPLINGTIME_7CYCLES_5, - LL_ADC_SAMPLINGTIME_13CYCLES_5, - LL_ADC_SAMPLINGTIME_28CYCLES_5, - LL_ADC_SAMPLINGTIME_41CYCLES_5, - LL_ADC_SAMPLINGTIME_55CYCLES_5, - LL_ADC_SAMPLINGTIME_71CYCLES_5, - LL_ADC_SAMPLINGTIME_239CYCLES_5, -}; - - /** Allocate data structure and set defaults */ error_t UADC_preInit(Unit *unit) { From 02f69b0e37e641dd37a156881dc9dd395e156330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 4 Feb 2018 17:43:59 +0100 Subject: [PATCH 08/20] work on ADC sampling, not tested, missing commands --- platform/hw_utils.c | 3 + platform/hw_utils.h | 12 ++ platform/irq_dispatcher.c | 13 +- platform/plat_init.c | 5 +- units/adc/_adc_core.c | 267 ++++++++++++++++++++++++++++++++++++++ units/adc/_adc_init.c | 102 ++++++--------- units/adc/_adc_internal.h | 75 ++++++++--- units/adc/_adc_settings.c | 14 +- units/adc/unit_adc.c | 1 + utils/malloc_safe.c | 6 + 10 files changed, 416 insertions(+), 82 deletions(-) diff --git a/platform/hw_utils.c b/platform/hw_utils.c index 9f08f31..9cfb4f1 100644 --- a/platform/hw_utils.c +++ b/platform/hw_utils.c @@ -349,6 +349,9 @@ bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit, uint16_t *presc, uint32_t *count, float *real_freq) { if (required_freq == 0) return false; + + // XXX consider using the LL macros __LL_TIM_CALC_PSC and __LL_TIM_CALC_ARR + const float fPresc = base_freq / required_freq; uint32_t wCount = (uint32_t) lrintf(fPresc); diff --git a/platform/hw_utils.h b/platform/hw_utils.h index d89a285..94195ea 100644 --- a/platform/hw_utils.h +++ b/platform/hw_utils.h @@ -183,4 +183,16 @@ void hw_periph_clock_disable(void *periph); bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit, uint16_t *presc, uint32_t *count, float *real_freq); +#define hw_wait_while(call, timeout) \ + do { \ + uint32_t _ts = HAL_GetTick(); \ + while (1 == (call)) { \ + if (HAL_GetTick() - _ts > (timeout)) { \ + trap("Timeout"); \ + } \ + } \ + } while (0) + +#define hw_wait_until(call, timeout) hw_wait_while(!(call), (timeout)) + #endif //GEX_PIN_UTILS_H diff --git a/platform/irq_dispatcher.c b/platform/irq_dispatcher.c index 13e18a0..be62258 100644 --- a/platform/irq_dispatcher.c +++ b/platform/irq_dispatcher.c @@ -65,6 +65,8 @@ static struct callbacks_ { struct cbslot tim7; struct cbslot tim15; + struct cbslot adc1; + // XXX add more callbacks here when needed } callbacks; @@ -96,7 +98,9 @@ void irqd_init(void) HAL_NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2, 0); HAL_NVIC_SetPriority(DMA1_Channel4_5_6_7_IRQn, 2, 0); -// NVIC_EnableIRQ(ADC1_COMP_IRQn); /*!< ADC1 and COMP interrupts (ADC interrupt combined with EXTI Lines 21 and 22 */ + NVIC_EnableIRQ(ADC1_COMP_IRQn); /*!< ADC1 and COMP interrupts (ADC interrupt combined with EXTI Lines 21 and 22 */ + HAL_NVIC_SetPriority(ADC1_COMP_IRQn, 1, 0); // ADC group completion - higher prio than DMA to let it handle the last halfword first + // NVIC_EnableIRQ(TIM1_IRQn); /*!< TIM1 global Interrupt */ // NVIC_EnableIRQ(TIM2_IRQn); /*!< TIM2 global Interrupt */ // NVIC_EnableIRQ(TIM3_IRQn); /*!< TIM3 global Interrupt */ @@ -159,6 +163,8 @@ static struct cbslot *get_slot_for_periph(void *periph) else if (periph == TIM7) slot = &callbacks.tim7; else if (periph == TIM15) slot = &callbacks.tim15; + else if (periph == ADC1) slot = &callbacks.adc1; + else if (periph >= EXTIS[0] && periph <= EXTIS[15]) { slot = &callbacks.exti[periph - EXTIS[0]]; } @@ -313,6 +319,11 @@ void TIM15_IRQHandler(void) CALL_IRQ_HANDLER(callbacks.tim15); } +void ADC1_COMP_IRQHandler(void) +{ + CALL_IRQ_HANDLER(callbacks.adc1); +} + // other ISRs... diff --git a/platform/plat_init.c b/platform/plat_init.c index 0015e4f..f233d6d 100644 --- a/platform/plat_init.c +++ b/platform/plat_init.c @@ -18,6 +18,8 @@ void plat_init(void) { + // GPIO clocks are enabled earlier in the GEX start-up hook + // Load system defaults systemsettings_init(); @@ -27,8 +29,9 @@ void plat_init(void) LockJumper_Init(); Indicator_Init(); - DebugUart_Init(); // resource claim + DebugUart_Init(); // resource claim (was inited earlier to allow debug outputs) + // Enable interrupts and set priorities irqd_init(); dbg("Loading settings ..."); diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index a2bcd11..ddb53fd 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -2,3 +2,270 @@ // Created by MightyPork on 2018/02/04. // +#include "platform.h" +#include "unit_base.h" + +#define ADC_INTERNAL +#include "_adc_internal.h" + +void UADC_DMA_Handler(void *arg) +{ + Unit *unit = arg; + + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + const uint32_t isrsnapshot = priv->DMAx->ISR; + + if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) { + const bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_chnum); + const bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_chnum); + const bool te = LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum); + + // check what mode we're in + const bool m_trig = priv->opmode == ADC_OPMODE_TRIGD; + const bool m_stream = priv->opmode == ADC_OPMODE_STREAM; + const bool m_fixcpt = priv->opmode == ADC_OPMODE_FIXCAPT; + + if (m_trig || m_stream || m_fixcpt) { + if (ht || tc) { + const uint16_t start = priv->stream_startpos; + uint16_t end; + + if (ht) { + dbg("HT"); + end = (uint16_t) (priv->dma_buffer_itemcount / 2); + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + } + else { + dbg("TC"); + end = (uint16_t) priv->dma_buffer_itemcount; + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + } + + assert_param(start < end); + + uint32_t sgcount = (end - start) / priv->nb_channels; + + if (m_trig || m_fixcpt) { + sgcount = MIN(priv->trig_stream_remain, sgcount); + priv->trig_stream_remain -= sgcount; + } + + dbg("Would send %d groups (u16 offset %d -> %d)", (int)sgcount, (int)start, (int)(start+sgcount*priv->nb_channels)); + // TODO send the data together with remaining count (used to detect end of transmission) + + if (m_trig || m_fixcpt) { + if (priv->trig_stream_remain == 0) { + dbg("End of capture"); + UADC_SwitchMode(unit, (priv->auto_rearm && m_trig) ? ADC_OPMODE_ARMED : ADC_OPMODE_IDLE); + } + } + + if (end == priv->dma_buffer_itemcount) { + priv->stream_startpos = 0; + } + else { + priv->stream_startpos = end; + } + } + } else { + // This shouldn't happen, the interrupt should be disabled in this opmode + dbg("(!) not streaming, DMA IT should be disabled"); + + if (ht) { + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + } + else { + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + } + } + + if (te) { + // this shouldn't happen - error + dbg("ADC DMA TE!"); + LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum); + } + } +} + +void UADC_ADC_EOS_Handler(void *arg) +{ + uint64_t timestamp = PTIM_GetMicrotime(); + Unit *unit = arg; + + dbg("ADC EOS ISR hit"); + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + // Wait for the DMA to complete copying the last sample + while (priv->DMA_CHx->CNDTR % priv->nb_channels != 0); + + uint32_t sample_pos; + if (priv->DMA_CHx->CNDTR == 0) { + sample_pos = (uint32_t) (priv->dma_buffer_itemcount - 1); + } else { + sample_pos = priv->DMA_CHx->CNDTR; + } + sample_pos -= priv->nb_channels; + dbg("Sample pos %d", (int)sample_pos); + + for (uint32_t i = 0; i < 18; i++) { + if (priv->extended_channels_mask & (1 << i)) { + uint16_t val = priv->dma_buffer[sample_pos]; + dbg("Trig line level %d", (int)val); + + if (priv->enable_averaging) { + priv->averaging_bins[i] = + priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + + ((float) val) * priv->avg_factor_as_float; + } else { + priv->last_sample[i] = val; + } + + if (priv->opmode == ADC_OPMODE_ARMED) { + if (i == priv->trigger_source) { + bool trigd = false; + bool rising = false; + if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) { + dbg("******** Rising edge"); + // Rising edge + trigd = (bool) (priv->trig_edge & 0b01); + rising = true; + } + else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) { + dbg("******** Falling edge"); + // Falling edge + trigd = (bool) (priv->trig_edge & 0b10); + } + + if (trigd) { + UADC_HandleTrigger(unit, rising, timestamp); + } + + priv->trig_prev_level = val; + } + } + } + } + + dbg(" EOS ISR end."); +} + +void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp) +{ + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) { + dbg("Trig discarded due to holdoff."); + return; + } + + if (priv->trig_holdoff > 0) { + priv->trig_holdoff_remain = priv->trig_holdoff; + // Start the tick + unit->tick_interval = 1; + unit->_tick_cnt = 0; + } + + dbg("Trigger condition hit, rising=%d", rising); + // TODO Send pre-trigger + + priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR; + priv->trig_stream_remain = priv->trig_len; + UADC_SwitchMode(unit, ADC_OPMODE_TRIGD); +} + +void UADC_updateTick(Unit *unit) +{ + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + if (priv->trig_holdoff_remain > 0) { + priv->trig_holdoff_remain--; + + if (priv->trig_holdoff_remain == 0) { + unit->tick_interval = 0; + unit->_tick_cnt = 0; + } + } +} + +void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) +{ + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + if (new_mode == priv->opmode) return; // nothing to do + + // if un-itied, can go only to IDLE + assert_param((priv->opmode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE)); + + if (new_mode == ADC_OPMODE_UNINIT) { + dbg("ADC switch -> UNINIT"); + // Stop the DMA, timer and disable ADC - this is called before tearing down the unit + LL_TIM_DisableCounter(priv->TIMx); + + // Switch off the ADC + if (LL_ADC_IsEnabled(priv->ADCx)) { + // Cancel ongoing conversion + if (LL_ADC_REG_IsConversionOngoing(priv->ADCx)) { + dbg("Stopping ADC conv"); + LL_ADC_REG_StopConversion(priv->ADCx); + hw_wait_while(LL_ADC_REG_IsStopConversionOngoing(priv->ADCx), 100); + } + + LL_ADC_Disable(priv->ADCx); + dbg("Disabling ADC"); + hw_wait_while(LL_ADC_IsDisableOngoing(priv->ADCx), 100); + } + + dbg("Disabling DMA"); + LL_DMA_DisableChannel(priv->DMAx, priv->dma_chnum); + LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); + } + else if (new_mode == ADC_OPMODE_IDLE) { + dbg("ADC switch -> IDLE"); + // IDLE and ARMED are identical with the exception that the trigger condition is not checked + + // In IDLE, we don't need the DMA interrupts + LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); + + // Use End Of Sequence to recover results for averaging from the DMA buffer and DR + LL_ADC_EnableIT_EOS(priv->ADCx); + + if (priv->opmode == ADC_OPMODE_UNINIT) { + // Nothing is started yet - this is the only way to leave UNINIT + LL_ADC_Enable(priv->ADCx); + LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); + LL_TIM_EnableCounter(priv->TIMx); + } + } + else if (new_mode == ADC_OPMODE_ARMED) { + dbg("ADC switch -> ARMED"); + assert_param(priv->opmode == ADC_OPMODE_IDLE); + // there's nothing else to do here + } + else if (new_mode == ADC_OPMODE_TRIGD || new_mode == ADC_OPMODE_STREAM) { + dbg("ADC switch -> TRIG'D or STREAM"); + assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE); + + // during the capture, we disallow direct readout and averaging to reduce overhead + LL_ADC_DisableIT_EOS(priv->ADCx); + + // Enable the DMA buffer interrupts + LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); + } + + // the actual switch + priv->opmode = new_mode; +} diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 1dd0a2d..5f5ed11 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -2,6 +2,7 @@ // Created by MightyPork on 2018/02/03. // +#include #include "platform.h" #include "unit_base.h" @@ -20,48 +21,12 @@ error_t UADC_preInit(Unit *unit) priv->sample_time = 0b010; // 13.5c priv->frequency = 1000; priv->buffer_size = 512; + priv->enable_averaging = false; + priv->averaging_factor = 500; - return E_SUCCESS; -} - -static void UADC_DMA_Handler(void *arg) -{ - Unit *unit = arg; - - dbg("ADC DMA ISR hit"); - assert_param(unit); - struct priv *priv = unit->data; - assert_param(priv); - - const uint32_t isrsnapshot = priv->DMAx->ISR; - - if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) { - bool tc = LL_DMA_IsActiveFlag_TC(isrsnapshot, priv->dma_chnum); - bool ht = LL_DMA_IsActiveFlag_HT(isrsnapshot, priv->dma_chnum); - - // Here we have to either copy it somewhere else, or notify another thread (queue?) - // that the data is ready for reading - - if (ht) { - uint16_t start = 0; - uint16_t end = (uint16_t) (priv->dma_buffer_size / 2); - // TODO handle first half - LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); - } - - if (tc) { - uint16_t start = (uint16_t) (priv->dma_buffer_size / 2); - uint16_t end = (uint16_t) priv->dma_buffer_size; - // TODO handle second half - LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); - } + priv->opmode = ADC_OPMODE_UNINIT; - if (LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum)) { - // this shouldn't happen - error - dbg("ADC DMA TE!"); - LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum); - } - } + return E_SUCCESS; } /** Finalize unit set-up */ @@ -133,6 +98,7 @@ error_t UADC_init(Unit *unit) // enable peripherals clock hw_periph_clock_enable(priv->ADCx); hw_periph_clock_enable(priv->TIMx); + // DMA and GPIO clocks are enabled on startup automatically } // ------------------- CONFIGURE THE TIMER -------------------------- @@ -142,8 +108,7 @@ error_t UADC_init(Unit *unit) uint16_t presc; uint32_t count; float real_freq; - if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, - &real_freq)) { + if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, &real_freq)) { dbg("Failed to resolve timer params."); return E_BAD_VALUE; } @@ -167,24 +132,29 @@ error_t UADC_init(Unit *unit) while (LL_ADC_IsCalibrationOnGoing(priv->ADCx)) {} dbg("ADC calibrated."); - uint32_t mask = 0; - if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT; - if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR; - LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask); + { + uint32_t mask = 0; + if (priv->enable_vref) mask |= LL_ADC_PATH_INTERNAL_VREFINT; + if (priv->enable_tsense) mask |= LL_ADC_PATH_INTERNAL_TEMPSENSOR; + LL_ADC_SetCommonPathInternalCh(priv->ADCx_Common, mask); + } + LL_ADC_SetDataAlignment(priv->ADCx, LL_ADC_DATA_ALIGN_RIGHT); LL_ADC_SetResolution(priv->ADCx, LL_ADC_RESOLUTION_12B); LL_ADC_REG_SetDMATransfer(priv->ADCx, LL_ADC_REG_DMA_TRANSFER_UNLIMITED); // configure channels - LL_ADC_REG_SetSequencerChannels(priv->ADCx, priv->channels); - if (priv->enable_tsense) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_TEMPSENSOR); - if (priv->enable_vref) LL_ADC_REG_SetSequencerChAdd(priv->ADCx, LL_ADC_CHANNEL_VREFINT); + priv->extended_channels_mask = priv->channels; + if (priv->enable_tsense) priv->extended_channels_mask |= (1<<16); + if (priv->enable_vref) priv->extended_channels_mask |= (1<<17); + + priv->ADCx->CHSELR = priv->extended_channels_mask; LL_ADC_REG_SetTriggerSource(priv->ADCx, LL_ADC_REG_TRIG_EXT_TIM15_TRGO); LL_ADC_SetSamplingTimeCommonChannels(priv->ADCx, LL_ADC_SAMPLETIMES[priv->sample_time]); - LL_ADC_Enable(priv->ADCx); +// LL_ADC_Enable(priv->ADCx); } // --------------------- CONFIGURE DMA ------------------------------- @@ -193,10 +163,14 @@ error_t UADC_init(Unit *unit) // The length must be a 2*multiple of the number of channels, in bytes uint16_t itemcount = (uint16_t) ((priv->nb_channels) * (uint16_t) (priv->buffer_size / (2 * priv->nb_channels))); if (itemcount % 2 == 1) itemcount -= priv->nb_channels; - priv->dma_buffer_size = (uint16_t) (itemcount * 2); - dbg("DMA item count is %d (%d bytes)", itemcount, priv->dma_buffer_size); + priv->dma_buffer_itemcount = itemcount; + + dbg("DMA item count is %d (%d bytes), There are %d 2-byte samples per group.", + priv->dma_buffer_itemcount, + priv->dma_buffer_itemcount*sizeof(uint16_t), + priv->nb_channels); - priv->dma_buffer = malloc_ck(priv->dma_buffer_size); + priv->dma_buffer = malloc_ck(priv->dma_buffer_itemcount * sizeof(uint16_t)); if (NULL == priv->dma_buffer) return E_OUT_OF_MEM; assert_param(((uint32_t) priv->dma_buffer & 3) == 0); // must be aligned @@ -218,21 +192,25 @@ error_t UADC_init(Unit *unit) assert_param(SUCCESS == LL_DMA_Init(priv->DMAx, priv->dma_chnum, &init)); - irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit); // Interrupt on transfer 1/2 and complete // We will capture the first and second half and send it while the other half is being filled. - LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum); - LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); +// LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum); +// LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); } LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); } - dbg("ADC inited, starting the timer ..."); + // prepare the avg factor float for the ISR + if (priv->averaging_factor > 1000) priv->averaging_factor = 1000; // normalize + priv->avg_factor_as_float = priv->averaging_factor/1000.0f; + + dbg("ADC peripherals configured."); - // FIXME - temporary demo - counter start... - LL_ADC_REG_StartConversion(priv->ADCx); // the first conversion must be started manually - LL_TIM_EnableCounter(priv->TIMx); + irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit); + irqd_attach(priv->ADCx, UADC_ADC_EOS_Handler, unit); + + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); return E_SUCCESS; } @@ -246,11 +224,15 @@ void UADC_deInit(Unit *unit) // de-init peripherals if (unit->status == E_SUCCESS ) { + UADC_SwitchMode(unit, ADC_OPMODE_UNINIT); + //LL_ADC_DeInit(priv->ADCx); LL_ADC_CommonDeInit(priv->ADCx_Common); LL_TIM_DeInit(priv->TIMx); irqd_detach(priv->DMA_CHx, UADC_DMA_Handler); + irqd_detach(priv->ADCx, UADC_ADC_EOS_Handler); + LL_DMA_DeInit(priv->DMAx, priv->dma_chnum); free_ck(priv->dma_buffer); diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index e3d0cdf..91c6591 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -11,36 +11,60 @@ #include "unit_base.h" +enum uadc_opmode { + ADC_OPMODE_UNINIT, //!< Not yet switched to any mode + ADC_OPMODE_IDLE, //!< Idle, each sample overwrites the previous. Allows immediate value readout and averaging. + ADC_OPMODE_ARMED, //!< Armed for a trigger. Direct access and averaging are disabled. + ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data. + ADC_OPMODE_FIXCAPT,//!< Capture of fixed length without a trigger + ADC_OPMODE_STREAM, //!< Unlimited capture +}; + /** Private data structure */ struct priv { // settings uint16_t channels; //!< bit flags (will be recorded in order 0-15) bool enable_tsense; //!< append a signal from the temperature channel (voltage proportional to Tj) - bool enable_vref; //!< append a signal from the internal voltage reference + bool enable_vref; //!< append a signal from the internal voltage reference uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately uint16_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer - // TODO averaging (maybe a separate component?) - // TODO threshold watchdog with hysteresis (maybe a separate component?) - // TODO trigger level, edge direction, hold-off, pre-trigger buffer (extract from the DMA buffer) + bool enable_averaging; //!< Enable exponential averaging + uint16_t averaging_factor; //!< Exponential averaging factor 0-1000 // internal state - ADC_TypeDef *ADCx; - ADC_Common_TypeDef *ADCx_Common; - TIM_TypeDef *TIMx; - DMA_TypeDef *DMAx; - uint8_t dma_chnum; - DMA_Channel_TypeDef *DMA_CHx; - uint16_t *dma_buffer; - uint8_t nb_channels; // nr of enabled adc channels - uint16_t dma_buffer_size; // real number of bytes + uint32_t extended_channels_mask; //!< channels bitfield including tsense and vref + float avg_factor_as_float; + ADC_TypeDef *ADCx; //!< The ADC peripheral used + ADC_Common_TypeDef *ADCx_Common; //!< The ADC common control block + TIM_TypeDef *TIMx; //!< ADC timing timer instance + DMA_TypeDef *DMAx; //!< DMA isnatnce used + uint8_t dma_chnum; //!< DMA channel number + DMA_Channel_TypeDef *DMA_CHx; //!< DMA channel instance + uint16_t *dma_buffer; //!< malloc'd buffer for the samples + uint8_t nb_channels; //!< nbr of enabled adc channels + uint16_t dma_buffer_itemcount; //!< real size of the buffer (adjusted from the configured size to evenly encompass 2*size of one sample) + + enum uadc_opmode opmode; //!< OpMode (state machine state) + union { + float averaging_bins[18]; //!< Averaging buffers, enough space to accommodate all channels (16 external + 2 internal) + uint16_t last_sample[18]; //!< If averaging is disabled, the last captured sample is stored here. + }; + uint8_t trigger_source; //!< number of the pin selected as a trigger source + uint16_t pretrig_len; //!< Pre-trigger length, nbr of historical samples to report when trigger occurs + uint32_t trig_len; //!< Trigger length, nbr of samples to report AFTER a trigger occurs + uint16_t trig_level; //!< Triggering level in LSB + uint16_t trig_prev_level; //!< Value of the previous sample, used to detect trigger edge + uint8_t trig_edge; //!< Which edge we want to trigger on. 1-rising, 2-falling, 3-both + uint32_t trig_stream_remain; //!< Counter of samples remaining to be sent in the post-trigger stream + bool auto_rearm; //!< Flag that the trigger should be re-armed after the stream finishes + uint16_t trig_holdoff; //!< Trigger hold-off time, set when configuring the trigger + uint16_t trig_holdoff_remain; //!< Tmp counter for the currently active hold-off + uint16_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start. + //!< Updated in TH/TC and on trigger (after the preceding data is sent as a pretrig buffer) }; -// max size of the DMA buffer. The actual buffer size will be adjusted to accommodate -// an even number of sample groups (sets of channels) -#define UADC_DMA_MAX_BUF_LEN 512 - /** Allocate data structure and set defaults */ error_t UADC_preInit(Unit *unit); @@ -66,4 +90,21 @@ error_t UADC_init(Unit *unit); /** Tear down the unit */ void UADC_deInit(Unit *unit); +// ------------------------------------------------------------------------ + +/** DMA half/complete handler */ +void UADC_DMA_Handler(void *arg); + +/** ADC eod of sequence handler */ +void UADC_ADC_EOS_Handler(void *arg); + +/** Switch to a different opmode */ +void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode); + +/** Handle trigger - process pre-trigger and start streaming the requested number of samples */ +void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp); + +/** Handle a periodic tick - expiring the hold-off */ +void UADC_updateTick(Unit *unit); + #endif //GEX_F072_ADC_INTERNAL_H diff --git a/units/adc/_adc_settings.c b/units/adc/_adc_settings.c index e44b457..cd2e71c 100644 --- a/units/adc/_adc_settings.c +++ b/units/adc/_adc_settings.c @@ -83,15 +83,16 @@ void UADC_writeIni(Unit *unit, IniWriter *iw) struct priv *priv = unit->data; iw_comment(iw, "Enabled channels, comma separated"); - iw_comment(iw, "0-7 = A0-A7, 8-9 = B0-B1, 10-15 = C0-C5"); + iw_comment(iw, "0-7 = A0-A7, 8-9 = B0-B1, 10-15 = C0-C5"); iw_entry(iw, "channels", "%s", pinmask2str_up(priv->channels, unit_tmp512)); - iw_comment(iw, "Enable Tsense channel"); + iw_comment(iw, "Enable Tsense channel (#16)"); iw_entry(iw, "enable_tsense", str_yn(priv->enable_tsense)); - iw_comment(iw, "Enable Vref channel"); + iw_comment(iw, "Enable Vref channel (#17)"); iw_entry(iw, "enable_vref", str_yn(priv->enable_tsense)); + iw_cmt_newline(iw); iw_comment(iw, "Sampling time (0-7)"); iw_entry(iw, "sample_time", "%d", (int)priv->sample_time); @@ -103,5 +104,12 @@ void UADC_writeIni(Unit *unit, IniWriter *iw) iw_comment(iw, "- the buffer is shared by all channels"); iw_comment(iw, "- insufficient buffer size can lead to data loss"); iw_entry(iw, "buffer_size", "%d", (int)priv->buffer_size); + + iw_cmt_newline(iw); + iw_comment(iw, "Enable exponential averaging (only when not streaming)"); + iw_comment(iw, "Used formula: y[t]=(1-k)*y[t-1]+k*u[t]"); + iw_entry(iw, "averaging", str_yn(priv->enable_averaging)); + iw_comment(iw, "Averaging factor k (permil, range 0-1000 ~ 0.000-1.000)"); + iw_entry(iw, "avg_factor", "%d", priv->averaging_factor); } diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index c1a3e73..37760fe 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -40,4 +40,5 @@ const UnitDriver UNIT_ADC = { .deInit = UADC_deInit, // Function .handleRequest = UADC_handleRequest, + .updateTick = UADC_updateTick, }; diff --git a/utils/malloc_safe.c b/utils/malloc_safe.c index fa57637..77b4b8d 100644 --- a/utils/malloc_safe.c +++ b/utils/malloc_safe.c @@ -5,6 +5,11 @@ void *malloc_ck_do(size_t size, const char *file, uint32_t line) { + if (size == 0) { + _warn_msg(file, line, "MALLOC OF SIZE 0"); + return NULL; + } + void *mem = pvPortMalloc(size); _malloc_trace(size, mem, file, line); if (mem == NULL) { @@ -16,6 +21,7 @@ void *malloc_ck_do(size_t size, const char *file, uint32_t line) void *calloc_ck_do(size_t nmemb, size_t size, const char *file, uint32_t line) { void *mem = malloc_ck_do(nmemb*size, file, line); + if (mem == NULL) return NULL; memset(mem, 0, size*nmemb); return mem; } From 260fcc3e65cd5a51387b81c2f365275f4cbeeb41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 4 Feb 2018 19:00:57 +0100 Subject: [PATCH 09/20] experimental ADC front-end with dummy reports --- framework/unit_registry.c | 3 +- units/1wire/_ow_api.c | 2 +- units/adc/_adc_core.c | 75 ++++++++++++--- units/adc/_adc_init.c | 1 - units/adc/_adc_internal.h | 36 +++++--- units/adc/_adc_settings.c | 17 +++- units/adc/unit_adc.c | 186 +++++++++++++++++++++++++++++++++++++- utils/error.h | 2 +- 8 files changed, 284 insertions(+), 38 deletions(-) diff --git a/framework/unit_registry.c b/framework/unit_registry.c index 482dda8..ec3e5d3 100644 --- a/framework/unit_registry.c +++ b/framework/unit_registry.c @@ -495,7 +495,8 @@ void ureg_deliver_unit_request(TF_Msg *msg) if (rv == E_SUCCESS) { if (confirmed) com_respond_ok(msg->frame_id); } - else { + else if (rv != E_FAILURE) { + // Failure is returned when the handler already sent an error response. com_respond_error(msg->frame_id, rv); } goto quit; diff --git a/units/1wire/_ow_api.c b/units/1wire/_ow_api.c index ea96635..6c9b94b 100644 --- a/units/1wire/_ow_api.c +++ b/units/1wire/_ow_api.c @@ -125,5 +125,5 @@ error_t UU_1WIRE_Search(Unit *unit, bool with_alarm, bool restart, return priv->searchState.error; } - return E_FAILURE; + return E_INTERNAL_ERROR; } diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index ddb53fd..aa56d81 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -8,6 +8,11 @@ #define ADC_INTERNAL #include "_adc_internal.h" +void UADC_ReportEndOfStream(Unit *unit) +{ + dbg("~~End Of Stream msg~~"); +} + void UADC_DMA_Handler(void *arg) { Unit *unit = arg; @@ -59,6 +64,7 @@ void UADC_DMA_Handler(void *arg) if (m_trig || m_fixcpt) { if (priv->trig_stream_remain == 0) { dbg("End of capture"); + UADC_ReportEndOfStream(unit); UADC_SwitchMode(unit, (priv->auto_rearm && m_trig) ? ADC_OPMODE_ARMED : ADC_OPMODE_IDLE); } } @@ -117,32 +123,31 @@ void UADC_ADC_EOS_Handler(void *arg) uint16_t val = priv->dma_buffer[sample_pos]; dbg("Trig line level %d", (int)val); - if (priv->enable_averaging) { - priv->averaging_bins[i] = - priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + - ((float) val) * priv->avg_factor_as_float; - } else { - priv->last_sample[i] = val; - } + priv->averaging_bins[i] = + priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + + ((float) val) * priv->avg_factor_as_float; + + priv->last_samples[i] = val; if (priv->opmode == ADC_OPMODE_ARMED) { if (i == priv->trigger_source) { bool trigd = false; - bool rising = false; + uint8_t edge_type = 0; if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) { dbg("******** Rising edge"); // Rising edge trigd = (bool) (priv->trig_edge & 0b01); - rising = true; + edge_type = 1; } else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) { dbg("******** Falling edge"); // Falling edge trigd = (bool) (priv->trig_edge & 0b10); + edge_type = 2; } if (trigd) { - UADC_HandleTrigger(unit, rising, timestamp); + UADC_HandleTrigger(unit, edge_type, timestamp); } priv->trig_prev_level = val; @@ -154,7 +159,7 @@ void UADC_ADC_EOS_Handler(void *arg) dbg(" EOS ISR end."); } -void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp) +void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) { assert_param(unit); struct priv *priv = unit->data; @@ -172,7 +177,7 @@ void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp) unit->_tick_cnt = 0; } - dbg("Trigger condition hit, rising=%d", rising); + dbg("Trigger condition hit, edge=%d", edge_type); // TODO Send pre-trigger priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR; @@ -180,6 +185,44 @@ void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp) UADC_SwitchMode(unit, ADC_OPMODE_TRIGD); } +void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id) +{ + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + priv->stream_frame_id = frame_id; + priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR; + priv->trig_stream_remain = len; + UADC_SwitchMode(unit, ADC_OPMODE_FIXCAPT); +} + +/** Start stream */ +void UADC_StartStream(Unit *unit, TF_ID frame_id) +{ + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + priv->stream_frame_id = frame_id; + priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR; + dbg("Start streaming."); + UADC_SwitchMode(unit, ADC_OPMODE_STREAM); +} + +/** End stream */ +void UADC_StopStream(Unit *unit) +{ + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + dbg("Stop stream."); + UADC_ReportEndOfStream(unit); + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); +} + +/** Handle unit update tick - expire the trigger hold-off */ void UADC_updateTick(Unit *unit) { assert_param(unit); @@ -234,6 +277,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) else if (new_mode == ADC_OPMODE_IDLE) { dbg("ADC switch -> IDLE"); // IDLE and ARMED are identical with the exception that the trigger condition is not checked + // ARMED can be only entered from IDLE, thus we do the init only here. // In IDLE, we don't need the DMA interrupts LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); @@ -254,8 +298,11 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) assert_param(priv->opmode == ADC_OPMODE_IDLE); // there's nothing else to do here } - else if (new_mode == ADC_OPMODE_TRIGD || new_mode == ADC_OPMODE_STREAM) { - dbg("ADC switch -> TRIG'D or STREAM"); + else if (new_mode == ADC_OPMODE_TRIGD || + new_mode == ADC_OPMODE_STREAM || + new_mode == ADC_OPMODE_FIXCAPT) { + + dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE); // during the capture, we disallow direct readout and averaging to reduce overhead diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 5f5ed11..e3e129b 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -21,7 +21,6 @@ error_t UADC_preInit(Unit *unit) priv->sample_time = 0b010; // 13.5c priv->frequency = 1000; priv->buffer_size = 512; - priv->enable_averaging = false; priv->averaging_factor = 500; priv->opmode = ADC_OPMODE_UNINIT; diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index 91c6591..bda6c61 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -29,8 +29,6 @@ struct priv { uint8_t sample_time; //!< 0-7 (corresponds to 1.5-239.5 cycles) - time for the sampling capacitor to charge uint32_t frequency; //!< Timer frequency in Hz. Note: not all frequencies can be achieved accurately uint16_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer - - bool enable_averaging; //!< Enable exponential averaging uint16_t averaging_factor; //!< Exponential averaging factor 0-1000 // internal state @@ -44,25 +42,25 @@ struct priv { DMA_Channel_TypeDef *DMA_CHx; //!< DMA channel instance uint16_t *dma_buffer; //!< malloc'd buffer for the samples uint8_t nb_channels; //!< nbr of enabled adc channels - uint16_t dma_buffer_itemcount; //!< real size of the buffer (adjusted from the configured size to evenly encompass 2*size of one sample) + uint16_t dma_buffer_itemcount; //!< real size of the buffer in samples (adjusted to fit 2x whole multiple of sample group) + uint32_t trig_stream_remain; //!< Counter of samples remaining to be sent in the post-trigger stream + uint16_t trig_holdoff_remain; //!< Tmp counter for the currently active hold-off + uint16_t trig_prev_level; //!< Value of the previous sample, used to detect trigger edge + uint16_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start. + //!< Updated in TH/TC and on trigger (after the preceding data is sent as a pretrig buffer) enum uadc_opmode opmode; //!< OpMode (state machine state) - union { - float averaging_bins[18]; //!< Averaging buffers, enough space to accommodate all channels (16 external + 2 internal) - uint16_t last_sample[18]; //!< If averaging is disabled, the last captured sample is stored here. - }; + float averaging_bins[18]; //!< Averaging buffers, enough space to accommodate all channels (16 external + 2 internal) + uint16_t last_samples[18]; //!< If averaging is disabled, the last captured sample is stored here. + uint8_t trigger_source; //!< number of the pin selected as a trigger source uint16_t pretrig_len; //!< Pre-trigger length, nbr of historical samples to report when trigger occurs uint32_t trig_len; //!< Trigger length, nbr of samples to report AFTER a trigger occurs uint16_t trig_level; //!< Triggering level in LSB - uint16_t trig_prev_level; //!< Value of the previous sample, used to detect trigger edge uint8_t trig_edge; //!< Which edge we want to trigger on. 1-rising, 2-falling, 3-both - uint32_t trig_stream_remain; //!< Counter of samples remaining to be sent in the post-trigger stream bool auto_rearm; //!< Flag that the trigger should be re-armed after the stream finishes uint16_t trig_holdoff; //!< Trigger hold-off time, set when configuring the trigger - uint16_t trig_holdoff_remain; //!< Tmp counter for the currently active hold-off - uint16_t stream_startpos; //!< Byte offset in the DMA buffer where the next capture for a stream should start. - //!< Updated in TH/TC and on trigger (after the preceding data is sent as a pretrig buffer) + TF_ID stream_frame_id; //!< Session ID for multi-part stream (response or report) }; /** Allocate data structure and set defaults */ @@ -102,9 +100,21 @@ void UADC_ADC_EOS_Handler(void *arg); void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode); /** Handle trigger - process pre-trigger and start streaming the requested number of samples */ -void UADC_HandleTrigger(Unit *unit, bool rising, uint64_t timestamp); +void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp); /** Handle a periodic tick - expiring the hold-off */ void UADC_updateTick(Unit *unit); +/** Send a end-of-stream message to PC's stream listener so it can shut down. */ +void UADC_ReportEndOfStream(Unit *unit); + +/** Start a block capture */ +void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id); + +/** Start stream */ +void UADC_StartStream(Unit *unit, TF_ID frame_id); + +/** End stream */ +void UADC_StopStream(Unit *unit); + #endif //GEX_F072_ADC_INTERNAL_H diff --git a/units/adc/_adc_settings.c b/units/adc/_adc_settings.c index cd2e71c..e19811e 100644 --- a/units/adc/_adc_settings.c +++ b/units/adc/_adc_settings.c @@ -25,6 +25,9 @@ void UADC_loadBinary(Unit *unit, PayloadParser *pp) if (version >= 1) { priv->buffer_size = pp_u16(pp); } + if (version >= 2) { + priv->averaging_factor = pp_u16(pp); + } } /** Write to a binary buffer for storing in Flash */ @@ -32,7 +35,7 @@ void UADC_writeBinary(Unit *unit, PayloadBuilder *pb) { struct priv *priv = unit->data; - pb_u8(pb, 1); // version + pb_u8(pb, 2); // version pb_u16(pb, priv->channels); pb_bool(pb, priv->enable_tsense); @@ -40,6 +43,7 @@ void UADC_writeBinary(Unit *unit, PayloadBuilder *pb) pb_u8(pb, priv->sample_time); pb_u32(pb, priv->frequency); pb_u16(pb, priv->buffer_size); + pb_u16(pb, priv->averaging_factor); } // ------------------------------------------------------------------------ @@ -69,6 +73,10 @@ error_t UADC_loadIni(Unit *unit, const char *key, const char *value) else if (streq(key, "buffer_size")) { priv->buffer_size = (uint16_t) avr_atoi(value); } + else if (streq(key, "avg_factor")) { + priv->averaging_factor = (uint16_t) avr_atoi(value); + if (priv->averaging_factor > 1000) return E_BAD_VALUE; + } else { return E_BAD_KEY; } @@ -106,10 +114,9 @@ void UADC_writeIni(Unit *unit, IniWriter *iw) iw_entry(iw, "buffer_size", "%d", (int)priv->buffer_size); iw_cmt_newline(iw); - iw_comment(iw, "Enable exponential averaging (only when not streaming)"); - iw_comment(iw, "Used formula: y[t]=(1-k)*y[t-1]+k*u[t]"); - iw_entry(iw, "averaging", str_yn(priv->enable_averaging)); - iw_comment(iw, "Averaging factor k (permil, range 0-1000 ~ 0.000-1.000)"); + iw_comment(iw, "Exponential averaging coefficient (permil, range 0-1000 ~ 0.000-1.000)"); + iw_comment(iw, "- used formula: y[t]=(1-k)*y[t-1]+k*u[t]"); + iw_comment(iw, "- available only for direct readout (i.e. not used in block capture)"); iw_entry(iw, "avg_factor", "%d", priv->averaging_factor); } diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index 37760fe..0e5213f 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -11,13 +11,195 @@ // ------------------------------------------------------------------------ enum TplCmd_ { - CMD_DUMMY, + CMD_READ_RAW = 0, + CMD_READ_SMOOTHED = 1, + + CMD_GET_ENABLED_CHANNELS = 10, + + CMD_SETUP_TRIGGER = 20, + CMD_ARM = 21, + CMD_DISARM = 22, + CMD_ABORT = 23, // abort any ongoing capture or stream + CMD_FORCE_TRIGGER = 24, + CMD_BLOCK_CAPTURE = 25, + CMD_STREAM_START = 26, + CMD_STREAM_STOP = 27, }; /** Handle a request message */ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) { + struct priv *priv = unit->data; + PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL); + switch (command) { + case CMD_GET_ENABLED_CHANNELS: + dbg("> Query channels"); + for (uint8_t i = 0; i < 18; i++) { + if (priv->extended_channels_mask & (1 << i)) { + pb_u8(&pb, i); + } + } + com_respond_pb(frame_id, MSG_SUCCESS, &pb); + return E_SUCCESS; + + case CMD_READ_RAW: + dbg("> Read raw"); + if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) { + return E_BUSY; + } + + for (uint8_t i = 0; i < 18; i++) { + if (priv->extended_channels_mask & (1 << i)) { + pb_u8(&pb, i); + pb_u16(&pb, priv->last_samples[i]); + } + } + assert_param(pb.ok); + com_respond_pb(frame_id, MSG_SUCCESS, &pb); + return E_SUCCESS; + + case CMD_READ_SMOOTHED: + dbg("> Read smoothed"); + if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) { + return E_BUSY; + } + + for (uint8_t i = 0; i < 18; i++) { + if (priv->extended_channels_mask & (1 << i)) { + pb_u8(&pb, i); + pb_float(&pb, priv->averaging_bins[i]); + } + } + assert_param(pb.ok); + com_respond_pb(frame_id, MSG_SUCCESS, &pb); + return E_SUCCESS; + + case CMD_SETUP_TRIGGER: + dbg("> Setup trigger"); + if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + { + uint8_t source = pp_u8(pp); + uint16_t level = pp_u16(pp); + uint8_t edge = pp_u8(pp); + uint16_t pretrig = pp_u16(pp); + uint32_t count = pp_u32(pp); + uint16_t holdoff = pp_u16(pp); + bool auto_rearm = pp_bool(pp); + + if (source > 17) { + com_respond_str(MSG_ERROR, frame_id, "Invalid trig source"); + return E_FAILURE; + } + + if (0 == (priv->extended_channels_mask & (1 << source))) { + com_respond_str(MSG_ERROR, frame_id, "Channel not enabled"); + return E_FAILURE; + } + + if (level > 4095) { + com_respond_str(MSG_ERROR, frame_id, "Level out of range (0-4095)"); + return E_FAILURE; + } + + if (edge == 0 || edge > 3) { + com_respond_str(MSG_ERROR, frame_id, "Bad edge"); + return E_FAILURE; + } + + // XXX the max size may be too much + uint16_t max_pretrig = (priv->dma_buffer_itemcount / priv->nb_channels); + if (pretrig > max_pretrig) { + com_respond_snprintf(frame_id, MSG_ERROR, + "Pretrig too large (max %d)", (int) max_pretrig); + return E_FAILURE; + } + + priv->trigger_source = source; + priv->trig_level = level; + priv->trig_prev_level = priv->last_samples[source]; + priv->trig_edge = edge; + priv->pretrig_len = pretrig; + priv->trig_len = count; + priv->trig_holdoff = holdoff; + priv->auto_rearm = auto_rearm; + } + return E_SUCCESS; + + case CMD_ARM: + dbg("> Arm"); + if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + + if (priv->trig_len == 0) { + com_respond_str(MSG_ERROR, frame_id, "Trigger not configured."); + return E_FAILURE; + } + + UADC_SwitchMode(unit, ADC_OPMODE_ARMED); + return E_SUCCESS; + + case CMD_DISARM: + dbg("> Disarm"); + if(priv->opmode == ADC_OPMODE_IDLE) { + return E_SUCCESS; // already idle, success - no work to do + } + // capture in progress + if(priv->opmode != ADC_OPMODE_ARMED) return E_BUSY; + + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + return E_SUCCESS; + + case CMD_ABORT:; + dbg("> Abort capture"); + enum uadc_opmode old_opmode = priv->opmode; + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + + if (old_opmode == ADC_OPMODE_FIXCAPT || + old_opmode == ADC_OPMODE_STREAM || + old_opmode == ADC_OPMODE_TRIGD) { + UADC_ReportEndOfStream(unit); + } + return E_SUCCESS; + + case CMD_FORCE_TRIGGER: + dbg("> Force trigger"); + if(priv->opmode == ADC_OPMODE_IDLE) { + com_respond_str(MSG_ERROR, frame_id, "Not armed"); + return E_FAILURE; + } + if(priv->opmode != ADC_OPMODE_ARMED) return E_BUSY; + + UADC_HandleTrigger(unit, 0b11, PTIM_GetMicrotime()); + return E_SUCCESS; + + case CMD_BLOCK_CAPTURE: + dbg("> Block cpt"); + if(priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + + uint32_t count = pp_u32(pp); + + UADC_StartBlockCapture(unit, count, frame_id); + return E_SUCCESS; + + case CMD_STREAM_START: + dbg("> Stream ON"); + if(priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + + UADC_StartStream(unit, frame_id); + return E_SUCCESS; + + case CMD_STREAM_STOP: + dbg("> Stream OFF"); + if(priv->opmode != ADC_OPMODE_STREAM) { + com_respond_str(MSG_ERROR, frame_id, "Not streaming"); + return E_FAILURE; + } + + UADC_StopStream(unit); + return E_SUCCESS; + default: return E_UNKNOWN_COMMAND; } @@ -28,7 +210,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P /** Unit template */ const UnitDriver UNIT_ADC = { .name = "ADC", - .description = "Analog inputs", + .description = "Analog/Digital converter", // Settings .preInit = UADC_preInit, .cfgLoadBinary = UADC_loadBinary, diff --git a/utils/error.h b/utils/error.h index b0daf02..3b165af 100644 --- a/utils/error.h +++ b/utils/error.h @@ -13,7 +13,7 @@ #define X_ERROR_CODES \ /* Shared errors */ \ X(SUCCESS, NULL) /* operation succeeded / unit loaded. Must be 0 */ \ - X(FAILURE, NULL) /* generic error */ \ + X(FAILURE, NULL) /* generic error. If returned from a unit handler, does NOT generate a response. */ \ X(INTERNAL_ERROR, NULL) /* a bug */ \ X(LOADING, NULL) /* unit is loading */ \ X(UNKNOWN_COMMAND, NULL) \ From 21d9e976539b97554fc554bbe7aeb04d93420638 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 4 Feb 2018 21:24:24 +0100 Subject: [PATCH 10/20] added docs --- units/adc/_adc_core.c | 32 ++++++++++++--------- units/adc/unit_adc.c | 66 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 84 insertions(+), 14 deletions(-) diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index aa56d81..b197d6d 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -8,6 +8,8 @@ #define ADC_INTERNAL #include "_adc_internal.h" +#define DMA_POS(priv) ((priv)->dma_buffer_itemcount - (priv)->DMA_CHx->CNDTR) + void UADC_ReportEndOfStream(Unit *unit) { dbg("~~End Of Stream msg~~"); @@ -101,27 +103,29 @@ void UADC_ADC_EOS_Handler(void *arg) uint64_t timestamp = PTIM_GetMicrotime(); Unit *unit = arg; - dbg("ADC EOS ISR hit"); assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + LL_ADC_ClearFlag_EOS(priv->ADCx); + // Wait for the DMA to complete copying the last sample - while (priv->DMA_CHx->CNDTR % priv->nb_channels != 0); + uint16_t dmapos; + while ((dmapos = (uint16_t) DMA_POS(priv)) % priv->nb_channels != 0); uint32_t sample_pos; - if (priv->DMA_CHx->CNDTR == 0) { - sample_pos = (uint32_t) (priv->dma_buffer_itemcount - 1); + if (dmapos == 0) { + sample_pos = (uint32_t) (priv->dma_buffer_itemcount); } else { - sample_pos = priv->DMA_CHx->CNDTR; + sample_pos = dmapos; } sample_pos -= priv->nb_channels; - dbg("Sample pos %d", (int)sample_pos); + int cnt = 0; // index of the sample within the group for (uint32_t i = 0; i < 18; i++) { if (priv->extended_channels_mask & (1 << i)) { - uint16_t val = priv->dma_buffer[sample_pos]; - dbg("Trig line level %d", (int)val); + uint16_t val = priv->dma_buffer[sample_pos+cnt]; + cnt++; priv->averaging_bins[i] = priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + @@ -131,6 +135,8 @@ void UADC_ADC_EOS_Handler(void *arg) if (priv->opmode == ADC_OPMODE_ARMED) { if (i == priv->trigger_source) { + dbg("Trig line level %d", (int)val); + bool trigd = false; uint8_t edge_type = 0; if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) { @@ -155,8 +161,6 @@ void UADC_ADC_EOS_Handler(void *arg) } } } - - dbg(" EOS ISR end."); } void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) @@ -180,7 +184,7 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) dbg("Trigger condition hit, edge=%d", edge_type); // TODO Send pre-trigger - priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR; + priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->trig_stream_remain = priv->trig_len; UADC_SwitchMode(unit, ADC_OPMODE_TRIGD); } @@ -192,7 +196,7 @@ void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id) assert_param(priv); priv->stream_frame_id = frame_id; - priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR; + priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->trig_stream_remain = len; UADC_SwitchMode(unit, ADC_OPMODE_FIXCAPT); } @@ -205,7 +209,7 @@ void UADC_StartStream(Unit *unit, TF_ID frame_id) assert_param(priv); priv->stream_frame_id = frame_id; - priv->stream_startpos = (uint16_t) priv->DMA_CHx->CNDTR; + priv->stream_startpos = (uint16_t) DMA_POS(priv); dbg("Start streaming."); UADC_SwitchMode(unit, ADC_OPMODE_STREAM); } @@ -291,6 +295,8 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_ADC_Enable(priv->ADCx); LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); LL_TIM_EnableCounter(priv->TIMx); + + LL_ADC_REG_StartConversion(priv->ADCx); } } else if (new_mode == ADC_OPMODE_ARMED) { diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index 0e5213f..f595e30 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -24,6 +24,7 @@ enum TplCmd_ { CMD_BLOCK_CAPTURE = 25, CMD_STREAM_START = 26, CMD_STREAM_STOP = 27, + CMD_SET_SMOOTHING_FACTOR = 28, }; /** Handle a request message */ @@ -33,6 +34,10 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL); switch (command) { + /** + * Get enabled channels. + * Response: bytes with indices of enabled channels, ascending order. + */ case CMD_GET_ENABLED_CHANNELS: dbg("> Query channels"); for (uint8_t i = 0; i < 18; i++) { @@ -43,6 +48,21 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P com_respond_pb(frame_id, MSG_SUCCESS, &pb); return E_SUCCESS; + /** + * Set smoothing factor 0-1000. + * pld: u16:factor + */ + case CMD_SET_SMOOTHING_FACTOR: + dbg("> Set smoothing"); + uint16_t fac = pp_u16(pp); + if (fac > 1000) return E_BAD_VALUE; + priv->avg_factor_as_float = fac/1000.0f; + return E_SUCCESS; + + /** + * Read raw values from the last measurement. + * Response: interleaved (u8:channel, u16:value) for all channels + */ case CMD_READ_RAW: dbg("> Read raw"); if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) { @@ -59,6 +79,10 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P com_respond_pb(frame_id, MSG_SUCCESS, &pb); return E_SUCCESS; + /** + * Read smoothed values. + * Response: interleaved (u8:channel, f32:value) for all channels + */ case CMD_READ_SMOOTHED: dbg("> Read smoothed"); if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) { @@ -75,9 +99,22 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P com_respond_pb(frame_id, MSG_SUCCESS, &pb); return E_SUCCESS; + /** + * Configure a trigger. This is legal only if the current state is IDLE. + * + * Payload: + * u8 - source channel + * u16 - triggering level + * u8 - edge to trigger on: 1-rising, 2-falling, 3-both + * u16 - pre-trigger samples count + * u32 - post-trigger samples count + * u16 - trigger hold-off in ms (dead time after firing, before it cna fire again if armed) + * u8(bool) - auto re-arm after firing and completing the capture + */ case CMD_SETUP_TRIGGER: dbg("> Setup trigger"); if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + { uint8_t source = pp_u8(pp); uint16_t level = pp_u16(pp); @@ -126,6 +163,9 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P } return E_SUCCESS; + /** + * Arm (permissible only if idle and the trigger is configured) + */ case CMD_ARM: dbg("> Arm"); if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; @@ -138,6 +178,10 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P UADC_SwitchMode(unit, ADC_OPMODE_ARMED); return E_SUCCESS; + /** + * Dis-arm. Permissible only when idle or armed. + * Switches to idle. + */ case CMD_DISARM: dbg("> Disarm"); if(priv->opmode == ADC_OPMODE_IDLE) { @@ -149,6 +193,9 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P UADC_SwitchMode(unit, ADC_OPMODE_IDLE); return E_SUCCESS; + /** + * Abort any ongoing capture and dis-arm. + */ case CMD_ABORT:; dbg("> Abort capture"); enum uadc_opmode old_opmode = priv->opmode; @@ -161,6 +208,10 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P } return E_SUCCESS; + /** + * Force a trigger (complete with pre-trigger capture and hold-off) + * The reported edge will be 0b11, here meaning "manual trigger" + */ case CMD_FORCE_TRIGGER: dbg("> Force trigger"); if(priv->opmode == ADC_OPMODE_IDLE) { @@ -172,6 +223,12 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P UADC_HandleTrigger(unit, 0b11, PTIM_GetMicrotime()); return E_SUCCESS; + /** + * Start a block capture (like manual trigger, but without pre-trigger and arming) + * + * Payload: + * u32 - sample count (for each channel) + */ case CMD_BLOCK_CAPTURE: dbg("> Block cpt"); if(priv->opmode != ADC_OPMODE_ARMED && @@ -182,6 +239,10 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P UADC_StartBlockCapture(unit, count, frame_id); return E_SUCCESS; + /** + * Start streaming (like block capture, but unlimited) + * The stream can be terminated by the stop command. + */ case CMD_STREAM_START: dbg("> Stream ON"); if(priv->opmode != ADC_OPMODE_ARMED && @@ -190,6 +251,9 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P UADC_StartStream(unit, frame_id); return E_SUCCESS; + /** + * Stop a stream. + */ case CMD_STREAM_STOP: dbg("> Stream OFF"); if(priv->opmode != ADC_OPMODE_STREAM) { @@ -210,7 +274,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P /** Unit template */ const UnitDriver UNIT_ADC = { .name = "ADC", - .description = "Analog/Digital converter", + .description = "Analog/digital converter", // Settings .preInit = UADC_preInit, .cfgLoadBinary = UADC_loadBinary, From a4e04dc04ebafa0ca4e2de04c4376d3a2a9c2e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 4 Feb 2018 22:32:22 +0100 Subject: [PATCH 11/20] triggering works, also autotrig --- framework/unit_registry.c | 5 ++++ units/adc/_adc_core.c | 48 +++++++++++++++++++++++++++------------ units/adc/unit_adc.c | 21 ++++++++--------- 3 files changed, 48 insertions(+), 26 deletions(-) diff --git a/framework/unit_registry.c b/framework/unit_registry.c index ec3e5d3..bb86421 100644 --- a/framework/unit_registry.c +++ b/framework/unit_registry.c @@ -490,6 +490,11 @@ void ureg_deliver_unit_request(TF_Msg *msg) if (pUnit->callsign == callsign && pUnit->status == E_SUCCESS) { error_t rv = pUnit->driver->handleRequest(pUnit, msg->frame_id, command, &pp); + if (!pp.ok) { + com_respond_error(msg->frame_id, E_MALFORMED_COMMAND); + goto quit; + } + // send extra SUCCESS confirmation message. // error is expected to have already been reported. if (rv == E_SUCCESS) { diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index b197d6d..53c0905 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -51,27 +51,37 @@ void UADC_DMA_Handler(void *arg) LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); } - assert_param(start < end); + dbg("start %d, end %d", (int)start, (int)end); + assert_param(start <= end); - uint32_t sgcount = (end - start) / priv->nb_channels; + if (start != end) { + uint32_t sgcount = (end - start) / priv->nb_channels; - if (m_trig || m_fixcpt) { - sgcount = MIN(priv->trig_stream_remain, sgcount); - priv->trig_stream_remain -= sgcount; - } + if (m_trig || m_fixcpt) { + sgcount = MIN(priv->trig_stream_remain, sgcount); + priv->trig_stream_remain -= sgcount; + } - dbg("Would send %d groups (u16 offset %d -> %d)", (int)sgcount, (int)start, (int)(start+sgcount*priv->nb_channels)); - // TODO send the data together with remaining count (used to detect end of transmission) + dbg("Would send %d groups (u16 offset %d -> %d)", (int) sgcount, + (int) start, (int) (start + sgcount * priv->nb_channels)); - if (m_trig || m_fixcpt) { - if (priv->trig_stream_remain == 0) { - dbg("End of capture"); - UADC_ReportEndOfStream(unit); - UADC_SwitchMode(unit, (priv->auto_rearm && m_trig) ? ADC_OPMODE_ARMED : ADC_OPMODE_IDLE); + // TODO send the data together with remaining count (used to detect end of transmission) + + if (m_trig || m_fixcpt) { + if (priv->trig_stream_remain == 0) { + dbg("End of capture"); + UADC_ReportEndOfStream(unit); + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + if (priv->auto_rearm && m_trig) { + UADC_SwitchMode(unit, ADC_OPMODE_ARMED); + } + } } + } else { + dbg("start==end, skip this irq"); } - if (end == priv->dma_buffer_itemcount) { + if (tc) { priv->stream_startpos = 0; } else { @@ -181,11 +191,13 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) unit->_tick_cnt = 0; } - dbg("Trigger condition hit, edge=%d", edge_type); // TODO Send pre-trigger priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->trig_stream_remain = priv->trig_len; + + dbg("Trigger condition hit, edge=%d, startpos %d", edge_type, (int)priv->stream_startpos); + UADC_SwitchMode(unit, ADC_OPMODE_TRIGD); } @@ -288,6 +300,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); // Use End Of Sequence to recover results for averaging from the DMA buffer and DR + LL_ADC_ClearFlag_EOS(priv->ADCx); LL_ADC_EnableIT_EOS(priv->ADCx); if (priv->opmode == ADC_OPMODE_UNINIT) { @@ -315,6 +328,11 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_ADC_DisableIT_EOS(priv->ADCx); // Enable the DMA buffer interrupts + + // we must first clear the flags, otherwise it will cause WEIRD bugs in the handler + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + LL_DMA_EnableIT_HT(priv->DMAx, priv->dma_chnum); LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); } diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index f595e30..bc28b71 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -39,7 +39,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Response: bytes with indices of enabled channels, ascending order. */ case CMD_GET_ENABLED_CHANNELS: - dbg("> Query channels"); for (uint8_t i = 0; i < 18; i++) { if (priv->extended_channels_mask & (1 << i)) { pb_u8(&pb, i); @@ -53,10 +52,11 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * pld: u16:factor */ case CMD_SET_SMOOTHING_FACTOR: - dbg("> Set smoothing"); - uint16_t fac = pp_u16(pp); - if (fac > 1000) return E_BAD_VALUE; - priv->avg_factor_as_float = fac/1000.0f; + { + uint16_t fac = pp_u16(pp); + if (fac > 1000) return E_BAD_VALUE; + priv->avg_factor_as_float = fac / 1000.0f; + } return E_SUCCESS; /** @@ -64,7 +64,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Response: interleaved (u8:channel, u16:value) for all channels */ case CMD_READ_RAW: - dbg("> Read raw"); if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) { return E_BUSY; } @@ -75,7 +74,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P pb_u16(&pb, priv->last_samples[i]); } } - assert_param(pb.ok); com_respond_pb(frame_id, MSG_SUCCESS, &pb); return E_SUCCESS; @@ -84,7 +82,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Response: interleaved (u8:channel, f32:value) for all channels */ case CMD_READ_SMOOTHED: - dbg("> Read smoothed"); if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) { return E_BUSY; } @@ -95,12 +92,11 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P pb_float(&pb, priv->averaging_bins[i]); } } - assert_param(pb.ok); com_respond_pb(frame_id, MSG_SUCCESS, &pb); return E_SUCCESS; /** - * Configure a trigger. This is legal only if the current state is IDLE. + * Configure a trigger. This is legal only if the current state is IDLE or ARMED (will re-arm). * * Payload: * u8 - source channel @@ -113,7 +109,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P */ case CMD_SETUP_TRIGGER: dbg("> Setup trigger"); - if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + if (priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) return E_BUSY; { uint8_t source = pp_u8(pp); @@ -175,6 +171,9 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P return E_FAILURE; } + // avoid firing immediately by the value jumping across the scale + priv->trig_prev_level = priv->last_samples[priv->trigger_source]; + UADC_SwitchMode(unit, ADC_OPMODE_ARMED); return E_SUCCESS; From f80761b327dacd7e839307420b88f3a042c60bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Sun, 4 Feb 2018 22:33:51 +0100 Subject: [PATCH 12/20] todos --- units/adc/unit_adc.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index bc28b71..c2d6744 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -114,10 +114,10 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P { uint8_t source = pp_u8(pp); uint16_t level = pp_u16(pp); - uint8_t edge = pp_u8(pp); - uint16_t pretrig = pp_u16(pp); + uint8_t edge = pp_u8(pp); // TODO test falling edge and dual edge + uint16_t pretrig = pp_u16(pp); // TODO test pre-trigger ... uint32_t count = pp_u32(pp); - uint16_t holdoff = pp_u16(pp); + uint16_t holdoff = pp_u16(pp); // TODO test hold-off ... bool auto_rearm = pp_bool(pp); if (source > 17) { @@ -182,6 +182,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Switches to idle. */ case CMD_DISARM: + // TODO test dbg("> Disarm"); if(priv->opmode == ADC_OPMODE_IDLE) { return E_SUCCESS; // already idle, success - no work to do @@ -196,6 +197,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Abort any ongoing capture and dis-arm. */ case CMD_ABORT:; + // TODO test dbg("> Abort capture"); enum uadc_opmode old_opmode = priv->opmode; UADC_SwitchMode(unit, ADC_OPMODE_IDLE); @@ -212,6 +214,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * The reported edge will be 0b11, here meaning "manual trigger" */ case CMD_FORCE_TRIGGER: + // TODO test dbg("> Force trigger"); if(priv->opmode == ADC_OPMODE_IDLE) { com_respond_str(MSG_ERROR, frame_id, "Not armed"); @@ -229,6 +232,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * u32 - sample count (for each channel) */ case CMD_BLOCK_CAPTURE: + // TODO test dbg("> Block cpt"); if(priv->opmode != ADC_OPMODE_ARMED && priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; @@ -243,6 +247,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * The stream can be terminated by the stop command. */ case CMD_STREAM_START: + // TODO test dbg("> Stream ON"); if(priv->opmode != ADC_OPMODE_ARMED && priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; @@ -254,6 +259,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Stop a stream. */ case CMD_STREAM_STOP: + // TODO test dbg("> Stream OFF"); if(priv->opmode != ADC_OPMODE_STREAM) { com_respond_str(MSG_ERROR, frame_id, "Not streaming"); From d4e1d85bb499cff049ed1688ea96933751165854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 15:52:55 +0100 Subject: [PATCH 13/20] =?UTF-8?q?some=20small=20adc=20fixes=20(impreoving?= =?UTF-8?q?=1B[D=1B[D=1B=20trigger)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- units/adc/_adc_core.c | 46 +++++++++++------- units/adc/_adc_internal.h | 3 +- units/adc/unit_adc.c | 98 ++++++++++++++++++++++++++------------- 3 files changed, 97 insertions(+), 50 deletions(-) diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index 53c0905..a854e2f 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -31,11 +31,11 @@ void UADC_DMA_Handler(void *arg) const bool te = LL_DMA_IsActiveFlag_TE(isrsnapshot, priv->dma_chnum); // check what mode we're in - const bool m_trig = priv->opmode == ADC_OPMODE_TRIGD; + const bool m_trigd = priv->opmode == ADC_OPMODE_TRIGD; const bool m_stream = priv->opmode == ADC_OPMODE_STREAM; const bool m_fixcpt = priv->opmode == ADC_OPMODE_FIXCAPT; - if (m_trig || m_stream || m_fixcpt) { + if (m_trigd || m_stream || m_fixcpt) { if (ht || tc) { const uint16_t start = priv->stream_startpos; uint16_t end; @@ -57,7 +57,7 @@ void UADC_DMA_Handler(void *arg) if (start != end) { uint32_t sgcount = (end - start) / priv->nb_channels; - if (m_trig || m_fixcpt) { + if (m_trigd || m_fixcpt) { sgcount = MIN(priv->trig_stream_remain, sgcount); priv->trig_stream_remain -= sgcount; } @@ -67,14 +67,16 @@ void UADC_DMA_Handler(void *arg) // TODO send the data together with remaining count (used to detect end of transmission) - if (m_trig || m_fixcpt) { + if (m_trigd || m_fixcpt) { // Trig'd or Block capture - check for the max count condition if (priv->trig_stream_remain == 0) { dbg("End of capture"); UADC_ReportEndOfStream(unit); - UADC_SwitchMode(unit, ADC_OPMODE_IDLE); - if (priv->auto_rearm && m_trig) { - UADC_SwitchMode(unit, ADC_OPMODE_ARMED); - } + + // If auto-arm enabled, we need to re-arm again. + // However, EOS irq is disabled during the capture. + // We have to wait for the next EOS interrupt to occur. + // TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled. + UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); } } } else { @@ -143,8 +145,8 @@ void UADC_ADC_EOS_Handler(void *arg) priv->last_samples[i] = val; - if (priv->opmode == ADC_OPMODE_ARMED) { - if (i == priv->trigger_source) { + if (i == priv->trigger_source) { + if (priv->opmode == ADC_OPMODE_ARMED) { dbg("Trig line level %d", (int)val); bool trigd = false; @@ -165,9 +167,19 @@ void UADC_ADC_EOS_Handler(void *arg) if (trigd) { UADC_HandleTrigger(unit, edge_type, timestamp); } - - priv->trig_prev_level = val; } + else if (priv->opmode == ADC_OPMODE_REARM_PENDING) { + if (!priv->auto_rearm) { + // It looks like the flag was cleared by DISARM before we got a new sample. + // Let's just switch to IDLE + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + } else { + // Re-arming for a new trigger + UADC_SwitchMode(unit, ADC_OPMODE_ARMED); + } + } + + priv->trig_prev_level = val; } } } @@ -290,8 +302,8 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); } - else if (new_mode == ADC_OPMODE_IDLE) { - dbg("ADC switch -> IDLE"); + else if (new_mode == ADC_OPMODE_IDLE || new_mode == ADC_OPMODE_REARM_PENDING) { + dbg("ADC switch -> IDLE or IDLE/REARM_PENDING"); // IDLE and ARMED are identical with the exception that the trigger condition is not checked // ARMED can be only entered from IDLE, thus we do the init only here. @@ -314,8 +326,10 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) } else if (new_mode == ADC_OPMODE_ARMED) { dbg("ADC switch -> ARMED"); - assert_param(priv->opmode == ADC_OPMODE_IDLE); - // there's nothing else to do here + assert_param(priv->opmode == ADC_OPMODE_IDLE || priv->opmode == ADC_OPMODE_REARM_PENDING); + + // avoid firing immediately by the value jumping across the scale + priv->trig_prev_level = priv->last_samples[priv->trigger_source]; } else if (new_mode == ADC_OPMODE_TRIGD || new_mode == ADC_OPMODE_STREAM || diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index bda6c61..a00edc3 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -13,7 +13,8 @@ enum uadc_opmode { ADC_OPMODE_UNINIT, //!< Not yet switched to any mode - ADC_OPMODE_IDLE, //!< Idle, each sample overwrites the previous. Allows immediate value readout and averaging. + ADC_OPMODE_IDLE, //!< Idle. Allows immediate value readout and averaging. + ADC_OPMODE_REARM_PENDING, //!< Idle, waiting for the next sample to re-arm (auto trigger). ADC_OPMODE_ARMED, //!< Armed for a trigger. Direct access and averaging are disabled. ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data. ADC_OPMODE_FIXCAPT,//!< Capture of fixed length without a trigger diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index c2d6744..d44d933 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -109,16 +109,20 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P */ case CMD_SETUP_TRIGGER: dbg("> Setup trigger"); - if (priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) return E_BUSY; + if (priv->opmode != ADC_OPMODE_IDLE && + priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_REARM_PENDING) { + return E_BUSY; + } { - uint8_t source = pp_u8(pp); - uint16_t level = pp_u16(pp); - uint8_t edge = pp_u8(pp); // TODO test falling edge and dual edge - uint16_t pretrig = pp_u16(pp); // TODO test pre-trigger ... - uint32_t count = pp_u32(pp); - uint16_t holdoff = pp_u16(pp); // TODO test hold-off ... - bool auto_rearm = pp_bool(pp); + const uint8_t source = pp_u8(pp); + const uint16_t level = pp_u16(pp); + const uint8_t edge = pp_u8(pp); + const uint16_t pretrig = pp_u16(pp); // TODO test pre-trigger ... + const uint32_t count = pp_u32(pp); + const uint16_t holdoff = pp_u16(pp); + const bool auto_rearm = pp_bool(pp); if (source > 17) { com_respond_str(MSG_ERROR, frame_id, "Invalid trig source"); @@ -141,7 +145,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P } // XXX the max size may be too much - uint16_t max_pretrig = (priv->dma_buffer_itemcount / priv->nb_channels); + const uint16_t max_pretrig = (priv->dma_buffer_itemcount / priv->nb_channels); if (pretrig > max_pretrig) { com_respond_snprintf(frame_id, MSG_ERROR, "Pretrig too large (max %d)", (int) max_pretrig); @@ -164,17 +168,28 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P */ case CMD_ARM: dbg("> Arm"); - if(priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + uint8_t sticky = pp_u8(pp); + + if(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_REARM_PENDING) { + // We are armed or will re-arm promptly, act like the call succeeded + // The auto flag is set regardless + } else { + if (priv->opmode != ADC_OPMODE_IDLE) { + return E_BUSY; // capture in progress + } - if (priv->trig_len == 0) { - com_respond_str(MSG_ERROR, frame_id, "Trigger not configured."); - return E_FAILURE; + if (priv->trig_len == 0) { + com_respond_str(MSG_ERROR, frame_id, "Trigger not configured."); + return E_FAILURE; + } + + UADC_SwitchMode(unit, ADC_OPMODE_ARMED); } - // avoid firing immediately by the value jumping across the scale - priv->trig_prev_level = priv->last_samples[priv->trigger_source]; + if (sticky != 255) { + priv->auto_rearm = (bool)sticky; + } - UADC_SwitchMode(unit, ADC_OPMODE_ARMED); return E_SUCCESS; /** @@ -182,13 +197,21 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Switches to idle. */ case CMD_DISARM: - // TODO test dbg("> Disarm"); + + priv->auto_rearm = false; + if(priv->opmode == ADC_OPMODE_IDLE) { return E_SUCCESS; // already idle, success - no work to do } + // capture in progress - if(priv->opmode != ADC_OPMODE_ARMED) return E_BUSY; + if (priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_REARM_PENDING) { + // Capture in progress, we already cleared auto rearm, so we're done for now + // auto_rearm is checked in the EOS isr and if cleared, does not re-arm. + return E_SUCCESS; + } UADC_SwitchMode(unit, ADC_OPMODE_IDLE); return E_SUCCESS; @@ -197,15 +220,18 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Abort any ongoing capture and dis-arm. */ case CMD_ABORT:; - // TODO test dbg("> Abort capture"); - enum uadc_opmode old_opmode = priv->opmode; - UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + { + enum uadc_opmode old_opmode = priv->opmode; + + priv->auto_rearm = false; + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); - if (old_opmode == ADC_OPMODE_FIXCAPT || - old_opmode == ADC_OPMODE_STREAM || - old_opmode == ADC_OPMODE_TRIGD) { - UADC_ReportEndOfStream(unit); + if (old_opmode == ADC_OPMODE_FIXCAPT || + old_opmode == ADC_OPMODE_STREAM || + old_opmode == ADC_OPMODE_TRIGD) { + UADC_ReportEndOfStream(unit); + } } return E_SUCCESS; @@ -214,13 +240,17 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * The reported edge will be 0b11, here meaning "manual trigger" */ case CMD_FORCE_TRIGGER: - // TODO test dbg("> Force trigger"); - if(priv->opmode == ADC_OPMODE_IDLE) { - com_respond_str(MSG_ERROR, frame_id, "Not armed"); + // This is similar to block capture, but includes the pre-trig buffer and has fixed size based on trigger config + // FORCE is useful for checking if the trigger is set up correctly + if (priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_IDLE && + priv->opmode != ADC_OPMODE_REARM_PENDING) return E_BUSY; + + if (priv->trig_len == 0) { + com_respond_str(MSG_ERROR, frame_id, "Trigger not configured."); return E_FAILURE; } - if(priv->opmode != ADC_OPMODE_ARMED) return E_BUSY; UADC_HandleTrigger(unit, 0b11, PTIM_GetMicrotime()); return E_SUCCESS; @@ -234,7 +264,8 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P case CMD_BLOCK_CAPTURE: // TODO test dbg("> Block cpt"); - if(priv->opmode != ADC_OPMODE_ARMED && + if (priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_REARM_PENDING && priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; uint32_t count = pp_u32(pp); @@ -249,8 +280,9 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P case CMD_STREAM_START: // TODO test dbg("> Stream ON"); - if(priv->opmode != ADC_OPMODE_ARMED && - priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + if (priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_REARM_PENDING && + priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; UADC_StartStream(unit, frame_id); return E_SUCCESS; @@ -261,7 +293,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P case CMD_STREAM_STOP: // TODO test dbg("> Stream OFF"); - if(priv->opmode != ADC_OPMODE_STREAM) { + if (priv->opmode != ADC_OPMODE_STREAM) { com_respond_str(MSG_ERROR, frame_id, "Not streaming"); return E_FAILURE; } From 72b331bce5f137a03eb10390e9a3861ba33b2ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 15:58:47 +0100 Subject: [PATCH 14/20] renamed enum constant to BLCAP --- units/adc/_adc_core.c | 6 +++--- units/adc/_adc_internal.h | 2 +- units/adc/unit_adc.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index a854e2f..bf7ef98 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -33,7 +33,7 @@ void UADC_DMA_Handler(void *arg) // check what mode we're in const bool m_trigd = priv->opmode == ADC_OPMODE_TRIGD; const bool m_stream = priv->opmode == ADC_OPMODE_STREAM; - const bool m_fixcpt = priv->opmode == ADC_OPMODE_FIXCAPT; + const bool m_fixcpt = priv->opmode == ADC_OPMODE_BLCAP; if (m_trigd || m_stream || m_fixcpt) { if (ht || tc) { @@ -222,7 +222,7 @@ void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id) priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->trig_stream_remain = len; - UADC_SwitchMode(unit, ADC_OPMODE_FIXCAPT); + UADC_SwitchMode(unit, ADC_OPMODE_BLCAP); } /** Start stream */ @@ -333,7 +333,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) } else if (new_mode == ADC_OPMODE_TRIGD || new_mode == ADC_OPMODE_STREAM || - new_mode == ADC_OPMODE_FIXCAPT) { + new_mode == ADC_OPMODE_BLCAP) { dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE); diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index a00edc3..b212739 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -17,7 +17,7 @@ enum uadc_opmode { ADC_OPMODE_REARM_PENDING, //!< Idle, waiting for the next sample to re-arm (auto trigger). ADC_OPMODE_ARMED, //!< Armed for a trigger. Direct access and averaging are disabled. ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data. - ADC_OPMODE_FIXCAPT,//!< Capture of fixed length without a trigger + ADC_OPMODE_BLCAP,//!< Capture of fixed length without a trigger ADC_OPMODE_STREAM, //!< Unlimited capture }; diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index d44d933..51dee51 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -227,7 +227,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P priv->auto_rearm = false; UADC_SwitchMode(unit, ADC_OPMODE_IDLE); - if (old_opmode == ADC_OPMODE_FIXCAPT || + if (old_opmode == ADC_OPMODE_BLCAP || old_opmode == ADC_OPMODE_STREAM || old_opmode == ADC_OPMODE_TRIGD) { UADC_ReportEndOfStream(unit); From 835423099b3b739f6dd49ed37baa6039254be642 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 17:17:56 +0100 Subject: [PATCH 15/20] fixeng stuff up , block capture verified and implemented in python --- comm/msg_responses.c | 30 ++++++------- units/adc/_adc_core.c | 95 +++++++++++++++++++++++++++++++-------- units/adc/_adc_internal.h | 10 ++++- units/adc/unit_adc.c | 2 - 4 files changed, 99 insertions(+), 38 deletions(-) diff --git a/comm/msg_responses.c b/comm/msg_responses.c index 44df67d..d1887dd 100644 --- a/comm/msg_responses.c +++ b/comm/msg_responses.c @@ -21,14 +21,13 @@ void com_respond_snprintf(TF_ID frame_id, TF_TYPE type, const char *format, ...) void com_respond_buf(TF_ID frame_id, TF_TYPE type, const uint8_t *buf, uint32_t len) { - TF_Msg msg; - TF_ClearMsg(&msg); - { - msg.type = type; - msg.frame_id = frame_id; - msg.data = buf; - msg.len = (TF_LEN) len; - } + TF_Msg msg = { + .type = type, + .frame_id = frame_id, + .data = buf, + .len = (TF_LEN) len, + }; + TF_Respond(comm, &msg); } @@ -57,14 +56,13 @@ void com_send_pb(TF_TYPE type, PayloadBuilder *pb) void com_send_buf(TF_TYPE type, const uint8_t *buf, uint32_t len) { - TF_Msg msg; - TF_ClearMsg(&msg); - { - msg.type = MSG_UNIT_REPORT; - msg.data = buf; - msg.len = (TF_LEN) len; - msg.type = type; - } + TF_Msg msg = { + .type = MSG_UNIT_REPORT, + .data = buf, + .len = (TF_LEN) len, + .type = type, + }; + TF_Send(comm, &msg); // no listener } diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index bf7ef98..c092f2e 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -10,9 +10,61 @@ #define DMA_POS(priv) ((priv)->dma_buffer_itemcount - (priv)->DMA_CHx->CNDTR) +static void UADC_JobSendBlockChunk(Job *job) +{ + Unit *unit = job->unit; + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + uint32_t start = job->data1; + uint32_t count = job->data2; + bool close = (bool) job->data3; + + dbg("Send indices [%d -> %d)", (int)start, (int)(start+count)); + + TF_TYPE type = close ? EVT_CAPT_DONE : EVT_CAPT_MORE; + + TF_Msg msg = { + .frame_id = priv->stream_frame_id, + .len = (TF_LEN) (1 + count*sizeof(uint16_t)), + .type = type, + }; + + TF_Respond_Multipart(comm, &msg); + TF_Multipart_Payload(comm, &priv->stream_serial, 1); + TF_Multipart_Payload(comm, (uint8_t *) (priv->dma_buffer + start), count * sizeof(uint16_t)); + TF_Multipart_Close(comm); + + priv->stream_serial++; +} + +static void UADC_JobSendEndOfStreamMsg(Job *job) +{ + Unit *unit = job->unit; + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + TF_Msg msg = { + .type = EVT_CAPT_DONE, + .frame_id = (TF_ID) job->data1 + }; + TF_Respond(comm, &msg); +} + void UADC_ReportEndOfStream(Unit *unit) { - dbg("~~End Of Stream msg~~"); + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + Job j = { + .unit = unit, + .data1 = priv->stream_frame_id, + .cb = UADC_JobSendEndOfStreamMsg + }; + scheduleJob(&j); } void UADC_DMA_Handler(void *arg) @@ -62,22 +114,24 @@ void UADC_DMA_Handler(void *arg) priv->trig_stream_remain -= sgcount; } - dbg("Would send %d groups (u16 offset %d -> %d)", (int) sgcount, - (int) start, (int) (start + sgcount * priv->nb_channels)); - - // TODO send the data together with remaining count (used to detect end of transmission) - - if (m_trigd || m_fixcpt) { // Trig'd or Block capture - check for the max count condition - if (priv->trig_stream_remain == 0) { - dbg("End of capture"); - UADC_ReportEndOfStream(unit); - - // If auto-arm enabled, we need to re-arm again. - // However, EOS irq is disabled during the capture. - // We have to wait for the next EOS interrupt to occur. - // TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled. - UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); - } + bool close = !m_stream && priv->trig_stream_remain == 0; + + Job j = { + .unit = unit, + .data1 = start, + .data2 = sgcount * priv->nb_channels, + .data3 = (uint32_t) close, + .cb = UADC_JobSendBlockChunk + }; + scheduleJob(&j); + + if (close) { + dbg("End of capture"); + // If auto-arm enabled, we need to re-arm again. + // However, EOS irq is disabled during the capture. + // We have to wait for the next EOS interrupt to occur. + // TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled. + UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); } } else { dbg("start==end, skip this irq"); @@ -203,10 +257,11 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) unit->_tick_cnt = 0; } - // TODO Send pre-trigger - priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->trig_stream_remain = priv->trig_len; + priv->stream_serial = 0; + + // TODO Send pre-trigger dbg("Trigger condition hit, edge=%d, startpos %d", edge_type, (int)priv->stream_startpos); @@ -222,6 +277,7 @@ void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id) priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->trig_stream_remain = len; + priv->stream_serial = 0; UADC_SwitchMode(unit, ADC_OPMODE_BLCAP); } @@ -234,6 +290,7 @@ void UADC_StartStream(Unit *unit, TF_ID frame_id) priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); + priv->stream_serial = 0; dbg("Start streaming."); UADC_SwitchMode(unit, ADC_OPMODE_STREAM); } diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index b212739..c850536 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -17,10 +17,17 @@ enum uadc_opmode { ADC_OPMODE_REARM_PENDING, //!< Idle, waiting for the next sample to re-arm (auto trigger). ADC_OPMODE_ARMED, //!< Armed for a trigger. Direct access and averaging are disabled. ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data. - ADC_OPMODE_BLCAP,//!< Capture of fixed length without a trigger + ADC_OPMODE_BLCAP, //!< Capture of fixed length without a trigger ADC_OPMODE_STREAM, //!< Unlimited capture }; +enum uadc_event { + EVT_CAPT_START = 50, //!< Capture start (used in event in the first frame when trigger is detected) + EVT_CAPT_MORE = 51, //!< Capture data payload (used as TYPE for all capture types) + EVT_CAPT_DONE = 52, //!< End of trig'd or block capture payload (last frame with data), + //!< or a farewell message after closing stream using abort(), in this case without data. +}; + /** Private data structure */ struct priv { // settings @@ -62,6 +69,7 @@ struct priv { bool auto_rearm; //!< Flag that the trigger should be re-armed after the stream finishes uint16_t trig_holdoff; //!< Trigger hold-off time, set when configuring the trigger TF_ID stream_frame_id; //!< Session ID for multi-part stream (response or report) + uint8_t stream_serial; }; /** Allocate data structure and set defaults */ diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index 51dee51..a01ff02 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -70,7 +70,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P for (uint8_t i = 0; i < 18; i++) { if (priv->extended_channels_mask & (1 << i)) { - pb_u8(&pb, i); pb_u16(&pb, priv->last_samples[i]); } } @@ -88,7 +87,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P for (uint8_t i = 0; i < 18; i++) { if (priv->extended_channels_mask & (1 << i)) { - pb_u8(&pb, i); pb_float(&pb, priv->averaging_bins[i]); } } From ddfbbeeb955fc0513bb08eec1445f05dbe98f643 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 20:57:02 +0100 Subject: [PATCH 16/20] more parts of adc fixed --- comm/event_reports.c | 24 ++++++--- comm/event_reports.h | 2 + comm/msg_responses.c | 3 +- tasks/task_main.c | 2 +- units/adc/_adc_core.c | 116 ++++++++++++++++++++++++++++++++++-------- 5 files changed, 118 insertions(+), 29 deletions(-) diff --git a/comm/event_reports.c b/comm/event_reports.c index 15c423a..45136bf 100644 --- a/comm/event_reports.c +++ b/comm/event_reports.c @@ -12,21 +12,26 @@ bool EventReport_Start(EventReport *report) { assert_param(report->timestamp != 0); - TF_Msg msg; - TF_ClearMsg(&msg); - msg.len = (TF_LEN) (report->length + 1 /*callsign*/ + 1 /*type*/ + 8 /*checksum*/); - msg.type = MSG_UNIT_REPORT; + const uint8_t len_overhead = 1 /*callsign*/ + 1 /*type*/ + 8 /*timestamp u64*/; + + TF_Msg msg = { + .len = (TF_LEN) (report->length + len_overhead), + .type = MSG_UNIT_REPORT, + }; + if (!TF_Send_Multipart(comm, &msg)) { dbg("!! Err sending event"); return false; } - PayloadBuilder pb = pb_start(evt_buf, 10, NULL); + report->sent_msg_id = msg.frame_id; + + PayloadBuilder pb = pb_start(evt_buf, len_overhead, NULL); pb_u8(&pb, report->unit->callsign); pb_u8(&pb, report->type); pb_u64(&pb, report->timestamp); assert_param(pb.ok); - TF_Multipart_Payload(comm, evt_buf, 10); + TF_Multipart_Payload(comm, evt_buf, len_overhead); return true; } @@ -36,6 +41,13 @@ void EventReport_Data(const uint8_t *buff, uint16_t len) TF_Multipart_Payload(comm, buff, len); } +void EventReport_PB(PayloadBuilder *pb) +{ + uint32_t len; + uint8_t *buf = pb_close(pb, &len); + TF_Multipart_Payload(comm, buf, len); +} + void EventReport_End(void) { TF_Multipart_Close(comm); diff --git a/comm/event_reports.h b/comm/event_reports.h index 56223ca..05f57c1 100644 --- a/comm/event_reports.h +++ b/comm/event_reports.h @@ -21,12 +21,14 @@ typedef struct event_report_ { uint64_t timestamp; //!< Microsecond timestamp of the event, captured as close as possible to the IRQ uint16_t length; //!< Payload length uint8_t *data; //!< Data if using the EventReport_Send() function, otherwise NULL and data is sent using EventReport_Data() + TF_ID sent_msg_id; } EventReport; bool EventReport_Send(EventReport *report); bool EventReport_Start(EventReport *report); void EventReport_Data(const uint8_t *buff, uint16_t len); +void EventReport_PB(PayloadBuilder *pb); void EventReport_End(void); #endif //GEX_F072_EVENT_REPORTS_H diff --git a/comm/msg_responses.c b/comm/msg_responses.c index d1887dd..312aa9b 100644 --- a/comm/msg_responses.c +++ b/comm/msg_responses.c @@ -57,10 +57,9 @@ void com_send_pb(TF_TYPE type, PayloadBuilder *pb) void com_send_buf(TF_TYPE type, const uint8_t *buf, uint32_t len) { TF_Msg msg = { - .type = MSG_UNIT_REPORT, + .type = type, .data = buf, .len = (TF_LEN) len, - .type = type, }; TF_Send(comm, &msg); // no listener diff --git a/tasks/task_main.c b/tasks/task_main.c index b7e8600..51d5f75 100644 --- a/tasks/task_main.c +++ b/tasks/task_main.c @@ -56,7 +56,7 @@ void TaskMain(void const * argument) // Periodically check stacks for overrun stackmon_check_canaries(); // Periodically dump all stacks - for checking levels before critical (to reduce size if not needed) - if ((cnt%75)==0) stackmon_dump(); + if ((cnt%150)==0) stackmon_dump(); continue; } diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index c092f2e..586b55a 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -39,6 +39,75 @@ static void UADC_JobSendBlockChunk(Job *job) priv->stream_serial++; } +static void UADC_JobSendTriggerCaptureHeader(Job *job) +{ + Unit *unit = job->unit; + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + EventReport er = { + .unit = unit, + .type = EVT_CAPT_START, + .timestamp = job->timestamp, + .length = (priv->pretrig_len+1)*priv->nb_channels*sizeof(uint16_t) + 2 /*pretrig len*/ + 1 /*edge*/ + 1 /* seq */ + }; + + uint16_t index_trigd = (uint16_t) job->data1; + uint8_t edge = (uint8_t) job->data2; + + EventReport_Start(&er); + priv->stream_frame_id = er.sent_msg_id; + dbg("Sending TRIG HEADER with id %d (idx %d)", (int)er.sent_msg_id, (int)index_trigd); + { + // preamble + uint8_t buf[4]; + PayloadBuilder pb = pb_start(buf, 4, NULL); + pb_u16(&pb, priv->pretrig_len); + pb_u8(&pb, edge); + pb_u8(&pb, priv->stream_serial++); // This is the serial counter for the first chunk + // (containing the pre-trigger, or empty if no pretrig configured) + EventReport_PB(&pb); + + if (priv->pretrig_len > 0) { + // pretrig + uint16_t pretrig_remain = (uint16_t) ((priv->pretrig_len + 1) * priv->nb_channels); // +1 because we want pretrig 0 to exactly start with the triggering sample + + assert_param(index_trigd <= priv->dma_buffer_itemcount); + + // this is one past the last entry of the triggering capture group + if (pretrig_remain > index_trigd) { + // used items in the wrap-around part of the buffer + uint16_t items_from_end = pretrig_remain - index_trigd; + assert_param(priv->dma_buffer_itemcount - items_from_end >= index_trigd); + + dbg("Pretrig wraparound part: start %d, len %d", + (int) (priv->dma_buffer_itemcount - items_from_end), + (int) items_from_end + ); + + EventReport_Data( + (uint8_t *) &priv->dma_buffer[priv->dma_buffer_itemcount - + items_from_end], + items_from_end * sizeof(uint16_t)); + + assert_param(items_from_end <= pretrig_remain); + pretrig_remain -= items_from_end; + } + + dbg("Pretrig front part: start %d, len %d", + (int) (index_trigd - pretrig_remain), + (int) pretrig_remain + ); + + assert_param(pretrig_remain <= index_trigd); + EventReport_Data((uint8_t *) &priv->dma_buffer[index_trigd - pretrig_remain], + pretrig_remain * sizeof(uint16_t)); + } + } + EventReport_End(); +} + static void UADC_JobSendEndOfStreamMsg(Job *job) { Unit *unit = job->unit; @@ -93,17 +162,17 @@ void UADC_DMA_Handler(void *arg) uint16_t end; if (ht) { - dbg("HT"); +// dbg("HT"); end = (uint16_t) (priv->dma_buffer_itemcount / 2); LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); } else { - dbg("TC"); +// dbg("TC"); end = (uint16_t) priv->dma_buffer_itemcount; LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); } - dbg("start %d, end %d", (int)start, (int)end); +// dbg("start %d, end %d", (int)start, (int)end); assert_param(start <= end); if (start != end) { @@ -126,7 +195,7 @@ void UADC_DMA_Handler(void *arg) scheduleJob(&j); if (close) { - dbg("End of capture"); +// dbg("End of capture"); // If auto-arm enabled, we need to re-arm again. // However, EOS irq is disabled during the capture. // We have to wait for the next EOS interrupt to occur. @@ -134,7 +203,7 @@ void UADC_DMA_Handler(void *arg) UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); } } else { - dbg("start==end, skip this irq"); +// dbg("start==end, skip this irq"); } if (tc) { @@ -201,18 +270,18 @@ void UADC_ADC_EOS_Handler(void *arg) if (i == priv->trigger_source) { if (priv->opmode == ADC_OPMODE_ARMED) { - dbg("Trig line level %d", (int)val); +// dbg("Trig line level %d", (int)val); bool trigd = false; uint8_t edge_type = 0; if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) { - dbg("******** Rising edge"); +// dbg("******** Rising edge"); // Rising edge trigd = (bool) (priv->trig_edge & 0b01); edge_type = 1; } else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) { - dbg("******** Falling edge"); +// dbg("******** Falling edge"); // Falling edge trigd = (bool) (priv->trig_edge & 0b10); edge_type = 2; @@ -246,7 +315,7 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) assert_param(priv); if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) { - dbg("Trig discarded due to holdoff."); +// dbg("Trig discarded due to holdoff."); return; } @@ -261,9 +330,16 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) priv->trig_stream_remain = priv->trig_len; priv->stream_serial = 0; - // TODO Send pre-trigger +// dbg("Trigger condition hit, edge=%d, startpos %d", edge_type, (int)priv->stream_startpos); - dbg("Trigger condition hit, edge=%d, startpos %d", edge_type, (int)priv->stream_startpos); + Job j = { + .unit = unit, + .timestamp = timestamp, + .data1 = priv->stream_startpos, + .data2 = edge_type, + .cb = UADC_JobSendTriggerCaptureHeader + }; + scheduleJob(&j); UADC_SwitchMode(unit, ADC_OPMODE_TRIGD); } @@ -291,7 +367,7 @@ void UADC_StartStream(Unit *unit, TF_ID frame_id) priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->stream_serial = 0; - dbg("Start streaming."); +// dbg("Start streaming."); UADC_SwitchMode(unit, ADC_OPMODE_STREAM); } @@ -302,7 +378,7 @@ void UADC_StopStream(Unit *unit) struct priv *priv = unit->data; assert_param(priv); - dbg("Stop stream."); +// dbg("Stop stream."); UADC_ReportEndOfStream(unit); UADC_SwitchMode(unit, ADC_OPMODE_IDLE); } @@ -336,7 +412,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) assert_param((priv->opmode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE)); if (new_mode == ADC_OPMODE_UNINIT) { - dbg("ADC switch -> UNINIT"); +// dbg("ADC switch -> UNINIT"); // Stop the DMA, timer and disable ADC - this is called before tearing down the unit LL_TIM_DisableCounter(priv->TIMx); @@ -344,23 +420,23 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) if (LL_ADC_IsEnabled(priv->ADCx)) { // Cancel ongoing conversion if (LL_ADC_REG_IsConversionOngoing(priv->ADCx)) { - dbg("Stopping ADC conv"); +// dbg("Stopping ADC conv"); LL_ADC_REG_StopConversion(priv->ADCx); hw_wait_while(LL_ADC_REG_IsStopConversionOngoing(priv->ADCx), 100); } LL_ADC_Disable(priv->ADCx); - dbg("Disabling ADC"); +// dbg("Disabling ADC"); hw_wait_while(LL_ADC_IsDisableOngoing(priv->ADCx), 100); } - dbg("Disabling DMA"); +// dbg("Disabling DMA"); LL_DMA_DisableChannel(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); } else if (new_mode == ADC_OPMODE_IDLE || new_mode == ADC_OPMODE_REARM_PENDING) { - dbg("ADC switch -> IDLE or IDLE/REARM_PENDING"); +// dbg("ADC switch -> IDLE or IDLE/REARM_PENDING"); // IDLE and ARMED are identical with the exception that the trigger condition is not checked // ARMED can be only entered from IDLE, thus we do the init only here. @@ -382,7 +458,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) } } else if (new_mode == ADC_OPMODE_ARMED) { - dbg("ADC switch -> ARMED"); +// dbg("ADC switch -> ARMED"); assert_param(priv->opmode == ADC_OPMODE_IDLE || priv->opmode == ADC_OPMODE_REARM_PENDING); // avoid firing immediately by the value jumping across the scale @@ -392,7 +468,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) new_mode == ADC_OPMODE_STREAM || new_mode == ADC_OPMODE_BLCAP) { - dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); +// dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE); // during the capture, we disallow direct readout and averaging to reduce overhead From 3584830dc5532e1e654f0761c15c1f7f70c9479c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 21:18:06 +0100 Subject: [PATCH 17/20] failing --- platform/hw_utils.c | 5 +++- units/adc/_adc_init.c | 53 +++++++++++++++++++++++++++++---------- units/adc/_adc_internal.h | 4 +++ units/adc/unit_adc.c | 26 ++++++++++++++++--- 4 files changed, 70 insertions(+), 18 deletions(-) diff --git a/platform/hw_utils.c b/platform/hw_utils.c index 9cfb4f1..c082ca4 100644 --- a/platform/hw_utils.c +++ b/platform/hw_utils.c @@ -371,7 +371,10 @@ bool solve_timer(uint32_t base_freq, uint32_t required_freq, bool is16bit, *presc = (uint16_t) wPresc; if (wPresc * wCount == 0) return false; - *real_freq = (base_freq / (wPresc * wCount)); + + if (real_freq != NULL) { + *real_freq = (base_freq / (wPresc * wCount)); + } return true; } diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index e3e129b..1de3f4f 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -28,6 +28,30 @@ error_t UADC_preInit(Unit *unit) return E_SUCCESS; } + +/** Configure frequency */ +error_t UADC_SetSampleRate(Unit *unit, uint32_t hertz) +{ + struct priv *priv = unit->data; + + uint16_t presc; + uint32_t count; + if (!solve_timer(PLAT_APB1_HZ, hertz, true, &presc, &count, + &priv->real_frequency)) { + dbg("Failed to resolve timer params."); + return E_BAD_VALUE; + } + dbg("Frequency error %d ppm, presc %d, count %d", + (int) lrintf(1000000.0f * + ((priv->real_frequency - hertz) / (float) hertz)), + (int) presc, (int) count); + + LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1)); + LL_TIM_SetAutoReload(priv->TIMx, count - 1); + + return E_SUCCESS; +} + /** Finalize unit set-up */ error_t UADC_init(Unit *unit) { @@ -103,19 +127,20 @@ error_t UADC_init(Unit *unit) // ------------------- CONFIGURE THE TIMER -------------------------- dbg("Setting up TIMER"); { - // Find suitable timer values - uint16_t presc; - uint32_t count; - float real_freq; - if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, &real_freq)) { - dbg("Failed to resolve timer params."); - return E_BAD_VALUE; - } - dbg("Frequency error %d ppm, presc %d, count %d", - (int) lrintf(1000000.0f * ((real_freq - priv->frequency) / (float)priv->frequency)), (int) presc, (int) count); - - LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1)); - LL_TIM_SetAutoReload(priv->TIMx, count - 1); + TRY(UADC_SetSampleRate(unit, priv->frequency)); +// // Find suitable timer values +// uint16_t presc; +// uint32_t count; +// float real_freq; +// if (!solve_timer(PLAT_APB1_HZ, priv->frequency, true, &presc, &count, &real_freq)) { +// dbg("Failed to resolve timer params."); +// return E_BAD_VALUE; +// } +// dbg("Frequency error %d ppm, presc %d, count %d", +// (int) lrintf(1000000.0f * ((real_freq - priv->frequency) / (float)priv->frequency)), (int) presc, (int) count); +// +// LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1)); +// LL_TIM_SetAutoReload(priv->TIMx, count - 1); LL_TIM_EnableARRPreload(priv->TIMx); LL_TIM_EnableUpdateEvent(priv->TIMx); LL_TIM_SetTriggerOutput(priv->TIMx, LL_TIM_TRGO_UPDATE); @@ -208,8 +233,10 @@ error_t UADC_init(Unit *unit) irqd_attach(priv->DMA_CHx, UADC_DMA_Handler, unit); irqd_attach(priv->ADCx, UADC_ADC_EOS_Handler, unit); + dbg("irqs attached"); UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + dbg("ADC done"); return E_SUCCESS; } diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index c850536..b46e3a9 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -40,6 +40,7 @@ struct priv { uint16_t averaging_factor; //!< Exponential averaging factor 0-1000 // internal state + float real_frequency; uint32_t extended_channels_mask; //!< channels bitfield including tsense and vref float avg_factor_as_float; ADC_TypeDef *ADCx; //!< The ADC peripheral used @@ -126,4 +127,7 @@ void UADC_StartStream(Unit *unit, TF_ID frame_id); /** End stream */ void UADC_StopStream(Unit *unit); +/** Configure frequency */ +error_t UADC_SetSampleRate(Unit *unit, uint32_t hertz); + #endif //GEX_F072_ADC_INTERNAL_H diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index a01ff02..f6ddb7c 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -15,6 +15,7 @@ enum TplCmd_ { CMD_READ_SMOOTHED = 1, CMD_GET_ENABLED_CHANNELS = 10, + CMD_GET_SAMPLE_RATE = 11, CMD_SETUP_TRIGGER = 20, CMD_ARM = 21, @@ -25,6 +26,7 @@ enum TplCmd_ { CMD_STREAM_START = 26, CMD_STREAM_STOP = 27, CMD_SET_SMOOTHING_FACTOR = 28, + CMD_SET_SAMPLE_RATE = 29, }; /** Handle a request message */ @@ -33,6 +35,8 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P struct priv *priv = unit->data; PayloadBuilder pb = pb_start(unit_tmp512, UNIT_TMP_LEN, NULL); + // TODO toggling individual channels - would require DMA re-init and various changes in the usage of the struct + switch (command) { /** * Get enabled channels. @@ -47,6 +51,23 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P com_respond_pb(frame_id, MSG_SUCCESS, &pb); return E_SUCCESS; + case CMD_SET_SAMPLE_RATE: + { + uint32_t freq = pp_u32(pp); + if (freq == 0) return E_BAD_VALUE; + + TRY(UADC_SetSampleRate(unit, freq)); + } + // Pass through - send back the obtained sample rate + /** + * Read the real used frequency, expressed as float. + * May differ from the configured or requested value due to prescaller limitations. + */ + case CMD_GET_SAMPLE_RATE: + pb_float(&pb, priv->real_frequency); + com_respond_pb(frame_id, MSG_SUCCESS, &pb); + return E_SUCCESS; + /** * Set smoothing factor 0-1000. * pld: u16:factor @@ -117,7 +138,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P const uint8_t source = pp_u8(pp); const uint16_t level = pp_u16(pp); const uint8_t edge = pp_u8(pp); - const uint16_t pretrig = pp_u16(pp); // TODO test pre-trigger ... + const uint16_t pretrig = pp_u16(pp); const uint32_t count = pp_u32(pp); const uint16_t holdoff = pp_u16(pp); const bool auto_rearm = pp_bool(pp); @@ -260,7 +281,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * u32 - sample count (for each channel) */ case CMD_BLOCK_CAPTURE: - // TODO test dbg("> Block cpt"); if (priv->opmode != ADC_OPMODE_ARMED && priv->opmode != ADC_OPMODE_REARM_PENDING && @@ -276,7 +296,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * The stream can be terminated by the stop command. */ case CMD_STREAM_START: - // TODO test dbg("> Stream ON"); if (priv->opmode != ADC_OPMODE_ARMED && priv->opmode != ADC_OPMODE_REARM_PENDING && @@ -289,7 +308,6 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * Stop a stream. */ case CMD_STREAM_STOP: - // TODO test dbg("> Stream OFF"); if (priv->opmode != ADC_OPMODE_STREAM) { com_respond_str(MSG_ERROR, frame_id, "Not streaming"); From dcddca9dd8778143d193ff28eccdd1981bb72efe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 22:33:54 +0100 Subject: [PATCH 18/20] Added some protections against creash due to high frequency, but its not ideal ... --- .../Class/MSC_CDC/usbd_msc_cdc.c | 2 +- framework/unit_registry.c | 7 +- platform/plat_compat.h | 2 +- tasks/task_msg.c | 17 ++-- tasks/task_msg.h | 2 +- units/adc/_adc_api.c | 18 ++++ units/adc/_adc_core.c | 98 ++++++++++++++----- units/adc/_adc_internal.h | 1 + units/adc/unit_adc.c | 13 +-- units/adc/unit_adc.h | 2 +- units/usart/unit_usart.c | 2 +- 11 files changed, 117 insertions(+), 47 deletions(-) diff --git a/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c b/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c index 7d05557..dca206d 100644 --- a/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c +++ b/USB/STM32_USB_Device_Library/Class/MSC_CDC/usbd_msc_cdc.c @@ -112,7 +112,7 @@ __ALIGN_BEGIN uint8_t USBD_MSC_CDC_CfgFSDesc[USBD_MSC_CDC_CONFIG_DESC_SIZ] __AL 0x03, /* bmAttributes: Interrupt */ LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: TODO: 2?*/ HIBYTE(CDC_CMD_PACKET_SIZE), - 0xFF, /* bInterval: TODO was 0x10?*/ + 0x10, //0xFF, /* bInterval: TODO was 0x10?*/ /********** CDC Data Class Interface Descriptor ***********/ /*75*/ 0x09, /* bLength: Endpoint Descriptor size */ diff --git a/framework/unit_registry.c b/framework/unit_registry.c index bb86421..23d2ae8 100644 --- a/framework/unit_registry.c +++ b/framework/unit_registry.c @@ -612,14 +612,17 @@ void ureg_tick_units(void) UlistEntry *li = ulist_head; while (li != NULL) { Unit *const pUnit = &li->unit; - if (pUnit && pUnit->data && pUnit->status == E_SUCCESS && pUnit->tick_interval > 0) { + if (pUnit && pUnit->data && pUnit->status == E_SUCCESS && (pUnit->tick_interval > 0 || pUnit->_tick_cnt > 0)) { + if (pUnit->_tick_cnt > 0) { + pUnit->_tick_cnt--; // check for 0 allows one-off timers + } + if (pUnit->_tick_cnt == 0) { if (pUnit->driver->updateTick) { pUnit->driver->updateTick(pUnit); } pUnit->_tick_cnt = pUnit->tick_interval; } - pUnit->_tick_cnt--; } li = li->next; } diff --git a/platform/plat_compat.h b/platform/plat_compat.h index bd6d7b3..994fe3e 100644 --- a/platform/plat_compat.h +++ b/platform/plat_compat.h @@ -24,7 +24,7 @@ #define FLASH_SAVE_BUF_LEN 128 // Malloc'd buffer for saving to flash #define MSG_QUE_SLOT_SIZE 64 // FIXME this should be possible to lower, but there's some bug with bulk transfer / INI parser -#define RX_QUE_CAPACITY 16 // TinyFrame rx queue size (64 bytes each) +#define RX_QUE_CAPACITY 32 // TinyFrame rx queue size (64 bytes each) #define TF_MAX_PAYLOAD_RX 512 // TF max Rx payload #define TF_SENDBUF_LEN 64 // TF transmit buffer (can be less than a full frame) diff --git a/tasks/task_msg.c b/tasks/task_msg.c index e58c260..c3a8b91 100644 --- a/tasks/task_msg.c +++ b/tasks/task_msg.c @@ -8,7 +8,7 @@ volatile uint32_t msgQueHighWaterMark = 0; -static void que_safe_post(struct rx_sched_combined_que_item *slot) +static bool que_safe_post(struct rx_sched_combined_que_item *slot) { uint32_t count = 0; assert_param(slot != NULL); @@ -18,7 +18,7 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) BaseType_t status = xQueueSendFromISR(queMsgJobHandle, slot, &xHigherPriorityTaskWoken); if (pdPASS != status) { dbg("! Que post from ISR failed"); - return; + return false; } #if USE_STACK_MONITOR @@ -30,7 +30,7 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) BaseType_t status = xQueueSend(queMsgJobHandle, slot, MSG_QUE_POST_TIMEOUT); if (pdPASS != status) { dbg("! Que post failed"); - return; + return false; } #if USE_STACK_MONITOR @@ -41,6 +41,8 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) #if USE_STACK_MONITOR msgQueHighWaterMark = MAX(msgQueHighWaterMark, count); #endif + + return true; } /** @@ -48,7 +50,7 @@ static void que_safe_post(struct rx_sched_combined_que_item *slot) * * @param callback - the callback function */ -void scheduleJob(Job *job) +bool scheduleJob(Job *job) { assert_param(job->cb != NULL); @@ -57,7 +59,7 @@ void scheduleJob(Job *job) .job = *job, // copy content of the struct }; - que_safe_post(&slot); + return que_safe_post(&slot); } /** @@ -112,7 +114,10 @@ void rxQuePostMsg(uint8_t *buf, uint32_t len) slot.msg.len = len > MSG_QUE_SLOT_SIZE ? MSG_QUE_SLOT_SIZE : len; memcpy(slot.msg.data, buf, slot.msg.len); - que_safe_post(&slot); + if (!que_safe_post(&slot)) { + dbg("rxQuePostMsg fail!"); + break; + } len -= slot.msg.len; buf += slot.msg.len; diff --git a/tasks/task_msg.h b/tasks/task_msg.h index 3b99d6a..f0a9990 100644 --- a/tasks/task_msg.h +++ b/tasks/task_msg.h @@ -25,7 +25,7 @@ void TaskMsgJob(const void *argument); * * @param job */ -void scheduleJob(Job *job); +bool scheduleJob(Job *job); /** * Add a message to the queue. This always takes the entire slot (64 bytes) or multiple diff --git a/units/adc/_adc_api.c b/units/adc/_adc_api.c index 872d3a1..b91ce37 100644 --- a/units/adc/_adc_api.c +++ b/units/adc/_adc_api.c @@ -9,3 +9,21 @@ #define ADC_INTERNAL #include "_adc_internal.h" +error_t UU_ADC_AbortCapture(Unit *unit) +{ + CHECK_TYPE(unit, &UNIT_ADC); + struct priv *priv = unit->data; + + enum uadc_opmode old_opmode = priv->opmode; + + priv->auto_rearm = false; + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + + if (old_opmode == ADC_OPMODE_BLCAP || + old_opmode == ADC_OPMODE_STREAM || + old_opmode == ADC_OPMODE_TRIGD) { + UADC_ReportEndOfStream(unit); + } + + return E_SUCCESS; +} diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index 586b55a..d101831 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -4,12 +4,16 @@ #include "platform.h" #include "unit_base.h" +#include "unit_adc.h" #define ADC_INTERNAL #include "_adc_internal.h" #define DMA_POS(priv) ((priv)->dma_buffer_itemcount - (priv)->DMA_CHx->CNDTR) +volatile bool emergency = false; +//#define CRUMB() if(emergency) trap("crumb") + static void UADC_JobSendBlockChunk(Job *job) { Unit *unit = job->unit; @@ -21,7 +25,7 @@ static void UADC_JobSendBlockChunk(Job *job) uint32_t count = job->data2; bool close = (bool) job->data3; - dbg("Send indices [%d -> %d)", (int)start, (int)(start+count)); +// dbg("Send indices [%d -> %d)", (int)start, (int)(start+count)); TF_TYPE type = close ? EVT_CAPT_DONE : EVT_CAPT_MORE; @@ -58,7 +62,7 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) EventReport_Start(&er); priv->stream_frame_id = er.sent_msg_id; - dbg("Sending TRIG HEADER with id %d (idx %d)", (int)er.sent_msg_id, (int)index_trigd); +// dbg("Sending TRIG HEADER with id %d (idx %d)", (int)er.sent_msg_id, (int)index_trigd); { // preamble uint8_t buf[4]; @@ -81,10 +85,10 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) uint16_t items_from_end = pretrig_remain - index_trigd; assert_param(priv->dma_buffer_itemcount - items_from_end >= index_trigd); - dbg("Pretrig wraparound part: start %d, len %d", - (int) (priv->dma_buffer_itemcount - items_from_end), - (int) items_from_end - ); +// dbg("Pretrig wraparound part: start %d, len %d", +// (int) (priv->dma_buffer_itemcount - items_from_end), +// (int) items_from_end +// ); EventReport_Data( (uint8_t *) &priv->dma_buffer[priv->dma_buffer_itemcount - @@ -95,10 +99,10 @@ static void UADC_JobSendTriggerCaptureHeader(Job *job) pretrig_remain -= items_from_end; } - dbg("Pretrig front part: start %d, len %d", - (int) (index_trigd - pretrig_remain), - (int) pretrig_remain - ); +// dbg("Pretrig front part: start %d, len %d", +// (int) (index_trigd - pretrig_remain), +// (int) pretrig_remain +// ); assert_param(pretrig_remain <= index_trigd); EventReport_Data((uint8_t *) &priv->dma_buffer[index_trigd - pretrig_remain], @@ -144,6 +148,13 @@ void UADC_DMA_Handler(void *arg) struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) { + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TE(priv->DMAx, priv->dma_chnum); + return; + } + const uint32_t isrsnapshot = priv->DMAx->ISR; if (LL_DMA_IsActiveFlag_G(isrsnapshot, priv->dma_chnum)) { @@ -192,7 +203,13 @@ void UADC_DMA_Handler(void *arg) .data3 = (uint32_t) close, .cb = UADC_JobSendBlockChunk }; - scheduleJob(&j); + if (!scheduleJob(&j)) { + // Abort if we can't queue - the stream would tear and we'd hog the system with error messages + dbg("(!) Buffers overflow, abort capture"); + emergency = true; + UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN); + return; + } if (close) { // dbg("End of capture"); @@ -243,10 +260,11 @@ void UADC_ADC_EOS_Handler(void *arg) assert_param(priv); LL_ADC_ClearFlag_EOS(priv->ADCx); + if (priv->opmode == ADC_OPMODE_UNINIT) return; // Wait for the DMA to complete copying the last sample uint16_t dmapos; - while ((dmapos = (uint16_t) DMA_POS(priv)) % priv->nb_channels != 0); + hw_wait_while((dmapos = (uint16_t) DMA_POS(priv)) % priv->nb_channels != 0, 100); uint32_t sample_pos; if (dmapos == 0) { @@ -313,6 +331,7 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; if (priv->trig_holdoff != 0 && priv->trig_holdoff_remain > 0) { // dbg("Trig discarded due to holdoff."); @@ -323,7 +342,7 @@ void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) priv->trig_holdoff_remain = priv->trig_holdoff; // Start the tick unit->tick_interval = 1; - unit->_tick_cnt = 0; + unit->_tick_cnt = 1; } priv->stream_startpos = (uint16_t) DMA_POS(priv); @@ -349,6 +368,7 @@ void UADC_StartBlockCapture(Unit *unit, uint32_t len, TF_ID frame_id) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); @@ -363,11 +383,11 @@ void UADC_StartStream(Unit *unit, TF_ID frame_id) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; priv->stream_frame_id = frame_id; priv->stream_startpos = (uint16_t) DMA_POS(priv); priv->stream_serial = 0; -// dbg("Start streaming."); UADC_SwitchMode(unit, ADC_OPMODE_STREAM); } @@ -377,8 +397,8 @@ void UADC_StopStream(Unit *unit) assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; -// dbg("Stop stream."); UADC_ReportEndOfStream(unit); UADC_SwitchMode(unit, ADC_OPMODE_IDLE); } @@ -390,6 +410,16 @@ void UADC_updateTick(Unit *unit) struct priv *priv = unit->data; assert_param(priv); + // Recover from shutdown after a delay + if (priv->opmode == ADC_OPMODE_EMERGENCY_SHUTDOWN) { + dbg("Recovering from emergency shutdown"); + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + LL_TIM_EnableCounter(priv->TIMx); + UADC_ReportEndOfStream(unit); + unit->tick_interval = 0; + return; + } + if (priv->trig_holdoff_remain > 0) { priv->trig_holdoff_remain--; @@ -406,10 +436,14 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) struct priv *priv = unit->data; assert_param(priv); - if (new_mode == priv->opmode) return; // nothing to do + const enum uadc_opmode old_mode = priv->opmode; + + if (new_mode == old_mode) return; // nothing to do // if un-itied, can go only to IDLE - assert_param((priv->opmode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE)); + assert_param((old_mode != ADC_OPMODE_UNINIT) || (new_mode == ADC_OPMODE_IDLE)); + + priv->opmode = ADC_OPMODE_UNINIT; if (new_mode == ADC_OPMODE_UNINIT) { // dbg("ADC switch -> UNINIT"); @@ -436,11 +470,12 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); } else if (new_mode == ADC_OPMODE_IDLE || new_mode == ADC_OPMODE_REARM_PENDING) { -// dbg("ADC switch -> IDLE or IDLE/REARM_PENDING"); // IDLE and ARMED are identical with the exception that the trigger condition is not checked // ARMED can be only entered from IDLE, thus we do the init only here. // In IDLE, we don't need the DMA interrupts + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); @@ -448,7 +483,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_ADC_ClearFlag_EOS(priv->ADCx); LL_ADC_EnableIT_EOS(priv->ADCx); - if (priv->opmode == ADC_OPMODE_UNINIT) { + if (old_mode == ADC_OPMODE_UNINIT) { // Nothing is started yet - this is the only way to leave UNINIT LL_ADC_Enable(priv->ADCx); LL_DMA_EnableChannel(priv->DMAx, priv->dma_chnum); @@ -457,9 +492,28 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_ADC_REG_StartConversion(priv->ADCx); } } + else if (new_mode == ADC_OPMODE_EMERGENCY_SHUTDOWN) { + // Emergency shutdown is used when the job queue overflows and the stream is torn + // This however doesn't help in the case when user sets such a high frequency + // that the whole app becomes unresponsive due to the completion ISR, need to verify the value manually. + + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + LL_DMA_DisableIT_HT(priv->DMAx, priv->dma_chnum); + LL_DMA_DisableIT_TC(priv->DMAx, priv->dma_chnum); + + LL_TIM_DisableCounter(priv->TIMx); + UADC_SetSampleRate(unit, 10000); // fallback to a known safe value + + LL_ADC_ClearFlag_EOS(priv->ADCx); + LL_ADC_DisableIT_EOS(priv->ADCx); + + unit->tick_interval = 0; + unit->_tick_cnt = 250; // 1-off + } else if (new_mode == ADC_OPMODE_ARMED) { // dbg("ADC switch -> ARMED"); - assert_param(priv->opmode == ADC_OPMODE_IDLE || priv->opmode == ADC_OPMODE_REARM_PENDING); + assert_param(old_mode == ADC_OPMODE_IDLE || old_mode == ADC_OPMODE_REARM_PENDING); // avoid firing immediately by the value jumping across the scale priv->trig_prev_level = priv->last_samples[priv->trigger_source]; @@ -469,7 +523,7 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) new_mode == ADC_OPMODE_BLCAP) { // dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); - assert_param(priv->opmode == ADC_OPMODE_ARMED || priv->opmode == ADC_OPMODE_IDLE); + assert_param(old_mode == ADC_OPMODE_ARMED || old_mode == ADC_OPMODE_IDLE); // during the capture, we disallow direct readout and averaging to reduce overhead LL_ADC_DisableIT_EOS(priv->ADCx); @@ -484,6 +538,6 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); } - // the actual switch + dbg("Now setting the new opmode"); priv->opmode = new_mode; } diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index b46e3a9..fe00843 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -19,6 +19,7 @@ enum uadc_opmode { ADC_OPMODE_TRIGD, //!< Triggered, sending pre-trigger and streaming captured data. ADC_OPMODE_BLCAP, //!< Capture of fixed length without a trigger ADC_OPMODE_STREAM, //!< Unlimited capture + ADC_OPMODE_EMERGENCY_SHUTDOWN, //!< Used when the buffers overrun to safely transition to IDLE after a delay }; enum uadc_event { diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index f6ddb7c..0f8cb83 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -240,18 +240,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P */ case CMD_ABORT:; dbg("> Abort capture"); - { - enum uadc_opmode old_opmode = priv->opmode; - - priv->auto_rearm = false; - UADC_SwitchMode(unit, ADC_OPMODE_IDLE); - - if (old_opmode == ADC_OPMODE_BLCAP || - old_opmode == ADC_OPMODE_STREAM || - old_opmode == ADC_OPMODE_TRIGD) { - UADC_ReportEndOfStream(unit); - } - } + TRY(UU_ADC_AbortCapture(unit)); return E_SUCCESS; /** diff --git a/units/adc/unit_adc.h b/units/adc/unit_adc.h index 84d48ba..1ee3501 100644 --- a/units/adc/unit_adc.h +++ b/units/adc/unit_adc.h @@ -11,6 +11,6 @@ extern const UnitDriver UNIT_ADC; -// UU_ prototypes +error_t UU_ADC_AbortCapture(Unit *unit); #endif //U_TPL_H diff --git a/units/usart/unit_usart.c b/units/usart/unit_usart.c index 985074a..e8aca86 100644 --- a/units/usart/unit_usart.c +++ b/units/usart/unit_usart.c @@ -65,7 +65,7 @@ void UUSART_DMA_HandleRxFromIRQ(Unit *unit, uint16_t endpos) .data2 = count, .cb = UUSART_SendReceivedDataToMaster }; - scheduleJob(&j); + scheduleJob(&j); // TODO disable unit on failure // Move the read cursor, wrap around if needed if (endpos == UUSART_RXBUF_LEN) endpos = 0; From d6e73a564166d3089ed4ff687fc67bedf94683e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 23:09:37 +0100 Subject: [PATCH 19/20] Some optimizations to make it better work at higher speeds. Triggers don't work --- FreeRTOSConfig.h | 2 +- freertos.c | 8 ++-- platform/plat_compat.h | 6 ++- units/adc/_adc_core.c | 86 ++++++++++++++++++++------------------- units/adc/_adc_init.c | 2 + units/adc/_adc_internal.h | 3 ++ units/adc/_adc_settings.c | 2 +- units/adc/unit_adc.c | 6 +++ 8 files changed, 67 insertions(+), 48 deletions(-) diff --git a/FreeRTOSConfig.h b/FreeRTOSConfig.h index 2be418b..4b9bfd9 100644 --- a/FreeRTOSConfig.h +++ b/FreeRTOSConfig.h @@ -111,7 +111,7 @@ #define configUSE_TIMERS 1 #define configTIMER_TASK_PRIORITY 4 // above normal -#define configTIMER_TASK_STACK_DEPTH 128 +#define configTIMER_TASK_STACK_DEPTH TSK_STACK_TIMERS //128 #define configTIMER_QUEUE_LENGTH 4 #define configTOTAL_HEAP_SIZE 4096 diff --git a/freertos.c b/freertos.c index 3c69f35..da8accd 100644 --- a/freertos.c +++ b/freertos.c @@ -114,13 +114,13 @@ __weak void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTas /* USER CODE BEGIN GET_IDLE_TASK_MEMORY */ static StaticTask_t xIdleTaskTCBBuffer; -static StackType_t xIdleStack[configMINIMAL_STACK_SIZE]; +static StackType_t xIdleStack[TSK_STACK_IDLE]; void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ) { *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer; *ppxIdleTaskStackBuffer = &xIdleStack[0]; - *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; + *pulIdleTaskStackSize = TSK_STACK_IDLE; /* place for user code */ } /* USER CODE END GET_IDLE_TASK_MEMORY */ @@ -128,13 +128,13 @@ void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackTy /* USER CODE BEGIN GET_IDLE_TASK_MEMORY */ static StaticTask_t xTimersTaskTCBBuffer; -static StackType_t xTimersStack[configTIMER_TASK_STACK_DEPTH]; +static StackType_t xTimersStack[TSK_STACK_TIMERS]; void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimersTaskTCBBuffer, StackType_t **ppxTimersTaskStackBuffer, uint32_t *pulTimersTaskStackSize ) { *ppxTimersTaskTCBBuffer = &xTimersTaskTCBBuffer; *ppxTimersTaskStackBuffer = &xTimersStack[0]; - *pulTimersTaskStackSize = configTIMER_TASK_STACK_DEPTH; + *pulTimersTaskStackSize = TSK_STACK_TIMERS; /* place for user code */ } /* USER CODE END GET_IDLE_TASK_MEMORY */ diff --git a/platform/plat_compat.h b/platform/plat_compat.h index 994fe3e..03b1a1f 100644 --- a/platform/plat_compat.h +++ b/platform/plat_compat.h @@ -18,13 +18,17 @@ // 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_IDLE 64 //configMINIMAL_STACK_SIZE +#define TSK_STACK_TIMERS 64 //configTIMER_TASK_STACK_DEPTH + + #define BULK_READ_BUF_LEN 256 // Buffer for TF bulk reads #define UNIT_TMP_LEN 512 // Buffer for internal unit operations #define FLASH_SAVE_BUF_LEN 128 // Malloc'd buffer for saving to flash #define MSG_QUE_SLOT_SIZE 64 // FIXME this should be possible to lower, but there's some bug with bulk transfer / INI parser -#define RX_QUE_CAPACITY 32 // TinyFrame rx queue size (64 bytes each) +#define RX_QUE_CAPACITY 36 // TinyFrame rx queue size (64 bytes each) #define TF_MAX_PAYLOAD_RX 512 // TF max Rx payload #define TF_SENDBUF_LEN 64 // TF transmit buffer (can be less than a full frame) diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index d101831..64a6698 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -183,7 +183,9 @@ void UADC_DMA_Handler(void *arg) LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); } -// dbg("start %d, end %d", (int)start, (int)end); + if (start > end) { + dbg("start %d, end %d", (int) start, (int) end); + } assert_param(start <= end); if (start != end) { @@ -252,19 +254,25 @@ void UADC_DMA_Handler(void *arg) void UADC_ADC_EOS_Handler(void *arg) { - uint64_t timestamp = PTIM_GetMicrotime(); Unit *unit = arg; - assert_param(unit); struct priv *priv = unit->data; assert_param(priv); + const bool trig_ready4_rising = (priv->trig_prev_level < priv->trig_level) && (bool) (priv->trig_edge & 0b01); + const bool trig_ready4_falling = (priv->trig_prev_level > priv->trig_level) && (bool) (priv->trig_edge & 0b10); + const bool armed = (trig_ready4_rising && trig_ready4_falling) && priv->opmode == ADC_OPMODE_ARMED; + + // Normally + uint64_t timestamp = 0; + if (armed) timestamp = PTIM_GetMicrotime(); + LL_ADC_ClearFlag_EOS(priv->ADCx); if (priv->opmode == ADC_OPMODE_UNINIT) return; // Wait for the DMA to complete copying the last sample uint16_t dmapos; - hw_wait_while((dmapos = (uint16_t) DMA_POS(priv)) % priv->nb_channels != 0, 100); + hw_wait_while((dmapos = (uint16_t) DMA_POS(priv)) % priv->nb_channels != 0, 100); // XXX this could be changed to reading it from the DR instead uint32_t sample_pos; if (dmapos == 0) { @@ -275,55 +283,52 @@ void UADC_ADC_EOS_Handler(void *arg) sample_pos -= priv->nb_channels; int cnt = 0; // index of the sample within the group - for (uint32_t i = 0; i < 18; i++) { - if (priv->extended_channels_mask & (1 << i)) { - uint16_t val = priv->dma_buffer[sample_pos+cnt]; + + const uint8_t trig_source = priv->trigger_source; + const bool can_average = priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING; + const uint16_t *dma_buffer = priv->dma_buffer; + const uint32_t channels_mask = priv->extended_channels_mask; + + for (uint8_t i = 0; i < 18; i++) { + if (channels_mask & (1 << i)) { + uint16_t val = dma_buffer[sample_pos+cnt]; cnt++; - priv->averaging_bins[i] = - priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + - ((float) val) * priv->avg_factor_as_float; + if (can_average) { + priv->averaging_bins[i] = + priv->averaging_bins[i] * (1.0f - priv->avg_factor_as_float) + + ((float) val) * priv->avg_factor_as_float; + } priv->last_samples[i] = val; - if (i == priv->trigger_source) { - if (priv->opmode == ADC_OPMODE_ARMED) { + if (armed && i == trig_source) { // dbg("Trig line level %d", (int)val); - - bool trigd = false; - uint8_t edge_type = 0; - if (priv->trig_prev_level < priv->trig_level && val >= priv->trig_level) { + if (trig_ready4_rising && val >= priv->trig_level) { // dbg("******** Rising edge"); - // Rising edge - trigd = (bool) (priv->trig_edge & 0b01); - edge_type = 1; - } - else if (priv->trig_prev_level > priv->trig_level && val <= priv->trig_level) { -// dbg("******** Falling edge"); - // Falling edge - trigd = (bool) (priv->trig_edge & 0b10); - edge_type = 2; - } - - if (trigd) { - UADC_HandleTrigger(unit, edge_type, timestamp); - } + // Rising edge + UADC_HandleTrigger(unit, 1, timestamp); } - else if (priv->opmode == ADC_OPMODE_REARM_PENDING) { - if (!priv->auto_rearm) { - // It looks like the flag was cleared by DISARM before we got a new sample. - // Let's just switch to IDLE - UADC_SwitchMode(unit, ADC_OPMODE_IDLE); - } else { - // Re-arming for a new trigger - UADC_SwitchMode(unit, ADC_OPMODE_ARMED); - } + else if (trig_ready4_falling && val <= priv->trig_level) { +// dbg("******** Falling edge"); + // Falling edge + UADC_HandleTrigger(unit, 2, timestamp); } - priv->trig_prev_level = val; } } } + + if (priv->opmode == ADC_OPMODE_REARM_PENDING) { + if (!priv->auto_rearm) { + // It looks like the flag was cleared by DISARM before we got a new sample. + // Let's just switch to IDLE + UADC_SwitchMode(unit, ADC_OPMODE_IDLE); + } else { + // Re-arming for a new trigger + UADC_SwitchMode(unit, ADC_OPMODE_ARMED); + } + } } void UADC_HandleTrigger(Unit *unit, uint8_t edge_type, uint64_t timestamp) @@ -538,6 +543,5 @@ void UADC_SwitchMode(Unit *unit, enum uadc_opmode new_mode) LL_DMA_EnableIT_TC(priv->DMAx, priv->dma_chnum); } - dbg("Now setting the new opmode"); priv->opmode = new_mode; } diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 1de3f4f..8fd1e47 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -49,6 +49,8 @@ error_t UADC_SetSampleRate(Unit *unit, uint32_t hertz) LL_TIM_SetPrescaler(priv->TIMx, (uint32_t) (presc - 1)); LL_TIM_SetAutoReload(priv->TIMx, count - 1); + priv->real_frequency_int = hertz; + return E_SUCCESS; } diff --git a/units/adc/_adc_internal.h b/units/adc/_adc_internal.h index fe00843..8972fff 100644 --- a/units/adc/_adc_internal.h +++ b/units/adc/_adc_internal.h @@ -11,6 +11,8 @@ #include "unit_base.h" +#define UADC_MAX_FREQ_FOR_AVERAGING 20000 + enum uadc_opmode { ADC_OPMODE_UNINIT, //!< Not yet switched to any mode ADC_OPMODE_IDLE, //!< Idle. Allows immediate value readout and averaging. @@ -42,6 +44,7 @@ struct priv { // internal state float real_frequency; + uint32_t real_frequency_int; uint32_t extended_channels_mask; //!< channels bitfield including tsense and vref float avg_factor_as_float; ADC_TypeDef *ADCx; //!< The ADC peripheral used diff --git a/units/adc/_adc_settings.c b/units/adc/_adc_settings.c index e19811e..e723c19 100644 --- a/units/adc/_adc_settings.c +++ b/units/adc/_adc_settings.c @@ -98,7 +98,7 @@ void UADC_writeIni(Unit *unit, IniWriter *iw) iw_entry(iw, "enable_tsense", str_yn(priv->enable_tsense)); iw_comment(iw, "Enable Vref channel (#17)"); - iw_entry(iw, "enable_vref", str_yn(priv->enable_tsense)); + iw_entry(iw, "enable_vref", str_yn(priv->enable_vref)); iw_cmt_newline(iw); iw_comment(iw, "Sampling time (0-7)"); diff --git a/units/adc/unit_adc.c b/units/adc/unit_adc.c index 0f8cb83..715b7bc 100644 --- a/units/adc/unit_adc.c +++ b/units/adc/unit_adc.c @@ -64,6 +64,7 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P * May differ from the configured or requested value due to prescaller limitations. */ case CMD_GET_SAMPLE_RATE: + pb_u32(&pb, priv->real_frequency_int); pb_float(&pb, priv->real_frequency); com_respond_pb(frame_id, MSG_SUCCESS, &pb); return E_SUCCESS; @@ -106,6 +107,11 @@ static error_t UADC_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P return E_BUSY; } + if (priv->real_frequency_int > UADC_MAX_FREQ_FOR_AVERAGING) { + com_respond_str(MSG_ERROR, frame_id, "Too fast for smoothing"); + return E_FAILURE; + } + for (uint8_t i = 0; i < 18; i++) { if (priv->extended_channels_mask & (1 << i)) { pb_float(&pb, priv->averaging_bins[i]); From 85453afa44483ba488af162354277d8af05ae683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Wed, 7 Feb 2018 23:50:30 +0100 Subject: [PATCH 20/20] Fixzed triggers and also missed TC/TH --- units/adc/_adc_core.c | 183 +++++++++++++++++++++++------------------- units/adc/_adc_init.c | 2 +- 2 files changed, 103 insertions(+), 82 deletions(-) diff --git a/units/adc/_adc_core.c b/units/adc/_adc_core.c index 64a6698..1ea4312 100644 --- a/units/adc/_adc_core.c +++ b/units/adc/_adc_core.c @@ -140,6 +140,80 @@ void UADC_ReportEndOfStream(Unit *unit) scheduleJob(&j); } +static void handle_httc(Unit *unit, bool tc) +{ + struct priv *priv = unit->data; + uint16_t start = priv->stream_startpos; + uint16_t end; + const bool ht = !tc; + + const bool m_trigd = priv->opmode == ADC_OPMODE_TRIGD; + const bool m_stream = priv->opmode == ADC_OPMODE_STREAM; + const bool m_fixcpt = priv->opmode == ADC_OPMODE_BLCAP; + + if (ht) { +// dbg("HT"); + end = (uint16_t) (priv->dma_buffer_itemcount / 2); + LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); + } + else { +// dbg("TC"); + end = (uint16_t) priv->dma_buffer_itemcount; + LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); + } + + if (ht == tc) { + // This shouldn't happen - looks like we missed the TC flag + dbg("!! %d -> %d", (int) start, (int) end); + // TODO we could try to catch up. for now, just take what is easy to grab and hope it doesnt matter + if (end == 64) start = 0; + } + + if (start != end) { + uint32_t sgcount = (end - start) / priv->nb_channels; + + if (m_trigd || m_fixcpt) { + sgcount = MIN(priv->trig_stream_remain, sgcount); + priv->trig_stream_remain -= sgcount; + } + + bool close = !m_stream && priv->trig_stream_remain == 0; + + Job j = { + .unit = unit, + .data1 = start, + .data2 = sgcount * priv->nb_channels, + .data3 = (uint32_t) close, + .cb = UADC_JobSendBlockChunk + }; + if (!scheduleJob(&j)) { + // Abort if we can't queue - the stream would tear and we'd hog the system with error messages + dbg("(!) Buffers overflow, abort capture"); + emergency = true; + UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN); + return; + } + + if (close) { +// dbg("End of capture"); + // If auto-arm enabled, we need to re-arm again. + // However, EOS irq is disabled during the capture. + // We have to wait for the next EOS interrupt to occur. + // TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled. + UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); + } + } else { +// dbg("start==end, skip this irq"); + } + + if (tc) { + priv->stream_startpos = 0; + } + else { + priv->stream_startpos = end; + } +} + void UADC_DMA_Handler(void *arg) { Unit *unit = arg; @@ -169,67 +243,17 @@ void UADC_DMA_Handler(void *arg) if (m_trigd || m_stream || m_fixcpt) { if (ht || tc) { - const uint16_t start = priv->stream_startpos; - uint16_t end; - - if (ht) { -// dbg("HT"); - end = (uint16_t) (priv->dma_buffer_itemcount / 2); - LL_DMA_ClearFlag_HT(priv->DMAx, priv->dma_chnum); - } - else { -// dbg("TC"); - end = (uint16_t) priv->dma_buffer_itemcount; - LL_DMA_ClearFlag_TC(priv->DMAx, priv->dma_chnum); - } - - if (start > end) { - dbg("start %d, end %d", (int) start, (int) end); - } - assert_param(start <= end); - - if (start != end) { - uint32_t sgcount = (end - start) / priv->nb_channels; - - if (m_trigd || m_fixcpt) { - sgcount = MIN(priv->trig_stream_remain, sgcount); - priv->trig_stream_remain -= sgcount; - } - - bool close = !m_stream && priv->trig_stream_remain == 0; - - Job j = { - .unit = unit, - .data1 = start, - .data2 = sgcount * priv->nb_channels, - .data3 = (uint32_t) close, - .cb = UADC_JobSendBlockChunk - }; - if (!scheduleJob(&j)) { - // Abort if we can't queue - the stream would tear and we'd hog the system with error messages - dbg("(!) Buffers overflow, abort capture"); - emergency = true; - UADC_SwitchMode(unit, ADC_OPMODE_EMERGENCY_SHUTDOWN); - return; - } - - if (close) { -// dbg("End of capture"); - // If auto-arm enabled, we need to re-arm again. - // However, EOS irq is disabled during the capture. - // We have to wait for the next EOS interrupt to occur. - // TODO verify if keeping the EOS irq enabled during capture has significant performance penalty. If not, we can leave it enabled. - UADC_SwitchMode(unit, (priv->auto_rearm && m_trigd) ? ADC_OPMODE_REARM_PENDING : ADC_OPMODE_IDLE); + if (ht && tc) { + uint16_t half = (uint16_t) (priv->dma_buffer_itemcount / 2); + if (priv->stream_startpos > half) { + handle_httc(unit, true); + handle_httc(unit, false); + } else { + handle_httc(unit, false); + handle_httc(unit, true); } } else { -// dbg("start==end, skip this irq"); - } - - if (tc) { - priv->stream_startpos = 0; - } - else { - priv->stream_startpos = end; + handle_httc(unit, tc); } } } else { @@ -259,13 +283,9 @@ void UADC_ADC_EOS_Handler(void *arg) struct priv *priv = unit->data; assert_param(priv); - const bool trig_ready4_rising = (priv->trig_prev_level < priv->trig_level) && (bool) (priv->trig_edge & 0b01); - const bool trig_ready4_falling = (priv->trig_prev_level > priv->trig_level) && (bool) (priv->trig_edge & 0b10); - const bool armed = (trig_ready4_rising && trig_ready4_falling) && priv->opmode == ADC_OPMODE_ARMED; - // Normally uint64_t timestamp = 0; - if (armed) timestamp = PTIM_GetMicrotime(); + if (priv->opmode == ADC_OPMODE_ARMED) timestamp = PTIM_GetMicrotime(); LL_ADC_ClearFlag_EOS(priv->ADCx); if (priv->opmode == ADC_OPMODE_UNINIT) return; @@ -284,14 +304,12 @@ void UADC_ADC_EOS_Handler(void *arg) int cnt = 0; // index of the sample within the group - const uint8_t trig_source = priv->trigger_source; const bool can_average = priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING; - const uint16_t *dma_buffer = priv->dma_buffer; const uint32_t channels_mask = priv->extended_channels_mask; for (uint8_t i = 0; i < 18; i++) { if (channels_mask & (1 << i)) { - uint16_t val = dma_buffer[sample_pos+cnt]; + uint16_t val = priv->dma_buffer[sample_pos+cnt]; cnt++; if (can_average) { @@ -301,24 +319,27 @@ void UADC_ADC_EOS_Handler(void *arg) } priv->last_samples[i] = val; + } + } - if (armed && i == trig_source) { -// dbg("Trig line level %d", (int)val); - if (trig_ready4_rising && val >= priv->trig_level) { -// dbg("******** Rising edge"); - // Rising edge - UADC_HandleTrigger(unit, 1, timestamp); - } - else if (trig_ready4_falling && val <= priv->trig_level) { -// dbg("******** Falling edge"); - // Falling edge - UADC_HandleTrigger(unit, 2, timestamp); - } - priv->trig_prev_level = val; - } + if (priv->opmode == ADC_OPMODE_ARMED) { + uint16_t val = priv->last_samples[priv->trigger_source]; + +// dbg("Trig line level %d", (int)val); + if ((priv->trig_prev_level < priv->trig_level) && val >= priv->trig_level && (bool) (priv->trig_edge & 0b01)) { +// dbg("******** Rising edge"); + // Rising edge + UADC_HandleTrigger(unit, 1, timestamp); + } + else if ((priv->trig_prev_level > priv->trig_level) && val <= priv->trig_level && (bool) (priv->trig_edge & 0b10)) { +// dbg("******** Falling edge"); + // Falling edge + UADC_HandleTrigger(unit, 2, timestamp); } + priv->trig_prev_level = val; } + // auto-rearm was waiting for the next sample if (priv->opmode == ADC_OPMODE_REARM_PENDING) { if (!priv->auto_rearm) { // It looks like the flag was cleared by DISARM before we got a new sample. diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c index 8fd1e47..eb3d3f7 100644 --- a/units/adc/_adc_init.c +++ b/units/adc/_adc_init.c @@ -196,7 +196,7 @@ error_t UADC_init(Unit *unit) priv->dma_buffer_itemcount*sizeof(uint16_t), priv->nb_channels); - priv->dma_buffer = malloc_ck(priv->dma_buffer_itemcount * sizeof(uint16_t)); + priv->dma_buffer = calloc_ck(priv->dma_buffer_itemcount, sizeof(uint16_t)); if (NULL == priv->dma_buffer) return E_OUT_OF_MEM; assert_param(((uint32_t) priv->dma_buffer & 3) == 0); // must be aligned