Below is a test program where I am trying to use the familiar high() and low() commands. The program compiles without any errors, but there is no flashing of the LED on pin 26.
If Eric does not mind, I am calling this BASIC, pBas or pBAS.
Will pBas provide an #include provision? I am thinking that, as an example, the high, low, and pausems commands could be placed in a lib, so you only code it once, and have it called within your program.
Ray
rem test2.bas
let mscycles = clkfreq / 1000'*******************************'Main
print "This is the test2.bas program"
do
high 26
pausems 500
low 26
pausems 26
loop
'*******************************sub high(pin)
dim pin%
direction(pin) = output
endsubsub low(pin)
dim pin%
direction(pin) = input
endsubsub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsub
Below is a test program where I am trying to use the familiar high() and low() commands. The program compiles without any errors, but there is no flashing of the LED on pin 26.
The second pausems is for only 26 ms. Is that what you intended?
Also, your high() and low() functions are changing the direction, not the output value. They really should be something like:
sub high(pin)
output(pin) = 1endsubsub low(pin)
output(pin) = 0endsub
And then in your main code do
direction(pin) = output
once to set the pin as an output.
If Eric does not mind, I am calling this BASIC, pBas or pBAS.
Probably "fBas" would be a better abbreviation, to avoid confusion with PropBasic.
Will pBas provide an #include provision?
It already does, actually. I need to write documentation for the preprocessor, but it supports #include, #define, #ifdef, #elseifdef, and #endif.
Besides #include (which just literally includes one file into another) there is also "class using filename", which lets you include files as objects, similar to the OBJ statement in Spin. "class using" works with both Spin and BASIC files.
I get a compile error. I tried adding 'dim pin%', but that did not work.
Ray
rem test2.bas
let mscycles = clkfreq / 1000
direction(pin) = output
'*******************************'Main
print "This is the test2.bas program"
do
high 26
pausems 500
low 26
pausems 500
loop
'*******************************sub high(pin)
output(pin) = 1endsubsub low(pin)
output(pin) = 0endsubsub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsub
G:/fastspin/spin2gui/bin/fastspin -l -O1 -L ./lib G:/fastspin/programs/test2/test2.bas
Propeller Spin/PASM Compiler 'FastSpin' (c) 2011-2018 Total Spectrum Software Inc.
Version 3.9.5-beta Compiled on: Oct 11 2018
test2.bas
G:/fastspin/programs/test2/test2.bas(5) error: Unknown symbol pin
child process exited abnormally
Thanks for testing it Ray. It sounds like I need to find a newer version of propeller-load that can handle WiFi.
proploader knows how to do wifi downloads. However, it doesn't handle XMM programs. I suspect that won't be a problem for you though.
Thanks David! I'll switch to proploader for the next version.
proploader defaults to trying to do a wifi load. You have to specify -s on the command line to get it to do serial loads. In retrospect, that might not have been the best default but proploader was written for the WX wifi module originally and later expanded to include serial support.
I get a compile error. I tried adding 'dim pin%', but that did not work.
Use "direction(26) = output". Or you could add
const ledpin = 26
at the beginning of your code so that it's easy to change the pin. For example:
rem blinker: blink an ledconst ledpin = 16' set the led pin as an output
direction(ledpin) = output
print "This is the LED blink test program"' loop blinking the leddo
high ledpin
pausems 500
low ledpin
pausems 500loopsub high(pin)
output(pin) = 1endsubsub low(pin)
output(pin) = 0endsub
The most recent version of fastspin has pausems as a builtin function so you probably don't need to define that, but your definition looked OK and you can use it if you like
Also note that 'dim pin%' declares a variable called 'pin%' and is different from 'dim pin'. I think that may be an area where fastspin differs from qbasic and freebasic.
I was trying to get around the use of 'const ' . The way it is now, you have to pre-define the pins that you will be using. In some ways this is like working with spin, where you deal with the pin defs up front. I was thinking along the lines of PropGCC, where you have pre-definitions under the hood, so all you do is high(26) or low(26), and it works.
I am liking the spin2gui program, although some improvements would be nice. One would be, a Load command, where it writes the program to the EEPROM. The other is, you do not have any support for .bas in terms of creating a or saving a .bas file.
I was trying to get around the use of 'const ' . The way it is now, you have to pre-define the pins that you will be using. In some ways this is like working with spin, where you deal with the pin defs up front. I was thinking along the lines of PropGCC, where you have pre-definitions under the hood, so all you do is high(26) or low(26), and it works.
Well, you could put a 'direction(pin) = output' line in both high() and low(), before setting the value of output(pin). I think that may be what the PropGCC functions do. It's a little less efficient, because it means you're constantly setting the direction when it's only necessary to set it once.
I am liking the spin2gui program, although some improvements would be nice. One would be, a Load command, where it writes the program to the EEPROM. The other is, you do not have any support for .bas in terms of creating a or saving a .bas file.
The program below compiles and runs as expected. The next program, I will try to have pin 27 LED blinking in the Main, and I will try to get pin 26 LED to blink in a cpu(COG).
I noticed that pBas does not have any break or exit commands. What would be the method for getting out of a while loop.
Ray
rem test2.bas
' October 12, 2018' General use of pBas compiler' Define clock freq
let mscycles = clkfreq / 1000' Variables
dim pin
'*** Subs & Functions ***sub high(pin)
direction(pin) = output
output(pin) = 1' LED is onendsubsub low(pin)
direction(pin) = output
output(pin) = 0' LED is offendsubsub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsub'*******************************'Main
print "This is the test2.bas program"
do
high 27' Pin 27 on
pausems 500
low 27' Pin 27 off
pausems 500
loop
'*******************************
The program below compiles and runs as expected. Not sure as to how to use the stack, I dim it one size and the usage is of a different size, but it works.
Does, or will, pBas have inline pasm? I am wondering as to the best way for pBas to utilize all the Spin driver code that is available. I think JonnyMac had written quite a few drivers in pasm, but there also is quite a few drivers that are written in Spin. One large driver, that could be of great use, is the SD driver, which is in Spin.
Since Parallax has the Activity Board WX, and the FLiP module, maybe there should be an effort made to start trying to create some usage of the different items for the these platforms. For the Activity Board WX: the SD, WiFi, ADC, sound, motor driver, …, etc. Other items of interest could be the RTC, Temp/humidity module and others.
Ray
rem test3.bas
' October 12, 2018' pBas cpu(COG) usage.' Are these declarations all Global?' Define clock freq
let mscycles = clkfreq / 1000' Variables
dim pin
'*** Subs & Functions ***sub high(pin)
direction(pin) = output
output(pin) = 1' LED is onendsubsub low(pin)
direction(pin) = output
output(pin) = 0' LED is offendsubsub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsub'*******************************'Main
dim stack(4) 'This is the minimal to run the cpu job1' Start the cpu(COG)var a = cpu(job1(26), @stack(1)) ' Not sure what size @stack(1) is
do
high 27' Pin 27 on
pausems 500
low 27' Pin 27 off
pausems 500
loop
'*******************************' This is the cpu(COG) job.sub job1(pin)
do
high pin ' Pin 27 on
pausems 300
low pin ' Pin 27 off
pausems 300
loop
endsub'*******************************
I noticed that pBas does not have any break or exit commands. What would be the method for getting out of a while loop.
The usual way would be to have the while loop test for a variable, and to set that inside the loop. For example, an (ugly) way to print some integers is:
var done = 0' indicates loop is finishedvar i = 1
do
print i
i = i + 1if (i > 4) done = 1
print "still looping"
loop until done <> 0
You could also use a goto:
var i = 1do
print i
i = i + 1if (i > 4) gotoout
print "still looping"
loop
out:
The program below compiles and runs as expected. Not sure as to how to use the stack, I dim it one size and the usage is of a different size, but it works.
The @stack(1) just returns a pointer to the first element of the stack array (remember that in fastspin BASIC array indices start at 1, not 0).
Does, or will, pBas have inline pasm? I am wondering as to the best way for pBas to utilize all the Spin driver code that is available.
fastspin supports inline assembly between "asm" and "endasm". But if you want to use Spin, you can import it directly with "class using". (The compiler is called "fastspin" after all, and that was the first language it supported -- the BASIC is a new addon!)
For example, to use the FullDuplexSerial.spin driver from a BASIC program you can do:
' create a FullDuplexSerial object named fds' this is just like the Spin command OBJ fds : "FullDuplexSerial.spin"dim fds asclassusing"FullDuplexSerial.spin"' initialize the fds object' WARNING: we're choosing to use pins 31 and 30 here, which' will conflict with the built-in print commands. Don't try to' use print after the fds.start. If we used other pins (to communicate' with some device, for example) then this wouldn't be an issue
fds.start(31, 30, 0, 115_200)
do
fds.str("hello, world!")
fds.tx(13)
fds.tx(10)
loop
"class using" accepts either a BASIC or Spin file.
I think I read somewhere that pBas will be able to be used with the P2 chip. I also think I read that pBas, at the moment is set up to run in LMM. What is available now is COG Ram, HUB Ram, and for the P2, HUB Execute. How will pBas be able to use these specific Ram areas? Will you have something like a setting in pBas:
TMM = Cog Ram
LMM = Hub Ram
XMM = P2 Hub Execute
fastspin defaults to hubexec in P2 mode (if the -2 switch is given) and LMM in P1 mode. If you use the --code=cog option then it will compile the code to run in COG memory in both cases.
I think I read somewhere that pBas will be able to be used with the P2 chip. I also think I read that pBas, at the moment is set up to run in LMM. What is available now is COG Ram, HUB Ram, and for the P2, HUB Execute. How will pBas be able to use these specific Ram areas? Will you have something like a setting in pBas:
TMM = Cog Ram
LMM = Hub Ram
XMM = P2 Hub Execute
Ray
Ahem....'t ain't called "pBas", it's called fastspin(BASIC).
OK fastspin(BASIC), it is, until Eric gives it an official name.
Since I have a WX Activity Board with DS1302 module installed, I thought I would try doing a real use program with fastspin(BASIC).
The program below compiles without complaints, but the value is showing up as 0, it should be 13, todays date. I believe that in the Spin program, the value(s) is presented as a byte. So, I am not sure if fastspin(BASIC) is doing a correct print of the day1 variable. Have not done anything in Spin, in the last ten years, so I could be wrong with my interpretation.
I attached the DS1302.spin program, for easy referencing.
Ray
test_rtc.bas
rem test_rtc.bas' October 13, 2018' Test of the RTC on the WX Activity Boarddim rtc asclassusing"DS1302_full.spin"let mscycles = clkfreq / 1000sub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsubdim day1 asbytedim month1 asbytedim year1 asbytedim dow1 asbytedim day
dim month
dim year
dim dow
rtc.init(10,11,9)
rtc.readDate(day1, month1, year1, dow1)
'day1 = daydo
print "day", day1
pausems 3000loop
ds1302_full.spin
{{ DS1302_full.spin
┌─────────────────────────────────────┬────────────────┬─────────────────────┬───────────────┐
│ DS1302 driver v1.3 │ BR │ (C)2010 │ 8Jul2010 │
├─────────────────────────────────────┴────────────────┴─────────────────────┴───────────────┤
│ │
│ A full-featured DS1302 timekeeping chip driver. Based on the original object by │
│ Daniel Robert. │
│ │
│ Notes: │
│ •Original object here: http://obex.parallax.com/objects/89/ │
│ •12 hour mode is not supported in this object. The reason is that it would add │
│ significant complexity to this object for minimal benefit. A better solution is │
│ to handle the 12/24 hour conversion in software outside of this object if needed. │
│ Okay. So it is nearly full-featured. │
│ •The DS1302 data sheet specs the maximum frequency on the clk pin as: │
│ 0.5MHz@2V, 2MHz@5V --> 1.5MHz@3.3V (1.5 uS per clock, 120 ticks@clkfreq=80MHz) │
│ Original object had a 2 uS pause between clock pin transitions. However, it seems to │
│ work fine with no wait @ clkfreq=80MHz...apparently spin can't outrun the DS1302. │
│ Not tested at clkfreq=100MHz. │
│ •Experimented with trickle charger using 1 diode+2K resistor setting. 10µF │
│ electrolytic cap kept DS1302 powered up for >10 minutes. Implies a 1000µF should be good │
│ for ~16 hrs of backup power...enough to last through a typical brown-out or black-out │
│ (if leakage current and wide tolerance band of electrolytics doesn't kill it first) │
│ •Note that this device needs to have a 32.768KHz crystal with 6pF capacitance in order to │
│ keep accurate time: http://forums.parallax.com/forums/default.aspx?f=25&m=467246 │
│ │
│ See end of file for terms of use. │
└────────────────────────────────────────────────────────────────────────────────────────────┘
DEVICE PINOUT & REFERENCE CIRCUIT
┌────┐ 1000µF
3.3V │ │ backup_Vin ── to gnd
xtal xi │DS1302│ clk ─── to prop pin
32.768 KHz xo │ │ io ─ 1KΩ resistor to prop pin
┌│ │ ce ─── to prop pin
GND └──────┘
TRICKLE CHARGER NOTES
'Trickle charger setup tc_enable diodeSel resistSel
' | | |
' write(command(clock,tc,w),(%1010 << 4) + (2 << 2)+ ( 3 ))
Diode select register bits: 00 = trickle charger disabled
01 = 1 diode enabled
10 = 2 diodes connected in series (more voltage drop when in charging mode)
11 = trickle charger disabled
Resistor select bits: 00 = no resistor 0Ω
01 = R1 2KΩ
10 = R2 4KΩ
11 = R3 8KΩ
'Examples of other useful config and control commands
'write(command(clock,sec,w),read(command(clock,sec,r))&%1000_0000) 'set clock halt bit
'write(command(clock,ctrl,w),%1000_0000) 'set write-protect bit
'write(command(clock,hr,w),read(command(clock,hr,r))&%1000_0000) 'set 12h mode
V1.1 - 27Oct09 fixed mis-labeled pins in docs (clk and io were swapped in 1.0)
V1.2 - 28Mar10 fixed bug in config method that caused seconds to be reset (thanks, Doug!)
v1.3 - 26Jun10 cleanup and additional documentation
}}
CON'command byte options
#0, clock,ram
#0, sec, mi, hr, day, mo, dow, yr, ctrl, tc, #31, burst
#0, w,r
VARbyte clk 'clockbyte io 'data iobyte ce 'chip enablebyte datar
PUBcommand(clock_or_ram, register, r_or_w)''Returns a DS1302 command byte''clock_or_ram : select clock or ram (allowed values: clock, ram)''register : register name (allowed values: 0-30, sec, mi, hr, day, mo, dow, yr, ctrl, tc, burst)''r_or_w : read or write (allowed values: w, r)''Usage : cmd:=command(rtc#ram, 21, rtc#w) --> write to RAM byte 21'' cmd:=command(rtc#clock, rtc#min, rtc#r) --> read minute registerreturn (1<<7) + (clock_or_ram<<6) + (register<<1) + r_or_w
PUBinit( inClk, inIo, inCe )''Initialize propeller serial com channel with DS1302. Call once after prop power-up.''Usage: rtc.init(clk_pin, io_pin, chipEnable_pin)
clk := inClk 'save pin numbers
io := inIo
ce := inCe
dira[ce]~~ 'configure chip enable pinouta[ce]~
dira[clk]~~ 'configure clock pinouta[clk]~
PUBconfig''Config DS1302. Call once after DS1302 power-up.''Usage: rtc.config
write(command(clock,ctrl,w),0) 'clear write-protect bit
write(command(clock,sec,w),read(command(clock,sec,r)) & %0111_1111) 'clear halt bit
write(command(clock,hr,w),read(command(clock,hr,r)) & %0111_1111) 'set 24hr mode
write(command(clock,tc,w),0) 'disable trickle chargerPUBsetDatetime( _mth, _day, _year, _dow, _hr, _min, _sec )''Set date and time
write($8c, bin2bcd( _year ) )
write($8a, _dow )
write($88, bin2bcd( _mth ) )
write($86, bin2bcd( _day ) )
write($84, bin2bcd( _hr ) )
write($82, bin2bcd( _min ) )
write($80, bin2bcd( _sec ) )
PUBreadDate( _day, _mth, _year, _dow )''Read current date (day, month, year, day-of-week)''Usage: rtc.readDate( @day, @month, @year, @dow )byte[_year] := bcd2bin( read($8d) )
byte[_mth] := bcd2bin( read($89) )
byte[_day] := bcd2bin( read($87) )
byte[_dow] := bcd2bin( read($8b) )
PUBreadTime( _hr, _min, _sec ) | tmp1, tmp2''Read current hour, minute, second''Usage: rtc.readTime( @hour, @minute, @second )byte[_hr] := bcd2bin( read($85) )
byte[_min] := bcd2bin( read($83) )
byte[_sec] := bcd2bin( read($81) )
Pubread( cmd ) | i''Read a byte of data per command byteouta[ce]~~
writeByte( cmd )
dira[io]~ 'set to input
readByte
outa[ce]~
return(datar)
Pubwrite( cmd, data )''Write a byte of data per cmd byteouta[ce]~~
writeByte( cmd )
writeByte( data )
outa[ce]~
PUBwriteN(cmd, dataPtr, n)|i''Write a stream of n bytes from byte array pointed to by dataPtrouta[ce]~~
writeByte( cmd )
repeat i from0to n
writeByte(byte[dataPtr][i])
outa[ce]~
PubreadN(cmd, dataPtr, n)|i''Read a stream of n bytes of data and put in byte array pointed''to by dataPtrouta[ce]~~
writeByte( cmd )
dira[io]~ 'set to inputrepeat i from0to n
readByte
byte[dataPtr][i]:=datar
outa[ce]~
PRIreadByte|i
datar~
repeat i from0to7ifina[io] == 1
datar |= |< i ' set bitouta[clk]~~
outa[clk]~
PRIwriteByte( cmd ) | i dira[io]~~ 'set to output repeat i from0to7outa[io] := cmd
outa[clk]~~
cmd >>= 1outa[clk]~
PRIbin2bcd(dataIn) | tmp'Convert a byte of binary data to binary coded decimal
tmp:= dataIn/10result := dataIn - ( tmp * 10 ) + ( tmp << 4 )
PRIbcd2bin(dataIn)'Convert a byte of binary coded decimal data to binaryresult := (dataIn & %00001111) + ((dataIn >> 4) & %00001111)*10DAT{{
┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 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. │
└─────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}
You're right that the day, month, year, etc. have to be bytes. You missed the comment in the Spin file that the readDate and readTime functions want the addresses of the bytes:
Adding the "@" did the trick, now the program runs as expected. This fastspin(BASIC) is starting to really grow on me.
I also did a program code size comparison, fastspin(BASIC) - 6616; SimpleIDE PropGCC - 6576. The PropGCC version was derived from DS1302_full.spin.
The next thing that I will try is CM2302 module, the Spin code for that looks a little more confusing. It works best with float variables, which fastspin(BASIC) can handle.
The next thing up would be to write the date/time and the temp/humidity to the SD card. But, I noticed that the open command and some sort of read/write to the SD card or file, are not available. So, this just might have to wait.
Ray
rem test_rtc.bas' October 13, 2018' Test of the RTC on the WX Activity Boarddim rtc asclassusing"DS1302_full.spin"let mscycles = clkfreq / 1000sub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsubdim day1 asbytedim month1 asbytedim year1 asbytedim dow1 asbytedim minutes asbytedim hour asbytedim seconds asbytedim dow
rtc.init(10,11,9)
do
rtc.readDate(@day1, @month1, @year1, @dow1)
rtc.readTime(@hour,@minutes,@seconds)
print month1;"/";day1;"/";"20";year1;" ";hour;":";minutes;":";seconds
pausems 3000loop
This one is a little more confusing. I think the DHTnn_Object.spin starts a COG, so I am not sure what happens within fastspin(BASIC). I think the starting of the COG is essential for the program to show the correct results. Right now the test_temp.bas compiles and runs, but it is showing 0.000 for the temp. Not sure what is going on.
Yep, fastspin(BASIC) ROCKS!
Ray
test_temp.bas
rem test_temp.bas' October 13, 2018' Test of the CM2302 module on the WX Activity Boarddim cm2302 asclassusing"DHTnn_Object.spin"'dim f as class using "FloatMath.spin"'dim fp as class using "FloatString.spin"let mscycles = clkfreq / 1000sub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsubdim temp#,humid#,misc#
cm2302.StartDHTnn(8,22,@temp#,@humid#,@misc#)
do
print "Temp ";temp#
pausems 3000loop
DHTnn_Object.spin
''******************************************''* Title: DHTnn_Object *''* Modified from DHT21_Object - *''* Walter T. Mosscrop 2016 *''* Original Author: Gregg Erickson 2012 *''* See MIT License for Related Copyright *''* See end of file and objects for . *''* related copyrights and terms of use *''* This object draws upon code from other*''* OBEX objects such as servoinput.spin *''* and DHT C++ from Adafruit Industries *''* *''* This object reads the temperature and *''* humidity from an DHT11 or DHT22 *''* using a unique 1-wire serial protocol *''* with 5 byte packets where 0s are 26uS *''* long and 1s are 70uS. *''* *''* The object automatically returns the *''* temperature and humidiy to variables *''* memory every few seconds as Deg C and *''* relative percent respectively. It also*''* return an error byte where true means *''* the data received had correct parity *''* *''* The read loop has been reworked to *''* stabilize the timing and reduce the *''* number of invalid reads due to timing *''* issues *''******************************************{
Vcc──────────────Red (Power)
│
10K (Pull-Up Resistor)
│
Prop Pin ────────Yellow (Data)
VSS──────────────Black (Ground)
}CON
MAX_SAMPLES = 40' 40 one-bit samples VARlong APin,Device
long MeasurementStack[50]
long clkpermicro, clkminpulse
long measurementCog, measuring
long MeasurementValues[MAX_SAMPLES]
long temperaturePtr, humidityPtr, statusPtr
OBJ
f : "FloatMath"PUBStartDHTnn(devicePin,DeviceModel,TempPtr,HumPtr,StatPtr) : result
APin := devicePin
Device := DeviceModel
measurementCog := -1{{
This method lauches the Measuring code in a new cog
to read the DHTnn autonomously with variables updated
every few seconds
}}
MeasurementInit(APin, @measuring, @MeasurementValues)
temperaturePtr := TempPtr
humidityPtr := HumPtr
statusPtr := StatPtr
PUBStop{{
stop cog if in use
}}
if measurementCog => 0cogstop(measurementCog)
PubDHTnn_Read | Data[5], Temp, Humid, ByteCount,BitCount,Pulsewidth,Parity,ptr,tr,hr{{
This method reads the DHTnn device autonomously with variables located at
the pointers updated every few seconds
}}
'Apin is data line in from DHTnn'Data[5] bytes received in order from DHTnn'DATA = upper 8 bits of RH data' + lower 8 bits of RH data' + upper 8 bits of temperature data' + lower 8 bits of temperature data' + 8 bits check-sum (equals other 4 bytes added)'ByteCount - Index counter for data Bytes'BitCount - Index counter for bits within data bytes'Pulsewidth - Width of bits received from DHTnn, 26uS = 0, 70S=1, in clock ticks'parity - Boolean - parity for data from DHTnn'clkpermicro - number of clock ticks per microsecond'clkminpulse - the number of clock ticks for a pulsewidth value to be considered a 1
clkpermicro := clkfreq / 1_000_000
clkminpulse := clkpermicro * 48' Minimum 48 us 'high' pulsewaitcnt(clkfreq*2+cnt) ' Pause to allow DHTnn to stabilizeDIRA[Apin]~~ ' Set selected Pin to outputOUTA[Apin]~~ ' Pull Up selected Pin to start sequenceRepeat ByteCount from0to4
Data[ByteCount]:=0' Clear Data of Each Byte Before Inputwaitcnt(clkfreq/4+cnt) ' Pause to allow DHTnn to stabilize' Send a low to the DHTnn to request dataDIRA[Apin]~~ ' Set selected Pin to outputOUTA[Apin]~ ' Pull Down selected Pin for 500 uS to Request Dataif device == 11waitcnt((clkpermicro * 20_000) + cnt) ' hold 20mselsewaitcnt(clkfreq / 2000 + cnt) ' Pause for 500uSOUTA[Apin]~~ ' Return Pin to HighDIRA[Apin]~ ' Set Pin to Input and Release Control to DHTnn' DHTnn reponds with a ready signal (80uS low, 80uS high, 40 uS low, before data)waitpne(|<Apin,|<Apin,0) ' Wait for low, high, low sequencewaitpeq(|<Apin,|<Apin,0)
waitpne(|<Apin,|<Apin,0)
MeasurementStart
' DHTnn will send 40 high bits where 0 is 26uS and 1 is 70uSrepeatuntil measuring == 0
ptr := 0Repeat ByteCount from0to4' Store Data in 5 BytesRepeat BitCount from7to0' Receive Data by Bit, MSB to LSB
pulsewidth := LONG[@MeasurementValues][ptr++]
if pulsewidth > clkminpulse ' If Pulse > min pulse then bit is 1 else 0
Data[ByteCount]|=|<BitCount ' Store the bit in byte' Check Parity
parity := ((data[0]+data[1]+data[2]+data[3])& $ff)==(data[4]) 'Last byte equals sum of first four bytes' Calculate Temperatureif parity
if (Device == 11)
tr := data[2] & $7f' Pull from data2, mask MSB Bitelse
tr := data[2] ' Pull from data2
tr <<= 8' Put into upper byte
tr += data[3] ' Add lower byte
Temp := f.FFloat(tr)
if (Device == 11)
Temp := f.FDiv(Temp, 256.0) ' Convert to DHT11 valueelse
Temp := f.FMul(Temp, 0.1) ' Convert to DHT22 value ' Calculate Humidityif parity
Humid:=data[0] ' Pull from data0
Humid<<=8' Put into upper byte
Humid+=data[1] ' Add lower byte
Humid := f.FFloat(Humid)
if (Device == 11)
Humid := f.FDiv(Humid, 256.0) ' Convert to DHT11 valueelse
Humid := f.FMul(Humid, 0.1) ' Convert to DHT22 value ' Return values to addresses provided in pointersif parity
Long[temperaturePtr] := temp ' TemperatureLong[humidityPtr] := humid ' HumidityLong[statusPtr] := 1' Parity okelseLong[statusPtr]:= 0' Error' Cog relies on these arguments, do not removepubMeasurementInit(pAPin, pMeasuringStatus, pMeasurementValues)
measurementCog := cognew(@MeasureCog, @pAPin)
pubMeasurementStartLong[statusPtr] := -1' We're updating values LONGFILL(@MeasurementValues, MAX_SAMPLES, 0)
measuring := 1DATORG0
MeasureCog
mov p1, par' Get data pointerrdlong APinNum, p1 ' Get APin numberadd p1, #4' Get next pointerrdlong measuringStatusPtr, p1 ' Get pointer to measuring valueadd p1, #4' Get next pointerrdlong arrayPtrSave, p1 ' Get monitor array pointermov APinMask, #1' Get pin maskshl APinMask, APinNum
' Used for timing pulsesmovictra, #%11111' Setup counter (always)movfrqa, #1' One count per tick' Used for timeout in case of device failuremovictrb, #%11111' Setup counter (always)movfrqb, #1' One count per tick
wait
rdlong t1, measuringStatusPtr wz' Check "measuring" valueif_zjmp #wait ' Wait until "measuring" value nonzero' Prepare for measurementmov arrayPtr, arrayPtrSave ' Set up array pointermov t2, #0' Reset countermovphsb, #0' Reset timeout
measure
' Handle low-to-high (pulse gap)mov t1, #0wc' Reset carry for waitpeqwaitpeq APinMask, APinMask ' Wait for high statemov t1, phsb' Have we timed out?cmp t1, timeoutTicks wz, wcif_ajmp #done ' Yes, set done & start overmovphsa, #0' Reset counter' Time high-to-low (pulse)waitpne APinMask, APinMask ' Wait for low statemov t1, phsa' Save counter value in t1, can't write directly to hubwrlong t1, arrayPtr ' Save duration valueadd arrayPtr, #4' Set up for next array valuemov t1, phsb' Have we timed out?cmp t1, timeoutTicks wz, wcif_ajmp #done ' Yes, set done & start overadd t2, #1' Count valuescmp maxSample, t2 wz' Have we reached the limit?if_nejmp #measure ' No, back for more
done mov t1, #0' Yes, set status value to zerowrlong t1, measuringStatusPtr ' Update status valuejmp #wait ' We're done monitoring
APinNum long0
maxSample long MAX_SAMPLES
measuringStatusPtr long0
arrayPtr long0
arrayPtrSave long0
timeoutTicks long5 * 80_000_000' Assumes 80 MHz clock; 5 seconds
APinMask long0
p1 long0
t1 long0
t2 long0{{
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ 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. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
}}
I got the program to run, it works as expected. The program provides the temp in C degrees, so I will have to figure out how to convert tp F degrees. The problem I see is with the humidity read out, not what it is supposed to be. I tried changing the variable from float to integer, but that did not help.
Ray
rem test_temp.bas' October 13, 2018' Test of the CM2302 module on the WX Activity Boarddim cm2302 asclassusing"DHTnn_Object.spin"'dim f as class using "FloatMath.spin"'dim fp as class using "FloatString.spin"let mscycles = clkfreq / 1000sub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsubdim temp#,humid#,misc#
dim humid1
cm2302.StartDHTnn(8,22,@temp#,@humid#,@misc#)
do
cm2302.DHTnn_Read()
print "Temp ";temp#;" Humid ";humid#
pausems 3000loop
Propeller Version 1 on COM4
Loading G:/fastspin/programs/test_temp/test_temp.binary to hub memory
8676 bytes sent
Verifying RAM ... OK
[ Entering terminal mode. Type ESC or Control-C to exit. ]
Temp 24.500 Humid 1.1034E+09
Temp 24.600 Humid 1.1034E+09
Temp 24.600 Humid 1.1034E+09
I tried to compiler the code snippet and got the following errors
class fullduplex using "FullDuplexSerial.spin"
class Sd using "fsrw.spin"'dowloaded from OBEX
dim ser as fullduplex
dim sd as fsrw
' test typewriter here
dim chr as ubyte
ser.start(31, 30, 0, 115200)
pausems(1000)
do
chr = ser.rx()
ser.tx(chr)
if chr = 13 then
ser.tx(10)
' handle SDcard commands here...but not ready yetendif
loop
Results of compiler, fastspin
line 246return @buf2 + (byteloc & constant(SECTORSIZE - 1))
line 483 floc := filesize & constant(!(SECTORSIZE - 1))
line 485 bufat := frem & constant(SECTORSIZE - 1)
fsrw.spin(246) error: Cannot handle expression yet
fsrw.spin(483) error: Cannot handle expression yet
fsrw.spin(485) error: Cannot handle expression yet
Not sure why the humidity is coming out wrong; the code looks OK to me. You could trying to compile with optimization turned off (-O0) just in case there may be a fastspin optimizer bug.
@Mickster : fastspin is an optimizing compiler, so the output is a little harder to read than PropBasic's. If you give it the -l switch it'll add the original code to the listing file as comments.
@rsut : thanks for trying that! I think the problem is the constant(x) expression. I hope to fix that soon.
It's a great piece of code on your part. I was trying port the sd code to pbasic but I could not fit the fast_spi_engine into one cog. (it's all inline pasm !!!)
I decided not to try and re-invent the wheel and give your fastspin basic a try.
I am using spin2gui, and I chose, from the Options - No Optimizations, that seemed to have done the job. Before I was using, Default Optimizations, which produced the weird humidity# values. I also tried the Full Optimizations, it showed the first five lines of information, and it froze, never got to running the program. So, it might be something in your spin2gui Options, that might be causing a problem.
The program below is outputting the correct values now, using-No Optimizations.
Now that this is working, I would like to tackle the ADC component that is on the WX Activity Board. I know that there is PropGCC function for that, but that is in C. In the OBEX, the only thing that is available is for a different kind of ADC chip. Now if you have a C2fastspin(BASIC) conversion program, and the C source for the ADC , then I guess we could accomplish something.
Now, in the mean time, I will have to try my hand at fsrw26, and see if I can make some progress. That looks like a fairly complicated Spin program.
Ray
rem test_temp.bas' October 13, 2018' Test of the CM2302 module on the WX Activity Boarddim cm2302 asclassusing"DHTnn_Object.spin"let mscycles = clkfreq / 1000sub pausems(ms)
waitcnt(getcnt() + ms * mscycles)
endsubdim temp#,humid#,misc#
cm2302.StartDHTnn(8,22,@temp#,@humid#,@misc#)
do
cm2302.DHTnn_Read()
print "Temp ";(9.0/5.0)*temp# + 32.0;" Humid ";humid#
pausems 3000loop
Propeller Version 1 on COM4
Loading G:/fastspin/programs/test_temp/test_temp.binary to hub memory
12840 bytes sent
Verifying RAM ... OK
[ Entering terminal mode. Type ESC or Control-C to exit. ]
Temp 73.040 Humid 55.500
Temp 73.220 Humid 55.100
Temp 73.220 Humid 55.100
Temp 73.220 Humid 55.000
Comments
If Eric does not mind, I am calling this BASIC, pBas or pBAS.
Will pBas provide an #include provision? I am thinking that, as an example, the high, low, and pausems commands could be placed in a lib, so you only code it once, and have it called within your program.
Ray
rem test2.bas let mscycles = clkfreq / 1000 '******************************* 'Main print "This is the test2.bas program" do high 26 pausems 500 low 26 pausems 26 loop '******************************* sub high(pin) dim pin% direction(pin) = output end sub sub low(pin) dim pin% direction(pin) = input end sub sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub
The second pausems is for only 26 ms. Is that what you intended?
Also, your high() and low() functions are changing the direction, not the output value. They really should be something like:
sub high(pin) output(pin) = 1 end sub sub low(pin) output(pin) = 0 end sub
And then in your main code dodirection(pin) = output
once to set the pin as an output.Probably "fBas" would be a better abbreviation, to avoid confusion with PropBasic.
It already does, actually. I need to write documentation for the preprocessor, but it supports #include, #define, #ifdef, #elseifdef, and #endif.
Besides #include (which just literally includes one file into another) there is also "class using filename", which lets you include files as objects, similar to the OBJ statement in Spin. "class using" works with both Spin and BASIC files.
Eric
Thanks David! I'll switch to proploader for the next version.
Ray
rem test2.bas let mscycles = clkfreq / 1000 direction(pin) = output '******************************* 'Main print "This is the test2.bas program" do high 26 pausems 500 low 26 pausems 500 loop '******************************* sub high(pin) output(pin) = 1 end sub sub low(pin) output(pin) = 0 end sub sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub
Use "direction(26) = output". Or you could add
const ledpin = 26
at the beginning of your code so that it's easy to change the pin. For example:rem blinker: blink an led const ledpin = 16 ' set the led pin as an output direction(ledpin) = output print "This is the LED blink test program" ' loop blinking the led do high ledpin pausems 500 low ledpin pausems 500 loop sub high(pin) output(pin) = 1 end sub sub low(pin) output(pin) = 0 end sub
The most recent version of fastspin has pausems as a builtin function so you probably don't need to define that, but your definition looked OK and you can use it if you like
Also note that 'dim pin%' declares a variable called 'pin%' and is different from 'dim pin'. I think that may be an area where fastspin differs from qbasic and freebasic.
Eric
I am liking the spin2gui program, although some improvements would be nice. One would be, a Load command, where it writes the program to the EEPROM. The other is, you do not have any support for .bas in terms of creating a or saving a .bas file.
Ray
Thanks for the suggestions!
I noticed that pBas does not have any break or exit commands. What would be the method for getting out of a while loop.
Ray
rem test2.bas ' October 12, 2018 ' General use of pBas compiler ' Define clock freq let mscycles = clkfreq / 1000 ' Variables dim pin '*** Subs & Functions *** sub high(pin) direction(pin) = output output(pin) = 1 ' LED is on end sub sub low(pin) direction(pin) = output output(pin) = 0 ' LED is off end sub sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub '******************************* 'Main print "This is the test2.bas program" do high 27 ' Pin 27 on pausems 500 low 27 ' Pin 27 off pausems 500 loop '*******************************
Does, or will, pBas have inline pasm? I am wondering as to the best way for pBas to utilize all the Spin driver code that is available. I think JonnyMac had written quite a few drivers in pasm, but there also is quite a few drivers that are written in Spin. One large driver, that could be of great use, is the SD driver, which is in Spin.
Since Parallax has the Activity Board WX, and the FLiP module, maybe there should be an effort made to start trying to create some usage of the different items for the these platforms. For the Activity Board WX: the SD, WiFi, ADC, sound, motor driver, …, etc. Other items of interest could be the RTC, Temp/humidity module and others.
Ray
rem test3.bas ' October 12, 2018 ' pBas cpu(COG) usage. ' Are these declarations all Global? ' Define clock freq let mscycles = clkfreq / 1000 ' Variables dim pin '*** Subs & Functions *** sub high(pin) direction(pin) = output output(pin) = 1 ' LED is on end sub sub low(pin) direction(pin) = output output(pin) = 0 ' LED is off end sub sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub '******************************* 'Main dim stack(4) 'This is the minimal to run the cpu job1 ' Start the cpu(COG) var a = cpu(job1(26), @stack(1)) ' Not sure what size @stack(1) is do high 27 ' Pin 27 on pausems 500 low 27 ' Pin 27 off pausems 500 loop '******************************* ' This is the cpu(COG) job. sub job1(pin) do high pin ' Pin 27 on pausems 300 low pin ' Pin 27 off pausems 300 loop end sub '*******************************
The usual way would be to have the while loop test for a variable, and to set that inside the loop. For example, an (ugly) way to print some integers is:
var done = 0 ' indicates loop is finished var i = 1 do print i i = i + 1 if (i > 4) done = 1 print "still looping" loop until done <> 0
You could also use a goto:var i = 1 do print i i = i + 1 if (i > 4) goto out print "still looping" loop out:
fastspin supports inline assembly between "asm" and "endasm". But if you want to use Spin, you can import it directly with "class using". (The compiler is called "fastspin" after all, and that was the first language it supported -- the BASIC is a new addon!)
For example, to use the FullDuplexSerial.spin driver from a BASIC program you can do:
' create a FullDuplexSerial object named fds ' this is just like the Spin command OBJ fds : "FullDuplexSerial.spin" dim fds as class using "FullDuplexSerial.spin" ' initialize the fds object ' WARNING: we're choosing to use pins 31 and 30 here, which ' will conflict with the built-in print commands. Don't try to ' use print after the fds.start. If we used other pins (to communicate ' with some device, for example) then this wouldn't be an issue fds.start(31, 30, 0, 115_200) do fds.str("hello, world!") fds.tx(13) fds.tx(10) loop
"class using" accepts either a BASIC or Spin file.
TMM = Cog Ram
LMM = Hub Ram
XMM = P2 Hub Execute
Ray
Ahem....'t ain't called "pBas", it's called fastspin(BASIC).
Other readers are going to be confused 😕
Since I have a WX Activity Board with DS1302 module installed, I thought I would try doing a real use program with fastspin(BASIC).
The program below compiles without complaints, but the value is showing up as 0, it should be 13, todays date. I believe that in the Spin program, the value(s) is presented as a byte. So, I am not sure if fastspin(BASIC) is doing a correct print of the day1 variable. Have not done anything in Spin, in the last ten years, so I could be wrong with my interpretation.
I attached the DS1302.spin program, for easy referencing.
Ray
test_rtc.bas
rem test_rtc.bas ' October 13, 2018 ' Test of the RTC on the WX Activity Board dim rtc as class using "DS1302_full.spin" let mscycles = clkfreq / 1000 sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub dim day1 as byte dim month1 as byte dim year1 as byte dim dow1 as byte dim day dim month dim year dim dow rtc.init(10,11,9) rtc.readDate(day1, month1, year1, dow1) 'day1 = day do print "day", day1 pausems 3000 loop
ds1302_full.spin{{ DS1302_full.spin ┌─────────────────────────────────────┬────────────────┬─────────────────────┬───────────────┐ │ DS1302 driver v1.3 │ BR │ (C)2010 │ 8Jul2010 │ ├─────────────────────────────────────┴────────────────┴─────────────────────┴───────────────┤ │ │ │ A full-featured DS1302 timekeeping chip driver. Based on the original object by │ │ Daniel Robert. │ │ │ │ Notes: │ │ •Original object here: http://obex.parallax.com/objects/89/ │ │ •12 hour mode is not supported in this object. The reason is that it would add │ │ significant complexity to this object for minimal benefit. A better solution is │ │ to handle the 12/24 hour conversion in software outside of this object if needed. │ │ Okay. So it is nearly full-featured. │ │ •The DS1302 data sheet specs the maximum frequency on the clk pin as: │ │ 0.5MHz@2V, 2MHz@5V --> 1.5MHz@3.3V (1.5 uS per clock, 120 ticks@clkfreq=80MHz) │ │ Original object had a 2 uS pause between clock pin transitions. However, it seems to │ │ work fine with no wait @ clkfreq=80MHz...apparently spin can't outrun the DS1302. │ │ Not tested at clkfreq=100MHz. │ │ •Experimented with trickle charger using 1 diode+2K resistor setting. 10µF │ │ electrolytic cap kept DS1302 powered up for >10 minutes. Implies a 1000µF should be good │ │ for ~16 hrs of backup power...enough to last through a typical brown-out or black-out │ │ (if leakage current and wide tolerance band of electrolytics doesn't kill it first) │ │ •Note that this device needs to have a 32.768KHz crystal with 6pF capacitance in order to │ │ keep accurate time: http://forums.parallax.com/forums/default.aspx?f=25&m=467246 │ │ │ │ See end of file for terms of use. │ └────────────────────────────────────────────────────────────────────────────────────────────┘ DEVICE PINOUT & REFERENCE CIRCUIT ┌────┐ 1000µF 3.3V │ │ backup_Vin ── to gnd xtal xi │DS1302│ clk ─── to prop pin 32.768 KHz xo │ │ io ─ 1KΩ resistor to prop pin ┌│ │ ce ─── to prop pin GND └──────┘ TRICKLE CHARGER NOTES 'Trickle charger setup tc_enable diodeSel resistSel ' | | | ' write(command(clock,tc,w),(%1010 << 4) + (2 << 2)+ ( 3 )) Diode select register bits: 00 = trickle charger disabled 01 = 1 diode enabled 10 = 2 diodes connected in series (more voltage drop when in charging mode) 11 = trickle charger disabled Resistor select bits: 00 = no resistor 0Ω 01 = R1 2KΩ 10 = R2 4KΩ 11 = R3 8KΩ 'Examples of other useful config and control commands 'write(command(clock,sec,w),read(command(clock,sec,r))&%1000_0000) 'set clock halt bit 'write(command(clock,ctrl,w),%1000_0000) 'set write-protect bit 'write(command(clock,hr,w),read(command(clock,hr,r))&%1000_0000) 'set 12h mode V1.1 - 27Oct09 fixed mis-labeled pins in docs (clk and io were swapped in 1.0) V1.2 - 28Mar10 fixed bug in config method that caused seconds to be reset (thanks, Doug!) v1.3 - 26Jun10 cleanup and additional documentation }} CON 'command byte options #0, clock,ram #0, sec, mi, hr, day, mo, dow, yr, ctrl, tc, #31, burst #0, w,r VAR byte clk 'clock byte io 'data io byte ce 'chip enable byte datar PUB command(clock_or_ram, register, r_or_w) ''Returns a DS1302 command byte ''clock_or_ram : select clock or ram (allowed values: clock, ram) ''register : register name (allowed values: 0-30, sec, mi, hr, day, mo, dow, yr, ctrl, tc, burst) ''r_or_w : read or write (allowed values: w, r) ''Usage : cmd:=command(rtc#ram, 21, rtc#w) --> write to RAM byte 21 '' cmd:=command(rtc#clock, rtc#min, rtc#r) --> read minute register return (1<<7) + (clock_or_ram<<6) + (register<<1) + r_or_w PUB init( inClk, inIo, inCe ) ''Initialize propeller serial com channel with DS1302. Call once after prop power-up. ''Usage: rtc.init(clk_pin, io_pin, chipEnable_pin) clk := inClk 'save pin numbers io := inIo ce := inCe dira[ce]~~ 'configure chip enable pin outa[ce]~ dira[clk]~~ 'configure clock pin outa[clk]~ PUB config ''Config DS1302. Call once after DS1302 power-up. ''Usage: rtc.config write(command(clock,ctrl,w),0) 'clear write-protect bit write(command(clock,sec,w),read(command(clock,sec,r)) & %0111_1111) 'clear halt bit write(command(clock,hr,w),read(command(clock,hr,r)) & %0111_1111) 'set 24hr mode write(command(clock,tc,w),0) 'disable trickle charger PUB setDatetime( _mth, _day, _year, _dow, _hr, _min, _sec ) ''Set date and time write($8c, bin2bcd( _year ) ) write($8a, _dow ) write($88, bin2bcd( _mth ) ) write($86, bin2bcd( _day ) ) write($84, bin2bcd( _hr ) ) write($82, bin2bcd( _min ) ) write($80, bin2bcd( _sec ) ) PUB readDate( _day, _mth, _year, _dow ) ''Read current date (day, month, year, day-of-week) ''Usage: rtc.readDate( @day, @month, @year, @dow ) byte[_year] := bcd2bin( read($8d) ) byte[_mth] := bcd2bin( read($89) ) byte[_day] := bcd2bin( read($87) ) byte[_dow] := bcd2bin( read($8b) ) PUB readTime( _hr, _min, _sec ) | tmp1, tmp2 ''Read current hour, minute, second ''Usage: rtc.readTime( @hour, @minute, @second ) byte[_hr] := bcd2bin( read($85) ) byte[_min] := bcd2bin( read($83) ) byte[_sec] := bcd2bin( read($81) ) Pub read( cmd ) | i ''Read a byte of data per command byte outa[ce]~~ writeByte( cmd ) dira[io]~ 'set to input readByte outa[ce]~ return(datar) Pub write( cmd, data ) ''Write a byte of data per cmd byte outa[ce]~~ writeByte( cmd ) writeByte( data ) outa[ce]~ PUB writeN(cmd, dataPtr, n)|i ''Write a stream of n bytes from byte array pointed to by dataPtr outa[ce]~~ writeByte( cmd ) repeat i from 0 to n writeByte(byte[dataPtr][i]) outa[ce]~ Pub readN(cmd, dataPtr, n)|i ''Read a stream of n bytes of data and put in byte array pointed ''to by dataPtr outa[ce]~~ writeByte( cmd ) dira[io]~ 'set to input repeat i from 0 to n readByte byte[dataPtr][i]:=datar outa[ce]~ PRI readByte|i datar~ repeat i from 0 to 7 if ina[io] == 1 datar |= |< i ' set bit outa[clk]~~ outa[clk]~ PRI writeByte( cmd ) | i dira[io]~~ 'set to output repeat i from 0 to 7 outa[io] := cmd outa[clk]~~ cmd >>= 1 outa[clk]~ PRI bin2bcd(dataIn) | tmp 'Convert a byte of binary data to binary coded decimal tmp:= dataIn/10 result := dataIn - ( tmp * 10 ) + ( tmp << 4 ) PRI bcd2bin(dataIn) 'Convert a byte of binary coded decimal data to binary result := (dataIn & %00001111) + ((dataIn >> 4) & %00001111)*10 DAT {{ ┌─────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 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. │ └─────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
You're right that the day, month, year, etc. have to be bytes. You missed the comment in the Spin file that the readDate and readTime functions want the addresses of the bytes:
''Usage: rtc.readDate( @day, @month, @year, @dow )
so if you change the line in your BASIC program fromrtc.readDate(day1, month1, year1, dow1)
tortc.readDate(@day1, @month1, @year1, @dow1)
then it may work. (I don't have hardware to test it myself.)I also did a program code size comparison, fastspin(BASIC) - 6616; SimpleIDE PropGCC - 6576. The PropGCC version was derived from DS1302_full.spin.
The next thing that I will try is CM2302 module, the Spin code for that looks a little more confusing. It works best with float variables, which fastspin(BASIC) can handle.
The next thing up would be to write the date/time and the temp/humidity to the SD card. But, I noticed that the open command and some sort of read/write to the SD card or file, are not available. So, this just might have to wait.
Ray
rem test_rtc.bas ' October 13, 2018 ' Test of the RTC on the WX Activity Board dim rtc as class using "DS1302_full.spin" let mscycles = clkfreq / 1000 sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub dim day1 as byte dim month1 as byte dim year1 as byte dim dow1 as byte dim minutes as byte dim hour as byte dim seconds as byte dim dow rtc.init(10,11,9) do rtc.readDate(@day1, @month1, @year1, @dow1) rtc.readTime(@hour,@minutes,@seconds) print month1;"/";day1;"/";"20";year1;" ";hour;":";minutes;":";seconds pausems 3000 loop
Yep, fastspin(BASIC) ROCKS!
Ray
test_temp.bas
rem test_temp.bas ' October 13, 2018 ' Test of the CM2302 module on the WX Activity Board dim cm2302 as class using "DHTnn_Object.spin" 'dim f as class using "FloatMath.spin" 'dim fp as class using "FloatString.spin" let mscycles = clkfreq / 1000 sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub dim temp#,humid#,misc# cm2302.StartDHTnn(8,22,@temp#,@humid#,@misc#) do print "Temp ";temp# pausems 3000 loop
DHTnn_Object.spin''****************************************** ''* Title: DHTnn_Object * ''* Modified from DHT21_Object - * ''* Walter T. Mosscrop 2016 * ''* Original Author: Gregg Erickson 2012 * ''* See MIT License for Related Copyright * ''* See end of file and objects for . * ''* related copyrights and terms of use * ''* This object draws upon code from other* ''* OBEX objects such as servoinput.spin * ''* and DHT C++ from Adafruit Industries * ''* * ''* This object reads the temperature and * ''* humidity from an DHT11 or DHT22 * ''* using a unique 1-wire serial protocol * ''* with 5 byte packets where 0s are 26uS * ''* long and 1s are 70uS. * ''* * ''* The object automatically returns the * ''* temperature and humidiy to variables * ''* memory every few seconds as Deg C and * ''* relative percent respectively. It also* ''* return an error byte where true means * ''* the data received had correct parity * ''* * ''* The read loop has been reworked to * ''* stabilize the timing and reduce the * ''* number of invalid reads due to timing * ''* issues * ''****************************************** { Vcc──────────────Red (Power) │ 10K (Pull-Up Resistor) │ Prop Pin ────────Yellow (Data) VSS──────────────Black (Ground) } CON MAX_SAMPLES = 40 ' 40 one-bit samples VAR long APin,Device long MeasurementStack[50] long clkpermicro, clkminpulse long measurementCog, measuring long MeasurementValues[MAX_SAMPLES] long temperaturePtr, humidityPtr, statusPtr OBJ f : "FloatMath" PUB StartDHTnn(devicePin,DeviceModel,TempPtr,HumPtr,StatPtr) : result APin := devicePin Device := DeviceModel measurementCog := -1 {{ This method lauches the Measuring code in a new cog to read the DHTnn autonomously with variables updated every few seconds }} MeasurementInit(APin, @measuring, @MeasurementValues) temperaturePtr := TempPtr humidityPtr := HumPtr statusPtr := StatPtr PUB Stop {{ stop cog if in use }} if measurementCog => 0 cogstop(measurementCog) Pub DHTnn_Read | Data[5], Temp, Humid, ByteCount,BitCount,Pulsewidth,Parity,ptr,tr,hr {{ This method reads the DHTnn device autonomously with variables located at the pointers updated every few seconds }} 'Apin is data line in from DHTnn 'Data[5] bytes received in order from DHTnn 'DATA = upper 8 bits of RH data ' + lower 8 bits of RH data ' + upper 8 bits of temperature data ' + lower 8 bits of temperature data ' + 8 bits check-sum (equals other 4 bytes added) 'ByteCount - Index counter for data Bytes 'BitCount - Index counter for bits within data bytes 'Pulsewidth - Width of bits received from DHTnn, 26uS = 0, 70S=1, in clock ticks 'parity - Boolean - parity for data from DHTnn 'clkpermicro - number of clock ticks per microsecond 'clkminpulse - the number of clock ticks for a pulsewidth value to be considered a 1 clkpermicro := clkfreq / 1_000_000 clkminpulse := clkpermicro * 48 ' Minimum 48 us 'high' pulse waitcnt(clkfreq*2+cnt) ' Pause to allow DHTnn to stabilize DIRA[Apin]~~ ' Set selected Pin to output OUTA[Apin]~~ ' Pull Up selected Pin to start sequence Repeat ByteCount from 0 to 4 Data[ByteCount]:=0 ' Clear Data of Each Byte Before Input waitcnt(clkfreq/4+cnt) ' Pause to allow DHTnn to stabilize ' Send a low to the DHTnn to request data DIRA[Apin]~~ ' Set selected Pin to output OUTA[Apin]~ ' Pull Down selected Pin for 500 uS to Request Data if device == 11 waitcnt((clkpermicro * 20_000) + cnt) ' hold 20ms else waitcnt(clkfreq / 2000 + cnt) ' Pause for 500uS OUTA[Apin]~~ ' Return Pin to High DIRA[Apin]~ ' Set Pin to Input and Release Control to DHTnn ' DHTnn reponds with a ready signal (80uS low, 80uS high, 40 uS low, before data) waitpne(|<Apin,|<Apin,0) ' Wait for low, high, low sequence waitpeq(|<Apin,|<Apin,0) waitpne(|<Apin,|<Apin,0) MeasurementStart ' DHTnn will send 40 high bits where 0 is 26uS and 1 is 70uS repeat until measuring == 0 ptr := 0 Repeat ByteCount from 0 to 4 ' Store Data in 5 Bytes Repeat BitCount from 7 to 0 ' Receive Data by Bit, MSB to LSB pulsewidth := LONG[@MeasurementValues][ptr++] if pulsewidth > clkminpulse ' If Pulse > min pulse then bit is 1 else 0 Data[ByteCount]|=|<BitCount ' Store the bit in byte ' Check Parity parity := ((data[0]+data[1]+data[2]+data[3])& $ff)==(data[4]) 'Last byte equals sum of first four bytes ' Calculate Temperature if parity if (Device == 11) tr := data[2] & $7f ' Pull from data2, mask MSB Bit else tr := data[2] ' Pull from data2 tr <<= 8 ' Put into upper byte tr += data[3] ' Add lower byte Temp := f.FFloat(tr) if (Device == 11) Temp := f.FDiv(Temp, 256.0) ' Convert to DHT11 value else Temp := f.FMul(Temp, 0.1) ' Convert to DHT22 value ' Calculate Humidity if parity Humid:=data[0] ' Pull from data0 Humid<<=8 ' Put into upper byte Humid+=data[1] ' Add lower byte Humid := f.FFloat(Humid) if (Device == 11) Humid := f.FDiv(Humid, 256.0) ' Convert to DHT11 value else Humid := f.FMul(Humid, 0.1) ' Convert to DHT22 value ' Return values to addresses provided in pointers if parity Long[temperaturePtr] := temp ' Temperature Long[humidityPtr] := humid ' Humidity Long[statusPtr] := 1 ' Parity ok else Long[statusPtr]:= 0 ' Error ' Cog relies on these arguments, do not remove pub MeasurementInit(pAPin, pMeasuringStatus, pMeasurementValues) measurementCog := cognew(@MeasureCog, @pAPin) pub MeasurementStart Long[statusPtr] := -1 ' We're updating values LONGFILL(@MeasurementValues, MAX_SAMPLES, 0) measuring := 1 DAT ORG 0 MeasureCog mov p1, par ' Get data pointer rdlong APinNum, p1 ' Get APin number add p1, #4 ' Get next pointer rdlong measuringStatusPtr, p1 ' Get pointer to measuring value add p1, #4 ' Get next pointer rdlong arrayPtrSave, p1 ' Get monitor array pointer mov APinMask, #1 ' Get pin mask shl APinMask, APinNum ' Used for timing pulses movi ctra, #%11111 ' Setup counter (always) mov frqa, #1 ' One count per tick ' Used for timeout in case of device failure movi ctrb, #%11111 ' Setup counter (always) mov frqb, #1 ' One count per tick wait rdlong t1, measuringStatusPtr wz ' Check "measuring" value if_z jmp #wait ' Wait until "measuring" value nonzero ' Prepare for measurement mov arrayPtr, arrayPtrSave ' Set up array pointer mov t2, #0 ' Reset counter mov phsb, #0 ' Reset timeout measure ' Handle low-to-high (pulse gap) mov t1, #0 wc ' Reset carry for waitpeq waitpeq APinMask, APinMask ' Wait for high state mov t1, phsb ' Have we timed out? cmp t1, timeoutTicks wz, wc if_a jmp #done ' Yes, set done & start over mov phsa, #0 ' Reset counter ' Time high-to-low (pulse) waitpne APinMask, APinMask ' Wait for low state mov t1, phsa ' Save counter value in t1, can't write directly to hub wrlong t1, arrayPtr ' Save duration value add arrayPtr, #4 ' Set up for next array value mov t1, phsb ' Have we timed out? cmp t1, timeoutTicks wz, wc if_a jmp #done ' Yes, set done & start over add t2, #1 ' Count values cmp maxSample, t2 wz ' Have we reached the limit? if_ne jmp #measure ' No, back for more done mov t1, #0 ' Yes, set status value to zero wrlong t1, measuringStatusPtr ' Update status value jmp #wait ' We're done monitoring APinNum long 0 maxSample long MAX_SAMPLES measuringStatusPtr long 0 arrayPtr long 0 arrayPtrSave long 0 timeoutTicks long 5 * 80_000_000 ' Assumes 80 MHz clock; 5 seconds APinMask long 0 p1 long 0 t1 long 0 t2 long 0 {{ ┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ 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. │ └──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ }}
I don't see anything obviously wrong with the code. Does the Spin object work as you expect it to when you use it in a Spin project?
Ray
rem test_temp.bas ' October 13, 2018 ' Test of the CM2302 module on the WX Activity Board dim cm2302 as class using "DHTnn_Object.spin" 'dim f as class using "FloatMath.spin" 'dim fp as class using "FloatString.spin" let mscycles = clkfreq / 1000 sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub dim temp#,humid#,misc# dim humid1 cm2302.StartDHTnn(8,22,@temp#,@humid#,@misc#) do cm2302.DHTnn_Read() print "Temp ";temp#;" Humid ";humid# pausems 3000 loop
What does the fastspin(BASIC) PASM code look like (assuming it does similar to PropBasic)?
Ray
I tried to compiler the code snippet and got the following errors
class fullduplex using "FullDuplexSerial.spin" class Sd using "fsrw.spin" 'dowloaded from OBEX dim ser as fullduplex dim sd as fsrw ' test typewriter here dim chr as ubyte ser.start(31, 30, 0, 115200) pausems(1000) do chr = ser.rx() ser.tx(chr) if chr = 13 then ser.tx(10) ' handle SDcard commands here...but not ready yet end if loop Results of compiler, fastspin line 246 return @buf2 + (byteloc & constant(SECTORSIZE - 1)) line 483 floc := filesize & constant(!(SECTORSIZE - 1)) line 485 bufat := frem & constant(SECTORSIZE - 1) fsrw.spin(246) error: Cannot handle expression yet fsrw.spin(483) error: Cannot handle expression yet fsrw.spin(485) error: Cannot handle expression yet
Posted in the hope that it is helpful.
Ron
print "Temp "; (9.0/5.0)*temp# + 32.0;" Humid ";humid#
Not sure why the humidity is coming out wrong; the code looks OK to me. You could trying to compile with optimization turned off (-O0) just in case there may be a fastspin optimizer bug.@Mickster : fastspin is an optimizing compiler, so the output is a little harder to read than PropBasic's. If you give it the -l switch it'll add the original code to the listing file as comments.
@rsut : thanks for trying that! I think the problem is the constant(x) expression. I hope to fix that soon.
It's a great piece of code on your part. I was trying port the sd code to pbasic but I could not fit the fast_spi_engine into one cog. (it's all inline pasm !!!)
I decided not to try and re-invent the wheel and give your fastspin basic a try.
Love it
Ron
The program below is outputting the correct values now, using-No Optimizations.
Now that this is working, I would like to tackle the ADC component that is on the WX Activity Board. I know that there is PropGCC function for that, but that is in C. In the OBEX, the only thing that is available is for a different kind of ADC chip. Now if you have a C2fastspin(BASIC) conversion program, and the C source for the ADC , then I guess we could accomplish something.
Now, in the mean time, I will have to try my hand at fsrw26, and see if I can make some progress. That looks like a fairly complicated Spin program.
Ray
rem test_temp.bas ' October 13, 2018 ' Test of the CM2302 module on the WX Activity Board dim cm2302 as class using "DHTnn_Object.spin" let mscycles = clkfreq / 1000 sub pausems(ms) waitcnt(getcnt() + ms * mscycles) end sub dim temp#,humid#,misc# cm2302.StartDHTnn(8,22,@temp#,@humid#,@misc#) do cm2302.DHTnn_Read() print "Temp ";(9.0/5.0)*temp# + 32.0;" Humid ";humid# pausems 3000 loop