>> I use cooperative multitasking with a state machine for each thread. >> Main loop calls thread if state is non-zero (maps nicely to TSTFSZ >> on 18F). Use states to implement timers and dismiss thread when it >> is waiting for UART to become ready (Tx) or to have a char (Rx). >> ... >> Remember that sending a dial string or reading a response take a >> _long_ time in PIC terms. You have to build a mechanism to block >> the processes in some sort of I/O wait. > Wow, thanks for the detailed reply. PIClist is great. > So are you interfacing a real analog MODEM? Yes. Connectix modem chip set on PC board with PIC 18F6621 and other peripheral chips. Modem presents parallel interface that looks identical to a PC 16550 UART with FIFOs. Modem interface would work the same if I had an external modem on the far side of a serial link via PIC UART. > What is the application? Medical with data comm; beyond that, client confidentiality kicks in. > Are you running your own OS (cooperative multitasking)? Yes. More of a round robin process switcher. All processes (more like light weight threads since they have to keep their own context) are staticly defined in code. Main loop is MAINLOOP TSTFSZ TICK50MS, ACCESS ; has a 50ms tick registered by ISR? CALL SYSTEM_THREAD ; yes ; TSTFSZ MODEMSTATE, ACCESS CALL MODEM_THREAD ; TSTFSZ FOOSTATE, ACCESS CALL FOO_THREAD ; TSTFSZ BARSTATE, ACCESS CALL BAR_THREAD ; BRA MAINLOOP System thread does switch scanning, debounce, LED on/off/blink, etc. Each thread (excluding system thread) starts with: MODEM_THREAD MOVF MODEMSTATE, W, ACCESS CALL DISPATCH_TOSTAB_BY_QUAD ; jump to appropriate code fragment RETURN ; bytes 1 & 2 of quad ; state 0 not used / not valid RETURN ; bytes 3 & 4 of quad ; each state MUST use exactly 4 octets GOTO M_1_HANGUP ; entry state GOTO M_2_HANGUP_DCD_OR_TIMER ; work state GOTO M_3_HANGUP_TIMER ; work state GOTO M_4_other_subtask DISPATCH_TOSTAB_BY_QUAD uses the value in W to select & execute one GOTO instruction by manipulating top of stack (TOS). After dispatch, return address on stack is one left by CALL MODEM_THREAD so a RETURN goes back to MAINLOOP. There's a matching DISPATCH_TOSTAB_BY_WORD if the addresses are localized enough to allow use of 2-octet BRA instructions. Threads adjust state variable to control which label gets control next time thread is activated. Frequently, same state is used for a while, e.g. take modem receive characters and fill a line buffer until a linefeed is seen for checking response codes. How you decide on the next state for a thread allows a whole lot of flexibility in code design. One thread can update the state variable of another thread to trigger an action (modem thread fills buffer, start flashrom thread to write buffer). You can have a multi-level stack of next states so you can sequence a thread's future actions. > What do you mean by the "a mechanism to block the processes > in some sort of I/O wait"? Just emphasizing the long (in PIC CPU instructions) delays between I/O events that require CPU action. Modem at 9,600 baud is ~1 character per millisecond. FlashROM goes away for 8-20 milliseconds after being given a command. Call setup & line speed negotiation can easily take half a minute. Parallel port printer may take thousands of octets as fast as you can feed it then go off-line for (literally) hours when it runs out of paper (and the staff is off at lunch). Since I'm dealing with multiple peripherals, there has to be some mechanism where a process can release the CPU (i.e. blocks itself) while waiting for an octet to arrive or depart. Threads must not spin wait. My software must time slice to multi-task. :-) When it runs out of data, thread just does a RETURN which goes back to the next TSTFSZ in MAINLOOP. Each thread state value keeps a bookmark into thread's overall task structure. Threads can almost be thought of as polled with polling rate varying but being pretty high. Designer must ensure that a single code fragment doesn't tie up the CPU for more than a few milliseconds. Keeping that restriction in mind, I limit each thread to only processing a certain amount of data before releasing CPU -- even if unlimited data is available. Usually, data runs out before (self imposed) time slice expires. Interrupts handle asynchronous data (e.g. modem receive chars) and fill a buffer. "Polled" modem thread empties buffer. This way, modem thread does not have to run several times per millisecond to keep from loosing data on a 56Kbps connection. Hope that all makes sense. Lee -- http://www.piclist.com PIC/SX FAQ & list archive View/change your membership options at http://mailman.mit.edu/mailman/listinfo/piclist