Ruthlessly remove old commented out and unused crap. Can get it back from history...
[freeems-vanilla.git] / src / commsISRs.c
blob8b7a40da2234d0df63854f370d6c26dc4dbc5497
1 /* FreeEMS - the open source engine management system
3 * Copyright 2008 Fred Cooke
5 * This file is part of the FreeEMS project.
7 * FreeEMS software is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * FreeEMS software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with any FreeEMS software. If not, see http://www.gnu.org/licenses/
20 * We ask that if you make any changes to this file you email them upstream to
21 * us at admin(at)diyefi(dot)org or, even better, fork the code on github.com!
23 * Thank you for choosing FreeEMS to run your engine!
27 /** @file commsISRs.c
28 * @ingroup interruptHandlers
29 * @ingroup communicationsFiles
31 * @brief Send and receive bytes serially
33 * This file contains the code for both send and receive of serial bytes
34 * through the UART SCI0 device. It is purely interrupt driven and controlled
35 * by a set of register and non-register flags that are toggled both inside
36 * and outside this file. Some additional helper functions are also kept here.
38 * @todo TODO SCI0ISR() needs to be split into some hash defines and an include file that formats it to be the ISR for a specific channel.
40 * @author Fred Cooke
44 #define COMMSISRS_C
45 #include "inc/freeEMS.h"
46 #include "inc/interrupts.h"
47 #include "inc/utils.h"
48 #include "inc/commsCore.h"
49 #include "inc/commsISRs.h"
52 /* The C89 standard is used in the 3.3.6 GCC compiler, please *
53 * see the following URL for more info on inline functions : *
54 * http://gcc.gnu.org/onlinedocs/gcc-3.3.6/Inline.html#Inline */
57 /** @brief Send And Increment
59 * Increment the pointer, decrement the length, and send it!
61 * @author Fred Cooke
63 * @note This is an extern inline function and as such is always inlined.
65 * @param rawValue is the raw byte to be sent down the serial line.
67 extern inline void sendAndIncrement(unsigned char rawValue){
68 SCI0DRL = rawValue;
69 TXPacketLengthToSendSCI0--;
70 TXBufferCurrentPositionSCI0++;
74 /** @brief Receive And Increment
76 * Store the value and add it to the checksum, then increment the pointer and length.
78 * @author Fred Cooke
80 * @note This is an extern inline function and as such is always inlined.
82 * @param value is the byte of data to store in the buffer and add to the checksum.
84 extern inline void receiveAndIncrement(const unsigned char value){
85 *RXBufferCurrentPosition = value;
86 RXCalculatedChecksum += value;
87 RXBufferCurrentPosition++;
88 RXPacketLengthReceived++;
92 /** @brief Reset Receive State
94 * Reset communications reception to the state provided.
96 * @author Fred Cooke
98 * @todo TODO this is in the wrong file!! Either move the header declaration or move the function!
100 * @param sourceIDState is the state to apply to the RX buffer state variable.
102 void resetReceiveState(unsigned char sourceIDState){
103 /* Set the receive buffer pointer to the beginning */
104 RXBufferCurrentPosition = (unsigned char*)&RXBuffer;
106 /* Zero the flags, buffer length and checksum */
107 RXPacketLengthReceived = 0;
108 RXCalculatedChecksum = 0;
109 RXStateFlags = 0;
111 /* Set the source ID state (clear all or all but one flag(s)) */
112 RXBufferContentSourceID = sourceIDState;
114 /* Which ever interface we are setting is the one we came from. By definition */
115 /* it must be on and we want it to stay on, so just turn off all the others. */
116 if(sourceIDState & COM_SET_SCI0_INTERFACE_ID){
117 /* Turn off all others here */
118 /// @todo TODO CAN0CTL1 &= CANCTL1_RX_DISABLE;
119 /// @todo TODO CAN0CTL1 &= CANCTL1_RX_ISR_DISABLE;
120 /* SPI ? I2C ? SCI1 ? */
121 }else if(sourceIDState & COM_SET_CAN0_INTERFACE_ID){
122 /* Turn off all others here */
123 /* Only SCI for now */
124 SCI0CR2 &= SCICR2_RX_DISABLE;
125 SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
126 /* SPI ? I2C ? SCI1 ? */
127 }else{ /* If clearing all flags then enable RX on all interfaces */
128 /* Only SCI for now */
129 SCI0CR2 |= SCICR2_RX_ENABLE;
130 SCI0CR2 |= SCICR2_RX_ISR_ENABLE;
131 /// @todo TODO CAN0CTL1 |= CANCTL1_RX_ENABLE;
132 /// @todo TODO CAN0CTL1 |= CANCTL1_RX_ISR_ENABLE;
133 /* SPI ? I2C ? SCI1 ? */
138 /** @brief Serial Communication Interface 0 ISR
140 * SCI0 ISR handles all interrupts for SCI0 by reading flags and acting
141 * appropriately. Its functions are to send raw bytes out over the wire from a
142 * buffer and to receive bytes from the wire un-escape them, checksum them and
143 * store them in a buffer.
145 * @author Fred Cooke
147 * @todo TODO Move this code into an include file much like the fuel interrupts such that it can be used for multiple UART SCI devices without duplication.
148 * @todo TODO Remove the debug code that uses the IO ports to light LEDs during specific actions.
150 void SCI0ISR(){
151 /* Read the flags register */
152 unsigned char flags = SCI0SR1;
153 /* Note: Combined with reading or writing the data register this also clears the flags. */
155 /* Start counting */
156 unsigned short start = TCNT;
158 /* If the RX interrupt is enabled check RX related flags */
159 if(SCI0CR2 & SCICR2_RX_ISR_ENABLE){
160 /* Grab the received byte from the register */
161 unsigned char rawByte = SCI0DRL;
163 PORTB |= BIT0;
165 /* Record error conditions always */
166 unsigned char resetOnError = 0;
167 /* If there is noise on the receive line record it */
168 if(flags & SCISR1_RX_NOISE){
169 Counters.serialNoiseErrors++;
170 resetOnError++;
171 }/* If an overrun occurs record it */
172 if(flags & SCISR1_RX_OVERRUN){
173 Counters.serialOverrunErrors++;
174 resetOnError++;
175 }/* If a framing error occurs record it */
176 if(flags & SCISR1_RX_FRAMING){
177 Counters.serialFramingErrors++;
178 resetOnError++;
179 }/* If a parity error occurs record it */
180 if(flags & SCISR1_RX_PARITY){
181 Counters.serialParityErrors++;
182 resetOnError++;
185 /* Drop out because of error flags */
186 if(resetOnError){
187 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
188 PORTB |= BIT1;
189 return;
192 /* If there is data waiting to be received */
193 if(flags & SCISR1_RX_REGISTER_FULL){
194 PORTB |= BIT2;
195 /* Look for a start bresetReceiveStateyte to indicate a new packet */
196 if(rawByte == START_BYTE){
197 PORTB |= BIT3;
198 /* If another interface is using it (Note, clear flag, not normal) */
199 if(RXBufferContentSourceID & COM_CLEAR_SCI0_INTERFACE_ID){
200 /* Turn off our reception */
201 SCI0CR2 &= SCICR2_RX_DISABLE;
202 SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
203 PORTB |= BIT4;
204 }else{
205 PORTB |= BIT5;
206 /* If we are using it */
207 if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){
208 /* Increment the counter */
209 Counters.serialStartsInsideAPacket++;
211 /* Reset to us using it unless someone else was */
212 resetReceiveState(COM_SET_SCI0_INTERFACE_ID);
214 }else if(RXPacketLengthReceived >= RX_BUFFER_SIZE){
215 /* Buffer was full, record and reset */
216 Counters.serialPacketsOverLength++;
217 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
218 PORTB |= BIT6;
219 }else if(RXBufferContentSourceID & COM_SET_SCI0_INTERFACE_ID){
220 if(RXStateFlags & RX_SCI_ESCAPED_NEXT){
221 PORTB |= BIT7;
222 /* Clear escaped byte next flag, thanks Karsten! ((~ != !) == (! ~= ~)) == LOL */
223 RXStateFlags &= RX_SCI_NOT_ESCAPED_NEXT;
225 if(rawByte == ESCAPED_ESCAPE_BYTE){
226 /* Store and checksum escape byte */
227 receiveAndIncrement(ESCAPE_BYTE);
228 }else if(rawByte == ESCAPED_START_BYTE){
229 /* Store and checksum start byte */
230 receiveAndIncrement(START_BYTE);
231 }else if(rawByte == ESCAPED_STOP_BYTE){
232 /* Store and checksum stop byte */
233 receiveAndIncrement(STOP_BYTE);
234 }else{
235 /* Otherwise reset and record as data is bad */
236 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
237 Counters.serialEscapePairMismatches++;
239 }else if(rawByte == ESCAPE_BYTE){
240 PORTA |= BIT0;
241 /* Set flag to indicate that the next byte should be un-escaped. */
242 RXStateFlags |= RX_SCI_ESCAPED_NEXT;
243 }else if(rawByte == STOP_BYTE){
244 PORTA |= BIT1;
245 /* Turn off reception */
246 SCI0CR2 &= SCICR2_RX_DISABLE;
247 SCI0CR2 &= SCICR2_RX_ISR_DISABLE;
249 /* Bring the checksum back to where it should be */
250 unsigned char RXReceivedChecksum = (unsigned char)*(RXBufferCurrentPosition - 1);
251 RXCalculatedChecksum -= RXReceivedChecksum;
253 /* Check that the checksum matches */
254 if(RXCalculatedChecksum == RXReceivedChecksum){
255 /* If it's OK set process flag */
256 RXStateFlags |= RX_READY_TO_PROCESS;
257 PORTA |= BIT2;
258 }else{
259 PORTA |= BIT3;
260 /* Otherwise reset the state and record it */
261 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS);
262 Counters.commsChecksumMismatches++;
264 }else{
265 PORTA |= BIT4;
266 /* If it isn't special process it! */
267 receiveAndIncrement(rawByte);
269 }else{
270 /* Do nothing : drop the byte */
271 PORTA |= BIT5;
276 /* If the TX interrupt is enabled check the register empty flag. */
277 if((SCI0CR2 & SCICR2_TX_ISR_ENABLE) && (flags & SCISR1_TX_REGISTER_EMPTY)){
278 /* Get the byte to be sent from the buffer */
279 unsigned char rawValue = *TXBufferCurrentPositionSCI0;
281 if(TXPacketLengthToSendSCI0 > 0){
282 if(TXByteEscaped == 0){
283 /* If the raw value needs to be escaped */
284 if(rawValue == ESCAPE_BYTE){
285 SCI0DRL = ESCAPE_BYTE;
286 TXByteEscaped = ESCAPED_ESCAPE_BYTE;
287 }else if(rawValue == START_BYTE){
288 SCI0DRL = ESCAPE_BYTE;
289 TXByteEscaped = ESCAPED_START_BYTE;
290 }else if(rawValue == STOP_BYTE){
291 SCI0DRL = ESCAPE_BYTE;
292 TXByteEscaped = ESCAPED_STOP_BYTE;
293 }else{ /* Otherwise just send it */
294 sendAndIncrement(rawValue);
296 }else{
297 sendAndIncrement(TXByteEscaped);
298 TXByteEscaped = 0;
300 }else{ /* Length is zero */
301 /* Turn off transmission interrupt */
302 SCI0CR2 &= SCICR2_TX_ISR_DISABLE;
303 /* Send the stop byte */
304 SCI0DRL = STOP_BYTE;
305 while(!(SCI0SR1 & 0x80)){/* Wait for ever until able to send then move on */}
306 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.
307 /* Clear the TX in progress flag */
308 TXBufferInUseFlags &= COM_CLEAR_SCI0_INTERFACE_ID;
312 /* Record how long the operation took */
313 RuntimeVars.serialISRRuntime = TCNT - start;