USBPIC Code Framework v0.3 M.Schippling 6/8/9 -- 8/19/12 A USBPIC program has this general layout: The top level user file will link with the master maincode.c file which contains interrupt service routines and the main() entry point. The user file will have a "preamble" header file named appconfig.h to set global values. This header will define sizes and conditionally compile various options. It is included in the compile for "core" source files, including maincode.c. Each interrupt handler has a user callout function that can be specified in the appconfig.h header which will be executed during the interrupt. Each handler will also have a user function and argument which it can post to the scheduler when it executes. If any of these are not defined in appconfig.h they will be left out of the compiled code... maincode.c outline: The main() routine will initialize the system, set ports and pins as needed, call the userinit() function to do application specific init, enable interrupts, and then call the scheduler infinite-loop. /* * Inline assembly that will jump to the ISR * for both interrupt levels. */ void low_interrupt (void) { /* Timer and ADC are on low priority */ } void high_interrupt (void) { /* Other devices as needed on high priority */ /* use should edit this if necessary */ } /* * Main program initializes ports and interrupts * calls the userInit() function in the user's application * and then falls into the scheduler loop. */ main() { /* System Init: /* initialize ports and pins, support devices, timer, etc */ /* User init function: Initialize specifics of user program */ /* Enable interrupts */ /* call scheduler */ } Interrupt routines: Timer0: A system timer runs at an appconfig.h pre-defined rate, and will maintain a 16 bit tick counter. It will run an ADC state machine to step through conversions on ADCs 0-3. It can run one or two stepper motor state machines. It can call-out to a user function every tick. the argument will be the tick counter value. It can post a function to the scheduler every tick or when the counter wraps around, the argument will be the tick counter value. ADC: The ADC can do sequential conversions on one or more of ADCs 0-3. It can call-out to a user function on each conversion complete, the argument is the ADC number. It can post a function to the scheduler on every conversion or when each full set is complete, the argument is the ADC number. Conversions can be averaged over a pre-defined number of samples, the call-out and posted functions will be executed at the end of the averaging period rather than on each conversion. Stepper: The stepper control will run the step state machine for one or two independent motors using these user supplied values: ticks -- number of timer ticks between steps; steps -- number of steps to do, 0 = stop, 0xff = forever; dir -- direction of stepping; The type of stepping can be pre-defined (for both motors) as: FULL -- regular single step 1-2-3-4; HALF -- half steps; DOUBLE -- double coil single step; The output bit sequence and port-bits can be defined in appconfig.h. The output tick counter will be maintained even when motors are disabled such that at least "ticks" will be counted between steps, except when ticks == 0. Each stepper can call-out to a pre-defined user function on every step and when the number of steps reaches 0. The argument will be the count of steps executed since last call. Each stepper can post a pre-defined user function to the scheduler on every step and when the number of steps reaches 0. The argument will be the count of steps executed since last call. USB: Polling only. UART serial: Whatever we need. TBD. I2C/SPI: Whatever we need. TBD. Capture/Compare/PWM: TBD. scheduler outline: The scheduler maintains a (short pre-defined-length) list of functions to be executed with specified tick delay counts. Each function has one 2-byte (word) argument. The scheduler data structure is: unit16 count -- timer ticks until execution, field is decremented on each timer interrupt, function is executed in main loop when count == 0, not decremented if count == 0xffff (NOENTRY) or 0xfffe (RUNENTRY); void *function() -- function pointer to execute; word arg -- function argument, can be a pointer The timer interrupt decrements each count on every tick. The scheduler itself runs in the main code context (not interrupt). It steps through the function list and executes them if their count == 0, round-robin with no priority. Before it is executed the function's count is set to 0xfffe which marks the schedule-slot as in-use. The function may reinitialize itself by setting a new count, or make new entries in the list. An entry made with a count of 0 will be executed ASAP with no guarantee of order. Interrupt routines may make entries in the function list. Scheduled functions should insure that they don't hog the processor... The scheduler API is: initScheduler() -- initialize stuff, called from main() before interrupts enabled. decrScheduler() -- decrement function tick counts, in timer interrupt. scheduler() -- run the scheduler, from main() in user-mode. User functions: void postTask( word ticks, // ticks until exec -- 0==now, -1==empty PFV func, // pointer to function to execute word arg ) // function argument void repostTask( word ticks, // ticks to execution word arg ) // function argument User Functions: /* * User init function, called from main() before interrupts are enabled. */ void userInit() { /* Initialize ports and pins */ /* Initialize function modules as needed */ /* post initial user functions to scheduler */ } /* * Prototype of something to execute from scheduler. */ void userFuncX( word arg ) { /* does something to system state */ /* may {re}postTask() userFuncions() for later execution */ }