EmbeddedRelated.com
Forums

Software architecture using C for mid-range PIC.

Started by Fevric J. Glandules September 9, 2009

Neil wrote:

> Vladimir Vassilevsky wrote: > > >> It is so much easier to use a basic time-slicing round robin >> preemptive multitasker rather then developing the complicated >> non-blocking state machines. Then, the tasks can be done as the >> straightforward linear code, and you don't have to worry about blocking. >> > In a PIC18 ?
What's the use of PICs or 8051x misery when AVRs, HC08s and other processors with the flat memory and the traditional stack are available? Vladimir Vassilevsky DSP and Mixed Signal Design Consultant http://www.abvolt.com
On Sep 9, 7:35=A0pm, "Fevric J. Glandules" <f...@invalid.invalid> wrote:
> If, for example, incoming commands are ASCII sequences > like "L11" for "LED 1 on" followed by an end-of-message > checksum byte, in pseudo-code, something like this: > > uart_isr() { // push chars into circular buffer > =A0 push_char_to_uart_buf() > =A0 if (char =3D=3D end_of_message) message_flag =3D 1; > }
I like this approximate approach, but I think you may find the specific implementation may break if you get complete messages queuing up in the receive buffer while you are handling a time-consuming one - your flag won't show how many, so you may miss the subsequent ones. I'd either change it to a count, or preferably detect the ends of messages when copying to the evaluation buffer. In the latter case, the ISR just becomes a software extensions of whatever tiny (typically 1 to 16 byte) buffer exists in the fifo peripheral.
nospam wrote:

> PIC18s are 8 bit processors.
So they are. I've spent enough time with the datasheet and looking at example assembler and my own disassembled C code that you'd think it would have percolated back to my consciousness by now. Mind, it was quite late when I wrote that.
Bob wrote:

<snip>
> Yes, that works fine. I've coded several things, both in-house and > outside products pretty much the way you describe.
<snip> Thanks to you and all the other respondents; sounds like I'm not straying too far into thedailywtf.com territory, so far.
Chris Stratton wrote:

> On Sep 9, 7:35&#4294967295;pm, "Fevric J. Glandules" <f...@invalid.invalid> wrote: >> If, for example, incoming commands are ASCII sequences >> like "L11" for "LED 1 on" followed by an end-of-message >> checksum byte, in pseudo-code, something like this: >> >> uart_isr() { // push chars into circular buffer >> &#4294967295; push_char_to_uart_buf() >> &#4294967295; if (char == end_of_message) message_flag = 1; >> } > > I like this approximate approach, but I think you may find the > specific implementation may break if you get complete messages queuing > up in the receive buffer while you are handling a time-consuming one - > your flag won't show how many, so you may miss the subsequent ones.
I'm bearing this in mind - the pseudo-code was as simple as I could make it, in order to make clear the general gist.
On Wed, 09 Sep 2009 23:35:07 +0000, Fevric J. Glandules wrote:

> Hi all, > > I am new to this group but checking over recent postings seems to > indicate a fair amount of Clue kicking around [1]. > > A bit of background before y'all tell me to RTFM or JFGI. > > I've got quite a lot of experience in programming 8 bit microcontrollers > with assembler; I've also done quite a lot of 32 bit embedded stuff > using an RTOS with C. What's new to me is using a 16 bit mid-range > PIC18 device in C. > > So I'm trying to make sure that I come up with a fairly sensible > architecture. Googling isn't much help - most of the "tutorial" stuff > out there is aimed at a very low level, or else you find discussion at > the nuts and bolts level of a particular interrupt handler. > > The application is probably fairly typical - inputs / commands come in > via the UART, peripheral devices need to be turned on and off in > response, other devices need to be queried via I2C for data, etc. > > My current thinking is to have a main() loop that is subdivided into > separate sections that deal with each peripheral in turn. > > Each code block will be non-blocking, and will implement a state machine > for each peripheral. Incoming comms will run off interrupts. In RTOS > terms, a sort-of round-robin system. > > If, for example, incoming commands are ASCII sequences like "L11" for > "LED 1 on" followed by an end-of-message checksum byte, in pseudo-code, > something like this: > > uart_isr() { // push chars into circular buffer > push_char_to_uart_buf() > if (char == end_of_message) message_flag = 1; > } > > main_loop { > if (message_flag) { > move_chars_from_uart_buf_to_command_buf() switch (command) { > case foo: foo_state = 1; > case bar: bar_state = 1; > } > } > // foo machine > if (foo_state) { > switch (foo_state) { > case 1: if (foo1()) foo_state = 2; > case 2: if (foo2()) foo_state = 0; > } > // bar machine > if (bar_state) { > switch (bar_state) { > case 1: if (bar1()) bar_state = 2; > case 2: if (bar2()) bar_state = 0; > } > } > > Does that make sense? Should the ISR do as little as possible or is it > a good idea to give it some "awareness"? > > The above pseudo-code is just meant to give an idea of where I'm > heading, so don't take it too seriously. I'm really looking for general > pointers on how to approach a mid-level architecture. > > Oh go on then, rip me to shreds. > > [1] I've also dug out the FAQ.
This looked interesting when I first saw it, but I haven't tried it: http://www.embedded.com/columns/technicalinsights/190302110? _requestid=61933. Writing for it will be a lot like the task loop you describe, except that you'll get prioritized tasking. Beware the stack -- the PIC 18xxx parts let you monitor the stack and move things off to RAM when it fills up, but making it work is your job. -- www.wescottdesign.com
On Thu, 10 Sep 2009 21:06:26 +0000, Fevric J. Glandules wrote:

> nospam wrote: > >> PIC18s are 8 bit processors. > > So they are. I've spent enough time with the datasheet and looking at > example assembler and my own disassembled C code that you'd think it > would have percolated back to my consciousness by now. Mind, it was > quite late when I wrote that.
I have found there are several coding constructs that help the MPLAB compiler create tighter code. Define a bit field for logical flags. The compiler will emit BTFSS, BTFSC instructions. If you use a full byte the compiler will emit a MOVF to W and test instructions. If you have something like: flag = 0; .... flag = 1; .... if (flag != 0) ... you can save a couple of instruction words by using flag += 1 instead of the flag=1. This is because the compiler will generate a INCF instruction instead of a MOVLW and MOVWF. Many times a switch statement will generate cleaner code than using a if/elsif/else The compiler does a lot of work dealing with pointers and pushing arguments on the stack. If I start running low on program memory I'll usually try different coding constructs and look at the assembly listing to find the best generated code. -- Joe Chisolm Marble Falls, Tx.
Tim Wescott wrote:

> This looked interesting when I first saw it, but I haven't tried it: > http://www.embedded.com/columns/technicalinsights/190302110? > _requestid=61933.
Thanks for that - it looks well worth reading.
Joe Chisolm wrote:

> Many times a switch statement will generate cleaner code than using a > if/elsif/else
I've written some code to parse incoming strings; 'AB1' triggers foo(), 'AB2' triggers bar(), and so on. Is the following sensible? // Coded with nested switches in the belief that this will // be far more code and RAM efficient than a strcmp... switch (*ptrUartRxBuf++) { case 'A': switch (*ptrUartRxBuf++) { case 'B': switch (*ptrUartRxBuf++) { case '1': foo(); break; case '2': bar(); break; } break; } break; }
Fevric J. Glandules wrote:
> Joe Chisolm wrote: > >> Many times a switch statement will generate cleaner code than using a >> if/elsif/else > > I've written some code to parse incoming strings; 'AB1' triggers foo(), > 'AB2' triggers bar(), and so on. Is the following sensible? > > // Coded with nested switches in the belief that this will > // be far more code and RAM efficient than a strcmp... > switch (*ptrUartRxBuf++) { > case 'A': > switch (*ptrUartRxBuf++) { > case 'B': > switch (*ptrUartRxBuf++) { > case '1': > foo(); > break; > case '2': > bar(); > break; > } > break; > } > break; > } >
Whether it is more efficient depends on a lot of things - compile it w/ assembly output and compare it to other implementations. It's not the way I would do it unless I had to. I'm always amazed at how hung-up programmers get about parsing. If you have the authority to define the command set, you can make it easy on yourself by having fixed length commands; Even a single character (A - Z, e.g.) might suffice. A query has a '?' in buf[1] (or whatever your command identifier length is), and a list of comma separated numeric arguments is trivial to collect. Since a small project often has no floating point, the numbers are all integers. Bob