Initial import
[avr_bc100.git] / BaseTinyFirmware / IAR / 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 <ioavr.h>\r
31 #include <inavr.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 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 #pragma vector=USI_OVF_vect\r
90 __interrupt void USI_OVF_ISR(void)\r
91 {\r
92         // If the communication timed out, set ST_CMD as current state.\r
93         if (!Time_Left(TIMER_USI)) {\r
94                 SPI.State = ST_CMD;\r
95         }\r
96 \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
100         \r
101         // Clear USI counter and flag completed transfer.\r
102         USISR = (1<<USIOIF);\r
103         SPI.XferComplete = TRUE;\r
104         \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
108         case ST_CMD:   \r
109                 SPI.Data = USIDR;  // Store the transferred byte.\r
110                 \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
115                                 SPI.Read = FALSE;\r
116                         } else {\r
117                                 SPI.Read = TRUE;\r
118                         }\r
119 \r
120                                 // From/to EEPROM or SRAM?\r
121                         if (SPI.Data &0x80) {\r
122                                 SPI.EEPROM = TRUE;\r
123                         } else {\r
124                                 SPI.EEPROM = FALSE;\r
125                         }\r
126 \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
129 \r
130                         SPI_Put(0xCC);  // Signal that command was received.\r
131                 }\r
132         break;\r
133 \r
134                         \r
135         case ST_ADDR:  \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
139 \r
140                 SPI_Put(0xBB);  // Signal that address was received.\r
141         break;\r
142 \r
143 \r
144         // Note well: this will process at least one byte, regardless of Count.\r
145         case ST_DATA:\r
146                 if (SPI.Count-- > 0) {\r
147                         // Write specified variable to SPI, "back to front".\r
148                         if (SPI.Read) {\r
149                                 switch (SPI.Address) {\r
150                                 case ADR_ADCS:\r
151                                         SPI_Put(*(((unsigned char*)&ADCS) + (SPI.Count)));\r
152                                 break;\r
153                                 \r
154                                 \r
155                                 case ADR_BATTACTIVE:\r
156                                         SPI_Put(*((unsigned char*)&BattActive + (SPI.Count)));\r
157                                 break;\r
158                                 \r
159                                 \r
160                                 case ADR_BATTDATA:\r
161                                         SPI_Put(*((unsigned char*)&BattData + (SPI.Count)));\r
162                                 break;\r
163                                 \r
164                                 \r
165                                 case ADR_BATTCTRL:\r
166                                         SPI_Put(*((__eeprom unsigned char*)&BattControl + (SPI.Count)));\r
167                                 break;\r
168                                 \r
169                                 case ADR_TIMERS:\r
170                                         SPI_Put(*((unsigned char*)&timeval + (SPI.Count)));\r
171                                 break;\r
172 \r
173                                 \r
174                                 default:\r
175                                         SPI_Put(0);\r
176                                 break;\r
177                                 }\r
178                         } else {\r
179                                 // Read byte from SPI\r
180                                 SPI.Data = USIDR;\r
181 \r
182                                 // ********************************************\r
183                                 // THIS FUNCTION HAS NOT BEEN FULLY IMPLEMENTED\r
184                                 // ********************************************\r
185                                                                 \r
186                                 // Save byte to specified variable.\r
187                                 switch (SPI.Address) {\r
188                                 case ADR_BATTCTRL:\r
189                                         *((__eeprom unsigned char*)&BattControl + SPI.Count) = SPI.Data;\r
190                                 break;\r
191 \r
192                                 \r
193                                 default:\r
194                                 break;\r
195                                 }\r
196                         }\r
197                         \r
198 \r
199                 } else {\r
200                         SPI.State = ST_CMD;\r
201                 }\r
202         break;\r
203 \r
204         default:  // Shouldn't end up here. (Unknown SPI-state)\r
205         break;\r
206         }\r
207 }\r
208 \r
209 \r
210 /*! \brief Initializes USI as an SPI slave\r
211  *\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
215  * \r
216  * \param spi_mode Specifies if USI should trigger on positive (0) or negative\r
217  * (1) edge of clock signal\r
218  *\r
219  * \note Clears the stored data\r
220  *\r
221  * \todo Timer should reset SPI protocol on timeout\r
222  */\r
223 void SPI_Init(unsigned char spi_mode)\r
224 {\r
225         __disable_interrupt();\r
226         \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
231         \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
234 \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
244 \r
245         __enable_interrupt();\r
246 }\r
247 \r
248 \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
254 //\r
255 // Returns 0 if a write collision occurred, 1 otherwise.\r
256 /*! \brief Write a byte to SPI bus\r
257  *\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
262  *\r
263  * \param val The byte to send.\r
264  *\r
265  * \retval FALSE A write collision happened.\r
266  * \retval TRUE Byte written to SPDR.\r
267  */\r
268 unsigned char SPI_Put(unsigned char val)\r
269 {\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
274                 return(FALSE);\r
275         }\r
276 \r
277         // Reinitialize flags.\r
278         SPI.XferComplete = FALSE;\r
279         SPI.WriteCollision = FALSE;\r
280 \r
281         USIDR = val;  // Put data in USI data register.\r
282 \r
283         return (TRUE);\r
284 }\r
285 \r
286 \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
291  *\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
294  *\r
295  * \retval SPI.Data The last byte read from SPI.\r
296  */\r
297 unsigned char SPI_Get(void)\r
298 {\r
299         return SPI.Data;\r
300 }\r
301 \r
302 \r
303 /*! \brief Wait for SPI transfer to complete\r
304  *\r
305  * This function waits for a transfer complete to be flagged.\r
306  */\r
307 void SPI_Wait(void)\r
308 {\r
309         do {  // Wait for transfer complete.\r
310         } while (SPI.XferComplete == FALSE);\r
311 }\r