From 590d550a09d1274231277ec68b526f3e4b68ceef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 8 Mar 2018 00:49:21 +0100 Subject: [PATCH] pulse generation for digital out --- platform/timebase.c | 27 ++++++-- platform/timebase.h | 22 +++++- units/1wire/unit_1wire.c | 4 +- units/digital_out/_dout_api.c | 106 ++++++++++++++++++++++++++++- units/digital_out/_dout_internal.h | 5 ++ units/digital_out/unit_dout.c | 12 +++- units/digital_out/unit_dout.h | 12 ++++ units/i2c/unit_i2c.c | 4 +- units/sipo/unit_sipo.c | 4 +- units/spi/unit_spi.c | 4 +- units/usart/unit_usart.c | 4 +- 11 files changed, 181 insertions(+), 23 deletions(-) diff --git a/platform/timebase.c b/platform/timebase.c index 050e62b..1f9fb41 100644 --- a/platform/timebase.c +++ b/platform/timebase.c @@ -7,8 +7,6 @@ // ---------------------------- HAL TIMEBASE ----------------------------- -#define TIMEBASE_TIMER TIM17 - HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { // EDIT - used 17 instead because 14 was needed for fcap @@ -102,21 +100,36 @@ uint64_t PTIM_GetMicrotime(void) return (uint64_t) uwMillis * 1000 + uwMicros; } + /** microsecond delay */ -void PTIM_MicroDelay(uint16_t usec) +void PTIM_MicroDelayAligned(uint32_t usec, register const uint32_t start) { assert_param(usec < 1000); - const uint16_t start = (uint16_t) TIMEBASE_TIMER->CNT; - const uint16_t remain = (uint16_t) (999 - start); + register const uint32_t remain = (1000 - start); if (remain > usec) { // timer still has enough space going forward to pass the required wait time - for (; TIMEBASE_TIMER->CNT < start + usec;); + register const uint32_t end = start + usec; + while (TIMEBASE_TIMER->CNT < end) { + __NOP(); + __NOP(); + __NOP(); + } + return; } else { // timer is too close to the end usec -= remain; - for (; TIMEBASE_TIMER->CNT >= start || TIMEBASE_TIMER->CNT < usec;); + while (1) { + register const uint32_t t = TIMEBASE_TIMER->CNT; + if (t < start && t >= usec) return; + } } } + +/** microsecond delay */ +void PTIM_MicroDelay(const uint32_t usec) +{ + PTIM_MicroDelayAligned(usec, TIMEBASE_TIMER->CNT); +} diff --git a/platform/timebase.h b/platform/timebase.h index 9286f14..255e154 100644 --- a/platform/timebase.h +++ b/platform/timebase.h @@ -13,6 +13,8 @@ #include "platform.h" +#define TIMEBASE_TIMER TIM17 + /** * Precision timer: get microtime as uint64_t * This timestamp should be monotonously increasing with a precision of ±0.5µs @@ -36,6 +38,24 @@ static inline uint32_t PTIM_GetTime(void) * * @param usec - max 998 */ -void PTIM_MicroDelay(uint16_t usec); +void PTIM_MicroDelay(uint32_t usec); + +/** + * Microsecond busy delay with alignment (reduced length jitter but possible up to 1 us delay) + * + * @param usec + */ +void PTIM_MicroDelayAligned(uint32_t usec, uint32_t start); + +/** + * Wait until the next micro timer tick + */ +static inline uint32_t PTIM_MicroDelayAlign(void) +{ + const uint32_t c = TIMEBASE_TIMER->CNT; + uint32_t res; + while ((res = TIMEBASE_TIMER->CNT) == c); + return res; +} #endif //GEX_F072_TIMEBASE_H diff --git a/units/1wire/unit_1wire.c b/units/1wire/unit_1wire.c index 9314ec9..2d8682c 100644 --- a/units/1wire/unit_1wire.c +++ b/units/1wire/unit_1wire.c @@ -89,7 +89,7 @@ enum PinCmd_ { CMD_SEARCH_CONTINUE = 3, // continue the previously started scan, retrieving more devices CMD_READ_ADDR = 4, // read the ROM code from a single device (for single-device bus) - CMD_WRITE = 10, // write multiple bytes using the SKIP_ROM command + CMD_QUERY = 10, // write multiple bytes using the SKIP_ROM command CMD_READ = 11, // write multiple bytes using a ROM address CMD_POLL_FOR_1 = 20, @@ -194,7 +194,7 @@ static error_t OW_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, Pay * addr:u64, rest:write_data * if addr is 0, use SKIP_ROM */ - case CMD_WRITE: + case CMD_QUERY: addr = pp_u64(pp); tail = pp_tail(pp, &remain); TRY(UU_1WIRE_Write(unit, addr, tail, remain)); diff --git a/units/digital_out/_dout_api.c b/units/digital_out/_dout_api.c index a7557a3..f297e55 100644 --- a/units/digital_out/_dout_api.c +++ b/units/digital_out/_dout_api.c @@ -7,8 +7,20 @@ #include "unit_dout.h" #define DOUT_INTERNAL + #include "_dout_internal.h" +static void clear_pulse_by_mask(struct priv *priv, uint16_t spread) +{ + assert_param(priv); + + for (int i = 0; i < 16; i++) { + if (spread & (1 << i)) { + priv->msec_pulse_cnt[i] = 0; + } + } +} + error_t UU_DOut_Write(Unit *unit, uint16_t packed) { CHECK_TYPE(unit, &UNIT_DOUT); @@ -16,6 +28,7 @@ error_t UU_DOut_Write(Unit *unit, uint16_t packed) struct priv *priv = unit->data; uint16_t mask = priv->pins; uint16_t spread = pinmask_spread(packed, mask); + clear_pulse_by_mask(priv, spread); uint16_t set = spread; uint16_t reset = ((~spread) & mask); @@ -30,6 +43,7 @@ error_t UU_DOut_Set(Unit *unit, uint16_t packed) struct priv *priv = unit->data; uint16_t mask = priv->pins; uint16_t spread = pinmask_spread(packed, mask); + clear_pulse_by_mask(priv, spread); priv->port->BSRR = spread; return E_SUCCESS; @@ -42,8 +56,9 @@ error_t UU_DOut_Clear(Unit *unit, uint16_t packed) struct priv *priv = unit->data; uint16_t mask = priv->pins; uint16_t spread = pinmask_spread(packed, mask); + clear_pulse_by_mask(priv, spread); - priv->port->BSRR = (spread<<16); + priv->port->BSRR = (spread << 16); return E_SUCCESS; } @@ -54,11 +69,12 @@ error_t UU_DOut_Toggle(Unit *unit, uint16_t packed) struct priv *priv = unit->data; uint16_t mask = priv->pins; uint16_t spread = pinmask_spread(packed, mask); + clear_pulse_by_mask(priv, spread); uint16_t flipped = (uint16_t) (~priv->port->ODR) & mask; uint16_t set = flipped & spread; uint16_t reset = ((~flipped) & mask) & spread; - priv->port->BSRR = set | (reset<<16); + priv->port->BSRR = set | (reset << 16); return E_SUCCESS; } @@ -68,6 +84,90 @@ error_t UU_DOut_GetPinCount(Unit *unit, uint8_t *count) struct priv *priv = unit->data; uint32_t packed = pinmask_pack(0xFFFF, priv->pins); - *count = (uint8_t)(32 - __CLZ(packed)); + *count = (uint8_t) (32 - __CLZ(packed)); return E_SUCCESS; } + +error_t UU_DOut_Pulse(Unit *unit, uint16_t packed, bool polarity, bool is_usec, uint16_t count) +{ + CHECK_TYPE(unit, &UNIT_DOUT); + struct priv *priv = unit->data; + assert_param(priv); + + uint16_t mask = priv->pins; + uint16_t spread = pinmask_spread(packed, mask); + clear_pulse_by_mask(priv, spread); + + vPortEnterCritical(); + + if (is_usec) { + // we're gonna do this right here as a delay loop. + if (count >= 1000) { + // too long, fall back to msec + count /= 1000; + is_usec = false; + } + else { + const uint32_t bsrr1 = spread << (polarity ? 0 : 16); + const uint32_t bsrr0 = spread << (polarity ? 16 : 0); + + const uint32_t start = PTIM_MicroDelayAlign(); + priv->port->BSRR = bsrr1; + PTIM_MicroDelayAligned(count, start); + priv->port->BSRR = bsrr0; + } + } + + if (!is_usec) { + // Load the counters + for (int i = 0; i < 16; i++) { + if (spread & (1 << i)) { + priv->msec_pulse_cnt[i] = (uint16_t) (count + 1); + } + } + + if (polarity) { + priv->msec_pulse_scheduled_1 |= spread; + } else { + priv->msec_pulse_scheduled_0 |= spread; + } + + unit->_tick_cnt = 0; + unit->tick_interval = 1; + } + + vPortExitCritical(); + + return E_SUCCESS; +} + +void DOut_Tick(Unit *unit) +{ + struct priv *priv = unit->data; + + uint16_t odr = (uint16_t) priv->port->ODR; + int live_cnt = 0; + for (int i = 0; i < 16; i++) { + if (priv->msec_pulse_scheduled_1 & (1<msec_pulse_scheduled_0 & (1<msec_pulse_cnt[i] > 0) { + live_cnt++; + priv->msec_pulse_cnt[i]--; + if (priv->msec_pulse_cnt[i] == 0) { + odr ^= 1 << i; + } + } + } + priv->port->ODR = odr; + priv->msec_pulse_scheduled_1 = 0; + priv->msec_pulse_scheduled_0 = 0; + + if (live_cnt == 0) { + unit->_tick_cnt = 0; + unit->tick_interval = 0; + } +} diff --git a/units/digital_out/_dout_internal.h b/units/digital_out/_dout_internal.h index 5c1888f..15a5fed 100644 --- a/units/digital_out/_dout_internal.h +++ b/units/digital_out/_dout_internal.h @@ -19,6 +19,9 @@ struct priv { uint16_t open_drain; // open drain pins GPIO_TypeDef *port; + uint16_t msec_pulse_cnt[16]; + uint16_t msec_pulse_scheduled_1; + uint16_t msec_pulse_scheduled_0; }; /** Allocate data structure and set defaults */ @@ -46,4 +49,6 @@ error_t DOut_init(Unit *unit); /** Tear down the unit */ void DOut_deInit(Unit *unit); +void DOut_Tick(Unit *unit); + #endif //GEX_F072_DOUT_INTERNAL_H diff --git a/units/digital_out/unit_dout.c b/units/digital_out/unit_dout.c index 04acee4..6db11c0 100644 --- a/units/digital_out/unit_dout.c +++ b/units/digital_out/unit_dout.c @@ -9,10 +9,11 @@ #include "_dout_internal.h" enum PinCmd_ { - CMD_TEST = 0, + CMD_QUERY = 0, CMD_SET = 1, CMD_CLEAR = 2, CMD_TOGGLE = 3, + CMD_PULSE = 4, }; /** Handle a request message */ @@ -21,7 +22,7 @@ static error_t DOut_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P uint16_t packed = pp_u16(pp); switch (command) { - case CMD_TEST: + case CMD_QUERY: return UU_DOut_Write(unit, packed); case CMD_SET: @@ -33,6 +34,12 @@ static error_t DOut_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P case CMD_TOGGLE: return UU_DOut_Toggle(unit, packed); + case CMD_PULSE:; + bool polarity = pp_bool(pp); + bool is_usec = pp_bool(pp); + uint16_t count = pp_u16(pp); + return UU_DOut_Pulse(unit, packed, polarity, is_usec, count); + default: return E_UNKNOWN_COMMAND; } @@ -55,4 +62,5 @@ const UnitDriver UNIT_DOUT = { .deInit = DOut_deInit, // Function .handleRequest = DOut_handleRequest, + .updateTick = DOut_Tick, }; diff --git a/units/digital_out/unit_dout.h b/units/digital_out/unit_dout.h index b14d1c7..3e66a50 100644 --- a/units/digital_out/unit_dout.h +++ b/units/digital_out/unit_dout.h @@ -56,4 +56,16 @@ error_t UU_DOut_Toggle(Unit *unit, uint16_t packed); */ error_t UU_DOut_GetPinCount(Unit *unit, uint8_t *count); +/** + * Send a pulse + * + * @param unit + * @param packed - pins to pulse + * @param polarity - pulse active level + * @param is_usec - use usec precision (for < 1 ms) + * @param count - number of units (msec or usec) + * @return success + */ +error_t UU_DOut_Pulse(Unit *unit, uint16_t packed, bool polarity, bool is_usec, uint16_t count); + #endif //U_DOUT_H diff --git a/units/i2c/unit_i2c.c b/units/i2c/unit_i2c.c index 7176578..9088cf3 100644 --- a/units/i2c/unit_i2c.c +++ b/units/i2c/unit_i2c.c @@ -12,7 +12,7 @@ #include "_i2c_internal.h" enum PinCmd_ { - CMD_TEST = 0, + CMD_QUERY = 0, CMD_READ = 1, CMD_WRITE_REG = 2, CMD_READ_REG = 3, @@ -30,7 +30,7 @@ static error_t UI2C_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P switch (command) { /** Write byte(s) - addr:u16, byte(s) */ - case CMD_TEST: + case CMD_QUERY: addr = pp_u16(pp); const uint8_t *bb = pp_tail(pp, &len); diff --git a/units/sipo/unit_sipo.c b/units/sipo/unit_sipo.c index d48cb1b..5c57b47 100644 --- a/units/sipo/unit_sipo.c +++ b/units/sipo/unit_sipo.c @@ -12,7 +12,7 @@ // ------------------------------------------------------------------------ enum SipoCmd_ { - CMD_WRITE = 0, + CMD_QUERY = 0, CMD_DIRECT_DATA = 1, CMD_DIRECT_SHIFT = 2, CMD_DIRECT_CLEAR = 3, @@ -23,7 +23,7 @@ enum SipoCmd_ { static error_t USIPO_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) { switch (command) { - case CMD_WRITE: + case CMD_QUERY: { uint32_t len; uint16_t terminal_packed = pp_u16(pp); diff --git a/units/spi/unit_spi.c b/units/spi/unit_spi.c index c05c771..a432058 100644 --- a/units/spi/unit_spi.c +++ b/units/spi/unit_spi.c @@ -15,7 +15,7 @@ // SPI master enum PinCmd_ { - CMD_TEST = 0, + CMD_QUERY = 0, CMD_MULTICAST = 1, }; @@ -33,7 +33,7 @@ static error_t USPI_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, P switch (command) { /** Write and read byte(s) - slave_num:u8, req_len:u16, resp_skip:u16, resp_len:u16, byte(s) */ - case CMD_TEST: + case CMD_QUERY: slave = pp_u8(pp); resp_skip = pp_u16(pp); resp_len = pp_u16(pp); diff --git a/units/usart/unit_usart.c b/units/usart/unit_usart.c index e8aca86..f8b8f0f 100644 --- a/units/usart/unit_usart.c +++ b/units/usart/unit_usart.c @@ -92,7 +92,7 @@ void UUSART_Tick(Unit *unit) } enum PinCmd_ { - CMD_WRITE = 0, + CMD_QUERY = 0, CMD_WRITE_SYNC = 1, }; @@ -103,7 +103,7 @@ static error_t UUSART_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, const uint8_t *pld; switch (command) { /** Write bytes to the USART. Payload consists of the data to send. Waits for completion. */ - case CMD_WRITE: + case CMD_QUERY: pld = pp_tail(pp, &len); TRY(UU_USART_Write(unit, pld, len)); return E_SUCCESS;