Skyward boardcore
Loading...
Searching...
No Matches
CanProtocol.cpp
Go to the documentation of this file.
1/* Copyright (c) 2022 Skyward Experimental Rocketry
2 * Author: Federico Mandelli
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23#include "CanProtocol.h"
24
25using namespace miosix;
26
27namespace Boardcore
28{
29
30namespace Canbus
31{
32
33CanProtocol::CanProtocol(CanbusDriver* can, MsgHandler onReceive,
34 miosix::Priority threadPriority)
35 : can(can), onReceive(std::move(onReceive)), threadPriority(threadPriority)
36{
37}
38
40{
41 stopFlag = false;
42
43 if (can == nullptr)
44 return false;
45
46 // Start sender (joinable thread)
47 if (!sndStarted)
48 {
49 sndThread = miosix::Thread::create(
50 sndLauncher, skywardStack(4 * 1024), threadPriority,
51 reinterpret_cast<void*>(this), miosix::Thread::JOINABLE);
52
53 if (sndThread != nullptr)
54 sndStarted = true;
55 else
56 LOG_ERR(logger, "Could not start sender!");
57 }
58
59 // Start receiver
60 if (!rcvStarted)
61 {
62 rcvThread = miosix::Thread::create(rcvLauncher, skywardStack(4 * 1024),
63 threadPriority,
64 reinterpret_cast<void*>(this));
65
66 if (rcvThread != nullptr)
67 rcvStarted = true;
68 else
69 LOG_ERR(logger, "Could not start receiver!");
70 }
71
72 if (sndStarted && rcvStarted)
73 LOG_DEBUG(logger, "Sender and receiver started");
74
75 return sndStarted && rcvStarted;
76}
77
78bool CanProtocol::isStarted() { return sndStarted && rcvStarted; }
79
81{
82 stopFlag = true;
83
84 // Wait for sender to stop
85 sndThread->join();
86}
87
89{
90 // Append the message to the queue
91 outQueue.put(msg);
92
93 // Update stats
94 // updateQueueStats(appended);
95
96 // Return always true because the circular buffer overrides current packets
97 // and can't get full.
98 return true;
99}
100
101bool CanProtocol::addFilter(uint8_t src, uint64_t dst)
102{
103 if (src > 0xF || dst > 0xF)
104 return false;
105
106 // The filter mask will cover only the source and destination bits
107 uint32_t mask = static_cast<uint32_t>(CanProtocolIdMask::SOURCE) |
108 static_cast<uint32_t>(CanProtocolIdMask::DESTINATION);
109
110 uint32_t id =
111 src << static_cast<uint8_t>(CanProtocolShiftInformation::SOURCE) |
112 dst << static_cast<uint8_t>(CanProtocolShiftInformation::DESTINATION);
113
114 Mask32FilterBank filterBank(id, mask, 1, 1, 0, 0, 0);
115
116 if (can == nullptr)
117 return false;
118 else
119 return can->addFilter(filterBank);
120}
121
122void CanProtocol::sendMessage(const CanMessage& msg)
123{
124 CanPacket packet = {};
125 uint32_t leftToSend = msg.length - 1;
126
127 // Create the id for the first packet
128 packet.ext = true; // Use extended packet id
129
130 // The number of left to send packets
131 packet.id = static_cast<uint32_t>(msg.id) |
132 ((static_cast<uint32_t>(0x3F) - leftToSend) &
133 static_cast<uint32_t>(CanProtocolIdMask::LEFT_TO_SEND));
134 packet.length = byteForUint64(msg.payload[0]);
135
136 // Splits payload[0] in the right number of uint8_t
137 for (int i = 0; i < packet.length; i++)
138 packet.data[i] = msg.payload[0] >> (8 * i);
139
140 // Send the first packet
141 can->send(packet);
142 leftToSend--;
143
144 // Prepare the remaining packets
145 for (int i = 1; i < msg.length; i++)
146 {
147 packet.id =
148 static_cast<uint32_t>(msg.id) |
149 static_cast<uint32_t>(CanProtocolIdMask::FIRST_PACKET_FLAG) |
150 ((static_cast<uint32_t>(0x3F) - leftToSend) &
151 static_cast<uint32_t>(CanProtocolIdMask::LEFT_TO_SEND));
152 packet.length = byteForUint64(msg.payload[i]);
153
154 // Splits payload[i] in the right number of uint8_t
155 for (int k = 0; k < packet.length; k++)
156 packet.data[k] = msg.payload[i] >> (8 * k);
157
158 can->send(packet);
159 leftToSend--;
160 }
161}
162
163bool CanProtocol::enqueueEvent(uint8_t priority, uint8_t primaryType,
164 uint8_t source, uint8_t destination,
165 uint8_t secondaryType)
166{
167 return enqueueSimplePacket(priority, primaryType, source, destination,
168 secondaryType, 0xFF);
169}
170
171bool CanProtocol::enqueueSimplePacket(uint8_t priority, uint8_t primaryType,
172 uint8_t source, uint8_t destination,
173 uint8_t secondaryType, uint64_t payload)
174{
175 if (priority > 0xF || primaryType > 0x3F || source > 0xF ||
176 destination > 0xF || secondaryType > 0xF)
177 {
178 return false;
179 }
180
181 CanMessage msg{};
182
183 // Length set to a minumum of 1 even if there is no payload
184 msg.length = 1;
185 msg.payload[0] = payload;
186
187 // clang-format off
188 msg.id = priority << static_cast<uint32_t>(CanProtocolShiftInformation::PRIORITY);
189 msg.id |= primaryType << static_cast<uint32_t>(CanProtocolShiftInformation::PRIMARY_TYPE);
190 msg.id |= source << static_cast<uint32_t>(CanProtocolShiftInformation::SOURCE);
191 msg.id |= destination << static_cast<uint32_t>(CanProtocolShiftInformation::DESTINATION);
192 msg.id |= secondaryType << static_cast<uint32_t>(CanProtocolShiftInformation::SECONDARY_TYPE);
193 // clang-format off
194
195 LOG_DEBUG(logger, "Sending message with id: {:x}", msg.id);
196
197 return enqueueMsg(msg);
198}
199
200void CanProtocol::runReceiver()
201{
202 CanMessage msg;
203 uint8_t nReceived = 0;
204
205 while (!stopFlag)
206 {
207 // Wait for the next packet
208 can->getRXBuffer().waitUntilNotEmpty();
209
210 // If the buffer is not empty retrieve the packet
211 if (!can->getRXBuffer().isEmpty())
212 {
213 CanPacket pkt = can->getRXBuffer().pop().packet;
214
215 uint8_t leftToReceive =
216 static_cast<uint32_t>(0x3F) -
217 (pkt.id &
218 static_cast<uint32_t>(CanProtocolIdMask::LEFT_TO_SEND));
219
220 // Check if the packet is the first in the sequence, if this is the
221 // case then the previous message is overriden
222 if ((pkt.id & static_cast<uint32_t>(
224 {
225 // If it is we save the id (without the sequence number) and the
226 // message length
227 msg.id = pkt.id & static_cast<uint32_t>(
229 msg.length = leftToReceive + 1;
230
231 // Reset the number of received packets
232 nReceived = 0;
233 }
234
235 // Accept the packet only if it has the expected id
236 // clang-format off
237 if (msg.id != -1 &&
238 (pkt.id & static_cast<uint32_t>(CanProtocolIdMask::MESSAGE_INFORMATION)) ==
239 (msg.id & static_cast<uint32_t>(CanProtocolIdMask::MESSAGE_INFORMATION)))
240 // clang-format on
241 {
242 // Check if the packet is expected in the sequence. The received
243 // packet must have the expected left to send value
244
245 if (msg.length - nReceived - 1 == leftToReceive)
246 {
247 uint64_t payload = 0;
248
249 // Assemble the packet data into a uint64_t
250 for (uint8_t i = 0; i < pkt.length; i++)
251 {
252 uint64_t tmp = pkt.data[i];
253 payload |= tmp << (i * 8);
254 }
255
256 // Add the data to the message
257 msg.payload[msg.length - leftToReceive - 1] = payload;
258 nReceived++;
259 }
260 }
261
262 // If we have received the right number of packet call onReceive and
263 // reset the message
264 if (nReceived == msg.length && nReceived != 0)
265 {
266 LOG_DEBUG(logger, "Message ready with id: {:x}", msg.id);
267
268 onReceive(msg);
269
270 // Reset the packet
271 msg.id = -1;
272 msg.length = 0;
273 }
274 }
275 }
276}
277
278void CanProtocol::runSender()
279{
280 LOG_DEBUG(logger, "Sender is running");
281 CanMessage msg;
282
283 while (!stopFlag)
284 {
285 outQueue.waitUntilNotEmpty();
286
287 if (!outQueue.isEmpty())
288 {
289 // Get the first packet in the queue, without removing it
290 msg = outQueue.pop();
291
292 LOG_DEBUG(logger, "Sending message, length: {}", msg.length);
293
294 sendMessage(msg);
295
296 // updateSenderStats();
297 }
298 else
299 {
300 // Wait before sending something else
301 miosix::Thread::sleep(50);
302 }
303 }
304}
305
306void CanProtocol::rcvLauncher(void* arg)
307{
308 reinterpret_cast<CanProtocol*>(arg)->runReceiver();
309}
310
311void CanProtocol::sndLauncher(void* arg)
312{
313 reinterpret_cast<CanProtocol*>(arg)->runSender();
314}
315
316uint8_t CanProtocol::byteForUint64(uint64_t number)
317{
318 uint8_t i;
319
320 for (i = 1; i <= 8; i++)
321 {
322 number >>= 8;
323 if (number == 0)
324 return i;
325 }
326
327 return i;
328}
329
330} // namespace Canbus
331
332} // namespace Boardcore
#define LOG_ERR(logger,...)
#define LOG_DEBUG(logger,...)
CanProtocol(CanbusDriver *can, MsgHandler onReceive, miosix::Priority threadPriority)
Construct a new CanProtocol object.
bool enqueueMsg(const CanMessage &msg)
Non-blocking send function, puts the messages in a queue. Message is discarded if the queue is full.
bool start()
Start the receiving and sending threads.
bool isStarted()
Tells whether the driver was started.
bool enqueueSimplePacket(uint8_t priority, uint8_t primaryType, uint8_t source, uint8_t destination, uint8_t secondaryType, uint64_t payload)
Non-blocking send function for a simple packet with a payload of 1 can packet, useful to send generic...
bool addFilter(uint8_t src, uint64_t dst)
Adds a filter to the can peripheral to receive only messages from the given source and targeted to th...
void stop()
Stops sender and receiver threads.
bool enqueueEvent(uint8_t priority, uint8_t primaryType, uint8_t source, uint8_t destination, uint8_t secondaryType)
Non-blocking send function for an event (a message without payload).
Low level CanBus driver, with support for both peripherals (CAN1 and CAN2) on stm32f4 microcontroller...
Definition CanDriver.h:45
uint32_t send(CanPacket packet)
Sends a packet on the bus. This function blocks until the message has been successfully put into a TX...
IRQCircularBuffer< CanRXPacket, RX_BUF_SIZE > & getRXBuffer()
Returns a reference to the buffer containing received packets.
Definition CanDriver.h:162
bool addFilter(FilterBank filter)
Adds a new filter to the bus, or returns false if there are no more filter banks available.
This file includes all the types the logdecoder script will decode.
unsigned int skywardStack(unsigned int stack)
Definition WIZ5500.h:318
Generic struct that contains a can protocol message.
uint8_t length
Length of the message content.
bool ext
Whether to use extended packet id.
32 Bit mask filter bank.
Definition Filters.h:99