Refactor the Makefile into pieces. This commit inspired by AncientGeek, my hatred...
[freeems-vanilla.git] / src / main / commsISRs.c
blob66dfb8d929a240f2db8255cadc213779baf56cb5
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!
27 /** @file
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.
43 #define COMMSISRS_C
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;
68 /* Zero the flags */
69 RXStateFlags = 0;
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?
109 void SCI0ISR(){
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;
157 }else{
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;
183 }else{
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;
196 }else{
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 */
222 SCI0DRL = rawValue;
223 TXBufferCurrentPositionSCI0++;
225 }else{
226 SCI0DRL = TXByteEscaped;
227 TXBufferCurrentPositionSCI0++;
228 TXByteEscaped = 0;
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;
237 }else{
238 coreStatusA |= BIT7;
239 /* Send the stop byte */
240 SCI0DRL = STOP_BYTE;
245 DEBUG_TURN_PIN_OFF(DECODER_BENCHMARKS, NBIT4, PORTB);