# USI.c File Reference

## Detailed Description

Functions for use of the Universal Serial Interface.

Contains high level functions for initializing the USI as an SPI slave, interrupt handling, sending and receiving single bytes.

Application note:
  AVR458: Charging Li-Ion Batteries with BC100
  AVR463: Charging NiMH Batteries with BC100

Documentation:
  For comprehensive code documentation, supported compilers, compiler settings and supported devices see readme.html

Author:
  Atmel Corporation: http://www.atmel.com
  Support email: avr@atmel.com
  Original author:

$Name$

Revision
  2299

$RCSfile$

URL
  http://revisor.norway.atmel.com/AppsAVR8/avr458_Charging_Li-Ion_Batteries_with_BC100/trunk/code/IAR/USI.c

Date
  2007-08-23 12:55:51 +0200 (to, 23 aug 2007)

Definition in file USI.c.

`#include <ioavr.h>`
`#include <inavr.h>`
`#include "enums.h"`
`#include "structs.h"`
`#include "main.h"`
`#include "ADC.h"`
`#include "battery.h"`
`#include "time.h"`
`#include "USI.h"`

Include dependency graph for USI.c:

Go to the source code of this file.

## Functions
unsigned char | SPI_Get (void)
| Get the last byte received from SPI bus.
void | SPI_Init (unsigned char spi_mode)
| Initializes USI as an SPI slave.
unsigned char | SPI_Put (unsigned char val)
| Write a byte to SPI bus.
void | SPI_Wait (void)
| Wait for SPI transfer to complete.
__interrupt void | USI_OVF_ISR (void)
| USI Counter Overflow Interrupt Service Routine.
## Variables
SPI_Status_t | SPI
| SPI status struct.

## Function Documentation

unsigned char SPI_Get | ( | void |  | ) |

Get the last byte received from SPI bus.

This function simply returns the last byte stored to the SPI status struct, without checking if a completed transfer is flagged.

Return values:

  | SPI.Data | The last byte read from SPI.

Definition at line 297 of file USI.c.

References SPI_Status_struct::Data.

    00298 {
    00299         return SPI.Data;
    00300 }

void SPI_Init | ( | unsigned char | spi_mode | ) |

Initializes USI as an SPI slave.

Initializes USI as a 3-wire SPI slave using the pins specified in USI.h for I/O and clock, and USI counter overflow interrupts enabled.
Also initializes the SPI status struct.

Parameters:

  | spi_mode | Specifies if USI should trigger on positive (0) or negative (1) edge of clock signal

Note:
  Clears the stored data

Todo:
  Timer should reset SPI protocol on timeout

Definition at line 223 of file USI.c.

References SPI_Status_struct::Address, SPI_Status_struct::Count, SPI_Status_struct::Data, SPI_Status_struct::EEPROM, FALSE, SPI_Status_struct::Read, ST_CMD, SPI_Status_struct::State, USI_CLOCK_PIN, USI_DATAIN_PIN, USI_DATAOUT_PIN, USI_DIR_REG, USI_OUT_REG, SPI_Status_struct::WriteCollision, and SPI_Status_struct::XferComplete.

Referenced by Initialize().

    00224 {
    00225         __disable_interrupt();
    00226
    00227         // Configure outputs and inputs, enable pull-ups for DATAIN and CLOCK pins.
    00228         USI_DIR_REG |= (1<<USI_DATAOUT_PIN);
    00229         USI_DIR_REG &= ~((1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN));
    00230         USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN);
    00231
    00232         // Configure USI to 3-wire slave mode with overflow interrupt
    00233         USICR = ( (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1) | (spi_mode<<USICS0) );
    00234
    00235         // Initialize the SPI struct
    00236         SPI.Data = 0;                 // Clear data.
    00237         SPI.State = ST_CMD;           // Initial SPI state: wait for command.
    00238         SPI.Read = FALSE;             // Doesn't matter right now.
    00239         SPI.EEPROM = FALSE;           // Doesn't matter right now.
    00240         SPI.Count = 0;                // Doesn't matter right now.
    00241         SPI.Address = 0;              // Doesn't matter right now.
    00242         SPI.XferComplete = FALSE;     // We haven't even started a transfer yet.
    00243         SPI.WriteCollision = FALSE;   // ..And therefore a collision hasn't happened.
    00244
    00245         __enable_interrupt();
    00246 }

