25#include <kernel/scheduler/scheduler.h>
35using namespace std::chrono;
64 can->MCR &= ~CAN_MCR_SLEEP;
65 can->MCR |= CAN_MCR_INRQ;
67 while ((can->MSR & CAN_MSR_INAK) == 0)
72 can->MCR |= CAN_MCR_AWUM;
76 can->MCR |= CAN_MCR_ABOM;
80 can->MCR |= CAN_MCR_NART;
83 can->BTR &= ~CAN_BTR_BRP;
84 can->BTR &= ~CAN_BTR_TS1;
85 can->BTR &= ~CAN_BTR_TS2;
86 can->BTR &= ~CAN_BTR_SJW;
88 can->BTR |= bitTiming.
BRP & 0x3FF;
89 can->BTR |= ((bitTiming.
BS1 - 1) & 0xF) << 16;
90 can->BTR |= ((bitTiming.
BS2 - 1) & 0x7) << 20;
91 can->BTR |= ((bitTiming.
SJW - 1) & 0x3) << 24;
94 can->BTR |= CAN_BTR_LBKM;
97 can->FMR |= CAN_FMR_FINIT;
105 can->IER |= CAN_IER_FMPIE0 | CAN_IER_FMPIE1 | CAN_IER_TMEIE;
110 NVIC_EnableIRQ(CAN1_RX0_IRQn);
111 NVIC_SetPriority(CAN1_RX0_IRQn, 14);
113 NVIC_EnableIRQ(CAN1_RX1_IRQn);
114 NVIC_SetPriority(CAN1_RX1_IRQn, 14);
116 NVIC_EnableIRQ(CAN1_TX_IRQn);
117 NVIC_SetPriority(CAN1_TX_IRQn, 14);
119 else if (can == CAN2)
121 NVIC_EnableIRQ(CAN2_RX0_IRQn);
122 NVIC_SetPriority(CAN2_RX0_IRQn, 14);
124 NVIC_EnableIRQ(CAN2_RX1_IRQn);
125 NVIC_SetPriority(CAN2_RX1_IRQn, 14);
127 NVIC_EnableIRQ(CAN2_TX_IRQn);
128 NVIC_SetPriority(CAN2_TX_IRQn, 14);
133 LOG_ERR(ls,
"Unsupported peripheral");
149 float costOpt = 1000;
158 for (uint8_t N = 3; N <= 25; N++)
161 cfgIter.BRP = std::max(
162 std::min((
int)roundf(apbclk * 1.0f / (autoBt.baudRate * N) - 1),
169 std::min(std::max((
int)roundf(autoBt.samplePoint * N - 1), 1),
170 std::min(N - 2, 16));
172 cfgIter.BS2 = N - cfgIter.BS1 - 1;
175 fabs(apbclk * 1.0f / (N * (cfgIter.BRP + 1)) - autoBt.baudRate) /
177 float sp = (1 + cfgIter.BS1) * 1.0f / (1 + cfgIter.BS1 + cfgIter.BS2);
179 float spErrPercent = fabs(sp - autoBt.samplePoint) / autoBt.samplePoint;
182 float cost = BR_ERR_WEIGHT * brErrPercent +
183 SP_ERR_WEIGHT * spErrPercent +
184 N_ERR_WEIGHT * fabs(N - 10) / 25;
195 cfgOpt.SJW = fminf(ceilf(NOpt * 1.0f / 5), 4);
198 "Optimal Bit Timing Registers: BRP={}, BS1={}, BS2={}, SJW={}",
199 cfgOpt.BRP, cfgOpt.BS1, cfgOpt.BS2, cfgOpt.SJW);
201 float brTrue = apbclk * 1.0f / ((cfgOpt.BRP + 1) * NOpt);
202 float spTrue = (1 + cfgOpt.BS1) * 1.0f / (1 + cfgOpt.BS1 + cfgOpt.BS2);
205 "Optimal Bit Timing: BR_true={:.2f}, spTrue:{:.2f}%, "
206 "BR_error={:.2f}%, SP_error={:.2f}%",
207 brTrue / 1000, spTrue * 100,
208 fabsf(brTrue - autoBt.baudRate) / autoBt.baudRate * 100,
209 fabs(spTrue - autoBt.samplePoint) / autoBt.samplePoint * 100);
221 can->FMR &= ~CAN_FMR_FINIT;
222 can->MCR &= ~CAN_MCR_INRQ;
224 LOG_DEBUG(ls,
"Waiting for CAN bus synchronization...");
226 auto timeoutTs = timeout == milliseconds::zero()
227 ? steady_clock::time_point::max()
228 : steady_clock::now() + timeout;
231 while ((can->MSR & CAN_MSR_INAK) > 0)
233 if (steady_clock::now() > timeoutTs)
235 LOG_ERR(ls,
"CAN bus synchronization timed out after {} ms",
242 LOG_INFO(ls,
"CAN bus synchronized, init done!");
251 uint8_t index = filterIndex;
259 LOG_ERR(ls,
"Cannot add filter: CAN bus already initialized");
263 if (index >= NUM_FILTER_BANKS)
265 LOG_ERR(ls,
"Cannot add filter: no more filter banks available");
271 CAN1->sFilterRegister[index].FR1 = filter.
FR1;
272 CAN1->sFilterRegister[index].FR2 = filter.
FR2;
276 CAN1->FFA1R |= (filter.
fifo & 0x1) << index;
279 CAN1->FA1R |= 1 << index;
292 LOG_ERR(ls,
"CAN bus is not initialized!");
296 bool didWait =
false;
299 miosix::FastInterruptDisableLock d;
302 while ((can->TSR & CAN_TSR_TME) == 0)
305 waiting = Thread::IRQgetCurrentThread();
308 miosix::FastInterruptEnableLock e(d);
321 uint8_t mbxCode = (can->TSR & CAN_TSR_CODE) >> 24;
325 LOG_ERR(ls,
"Invalid TSR_CODE!");
329 uint32_t seq = txSeq++;
330 txMailboxSeq[mbxCode] = seq;
332 CAN_TxMailBox_TypeDef* mailbox = &can->sTxMailBox[mbxCode];
334 can->sTxMailBox[mbxCode].TIR &= CAN_TI0R_TXRQ;
337 can->sTxMailBox[mbxCode].TIR |= ((packet.
id & 0x1FFFFFFF) << 3);
338 can->sTxMailBox[mbxCode].TIR |= 1 << 2;
342 can->sTxMailBox[mbxCode].TIR |= ((packet.
id & 0x7FF) << 21);
345 can->sTxMailBox[mbxCode].TIR |= (packet.
rtr ? 1 : 0) << 1;
347 mailbox->TDTR = (packet.
length & 0xF);
354 for (uint8_t i = 0; i < packet.
length; ++i)
356 mailbox->TDLR |= packet.
data[i] << i * 8;
358 mailbox->TDHR |= packet.
data[i] << (i - 4) * 8;
361 can->sTxMailBox[mbxCode].TIR |= CAN_TI0R_TXRQ;
370 volatile uint32_t* RFR;
371 CAN_FIFOMailBox_TypeDef* mailbox;
373 mailbox = &can->sFIFOMailBox[
fifo];
380 status.
fifoFull = (*RFR & CAN_RF0R_FULL0) > 0;
388 if ((*RFR & CAN_RF0R_FMP0) > 0)
392 status.
rxStatus = *RFR & (CAN_RF0R_FULL0 | CAN_RF0R_FOVR0) >> 3;
393 status.
errCode = (can->ESR | CAN_ESR_LEC) >> 4;
396 p.
ext = (mailbox->RIR & CAN_RI0R_IDE) > 0;
397 p.
rtr = (mailbox->RIR & CAN_RI0R_RTR) > 0;
400 p.
id = (mailbox->RIR >> 3) & 0x1FFFFFFF;
402 p.
id = (mailbox->RIR >> 21) & 0x7FF;
404 p.
length = mailbox->RDTR & CAN_RDT0R_DLC;
406 for (uint8_t i = 0; i < p.
length; i++)
408 p.
data[i] = (mailbox->RDLR >> (i * 8)) & 0xFF;
410 p.
data[i] = (mailbox->RDHR >> ((i - 4) * 8)) & 0xFF;
412 *RFR |= CAN_RF0R_RFOM0;
419 miosix::Scheduler::IRQfindNextThread();
426 waiting->IRQwakeup();
#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...
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...
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 wakeTXThread()
Wakes the transmission thread. ONLY to be called from the Canbus TX interrupt handler routine.
bool init(std::chrono::milliseconds timeout=std::chrono::milliseconds::zero())
Exits initialization mode and starts CanBus operation.
static PrintLogger getLogger(const string &name)
PrintLogger getChild(const string &name)
CanbusDriver * canDrivers[2]
bool disablePeripheralClock(void *peripheral)
Disables a peripheral clock source from the APB1 and APB2 peripheral buses.
bool enablePeripheralClock(void *peripheral)
Enables a peripheral clock source from the APB1 and APB2 peripheral buses.
uint32_t getAPBPeripheralsClock(APB bus)
Computes the output clock frequency for peripherals on the given APB.
long long IRQgetOldTick()
Get the current time in milliseconds.
Driver for the VN100S IMU.
bool ext
Whether to use extended packet id.
Struct defining high level bit timing requirements. Register values will then be calculated automatic...
Struct specifying exact bit timing registers values.
Configuration struct for basic CanBus operation.
Base class for a Canbus filter bank.