I have a basic version of the 1-Wire ROM search procedure running.
And while cleaning up I came across a point, that I come across every now and then.
It is the missing namespaces in Tachyon, to make sure that I don't accidentally overwrite
an existing word.
Besides the effort of a technical solution, there would be a very simple
organisational solution.
Like prefixing all the words of the package with a code like '1W.'
then it would be clear for everybody, and no name conflicts could occure.
It would cost a few chars more in name space dictionary, but since most words already contain 1W in the name
it would be only the '.'.
so 1W.RESET instead of 1WRESET ...
what do you think?
Markus
Here's a basic driver for the AdaFruit MAX31855 breakout Thermocouple board.
Peter you can by all means cut and paste the code to place it in the Tachyon dropbox as you see fit.
TACHYON
FORGET MAX31855.fth
pub MAX31855.fth ." MAX31855 DRIVER - 140525-15:00 " ;
( MAX31855 ThermoCouple DRIVER )
[~
BYTE sbit \ sign bit
#P5 |< == MCLK \ clock mask
#P3 |< == MCS \ chip select mask
#P1 == MDO \ data input pin
MCS OUTSET \ initialize these pins
MCLK OUTSET
MDO |< INPUTS
pub maxtc@ ( -- 32bitsMSBFirst ) \ bit bang to read MAX31855
MCLK OUTCLR \ just following what worked on Arduino, no joy with SHRINP
1 ms
MCS OUTCLR
1 ms
0 \ data destination placeholder
#32 FOR
MCLK OUTCLR
1 ms
1 SHL \ bump it over
MDO PIN@
IF 1 OR THEN \ set bit accordingly
MCLK OUTSET
1 ms
NEXT
MCLK OUTCLR
MCS OUTSET
;
pub inttemp@ ( -- degreeC )
maxtc@
4 SHR \ drop 4 lsb bits
DUP
$800 AND
sbit C! \ save sign bit
$7FF AND \ get 11 bits
#625 * #10000 / \ convert the temp
sbit C@ \ get the sign bit, could use stack
IF -1 * THEN \ convert for sign bit
;
pub tctemp@ ( -- degreeC )
maxtc@
#18 SHR \ shift upper 14 bits right
DUP \ two copies
$2000 AND \ get bit 14 the sign bit
sbit C! \ save sign bit
$1FFF AND \ get other 13 bits
#25 * #100 / \ convert the temp
sbit C@
IF -1 * THEN \ convert for sign bit
;
pub .inttemp \ display internal MAX31855 temp
inttemp@
DUP
." Cold Junc. Temp: " . ." C " #40 + #9 * 5 / #40 - . ." F" CR
;
pub .tctemp \ display tc temp
tctemp@
DUP
CR ." TC Temp: " . ." C " #40 + #9 * 5 / #40 - . ." F "
;
pub .runtc \ routine to display readings to console every 2 seconds until ESC key hit
#10 >RADIX
BEGIN
#1000 ms
.tctemp
.inttemp #1000 ms
ESC? UNTIL
RADIX>
;
{ .runtc
TC Temp: 21 C 69 F Cold Junc. Temp: 22 C 71 F
.
.
.
}
]~
END
Here's a basic driver for the AdaFruit MAX31855 breakout Thermocouple board.
Peter you can by all means cut and paste the code to place it in the Tachyon dropbox as you see fit.
TACHYON
FORGET MAX31855.fth
pub MAX31855.fth ." MAX31855 DRIVER - 140525-15:00 " ;
( MAX31855 ThermoCouple DRIVER )
[~
BYTE sbit \ sign bit
#P5 |< == MCLK \ clock mask
#P3 |< == MCS \ chip select mask
#P1 == MDO \ data input pin
MCS OUTSET \ initialize these pins
MCLK OUTSET
MDO |< INPUTS
pub maxtc@ ( -- 32bitsMSBFirst ) \ bit bang to read MAX31855
MCLK OUTCLR \ just following what worked on Arduino, no joy with SHRINP
1 ms
MCS OUTCLR
1 ms
0 \ data destination placeholder
#32 FOR
MCLK OUTCLR
1 ms
1 SHL \ bump it over
MDO PIN@
IF 1 OR THEN \ set bit accordingly
MCLK OUTSET
1 ms
NEXT
MCLK OUTCLR
MCS OUTSET
;
pub inttemp@ ( -- degreeC )
maxtc@
4 SHR \ drop 4 lsb bits
DUP
$800 AND
sbit C! \ save sign bit
$7FF AND \ get 11 bits
#625 * #10000 / \ convert the temp
sbit C@ \ get the sign bit, [COLOR=#0000cd]could use stack [/COLOR]:-)
\ [COLOR=#ff0000]IF -1 * THEN \ convert for sign bit[/COLOR]
[COLOR=#0000cd] ?NEGATE \ is faster[/COLOR]
;
pub tctemp@ ( -- degreeC )
maxtc@
#18 SHR \ shift upper 14 bits right
DUP \ two copies
$2000 AND \ get bit 14 the sign bit
sbit C! \ save sign bit
$1FFF AND \ get other 13 bits
#25 * #100 / \ convert the temp
sbit C@
\ IF -1 * THEN \ convert for sign bit
[COLOR=#0000cd] ?NEGATE \ is faster[/COLOR]
;
pub .inttemp \ display internal MAX31855 temp
inttemp@
DUP
." Cold Junc. Temp: " . ." C " #40 + #9 * 5 / #40 - . ." F" CR
;
pub .tctemp \ display tc temp
tctemp@
DUP
CR ." TC Temp: " . ." C " #40 + #9 * 5 / #40 - . ." F "
[COLOR=#0000cd]\ : C>F #40 + #9 * 5 / #40 - ; [/COLOR]
;
pub .runtc \ routine to display readings to console every 2 seconds until ESC key hit
#10 >RADIX [COLOR=#0000cd]\ instead of switching radix you can use the .NUM formatting see EXTEND [/COLOR]
BEGIN
[COLOR=#ff0000]\ #1000 ms[/COLOR]
.tctemp
.inttemp [COLOR=#ff0000]\ #1000 ms[/COLOR]
[COLOR=#0000cd] 2 seconds[/COLOR]
ESC? UNTIL
RADIX>
;
{ .runtc
TC Temp: 21 C 69 F Cold Junc. Temp: 22 C 71 F
.
.
.
}
]~
END
TACHYON
FORGET MAX31855.fth
pub MAX31855.fth ." MAX31855 DRIVER - 140525-16:00 " ;
\ update after mjb comments
( MAX31855 ThermoCouple DRIVER )
[~
BYTE sbit \ sign bit
#P5 |< == MCLK \ clock mask
#P3 |< == MCS \ chip select mask
#P1 == MDO \ data input pin
MCS OUTSET \ initialize these pins
MCLK OUTSET
MDO |< INPUTS
pub maxtc@ ( -- 32bitsMSBFirst ) \ bit bang to read MAX31855
MCLK OUTCLR \ just following what worked on Arduino, no joy with SHRINP
1 ms
MCS OUTCLR
1 ms
0 \ data destination placeholder
#32 FOR
MCLK OUTCLR
1 ms
1 SHL \ bump it over
MDO PIN@
IF 1 OR THEN \ set bit accordingly
MCLK OUTSET
1 ms
NEXT
MCLK OUTCLR
MCS OUTSET
;
pub inttemp@ ( -- degreeC )
maxtc@
4 SHR \ drop 4 lsb bits
DUP
$800 AND
sbit C! \ save sign bit
$7FF AND \ get 11 bits
#625 * #10000 / \ convert the temp
sbit C@ \ get the sign bit, could use stack :-)
?NEGATE \ negate if sign bit
;
pub tctemp@ ( -- degreeC )
maxtc@
#18 SHR \ shift upper 14 bits right
DUP \ two copies
$2000 AND \ get bit 14 the sign bit
sbit C! \ save sign bit
$1FFF AND \ get other 13 bits
#25 * #100 / \ convert the temp
sbit C@
?NEGATE \ negate if sign bit
;
pub C>F ( C -- F ) \ convert C degrees to F
#40 + #9 * 5 / #40 -
;
pub .inttemp \ display internal MAX31855 temp
inttemp@
DUP
." Cold Junc. Temp: " $240A .NUM ." C " C>F $240A .NUM ." F " \ .NUM 4 digits, leading blanks base 10
;
pub .tctemp \ display tc temp
tctemp@
DUP
CR ." TC Temp: " $240A .NUM ." C " C>F $240A .NUM ." F "
;
pub .runtc \ routine to display readings to console every 2 seconds until ESC key hit
BEGIN
2 seconds
.tctemp
.inttemp
ESC? UNTIL
;
{ .runtc
TC Temp: 21 C 69 F Cold Junc. Temp: 22 C 71 F
.
.
.
}
]~
END
TACHYON
FORGET MAX31855.fth
pub MAX31855.fth ." MAX31855 DRIVER - 140525-16:00 " ;
\ update after mjb comments
( MAX31855 ThermoCouple DRIVER )
[~
BYTE sbit \ sign bit
#P5 |< == MCLK \ clock mask
#P3 |< == MCS \ chip select mask
#P1 == MDO \ data input pin
MCS OUTSET \ initialize these pins
MCLK OUTSET
MDO |< INPUTS
pub maxtc@ ( -- 32bitsMSBFirst ) \ bit bang to read MAX31855
[COLOR=#0000ff]\ here the SPI words should help:
\ I propose the kernel embedded SPI here, since it seems sufficient.
\ for more complex SPI requirements, like bidirectional, the loadable RUNMOD modules can be used
\ like
{ \ Define COGREG constants for most SPI operations based on kernel code, usually not for RUNMODs
-5 CONSTANT @SPISCK
-2 CONSTANT @SPICE
-1 CONSTANT @SPICNT
}
32 @SPICNT COGREG! \ set SPI to 32 bits at once
MCS @SPICE COGREG! \ define CS mask
MCLK @SPISCK COGREG! \ define SCK mask
-3 CONSTANT @SPIINP \ definition is missing in EXTEND ( I'll try to add it )
MDO |< @SPIINP COGREG! \ define input mask
[/COLOR]
[COLOR=#0000ff]{ let Tachyon do the work ... from Tachyon.spin
' Receive data and shift into existing stack parameter
' If MOSI needs to be in a certain state then this should be set beforehand
' Clock will simply pulse from it's starting state - normally low
' Useage: 0 SPIRX 'read 32-bits as one long
' SPIRD ( data -- data<<8 ) ' 11.2us for 32 bits == 2.8 MHz ( sensor is specified up to 5 MHz )
}[/COLOR]
[COLOR=#b22222]{ your code ...
MCLK OUTCLR \ just following what worked on Arduino, no joy with SHRINP
1 ms
MCS OUTCLR
1 ms
0 \ data destination placeholder
#32 FOR
MCLK OUTCLR
1 ms
1 SHL \ bump it over
MDO PIN@
IF 1 OR THEN \ set bit accordingly
MCLK OUTSET
1 ms
NEXT
MCLK OUTCLR
MCS OUTSET
}[/COLOR]
[COLOR=#0000ff]\ set defined start conditions
MCLK OUTCLR
MCS OUTCLR
0 SPIRDX \ inputs 32 bits at once -- done
\ not sure if those are needed
MCLK OUTCLR
MCS OUTSET[/COLOR]
;
pub inttemp@ ( -- degreeC )
maxtc@
4 SHR \ drop 4 lsb bits
DUP
$800 AND
sbit C! \ save sign bit
$7FF AND \ get 11 bits
#625 * #10000 / \ convert the temp
sbit C@ \ get the sign bit, could use stack :-)
?NEGATE \ negate if sign bit
;
pub tctemp@ ( -- degreeC )
maxtc@
#18 SHR \ shift upper 14 bits right
DUP \ two copies
$2000 AND \ get bit 14 the sign bit
sbit C! \ save sign bit
$1FFF AND \ get other 13 bits
#25 * #100 / \ convert the temp
sbit C@
?NEGATE \ negate if sign bit
;
pub C>F ( C -- F ) \ convert C degrees to F
#40 + #9 * 5 / #40 -
;
pub .inttemp \ display internal MAX31855 temp
inttemp@
DUP
." Cold Junc. Temp: " $240A .NUM ." C " C>F $240A .NUM ." F " \ .NUM 4 digits, leading blanks base 10
;
pub .tctemp \ display tc temp
tctemp@
DUP
CR ." TC Temp: " $240A .NUM ." C " C>F $240A .NUM ." F "
;
pub .runtc \ routine to display readings to console every 2 seconds until ESC key hit
BEGIN
2 seconds
.tctemp
.inttemp
ESC? UNTIL
;
{ .runtc
TC Temp: 21 C 69 F Cold Junc. Temp: 22 C 71 F
.
.
.
}
]~
END
p.s. I changed the title to s.th. meaningful
ALL:
unfortunately most writers just leave the previous post title, so the title says nothing about the new post itself - this is bad for searching later - so I'd like to encourage use of meaningful post titles !! thanks MJB
I've been away for a while taking care of non-code and chip kinds of things. Recently working real hard on learning to use my new AdaFruit TI-ADS1115 16 bit ADC breakout. Yep, I dumped the DMM idea. The points against started adding up too fast.
The TI-ADS1115 is my first foray into the I2C world. Thanks to Peter and Tachyon the lowest level stuff was not hard.
Here is the code I have done so far:
FORGET ADC16@
: ADC16@ \ read contents from a TI-1115 register ( rp --- msb lsb ) rp = TI-1115 register pointer
I2CSTART \ wakeup call
$90 I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
I2CSTART \ not done yet
$91 I2C! \ send address $48 plus read bit
ackI2C@ \ get MSB of target register
ackI2C@ \ get LSB
I2CSTOP \ that's all for now
;
: ADC16! \ get write n to TI-1115 register rp ( n rp --- n )
I2CSTART \ wakeup call
$90 I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
DUP 8 SHR \ get MSB
I2C! \ write to target register
$FF AND I2C! \ write LSB
I2CSTOP \ that's all for now
;
$8BA3 CONSTANT ADcfg
: ADC16cfg \ ( 16b --- ) \ configure per 16bit input value
\ see table of values in C:\Users\Art\Documents\Tech\!Propeller\ADC Add On\ADC_Board.txt
1 ADC16! \ point to config register and write
;
: ADC16ss \ get Single-Shot reading from TI-1115 ( 16b --- MSBrd LSBrd ) configuration --- result
ADcfg ADC16cfg \ config and trigger conversion - temp, hard coded configuration
BEGIN \ repeat
1 ADC16@ \ config register, bit 15
DROP $80 AND \ returns HI indicating completion of conversion
UNTIL \ until
0 ADC16@ \ read data register for conversion result
SWAP \ pop MSB
8 SHL + \ shift MSB and combine with LSB
;
: 2s>R DUP #32767 > IF #65536 - THEN \ convert 16 bit twos complement number to real decimal
;
If anyone sees where it could be improved - have at it. If it is useful to anyone . . . enjoy.
I have also written a Excel sheet to assist in compiling the configuration value . . . which I have just attempted to attach and get an "Invalid File" error. Does the Forum not allow *.xlsx files? It sure is useful to me.
I changed 2s>R to allow designated bit lengths.
I imagine this has been done before but I didn't find it so I did it again. Good practice.
"2s>R" might not be the best name.
Should it be called something else?
FORGET ADC16@
: ADC16@ \ read contents from a TI-1115 register ( rp --- msb lsb ) rp = TI-1115 register pointer
I2CSTART \ wakeup call
$90 I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
I2CSTART \ not done yet
$91 I2C! \ send address $48 plus read bit
ackI2C@ \ get MSB of target register
ackI2C@ \ get LSB
I2CSTOP \ that's all for now
;
: ADC16! \ get write n to TI-1115 register rp ( n rp --- n )
I2CSTART \ wakeup call
$90 I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
DUP 8 SHR \ get MSB
I2C! \ write to target register
$FF AND I2C! \ write LSB
I2CSTOP \ that's all for now
;
$8BA3 CONSTANT ADcfg
: ADC16cfg \ ( 16b --- ) \ configure per 16bit input value
\ see table of values in C:\Users\Art\Documents\Tech\!Propeller\ADC Add On\ADC_Board.txt
1 ADC16! \ point to config register and write
;
: ADC16ss \ get Single-Shot reading from TI-1115 ( 16b --- MSBrd LSBrd ) configuration --- result
ADcfg ADC16cfg \ config and trigger conversion - temp, hard coded configuration
BEGIN \ repeat
1 ADC16@ \ config register, bit 15
DROP $80 AND \ returns HI indicating completion of conversion
UNTIL \ until
0 ADC16@ \ read data register for conversion result
SWAP \ pop MSB
8 SHL + \ shift MSB and combine with LSB
;
: 2s>R \ ( n len --- n1 ) n = twos complement number, len = bit length, n1 = converted to signed decimal number
1- 2 SWAP ^ \ compute 2 ^ (bitlen - 1)
2DUP \ copy for test
=> IF \ if n greater it is negative
2 * - \ n - 2 ^ len
ELSE \ else n is positive
DROP \ no change necessary
THEN \ then
;
My last two posts are not really replies to [h=2]Re: MAX31855 SPI ADC for Thermocouples DRIVER[/h]But to the ongoing Tachyon thread.
Oops!
Hi Art,
I saw your post in the other thread with Tracy responding with the spin objects he wrote for the chip (ADS1111).
Looking forward to see how you go about the "math" that Tracy provided.
I'm sure as soon as Peter gets some more Tachyon cycles we will see F32 as a loadable module, I plan to just use this in an empty cog as a full time floating point math engine
since I want to calculate saturation dew point temperatures and hence RH using standard saturation vapor point equations.
I changed 2s>R to allow designated bit lengths.
I imagine this has been done before but I didn't find it so I did it again. Good practice.
"2s>R" might not be the best name.
Should it be called something else?
FORGET ADC16@
: ADC16@ \ read contents from a TI-1115 register ( rp --- msb lsb ) rp = TI-1115 register pointer
I2CSTART \ wakeup call
$90 I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
I2CSTART \ not done yet
$91 I2C! \ send address $48 plus read bit
ackI2C@ \ get MSB of target register
ackI2C@ \ get LSB
I2CSTOP \ that's all for now
;
: ADC16! \ get write n to TI-1115 register rp ( n rp --- n )
I2CSTART \ wakeup call
$90 I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
DUP 8 SHR \ get MSB
I2C! \ write to target register
$FF AND I2C! \ write LSB
I2CSTOP \ that's all for now
;
$8BA3 CONSTANT ADcfg
: ADC16cfg \ ( 16b --- ) \ configure per 16bit input value
\ see table of values in C:\Users\Art\Documents\Tech\!Propeller\ADC Add On\ADC_Board.txt
1 ADC16! \ point to config register and write
;
: ADC16ss \ get Single-Shot reading from TI-1115 ( 16b --- MSBrd LSBrd ) configuration --- result
ADcfg ADC16cfg \ config and trigger conversion - temp, hard coded configuration
BEGIN \ repeat
1 ADC16@ \ config register, bit 15
DROP $80 AND \ returns HI indicating completion of conversion
UNTIL \ until
0 ADC16@ \ read data register for conversion result
SWAP \ pop MSB
8 SHL + \ shift MSB and combine with LSB
;
: 2s>R \ ( n len --- n1 ) n = twos complement number, len = bit length, n1 = converted to signed decimal number
1- 2 SWAP ^ \ compute 2 ^ (bitlen - 1)
2DUP \ copy for test
=> IF \ if n greater it is negative
2 * - \ n - 2 ^ len
ELSE \ else n is positive
DROP \ no change necessary
THEN \ then
;
Hi Art, I had a look at this code when you first posted it but I've been a bit busy since, as always. The code looks clean and tidy and the only real critique I would have is that you should always standardize the get and put method as you have ADC16! requiring a 16-bit word for the data yet ADC16@ returns with that same word split into two bytes. Here's my take on your original code.
[FONT=courier new]FORGET ADC16@
$90 == @ADC --- adc 8-bit I2C address
: ADC16@ ( reg -- dat16 ) \ read contents from a TI-1115 register
I2CSTART \ wakeup call
@ADC I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
I2CSTART \ not done yet
@ADC 1+ I2C! \ send address $48 plus read bit
ackI2C@ \ get MSB of target register
ackI2C@ \ get LSB
SWAP B>W \ combine bytes into a word
I2CSTOP \ that's all for now
;
: ADC16! ( dat16 reg -- ) \ get write n to TI-1115 register rp ( n rp --- n )
I2CSTART \ wakeup call
@ADC I2C! \ send address $48 plus write bit
I2C! \ write rp to pointer register
W>B \ Split word into high and low bytes ( bytel byteh )
I2C! \ write MSB to target register
I2C! \ write LSB
I2CSTOP \ that's all for now
;
$8BA3 CONSTANT ADcfg
: ADC16ss ( config -- data16 ) \ get Single-Shot reading from TI-1115
ADcfg 1 ADC16! \ config and trigger conversion - temp, hard coded configuration
BEGIN \ repeat
1 ADC16@ \ config register, bit 15
$8000 AND \ returns HI indicating completion of conversion
UNTIL \ until
0 ADC16@ \ read data register for conversion result
;
--- extend sign of 16-bit word into a long
: EXT16 ( sign16 -- sign32 ) DUP $8000 AND IF $1.0000 - THEN \ convert 16 bit twos complement number to real decimal
;
[/FONT]
Hi Art, I had a look at this code when you first posted it but I've been a bit busy since, as always. The code looks clean and tidy and the only real critique I would have is that you should always standardize the get and put method as you have ADC16! requiring a 16-bit word for the data yet ADC16@ returns with that same word split into two bytes. Here's my take on your original code.
I see the beginnings of CREATE DOES in the comments section of tachyon 06/02, hmmm.
I see the beginnings of CREATE DOES in the comments section of tachyon 06/02, hmmm.
My EPRINT routine which stores literal print strings to spare EEPROM instead of program memory is the reason I'm looking into it.
TF is setup for compactness as well as speed and certain decisions were made at the time in this regard. I'm not quite sure why I ended up using bytecode rather than a pointer in the header field but this made sense since bytecodes are compiled, not pointers. Some bytecode are calls via a vector table which then points to the code, once again this keeps the actual code compact at 2 bytes rather than 3 but adds the overhead of a table. The advantage of the table is that addresses can be extended to beyond 64K and still compile no more than 2 bytes, something that makes the transition to P2 easier.
So back to the CREATE DOES, I trying to think how best to implement this in TF given it's internal quirks. If you see a way that works, such as in EPRINT then by all means please suggest it!
I've just trimmed the kernel again by removing some fast constants such as 5,6,7 and optimizing the code. I added >N and >B to perform quick nibble and byte masking ops as it is quite common to say $0F AND or $FF AND but this would take 3 bytes and 2.6us but now uses only a single byte and takes 400ns.
EDIT: Just thought of another way to generate fast literals, so instead of adding to an accumulator I use the pasm MIN instead to load the literal accumulator ACC which is normally zeroed and then set the carry which is normally cleared on every bytecode. So this works a treat and lets me put in any value in any order.
7704(0081) 21 4E CF 49 | BL if_nc min ACC,#32+1 wc
7708(0082) 11 4E CF 49 | _16 if_nc min ACC,#16+1 wc
770C(0083) 09 4E CF 49 | _8 if_nc min ACC,#8+1 wc
7710(0084) 04 4E CF 49 | _3 if_nc min ACC,#3+1 wc
7714(0085) 05 4E CF 49 | _4 if_nc min ACC,#4+1 wc
7718(0086) 03 4E CF 49 | _2 if_nc min ACC,#2+1 wc
771C(0087) 02 4E CF 49 | _1 if_nc min ACC,#1+1 wc
7720(0088) | _TRUE
7720(0088) 01 4E FF 84 | MINUS1 sub ACC,#1
7724(0089) | _FALSE
7724(0089) 7F 00 7C 5C | _0 jmp #PUSHACC
My EPRINT routine which stores literal print strings to spare EEPROM instead of program memory is the reason I'm looking into it.
TF is setup for compactness as well as speed and certain decisions were made at the time in this regard. I'm not quite sure why I ended up using bytecode rather than a pointer in the header field but this made sense since bytecodes are compiled, not pointers. Some bytecode are calls via a vector table which then points to the code, once again this keeps the actual code compact at 2 bytes rather than 3 but adds the overhead of a table. The advantage of the table is that addresses can be extended to beyond 64K and still compile no more than 2 bytes, something that makes the transition to P2 easier.
So back to the CREATE DOES, I trying to think how best to implement this in TF given it's internal quirks. If you see a way that works, such as in EPRINT then by all means please suggest it!
I've just trimmed the kernel again by removing some fast constants such as 5,6,7 and optimizing the code. I added >N and >B to perform quick nibble and byte masking ops as it is quite common to say $0F AND or $FF AND but this would take 3 bytes and 2.6us but now uses only a single byte and takes 400ns.
EDIT: Just thought of another way to generate fast literals, so instead of adding to an accumulator I use the pasm MIN instead to load the literal accumulator ACC which is normally zeroed and then set the carry which is normally cleared on every bytecode. So this works a treat and lets me put in any value in any order.
7704(0081) 21 4E CF 49 | BL if_nc min ACC,#32+1 wc
7708(0082) 11 4E CF 49 | _16 if_nc min ACC,#16+1 wc
770C(0083) 09 4E CF 49 | _8 if_nc min ACC,#8+1 wc
7710(0084) 04 4E CF 49 | _3 if_nc min ACC,#3+1 wc
7714(0085) 05 4E CF 49 | _4 if_nc min ACC,#4+1 wc
7718(0086) 03 4E CF 49 | _2 if_nc min ACC,#2+1 wc
771C(0087) 02 4E CF 49 | _1 if_nc min ACC,#1+1 wc
7720(0088) | _TRUE
7720(0088) 01 4E FF 84 | MINUS1 sub ACC,#1
7724(0089) | _FALSE
7724(0089) 7F 00 7C 5C | _0 jmp #PUSHACC
Neat!
I have a few spinnerettes that I would like to use. Do you have any backups of Tachyon V2.1 and W5100.fth that support UDP clients? I have tried links on the drop box but couldn't get any UDP client "action" to work. I have telnet, ftp working but no UDP client. My current build is:
Propeller .:.:--TACHYON--:.:. Forth V21130620.0000
NAMES: $5D96...7444 for 5806 (19120 bytes added) with 21546 bytes free
CODE: $0000...2ECB for 6173 (0922 bytes added) with 20789 bytes free
CALLS: 0511 vectors free
AUTORUN BOOT
MODULES LOADED:
2B31: WIZNET.fth WIZNET 5100/5200 driver 130216.1500
1880: EXTEND.fth Primary extensions to TACHYON kernel - 130722.0000
----------------------------------------------------------------
I can't find what I did to get UDP clients working during this timeframe.
UPDATE: oh snap, the version of W5100 driver doesn't have these two helper words to set destination and port
pub DIP ( ip -- ) sDIP WIZ4! ; \ destination IP
pub DPORT ( port -- ) sDPORT WIZ2! ; \ destination PORT
Got confused with the UIP and UPORT words. All better, it's funny how just posting a question to the forum help to resolve issues without any feedback!
My EPRINT routine which stores literal print strings to spare EEPROM instead of program memory is the reason I'm looking into it.
<snip>
So back to the CREATE DOES, I trying to think how best to implement this in TF given it's internal quirks. If you see a way that works, such as in EPRINT then by all means please suggest it!
<snip>
My understanding:
CREATE DOES> is used to create so called DEFINING WORDS like
: VARIABLE CONSTANT and the like.
from FORTH PRIMER:
Michael Ham has called the word pair [B][COLOR=red]CREATE...DOES>[/COLOR][/B], the pearl of Forth.
[B][COLOR=red]CREATE[/COLOR][/B] is a component of the compiler, whose function is to make a new
dictionary entry with a given name (the next name in the input stream)
and nothing else. [B][COLOR=red]
DOES>[/COLOR][/B] assigns a specific run-time action to a newly [B]CREATE[/B]d word.
a. [B]Defining defining words[/B] [B]
CREATE[/B] finds its most important use in extending the powerful class of
Forth words called defining words. The colon compiler "[B][COLOR=red]:[/COLOR][/B]" is such
a word, as are [B]VARIABLE[/B] and [B]CONSTANT[/B].
The definition of [B]VARIABLE[/B] in high-level Forth is simple
[COLOR=red]: VARIABLE CREATE 1 CELLS ALLOT ;[/COLOR]
We have already seen how [B]VARIABLE[/B] is used in a program. (An
alternative definition found in some Forths is
[COLOR=red]: VARIABLE CREATE 0 , ;[/COLOR] these variables are initialized to 0.)
Forth lets us define words initialized to contain specific values: for
example, we might want to define the number 17 to be a word. [B]
CREATE[/B] and "[B][COLOR=red],[/COLOR][/B]" ("comma") can do this: [COLOR=red]
17 CREATE SEVENTEEN ,[/COLOR] <cr> ok
Now test it via [COLOR=red]
SEVENTEEN @ .[/COLOR] <cr> 17 ok .
Remarks:
> The word [B][COLOR=red],[/COLOR][/B] ("comma") puts TOS into the next cell of the dictionary
and increments the dictionary pointer by that number of bytes.
> A word "[B][COLOR=red]C,[/COLOR][/B]" ("see-comma") exists also it puts a character into
the next character-length slot of the dictionary and increments
the pointer by 1 such slot. (In the ASCII character representation
the slots are 1 byte long; Unicode characters require 2 bytes.)
b. [B]Run-time [I]vs.[/I] compile-time actions[/B]
In the preceding example, we were able to initialize the variable [B]
SEVENTEEN[/B] to 17 when we [B]CREATE[/B]d it, but we still have to fetch it to
the stack [I]via[/I] [B]SEVENTEEN @[/B] whenever we want it. This is not quite what
we had in mind. We would like to find 17 in TOS when [B]SEVENTEEN[/B] is
named. The word [B]DOES>[/B] gives us the tool to do this.
The function of [B]DOES>[/B] is to specify a run-time action for the child
words of a defining word. Consider the defining word [B]CONSTANT[/B] ,
defined in high-level (of course [B]CONSTANT[/B] is usually defined in machine
code for speed) Forth by [COLOR=red]
: CONSTANT CREATE , DOES> @ ; [/COLOR]
and used as [COLOR=red]
53 CONSTANT PRIME[/COLOR] <cr> ok
Now test it:
[COLOR=red]PRIME .[/COLOR] <cr> 53 ok .
What is happening here?
> [B]CREATE[/B] (hidden in [B]CONSTANT[/B]) makes an entry named [B]PRIME[/B]
(the first word in the input stream following [B]CONSTANT[/B]). Then "[B][COLOR=red],[/COLOR][/B]"
places the TOS (the number 53) in the next cell of the dictionary.
> Then [B]DOES>[/B] (inside [B]CONSTANT[/B]) appends the actions of all words
between it and "[B][COLOR=red];[/COLOR][/B]" (the end of the definition) in this case, "[B][COLOR=red]@[/COLOR][/B]"
to the child word(s) defined by [B]CONSTANT[/B].
in TACHYON there is CREATE ( precompiled kernel code )
which creates the dictionary entry,
reads the next word as a name for the newly created word
and then assigns the default BEHAVIOR - which is the code of VARB - makes it a variable,
means, it just puts it's address on the stack at runtime.
' CREATE <name> - Create a name in the dictionary and also a code entry
CREATE byte REG,createvec,WFETCH,QDUP,_IF,01,AJMP ' execute extended or user CREATE?
byte XCALL,xCREATEWORD '
byte _BYTE,VARB,XCALL,xBCOMP,_0 ' set default bytecode as a VARIABLE
' ALLOT ( bytes -- )
ALLOT byte REG,codes,WPLUSST
ALLOCATED
byte REG,codes,WFETCH,REG,here,WSTORE
byte EXIT
so a DOES> could just create a dummy word, compile the code following DOES> to it
AND then put the new byte code to the previous definitions PFA.
But I do not see the relation to the new PRINT" word.
I think PRINT" is not a normal defining word since it does not define a new word each time it is called,
as compared to what VARIABLE or CONSTANT do.
To me it belongs into the category of compiler extension words,
like the IF DO LOOP etc. words.
Those have a compile time behavior (like PRINT" copying the chars to EE) and leave BYTECODES
in the compiled code (like PRINT" retrieving the chars from EE and printing them ).
I don't feel having a solid grasp on CREATE DOES> so maybe discussing it here a little helps.
My understanding:
CREATE DOES> is used to create so called DEFINING WORDS like
: VARIABLE CONSTANT and the like.
from FORTH PRIMER:
Michael Ham has called the word pair [B][COLOR=red]CREATE...DOES>[/COLOR][/B], the “pearl of Forth”.
[B][COLOR=red]CREATE[/COLOR][/B] is a component of the compiler, whose function is to make a new
dictionary entry with a given name (the next name in the input stream)
and nothing else. [B][COLOR=red]
DOES>[/COLOR][/B] assigns a specific run-time action to a newly [B]CREATE[/B]d word.
a. [B]Defining “defining” words[/B] [B]
CREATE[/B] finds its most important use in extending the powerful class of
Forth words called “defining” words. The colon compiler "[B][COLOR=red]:[/COLOR][/B]" is such
a word, as are [B]VARIABLE[/B] and [B]CONSTANT[/B].
The definition of [B]VARIABLE[/B] in high-level Forth is simple
[COLOR=red]: VARIABLE CREATE 1 CELLS ALLOT ;[/COLOR]
We have already seen how [B]VARIABLE[/B] is used in a program. (An
alternative definition found in some Forths is
[COLOR=red]: VARIABLE CREATE 0 , ;[/COLOR] —these variables are initialized to 0.)
Forth lets us define words initialized to contain specific values: for
example, we might want to define the number 17 to be a word. [B]
CREATE[/B] and "[B][COLOR=red],[/COLOR][/B]" ("comma") can do this: [COLOR=red]
17 CREATE SEVENTEEN ,[/COLOR] <cr> ok
Now test it via [COLOR=red]
SEVENTEEN @ .[/COLOR] <cr> 17 ok .
Remarks:
> The word [B][COLOR=red],[/COLOR][/B] ("comma") puts TOS into the next cell of the dictionary
and increments the dictionary pointer by that number of bytes.
> A word "[B][COLOR=red]C,[/COLOR][/B]" ("see-comma") exists also — it puts a character into
the next character-length slot of the dictionary and increments
the pointer by 1 such slot. (In the ASCII character representation
the slots are 1 byte long; Unicode characters require 2 bytes.)
b. [B]Run-time [I]vs.[/I] compile-time actions[/B]
In the preceding example, we were able to initialize the variable [B]
SEVENTEEN[/B] to 17 when we [B]CREATE[/B]d it, but we still have to fetch it to
the stack [I]via[/I] [B]SEVENTEEN @[/B] whenever we want it. This is not quite what
we had in mind. We would like to find 17 in TOS when [B]SEVENTEEN[/B] is
named. The word [B]DOES>[/B] gives us the tool to do this.
The function of [B]DOES>[/B] is to specify a run-time action for the “child”
words of a defining word. Consider the defining word [B]CONSTANT[/B] ,
defined in high-level (of course [B]CONSTANT[/B] is usually defined in machine
code for speed) Forth by [COLOR=red]
: CONSTANT CREATE , DOES> @ ; [/COLOR]
and used as [COLOR=red]
53 CONSTANT PRIME[/COLOR] <cr> ok
Now test it:
[COLOR=red]PRIME .[/COLOR] <cr> 53 ok .
What is happening here?
> [B]CREATE[/B] (hidden in [B]CONSTANT[/B]) makes an entry named [B]PRIME[/B]
(the first word in the input stream following [B]CONSTANT[/B]). Then "[B][COLOR=red],[/COLOR][/B]"
places the TOS (the number 53) in the next cell of the dictionary.
> Then [B]DOES>[/B] (inside [B]CONSTANT[/B]) appends the actions of all words
between it and "[B][COLOR=red];[/COLOR][/B]" (the end of the definition) —in this case, "[B][COLOR=red]@[/COLOR][/B]"—
to the child word(s) defined by [B]CONSTANT[/B].
in TACHYON there is CREATE ( precompiled kernel code )
which creates the dictionary entry,
reads the next word as a name for the newly created word
and then assigns the default BEHAVIOR - which is the code of VARB - makes it a variable,
means, it just puts it's address on the stack at runtime.
' CREATE <name> - Create a name in the dictionary and also a code entry
CREATE byte REG,createvec,WFETCH,QDUP,_IF,01,AJMP ' execute extended or user CREATE?
byte XCALL,xCREATEWORD '
byte _BYTE,VARB,XCALL,xBCOMP,_0 ' set default bytecode as a VARIABLE
' ALLOT ( bytes -- )
ALLOT byte REG,codes,WPLUSST
ALLOCATED
byte REG,codes,WFETCH,REG,here,WSTORE
byte EXIT
so a DOES> could just create a dummy word, compile the code following DOES> to it
AND then put the new byte code to the previous definitions PFA.
But I do not see the relation to the new PRINT" word.
I think PRINT" is not a normal defining word since it does not define a new word each time it is called,
as compared to what VARIABLE or CONSTANT do.
To me it belongs into the category of compiler extension words,
like the IF DO LOOP etc. words.
Those have a compile time behavior (like PRINT" copying the chars to EE) and leave BYTECODES
in the compiled code (like PRINT" retrieving the chars from EE and printing them ).
I don't feel having a solid grasp on CREATE DOES> so maybe discussing it here a little helps.
I've been reading through material on CREATE DOES> as well. This is very powerful and I can see it as a way to create arrays that have indexing methods, create words that parse themselves etc.
Also when I stumble upon syntax such as this I am humbled by its eligance and brevity although the mental gymnastics of pushing a few parameters around in headspace can be hard for the beginner it is getting easier all the time.
So I am not qualified to give input on an approach for CREATE DOES> implementation in Tachyon, yet!
--- revision 140602 - Added timeout loop counter to prevent hanging
pub @EEWAIT ( adr -- )
#50 BEGIN 1- OVER @EE 0= OVER 0= OR UNTIL 2DROP
;
I have a version of Tachyon running on the spinneret with the appropriate Wiznet.fth module. The module works properly. I have a K30 CO2 senor connected to the I2C bus same as standard EEPROM.
I have words that access the sensor correctly. I can redirect output via the Lan connection just fine, but as soon as I issue any I2C commands i.e. I2CSTART, the networking hangs completely. I don't see any conficts in the ESPIO section in the source or in the Wiznet.fth driver.
?
Thanks
Propeller .:.:--TACHYON--:.:. Forth V21130620.0000
MODULES LOADED:
2ED5: K30.fth Air Sense C02 Sensor Driver 140602-11:00
2B31: WIZNET.fth WIZNET 5100/5200 driver 130216.1500
1880: EXTEND.fth Primary extensions to TACHYON kernel - 130722.0000
ok
Here's the code in question, you can see the debugging in BEGIN UNTIL loop. Commenting out the I2CSTART I2CSTOP line and the networking runs correctly.
The rc2 word that reads the sensor works correctly as well.
pub lanC
#27 #26 I2CPINS \ set i2c pins other than eeprom - conflict testing
DECIMAL \ set us in decimal mode
CR ." Setting up networking.... " CR
!WIZIO \ initialize the wiznet SPI setup for spinnerette
#20 ms
&192.168.10.1 GATEWAY \ set the gateway
&255.255.255.0 SUBNET \ subnet
&192.168.10.80 SRCIP \ source ip
$00.08 $DC.01.02.03 SHAR \ mac
#50 ms
0 SOCKET CLOSE \ select socket 0 and force it into life with CLOSE
#50 ms
UDP \ enable socket MODE UDP
#33333 PORT \ source port
#22222 DPORT \ destination port 22222
&192.168.10.10 DIP \ destination ip
#50 ms
OPEN \ open the socket
#50 ms
lEmit \ redirect output to lan
BEGIN
I2CSTART I2CSTOP
\ rc2 DROP \ get a co2 reading
\ 0= IF CO2? W@ DUP #10000 < \ if the ack flag from the IC2 write is zero make sure value < #10K
\ IF co2$ .STR CO2? W@ . CR \ then write the banner and the value
co2$ .STR CO2? W@ . CR \ then write the banner and the value
\
\ ELSE DROP \ num > #10K drop the dup
\ THEN
\ THEN
#1500 ms
ESC? UNTIL \ esc key pressed ?
CON \ redirect back to console
EEPROM \ set i2c pins back to eeprom
;
On the subject of DOES>, pfth includes a 16-bit address in the dictionary entry for each word that points to the DOES> code. This value is normally set to zero unless DOES> is specified. The variable kernel function will load the address of the word's body onto the stack, and then jump to the DOES> code if the pointer is non-zero. You may want to have two different variable kernel functions where one is used when DOES> is specified and the other is used when DOES> is not specified.
I have a version of Tachyon running on the spinneret with the appropriate Wiznet.fth module. The module works properly. I have a K30 CO2 senor connected to the I2C bus same as standard EEPROM.
I have words that access the sensor correctly. I can redirect output via the Lan connection just fine, but as soon as I issue any I2C commands i.e. I2CSTART, the networking hangs completely. I don't see any conficts in the ESPIO section in the source or in the Wiznet.fth driver.
I see that you are using an earlier kernel and I think that since I2C uses the CLOCK word which uses a mask in a COGREG to toggle the SCL line that this also overwrites one of the SPIO COGREGs. V2.3 uses a separate COGREG for CLOCK but maybe as a quick hack you could patch your I2C routines by replacing any reference to CLOCK sequences such as CLOCK NOP CLOCK with SCL PINSET SCL PINCLR just as a quick suggestion. The timing is a little slower but that shouldn't matter.
I see that you are using an earlier kernel and I think that since I2C uses the CLOCK word which uses a mask in a COGREG to toggle the SCL line that this also overwrites one of the SPIO COGREGs. V2.3 uses a separate COGREG for CLOCK but maybe as a quick hack you could patch your I2C routines by replacing any reference to CLOCK sequences such as CLOCK NOP CLOCK with SCL PINSET SCL PINCLR just as a quick suggestion. The timing is a little slower but that shouldn't matter.
You are correct, all is well in spinneret land, thanks.
This comment made me not question any conflicts in the older kernel.
130605 Modified the CLOCK instruction which is used by I2C routines to use COGREG 4 as the clock mask rather than 0 to prevent conflict with SPI routines which may be operating at the same time.
\MJB thoughts about create does> for Tachyon
{
: CONSTANT CREATE , DOES> @ ;
and used as
53 CONSTANT PRIME <cr> ok
Now test it:
PRIME . <cr> 53 ok .
}
: ` [COMPILE] [COMPILE] ;
: mjbCREATE
\ will be stored in the ucreate vector so can be used by new code ...
\ not sure if this is good ... or need to use another name since create and CREATE are in use already ...
\ maybe Create ;-)
` CREATEWORD
\ insert CFA of NEXT definition - which will be created by does>
\ so either does> does the substitution and places it's newly created dummy word PFA here,
\ or we start from this word and search at runtime for the next words PFA
\ then continue compiling the create part of the create does> pair.
\ DOES> first performs a ; to finish the definition, the CREATE is part of.
\ then DOES> starts a new dummy definition pri dummyXYZ
\ and stores the PFA into the previous definition, so at each execution of the creation of a new
\ child word, the child words PFA gets set correctly.
\ can the dummy definitions NAME be removed from the dictionary already now, before it is completed ???
\ now all code following DOES> gets compiled into the dummy definition
\ which is automatically completed with the next ;
I have a version of Tachyon running on the spinneret with the appropriate Wiznet.fth module.
Propeller .:.:--TACHYON--:.:. Forth V21130620.0000
MODULES LOADED:
2ED5: K30.fth Air Sense C02 Sensor Driver 140602-11:00
2B31: WIZNET.fth WIZNET 5100/5200 driver 130216.1500
1880: EXTEND.fth Primary extensions to TACHYON kernel - 130722.0000
ok
<snip>
hi D.P. I have an idle Spinneret sitting here waiting for Tachyon as well :-)
I want to use it as the internet gateway to my sensor/actor network I am setting up here.
The kernel, Extend and Wiznet versions are from DROPBOX ?
Original or did you have to adapt things?
As I understand the WIZNET.fth for W5100 does not run with Tachyon2.3 ??
That's why I postponed my Spinneret experiments atm. ( busy with construction site, house in the mountains ..)
Thanks MJB
hi D.P. I have an idle Spinneret sitting here waiting for Tachyon as well :-)
I want to use it as the internet gateway to my sensor/actor network I am setting up here.
The kernel, Extend and Wiznet versions are from DROPBOX ?
Original or did you have to adapt things?
As I understand the WIZNET.fth for W5100 does not run with Tachyon2.3 ??
That's why I postponed my Spinneret experiments atm. ( busy with construction site, house in the mountains ..)
Thanks MJB
I will revisit the W5100 and see if I can modify W5200.fth to work with it. That way it will just fit in with all the new stuff, even the microSD.
I was going to try implementing DOES> in Tachyon, but I can't seem to find the Tachyon source code. I have been able to download it before, but I can't recall where I got it. Every link I click on sends me to web pages with lots of documentation on Tachyon, and I can't find a link for the source. The link is probably buried somewhere in the documentation. Does anybody know the direct link to the source code?
I was going to try implementing DOES> in Tachyon, but I can't seem to find the Tachyon source code. I have been able to download it before, but I can't recall where I got it. Every link I click on sends me to web pages with lots of documentation on Tachyon, and I can't find a link for the source. The link is probably buried somewhere in the documentation. Does anybody know the direct link to the source code?
Yes, the intro points to the links page and it's in there but here it is as well
Here are a few words that I use to implement DOES> in the Fast forth interpreter. The word DOES> is used only during compilation, and it inserts the word _DOES into the compiled word, which is a runtime word. _DOES is followed by EXIT, and then followed by the code defined after DOES>. The _DOES word will add a pointer to this code to the header of the last word defined.
\ WORD HEADER ACCESSORS
: >does 1+ ;
: >body 4 + ;
: link>does link>xt 1+ ;
: link>body link>xt 4 + ;
\ DEFINE DOES> WORDS
: cc! over 8 rshift over c! 1+ c! ;
: _does r> dup >r 1+ last @ link>does cc! ;
: does> compile _does [compile] exit ; immediate
\ WORDS THAT USE DOES>
: marker create last @ , does> @ dup dp ! @ last ! ;
: defer create 0 , does> @ execute ;
: value create , does> @ ;
: 2constant create swap , , does> dup @ swap cellsize + @ ;
Here's the Fast Forth PASM code that implements the kernel code used for words that are created using CREATE. It loads the address of the body onto the stack, and then checks for a non-zero pointer. If the pointer is zero it just exits the word. Otherwise, it jumps to the DOES> code by setting the PC equal to the pointer. To prevent normal variables by being slowed down by the DOES> code you use a kernel word that does not support the pointer, and have another kernel word that is only used for words that do have a DOES> pointer. CREATE could default to the non-does kernel word, and the DOES> word could replace it with the other word that supports DOES>.
_var wrlong nos, dstackptr
mov nos, tos
mov tos, pc
rdbyte temp1, pc
add pc, #1
add tos, #3
rdbyte pc, pc
shl temp1, #8
or pc, temp1 wz
add dstackptr, #4
if_z jmp #exit
jmp #innerloop
Comments
I have a basic version of the 1-Wire ROM search procedure running.
And while cleaning up I came across a point, that I come across every now and then.
It is the missing namespaces in Tachyon, to make sure that I don't accidentally overwrite
an existing word.
Besides the effort of a technical solution, there would be a very simple
organisational solution.
Like prefixing all the words of the package with a code like '1W.'
then it would be clear for everybody, and no name conflicts could occure.
It would cost a few chars more in name space dictionary, but since most words already contain 1W in the name
it would be only the '.'.
so 1W.RESET instead of 1WRESET ...
what do you think?
Markus
Peter you can by all means cut and paste the code to place it in the Tachyon dropbox as you see fit.
Thanks for the review, still learning.
Updated:
I have no SPI HW set up, so I can not test it here ATM
p.s. I changed the title to s.th. meaningful
ALL:
unfortunately most writers just leave the previous post title, so the title says nothing about the new post itself - this is bad for searching later - so I'd like to encourage use of meaningful post titles !! thanks MJB
I've been away for a while taking care of non-code and chip kinds of things. Recently working real hard on learning to use my new AdaFruit TI-ADS1115 16 bit ADC breakout. Yep, I dumped the DMM idea. The points against started adding up too fast.
The TI-ADS1115 is my first foray into the I2C world. Thanks to Peter and Tachyon the lowest level stuff was not hard.
Here is the code I have done so far:
If anyone sees where it could be improved - have at it. If it is useful to anyone . . . enjoy.
I have also written a Excel sheet to assist in compiling the configuration value . . . which I have just attempted to attach and get an "Invalid File" error. Does the Forum not allow *.xlsx files? It sure is useful to me.
I imagine this has been done before but I didn't find it so I did it again. Good practice.
"2s>R" might not be the best name.
Should it be called something else?
Oops!
Hi Art,
I saw your post in the other thread with Tracy responding with the spin objects he wrote for the chip (ADS1111).
Looking forward to see how you go about the "math" that Tracy provided.
I'm sure as soon as Peter gets some more Tachyon cycles we will see F32 as a loadable module, I plan to just use this in an empty cog as a full time floating point math engine
since I want to calculate saturation dew point temperatures and hence RH using standard saturation vapor point equations.
Hi Art, I had a look at this code when you first posted it but I've been a bit busy since, as always. The code looks clean and tidy and the only real critique I would have is that you should always standardize the get and put method as you have ADC16! requiring a 16-bit word for the data yet ADC16@ returns with that same word split into two bytes. Here's my take on your original code.
I see the beginnings of CREATE DOES in the comments section of tachyon 06/02, hmmm.
My EPRINT routine which stores literal print strings to spare EEPROM instead of program memory is the reason I'm looking into it.
TF is setup for compactness as well as speed and certain decisions were made at the time in this regard. I'm not quite sure why I ended up using bytecode rather than a pointer in the header field but this made sense since bytecodes are compiled, not pointers. Some bytecode are calls via a vector table which then points to the code, once again this keeps the actual code compact at 2 bytes rather than 3 but adds the overhead of a table. The advantage of the table is that addresses can be extended to beyond 64K and still compile no more than 2 bytes, something that makes the transition to P2 easier.
So back to the CREATE DOES, I trying to think how best to implement this in TF given it's internal quirks. If you see a way that works, such as in EPRINT then by all means please suggest it!
I've just trimmed the kernel again by removing some fast constants such as 5,6,7 and optimizing the code. I added >N and >B to perform quick nibble and byte masking ops as it is quite common to say $0F AND or $FF AND but this would take 3 bytes and 2.6us but now uses only a single byte and takes 400ns.
EDIT: Just thought of another way to generate fast literals, so instead of adding to an accumulator I use the pasm MIN instead to load the literal accumulator ACC which is normally zeroed and then set the carry which is normally cleared on every bytecode. So this works a treat and lets me put in any value in any order.
Neat!
I have a few spinnerettes that I would like to use. Do you have any backups of Tachyon V2.1 and W5100.fth that support UDP clients? I have tried links on the drop box but couldn't get any UDP client "action" to work. I have telnet, ftp working but no UDP client. My current build is:
I can't find what I did to get UDP clients working during this timeframe.
UPDATE: oh snap, the version of W5100 driver doesn't have these two helper words to set destination and port Got confused with the UIP and UPORT words. All better, it's funny how just posting a question to the forum help to resolve issues without any feedback!
CREATE DOES> is used to create so called DEFINING WORDS like
: VARIABLE CONSTANT and the like.
in TACHYON there is CREATE ( precompiled kernel code )
which creates the dictionary entry,
reads the next word as a name for the newly created word
and then assigns the default BEHAVIOR - which is the code of VARB - makes it a variable,
means, it just puts it's address on the stack at runtime.
so a DOES> could just create a dummy word, compile the code following DOES> to it
AND then put the new byte code to the previous definitions PFA.
But I do not see the relation to the new PRINT" word.
I think PRINT" is not a normal defining word since it does not define a new word each time it is called,
as compared to what VARIABLE or CONSTANT do.
To me it belongs into the category of compiler extension words,
like the IF DO LOOP etc. words.
Those have a compile time behavior (like PRINT" copying the chars to EE) and leave BYTECODES
in the compiled code (like PRINT" retrieving the chars from EE and printing them ).
I don't feel having a solid grasp on CREATE DOES> so maybe discussing it here a little helps.
I've been reading through material on CREATE DOES> as well. This is very powerful and I can see it as a way to create arrays that have indexing methods, create words that parse themselves etc.
Also when I stumble upon syntax such as this I am humbled by its eligance and brevity although the mental gymnastics of pushing a few parameters around in headspace can be hard for the beginner it is getting easier all the time.
So I am not qualified to give input on an approach for CREATE DOES> implementation in Tachyon, yet!
I have words that access the sensor correctly. I can redirect output via the Lan connection just fine, but as soon as I issue any I2C commands i.e. I2CSTART, the networking hangs completely. I don't see any conficts in the ESPIO section in the source or in the Wiznet.fth driver.
?
Thanks
Here's the code in question, you can see the debugging in BEGIN UNTIL loop. Commenting out the I2CSTART I2CSTOP line and the networking runs correctly.
The rc2 word that reads the sensor works correctly as well.
I see that you are using an earlier kernel and I think that since I2C uses the CLOCK word which uses a mask in a COGREG to toggle the SCL line that this also overwrites one of the SPIO COGREGs. V2.3 uses a separate COGREG for CLOCK but maybe as a quick hack you could patch your I2C routines by replacing any reference to CLOCK sequences such as CLOCK NOP CLOCK with SCL PINSET SCL PINCLR just as a quick suggestion. The timing is a little slower but that shouldn't matter.
You are correct, all is well in spinneret land, thanks.
This comment made me not question any conflicts in the older kernel.
I want to use it as the internet gateway to my sensor/actor network I am setting up here.
The kernel, Extend and Wiznet versions are from DROPBOX ?
Original or did you have to adapt things?
As I understand the WIZNET.fth for W5100 does not run with Tachyon2.3 ??
That's why I postponed my Spinneret experiments atm. ( busy with construction site, house in the mountains ..)
Thanks MJB
I will revisit the W5100 and see if I can modify W5200.fth to work with it. That way it will just fit in with all the new stuff, even the microSD.
There's a link to the Tachyon Dropbox in Peter's signature.