unsigned char SPI_Put | ( | unsigned char | val | ) |

Write a byte to SPI bus.

This function first checks if a transmission is in progress, and if so, flags a write collision, and returns FALSE.
If a transmission is not in progress, the flags for write collision and transfer complete are cleared, and the input byte is written to SPDR.

Parameters:

  | val | The byte to send.

Return values:

  | FALSE | A write collision happened.
  | TRUE | Byte written to SPDR.

Definition at line 268 of file USI.c.

References FALSE, TRUE, SPI_Status_struct::WriteCollision, and SPI_Status_struct::XferComplete.

Referenced by USI_OVF_ISR().

    00269 {
    00270         // Check if transmission in progress, i.e. if USI counter doesn't equal zero.
    00271         // If this fails, flag a write collision and return.
    00272         if((USISR & 0x0F) != 0) {
    00273                 SPI.WriteCollision = TRUE;
    00274                 return(FALSE);
    00275         }
    00276
    00277         // Reinitialize flags.
    00278         SPI.XferComplete = FALSE;
    00279         SPI.WriteCollision = FALSE;
    00280
    00281         USIDR = val;  // Put data in USI data register.
    00282
    00283         return (TRUE);
    00284 }

void SPI_Wait | ( | void |  | ) |

Wait for SPI transfer to complete.

This function waits for a transfer complete to be flagged.

Definition at line 307 of file USI.c.

References FALSE, and SPI_Status_struct::XferComplete.

    00308 {
    00309         do {  // Wait for transfer complete.
    00310         } while (SPI.XferComplete == FALSE);
    00311 }

__interrupt void USI_OVF_ISR | ( | void |  | ) |

USI Counter Overflow Interrupt Service Routine.

When the USI counter overflows, a byte has been transferred.
The USIDR contents are stored and flags are updated.

The protocol is quite simple and has three sequential states: command, address and data. (Keep in mind that the Master is in charge of data clocking, which means there is a one byte "delay" from when the Slave puts something to SPI till the Master can read it.)

1. If a non-zero byte is received in the command state, the ISR will store the commands to the SPI struct (read/write, EEPROM/SRAM, number of bytes). To signal that the command was received, 0xCC is put to the SPI bus. If a zero byte (0x00) is received in the command state, it is simply ignored because it is an invalid command.

2. When a byte is received in the address state, it is stored to the SPI struct. To signal that the address was received, 0xBB is put to SPI bus.

3. In the data state, variables are read/written "from back to front" until the byte counter reaches zero. Since the Master is in charge of the data clocking, the Slave will go to command state before the last byte is transferred during reading. This means that the Master should send an invalid command when getting each byte, ie 0x00.

If the time between two transfers is 1 second or more, the Slave automatically reverts to command state.

Note:
  Battery charging is not automatically halted during SPI communication. This means that the current charge state (current and voltage) will remain constant during heavy and prolonged serial traffic.

Todo:
  Variable writing not implemented yet.

Todo:
  EEPROM/SRAM flag doesn't really do anything with this implementation.

Definition at line 90 of file USI.c.

