Forums

Software architecture using C for mid-range PIC.

Started by Fevric J. Glandules September 9, 2009
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.

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. >
Yes, that works fine. I've coded several things, both in-house and outside products pretty much the way you describe. It's a non-preemtive tasking system. In my projects, the main loop usually looks like: while (1) //lint !e716 yes, it's an infinite loop! { DoCommands(); DoJobs(); } DoJobs() iterates a list of "jobs" that can be triggered by time or by some other boolean result and executes the associated DoWork() pointer. The real time functions are handled in ISRs fired by things like UART Rx/Tx, external pins, and hardware timers. There's typically a "housekeeping" timer interrupt that increments systime and the keypad and display scanning, reads ADCs, sends DAC values, etc. If you're careful with ISR priorities, it's even OK for the housekeeping ISR to load the CPU at 90% and above. (Of course, the main loop only gets the CPU time that's left over - UI and remote commands are usually fine that way) Bob
On Wed, 9 Sep 2009 23:35:07 +0000 (UTC), "Fevric J. Glandules"
<fjg@invalid.invalid> wrote:

[snippety snip]

>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:
[snippage]
>Does that make sense? Should the ISR do as little as possible or >is it a good idea to give it some "awareness"?
Opinions differ. I'm from the do as little as possible school. Handle the interrupt, set a flag, and let the main loop do what needs to be done. There are exceptions, of course, where quite a lot has to happen within fixed time constraints and so the interrupt must accommodate it.
>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.
Quite a reasonable approach. I typically find it helpful use FIFO queues for very slow things like serial I/O. -- Rich Webb Norfolk, VA
"Fevric J. Glandules" <fjg@invalid.invalid> wrote:

>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.
PIC18s are 8 bit processors. Like PIC16s with a lot of ugly stuff kludged in to make them more powerful and slightly less unfriendly towards compiler writers. --

Fevric J. Glandules wrote:

> 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.
> 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.
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. Vladimir Vassilevsky DSP and Mixed Signal Design Consultant http://www.abvolt.com
Vladimir Vassilevsky wrote:
> > > Fevric J. Glandules wrote: > >> 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. > > >> 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. > > 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. > > > > Vladimir Vassilevsky > DSP and Mixed Signal Design Consultant > http://www.abvolt.com
In a PIC18 ?
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. >
It is a reasonable way. The interrupt usually should do as little as possible there are only two so they have to share. Your application may vary.
Vladimir Vassilevsky <nospam@nowhere.com> wrote:

>> Each code block will be non-blocking, and will implement a >> state machine for each peripheral. > >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.
No you just have to worry about finding program memory for an RTOS and RAM for task control structures and context saves and multiple stacks and worry about interrupt latencies and critical sections and resource protection and reentrant library functions.......on an 8 bit processor that has shit pointer handling and a 32 level hardware stack. --
Fevric J. Glandules wrote:

	(snip)

> Does that make sense? Should the ISR do as little as possible or > is it a good idea to give it some "awareness"?
It's a reasonable approach. I did a multi-serial port protocol converter much the same way. Incoming messages were buffered by a bunch of serial ISRs which triggered different semaphores when a full packet was ready. Conversion and pushing into the transmit buffer was done in the main loop. Again, serial ISRs handled the actual transmission IIRC. As for the 2nd question, the answer is (always) "depends". Depends on the requirements of your system. The rule is, do as little as you can get away with inside the ISR. Regards, -- Mark McDougall, Engineer Virtual Logic Pty Ltd, <http://www.vl.com.au> 21-25 King St, Rockdale, 2216 Ph: +612-9599-3255 Fax: +612-9599-3266
"Fevric J. Glandules" <fjg@invalid.invalid> wrote in message 
news:h89e3b$moi$1@news.tornevall.net...
> 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. >
What I did on a PIC16 was: .... in a header: // // Most state machines follow the same form. // They have a local static variable which is their next state. // The first time they are called, they perform their own // initialisation. fFirstCall is true only on their first call. // typedef INT16 FsmStateT; #define ReturnState(Label) {FsmState = label_address(Label);return;} INT1 fFirstCall; Somewhere in main() set fFirstCall, go round once, reset fFirstCall, then keep going round. Each finite state machine then looks something like this: #separate VOID FSM_UserIf(void) { static FsmStateT FsmState; PutState('v',FsmState); if ( ! fFirstCall ) { if ( MainStatus.MotorRunning ) return; goto_address(FsmState); } // // Initial state depends on the switches. Wait // for them to be known before deciding. // ++nInitialising; fArm = 0; fDisarm = 0; WAITING_TO_START: if ( ! Switches[0].Known || ! Switches[1].Known ) ReturnState(WAITING_TO_START); --nInitialising; DECIDE_NEW_STATE: .... }