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:47
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:167
bool addFilter(FilterBank filter)
Adds a new filter to the bus, or returns false if there are no more filter banks available.
Driver for the VN100S IMU.
unsigned int skywardStack(unsigned int stack)
Definition WIZ5500.h:339
Generic struct that contains a can protocol message.
int32_t id
Id of the message without sequential infos.
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