00001 /* FreeEMS - the open source engine management system 00002 * 00003 * Copyright 2008, 2009 Fred Cooke 00004 * 00005 * This file is part of the FreeEMS project. 00006 * 00007 * FreeEMS software is free software: you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation, either version 3 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * FreeEMS software is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/ 00019 * 00020 * We ask that if you make any changes to this file you email them upstream to 00021 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com! 00022 * 00023 * Thank you for choosing FreeEMS to run your engine! 00024 */ 00025 00026 00044 #define COMMSISRS_C 00045 #include "inc/freeEMS.h" 00046 #include "inc/interrupts.h" 00047 #include "inc/utils.h" 00048 #include "inc/commsCore.h" 00049 #include "inc/commsISRs.h" 00050 00051 00052 /* The C89 standard is used in the 3.3.6 GCC compiler, please * 00053 * see the following URL for more info on inline functions : * 00054 * http://gcc.gnu.org/onlinedocs/gcc-3.3.6/Inline.html#Inline */ 00055 00056 00067 extern inline void sendAndIncrement(unsigned char rawValue){ 00068 SCI0DRL = rawValue; 00069 TXPacketLengthToSendSCI0--; 00070 TXBufferCurrentPositionSCI0++; 00071 } 00072 00073 00084 extern inline void receiveAndIncrement(const unsigned char value){ 00085 *RXBufferCurrentPosition = value; 00086 RXCalculatedChecksum += value; 00087 RXBufferCurrentPosition++; 00088 RXPacketLengthReceived++; 00089 } 00090 00091 00102 void resetReceiveState(unsigned char sourceIDState){ 00103 /* Set the receive buffer pointer to the beginning */ 00104 RXBufferCurrentPosition = (unsigned char*)&RXBuffer; 00105 00106 /* Zero the flags, buffer length and checksum */ 00107 RXPacketLengthReceived = 0; 00108 RXCalculatedChecksum = 0; 00109 RXStateFlags = 0; 00110 00111 /* Set the source ID state (clear all or all but one flag(s)) */ 00112 RXBufferContentSourceID = sourceIDState; 00113 00114 /* Which ever interface we are setting is the one we came from. By definition */ 00115 /* it must be on and we want it to stay on, so just turn off all the others. */ 00116 if(sourceIDState & COM_SET_SCI0_INTERFACE_ID){ 00117 /* Turn off all others here */ 00120 /* SPI ? I2C ? SCI1 ? */ 00121 }else if(sourceIDState & COM_SET_CAN0_INTERFACE_ID){ 00122 /* Turn off all others here */ 00123 /* Only SCI for now */ 00124 SCI0CR2 &= SCICR2_RX_DISABLE; 00125 SCI0CR2 &= SCICR2_RX_ISR_DISABLE; 00126 /* SPI ? I2C ? SCI1 ? */ 00127 }else{ /* If clearing all flags then enable RX on all interfaces */ 00128 /* Only SCI for now */ 00129 SCI0CR2 |= SCICR2_RX_ENABLE; 00130 SCI0CR2 |= SCICR2_RX_ISR_ENABLE; 00133 /* SPI ? I2C ? SCI1 ? */ 00134 } 00135 } 00136 00137 00150 void SCI0ISR(){ 00151 /* Read the flags register */ 00152 unsigned char flags = SCI0SR1; 00153 /* Note: Combined with reading or writing the data register this also clears the flags. */ 00154 00155 /* Start counting */ 00156 unsigned short start = TCNT; 00157 00158 /* If the RX interrupt is enabled check RX related flags */ 00159 if(SCI0CR2 & SCICR2_RX_ISR_ENABLE){ 00160 /* Grab the received byte from the register */ 00161 unsigned char rawByte = SCI0DRL; 00162 00163 PORTB |= BIT0; 00164 00165 /* Record error conditions always */ 00166 unsigned char resetOnError = 0; 00167 /* If there is noise on the receive line record it */ 00168 if(flags & SCISR1_RX_NOISE){ 00169 Counters.serialNoiseErrors++; 00170 resetOnError++; 00171 }/* If an overrun occurs record it */ 00172 if(flags & SCISR1_RX_OVERRUN){ 00173 Counters.serialOverrunErrors++; 00174 resetOnError++; 00175 }/* If a framing error occurs record it */ 00176 if(flags & SCISR1_RX_FRAMING){ 00177 Counters.serialFramingErrors++; 00178 resetOnError++; 00179 }/* If a parity error occurs record it */ 00180 if(flags & SCISR1_RX_PARITY){ 00181 Counters.serialParityErrors++; 00182 resetOnError++; 00183 } 00184 00185 /* Drop out because of error flags */ 00186 if(resetOnError){ 00187 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); 00188 PORTB |= BIT1; 00189 return; 00190 } 00191 00192 /* If there is data waiting to be received */ 00193 if(flags & SCISR1_RX_REGISTER_FULL){ 00194 PORTB |= BIT2; 00195 /* Look for a start bresetReceiveStateyte to indicate a new packet */ 00196 if(rawByte == START_BYTE){ 00197 PORTB |= BIT3; 00198 /* If another interface is using it (Note, clear flag, not normal) */ 00199 if(RXBufferContentSourceID & COM_CLEAR_SCI0_INTERFACE_ID){ 00200 /* Turn off our reception */ 00201 SCI0CR2 &= SCICR2_RX_DISABLE; 00202 SCI0CR2 &= SCICR2_RX_ISR_DISABLE; 00203 PORTB |= BIT4; 00204 }else{ 00205 PORTB |= BIT5; 00206 /* If we are using it */ 00207 if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){ 00208 /* Increment the counter */ 00209 Counters.serialStartsInsideAPacket++; 00210 } 00211 /* Reset to us using it unless someone else was */ 00212 resetReceiveState(COM_SET_SCI0_INTERFACE_ID); 00213 } 00214 }else if(RXPacketLengthReceived >= RX_BUFFER_SIZE){ 00215 /* Buffer was full, record and reset */ 00216 Counters.serialPacketsOverLength++; 00217 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); 00218 PORTB |= BIT6; 00219 }else if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){ 00220 if(RXStateFlags & RX_SCI_ESCAPED_NEXT){ 00221 PORTB |= BIT7; 00222 /* Clear escaped byte next flag, thanks Karsten! ((~ != !) == (! ~= ~)) == LOL */ 00223 RXStateFlags &= RX_SCI_NOT_ESCAPED_NEXT; 00224 00225 if(rawByte == ESCAPED_ESCAPE_BYTE){ 00226 /* Store and checksum escape byte */ 00227 receiveAndIncrement(ESCAPE_BYTE); 00228 }else if(rawByte == ESCAPED_START_BYTE){ 00229 /* Store and checksum start byte */ 00230 receiveAndIncrement(START_BYTE); 00231 }else if(rawByte == ESCAPED_STOP_BYTE){ 00232 /* Store and checksum stop byte */ 00233 receiveAndIncrement(STOP_BYTE); 00234 }else{ 00235 /* Otherwise reset and record as data is bad */ 00236 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); 00237 Counters.serialEscapePairMismatches++; 00238 } 00239 }else if(rawByte == ESCAPE_BYTE){ 00240 PORTA |= BIT0; 00241 /* Set flag to indicate that the next byte should be un-escaped. */ 00242 RXStateFlags |= RX_SCI_ESCAPED_NEXT; 00243 }else if(rawByte == STOP_BYTE){ 00244 PORTA |= BIT1; 00245 /* Turn off reception */ 00246 SCI0CR2 &= SCICR2_RX_DISABLE; 00247 SCI0CR2 &= SCICR2_RX_ISR_DISABLE; 00248 00249 /* Bring the checksum back to where it should be */ 00250 unsigned char RXReceivedChecksum = (unsigned char)*(RXBufferCurrentPosition - 1); 00251 RXCalculatedChecksum -= RXReceivedChecksum; 00252 00253 /* Check that the checksum matches */ 00254 if(RXCalculatedChecksum == RXReceivedChecksum){ 00255 /* If it's OK set process flag */ 00256 RXStateFlags |= RX_READY_TO_PROCESS; 00257 PORTA |= BIT2; 00258 }else{ 00259 PORTA |= BIT3; 00260 /* Otherwise reset the state and record it */ 00261 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS); 00262 Counters.commsChecksumMismatches++; 00263 } 00264 }else{ 00265 PORTA |= BIT4; 00266 /* If it isn't special process it! */ 00267 receiveAndIncrement(rawByte); 00268 } 00269 }else{ 00270 /* Do nothing : drop the byte */ 00271 PORTA |= BIT5; 00272 } 00273 } 00274 } 00275 00276 /* If the TX interrupt is enabled check the register empty flag. */ 00277 if((SCI0CR2 & SCICR2_TX_ISR_ENABLE) && (flags & SCISR1_TX_REGISTER_EMPTY)){ 00278 /* Get the byte to be sent from the buffer */ 00279 unsigned char rawValue = *TXBufferCurrentPositionSCI0; 00280 00281 if(TXPacketLengthToSendSCI0 > 0){ 00282 if(TXByteEscaped == 0){ 00283 /* If the raw value needs to be escaped */ 00284 if(rawValue == ESCAPE_BYTE){ 00285 SCI0DRL = ESCAPE_BYTE; 00286 TXByteEscaped = ESCAPED_ESCAPE_BYTE; 00287 }else if(rawValue == START_BYTE){ 00288 SCI0DRL = ESCAPE_BYTE; 00289 TXByteEscaped = ESCAPED_START_BYTE; 00290 }else if(rawValue == STOP_BYTE){ 00291 SCI0DRL = ESCAPE_BYTE; 00292 TXByteEscaped = ESCAPED_STOP_BYTE; 00293 }else{ /* Otherwise just send it */ 00294 sendAndIncrement(rawValue); 00295 } 00296 }else{ 00297 sendAndIncrement(TXByteEscaped); 00298 TXByteEscaped = 0; 00299 } 00300 }else{ /* Length is zero */ 00301 /* Turn off transmission interrupt */ 00302 SCI0CR2 &= SCICR2_TX_ISR_DISABLE; 00303 /* Send the stop byte */ 00304 SCI0DRL = STOP_BYTE; 00305 while(!(SCI0SR1 & 0x80)){/* Wait for ever until able to send then move on */} 00306 SCI0DRL = STOP_BYTE; // nasty hack that works... means at least one and most 2 stops are sent so stuff works, but is messy... there must be a better way. 00307 /* Clear the TX in progress flag */ 00308 TXBufferInUseFlags &= COM_CLEAR_SCI0_INTERFACE_ID; 00309 } 00310 } 00311 00312 /* Record how long the operation took */ 00313 RuntimeVars.serialISRRuntime = TCNT - start; 00314 }