References ADCS, SPI_Status_struct::Address, ADR_ADCS, ADR_BATTACTIVE, ADR_BATTCTRL, ADR_BATTDATA, ADR_TIMERS, BattActive, BattControl, BattData, SPI_Status_struct::Count, SPI_Status_struct::Data, SPI_Status_struct::EEPROM, FALSE, SPI_Status_struct::Read, SPI_Put(), ST_ADDR, ST_CMD, ST_DATA, SPI_Status_struct::State, Time_Left(), Time_Set(), TIMER_USI, timeval, TRUE, and SPI_Status_struct::XferComplete.

    00091 {
    00092         // If the communication timed out, set ST_CMD as current state.
    00093         if (!Time_Left(TIMER_USI)) {
    00094                 SPI.State = ST_CMD;
    00095         }
    00096
    00097         // Start communication timer. If further communication doesn't happen
    00098         // within 1 second, the SPI communication state is reset to CMD.
    00099         Time_Set(TIMER_USI, 0, 1, 0);
    00100
    00101         // Clear USI counter and flag completed transfer.
    00102         USISR = (1<<USIOIF);
    00103         SPI.XferComplete = TRUE;
    00104
    00105         // Process incoming data.
    00106         switch(SPI.State)       {
    00107         // A valid SPI transfer starts with a Command Byte sent by the Master.
    00108         case ST_CMD:
    00109                 SPI.Data = USIDR;  // Store the transferred byte.
    00110
    00111                 // If the master sent 0, it is trying to get data. Ignore in this state.
    00112                 if (SPI.Data != 0) {
    00113                         // Does the master want to read or write?
    00114                         if (SPI.Data & 0x40) {
    00115                                 SPI.Read = FALSE;
    00116                         } else {
    00117                                 SPI.Read = TRUE;
    00118                         }
    00119
    00120                                 // From/to EEPROM or SRAM?
    00121                         if (SPI.Data &0x80) {
    00122                                 SPI.EEPROM = TRUE;
    00123                         } else {
    00124                                 SPI.EEPROM = FALSE;
    00125                         }
    00126
    00127                         SPI.Count = (SPI.Data & 0x3F);  // Get number of bytes to receive/send.
    00128                         SPI.State = ST_ADDR;  // The Master will send the address byte next.
    00129
    00130                         SPI_Put(0xCC);  // Signal that command was received.
    00131                 }
    00132         break;
    00133
    00134
    00135         case ST_ADDR:
    00136                 SPI.Data = USIDR;  // Store the address.
    00137                 SPI.Address = SPI.Data;
    00138                 SPI.State = ST_DATA;  // The master will send/wait for data next.
    00139
    00140                 SPI_Put(0xBB);  // Signal that address was received.
    00141         break;
    00142
    00143
    00144         // Note well: this will process at least one byte, regardless of Count.
    00145         case ST_DATA:
    00146                 if (SPI.Count-- > 0) {
    00147                         // Write specified variable to SPI, "back to front".
    00148                         if (SPI.Read) {
    00149                                 switch (SPI.Address) {
    00150                                 case ADR_ADCS:
    00151                                         SPI_Put(*(((unsigned char*)&ADCS) + (SPI.Count)));
    00152                                 break;
    00153
    00154
    00155                                 case ADR_BATTACTIVE:
    00156                                         SPI_Put(*((unsigned char*)&BattActive + (SPI.Count)));
    00157                                 break;
    00158
    00159
    00160                                 case ADR_BATTDATA:
    00161                                         SPI_Put(*((unsigned char*)&BattData + (SPI.Count)));
    00162                                 break;
    00163
    00164
    00165                                 case ADR_BATTCTRL:
    00166                                         SPI_Put(*((__eeprom unsigned char*)&BattControl + (SPI.Count)));
    00167                                 break;
    00168
    00169                                 case ADR_TIMERS:
    00170                                         SPI_Put(*((unsigned char*)&timeval + (SPI.Count)));
    00171                                 break;
    00172
    00173
    00174                                 default:
    00175                                         SPI_Put(0);
    00176                                 break;
    00177                                 }
    00178                         } else {
    00179                                 // Read byte from SPI
    00180                                 SPI.Data = USIDR;
    00181
    00182                                 // ********************************************
    00183                                 // THIS FUNCTION HAS NOT BEEN FULLY IMPLEMENTED
    00184                                 // ********************************************
    00185
    00186                                 // Save byte to specified variable.
    00187                                 switch (SPI.Address) {
    00188                                 case ADR_BATTCTRL:
    00189                                         *((__eeprom unsigned char*)&BattControl + SPI.Count) = SPI.Data;
    00190                                 break;
    00191
    00192
    00193                                 default:
    00194                                 break;
    00195                                 }
    00196                         }
    00197
    00198
    00199                 } else {
    00200                         SPI.State = ST_CMD;
    00201                 }
    00202         break;
    00203
    00204         default:  // Shouldn't end up here. (Unknown SPI-state)
    00205         break;
    00206         }
    00207 }

Here is the call graph for this function:

## Variable Documentation

SPI_Status_t SPI

SPI status struct.

Definition at line 47 of file USI.c.