Depends on the communications flow. If the commands occur anywhere in the buffer and you have an ISR filling the UartRXBuf then things get complicated. If the flow is a simple command/response and the commands will always be at the beginning of the buffer, then simplify life. UartRxBuf will be static, hopefully in access memory. If this can be the case then use something like: switch(UartRxBuf[0]) case 'A': switch(UartRxBuf[1]) ...
Of course, part of this decision is if you are speed or memory size constrained. If you are not hitting up against a program memory size limit or not hitting a execution speed limit, then program in a style you like. If you have to start playing tricks, please be nice to the next person who has to look at the code and comment, comment comment - and not crappy comments like // load character from buffer
Joe Chisolm
Marble Falls, Tx.
Didn't find your answer? Ask the community — no account required.
F
Fevric J. Glandules
That's probably what I would have done; OTOH at least they are all exactly three characters long.
And numbers - whatever they are - are also all exactly three characters long, which helps. E.g. 12.3 volts is "123".
H
Hans-Bernhard Bröker
It can be. It may not be. It all depends on the circumstances. How many commands are there? Do you get any chance to change the command names? Is the command set fixed for the foreseeable future or likely to change at a whim?
For large-scale applications of such lexical recognition, there are general-purpose tools like regexp, tool generators like lex/yacc, and perfect hash generators.
I'll second the suggestion to leave the pointer alone. Make those ptrUartRxBuf[0] and ptrUartRxBuf[1]. *ptr++ is a useful idiom sometimes, but not really in this case.
F
Fevric J. Glandules
Still up in the air.
I was going on something I'd remembered from this:
formatting link
but now you've made me go back and check my facts it seems that array indexes should only be avoided (and pointers used instead) for "large" (>256 byte) arrays.
Or perhaps the last line on page 64 really does mean that pointers are /always/ more code efficient than array indexing.
Either way, I really shouldn't be thinking about these issues at this stage at all! Premature optimisation and all that...
+1 to that.
F
Fevric J. Glandules
The command set is fixed, and consists of about 30 ASCII triplets, and with any luck won't change too much.
What I'm doing at this stage is, I'm afraid, asking rather open-ended and general questions with the aim of getting into the right mindset for this level of device / code. I've done embedded stuff with bytes of RAM and with megabytes of RAM; just not with kilobytes. In addition, all that was a few years ago - I've mostly been doing PHP, mysql, and Linux server admin in recent years.
Meanwhile I've got a little less than 4K of RAM to play with .
As per the other post, I was going on some Microchip advice to use pointers not indexes - advice I probably misunderstood the first time I skimmed through the hundreds of slides in that PDF.
Thanks to all, again, for the responses, questions and suggestions.
And if anybody here is struggling with the DMA engine on a PPC403, I may have some tips for you .
H
Hans-Bernhard Bröker
Encoding a mere 30 commands as three-letter tokens is excessively redundant. I would strongly suggest to abbreviate further, to one or two letters. In the process you would both reduce your communication load and speed up the interpretation.
You could still use a perfect hash function. That would basically amount to letting a machine condense your command set to single letters, in whichever way it liked.
Or you could combine all three letters into a single number, then switch() directly on that:
switch((u16)buf[0]
S
Stef
F
Fevric J. Glandules
So would I but the spec says (e.g.) "0x01 0x44 0x45 0x41 0x03" [0] i.e. "start-byte ascii ascii ascii end-byte" and the chip that's talking to me says the same too. I'm not designing [1], I'm implementing. If the spec says "jump" I ask "how high?". Only if the spec says "jump off a cliff" do I raise objections.
P
Paul Keinanen
The command table would consume 30x3 bytes and a jump table based on the command index 30x2 bytes (assuming 16 bit addresses), so the total overhead would be 30x5=150 bytes and perhaps 10-20 bytes for scanning the command table and then performing an indexed jump based on the result.
Or is that processor too braindead to perform an indexed jump ?
With such a large number of commands, it is more memory space effective (on any regular processor at least) to scan a command table and jump/call an action routine based on the command index than build a case/switch structure on a character by character basis.
Anyway, if the commands are generated by a human user, 3 unambigous characters is quite sufficient to create a large command set, in which the user can type in the whole command 3 to INFINITY characters long (which is great for documentation purposes).
A command table is also useful if support for commands in different laguages is needed. A switch/case structure would be a mess to maintain for each language :-).
Paul
P
Paul Carpenter
I agree with that and have implemented such schemes several times, first time being in 1983 on an 8 bit processor with limited memory.
So much easier to add/remove commands to the set.
Extending this to a command line structure becomes a lot easier where you match the first part (upto first delimiter often a SPACE), and get each command function to call common routines to check the additional parameters and convert them.
Easier to reuse the code later in another project.
Paul Carpenter | paul@pcserviceselectronics.co.uk
PC Services
Timing Diagram Font
GNU H8 - compiler & Renesas H8/H8S/H8 Tiny
For those web sites you hate
P
Paul Carpenter
=20
So put the list of triplets in a command table, that means it is easy to maintain, then have a matching table of functions to jump to.
If you are clever then if the last digit refers to a mode you only match the first two and then process the last digit appropraitely. Especially if the command set is two digit alpha for function and third digit is numerical parameter.
With a command table you can choose whether to match all three or the first 1 or 2 characters, so the rest are checked by the called=20 function.
Alternatively you can decode the last digit if it is always numerical and pass that to the called function.
=20
A scan of command table is easy to write in C or even assembler. If all commands start and end with the same byte sequence the processing can be easier, and these bytes handled before processing the data between the terminators by scanning a command table.
Also a command table allows you later add a four or larger command as=20 well. =20
In 1983 I wrote an application to control TV studio Digital Video Effects controller on an 8 bit micro, that had debug routines to enable the maintenance and developers to examine memory, registers, devices and had a command line structure that decoded additional numerical/alpha=20 parameters.
This sat in an 8 bit processor with external RAM of 2kBytes for the whole application with over 1kBytes of the RAM spare!
All written in assembler. I have since rewritten and used similar code or reused similar code in C.
,=20
I have done command table with index jump on an 8 bit processor, it is nothing new.
If you build the right flexibility into the command table handling once, you can make all your command processing easier and smaller.
Some of my command table implementations, include=20
=09min. max character match =09min, max parameters on same line =09System mode that command can be entered in =09different tables sometimes for which mode of operation. =09function(s) to call. (same code different table)
Then generalised functions deal with parsing, function calling, parameter checks, parameter conversions, error reporting handling.
I personally would not use a PIC, but have used various small processors to do this sort of command processing, even ones with binary packet transmissions.
--=20 Paul Carpenter | snipped-for-privacy@pcserviceselectronics.co.uk PC Services Timing Diagram Font GNU H8 - compiler & Renesas H8/H8S/H8 Tiny For those web sites you hate
C
ChrisQ
Table driven methods are invariably the best way to deal with this, as there will always be more commands later. The standard way here is to define the parse table as a n way linked list of nodes, where each node has a table of valid chars at that node, and associated links to either another node or function pointer to exec the command. As the syntax is defined by the structure of the tree, it can handle command sequences of any length.
It works well for small and large command sets and the code to drive it just becomes a list processor, around a dozen lines of C from start to end. Also, you only have to write the code once, a new syntax just needs a new set of node definitions.
Regards,
Chris
F
Frank Buss
And if you are lazy and don't want to write tree tables by hand, you can use flex for it. This one, just in case you don't know it:
formatting link
Frank Buss, fb@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
R
Rocky
f
Or if more lazy and using C define a structure that contains a pointer to a const string and a pointer to the function that handles that particular string. Declare an intitilised array of this structure. Also been using it since the assembler days on a Z80 and now on the
8052 and the PIC in C.
F
Frank Buss
Yes, this is possible for very simple protocols. If you have many commands or some kind of syntax, flex is better, because you can match tokens with regular expressions. Maybe combined with Yacc, or your own simple recursive descent parser.
Frank Buss, fb@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
V
Vladimir Vassilevsky
Create an unambiguous fixed width binary protocol over RS-232. Implement some basic integrity checking (such as checksums) so the occasional garbage characters would not be interpreted as the valid commands. Create a PC GUI application to communicate to your device. The common users love GUI and hate command line and batch files. Depending on your application, it could make sense to stick with a standard protocol, such as MODBUS. This makes the things lot simpler for both developers and users.
Vladimir Vassilevsky DSP and Mixed Signal Design Consultant
formatting link
C
ChrisQ
I've never used flex or yacc, perhaps because every time I look at it, it seems to be more than I really need for simple command parser. It's probably very good if you want to write a compiler, but most embedded command processors use a simple syntax to reduce processing overhead. Have also looked at tcl some years ago, but once again, more than is really needed to do the job, or there were bits I just didn't like, or just simply that it's easier to pull a previous library module out, copy, edit, resave under a new name, to do the job.
What would be ideal would be a sort of half way house between flex and table editing manually. Something that is aware of the defined data structures and allows some sort of simplified formal language specification, which then gets compiled into the tables. The key thing here is 'aware of the data structures', which basically means do it yourself. Thus, all there is at present is a set of macros to make the table editing easier.
There's a lot of difference between a true command language syntax, for example 'set voltage 24-e3', where it's often easier to use a tokeniser followed by lexical and syntax analysis and the sort of fixed format protocol embedded commands:
cmd arg1 arg2 ... arg-n
cmd is fixed at 1 to n chars long, with a variable number of args. It's quite easy to write a completely deterministic parser for this using the tree of nodes method, with everything that doesn't match defined as an error. Because there's not much of it, it's quite easy to follow and understand. Contrast that with the typical method of deeply indented case statements implementation, which just becomes unreadable after a couple of pages :-)...
Regards,
Chris
T
Tauno Voipio
DO NOT use MODBUS. The protocol has tight timing requirements for the characters in a packet and for the intervals between packets. It is practically impossible to create a MODBUS program with proper timing running in a PC (Windows or whatever).
Yes, there are MODBUS programs for PC's, but I have not seen yet one with the timing according to the bus specifications.
Tauno Voipio
tauno voipio (at) iki fi
H
Hans-Bernhard Bröker
Erm ... no.
Well, I won't be held liable for you having bad tools.
If you really can't trust your C compiler to generate efficient code for a sparse switch on a non-minimal integer type, and since you appear to be allergic to any and all code generators, go ahead and code it yourself: binary search in a table of {command_string,action_function} structs, sorted by command_string. Points will be deducted for using any standard library function like bsearch(), of course. ;-)
F
Fevric J. Glandules
Yes, I think my switch / case thing is headed for /dev/null.
Thanks to all for the pointers. I'll be looking into some sort of command table / tree in due course.
Join the Discussion
Have something to add? Share your thoughts — no account required.
Didn't find your answer?
Ask the community — no account required
Report Content
You are reporting this content to the moderators. They will look at it
ASAP.