From: Frank Buss on 13 Sep 2009 00:01 Fevric J. Glandules wrote: > 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. If your protocol is simple, e.g. "command arg1 arg2 ..." newline "command arg1 arg2 ...", I would just start with something like Rocky described: void hello(void) { // you can parse command arguments here printf("hello "); } void world(void) { printf("world!"); } typedef void(*CommandFunction)(void); struct Command { char* name; CommandFunction function; }; struct Command commands[] = { { "hello", hello }, { "world", world } }; void evaluate(char* commandName) { int i; for (i = 0; i < sizeof(commands) / sizeof(struct Command); i++) { struct Command* command = &commands[i]; if (strcmp(command->name, commandName) == 0) { command->function(); return; } } printf("command not found: %s\n", commandName); } int main(int argc, char** argv) { evaluate("hello"); evaluate("world"); evaluate("foo"); return 0; } If this is too slow, sort the commands alphabetically and use a binary search. For most projects C18 produces code which is fast enough. Don't worry about speed until it is too slow :-) E.g. if you have a 100 ms measure cycle, it doesn't matter if a function needs 100 us or 1 ms and writing code which is easy to read and to maintain is more important. -- Frank Buss, fb(a)frank-buss.de http://www.frank-buss.de, http://www.it4-systems.de
From: Joe Chisolm on 13 Sep 2009 01:26 On Sat, 12 Sep 2009 22:26:57 +0000, Fevric J. Glandules wrote: > Hans-Bernhard Bröker wrote: > >> Fevric J. Glandules wrote: >> >>> Let me guess - you live in a 32 bit world, right? <grin> >> >> Erm ... no. > > Apologies. I based my assumption on: > switch((u16)buf[0]<<16 + (u16)buf[1]<<8 + (u16)buf[2]<<0) > > Is this 8-bit PIC friendly? As I said, I'm trying to get myself (back) > into the 8 bit world. So I really don't mind (much) having my ignorance > pointed out. > The C18 compiler will compile your construct but will generate a lot of instructions to handle it. The PIC architecture does not have a bunch of general registers for C18 to use for register allocation, it only has an accumulator (the W reg) and a few other registers you can use. >>> This is an 8 bit chip, and from what I've seen of its output, the C18 >>> compiler isn't all that brilliant [2]. >> >> Well, I won't be held liable for you having bad tools. > > Well, I'm stuck with it. Unless the C18 is *so* bad that I can make a > case for the client spending more money. Any opinions here on how the > C18 measures up? > The free C18 compiler does not do sub-expression elimination and some other common optimization techniques but does generate reasonably tight code. It wont beat carefully crafted assembler but I know of only 3 or 4 compilers I've ever run into that could beat hand generated assembler for code density. You will generally find that the 2K SRAM is enough. I've had some pretty big projects using serial, USB, driving LEDs and relays along with a bunch of other stuff and not used more than 1000 to 1200 bytes of SRAM. The IDE has some tools to help you watch memory usage. >> 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 > > I am in no way allergic to anything that will save coding time - but I > do worry about how much code space, RAM and CPU cycles general purpose > tools will consume. > >> 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. ;-) > > Happy to lose points if I gain time! Lookup tables, constants, etc can be forced into flash locations to reduce runtime SRAM requirements. But you suffer a hit having to do a flash read. Since you are new to PIC, carefully study the manual regarding the various run time libraries, pay close attention to the string functions, memory functions and printf. There are different flavors depending on if the source (or destination) is in flash. I have also found that the default IDE libraries are compiled for 24 bit pointers and you suffer a hit with this. They work but you have to make sure your code memory model matches the library's LARGE model. Anytime I load the IDE I recompile the libraries with the SMALL memory model which gives me 16 bit pointers. This works for all the low end PICs that have less than 64K of flash. -- Joe Chisolm Marble Falls, Tx.
From: Paul Keinanen on 13 Sep 2009 05:39 On Sat, 12 Sep 2009 16:36:55 GMT, Tauno Voipio <news(a)sem.pp.fi> wrote: > >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. In practice, accurate timing is only required in multidrop slaves. Multiple PCs are seldom used as slaves on the same RS-485 bus. When the PC acts as a Modbus master or a point-to-point slave, the connection works well with conservative timing, although there might be a few performance in throughput. Paul
From: Paul Carpenter on 13 Sep 2009 07:01 In article <1lzcgh2brzgjb.18skbxt6uqpxo$.dlg(a)40tude.net>, fb(a)frank- buss.de says... > Fevric J. Glandules wrote: > > > 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. > > If your protocol is simple, e.g. "command arg1 arg2 ..." newline "command > arg1 arg2 ...", I would just start with something like Rocky described: > > void hello(void) > { > // you can parse command arguments here > printf("hello "); > } I tend to use a command function that returns an integer/byte (depending on architecure). Reason being if you return an error code you can do some common error reporting for the parsing such as using return values for 0 Success 1 first argument missing ... 'n' max argument number missing 0x81 first argument invalid 0x8n max argument invalid 0xFF hardware failure I normally make the first command tried a 'help' command to list in short form valid commands accepted, and two other commands to return strings of product name and software version. These of course normally return no errors. This way you prove the structure of the code before proving the more complicated commands. ..... > void evaluate(char* commandName) > { > int i; > for (i = 0; i < sizeof(commands) / sizeof(struct Command); i++) { > struct Command* command = &commands[i]; > if (strcmp(command->name, commandName) == 0) { > command->function(); or error = command->function(); // insert common argument function error handling here > return; > } > } > printf("command not found: %s\n", commandName); > } -- Paul Carpenter | paul(a)pcserviceselectronics.co.uk <http://www.pcserviceselectronics.co.uk/> PC Services <http://www.pcserviceselectronics.co.uk/fonts/> Timing Diagram Font <http://www.gnuh8.org.uk/> GNU H8 - compiler & Renesas H8/H8S/H8 Tiny <http://www.badweb.org.uk/> For those web sites you hate
From: ChrisQ on 13 Sep 2009 06:32
Frank Buss wrote: > Fevric J. Glandules wrote: > >> 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. > > If your protocol is simple, e.g. "command arg1 arg2 ..." newline "command > arg1 arg2 ...", I would just start with something like Rocky described: > > void hello(void) > { > // you can parse command arguments here > printf("hello "); > } > > void world(void) > { > printf("world!"); > } > > typedef void(*CommandFunction)(void); > > struct Command { > char* name; > CommandFunction function; > }; > > struct Command commands[] = { > { "hello", hello }, > { "world", world } > }; > > void evaluate(char* commandName) > { > int i; > for (i = 0; i < sizeof(commands) / sizeof(struct Command); i++) { > struct Command* command = &commands[i]; > if (strcmp(command->name, commandName) == 0) { > command->function(); > return; > } > } > printf("command not found: %s\n", commandName); > } > > int main(int argc, char** argv) > { > evaluate("hello"); > evaluate("world"); > evaluate("foo"); > return 0; > } > > Sorry, but must disagree :-). The problem with the above is that you still have inband data embedded in the code. The only way to make the code completely reusable is to separate form and function. In this case, all the parser needs to know about is the *structure* of the data, not it's content. I would rewrite the code to allow this: eStatus = eParseExec (&sRootCommandNode); Where sRootCommandNode is the command parser table root and the command functions are called via the table contents. It might seem a trivial change, but can have a major impact w/regard to future maintenance and spec change. The command parser is completely encapsulated and can be replaced in the same application if the command format changes. Being encapsulated, with no global data, it's easy to make it into a library module. After all, who has the time to rewrite the same stuff over and over again ?. Taking the time to design good data structures is worthwhile, imo and is the key to a robust design... Regards, Chris |