/////////////////////////////////////////////////////////////////////////////// // / // IAR Atmel AVR C/C++ Compiler V4.30F/W32 13/Mar/2008 04:52:02 / // Copyright 1996-2007 IAR Systems. All rights reserved. / // / // Source file = C:\home\kevin\pub\src\bc100\IAR\USI.c / // Command line = C:\home\kevin\pub\src\bc100\IAR\USI.c / // --cpu=tiny861 -ms -o C:\home\kevin\pub\src\bc1 / // 00\IAR\Release\Obj\ -D NDEBUG -lCN / // C:\home\kevin\pub\src\bc100\IAR\Release\List\ / // -lB C:\home\kevin\pub\src\bc100\IAR\Release\Li / // st\ --initializers_in_flash -s9 / // --no_cross_call --no_tbaa / // -DENABLE_BIT_DEFINITIONS -e -I "C:\Program / // Files\IAR Systems\Embedded Workbench / // 4.0\avr\INC\" -I "C:\Program Files\IAR / // Systems\Embedded Workbench 4.0\avr\INC\CLIB\" / // --eeprom_size 512 --misrac=5-9,11-12,14,16-17, / // 19-21,24-26,29-32,34-35,38-39,42-43,46,50, / // 52-54,56-59,61-62,64-65,68-80,83-84,87-91, / // 94-95,98-100,103-110,112-126 / // Enabled MISRA C rules = 5-9,11-12,14,16-17,19-21,24-26,29-32,34-35, / // 38-39,42-43,46,50,52-54,56-59,61-62,64-65, / // 68-80,83-84,87-91,94-95,98-100,103-110,112-126 / // Checked = 5,7-9,11-12,14,17,19-21,24,29-32,34-35,38-39, / // 42,46,50,52-54,56-59,61-62,64,68-69,71-80, / // 83-84,87-89,91,94-95,98,100,104-105,108-109, / // 112-115,118-126 / // Not checked = 6,16,25-26,43,65,70,90,99,103,106-107,110, / // 116-117 / // List file = C:\home\kevin\pub\src\bc100\IAR\Release\List\U / // SI.s90 / // / // / /////////////////////////////////////////////////////////////////////////////// NAME USI RSEG CSTACK:DATA:NOROOT(0) RSEG RSTACK:DATA:NOROOT(0) EXTERN ?need_segment_init EXTERN __eeget8_16 EXTERN __eeput8_16 PUBWEAK `?` PUBWEAK `??USI_OVF_ISR??INTVEC 16` PUBLIC SPI PUBLIC SPI_Get PUBLIC SPI_Init PUBLIC SPI_Put PUBLIC SPI_Wait PUBLIC USI_OVF_ISR PUBWEAK _A_DDRB PUBWEAK _A_PORTB PUBWEAK _A_USICR PUBWEAK _A_USIDR PUBWEAK _A_USISR PUBWEAK __?EEARH PUBWEAK __?EEARL PUBWEAK __?EECR PUBWEAK __?EEDR USI_OVF_ISR SYMBOL "USI_OVF_ISR" `??USI_OVF_ISR??INTVEC 16` SYMBOL "??INTVEC 16", USI_OVF_ISR EXTERN Time_Left EXTERN Time_Set EXTERN ADCS EXTERN BattActive EXTERN BattControl EXTERN BattData EXTERN timeval // C:\home\kevin\pub\src\bc100\IAR\USI.c // 1 /* This file has been prepared for Doxygen automatic documentation generation.*/ // 2 /*! \file ********************************************************************* // 3 * // 4 * \brief // 5 * Functions for use of the Universal Serial Interface // 6 * // 7 * Contains high level functions for initializing the USI as an SPI slave, // 8 * interrupt handling, sending and receiving single bytes. // 9 * // 10 * \par Application note: // 11 * AVR458: Charging Li-Ion Batteries with BC100 \n // 12 * AVR463: Charging NiMH Batteries with BC100 // 13 * // 14 * \par Documentation: // 15 * For comprehensive code documentation, supported compilers, compiler // 16 * settings and supported devices see readme.html // 17 * // 18 * \author // 19 * Atmel Corporation: http://www.atmel.com \n // 20 * Support email: avr@atmel.com \n // 21 * Original author: \n // 22 * // 23 * $Name$ // 24 * $Revision: 2299 $ // 25 * $RCSfile$ // 26 * $URL: http://svn.norway.atmel.com/AppsAVR8/avr458_Charging_Li-Ion_Batteries_with_BC100/tag/20070904_release_1.0/code/IAR/USI.c $ // 27 * $Date: 2007-08-23 12:55:51 +0200 (to, 23 aug 2007) $\n // 28 ******************************************************************************/ // 29 // 30 #include ASEGN ABSOLUTE:DATA:NOROOT,038H // volatile __io _A_PORTB _A_PORTB: DS 1 ASEGN ABSOLUTE:DATA:NOROOT,037H // volatile __io _A_DDRB _A_DDRB: DS 1 ASEGN ABSOLUTE:DATA:NOROOT,02fH // volatile __io _A_USIDR _A_USIDR: DS 1 ASEGN ABSOLUTE:DATA:NOROOT,02eH // volatile __io _A_USISR _A_USISR: DS 1 ASEGN ABSOLUTE:DATA:NOROOT,02dH // volatile __io _A_USICR _A_USICR: DS 1 // 31 #include // 32 // 33 #include "enums.h" // 34 #include "structs.h" // 35 // 36 #include "main.h" // 37 #include "ADC.h" // 38 #include "battery.h" // 39 #include "time.h" // 40 #include "USI.h" // 41 // 42 // 43 //****************************************************************************** // 44 // Variables // 45 //****************************************************************************** // 46 //! SPI status struct RSEG NEAR_Z:DATA:NOROOT(0) REQUIRE `?` // 47 SPI_Status_t SPI; SPI: DS 4 // 48 // 49 // 50 //****************************************************************************** // 51 //Functions // 52 //***************************************************************************** // 53 /*! \brief USI Counter Overflow Interrupt Service Routine // 54 * // 55 * When the USI counter overflows, a byte has been transferred.\n // 56 * The USIDR contents are stored and flags are updated. // 57 * // 58 * The protocol is quite simple and has three sequential states: command, // 59 * address and data. // 60 * (Keep in mind that the Master is in charge of data clocking, which means // 61 * there is a one byte "delay" from when the Slave puts something to SPI till // 62 * the Master can read it.) // 63 * // 64 * 1. If a non-zero byte is received in the command state, the ISR will // 65 * store the commands to the SPI struct (read/write, EEPROM/SRAM, number of // 66 * bytes). To signal that the command was received, 0xCC is put to the SPI bus. // 67 * If a zero byte (0x00) is received in the command state, it is simply ignored // 68 * because it is an invalid command. // 69 * // 70 * 2. When a byte is received in the address state, it is stored to the SPI // 71 * struct. To signal that the address was received, 0xBB is put to SPI bus. // 72 * // 73 * 3. In the data state, variables are read/written "from back to front" until // 74 * the byte counter reaches zero. Since the Master is in charge of the data // 75 * clocking, the Slave will go to command state before the last byte is // 76 * transferred during reading. This means that the Master should send an // 77 * invalid command when getting each byte, ie 0x00. // 78 * // 79 * If the time between two transfers is 1 second or more, the Slave // 80 * automatically reverts to command state. // 81 * // 82 * \note Battery charging is not automatically halted during SPI communication. // 83 * This means that the current charge state (current and voltage) will // 84 * remain constant during heavy and prolonged serial traffic. // 85 * // 86 * \todo Variable writing not implemented yet. // 87 * \todo EEPROM/SRAM flag doesn't really do anything with this implementation. // 88 */ // 89 #pragma vector=USI_OVF_vect RSEG CODE:CODE:NOROOT(1) // 90 __interrupt void USI_OVF_ISR(void) USI_OVF_ISR: // 91 { ST -Y, R24 ST -Y, R31 ST -Y, R30 ST -Y, R3 ST -Y, R2 ST -Y, R1 ST -Y, R0 ST -Y, R23 ST -Y, R22 ST -Y, R21 ST -Y, R20 ST -Y, R19 ST -Y, R18 ST -Y, R17 ST -Y, R16 IN R24, 0x3F // 92 // If the communication timed out, set ST_CMD as current state. // 93 if (!Time_Left(TIMER_USI)) { LDI R16, 0 RCALL Time_Left TST R16 BRNE ??USI_OVF_ISR_0 // 94 SPI.State = ST_CMD; LDI R30, LOW(SPI) LDI R31, (SPI) >> 8 LDD R16, Z+2 ANDI R16, 0x3F ORI R16, 0x40 STD Z+2, R16 // 95 } // 96 // 97 // Start communication timer. If further communication doesn't happen // 98 // within 1 second, the SPI communication state is reset to CMD. // 99 Time_Set(TIMER_USI, 0, 1, 0); ??USI_OVF_ISR_0: LDI R20, 0 LDI R17, 1 LDI R18, 0 LDI R19, 0 LDI R16, 0 RCALL Time_Set // 100 // 101 // Clear USI counter and flag completed transfer. // 102 USISR = (1<> 8 LDD R16, Z+3 ORI R16, 0x04 STD Z+3, R16 // 104 // 105 // Process incoming data. // 106 switch(SPI.State) { LDD R17, Z+2 MOV R16, R17 LSL R16 ROL R16 ROL R16 ANDI R16, 0x03 SUBI R16, 1 BREQ ??USI_OVF_ISR_1 DEC R16 BREQ ??USI_OVF_ISR_2 DEC R16 BREQ ??USI_OVF_ISR_3 RJMP ??USI_OVF_ISR_4 // 107 // A valid SPI transfer starts with a Command Byte sent by the Master. // 108 case ST_CMD: // 109 SPI.Data = USIDR; // Store the transferred byte. ??USI_OVF_ISR_1: IN R16, 0x0F ST Z, R16 // 110 // 111 // If the master sent 0, it is trying to get data. Ignore in this state. // 112 if (SPI.Data != 0) { TST R16 BRNE $+2+2 RJMP ??USI_OVF_ISR_4 // 113 // Does the master want to read or write? // 114 if (SPI.Data & 0x40) { SBRS R16, 6 RJMP ??USI_OVF_ISR_5 // 115 SPI.Read = FALSE; LDD R16, Z+3 ANDI R16, 0xFE RJMP ??USI_OVF_ISR_6 // 116 } else { // 117 SPI.Read = TRUE; ??USI_OVF_ISR_5: LDD R16, Z+3 ORI R16, 0x01 ??USI_OVF_ISR_6: STD Z+3, R16 // 118 } // 119 // 120 // From/to EEPROM or SRAM? // 121 if (SPI.Data &0x80) { LD R16, Z SBRS R16, 7 RJMP ??USI_OVF_ISR_7 // 122 SPI.EEPROM = TRUE; LDD R16, Z+3 ORI R16, 0x02 RJMP ??USI_OVF_ISR_8 // 123 } else { // 124 SPI.EEPROM = FALSE; ??USI_OVF_ISR_7: LDD R16, Z+3 ANDI R16, 0xFD ??USI_OVF_ISR_8: STD Z+3, R16 // 125 } // 126 // 127 SPI.Count = (SPI.Data & 0x3F); // Get number of bytes to receive/send. // 128 SPI.State = ST_ADDR; // The Master will send the address byte next. LD R16, Z ANDI R16, 0x3F ORI R16, 0x80 STD Z+2, R16 // 129 // 130 SPI_Put(0xCC); // Signal that command was received. LDI R16, 204 ??USI_OVF_ISR_9: RCALL SPI_Put RJMP ??USI_OVF_ISR_4 // 131 } // 132 break; // 133 // 134 // 135 case ST_ADDR: // 136 SPI.Data = USIDR; // Store the address. ??USI_OVF_ISR_2: IN R16, 0x0F ST Z, R16 // 137 SPI.Address = SPI.Data; STD Z+1, R16 // 138 SPI.State = ST_DATA; // The master will send/wait for data next. MOV R16, R17 ORI R16, 0xC0 STD Z+2, R16 // 139 // 140 SPI_Put(0xBB); // Signal that address was received. LDI R16, 187 RJMP ??USI_OVF_ISR_9 // 141 break; // 142 // 143 // 144 // Note well: this will process at least one byte, regardless of Count. // 145 case ST_DATA: // 146 if (SPI.Count-- > 0) { ??USI_OVF_ISR_3: MOV R18, R17 ANDI R17, 0xC0 MOV R16, R18 DEC R16 ANDI R16, 0x3F OR R16, R17 STD Z+2, R16 ANDI R18, 0x3F BRNE $+2+2 RJMP ??USI_OVF_ISR_10 // 147 // Write specified variable to SPI, "back to front". // 148 if (SPI.Read) { LDD R18, Z+1 LDD R17, Z+3 SBRS R17, 0 RJMP ??USI_OVF_ISR_11 // 149 switch (SPI.Address) { DEC R18 BREQ ??USI_OVF_ISR_12 DEC R18 BREQ ??USI_OVF_ISR_13 DEC R18 BREQ ??USI_OVF_ISR_14 DEC R18 BREQ ??USI_OVF_ISR_15 DEC R18 BREQ ??USI_OVF_ISR_16 RJMP ??USI_OVF_ISR_17 // 150 case ADR_ADCS: // 151 SPI_Put(*(((unsigned char*)&ADCS) + (SPI.Count))); ??USI_OVF_ISR_12: ANDI R16, 0x3F LDI R17, 0 MOVW R31:R30, R17:R16 SUBI R30, LOW((-(ADCS) & 0xFFFF)) SBCI R31, (-(ADCS) & 0xFFFF) >> 8 ??USI_OVF_ISR_18: LD R16, Z RJMP ??USI_OVF_ISR_9 // 152 break; // 153 // 154 // 155 case ADR_BATTACTIVE: // 156 SPI_Put(*((unsigned char*)&BattActive + (SPI.Count))); ??USI_OVF_ISR_13: ANDI R16, 0x3F LDI R17, 0 MOVW R31:R30, R17:R16 SUBI R30, LOW((-(BattActive) & 0xFFFF)) SBCI R31, (-(BattActive) & 0xFFFF) >> 8 RJMP ??USI_OVF_ISR_18 // 157 break; // 158 // 159 // 160 case ADR_BATTDATA: // 161 SPI_Put(*((unsigned char*)&BattData + (SPI.Count))); ??USI_OVF_ISR_14: ANDI R16, 0x3F LDI R17, 0 MOVW R31:R30, R17:R16 SUBI R30, LOW((-(BattData) & 0xFFFF)) SBCI R31, (-(BattData) & 0xFFFF) >> 8 RJMP ??USI_OVF_ISR_18 // 162 break; // 163 // 164 // 165 case ADR_BATTCTRL: // 166 SPI_Put(*((__eeprom unsigned char*)&BattControl + (SPI.Count))); ??USI_OVF_ISR_15: ANDI R16, 0x3F LDI R17, 0 LDI R20, LOW(BattControl) LDI R21, (BattControl) >> 8 ADD R20, R16 ADC R21, R17 RCALL __eeget8_16 RJMP ??USI_OVF_ISR_9 // 167 break; // 168 // 169 case ADR_TIMERS: // 170 SPI_Put(*((unsigned char*)&timeval + (SPI.Count))); ??USI_OVF_ISR_16: ANDI R16, 0x3F LDI R17, 0 MOVW R31:R30, R17:R16 SUBI R30, LOW((-(timeval) & 0xFFFF)) SBCI R31, (-(timeval) & 0xFFFF) >> 8 RJMP ??USI_OVF_ISR_18 // 171 break; // 172 // 173 // 174 default: // 175 SPI_Put(0); ??USI_OVF_ISR_17: LDI R16, 0 RJMP ??USI_OVF_ISR_9 // 176 break; // 177 } // 178 } else { // 179 // Read byte from SPI // 180 SPI.Data = USIDR; ??USI_OVF_ISR_11: IN R17, 0x0F ST Z, R17 // 181 // 182 // ******************************************** // 183 // THIS FUNCTION HAS NOT BEEN FULLY IMPLEMENTED // 184 // ******************************************** // 185 // 186 // Save byte to specified variable. // 187 switch (SPI.Address) { SUBI R18, 4 BRNE ??USI_OVF_ISR_4 // 188 case ADR_BATTCTRL: // 189 *((__eeprom unsigned char*)&BattControl + SPI.Count) = SPI.Data; MOV R18, R17 ANDI R16, 0x3F LDI R17, 0 LDI R20, LOW(BattControl) LDI R21, (BattControl) >> 8 ADD R20, R16 ADC R21, R17 MOV R16, R18 RCALL __eeput8_16 RJMP ??USI_OVF_ISR_4 // 190 break; // 191 // 192 // 193 default: // 194 break; // 195 } // 196 } // 197 // 198 // 199 } else { // 200 SPI.State = ST_CMD; ??USI_OVF_ISR_10: ANDI R16, 0x3F ORI R16, 0x40 STD Z+2, R16 // 201 } // 202 break; // 203 // 204 default: // Shouldn't end up here. (Unknown SPI-state) // 205 break; // 206 } // 207 } ??USI_OVF_ISR_4: OUT 0x3F, R24 LD R16, Y+ LD R17, Y+ LD R18, Y+ LD R19, Y+ LD R20, Y+ LD R21, Y+ LD R22, Y+ LD R23, Y+ LD R0, Y+ LD R1, Y+ LD R2, Y+ LD R3, Y+ LD R30, Y+ LD R31, Y+ LD R24, Y+ RETI REQUIRE _A_USIDR REQUIRE _A_USISR // 208 // 209 // 210 /*! \brief Initializes USI as an SPI slave // 211 * // 212 * Initializes USI as a 3-wire SPI slave using the pins specified in USI.h for // 213 * I/O and clock, and USI counter overflow interrupts enabled.\n // 214 * Also initializes the SPI status struct. // 215 * // 216 * \param spi_mode Specifies if USI should trigger on positive (0) or negative // 217 * (1) edge of clock signal // 218 * // 219 * \note Clears the stored data // 220 * // 221 * \todo Timer should reset SPI protocol on timeout // 222 */ RSEG CODE:CODE:NOROOT(1) // 223 void SPI_Init(unsigned char spi_mode) SPI_Init: // 224 { // 225 __disable_interrupt(); CLI // 226 // 227 // Configure outputs and inputs, enable pull-ups for DATAIN and CLOCK pins. // 228 USI_DIR_REG |= (1<> 8 LDI R16, 0 ST Z, R16 // 237 SPI.State = ST_CMD; // Initial SPI state: wait for command. // 238 SPI.Read = FALSE; // Doesn't matter right now. // 239 SPI.EEPROM = FALSE; // Doesn't matter right now. LDD R16, Z+3 ANDI R16, 0xFC STD Z+3, R16 // 240 SPI.Count = 0; // Doesn't matter right now. LDI R16, 64 STD Z+2, R16 // 241 SPI.Address = 0; // Doesn't matter right now. LDI R16, 0 STD Z+1, R16 // 242 SPI.XferComplete = FALSE; // We haven't even started a transfer yet. // 243 SPI.WriteCollision = FALSE; // ..And therefore a collision hasn't happened. LDD R16, Z+3 ANDI R16, 0xF3 STD Z+3, R16 // 244 // 245 __enable_interrupt(); SEI // 246 } RET REQUIRE _A_PORTB REQUIRE _A_DDRB REQUIRE _A_USICR // 247 // 248 // 249 // Put one byte on bus. Use this function like you would write to the SPDR // 250 // register in the native SPI module. Calling this function will prepare a // 251 // byte for the next transfer initiated by the master device. If a transfer // 252 // is in progress, this function will set the write collision flag and return // 253 // without altering the data registers. // 254 // // 255 // Returns 0 if a write collision occurred, 1 otherwise. // 256 /*! \brief Write a byte to SPI bus // 257 * // 258 * This function first checks if a transmission is in progress, and if so, flags // 259 * a write collision, and returns FALSE.\n // 260 * If a transmission is not in progress, the flags for write collision and // 261 * transfer complete are cleared, and the input byte is written to SPDR.\n // 262 * // 263 * \param val The byte to send. // 264 * // 265 * \retval FALSE A write collision happened. // 266 * \retval TRUE Byte written to SPDR. // 267 */ RSEG CODE:CODE:NOROOT(1) // 268 unsigned char SPI_Put(unsigned char val) SPI_Put: // 269 { // 270 // Check if transmission in progress, i.e. if USI counter doesn't equal zero. // 271 // If this fails, flag a write collision and return. // 272 if((USISR & 0x0F) != 0) { IN R17, 0x0E ANDI R17, 0x0F LDI R30, LOW(SPI) LDI R31, (SPI) >> 8 BREQ ??SPI_Put_0 // 273 SPI.WriteCollision = TRUE; LDD R16, Z+3 ORI R16, 0x08 STD Z+3, R16 // 274 return(FALSE); LDI R16, 0 RET // 275 } // 276 // 277 // Reinitialize flags. // 278 SPI.XferComplete = FALSE; // 279 SPI.WriteCollision = FALSE; ??SPI_Put_0: LDD R17, Z+3 ANDI R17, 0xF3 STD Z+3, R17 // 280 // 281 USIDR = val; // Put data in USI data register. OUT 0x0F, R16 // 282 // 283 return (TRUE); LDI R16, 1 RET REQUIRE _A_USIDR REQUIRE _A_USISR // 284 } // 285 // 286 // 287 // Get one byte from bus. This function only returns the previous stored // 288 // USIDR value. The transfer complete flag is not checked. Use this function // 289 // like you would read from the SPDR register in the native SPI module. // 290 /*! \brief Get the last byte received from SPI bus // 291 * // 292 * This function simply returns the last byte stored to the SPI status struct, // 293 * without checking if a completed transfer is flagged. // 294 * // 295 * \retval SPI.Data The last byte read from SPI. // 296 */ RSEG CODE:CODE:NOROOT(1) // 297 unsigned char SPI_Get(void) SPI_Get: // 298 { // 299 return SPI.Data; LDS R16, SPI RET // 300 } // 301 // 302 // 303 /*! \brief Wait for SPI transfer to complete // 304 * // 305 * This function waits for a transfer complete to be flagged. // 306 */ RSEG CODE:CODE:NOROOT(1) // 307 void SPI_Wait(void) SPI_Wait: // 308 { LDS R16, (SPI + 3) SBRS R16, 2 // 309 do { // Wait for transfer complete. // 310 } while (SPI.XferComplete == FALSE); ??SPI_Wait_0: RJMP ??SPI_Wait_0 // 311 } ??SPI_Wait_1: RET ASEGN ABSOLUTE:DATA:NOROOT,01cH __?EECR: ASEGN ABSOLUTE:DATA:NOROOT,01dH __?EEDR: ASEGN ABSOLUTE:DATA:NOROOT,01eH __?EEARL: ASEGN ABSOLUTE:DATA:NOROOT,01fH __?EEARH: COMMON INTVEC:CODE:ROOT(1) ORG 16 `??USI_OVF_ISR??INTVEC 16`: RJMP USI_OVF_ISR RSEG INITTAB:CODE:NOROOT(0) `?`: DW SFE(NEAR_Z) - SFB(NEAR_Z) DW SFB(NEAR_Z) DW 0 REQUIRE ?need_segment_init END // // 5 bytes in segment ABSOLUTE // 470 bytes in segment CODE // 6 bytes in segment INITTAB // 2 bytes in segment INTVEC // 4 bytes in segment NEAR_Z // // 470 bytes of CODE memory (+ 8 bytes shared) // 4 bytes of DATA memory (+ 5 bytes shared) // //Errors: none //Warnings: none