Device Driver: CAN Controller JF0815#

Plan#

  • Wrap JF0815 HW wrapper in class with blocking interface

  • Use semaphores to block in either direction

  • Register ISRs for wakeups, internally (as an implementation detail)

Driver Usage: Transmit#

#include "can-jf0815.h"

#include <why-time.h>
#include <why-irq.h>

#include <print>

int main()
{
    Why::init();

    CAN_JF0815 can("my-can");
    while (true) {
        can.xmit(CANFrame{/*id*/42, /*dlc*/7, "1234567"});
        Why::sleep_milli(500);
    }
    return 0;
}

Driver Usage: Receive#

#include "can-jf0815.h"

#include <why-time.h>
#include <why-irq.h>

#include <print>

int main()
{
    Why::init();

    CAN_JF0815 can("my-can");

    while (true) {
        auto frame = can.recv();
        std::println("ID {}, dlc {}, data {}", frame.id, frame.dlc, std::string((const char*)frame.data, frame.dlc));
    }

    return 0;
}

Driver Implementation#

#pragma once

#include <can-jf0815-hw.h>
#include <why-semaphore.h>

#include <string>


class CAN_JF0815
{
public:
    CAN_JF0815(const std::string& can_iface);

    void xmit(const CANFrame&);                        // <-- blocks caller until frame xmitted
    CANFrame recv();                                   // <-- blocks caller until frame received

private:
    CAN_JF0815_HW _controller;    
    Why::Semaphore _sem_xmit_done;
    Why::Semaphore _sem_recv_ready;
    void _isr_xmit_done();
    void _isr_recv_ready();
};
#include "can-jf0815.h"

#include <why-irq.h>


CAN_JF0815::CAN_JF0815(const std::string& can_iface)
: _controller(can_iface),
  _sem_xmit_done(Why::Semaphore(0)),
  _sem_recv_ready(Why::Semaphore(0))
{
    Why::IRQ::connect(CAN_JF0815_HW::IRQ_XMIT_DONE, [this](int irqnum){_isr_xmit_done();});
    Why::IRQ::connect(CAN_JF0815_HW::IRQ_RECV_READY, [this](int irqnum){_isr_recv_ready();});
}

void CAN_JF0815::xmit(const CANFrame& frame)
{
    _controller.begin_xmit(frame);
    _sem_xmit_done.take();                             // <-- block until xmitted
}

CANFrame CAN_JF0815::recv()
{
    _sem_recv_ready.take();                            // <-- block until frame in recv buffer
    return _controller.commit_recv();
}

void CAN_JF0815::_isr_xmit_done()
{
    _sem_xmit_done.give();                             // <-- wakeup xmitter thread
}

void CAN_JF0815::_isr_recv_ready()
{
    _sem_recv_ready.give();                            // <-- wakeup receiver thread
}