1 /* FreeEMS - the open source engine management system
3 * Copyright 2008-2011 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!
29 * @ingroup interruptHandlers
30 * @ingroup communicationsFiles
32 * @brief Send and receive bytes serially
34 * This file contains the code for both send and receive of serial bytes
35 * through the UART SCI0 device. It is purely interrupt driven and controlled
36 * by a set of register and non-register flags that are toggled both inside
37 * and outside this file. Some additional helper functions are also kept here.
39 * @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.
46 #include "inc/freeEMS.h"
47 #include "inc/interrupts.h"
48 #include "inc/utils.h"
49 #include "inc/commsCore.h"
50 #include "inc/commsISRs.h"
53 /* The C89 standard is used in the 3.3.6 GCC compiler, please *
54 * see the following URL for more info on inline functions : *
55 * http://gcc.gnu.org/onlinedocs/gcc-3.3.6/Inline.html#Inline */
58 /** @brief Reset Receive State
60 * Reset communications reception to the state provided.
64 * @todo TODO this is in the wrong file!! Either move the header declaration or move the function!
66 * @param sourceIDState is the state to apply to the RX buffer state variable.
68 void resetReceiveState(unsigned char sourceIDState
){
69 /* Set the receive buffer pointer to the beginning */
70 RXBufferCurrentPosition
= (unsigned char*)&RXBuffer
;
75 /* Set the source ID state (clear all or all but one flag(s)) */
76 RXBufferContentSourceID
= sourceIDState
;
78 /* Which ever interface we are setting is the one we came from. By definition */
79 /* it must be on and we want it to stay on, so just turn off all the others. */
80 if(sourceIDState
& COM_SET_SCI0_INTERFACE_ID
){
81 /* Turn off all others here */
82 /// @todo TODO CAN0CTL1 &= CANCTL1_RX_DISABLE;
83 /// @todo TODO CAN0CTL1 &= CANCTL1_RX_ISR_DISABLE;
84 /* SPI ? I2C ? SCI1 ? */
85 }else if(sourceIDState
& COM_SET_CAN0_INTERFACE_ID
){
86 /* Turn off all others here */
87 /* Only SCI for now */
88 SCI0CR2
&= SCICR2_RX_ISR_DISABLE
;
89 /* SPI ? I2C ? SCI1 ? */
90 }else{ /* If clearing all flags then enable RX on all interfaces */
91 /* Only SCI for now */
92 unsigned char devnull
; // Is there a better way to do this?
93 devnull
= SCI0SR1
; // Reading the flags combined with...
94 devnull
= SCI0DRL
; // ...reading the data clears the flags
95 SCI0CR2
|= SCICR2_RX_ISR_ENABLE
;
96 /// @todo TODO CAN0CTL1 |= CANCTL1_RX_ENABLE;
97 /// @todo TODO CAN0CTL1 |= CANCTL1_RX_ISR_ENABLE;
98 /* SPI ? I2C ? SCI1 ? */
103 /** @brief Serial Communication Interface 0 ISR
105 * SCI0 ISR handles all interrupts for SCI0 by reading flags and acting
106 * appropriately. Its functions are to send raw bytes out over the wire from a
107 * buffer and to receive bytes from the wire un-escape them, checksum them and
108 * store them in a buffer.
112 * @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.
113 * @todo TODO Fix the init code such that this doesn't run at boot without a serail device attached. Clear buffer maybe? or flag clearing/isr enabling ordering?
116 // OK before flag reading because cleared when SCI0DRL accessed (R or W)
117 DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS
, BIT4
, PORTB
);
119 /* Read the flags register */
120 unsigned char flags
= SCI0SR1
;
121 /* Note: Combined with reading or writing the data register this also clears the flags. */
123 /* If either of these flags is set, we need to read the data to clear the flag */
124 if(flags
& (SCISR1_RX_REGISTER_FULL
| SCISR1_RX_OVERRUN
)){
125 /* Grab the received byte from the register to clear the flag, whether we want the data or not */
126 unsigned char rawByte
= SCI0DRL
;
128 /* If the RX interrupt is enabled do something useful */
129 if(SCI0CR2
& SCICR2_RX_ISR_ENABLE
){
130 if(flags
& (SCISR1_RX_NOISE
| SCISR1_RX_FRAMING
| SCISR1_RX_PARITY
| SCISR1_RX_OVERRUN
)){
131 /* If there is noise on the receive line record it */
132 if(flags
& SCISR1_RX_NOISE
){
133 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_NOISE_ERRORS_OFFSET
);
134 KeyUserDebugs
.serialHardwareErrors
++;
137 /* If a framing error occurs record it */
138 if(flags
& SCISR1_RX_FRAMING
){
139 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_FRAMING_ERRORS_OFFSET
);
140 KeyUserDebugs
.serialHardwareErrors
++;
143 /* If a parity error occurs record it */
144 if(flags
& SCISR1_RX_PARITY
){
145 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PARITY_ERRORS_OFFSET
);
146 KeyUserDebugs
.serialHardwareErrors
++;
149 /* If an overrun occurs record it */
150 if(flags
& SCISR1_RX_OVERRUN
){
151 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_OVERRUN_ERRORS_OFFSET
);
152 KeyUserDebugs
.serialOverrunErrors
++;
155 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
156 }else{ // Process the received data
157 /* Look for a start byte to indicate a new packet */
158 if(rawByte
== START_BYTE
){
159 /* If another interface is using it (Note, clear flag, not normal) */
160 if(RXBufferContentSourceID
& COM_CLEAR_SCI0_INTERFACE_ID
){
161 /* Turn off our reception */
162 SCI0CR2
&= SCICR2_RX_ISR_DISABLE
;
164 /* If we are using it */
165 if(RXBufferContentSourceID
& COM_SET_SCI0_INTERFACE_ID
){
166 /* Increment the counter */
167 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_STARTS_INSIDE_A_PACKET_OFFSET
);
168 KeyUserDebugs
.serialAndCommsCodeErrors
++;
170 /* Reset to us using it unless someone else was */
171 resetReceiveState(COM_SET_SCI0_INTERFACE_ID
);
173 }else if((unsigned short)RXBufferCurrentPosition
>= ((unsigned short)&RXBuffer
+ RX_BUFFER_SIZE
)){
174 /* Buffer was full, record and reset */
175 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_OVER_LENGTH_OFFSET
);
176 KeyUserDebugs
.serialAndCommsCodeErrors
++;
177 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
178 }else if(RXBufferContentSourceID
& COM_SET_SCI0_INTERFACE_ID
){
179 if(RXStateFlags
& RX_SCI_ESCAPED_NEXT
){
180 /* Clear escaped byte next flag, thanks Karsten! ((~ != !) == (! ~= ~)) == LOL */
181 RXStateFlags
&= RX_SCI_NOT_ESCAPED_NEXT
;
183 if(rawByte
== ESCAPED_ESCAPE_BYTE
){
184 *RXBufferCurrentPosition
++ = ESCAPE_BYTE
;
185 }else if(rawByte
== ESCAPED_START_BYTE
){
186 *RXBufferCurrentPosition
++ = START_BYTE
;
187 }else if(rawByte
== ESCAPED_STOP_BYTE
){
188 *RXBufferCurrentPosition
++ = STOP_BYTE
;
190 /* Otherwise reset and record as data is bad */
191 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
192 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_ESCAPE_PAIR_MISMATCHES_OFFSET
);
193 KeyUserDebugs
.serialAndCommsCodeErrors
++;
195 }else if(rawByte
== ESCAPE_BYTE
){
196 /* Drop the escape and set the flag to indicate that the next byte should be un-escaped. */
197 RXStateFlags
|= RX_SCI_ESCAPED_NEXT
;
198 }else if(rawByte
== STOP_BYTE
){
199 /* Turn off reception */
200 SCI0CR2
&= SCICR2_RX_ISR_DISABLE
;
201 RXStateFlags
|= RX_READY_TO_PROCESS
;
203 *RXBufferCurrentPosition
++ = rawByte
;
205 } /* ELSE: Do nothing : drop the byte */
210 /* If the TX interrupt is enabled check the register empty flag. */
211 if((SCI0CR2
& SCICR2_TX_ISR_ENABLE
) && (flags
& SCISR1_TX_REGISTER_EMPTY
)){
212 /* Get the byte to be sent from the buffer */
213 unsigned char rawValue
= *TXBufferCurrentPositionSCI0
;
215 if(TXBufferCurrentPositionSCI0
<= TXBufferCurrentPositionHandler
){
216 if(TXByteEscaped
== 0){
217 /* If the raw value needs to be escaped */
218 if(rawValue
== ESCAPE_BYTE
){
219 SCI0DRL
= ESCAPE_BYTE
;
220 TXByteEscaped
= ESCAPED_ESCAPE_BYTE
;
221 }else if(rawValue
== START_BYTE
){
222 SCI0DRL
= ESCAPE_BYTE
;
223 TXByteEscaped
= ESCAPED_START_BYTE
;
224 }else if(rawValue
== STOP_BYTE
){
225 SCI0DRL
= ESCAPE_BYTE
;
226 TXByteEscaped
= ESCAPED_STOP_BYTE
;
227 }else{ /* Otherwise just send it */
229 TXBufferCurrentPositionSCI0
++;
232 SCI0DRL
= TXByteEscaped
;
233 TXBufferCurrentPositionSCI0
++;
236 }else{ /* Length is zero */
237 if(coreStatusA
& BIT7
){
238 /* Turn off transmission interrupt */
239 SCI0CR2
&= (SCICR2_TX_ISR_DISABLE
& SCICR2_TX_DISABLE
);
240 /* Clear the TX in progress flag */
241 TXBufferInUseFlags
&= COM_CLEAR_SCI0_INTERFACE_ID
;
242 coreStatusA
&= NBIT7
;
245 /* Send the stop byte */
251 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS
, NBIT4
, PORTB
);