Initial import
[avr_bc100.git] / BaseTinyFirmware / IAR / Release / List / ADC.s90
1 ///////////////////////////////////////////////////////////////////////////////\r
2 //                                                                            /\r
3 // IAR Atmel AVR C/C++ Compiler V4.30F/W32              13/Mar/2008  04:49:35 /\r
4 // Copyright 1996-2007 IAR Systems. All rights reserved.                      /\r
5 //                                                                            /\r
6 //    Source file           =  C:\home\kevin\pub\src\bc100\IAR\ADC.c          /\r
7 //    Command line          =  C:\home\kevin\pub\src\bc100\IAR\ADC.c          /\r
8 //                             --cpu=tiny861 -ms -o C:\home\kevin\pub\src\bc1 /\r
9 //                             00\IAR\Release\Obj\ -D NDEBUG -lCN             /\r
10 //                             C:\home\kevin\pub\src\bc100\IAR\Release\List\  /\r
11 //                             -lB C:\home\kevin\pub\src\bc100\IAR\Release\Li /\r
12 //                             st\ --initializers_in_flash -s9                /\r
13 //                             --no_cross_call --no_tbaa                      /\r
14 //                             -DENABLE_BIT_DEFINITIONS -e -I "C:\Program     /\r
15 //                             Files\IAR Systems\Embedded Workbench           /\r
16 //                             4.0\avr\INC\" -I "C:\Program Files\IAR         /\r
17 //                             Systems\Embedded Workbench 4.0\avr\INC\CLIB\"  /\r
18 //                             --eeprom_size 512 --misrac=5-9,11-12,14,16-17, /\r
19 //                             19-21,24-26,29-32,34-35,38-39,42-43,46,50,     /\r
20 //                             52-54,56-59,61-62,64-65,68-80,83-84,87-91,     /\r
21 //                             94-95,98-100,103-110,112-126                   /\r
22 //    Enabled MISRA C rules =  5-9,11-12,14,16-17,19-21,24-26,29-32,34-35,    /\r
23 //                             38-39,42-43,46,50,52-54,56-59,61-62,64-65,     /\r
24 //                             68-80,83-84,87-91,94-95,98-100,103-110,112-126 /\r
25 //      Checked             =  5,7-9,11-12,14,17,19-21,24,29-32,34-35,38-39,  /\r
26 //                             42,46,50,52-54,56-59,61-62,64,68-69,71-80,     /\r
27 //                             83-84,87-89,91,94-95,98,100,104-105,108-109,   /\r
28 //                             112-115,118-126                                /\r
29 //      Not checked         =  6,16,25-26,43,65,70,90,99,103,106-107,110,     /\r
30 //                             116-117                                        /\r
31 //    List file             =  C:\home\kevin\pub\src\bc100\IAR\Release\List\A /\r
32 //                             DC.s90                                         /\r
33 //                                                                            /\r
34 //                                                                            /\r
35 ///////////////////////////////////////////////////////////////////////////////\r
36 \r
37         NAME `ADC`\r
38 \r
39         RSEG CSTACK:DATA:NOROOT(0)\r
40         RSEG RSTACK:DATA:NOROOT(0)\r
41 \r
42         EXTERN ?Register_R4_is_cg_reg\r
43         EXTERN ?SS_DIVMOD_L02\r
44         EXTERN ?S_MUL_L02\r
45         EXTERN ?US_SHR_L02\r
46         EXTERN ?need_segment_init\r
47         EXTERN __eeget8_16\r
48 \r
49         PUBWEAK `?<Segment init: NEAR_Z>`\r
50         PUBWEAK `??ADC_ISR??INTVEC 22`\r
51         PUBLIC ADCS\r
52         PUBLIC ADC_ISR\r
53         PUBLIC ADC_Init\r
54         PUBLIC ADC_Wait\r
55         PUBLIC ScaleI\r
56         PUBLIC ScaleU\r
57         PUBLIC VBAT_RANGE\r
58         PUBWEAK _A_ADC\r
59         PUBWEAK _A_ADCSRA\r
60         PUBWEAK _A_ADCSRB\r
61         PUBWEAK _A_ADMUX\r
62         PUBWEAK _A_DDRA\r
63         PUBWEAK _A_PORTA\r
64         PUBWEAK __?EEARH\r
65         PUBWEAK __?EEARL\r
66         PUBWEAK __?EECR\r
67         PUBWEAK __?EEDR\r
68 \r
69 ADC_ISR             SYMBOL "ADC_ISR"\r
70 `??ADC_ISR??INTVEC 22` SYMBOL "??INTVEC 22", ADC_ISR\r
71 \r
72 // C:\home\kevin\pub\src\bc100\IAR\ADC.c\r
73 //    1 /* This file has been prepared for Doxygen automatic documentation generation.*/\r
74 //    2 /*! \file *********************************************************************\r
75 //    3  *\r
76 //    4  * \brief\r
77 //    5  *      Functions for use of ADC\r
78 //    6  *\r
79 //    7  *      Contains high level functions for initializing the ADC, interrupt\r
80 //    8  *      handling, and treatment of samples.\n\r
81 //    9  *      The ADC is set to free running mode and uses an external reference\r
82 //   10  *      voltage.\n\r
83 //   11  *      To make all sampling take at least 25 clock cycles the ADC is stopped\r
84 //   12  *      and restarted by the ISR.\r
85 //   13  *\r
86 //   14  * \par Application note:\r
87 //   15  *      AVR458: Charging Li-Ion Batteries with BC100 \n\r
88 //   16  *      AVR463: Charging NiMH Batteries with BC100\r
89 //   17  *\r
90 //   18  * \par Documentation:\r
91 //   19  *      For comprehensive code documentation, supported compilers, compiler\r
92 //   20  *      settings and supported devices see readme.html\r
93 //   21  *\r
94 //   22  * \author\r
95 //   23  *      Atmel Corporation: http://www.atmel.com \n\r
96 //   24  *      Support email: avr@atmel.com \n\r
97 //   25  *      Original author: \n\r
98 //   26  *\r
99 //   27  * $Name$\r
100 //   28  * $Revision: 2299 $\r
101 //   29  * $RCSfile$\r
102 //   30  * $URL: http://svn.norway.atmel.com/AppsAVR8/avr458_Charging_Li-Ion_Batteries_with_BC100/tag/20070904_release_1.0/code/IAR/ADC.c $\r
103 //   31  * $Date: 2007-08-23 12:55:51 +0200 (to, 23 aug 2007) $\n\r
104 //   32  ******************************************************************************/\r
105 //   33 \r
106 //   34 #include <ioavr.h>\r
107 \r
108         ASEGN ABSOLUTE:DATA:NOROOT,03bH\r
109 // <unnamed> volatile __io _A_PORTA\r
110 _A_PORTA:\r
111         DS 1\r
112 \r
113         ASEGN ABSOLUTE:DATA:NOROOT,03aH\r
114 // <unnamed> volatile __io _A_DDRA\r
115 _A_DDRA:\r
116         DS 1\r
117 \r
118         ASEGN ABSOLUTE:DATA:NOROOT,027H\r
119 // <unnamed> volatile __io _A_ADMUX\r
120 _A_ADMUX:\r
121         DS 1\r
122 \r
123         ASEGN ABSOLUTE:DATA:NOROOT,026H\r
124 // <unnamed> volatile __io _A_ADCSRA\r
125 _A_ADCSRA:\r
126         DS 1\r
127 \r
128         ASEGN ABSOLUTE:DATA:NOROOT,024H\r
129 // <unnamed> volatile __io _A_ADC\r
130 _A_ADC:\r
131         DS 2\r
132 \r
133         ASEGN ABSOLUTE:DATA:NOROOT,023H\r
134 // <unnamed> volatile __io _A_ADCSRB\r
135 _A_ADCSRB:\r
136         DS 1\r
137 //   35 #include <inavr.h>\r
138 //   36 \r
139 //   37 #include "structs.h"\r
140 //   38 \r
141 //   39 #include "main.h"\r
142 //   40 #include "ADC.h"\r
143 //   41 \r
144 //   42 \r
145 //   43 //******************************************************************************\r
146 //   44 // Variables\r
147 //   45 //******************************************************************************\r
148 //   46 // ADC status struct.\r
149 //   47 //! \brief Holds sampled data and ADC-status\r
150 //   48 ADC_Status_t ADCS;\r
151 //   49 \r
152 //   50 \r
153 //   51 /*! \brief Indicates maximum battery voltage.\r
154 //   52  *\r
155 //   53  * This variable is stored in EEPROM and indicates how much the battery voltage\r
156 //   54  * is downscaled by HW before it is sampled. The amount of downscaling depends\r
157 //   55  * on the maximum battery voltage, and is necessary to avoid saturation of the\r
158 //   56  * ADC (reference voltage is 2.5 V).\r
159 //   57  *\r
160 //   58  * \note Used by the ADC ISR when calling ScaleU() and ScaleI().\r
161 //   59  *\r
162 //   60  * \note Defaults to 1, which means 10 V max battery voltage.\r
163 //   61  *\r
164 //   62  * \note Table of settings:\r
165 //   63  * <pre>\r
166 //   64  * VBAT_RANGE | Max battery voltage | Jumper setting\r
167 //   65  *         0  |             5V      |        1/2\r
168 //   66  *         1  |            10V      |        1/4\r
169 //   67  *         2  |            20V      |        1/8\r
170 //   68  *         3  |            30V      |       1/12\r
171 //   69  *         4  |            40V      |       1/16\r
172 //   70  * </pre>\r
173 //   71  */\r
174 //   72 // Maximum battery voltage (affects scaling of samples).\r
175 \r
176         RSEG EEPROM_I:XDATA:NOROOT(0)\r
177 //   73 __eeprom unsigned char VBAT_RANGE = 1;\r
178 VBAT_RANGE:\r
179         DB 1\r
180 //   74 \r
181 //   75 \r
182 //   76 //******************************************************************************\r
183 //   77 // Functions\r
184 //   78 //******************************************************************************\r
185 //   79 /*! \brief Interrupt Service routine for ADC.\r
186 //   80  *\r
187 //   81  * This ISR stores the sampled values in the ADC status-struct, then\r
188 //   82  * updates the ADC MUX to the next channel in the scanning-sequence.\n\r
189 //   83  * Once the sequence is completed, ADCS.Flag is set and unless\r
190 //   84  * ADCS.Halt has been set, the sequence starts over. Otherwise, the ADC \r
191 //   85  * is disabled.\n\r
192 //   86  * If the mains voltage is below minimum, ADCS.Mains gets set to FALSE.\r
193 //   87  *\r
194 //   88  * \note Table of scanning sequence:\r
195 //   89  * <pre>\r
196 //   90  * Seq |    MUX |  pos I/P |  neg I/P | gain | measure | signed\r
197 //   91  * ----+--------+----------+----------+------+---------+-------\r
198 //   92  *  01 | 000001 | ADC1/PA1 |      n/a |   1x |     NTC |     no\r
199 //   93  *  02 | 000010 | ADC2/PA2 |      n/a |   1x |     RID |     no\r
200 //   94  *  03 | 000011 | ADC3/PA4 |      n/a |   1x |    VIN- |     no\r
201 //   95  *  04 | 000100 | ADC4/PA5 |      n/a |   1x |    VIN+ |     no\r
202 //   96  *  05 | 000101 | ADC5/PA6 |      n/a |   1x |   VBAT- |     no\r
203 //   97  *  06 | 000110 | ADC6/PA7 |      n/a |   1x |   VBAT+ |     no\r
204 //   98  *  07 | 010010 | ADC4/PA5 | ADC3/PA4 |  20x |     IIN |     no\r
205 //   99  *  08 | 010111 | ADC6/PA7 | ADC5/PA6 |  20x |    IBAT |    yes\r
206 //  100  * </pre>\r
207 //  101  *\r
208 //  102  * \todo IIN (#7 in sequence) is never used.\r
209 //  103  *\r
210 //  104  * \todo Signed is never set. Signed measurements of IBAT will halve the\r
211 //  105  * measuring sensitivity, and is therefore not favourable. At the moment,\r
212 //  106  * great currents (f.ex. if something happens with the battery) will be\r
213 //  107  * interpreted as negative, which might cause unfavourable behaviour during\r
214 //  108  * charging (depending on what PWM behaviour is defined), f.ex.\r
215 //  109  * ConstantCurrent() will keep increasing the PWM output. This results in an\r
216 //  110  * PWM controller error being flagged and the program going into\r
217 //  111  * error-state and eventually reinitializing.\r
218 //  112  */\r
219 //  113 #pragma vector=ADC_vect\r
220 \r
221         RSEG CODE:CODE:NOROOT(1)\r
222 //  114 __interrupt void ADC_ISR(void)\r
223 ADC_ISR:\r
224 //  115 {\r
225         ST      -Y, R4\r
226         ST      -Y, R27\r
227         ST      -Y, R26\r
228         ST      -Y, R25\r
229         ST      -Y, R24\r
230         ST      -Y, R31\r
231         ST      -Y, R30\r
232         ST      -Y, R3\r
233         ST      -Y, R2\r
234         ST      -Y, R1\r
235         ST      -Y, R0\r
236         ST      -Y, R23\r
237         ST      -Y, R22\r
238         ST      -Y, R21\r
239         ST      -Y, R20\r
240         ST      -Y, R19\r
241         ST      -Y, R18\r
242         ST      -Y, R17\r
243         ST      -Y, R16\r
244         IN      R4, 0x3F\r
245         REQUIRE ?Register_R4_is_cg_reg\r
246 //  116         static unsigned char avgIndex = 0;\r
247 //  117         unsigned char i, Next, Signed;\r
248 //  118         signed int  temp = 0;\r
249         LDI     R24, 0\r
250         LDI     R25, 0\r
251 //  119         \r
252 //  120         Signed = FALSE;  // Presume next conversion is unipolar.\r
253 //  121         ADCSRA &= ~(1<<ADEN);  // Stop conversion before handling. This makes all\r
254         CBI     0x06, 0x07\r
255 //  122           // conversions take at least 25 ADCCLK. (It is restarted later)\r
256 //  123         \r
257 //  124         // Handle the conversion, depending on what channel it is from, then\r
258 //  125         // switch to the next channel in the sequence.\r
259 //  126         switch (ADCS.MUX){\r
260         LDI     R26, LOW(ADCS)\r
261         LDI     R27, (ADCS) >> 8\r
262         LD      R16, X\r
263         ANDI    R16, 0x1F\r
264         DEC     R16\r
265         BREQ    ??ADC_ISR_0\r
266         DEC     R16\r
267         BREQ    ??ADC_ISR_1\r
268         DEC     R16\r
269         BREQ    ??ADC_ISR_2\r
270         SUBI    R16, 2\r
271         BREQ    ??ADC_ISR_3\r
272         SUBI    R16, 18\r
273         BREQ    ??ADC_ISR_4\r
274         RJMP    ??ADC_ISR_5\r
275 //  127                 // MUX = 0b000001 => ADC1 (PA1) = NTC\r
276 //  128                 case 0x01:\r
277 //  129                         ADCS.rawNTC = ADC;\r
278 ??ADC_ISR_0:\r
279         IN      R16, 0x04\r
280         IN      R17, 0x05\r
281         MOVW    R31:R30, R27:R26\r
282         STD     Z+4, R16\r
283         STD     Z+5, R17\r
284 //  130                         Next=0x02;\r
285         LDI     R16, 2\r
286         RJMP    ??ADC_ISR_6\r
287 //  131                 break;\r
288 //  132 \r
289 //  133                 \r
290 //  134                 // MUX = 0b000010 => ADC2 (PA2) = RID\r
291 //  135                 case 0x02:\r
292 //  136                         ADCS.rawRID = ADC;\r
293 ??ADC_ISR_1:\r
294         IN      R16, 0x04\r
295         IN      R17, 0x05\r
296         MOVW    R31:R30, R27:R26\r
297         STD     Z+2, R16\r
298         STD     Z+3, R17\r
299 //  137                         Next=0x03;\r
300         LDI     R16, 3\r
301         RJMP    ??ADC_ISR_6\r
302 //  138                 break;\r
303 //  139 \r
304 //  140                 \r
305 //  141                 // MUX = 0b000011 => ADC3 (PA4) = VIN-\r
306 //  142                 case 0x03:\r
307 //  143                         // Supply voltage is always divided by 16.\r
308 //  144                         ADCS.VIN = ScaleU(4, (unsigned int)ADC);  // Cast because ADC is short.\r
309 ??ADC_ISR_2:\r
310         IN      R18, 0x04\r
311         IN      R19, 0x05\r
312         LDI     R16, 4\r
313         RCALL   ScaleU\r
314         MOVW    R31:R30, R27:R26\r
315         STD     Z+8, R16\r
316         STD     Z+9, R17\r
317 //  145                         \r
318 //  146                         // Is mains failing?\r
319 //  147                         if (ADCS.VIN < VIN_MIN) {\r
320         CPI     R16, 48\r
321         SBCI    R17, 17\r
322         LD      R16, X\r
323         BRCC    ??ADC_ISR_7\r
324 //  148                                 ADCS.Mains = FALSE;\r
325         ANDI    R16, 0xBF\r
326         RJMP    ??ADC_ISR_8\r
327 //  149                         } else {\r
328 //  150                                 ADCS.Mains = TRUE;\r
329 ??ADC_ISR_7:\r
330         ORI     R16, 0x40\r
331 ??ADC_ISR_8:\r
332         ST      X, R16\r
333 //  151                         }\r
334 //  152                         \r
335 //  153                         Next=0x05;\r
336         LDI     R16, 5\r
337         RJMP    ??ADC_ISR_6\r
338 //  154                 break;\r
339 //  155 \r
340 //  156                 \r
341 //  157                 // MUX = 0b000101 => ADC5 (PA6) = VBAT-\r
342 //  158                 case 0x05:\r
343 //  159                         ADCS.rawVBAT = ADC;\r
344 ??ADC_ISR_3:\r
345         IN      R16, 0x04\r
346         IN      R17, 0x05\r
347         MOVW    R31:R30, R27:R26\r
348         STD     Z+6, R16\r
349         STD     Z+7, R17\r
350 //  160                         \r
351 //  161                         // Scale voltage according to jumper setting.\r
352 //  162                         ADCS.VBAT = ScaleU(VBAT_RANGE, (unsigned int)ADC); // ADC is a short.\r
353         IN      R18, 0x04\r
354         IN      R19, 0x05\r
355         LDI     R20, LOW(VBAT_RANGE)\r
356         LDI     R21, (VBAT_RANGE) >> 8\r
357         RCALL   __eeget8_16\r
358         RCALL   ScaleU\r
359         MOVW    R31:R30, R27:R26\r
360         STD     Z+10, R16\r
361         STD     Z+11, R17\r
362 //  163                         Next=0x17;\r
363         LDI     R16, 23\r
364         RJMP    ??ADC_ISR_6\r
365 //  164 //                      Signed = TRUE;  // Next conversion is bipolar. Halves sensitivity!\r
366 //  165                 break;\r
367 //  166 \r
368 //  167 \r
369 //  168                 case 0x17:  // MUX = 0b010111 => 20 x [ADC6(PA7) - ADC5(PA6)] = IBAT\r
370 //  169                         // If bipolar, from -512 to 0, to 511:\r
371 //  170                         // 0x200 ... 0x3ff, 0x000, 0x001 ... 0x1FF\r
372 //  171                 \r
373 //  172                         // Scale sample according to jumper setting, handle negative numbers.\r
374 //  173                         if (ADC > 511) {\r
375 ??ADC_ISR_4:\r
376         IN      R16, 0x04\r
377         IN      R17, 0x05\r
378         CPI     R17, 2\r
379         BRCS    ??ADC_ISR_9\r
380 //  174                                 ADCS.IBAT = -(signed int)ScaleI(VBAT_RANGE,\r
381 //  175                                              (1024 - (ADC-ADCS.ADC5_G20_OS)));\r
382         LDI     R16, 0\r
383         LDI     R17, 4\r
384         IN      R18, 0x04\r
385         IN      R19, 0x05\r
386         SUB     R16, R18\r
387         SBC     R17, R19\r
388         MOVW    R31:R30, R27:R26\r
389         LDD     R18, Z+1\r
390         SWAP    R18\r
391         ANDI    R18, 0x0F\r
392         LDI     R19, 0\r
393         ADD     R18, R16\r
394         ADC     R19, R17\r
395         LDI     R20, LOW(VBAT_RANGE)\r
396         LDI     R21, (VBAT_RANGE) >> 8\r
397         RCALL   __eeget8_16\r
398         RCALL   ScaleI\r
399         NEG     R17\r
400         NEG     R16\r
401         SBCI    R17, 0\r
402 ??ADC_ISR_10:\r
403         MOVW    R31:R30, R27:R26\r
404         STD     Z+12, R16\r
405         STD     Z+13, R17\r
406         RJMP    ??ADC_ISR_11\r
407 //  176                         } else if (ADC > 0) {\r
408 ??ADC_ISR_9:\r
409         IN      R16, 0x04\r
410         IN      R17, 0x05\r
411         OR      R16, R17\r
412         BREQ    ??ADC_ISR_12\r
413 //  177                                 ADCS.IBAT = ScaleI(VBAT_RANGE, (ADC-ADCS.ADC5_G20_OS));\r
414         IN      R18, 0x04\r
415         IN      R19, 0x05\r
416         MOVW    R31:R30, R27:R26\r
417         LDD     R16, Z+1\r
418         SWAP    R16\r
419         ANDI    R16, 0x0F\r
420         SUB     R18, R16\r
421         SBCI    R19, 0\r
422         LDI     R20, LOW(VBAT_RANGE)\r
423         LDI     R21, (VBAT_RANGE) >> 8\r
424         RCALL   __eeget8_16\r
425         RCALL   ScaleI\r
426         RJMP    ??ADC_ISR_10\r
427 //  178                         } else {\r
428 //  179                                 ADCS.IBAT = 0;\r
429 ??ADC_ISR_12:\r
430         MOVW    R31:R30, R27:R26\r
431         STD     Z+12, R24\r
432         STD     Z+13, R24\r
433 //  180                         }\r
434 //  181 \r
435 //  182                         // Insert sample of battery current into the averaging-array\r
436 //  183                         // (overwriting the oldest sample), then recalculate and store the\r
437 //  184                         // average. This is the last conversion in the sequence, so\r
438 //  185                         // flag a complete ADC-cycle and restart sequence.\r
439 //  186                         ADCS.discIBAT[(avgIndex++ & 0x03)] = ADCS.IBAT;\r
440 ??ADC_ISR_11:\r
441         LDD     R18, Z+12\r
442         LDD     R19, Z+13\r
443         LDD     R16, Z+24\r
444         ANDI    R16, 0x03\r
445         LSL     R16\r
446         ADD     R30, R16\r
447         ADC     R31, R24\r
448         STD     Z+14, R18\r
449         STD     Z+15, R19\r
450         MOVW    R31:R30, R27:R26\r
451         LDD     R16, Z+24\r
452         INC     R16\r
453         STD     Z+24, R16\r
454 //  187                         for (i = 0; i < 4 ; i++) {\r
455         ADIW    R31:R30, 14\r
456         LDI     R16, 4\r
457 //  188                                 temp += ADCS.discIBAT[i];\r
458 ??ADC_ISR_13:\r
459         LD      R18, Z+\r
460         LD      R19, Z+\r
461         ADD     R24, R18\r
462         ADC     R25, R19\r
463 //  189                         }\r
464         DEC     R16\r
465         BRNE    ??ADC_ISR_13\r
466 //  190                         \r
467 //  191                         ADCS.avgIBAT = (temp / 4);\r
468         MOVW    R17:R16, R25:R24\r
469         LDI     R20, 4\r
470         LDI     R21, 0\r
471         RCALL   ?SS_DIVMOD_L02\r
472         MOVW    R31:R30, R27:R26\r
473         STD     Z+22, R16\r
474         STD     Z+23, R17\r
475 //  192                         \r
476 //  193                         ADCS.Flag = TRUE;\r
477         LD      R16, X\r
478         ORI     R16, 0x20\r
479         ST      X, R16\r
480 //  194                         Next=0x01;\r
481 //  195                         Signed = FALSE;  // This is the only bipolar conversion.\r
482 //  196                 break;\r
483 //  197 \r
484 //  198                 \r
485 //  199                 default:  // Should not happen. (Invalid MUX-channel)\r
486 //  200                         Next=0x01;  // Start at the beginning of sequence.\r
487 ??ADC_ISR_5:\r
488         LDI     R16, 1\r
489 //  201                 break;\r
490 //  202         }\r
491 //  203         \r
492 //  204         // Update MUX to next channel in sequence, set a bipolar conversion if\r
493 //  205         // this has been flagged.\r
494 //  206         ADCS.MUX = Next;                    \r
495 ??ADC_ISR_6:\r
496         LD      R17, X\r
497         ANDI    R17, 0xE0\r
498         ANDI    R16, 0x1F\r
499         OR      R16, R17\r
500         ST      X, R16\r
501 //  207         ADMUX = (1<<REFS0) + ADCS.MUX;      \r
502         ANDI    R16, 0x1F\r
503         SUBI    R16, 192\r
504         OUT     0x07, R16\r
505 //  208 \r
506 //  209         if (Signed)     {\r
507 //  210           ADCSRB |= (1<<BIN);               \r
508 //  211         } else {\r
509 //  212           ADCSRB &= ~(1<<BIN);              \r
510         CBI     0x03, 0x07\r
511 //  213         }\r
512 //  214 \r
513 //  215         // Re-enable the ADC unless a halt has been flagged and a conversion\r
514 //  216         // cycle has completed.\r
515 //  217         if (!((ADCS.Halt) && (ADCS.Flag))) {\r
516         LD      R16, X\r
517         ANDI    R16, 0xA0\r
518         CPI     R16, 160\r
519         BREQ    ??ADC_ISR_14\r
520 //  218           ADCSRA |= (1<<ADEN)|(1<<ADSC);    \r
521         IN      R16, 0x06\r
522         ORI     R16, 0xC0\r
523         OUT     0x06, R16\r
524 //  219         }\r
525 //  220 }\r
526 ??ADC_ISR_14:\r
527         OUT     0x3F, R4\r
528         LD      R16, Y+\r
529         LD      R17, Y+\r
530         LD      R18, Y+\r
531         LD      R19, Y+\r
532         LD      R20, Y+\r
533         LD      R21, Y+\r
534         LD      R22, Y+\r
535         LD      R23, Y+\r
536         LD      R0, Y+\r
537         LD      R1, Y+\r
538         LD      R2, Y+\r
539         LD      R3, Y+\r
540         LD      R30, Y+\r
541         LD      R31, Y+\r
542         LD      R24, Y+\r
543         LD      R25, Y+\r
544         LD      R26, Y+\r
545         LD      R27, Y+\r
546         LD      R4, Y+\r
547         RETI\r
548         REQUIRE _A_ADMUX\r
549         REQUIRE _A_ADCSRA\r
550         REQUIRE _A_ADC\r
551         REQUIRE _A_ADCSRB\r
552 \r
553         RSEG NEAR_Z:DATA:NOROOT(0)\r
554         REQUIRE `?<Segment init: NEAR_Z>`\r
555 ADCS:\r
556         DS 24\r
557         DS 1\r
558 //  221 \r
559 //  222 \r
560 //  223 /*! \brief Scales sample to represent "actual voltage" in mV.\r
561 //  224  *\r
562 //  225  * This function returns the actual sampled voltage, scaled according\r
563 //  226  * to the jumper settings.\r
564 //  227  *\r
565 //  228  * \param setting Indicates what downscaling was used.\r
566 //  229  * \param data The sampled value.\r
567 //  230  *\r
568 //  231  * \note Table for setting-parameter:\n\r
569 //  232  * <pre>\r
570 //  233  * Presume VREF = 2.5V and Gain = 1x.\r
571 //  234  * => Resolution @ 1/1 = 2.5V / 1024 = 2.4414 mV/LSB\r
572 //  235  * setting | source |   R1 | R2/(R1+R2) | UADC(LSB) | U(MAX)\r
573 //  236  * --------+--------+------+------------+-----------+-------\r
574 //  237  *     N/A |        |    - |       -    |   2.441mV |  2.50V\r
575 //  238  *       0 |   VBAT |  10k |     1/2    |   4.883mV |  5.00V\r
576 //  239  *       1 |   VBAT |  30k |     1/4    |   9.766mV |  9.99V\r
577 //  240  *       2 |   VBAT |  70k |     1/8    |   19.53mV | 19.98V\r
578 //  241  *       3 |   VBAT | 110k |    1/12    |   29.30mV | 29.97V\r
579 //  242  *       4 |   VBAT | 150k |    1/16    |   39.06mV | 39.96V\r
580 //  243  *       4 |    VIN | 150k |    1/16    |   39.06mV | 39.96V\r
581 //  244  * </pre>\r
582 //  245  */\r
583 \r
584         RSEG CODE:CODE:NOROOT(1)\r
585 //  246 unsigned int ScaleU(unsigned char setting, unsigned int data)\r
586 ScaleU:\r
587 //  247 {\r
588         MOV     R2, R26\r
589         MOVW    R23:R22, R25:R24\r
590         MOV     R26, R16\r
591         MOVW    R25:R24, R19:R18\r
592 //  248         // Temporary variable needed.\r
593 //  249         unsigned int scaled = 0;\r
594 //  250 \r
595 //  251         // Jumper setting 3: mV/LSB = 29.30 ~= 29 + 1/4 + 1/16\r
596 //  252         if (setting == 3)       {\r
597         CPI     R16, 3\r
598         MOVW    R21:R20, R19:R18\r
599         BRNE    ??ScaleU_0\r
600 //  253                 scaled = 29 * data;\r
601 //  254                 scaled += (data >> 2);\r
602 //  255                 scaled += (data >> 4);\r
603         LDI     R16, 29\r
604         LDI     R17, 0\r
605         RCALL   ?S_MUL_L02\r
606         LSR     R19\r
607         ROR     R18\r
608         LSR     R19\r
609         ROR     R18\r
610         ADD     R18, R0\r
611         ADC     R19, R1\r
612         LSR     R25\r
613         ROR     R24\r
614         LSR     R25\r
615         ROR     R24\r
616         LSR     R25\r
617         ROR     R24\r
618         LSR     R25\r
619         ROR     R24\r
620         ADD     R24, R18\r
621         ADC     R25, R19\r
622         RJMP    ??ScaleU_1\r
623 //  256         } else {\r
624 //  257                 // Jumper setting 4: mV/LSB = 39.06 ~= 39 + 1/16\r
625 //  258                 scaled = 39 * data;\r
626 //  259                 scaled += (data >> 4);\r
627 ??ScaleU_0:\r
628         LDI     R16, 39\r
629         LDI     R17, 0\r
630         RCALL   ?S_MUL_L02\r
631         LSR     R25\r
632         ROR     R24\r
633         LSR     R25\r
634         ROR     R24\r
635         LSR     R25\r
636         ROR     R24\r
637         LSR     R25\r
638         ROR     R24\r
639         ADD     R24, R0\r
640         ADC     R25, R1\r
641 //  260                 \r
642 //  261                 if (setting <3) {\r
643         CPI     R26, 3\r
644         BRCC    ??ScaleU_1\r
645 //  262                         // Jumper setting 0: mV/LSB = 4.883 = 39.06 / 8\r
646 //  263                         //                1: mV/LSB = 9.766 = 39.06 / 4\r
647 //  264                         //                2: mV/LSB = 19.53 = 39.06 / 2\r
648 //  265                         scaled = (scaled >> (3-setting));\r
649         MOVW    R17:R16, R25:R24\r
650         LDI     R20, 3\r
651         SUB     R20, R26\r
652         RCALL   ?US_SHR_L02\r
653         MOVW    R25:R24, R17:R16\r
654 //  266                 }\r
655 //  267         }\r
656 //  268 \r
657 //  269         return(scaled);\r
658 ??ScaleU_1:\r
659         MOVW    R17:R16, R25:R24\r
660         MOVW    R25:R24, R23:R22\r
661         MOV     R26, R2\r
662         RET\r
663 //  270 }\r
664 //  271 \r
665 //  272 \r
666 //  273 /*! \brief Scales sample to represent "actual current" in mA.\r
667 //  274  *\r
668 //  275  * This function returns the actual sampled current, scaled according\r
669 //  276  * to the jumper settings.\r
670 //  277  *\r
671 //  278  * \param setting Indicates what downscaling was used.\r
672 //  279  * \param data The sampled value.\r
673 //  280  *\r
674 //  281  * \note Table for setting-parameter:\n\r
675 //  282  * <pre>\r
676 //  283  * Presume VREF = 2.5V and Gain = 1x or 20x.\r
677 //  284  * => Resolution(U) @ (1/1 and 20x) = 2.5V / (GAIN x 1024) = 0.1221 mV/LSB\r
678 //  285  * => Resolution(I) = Resolution(U) / Rshunt = Resolution(U) / 0.07\r
679 //  286  * Setting |   R1 | R2/(R1+R2) |   U(LSB) |   I(LSB) | I(MAX) | Gain\r
680 //  287  * --------+------+------------+----------+----------+--------+-----\r
681 //  288  *     N/A |    - |       -    | 0.1221mV |  1.744mA |  1.78A |  20x\r
682 //  289  *       0 |  10k |     1/2    | 0.2442mV |  3.489mA |  3.57A |  20x\r
683 //  290  *       1 |  30k |     1/4    | 0.4884mV |  6.978mA |  7.14A |  20x\r
684 //  291  *       2 |  70k |     1/8    | 0.9768mV | 13.955mA |  14.3A |  20x\r
685 //  292  *       3 | 110k |    1/12    | 1.4652mV | 20.931mA |  21.4A |  20x\r
686 //  293  *       4 | 150k |    1/16    | 1.9536mV | 27.909mA |  28.5A |  20x\r
687 //  294  *       5 |  10k |     1/2    | 2.4414mV | 34.877mA |  35.7A |   1x\r
688 //  295  * </pre>\r
689 //  296  */\r
690 \r
691         RSEG CODE:CODE:NOROOT(1)\r
692 //  297 unsigned int ScaleI(unsigned char setting, unsigned int data)\r
693 ScaleI:\r
694 //  298 {\r
695         MOV     R2, R26\r
696         MOVW    R23:R22, R25:R24\r
697         MOV     R26, R16\r
698         MOVW    R25:R24, R19:R18\r
699 //  299         // Temporary variable needed.\r
700 //  300         unsigned int  scaled = 0;\r
701 //  301         \r
702 //  302         // Jumper setting 3: mA/LSB = 20.931mA ~= 21 - 1/16 + 1/128\r
703 //  303         if (setting == 3) {\r
704         CPI     R16, 3\r
705         MOVW    R21:R20, R19:R18\r
706         BRNE    ??ScaleI_0\r
707 //  304                 scaled = 21 * data;\r
708 //  305                 scaled -= (data >> 4);\r
709 //  306                 scaled += (data >> 7);\r
710         LDI     R16, 21\r
711         LDI     R17, 0\r
712         RCALL   ?S_MUL_L02\r
713         LSR     R19\r
714         ROR     R18\r
715         LSR     R19\r
716         ROR     R18\r
717         LSR     R19\r
718         ROR     R18\r
719         LSR     R19\r
720         ROR     R18\r
721         SUB     R16, R18\r
722         SBC     R17, R19\r
723         LSL     R24\r
724         MOV     R24, R25\r
725         ROL     R24\r
726         LDI     R25, 0\r
727         ROL     R25\r
728         ADD     R24, R16\r
729         ADC     R25, R17\r
730         RJMP    ??ScaleI_1\r
731 //  307         }       else    { // Jumper setting 4: mA/LSB = 27.909mA ~= 28 - 1/8 + 1/32\r
732 //  308                 scaled = 28 * data;\r
733 //  309                 scaled -= (data >> 3);\r
734 //  310                 scaled += (data >> 5);\r
735 ??ScaleI_0:\r
736         LDI     R16, 28\r
737         LDI     R17, 0\r
738         RCALL   ?S_MUL_L02\r
739         MOVW    R19:R18, R17:R16\r
740         MOVW    R17:R16, R25:R24\r
741         LSR     R17\r
742         ROR     R16\r
743         LSR     R17\r
744         ROR     R16\r
745         LSR     R17\r
746         ROR     R16\r
747         SUB     R18, R16\r
748         SBC     R19, R17\r
749         MOVW    R17:R16, R25:R24\r
750         LDI     R20, 5\r
751         RCALL   ?US_SHR_L02\r
752         MOVW    R25:R24, R17:R16\r
753         ADD     R24, R18\r
754         ADC     R25, R19\r
755 //  311                 \r
756 //  312                 if (setting <3) {\r
757         CPI     R26, 3\r
758         BRCC    ??ScaleI_1\r
759 //  313                         // Jumper setting 0: mA/LSB = 3.489mA = 27.909 / 8\r
760 //  314                         //                1: mA/LSB = 6.978mA = 27.909 / 4\r
761 //  315                         //                2: mA/LSB = 13.955mA = 27.909 / 2\r
762 //  316                         scaled = (scaled >> (3-setting));\r
763         MOVW    R17:R16, R25:R24\r
764         LDI     R20, 3\r
765         SUB     R20, R26\r
766         RCALL   ?US_SHR_L02\r
767         MOVW    R25:R24, R17:R16\r
768 //  317                 }\r
769 //  318         }\r
770 //  319         \r
771 //  320         return(scaled);\r
772 ??ScaleI_1:\r
773         MOVW    R17:R16, R25:R24\r
774         MOVW    R25:R24, R23:R22\r
775         MOV     R26, R2\r
776         RET\r
777 //  321 }\r
778 //  322 \r
779 //  323 \r
780 //  324 /*! \brief Waits for two full cycles of ADC-conversions to occur.\r
781 //  325  *\r
782 //  326  * This function clears the cycle complete-flag, then waits for it to be set\r
783 //  327  * again. This is then repeated once before the function exits.\r
784 //  328  * \r
785 //  329  */\r
786 \r
787         RSEG CODE:CODE:NOROOT(1)\r
788 //  330 void ADC_Wait(void)\r
789 ADC_Wait:\r
790 ??ADC_Wait_0:\r
791 //  331 {\r
792 //  332         // Clear ADC flag and wait for cycle to complete.\r
793 //  333         ADCS.Flag = FALSE;              \r
794 //  334         do {\r
795 //  335         } while (ADCS.Flag == FALSE);      \r
796         RJMP    ??ADC_Wait_0\r
797 //  336         \r
798 //  337         // Repeat, so we are sure the data beong to the same cycle.\r
799 //  338         ADCS.Flag = FALSE;              \r
800 //  339         do {\r
801 //  340         } while (ADCS.Flag == FALSE);      \r
802 //  341 }\r
803 //  342 \r
804 //  343 \r
805 //  344 /*! \brief Initializes ADC and input pins.\r
806 //  345  *\r
807 //  346  * This function initializes the ADC to free running mode, sampling from\r
808 //  347  * PA1/2/4/5/6/7, and using an external reference voltage (PA3).\n\r
809 //  348  * It also measures and stores calibration data for offset.\r
810 //  349  *\r
811 //  350  * \todo Odd offset measurement for ADC3_G20_OS? It is never used anyway.\r
812 //  351  *\r
813 //  352  * \note Table of MUX settings for offset measurement:\r
814 //  353  * <pre>\r
815 //  354  *    Ch | Pin |    Gain |    MUX\r
816 //  355  * ------+-----+---------+-------\r
817 //  356  *  ADC1 | PA1 |     20x | 001101\r
818 //  357  *  ADC3 | PA4 |     20x | 010001\r
819 //  358  *  ADC5 | PA6 |     20x | 010110\r
820 //  359  *  ADC9 | PB6 |     20x | 011011\r
821 //  360  *  ADC0 | PA0 | 20x/32x | 111000\r
822 //  361  *  ADC0 | PA0 |   1x/8x | 111001\r
823 //  362  *  ADC1 | PA1 | 20x/32x | 111010\r
824 //  363  *  ADC2 | PA2 | 20x/32x | 111011\r
825 //  364  *  ADC4 | PA5 | 20x/32x | 111100\r
826 //  365  *  ADC5 | PA6 | 20x/32x | 111101\r
827 //  366  *  ADC6 | PA7 | 20x/32x | 111110\r
828 //  367  * </pre>\r
829 //  368  */\r
830 \r
831         RSEG CODE:CODE:NOROOT(1)\r
832 //  369 void ADC_Init(void)\r
833 ADC_Init:\r
834 //  370 {\r
835 //  371         unsigned char i;\r
836 //  372 \r
837 //  373         __disable_interrupt();\r
838         CLI\r
839 //  374 \r
840 //  375         ADCS.Halt = FALSE; // Enable consecutive runs of ADC.\r
841         LDI     R30, LOW(ADCS)\r
842         LDI     R31, (ADCS) >> 8\r
843         LD      R16, Z\r
844         ANDI    R16, 0x7F\r
845         ST      Z, R16\r
846 //  376 \r
847 //  377         // Configure ADC pins (inputs and disabled pull-ups).\r
848 //  378         DDRA &= ~((1<<PA1)|(1<<PA2)|(1<<PA4)|(1<<PA5)|(1<<PA6)|(1<<PA7));\r
849         IN      R16, 0x1A\r
850         ANDI    R16, 0x09\r
851         OUT     0x1A, R16\r
852 //  379         PORTA &= ~((1<<PA1)|(1<<PA2)|(1<<PA4)|(1<<PA5)|(1<<PA6)|(1<<PA7));\r
853         IN      R16, 0x1B\r
854         ANDI    R16, 0x09\r
855         OUT     0x1B, R16\r
856 //  380 \r
857 //  381         // Set ADC3 as reference, and MUX to measure the same pin.\r
858 //  382         ADMUX = (1<<REFS0) | (1<<MUX0) | (1<<MUX1);\r
859         LDI     R16, 67\r
860         OUT     0x07, R16\r
861 //  383         \r
862 //  384         ADCSRB = 0;\r
863         LDI     R16, 0\r
864         OUT     0x03, R16\r
865 //  385 \r
866 //  386         // Start conversion, no interrupt (disable ADC-ISR).\r
867 //  387         ADCSRA = (1<<ADEN) | (1<<ADSC) | ADC_PRESCALER; \r
868         LDI     R16, 199\r
869         OUT     0x06, R16\r
870 //  388 \r
871 //  389         do { // Wait for conversion to finish.\r
872 //  390         } while (!(ADCSRA & (1<<ADIF)));\r
873 ??ADC_Init_0:\r
874         SBIS    0x06, 0x04\r
875         RJMP    ??ADC_Init_0\r
876 //  391 \r
877 //  392         ADCSRA |= (1<<ADIF);  // Clear ADC interrupt flag manually.\r
878         SBI     0x06, 0x04\r
879 //  393 \r
880 //  394         ADCS.ADC3_G20_OS = ADC;  // Save the sampled offset.\r
881         LDD     R18, Z+1\r
882         ANDI    R18, 0xF0\r
883         IN      R16, 0x04\r
884         IN      R17, 0x05\r
885         ANDI    R16, 0x0F\r
886         OR      R16, R18\r
887         STD     Z+1, R16\r
888 //  395 \r
889 //  396         ADMUX = (1<<REFS0) | 0x16;  // ADC5/ADC5 (external ref.), 20x\r
890         LDI     R17, 86\r
891         OUT     0x07, R17\r
892 //  397         \r
893 //  398         // Start conversion, no interrupt. ADC_PRESCALER is defined in ADC.h.\r
894 //  399         ADCSRA = (1<<ADEN) | (1<<ADSC) | ADC_PRESCALER; \r
895         LDI     R17, 199\r
896         OUT     0x06, R17\r
897 //  400 \r
898 //  401         do { // Wait for conversion to finish.\r
899 //  402         } while (!(ADCSRA & (1<<ADIF)));\r
900 ??ADC_Init_1:\r
901         SBIS    0x06, 0x04\r
902         RJMP    ??ADC_Init_1\r
903 //  403 \r
904 //  404         ADCSRA |= (1<<ADIF);  // Clear ADC interrupt flag.\r
905         SBI     0x06, 0x04\r
906 //  405 \r
907 //  406         ADCS.ADC5_G20_OS = ADC;  // Save the sampled offset.\r
908         ANDI    R16, 0x0F\r
909         IN      R18, 0x04\r
910         IN      R19, 0x05\r
911         SWAP    R18\r
912         ANDI    R18, 0xF0\r
913         OR      R18, R16\r
914         STD     Z+1, R18\r
915 //  407 \r
916 //  408         // Reset the ADC-cycle.\r
917 //  409         ADCS.Flag = FALSE;      \r
918 //  410         ADCS.MUX = 0x01;                    \r
919         LD      R16, Z\r
920         ANDI    R16, 0xC0\r
921         ORI     R16, 0x01\r
922         ST      Z, R16\r
923 //  411         ADMUX = (1<<REFS0) | ADCS.MUX;      \r
924         LDI     R16, 65\r
925         OUT     0x07, R16\r
926 //  412 \r
927 //  413         // Clear averaged battery current and the discrete readings.\r
928 //  414         ADCS.avgIBAT = 0;\r
929         LDI     R16, 0\r
930         STD     Z+22, R16\r
931         STD     Z+23, R16\r
932 //  415         \r
933 //  416         for (i = 0; i < 4; i++) {\r
934         ADIW    R31:R30, 14\r
935         LDI     R18, 4\r
936 //  417                 ADCS.discIBAT[i] = 0;             \r
937 ??ADC_Init_2:\r
938         ST      Z+, R16\r
939         ST      Z+, R16\r
940 //  418         }\r
941         DEC     R18\r
942         BRNE    ??ADC_Init_2\r
943 //  419         \r
944 //  420         // Re-enable the ADC and ISR.\r
945 //  421         ADCSRA=(1<<ADEN)|(1<<ADSC)|(1<<ADIE)|ADC_PRESCALER;\r
946         LDI     R16, 207\r
947         OUT     0x06, R16\r
948 //  422         \r
949 //  423         __enable_interrupt();\r
950         SEI\r
951 //  424 \r
952 //  425         // Get a complete cycle of data before returning.\r
953 //  426         ADC_Wait();\r
954 ??ADC_Init_3:\r
955         RJMP    ??ADC_Init_3\r
956         REQUIRE _A_PORTA\r
957         REQUIRE _A_DDRA\r
958         REQUIRE _A_ADMUX\r
959         REQUIRE _A_ADCSRA\r
960         REQUIRE _A_ADC\r
961         REQUIRE _A_ADCSRB\r
962 //  427 }\r
963 \r
964         ASEGN ABSOLUTE:DATA:NOROOT,01cH\r
965 __?EECR:\r
966 \r
967         ASEGN ABSOLUTE:DATA:NOROOT,01dH\r
968 __?EEDR:\r
969 \r
970         ASEGN ABSOLUTE:DATA:NOROOT,01eH\r
971 __?EEARL:\r
972 \r
973         ASEGN ABSOLUTE:DATA:NOROOT,01fH\r
974 __?EEARH:\r
975 \r
976         COMMON INTVEC:CODE:ROOT(1)\r
977         ORG 22\r
978 `??ADC_ISR??INTVEC 22`:\r
979         RJMP    ADC_ISR\r
980 \r
981         RSEG INITTAB:CODE:NOROOT(0)\r
982 `?<Segment init: NEAR_Z>`:\r
983         DW      SFE(NEAR_Z) - SFB(NEAR_Z)\r
984         DW      SFB(NEAR_Z)\r
985         DW      0\r
986         REQUIRE ?need_segment_init\r
987 \r
988         END\r
989 // \r
990 //   7 bytes in segment ABSOLUTE\r
991 // 746 bytes in segment CODE\r
992 //   1 byte  in segment EEPROM_I\r
993 //   6 bytes in segment INITTAB\r
994 //   2 bytes in segment INTVEC\r
995 //  25 bytes in segment NEAR_Z\r
996 // \r
997 // 746 bytes of CODE  memory (+ 8 bytes shared)\r
998 //  25 bytes of DATA  memory (+ 7 bytes shared)\r
999 //   1 byte  of XDATA memory\r
1000 //\r
1001 //Errors: none\r
1002 //Warnings: none\r