1 /* This file has been prepared for Doxygen automatic documentation generation.*/
\r
2 /*! \file *********************************************************************
\r
5 * Functions for use of the Universal Serial Interface
\r
7 * Contains high level functions for initializing the USI as an SPI slave,
\r
8 * interrupt handling, sending and receiving single bytes.
\r
10 * \par Application note:
\r
11 * AVR458: Charging Li-Ion Batteries with BC100 \n
\r
12 * AVR463: Charging NiMH Batteries with BC100
\r
14 * \par Documentation:
\r
15 * For comprehensive code documentation, supported compilers, compiler
\r
16 * settings and supported devices see readme.html
\r
19 * Atmel Corporation: http://www.atmel.com \n
\r
20 * Support email: avr@atmel.com \n
\r
21 * Original author: \n
\r
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
27 * $Date: 2007-08-23 12:55:51 +0200 (to, 23 aug 2007) $\n
\r
28 ******************************************************************************/
\r
34 #include "structs.h"
\r
38 #include "battery.h"
\r
43 //******************************************************************************
\r
45 //******************************************************************************
\r
46 //! SPI status struct
\r
50 //******************************************************************************
\r
52 //*****************************************************************************
\r
53 /*! \brief USI Counter Overflow Interrupt Service Routine
\r
55 * When the USI counter overflows, a byte has been transferred.\n
\r
56 * The USIDR contents are stored and flags are updated.
\r
58 * The protocol is quite simple and has three sequential states: command,
\r
60 * (Keep in mind that the Master is in charge of data clocking, which means
\r
61 * there is a one byte "delay" from when the Slave puts something to SPI till
\r
62 * the Master can read it.)
\r
64 * 1. If a non-zero byte is received in the command state, the ISR will
\r
65 * store the commands to the SPI struct (read/write, EEPROM/SRAM, number of
\r
66 * bytes). To signal that the command was received, 0xCC is put to the SPI bus.
\r
67 * If a zero byte (0x00) is received in the command state, it is simply ignored
\r
68 * because it is an invalid command.
\r
70 * 2. When a byte is received in the address state, it is stored to the SPI
\r
71 * struct. To signal that the address was received, 0xBB is put to SPI bus.
\r
73 * 3. In the data state, variables are read/written "from back to front" until
\r
74 * the byte counter reaches zero. Since the Master is in charge of the data
\r
75 * clocking, the Slave will go to command state before the last byte is
\r
76 * transferred during reading. This means that the Master should send an
\r
77 * invalid command when getting each byte, ie 0x00.
\r
79 * If the time between two transfers is 1 second or more, the Slave
\r
80 * automatically reverts to command state.
\r
82 * \note Battery charging is not automatically halted during SPI communication.
\r
83 * This means that the current charge state (current and voltage) will
\r
84 * remain constant during heavy and prolonged serial traffic.
\r
86 * \todo Variable writing not implemented yet.
\r
87 * \todo EEPROM/SRAM flag doesn't really do anything with this implementation.
\r
89 #pragma vector=USI_OVF_vect
\r
90 __interrupt void USI_OVF_ISR(void)
\r
92 // If the communication timed out, set ST_CMD as current state.
\r
93 if (!Time_Left(TIMER_USI)) {
\r
97 // Start communication timer. If further communication doesn't happen
\r
98 // within 1 second, the SPI communication state is reset to CMD.
\r
99 Time_Set(TIMER_USI, 0, 1, 0);
\r
101 // Clear USI counter and flag completed transfer.
\r
102 USISR = (1<<USIOIF);
\r
103 SPI.XferComplete = TRUE;
\r
105 // Process incoming data.
\r
106 switch(SPI.State) {
\r
107 // A valid SPI transfer starts with a Command Byte sent by the Master.
\r
109 SPI.Data = USIDR; // Store the transferred byte.
\r
111 // If the master sent 0, it is trying to get data. Ignore in this state.
\r
112 if (SPI.Data != 0) {
\r
113 // Does the master want to read or write?
\r
114 if (SPI.Data & 0x40) {
\r
120 // From/to EEPROM or SRAM?
\r
121 if (SPI.Data &0x80) {
\r
124 SPI.EEPROM = FALSE;
\r
127 SPI.Count = (SPI.Data & 0x3F); // Get number of bytes to receive/send.
\r
128 SPI.State = ST_ADDR; // The Master will send the address byte next.
\r
130 SPI_Put(0xCC); // Signal that command was received.
\r
136 SPI.Data = USIDR; // Store the address.
\r
137 SPI.Address = SPI.Data;
\r
138 SPI.State = ST_DATA; // The master will send/wait for data next.
\r
140 SPI_Put(0xBB); // Signal that address was received.
\r
144 // Note well: this will process at least one byte, regardless of Count.
\r
146 if (SPI.Count-- > 0) {
\r
147 // Write specified variable to SPI, "back to front".
\r
149 switch (SPI.Address) {
\r
151 SPI_Put(*(((unsigned char*)&ADCS) + (SPI.Count)));
\r
155 case ADR_BATTACTIVE:
\r
156 SPI_Put(*((unsigned char*)&BattActive + (SPI.Count)));
\r
161 SPI_Put(*((unsigned char*)&BattData + (SPI.Count)));
\r
166 SPI_Put(*((__eeprom unsigned char*)&BattControl + (SPI.Count)));
\r
170 SPI_Put(*((unsigned char*)&timeval + (SPI.Count)));
\r
179 // Read byte from SPI
\r
182 // ********************************************
\r
183 // THIS FUNCTION HAS NOT BEEN FULLY IMPLEMENTED
\r
184 // ********************************************
\r
186 // Save byte to specified variable.
\r
187 switch (SPI.Address) {
\r
189 *((__eeprom unsigned char*)&BattControl + SPI.Count) = SPI.Data;
\r
200 SPI.State = ST_CMD;
\r
204 default: // Shouldn't end up here. (Unknown SPI-state)
\r
210 /*! \brief Initializes USI as an SPI slave
\r
212 * Initializes USI as a 3-wire SPI slave using the pins specified in USI.h for
\r
213 * I/O and clock, and USI counter overflow interrupts enabled.\n
\r
214 * Also initializes the SPI status struct.
\r
216 * \param spi_mode Specifies if USI should trigger on positive (0) or negative
\r
217 * (1) edge of clock signal
\r
219 * \note Clears the stored data
\r
221 * \todo Timer should reset SPI protocol on timeout
\r
223 void SPI_Init(unsigned char spi_mode)
\r
225 __disable_interrupt();
\r
227 // Configure outputs and inputs, enable pull-ups for DATAIN and CLOCK pins.
\r
228 USI_DIR_REG |= (1<<USI_DATAOUT_PIN);
\r
229 USI_DIR_REG &= ~((1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN));
\r
230 USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN);
\r
232 // Configure USI to 3-wire slave mode with overflow interrupt
\r
233 USICR = ( (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1) | (spi_mode<<USICS0) );
\r
235 // Initialize the SPI struct
\r
236 SPI.Data = 0; // Clear data.
\r
237 SPI.State = ST_CMD; // Initial SPI state: wait for command.
\r
238 SPI.Read = FALSE; // Doesn't matter right now.
\r
239 SPI.EEPROM = FALSE; // Doesn't matter right now.
\r
240 SPI.Count = 0; // Doesn't matter right now.
\r
241 SPI.Address = 0; // Doesn't matter right now.
\r
242 SPI.XferComplete = FALSE; // We haven't even started a transfer yet.
\r
243 SPI.WriteCollision = FALSE; // ..And therefore a collision hasn't happened.
\r
245 __enable_interrupt();
\r
249 // Put one byte on bus. Use this function like you would write to the SPDR
\r
250 // register in the native SPI module. Calling this function will prepare a
\r
251 // byte for the next transfer initiated by the master device. If a transfer
\r
252 // is in progress, this function will set the write collision flag and return
\r
253 // without altering the data registers.
\r
255 // Returns 0 if a write collision occurred, 1 otherwise.
\r
256 /*! \brief Write a byte to SPI bus
\r
258 * This function first checks if a transmission is in progress, and if so, flags
\r
259 * a write collision, and returns FALSE.\n
\r
260 * If a transmission is not in progress, the flags for write collision and
\r
261 * transfer complete are cleared, and the input byte is written to SPDR.\n
\r
263 * \param val The byte to send.
\r
265 * \retval FALSE A write collision happened.
\r
266 * \retval TRUE Byte written to SPDR.
\r
268 unsigned char SPI_Put(unsigned char val)
\r
270 // Check if transmission in progress, i.e. if USI counter doesn't equal zero.
\r
271 // If this fails, flag a write collision and return.
\r
272 if((USISR & 0x0F) != 0) {
\r
273 SPI.WriteCollision = TRUE;
\r
277 // Reinitialize flags.
\r
278 SPI.XferComplete = FALSE;
\r
279 SPI.WriteCollision = FALSE;
\r
281 USIDR = val; // Put data in USI data register.
\r
287 // Get one byte from bus. This function only returns the previous stored
\r
288 // USIDR value. The transfer complete flag is not checked. Use this function
\r
289 // like you would read from the SPDR register in the native SPI module.
\r
290 /*! \brief Get the last byte received from SPI bus
\r
292 * This function simply returns the last byte stored to the SPI status struct,
\r
293 * without checking if a completed transfer is flagged.
\r
295 * \retval SPI.Data The last byte read from SPI.
\r
297 unsigned char SPI_Get(void)
\r
303 /*! \brief Wait for SPI transfer to complete
\r
305 * This function waits for a transfer complete to be flagged.
\r
307 void SPI_Wait(void)
\r
309 do { // Wait for transfer complete.
\r
310 } while (SPI.XferComplete == FALSE);
\r