Contains high level functions for initializing the USI as an SPI slave, interrupt handling, sending and receiving single bytes.
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"
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. |
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.
SPI.Data | The last byte read from SPI. |
Definition at line 297 of file USI.c.
References SPI_Status_struct::Data.
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.
spi_mode | Specifies if USI should trigger on positive (0) or negative (1) edge of clock signal |
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.
val | The byte to send. |
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.
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 }