Hardware: CAN Controller JF0815#

Software Paradigm#

  • Driver is a bare wrapper around bare hardware

  • Sending

    • User registers ISR for IRQ#1001

    • Drops CAN frame into device buffer

    • Kick controller (set flag in some device register)

    • ISR called when frame transmitted (⟶ ready for next transmission)

  • Receiving

    • User registers ISR for IRQ#1002

    • ISR called when device buffer contains received frame

Driver Interface#

#pragma once

#include <why-error.h>
#include <why-fd.h>
#include <jqueue.h>

#include <cstdint>
#include <atomic>


struct CANFrame
{
    uint32_t id;
    uint8_t dlc;
    uint8_t data[8];
};

class CAN_JF0815_HW
{
public:
    static const int IRQ_XMIT_DONE = 1001;
    static const int IRQ_RECV_READY = 1002;

public:
    CAN_JF0815_HW(const std::string& can_iface);
    ~CAN_JF0815_HW();

    void begin_xmit(const CANFrame&);
    CANFrame commit_recv();

    bool xmit_busy() { return _frame_to_send_valid.load(); }

private:
    void _hw_loop();
    void _hw_send_requested_func();
    void _hw_recv_ready_func();

private:
    CANFrame _frame_to_send;
    std::atomic<bool> _frame_to_send_valid = false;

    CANFrame _frame_received;
    std::atomic<bool> _frame_received_valid = false;

    Why::FD _can_socket;
    Why::FD _send_requested_event;
    Why::FD _quit_requested_event;

    std::thread _hw_thread;
};

Sample Send Program#

#include "can-jf0815-hw.h"

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

#include <print>

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

    CAN_JF0815_HW can("my-can");

    Why::IRQ::connect(CAN_JF0815_HW::IRQ_XMIT_DONE, [](int irqnum){
        std::println("xmit done (IRQ {})", irqnum);
    });

    can.begin_xmit(CANFrame{/*id*/42, /*dlc*/7, "1234567"});

    Why::pause();

    return 0;
}

Sample Receive Program#

#include "can-jf0815-hw.h"

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

#include <print>

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

    CAN_JF0815_HW can("my-can");

    Why::IRQ::connect(CAN_JF0815_HW::IRQ_RECV_READY, [&can](int irqnum){
        auto frame = can.commit_recv();
        std::println("ID {}, dlc {}, data {}", frame.id, frame.dlc, std::string((const char*)frame.data, frame.dlc));
    });

    Why::pause();

    return 0;
}