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!
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.
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!
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
){
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.
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.
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;
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.
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.
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. */
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
;
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
++;
171 }/* If an overrun occurs record it */
172 if(flags
& SCISR1_RX_OVERRUN
){
173 Counters
.serialOverrunErrors
++;
175 }/* If a framing error occurs record it */
176 if(flags
& SCISR1_RX_FRAMING
){
177 Counters
.serialFramingErrors
++;
179 }/* If a parity error occurs record it */
180 if(flags
& SCISR1_RX_PARITY
){
181 Counters
.serialParityErrors
++;
185 /* Drop out because of error flags */
187 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
192 /* If there is data waiting to be received */
193 if(flags
& SCISR1_RX_REGISTER_FULL
){
195 /* Look for a start bresetReceiveStateyte to indicate a new packet */
196 if(rawByte
== START_BYTE
){
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
;
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
);
219 }else if(RXBufferContentSourceID
& COM_SET_SCI0_INTERFACE_ID
){
220 if(RXStateFlags
& RX_SCI_ESCAPED_NEXT
){
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
);
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
){
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
){
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
;
260 /* Otherwise reset the state and record it */
261 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
262 Counters
.commsChecksumMismatches
++;
266 /* If it isn't special process it! */
267 receiveAndIncrement(rawByte
);
270 /* Do nothing : drop the byte */
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
);
297 sendAndIncrement(TXByteEscaped
);
300 }else{ /* Length is zero */
301 /* Turn off transmission interrupt */
302 SCI0CR2
&= SCICR2_TX_ISR_DISABLE
;
303 /* Send the 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
;