The C Programmer’s Guide to the QVGA ControllerTable of ContentsPART 1 GETTING STARTED Introduction. How to Use This Manual Chapter 1: Getting to Know Your QVGA PART 2 PROGRAMMING THE QVGA CONTROLLER Chapter 3: The IDE: Writing, Compiling, Downloading and Debugging Programs Chapter 4: Making Effective Use of Memory Chapter 5: Programming the Graphical User Interface Chapter 6: Real Time Programming Chapter 7: Failure and Run-Time Error Recovery PART 3 COMMUNICATIONS, MEASUREMENT, AND CONTROL Chapter 8: Digital and Timer-Controlled I/O Chapter 9: Data Acquisition Using the QVGA Controller Chapter 10: Outputting Voltages with Digital to Analog Conversion
Chapter 11: Serial Communications Chapter 12: The Battery-Backed Real Time Clock PART 4: PUTTING IT ALL TOGETHER Chapter 13: A Turnkeyed Application Hardware Required for the Example Application Output Compare 3 Interrupt Code Assembly Coding a Function Definition Using SAVE, RESTORE and Write-Protection During Debugging Configure the Board to Autostart the Program Turnkeyed Application Code Listing
PART 5: REFERENCE DATA Appendix A: QVGA Electrical Specifications |
Chapter 13 (Continued) Turnkeyed Application Code ListingListing 13‑1 Turnkeyed Application // Turnkeyed Application Code Listing, C Language version. // This is a complete real-time application program, with instructions on // how to set up the program to AUTOSTART each time the processor starts up.
// Copyright 2004 Mosaic Industries, Inc. All Rights Reserved. // Disclaimer: THIS SOFTWARE IS PROVIDED ON AN “AS IS” BASIS, WITHOUT ANY // WARRANTIES OR REPRESENTATIONS EXPRESS OR IMPLIED, INCLUDING, BUT NOT // LIMITED TO, ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE.
// ************************ Turnkeying a QED Application ******************** // // This is the code for an example application. The accompanying chapter // presents a detailed explanation of this code. The code can run on a QVGA // Controller with the minimum memory configuration.
// Description of the application: // This program reads an input voltage, creates a pulse width modulated (PWM) // signal whose average value tracks the input voltage, and reports the // mean and standard deviation of the input voltage. The following 3 tasks do // the work:
// Task 1: Reads 8 bit A/D input AN0, measuring the voltage on a potentiometer // connected to PortE pin PE0/AN0.
// Task 2: Outputs on Port A bit 5 a PWM signal whose average equals the A/D // input.
// Task 3: Every 1 second puts A/D value into a matrix, and every 10 seconds // displays the mean and standard deviation of the last 10 seconds // worth of data. // // The following issues are addressed: // // Using the default C memory map as set up by the MAKE utility // Proper initialization of tasks at startup // Autostarting // Interrupt service routines and interrupt attaching // In-line assembly coding // Initialization of the heap memory manager // Timesliced multitasking // ColdOnReset() for secure restart // Floating point calculations // Using the display // Generation of a PROM image of application // Going into production
// ************************ Default Memory Map ************************
// 0-7FFF (32K) in page 4 is user application code
// 4600-7FFF (14.5K) in page 0F is RAM Heap area that holds banked array data.
// 3000-45FF (5.5K) in page 0F is a heap for the graphics display buffer.
// 8E00-ADFF is available common RAM (the C .data and .init sections) // which hold C variables, C arrays, and pfa’s (parameter field areas) // of FORTH_ARRAYs.
// B000-B3FF is 1K 68HC11 on-chip RAM; the top 48 bytes // at B3D0-B3FF are reserved.
// AEC0-AFFF is available EEPROM referenced by the .eeprom section // {EEPROM at AE00-AEBF is reserved for startup utilities // and interrupt revectoring}
// QED-Forth ROM occupies 0-7FFF on page 0, 0-2FFF on page F, // and B400-FFFF in common ROM.
#include <\mosaic\allqed.h> // include all of the qed and C utilities
// *********************** 8 BIT A/D Data Gathering Task *********************
// This task gathers data from the 8 bit A/D and places it in a variable // easily accessed by other tasks.
// Define the control registers and some useful constants:
#define AD8_INPUT_CHANNEL ((char) 0) // You can use any channel you want and place the input potentiometer on // the corresponding A/D8 input pin. For example, if 0 is your input // channel, place the input signal on the pin labeled AN0 (pin 10 of // the analog connector).
static float input_voltage; // Holds voltage corresponding to latest result of A/D input. Because the // contents of this variable are shared among several tasks, we use // uninterruptable PokeFloatUninterrupted and PeekFloatUninterrupted // to access it.
// Now we convert a measured count from the A/D that ranges from 0 to 255 // into a voltage that ranges from 0.0 Volts (the low reference voltage) // to nearly 5.0 Volts (the high reference voltage). // This involves solving the equation: // Voltage=[(high.ref.voltage - low.ref.voltage)*measured.count/256]+low.ref.voltage
// First let’s define some constants: #define FULL_SCALE_COUNTS 256 // 256 counts in an 8 bit converter #define LOW_VREF 0.0 #define HIGH_VREF 5.0 // NOTE: For maximum accuracy, measure +5VAN with a voltmeter and // set HIGH.REF.VOLTAGE equal to the result.
_Q float CountToVolts(int count) // This routine converts 8 bit A/D measured count n { 0 <= n <= 255 } // to the corresponding floating point voltage r // {typically, 0.0 <= r <= 5.0 } by solving the equation: // r = [ (high.ref - low.ref) * count / 256 ] + low.ref { return( ((HIGH_VREF - LOW_VREF) * count / FULL_SCALE_COUNTS) + LOW_VREF ); }
_Q _protect void PokeFloatUninterrupted(float value, float* destination ) // stores value into the address specified by destination; // the _protect keyword guarantees that // interrupts will be disabled while this function executes; // this prevents corruption of data when two tasks are accessing the same // 4-byte variables { *destination = value; } _Q _protect float PeekFloatUninterrupted(float * source ) // fetches and returns the contents from the address specified by source; // the _protect keyword guarantees that // interrupts will be disabled while this function executes; // this prevents corruption of data when two tasks are accessing the same // 4-byte variables { return *source; }
_Q void GatherData(void) // This is the activation routine for the data gathering task. // It continually acquires readings from the A/D, converts the readings // to voltages, and updates the input_voltage variable that is accessed // by other tasks. { uchar sample; float sample_voltage; AD8On(); // make sure A/D is powered up while(1) // start infinite loop { sample = AD8Sample(AD8_INPUT_CHANNEL); sample_voltage = CountToVolts(sample); PokeFloatUninterrupted(sample_voltage, &input_voltage); Pause(); } }
// ********************* Pulse Width Modulation (PWM) Task ********************
// This task calculates the high time and low time of the PWM output based on // the value of the input_voltage which is updated by the data gathering task. // The goal is to set the duty cycle of the PWM output so that the average of // the PWM signal equals the input_voltage. This is achieved by solving the // equation: // DutyCycle = (input_voltage - low.output) / (high.output - low.output) // in which low.output and high.output are the voltage levels that appear // on the PWM output pin in the low and high states, respectively.
// Given the duty cycle, and given our choice of a PWM period of 130 msec, we // calculate the high.time and low.time of the PWM signal in terms of the timer // counts; each timer count equals 2 microseconds. high_time is the number of // timer counts that the signal is in the high state and low_time is the number // of timer counts that the signal is in the low state during each 130 msec // period. high_time and low_time are calculated as: // high_time = Duty.cycle * TIMER_COUNTS_PER_PERIOD // low_time = TIMER_COUNTS_PER_PERIOD - high_time // where TIMER_COUNTS_PER_PERIOD = 130 msec/2 microsec = 65,000.
// We also “clamp” high_time and low_time to a minimum value to prevent a // situation where interrupts are requested so rapidly that the processor // can’t service them.
// The OC3 (output compare 3) interrupt code in the subsequent section uses // these calculated values to pulse width modulate the PORTA output port pin // PA5. // We define some variables and constants:
static uint high_time; // the number of timer counts that PWM output is high static uint low_time; // the number of timer counts that PWM output is low static float duty_cycle;
#define LOW_OUTPUT_VOLTAGE 0.0 // the voltage on PA5 when it is low #define HIGH_OUTPUT_VOLTAGE 5.0 // the voltage on PA5 when it is high // NOTE: for maximum accuracy, the voltage output of pin PA5 could be measured // in the low and high states and LOW_OUTPUT_VOLTAGE and HIGH_OUTPUT_VOLTAGE // set accordingly.
#define MS_PER_PERIOD 130 // use a 130 msec period for PWM output // NOTE: the timer “rolls over” every 131 msec
#define TIMER_COUNTS_PER_PERIOD 65000 // 130 milliseconds corresponds to 65,000 counts of 2 microseconds (usec) // each on the free-running timer. QED-Forth sets the contents of TMSK2 so // that each timer count represents 2 usec. This is true whether the crystal // speed is 8 MHz or 16MHz. We assume here that the programmer has not // installed a different value in TMSK2 using InstallRegisterInits().
#define MINIMUM_HIGH_OR_LOW_TIME ((int) 250) // corresponds to 500usec // we choose a minimum high or low time approximately equal to the maximum // number of timer counts divided by 256. That is, we only require 8 bits of // resolution from our PWM. The benefit of imposing a minimum high or low time // is that bey doing so we can ensure that the minimum time between PWM // interrupts is 500 usec. This is more than sufficient time to allow the // service routine to execute.
_Q void CalculateDutyCycle(void) // implements the equation: // duty_cycle = (input_voltage-low.output)/(high_output-low_output) // duty_cycle is in the range 0.0 to 1.0 { duty_cycle = (input_voltage - LOW_OUTPUT_VOLTAGE) / (HIGH_OUTPUT_VOLTAGE - LOW_OUTPUT_VOLTAGE); duty_cycle = MIN((float) 1.0, duty_cycle) ; duty_cycle = MAX((float) 0.0, duty_cycle) ; // clamp to 0<=duty_cycle<=1.0 }
_Q void HighAndLowTimes(void) // saves high and low times as 16bit integer counts in the variables // high_time and low_time using the equations: // high_time = duty_cycle * TIMER_COUNTS_PER_PERIOD // low_time = TIMER_COUNTS_PER_PERIOD - high_time // both high_time and low_time are clamped to a minimum so that timer interrupts // don’t occur more frequently than they can be reliably serviced. { high_time = MAX((TIMER_COUNTS_PER_PERIOD * duty_cycle),MINIMUM_HIGH_OR_LOW_TIME); low_time = MAX((TIMER_COUNTS_PER_PERIOD - high_time),MINIMUM_HIGH_OR_LOW_TIME); high_time = TIMER_COUNTS_PER_PERIOD - low_time; // make sure that high_time + low_time is exactly one period despite clamping }
_Q void SetPulseParameters( void ) // this is the activation roution for the pwm task. It updates the // values in high_time and low_time for use by the oc3 interrupt which // generates the pwm output waveform on PORTA pin PA5. { while(1) { CalculateDutyCycle(); HighAndLowTimes(); Pause(); } }
// ********************* OC3 Interrupt Code ********************** // This interrupt routine generates a pulse width modulated output on Port A // bit 5 based on the duty cycle calculated by the PWM task. // There are 4 steps involved in coding an interrupt service routine: // 1. Name all required hardware registers (see the QEDREGS.H file in the // \MOSAIC\FABIUS\INCLUDE\MOSAIC directory), // and name all required bit masks with appropriate mnemonics. // 2. Use C or assembly code to define an interrupt handler which // must clear the interrupt request flag and perform required // service actions. // 3. Install the interrupt handler using the Attach() function. // 4. Write routines to enable and disable the interrupt. (We combine // steps 3 and 4 in a routine that ATTACHes and enables the interrupt).
// Define output mode configuration flags and masks that specify action // to be performed when a successful output compare occurs: #define PA5_MASK 0x20 // mask for setting/resetting PA5 #define OC3_MASK 0x20 // to set/clr OC3 interrupt flag & mask #define OC3_MODE_MASK 0x20 // mask in TCTL1; enables PA5 pin control #define OC3_LEVEL_MASK 0x10 // mask in TCTL1; controls level of PA5
// Summary of the functions of these registers: OC3 (output compare 3) is // associated with pin PA5 on PORTA. We can write a 16 bit count into the TOC3 // register, and when the count in TOC3 matches the count in the main counter // TCNT, an interrupt request can occur. The request only occurs if interrupts // are globally enabled and if the OC3 interrupt is locally enabled. The OC3 // interrupt is locally enabled by setting the bit specified by the OC3_MASK in // the TMSK1 register. When the interrupt request occurs, the 68HC11 // automatically sets the bit specified by OC3_MASK in the TFLG1 (timer flag // #1) register. Our interrupt service routine must clear this bit (oddly // enough, interrupt request bits are cleared by writing a 1 to the bit // position!) The register TCTL1 controls whether the state of the PA5 bit is // automatically changed when TOC3 matches TCNT. In this application we enable // this automatic “pin control” action by setting the bit specified by // OC3_MODE_MASK in TCTL1. The bit specified by OC3_LEVEL_MASK in TCTL1 then // controls the level (high or low) to which PA5 is set upon a successful // output compare.
_Q void OC3Service(void) { char temp = OC3_LEVEL_MASK; TFLG1 = OC3_MASK; // reset the oc3 interrupt flag so that new oc3 // interrupts will be recognized. because the flag // is cleared by writing a one to it we can use a // assignment command without affecting other bits. if( temp &= TCTL1 ) // look at the oc3/pa5 pin output level { TCTL1 &= ~OC3_LEVEL_MASK; // AND with complement of mask TOC3 += high_time; } // if the output level just toggled high we’ll // set the mode/level bit so the next output // compare forces the pin low after the high_time else // else if the output level just toggled low... { TCTL1 |= OC3_LEVEL_MASK; TOC3 += low_time; // set the mode/level bit so the next output } // compare forces the pin high after low_time }
// Pass the required constants through to the assembler: // Don’t insert any leading spaces on a line that contains an EQU directive. #asm TOC3 EQU $801A TCTL1 EQU $8020 TFLG1 EQU $8023
PA5_MASK EQU $205 OC3_MASK EQU $20 OC3_MODE_MASK EQU $20 OC3_LEVEL_MASK EQU $10 NOT_OC3_LEVEL_MASK EQU $EF
#endasm
_Q void AssembledOC3Service( void ) // This interrupt service routine performs the same functions as the high level // service routine named OC3Service. This alternative interrupt service routine // is assembly coded to illustrate the principles of assembly coding with // this environment. This routine it executes in under 45 usec // including the call and return overhead. The high level version // is nearly as fast. // NOTE: #asm and #endasm in-line assembly directives // must be the first on a line, // AND: each line of assembly code MUST START WITH A TAB OR SPACE, // BUT: labels must NOT be preceeded by a tab or space. // The comment character for assembly code is the * as shown below. { #asm ldab TCTL1 bitb #OC3_LEVEL_MASK * Look at the OC3/PA5 pin output level beq is_low * If output just went high, clear the level bit in TCTL1: next OC3 clrs PA5 andb #NOT_OC3_LEVEL_MASK stab TCTL1 ldd TOC3 addd high_time bra finished is_low * If output just went low, set the level bit in TCTL1: next OC3 sets PA5 orab #OC3_LEVEL_MASK stab TCTL1 ldd TOC3 addd low_time finished std TOC3 * update the OC count for the next output compare ldaa #OC3_MASK * Reset the OC3 interrupt flag by writing a 1 staa TFLG1 * so that new OC3 interrupts will be recognized. #endasm }
_Q void InstallOC3( void ) // This installation routine enables the “pin control” function, ATTACHES the // service routine, configures PortA pin 5 as an output, initializes the output // compare register TOC3, clears the OC3 interrupt request flag (thus ignoring // prior interrupt requests), and locally enables the OC3 interrupt. // PWM can begin when interrupts are globally enabled. { TMSK1 &= ~OC3_MASK; // Disable OC3 interrupts. TCTL1 |= OC3_MODE_MASK; // Set the OC3 mode bit so that an output compare sets or clears PA5 pin // depending on the level bit which is toggled by the interrupt routine. // ATTACH(AssembledOC3Service, OC3_ID); // optional assembly service routine ATTACH(OC3Service, OC3_ID); // use the C-coded version TCTL1 &= ~OC3_LEVEL_MASK; // Set to low before pulses start. CFORC |= OC3_MASK; // and init by forcing a compare. TOC3 = TCNT + TIMER_COUNTS_PER_PERIOD; // start after a min 130ms delay TFLG1 = OC3_MASK; // clear interrupt flag OC3F TMSK1 |= OC3_MASK; // set OC3I to locally enable OC3 }
// ************************** Statistics Task **************************
// This task samples the analog input voltage (see task 1) once per second, // stores it in a matrix, and calculates and prints the mean and standard // deviation of the data every 10 seconds.
FORTH_ARRAY Last10Voltages; // row matrix; holds 1 voltage per second for past 10 seconds
// these index variables keep track of where data is placed in Last10Voltages: static int matrix_index, last_index;
// these hold the derived statistics: static float mean, standard_deviation;
_Q float RowMean(int rownum, FORTH_ARRAY * array_ptr) // sums the elements in the specified row of the specified matrix and // divides by the number of elements in the row to calculate the mean. { float accumulator = 0.0; int i, numcolumns = NUMCOLUMNS(array_ptr); // see \Mosaic\ARRAY.H file for(i=0; i < numcolumns; i++) accumulator += FARRAYFETCH(float, rownum, i, array_ptr); return (accumulator / numcolumns); }
_Q float RowStandardDeviation(int rownum, FORTH_ARRAY* array_ptr, float row_mean) // subtracts the row mean from each element and sums the squares of all // resulting differences in the specified row of the specified matrix // and takes the square root of the sum to calculate the standard deviation. { float accumulator = 0.0, temp; int i, numcolumns = NUMCOLUMNS(array_ptr); // see \Mosaic\ARRAY.H file for(i=0; i < numcolumns; i++) { temp = FARRAYFETCH(float, rownum, i, array_ptr) - row_mean; accumulator += (temp * temp); } return sqrt(accumulator); }
_Q void InitFPArray(float value, FORTH_ARRAY * array_ptr) // stores specified floating point value in each element of the array // do not use this function to init arrays that hold char, int or long data { int r, c, numcolumns = NUMCOLUMNS(array_ptr), // see \Mosaic\ARRAY.H file numrows = NUMROWS(array_ptr); for(c=0; c< numcolumns; c++) for(r=0; r < numrows; r++) FARRAYSTORE(value, r, c, array_ptr); } _Q void CalcStats( void ) // This routine calculates mean and standard deviation of the // Last10Voltages matrix and stores them in variables. { mean = RowMean(0, &Last10Voltages); // row# = 0 standard_deviation = RowStandardDeviation(0, &Last10Voltages, mean); }
// These routines manage the LCD display. // The message on the display will look like this: // Mean = // x.xxx Volts // Standard Deviation = // x.xxx Volts
#define FP_OFFSET 4 // character offset to start of mean or // std.deviation in display line #define VOLTS_OFFSET 14 // character offset to start of “Volts” label // in display line
#define MAX_CHARS_PER_DISPLAY_LINE 40 // large enough for 4x20 character display as well as 16 x 40 graphics display
#define SPACE 0x20 // ascii space {blank}
#define DISPLAY_CONFIG_BYTE ((uchar*) 0xAE1E) // see grphext.c file #define TOSHIBA_VS_HITACHI_MASK 0x40 // 1 = toshiba; 0 = hitachi
char linebuffer[MAX_CHARS_PER_DISPLAY_LINE+1]; // single line buffer to assist in writing to display // NOTE: it’s all right if this buffer is longer than the display line; // StrToDisplay() will ignore the extra characters.
_Q uint Toshiba(void) // returns flag, = true if toshiba display; false if hitachi { return (((*DISPLAY_CONFIG_BYTE) & TOSHIBA_VS_HITACHI_MASK) ? 1 : 0); }
_Q void BlankDisplayString(char* string_base, int numelements) // writes “blanks” into the specified string, taking into account that // Toshiba graphics displays in text mode use 0x00 as the code for BL // numelements includes the terminating null character { int i; char blankchar = (Toshiba() ? 0 : SPACE); for( i=0; i < (numelements-1); i++) string_base[i] = blankchar; string_base[numelements-1] = 0; // terminating null }
_Q void SetupDisplay( void ) // writes headings to display; leaves numbers blank. { xaddr display_buffer_base = DisplayBuffer(); int numlines = LinesPerDisplay(); int chars_per_line = CharsPerDisplayLine(); char blankchar = (Toshiba() ? 0 : SPACE); FillMany(display_buffer_base, (numlines * chars_per_line), blankchar); // FillMany() blanks the display buffer // blank display buffer STRING_TO_DISPLAY(“mean = “, 0, 0 ); // line 0 STRING_TO_DISPLAY(“volts”, 1, VOLTS_OFFSET); // line 1 STRING_TO_DISPLAY(“standard deviation =“, 2, 0 ); // line 2 STRING_TO_DISPLAY(“volts”, 3, VOLTS_OFFSET); // line 3 UpdateDisplay(); // write buffer contents to display }
// sprintf(char* s, const char *format, ... ) does formatted write to specified // string. Returns #chars assigned, // not including the terminating null that it writes after the last char. // Returns a negative number if an error occurs.
_Q void ShowStats( void ) // displays mean and standard deviation of Last10Voltages on LCD display { int num_chars_placed; BlankDisplayString(linebuffer, MAX_CHARS_PER_DISPLAY_LINE); num_chars_placed = sprintf(linebuffer, “%7.3f”, mean); // use field width = 7 chars; 3 digits to right of the decimal point if(num_chars_placed > 0) STRING_TO_DISPLAY(linebuffer, 1, FP_OFFSET); // line 1 BlankDisplayString(linebuffer, MAX_CHARS_PER_DISPLAY_LINE); num_chars_placed = sprintf(linebuffer, “%7.3f”, standard_deviation); if(num_chars_placed > 0) STRING_TO_DISPLAY(linebuffer, 3, FP_OFFSET); // line 3 UpdateDisplay(); // write to display }
_Q void LogDataAndShowStats( void ) // increments matrix_index every second, // loads input_voltage data collected by task 1 into Last10Voltages array, // and displays statistics on LCD display every 10 seconds { float latest_input; // we need this temporary because we must do an uninterrupted fetch // from input_voltage, then pass the result to FARRAYSTORE matrix_index = (ReadElapsedSeconds()) % 10; // 0<= matrix_index <= 9 if(matrix_index != last_index) // if a second has elapsed... { latest_input = PeekFloatUninterrupted(&input_voltage); FARRAYSTORE(latest_input, 0, matrix_index, &Last10Voltages); // store reading in matrix last_index = matrix_index; // update last_index if(matrix_index == 9) // if 10 seconds have elapsed... { CalcStats(); // calculate new statistics ShowStats(); // update display } } } _Q void Statistics( void ) // this is the activation routine for the statistics task; // it calculates and displays the mean and standard deviation of the data. { DIM(float, 1, 10, &Last10Voltages); // dimension as a 1-row array InitFPArray(0.0, &Last10Voltages); // initialize array last_index = -1; // initialize last_index SetupDisplay(); // write headings to display while(1) { LogDataAndShowStats(); // calc and display statistics Pause(); } }
// ********************* BUILD TASKS ****************************
// Note: we’ll keep the Forth interpreter task active during // development/debugging, and put it ASLEEP in the final turnkeyed version.
// First declare the tasks and allocate their 1K task areas: // FORTH_TASK (see MTASKER.H) is the default task running QED-Forth; // this task is automatically built and started upon each reset/restart; // in the autostart routine FORTH_TASK puts itself ASLEEP so the end // user can’t run Forth.
TASK ReadInputTask; // data gathering task base xaddr
TASK ControlOutputTask; // PWM task base xaddr
TASK StatisticsTask; // statistics reporting task
// BUILD_C_TASK(HeapStart,HeapEnd,Base) and // ACTIVATE(function_ptr, task_base_addr) // macros are defined in \mosaic\mtasker.h file.
_Q void BuildTasks( void ) // Empties the round robin task loop and then // carefully builds the tasks every time we start up. // Note that only the statistics task has access to the heap. { NEXT_TASK = TASKBASE; // important! empties task loop before building SERIAL_ACCESS = RELEASE_ALWAYS; // allow sharing of serial ports BUILD_C_TASK(0,0,&ReadInputTask); BUILD_C_TASK(0,0,&ControlOutputTask); BUILD_C_TASK(DEFAULT_HEAPSTART,DEFAULT_HEAPEND,&StatisticsTask); }
_Q void ActivateTasks( void ) // associate activation routines with each of the tasks. { ACTIVATE(GatherData, &ReadInputTask); ACTIVATE(SetPulseParameters, &ControlOutputTask); ACTIVATE(Statistics, &StatisticsTask); }
// ********************* SET UP AUTOSTART ROUTINE *********************
// We’ll designate the top level word main as the PRIORITY.AUTOSTART routine. // Every time the QVGA Controller is powered up or reset, the main routine will // automatically be executed by the default FORTH_TASK // main() zeros the variable area and initializes the heap. // During debugging, the FORTH task which runs main() has access to // all defined function names declared using the _Q keyword; // the names are sent to the QVGA Controller via the .DLF download file. // This helps to debug the program. // main() initializes the elapsed time clock, installs the // OC3 interrupt service routines, and builds and activates the tasks. // It releases control of the serial line, starts the timeslicer, and PAUSEs // to begin execution of the application. // After debugging is complete, the optional commands which // specify a COLD restart and which put the FORTH_TASK ASLEEP can be inserted; // these commands are “commented out” in the code shown here.
_Q void InitVariables(void) { // init static variables & strings at runtime, and delete array // to clean up the heap before allocating heap items input_voltage = 0; high_time = low_time = 0; DELETED(&Last10Voltages); matrix_index = last_index = 0; mean = standard_deviation = 0.0; BlankDisplayString(linebuffer, MAX_CHARS_PER_DISPLAY_LINE); }
void main(void) // this is the highest level routine in the turnkeyed application. { InitVariables(); // init variables, delete arrays INIT_DEFAULT_HEAP(); // it’s important to init heap at startup InitElapsedTime(); // initialize qed elapsed time clock InstallOC3(); // install service routine for pwm generation BuildTasks(); // initialize user areas of the tasks ActivateTasks(); // associate action routine with each task // the following 2 commands are removed during debugging;present in final version // ColdOnReset(); // ensures full initialization upon each reset // STATUS = ASLEEP; // puts forth task asleep; RELEASE(SERIAL); // in case another tasks needs to use serial StartTimeslicer(); // starts elapsed time clock, enables interrupts Pause(); // start next task immediately }
// Now from your terminal, type: // CFA.FOR main PRIORITY.AUTOSTART
// this will install main() as the routine that is automatically executed // each time the board is reset or powered on. // The PRIORITY.AUTOSTART routine initializes the priority.autostart vector // at locations 7FFA-7FFF on page 4 which will be in Flash.
// Then upon the next restart the main() routine will automatically execute. // NOTE: To erase the autostart vector and return to QED-Forth, // type at the terminal: // NO.AUTOSTART // If you have put the FORTH_TASK asleep so that it does not // respond to the terminal, // activate the Special Cleanup Mode by turning on dip switch #6, toggling // dip switch #7, and then restoring switch #6 to its original position. // The Special Cleanup Mode erases the autostart vector and restores the // board to a “pristine” condition (it also sets the default // baud rate to 9600, and the LCD display type to 4x20 character // display; use Baud1AtStartup() and IsDisplay() if you need to // change these defaults). // // ******************* BURNING THE FINAL APPLICATION FLASH ******************
// To generate a Flash device that contains this application, download the // download the TURNKEY.DLF file, and execute the // CFA.FOR MAIN PRIORITY.AUTOSTART // command to install the autostart vector. // Before resetting the board or typing main (which could put FORTH asleep // and make communications difficult), we’ll make an image of page 4 // which holds the object code and autostart vector: // To make a Motorola S2-record file to send to the Flash Programmer, // set your terminal to capture incoming text to a disk file using the // “Save File” menu item, and interactively type from the terminal: // HEX 0 4 DIN 0 8000 DUMP.S2 // Close the file when the dump terminates, and use Textpad to remove // any extraneous file contents such as the DUMP.S2 command and Forth’s // “ok” prompts. Then send the resulting file to a Flash Programmer. // Power up the QVGA Controller and it will automatically run the application!
|
Home|Site Map|Products|Manuals|Resources|Order|About Us
Copyright (c) 2006 Mosaic Industries, Inc.
Your source for single board computers, embedded controllers, and operator interfaces for instruments and automation