Skip to content

Commit

Permalink
refactor: Improve CAN Demo projects (#167)
Browse files Browse the repository at this point in the history
* refactor: Improve CAN Demo project names

Rename Foo to Send and Bar to Receive to be clearer about their purposes. Update CAN bus name from `veh` to `demobus` to emphasize this demo. Improves READMEs

* fixes filenames

* initial periphs

* Linux MCAL

* Demo CAN Work, Blink for linux

* adds button to Demo CAN Send cli

* renames postbuild to fix casing

* Fix hardcoded ID in mcal/linux. Improve README to add instructions. Add IO for stm32 & CLI platforms.

* Simplify message to use just one field. Fix button type in Send

* Print CAN bytes only up to frame DLC

* updates Receive pinout & adds docs for stm platform

* Add link to WSL article

* Remove Linux Toolchain cmds since they did not fix their listed problem

* Use std::format instead of stream operator.

* Simplify CMakeLists

* Update to new config scheme

* Update to new config scheme
  • Loading branch information
BlakeFreer authored Nov 8, 2024
1 parent 72c9b35 commit 7a6a00a
Show file tree
Hide file tree
Showing 68 changed files with 1,053 additions and 599 deletions.
6 changes: 6 additions & 0 deletions firmware/mcal/linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
add_library(mcal-linux STATIC)

target_include_directories(mcal-linux PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../..)

add_subdirectory(periph)
target_link_libraries(mcal-linux INTERFACE shared)
1 change: 1 addition & 0 deletions firmware/mcal/linux/PostBuild.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# No special postbuild actions
11 changes: 11 additions & 0 deletions firmware/mcal/linux/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Linux MCAL

A more advanced CLI. CAN messages are sent over virtual can (vcan) to enable true CAN communication between processes.

## Note on Namespace Name

The namespace is `mcal::lnx`, not `mcal::linux` as one might expect. This is because g++ defines `linux=1`, preventing `linux` from being used as an identifier.

## WSL Compatability

You can run this platform through the Windows Subsystem for Linux but must recompile your WSL kernel to include the CAN modules. See <https://macformula.github.io/racecar/tutorials/wsl-can/>.
3 changes: 3 additions & 0 deletions firmware/mcal/linux/Toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Blake Freer
# November 8, 2024
# No cmake instructions as the default Linux compiler should work.
10 changes: 10 additions & 0 deletions firmware/mcal/linux/periph/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
target_sources(mcal-linux
PRIVATE
adc.cc
can.cc
digital_input.cc
digital_output.cc
)

add_subdirectory(vcan)
target_link_libraries(mcal-linux PUBLIC vcan pthread)
26 changes: 26 additions & 0 deletions firmware/mcal/linux/periph/adc.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <cstdint>
#include <format>
#include <iostream>

#include "adc.h"

namespace mcal::lnx::periph {

ADCInput::ADCInput(std::string name) : name_(name) {}

void ADCInput::Start() {
std::cout << std::format("Reading ADC \"{}\"\n", name_);
}

uint32_t ADCInput::Read() {
Start();

uint32_t adc_val;
std::cout << " | Enter an unsigned 32-bit value: ";
std::cin >> adc_val;
std::cout << " | Obtained value " << adc_val << std::endl;

return adc_val;
}

} // namespace mcal::lnx::periph
21 changes: 21 additions & 0 deletions firmware/mcal/linux/periph/adc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <cstdint>
#include <string>

#include "shared/periph/adc.h"

namespace mcal::lnx::periph {

class ADCInput : public shared::periph::ADCInput {
public:
ADCInput(std::string name);

void Start() override;
uint32_t Read() override;

private:
std::string name_;
};

} // namespace mcal::lnx::periph
101 changes: 101 additions & 0 deletions firmware/mcal/linux/periph/can.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#include <linux/can.h>
#include <sys/types.h>

#include <cstring>
#include <format>
#include <iomanip>
#include <mutex>
#include <string>
#include <thread>

#include "can.h"
#include "shared/comms/can/raw_can_msg.h"
#include "vcan/vcan.h"

static std::string message_to_string(const shared::can::RawCanMsg& msg) {
std::string str = std::format("[{:02X}]", msg.header.id);
for (int i = 0; i < sizeof(msg.header.data_len); ++i) {
str += std::format(" {:02X}", msg.data[i]);
}
return str;
}

static can_frame to_can_frame(const shared::can::RawCanMsg& msg) {
struct can_frame frame{.can_id = msg.header.id,
.can_dlc = msg.header.data_len};
std::memcpy(&frame.data, msg.data, 8);
return frame;
}

static shared::can::RawCanMsg from_can_frame(const can_frame& frame) {
shared::can::RawCanMsg msg;
msg.header = {
.id = frame.can_id,
.data_len = frame.can_dlc,
.is_extended_frame = static_cast<bool>(frame.can_id & CAN_EFF_FLAG),
};
msg.tick_timestamp = 0;

std::copy(frame.data, frame.data + 8, msg.data);

return msg;
}

namespace mcal::lnx::periph {

CanBase::CanBase(std::string iface) : socket_(iface) {}

void CanBase::Setup() {
socket_.Open();

// Another thread handles reading
reader_thread_ = std::thread(&CanBase::StartReading, this);
}

void CanBase::Send(const shared::can::RawCanMsg& can_tx_msg) {
std::cout << std::format("CanBase {}: Sending\n| {}", socket_.GetIface(),
message_to_string(can_tx_msg))
<< std::endl;

auto frame = to_can_frame(can_tx_msg);
socket_.Write(&frame);
}

void CanBase::ReadQueue(shared::can::RawCanMsg can_rx_msgs[], size_t len) {
uint16_t msg_idx = 0;
std::lock_guard<std::mutex> lock(queue_access_);
while (!can_queue_.empty() && msg_idx < len) {
can_rx_msgs[msg_idx] = can_queue_.front();

std::cout << std::format("CanBase {}: Received\n| {}",
socket_.GetIface(),
message_to_string(can_rx_msgs[msg_idx]))
<< std::endl;

can_queue_.pop();
msg_idx++;
}
}

void CanBase::StartReading() {
struct can_frame frame;
shared::can::RawCanMsg raw_msg;

while (true) {
// Block until a frame arrives
ssize_t bytes_read = socket_.Read(&frame);
if (bytes_read == -1) {
perror("Error reading from CAN socket.");
exit(1);
}

raw_msg = from_can_frame(frame);

{ // push RawCanMsg to queue
std::lock_guard<std::mutex> lock(queue_access_);
can_queue_.push(raw_msg);
}
}
}

} // namespace mcal::lnx::periph
31 changes: 31 additions & 0 deletions firmware/mcal/linux/periph/can.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#pragma once

#include <mutex>
#include <queue>
#include <thread>

#include "mcal/linux/periph/vcan/vcan.h"
#include "shared/comms/can/raw_can_msg.h"
#include "shared/periph/can.h"
#include "vcan/vcan.h"

namespace mcal::lnx::periph {

class CanBase : public shared::periph::CanBase {
public:
CanBase(std::string can_iface);

void Setup();
void Send(const shared::can::RawCanMsg&);
void ReadQueue(shared::can::RawCanMsg[], size_t len);

private:
struct vcan::VcanSocket socket_;

std::queue<shared::can::RawCanMsg> can_queue_;
std::mutex queue_access_;
std::thread reader_thread_;

void StartReading();
};
} // namespace mcal::lnx::periph
23 changes: 23 additions & 0 deletions firmware/mcal/linux/periph/digital_input.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#include <format>
#include <iostream>
#include <string>

#include "digital_input.h"

namespace mcal::lnx::periph {

DigitalInput::DigitalInput(std::string name) : name_(name) {};

bool DigitalInput::Read() {
int value;

std::cout << std::format("Reading DigitalInput \"{}\"", name_) << std::endl;
std::cout << "| Enter 0 for False, 1 for True: ";
std::cin >> value;
std::cout << std::format("| Value was {}", value ? "true" : "false")
<< std::endl;

return value;
}

} // namespace mcal::lnx::periph
19 changes: 19 additions & 0 deletions firmware/mcal/linux/periph/digital_input.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#pragma once

#include <string>

#include "shared/periph/gpio.h"

namespace mcal::lnx::periph {

class DigitalInput : public shared::periph::DigitalInput {
public:
DigitalInput(std::string name);

bool Read() override;

private:
std::string name_;
};

} // namespace mcal::lnx::periph
25 changes: 25 additions & 0 deletions firmware/mcal/linux/periph/digital_output.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <format>
#include <iostream>
#include <string>

#include "digital_output.h"

namespace mcal::lnx::periph {

DigitalOutput::DigitalOutput(std::string name) : name_(name) {}

void DigitalOutput::Set(bool value) {
std::cout << std::format("DigitalOutput \"{}\" => {}", name_,
value ? "true" : "false")
<< std::endl;
}

void DigitalOutput::SetHigh() {
Set(true);
}

void DigitalOutput::SetLow() {
Set(false);
}

} // namespace mcal::lnx::periph
21 changes: 21 additions & 0 deletions firmware/mcal/linux/periph/digital_output.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <iostream>
#include <string>

#include "shared/periph/gpio.h"

namespace mcal::lnx::periph {
class DigitalOutput : public shared::periph::DigitalOutput {
public:
DigitalOutput(std::string name);

void Set(bool value) override;
void SetHigh() override;
void SetLow() override;

private:
std::string name_;
};

} // namespace mcal::lnx::periph
1 change: 1 addition & 0 deletions firmware/mcal/linux/periph/vcan/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_library(vcan STATIC vcan.cc)
60 changes: 60 additions & 0 deletions firmware/mcal/linux/periph/vcan/vcan.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#include <linux/can.h>
#include <linux/can/raw.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstdio>
#include <cstring>
#include <format>
#include <iostream>
#include <string>

#include "vcan.h"

namespace mcal::lnx::periph::vcan {

VcanSocket::VcanSocket(std::string iface) : iface_(iface) {}

int VcanSocket::Open() {
socket_ = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (socket_ == -1) {
perror("Error while opening socket.");
return -1;
}
strcpy(ifr_.ifr_name, iface_.c_str());
ioctl(socket_, SIOGIFINDEX, &ifr_);

addr_.can_family = AF_CAN;
addr_.can_ifindex = ifr_.ifr_ifindex;

int bind_status = bind(socket_, (struct sockaddr*)&addr_, sizeof(addr_));
if (bind_status == -1) {
perror("Error in socket bind.");
return -1;
}

std::cout << std::format("Opened \"{}\" at index {}.\n", iface_,
addr_.can_ifindex);

return 0;
}

VcanSocket::~VcanSocket() {
close(socket_);
}

int VcanSocket::Write(struct can_frame* frame) {
return write(socket_, frame, sizeof(struct can_frame));
}

int VcanSocket::Read(struct can_frame* frame) {
return read(socket_, frame, sizeof(struct can_frame));
}

std::string VcanSocket::GetIface() const {
return iface_;
}

} // namespace mcal::lnx::periph::vcan
Loading

0 comments on commit 7a6a00a

Please sign in to comment.