1 /* FreeEMS - the open source engine management system
3 * Copyright 2008-2012 Fred Cooke
5 * This file is part of the FreeEMS project.
7 * FreeEMS software is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * FreeEMS software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/
20 * We ask that if you make any changes to this file you email them upstream to
21 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
23 * Thank you for choosing FreeEMS to run your engine!
29 * @brief Utility functions only
31 * General purpose utility functions that are used in various places throughout
32 * the code base. Functions should only be placed here if they are not strongly
33 * related to any other set of functionality.
38 #include "inc/freeEMS.h"
39 #include "inc/commsISRs.h"
40 #include "inc/utils.h"
44 /** @brief Add two unsigned shorts safely
46 * This will either return short max or the sum of the two arguments.
51 unsigned short safeAdd(unsigned short addend1
, unsigned short addend2
){
52 if((SHORTMAX
- addend1
) > addend2
){
53 return addend1
+ addend2
;
60 /** @brief Add signed short to an unsigned short safely
62 * This will either return short max, zero, or the sum of the two arguments.
67 unsigned short safeTrim(unsigned short addend1
, signed short addend2
){
70 if(addend1
> -addend2
){
71 return addend1
+ addend2
;
75 }else if(addend2
> 0){
76 if(addend2
< (SHORTMAX
- addend1
)){
77 return addend1
+ addend2
;
87 /** @brief Scale without overflow
89 * Takes a base value and a scaler where 0x8000/32768 means 100%, 0 means 0%
90 * and 0xFFFF/65535 means 200%, and returns the baseValue multiplied, in effect, by the
91 * resulting percentage figure.
97 unsigned short safeScale(unsigned short baseValue
, unsigned short dividend
, unsigned short divisor
){
98 /* Perform the scaling */
99 unsigned short scaled
= ((unsigned long)baseValue
* dividend
) / divisor
;
101 /* If the trim is greater than 100% then the trimmedPW MUST be larger */
102 /* If it's less than 100% it can't have overflowed. If it's not larger, it overflowed */
103 if((dividend
> divisor
) && (baseValue
> scaled
)){
111 /** @brief Setup tune switching
113 * Place the correct set of tables in RAM based on a boolean parameter
115 * @todo TODO change parameter style to be a pointer to a register and a mask?
117 * @param bool which set of data to enable.
119 void setupPagedRAM(unsigned char bool){
121 currentFuelRPage
= RPAGE_FUEL_ONE
;
122 currentTimeRPage
= RPAGE_TIME_ONE
;
123 currentTuneRPage
= RPAGE_TUNE_ONE
;
125 currentFuelRPage
= RPAGE_FUEL_TWO
;
126 currentTimeRPage
= RPAGE_TIME_TWO
;
127 currentTuneRPage
= RPAGE_TUNE_TWO
;
130 RPAGE
= currentTuneRPage
;
134 /** @brief Demonstrate PWM
136 * Demonstrate basic PWM module usage by setting duty to scaled ADC inputs.
139 PWMDTY0
= ATD0DR0
>> 2; // scale raw adc to a duty
140 PWMDTY1
= ATD0DR1
>> 2; // scale raw adc to a duty
141 PWMDTY2
= ATD0DR2
>> 2; // scale raw adc to a duty
142 PWMDTY3
= ATD0DR3
>> 2; // scale raw adc to a duty
143 PWMDTY4
= ATD0DR4
>> 2; // scale raw adc to a duty
144 PWMDTY5
= ATD0DR5
>> 2; // scale raw adc to a duty
145 PWMDTY6
= ATD0DR6
>> 2; // scale raw adc to a duty
146 PWMDTY7
= ATD0DR7
>> 2; // scale raw adc to a duty (user led instead at the moment, see init)
150 /** @brief Read ADCs one at a time
152 * Read ADCs into the correct bank one at a time by name.
154 * @param Arrays a pointer to an ADCBuffer struct to store ADC values in.
156 void sampleEachADC(ADCBuffer
*Arrays
){
158 Arrays
->IAT
= ATD0DR0
;
159 Arrays
->CHT
= ATD0DR1
;
160 Arrays
->TPS
= ATD0DR2
;
161 Arrays
->EGO
= ATD0DR3
;
162 Arrays
->MAP
= ATD0DR4
;
163 Arrays
->AAP
= ATD0DR5
;
164 Arrays
->BRV
= ATD0DR6
;
165 Arrays
->MAT
= ATD0DR7
;
168 Arrays
->EGO2
= ATD1DR0
;
169 Arrays
->IAP
= ATD1DR1
;
170 Arrays
->MAF
= ATD1DR2
;
171 Arrays
->SpareADC3
= ATD1DR3
;
172 Arrays
->SpareADC4
= ATD1DR4
;
173 Arrays
->SpareADC5
= ATD1DR5
;
174 Arrays
->SpareADC6
= ATD1DR6
;
175 Arrays
->SpareADC7
= ATD1DR7
;
179 /** @brief Read ADCs in a loop
181 * Read ADCs into the correct bank in a loop using pointers.
183 * @param Arrays a pointer to an ADCBuffer struct to store ADC values in.
185 void sampleLoopADC(ADCBuffer
*Arrays
){
186 // get the address of the ADC array
187 unsigned short addr
= (unsigned short)Arrays
;
191 /* (value of((address of ADCBuffers struct) + (offset to start of bank(0 or half struct length)) + (offset to particular ADC (loopcounter * 4)) + (offset to correct element(0 or 2)))) =
192 * (value of((address of ARRAY block) + (loop counter * 2))) */
194 for(loop
=0;loop
<16;loop
+= 2){
195 /* Do the first block */
196 DVUSP(addr
+ loop
) = DVUSP(ATD0_BASE
+ loop
);
198 /* Do the second block */
199 DVUSP(addr
+ 16 + loop
) = DVUSP(ATD1_BASE
+ loop
);
200 /// @todo TODO this needs to be split into two loops one for the small block and one for the big one for the future chips.
205 /* @brief Read ADCs with memcpy()
207 * Read ADCs into the correct bank using two fixed calls to memcpy()
209 * @param Arrays a pointer to an ADCBuffer struct to store ADC values in.
211 * @warning this will corrupt your comms if you use it... don't use it
212 * @bug this will corrupt your comms if you use it... don't use it
214 void sampleBlockADC(ADCBuffer *Arrays){
215 memcpy(Arrays, (void*)ATD0_BASE, 16);
216 memcpy(Arrays+16, (void*)ATD1_BASE, 16);
220 /** @brief Sleep for X milli seconds
222 * Run in a nested loop repeatedly for X milli seconds.
224 * @param ms the number of milli seconds to kill
226 void sleep(unsigned short ms
){
235 /** @brief Sleep for X micro seconds
237 * Run in a nested loop repeatedly for X micro seconds.
239 * @note Very approximate...
241 * @param us the number of micro seconds to kill
243 void sleepMicro(unsigned short us
){
252 /** @brief Simple checksum
254 * Generate a simple additive checksum for a block of data.
256 * @param block a pointer to a memory region to checksum.
257 * @param length how large the memory region to checksum is.
259 * @return a simple additive checksum.
261 unsigned char checksum(unsigned char *block
, unsigned short length
){
262 unsigned char sum
= 0;
263 while (length
-- > 0){
270 /** @brief Homebrew strcpy()
272 * strcpy() wouldn't compile for me for some reason so I wrote my own.
274 * @param dest where to copy the null terminated string to.
275 * @param source where to copy the null terminated string from.
277 * @return the length of the string copied, including the zero byte terminator.
279 unsigned short stringCopy(unsigned char* dest
, unsigned char* source
){
280 unsigned short length
= 0;
284 } while(*(source
-1) != 0);
289 * @returns a one based index of the failure point
291 * @note this will return a positive result with bad data in the last position of a maximum sized block
293 unsigned short compare(unsigned char* original
, unsigned char* toCheck
, unsigned short length
){
295 for(i
=0;i
<length
;i
++){
296 if(original
[i
] != toCheck
[i
]){
297 return i
+ 1; // zero = success