Beruflich Dokumente
Kultur Dokumente
Group FOCUS
: : : :
1 of 122
2 of 122
3 of 122
A. INTRODUCTION......................................................................................................................... 7 A.1. DOCUMENT IDENTIFICATION....................................................................................................7 A.2. SYSTEM OVERVIEW..................................................................................................................7 A.3. DOCUMENT OVERVIEW............................................................................................................7 A.4. REFERENCE DOCUMENTS .........................................................................................................7 SYSTEM DESCRIPTION................................................................................................................. 9 A.5. INTRODUCTION.........................................................................................................................9 A.6. OPERATIONAL SCENARIOS........................................................................................................9 A.7. SYSTEM REQUIREMENTS ........................................................................................................10 A.8. MODULE DESIGN.....................................................................................................................11 A.9. MODULE REQUIREMENTS : POWER MANAGEMENT SYSTEM .................................................14 A.10. CONCEPTUAL DESIGN: POWER CONTROL SYSTEM ..............................................................15 A.11. MODULE REQUIREMENTS : SOFTWARE FILTER....................................................................18 A.12. CONCEPTUAL DESIGN: SOFTWARE FILTER..........................................................................19 A.13. MODULE REQUIREMENTS : STATISTICS MODULE.................................................................23 A.14. CONCEPTUAL DESIGN: STATISTICS MODULE.......................................................................25 A.15. MODULE REQUIREMENTS : LCD...........................................................................................26 A.16. CONCEPTUAL DESIGN: LCD.................................................................................................28 A.17. MODULE REQUIREMENTS : WEIGH/COUNT..........................................................................30 A.18. CONCEPTUAL DESIGN: WEIGH/COUNT................................................................................32 A.19. MODULE REQUIREMENTS .....................................................................................................36 A.20. CONCEPTUAL DESIGN...........................................................................................................41 B. USER INTERFACE DESIGN..................................................................................................... 51 B.1. CLASSES OF USER...................................................................................................................51 B.2. INTERFACE DESIGN.................................................................................................................51 C. HARDWARE DESIGN................................................................................................................ 53 C.1. SCOPE OF THE CCS SYSTEM HARDWARE..............................................................................54 C.2. HARDWARE DESIGN................................................................................................................54 C.3. HARDWARE VALIDATION........................................................................................................58 C.4. HARDWARE CALIBRATION PROCEDURES ...............................................................................58 C.5. HARDWARE MAINTENANCE AND ADJUSTMENT .....................................................................59 D. SOFTWARE DESIGN................................................................................................................. 60 D.1. SOFTWARE DESIGN PROCESS .................................................................................................60 D.2. SOFTWARE QUALITY ASSURANCE..........................................................................................60 D.3. SOFTWARE DESIGN DESCRIPTION..........................................................................................60 D.4. PRECONDITIONS FOR SOFTWARE............................................................................................62 E. SYSTEM PERFORMANCE........................................................................................................ 63
F. SAFETY IMPLICATIONS.......................................................................................................... 65
5 of 122
G. CONCLUSIONS.......................................................................................................................... 65
........................................................................................................................................................... 76
........................................................................................................................................................... 77
........................................................................................................................................................... 78
........................................................................................................................................................... 79
6 of 122
A. Introduction
A.1. Document Identification
This document describes the design of the Computer Controlled Scales (CCS). This document is prepared by Group FOCUS for assessment in MTRX 3700 in 2006.
7 of 122
A.4.1.
Table
8 of 122
System Description
This section is intended to give a general overview of the basis for the CCS system design, of its division into hardware and software modules, and of its development and implementation.
A.5. Introduction
The CCS can generally be divided into two components, hardware and software. Hardware components include: LCD screen Text To Speech (TTS) chip PIC18F452 MNML board Keypad grid Buttons RS232 serial port Cantilever Load Beam and Bridge Rectifier Circuit The software component can be further divided into six modules, these are: Main function module LCD module T2S module Filter module Statistics module Factory mode module. This report aims to explore each of the above in detail.
9 of 122
The factory mode is not available to the normal user, only authorised persons with the correct password will be allowed to access this mode. In this mode, extra options become available, these includes having the mean and variance of the last weightings printed to screen, printing of raw data. To turn off the scale, press the off button. Factory Mode The factory mode is designed for diagnostic and more in depth use than that of the casual user. The factory mode, apart from providing the same functions as that of the user remote mode, ie count and weigh, also provides a calibrate and statistics mode. The calibration function is important in maintaining the accuracy of the scale as the algorithm through which the weight of items is calculated can be updated through activation of the calibration mode. If suspicious values are being returned by the device, the calibration mode is important in making sure that the device able to operate correctly throughout the life of the device. The statistics module allows the user to examine the raw and filtered data that is being outputted from the device and hence observe the accuracy and consitency of the values being returned by the device. The mean and variance functions are also able to give a more accurate reading on the precise weight of an item Rather than taking a reading at one point in time, a value may be calculated over a number of points in time giving an average value and removing some erratic results.
10 of 122
Factory Mode Factor mode is divided in three main modules: Calibration mode => Fits appropriate functions between intervals of weights to give the most accurate weight value. Shows statistics mode => Displays all the statistical data requested by the user. User remote mode => Divided into two sub modules. The determine weight module calculates the weight currently placed on the scales and the count module measures the number of items currently placed on the scales based on information the user had entered when requested.
Figure F1 below shows how these functions are broken into separate modules
11 of 122
FACTORY MODE
CALIBRATION
SHOW STATISTICS
DETERMINE WEIGHT
COUNT
Each module calls a series of other functions which each perform a single task to assist its corresponding main module. The figure below shows which functions are called by which modules.
transmit_text
calculations
transmit_text
12 of 122
Display _weight
show_weight _grams
Initialise
transmit_text
transmit_characte r
determine_fn
13 of 122
A.9.1.
Functional Requirements
This section describes the functional requirements of the power management sub-system; those requirements that must be met if the module (and system) is to function correctly and according to specification. Power control is implemented purely in hardware and is interfaced to both hardware and software sub-systems A.9.1.1. Inputs The power sub-system is the first contact point for the power supplied to the unit from all sources. These include battery power input through the 9v battery snap and AC adaptor power input through the switching jack. Power system input voltage is limited to 12v owing to the use of 16v rated capacitors and 16v maximum rating on the NE555. Implementation of the two minute timer requires that this sub-system take input from the power-on button and feedback from the microcontroller indicating that an event has occurred considered as activity, e.g. serial bus communications. The sub-system must also take input from the keypad indicating that a key has been pressed constituting valid activity. Inputs from other subsystems are TTL compatible. A.9.1.2. Process The two minute period is implemented around a NE555 timer IC configured as a mono-stable timer. The 555 circuit provides a two minute high output on power-up, switching power to a BUZ71 power control MOSFET, in turn switching the power relay. The ability to restart the period is provided by a transistor which discharges the 555s timing capacitor. This transistor is fully switched at the TTL on state voltage with minimal current. Noise below 0.7v on the restart_period line is insufficient to bias the transistor to on state and is thus ignored. A.9.1.3. Outputs It is imperative that there be minimal voltage drop through the power circuit as the regulator voltage overhead is of concern operating a 5v regulator from 9v, even with the use of the ZSR500 low dropout regulator. The output of the power controller is full input voltage to the MML18 onboard regulator. This was achieved by using an output switching relay. The power board also provides distribution for 5v regulated power taken from the auxiliary power pins on the MML18 board. A.9.1.4. Timing Timer period is adjustable from approximately 35 seconds to 10 minutes, and is set in accordance with specification to 2 minutes after last activity. A.9.1.5. Failure Modes
14 of 122
Over voltage will not damage the unit when powered on if below 16v. However voltages above 50v will cause breakdown across the BUZ71 switching power on to the unit. Over current protection is provided by the thermal shut-down feature of the ZSR-500 regulator. Additionally it was intended to provide a 16v zener diode as over voltage protection across the input to the power controller. Reverse polarity protection to the MML18 board is provided inherently by the ZSR-500 without the voltage loss associated with a rectifier diode.
A.9.2.
A.9.2.1. Performance There do exist much simpler methods to implement a two minute timer, this method was chosen for its repeatability as well as last minute component availability. Other options considered are detailed in the hardware appendices. A.9.2.2. Interfaces It would be desirable to include a reverse polarity protection facility on the switching side of the power controller circuit. A.9.2.3. Design Constraints The number of parts used in this design, and as such associated cost, makes it a poor choice for a commercial implementation.
A.10.
A.10.1.1. Rationale The power module was designed to function without any processor overhead to increase the possible sampling rate of the AD conversion and subsequent data processing. Within the design evolution there were initially other timer designs formed and implemented. Both were unable to be used in the final implementation due the damage to MOSFET devices at the last minute. Details are outlined in the appendices.
15 of 122
Start-up
Activit y
16 of 122
The time period invoked by the 555 timer is a function of the time taken fir the 220uf capacitor to charge from 1/3 of rail value to 2/3 of rail value, and is controlled by the selection of the variable resistor value once capacitor choice is made to standard values. The use of a low leakage capacitor allows for increased interval reliability. Time period is calculable by; Time = 1.1 x C x R Final power switching is achieved through the use of a relay in order that there is zero voltage drop between input and output.
17 of 122
A.11.
The operational scenarios considered place certain requirements on the input data conditioning module, and on the processes that comprise it.
18 of 122
A.12.
A.12.1.1. Rationale Filtering is required between the beams differential amplifier and the weight calculation algorithm to remove both beam oscillation and noise components of the signal. An anti-aliasing filter is also required prior to input of the signal to the AD system. Frequencies of interest lie between 0-3 Hz for weight calculation. As the corner frequency of an active analogue filter is lowered to this value the resistor values required for its implementation increase to values around 5 M ohm and filters become more prone to oscillation effects. Specification requires that in user remote mode a weight sample is output every 200 milliseconds. If each output were to have 20 contributing measurements a sample frequency of 100 Hz is required as a minimum. As such 100Hz was decided as the starting frequency for implementation. To prevent aliasing of the input signal it was decided to implement an analogue low-pass filter with a 40 Hz corner frequency, followed by a 3Hz low-pass software filter, to avoid the undesirable effects observable in an analogue filter with a 3 Hz corner. A maximally flat pass-band is desirable along with a sharp cut-off curve. Ideally an infinite impulse response filter can provide very desirable characteristics, however when operated with such a low corner frequency the possibility of runaway increases. Runaway traps can be placed on the coefficients; however the filter performance and predictability will be severely compromised when these traps trigger frequently. It was decided that an agreeable compromise could be reached between optimal performance and predictability by using a 20 tap finite impulse response filter. The performance loss was justified by no possibility for runaway and easier implementation without the use of floating point numbers. The FIR filter was designed and analysed using DigiFilt to have a gain of 1. This gain value was chosen such that the filter could be implemented after other sections with minimal change to surrounding code. However the resulting filter coefficients were far smaller than 1 for a 21 tap filter. In order that these coefficients be integerised a multiplier was applied, large enough to retain accuracy in the smallest coefficient but to also avoid overflow of the integer type when multiplied by the data point (0-1024). A multiplier of 512 was chosen as it achieved both objectives and reduced computational time when the multiplier was removed simply using a left shift operation. The use of a false pointer to the current element in the circular data buffer eases the logic of math operations on the pointers. The insertion pointer is the same in all arrays during the data operation sequence. It was intended that in final implementation the compiler chosen multiplications of 16x8 bit numbers with a 16x16 routine be replaced by a more efficient 16x8 assembler routine, however the implementation was never achieved due to variable passing issues and problems in other modules.
19 of 122
Data Array
20 of 122
21 of 122
22 of 122
A.13.
The operational scenarios considered place certain requirements on the statistics module, and on the functions that comprise it.
A.13.1.3. Outputs Output of both values is to 2 d.p. accuracy through multiplication by 100 prior to division operations and subsequent return passing. This must be accounted for when displaying or utilising these values. It is felt that this resolution is required especially for the meaningful output of variance. The mean and variance functions are run every sample period and the variance output is designed to be used as a measure of weight stability leading to display of a stabilised weight reading.
A.13.1.4. Timing Both functions are engineered to require minimal computational time. A.13.1.5. Failure modes Failure of the functions due to out of range input values will result through data type overflow, however for this to occur the critical error would be in the passing function and as such no error trap or rectification is included within this module.
23 of 122
A.13.2.2. Design Constraints Minimising data type sizes has been traded off against greater resolution. Floating point math is to be avoided at all costs due to the massive computational overhead involved when emulated on this processor.
24 of 122
A.14.
Calculation of the weight variance provides a simple manner in which to quantify the stability of the measured data point. This information can be used to determine whether or not a stable reading has been achieved and subsequently invoke the display_weight function. As the variance can be used to this end it was decided to invoke the weight_variance function every sample, requiring that the mean_weight function also be invoked beforehand. The mean function is able to be used to average the weight before display to provide further weight stabilisation. Both functions were designed to provide a simple return value, allowing their utilisation wherever desired. The return values were made global so as to be available not only to the calling function but also remain valid for use by the serial functions without requiring that the function be invoked again. To remove the possibility of there being a change to the number of samples used between calls to the mean_weight and weight_variance functions a local copy of the variable is made upon entry to the mean_weight function. The C18 specific short long int data type is used where more than 16 bits are required but less than 32 bits.
Sample
Mean Weight
Weight Variance
Y A
Display Weight
25 of 122
A.15.
The operational scenarios considered place certain requirements on the LCD, and on the modules that comprise it.
To setup the LCD to receive inputs, it must be initialised accordingly. Following a successful initialisation, commands can be sent from software to write various strings on the LCD screen. The strings that are written to screen depend on the functional requirements. The two lines are therefore utilised to serve its functional requirement. Line 1 is used to display weight or count data to the user, whereas Line 2 is used to label the buttons that are physically placed below it. Hence, there are two types of strings that are required:. The first is the RAM string, where the data is constantly changing and needs to be calculated by the MCU before being printed to screen. The second set of strings are hard coded into ROM as they specifically either label the data, which describes a certain mode, or label a button. The LCD interfaces to the microcontroller requiring four data lines and three control lines. Another three lines (0V reference, +5V, and 0.6V contrast pin) are also required to power the device. Information that is to be displayed on the screen is sent via the data lines whereas slave commands are sent via the control lines. In this way, the appropriate instructions are sent with the required data to display a desired result. A.15.1.2. Process
There are three main processes within this module: Initialisation Parse Integer Print String to LCD RAM Initialisation This process is called once when the system is powered up. The purpose is to ready the device to receive data and display it appropriately. Parse Integer This process simply converts an integer into its comprising digits. These digits are then stored in data memory. This is done so that the oscillations from the strain gauge are clearly visible. If a simple standard C programming library function were to be used, the result could potentially result in a poor response time. By parsing the number and storing the digits, the program can repeatedly refer to these stored digits and print the numbers appropriately. Print String to LCD RAM This simple process is as per the manufacturers data sheet. A ASCII character is loaded into the LCD buffer. This process is repeatedly called to print a string. A.15.1.3. Outputs
Any output is considered successful if the information displayed on the screen makes logical sense. That is, if a label were to be printed, the label must be visible. Likewise, if a fluctuating number is required to show that the strain gauge is oscillating, the numbers should fluctuate in phase with the frequency of vibration of the cantilever load beam. A.15.1.4. Timing
26 of 122
The weight reading, or any change in the deflection of the cantilever load beam must be immediately present. That is, if additional weight is added to the scales, the number on the screen should move up accordingly. Any latency reflects a poor response within the software and cannot be deemed acceptable for the purposes of this operational scenario.
The purpose of this module is to display current state data along with any weight measurements computed by other modules. Hence, the information must be clearly represented. Any change in any of these data requires a swift change in the information on the screen. A.15.2.2. Interfaces
Displayed information must be easy to read, easy to comprehend and make common sense. That is, the information presented on the LCD screen should be self explanatory. The user should not have to refer to the Operational Manual to determine what state the machine is currently in. Similarly, the options that are available to the user must also be clearly obvious. Once the machine has changed state, the change in state must be obvious to the user and any other relevant instructions or information must be clearly displayed. A.15.2.3. Design Constraints
The KS0066U Samsung LCD is a 2 x 16 dot matrix display. This implies only 32 characters including formatting space characters can only be displayed at one time. Therefore, the strings that are to be printed to screen must be short and simple to convey the maximum information in this restricted space. Secondly, there is no background lighting for the module. This could make reading the display a little more difficult. To overcome this, the device must be mounted to ensure the easy viewing from an appropriate viewing angle. Finally, it is important to keep track of the current position of the cursor. To avoid this problem, the LCD buffer is continuously refreshed to ensure the cursor is always at the start of the buffer.
27 of 122
A.16.
The LCD module performs the main task of conveying information to the user. This is done by printing strings to screen. For standard labels and Mode Descriptors, the strings are hard coded into memory and sent whenever there is a state transition. Printing strings from ROM is the simplest task. The string is sent using a pointed to a buffer loading method. The buffer loading routine places the ASCII code of the character in the string onto the Data bus. The way in which this is done is a standard LCD process which is detailed in the LCD datasheet. The routing therefore keeps repeating this process until it reaches the end of the string, which is designated by a null character. This ends the process by telling the LCD that the transmission is complete. The data is displayed on the screen To display the weight data, parsing is required. The other software modules manipulate a integer cast data variable to determine the current weight on the scale. This number is then required to be displayed on the LCD screen. To do this, the digits must be parsed out of the Integer and converted into its corresponding ASCII representation. The parsing method is as follows: Grams When the user wishes to display the current weight on the scale in grams, the software calculates the current weight and stores it in the grams variable. The product specification demands that the final weight reading in grams must be correct to +/- 1g. Hence, the integer representation of this number needs to be displayed. The algorithm therefore uses simple division to determine the respective digits. (NB. tenEn implies ten raised to the exponent n. tenen implies ten raised to the exponent n). The general method is described below. Suppose we want to find the digit in the nth place value.
digit =
This algorithm extracts the nth digit from the number. Eg: Weight = 137;
tenE 2 = tenE1 =
tenE 0 =
The parse digit process is repeatedly called to update the respective variables tenEn. This process is interrupted by the ISR to update the weight variable. This ensures a quick reponse time as well as an accurate representation of the weight on the scale. Ounces When the user want the weight to be represented in Imperial units, the weight integer needs to be converted into ounces. This will therefore require the use of floating point arithmetic. However, any such operation is best avoided. The stored weight value is therefore multiplied by 10 before it is divided by the appropriate conversion factor. The above algorithm described in the Grams section is then used to determine the various digits. The aim of multiplying the weight number by ten moves the first decimal into the ones column (place value). The printing function then moves the first decimal place back into is correct position so that it appears in a manner that would seem logical to the user.
28 of 122
When printing strings to the screen, the second line of the LCD requires only one write operation. This is because it serves the purpose of a label for the buttons that are meant to be below it. These will only change when the system changes state. The current state is repeatedly printed on the first line. This is to ensure that the cursor is always at the end of the mode label. The next write function is always an update of the weight reading. Following this, the weight label is reprinted. This is looped to ensure all changes are written on the visible part of the screen and not just to the LCD buffer. The final step in the parsing process utilises the Lookup nature of the ASCII chart. The parsing algorithm will always yield a number between zero and nine. This is because any division when the variable is cast as a integer results in a truncation of the numbers following the decimal place. Hence, this number is used as an index. The ASCII values for the numbers from zero to nine are (decimal) 48 to 57. Hence, following the algorithm, the parsed digits are added with (decimal) 48 before being stored. Hence there is no need for calling string parsing methods which come standard in most C programming compiler libraries.
29 of 122
A.17.
The operational scenarios considered place certain requirements on the Weigh/Count Module, and on the processes that comprise it.
30 of 122
A.17.2.2. Interfaces The AD converter expects a voltage. This is converted into various forms depending on the mode of operation and is then stored within ROM.
A.17.2.3. Design Constraints The numbers that are to be displayed range from (decimal) 0 to 1500. The resolution of the AD converter is 10 bits and so this data must be scaled to represent that range. To do this, a larger data type needs to be used to handle the size of the data. Performing multiplications and division by large numbers (~16 bit) take time since the standard compiler routine creates a listing that is quite lengthy. This in any case cannot be avoided and so we choose this option at the expense of performance. In any case, implementing integer arithmetic routines to deal with floating point numbers was not done due to time constraints .
31 of 122
A.18.
The AD module interfaces to the Strain Gauge via its input channels. Simply, pin AN0 on PORTA of the MCU is connected to the Output of the strain gauge. The output signal ground reference line is connected to the ground of the MCU MNML board. AD Module The AD is configured to accept a voltage between 0 and 3.3V on pin AN0. The other pins on PORTA are configured to be digital. This prevents any fast switching on these pins, which prevents any noise from interfering with the Strain Gauge Signal. The voltage is then converted into a 10bit number. The maximum resolution of the AD converted is used to improve precision. The option of attaching a reference voltage to increase conversion resolution was dismissed to minimise complexity. The AD converter converts the voltage in a very small amount of time. Therefore, to remove some of the oscillations of the beam, the AD conversion was triggered to start every 10ms, producing a sampling frequency of 100Hz. There is no need to consider aliasing as we are looking for the steady state value of the strain gauge. The trigger was driven in software by setting the GO bit when a 10ms capture compare interrupt fired on CCP1. The other alternative was to implement a hardware interrupt where a capture compare on CCP2 triggers a special event. This method however only saves a few microseconds. The result of the conversion is saved in a variable in program memory. As this process is interrupt driver, the weight variable is constantly changing. This implies that at any given point in time, the number displayed on the LCD is not the latest measured value. There is a good chance that a converted value is never displayed. Some of the routines are very long, and are hence interrupted to update the weight variable. For example, the LCD could be printing a weight on screen but the weight variable would have updated almost 100 times before the next update is written to screen. Since we are only concerned with the steady state value, this is not a problem. However, since we are also collecting statistical data such as the mean and variance, it is important that all this sampling frequency is implemented. By obtaining a rough shape of the transient response, we can calculate the mean and variance, which fixes the cut-off frequency and filter coefficients. This will remove the low frequency oscillations, which will allow the user to see the steady state value sooner. Number Conversion Following a AD conversion, the number is both buffered in an array for statistical purposes and passed to this module. This value then tested to fit in a certain category. The categories are defined as certain number ranges corresponding to 200g intervals starting from 0 to and ending at 1200g. Each category has a certain mapping function. It was experimentally found that the relationship between weight and voltage was linear. Hence, the 10 bit number was multiplied by a gradient and an offset to find its representation as a weight in grams. Due to complications with the calibrate function, this method was not tested with the scales and so was not implemented. Instead, the gradient and offset values were hard coded for all categories. This gave a weight reading with a +/- 10g accuracy. The experimental values are shown below. The graph represents the relationship between the converted value and the weight that is placed on the scale. As we can see, these gradient and offset values were used.
32 of 122
Weight (g)
1000
Weight 5 10 15 20 25 30 35 40 45 50 100 150 200 250 300 350 400 450 500 550 600 650 700 750 800 850 900 950 1000
Output 6 8 11 15 19 20 24 26 29 33 63 94 124 153 184 215 244 278 307 339 370 402 430 462 489 520 551 581 611
NB Refer to Appendix fort Experimental Data The algorithm with respect to the Count Mode is however not as simple. Essentially, the user enters the number of items via the LCD user interface. This number defines the number of items that are on the scales. The program then takes this number and divides the latest weight reading to determine the weight of one object. This number is then stored to divide future weight readings to calculate the number of items on the scale. (NB all items must be identical to ensure validity). This new number is stored. The interesting feature of this design is that the program uses the result of the AD conversion directly. There is no need to reference the variables that store the weight in grams or ounces. The reason is rather simple. The weight of a single object is with reference to a converted analog value. Since the relationship between the converted analog value and the weight is linear, the software can use this result to determine the number of items on the scale. Eg: weight of n items = D1 (10 bit number) weight of 1 item # items = D1/n; = (D2)*(n/D1);
The linear relationship can be exploited to give good results. If however, the relationship between the weight placed on the scale and the resultant digital 10 bit number from the AD conversion were not linear, the above logic will fail. In that instance, we would have to find the weight in normal weight units such as grams or ounces and then perform the division and multiplication to determine the new number of items on the scale. All stored numbers, such as current weight or number of items, are referenced by the LCD printing routines, which eventually display the number to the user.
34 of 122
35 of 122
A.19.
Module Requirements
The operational scenarios considered place certain requirements on the CCSS, and on the modules that comprise it. This section describes the functional requirements and non-functional requirements of each module. Before each module is analysed, each individual function are defined in terms of its purpose, inputs, processes and outputs. FACTORY MODE Initialise function => declares all the requirements for serial transmission and reception as well as receive interrupts. It has no inputs or outputs other then configuring the necessary registers. transmit_text => sends a string to the serial terminal. Inputs are a pointer to the start of the string as well as the final character in the string so that the function knows when to stop transmitting. Outputs the string in hyperterminal. transmit_character => sends a character to the serial terminal. The only input required is the character to be displayed. Output is the character displayed in hyperterminal. determine_fn function => determines the weight-voltage function to be used in calculating the weight depending on the current voltage reading from the scales. Inputs are an index to indicate the location of the gradient and offset in their corresponding arrays, the voltage arrays used for calibrating and the current voltage reading. calc_weight function => calculates the weight on the scales using the function determined by determine_fn. Its input parameters are the determined gradient, the determined offset and the current voltage reading. show_weight_grams => after allowing the user to select grams or ounces via a keyboard entry the sub routine converts the weight integer into ascii form and publishes this number to the screen show_weight_ounces => after allowing the user to select grams or ounces via a keyboard entry the subroutine performs a mathematical calculation upon the raw weight so as to convert the figure to the relevant ounces number. This new number is then converted to ascii form and published to the screen rx232lsr function => is an interrupt sub-routine that is triggered whenever a character is entered. It simply reads the value in the receive register. Therefore it input are just the entered character. calculations function => calculates the gradient and offset at each interval of weights. Its inputs are the voltage values corresponding the weight intervals, the weight themselves as well as a counter which refers to the position in which the gradient and offset are to be stored within an array. Display_start_menu => This transmit the main menu strings to the remote terminal through the serial communication port. These strings are sent using the transmit_character and transmit_text functions Display_stats => This displays the relevant strings for the module using the transmit_text and transmit_character functions. The subroutine loops to output the raw and filtered data from the circular buffer and concludes by printing the calculated data mean and variance. The function waits for user input before terminating and returning to the main function. display_user_remote => displays the relevant strings for the weigh and count menu through use of the transmit_text and transmit_character subroutines. display_weight => displays the string used for the weigh function using the tranmit_text and transmit_character functions. Password => allows the entry of three characters from the remote terminal. A counter is incremented for every correct entry. If the counter reaches three the password is deemed
36 of 122
to be correct and a flag is then set allowing continuation into the statistics and calibration modules
37 of 122
The result of each calculation would be published only to 1 decimal place as this would reflect the limited accuracy of the filtered data. Any more accuracy from the statistics module would suggest accuracy which cannot be justified by the performance of the hardware and software in this project. A.19.3.3. Outputs Outputs that must be created for the statistics module to function correctly include the addition of new data to the circular buffer so as to allow new calculations of the mean and variance. Serial signals must also be ouputted so as to allow the relevant statistics results to be called and displayed on the remote terminal. A.19.3.4. Timing The statistics results may be called at any time as the values are constantly updated and the mean and variances continually updated as the program runs.
38 of 122
39 of 122
A.19.5.3. Outputs The determine weight function outputs the weight in grams. After the user has specified whether to display in grams or ounces, User remote module will display the weight in the desired units using either the show_weight_grams function or the show_weight_ounces function. Both these functions use transmit_text and transmit_character to display the weight. Count mode module displays the number of items currently on the scales using transmit_text and transmit_character functions.
40 of 122
A.20.
Conceptual Design
This section gives the necessary details on how each module works and why they were designed that way they have. FACTORY MODE Figure F2.5 is a flowchart of factory mode showing how the algorithm works.
BEGIN
Display_start_menu
CASE : choice
2 passwor d
No
41 of 122
gradient =
This equation can only be applied when the relationship is linear. After several tests, it was found an almost linear relationship existed as shown in Figure F3.
42 of 122
y = 3.0514x + 6.087
500
1000
1500
Fig F3: Plot of Weight vs Voltage The voltage offset is the difference between the weight=0 voltage point to the zero voltage point as shown in Figure F4. The gradient and voltage offset could have been taken to be the single values found in the testing shown in Figure F3. However, the line fitted in Figure F3 is not completely a straight line and so to obtain higher accuracy, the gradient and voltage offset is calculated in 200 gram intervals. Hence, the user is asked to place weights at 200 gram intervals. Figure F4 illustrates how the gradient and offset can be different. Ofcourse, smaller intervals would lead to higher accuracy but any smaller intervals would cause further unnecessary calibration that would lead to only slight increases in accuracy.
43 of 122
The voltage offset is determined using the following method derived from a straight line graph equation, point gradient formula. Letting m be the gradient:
A.20.1.1. Assumptions made - Looking at Figure F3, we can see the graph is not linear. However, we have assumed it is linear and use linear methods to calculate gradient and offset values. This is compensated by calibrating different weight-voltage functions at every 200 gram intervals. It is assumed that the user places the requested weights during calibration. There is no way for the system to know whether the user has placed the correct weight or not.
44 of 122
A.20.1.2. Constraints on Module Calibration Performance - It is possible inaccurate calibration can happen if the user does not place the weight gently on the scales and wait for vibrations to stop before pressing any key. The filer should be able to separate out the noise from the actual signal.
45 of 122
BEGIN
counter++
Yes
END
counter++
46 of 122
counter++
END
The statisctics module is accessed though the factory remote mode of the device. The module is password protected as this part of the function of the device should not be able to be
47 of 122
accessed by the casual user. The module was designed so as to simply print all necessary data as soon as it was called. The statistics mode outputs a variety of data (1) Raw Data This data is the raw voltage values outputted from the A/D convertor to be stored in a circular buffer. The statistics mode lists each piece of data stored in the circular buffer which consists of 50 values. (2) Filtered Data This data is the filtered version of the raw data. The software filtering attempts to remove noise that will be present in the raw voltages. The filtered data is similarly stored in a circular buffer which is updated with each A/D conversion. (3) Mean As each new value is submitted to the circular buffer the mean of the values is recalculated. This means that the mean value can be simply called at any time. (4) Variance Similarly the variance of the circular buffer values is calculated as each new value enters the circular buffer and can be called at any time. The statistics module increases a counter as each raw and filtered value is extracted from the circular buffer and printed through the serial communications to the remote terminal. Upon printing the last value the counter limit is reached and this signals that the program may proceed to printing the mean and variance values. This is simply achieved by using the values stored in these variables which are global. The numerical functions used to calculate the statistics are as follows Mean = X/N Variance
A.20.2.1. Assumptions made We assume that 50 values are enough to provide a reasonable degree of accuracy in the calculation of the mean value for the module. A.20.2.2. Constraints on Module Statistics Performance Constraints on the module include the limited amount of space on the chip so as to restrict the amount of raw and filtered data that may be stored and hence operated on by the statistics module
48 of 122
User Remote then asks the user if they wish to display the weight in grams or ounces. If the user selects grams, User remote will call the show_weight_grams function which will display the weight as it is. If the user selects ounces, User remote will call the show_weight_ounces function which converts the weight value from grams to ounces by dividing the weight by 28.35. This is because 1 ounce = 28.35 grams. It then displays the weight in ounces. Note that for this part of the program, receive interrupts are disabled as the program is not required to run any other part of the modules. If the user enters an invalid choice, nothing will happen. Program continues to wait for a valid entry. User remote constantly loops, always updating the displayed weight until the user enters a valid command. The 2nd module part of User remote is Count mode. Only when the user requests this mode will the program display Count mode. Count mode essentially counts the number of items placed on the scales based on information given by the user. Once in Count mode, the program follows these steps: 1. Receive interrupts are disabled since the program does not need to perform other operations while waiting for the user the user input. The user is asked to enter the number of items currently on the scales. They can go back if they wish by pressing b. 2. Once entered, interrupts are enabled to allow the user to press b at any time to o back to the Determine weight module. 3. Count mode calls the determine weight module to measure the current weight. Count mode stores this as the weight of the number of items entered by the user. 4. Count mode than calculates the weight of one item by dividing the weight with the number of items. Notice the weight of one item is multiplied by 256 to obtain higher accuracy as mentioned before. 5. The current weight in the scales is determined using determine weight mode. 6. This weight is then divided by the weight of one item giving the number of items currently on the scales. 7. Count mode continues to update the count number until the user enters a valid command.
A.20.3.1. Assumptions made - User enters correct count number. A.20.3.2. Constraints on Module User Remote Performance - The number of items calculated has limited accuracy since the count number is declared as an integer and so any decimal places with the calculated values are cut off rather than rounded. This is however improved by multiplying the weight of one item by 256 to keep further decimal places but needs to be divided later on. The above also applies to the calculate weight calculations. Figure F7 is a flowchart of the User remote algorithm and Figure F8 is the flowchart for count mode.
49 of 122
BEGIN
Initialise
display_user_remote
Determine_weight
grams
Grams or ounces?
ounces
show_weight_gram s
show_weight_ounce s
Count mode ?
Yes
Count_mode No
No
50 of 122
Customer can use this mode by inserting a 9V battery into the scale and turning it on. Inputs to the scale includes the weights on the scale and the keys entered. The default mode for the scale is weighting mode, once the scale is on and item been placed onto the bowl, the total weight will display on the LCD. Pressing the option select key can change the state the scale from weighing to counting the number of items, the speaker will output the result. The speaker can be turned off by pressing the mute key. The tare key will set the displayed back to zero and the Gram and Ounce switch is use to change the weighing units.
B.2.2.
The System can accept data from the User in two ways. In the Remote Setting, data is entered via a numeric keypad and a button array. In the Local Factory Setting, the User inputs data via a standard computer keyboard. B.2.2.1. User Local Mode: Data Entry Validation and Error Trapping
Data is entered via the keypad to setup the count mode. To do this, a number between 0 and 99 can be entered. For two digit numbers, the user can enter the value as it is written on paper. However, for digits between 0 and 10, the user is required to place a zero in front of the single digit. If the user does not enter any value, the default setting is 01. The user may enter a number as many times as required. For example: Press 0, 1 Press 0 1 4 Press 0 1 4 7 Press 0 1 4 7 3 achieves achieves achieves achieves 01 41 47 37
51 of 122
3.2.2.2
Each menu handled error checking in similar fashions. The main menu consisted of three options: calibrate show statistics user remote
To choose one of these options, the user must select 1, 2 or 3. If this was not done, an error message would be displayed saying Invalid option, try again. And the options would be displayed again. This is done using a case statelement, checking if 1, 2, or 3 was entered otherwise by default the error message would be displayed. In calibrate mode, the user needs to enter a password to calibrate the scales. If an incorrect password is not entered, the program will tell the user the password is incorrect and they can not access this mode. The program then returns to the main menu. It does this by comparing what has been entered to the expected value. Once in calibration mode, the user does not need to enter anything via the keyboard other than pressing any key when they have placed the requested weight on the scales. Hecnce, error checking is not required. In show statistics, the user only needs to enter the correct password to access this module. The program checks this the same way as is done in calibration mode. Once the password is entered and entrance is granted, the statistics are displayed and the user can press any key to return to the main menu and so no error checking is necessary here. In user remote mode, no password is required. It does however, ask if the weight wants to be displayed in grams of ounces and the user does so by entering g or o. If the user enters an invalid options, nothing will happen. The program will continue to wait for a valid option to be entered. It does this again by comparing what is expected to be the input. The user can chose count mode from here by pressing c. The program continually checks for this after g or o has been entered. Once in count mode, the user is asked to enter the number of items on scales. If the user does not enter a number, the program will display an error message and ask the user to try again. This is done by checking each character the user enters, ensuring they lie between in hex values, 30 and 39 since between these values represent a digit between 0 and 9. Also, since count can only allow a 3 digit number to be entered, the program checks to see if 3 number have been entered. If so, the program will immediately display the number of items on the scale preventing the user from entering higher numbers. It checks this through the user of a counter, incrementing each time a number has been entered "When calculating the weight, the determine weight function uses error checking procedures on the weight voltage measured by the strain gauge. If the voltage measured happens to be less or equal to the voltage corresponding to zero weight, the program will interpret this as no weight on the scales and so will display zero in user remote mode. Also, when the voltage reading exceeds the voltage corresponding to 1000 grams, the last components of the weightfunction relationship are used, that is the gradient and voltage offset for the interval between 800 and 1000 grams.
52 of 122
C. Hardware Design
There are two types of hardware that is used within this system: Manufactured Hardware; Designed Hardware;
The Manufactured hardware comprises of the various pieces of hardware that were implemented as black box systems. The hardware was obtained and then implemented according to the manufacturers instructions and interfacing requirements. These modules include: LCD Required a minimum of seven pins to interface to the MCU. An additional 0V, 5V and 0.6V lines were required to power and display the device. The 0.6V was achieved by using a simple voltage divider between a 5V potential using a 10K and a 1.2K resistor (1% tolerance). Text to Speech Required an SPI interface between the MCU and itself. The datasheet for this hardware indicates that the RESET pin should be attached to a pin that could be set or cleared. Good operation and functionality was achieved only when this RESET pin was grounded. Apart from this, a speaker (4 ohm, 0.2W) was attached to the differential output on the board to hear the text readout. The CODEC and AUX pins were not used. The power from the board is very small and so appropriate amplification is required. This could be achieved using a 9V battery powered pre amplifier. However, to minimise cost and save time, the speaker was placed in a simple plastic cup, which behaved as a speaker cone. The sound was amplified adequately to be audible in a relatively silent room. Strain Gauge Pre Designed strain gauge attached to a Cantilever Load beam to yield a voltage between 0 and 3.3 3.7 V to map the deflection of the beam as a voltage. The device requires a 5V potential to operate. The output analog signal is relative to a ground pin. Any interface therefore demands that the ground pin is connected to the same ground as that of the device to which the output pin is connected. Microcontroller See Section 4.2.2. The 5V-regulated output is multiplexed to power the interfaced devices. The serial port is used to interface to a standard Desktop computer. The transmitted data is displayed using the Microsoft Windows HyperTerminal Software over a USART serial interface at 9600 baud. Details of the Designed Hardware are described in the next section. Samsung KS0066U LCD driver on an ALTRONICS board; Winbond WTS701 Text to Speech Module; Strain Gauge Microchip PIC18F452 Microcontroller on the PIC MNML board.
53 of 122
Power is sourced from a 9v battery and alternatively through a DC power jack. The jack is of a switching type which effectively switches the battery out of the circuit when connected. Incoming power is switched through the power switching module after which it passes to the power input of the MML18. Power is regulated by the ZSR-500 low dropout 5v regulator on the MML18 board. Regulated 5v power is sourced from the auxiliary connections on the MML18 board and is distributed to other hardware sections via the power distribution board. Without a heat sink the ZSR-500 is capable of providing 200 mA continuously.
C.2.2.
Computer Design
The scales are based around a Microchip PIC18F452 MCU supported on a MNML-PIC-18 carrier board. This board provides access to the 452s ports via header pins and contains all necessary hardware for MCU operational support. Further details of the MNML-PIC-18 board are provided via the design notes supplied with the board; to be found in the hardware appendix. The MCU ports are utilised as follows: PORTA PORTB PB0 PB1 PB2 PB3 PB4 Timer Period Update Keypad data available interrupt Keypad data LSB Keypad data Keypad data PA0 PA1 PA2 PA3 PA4 PA5 PA6 PA7 Analogue data from differential amplifier unimplemented Reserved for later implementation of VrefReserved for later implementation of Vref+ unimplemented unimplemented unimplemented unimplemented
54 of 122
unimplemented SPI TTS SS LATCH SPI TTS R/B SPI CLK SPI SDO SPI SDI unimplemented unimplemented unimplemented
LCD DATABUS LCD DATABUS LCD DATABUS LCD DATABUS unimplemented LCD CONTROL LCD CONTROL LCD CONTROL LCD GROUND
Following advice received during the project walkthrough with ACFR staff it was decided to avoid having high speed data moving through the simulated digital pins available on Port A to reduce the introduction of noise in the AD conversion on PA 0. Time permitting there was intention to implement a 3.5v positive reference into PA3 to increase the resolution of the conversion of the differential amplifiers output, which has maximum amplitude of 3.2v. This was to be achieved using an LM324 variable voltage source following difficulties obtaining a packaged voltage reference. Further details are included in the hardware appendix.
55 of 122
C.2.3.
Sensor Hardware
Serial port activity is monitored through connection to the RTS/CTS line loop in the port connection. In order to sample this line a set of intermediate plugs were used to allow probing of this line without direct connection to the serial port on the MNML18 board. Depending on the serial handshaking system in place the RTS line may stay high throughout the serial session or pulse high every byte transaction. A 30 second hysterisis period is implemented to cater for both these situations, allowing one pulse every 30 seconds to keep the serial sense input to the microcontroller to remain high. A 30 second timer based around an NE555 timer IC configured as a monostable timer for which the timing capacitor can be drained to prevent the 2/3 voltage cut-off level being reached. This system was tested and found to function appropriately.
Although final implementation was not achieved, an anti-aliasing active analogue filter was designed for use between the output of the differential amplifier and the AD converter. The filter was designed with a 40 Hz corner frequency and unity gain, based around a 741 Op-amp. The possibility of increasing gain to 1.56 to improve AD conversion resolution was also explored. Shielded cables were made to reduce noise in the links between amp and filter and filter and AD.
56 of 122
C.2.4.
Operator input is via 9 numerical digits from a tactile keypad and an additional 5 tactile switches. The additional switches are wired so as to emulate the A B C D & # keys on an alphanumeric keypad. All are multiplexed into a 4 bit data bus via the Fairchild MM74922 key decoder chip. Prior to the decision to use the 74922 several options for de-bouncing the switches were considered; including the use of a capacitive/resistive array, flip-flops and a key-scan mechanism using the microcontroller. The availability of low bounce conductive rubber switches as well as dedicated de-bounce ICs; such as those manufactured by Maxim, was also explored.
57 of 122
Choice upon the 74922 was made to reduce construction time, complexity and room for intermittent hardware errors. The 74922 requires minimal support hardware, provides switch rail pull up resistors and has low quiescent power draw. The 74922 output is via an internal tri-state buffer which requires driving the OE pin low to present data on the bus; this is achieved by inverting the data available pin DA through a 7404 hex inverter IC. A 74LS04 hex inverter was used to drive TTL logic levels from the tri-state CMOS level outputs of the 74922 chip. Inversion of the signal occurred as a result but was easily reversed in software. A non-inverting buffer would have removed the need for this; however the 7404 was on hand and could also provide the inversion of DA to feed OE.
C.2.5. C.2.6.
There are a few measures taken to assure the quality of the hardware:
58 of 122
4. Follow the instructions given by the program. First, remove all weights from the scale and press enter when this has been done. 5. Add weight 200 grams to the scales and press enter. 6. Continue to add 200 gram weights as instructed by the program. This will continue until 1000 grams has been placed on the scales. 7. Once calibration is complete, the program will respond to the user displaying Calibration successful. Note: It is recommended to improve accuracy of calibration that when placing weight to scales to place them gently and wait for vibrations on scales to settle before pressing enter.
59 of 122
D. Software Design
The software requirements and overview have been dealt with elsewhere in this document. The present section addresses the design and implementation of the software that forms the CCS.
D.1.1.
The processor used for the scale is the PIC18F452 MNML board therefore all the coding are designed in the fashion for this processor. C language is main programming language used for the scale for easier understanding. The environment used for programming is MPLAB.
D.1.2.
The software is divided into different module first, some are written to support corresponding hardware, others are for the scale to perform certain function. Global variables are set to be the same in all individual modules to make integration in later stage easier to perform. A/D conversion, LCD component and HyperTerminal are the modules are completed first. Then keypad and Text to Speech module are written, and then finally the coding for calibration, statistics, count and weight are required. The integration will take place, first all the software for assisting the performance of hardware are required which are A/D conversion, LCD, HyperTerminal and Text to Speech. Once all these hardware are reacting in the fashion desired, coding for varies mode are implemented, and then test will be conducted for the scale under different mode conditions to test the overall accuracy of it. Each module goes through individual unit testing, where a selection of inputs and outputs are recorded and the outputs compared with expected values. Next a selection of modules are integrated and tests carried out again. When the results are satisfactory, the whole system is compiled with all the software and hardware modules and final tests are carried out.
Architecture
Samsung KS0066U LCD Driver Software; Winbond WTS701 Text to Speech Driver Software; Strain Gauge Functions; Filter Software;
60 of 122
Statistics and Data Collection; Factory Mode and User Local Software; Main Initialise, Run Commands and Interrupt Service Routine.
The primary function was to utilise the Strain gauge to display a weight reading on the LCD screen. As an option, the user would also be able to press a button to speak the weight. The diagrams below describes the Communication between the various modules.
D.3.1.1.
The diagram below describes the interface between the software modules that yield a weight reading on the screen.
NB: The ISR is an Interrupt Service Routine and can trigger at any point throughout this process. The return position will never be in a predefined place and so the return position is denoted by an disconnected arrow. D.3.1.2. Count Mode Architecture The Architecture follows that which is illustrated in section 5.3.1.1. The only difference is that the function block that denotes the grams or ounce conversion is replaced by a Count Setup function. Here, the Weigh/Count module is called to setup the weight of one item before
61 of 122
determining the number of items in a given weight sample. executions follows that as described in the diagram. D.3.1.3.
Following an interrupt setup to trigger for a physical serial port connection, the system changes into the factory mode or User Remote mode. Here, the software calls its own internal functions to perform the various tasks it contains. Further details of this module can be found in the relevant section.
D.3.2.
Software Interface
D.3.3.
Software Components
D.4.2.
The System can be shutdown at any time. This includes pulling the power plug or disconnecting the battery. This however is not advisable, and it is recommended that the system be left alone for two minutes. After this, the system will shut down. If the system does not speak upon a restart, disconnect all power sources and leave the unit alone for five minutes. Reconnect all cables and return the power. The system will be stable once again. .
62 of 122
E. System Performance
E.1.1. Performance Testing
The system was able to iterate through the respective features of weigh and count modes. The Factory mode, although demonstrated separately was also functioning as an individual unit.
E.1.2.
The final state of the system as delivered on 8 June 2006 was essentially the bare pieces. The following modules were not complete: Calibrate; Text to Speech Statistics User Local and some Factory Settings; External Casings.
The LCD, T2S, Scales, and Power were integrated. However, the rest of the functions were not integrated and as a result, all the above discussed modules were not fully functional. The system was however able to measure weight and display this on the screen with up to a -10g error. The system was also able to count items placed on the tray with up to -1 item accuracy. Furthermore, these results could be visibly seen on the LCD screen, and audibly heard throughout the testing process. (The T2S failed to function on demonstration). The power circuitry all performed to design specifications, with the shutdown timer physically implemented with a potentiometer. The keypad and buttons all triggered correctly. This is because the data and interrupt pins for all the buttons were common to one line.
E.1.3.
Future Improvements
Code Logic and sequencing; T2S reliability; Audible Sound output Pre amplifier may be required; LCD error messages implemented to show the user that he has entered an invalid input; Overflow and underflow checking; Integer arithmetic to minimise program memory usage when parsing integers; Remove nested while loops; Serial functions should be interrupt driven; A prettier final enclosure; Scale tuning Calibration with the use of one weight. Code Portability; Low frequency cutoff filter remove basket pendulum oscillations; Weighing accuracy;
63 of 122
Rather than declaring most variables as global variables, it would be more efficient and improve robustness by declaring variables locally wherever possible. This will also prevent error with idata overload as since strings being declared globally, will last throughout the entire run time where as defining them locally will be erased when the corresponding function is complete.
64 of 122
F. Safety Implications
Make sure all wires are properly connected to prevent an electrical short this could start a fire. Wear Insulated footwear when operating on the inside of the unit; Do not plug unit into AC power source Maximum into power unit is 12V Regulated DC supply
G. Conclusions
The fundamental aspect of this process is not the task itself but the way in which engineers communicate and cooperate as a team and contribute with dedication as an individual to get a job done. Without this, not only will the final product suffer, but friendships and impressions of people can change forever.
65 of 122
66 of 122
97.3
97.2
692
1850
67 of 122
750 800 825 850 875 890 900 910 925 950 1000 1025 1050 1075
2270 2470 2550 2630 2660 2720 2740 2790 2840 2910 3040 3120 3220 3300
2285 2445 2545 2625 2665 2720 2740 2790 2835 2915 3045 3120 3220 3300
2263.467 2423.467 2523.467 2603.467 2643.467 2698.467 2718.467 2768.467 2813.467 2893.467 3023.467 3098.467 3198.467 3278.467
68 of 122
69 of 122
70 of 122
71 of 122
72 of 122
73 of 122
74 of 122
75 of 122
76 of 122
77 of 122
78 of 122
79 of 122
80 of 122
void main(void) { TRISB = 0; ADCON1=0x8E; //ADCON1 = 0x00; XLCDInit(); XLCDL2home(); XLCDPutRomString(bbb); XLCDL1home(); XLCDPutRomString(aaa); //ADCON1= 0x0E; ADCON0= 0x41; T0CON= 0xC7; // // // { init_Itx(); while(INTCON != 0xC4); { } //ADCON0bits.GO = 1; while(1) getDigits(); dispWeight(); XLCDL1home(); XLCDPutRomString(aaa); //prepare for LCD
void XLCDDelay15ms (void) { int i; for(i=0;i<10000;i++) { Nop(); } return; } void XLCDDelay4ms (void) { int i; for(i=0;i<2500;i++) { Nop(); } return; } void XLCD_Delay500ns(void) {
rom const char aaa[] = "Weight:"; rom const char bbb[] = " unsigned unsigned unsigned unsigned double grams = 0; double ounces = 0; int digData = 0; char unit_flag = 0;
81 of 122
void XLCDDelay(void) { int i; for(i=0;i<1000;i++) { Nop(); } return; } void getDigits(void) { unsigned int weight; unsigned int calbr; calbr = digData - 0; if (unit_flag == 1) { ounces = ((calbr*16.34) - 31.32)/28.4; weight = 10*ounces; } else { grams = calbr*16.34 - 31.32; weight = grams; } //} tenE3 = weight/10000; tenE2 = (weight - tenE3*10000)/1000; tenE1 = (weight - tenE3*10000 - tenE2*1000)/100; tenE0 = (weight - tenE3*10000 - tenE2*1000 - tenE1*100)/10; tene1 = weight - tenE3*10000 - tenE2*1000 - tenE1*100 - tenE0*10; tenE3 = tenE3 + 48; tenE2 = tenE2 + 48; tenE1 = tenE1 + 48; tenE0 = tenE0 + 48; tene1 = tene1 + 48; return;
void init_Itx(void) { RCONbits.IPEN = 1; PIE1bits.ADIE = 1; IPR1bits.ADIP = 1; //INTCON = 0b11000000; INTCONbits.GIEH = 1; INTCONbits.GIEL = 1; IPR1bits.CCP1IP = 1; PIE1bits.CCP1IE = 1; CCPR1L = 0xe8; CCPR1H = 0x03; intervals CCP1CON = 0b00001011; T1CON = 0b10000001; } void init_ADConv(void) {} #pragma code high_vector=0x08 void interrupt(void) {
//enalbe priority on interrupts //enalbe AD conversion interrupt //enable AD itx high priority //turn on interrupts
//prescale 1:1 to trigger compare at 10ms //enable special event trigger on compare //enable timer 1, 1:1 prescale
} #pragma code
#pragma interrupt HighISR void HighISR(void) { //PORTB = 0x01; if(PIR1bits.CCP1IF == 1) { //timer interrupt PIR1bits.CCP1IF = 0; ADCON0bits.GO = 1; }
void AD_Complete(void) { digData = ADRESH; getDigits(); XLCDPut(' '); XLCDPut(' '); XLCDPut(tenE3); XLCDPut(tenE2); XLCDPut(tenE1); XLCDPut(tenE0); XLCDL1home(); XLCDPutRomString(aaa); return;
if (PIR1bits.ADIF == 1) { //AD conversion complete interrupt PIR1bits.ADIF = 0; //reset flag for next conversion digData = ADRESH; //store digital data digData <<= 8; digData += ADRESL; }
82 of 122
XLCDPut(tenE0); XLCDPut('.'); XLCDPut(tene1); if (unit_flag == 1) { XLCDPut('o'); XLCDPut('z'); } else { XLCDPut(' '); XLCDPut('g'); } return;
83 of 122
/********************************************************************* * * External LCD access routines * ********************************************************************* * FileName: ALCD.c * Dependencies: alcd.h * Processor: PIC18 * Complier: MCC18 v1.00.50 or higher * HITECH PICC-18 V8.10PL1 or higher * Company: Microchip Technology, Inc. * * This program initialises and calls the various commands on the LCD // whic will allow various stings to be seen on the LCD * THIS SOFTWARE IS PROVIDED IN AN AS IS CONDITION. NO WARRANTIES, * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT, * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. * * HiTech PICC18 Compiler Options excluding device selection: * -FAKELOCAL -G -E -C * * Author Date Comment *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Ganesha Thayaparan 8/6/06 Original (Rev 1.0) ********************************************************************/ #include "p18f452.h" #include "alcd.h" void alcd_init(void); void alcdCommand(unsigned char cmd); char alcdCheckBusy(void); void alcd_init(void) { //Clear Dataport and its TRIS alcd_dataportTRIS &= 0xf0; //check PORT config before clearing whole port alcd_dataport &= 0x10; //Clear Control Port and TRIS alcd_RSPinTRIS = 0; alcd_EPinTRIS = 0; alcd_RWPinTRIS = 0; alcd_RSPin = 0; alcd_EPin = 0; alcd_RWPin = 0; //Setup for 4 Bit Lower by Datasheet specification Delay15ms(); Delay15ms(); Delay15ms(); //-----NEW STEP----------//Clear Lower nibble
alcd_dataport &= 0xf0; alcd_dataport |= 0b00000010; //PORTC = 0x02; alcd_EPin = 1; //clock in Delay500ns(); alcd_EPin = 0; alcd_dataport &= 0xf0; alcd_dataport |= 0b00000010; //PORTC = 0x02; alcd_EPin = 1; //clock in Delay500ns(); alcd_EPin = 0; alcd_dataport &= 0xf0; alcd_dataport |= 0b00001100; //PORTC = 0x0C; alcd_EPin = 1; //clock in Delay500ns(); alcd_EPin = 0; Delay15ms(); alcdCommand(0x0c); Delay15ms(); alcdCommand(0x01); Delay15ms(); } alcdCommand(0x06);
void alcdCommand(unsigned char cmd) { Delay(); alcd_RSPin = 0; alcd_RWPin = 0; alcd_EPin = 0; alcd_dataport &= 0xf0; //clear port alcd_dataport |= ((cmd>>4)&0x0f); alcd_EPin = 1; //clock in Delay500ns(); alcd_EPin = 0; alcd_dataport &= 0xf0; alcd_dataport |= cmd&0x0f; alcd_EPin = 1; //clock in Delay500ns(); alcd_EPin = 0; } return;
84 of 122
alcd_RSPin = 1; alcd_RWPin = 0; alcd_EPin = 0; alcd_dataport &= 0xf0; //clear port alcd_dataport |= ((data>>4)&0x0f); alcd_EPin = 1; //clock in Delay500ns(); alcd_EPin = 0; alcd_dataport &= 0xf0; alcd_dataport |= data&0x0f; alcd_EPin = 1; //clock in Delay500ns(); alcd_EPin = 0; } return;
void alcdPutString(rom char *string) { Delay15ms(); while(*string) //keep looping until the null term (end of string) is reached { //while(alcdCheckBusy()); alcdWrite(*string); string++; } } char alcdCheckBusy(void) { if (alcd_dataport&0x08) { alcd_EPin = 0; Delay500ns(); alcd_EPin = 1; Delay500ns(); alcd_EPin = 0; Delay500ns(); alcd_dataportTRIS = 0x00; //set for input return 1; } else { alcd_EPin = 0; Delay500ns(); alcd_EPin = 1; Delay500ns(); alcd_EPin = 0; Delay500ns(); alcd_dataportTRIS = 0x00; //set for input return 0; } }
85 of 122
/********************************************************************* * * External LCD access routines * ********************************************************************* * FileName: XLCD.c * Dependencies: xlcd.h * Processor: PIC18 * Complier: MCC18 v1.00.50 or higher * HITECH PICC-18 V8.10PL1 or higher * Company: Microchip Technology, Inc. * * This module intialises and calls the various functions of the winbond * text to speech module. What seems to be missing here is the simple stop * command which enables the buffer to empty before sending another string. * Instead, various delays have been put in and the T2S module is given real * time to empty its buffer contents. * THIS SOFTWARE IS PROVIDED IN AN AS IS CONDITION. NO WARRANTIES, * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT, * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER. * * HiTech PICC18 Compiler Options excluding device selection: * -FAKELOCAL -G -E -C * * Author Date Comment *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Ganesha Thayaparan 8/6/06 Original () ********************************************************************/ #include "p18f452.h" #include "alcd.h"
SSPSTATbits.CKE = 1; SS = 1; RESET = 0; t2sDelay(); //t2s init SS = 0; loadBuff(0x14); loadBuff(0x00); SS = 1; t2sDelay(); SS = 0; loadBuff(0x02); loadBuff(0x00); SS = 1; t2sDelay(); SS = 0; loadBuff(0x4e); loadBuff(0x00); SS = 1; t2sDelay(); SS = 0; loadBuff(0x4f); loadBuff(0x01); SS = 1; t2sDelay(); SS = 0; loadBuff(0x50); loadBuff(0x43); ohm speaker SS = 1; t2sDelay(); SS = 0; loadBuff(0x51); loadBuff(0x03); SS = 1; t2sDelay(); SS = 0; loadBuff(0x52); loadBuff(0x02); SS = 1; t2sDelay(); SS = 0; loadBuff(0x77); loadBuff(0x05); SS = 1; } void loadBuff(unsigned char byte) { while(RB==0); SSPBUF = byte; while(!SSPSTATbits.BF); rxData = SSPBUF; //return rxData;
//start tx
//start tx //CODEC control //ignore - disconnected //start tx //set analog audio //power up buffer and drifer, use 8
//start tx //set volume //0dB volume //start tx //speech rate command //set default speed //start tx //pitch command //set pitch to default
unsigned char rxData; void t2sInit(void) { char return1,return2; //TRISCbits.TRISC5 = 0; TRISCbits.TRISC1 = 0; TRISCbits.TRISC2 = 1; //PIC initialisation TRISCbits.TRISC5 = 0; TRISCbits.TRISC3 = 0; TRISCbits.TRISC4 = 1; //SSPCON1bits.SSPEN = 1; SSPCON1bits.CKP = 1; SSPCON1bits.SSPEN = 1; SSPSTATbits.SMP = 1; //clear SDO //clear SCK //idle on cke high, enable SSP ports // input data sampled at end of data output time */
86 of 122
} void t2sDelay(void) { int i = 0; for(i=0;i<100;i++) { Nop(); } return; } void t2sSaySomething(unsigned char *string) { t2sDelay(); SS = 0; loadBuff(0x81); loadBuff(0x00); while(*string) //keep looping until the null term (end of string) is reached { //while(alcdCheckBusy()); loadBuff(*string); string++; } loadBuff(0x1A); SS = 1; t2sDelay(); } void readWeight(unsigned int weight) // simple parsing alogrithm that will extract the digits // out of the argument 'weight' { unsigned int tenE0,tenE1,tenE2,tenE3; tenE3 = weight/1000; tenE2 = (weight%1000)/100; tenE1 = (weight%100)/10; tenE0 = (weight%10); tenE3 tenE2 tenE1 tenE0 += += += += 48; 48; 48; 48; }
loadBuff(tenE1); } loadBuff(tenE0); loadBuff(' '); loadBuff('g'); loadBuff('r'); loadBuff('a'); loadBuff('m'); loadBuff('s'); loadBuff(0x1A); SS = 1;
void readWeight2(unsigned int weight) //make sure the ounce value is multiplied by ten before it is passed to this method { unsigned int tene1,tenE0,tenE1,tenE2,tenE3; tenE3 = tenE2 = tenE1 = tenE0 = //tene1 weight/1000; (weight - tenE3*1000)/100; (weight - tenE3*1000 - tenE2*100)/10; (weight - tenE3*1000 - tenE2*100 - tenE1*10); = weight - tenE3*10000 - tenE2*1000 - tenE1*100 - tenE0*10;
tenE3 += 48; tenE2 += 48; tenE1 += 48; SS = 0; loadBuff(0x81); loadBuff(0x00); if (tenE3 != 0x30) { loadBuff(tenE3); } if (tenE2 != 0x30) { loadBuff(tenE2); } loadBuff(' '); loadBuff('p'); loadBuff('o'); loadBuff('i'); loadBuff('n'); loadBuff('t'); loadBuff(' '); loadBuff(tenE1); loadBuff(' '); loadBuff('o'); loadBuff('u'); loadBuff('n'); loadBuff('c'); loadBuff('e'); loadBuff('s'); loadBuff(0x1A); SS = 1; while(!RB);
SS = 0; loadBuff(0x81); loadBuff(0x00); if(tenE3 != 0x30) { loadBuff(tenE3); } if(tenE2 != 0x30) { loadBuff(tenE2); } if(tenE1 != 0x30)
87 of 122
} void readWeight3(unsigned int weight) { unsigned int tenE0,tenE1,tenE2,tenE3; tenE3 = weight/1000; tenE2 = (weight%1000)/100; tenE1 = (weight%100)/10; tenE0 = (weight%10); tenE3 tenE2 tenE1 tenE0 += += += += 48; 48; 48; 48;
SS = 0; loadBuff(0x81); loadBuff(0x00); loadBuff(tenE3); loadBuff(tenE2); loadBuff(tenE1); loadBuff(tenE0); loadBuff(' '); loadBuff('i'); loadBuff('t'); loadBuff('e'); loadBuff('m'); loadBuff('s'); loadBuff(0x1A); SS = 1; }
88 of 122
#include "p18f452.h" #define alcd_dataport #define alcd_dataportTRIS #define alcd_EPin #define alcd_EPinTRIS #define alcd_RWPin #define alcd_RWPinTRIS #define alcd_RSPin #define alcd_RSPinTRIS #define SS #define RESET #define RB #define SDO #define SDI #define SCK void void void void void void void PORTCbits.RC1 PORTDbits.RD4 PORTCbits.RC2 PORTCbits.RC5 PORTCbits.RC4 PORTCbits.RC3 TRISD PORTD PORTDbits.RD7 TRISDbits.TRISD7 PORTDbits.RD6 TRISDbits.TRISD6 PORTDbits.RD5 TRISDbits.TRISD5
alcd_init(void); Delay(void); Delay15ms (void); Delay4ms (void); Delay500ns(void); alcdPutString(rom char *string); alcdWrite(unsigned char data);
void runButton(void); void alcdCommand(unsigned char cmd); void t2sInit(void); void t2sSaySomething(unsigned char *string); #define #define #define #define alcdL1home() alcdCommand(0x80) alcdL2home() alcdCommand(0xc0) alcdClear() alcdCommand(0x01) alcdReturnHome() alcdCommand(0x02)
void readWeight(unsigned int weight); void readWeight2(unsigned int ounces); void readWeight3(unsigned int weight); extern unsigned int digData; extern unsigned int weight; extern char selection; extern char read; //unsigned int grad_array[5]; //unsigned int offset_array[5]; int getArray(unsigned char position, unsigned char select);
89 of 122
/****************************************************************************** MODULE Finite Element Response Software Filter FILE FilterCXXXXXX.c ddmmyy
/****************************************************************************** F O R W A R D D E C L A R A T I O N *******************************************************************************/ unsigned short int filter(void); /****************************************************************************** E N T R Y R O U T I N E D E F I N I T I O N Entry routines is the start of the filter function and exit returns int data type fil_data to the calling function. *******************************************************************************/ #pragma code unsigned short int filter(void) { char i,j; unsigned short long sum; /* 24 bits C18 type */ j = data_index - FIR_WEIGHTS; if(j < 0) {j = j + MAX_DATA_SAMPLES;} sum = 0; for(i=0; i<FIR_WEIGHTS; i++) { sum = sum + (weights[i] * AD_data_buffer[j]); j = j + 1; if(j >= MAX_DATA_SAMPLES) {j = 0;} } sum=(sum>>10); /*Right shift 10 places equiv. to /512 */ filter_data[data_index]= (unsigned short int) sum; return filter_data[data_index]; } /* filter() */
ENTRY (PUBLIC) ROUTINE - May be called from outside this module int filter(void) INITIALISATION ROUTINES Implementation of the store_data.h to enumerate buffers and constants and arrays. DESCRIPTION 21 tap FIR Filter with; -corner frequency -stopband att -passband att -passband ripple LAST UPDATE 1 June 2006 5Hz -60 dB 1dB 1 dB
by Tycho
*******************************************************************************/
/****************************************************************************** I M P O R T S *******************************************************************************/ #include <stdlib.h> #include "store_data.h" /* standard C header file */ /* public definitions required for filter.c */
/* Relevant constants as defined in store_data.h */ /* #define BUFFER_SIZE 100 #define FIR_WEIGHTS 21 #define MAX_DATA_SAMPLES 90 */ /****************************************************************************** P R I V A T E D A T A D E F I N E D T Y P E S A N D M O D U L E V A R I A B L E S *******************************************************************************/ #pragma romdata static char weights[FIR_WEIGHTS]={0, 1, 2, 6, 12, 21, 31, 42, 52, 59, 61, 59, 52, 42, 31, 21, 12, 6, 2, 1, 0}; /* Weights are multiplied by 512 to integerise for calcs. */
/************************************************************************************* ENTRY OF DATA Attn; John Insertion routine...... /* data_buffer[data_index]=dig_data if(data_index >= MAX_DATA_SAMPLES) data_index = 0;
*/ /*************************************************************************************/
90 of 122
/****************************************************************************** MODULE High ISR FILE HighISR.c ENTRY (PUBLIC) ROUTINE main - the C main() function provides compile time test functionality INITIALISATION ROUTINES Must run Setup routine prior to enabling interrupts. Interrupt must first be enabled before ISR function. Must include store_data header file containing global data refeenced. DESCRIPTION This module is the high ISR for use in the scales. Initially designed to test button press functionality it is then incrementally updated to reflect growing program complexity. LAST UPDATE 29 May 2006 added flags press_flag. Tycho *******************************************************************************/ /****************************************************************************** P R I V A T E D A T A N A M E D C O N S T A N T S A N D M A C R O S *******************************************************************************/ #if !defined (MAX_DATA_SAMPLES) # define MAX_DATA_SAMPLES 90 # endif /****************************************************************************** I M P O R T S *******************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <p18f452.h> //#include "store_data.h" /****************************************************************************** P R I V A T E D A T A D E F I N E D T Y P E S A N D M O D U L E V A R I A B L E S *******************************************************************************/ /* extern unsigned char button=0;.................. */ unsigned char button=0; unsigned int digData=0; /* standard C header file */
extern unsigned short int filter_data[MAX_DATA_SAMPLES]; extern signed char data_index; /* points to most recently received data sample */ /****************************************************************************** F O R W A R D D E C L A R A T I O N S *******************************************************************************/ void high_isr (void); void SetupInterrupts (void); /****************************************************************************** E N T R Y R O U T I N E D E F I N I T I O N *******************************************************************************/ #pragma code void main(void) { SetupInterrupts(); registers */ while (1); } /* testing functionality */ /* Setup Interrupts initialises appropriate
/****************************************************************************** HIGH INTERRUPT SERVICE ROUTINE *******************************************************************************/ #pragma code high_vector=0x08 void interrupt_at_high_vector (void) { _asm GOTO high_isr _endasm } #pragma code #pragma interrupt high_isr void high_isr (void) { INTCONbits.GIE=0; if (INTCON3bits.INT1IF=1) button */ /* Disable all interrupts */ /* Interrupt caused by button press, capture /*
character to "button" and return, re-enablng interrupts. */ { /* TESTING FOR EVB */ //button=~PORTD; //button= (button&(0b00001111)); //Nop(); /* END TEST CODE */
91 of 122
/* MML18 CODE */ button=~PORTB; button= (button&(0b00111100)); button>>=2; /* END MML18 CODE */ if (button==12) { tare_flag=1; tare=filter_data[data_index]; /* tare=digData; */ implementation. */ button_press_flag=0; } else if (button==16) { mute_flag=(~mute_flag); button_press_flag=0; } else { }
/*********************************************************************************/
SYNOPSIS void SetupInterrupts (void); PARAMETER takes none. REMARKS Contains code for both testing on PicDem 2 Plus and MML18 boards. ------------------------------------------------------------------------------*/ void SetupInterrupts (void) { INTCONbits.GIE=1; */
INTCON3bits.INT1IF=0; } else if (PIR1bits.CCP1IF==1) required */ { PIR1bits.CCP1IF=0; ADCON0bits.GO=1; } else if (PIR1bits.ADIF==1) required */ { PIR1bits.ADIF=0; digData=ADRESH; digData<<=8; digData=ADRESL; } else { Nop(); } INTCONbits.GIE=1; return; }
INTCON2bits.INTEDG1=1; relevant */ INTCON3bits.INT1IP=1; INTCON3bits.INT1IE=1; // TRISBbits.TRISB0=0; TRISBbits.TRISB1=1; /* PicDem 2 Plus CODE TRISDbits.TRISD0=1; TRISDbits.TRISD1=1; TRISDbits.TRISD2=1; TRISDbits.TRISD3=1; END PicDem 2 Plus CODE MML18 CODE TRISBbits.TRISB2=1; TRISBbits.TRISB3=1; TRISBbits.TRISB4=1; TRISBbits.TRISB5=1; END MML18 CODE */
/* /*
*/ */
/* /* */ }
*/
/* Re-enable interrupts */
92 of 122
/*------------------------------------------------------------------------* $Id: p18f452.h,v 1.7 2004/10/20 22:49:39 kuhrtt Exp $ * MPLAB-Cxx PIC18F452 processor header * * (c) Copyright 1999-2004 Microchip Technology, All rights reserved *-------------------------------------------------------------------------*/ #ifndef __18F452_H #define __18F452_H extern volatile near unsigned char extern volatile near union { struct { unsigned RA0:1; unsigned RA1:1; unsigned RA2:1; unsigned RA3:1; unsigned RA4:1; unsigned RA5:1; unsigned RA6:1; }; struct { unsigned AN0:1; unsigned AN1:1; unsigned AN2:1; unsigned AN3:1; unsigned :1; unsigned AN4:1; unsigned OSC2:1; }; struct { unsigned :2; unsigned VREFM:1; unsigned VREFP:1; unsigned T0CKI:1; unsigned SS:1; unsigned CLK0:1; }; struct { unsigned :5; unsigned LVDIN:1; }; } PORTAbits; extern volatile near unsigned char extern volatile near union { struct { unsigned RB0:1; unsigned RB1:1; unsigned RB2:1; unsigned RB3:1; unsigned RB4:1; unsigned RB5:1; unsigned RB6:1; unsigned RB7:1; }; struct { unsigned INT0:1; unsigned INT1:1; unsigned INT2:1; unsigned CCP2:1; unsigned :1; PORTA;
PORTB;
unsigned PGM:1; unsigned PGC:1; unsigned PGD:1; }; } PORTBbits; extern volatile near unsigned char extern volatile near union { struct { unsigned RC0:1; unsigned RC1:1; unsigned RC2:1; unsigned RC3:1; unsigned RC4:1; unsigned RC5:1; unsigned RC6:1; unsigned RC7:1; }; struct { unsigned T1OSO:1; unsigned T1OSI:1; unsigned :1; unsigned SCK:1; unsigned SDI:1; unsigned SDO:1; unsigned TX:1; unsigned RX:1; }; struct { unsigned T1CKI:1; unsigned CCP2:1; unsigned CCP1:1; unsigned SCL:1; unsigned SDA:1; unsigned :1; unsigned CK:1; unsigned DT:1; }; } PORTCbits; extern volatile near unsigned char extern volatile near union { struct { unsigned RD0:1; unsigned RD1:1; unsigned RD2:1; unsigned RD3:1; unsigned RD4:1; unsigned RD5:1; unsigned RD6:1; unsigned RD7:1; }; struct { unsigned PSP0:1; unsigned PSP1:1; unsigned PSP2:1; unsigned PSP3:1; unsigned PSP4:1; unsigned PSP5:1; unsigned PSP6:1; unsigned PSP7:1; }; } PORTDbits;
PORTC;
PORTD;
93 of 122
extern volatile near extern volatile near struct { unsigned RE0:1; unsigned RE1:1; unsigned RE2:1; }; struct { unsigned AN5:1; unsigned AN6:1; unsigned AN7:1; }; } PORTEbits; extern volatile near extern volatile near unsigned LATA0:1; unsigned LATA1:1; unsigned LATA2:1; unsigned LATA3:1; unsigned LATA4:1; unsigned LATA5:1; unsigned LATA6:1; } LATAbits; extern volatile near extern volatile near unsigned LATB0:1; unsigned LATB1:1; unsigned LATB2:1; unsigned LATB3:1; unsigned LATB4:1; unsigned LATB5:1; unsigned LATB6:1; unsigned LATB7:1; } LATBbits; extern volatile near extern volatile near unsigned LATC0:1; unsigned LATC1:1; unsigned LATC2:1; unsigned LATC3:1; unsigned LATC4:1; unsigned LATC5:1; unsigned LATC6:1; unsigned LATC7:1; } LATCbits; extern volatile near extern volatile near unsigned LATD0:1; unsigned LATD1:1; unsigned LATD2:1; unsigned LATD3:1; unsigned LATD4:1; unsigned LATD5:1; unsigned LATD6:1; unsigned LATD7:1; } LATDbits; extern volatile near extern volatile near unsigned LATE0:1; unsigned LATE1:1; unsigned LATE2:1;
PORTE;
LATA;
LATB;
LATC;
LATD;
LATE;
} LATEbits; extern volatile near extern volatile near unsigned RA0:1; unsigned RA1:1; unsigned RA2:1; unsigned RA3:1; unsigned RA4:1; unsigned RA5:1; unsigned RA6:1; } DDRAbits; extern volatile near extern volatile near unsigned TRISA0:1; unsigned TRISA1:1; unsigned TRISA2:1; unsigned TRISA3:1; unsigned TRISA4:1; unsigned TRISA5:1; unsigned TRISA6:1; } TRISAbits; extern volatile near extern volatile near unsigned RB0:1; unsigned RB1:1; unsigned RB2:1; unsigned RB3:1; unsigned RB4:1; unsigned RB5:1; unsigned RB6:1; unsigned RB7:1; } DDRBbits; extern volatile near extern volatile near unsigned TRISB0:1; unsigned TRISB1:1; unsigned TRISB2:1; unsigned TRISB3:1; unsigned TRISB4:1; unsigned TRISB5:1; unsigned TRISB6:1; unsigned TRISB7:1; } TRISBbits; extern volatile near extern volatile near unsigned RC0:1; unsigned RC1:1; unsigned RC2:1; unsigned RC3:1; unsigned RC4:1; unsigned RC5:1; unsigned RC6:1; unsigned RC7:1; } DDRCbits; extern volatile near extern volatile near unsigned TRISC0:1; unsigned TRISC1:1; unsigned TRISC2:1; unsigned TRISC3:1; unsigned TRISC4:1;
DDRA;
TRISA;
DDRB;
TRISB;
DDRC;
TRISC;
94 of 122
unsigned TRISC5:1; unsigned TRISC6:1; unsigned TRISC7:1; } TRISCbits; extern volatile near unsigned extern volatile near struct { unsigned RD0:1; unsigned RD1:1; unsigned RD2:1; unsigned RD3:1; unsigned RD4:1; unsigned RD5:1; unsigned RD6:1; unsigned RD7:1; } DDRDbits; extern volatile near unsigned extern volatile near struct { unsigned TRISD0:1; unsigned TRISD1:1; unsigned TRISD2:1; unsigned TRISD3:1; unsigned TRISD4:1; unsigned TRISD5:1; unsigned TRISD6:1; unsigned TRISD7:1; } TRISDbits; extern volatile near unsigned extern volatile near union { struct { unsigned RE0:1; unsigned RE1:1; unsigned RE2:1; }; struct { unsigned :4; unsigned PSPMODE:1; unsigned IBOV:1; unsigned OBF:1; unsigned IBF:1; }; } DDREbits; extern volatile near unsigned extern volatile near union { struct { unsigned TRISE0:1; unsigned TRISE1:1; unsigned TRISE2:1; }; struct { unsigned :4; unsigned PSPMODE:1; unsigned IBOV:1; unsigned OBF:1; unsigned IBF:1; }; } TRISEbits; extern volatile near unsigned extern volatile near struct { unsigned TMR1IE:1; unsigned TMR2IE:1; unsigned CCP1IE:1;
char
DDRD;
char
TRISD;
char
DDRE;
char
TRISE;
char
PIE1;
unsigned SSPIE:1; unsigned TXIE:1; unsigned RCIE:1; unsigned ADIE:1; unsigned PSPIE:1; } PIE1bits; extern volatile near extern volatile near unsigned TMR1IF:1; unsigned TMR2IF:1; unsigned CCP1IF:1; unsigned SSPIF:1; unsigned TXIF:1; unsigned RCIF:1; unsigned ADIF:1; unsigned PSPIF:1; } PIR1bits; extern volatile near extern volatile near unsigned TMR1IP:1; unsigned TMR2IP:1; unsigned CCP1IP:1; unsigned SSPIP:1; unsigned TXIP:1; unsigned RCIP:1; unsigned ADIP:1; unsigned PSPIP:1; } IPR1bits; extern volatile near extern volatile near unsigned CCP2IE:1; unsigned TMR3IE:1; unsigned LVDIE:1; unsigned BCLIE:1; unsigned EEIE:1; } PIE2bits; extern volatile near extern volatile near unsigned CCP2IF:1; unsigned TMR3IF:1; unsigned LVDIF:1; unsigned BCLIF:1; unsigned EEIF:1; } PIR2bits; extern volatile near extern volatile near unsigned CCP2IP:1; unsigned TMR3IP:1; unsigned LVDIP:1; unsigned BCLIP:1; unsigned EEIP:1; } IPR2bits; extern volatile near extern volatile near unsigned RD:1; unsigned WR:1; unsigned WREN:1; unsigned WRERR:1; unsigned FREE:1; unsigned :1; unsigned CFGS:1;
PIR1;
IPR1;
PIE2;
PIR2;
IPR2;
EECON1;
95 of 122
unsigned EEPGD:1; } EECON1bits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near struct { unsigned RX9D:1; unsigned OERR:1; unsigned FERR:1; unsigned ADDEN:1; unsigned CREN:1; unsigned SREN:1; unsigned RX9:1; unsigned SPEN:1; } RCSTAbits; extern volatile near unsigned extern volatile near struct { unsigned TX9D:1; unsigned TRMT:1; unsigned BRGH:1; unsigned :1; unsigned SYNC:1; unsigned TXEN:1; unsigned TX9:1; unsigned CSRC:1; } TXSTAbits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near union { struct { unsigned TMR3ON:1; unsigned TMR3CS:1; unsigned T3SYNC:1; unsigned T3CCP1:1; unsigned T3CKPS0:1; unsigned T3CKPS1:1; unsigned T3CCP2:1; unsigned RD16:1; }; struct { unsigned :2; unsigned NOT_T3SYNC:1; }; } T3CONbits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near union { struct { unsigned CCP2M0:1; unsigned CCP2M1:1; unsigned CCP2M2:1; unsigned CCP2M3:1; unsigned CCP2Y:1; unsigned CCP2X:1; }; struct { unsigned :4;
char
TXSTA;
unsigned DC2B0:1; unsigned DC2B1:1; }; struct { unsigned :5; unsigned DCCPX:1; }; } CCP2CONbits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near union { struct { unsigned CCP1M0:1; unsigned CCP1M1:1; unsigned CCP1M2:1; unsigned CCP1M3:1; unsigned CCP1Y:1; unsigned CCP1X:1; }; struct { unsigned :4; unsigned DC1B0:1; unsigned DC1B1:1; }; } CCP1CONbits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near struct { unsigned PCFG0:1; unsigned PCFG1:1; unsigned PCFG2:1; unsigned PCFG3:1; unsigned :2; unsigned ADCS2:1; unsigned ADFM:1; } ADCON1bits; extern volatile near unsigned extern volatile near union { struct { unsigned ADON:1; unsigned :1; unsigned GO:1; unsigned CHS0:1; unsigned CHS1:1; unsigned CHS2:1; unsigned ADCS0:1; unsigned ADCS1:1; }; struct { unsigned :2; unsigned NOT_DONE:1; }; struct { unsigned :2; unsigned DONE:1; }; struct {
char
ADCON0;
96 of 122
unsigned :2; unsigned GO_DONE:1; }; } ADCON0bits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near struct { unsigned SEN:1; unsigned RSEN:1; unsigned PEN:1; unsigned RCEN:1; unsigned ACKEN:1; unsigned ACKDT:1; unsigned ACKSTAT:1; unsigned GCEN:1; } SSPCON2bits; extern volatile near unsigned extern volatile near struct { unsigned SSPM0:1; unsigned SSPM1:1; unsigned SSPM2:1; unsigned SSPM3:1; unsigned CKP:1; unsigned SSPEN:1; unsigned SSPOV:1; unsigned WCOL:1; } SSPCON1bits; extern volatile near unsigned extern volatile near struct { unsigned BF:1; unsigned UA:1; unsigned R_W:1; unsigned S:1; unsigned P:1; unsigned D_A:1; unsigned CKE:1; unsigned SMP:1; } SSPSTATbits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near struct { unsigned T2CKPS0:1; unsigned T2CKPS1:1; unsigned TMR2ON:1; unsigned TOUTPS0:1; unsigned TOUTPS1:1; unsigned TOUTPS2:1; unsigned TOUTPS3:1; } T2CONbits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near union { struct { unsigned TMR1ON:1; unsigned TMR1CS:1; unsigned NOT_T1SYNC:1; unsigned T1OSCEN:1;
char
SSPCON1;
char
SSPSTAT;
unsigned T1CKPS0:1; unsigned T1CKPS1:1; unsigned :1; unsigned RD16:1; }; struct { unsigned :2; unsigned T1SYNC:1; }; } T1CONbits; extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near union { struct { unsigned NOT_BOR:1; unsigned NOT_POR:1; unsigned NOT_PD:1; unsigned NOT_TO:1; unsigned NOT_RI:1; unsigned :1; unsigned NOT_LWRT:1; unsigned NOT_IPEN:1; }; struct { unsigned BOR:1; unsigned POR:1; unsigned PD:1; unsigned TO:1; unsigned RI:1; unsigned :1; unsigned LWRT:1; unsigned IPEN:1; }; } RCONbits; extern volatile near unsigned extern volatile near union { struct { unsigned SWDTEN:1; }; struct { unsigned SWDTE:1; }; } WDTCONbits; extern volatile near unsigned extern volatile near struct { unsigned LVDL0:1; unsigned LVDL1:1; unsigned LVDL2:1; unsigned LVDL3:1; unsigned LVDEN:1; unsigned IRVST:1; } LVDCONbits; extern volatile near unsigned extern volatile near struct { unsigned SCS:1; } OSCCONbits; extern volatile near unsigned extern volatile near struct { unsigned T0PS0:1; unsigned T0PS1:1;
char
WDTCON;
char
LVDCON;
char
OSCCON;
char
T0CON;
97 of 122
unsigned T0PS2:1; unsigned PSA:1; unsigned T0SE:1; unsigned T0CS:1; unsigned T08BIT:1; unsigned TMR0ON:1; } T0CONbits; extern volatile near unsigned extern volatile near unsigned extern near unsigned extern near struct { unsigned C:1; unsigned DC:1; unsigned Z:1; unsigned OV:1; unsigned N:1; } STATUSbits; extern near unsigned extern near unsigned extern near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern near unsigned extern near unsigned extern near unsigned extern near unsigned extern near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern near unsigned extern near unsigned extern near unsigned extern near unsigned extern near unsigned extern near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern near unsigned extern volatile near unsigned extern volatile near union { struct { unsigned INT1F:1; unsigned INT2F:1; unsigned :1; unsigned INT1E:1; unsigned INT2E:1; unsigned :1; unsigned INT1P:1; unsigned INT2P:1; }; struct { unsigned INT1IF:1; unsigned INT2IF:1; unsigned :1; unsigned INT1IE:1;
char char char char char char char char char char char char char char char char char char char char char char char char char
FSR2; FSR2L; FSR2H; PLUSW2; PREINC2; POSTDEC2; POSTINC2; INDF2; BSR; FSR1; FSR1L; FSR1H; PLUSW1; PREINC1; POSTDEC1; POSTINC1; INDF1; W; WREG; FSR0; FSR0L; FSR0H; PLUSW0; PREINC0; POSTDEC0; POSTINC0; INDF0; INTCON3;
unsigned INT2IE:1; unsigned :1; unsigned INT1IP:1; unsigned INT2IP:1; }; } INTCON3bits; extern volatile near unsigned extern volatile near union { struct { unsigned RBIP:1; unsigned INT3P:1; unsigned T0IP:1; unsigned INTEDG3:1; unsigned INTEDG2:1; unsigned INTEDG1:1; unsigned INTEDG0:1; unsigned RBPU:1; }; struct { unsigned :2; unsigned TMR0IP:1; }; } INTCON2bits; extern volatile near unsigned extern volatile near union { struct { unsigned RBIF:1; unsigned INT0F:1; unsigned T0IF:1; unsigned RBIE:1; unsigned INT0E:1; unsigned T0IE:1; unsigned PEIE:1; unsigned GIE:1; }; struct { unsigned :1; unsigned INT0IF:1; unsigned TMR0IF:1; unsigned :1; unsigned INT0IE:1; unsigned TMR0IE:1; unsigned GIEL:1; unsigned GIEH:1; }; } INTCONbits; extern near unsigned extern near unsigned extern near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near unsigned extern volatile near struct { unsigned STKPTR0:1;
char
INTCON2;
char
INTCON;
PROD; char PRODL; char PRODH; char TABLAT; short long TBLPTR; char TBLPTRL; char TBLPTRH; char TBLPTRU; short long PC; char PCL; char PCLATH; char PCLATU; char STKPTR;
98 of 122
unsigned STKPTR1:1; unsigned STKPTR2:1; unsigned STKPTR3:1; unsigned STKPTR4:1; unsigned :1; unsigned STKUNF:1; unsigned STKFUL:1; } STKPTRbits; extern near unsigned extern near unsigned extern near unsigned extern near unsigned
/*------------------------------------------------------------------------* Some useful defines for inline assembly stuff *-------------------------------------------------------------------------*/ #define ACCESS 0 #define BANKED 1 /*------------------------------------------------------------------------* Some useful macros for inline assembly stuff *-------------------------------------------------------------------------*/ #define Nop() {_asm nop _endasm} #define ClrWdt() {_asm clrwdt _endasm} #define Sleep() {_asm sleep _endasm} #define Reset() {_asm reset _endasm} #define #define #define #define #define Rlcf(f,dest,access) Rlncf(f,dest,access) Rrcf(f,dest,access) Rrncf(f,dest,access) Swapf(f,dest,access) {_asm {_asm {_asm {_asm {_asm movlb movlb movlb movlb movlb f f f f f rlcf f,dest,access _endasm} rlncf f,dest,access _endasm} rrcf f,dest,access _endasm} rrncf f,dest,access _endasm} swapf f,dest,access _endasm }
_CONFIG5H, \ _CONFIG6L, \ _CONFIG6H, \ _CONFIG7L, \ _CONFIG7H) \ const rom unsigned char _configuration[14] = { \ 0xFF, \ _CONFIG1H, \ _CONFIG2L, \ _CONFIG2H, \ 0xFF, \ _CONFIG3H, \ _CONFIG4L, \ 0xFF, \ _CONFIG5L, \ _CONFIG5H, \ _CONFIG6L, \ _CONFIG6H, \ _CONFIG7L, \ _CONFIG7H \ } /*------------------------------------------------------------------------* CONFIG1H (0x300001) *-------------------------------------------------------------------------*/ #define _CONFIG1H_DEFAULT 0x27 #define _OSC_LP_1H 0xF8 #define _OSC_XT_1H 0xF9 #define _OSC_HS_1H 0xFA #define _OSC_RC_1H 0xFB #define _OSC_EC_1H 0xFC #define _OSC_ECIO_1H 0xFD #define _OSC_HSPLL_1H 0xFE #define _OSC_RCIO_1H 0xFF #define _OSCS_ON_1H #define _OSCS_OFF_1H 0xDF 0xFF
/*------------------------------------------------------------------------* A fairly inclusive set of registers to save for interrupts. * These are locations which are commonly used by the compiler. *-------------------------------------------------------------------------*/ #define INTSAVELOCS TBLPTR, TABLAT, PROD /*------------------------------------------------------------------------* IMPORTANT: The _CONFIG_DECL macro has been deprecated. Please utilize * the #pragma config directive. Refer to the "MPLAB C18 C * Compiler User's Guide" for more information relating to the * #pragma config directive. * * Defines for configuration words: * _CONFIG_DECL should be placed between a #pragma romdata CONFIG * and a #pragma romdata. For example: , * #pragma romdata CONFIG * _CONFIG_DECL(...) * #pragma romdata * NOTE: This macro only works when using the default linker script files * and the CONFIG section exists. *-------------------------------------------------------------------------*/ #define _CONFIG_DECL(_CONFIG1H, \ _CONFIG2L, \ _CONFIG2H, \ _CONFIG3H, \ _CONFIG4L, \ _CONFIG5L, \
/*------------------------------------------------------------------------* CONFIG2L (0x300002) *-------------------------------------------------------------------------*/ #define _CONFIG2L_DEFAULT 0x0F #define _PWRT_ON_2L 0xFE #define _PWRT_OFF_2L 0xFF #define _BOR_OFF_2L #define _BOR_ON_2L #define #define #define #define _BORV_45_2L _BORV_42_2L _BORV_27_2L _BORV_25_2L 0xFD 0xFF 0xF3 0xF7 0xFB 0xFF
/*------------------------------------------------------------------------* CONFIG2H (0x300003) *-------------------------------------------------------------------------*/ #define _CONFIG2H_DEFAULT 0x0F #define _WDT_OFF_2H 0xFE #define _WDT_ON_2H 0xFF #define _WDTPS_1_2H 0xF1
99 of 122
#define _WRT1_ON_6L #define _WRT1_OFF_6L #define _WRT2_ON_6L #define _WRT2_OFF_6L #define _WRT3_ON_6L #define _WRT3_OFF_6L
/*------------------------------------------------------------------------* CONFIG3H (0x300005) *-------------------------------------------------------------------------*/ #define _CONFIG3H_DEFAULT 0x01 #define _CCP2MUX_OFF_3H 0xFE #define _CCP2MUX_ON_3H 0xFF /*------------------------------------------------------------------------* CONFIG4L (0x300006) *-------------------------------------------------------------------------*/ #define _CONFIG4L_DEFAULT 0x85 #define _STVR_OFF_4L 0xFE #define _STVR_ON_4L 0xFF #define _LVP_OFF_4L #define _LVP_ON_4L #define _DEBUG_ON_4L #define _DEBUG_OFF_4L 0xFB 0xFF 0x7F 0xFF
/*------------------------------------------------------------------------* CONFIG6H (0x30000b) *-------------------------------------------------------------------------*/ #define _CONFIG6H_DEFAULT 0xE0 #define _WRTB_ON_6H 0xBF #define _WRTB_OFF_6H 0xFF #define _WRTC_ON_6H #define _WRTC_OFF_6H #define _WRTD_ON_6H #define _WRTD_OFF_6H 0xDF 0xFF 0x7F 0xFF
/*------------------------------------------------------------------------* CONFIG5L (0x300008) *-------------------------------------------------------------------------*/ #define _CONFIG5L_DEFAULT 0x0F #define _CP0_ON_5L 0xFE #define _CP0_OFF_5L 0xFF #define _CP1_ON_5L #define _CP1_OFF_5L #define _CP2_ON_5L #define _CP2_OFF_5L #define _CP3_ON_5L #define _CP3_OFF_5L 0xFD 0xFF 0xFB 0xFF 0xF7 0xFF
/*------------------------------------------------------------------------* CONFIG7L (0x30000c) *-------------------------------------------------------------------------*/ #define _CONFIG7L_DEFAULT 0x0F #define _EBTR0_ON_7L 0xFE #define _EBTR0_OFF_7L 0xFF #define _EBTR1_ON_7L #define _EBTR1_OFF_7L #define _EBTR2_ON_7L #define _EBTR2_OFF_7L #define _EBTR3_ON_7L #define _EBTR3_OFF_7L 0xFD 0xFF 0xFB 0xFF 0xF7 0xFF
/*------------------------------------------------------------------------* CONFIG5H (0x300009) *-------------------------------------------------------------------------*/ #define _CONFIG5H_DEFAULT 0xC0 #define _CPB_ON_5H 0xBF #define _CPB_OFF_5H 0xFF #define _CPD_ON_5H #define _CPD_OFF_5H 0x7F 0xFF
/*------------------------------------------------------------------------* CONFIG7H (0x30000d) *-------------------------------------------------------------------------*/ #define _CONFIG7H_DEFAULT 0x40 #define _EBTRB_ON_7H 0xBF #define _EBTRB_OFF_7H 0xFF #endif
/*------------------------------------------------------------------------* CONFIG6L (0x30000a) *-------------------------------------------------------------------------*/ #define _CONFIG6L_DEFAULT 0x0F #define _WRT0_ON_6L 0xFE #define _WRT0_OFF_6L 0xFF
100 of 122
/****************************************************************************** MODULE Statistics module FILE statistics.c ENTRY (PUBLIC) ROUTINE - May be called from outside this module mean_weight(unsigned char samples_used); weight_variance(unsigned char samples_used, unsigned short long int mean); INITIALISATION ROUTINES Makes use of the data array structure established in store_data header. DESCRIPTION Computes mean and variance over a sample set of size samples_used and returns values into the global variables; mean & variance. LAST UPDATE 4 June 2006 Tycho *******************************************************************************/
unsigned short int weight_buffer[]; signed char data_index; unsigned char samples_used; long int variance; unsigned short long int mean;
/* size = MAX_DATA_SAMPLES
/* 32 bit */
/* 24 bit
static long int temp_var; static unsigned char store_samples_used; /****************************************************************************** F O R W A R D D E C L A R A T I O N S In C, functions are assumed to return int unless declared otherwise. Here, the return type and the type(s) of argument(s) of all functions are declared. This "function prototyping" for ANSI compilers allows the compiler to do full type checking of function arguments and return types. The statement below declares cmdint() to be a function which does not return a value. static means that the function is not visible outside this file ("private" to this module). *******************************************************************************/ unsigned short long int mean_weight(unsigned char samples_used); long int weight_variance(unsigned short long int mean); /****************************************************************************** E N T R Y R O U T I N E D E F I N I T I O N Entry routines (public routines) are those that can be called directly by functions outside this module (file). Here there is only one entry routine, the main() function. *******************************************************************************/ /* unsigned short long int mean_weight(unsigned char samples_used); long int weight_variance(unsigned short long int mean); */
/****************************************************************************** I M P O R T S *******************************************************************************/ #include <stdlib.h> //#include "store_data.h" /* standard C header file */ /* Defines the data arrays read in this file. */
/****************************************************************************** P R I V A T E D A T A N A M E D C O N S T A N T S A N D M A C R O S ******************************************************************************/ /* Relevant constants as defined in store_data.h */ /* #define BUFFER_SIZE 100 #define FIR_WEIGHTS 21 */ #if !defined (MAX_DATA_SAMPLES) # define MAX_DATA_SAMPLES 90 # endif /****************************************************************************** P R I V A T E D A T A D E F I N E D T Y P E S A N D M O D U L E V A R I A B L E S *******************************************************************************/
SYNOPSIS unsigned short long int mean_weight(unsigned char samples_used); Function uses a looping addition to first add all samples used to calculate the last measurement then calculate mean value. PARAMETER samples_used -the number of samples used in the calculation of a weight value.
101 of 122
REMARKS Intermediate mean value is multiplied by 100 before division to retain 2 d.p. of accuracy. This must be taken into account when printing data to screen in user remote mode. ------------------------------------------------------------------------------*/
weight value as read at the start of the mean function. Done to remove possibility of a change to samples_used between call to mean_weight and weight_variance. mean - the previously calculated sample population mean. The x100 operation to give 2 d.p. resolution is taken into account before calculation. REMARKS Variance is given 2 d.p. resolution via a x100 operation.
unsigned short long int mean_weight(unsigned char samples_used) { char i,j; /* local loop and sample index */ mean=0; store_samples_used=samples_used; j = data_index - store_samples_used; to first value used in mean calc. */ if(j < 0) {j = j + MAX_DATA_SAMPLES;} condition */ for(i=0; i<samples_used; i++) { mean=mean+weight_buffer[i]; weight_data will not exceed 1024 */ j = j + 1; /* so long as samples_used does not exceed 63, if(j >= MAX_DATA_SAMPLES) revised to allow for 100 samples, */ {j = 0;} /* i.e. one second @ 100Hz. */ } mean=mean*100; /* Giving mean to 2 d.p. */ mean=(mean/store_samples_used); return mean; /* sets j pointer /* buffer overflow
------------------------------------------------------------------------------*/ long int weight_variance(unsigned short long int mean) { char i,j; variance=0; mean=mean/100; j = data_index - store_samples_used; if(j < 0) {j = j + MAX_DATA_SAMPLES;} for(i=0; i<samples_used; i++) { temp_var=(((weight_buffer[j]-mean)*(weight_buffer[j]-mean))); variance=(variance+temp_var); j = j + 1; if(j >= MAX_DATA_SAMPLES) {j = 0;} } variance=variance*100; /* giving variance to simulated 2 d.p. */ variance=(variance/store_samples_used); return variance;
/* */
/* mean is 16 bit.
SYNOPSIS long int weight_variance(unsigned char samples_used, unsigned short long int mean); Function uses a looping calculation to first calculate variance element which is then added to intermediate variance. Finally the intermediate variance is divided by samples used, giving the variance of the weight POPULATION. (as opposed to running sample set) PARAMETER store_samples_used -the number of samples used in the calculation of a
102 of 122
/****************************************************************************** MODULE Data storage header. FILE store_data.h DESCRIPTION This header file contains the arrays in which data pertaining to weighing operations is defined and stored. LAST UPDATE 1 June 2006 first code *******************************************************************************/ #if !defined(store_data) /*#define store_data*/ /* include this file only once */
100
/****************************************************************************** P U B L I C D A T A D E F I N E D T Y P E S A N D G L O B A L V A R I A B L E S *******************************************************************************/ unsigned short int AD_data_buffer[MAX_DATA_SAMPLES]; unsigned short int weight_buffer[MAX_DATA_SAMPLES]; unsigned short int filter_data[MAX_DATA_SAMPLES]; signed char data_index; /* points to most recently received data sample */ unsigned char samples_used; long int variance; unsigned short long int mean; #endif /* if !defined(store_data) */ /* 32 bit */ /* 24 bit */
103 of 122
/**************************************************************** FACTORY MODE * Author: John Yeh Truong. * Date: 10th of May, 2006. * Function Description: * Factory mode provides the options of calibrating the FOCUS scales, display statistics of recent measurements and use the scales through the same functions on the scales via the computer. Only FOCUS scale producers, technician or other appropraite personel will have access to calibration and statistics options. * ****************************************************************/
int input_voltage=1; for testing. Actual value is gathered from filtered voltage double mean = 35.3; double variance = 5.7; //test
// arbitrary value
//test
* * * * *
unsigned int weight_calibrate_array[]={0, 200, 400, 600, 800, 1000}; unsigned int grad_array[5]; // Need to specify size since don't give it assign it to be anything unsigned int voltage_offset; // Define Function Prototypes void Initialise(void); void Display_start_menu(void); void Display_calibrate(void); void Display_stats(void); void show_weight_ounces(void); void transmit_text( unsigned char *pointer, unsigned char end_character); void transmit_character(char character); int determine_fn(int index, int raw_voltage); void calc_weight(int gradient, int offset, int raw_voltage); void display_user_remote(void); void display_weight(void); void show_weight_grams(void); void count_mode(void); void rx232Isr(void); void determine_weight(void); void user_remote(void); void calculations(int weight1, int weight2, int voltage1, int voltage2, int counter); void start_program(void); int Password(void); void main(void) { start_program(); prgram }
#include <p18f452.h> #defines #include <string.h> functions #include <stdlib.h> standard Library #include <math.h> // Program Defines #define BCF 0 and clear bits #define BSF 1 #define baud_rate 25 #define cr 0x0D #define lf 0x0A #define colon 0x3A #define zero 0x30 #define null 0x00 char choice; char end_character; char *pointer; char weight_string[8]; char selection='n'; unsigned char character; char read; int weight; int zero_weight=0; int index; int gradient; int offset; int raw_voltage=100; //int offset_array[]={1,10,20,30,40}; int voltage_array[]={1,10,20,30,40,50}; int bol=0; int offset_array[5]; int input_voltage = 1; int counter;
// Next two lines are used to set // For Serial transmission // carriage return character // line feed character // colon character // 0 // null
//for testing
// for testing
void start_program(void) /**************************************************************************************** Author: John Yeh Truong. * Function description: * This function acts as a main module which combines all the function together. * start_program provides the interface for the user to choose between the three * different options: Calibrate, display statistics and user remote mode. In order * to gain accesss to calibrate and display statistics, a password is required. If * an options is selected that is not avaliable, an error message will be displayed * and the program will allow the user to select an option again. * ****************************************************************************************/
104 of 122
char error_message[]="Invalid.Try again.\r\n\n\n"; int password_flag = 0; Initialise(); serial and interupts while (1) { Display_start_menu(); // menu PIE1bits.RCIE=BCF; off interupts while (PIR1bits.RCIF==0){} // wait for key choice = RCREG; switch (choice) user input { case '1': { password_flag=Password(); if(password_flag) // { Display_calibrate(); // function read=RCREG; // to clear RCREG } break; } case '2': { password_flag=Password(); if(password_flag) // { Display_stats(); // read=RCREG; // to clear RCREG } break; } case '3': { user_remote(); user remote option break; } default: { transmit_text(error_message, null); break; } } } }
//setup
* Display_start_menu shows the start menu when serial is connected to the computer, * indicating what are the options to be selected. * ****************************************************************************************/ { char title[]="\n\n\rMAIN MENU\n\n\r"; char start[]=" \r\n\nOptions:\r\n\n"; char op1[]="1 Calibrate.\r\n"; char op2[]="2 Stats.\r\n"; char op3[]="3 User remote.\r\n"; transmit_text(title, null); transmit_text(start, null); transmit_text(op1, null); transmit_text(op2, null); transmit_text(op3, null);
Function description:
//go to
void transmit_text(unsigned char *pointer, unsigned char end_character) /**************************************************************************************** Author: John Yeh Truong. * Inputs: Pointer to start of sting, last character to be sent. This is usually null. * Function description: * transmit_text transmits a string to be displayed via serial. * ****************************************************************************************/ { unsigned char flag = 0; // flag used to indicated end of string while (flag==0) // As long as not end of string { while (TXSTAbits.TRMT==0){};// wait for transmission to finish TXREG = *pointer; // transmit next character if (*pointer == end_character) // checks if end has been reached { flag=1; // indicate end of string } else // if not end of string { pointer++; // increment pointer position } } } void transmit_character(char character) /**************************************************************************************** Author: John Yeh Truong. *
105 of 122
* transmit_character displays the input character on screen via serial. * ****************************************************************************************/ { while (TXSTAbits.TRMT==0){}; // wait for transmission to finish TXREG = character; // Send Character } void Display_calibrate(void) /**************************************************************************************** Author: John Yeh Truong. * Function description: * Display_calibrate displays information regarding calibrate. It also carries out the * calibration procedures. The user is first asked to remove all weights from scales * and press enter when done. Then the user is asked to add 200 grams and press enter. * 200 grams is continullay added until 1000 grams is reached. Voltage readings from * the strain gauage are then stored in an array where functions are used to calculate * the weight-voltage realationship between the 200 gram intervals. * ****************************************************************************************/ { char text[]="Remove any weights.\n\r"; char text2[]="Press enter when done\n\n\r"; char text3[]="Add 200 grams to the scale\n\r"; char text4[]="\n\n\rCALIBRATION SUCCESSFUL\n\n\r"; while (TXSTAbits.TRMT==0){}; // wait for transmission to finish counter = 0; while (counter<6) //size of weight_calibrate_array { if (counter == 0) // if calibrate is rn for the first time { transmit_text(text, null); // send text to serial transmit_text(text2, null); // send text2 to serial for user to press key while (PIR1bits.RCIF==0);//{;} // // wait fil_data
transmit_text(text2, null); read=RCREG; // done to clear RCIF bit for user to press key while (PIR1bits.RCIF==0){bol=1;} // wait // this line reads
} counter = 0; while (counter < 5) // size of weight_calibrate_array-1 { calculations(weight_calibrate_array[counter], weight_calibrate_array[counter+1], voltage_array[counter], voltage_array[counter+1], counter); counter++; } transmit_text(text4, null);
//voltage_array[counter] = fil_data; is the current voltage reading defined in other modules module
transmit_text(text3, null);
106 of 122
107 of 122
108 of 122
109 of 122
110 of 122
111 of 122
112 of 122
113 of 122
114 of 122
115 of 122
116 of 122
} void Display_stats(void) /**************************************************************************************** Author: Chris Currie. * Function description: * Display_stats displays on screen or the statistical information on the most recent * weight measurements. * ****************************************************************************************/ { int counter = 0; // counter used for looping print char raw_array_ascii[4]; // variable to store ascii val of raw voltage char filtered_array_ascii[4]; // variable to store ascii val of filtered voltage char variance_ascii[2]; // characters to store ascii val of variance char mean_ascii[4]; // characters to store ascii val of mean char dec_ascii[2]; // character to store ascii val of mean decimal char dec2_ascii[2]; // character to store ascii val of variance decimal int temp1; // temp val to extract values from raw array int temp2; // temp val to extract values from filter array int buffer_size = 7 ; // Size of buffer char stats1[] = "Raw Filtered \r\n"; char stats2[] = "Mean: "; char stats3[] = " Var: "; char space[] = " "; char exit_message[] = "\n\rPress any key to exit"; int raw_voltage_array[] = {20, 36, 47, 75, 128, 253, 506}; int filtered_voltage_array[] = {22, 36, 48, 76, 125, 250, 500}; unsigned int temp_mean; // temp val unsigned int dec; // holds val of numeral after decimal point for mean unsigned int temp_variance; // temp val unsigned int dec2; // holds val of numeral after decimal point for variance // for testing // for testing
transmit_text(stats1, null); while(counter <= (buffer_size - 1)) the end of the array { temp1 = raw_voltage_array[counter]; extract from array temp2 = filtered_voltage_array[counter]; array itoa(temp1, (char*)&raw_array_ascii); ascii transformation on raw voltage itoa(temp2, (char*)&filtered_array_ascii); transformation on filtered voltage transmit_text(raw_array_ascii, null); transmit_text(space, null); transmit_text(filtered_array_ascii, null); transmit_character(cr); transmit_character(lf); counter++; } // while not at // temp val to // temp val to extract from // integer to // integer to ascii
temp_mean=mean*10; // only want to one decimal places dec=temp_mean%10; // take modulus temp_variance = variance*10; // only want to one decimal places dec2 = temp_variance%10; // take modulus itoa(dec, (char*)&dec_ascii); // integer to ascii transformation of mean decimal itoa(dec2, (char*)&dec2_ascii); // integer to ascii transformation of variance decimal itoa(mean, (char*)&mean_ascii); // integer to ascii transformation of mean itoa(variance, (char*)&variance_ascii); // integer to ascii transformation of variance transmit_character(cr); transmit_character(lf); transmit_text(stats2, null); transmit_text(mean_ascii, null); transmit_character('.'); transmit_text(dec_ascii, null); transmit_text(stats3, null); transmit_text(variance_ascii, null); transmit_character('.'); transmit_text(dec2_ascii, null); transmit_text(exit_message, null); while (PIR1bits.RCIF==0){} // wait for user to press key } }
purposes
117 of 122
Function description:
Initialise function does all the configuration requirements * of the serial, serial interrupt and ports. * ****************************************************************************************/ { RCSTAbits.SPEN=BSF; // enable serial port TRISC=0b10000000; // configure pins for input/output TXSTA=0b00100100; // high speed transmission enabled SPBRG=baud_rate; // select baud rate RCSTAbits.CREN=BSF; // receive enable PIE1bits.RCIE=BSF; // enable receive interupts INTCONbits.GIE=BSF; // enable global interupts INTCONbits.PEIE=BSF; // enable peripheral interupts RCONbits.IPEN=BSF; // enable priorities on interupts IPR1bits.RCIP=BCF; // low priority interupts }
int determine_fn(int index, int raw_voltage) /**************************************************************************************** Author: John Yeh Truong. * Inputs: index which represents location of weight-function realationship components.* These being gradient and voltage offset. Second input is the voltage reading form * the scales. * Output: index, location of gradient and voltage offset in their arrays. * Function descrption: * determine_fn works out the location of the componenets of the function to be calculate the current weight on the scales. It does this by working out what * interval the current weight lies and returns their location which corresponds to
index=4; // last function components are used } else if (raw_voltage<=voltage_array[0]) // Checks if coltage reading is less than zero weight voltage { index=0; bol=1; // flag is tripped zero_weight=1; // zero weight flag tripped to indicate in outer module to display zero weight } else if ((raw_voltage<voltage_array[index+1])&&(raw_voltage>=voltage_array[index])) // Checks what voltage interval current reading is in { bol=1; // flag is tripped } else { index++; // else index increased so reading can be compared to next interval } } return index; } void calc_weight(int gradient, int offset, int raw_voltage) /**************************************************************************************** Author: John Yeh Truong. * Input: gradient of equation, voltage offset and curent reading. * Function description: * Calculates weight in grams from voltage reading. * ****************************************************************************************/ { raw_voltage=raw_voltage-offset; // calculation due to offset weight=gradient*raw_voltage/256; // calculation due to gradient // weight is divided by 256 as gradient was multiplied before to increase accuracy. } void display_user_remote(void) /**************************************************************************************** Author: John Yeh Truong. * Function description: * Displays on screen the menu for user remote mode. * ****************************************************************************************/ { char first_line[]="\n\r\nUSER REMOTE\n\n\r";
int bol=0; index=0; values selected while (bol==0) { if (raw_voltage>=voltage_array[5]) is greater than the 1000 grams voltage { bol=1; // flag is tripped
118 of 122
char instructions[]="Select grams or ounces before choosing count mode or back\n\r\n\r"; char second_line[]="Press c to count\n\r"; char third_line[]="Press b to go back"; transmit_text(first_line, null); transmit_text(instructions, null); transmit_text(second_line, null); transmit_text(third_line, null); transmit_character(lf); formatting transmit_character(lf); transmit_character(cr); } void display_weight(void) /**************************************************************************************** Author: John Yeh Truong. * Function description: * Displys on screen 'Weight:' to indicate weight on scales. * ****************************************************************************************/ { char weight_text[]="Weight:"; transmit_character(lf); transmit_character(cr); pointer=weight_text; transmit_text(pointer, colon); transmit_character(lf); // used for formatting transmit_character(cr); }
transmit_text(grams, null);
// used for
void show_weight_ounces(void) /**************************************************************************************** Author: John Yeh Truong. * Function decription: * Calculates weight in ounces and displays weight in ounces. * ****************************************************************************************/ { char g_or_o[]="\n\rSelect units\n\r"; char options[]="Press g for grams or o for ounces\n\r"; int flag=0; char int_weight[4]; char ounces[]=" ounces"; weight=weight/28.35; recalculate weight for ounces itoa(weight, (char*)&int_weight); conversion on weight transmit_text(int_weight, null); transmit_text(ounces, null); } // // integer to ascii
void show_weight_grams(void) /**************************************************************************************** Author: John Yeh Truong. * Function decription: * Displays weight in grams. * ****************************************************************************************/ { char g_or_o[]="\n\rSelect units\n\r"; char options[]="Press g for grams or o for ounces\n\r"; int flag=0; char int_weight[4]; char grams[]=" grams"; itoa(weight, (char*)&int_weight); conversion on weight transmit_text(int_weight, null); // integer to ascii
void count_mode(void) /**************************************************************************************** Author: John Yeh Truong. * Function decription: * Displays count mode menu and runs count mode. ****************************************************************************************/ { int counter=0; int bol=0; int no_of_items; char title[]="\r\nCOUNT MODE\n\n\r"; char count_mode_text[]="Enter num of items\r\n"; char go_back[]="Enter b to display weight\r\n"; char item_no_text[]="\n\n\rNum of items:\r\n"; char error_message[]="Try again\r\n"; char int_count_no[4]; storage for number of items as a string to be received char number_of_items[4]; number of items as a string to be transmitted int flag=0; int count_no; int weight_of_one_item;
// // storage for
119 of 122
int raw_no_of_items;
PIE1bits.RCIE=BCF; // disable interupts transmit_text(title, null); transmit_text(go_back, null); transmit_text(count_mode_text, null); while (PIR1bits.RCIF==0){} to press key read=RCREG; // read takes value of RCREG bol = 0 ; while (read!='b') the chracter "b" for back is not entered { read = RCREG; while (bol==0) // while flag is not tripped { read = RCREG; if ((read>=0x30)&&(read<=0x39)) entered character is a digit { transmit_character(read); digit to show number has been received bol=0; storage array number_of_items[counter]=read; counter++; // checks if // transmit the // while // wait for user
} else // if entered character is anything but a digit { transmit_text(error_message, null); counter=0; bol=0; } } if (counter==2) // max digits that can be entered are three { bol=1; number_of_items[counter]=null; } if (bol==0) { to press key } } while (PIR1bits.RCIF==0){} // wait for user
// placed into
} else if (read==cr) // if carriage return detected { number_of_items[counter]=null; place into string bol=1; } else { if (read=='b') // if user wishes to go back { bol=1; // get out of loop flag=1; // to prevent calculating number of items
// null charcter
if (flag==0) // if 1st time { no_of_items=atol(number_of_items); // ascii to integer transformation of number of items determine_weight(); // weight calculated weight_of_one_item=weight/no_of_items; flag=1; // flag set so as not to repeat this step transmit_text(item_no_text, null); } if (no_of_items==0) // if 0 is entered { raw_no_of_items=0; // then number of items is zero } else { determine_weight(); // determine weight raw_no_of_items=weight/weight_of_one_item; } count_no=(int)raw_no_of_items; // count number = rounded number itoa(count_no, (char*)&int_count_no); // integer to ascii conversion transmit_character(cr); transmit_text(int_count_no, null);
120 of 122
PIE1bits.RCIE=BSF; } }
//if voltage reading is not valid, that is if the voltage reading gives zero weight or less { weight=0; // Declares low zero_weight=0; // zero weight flag is reset
else
#pragma code low_vector=0x18 priority interupt void interrupt(void) { _asm GOTO rx232Isr _endasm } #pragma code #pragma interrupt rx232Isr
} return index; }
void rx232Isr(void) /**************************************************************************************** Author: John Yeh Truong. * Function decription: * Interrupt is driven when recieve register receives a character, that is when user * enters a character. The entered cahracters are read into the following variables. * ****************************************************************************************/ { selection=RCREG; read=RCREG; }
void user_remote(void) /**************************************************************************************** Author: John Yeh Truong. * Function decription: * user_remote runs the user remote mode, displaying the weight on scales by default. * ****************************************************************************************/ { int boolean=0; int bol=0; char units='x'; char GorO[]="\r\nPress g for grams or o of ounces\r\n"; while (bol==0) { bol=1; Initialise(); // esentially used to reset of configuration requirements. display_user_remote(); while (selection!='b') if user wishes to return in the main menu { if (selection=='c') // checks if user wants count mode { count_mode(); bol=0; } { // checks
void determine_weight(void) /**************************************************************************************** Author: John Yeh Truong. * Function decription: * Runs the appropriate functions to calculate the weight on scales. * ****************************************************************************************/ { index=0; // resets index index=determine_fn(index, raw_voltage); if (zero_weight==0) // If the voltage reading is valid { gradient=grad_array[index]; // appropriate gradient is chosen offset=offset_array[index]; // appropriate offset chosen calc_weight(gradient, offset, raw_voltage); // weigh calculated through function }
determine_weight(); while (boolean==0) { PIE1bits.RCIE=0; transmit_text(GorO, null); while (PIR1bits.RCIF==0){} // waits for user to input what units to display weight read=RCREG; if (read=='g') { units='g'; display_weight(); show_weight_grams(); transmit_character(cr);
121 of 122
boolean=1; // prevents asking user what units are desired again } else if (read=='o') { units='o'; display_weight(); show_weight_ounces(); transmit_character(cr); boolean=1; // prevents asking user what units are desired again } } PIE1bits.RCIE=1; } if (units=='g') { show_weight_grams(); transmit_character(cr); } else if (units=='o') { show_weight_ounces(); transmit_character(cr); } } selection='n'; // resets selection whic is to be used in other menus }
/**************************************************************************************** Author: Chris Currie. * Function decription: * Checks if entered string matches the password. * ****************************************************************************************/ { char unsuccessful_password[] = "\n\n\r You do not have access to this part of the program\n\n\r"; char enter_password[] = "\n\r Please enter the password \n\r"; char successful[] = "\n\n\n\r ACCESS GRANTED \n\n\n\r"; int loop_counter = 0; int counter_password = 0; char password_char; transmit_text(enter_password, null); while (loop_counter <= 2) { while (PIR1bits.RCIF==0){};
input
void calculations(int weight1, int weight2, int voltage1, int voltage2, int counter) /**************************************************************************************** Author: John Yeh Truong. * Function decription: * Calculates the gradient and voltage offset in the current weight interval and stores* those values into their corresponding arrays, sequentially so they can be retrieved * based on their location using the variable 'index'. * ****************************************************************************************/ { places gradient = ((weight2-weight1)*256)/(voltage2-voltage1); //to keep decimal
password_char = RCREG; if(password_char == '1') // checks each character is the part of the password { transmit_character('*'); counter_password++; loop_counter++; } else { transmit_character('*'); loop_counter++; } } if (counter_password == 3) { password_flag = 1; transmit_text(successful, null); } else { password_flag = 0; transmit_text(unsuccessful_password, null); } } return password_flag;
// weight is multiplied by 256 to maintain 2 decimal point resolution which improves accuracy of weight. voltage_offset = voltage1-(weight1/(gradient/256)); // divided by 256 to avoid scaling errors grad_array[counter] = gradient; // stores gradient in array offset_array[counter] = voltage_offset; // stores voltage offset in array } int Password(void)
122 of 122