From 0a260fc1d4fef0f9c9df5b2c73f2afc9bc736189 Mon Sep 17 00:00:00 2001 From: PropGit Date: Wed, 19 Aug 2020 10:09:28 -0700 Subject: [PATCH] Added Jon McPhalen's Full Duplex Serial object. --- docs/p2.md | 1 + .../p2/All/jm_full_duplex_serial/README.md | 14 + .../jm_format_strings_demo.spin2 | 180 +++++ .../jm_fullduplexserial.spin2 | 712 ++++++++++++++++++ .../All/jm_full_duplex_serial/jm_nstr.spin2 | 240 ++++++ libraries/community/p2/Protocol/README.md | 2 + 6 files changed, 1149 insertions(+) create mode 100644 libraries/community/p2/All/jm_full_duplex_serial/README.md create mode 100644 libraries/community/p2/All/jm_full_duplex_serial/jm_format_strings_demo.spin2 create mode 100644 libraries/community/p2/All/jm_full_duplex_serial/jm_fullduplexserial.spin2 create mode 100644 libraries/community/p2/All/jm_full_duplex_serial/jm_nstr.spin2 diff --git a/docs/p2.md b/docs/p2.md index bdb275b9..1c8dd5b9 100644 --- a/docs/p2.md +++ b/docs/p2.md @@ -180,6 +180,7 @@ by Dave Hein ## Recent Activity ##### _Full details [here](https://github.com/parallaxinc/propeller/pulls?q=is%3Aclosed)._ +* Added Jon "JonnyMac" McPhalen's object [Full Duplex Serial](https://github.com/parallaxinc/propeller/tree/master/libraries/community/p2/All/jm_full_duplex_serial) * Added [latest newsletter](https://propeller.parallax.com/p2.html#p2-community-newsletter) about upcoming P2 Live Forums. * Added Eric R Smith's objects * [ANSI VGA Text](https://github.com/parallaxinc/propeller/tree/master/libraries/community/p2/All/ansi_vgatext) diff --git a/libraries/community/p2/All/jm_full_duplex_serial/README.md b/libraries/community/p2/All/jm_full_duplex_serial/README.md new file mode 100644 index 00000000..aa9e108c --- /dev/null +++ b/libraries/community/p2/All/jm_full_duplex_serial/README.md @@ -0,0 +1,14 @@ +# Full Duplex Serial + +By: Jon "JonnyMac" McPhalen + +Language: Spin2 and PASM2 + +Created: 19-AUG-2020 + +Category: protocol + +Description: +This object is a P2 update to FullDuplexSerial for the P1. There are minor changes and additions to the numeric output methods, and the inclusion of string formatting for complex output as is available in other languages. + +License: MIT (see end of source code) diff --git a/libraries/community/p2/All/jm_full_duplex_serial/jm_format_strings_demo.spin2 b/libraries/community/p2/All/jm_full_duplex_serial/jm_format_strings_demo.spin2 new file mode 100644 index 00000000..77da7390 --- /dev/null +++ b/libraries/community/p2/All/jm_full_duplex_serial/jm_format_strings_demo.spin2 @@ -0,0 +1,180 @@ +'' ================================================================================================= +'' +'' File....... jm_formatted_strings_test.spin2 +'' Purpose.... +'' Author..... Jon "JonnyMac" McPhalen +'' Copyright (c) 2020 Jon McPhalen +'' -- see below for terms of use +'' E-mail..... jon.mcphalen@gmail.com +'' Started.... +'' Updated.... 19 AUG 2020 +'' +'' ================================================================================================= + + +con { timing } + + CLK_FREQ = 200_000_000 ' system freq as a constant + MS_001 = CLK_FREQ / 1_000 ' ticks in 1ms + US_001 = CLK_FREQ / 1_000_000 ' ticks in 1us + + BR_TERM = 115_200 ' terminal baud rate + + _clkfreq = CLK_FREQ ' set system clock + + +con { fixed io pins } + + RX1 = 63 { I } ' programming / debug + TX1 = 62 { O } + + FS_CS = 61 { O } ' flash storage + FS_SCLK = 60 { O } + FS_MOSI = 59 { O } + FS_MISO = 58 { I } + + SD_SCLK = 61 { O } ' usd card storage + SD_CS = 60 { O } + SD_MOSI = 59 { O } + SD_MISO = 58 { I } + + SDA1 = 57 { IO } ' i2c (optional) + SCL1 = 56 { IO } + + +con + + BUF_SIZE = 32 + + +obj + +' main ' * master Spin cog + term : "jm_fullduplexserial" ' * serial IO for terminal + + +var + + byte buffer[BUF_SIZE] + + +dat + + Device byte "P2X8C4M64P", 0 + + +pub main() | x, y + + setup() + + wait_for_terminal(true) + + term.fstr1(string("%s Formatted Strings Demo\r"), @Device) + term.fstr1(string("%033c\r\r"), "-") + + term.fstr0(string("Please enter your name: ")) + get_str(BUF_SIZE-2) + term.fstr1(string("\r\rHello, %s, let me show you some \rformatted strings...\r\r"), @buffer) + waitms(1000) + + ' use \nnn for arbitrary character + ' -- \176 is the degrees symbol in PST + + x := 23 + term.fstr2(string("%d\176C --> %d\176F\r\r"), x, x * 9 / 5 + 32) + + ' negative width fields are left justified + ' positive width fields are right justified + + x := 123 + repeat 6 + term.fstr2(string("%-10d %13.3f\r"), x, x) + x *= 10 + waitms(5) + + term.txn(13, 2) + + ' fixed field widths with leading spaces + + term.fstr0(string("DEC HEX OCT QRT BIN\r")) + term.fstr0(string("--- --- --- --- ----\r")) + + repeat x from 0 to 15 + term.fstr5(string("%3d %3x %3o %3q %4b\r"), x, x, x, x, x) + waitms(5) + + term.txn(13, 2) + + ' fixed field width with leading 0s + + term.fstr0(string("DEC HEX OCT QRT BIN\r")) + term.fstr0(string("--- --- --- --- ----\r")) + + repeat x from 0 to 15 + term.fstr5(string("%0.3d %0.3x %0.3o %0.3q %0.4b\r"), x, x, x, x, x) + waitms(5) + + repeat + waitct(0) + + +pub get_str(maxlen) : len | k + + bytefill(@buffer, 0, BUF_SIZE) ' clear input buffer + + term.rxflush() ' clear trash from terminal + + repeat + k := term.rx() ' wait for a character + case k + 31..127 : ' if valid + if (len < maxlen) ' and room + buffer[len++] := k ' add to buffer + + term.BKSP : + if (len > 0) ' if character(s) in buffer + buffer[--len] := 0 ' backup and erase last + + term.CR : + buffer[len] := 0 ' terminate string + return ' and return to caller + + +pub wait_for_terminal(clear) + + term.rxflush() + term.rx() ' wait for keypress + if (clear) + term.tx(term.CLS) + + +pub setup() + + term.start(RX1, TX1, %0000, BR_TERM) ' start terminal serial + + +con { license } + +{{ + + MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +}} \ No newline at end of file diff --git a/libraries/community/p2/All/jm_full_duplex_serial/jm_fullduplexserial.spin2 b/libraries/community/p2/All/jm_full_duplex_serial/jm_fullduplexserial.spin2 new file mode 100644 index 00000000..f53f6528 --- /dev/null +++ b/libraries/community/p2/All/jm_full_duplex_serial/jm_fullduplexserial.spin2 @@ -0,0 +1,712 @@ +'' ================================================================================================= +'' +'' File....... jm_fullduplexserial.spin2 +'' Purpose.... Buffered serial communications using smart pins +'' -- mostly matches FullDuplexSerial from P1 +'' -- does NOT support half-duplex communications using shared RX/TX pin +'' Authors.... Jon McPhalen +'' -- based on work by Chip Gracey +'' -- see below for terms of use +'' E-mail..... jon.mcphalen@gmail.com +'' Started.... +'' Updated.... 19 AUG 2020 +'' +'' ================================================================================================= + +{{ + + Note: Buffer size no longer has to be power-of-2 integer. + + Note: The dec(), bin(), and hex() methods will no longer require the digits parameter as + in older versions of FullDuplexSerial. Use fdec(), fbin(), and fhex() for code that + requires a specific field width. + + + The smart pin uarts use a 16-bit value for baud timing which can limit low baud rates for + some system frequencies -- beware of these limits when connecting to older devices. + + Baud 20MHz 40MHz 80MHz 100MHz 200MHz 300MHz + ------ ----- ----- ----- ------ ------ ------ + 300 No No No No No No + 600 Yes No No No No No + 1200 Yes Yes No No No No + 2400 Yes Yes Yes Yes No No + 4800 Yes Yes Yes Yes Yes Yes + +}} + + +con { fixed io pins } + + RX1 = 63 { I } ' programming / debug + TX1 = 62 { O } + + SF_CS = 61 { O } ' serial flash + SF_SCK = 60 { O } + SF_SDO = 59 { O } + SF_SDI = 58 { I } + + +con { pst formatting } + + HOME = 1 + CRSR_XY = 2 + CRSR_LF = 3 + CRSR_RT = 4 + CRSR_UP = 5 + CRSR_DN = 6 + BELL = 7 + BKSP = 8 + TAB = 9 + LF = 10 + CLR_EOL = 11 + CLR_DN = 12 + CR = 13 + CRSR_X = 14 + CRSR_Y = 15 + CLS = 16 + + +con + + BUF_SIZE = 64 + + +obj + + nstr : "jm_nstr" ' number-to-string + + +var + + long cog ' cog flag/id + + long rxp ' rx smart pin + long txp ' tx smart pin + long rxhub ' hub address of rxbuf + long txhub ' hub address of txbuf + + long rxhead ' rx head index + long rxtail ' rx tail index + long txhead ' tx head index + long txtail ' tx tail index + + long txdelay ' ticks to transmit one byte + + byte rxbuf[BUF_SIZE] ' buffers + byte txbuf[BUF_SIZE] + + byte pbuf[80] ' padded strings + + +pub null() + +'' This is not a top level object + + +pub start(rxpin, txpin, mode, baud) : result | baudcfg, spmode + +'' Start simple serial coms on rxpin and txpin at baud +'' -- rxpin... receive pin (-1 if not used) +'' -- txpin... transmit pin (-1 if not used) +'' -- mode.... %0xx1 = invert rx +'' %0x1x = invert tx +'' %01xx = open-drain/open-source tx + + stop() + + if (rxpin == txpin) ' pin must be unique + return false + + longmove(@rxp, @rxpin, 2) ' save pins + rxhub := @rxbuf ' point to buffers + txhub := @txbuf + + txdelay := clkfreq / baud * 11 ' tix to transmit one byte + + baudcfg := muldiv64(clkfreq, $1_0000, baud) & $FFFFFC00 ' set bit timing + baudcfg |= (8-1) ' set bits (8) + + if (rxp >= 0) ' configure rx pin if used + spmode := P_ASYNC_RX + if (mode.[0]) + spmode |= P_INVERT_IN + pinstart(rxp, spmode, baudcfg, 0) + + if (txp >= 0) ' configure tx pin if used + spmode := P_ASYNC_TX | P_OE + case mode.[2..1] + %01 : spmode |= P_INVERT_OUTPUT + %10 : spmode |= P_HIGH_FLOAT ' requires external pull-up + %11 : spmode |= P_INVERT_OUTPUT | P_LOW_FLOAT ' requires external pull-down + pinstart(txp, spmode, baudcfg, 0) + + cog := coginit(COGEXEC_NEW, @uart_mgr, @rxp) + 1 ' start uart manager cog + + return cog + + +pub stop() + +'' Stop serial driver +'' -- frees a cog if driver was running + + if (cog) ' cog active? + cogstop(cog-1) ' yes, shut it down + cog := 0 ' and mark stopped + + longfill(@rxp, -1, 2) ' reset object globals + longfill(@rxhub, 0, 7) + + +pub rx() : b + +'' Pulls byte from receive buffer if available +'' -- will wait if buffer is empty + + repeat while (rxtail == rxhead) ' hold while buffer empty + + b := rxbuf[rxtail] ' get a byte + if (++rxtail == BUF_SIZE) ' update tail pointer + rxtail := 0 + + +pub rxcheck() : b + +'' Pulls byte from receive buffer if available +'' -- returns -1 if buffer is empty + + if (rxtail <> rxhead) ' something in buffer? + b := rxbuf[rxtail] ' get it + if (++rxtail == BUF_SIZE) ' update tail pointer + rxtail := 0 + else + b := -1 ' mark no byte available + + +pub rxtime(ms) : b | mstix, t + +'' Wait ms milliseconds for a byte to be received +'' -- returns -1 if no byte received, $00..$FF if byte + + mstix := clkfreq / 1000 + + t := getct() + repeat until ((b := rxcheck()) >= 0) || (((getct()-t) / mstix) >= ms) + + +pub rxtix(tix) : b | t + +'' Waits tix clock ticks for a byte to be received +'' -- returns -1 if no byte received + + t := getct() + repeat until ((b := rxcheck()) >= 0) || ((getct()-t) >= tix) + + +pub available() : count + +'' Returns # of bytes waiting in rx buffer + + if (rxtail <> rxhead) ' if byte(s) available + count := rxhead - rxtail ' get count + if (count < 0) + count += BUF_SIZE ' fix for wrap around + + +pub rxflush() + +'' Flush receive buffer + + repeat while (rxcheck() >= 0) + + +pub tx(b) | n + +'' Move byte into transmit buffer if room is available +'' -- will wait if buffer is full + + repeat + n := txhead - txtail ' bytes in buffer + if (n < 0) ' fix for index wrap-around + n += BUF_SIZE + if (n < BUF_SIZE-1) + quit + + txbuf[txhead] := b ' move to buffer + if (++txhead == BUF_SIZE) ' update head pointer + txhead := 0 + + +pub txn(b, n) + +'' Emit byte n times + + repeat n + tx(b) + + +pub str(p_str) + +'' Emit z-string at p_str + + repeat (strsize(p_str)) + tx(byte[p_str++]) + + +pub substr(p_str, len) | b + +'' Emit len characters of string at p_str +'' -- aborts if end of string detected + + repeat len + b := byte[p_str++] + if (b > 0) + tx(b) + else + quit + + +pub padstr(p_str, width, pad) + +'' Emit p_str as padded field of width characters +'' -- pad is character to use to fill out field +'' -- positive width causes right alignment +'' -- negative width causes left alignment + + str(nstr.padstr(p_str, width, pad)) + + +pub txflush() + +'' Wait for transmit buffer to empty +'' -- will delay one byte period after buffer is empty + + repeat until (txtail == txhead) ' let buffer empty + waitct(getct() + txdelay) ' delay for last byte + + +con { formatted strings commands } + +{{ + Escaped characters + + \\ backslash char + \% percent char + \q double quote + \b backspace + \t tab (horizontal) + \n new line (vertical tab) + \r carriage return + \nnn arbitrary ASCII value (nnn is decimal) + + Formatted arguments + + %w.pf print argument as decimal width decimal point + %[w[.p]]d print argument as decimal + %[w[.p]]u print argument as unsigned decimal + %[w[.p]]x print argument as hex + %[w[.p]]o print argument as octal + %[w[.p]]q print argument as quarternary + %[w[.p]]b print argument as binary + %[w]s print argument as string + %[w]c print argument as character ( + + -- w is field width + * positive w causes right alignment in field + * negative w causes left alignment in field + -- %ws aligns s in field (may truncate) + -- %wc prints w copies of c + -- p is precision characters + * number of characters to use, aligned in field + -- prefix with 0 if needed to match p + -- for %w.pf, p is number of digits after decimal point +}} + + +pub fstr0(p_str) + +'' Emit string with formatting characters. + + format(p_str, 0) + + +pub fstr1(p_str, arg1) + +'' Emit string with formatting characters and one argument. + + format(p_str, @arg1) + + +pub fstr2(p_str, arg1, arg2) + +'' Emit string with formatting characters and two arguments. + + format(p_str, @arg1) + + +pub fstr3(p_str, arg1, arg2, arg3) + +'' Emit string with formatting characters and three arguments. + + format(p_str, @arg1) + + +pub fstr4(p_str, arg1, arg2, arg3, arg4) + +'' Emit string with formatting characters and four arguments. + + format(p_str, @arg1) + + +pub fstr5(p_str, arg1, arg2, arg3, arg4, arg5) + +'' Emit string with formatting characters and five arguments. + + format(p_str, @arg1) + + +pub fstr6(p_str, arg1, arg2, arg3, arg4, arg5, arg6) + +'' Emit string with formatting characters and six arguments. + + format(p_str, @arg1) + + +pub format(p_str, p_args) | idx, c, asc, field, digits + +'' Emit formatted string with escape sequences and embedded values +'' -- p_str is a pointer to the format control string +'' -- p_args is pointer to array of longs that hold field values +'' * field values can be numbers, characters, or pointers to strings + + idx := 0 ' value index + + repeat + c := byte[p_str++] + if (c == 0) + return + + elseif (c == "\") + c := lcase(byte[p_str++]) + if (c == "\") + tx("\") + elseif (c == "%") + tx("%") + elseif (c == "q") + tx(34) + elseif (c == "b") + tx(BKSP) + elseif (c == "t") + tx(TAB) + elseif (c == "n") + tx(LF) + elseif (c == "r") + tx(CR) + elseif ((c >= "0") and (c <= "9")) + --p_str + p_str, asc, _ := get_nargs(p_str) + if ((asc >= 0) and (asc <= 255)) + tx(asc) + + elseif (c == "%") + p_str, field, digits := get_nargs(p_str) + c := lcase(byte[p_str++]) + if (c == "f") + str(nstr.fmt_number(long[p_args][idx++], c, digits, field, " ")) + elseif (c == "d") + str(nstr.fmt_number(long[p_args][idx++], c, digits, field, " ")) + elseif (c == "u") + str(nstr.fmt_number(long[p_args][idx++], c, digits, field, " ")) + elseif (c == "x") + str(nstr.fmt_number(long[p_args][idx++], c, digits, field, " ")) + elseif (c == "o") + str(nstr.fmt_number(long[p_args][idx++], c, digits, field, " ")) + elseif (c == "q") + str(nstr.fmt_number(long[p_args][idx++], c, digits, field, " ")) + elseif (c == "b") + str(nstr.fmt_number(long[p_args][idx++], c, digits, field, " ")) + elseif (c == "s") + str(nstr.padstr(long[p_args][idx++], field, " ")) + elseif (c == "c") + txn(long[p_args][idx++], (abs field) #> 1) + + else + tx(c) + + +pri lcase(c) : result + + if ((c >= "A") && (c <= "Z")) + c += 32 + + return c + + +pri get_nargs(p_str) : p_str1, val1, val2 | c, sign + +'' Parse one or two numbers from string in n, -n, n.n, or -n.n format +'' -- dpoint separates values +'' -- only first # may be negative +'' -- returns pointer to 1st char after value(s) + + c := byte[p_str] ' check for negative on first value + if (c == "-") + sign := -1 + ++p_str + else + sign := 0 + + repeat ' get first value + c := byte[p_str++] + if ((c >= "0") && (c <= "9")) + val1 := (val1 * 10) + (c - "0") + else + if (sign) + val1 := -val1 + quit + + if (c == ".") ' if dpoint + repeat ' get second value + c := byte[p_str++] + if ((c >= "0") && (c <= "9")) + val2 := (val2 * 10) + (c - "0") + else + quit + + p_str1 := p_str - 1 ' back up to non-digit + + +pub fmt_number(value, base, digits, width, pad) + +'' Emit value converted to number in padded field +'' -- value is converted using base as radix +'' * 99 for decimal with digits after decimal point +'' -- digits is max number of digits to use +'' -- width is width of final field (max) +'' -- pad is character that fills out field + + str(nstr.fmt_number(value, base, digits, width, pad)) + + +pub dec(value) + +'' Emit value as decimal + + str(nstr.itoa(value, 10, 0)) + + +pub fdec(value, digits) + +'' Emit value as decimal using fixed # of digits +'' -- may add leading zeros + + str(nstr.itoa(value, 10, digits)) + + +pub jdec(value, digits, width, pad) + +'' Emit value as decimal using fixed # of digits +'' -- aligned in padded field (negative width to left-align) +'' -- digits is max number of digits to use +'' -- width is width of final field (max) +'' -- pad is character that fills out field + + str(nstr.fmt_number(value, 10, digits, width, pad)) + + +pub dpdec(value, dp) + +'' Emit value as decimal with decimal point +'' -- dp is number of digits after decimal point + + str(nstr.dpdec(value, dp)) + + +pub jdpdec(value, dp, width, pad) + +'' Emit value as decimal with decimal point +'' -- aligned in padded field (negative width to left-align) +'' -- dp is number of digits after decimal point +'' -- width is width of final field (max) +'' -- pad is character that fills out field + + str(nstr.fmt_number(value, 99, dp, width, pad)) + + +pub hex(value) + +'' Emit value as hexadecimal + + str(nstr.itoa(value, 16, 0)) + + +pub fhex(value, digits) + +'' Emit value as hexadecimal using fixed # of digits + + str(nstr.itoa(value, 16, digits)) + + +pub jhex(value, digits, width, pad) + +'' Emit value as quarternary using fixed # of digits +'' -- aligned inside field +'' -- pad fills out field + + str(nstr.fmt_number(value, 16, digits, width, pad)) + + +pub oct(value) + +'' Emit value as octal + + str(nstr.itoa(value, 8, 0)) + + +pub foct(value, digits) + +'' Emit value as octal using fixed # of digits + + str(nstr.itoa(value, 8, digits)) + + +pub joct(value, digits, width, pad) + +'' Emit value as octal using fixed # of digits +'' -- aligned inside field +'' -- pad fills out field + + str(nstr.fmt_number(value, 8, digits, width, pad)) + + +pub qrt(value) + +'' Emit value as quarternary + + str(nstr.itoa(value, 4, 0)) + + +pub fqrt(value, digits) + +'' Emit value as quarternary using fixed # of digits + + str(nstr.itoa(value, 4, digits)) + + +pub jqrt(value, digits, width, pad) + +'' Emit value as quarternary using fixed # of digits +'' -- aligned inside field +'' -- pad fills out field + + str(nstr.fmt_number(value, 4, digits, width, pad)) + + +pub bin(value) + +'' Emit value as binary + + str(nstr.itoa(value, 2, 0)) + + +pub fbin(value, digits) + +'' Emit value as binary using fixed # of digits + + str(nstr.itoa(value, 2, digits)) + + +pub jbin(value, digits, width, pad) + +'' Emit value as binary using fixed # of digits +'' -- aligned inside field +'' -- pad fills out field + + str(nstr.fmt_number(value, 2, digits, width, pad)) + + +dat { smart pin uart/buffer manager } + + org + +uart_mgr setq #4-1 ' get 4 parameters from hub + rdlong rxd, ptra + + +uart_main testb rxd, #31 wc ' rx in use? + if_nc call #rx_serial + + testb txd, #31 wc ' tx in use? + if_nc call #tx_serial + + jmp #uart_main + + +rx_serial testp rxd wc ' anything waiting? + if_nc ret + + rdpin t3, rxd ' read new byte + shr t3, #24 ' align lsb + mov t1, p_rxbuf ' t1 := @rxbuf + rdlong t2, ptra[4] ' t2 := rxhead + add t1, t2 + wrbyte t3, t1 ' rxbuf[rxhead] := t3 + incmod t2, #(BUF_SIZE-1) ' update head index + _ret_ wrlong t2, ptra[4] ' write head index back to hub + + +tx_serial rdpin t1, txd wc ' check busy flag + if_c ret ' abort if busy + + rdlong t1, ptra[6] ' t1 = txhead + rdlong t2, ptra[7] ' t2 = txtail + cmp t1, t2 wz ' byte(s) to tx? + if_e ret + + mov t1, p_txbuf ' start of tx buffer + add t1, t2 ' add tail index + rdbyte t3, t1 ' t3 := txbuf[txtail] + wypin t3, txd ' load into sp uart + incmod t2, #(BUF_SIZE-1) ' update tail index + _ret_ wrlong t2, ptra[7] ' write tail index back to hub + + +' -------------------------------------------------------------------------------------------------- + +rxd res 1 ' receive pin +txd res 1 ' transmit pin +p_rxbuf res 1 ' pointer to rxbuf +p_txbuf res 1 ' pointer to txbuf + +t1 res 1 ' work vars +t2 res 1 +t3 res 1 + + fit 472 + + +con { license } + +{{ + + Terms of Use: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy of this + software and associated documentation files (the "Software"), to deal in the Software + without restriction, including without limitation the rights to use, copy, modify, + merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be included in all copies + or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +}} \ No newline at end of file diff --git a/libraries/community/p2/All/jm_full_duplex_serial/jm_nstr.spin2 b/libraries/community/p2/All/jm_full_duplex_serial/jm_nstr.spin2 new file mode 100644 index 00000000..f1c6d823 --- /dev/null +++ b/libraries/community/p2/All/jm_full_duplex_serial/jm_nstr.spin2 @@ -0,0 +1,240 @@ +'' ================================================================================================= +'' +'' File....... jm_nstr.spin2 +'' Purpose.... Convert numbers to strings +'' Authors.... Jon McPhalen +'' -- Copyright (c) 2020 Jon McPhalen +'' -- see below for terms of use +'' E-mail..... jon.mcphalen@gmail.com +'' Started.... +'' Updated.... 19 AUG 2020 +'' +'' ================================================================================================= + + +con + + NBUF_SIZE = 48 + PBUF_SIZE = 128 + + +var + + byte nbuf[NBUF_SIZE] ' number conversions + byte pbuf[PBUF_SIZE] ' padded strings + + +pub null() + +'' This is not a top level object + + +pub fmt_number(value, radix, digits, width, pad) : p_str ' *** changed 19 AUG 2020 *** + +'' Return pointer to string of value converted to number in padded field +'' -- value is converted using radix +'' -- radix is chararacter indicating type ' *** used to be number *** +'' -- digits is max number of digits to use +'' -- width is width of final fields (max) +'' -- pad is character used to pad final field (if needed) + + case radix + "b", "B" : p_str := padstr(itoa(value, 2, digits), width, pad) + "q", "Q" : p_str := padstr(itoa(value, 4, digits), width, pad) + "o", "O" : p_str := padstr(itoa(value, 8, digits), width, pad) + "d", "D" : p_str := padstr(itoa(value, 10, digits), width, pad) + "u", "U" : p_str := padstr(usdec(value, digits), width, pad) + "x", "X" : p_str := padstr(itoa(value, 16, digits), width, pad) + "f", "F" : p_str := padstr(dpdec(value, digits), width, pad) + other : p_str := string("?") + + +pub dec(value, digits) : p_str | sign, len + +'' Convert decimal value to string +'' -- digits is 0 (auto size) to 10 + + p_str := itoa(value, 10, digits) + + +pub usdec(value, digits) : p_str | len + +'' Convert unsigned decimal value to string +'' -- digits is 0 (auto size) to 10 + + digits := 0 #> digits <# 10 ' limit printable digits + + bytefill(@nbuf, 0, NBUF_SIZE) ' clear buffer + p_str := @nbuf + 9 ' point to end of udec string + + len := 0 + + repeat + byte[--p_str] := (value +// 10) + "0" ' extract digit, convert to ASCII + value +/= 10 ' remove digit from value + if (digits) ' length limited? + if (++len == digits) ' check size + quit + else + if (value == 0) ' done? + quit + + +pub dpdec(value, dp) : p_str | len, byte scratch[12] + +'' Convert value to string with decimal point +'' -- dp is digits after decimal point +'' -- returns pointer to updated fp string +'' -- modifies original string +'' -- return pointer to converted string + + p_str := itoa(value, 10, 0) + + if (dp <= 0) ' abort if no decimal point + return p_str + + len := strsize(p_str) ' digits + bytefill(@scratch, 0, 12) ' clear scratch buffer + + if (value < 0) ' ignore "-" if present + ++p_str + --len + + if (len < (dp+1)) ' insert 0s? + bytemove(@scratch, p_str, len) ' move digits to scratch buffer + bytefill(p_str, "0", dp+2-len) ' pad string with 0s + bytemove(p_str+dp+2-len, @scratch, len+1) ' move digits back + byte[p_str+1] := "." ' insert dpoint + else + bytemove(@scratch, p_str+len-dp, dp) ' move decimal part to buffer + byte[p_str+len-dp] := "." ' insert dpoint + bytemove(p_str+len-dp+1, @scratch, dp+1) ' move decimal part back + + if (value < 0) ' fix pointer for negative #s + --p_str + + +pub itoa(value, radix, digits) : p_str | sign, len, d + +'' Convert signed integer to string +'' -- supports radix 10, 2, 4, 8, and 16 +'' -- digits is 0 (auto size) to limit for long using radix + + bytefill(@nbuf, 0, NBUF_SIZE) ' clear buffer + p_str := @nbuf ' point to it + + case radix ' limit printable digits + 02 : digits := 0 #> digits <# 32 + 04 : digits := 0 #> digits <# 16 + 08 : digits := 0 #> digits <# 11 + 10 : digits := 0 #> digits <# 10 + 16 : digits := 0 #> digits <# 8 + other : + byte[p_str] := 0 + return + + if ((radix == 10) && (value < 0)) ' deal with negative decimals + if (value == negx) + sign := 2 + value := posx + else + sign := 1 + value := -value + else + sign := 0 + + len := 0 + + repeat + d := value +// radix ' get digit (1s column) + byte[p_str++] := (d < 10) ? d + "0" : d - 10 + "A" ' convert to ASCII + value +/= radix ' remove digit + if (digits) ' length limited? + if (++len == digits) ' check size + quit + else + if (value == 0) ' done? + quit + + if (sign) + byte[p_str++] := "-" ' add sign if needed + if (sign == 2) + nbuf[0] := "8" ' fix negx if needed + + byte[p_str++] := 0 ' terminate string + + return revstr(@nbuf) ' fix order (reverse) + + +pub revstr(p_str) : result | first, len, last + +'' Reverse the order of characters in a string. + + result := first := p_str ' start + len := strsize(p_str) ' length + last := first + len - 1 ' end + + repeat (len >> 1) ' reverse them + byte[first++], byte[last--] := byte[last], byte[first] + + +pub padstr(p_str, width, padchar) : p_pad | len + +'' Pad string with padchar character +'' -- positive width uses left pad, negative field width uses right pad +'' -- truncate if string len > width +'' -- input string is not modified +'' -- returns pointer to padded string + + bytefill(@pbuf, 0, PBUF_SIZE) ' clear padded buffer + len := strsize(p_str) ' get length of input + width := -PBUF_SIZE+1 #> width <# PBUF_SIZE-1 ' constrain to buffer size + + if (width > 0) ' right-justify in padded field + if (width > len) + bytefill(@pbuf, padchar, width-len) + bytemove(@pbuf+width-len, p_str, len) + p_pad := @pbuf + else + bytemove(@pbuf, p_str+len-width, width) ' truncate to right-most characters + p_pad := @pbuf + + elseif (width < 0) ' left-justify in padded field + width := -width + if (width > len) + bytemove(@pbuf, p_str, len) + bytefill(@pbuf+len, padchar, width-len) + p_pad := @pbuf + else + bytemove(@pbuf, p_str, width) ' truncate to leftmost characters + p_pad := @pbuf + + else + p_pad := p_str + + +con { license } + +{{ + + Terms of Use: MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +}} \ No newline at end of file diff --git a/libraries/community/p2/Protocol/README.md b/libraries/community/p2/Protocol/README.md index 3909ac36..f9f2df23 100644 --- a/libraries/community/p2/Protocol/README.md +++ b/libraries/community/p2/Protocol/README.md @@ -1,5 +1,7 @@ [JM 1-Wire](../All/jm_1-wire) +[JM Full Duplex Serial](../All/jm_full_duplex_serial) + [JM I2C](../All/jm_i2c) [JM Quadrature Encoder](../All/jm_quadrature)