Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 17:50 29 Mar 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : Alternative to s$=INPUT(a,#b) ?

Author Message
MustardMan

Senior Member

Joined: 30/08/2019
Location: Australia
Posts: 175
Posted: 01:37am 23 Oct 2020
Copy link to clipboard 
Print this post

Hi,

Is there an alternative to the function s$=INPUT(a,#b) where a is always 1, and b is pointing to a port other than the console?

If I simply wanted to grab a single character from the console I would/could use INKEY$

However, I want to grab a single character from a serial (COM) port. I have looked through the manual but come up empty.

Cut from my code:
OPEN "COM2: 38400, 70, ComInt2" AS #2

...process string, if special character is received zero string & reset DataLen...

SUB ComInt2
 tmp$ = INPUT(1,#2)
 IF tmp$=CHR$(1) THEN Special = 1
 IF DataLen2 < 65 THEN
   Buffer2A = Buffer2A + tmp$
   DataLen2 = DataLen2 + 1               ' Sadly BASIC has no increment function
 ENDIF
 DataFlag2 = 1                           ' We have data!
END SUB


Unfortunately the interrupt routine takes longer than quarter of a millisecond, which I suspect is primarily because of the string manipulating functions.

I can't read multiple characters [eg: INPUT(10,#2)] to speed things up because that won't allow me to check for an occasional special character without doing a string search which slows everything down again!

I can avoid using strings for the most part by simply POKEing and PEEKing characters into an array, but I have not found a function (like GETCH in C) that will let me grab a single character from a port. Any tips?

Cheers,
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3163
Posted: 03:16am 23 Oct 2020
Copy link to clipboard 
Print this post

I might be missing something here but what is wrong with s$=INPUT$(1,#nn) ?  It does just what you want, ie, grabs a single character from the stream opened as #nn.

If you are using the Micromite you can set an interrupt when a certain number of characters has arrived or when a specific character such as carriage return has arrived.  That is useful when you don't want to interrupt on every character.

Geoff
Geoff Graham - http://geoffg.net
 
MustardMan

Senior Member

Joined: 30/08/2019
Location: Australia
Posts: 175
Posted: 05:40am 23 Oct 2020
Copy link to clipboard 
Print this post

Hi Geoff,

I did not realise that I could interrupt when a certain character is received, which is exactly what I would like to do (assuming 'certain character' can be any character I like).

That will save me considerable effort building a string while checking every time if the character is a 'special' character!

However, I can't find reference in any of the manuals I have of how to do this (looking under the 'OPEN' command, or in the more lavish appendices description of Serial Communications). Perhaps I have an older set of manuals? Is it something CMM or MM or MM+ specific?

Wondering also how I would read a block up to that character without knowing the number of characters to get... If I read a block (eg: 200) characters hoping the last character would be my 'special' character (and therefore return), there might be more in the buffer by the time I get there?

Cheers,
 
Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1584
Posted: 05:56am 23 Oct 2020
Copy link to clipboard 
Print this post

It is for the Micromite only look in the latest manual under the OPEN command. Unfortunately not available in the CMM2 of the other Maximites.

  Quote  ‘comspec$’ is the communication specification and is a string (it can be a string variable) specifying the serial port to be opened and optional parameters. The default is 9600 baud, 8 data bits, no parity and one stop bit.
It has the form "COMn: baud, buf, int, int-trigger, DE, 9BIT, INV, OC, S2"


  Quote  ‘int-trigger’ sets the trigger condition for calling the interrupt subroutine. If it is a normal number the interrupt subroutine will be called when this number of characters has arrived in the receive queue. Alternatively, if the number is prefixed with an equals character (=) the interrupt subroutine will be called when a character with this ASCII value has arrived in the receive queue. For example, if the following was used "COM1: 300, 256, MyInt, =13" the interrupt subroutine will be called when a
carriage return character (ASCII value of 13 decimal) was received.


Bill
Keep safe. Live long and prosper.
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3163
Posted: 08:13am 23 Oct 2020
Copy link to clipboard 
Print this post

Thanks Bill...

  MustardMan said  Wondering also how I would read a block up to that character without knowing the number of characters to get... If I read a block (eg: 200) characters hoping the last character would be my 'special' character (and therefore return), there might be more in the buffer by the time I get there?

I would do it in this way.
In the interrupt sub:
- Read the whole comms buffer into a local string variable.
- Use INSTR() to find your special char (that caused the interrupt).
- Split the string into two (before and after the position of your special char)
- Save the part after your special char in a static variable.  
- On the next interrupt you would prefix this to the part before your special char.

Probably easier to just present a possible solution (the special char is CR, decimal 13):
SUB MyInt
 LOCAL in$ = INPUT$(255, #nn)
 LOCAL posit = INSTR(in$, CHR$(13))
 STATIC overrun$
 GlobalString$ = overrun$ + LEFT$(in$, posit)
 overrun$ = MID$(in$, posit + 1)
END SUB

Note: This untested and will need some error checking.

Geoff
Geoff Graham - http://geoffg.net
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1983
Posted: 10:12am 23 Oct 2020
Copy link to clipboard 
Print this post

you have a load of valid answers here but just to add my 2p.

I dislike INPUT as a "finished product" input method as your program loses control while MMBasic zips round internally waiting for something to happen - they are "Blocking". Fine for a quick scribble bit of code etc but not for anything other people are going to use.

I prefer to have a general string that I build up my response into and then set a flag when my special character is received. This gives my code total control on what it does, when and what it does in the meantime.


dim a$,r$' r$ will be the aggregate of any key presses
dim integer crfg' this is the "magic" character indicator flag

Main:
a$=inkey$ ' grab a key from the keyboard...
if a$<>"" then ' ... if there is one
 select asc(a$)
  case 13 ' ... is it CR?
   crfg=1
  case 32 to 126 ' is it a printable ascii char?
   if len(r$)<255 then r$=r$+a$' pop it on the string
 end select
endif

...

if crfg then ' ... is the CR flag set?
 crfg=0 ' ... clear it for next time
 'process your string here
endif


with the above, it just free-wheels with the main loop taking characters from the buffer as they appear and setting a flag to let anything interested know that a string is ready. It won't stop anything else running, takes up a minuscule amount of resource and program space and doesn't require an ISR.

It could be improved... dealing with characters that come in after the CR etc. but might give you some ideas

have fun
Edited 2020-10-23 20:16 by CaptainBoing
 
MustardMan

Senior Member

Joined: 30/08/2019
Location: Australia
Posts: 175
Posted: 10:20am 23 Oct 2020
Copy link to clipboard 
Print this post

@ Bill,

Thanks for that little piece of gold!

That certainly isn't in any of my manuals (v5.05), but it is certainly there in the latest v5.05.03 manual from Geoffs site (which I've now just downloaded).

I have the MM+ v5.05.01 interpreter running on my system. It is probably about time I update that too.

@ Geoff,
Thanks for the hint on code to deal with 'extra' characters after the trigger! Much appreciated.


I think it is fantastic that you (and others too) put so much effort into this stuff. I wish the MM family was known about more widely - it primarily seems to spread by word-of-mouth (eg: the '8-bit guy' giving the CMM a talk up on youtube).


Cheers,
 
MustardMan

Senior Member

Joined: 30/08/2019
Location: Australia
Posts: 175
Posted: 11:04am 23 Oct 2020
Copy link to clipboard 
Print this post

@ Captain

That is very close to what I was doing with my code, except my data was not coming from the console! My (now corrected) thoughts were thinking "I wonder if there is an INKEY$ function for a COM port?"

The suggestions made by Bill & Geoff will get me out of upcoming trouble. My 'single character' code works fine for testing a simple single channel low-speed input, but as I start to add the project dictated improvements (eg: a second channel is required, and output too, and graphics, and touch...), my ISR was taking an uncomfortably long time to deal with grabbing single characters. Time that I probably wont end up having.

Cheers,
 
CaptainBoing

Guru

Joined: 07/09/2016
Location: United Kingdom
Posts: 1983
Posted: 11:49am 23 Oct 2020
Copy link to clipboard 
Print this post

ah yes, for COM ports other than the console...

replace  
a$=inkey$

in the above code with

a$=input$(1,2)

... assuming your com port is 2 - change as appropriate.

As already suggested it is a direct replacement for Inkey$ for a com port.

The above change still gets the characters one at a time which might be inefficient but as a drop in replacement for Inkey$ it will deliver exactly the same functionality.

My worries about Input blocking your code does not apply to Input$ as it returns immediately, it is non-blocking.

you can actually use Input$(n,0) instead of Inkey$ for the console (it is stream 0). Which makes for tidy code... you could set a Const to zero while testing and feed your prog from the keyboard, then set it to the actual com port number for the final thing. Same for logging - easy to switch between console and a log file.

hth
Edited 2020-10-23 22:05 by CaptainBoing
 
MustardMan

Senior Member

Joined: 30/08/2019
Location: Australia
Posts: 175
Posted: 08:52pm 23 Oct 2020
Copy link to clipboard 
Print this post

Another question, sort of the opposite to trying to read more characters than is available... best an example:

OPEN "COM2: 38400, 70, ComInt2, =0" AS #2
20 characters come in, then my special character (&H00). Interrupt triggers.
scenario 1: x$=INPUT(30,#2)   ' Would return after 21 characters are read
scenario 2: x$=INPUT(10,#2)   ' I like using the hash symbol so my intent is obvious

I have not had a chance yet to try the "=trig" trigger condition in the OPEN command. That will be later this-morning, or this-afternoon! (kids & soccer, you know the drill).

If I run scenario 2, will the interrupt trigger again since I have not actually removed my special character from the buffer?

I don't like coming here asking silly questions, that is things I can easily try out to see the result, but I can't think of a way to test this with my data source, which is something I can't change.

My sending device constantly spews out data (like a GPS module, but it is not). Frame start, data, frame stop. Next frame start, data, frame stop. Frame number in binary (&H01, &H02, etc) and frame stop is always &H00.

Actually while typing I thought I could use x$=INPUT(n,#0) to point to the console and do a test while typing, setting my special to eg lower case z, and see what happens. And rather than delete this post and all my typing, I'll leave it here...

Cheers,
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3163
Posted: 09:20pm 23 Oct 2020
Copy link to clipboard 
Print this post

  MustardMan said  If I run scenario 2, will the interrupt trigger again since I have not actually removed my special character from the buffer?

No, you won't get a re-trigger of the interrupt routine.  MMBasic watches the incoming data and only flags an interrupt when it sees the special character arrive.

  MustardMan said  Actually while typing I thought I could use x$=INPUT(n,#0) to point to the console and do a test while typing, setting my special to eg lower case z, and see what happens

That will not work because the console is always open and you need to set the special character in the OPEN command.  If you have another Micromite you can program it to generate a suitable test serial stream.  For example, I have a program that simulates the output from a GPS module and it is invaluable when I am developing something using GPS.

Geoff
Edited 2020-10-24 07:30 by Geoffg
Geoff Graham - http://geoffg.net
 
JohnS
Guru

Joined: 18/11/2011
Location: United Kingdom
Posts: 3641
Posted: 09:36pm 23 Oct 2020
Copy link to clipboard 
Print this post

edit: D'oh! Geoff beat me to it!

I can't try those either so am to some extent guessing...

I think you need to show what is in ComInt2, but if it has those INPUTs:

scenario 1: will wait for more chars to get 30 of them - you don't want to do this in an interrupt and also it sounds wrong for your use case

scenario 2: no, you won't get another interrupt (unless another 0 byte occurs)

You can use LOC to find out how many chars have been received...

John
Edited 2020-10-24 07:36 by JohnS
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5867
Posted: 09:54pm 23 Oct 2020
Copy link to clipboard 
Print this post

You can link com1 to com2 and then send your test string out com1.

A USB - TTL adapter is handy to connect to com2 and use MMBasic/DOS to generate test streams if you don't have a spare micromite.

More than one way to skin a cat.
Lots of cats getting skun this weekend in OZ.


Jim
VK7JH
MMedit   MMBasic Help
 
MustardMan

Senior Member

Joined: 30/08/2019
Location: Australia
Posts: 175
Posted: 11:11pm 23 Oct 2020
Copy link to clipboard 
Print this post

Well, I went away to try my ideas...

As I suspected I had to upgrade to the latest MM+ version so "=trig" was available in the OPEN command. About time anyway.

So then I tried:
OPEN "COM4: 38400,70,ComInt2,=122" AS #2   ' z = 122
Error: Serial port is used for the consol

Of course! I should have thought of that!
(and yes, there is no 'e' on the end of the error message)

OPTION CONSOLE OFF           ' Inside the program (so program can turn it back on later)
OPEN "COM4: 38400,70,ComInt2,=122" AS #2
Error: Pin 89 is reserved on startup

This is getting difficult! Time to return to my browser, report my findings, and have a harder think.

So, people have suggested I use another port to output data. I hadn't thought of that. A solution is at hand!

But, Geoff has already answered my question about triggering (attempting use with a 'buffer' that is a bit small).
Geoffg
  Quote  No, you won't get a re-trigger of the interrupt routine.  MMBasic watches the incoming data and only flags an interrupt when it sees the special character arrive.

Well, no need for further testing on what happens with 'under sized' buffers then.

And Geoff has also said in that same reply why console/COM4 would not work.


However I came across a much more serious problem:
OPEN "COM2: 38400,70,ComInt2,=0" AS #2
Error: 0 is invalid (valid is 1 to 255)

Oh well, it looks like I'm stuck with the way I am currently doing it (it works, it just takes a long time in the ISR). Maybe I could write a CSUB... wonder if that would be faster...

Cheers,
Edited 2020-10-24 09:14 by MustardMan
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 5867
Posted: 11:33pm 23 Oct 2020
Copy link to clipboard 
Print this post

  Quote    OPEN "com2:38400,256, dataIn,10" AS #2
 
 
DO
   
IF flag = 1 THEN
   
'do stuff with packet$
   ENDIF
 
LOOP
 
SUB dataIn
 buffer$ = buffer$ +
INPUT$(50,#2) ' clean out the incomming and append to buffer.
 endbyte = INSTR(buffer$,CHR$(0)) ' find the end-of-data
 IF endbyte > 0 THEN
   packet$ =
LEFT$(buffer$, endbyte) ' strip off the single packet of interest
   buffer$ = MID$(buffer$,endbyte+1)
   flag =
1 ' set a flag to signal data has arrived
 ENDIF
 
END SUB


It will need some adjusting to the interrupt level to suit your data.
Grab all available bytes and add then to the pile.
Rinse and repeat...

Be careful with the maximum string length.

Jim
VK7JH
MMedit   MMBasic Help
 
Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1584
Posted: 11:34pm 23 Oct 2020
Copy link to clipboard 
Print this post

Murphy's law got you, you picked the one character that it wont accept. In your code snippet in the first post you where looking for chr$(1) though?

I would have thought that chr$(0) should have been OK though it's still a valid character in a data stream.

Bill
Keep safe. Live long and prosper.
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3163
Posted: 03:44am 24 Oct 2020
Copy link to clipboard 
Print this post

  MustardMan said  So then I tried:
OPEN "COM4: 38400,70,ComInt2,=122" AS #2   ' z = 122
Error: Serial port is used for the consol

Something is strange here.  The error should have been: "Error: In use by the console"
(with an e at the end)

  MustardMan said  OPTION CONSOLE OFF           ' Inside the program (so program can turn it back on later)

In the change log for Micromite MMBasic Ver 5.05.03
Fixed an issue with initialising COM4:. OPTION CONSOLE OFF or ON will now cause a reboot and is recalled on startup.

So when you used it in your program it caused a reboot (I need to disallow OPTION CONSOLE OFF in a program).

  MustardMan said  However I came across a much more serious problem:
OPEN "COM2: 38400,70,ComInt2,=0" AS #2
Error: 0 is invalid (valid is 1 to 255)

That sounds like a bug.  Give me a little time and I will report back.

Geoff
Geoff Graham - http://geoffg.net
 
Geoffg

Guru

Joined: 06/06/2011
Location: Australia
Posts: 3163
Posted: 04:23am 24 Oct 2020
Copy link to clipboard 
Print this post

For complicated reasons to do with the way serial interrupts are handled you are only allowed to use a special character for interrupt in the range of 1 to 255 decimal.  So the error message was correct.

Even worse, you cannot use interrupts at all on COM4.  So sorry Owen, you are right out of luck there.

I will update the manuals to reflect these restrictions.

Geoff
Geoff Graham - http://geoffg.net
 
MustardMan

Senior Member

Joined: 30/08/2019
Location: Australia
Posts: 175
Posted: 08:51am 24 Oct 2020
Copy link to clipboard 
Print this post

Hi Geoff,

Thankyou for your very useful comments, and many thanks for spending the time looking at the interpreter innards. I really appreciate it.

I'll try the OPEN "COM4...." again to see if the 'e' is dropped off the end of the error message. It could have been my terminal emulator playing up.
EDIT: Yep: just verified. The 'e' is present.

@TassyJim
Unfortunately response time is pretty important in my scenario, so I can't really afford to wait for several extra characters if I luck-out and my frame terminating character happens to be early on (eg: 1 or 2). I'm lucky that the data is not screeching out at 38400 baud (that is the baud rate, but the data is coming out at about a character per millisecond).

At the moment I have it working pretty well with an interrupt after a single character, with my graphics drawing functions mostly implemented. I'll be starting implementation of my touch functions tonight.
Currently, after it finds my special character (0x00) and it has to process the so-far-received-buffer, the interrupts certainly get busy for a while!
It is actually looking pretty hopeful it will get everything done (it seems the speed of processing everything else is going to be fast enough to make up for the loss of time in the ISR).

Cheers, Owen.
Edited 2020-10-24 19:04 by MustardMan
 
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024