1 /* FreeEMS - the open source engine management system
3 * Copyright 2008-2012 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.
44 #include "inc/freeEMS.h"
45 #include "inc/interrupts.h"
46 #include "inc/utils.h"
47 #include "inc/commsCore.h"
48 #include "inc/commsISRs.h"
51 /* The C89 standard is used in the 3.3.6 GCC compiler, please *
52 * see the following URL for more info on inline functions : *
53 * http://gcc.gnu.org/onlinedocs/gcc-3.3.6/Inline.html#Inline */
56 /** @brief Reset Receive State
58 * Reset communications reception to the state provided.
60 * @todo TODO this is in the wrong file!! Either move the header declaration or move the function!
62 * @param sourceIDState is the state to apply to the RX buffer state variable.
64 void resetReceiveState(unsigned char sourceIDState
){
65 /* Set the receive buffer pointer to the beginning */
66 RXBufferCurrentPosition
= (unsigned char*)&RXBuffer
;
71 /* Set the source ID state (clear all or all but one flag(s)) */
72 RXBufferContentSourceID
= sourceIDState
;
74 /* Which ever interface we are setting is the one we came from. By definition */
75 /* it must be on and we want it to stay on, so just turn off all the others. */
76 if(sourceIDState
& COM_SET_SCI0_INTERFACE_ID
){
77 /* Turn off all others here */
78 /// @todo TODO CAN0CTL1 &= CANCTL1_RX_DISABLE;
79 /// @todo TODO CAN0CTL1 &= CANCTL1_RX_ISR_DISABLE;
80 /* SPI ? I2C ? SCI1 ? */
81 }else if(sourceIDState
& COM_SET_CAN0_INTERFACE_ID
){
82 /* Turn off all others here */
83 /* Only SCI for now */
84 SCI0CR2
&= SCICR2_RX_ISR_DISABLE
;
85 /* SPI ? I2C ? SCI1 ? */
86 }else{ /* If clearing all flags then enable RX on all interfaces */
87 /* Only SCI for now */
88 unsigned char devnull
; // Is there a better way to do this?
89 devnull
= SCI0SR1
; // Reading the flags combined with...
90 devnull
= SCI0DRL
; // ...reading the data clears the flags
91 SCI0CR2
|= SCICR2_RX_ISR_ENABLE
;
92 /// @todo TODO CAN0CTL1 |= CANCTL1_RX_ENABLE;
93 /// @todo TODO CAN0CTL1 |= CANCTL1_RX_ISR_ENABLE;
94 /* SPI ? I2C ? SCI1 ? */
99 /** @brief Serial Communication Interface 0 ISR
101 * SCI0 ISR handles all interrupts for SCI0 by reading flags and acting
102 * appropriately. Its functions are to send raw bytes out over the wire from a
103 * buffer and to receive bytes from the wire un-escape them, checksum them and
104 * store them in a buffer.
106 * @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.
107 * @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?
110 // OK before flag reading because cleared when SCI0DRL accessed (R or W)
111 DEBUG_TURN_PIN_ON(DECODER_BENCHMARKS
, BIT4
, PORTB
);
113 /* Read the flags register */
114 unsigned char flags
= SCI0SR1
;
115 /* Note: Combined with reading or writing the data register this also clears the flags. */
117 /* If either of these flags is set, we need to read the data to clear the flag */
118 if(flags
& (SCISR1_RX_REGISTER_FULL
| SCISR1_RX_OVERRUN
)){
119 /* Grab the received byte from the register to clear the flag, whether we want the data or not */
120 unsigned char rawByte
= SCI0DRL
;
122 /* If the RX interrupt is enabled do something useful */
123 if(SCI0CR2
& SCICR2_RX_ISR_ENABLE
){
124 if(flags
& (SCISR1_RX_NOISE
| SCISR1_RX_FRAMING
| SCISR1_RX_PARITY
| SCISR1_RX_OVERRUN
)){
125 /* If there is noise on the receive line record it */
126 if(flags
& SCISR1_RX_NOISE
){
127 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_NOISE_ERRORS_OFFSET
);
128 KeyUserDebugs
.serialHardwareErrors
++;
131 /* If a framing error occurs record it */
132 if(flags
& SCISR1_RX_FRAMING
){
133 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_FRAMING_ERRORS_OFFSET
);
134 KeyUserDebugs
.serialHardwareErrors
++;
137 /* If a parity error occurs record it */
138 if(flags
& SCISR1_RX_PARITY
){
139 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PARITY_ERRORS_OFFSET
);
140 KeyUserDebugs
.serialHardwareErrors
++;
143 /* If an overrun occurs record it */
144 if(flags
& SCISR1_RX_OVERRUN
){
145 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_OVERRUN_ERRORS_OFFSET
);
146 KeyUserDebugs
.serialOverrunErrors
++;
149 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
150 }else{ // Process the received data
151 /* Look for a start byte to indicate a new packet */
152 if(rawByte
== START_BYTE
){
153 /* If another interface is using it (Note, clear flag, not normal) */
154 if(RXBufferContentSourceID
& COM_CLEAR_SCI0_INTERFACE_ID
){
155 /* Turn off our reception */
156 SCI0CR2
&= SCICR2_RX_ISR_DISABLE
;
158 /* If we are using it */
159 if(RXBufferContentSourceID
& COM_SET_SCI0_INTERFACE_ID
){
160 /* Increment the counter */
161 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_STARTS_INSIDE_A_PACKET_OFFSET
);
162 KeyUserDebugs
.serialAndCommsCodeErrors
++;
164 /* Reset to us using it unless someone else was */
165 resetReceiveState(COM_SET_SCI0_INTERFACE_ID
);
167 }else if((unsigned short)RXBufferCurrentPosition
>= ((unsigned short)&RXBuffer
+ RX_BUFFER_SIZE
)){
168 /* Buffer was full, record and reset */
169 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_PACKETS_OVER_LENGTH_OFFSET
);
170 KeyUserDebugs
.serialAndCommsCodeErrors
++;
171 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
172 }else if(RXBufferContentSourceID
& COM_SET_SCI0_INTERFACE_ID
){
173 if(RXStateFlags
& RX_SCI_ESCAPED_NEXT
){
174 /* Clear escaped byte next flag, thanks Karsten! ((~ != !) == (! ~= ~)) == LOL */
175 RXStateFlags
&= RX_SCI_NOT_ESCAPED_NEXT
;
177 if(rawByte
== ESCAPED_ESCAPE_BYTE
){
178 *RXBufferCurrentPosition
++ = ESCAPE_BYTE
;
179 }else if(rawByte
== ESCAPED_START_BYTE
){
180 *RXBufferCurrentPosition
++ = START_BYTE
;
181 }else if(rawByte
== ESCAPED_STOP_BYTE
){
182 *RXBufferCurrentPosition
++ = STOP_BYTE
;
184 /* Otherwise reset and record as data is bad */
185 resetReceiveState(CLEAR_ALL_SOURCE_ID_FLAGS
);
186 FLAG_AND_INC_FLAGGABLE(FLAG_SERIAL_ESCAPE_PAIR_MISMATCHES_OFFSET
);
187 KeyUserDebugs
.serialAndCommsCodeErrors
++;
189 }else if(rawByte
== ESCAPE_BYTE
){
190 /* Drop the escape and set the flag to indicate that the next byte should be un-escaped. */
191 RXStateFlags
|= RX_SCI_ESCAPED_NEXT
;
192 }else if(rawByte
== STOP_BYTE
){
193 /* Turn off reception */
194 SCI0CR2
&= SCICR2_RX_ISR_DISABLE
;
195 RXStateFlags
|= RX_READY_TO_PROCESS
;
197 *RXBufferCurrentPosition
++ = rawByte
;
199 } /* ELSE: Do nothing : drop the byte */
204 /* If the TX interrupt is enabled check the register empty flag. */
205 if((SCI0CR2
& SCICR2_TX_ISR_ENABLE
) && (flags
& SCISR1_TX_REGISTER_EMPTY
)){
206 /* Get the byte to be sent from the buffer */
207 unsigned char rawValue
= *TXBufferCurrentPositionSCI0
;
209 if(TXBufferCurrentPositionSCI0
<= TXBufferCurrentPositionHandler
){
210 if(TXByteEscaped
== 0){
211 /* If the raw value needs to be escaped */
212 if(rawValue
== ESCAPE_BYTE
){
213 SCI0DRL
= ESCAPE_BYTE
;
214 TXByteEscaped
= ESCAPED_ESCAPE_BYTE
;
215 }else if(rawValue
== START_BYTE
){
216 SCI0DRL
= ESCAPE_BYTE
;
217 TXByteEscaped
= ESCAPED_START_BYTE
;
218 }else if(rawValue
== STOP_BYTE
){
219 SCI0DRL
= ESCAPE_BYTE
;
220 TXByteEscaped
= ESCAPED_STOP_BYTE
;
221 }else{ /* Otherwise just send it */
223 TXBufferCurrentPositionSCI0
++;
226 SCI0DRL
= TXByteEscaped
;
227 TXBufferCurrentPositionSCI0
++;
230 }else{ /* Length is zero */
231 if(coreStatusA
& BIT7
){
232 /* Turn off transmission interrupt */
233 SCI0CR2
&= (SCICR2_TX_ISR_DISABLE
& SCICR2_TX_DISABLE
);
234 /* Clear the TX in progress flag */
235 TXBufferInUseFlags
&= COM_CLEAR_SCI0_INTERFACE_ID
;
236 coreStatusA
&= NBIT7
;
239 /* Send the stop byte */
245 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS
, NBIT4
, PORTB
);