diff --git a/FreeRTOSConfig.h b/FreeRTOSConfig.h index 51bc917..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 @@ -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/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/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 44df67d..312aa9b 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,12 @@ 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 = type, + .data = buf, + .len = (TF_LEN) len, + }; + TF_Send(comm, &msg); // no listener } 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..23d2ae8 100644 --- a/framework/unit_registry.c +++ b/framework/unit_registry.c @@ -490,12 +490,18 @@ 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) { 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; @@ -606,14 +612,17 @@ 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 || 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/freertos.c b/freertos.c index 045e3e0..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 */ @@ -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..c082ca4 100644 --- a/platform/hw_utils.c +++ b/platform/hw_utils.c @@ -3,100 +3,9 @@ // #include "platform.h" -#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) @@ -263,16 +172,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 +344,42 @@ 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; + + // 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); + + 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; + + if (real_freq != NULL) { + *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..94195ea 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. @@ -78,6 +73,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 +81,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 +132,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 +146,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,46 +167,32 @@ void hw_periph_clock_enable(void *periph); */ void hw_periph_clock_disable(void *periph); -// ---------- 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)); -} +/** + * 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); + +#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 2908254..be62258 100644 --- a/platform/irq_dispatcher.c +++ b/platform/irq_dispatcher.c @@ -61,6 +61,12 @@ static struct callbacks_ { struct cbslot dma2_7; struct cbslot dma2_8; + struct cbslot tim6; + struct cbslot tim7; + struct cbslot tim15; + + struct cbslot adc1; + // XXX add more callbacks here when needed } callbacks; @@ -68,41 +74,64 @@ 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); -// NVIC_EnableIRQ(ADC1_COMP_IRQn); /*!< ADC1 and COMP interrupts (ADC interrupt combined with EXTI Lines 21 and 22 */ + 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 */ + 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 */ -// 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 +159,12 @@ 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 == ADC1) slot = &callbacks.adc1; + else if (periph >= EXTIS[0] && periph <= EXTIS[15]) { slot = &callbacks.exti[periph - EXTIS[0]]; } @@ -265,6 +300,31 @@ 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); +} + +void ADC1_COMP_IRQHandler(void) +{ + CALL_IRQ_HANDLER(callbacks.adc1); +} + + // other ISRs... #if 0 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/platform/plat_compat.h b/platform/plat_compat.h index bd6d7b3..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 16 // 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/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/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/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/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/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/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..6dead85 100644 --- a/units/1wire/unit_1wire.c +++ b/units/1wire/unit_1wire.c @@ -9,10 +9,25 @@ // 1WIRE master #define OW_INTERNAL #include "_ow_internal.h" -#include "_ow_init.h" -#include "_ow_settings.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. * @@ -30,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; @@ -47,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); } @@ -112,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, @@ -162,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); @@ -175,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); diff --git a/units/adc/_adc_api.c b/units/adc/_adc_api.c new file mode 100644 index 0000000..b91ce37 --- /dev/null +++ b/units/adc/_adc_api.c @@ -0,0 +1,29 @@ +// +// 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" + +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 new file mode 100644 index 0000000..1ea4312 --- /dev/null +++ b/units/adc/_adc_core.c @@ -0,0 +1,568 @@ +// +// Created by MightyPork on 2018/02/04. +// + +#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; + 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_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; + 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) +{ + 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); +} + +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; + + assert_param(unit); + 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)) { + 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_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 (m_trigd || m_stream || m_fixcpt) { + if (ht || tc) { + 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 { + handle_httc(unit, tc); + } + } + } 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) +{ + Unit *unit = arg; + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + + // Normally + uint64_t timestamp = 0; + if (priv->opmode == ADC_OPMODE_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); // XXX this could be changed to reading it from the DR instead + + uint32_t sample_pos; + if (dmapos == 0) { + sample_pos = (uint32_t) (priv->dma_buffer_itemcount); + } else { + sample_pos = dmapos; + } + sample_pos -= priv->nb_channels; + + int cnt = 0; // index of the sample within the group + + const bool can_average = priv->real_frequency_int < UADC_MAX_FREQ_FOR_AVERAGING; + 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 = priv->dma_buffer[sample_pos+cnt]; + cnt++; + + 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 (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. + // 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) +{ + 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."); + return; + } + + if (priv->trig_holdoff > 0) { + priv->trig_holdoff_remain = priv->trig_holdoff; + // Start the tick + unit->tick_interval = 1; + unit->_tick_cnt = 1; + } + + priv->stream_startpos = (uint16_t) DMA_POS(priv); + priv->trig_stream_remain = priv->trig_len; + priv->stream_serial = 0; + +// 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); +} + +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); + priv->trig_stream_remain = len; + priv->stream_serial = 0; + UADC_SwitchMode(unit, ADC_OPMODE_BLCAP); +} + +/** Start stream */ +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; + UADC_SwitchMode(unit, ADC_OPMODE_STREAM); +} + +/** End stream */ +void UADC_StopStream(Unit *unit) +{ + assert_param(unit); + struct priv *priv = unit->data; + assert_param(priv); + if (priv->opmode == ADC_OPMODE_UNINIT) return; + + 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); + 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--; + + 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); + + 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((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"); + // 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 || new_mode == ADC_OPMODE_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); + + // 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 (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); + LL_TIM_EnableCounter(priv->TIMx); + + 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(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]; + } + else if (new_mode == ADC_OPMODE_TRIGD || + new_mode == ADC_OPMODE_STREAM || + new_mode == ADC_OPMODE_BLCAP) { + +// dbg("ADC switch -> TRIG'D / STREAM / BLOCK"); + 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); + + // 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); + } + + priv->opmode = new_mode; +} diff --git a/units/adc/_adc_init.c b/units/adc/_adc_init.c new file mode 100644 index 0000000..eb3d3f7 --- /dev/null +++ b/units/adc/_adc_init.c @@ -0,0 +1,274 @@ +// +// Created by MightyPork on 2018/02/03. +// + +#include +#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; + + priv->channels = 1; // PA0 + priv->enable_tsense = false; + priv->enable_vref = false; + priv->sample_time = 0b010; // 13.5c + priv->frequency = 1000; + priv->buffer_size = 512; + priv->averaging_factor = 500; + + priv->opmode = ADC_OPMODE_UNINIT; + + 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); + + priv->real_frequency_int = hertz; + + return E_SUCCESS; +} + +/** 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; + } + + if (priv->buffer_size < priv->nb_channels*2*2) { + dbg("Insufficient buf size"); + return E_BAD_CONFIG; + } + } + + // ------------------- ENABLE CLOCKS -------------------------- + { + // 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 -------------------------- + dbg("Setting up TIMER"); + { + 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); + 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 + 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); + } + + // --------------------- CONFIGURE DMA ------------------------------- + dbg("Setting up DMA"); + { + // 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_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 = 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 + + { + 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)); + + // 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); + } + + // 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."); + + 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; +} + + + +/** Tear down the unit */ +void UADC_deInit(Unit *unit) +{ + struct priv *priv = unit->data; + + // 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); + } + + // 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..8972fff --- /dev/null +++ b/units/adc/_adc_internal.h @@ -0,0 +1,137 @@ +// +// 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" + +#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. + 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_STREAM, //!< Unlimited capture + ADC_OPMODE_EMERGENCY_SHUTDOWN, //!< Used when the buffers overrun to safely transition to IDLE after a delay +}; + +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 + 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 + uint16_t buffer_size; //!< Buffer size in bytes (count 2 bytes per channel per measurement) - faster sampling freq needs bigger buffer + uint16_t averaging_factor; //!< Exponential averaging factor 0-1000 + + // 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 + 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 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) + 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 + uint8_t trig_edge; //!< Which edge we want to trigger on. 1-rising, 2-falling, 3-both + 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 */ +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); + +// ------------------------------------------------------------------------ + +/** 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, 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); + +/** Configure frequency */ +error_t UADC_SetSampleRate(Unit *unit, uint32_t hertz); + +#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..e723c19 --- /dev/null +++ b/units/adc/_adc_settings.c @@ -0,0 +1,122 @@ +// +// 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; + + 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); + + 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 */ +void UADC_writeBinary(Unit *unit, PayloadBuilder *pb) +{ + struct priv *priv = unit->data; + + pb_u8(pb, 2); // 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); + pb_u16(pb, priv->averaging_factor); +} + +// ------------------------------------------------------------------------ + +/** 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 (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 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; + } + + 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; + + 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 (#16)"); + 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_vref)); + + iw_cmt_newline(iw); + 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); + + 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); + + iw_cmt_newline(iw); + 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 new file mode 100644 index 0000000..715b7bc --- /dev/null +++ b/units/adc/unit_adc.c @@ -0,0 +1,338 @@ +// +// Created by MightyPork on 2017/11/25. +// + +#include "unit_base.h" +#include "unit_adc.h" + +#define ADC_INTERNAL +#include "_adc_internal.h" + +// ------------------------------------------------------------------------ + +enum TplCmd_ { + CMD_READ_RAW = 0, + CMD_READ_SMOOTHED = 1, + + CMD_GET_ENABLED_CHANNELS = 10, + CMD_GET_SAMPLE_RATE = 11, + + 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, + CMD_SET_SMOOTHING_FACTOR = 28, + CMD_SET_SAMPLE_RATE = 29, +}; + +/** 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); + + // TODO toggling individual channels - would require DMA re-init and various changes in the usage of the struct + + switch (command) { + /** + * Get enabled channels. + * Response: bytes with indices of enabled channels, ascending order. + */ + case CMD_GET_ENABLED_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_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_u32(&pb, priv->real_frequency_int); + 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 + */ + case CMD_SET_SMOOTHING_FACTOR: + { + 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: + 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_u16(&pb, priv->last_samples[i]); + } + } + 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: + if(priv->opmode != ADC_OPMODE_IDLE && priv->opmode != ADC_OPMODE_ARMED) { + 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]); + } + } + com_respond_pb(frame_id, MSG_SUCCESS, &pb); + return E_SUCCESS; + + /** + * Configure a trigger. This is legal only if the current state is IDLE or ARMED (will re-arm). + * + * 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 && + priv->opmode != ADC_OPMODE_ARMED && + priv->opmode != ADC_OPMODE_REARM_PENDING) { + return E_BUSY; + } + + { + 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); + 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"); + 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 + 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); + 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; + + /** + * Arm (permissible only if idle and the trigger is configured) + */ + case CMD_ARM: + dbg("> Arm"); + 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; + } + + UADC_SwitchMode(unit, ADC_OPMODE_ARMED); + } + + if (sticky != 255) { + priv->auto_rearm = (bool)sticky; + } + + return E_SUCCESS; + + /** + * Dis-arm. Permissible only when idle or armed. + * Switches to idle. + */ + case CMD_DISARM: + 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 && + 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; + + /** + * Abort any ongoing capture and dis-arm. + */ + case CMD_ABORT:; + dbg("> Abort capture"); + TRY(UU_ADC_AbortCapture(unit)); + 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"); + // 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; + } + + 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 && + priv->opmode != ADC_OPMODE_REARM_PENDING && + priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + + uint32_t count = pp_u32(pp); + + 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 && + priv->opmode != ADC_OPMODE_REARM_PENDING && + priv->opmode != ADC_OPMODE_IDLE) return E_BUSY; + + UADC_StartStream(unit, frame_id); + return E_SUCCESS; + + /** + * Stop a stream. + */ + 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; + } +} + +// ------------------------------------------------------------------------ + +/** Unit template */ +const UnitDriver UNIT_ADC = { + .name = "ADC", + .description = "Analog/digital converter", + // 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, + .updateTick = UADC_updateTick, +}; diff --git a/units/adc/unit_adc.h b/units/adc/unit_adc.h new file mode 100644 index 0000000..1ee3501 --- /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; + +error_t UU_ADC_AbortCapture(Unit *unit); + +#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/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/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/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/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 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/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; 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) \ 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; } 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