1 ///////////////////////////////////////////////////////////////////////////////
\r
3 // IAR Atmel AVR C/C++ Compiler V4.30F/W32 13/Mar/2008 04:52:02 /
\r
4 // Copyright 1996-2007 IAR Systems. All rights reserved. /
\r
6 // Source file = C:\home\kevin\pub\src\bc100\IAR\USI.c /
\r
7 // Command line = C:\home\kevin\pub\src\bc100\IAR\USI.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
31 // List file = C:\home\kevin\pub\src\bc100\IAR\Release\List\U /
\r
35 ///////////////////////////////////////////////////////////////////////////////
\r
39 RSEG CSTACK:DATA:NOROOT(0)
\r
40 RSEG RSTACK:DATA:NOROOT(0)
\r
42 EXTERN ?need_segment_init
\r
46 PUBWEAK `?<Segment init: NEAR_Z>`
\r
47 PUBWEAK `??USI_OVF_ISR??INTVEC 16`
\r
64 USI_OVF_ISR SYMBOL "USI_OVF_ISR"
\r
65 `??USI_OVF_ISR??INTVEC 16` SYMBOL "??INTVEC 16", USI_OVF_ISR
\r
75 // C:\home\kevin\pub\src\bc100\IAR\USI.c
\r
76 // 1 /* This file has been prepared for Doxygen automatic documentation generation.*/
\r
77 // 2 /*! \file *********************************************************************
\r
80 // 5 * Functions for use of the Universal Serial Interface
\r
82 // 7 * Contains high level functions for initializing the USI as an SPI slave,
\r
83 // 8 * interrupt handling, sending and receiving single bytes.
\r
85 // 10 * \par Application note:
\r
86 // 11 * AVR458: Charging Li-Ion Batteries with BC100 \n
\r
87 // 12 * AVR463: Charging NiMH Batteries with BC100
\r
89 // 14 * \par Documentation:
\r
90 // 15 * For comprehensive code documentation, supported compilers, compiler
\r
91 // 16 * settings and supported devices see readme.html
\r
94 // 19 * Atmel Corporation: http://www.atmel.com \n
\r
95 // 20 * Support email: avr@atmel.com \n
\r
96 // 21 * Original author: \n
\r
99 // 24 * $Revision: 2299 $
\r
101 // 26 * $URL: http://svn.norway.atmel.com/AppsAVR8/avr458_Charging_Li-Ion_Batteries_with_BC100/tag/20070904_release_1.0/code/IAR/USI.c $
\r
102 // 27 * $Date: 2007-08-23 12:55:51 +0200 (to, 23 aug 2007) $\n
\r
103 // 28 ******************************************************************************/
\r
105 // 30 #include <ioavr.h>
\r
107 ASEGN ABSOLUTE:DATA:NOROOT,038H
\r
108 // <unnamed> volatile __io _A_PORTB
\r
112 ASEGN ABSOLUTE:DATA:NOROOT,037H
\r
113 // <unnamed> volatile __io _A_DDRB
\r
117 ASEGN ABSOLUTE:DATA:NOROOT,02fH
\r
118 // <unnamed> volatile __io _A_USIDR
\r
122 ASEGN ABSOLUTE:DATA:NOROOT,02eH
\r
123 // <unnamed> volatile __io _A_USISR
\r
127 ASEGN ABSOLUTE:DATA:NOROOT,02dH
\r
128 // <unnamed> volatile __io _A_USICR
\r
131 // 31 #include <inavr.h>
\r
133 // 33 #include "enums.h"
\r
134 // 34 #include "structs.h"
\r
136 // 36 #include "main.h"
\r
137 // 37 #include "ADC.h"
\r
138 // 38 #include "battery.h"
\r
139 // 39 #include "time.h"
\r
140 // 40 #include "USI.h"
\r
143 // 43 //******************************************************************************
\r
145 // 45 //******************************************************************************
\r
146 // 46 //! SPI status struct
\r
148 RSEG NEAR_Z:DATA:NOROOT(0)
\r
149 REQUIRE `?<Segment init: NEAR_Z>`
\r
150 // 47 SPI_Status_t SPI;
\r
155 // 50 //******************************************************************************
\r
157 // 52 //*****************************************************************************
\r
158 // 53 /*! \brief USI Counter Overflow Interrupt Service Routine
\r
160 // 55 * When the USI counter overflows, a byte has been transferred.\n
\r
161 // 56 * The USIDR contents are stored and flags are updated.
\r
163 // 58 * The protocol is quite simple and has three sequential states: command,
\r
164 // 59 * address and data.
\r
165 // 60 * (Keep in mind that the Master is in charge of data clocking, which means
\r
166 // 61 * there is a one byte "delay" from when the Slave puts something to SPI till
\r
167 // 62 * the Master can read it.)
\r
169 // 64 * 1. If a non-zero byte is received in the command state, the ISR will
\r
170 // 65 * store the commands to the SPI struct (read/write, EEPROM/SRAM, number of
\r
171 // 66 * bytes). To signal that the command was received, 0xCC is put to the SPI bus.
\r
172 // 67 * If a zero byte (0x00) is received in the command state, it is simply ignored
\r
173 // 68 * because it is an invalid command.
\r
175 // 70 * 2. When a byte is received in the address state, it is stored to the SPI
\r
176 // 71 * struct. To signal that the address was received, 0xBB is put to SPI bus.
\r
178 // 73 * 3. In the data state, variables are read/written "from back to front" until
\r
179 // 74 * the byte counter reaches zero. Since the Master is in charge of the data
\r
180 // 75 * clocking, the Slave will go to command state before the last byte is
\r
181 // 76 * transferred during reading. This means that the Master should send an
\r
182 // 77 * invalid command when getting each byte, ie 0x00.
\r
184 // 79 * If the time between two transfers is 1 second or more, the Slave
\r
185 // 80 * automatically reverts to command state.
\r
187 // 82 * \note Battery charging is not automatically halted during SPI communication.
\r
188 // 83 * This means that the current charge state (current and voltage) will
\r
189 // 84 * remain constant during heavy and prolonged serial traffic.
\r
191 // 86 * \todo Variable writing not implemented yet.
\r
192 // 87 * \todo EEPROM/SRAM flag doesn't really do anything with this implementation.
\r
194 // 89 #pragma vector=USI_OVF_vect
\r
196 RSEG CODE:CODE:NOROOT(1)
\r
197 // 90 __interrupt void USI_OVF_ISR(void)
\r
216 // 92 // If the communication timed out, set ST_CMD as current state.
\r
217 // 93 if (!Time_Left(TIMER_USI)) {
\r
221 BRNE ??USI_OVF_ISR_0
\r
222 // 94 SPI.State = ST_CMD;
\r
224 LDI R31, (SPI) >> 8
\r
231 // 97 // Start communication timer. If further communication doesn't happen
\r
232 // 98 // within 1 second, the SPI communication state is reset to CMD.
\r
233 // 99 Time_Set(TIMER_USI, 0, 1, 0);
\r
242 // 101 // Clear USI counter and flag completed transfer.
\r
243 // 102 USISR = (1<<USIOIF);
\r
246 // 103 SPI.XferComplete = TRUE;
\r
248 LDI R31, (SPI) >> 8
\r
253 // 105 // Process incoming data.
\r
254 // 106 switch(SPI.State) {
\r
262 BREQ ??USI_OVF_ISR_1
\r
264 BREQ ??USI_OVF_ISR_2
\r
266 BREQ ??USI_OVF_ISR_3
\r
267 RJMP ??USI_OVF_ISR_4
\r
268 // 107 // A valid SPI transfer starts with a Command Byte sent by the Master.
\r
269 // 108 case ST_CMD:
\r
270 // 109 SPI.Data = USIDR; // Store the transferred byte.
\r
275 // 111 // If the master sent 0, it is trying to get data. Ignore in this state.
\r
276 // 112 if (SPI.Data != 0) {
\r
279 RJMP ??USI_OVF_ISR_4
\r
280 // 113 // Does the master want to read or write?
\r
281 // 114 if (SPI.Data & 0x40) {
\r
283 RJMP ??USI_OVF_ISR_5
\r
284 // 115 SPI.Read = FALSE;
\r
287 RJMP ??USI_OVF_ISR_6
\r
289 // 117 SPI.Read = TRUE;
\r
297 // 120 // From/to EEPROM or SRAM?
\r
298 // 121 if (SPI.Data &0x80) {
\r
301 RJMP ??USI_OVF_ISR_7
\r
302 // 122 SPI.EEPROM = TRUE;
\r
305 RJMP ??USI_OVF_ISR_8
\r
307 // 124 SPI.EEPROM = FALSE;
\r
315 // 127 SPI.Count = (SPI.Data & 0x3F); // Get number of bytes to receive/send.
\r
316 // 128 SPI.State = ST_ADDR; // The Master will send the address byte next.
\r
322 // 130 SPI_Put(0xCC); // Signal that command was received.
\r
326 RJMP ??USI_OVF_ISR_4
\r
331 // 135 case ST_ADDR:
\r
332 // 136 SPI.Data = USIDR; // Store the address.
\r
336 // 137 SPI.Address = SPI.Data;
\r
338 // 138 SPI.State = ST_DATA; // The master will send/wait for data next.
\r
343 // 140 SPI_Put(0xBB); // Signal that address was received.
\r
345 RJMP ??USI_OVF_ISR_9
\r
349 // 144 // Note well: this will process at least one byte, regardless of Count.
\r
350 // 145 case ST_DATA:
\r
351 // 146 if (SPI.Count-- > 0) {
\r
362 RJMP ??USI_OVF_ISR_10
\r
363 // 147 // Write specified variable to SPI, "back to front".
\r
364 // 148 if (SPI.Read) {
\r
368 RJMP ??USI_OVF_ISR_11
\r
369 // 149 switch (SPI.Address) {
\r
371 BREQ ??USI_OVF_ISR_12
\r
373 BREQ ??USI_OVF_ISR_13
\r
375 BREQ ??USI_OVF_ISR_14
\r
377 BREQ ??USI_OVF_ISR_15
\r
379 BREQ ??USI_OVF_ISR_16
\r
380 RJMP ??USI_OVF_ISR_17
\r
381 // 150 case ADR_ADCS:
\r
382 // 151 SPI_Put(*(((unsigned char*)&ADCS) + (SPI.Count)));
\r
386 MOVW R31:R30, R17:R16
\r
387 SUBI R30, LOW((-(ADCS) & 0xFFFF))
\r
388 SBCI R31, (-(ADCS) & 0xFFFF) >> 8
\r
391 RJMP ??USI_OVF_ISR_9
\r
395 // 155 case ADR_BATTACTIVE:
\r
396 // 156 SPI_Put(*((unsigned char*)&BattActive + (SPI.Count)));
\r
400 MOVW R31:R30, R17:R16
\r
401 SUBI R30, LOW((-(BattActive) & 0xFFFF))
\r
402 SBCI R31, (-(BattActive) & 0xFFFF) >> 8
\r
403 RJMP ??USI_OVF_ISR_18
\r
407 // 160 case ADR_BATTDATA:
\r
408 // 161 SPI_Put(*((unsigned char*)&BattData + (SPI.Count)));
\r
412 MOVW R31:R30, R17:R16
\r
413 SUBI R30, LOW((-(BattData) & 0xFFFF))
\r
414 SBCI R31, (-(BattData) & 0xFFFF) >> 8
\r
415 RJMP ??USI_OVF_ISR_18
\r
419 // 165 case ADR_BATTCTRL:
\r
420 // 166 SPI_Put(*((__eeprom unsigned char*)&BattControl + (SPI.Count)));
\r
424 LDI R20, LOW(BattControl)
\r
425 LDI R21, (BattControl) >> 8
\r
429 RJMP ??USI_OVF_ISR_9
\r
432 // 169 case ADR_TIMERS:
\r
433 // 170 SPI_Put(*((unsigned char*)&timeval + (SPI.Count)));
\r
437 MOVW R31:R30, R17:R16
\r
438 SUBI R30, LOW((-(timeval) & 0xFFFF))
\r
439 SBCI R31, (-(timeval) & 0xFFFF) >> 8
\r
440 RJMP ??USI_OVF_ISR_18
\r
448 RJMP ??USI_OVF_ISR_9
\r
452 // 179 // Read byte from SPI
\r
453 // 180 SPI.Data = USIDR;
\r
458 // 182 // ********************************************
\r
459 // 183 // THIS FUNCTION HAS NOT BEEN FULLY IMPLEMENTED
\r
460 // 184 // ********************************************
\r
462 // 186 // Save byte to specified variable.
\r
463 // 187 switch (SPI.Address) {
\r
465 BRNE ??USI_OVF_ISR_4
\r
466 // 188 case ADR_BATTCTRL:
\r
467 // 189 *((__eeprom unsigned char*)&BattControl + SPI.Count) = SPI.Data;
\r
471 LDI R20, LOW(BattControl)
\r
472 LDI R21, (BattControl) >> 8
\r
477 RJMP ??USI_OVF_ISR_4
\r
488 // 200 SPI.State = ST_CMD;
\r
496 // 204 default: // Shouldn't end up here. (Unknown SPI-state)
\r
522 // 210 /*! \brief Initializes USI as an SPI slave
\r
524 // 212 * Initializes USI as a 3-wire SPI slave using the pins specified in USI.h for
\r
525 // 213 * I/O and clock, and USI counter overflow interrupts enabled.\n
\r
526 // 214 * Also initializes the SPI status struct.
\r
528 // 216 * \param spi_mode Specifies if USI should trigger on positive (0) or negative
\r
529 // 217 * (1) edge of clock signal
\r
531 // 219 * \note Clears the stored data
\r
533 // 221 * \todo Timer should reset SPI protocol on timeout
\r
536 RSEG CODE:CODE:NOROOT(1)
\r
537 // 223 void SPI_Init(unsigned char spi_mode)
\r
540 // 225 __disable_interrupt();
\r
543 // 227 // Configure outputs and inputs, enable pull-ups for DATAIN and CLOCK pins.
\r
544 // 228 USI_DIR_REG |= (1<<USI_DATAOUT_PIN);
\r
546 // 229 USI_DIR_REG &= ~((1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN));
\r
550 // 230 USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN);
\r
555 // 232 // Configure USI to 3-wire slave mode with overflow interrupt
\r
556 // 233 USICR = ( (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1) | (spi_mode<<USICS0) );
\r
562 // 235 // Initialize the SPI struct
\r
563 // 236 SPI.Data = 0; // Clear data.
\r
565 LDI R31, (SPI) >> 8
\r
568 // 237 SPI.State = ST_CMD; // Initial SPI state: wait for command.
\r
569 // 238 SPI.Read = FALSE; // Doesn't matter right now.
\r
570 // 239 SPI.EEPROM = FALSE; // Doesn't matter right now.
\r
574 // 240 SPI.Count = 0; // Doesn't matter right now.
\r
577 // 241 SPI.Address = 0; // Doesn't matter right now.
\r
580 // 242 SPI.XferComplete = FALSE; // We haven't even started a transfer yet.
\r
581 // 243 SPI.WriteCollision = FALSE; // ..And therefore a collision hasn't happened.
\r
586 // 245 __enable_interrupt();
\r
595 // 249 // Put one byte on bus. Use this function like you would write to the SPDR
\r
596 // 250 // register in the native SPI module. Calling this function will prepare a
\r
597 // 251 // byte for the next transfer initiated by the master device. If a transfer
\r
598 // 252 // is in progress, this function will set the write collision flag and return
\r
599 // 253 // without altering the data registers.
\r
601 // 255 // Returns 0 if a write collision occurred, 1 otherwise.
\r
602 // 256 /*! \brief Write a byte to SPI bus
\r
604 // 258 * This function first checks if a transmission is in progress, and if so, flags
\r
605 // 259 * a write collision, and returns FALSE.\n
\r
606 // 260 * If a transmission is not in progress, the flags for write collision and
\r
607 // 261 * transfer complete are cleared, and the input byte is written to SPDR.\n
\r
609 // 263 * \param val The byte to send.
\r
611 // 265 * \retval FALSE A write collision happened.
\r
612 // 266 * \retval TRUE Byte written to SPDR.
\r
615 RSEG CODE:CODE:NOROOT(1)
\r
616 // 268 unsigned char SPI_Put(unsigned char val)
\r
619 // 270 // Check if transmission in progress, i.e. if USI counter doesn't equal zero.
\r
620 // 271 // If this fails, flag a write collision and return.
\r
621 // 272 if((USISR & 0x0F) != 0) {
\r
625 LDI R31, (SPI) >> 8
\r
627 // 273 SPI.WriteCollision = TRUE;
\r
631 // 274 return(FALSE);
\r
636 // 277 // Reinitialize flags.
\r
637 // 278 SPI.XferComplete = FALSE;
\r
638 // 279 SPI.WriteCollision = FALSE;
\r
644 // 281 USIDR = val; // Put data in USI data register.
\r
647 // 283 return (TRUE);
\r
655 // 287 // Get one byte from bus. This function only returns the previous stored
\r
656 // 288 // USIDR value. The transfer complete flag is not checked. Use this function
\r
657 // 289 // like you would read from the SPDR register in the native SPI module.
\r
658 // 290 /*! \brief Get the last byte received from SPI bus
\r
660 // 292 * This function simply returns the last byte stored to the SPI status struct,
\r
661 // 293 * without checking if a completed transfer is flagged.
\r
663 // 295 * \retval SPI.Data The last byte read from SPI.
\r
666 RSEG CODE:CODE:NOROOT(1)
\r
667 // 297 unsigned char SPI_Get(void)
\r
670 // 299 return SPI.Data;
\r
676 // 303 /*! \brief Wait for SPI transfer to complete
\r
678 // 305 * This function waits for a transfer complete to be flagged.
\r
681 RSEG CODE:CODE:NOROOT(1)
\r
682 // 307 void SPI_Wait(void)
\r
687 // 309 do { // Wait for transfer complete.
\r
688 // 310 } while (SPI.XferComplete == FALSE);
\r
695 ASEGN ABSOLUTE:DATA:NOROOT,01cH
\r
698 ASEGN ABSOLUTE:DATA:NOROOT,01dH
\r
701 ASEGN ABSOLUTE:DATA:NOROOT,01eH
\r
704 ASEGN ABSOLUTE:DATA:NOROOT,01fH
\r
707 COMMON INTVEC:CODE:ROOT(1)
\r
709 `??USI_OVF_ISR??INTVEC 16`:
\r
712 RSEG INITTAB:CODE:NOROOT(0)
\r
713 `?<Segment init: NEAR_Z>`:
\r
714 DW SFE(NEAR_Z) - SFB(NEAR_Z)
\r
717 REQUIRE ?need_segment_init
\r
721 // 5 bytes in segment ABSOLUTE
\r
722 // 470 bytes in segment CODE
\r
723 // 6 bytes in segment INITTAB
\r
724 // 2 bytes in segment INTVEC
\r
725 // 4 bytes in segment NEAR_Z
\r
727 // 470 bytes of CODE memory (+ 8 bytes shared)
\r
728 // 4 bytes of DATA memory (+ 5 bytes shared)
\r