Skyward boardcore
Loading...
Searching...
No Matches
CanDriver.cpp
Go to the documentation of this file.
1/* Copyright (c) 2018 Skyward Experimental Rocketry
2 * Author: Luca Erbetta
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 "CanDriver.h"
24
25#include <kernel/scheduler/scheduler.h>
26#include <utils/ClockUtils.h>
27#include <utils/KernelTime.h>
28
29#include <algorithm>
30#include <cmath>
31
32#include "CanInterrupt.h"
34
35namespace Boardcore
36{
37
38namespace Canbus
39{
40
42
44 AutoBitTiming bitTiming)
45 : CanbusDriver(can, config, calcBitTiming(bitTiming))
46{
47}
48
50 BitTiming bitTiming)
51 : can(can)
52{
53 if (can == CAN2)
54 {
55 // CAN2 also need the CAN1 clock
57 }
58 // Enable the peripheral clock
60
61 // Enter init mode
62 can->MCR &= ~CAN_MCR_SLEEP;
63 can->MCR |= CAN_MCR_INRQ;
64
65 while ((can->MSR & CAN_MSR_INAK) == 0)
66 ;
67
68 // Automatic wakeup when a new packet is available
69 if (config.awum)
70 can->MCR |= CAN_MCR_AWUM;
71
72 // Automatically recover from Bus-Off mode
73 if (config.abom)
74 can->MCR |= CAN_MCR_ABOM;
75
76 // Disable automatic retransmission
77 if (config.nart)
78 can->MCR |= CAN_MCR_NART;
79
80 // Bit timing configuration
81 can->BTR &= ~CAN_BTR_BRP;
82 can->BTR &= ~CAN_BTR_TS1;
83 can->BTR &= ~CAN_BTR_TS2;
84 can->BTR &= ~CAN_BTR_SJW;
85
86 can->BTR |= bitTiming.BRP & 0x3FF;
87 can->BTR |= ((bitTiming.BS1 - 1) & 0xF) << 16;
88 can->BTR |= ((bitTiming.BS2 - 1) & 0x7) << 20;
89 can->BTR |= ((bitTiming.SJW - 1) & 0x3) << 24;
90
91 if (config.loopback)
92 can->BTR |= CAN_BTR_LBKM;
93
94 // Enter filter initialization mode
95 can->FMR |= CAN_FMR_FINIT;
96
97 if (can == CAN1)
98 canDrivers[0] = this;
99 else
100 canDrivers[1] = this;
101
102 // Enable interrupts
103 can->IER |= CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | CAN_IER_TMEIE;
104
105 // Enable the corresponding interrupts
106 if (can == CAN1)
107 {
108 NVIC_EnableIRQ(CAN1_RX0_IRQn);
109 NVIC_SetPriority(CAN1_RX0_IRQn, 14);
110
111 NVIC_EnableIRQ(CAN1_RX1_IRQn);
112 NVIC_SetPriority(CAN1_RX1_IRQn, 14);
113
114 NVIC_EnableIRQ(CAN1_TX_IRQn);
115 NVIC_SetPriority(CAN1_TX_IRQn, 14);
116 }
117 else if (can == CAN2)
118 {
119 NVIC_EnableIRQ(CAN2_RX0_IRQn);
120 NVIC_SetPriority(CAN2_RX0_IRQn, 14);
121
122 NVIC_EnableIRQ(CAN2_RX1_IRQn);
123 NVIC_SetPriority(CAN2_RX1_IRQn, 14);
124
125 NVIC_EnableIRQ(CAN2_TX_IRQn);
126 NVIC_SetPriority(CAN2_TX_IRQn, 14);
127 }
128 else
129 {
130 PrintLogger ls = l.getChild("constructor");
131 LOG_ERR(ls, "Not supported peripheral");
132 }
133}
134
141
142CanbusDriver::BitTiming CanbusDriver::calcBitTiming(AutoBitTiming autoBt)
143{
144 PrintLogger ls = l.getChild("bittiming");
145
146 BitTiming cfgOpt;
147 float costOpt = 1000;
148 uint8_t NOpt = 5;
149
150 BitTiming cfgIter;
151 cfgIter.SJW = 0;
153
154 // Iterate over the possible number of quanta in a bit to find the best
155 // settings
156 for (uint8_t N = 3; N <= 25; N++)
157 {
158 // Calc optimal baud rate prescaler
159 cfgIter.BRP = std::max(
160 std::min((int)roundf(apbclk * 1.0f / (autoBt.baudRate * N) - 1),
161 1 << 10),
162 1);
163
164 // Given N, calculate BS1 and BS2 that result in a sample time as
165 // close as possible to the target one
166 cfgIter.BS1 =
167 std::min(std::max((int)roundf(autoBt.samplePoint * N - 1), 1),
168 std::min(N - 2, 16));
169
170 cfgIter.BS2 = N - cfgIter.BS1 - 1;
171
172 float brErrPercent =
173 fabs(apbclk * 1.0f / (N * (cfgIter.BRP + 1)) - autoBt.baudRate) /
174 autoBt.baudRate;
175 float sp = (1 + cfgIter.BS1) * 1.0f / (1 + cfgIter.BS1 + cfgIter.BS2);
176
177 float spErrPercent = fabs(sp - autoBt.samplePoint) / autoBt.samplePoint;
178
179 // Calculate the cost function over N
180 float cost = BR_ERR_WEIGHT * brErrPercent +
181 SP_ERR_WEIGHT * spErrPercent +
182 N_ERR_WEIGHT * fabs(N - 10) / 25;
183
184 // Find config that minimizes the cost function
185 if (cost < costOpt)
186 {
187 cfgOpt = cfgIter;
188 costOpt = cost;
189 NOpt = N;
190 }
191 }
192
193 cfgOpt.SJW = fminf(ceilf(NOpt * 1.0f / 5), 4);
194
195 LOG_DEBUG(ls,
196 "Optimal Bit Timing Registers: BRP={}, BS1={}, BS2={}, SJW={}",
197 cfgOpt.BRP, cfgOpt.BS1, cfgOpt.BS2, cfgOpt.SJW);
198
199 float brTrue = apbclk * 1.0f / ((cfgOpt.BRP + 1) * NOpt);
200 float spTrue = (1 + cfgOpt.BS1) * 1.0f / (1 + cfgOpt.BS1 + cfgOpt.BS2);
201
202 LOG_DEBUG(ls,
203 "Optimal Bit Timing: BR_true={:.2f}, spTrue:{:.2f}%, "
204 "BR_error={:.2f}%, SP_error={:.2f}%",
205 brTrue / 1000, spTrue * 100,
206 fabsf(brTrue - autoBt.baudRate) / autoBt.baudRate * 100,
207 fabs(spTrue - autoBt.samplePoint) / autoBt.samplePoint * 100);
208
209 return cfgOpt;
210}
211
213{
214 if (isInit)
215 return;
216
217 PrintLogger ls = l.getChild("init");
218
219 can->FMR &= ~CAN_FMR_FINIT; // Exit filter init mode
220 can->MCR &= ~CAN_MCR_INRQ; // Enable canbus
221
222 // Wait until the can peripheral synchronizes with the bus
223
224 LOG_DEBUG(ls, "Waiting for canbus synchronization...");
225 while ((can->MSR & CAN_MSR_INAK) > 0)
226 Thread::sleep(1);
227
228 LOG_INFO(ls, "Canbus synchronized! Init done!");
229
230 isInit = true;
231}
232
234{
235 PrintLogger ls = l.getChild("addfilter");
236 uint8_t index = filterIndex;
237
238 // CAN2 filters start from the 15th position
239 if (can == CAN2)
240 index = index + 14;
241
242 if (isInit)
243 {
244 LOG_ERR(ls, "Cannot add filter: canbus already initialized");
245 return false;
246 }
247
248 if (index >= NUM_FILTER_BANKS)
249 {
250 LOG_ERR(ls, "Cannot add filter: no more filter banks available");
251 return false;
252 }
253
254 // NOTE: the filters are set in CAN1 peripheral because the filter registers
255 // between the peripherals are in common.
256 CAN1->sFilterRegister[index].FR1 = filter.FR1;
257 CAN1->sFilterRegister[index].FR2 = filter.FR2;
258
259 CAN1->FM1R |= (filter.mode == FilterMode::MASK ? 0 : 1) << index;
260 CAN1->FS1R |= (filter.scale == FilterScale::DUAL16 ? 0 : 1) << index;
261 CAN1->FFA1R |= (filter.fifo & 0x1) << index;
262
263 // Enable the filter
264 CAN1->FA1R |= 1 << index;
265
266 ++filterIndex;
267
268 return true;
269}
270
272{
273 PrintLogger ls = l.getChild("send");
274
275 if (!isInit)
276 {
277 LOG_ERR(ls, "Canbus is not initialized!");
278 return 0;
279 }
280
281 bool didWait = false;
282
283 {
284 miosix::FastInterruptDisableLock d;
285
286 // Wait until there is an empty mailbox available to use
287 while ((can->TSR & CAN_TSR_TME) == 0)
288 {
289 didWait = true;
290 waiting = Thread::IRQgetCurrentThread();
291 Thread::IRQwait();
292 {
293 miosix::FastInterruptEnableLock e(d);
294 Thread::yield();
295 }
296 }
297 }
298
299 if (didWait)
300 {
301 // Warn that the function blocked. We are probably transmitting too fast
302 LOG_WARN_ASYNC(ls, "Had to wait for an empty mailbox!");
303 }
304
305 // Index of first empty mailbox
306 uint8_t mbxCode = (can->TSR & CAN_TSR_CODE) >> 24;
307
308 if (mbxCode > 2)
309 {
310 LOG_ERR(ls, "Error! Invalid TSR_CODE!");
311 return 0;
312 }
313
314 uint32_t seq = txSeq++;
315 txMailboxSeq[mbxCode] = seq;
316
317 CAN_TxMailBox_TypeDef* mailbox = &can->sTxMailBox[mbxCode];
318
319 can->sTxMailBox[mbxCode].TIR &= CAN_TI0R_TXRQ;
320 if (packet.ext)
321 {
322 can->sTxMailBox[mbxCode].TIR |= ((packet.id & 0x1FFFFFFF) << 3);
323 can->sTxMailBox[mbxCode].TIR |= 1 << 2;
324 }
325 else
326 {
327 can->sTxMailBox[mbxCode].TIR |= ((packet.id & 0x7FF) << 21);
328 }
329
330 can->sTxMailBox[mbxCode].TIR |= (packet.rtr ? 1 : 0) << 1;
331
332 mailbox->TDTR = (packet.length & 0xF);
333
334 // Reset data registers
335 mailbox->TDLR = 0;
336 mailbox->TDHR = 0;
337
338 // Fill data registers
339 for (uint8_t i = 0; i < packet.length; ++i)
340 if (i < 4)
341 mailbox->TDLR |= packet.data[i] << i * 8;
342 else
343 mailbox->TDHR |= packet.data[i] << (i - 4) * 8;
344
345 // Finally send the packet
346 can->sTxMailBox[mbxCode].TIR |= CAN_TI0R_TXRQ;
347
348 return seq;
349}
350
352{
353 CanRXStatus status;
354 status.fifo = fifo;
355 volatile uint32_t* RFR;
356 CAN_FIFOMailBox_TypeDef* mailbox;
357
358 mailbox = &can->sFIFOMailBox[fifo];
359 if (fifo == 0)
360 RFR = &can->RF0R;
361 else
362 RFR = &can->RF1R;
363
364 status.fifoOverrun = (*RFR & CAN_RF0R_FOVR0) > 0;
365 status.fifoFull = (*RFR & CAN_RF0R_FULL0) > 0;
366
367 CanPacket p;
368 bool hppw = false;
369
370 // Note: Bit position definitions are the same for both FIFOs
371 // eg: CAN_RF0R_FMP0 == CAN_RF1R_FMP1
372
373 if ((*RFR & CAN_RF0R_FMP0) > 0)
374 {
376
377 status.rxStatus = *RFR & (CAN_RF0R_FULL0 | CAN_RF0R_FOVR0) >> 3;
378 status.errCode = (can->ESR | CAN_ESR_LEC) >> 4;
379 status.rxErrCounter = (can->ESR | CAN_ESR_REC) >> 24;
380
381 p.ext = (mailbox->RIR & CAN_RI0R_IDE) > 0;
382 p.rtr = (mailbox->RIR & CAN_RI0R_RTR) > 0;
383
384 if (p.ext)
385 p.id = (mailbox->RIR >> 3) & 0x1FFFFFFF;
386 else
387 p.id = (mailbox->RIR >> 21) & 0x7FF;
388
389 p.length = mailbox->RDTR & CAN_RDT0R_DLC;
390
391 for (uint8_t i = 0; i < p.length; i++)
392 if (i < 4) // Low register
393 p.data[i] = (mailbox->RDLR >> (i * 8)) & 0xFF;
394 else // High register
395 p.data[i] = (mailbox->RDHR >> ((i - 4) * 8)) & 0xFF;
396
397 *RFR |= CAN_RF0R_RFOM0;
398
399 // Put the message into the queue
400 bufRxPackets.IRQput(CanRXPacket{p, status}, hppw);
401 }
402
403 if (hppw)
404 miosix::Scheduler::IRQfindNextThread();
405}
406
408{
409 if (waiting)
410 {
411 waiting->IRQwakeup();
412 waiting = 0;
413 }
414}
415
416} // namespace Canbus
417
418} // namespace Boardcore
void int fifo
#define LOG_INFO(logger,...)
#define LOG_ERR(logger,...)
#define LOG_WARN_ASYNC(logger,...)
#define LOG_DEBUG(logger,...)
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...
CanbusDriver(CAN_TypeDef *can, CanbusConfig config, AutoBitTiming bitTiming)
Construct a new Canbus object, automatically calculating timing register values from high level requi...
Definition CanDriver.cpp:43
void handleRXInterrupt(int fifo)
Handles an incoming RX interrupt. ONLY to be called from the Canbus RX interrupt handler routine.
bool addFilter(FilterBank filter)
Adds a new filter to the bus, or returns false if there are no more filter banks available.
~CanbusDriver()
Disables the peripheral clock.
void init()
Exits initialization mode and starts CanBus operation.
void wakeTXThread()
Wakes the transmission thread. ONLY to be called from the Canbus TX interrupt handler routine.
static PrintLogger getLogger(const string &name)
PrintLogger getChild(const string &name)
CanbusDriver * canDrivers[2]
PrintLogger l
Definition CanDriver.cpp:41
bool disablePeripheralClock(void *peripheral)
Disables a peripheral clock source from the APB1 and APB2 peripheral buses.
Definition ClockUtils.h:531
bool enablePeripheralClock(void *peripheral)
Enables a peripheral clock source from the APB1 and APB2 peripheral buses.
Definition ClockUtils.h:155
uint32_t getAPBPeripheralsClock(APB bus)
Computes the output clock frequency for peripherals on the given APB.
Definition ClockUtils.h:73
long long IRQgetOldTick()
Get the current time in milliseconds.
Definition KernelTime.h:53
This file includes all the types the logdecoder script will decode.
bool ext
Whether to use extended packet id.
Struct defining high level bit timing requirements. Register values will then be calculated automatic...
Definition CanDriver.h:81
Struct specifying exact bit timing registers values.
Definition CanDriver.h:99
Configuration struct for basic CanBus operation.
Definition CanDriver.h:62
Base class for a Canbus filter bank.
Definition Filters.h:49