forked from ttlappalainen/NMEA2000_mcp
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathNMEA2000_mcp.cpp
167 lines (138 loc) · 6.09 KB
/
NMEA2000_mcp.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
NMEA2000_mcp.cpp
2015-2016 Copyright (c) Kave Oy, www.kave.fi All right reserved.
Author: Timo Lappalainen
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-
1301 USA
*/
#include <NMEA2000_mcp.h>
typedef struct tCANFrame {
uint32_t id; // can identifier
uint8_t len; // length of data
uint8_t buf[8];
} CAN_message_t;
bool CanInUse=false;
MCP_CAN *pN2kCAN=0;
volatile tCANFrame *rx_frame_buff=0;
volatile uint16_t rx_frame_buf_size=0;
volatile uint8_t rx_buffer_read=0;
volatile uint8_t rx_buffer_write=0;
void CanIdToN2k(unsigned long id, unsigned char &prio, unsigned long &pgn, unsigned char &src, unsigned char &dst);
void CanInterrupt();
//*****************************************************************************
void PrintDecodedCanIdAndLen(unsigned long id, unsigned char len) {
unsigned char prio;
unsigned long pgn;
unsigned char src;
unsigned char dst;
if (id!=0) {
CanIdToN2k(id,prio,pgn,src,dst);
Serial.print("pgn: "); Serial.print(pgn); Serial.print(", prio: "); Serial.print(prio);
Serial.print(", src: "); Serial.print(src); Serial.print(", dst: "); Serial.print(dst);
} else {
Serial.print("id: "); Serial.print(id);
}
Serial.print(", len: "); Serial.println(len);
}
//*****************************************************************************
tNMEA2000_mcp::tNMEA2000_mcp(unsigned char _N2k_CAN_CS_pin, unsigned char _N2k_CAN_clockset,
unsigned char _N2k_CAN_int_pin, uint16_t _rx_frame_buf_size) : tNMEA2000(), N2kCAN() {
IsOpen=false;
N2k_CAN_CS_pin=_N2k_CAN_CS_pin;
N2k_CAN_clockset=_N2k_CAN_clockset;
if (pN2kCAN==0) { // Currently only first instace can use interrupts.
N2k_CAN_int_pin=_N2k_CAN_int_pin;
if ( UseInterrupt() ) {
rx_frame_buf_size=_rx_frame_buf_size;
if (rx_frame_buf_size<2) rx_frame_buf_size=2;
rx_frame_buff=new tCANFrame[rx_frame_buf_size];
pN2kCAN=&N2kCAN;
}
} else {
N2k_CAN_int_pin=0xff;
}
}
//*****************************************************************************
bool tNMEA2000_mcp::CANSendFrame(unsigned long id, unsigned char len, const unsigned char *buf, bool wait_sent) {
INT8U result;
// Also sending should be changed to be done by interrupt. This requires modifications for mcp_can.
uint8_t SaveSREG = SREG; // save interrupt flag
if ( UseInterrupt() ) cli(); // disable interrupts
result=N2kCAN.sendMsgBuf(id, 1, len, buf,wait_sent);
if ( UseInterrupt() ) SREG = SaveSREG; // restore the interrupt flag
// Serial.println(result);
return (result==CAN_OK);
}
//*****************************************************************************
bool tNMEA2000_mcp::CANOpen() {
if (IsOpen) return true;
if (CanInUse) return false; // currently prevent accidental second instance. Maybe possible in future.
N2kCAN.init_CS(N2k_CAN_CS_pin);
IsOpen=(N2kCAN.begin(CAN_250KBPS,N2k_CAN_clockset)==CAN_OK);
if (IsOpen && UseInterrupt() ) {
rx_buffer_read=0;
rx_buffer_write=0;
attachInterrupt(digitalPinToInterrupt(N2k_CAN_int_pin), CanInterrupt, FALLING);
}
CanInUse=IsOpen;
return IsOpen;
}
//*****************************************************************************
bool tNMEA2000_mcp::CANGetFrame(unsigned long &id, unsigned char &len, unsigned char *buf) {
bool HasFrame=false;
if ( UseInterrupt() ) {
uint8_t SaveSREG = SREG; // save interrupt flag
cli(); // disable interrupts
if (rx_buffer_read!=rx_buffer_write) {
// Serial.print("read: "); Serial.print(rx_buffer_read); Serial.print(", write: "); Serial.println(rx_buffer_write);
id = rx_frame_buff[rx_buffer_read].id;
len = rx_frame_buff[rx_buffer_read].len;
for (int i=0; i<len; buf[i]=rx_frame_buff[rx_buffer_read].buf[i], i++);
rx_buffer_read = (rx_buffer_read + 1) % rx_frame_buf_size;
HasFrame=( (id!=0) && (len!=0) );
}
SREG = SaveSREG; // restore the interrupt flag
} else {
if ( CAN_MSGAVAIL == N2kCAN.checkReceive() ) { // check if data coming
N2kCAN.readMsgBuf(&len, buf); // read data, len: data length, buf: data buf
id = N2kCAN.getCanId();
HasFrame=true;
}
}
// if (HasFrame) PrintDecodedCanIdAndLen(id,len);
return HasFrame;
}
//*****************************************************************************
// I am still note sure am I handling volatile right here since mcp_can has not
// been defined volatile. see. http://blog.regehr.org/archives/28
// In my tests I have used only to receive data or transmit data but not both.
void CanInterrupt() {
// Iterate over all pending messages.
// If either the bus is saturated or the MCU is busy, both RX buffers may be in use and
// reading a single message does not clear the IRQ conditon.
while ( CAN_MSGAVAIL == pN2kCAN->checkReceive() ) { // check if data coming
uint8_t temp = (rx_buffer_write + 1) % rx_frame_buf_size;
uint32_t id;
unsigned char len;
unsigned char buf[8];
pN2kCAN->readMsgBuf(&len,buf);
id=pN2kCAN->getCanId();
asm volatile ("" : : : "memory");
if ( (temp != rx_buffer_read) && (len<=8) ) { // check is there room for new message. If not, we loose it.
rx_frame_buff[rx_buffer_write].id=id;
rx_frame_buff[rx_buffer_write].len=len;
for (int i=0; i<len; rx_frame_buff[rx_buffer_write].buf[i]=buf[i], i++);
rx_buffer_write = temp;
}
}
}