![]() |
Forum Index : Microcontroller and PC projects : MMBasic: New in-built functions
Author | Message | ||||
matherp Guru ![]() Joined: 11/12/2012 Location: United KingdomPosts: 10310 |
Working with Geoff, and coming soon to all versions of MMBasic, two useful new functions have been created. These make it easy to save and load binary data from file and convert data to and from sensors like the BMP180 family. For File I/O the size of data saved can be easily tuned to match the requirement ensuring file sizes are kept to a minimum. The functions also make it easy to work with things like flash memory chips more efficiently than with ascii data. ![]() ![]() Test program attached ' 'write out to file numbers in the range 0 to 65535 using 2 bytes per number ' open "bin.tst" for output as #1 for j%=0 to 65535 print #1,bin2str$(uint16,j%); next j% close #1 'Read the file and check data accuracy open "bin.tst" for input as #1 for j%=0 to 65535 k%=str2bin(uint16,input$(2,1)) if j%<>k% then print "error k = ", k%," j = ",j% next j% close #1 ' ' Demo all modes of conversion ' c%=123 a$=Bin2str$(int8,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print "" b%=Str2bin(int8,a$) If b%<>c% Then Print "error in int8" c%=123 a$=Bin2str$(int8,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(int8,a$,big) If b%<>c% Then Print "error in int8, big" c%=223 a$=Bin2str$(uint8,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint8,a$) If b%<>c% Then Print "error in uint8" c%=223 a$=Bin2str$(uint8,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint8,a$,big) If b%<>c% Then Print "error in uint8, big" ' c%=11123 a$=Bin2str$(int16,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print "" b%=Str2bin(int16,a$) If b%<>c% Then Print "error in int16" c%=11123 a$=Bin2str$(int16,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(int16,a$,big) If b%<>c% Then Print "error in int16, big" c%=51223 a$=Bin2str$(uint16,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint16,a$) If b%<>c% Then Print "error in uint16" c%=51223 a$=Bin2str$(uint16,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint16,a$,big) If b%<>c% Then Print "error in uint16, big" ' c%=2147483600 a$=Bin2str$(int32,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print "" b%=Str2bin(int32,a$) If b%<>c% Then Print "error in int32" c%=2147483600 a$=Bin2str$(int32,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(int32,a$,big) If b%<>c% Then Print "error in int32, big" c%=3147483600 a$=Bin2str$(uint32,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint32,a$) If b%<>c% Then Print "error in uint32" c%=3147483600 a$=Bin2str$(uint32,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint32,a$,big) If b%<>c% Then Print "error in uint32, big" ' c%=9223372036854775800 a$=Bin2str$(int64,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i:Print "" b%=Str2bin(int64,a$) If b%<>c% Then Print "error in int64" c%=9223372036854775800 a$=Bin2str$(int64,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(int64,a$,big) c%=&HEEDDCCBBAA998877 a$=Bin2str$(uint64,c%) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint64,a$) If b%<>c% Then Print "error in uint64" c%=&HEEDDCCBBAA998877 a$=Bin2str$(uint64,c%,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" b%=Str2bin(uint64,a$,big) If b%<>c% Then Print "error in uint64, big" ' f=pi a$=Bin2str$(double,f) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" g=Str2bin(double,a$) If f<>g Then Print "error in double" f=pi a$=Bin2str$(double,f,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" g=Str2bin(double,a$,big) If f<>g Then Print "error in double, big" f=32768.5 'should be exact in single precision a$=Bin2str$(single,f) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" g=Str2bin(single,a$) If f<>g Then Print "error in single" f=32768.5 'should be exact in single precision a$=Bin2str$(single,f,big) Print Len(a$);:For i=1 To Len(a$):Print " ",Asc(Mid$(a$,i,1));:Next i: Print "" g=Str2bin(single,a$,big) If f<>g Then Print "error in single, big" ' ' BMP180 example using STR2BIN ' OPTION EXPLICIT OPTION DEFAULT FLOAT const i2caddr=&b1110111 const MS7=7 'set default wait period const signed=1 const unsigned=0 ' dim i2cin$ length 8 'max size for integer conversion dim UT%,UP% dim ac1%,ac2%,ac3,ac4%,ac5%,ac6%,b1%,b2%,mb%,mc%,md% 'bmp180 parameters dim x1%,x2%,b5%,b6%,x3,b3%,b4%,b7%,OSS% dim temperature%,pressure% DIM altitude,QNH,pressureinHpa dim OSSdata%(4) dim OSSscale%(4) on error skip 1 I2C OPEN 400,1000 init: OSS%=1 'set oversampling ratio ' OSS%=0 ' Uncomment this line to check algorithm against datasheet OSSdata%(0)=&H34 'commands to sample pressure% with different levels of oversampling OSSdata%(1)=&H74 OSSdata%(2)=&Hb4 OSSdata%(3)=&HF4 OSSscale%(0)=1 'scale factors for calcs when oversampled OSSscale%(1)=2 OSSscale%(2)=4 OSSscale%(3)=8 ' I2C WRITE i2caddr,1,1,&HAA 'send read calibration data command I2C READ i2caddr,0,22,i2cin$ 'read in calibration data ac1%=str2bin(int16,mid$(i2cin$,1,2),big) ac2%=str2bin(int16,mid$(i2cin$,3,2),big) ac3=str2bin(int16,mid$(i2cin$,5,2),big) ac4%=str2bin(uint16,mid$(i2cin$,7,2),big) ac5%=str2bin(uint16,mid$(i2cin$,9,2),big) ac6%=str2bin(uint16,mid$(i2cin$,11,2),big) b1%=str2bin(int16,mid$(i2cin$,13,2),big) b2%=str2bin(int16,mid$(i2cin$,15,2),big) mb%=str2bin(int16,mid$(i2cin$,17,2),big) mc%=str2bin(int16,mid$(i2cin$,19,2),big) md%=str2bin(int16,right$(i2cin$,2),big) ' ' Uncomment this block to check algorithm against datasheet ' ' ac1%=408 ' ac2%=-72 ' AC3=-14383 ' ac4%=32741 ' ac5%=32757 ' ac6%=23153 ' b1%=6190 ' b2%=4 ' mb%=-32768 ' mc%=-8711 ' md%=2868 main: I2C WRITE i2caddr,0,2,&HF4,&H2E 'send temp conversion pause MS7 'wait for temperature% conversion I2C WRITE i2caddr,1,1,&HF6 'send read data I2C READ i2caddr,0,2,i2cin$ 'read 2 bytes UT%=str2bin(uint16,i2cin$,big) ' UT%=27898 ' Uncomment this line to check algorithm against datasheet I2C WRITE i2caddr,0,2,&HF4,ossdata%(oss%) 'send pressure% conversion pause (oss%+1)*ms7 'wait for the p ressure% conversion I2C WRITE i2caddr,1,1,&HF6 'send read data I2C READ i2caddr,0,3,i2cin$ 'read 3 bytes i2cin$=chr$(0)+i2cin$ 'convert to big endian unsigned 4 byte integer UP%=str2bin(uint32,i2cin$,big) UP%=UP%>>(8-oss%) 'scale the oUT%pUT% by the numb%er of unused bits in the xlsb byte ' UP%=23843' Uncomment this line to check algorithm against datasheet calc_temp() calc_pressure() pressureinHpa=pressure%/100 print "Temperature = ",str$(temperature%/10,4,1)," Deg C" print "Local pressure = ",str$(pressure%/100,4,1)," Hectopascal/mb" end ' ' calc_temperature%: calculate the temperature% from the raw temperature% given the calibration parameters ' sub calc_temp: x1%=(UT%-ac6%)*ac5%\powerof2(15) x2%=mc%*powerof2(11)/(x1%+md%) 'This needs to be a floating divide to match the datasheet b5%=x1%+x2% temperature%=(b5%+8)\powerof2(4) end sub ' ' calc_pressure: calculate the pressure% from the raw pressure% given the calibration parameters and temperature% oUT%pUT% ' sub calc_pressure b6%=b5%-4000 x1%=(b2%*(b6%*b6%/powerof2(12)))\powerof2(11) x2%=ac2%*b6%\powerof2(11) x3=x1%+x2% b3%=(((ac1%*4+x3)*ossscale%(oss%))+2)\4 x1%=AC3*b6%\POWEROF2(13) x2%=(b1%*(b6%*b6%/POWEROF2(12)))\POWEROF2(16) x3=((x1%+x2%)+2)\4 b4%=ac4%*(abs(x3+32768))\powerof2(15) b7%=abs(UP%-b3%)*(50000\ossscale%(oss%)) pressure%=(b7%*2)\b4% x1%=(pressure%\powerof2(8))*(pressure%\powerof2(8)) x1%=(x1%*3038)\powerof2(16) x2%=(-7357*pressure%)\powerof2(16) pressure%=pressure%+(x1%+x2%+3791)\powerof2(4) end sub ' Function powerof2(i as integer) as integer powerof2=(1<<i) End Function |
||||
CircuitGizmos![]() Guru ![]() Joined: 08/09/2011 Location: United StatesPosts: 1427 |
That is fantastic and very timely! Micromites and Maximites! - Beginning Maximite |
||||
seco61 Senior Member ![]() Joined: 15/06/2011 Location: AustraliaPosts: 205 |
Hi Peter. Just curious - the current output of the "list commands" and "list functions" on the v5.05.06 H7 code shows 127 of each. I thought the current limit was 128 functions & commands - how do you add 2 extra functions? Mind you, I am very much looking forward to these new functions... Regards Gerard Regards Gerard (vk3cg/vk3grs) |
||||
seco61 Senior Member ![]() Joined: 15/06/2011 Location: AustraliaPosts: 205 |
Or are the operators treated separately? Regards Gerard (vk3cg/vk3grs) |
||||
Geoffg![]() Guru ![]() Joined: 06/06/2011 Location: AustraliaPosts: 3292 |
Yes, commands and functions+operators have separate token tables with each capable of holding 127 keywords/operators. When MMBasic loads a program it will substitute the appropriate token for each keyword so that the program executes much faster. In the early days a provision for 254 tokens was more than generous, if I remember correctly only 100 were needed for the first version. But as time marched on more features were added and the tables are now close to full. Peter's ports with even more features has filled the tables. There are a number of solutions but they all require an extensive rewrite of the interpreter's core which in turn will inevitably create a hoard of new bugs. Because of my aversion to bugs and desire to keep MMBasic stable I have held this day off by being careful when adding new keywords, reserving them for only the most deserving of features. Geoff Geoff Graham - http://geoffg.net |
||||
vegipete![]() Guru ![]() Joined: 29/01/2013 Location: CanadaPosts: 1132 |
Yeah, no one will ever need more than 640K of RAM. Could you assign the last token as a sort of shift key or ESC code, indicating a 2 byte token? (Or 2 of them, one for keywords, the other for operators.) A bit like ANSI terminal escape codes. Visit Vegipete's *Mite Library for cool programs. |
||||
robert.rozee Guru ![]() Joined: 31/12/2012 Location: New ZealandPosts: 2442 |
i presume that LIST COMMANDS and LIST FUNCTIONS is unlikely to make it into the MX170 due to space constraints. however, what would be really neat would be if there were a couple of pointers to the tables so one could easily write a short basic program to retrieve the text data. for instance MM.CMDTABLE and MM.FUNTABLE. cheers, rob :-) |
||||
Geoffg![]() Guru ![]() Joined: 06/06/2011 Location: AustraliaPosts: 3292 |
Yes, that is the best solution but it will require major changes to the core of the interpreter with the associated issues. Entirely possible but MM.CMDTABLE + MM.FUNTABLE would take another two tokens. A better solution would be to extend the PEEK() function. My only problem is wondering why would you want to do this? The documentation lists all the commands/functions/operators in much better detail. Geoff Graham - http://geoffg.net |
||||
robert.rozee Guru ![]() Joined: 31/12/2012 Location: New ZealandPosts: 2442 |
it is definitely not worth using up another two tokens for, or any significant flash space, as very few people would use the feature. it would just be nice to be able to generate command and function lists directly from the loaded firmware to use in creating an index. remember ages ago i was tinkering with something similar, that searched through the mmbasic source code and generated a similar list? this was part of an idea to automatically create a documentation database - a project that i've had on the back-burner for quite a while! i take it extending PEEK would not entail using more tokens? +1 for vegipete's idea of introducing a shift token ![]() cheers, rob :-) |
||||
robert.rozee Guru ![]() Joined: 31/12/2012 Location: New ZealandPosts: 2442 |
just a thought: introducing one or more shift tokens would allow merging commands and functions into a single token space, ie there would be no overlap of token values between commands and functions. this may simplify the interpreter. the simplest way of handling the shifting may be to convert tokens immediately (when interpreting) into 16-bit unsigned integers. that is: 0x80 to 0xEF = 1-byte tokens 0xF0 to 0xFE = shift tokens decode using: if token < 0xF0 then token16 = token else shift = token token = get_next_token() token16 = token + ((shift - 0xEF) * 0x70) this gives you a little under 1800 tokens to play with, ranging from 0x0080 to 0x077F. cheers, rob :-) |
||||
![]() |
![]() |
The Back Shed's forum code is written, and hosted, in Australia. | © JAQ Software 2025 |