From 393ec1e9514fd546178b68fdc2054e9f404e3032 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Hru=C5=A1ka?= Date: Thu, 28 Dec 2017 13:52:04 +0100 Subject: [PATCH] reworking pins, flash doesnt work --- gex.mk | 3 +- platform/lock_jumper.c | 6 +- platform/pin_utils.c | 181 ++++++++++++++++++++-- platform/pin_utils.h | 36 ++++- platform/plat_compat.h | 4 +- platform/platform.c | 4 +- platform/status_led.c | 6 +- units/digital_out/unit_dout.c | 265 +++++++++++++++++++++++++++++++++ units/digital_out/unit_dout.h | 12 ++ units/neopixel/unit_neopixel.c | 8 +- units/pin/unit_pin.c | 8 +- utils/str_utils.c | 27 +--- utils/str_utils.h | 65 +++++++- 13 files changed, 570 insertions(+), 55 deletions(-) create mode 100644 units/digital_out/unit_dout.c create mode 100644 units/digital_out/unit_dout.h diff --git a/gex.mk b/gex.mk index adb4cca..5a5c35a 100644 --- a/gex.mk +++ b/gex.mk @@ -9,6 +9,7 @@ GEX_SRC_DIR = \ User/units/neopixel \ User/units/test \ User/units/pin \ + User/units/digital_out \ User/TinyFrame \ User/CWPack \ User/tasks @@ -80,7 +81,7 @@ GEX_CDEFS = $(GEX_CDEFS_BASE) \ -DUSE_FULL_ASSERT=1 \ -DVERBOSE_ASSERT=1 \ -DDEBUG_VFS=0 \ - -DDEBUG_FLASH_WRITE=0 \ + -DDEBUG_FLASH_WRITE=1 \ -DVERBOSE_HARDFAULT=1 \ -DUSE_STACK_MONITOR=1 \ -DUSE_DEBUG_UART=1 diff --git a/platform/lock_jumper.c b/platform/lock_jumper.c index 42c12a2..a811519 100644 --- a/platform/lock_jumper.c +++ b/platform/lock_jumper.c @@ -24,15 +24,15 @@ void LockJumper_Init(void) bool suc = true; // Resolve and claim resource - Resource rsc = plat_pin2resource(LOCK_JUMPER_PORT, LOCK_JUMPER_PIN, &suc); + Resource rsc = pin2resource(LOCK_JUMPER_PORT, LOCK_JUMPER_PIN, &suc); assert_param(suc); suc &= rsc_claim(&UNIT_SYSTEM, rsc); assert_param(suc); // Resolve pin - lock_periph = plat_port2periph(LOCK_JUMPER_PORT, &suc); - lock_llpin = plat_pin2ll(LOCK_JUMPER_PIN, &suc); + lock_periph = port2periph(LOCK_JUMPER_PORT, &suc); + lock_llpin = pin2ll(LOCK_JUMPER_PIN, &suc); assert_param(suc); // Configure for input diff --git a/platform/pin_utils.c b/platform/pin_utils.c index 47cae46..1ccd742 100644 --- a/platform/pin_utils.c +++ b/platform/pin_utils.c @@ -2,6 +2,7 @@ // Created by MightyPork on 2017/11/26. // +#include #include "pin_utils.h" #include "macro.h" @@ -45,7 +46,7 @@ static GPIO_TypeDef * const port_periphs[] = { COMPILER_ASSERT(PORTS_COUNT == ELEMENTS_IN_ARRAY(port_periphs)); /** Convert pin number to LL bitfield */ -uint32_t plat_pin2ll(uint8_t pin_number, bool *suc) +uint32_t pin2ll(uint8_t pin_number, bool *suc) { assert_param(suc != NULL); @@ -59,13 +60,12 @@ uint32_t plat_pin2ll(uint8_t pin_number, bool *suc) } /** Convert port name (A,B,C...) to peripheral struct pointer */ -GPIO_TypeDef *plat_port2periph(char port_name, bool *suc) +GPIO_TypeDef *port2periph(char port_name, bool *suc) { assert_param(suc != NULL); if(port_name < 'A' || port_name >= ('A'+PORTS_COUNT)) { - dbg("Bad port: %c", port_name); - // TODO proper report + dbg("Bad port: %c", port_name); // TODO proper report *suc = false; return NULL; } @@ -75,20 +75,18 @@ GPIO_TypeDef *plat_port2periph(char port_name, bool *suc) } /** Convert a pin to resource handle */ -Resource plat_pin2resource(char port_name, uint8_t pin_number, bool *suc) +Resource pin2resource(char port_name, uint8_t pin_number, bool *suc) { assert_param(suc != NULL); if(port_name < 'A' || port_name >= ('A'+PORTS_COUNT)) { - dbg("Bad port: %c", port_name); - // TODO proper report + dbg("Bad port: %c", port_name); // TODO proper report *suc = false; return R_NONE; } if(pin_number >= PINS_COUNT) { - // TODO proper report - dbg("Bad pin: %d", pin_number); + dbg("Bad pin: %d", pin_number); // TODO proper report *suc = false; return R_NONE; } @@ -97,3 +95,168 @@ Resource plat_pin2resource(char port_name, uint8_t pin_number, bool *suc) return R_PA0 + num*16 + pin_number; } + +/** Parse single pin */ +bool parse_pin(const char *value, char *targetName, uint8_t *targetNumber) +{ + // discard leading 'P' + if (value[0] == 'P') { + value++; + } + + size_t len = strlen(value); + if (len<2||len>3) return false; + + *targetName = (uint8_t) value[0]; + if (!(*targetName >= 'A' && *targetName <= 'H')) return false; + + // lets just hope it's OK + *targetNumber = (uint8_t) avr_atoi(value + 1); + return true; +} + +/** Parse port name */ +bool parse_port(const char *value, char *targetName) +{ + *targetName = (uint8_t) value[0]; + if (!(*targetName >= 'A' && *targetName < 'A' + PORTS_COUNT)) return false; + return true; +} + +/** Parse a list of pin numbers with ranges and commans/semicolons to a bitmask */ +uint16_t parse_pinmask(const char *value, bool *suc) +{ + uint32_t bits = 0; + uint32_t acu = 0; + bool inrange = false; + uint32_t rangestart = 0; + + // shortcut if none are set + if (value[0] == 0) return 0; + + char c; + do { + c = *value++; + if (c == ' ' || c == '\t') { + // skip + } + else if (c >= '0' && c <= '9') { + acu = acu*10 + (c-'0'); + } + else if (c == ',' || c == ';' || c == 0) { + // end of number or range + if (!inrange) rangestart = acu; + + // swap them if they're in the wrong order + if (acu < rangestart) { + uint32_t swp = acu; + acu = rangestart; + rangestart = swp; + } + + for(uint32_t i=rangestart; i<=acu; i++) { + bits |= 1< 0xFFFF) *suc = false; + + return (uint16_t) bits; +} + +/** Convert a pin bitmask to the ASCII format understood by str_parse_pinmask() */ +char * str_pinmask(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 = 15; i >= -1; i--) { + bool bit; + + if (i == -1) { + bit = false; + } else { + bit = 0 != (pins & 0x8000); + 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 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; +} + +/** Spread packed port pins using a mask */ +uint16_t port_spread(uint16_t packed, uint16_t mask) +{ + uint16_t result = 0; + uint16_t poke = 1; + for (int i = 0; i<16; i++) { + if (mask & (1<data; + + priv->port_name = pp_char(pp); + priv->pins = pp_u16(pp); + priv->initial = pp_u16(pp); + priv->open_drain = pp_u16(pp); +} + +/** Write to a binary buffer for storing in Flash */ +static void DO_writeBinary(Unit *unit, PayloadBuilder *pb) +{ + struct priv *priv = unit->data; + + pb_char(pb, priv->port_name); + pb_u16(pb, priv->pins); + pb_u16(pb, priv->initial); + pb_u16(pb, priv->open_drain); +} + +// ------------------------------------------------------------------------ + +/** Parse a key-value pair from the INI file */ +static bool DO_loadIni(Unit *unit, const char *key, const char *value) +{ + bool suc = true; + struct priv *priv = unit->data; + + if (streq(key, "port")) { + suc = parse_port(value, &priv->port_name); + } + else if (streq(key, "pins")) { + priv->pins = parse_pinmask(value, &suc); + } + else if (streq(key, "initial")) { + priv->initial = parse_pinmask(value, &suc); + } + else if (streq(key, "opendrain")) { + priv->open_drain = parse_pinmask(value, &suc); + } + else { + return false; + } + + return suc; +} + +/** Generate INI file section for the unit */ +static void DO_writeIni(Unit *unit, IniWriter *iw) +{ + struct priv *priv = unit->data; + char buf[64]; + + iw_comment(iw, "Port name"); + iw_entry(iw, "port", "%c", priv->port_name); + + iw_comment(iw, "Pins (descending, csv, ranges using colon)"); + iw_entry(iw, "pins", "%s", str_pinmask(priv->pins, buf)); + + iw_comment(iw, "Initially high pins"); + iw_entry(iw, "initial", "%s", str_pinmask(priv->initial, buf)); + + iw_comment(iw, "Open-drain pins"); + iw_entry(iw, "opendrain", "%s", str_pinmask(priv->open_drain, buf)); +} + +// ------------------------------------------------------------------------ + +/** Allocate data structure and set defaults */ +static bool DO_preInit(Unit *unit) +{ + bool suc = true; + struct priv *priv = unit->data = calloc_ck(1, sizeof(struct priv), &suc); + CHECK_SUC(); + + // some defaults + priv->port_name = 'A'; + priv->pins = 0x0001; + priv->open_drain = 0x0000; + priv->initial = 0x0000; + + return true; +} + +/** Finalize unit set-up */ +static bool DO_init(Unit *unit) +{ + bool suc = true; + struct priv *priv = unit->data; + + priv->initial &= priv->pins; + priv->open_drain &= priv->pins; + + // --- Parse config --- + priv->port = port2periph(priv->port_name, &suc); + if (!suc) { + unit->status = E_BAD_CONFIG; + return false; + } + + dbg("Claim rsc"); + + // Claim all needed pins + for (int i = 0; i < 16; i++) { + if (priv->pins & (1 << i)) { + Resource rsc = pin2resource(priv->port_name, (uint8_t) i, &suc); + if (!suc) { + unit->status = E_BAD_CONFIG; + + rsc_teardown(unit); + return false; + } + + suc = rsc_claim(unit, rsc); + if (!suc) { + rsc_teardown(unit); + return false; + } + } + } + + dbg("Init pins"); + + uint16_t mask = 1; + for (int i = 0; i < 16; i++, mask <<= 1) { + if (priv->pins & mask) { + uint32_t ll_pin = pin2ll((uint8_t) i, &suc); + + // --- Init hardware --- + LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_OUTPUT); + + LL_GPIO_SetPinOutputType(priv->port, ll_pin, (priv->open_drain & mask) ? + LL_GPIO_OUTPUT_OPENDRAIN : + LL_GPIO_OUTPUT_PUSHPULL); + + LL_GPIO_SetPinSpeed(priv->port, ll_pin, LL_GPIO_SPEED_FREQ_HIGH); + } + } + + // Set the initial state + priv->port->ODR &= ~priv->pins; + priv->port->ODR |= priv->initial; + + return true; +} + +/** Tear down the unit */ +static void DO_deInit(Unit *unit) +{ + struct priv *priv = unit->data; + + bool suc = true; + uint16_t mask = 1; + for (int i = 0; i < 16; i++, mask <<= 1) { + if (priv->pins & mask) { + + uint32_t ll_pin = pin2ll((uint8_t) i, &suc); + assert_param(suc); // this should never fail if we got this far + + // configure the pin as analog + LL_GPIO_SetPinMode(priv->port, ll_pin, LL_GPIO_MODE_ANALOG); + } + } + + // Release all resources + rsc_teardown(unit); + + // Free memory + free(unit->data); + unit->data = NULL; +} + +// ------------------------------------------------------------------------ + +enum PinCmd_ { + CMD_WRITE = 0, + CMD_SET = 1, + CMD_CLEAR = 2, + CMD_TOGGLE = 3, +}; + +/** Handle a request message */ +static bool DO_handleRequest(Unit *unit, TF_ID frame_id, uint8_t command, PayloadParser *pp) +{ + (void)pp; + + struct priv *priv = unit->data; + + uint16_t packed = pp_u16(pp); + + uint16_t mask = priv->pins; + uint16_t spread = port_spread(packed, mask); + + uint16_t set, reset; + + switch (command) { + case CMD_WRITE:; + set = spread; + reset = (~spread) & mask; + priv->port->BSRR = set | (reset<<16); + break; + + case CMD_SET: + priv->port->BSRR = spread; + break; + + case CMD_CLEAR: + priv->port->BSRR = (spread<<16); + break; + + case CMD_TOGGLE:; + spread = (uint16_t) (~priv->port->ODR) & mask; + set = spread; + reset = (~spread) & mask; + priv->port->BSRR = set | (reset<<16); + break; + + default: + com_respond_bad_cmd(frame_id); + return false; + } + + return true; +} + +// ------------------------------------------------------------------------ + +/** Unit template */ +const UnitDriver UNIT_DOUT = { + .name = "DO", + .description = "Digital output", + // Settings + .preInit = DO_preInit, + .cfgLoadBinary = DO_loadBinary, + .cfgWriteBinary = DO_writeBinary, + .cfgLoadIni = DO_loadIni, + .cfgWriteIni = DO_writeIni, + // Init + .init = DO_init, + .deInit = DO_deInit, + // Function + .handleRequest = DO_handleRequest, +}; diff --git a/units/digital_out/unit_dout.h b/units/digital_out/unit_dout.h new file mode 100644 index 0000000..5eada10 --- /dev/null +++ b/units/digital_out/unit_dout.h @@ -0,0 +1,12 @@ +// +// Created by MightyPork on 2017/11/25. +// + +#ifndef U_DOUT_H +#define U_DOUT_H + +#include "unit.h" + +extern const UnitDriver UNIT_DOUT; + +#endif //U_DOUT_H diff --git a/units/neopixel/unit_neopixel.c b/units/neopixel/unit_neopixel.c index 3610590..a6ce3b2 100644 --- a/units/neopixel/unit_neopixel.c +++ b/units/neopixel/unit_neopixel.c @@ -49,7 +49,7 @@ static bool Npx_loadIni(Unit *unit, const char *key, const char *value) struct priv *priv = unit->data; if (streq(key, "pin")) { - suc = str_parse_pin(value, &priv->port_name, &priv->pin_number); + suc = parse_pin(value, &priv->port_name, &priv->pin_number); } else if (streq(key, "pixels")) { priv->pixels = (uint16_t) avr_atoi(value); @@ -97,9 +97,9 @@ static bool Npx_init(Unit *unit) struct priv *priv = unit->data; // --- Parse config --- - priv->ll_pin = plat_pin2ll(priv->pin_number, &suc); - priv->port = plat_port2periph(priv->port_name, &suc); - Resource rsc = plat_pin2resource(priv->port_name, priv->pin_number, &suc); + priv->ll_pin = pin2ll(priv->pin_number, &suc); + priv->port = port2periph(priv->port_name, &suc); + Resource rsc = pin2resource(priv->port_name, priv->pin_number, &suc); if (!suc) { unit->status = E_BAD_CONFIG; return false; diff --git a/units/pin/unit_pin.c b/units/pin/unit_pin.c index fb5a299..578e148 100644 --- a/units/pin/unit_pin.c +++ b/units/pin/unit_pin.c @@ -53,7 +53,7 @@ static bool Pin_loadIni(Unit *unit, const char *key, const char *value) struct priv *priv = unit->data; if (streq(key, "pin")) { - suc = str_parse_pin(value, &priv->port_name, &priv->pin_number); + suc = parse_pin(value, &priv->port_name, &priv->pin_number); } else if (streq(key, "dir")) { priv->output = str_parse_01(value, "IN", "OUT", &suc); @@ -114,9 +114,9 @@ static bool Pin_init(Unit *unit) struct priv *priv = unit->data; // --- Parse config --- - priv->ll_pin = plat_pin2ll(priv->pin_number, &suc); - priv->port = plat_port2periph(priv->port_name, &suc); - Resource rsc = plat_pin2resource(priv->port_name, priv->pin_number, &suc); + priv->ll_pin = pin2ll(priv->pin_number, &suc); + priv->port = port2periph(priv->port_name, &suc); + Resource rsc = pin2resource(priv->port_name, priv->pin_number, &suc); if (!suc) { unit->status = E_BAD_CONFIG; return false; diff --git a/utils/str_utils.c b/utils/str_utils.c index 8a27f92..b0fb104 100644 --- a/utils/str_utils.c +++ b/utils/str_utils.c @@ -3,16 +3,19 @@ // #include "str_utils.h" +#include "platform.h" #include "avrlibc.h" bool str_parse_yn(const char *str, bool *suc) { + // TODO implement strcasecmp without the locale crap from newlib and use it here if (streq(str, "Y")) return true; - if (streq(str, "1")) return true; - if (streq(str, "YES")) return true; - if (streq(str, "N")) return false; + + if (streq(str, "1")) return true; if (streq(str, "0")) return false; + + if (streq(str, "YES")) return true; if (streq(str, "NO")) return false; *suc = false; @@ -37,21 +40,3 @@ uint8_t str_parse_012(const char *str, const char *a, const char *b, const char *suc = false; return 0; } - -bool str_parse_pin(const char *value, char *targetName, uint8_t *targetNumber) -{ - // discard leading 'P' - if (value[0] == 'P') { - value++; - } - - size_t len = strlen(value); - if (len<2||len>3) return false; - - *targetName = (uint8_t) value[0]; - if (!(*targetName >= 'A' && *targetName <= 'H')) return false; - - // lets just hope it's OK - *targetNumber = (uint8_t) avr_atoi(value + 1); - return true; -} diff --git a/utils/str_utils.h b/utils/str_utils.h index c72c3cb..bffd070 100644 --- a/utils/str_utils.h +++ b/utils/str_utils.h @@ -5,55 +5,112 @@ #include #include "stringbuilder.h" +/** + * Test equality two strings + * + * @param str1 + * @param str2 + * @param limit + * @return + */ static inline bool __attribute__((const)) streq(const char *restrict str1, const char *restrict str2) { return strcmp(str1, str2) == 0; } +/** + * Test equality of two strings, case insensitive + * + * @param str1 + * @param str2 + * @param limit + * @return + */ static inline bool __attribute__((const)) strcaseq(const char *restrict str1, const char *restrict str2) { return strcasecmp(str1, str2) == 0; } +/** + * Test if a string starts with another + * + * @param str + * @param prefix + * @param limit + * @return + */ static inline bool __attribute__((const)) -strneq(const char *restrict str1, const char *restrict str2, uint32_t limit) +strneq(const char *restrict str, const char *restrict prefix, uint32_t limit) { - return strncmp(str1, str2, limit) == 0; + return strncmp(str, prefix, limit) == 0; } +/** + * Test if a string starts with another, case insensitive + * + * @param str1 + * @param str2 + * @return match + */ static inline bool __attribute__((const)) strncaseq(const char *restrict str1, const char *restrict str2, uint32_t limit) { return strncasecmp(str1, str2, limit) == 0; } +/** + * Test if a string starts with another + * + * @param str + * @param prefix + * @return match + */ static inline bool __attribute__((const)) strstarts(const char *restrict str, const char *restrict prefix) { return strncmp(str, prefix, strlen(prefix)) == 0; } +/** + * Get n-th character from the end + * + * @param str + * @param nth - n-th character (1 means last char) + * @return the char + */ static inline char __attribute__((const)) last_char_n(const char *str, uint32_t nth) { return str[strlen(str) - nth]; } +/** + * Get last character of a string + * + * @param str + * @return last char + */ static inline char __attribute__((const)) last_char(const char *str) { return str[strlen(str) - 1]; } +/** Parse Y/N to bool */ bool str_parse_yn(const char *str, bool *suc); + +/** Compare string with two options */ uint8_t str_parse_01(const char *str, const char *a, const char *b, bool *suc); + +/** Compare string with three options */ uint8_t str_parse_012(const char *str, const char *a, const char *b, const char *c, bool *suc); +/** Convert bool to one of two options */ #define str_01(cond, a, b) ((cond) ? (b) : (a)) -#define str_yn(cond) ((cond) ? ("Y") : ("N")) -bool str_parse_pin(const char *str, char *targetName, uint8_t *targetNumber); +/** Convert bool to Y or N */ +#define str_yn(cond) ((cond) ? ("Y") : ("N")) #endif