2008-03-14 Martin Thomas <mthomas@rhrk.uni-kl.de>
[avr_bc100.git] / BaseTinyFirmware / GCC / USI.c
1 /* This file has been prepared for Doxygen automatic documentation generation.*/\r
2 /*! \file *********************************************************************\r
3  *\r
4  * \brief\r
5  *      Functions for use of the Universal Serial Interface\r
6  *\r
7  *      Contains high level functions for initializing the USI as an SPI slave,\r
8  *      interrupt handling, sending and receiving single bytes.\r
9  *\r
10  * \par Application note:\r
11  *      AVR458: Charging Li-Ion Batteries with BC100 \n\r
12  *      AVR463: Charging NiMH Batteries with BC100\r
13  *\r
14  * \par Documentation:\r
15  *      For comprehensive code documentation, supported compilers, compiler\r
16  *      settings and supported devices see readme.html\r
17  *\r
18  * \author\r
19  *      Atmel Corporation: http://www.atmel.com \n\r
20  *      Support email: avr@atmel.com \n\r
21  *      Original author: \n\r
22  *\r
23  * $Name$\r
24  * $Revision: 2299 $\r
25  * $RCSfile$\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
29 \r
30 #include <avr/io.h>\r
31 #include <avr/interrupt.h>\r
32 \r
33 #include "enums.h"\r
34 #include "structs.h"\r
35 \r
36 #include "main.h"\r
37 #include "ADC.h"\r
38 #include "battery.h"\r
39 #include "time.h"\r
40 #include "USI.h"\r
41 \r
42 \r
43 //******************************************************************************\r
44 // Variables\r
45 //******************************************************************************\r
46 //! SPI status struct\r
47 volatile SPI_Status_t SPI;\r
48 \r
49 \r
50 //******************************************************************************\r
51 //Functions\r
52 //*****************************************************************************\r
53 /*! \brief USI Counter Overflow Interrupt Service Routine\r
54  *\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
57  *\r
58  * The protocol is quite simple and has three sequential states: command,\r
59  * address and data.\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
63  *\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
69  *\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
72  * \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
78  *\r
79  * If the time between two transfers is 1 second or more, the Slave\r
80  * automatically reverts to command state.\r
81  *\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
85  *\r
86  * \todo Variable writing not implemented yet.\r
87  * \todo EEPROM/SRAM flag doesn't really do anything with this implementation.\r
88  */\r
89 ISR(USI_OVF_vect)\r
90 {\r
91         // If the communication timed out, set ST_CMD as current state.\r
92         if (!Time_Left(TIMER_USI)) {\r
93                 SPI.State = ST_CMD;\r
94         }\r
95 \r
96         // Start communication timer. If further communication doesn't happen\r
97         // within 1 second, the SPI communication state is reset to CMD.\r
98         Time_Set(TIMER_USI, 0, 1, 0);\r
99         \r
100         // Clear USI counter and flag completed transfer.\r
101         USISR = (1<<USIOIF);\r
102         SPI.XferComplete = TRUE;\r
103         \r
104         // Process incoming data.\r
105         switch(SPI.State)       {\r
106         // A valid SPI transfer starts with a Command Byte sent by the Master.\r
107         case ST_CMD:   \r
108                 SPI.Data = USIDR;  // Store the transferred byte.\r
109                 \r
110                 // If the master sent 0, it is trying to get data. Ignore in this state.\r
111                 if (SPI.Data != 0) {\r
112                         // Does the master want to read or write?\r
113                         if (SPI.Data & 0x40) {\r
114                                 SPI.Read = FALSE;\r
115                         } else {\r
116                                 SPI.Read = TRUE;\r
117                         }\r
118 \r
119                                 // From/to EEPROM or SRAM?\r
120                         if (SPI.Data &0x80) {\r
121                                 SPI.EEPROM = TRUE;\r
122                         } else {\r
123                                 SPI.EEPROM = FALSE;\r
124                         }\r
125 \r
126                         SPI.Count = (SPI.Data & 0x3F);  // Get number of bytes to receive/send.\r
127                         SPI.State = ST_ADDR;  // The Master will send the address byte next.\r
128 \r
129                         SPI_Put(0xCC);  // Signal that command was received.\r
130                 }\r
131         break;\r
132 \r
133                         \r
134         case ST_ADDR:  \r
135                 SPI.Data = USIDR;  // Store the address.\r
136                 SPI.Address = SPI.Data;\r
137                 SPI.State = ST_DATA;  // The master will send/wait for data next.\r
138 \r
139                 SPI_Put(0xBB);  // Signal that address was received.\r
140         break;\r
141 \r
142 \r
143         // Note well: this will process at least one byte, regardless of Count.\r
144         case ST_DATA:\r
145                 if (SPI.Count-- > 0) {\r
146                         // Write specified variable to SPI, "back to front".\r
147                         if (SPI.Read) {\r
148                                 switch (SPI.Address) {\r
149                                 case ADR_ADCS:\r
150                                         SPI_Put(*(((unsigned char*)&ADCS) + (SPI.Count)));\r
151                                 break;\r
152                                 \r
153                                 \r
154                                 case ADR_BATTACTIVE:\r
155                                         SPI_Put(*((unsigned char*)&BattActive + (SPI.Count)));\r
156                                 break;\r
157                                 \r
158                                 \r
159                                 case ADR_BATTDATA:\r
160                                         SPI_Put(*((unsigned char*)&BattData + (SPI.Count)));\r
161                                 break;\r
162                                 \r
163                                 \r
164                                 case ADR_BATTCTRL:\r
165                                         SPI_Put(eeprom_read_byte((unsigned char*)&BattControl + (SPI.Count)));\r
166                                 break;\r
167                                 \r
168                                 case ADR_TIMERS:\r
169                                         SPI_Put(*((unsigned char*)&timeval + (SPI.Count)));\r
170                                 break;\r
171 \r
172                                 \r
173                                 default:\r
174                                         SPI_Put(0);\r
175                                 break;\r
176                                 }\r
177                         } else {\r
178                                 // Read byte from SPI\r
179                                 SPI.Data = USIDR;\r
180 \r
181                                 // ********************************************\r
182                                 // THIS FUNCTION HAS NOT BEEN FULLY IMPLEMENTED\r
183                                 // ********************************************\r
184                                                                 \r
185                                 // Save byte to specified variable.\r
186                                 switch (SPI.Address) {\r
187                                 case ADR_BATTCTRL:\r
188                                         eeprom_write_byte((unsigned char*)&BattControl + SPI.Count, SPI.Data);\r
189                                 break;\r
190 \r
191                                 \r
192                                 default:\r
193                                 break;\r
194                                 }\r
195                         }\r
196                         \r
197 \r
198                 } else {\r
199                         SPI.State = ST_CMD;\r
200                 }\r
201         break;\r
202 \r
203         default:  // Shouldn't end up here. (Unknown SPI-state)\r
204         break;\r
205         }\r
206 }\r
207 \r
208 \r
209 /*! \brief Initializes USI as an SPI slave\r
210  *\r
211  * Initializes USI as a 3-wire SPI slave using the pins specified in USI.h for\r
212  * I/O and clock, and USI counter overflow interrupts enabled.\n\r
213  * Also initializes the SPI status struct.\r
214  * \r
215  * \param spi_mode Specifies if USI should trigger on positive (0) or negative\r
216  * (1) edge of clock signal\r
217  *\r
218  * \note Clears the stored data\r
219  *\r
220  * \todo Timer should reset SPI protocol on timeout\r
221  */\r
222 void SPI_Init(unsigned char spi_mode)\r
223 {\r
224         unsigned char sreg_saved;\r
225 \r
226         sreg_saved = SREG;\r
227         cli();\r
228         \r
229         // Configure outputs and inputs, enable pull-ups for DATAIN and CLOCK pins.\r
230         USI_DIR_REG |= (1<<USI_DATAOUT_PIN);\r
231         USI_DIR_REG &= ~((1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN));\r
232         USI_OUT_REG |= (1<<USI_DATAIN_PIN) | (1<<USI_CLOCK_PIN);\r
233         \r
234         // Configure USI to 3-wire slave mode with overflow interrupt\r
235         USICR = ( (1<<USIOIE) | (1<<USIWM0) | (1<<USICS1) | (spi_mode<<USICS0) );\r
236 \r
237         // Initialize the SPI struct\r
238         SPI.Data = 0;                 // Clear data.\r
239         SPI.State = ST_CMD;           // Initial SPI state: wait for command.\r
240         SPI.Read = FALSE;             // Doesn't matter right now.\r
241         SPI.EEPROM = FALSE;           // Doesn't matter right now.\r
242         SPI.Count = 0;                // Doesn't matter right now.\r
243         SPI.Address = 0;              // Doesn't matter right now.\r
244         SPI.XferComplete = FALSE;     // We haven't even started a transfer yet.\r
245         SPI.WriteCollision = FALSE;   // ..And therefore a collision hasn't happened.\r
246 \r
247         SREG = sreg_saved;\r
248 }\r
249 \r
250 \r
251 // Put one byte on bus. Use this function like you would write to the SPDR\r
252 // register in the native SPI module. Calling this function will prepare a\r
253 // byte for the next transfer initiated by the master device. If a transfer\r
254 // is in progress, this function will set the write collision flag and return\r
255 // without altering the data registers.\r
256 //\r
257 // Returns 0 if a write collision occurred, 1 otherwise.\r
258 /*! \brief Write a byte to SPI bus\r
259  *\r
260  * This function first checks if a transmission is in progress, and if so, flags\r
261  * a write collision, and returns FALSE.\n\r
262  * If a transmission is not in progress, the flags for write collision and\r
263  * transfer complete are cleared, and the input byte is written to SPDR.\n\r
264  *\r
265  * \param val The byte to send.\r
266  *\r
267  * \retval FALSE A write collision happened.\r
268  * \retval TRUE Byte written to SPDR.\r
269  */\r
270 unsigned char SPI_Put(unsigned char val)\r
271 {\r
272         // Check if transmission in progress, i.e. if USI counter doesn't equal zero.\r
273         // If this fails, flag a write collision and return.\r
274         if((USISR & 0x0F) != 0) {\r
275                 SPI.WriteCollision = TRUE;\r
276                 return(FALSE);\r
277         }\r
278 \r
279         // Reinitialize flags.\r
280         SPI.XferComplete = FALSE;\r
281         SPI.WriteCollision = FALSE;\r
282 \r
283         USIDR = val;  // Put data in USI data register.\r
284 \r
285         return (TRUE);\r
286 }\r
287 \r
288 \r
289 // Get one byte from bus. This function only returns the previous stored\r
290 // USIDR value. The transfer complete flag is not checked. Use this function\r
291 // like you would read from the SPDR register in the native SPI module.\r
292 /*! \brief Get the last byte received from SPI bus\r
293  *\r
294  * This function simply returns the last byte stored to the SPI status struct,\r
295  * without checking if a completed transfer is flagged.\r
296  *\r
297  * \retval SPI.Data The last byte read from SPI.\r
298  */\r
299 unsigned char SPI_Get(void)\r
300 {\r
301         return SPI.Data;\r
302 }\r
303 \r
304 \r
305 /*! \brief Wait for SPI transfer to complete\r
306  *\r
307  * This function waits for a transfer complete to be flagged.\r
308  */\r
309 void SPI_Wait(void)\r
310 {\r
311         do {  // Wait for transfer complete.\r
312         } while (SPI.XferComplete == FALSE);\r
313 }\r