mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-10-28 00:53:45 +08:00
Power
Manage device power, parked services and vehicle-specific Panda wake behavior. Co-authored-by: Adeeb Shihadeh <adeebshihadeh@gmail.com> Co-authored-by: Daniel Koepping <elkoled@gmail.com> Co-authored-by: firestarsdog <229254897+firestarsdog@users.noreply.github.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -21,6 +22,7 @@ public:
|
||||
|
||||
static cereal::InitData::DeviceType get_device_type() {
|
||||
static const std::map<std::string, cereal::InitData::DeviceType> device_map = {
|
||||
{"tici", cereal::InitData::DeviceType::TICI},
|
||||
{"tizi", cereal::InitData::DeviceType::TIZI},
|
||||
{"mici", cereal::InitData::DeviceType::MICI}
|
||||
};
|
||||
@@ -29,6 +31,9 @@ public:
|
||||
return it->second;
|
||||
}
|
||||
|
||||
static int get_voltage() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/in1_input").c_str()); }
|
||||
static int get_current() { return std::atoi(util::read_file("/sys/class/hwmon/hwmon1/curr1_input").c_str()); }
|
||||
|
||||
static std::string get_serial() {
|
||||
static std::string serial("");
|
||||
if (serial.empty()) {
|
||||
@@ -49,7 +54,7 @@ public:
|
||||
|
||||
static void set_ir_power(int percent) {
|
||||
auto device = get_device_type();
|
||||
if (device == cereal::InitData::DeviceType::TIZI) {
|
||||
if (device == cereal::InitData::DeviceType::TICI || device == cereal::InitData::DeviceType::TIZI) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ int main(int argc, char *argv[]) {
|
||||
assert(err == 0);
|
||||
}
|
||||
|
||||
std::string serial = (argc > 1) ? argv[1] : "";
|
||||
pandad_main_thread(serial);
|
||||
std::vector<std::string> serials;
|
||||
for (int i = 1; i < argc; ++i) serials.emplace_back(argv[i]);
|
||||
pandad_main_thread(serials);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -11,12 +13,51 @@
|
||||
#define SPI_BUF_SIZE 4096
|
||||
|
||||
|
||||
class PandaSpiHandle {
|
||||
class PandaCommsHandle {
|
||||
public:
|
||||
virtual ~PandaCommsHandle() = default;
|
||||
std::string hw_serial;
|
||||
std::atomic<bool> connected = true;
|
||||
std::atomic<bool> comms_healthy = true;
|
||||
virtual int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT) = 0;
|
||||
virtual int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT) = 0;
|
||||
virtual int bulk_write(unsigned char endpoint, unsigned char *data, int length, unsigned int timeout=TIMEOUT) = 0;
|
||||
virtual int bulk_read(unsigned char endpoint, unsigned char *data, int length, unsigned int timeout=TIMEOUT) = 0;
|
||||
virtual void cleanup() = 0;
|
||||
};
|
||||
|
||||
template <typename SpiFactory, typename UsbFactory>
|
||||
std::unique_ptr<PandaCommsHandle> open_panda_handle(const std::string &serial, SpiFactory spi, UsbFactory usb) {
|
||||
try {
|
||||
return usb(serial);
|
||||
} catch (const std::exception &) {
|
||||
return spi(serial);
|
||||
}
|
||||
}
|
||||
|
||||
struct libusb_context;
|
||||
struct libusb_device_handle;
|
||||
|
||||
class PandaUsbHandle : public PandaCommsHandle {
|
||||
public:
|
||||
explicit PandaUsbHandle(std::string serial);
|
||||
~PandaUsbHandle() override;
|
||||
int control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout=TIMEOUT) override;
|
||||
int control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout=TIMEOUT) override;
|
||||
int bulk_write(unsigned char endpoint, unsigned char *data, int length, unsigned int timeout=TIMEOUT) override;
|
||||
int bulk_read(unsigned char endpoint, unsigned char *data, int length, unsigned int timeout=TIMEOUT) override;
|
||||
void cleanup() override;
|
||||
static std::vector<std::string> list();
|
||||
private:
|
||||
libusb_context *ctx = nullptr;
|
||||
libusb_device_handle *dev_handle = nullptr;
|
||||
std::recursive_mutex hw_lock;
|
||||
bool claimed = false;
|
||||
void handle_usb_issue(int err);
|
||||
};
|
||||
|
||||
class PandaSpiHandle : public PandaCommsHandle {
|
||||
public:
|
||||
PandaSpiHandle(std::string serial);
|
||||
~PandaSpiHandle();
|
||||
|
||||
|
||||
@@ -23,15 +23,17 @@ void PandaSafety::updateMultiplexingMode() {
|
||||
// Initialize to ELM327 without OBD multiplexing for initial fingerprinting
|
||||
if (!initialized_) {
|
||||
prev_obd_multiplexing_ = false;
|
||||
panda_->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U);
|
||||
for (auto *panda : pandas_) panda->set_safety_model(cereal::CarParams::SafetyModel::ELM327, 1U);
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
// Switch between multiplexing modes based on the OBD multiplexing request
|
||||
bool obd_multiplexing_requested = params_.getBool("ObdMultiplexingEnabled");
|
||||
if (obd_multiplexing_requested != prev_obd_multiplexing_) {
|
||||
const uint16_t safety_param = obd_multiplexing_requested ? 0U : 1U;
|
||||
panda_->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param);
|
||||
for (size_t i = 0; i < pandas_.size(); ++i) {
|
||||
const uint16_t safety_param = (i > 0 || !obd_multiplexing_requested) ? 1U : 0U;
|
||||
pandas_[i]->set_safety_model(cereal::CarParams::SafetyModel::ELM327, safety_param);
|
||||
}
|
||||
prev_obd_multiplexing_ = obd_multiplexing_requested;
|
||||
params_.putBool("ObdMultiplexingChanged", true);
|
||||
}
|
||||
@@ -61,10 +63,56 @@ void PandaSafety::setSafetyMode(const std::string ¶ms_string) {
|
||||
auto safety_configs = car_params.getSafetyConfigs();
|
||||
uint16_t alternative_experience = car_params.getAlternativeExperience();
|
||||
|
||||
cereal::CarParams::SafetyModel safety_model = safety_configs[0].getSafetyModel();
|
||||
uint16_t safety_param = safety_configs[0].getSafetyParam();
|
||||
alternative_experience_ = alternative_experience;
|
||||
safety_config_count_ = safety_configs.size();
|
||||
safety_slots_.clear();
|
||||
for (size_t i = 0; i < pandas_.size(); ++i) {
|
||||
auto model = cereal::CarParams::SafetyModel::SILENT;
|
||||
uint16_t param = 0U;
|
||||
if (i < safety_configs.size()) {
|
||||
model = safety_configs[i].getSafetyModel();
|
||||
param = safety_configs[i].getSafetyParam();
|
||||
}
|
||||
safety_slots_.emplace_back(model, param);
|
||||
LOGW("Panda %zu: setting safety model: %d, param: %d, alternative experience: %d", i, (int)model, param, alternative_experience);
|
||||
pandas_[i]->set_alternative_experience(alternative_experience);
|
||||
pandas_[i]->set_safety_model(model, param);
|
||||
}
|
||||
}
|
||||
|
||||
LOGW("setting safety model: %d, param: %d, alternative experience: %d", (int)safety_model, safety_param, alternative_experience);
|
||||
panda_->set_alternative_experience(alternative_experience);
|
||||
panda_->set_safety_model(safety_model, safety_param);
|
||||
Panda *PandaSafety::aolOwner() {
|
||||
if (pandas_.size() == 1) return pandas_[0];
|
||||
if (pandas_.size() != 2 || !safety_configured_ || safety_config_count_ != pandas_.size() ||
|
||||
safety_slots_.size() != pandas_.size()) return nullptr;
|
||||
if (pandas_[0]->hw_serial().empty() || pandas_[1]->hw_serial().empty() ||
|
||||
pandas_[0]->hw_serial() == pandas_[1]->hw_serial()) return nullptr;
|
||||
Panda *owner = nullptr;
|
||||
for (size_t i = 0; i < pandas_.size(); ++i) {
|
||||
auto *panda = pandas_[i];
|
||||
if (!panda->connected() || !panda->comms_healthy()) return nullptr;
|
||||
const auto health = panda->get_state();
|
||||
if (!health || health->alternative_experience_pkt != alternative_experience_) return nullptr;
|
||||
const auto [model, param] = safety_slots_[i];
|
||||
const bool passive = model == cereal::CarParams::SafetyModel::NO_OUTPUT || model == cereal::CarParams::SafetyModel::SILENT;
|
||||
if (passive) {
|
||||
if (param != 0U || (health->safety_mode_pkt != (uint8_t)cereal::CarParams::SafetyModel::NO_OUTPUT &&
|
||||
health->safety_mode_pkt != (uint8_t)cereal::CarParams::SafetyModel::SILENT) ||
|
||||
health->safety_param_pkt != 0U || (health->flags_pkt & HEALTH_FLAG_CONTROLS_ALLOWED) != 0U) return nullptr;
|
||||
} else {
|
||||
if (owner != nullptr || health->safety_mode_pkt != (uint8_t)model || health->safety_param_pkt != param) return nullptr;
|
||||
owner = panda;
|
||||
}
|
||||
}
|
||||
return owner;
|
||||
}
|
||||
|
||||
bool PandaSafety::aolStatusMatches(Panda *owner, const aol_safety_health_t &status) const {
|
||||
if (pandas_.size() == 1) return owner == pandas_[0];
|
||||
if (pandas_.size() != 2 || safety_config_count_ != pandas_.size() || safety_slots_.size() != pandas_.size()) return false;
|
||||
for (size_t i = 0; i < pandas_.size(); ++i) {
|
||||
if (pandas_[i] == owner) {
|
||||
return status.safety_mode == (uint8_t)safety_slots_[i].first && status.safety_param == safety_slots_[i].second;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#include "selfdrive/pandad/panda_comms.h"
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "common/swaglog.h"
|
||||
|
||||
namespace {
|
||||
bool is_panda(const libusb_device_descriptor &desc) {
|
||||
return (desc.idVendor == 0x3801 || desc.idVendor == 0xbbaa) && desc.idProduct == 0xddcc;
|
||||
}
|
||||
|
||||
unsigned int usb_timeout(unsigned int timeout) {
|
||||
return timeout == 0 ? 500 : timeout;
|
||||
}
|
||||
|
||||
std::string usb_serial(libusb_device_handle *handle, uint8_t index) {
|
||||
unsigned char serial[26] = {};
|
||||
int count = libusb_get_string_descriptor_ascii(handle, index, serial, sizeof(serial));
|
||||
return count > 0 ? std::string(reinterpret_cast<char *>(serial), count) : std::string();
|
||||
}
|
||||
}
|
||||
|
||||
PandaUsbHandle::PandaUsbHandle(std::string serial) {
|
||||
if (libusb_init(&ctx) != 0) throw std::runtime_error("libusb initialization failed");
|
||||
libusb_device **devices = nullptr;
|
||||
ssize_t count = libusb_get_device_list(ctx, &devices);
|
||||
if (count >= 0) {
|
||||
for (ssize_t i = 0; i < count; ++i) {
|
||||
libusb_device_descriptor desc = {};
|
||||
if (libusb_get_device_descriptor(devices[i], &desc) != 0 || !is_panda(desc)) continue;
|
||||
libusb_device_handle *candidate = nullptr;
|
||||
if (libusb_open(devices[i], &candidate) != 0) continue;
|
||||
auto candidate_serial = usb_serial(candidate, desc.iSerialNumber);
|
||||
if (!candidate_serial.empty() && (serial.empty() || serial == candidate_serial)) {
|
||||
dev_handle = candidate;
|
||||
hw_serial = candidate_serial;
|
||||
break;
|
||||
}
|
||||
libusb_close(candidate);
|
||||
}
|
||||
libusb_free_device_list(devices, 1);
|
||||
}
|
||||
if (dev_handle != nullptr) {
|
||||
int err = 0;
|
||||
if (libusb_kernel_driver_active(dev_handle, 0) == 1) err = libusb_detach_kernel_driver(dev_handle, 0);
|
||||
if (err == 0) err = libusb_set_configuration(dev_handle, 1);
|
||||
if (err == 0) err = libusb_claim_interface(dev_handle, 0);
|
||||
if (err == 0) {
|
||||
claimed = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
cleanup();
|
||||
throw std::runtime_error("USB panda unavailable");
|
||||
}
|
||||
|
||||
PandaUsbHandle::~PandaUsbHandle() {
|
||||
cleanup();
|
||||
}
|
||||
|
||||
void PandaUsbHandle::cleanup() {
|
||||
std::lock_guard<std::recursive_mutex> guard(hw_lock);
|
||||
connected = false;
|
||||
if (dev_handle != nullptr) {
|
||||
if (claimed) libusb_release_interface(dev_handle, 0);
|
||||
libusb_close(dev_handle);
|
||||
dev_handle = nullptr;
|
||||
claimed = false;
|
||||
}
|
||||
if (ctx != nullptr) {
|
||||
libusb_exit(ctx);
|
||||
ctx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> PandaUsbHandle::list() {
|
||||
std::vector<std::string> serials;
|
||||
libusb_context *context = nullptr;
|
||||
if (libusb_init(&context) != 0) return serials;
|
||||
libusb_device **devices = nullptr;
|
||||
ssize_t count = libusb_get_device_list(context, &devices);
|
||||
if (count >= 0) {
|
||||
for (ssize_t i = 0; i < count; ++i) {
|
||||
libusb_device_descriptor desc = {};
|
||||
if (libusb_get_device_descriptor(devices[i], &desc) != 0 || !is_panda(desc)) continue;
|
||||
libusb_device_handle *device = nullptr;
|
||||
if (libusb_open(devices[i], &device) != 0) continue;
|
||||
auto serial = usb_serial(device, desc.iSerialNumber);
|
||||
if (!serial.empty()) serials.push_back(serial);
|
||||
libusb_close(device);
|
||||
}
|
||||
libusb_free_device_list(devices, 1);
|
||||
}
|
||||
libusb_exit(context);
|
||||
return serials;
|
||||
}
|
||||
|
||||
void PandaUsbHandle::handle_usb_issue(int err) {
|
||||
LOGE("USB panda transfer failed: %s", libusb_error_name(err));
|
||||
if (err == LIBUSB_ERROR_NO_DEVICE) connected = false;
|
||||
if (err != LIBUSB_ERROR_TIMEOUT) comms_healthy = false;
|
||||
}
|
||||
|
||||
int PandaUsbHandle::control_write(uint8_t request, uint16_t param1, uint16_t param2, unsigned int timeout) {
|
||||
std::lock_guard<std::recursive_mutex> guard(hw_lock);
|
||||
if (!connected) return LIBUSB_ERROR_NO_DEVICE;
|
||||
int count = libusb_control_transfer(dev_handle, 0x40, request, param1, param2, nullptr, 0, usb_timeout(timeout));
|
||||
if (count < 0) {
|
||||
comms_healthy = false;
|
||||
handle_usb_issue(count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int PandaUsbHandle::control_read(uint8_t request, uint16_t param1, uint16_t param2, unsigned char *data, uint16_t length, unsigned int timeout) {
|
||||
std::lock_guard<std::recursive_mutex> guard(hw_lock);
|
||||
if (!connected) return LIBUSB_ERROR_NO_DEVICE;
|
||||
int count = libusb_control_transfer(dev_handle, 0xc0, request, param1, param2, data, length, usb_timeout(timeout));
|
||||
if (count < 0) handle_usb_issue(count);
|
||||
return count;
|
||||
}
|
||||
|
||||
int PandaUsbHandle::bulk_write(unsigned char endpoint, unsigned char *data, int length, unsigned int timeout) {
|
||||
std::lock_guard<std::recursive_mutex> guard(hw_lock);
|
||||
if (!connected) return LIBUSB_ERROR_NO_DEVICE;
|
||||
int transferred = 0;
|
||||
int err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, usb_timeout(timeout));
|
||||
if (err != 0 || transferred != length) {
|
||||
comms_healthy = false;
|
||||
if (err == 0) err = LIBUSB_ERROR_IO;
|
||||
handle_usb_issue(err);
|
||||
return err;
|
||||
}
|
||||
return transferred;
|
||||
}
|
||||
|
||||
int PandaUsbHandle::bulk_read(unsigned char endpoint, unsigned char *data, int length, unsigned int timeout) {
|
||||
std::lock_guard<std::recursive_mutex> guard(hw_lock);
|
||||
if (!connected) return LIBUSB_ERROR_NO_DEVICE;
|
||||
int transferred = 0;
|
||||
int err = libusb_bulk_transfer(dev_handle, endpoint, data, length, &transferred, timeout == 0 ? 5 : timeout);
|
||||
if (err < 0 && err != LIBUSB_ERROR_TIMEOUT) handle_usb_issue(err);
|
||||
return err == 0 || err == LIBUSB_ERROR_TIMEOUT ? transferred : err;
|
||||
}
|
||||
@@ -5,12 +5,14 @@
|
||||
#include "common/params.h"
|
||||
#include "selfdrive/pandad/panda.h"
|
||||
|
||||
void pandad_main_thread(std::string serial);
|
||||
void pandad_main_thread(std::vector<std::string> serials);
|
||||
|
||||
class PandaSafety {
|
||||
public:
|
||||
PandaSafety(Panda *panda) : panda_(panda) {}
|
||||
PandaSafety(const std::vector<Panda *> &pandas) : pandas_(pandas) {}
|
||||
void configureSafetyMode(bool is_onroad);
|
||||
Panda *aolOwner();
|
||||
bool aolStatusMatches(Panda *owner, const aol_safety_health_t &status) const;
|
||||
|
||||
private:
|
||||
void updateMultiplexingMode();
|
||||
@@ -21,6 +23,9 @@ private:
|
||||
bool log_once_ = false;
|
||||
bool safety_configured_ = false;
|
||||
bool prev_obd_multiplexing_ = false;
|
||||
Panda *panda_;
|
||||
std::vector<Panda *> pandas_;
|
||||
std::vector<std::pair<cereal::CarParams::SafetyModel, uint16_t>> safety_slots_;
|
||||
uint16_t alternative_experience_ = 0;
|
||||
size_t safety_config_count_ = 0;
|
||||
Params params_;
|
||||
};
|
||||
|
||||
@@ -41,8 +41,8 @@ const std::string SPI_DEVICE = "/dev/spidev0.0";
|
||||
class LockEx {
|
||||
public:
|
||||
LockEx(int fd_, std::recursive_mutex &m_) : fd(fd_), m(m_) {
|
||||
m.lock();
|
||||
flock(fd, LOCK_EX);
|
||||
{ m.lock(); }
|
||||
{ flock(fd, LOCK_EX); }
|
||||
}
|
||||
|
||||
~LockEx() {
|
||||
@@ -283,7 +283,8 @@ int PandaSpiHandle::lltransfer(spi_ioc_transfer &t) {
|
||||
corrupt(t.tx_buf);
|
||||
}
|
||||
|
||||
int ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &t);
|
||||
int ret;
|
||||
{ ret = util::safe_ioctl(spi_fd, SPI_IOC_MESSAGE(1), &t); }
|
||||
if (err_prob > 0 && chance(err_prob)) {
|
||||
printf("ioctl error\n");
|
||||
ret = -1;
|
||||
@@ -300,14 +301,15 @@ int PandaSpiHandle::lltransfer(spi_ioc_transfer &t) {
|
||||
// or the deadline. Every transfer either sees all of what the panda queued from its first byte, or
|
||||
// nothing it can mistake for a response. Returns the response length, or a negative SpiError
|
||||
int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint32_t id, uint8_t *tx_data, uint16_t tx_len, uint8_t *rx_data, uint16_t max_rx_len, double deadline) {
|
||||
|
||||
static const auto poll = [] {
|
||||
std::array<uint8_t, SPI_BUF_SIZE> p;
|
||||
p.fill(SPI_POLL);
|
||||
return p;
|
||||
}();
|
||||
|
||||
LockEx lock(spi_fd, hw_lock);
|
||||
|
||||
|
||||
// the response is always read in one transfer of rx_len
|
||||
const uint32_t rx_len = sizeof(spi_resp_header) + max_rx_len + SPI_CRC_SIZE;
|
||||
const uint32_t req_len = sizeof(header) + tx_len;
|
||||
@@ -346,7 +348,9 @@ int PandaSpiHandle::spi_transfer(uint8_t endpoint, uint32_t id, uint8_t *tx_data
|
||||
alive = millis_since_boot();
|
||||
// read until the response. if the panda is ready again, it never got the request
|
||||
do {
|
||||
if (xfer(poll.data(), rx_len) < 0) {
|
||||
int poll_ret;
|
||||
{ poll_ret = xfer(poll.data(), rx_len); }
|
||||
if (poll_ret < 0) {
|
||||
break;
|
||||
}
|
||||
if (rx_buf[0] == SPI_SLAVE_READY) {
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
#include <cstring>
|
||||
#include <unistd.h>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/tests/native_test.h"
|
||||
#include "openpilot/cereal/messaging/messaging.h"
|
||||
#include "selfdrive/pandad/pandad.h"
|
||||
#include "selfdrive/pandad/aol_protocol.h"
|
||||
|
||||
class OwnerHandle : public PandaCommsHandle {
|
||||
public:
|
||||
health_t health{};
|
||||
aol_safety_health_t aol{};
|
||||
std::vector<uint16_t> requests;
|
||||
explicit OwnerHandle(std::string serial) {
|
||||
hw_serial = serial;
|
||||
aol.magic = AOL_SAFETY_PROTOCOL_MAGIC;
|
||||
aol.version = AOL_SAFETY_PROTOCOL_VERSION;
|
||||
}
|
||||
int control_write(uint8_t request, uint16_t p1, uint16_t p2, unsigned int) override {
|
||||
if (request == 0xdc) {
|
||||
health.safety_mode_pkt = p1; health.safety_param_pkt = p2;
|
||||
aol.safety_mode = p1; aol.safety_param = p2;
|
||||
} else if (request == 0xdf) {
|
||||
health.alternative_experience_pkt = p1;
|
||||
} else if (request == 0xf5) {
|
||||
requests.push_back(p1); aol.request_mask = p1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int control_read(uint8_t request, uint16_t, uint16_t, unsigned char *data, uint16_t length, unsigned int) override {
|
||||
if (request == 0xd2 && length == sizeof(health)) { memcpy(data, &health, length); return length; }
|
||||
if (request == 0xd5 && length == sizeof(aol)) { memcpy(data, &aol, length); return length; }
|
||||
return -1;
|
||||
}
|
||||
int bulk_write(unsigned char, unsigned char *, int, unsigned int) override { return 0; }
|
||||
int bulk_read(unsigned char, unsigned char *, int, unsigned int) override { return 0; }
|
||||
void cleanup() override {}
|
||||
};
|
||||
class OwnerPanda : public Panda {
|
||||
public:
|
||||
OwnerPanda(std::unique_ptr<OwnerHandle> h, uint32_t offset) : Panda(std::move(h), cereal::PandaState::PandaType::RED_PANDA, offset) {}
|
||||
};
|
||||
|
||||
void test_actual_config_slots_and_owner_handle() {
|
||||
using Mode = cereal::CarParams::SafetyModel;
|
||||
const std::string prefix = "test-panda-owner-" + std::to_string(getpid());
|
||||
const char *old = getenv("OPENPILOT_PREFIX");
|
||||
const std::string old_prefix = old ? old : "";
|
||||
setenv("OPENPILOT_PREFIX", prefix.c_str(), 1);
|
||||
Params params;
|
||||
struct Restore {
|
||||
Params ¶ms;
|
||||
std::string old;
|
||||
~Restore() {
|
||||
for (auto key : {"CarParams", "FirmwareQueryDone", "ControlsReady", "ObdMultiplexingChanged"}) params.remove(key);
|
||||
if (old.empty()) unsetenv("OPENPILOT_PREFIX"); else setenv("OPENPILOT_PREFIX", old.c_str(), 1);
|
||||
}
|
||||
} restore{params, old_prefix};
|
||||
auto aux_handle = std::make_unique<OwnerHandle>("auxiliary");
|
||||
auto owner_handle = std::make_unique<OwnerHandle>("owner");
|
||||
auto extra_handle = std::make_unique<OwnerHandle>("extra");
|
||||
auto *aux = aux_handle.get(); auto *owner = owner_handle.get(); auto *extra = extra_handle.get();
|
||||
OwnerPanda auxiliary(std::move(aux_handle), 0), primary(std::move(owner_handle), 4), excess(std::move(extra_handle), 8);
|
||||
std::vector<Panda *> pandas{&auxiliary, &primary, &excess};
|
||||
PandaSafety safety(pandas);
|
||||
CHECK(safety.aolOwner() == nullptr);
|
||||
capnp::MallocMessageBuilder message;
|
||||
auto cp = message.initRoot<cereal::CarParams>();
|
||||
cp.setAlternativeExperience(32);
|
||||
auto slots = cp.initSafetyConfigs(2);
|
||||
slots[0].setSafetyModel(Mode::NO_OUTPUT); slots[0].setSafetyParam(0);
|
||||
slots[1].setSafetyModel(Mode::HYUNDAI_CANFD); slots[1].setSafetyParam(0x809);
|
||||
auto words = capnp::messageToFlatArray(message);
|
||||
auto bytes = words.asBytes();
|
||||
params.put("CarParams", reinterpret_cast<const char *>(bytes.begin()), bytes.size());
|
||||
params.putBool("FirmwareQueryDone", true); params.putBool("ControlsReady", true);
|
||||
safety.configureSafetyMode(true);
|
||||
CHECK(aux->health.safety_mode_pkt == (uint8_t)Mode::NO_OUTPUT);
|
||||
CHECK(owner->health.safety_mode_pkt == (uint8_t)Mode::HYUNDAI_CANFD);
|
||||
CHECK(owner->health.safety_param_pkt == 0x809);
|
||||
CHECK(extra->health.safety_mode_pkt == (uint8_t)Mode::SILENT);
|
||||
CHECK(aux->health.alternative_experience_pkt == 32 && owner->health.alternative_experience_pkt == 32);
|
||||
CHECK(safety.aolOwner() == nullptr);
|
||||
std::vector<Panda *> exact{&auxiliary, &primary};
|
||||
PandaSafety owner_safety(exact); owner_safety.configureSafetyMode(true);
|
||||
CHECK(owner_safety.aolOwner() == &primary);
|
||||
CHECK(owner_safety.aolStatusMatches(&primary, owner->aol));
|
||||
auto foreign = owner->aol; foreign.safety_param ^= 1;
|
||||
CHECK(!owner_safety.aolStatusMatches(&primary, foreign));
|
||||
CHECK(!owner_safety.aolStatusMatches(&auxiliary, owner->aol));
|
||||
aux->hw_serial = "owner"; CHECK(owner_safety.aolOwner() == nullptr); aux->hw_serial = "auxiliary";
|
||||
for (auto *peer : {aux}) {
|
||||
peer->health.flags_pkt |= HEALTH_FLAG_CONTROLS_ALLOWED;
|
||||
CHECK(owner_safety.aolOwner() == nullptr);
|
||||
peer->health.flags_pkt = 0;
|
||||
peer->health.safety_param_pkt = 0x809;
|
||||
CHECK(owner_safety.aolOwner() == nullptr);
|
||||
peer->health.safety_param_pkt = 0;
|
||||
peer->comms_healthy = false;
|
||||
CHECK(owner_safety.aolOwner() == nullptr);
|
||||
peer->comms_healthy = true;
|
||||
peer->connected = false;
|
||||
CHECK(owner_safety.aolOwner() == nullptr);
|
||||
peer->connected = true;
|
||||
}
|
||||
owner->health.safety_param_pkt = 0x808; CHECK(owner_safety.aolOwner() == nullptr);
|
||||
owner->health.safety_param_pkt = 0x809;
|
||||
owner->health.alternative_experience_pkt = 0; CHECK(owner_safety.aolOwner() == nullptr);
|
||||
owner->health.alternative_experience_pkt = 32;
|
||||
slots[0].setSafetyParam(1);
|
||||
{ auto changed = capnp::messageToFlatArray(message); auto data = changed.asBytes();
|
||||
params.put("CarParams", reinterpret_cast<const char *>(data.begin()), data.size()); }
|
||||
owner_safety.configureSafetyMode(false); owner_safety.configureSafetyMode(true);
|
||||
aux->health.safety_param_pkt = 0;
|
||||
CHECK(owner_safety.aolOwner() == nullptr);
|
||||
slots[0].setSafetyParam(0);
|
||||
{ auto changed = capnp::messageToFlatArray(message); auto data = changed.asBytes();
|
||||
params.put("CarParams", reinterpret_cast<const char *>(data.begin()), data.size()); }
|
||||
owner_safety.configureSafetyMode(false); owner_safety.configureSafetyMode(true);
|
||||
CHECK(owner_safety.aolOwner() == &primary);
|
||||
const AolSafetyProfile profiles[] = {{(uint8_t)Mode::HYUNDAI_CANFD, [](uint16_t p) { return p == 0x809; }, true}};
|
||||
AolProfileRegistry registry{profiles, 1}; AolAxisNegotiator negotiator(registry);
|
||||
owner->aol.capability_flags = 1;
|
||||
aux->aol = owner->aol; aux->aol.permission_mask = 3; aux->aol.request_mask = 3;
|
||||
AolAxisInput axis{true, true, "session", 1, 200000001, 1, true, false};
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
Panda *selected = owner_safety.aolOwner(); CHECK(selected == &primary);
|
||||
auto before = selected->get_aol_safety_state();
|
||||
auto plan = negotiator.prepare(before, axis, 2, before->safety_mode);
|
||||
CHECK(plan.request_mask == (i == 0 ? 0 : 1));
|
||||
CHECK(selected->set_aol_axis_request(plan.request_mask));
|
||||
auto outcome = negotiator.complete(plan, true, selected->get_aol_safety_state());
|
||||
CHECK(outcome.compatible); CHECK(outcome.status->permission_mask == 0);
|
||||
CHECK(selected->hw_serial() == "owner");
|
||||
}
|
||||
CHECK(aux->requests.empty() && extra->requests.empty());
|
||||
CHECK(owner->requests == std::vector<uint16_t>({0, 1}));
|
||||
owner_safety.configureSafetyMode(false); CHECK(owner_safety.aolOwner() == nullptr);
|
||||
PandaSafety single({&primary}); CHECK(single.aolOwner() == &primary);
|
||||
}
|
||||
|
||||
int main() { return run_native_test(test_actual_config_slots_and_owner_handle); }
|
||||
@@ -0,0 +1,190 @@
|
||||
#include <libusb-1.0/libusb.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
|
||||
#include "selfdrive/pandad/panda_comms.h"
|
||||
|
||||
namespace {
|
||||
struct Device {
|
||||
uint16_t vendor;
|
||||
uint16_t product;
|
||||
const char *serial;
|
||||
bool accessible = true;
|
||||
};
|
||||
std::array<Device, 4> devices;
|
||||
std::array<libusb_device *, 5> inventory;
|
||||
int closes, exits, releases, claim_error, configuration_error, transfer_error, transferred;
|
||||
unsigned int last_timeout;
|
||||
uint8_t last_endpoint, last_request, last_request_type;
|
||||
uint16_t last_value, last_index;
|
||||
|
||||
size_t device_index(const void *device) {
|
||||
return reinterpret_cast<uintptr_t>(device) - 1;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
devices = {{{0x3801, 0xddcc, "first"}, {0xbbaa, 0xddcc, "second"},
|
||||
{0x3801, 0xddee, "bootloader"}, {0x1234, 0xddcc, "other"}}};
|
||||
for (size_t i = 0; i < devices.size(); ++i) inventory[i] = reinterpret_cast<libusb_device *>(i + 1);
|
||||
inventory.back() = nullptr;
|
||||
closes = exits = releases = claim_error = configuration_error = transfer_error = transferred = 0;
|
||||
last_timeout = last_endpoint = last_request = last_request_type = last_value = last_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
int LIBUSB_CALL libusb_init(libusb_context **context) {
|
||||
*context = reinterpret_cast<libusb_context *>(1);
|
||||
return 0;
|
||||
}
|
||||
void LIBUSB_CALL libusb_exit(libusb_context *) { ++exits; }
|
||||
ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *, libusb_device ***list) {
|
||||
*list = inventory.data();
|
||||
return devices.size();
|
||||
}
|
||||
void LIBUSB_CALL libusb_free_device_list(libusb_device **, int) {}
|
||||
int LIBUSB_CALL libusb_get_device_descriptor(libusb_device *device, libusb_device_descriptor *desc) {
|
||||
*desc = {};
|
||||
const auto &entry = devices.at(device_index(device));
|
||||
desc->idVendor = entry.vendor;
|
||||
desc->idProduct = entry.product;
|
||||
desc->iSerialNumber = 1;
|
||||
return 0;
|
||||
}
|
||||
int LIBUSB_CALL libusb_open(libusb_device *device, libusb_device_handle **handle) {
|
||||
if (!devices.at(device_index(device)).accessible) return LIBUSB_ERROR_ACCESS;
|
||||
*handle = reinterpret_cast<libusb_device_handle *>(device);
|
||||
return 0;
|
||||
}
|
||||
void LIBUSB_CALL libusb_close(libusb_device_handle *) { ++closes; }
|
||||
int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *handle, uint8_t, unsigned char *data, int length) {
|
||||
const char *serial = devices.at(device_index(handle)).serial;
|
||||
int count = std::min(length, static_cast<int>(strlen(serial)));
|
||||
memcpy(data, serial, count);
|
||||
return count;
|
||||
}
|
||||
int LIBUSB_CALL libusb_kernel_driver_active(libusb_device_handle *, int) { return 1; }
|
||||
int LIBUSB_CALL libusb_detach_kernel_driver(libusb_device_handle *, int) { return 0; }
|
||||
int LIBUSB_CALL libusb_set_configuration(libusb_device_handle *, int) { return configuration_error; }
|
||||
int LIBUSB_CALL libusb_claim_interface(libusb_device_handle *, int) { return claim_error; }
|
||||
int LIBUSB_CALL libusb_release_interface(libusb_device_handle *, int) { ++releases; return 0; }
|
||||
const char *LIBUSB_CALL libusb_error_name(int) { return "mock error"; }
|
||||
int LIBUSB_CALL libusb_control_transfer(libusb_device_handle *, uint8_t type, uint8_t request,
|
||||
uint16_t value, uint16_t index, unsigned char *data,
|
||||
uint16_t length, unsigned int timeout) {
|
||||
last_request_type = type;
|
||||
last_request = request;
|
||||
last_value = value;
|
||||
last_index = index;
|
||||
last_timeout = timeout;
|
||||
if (transfer_error) return transfer_error;
|
||||
if (data) memset(data, 0x5a, length);
|
||||
return length;
|
||||
}
|
||||
int LIBUSB_CALL libusb_bulk_transfer(libusb_device_handle *, unsigned char endpoint, unsigned char *data,
|
||||
int length, int *count, unsigned int timeout) {
|
||||
last_endpoint = endpoint;
|
||||
last_timeout = timeout;
|
||||
*count = std::min(length, transferred);
|
||||
if (endpoint & 0x80) memset(data, 0x7b, *count);
|
||||
return transfer_error;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
reset();
|
||||
int spi_opens = 0, usb_opens = 0;
|
||||
auto spi = [&](const std::string &serial) {
|
||||
++spi_opens;
|
||||
return std::make_unique<PandaUsbHandle>(serial);
|
||||
};
|
||||
auto usb = [&](const std::string &serial) {
|
||||
++usb_opens;
|
||||
return std::make_unique<PandaUsbHandle>(serial);
|
||||
};
|
||||
{
|
||||
auto handle = open_panda_handle("second", spi, usb);
|
||||
assert(handle->hw_serial == "second" && spi_opens == 0 && usb_opens == 1);
|
||||
}
|
||||
{
|
||||
auto unavailable_usb = [&](const std::string &) -> std::unique_ptr<PandaCommsHandle> {
|
||||
++usb_opens;
|
||||
throw std::runtime_error("USB unavailable");
|
||||
};
|
||||
auto handle = open_panda_handle("second", spi, unavailable_usb);
|
||||
assert(handle->hw_serial == "second" && spi_opens == 1 && usb_opens == 2);
|
||||
}
|
||||
reset();
|
||||
assert((PandaUsbHandle::list() == std::vector<std::string>{"first", "second"}));
|
||||
assert(closes == 2 && exits == 1);
|
||||
|
||||
reset();
|
||||
devices[0].accessible = false;
|
||||
assert((PandaUsbHandle::list() == std::vector<std::string>{"second"}));
|
||||
{
|
||||
PandaUsbHandle handle("second");
|
||||
assert(handle.hw_serial == "second");
|
||||
}
|
||||
assert(releases == 1);
|
||||
|
||||
reset();
|
||||
{
|
||||
PandaUsbHandle handle("second");
|
||||
assert(handle.hw_serial == "second" && closes == 1);
|
||||
unsigned char data[8] = {};
|
||||
assert(handle.control_read(0xdd, 12, 34, data, sizeof(data)) == sizeof(data));
|
||||
assert(last_timeout == 500 && last_request_type == 0xc0 && last_request == 0xdd);
|
||||
assert(last_value == 12 && last_index == 34 && data[0] == 0x5a);
|
||||
assert(handle.control_write(0xdc, 56, 78, 7) == 0);
|
||||
assert(last_timeout == 7 && last_request_type == 0x40 && last_request == 0xdc);
|
||||
assert(last_value == 56 && last_index == 78);
|
||||
transferred = 3;
|
||||
transfer_error = LIBUSB_ERROR_TIMEOUT;
|
||||
assert(handle.bulk_read(0x81, data, sizeof(data)) == 3);
|
||||
assert(last_timeout == 5 && last_endpoint == 0x81 && data[0] == 0x7b);
|
||||
assert(handle.connected && handle.comms_healthy);
|
||||
assert(handle.bulk_write(3, data, sizeof(data), 5) == LIBUSB_ERROR_TIMEOUT);
|
||||
assert(!handle.comms_healthy);
|
||||
assert(last_endpoint == 3 && last_timeout == 5);
|
||||
transfer_error = 0;
|
||||
assert(handle.bulk_write(3, data, sizeof(data), 5) == LIBUSB_ERROR_IO);
|
||||
transferred = sizeof(data);
|
||||
assert(handle.bulk_write(3, data, sizeof(data), 5) == sizeof(data));
|
||||
transfer_error = LIBUSB_ERROR_IO;
|
||||
assert(handle.bulk_write(3, data, sizeof(data), 5) == LIBUSB_ERROR_IO);
|
||||
assert(!handle.comms_healthy);
|
||||
transfer_error = LIBUSB_ERROR_NO_DEVICE;
|
||||
assert(handle.control_read(0xc1, 0, 0, data, 1) == LIBUSB_ERROR_NO_DEVICE);
|
||||
assert(!handle.connected);
|
||||
handle.cleanup();
|
||||
handle.cleanup();
|
||||
}
|
||||
assert(closes == 2 && releases == 1 && exits == 1);
|
||||
|
||||
reset();
|
||||
{
|
||||
PandaUsbHandle handle("first");
|
||||
transfer_error = LIBUSB_ERROR_TIMEOUT;
|
||||
assert(handle.control_write(0xf3, 1, 0) == LIBUSB_ERROR_TIMEOUT);
|
||||
assert(handle.connected && !handle.comms_healthy);
|
||||
assert(last_timeout == 500 && last_request_type == 0x40);
|
||||
}
|
||||
|
||||
for (bool fail_configuration : {false, true}) {
|
||||
reset();
|
||||
if (fail_configuration) configuration_error = LIBUSB_ERROR_BUSY;
|
||||
else claim_error = LIBUSB_ERROR_BUSY;
|
||||
bool failed = false;
|
||||
try { PandaUsbHandle handle("first"); } catch (const std::exception &) { failed = true; }
|
||||
assert(failed && closes == 1 && exits == 1 && releases == 0);
|
||||
}
|
||||
|
||||
reset();
|
||||
bool failed = false;
|
||||
try { PandaUsbHandle handle("missing"); } catch (const std::exception &) { failed = true; }
|
||||
assert(failed && closes == 2 && exits == 1 && releases == 0);
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
#include <climits>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "common/tests/native_test.h"
|
||||
#include "openpilot/cereal/messaging/messaging.h"
|
||||
#include "selfdrive/pandad/panda.h"
|
||||
|
||||
struct PandaTest : public Panda {
|
||||
PandaTest(int can_list_size, cereal::PandaState::PandaType hw_type);
|
||||
PandaTest(int can_list_size, cereal::PandaState::PandaType hw_type, uint32_t offset=0);
|
||||
void test_can_send();
|
||||
void test_can_recv(uint32_t chunk_size = 0);
|
||||
void test_chunked_can_recv();
|
||||
@@ -17,9 +21,9 @@ struct PandaTest : public Panda {
|
||||
capnp::List<cereal::CanData>::Reader can_data_list;
|
||||
};
|
||||
|
||||
PandaTest::PandaTest(int can_list_size_, cereal::PandaState::PandaType hw_type_) : can_list_size(can_list_size_), Panda() {
|
||||
PandaTest::PandaTest(int can_list_size_, cereal::PandaState::PandaType hw_type_, uint32_t offset) : can_list_size(can_list_size_), Panda(offset) {
|
||||
this->hw_type = hw_type_;
|
||||
int data_limit = ((hw_type == cereal::PandaState::PandaType::RED_PANDA) ? std::size(dlc_to_len) : 8);
|
||||
int data_limit = ((hw_type == cereal::PandaState::PandaType::RED_PANDA) ? std::size(dlc_to_len) : 9);
|
||||
// prepare test data
|
||||
for (int i = 0; i < data_limit; ++i) {
|
||||
int data_len = dlc_to_len[i];
|
||||
@@ -35,7 +39,7 @@ PandaTest::PandaTest(int can_list_size_, cereal::PandaState::PandaType hw_type_)
|
||||
uint32_t id = i % data_limit;
|
||||
const std::string &dat = test_data[dlc_to_len[id]];
|
||||
can.setAddress(i);
|
||||
can.setSrc(i % 3);
|
||||
can.setSrc(i % 3 + bus_offset);
|
||||
can.setDat(kj::ArrayPtr((uint8_t *)dat.data(), dat.size()));
|
||||
total_pakets_size += sizeof(can_header) + dat.size();
|
||||
}
|
||||
@@ -46,6 +50,7 @@ PandaTest::PandaTest(int can_list_size_, cereal::PandaState::PandaType hw_type_)
|
||||
void PandaTest::test_can_send() {
|
||||
std::vector<uint8_t> unpacked_data;
|
||||
this->pack_can_buffer(can_data_list, [&](uint8_t *chunk, size_t size) {
|
||||
CHECK(size > 0 && size < USB_TX_SOFT_LIMIT + sizeof(can_header) + 64);
|
||||
unpacked_data.insert(unpacked_data.end(), chunk, &chunk[size]);
|
||||
});
|
||||
CHECK(unpacked_data.size() == total_pakets_size);
|
||||
@@ -58,6 +63,9 @@ void PandaTest::test_can_send() {
|
||||
pckt_len = sizeof(can_header) + data_len;
|
||||
|
||||
CHECK(header.addr == cnt);
|
||||
CHECK(header.bus == cnt % 3);
|
||||
CHECK(!header.rejected && !header.returned && !header.extended);
|
||||
CHECK(calculate_checksum(&unpacked_data[pos], pckt_len) == 0);
|
||||
CHECK(test_data.find(data_len) != test_data.end());
|
||||
const std::string &dat = test_data[data_len];
|
||||
CHECK(memcmp(dat.data(), &unpacked_data[pos + sizeof(can_header)], dat.size()) == 0);
|
||||
@@ -89,27 +97,140 @@ void PandaTest::test_can_recv(uint32_t rx_chunk_size) {
|
||||
CHECK(frames.size() == can_list_size);
|
||||
for (int i = 0; i < frames.size(); ++i) {
|
||||
CHECK(frames[i].address == i);
|
||||
CHECK(frames[i].src == i % 3 + bus_offset);
|
||||
CHECK(test_data.find(frames[i].dat.size()) != test_data.end());
|
||||
const std::string &dat = test_data[frames[i].dat.size()];
|
||||
CHECK(memcmp(dat.data(), frames[i].dat.data(), dat.size()) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void test_can_protocol() {
|
||||
for (auto hw_type : {cereal::PandaState::PandaType::DOS, cereal::PandaState::PandaType::RED_PANDA}) {
|
||||
for (int can_list_size : {1, 3, 5, 10, 30, 60, 100, 200}) {
|
||||
PandaTest send_test(can_list_size, hw_type);
|
||||
send_test.test_can_send();
|
||||
class FirmwareHandle : public PandaCommsHandle {
|
||||
public:
|
||||
explicit FirmwareHandle(unsigned char value) : value(value) {}
|
||||
int control_read(uint8_t request, uint16_t, uint16_t, unsigned char *data, uint16_t length, unsigned int) override {
|
||||
if ((request != 0xd3 && request != 0xd4) || length != 64) return -1;
|
||||
memset(data, value, length);
|
||||
return length;
|
||||
}
|
||||
int control_write(uint8_t, uint16_t, uint16_t, unsigned int) override { return 0; }
|
||||
int bulk_write(unsigned char, unsigned char *, int, unsigned int) override { return 0; }
|
||||
int bulk_read(unsigned char, unsigned char *, int, unsigned int) override { return 0; }
|
||||
void cleanup() override {}
|
||||
private:
|
||||
unsigned char value;
|
||||
};
|
||||
|
||||
PandaTest receive_test(can_list_size, hw_type);
|
||||
receive_test.test_can_recv();
|
||||
class FirmwarePanda : public Panda {
|
||||
public:
|
||||
FirmwarePanda(cereal::PandaState::PandaType type, unsigned char signature)
|
||||
: Panda(std::make_unique<FirmwareHandle>(signature), type) {}
|
||||
};
|
||||
|
||||
PandaTest chunked_receive_test(can_list_size, hw_type);
|
||||
chunked_receive_test.test_can_recv(0x40);
|
||||
void test_firmware_selection() {
|
||||
namespace fs = std::filesystem;
|
||||
char directory[] = "/tmp/pandad-firmware-XXXXXX";
|
||||
CHECK(mkdtemp(directory) != nullptr);
|
||||
struct Restore {
|
||||
fs::path cwd = fs::current_path();
|
||||
fs::path directory;
|
||||
~Restore() { fs::current_path(cwd); fs::remove_all(directory); }
|
||||
} restore{fs::current_path(), directory};
|
||||
fs::create_directories(fs::path(directory) / "panda/board/obj");
|
||||
fs::create_directories(fs::path(directory) / "work/a/b");
|
||||
for (const auto &[filename, value] : std::vector<std::pair<std::string, unsigned char>>{
|
||||
{"panda.bin.signed", 0x11}, {"panda_h7.bin.signed", 0x22}}) {
|
||||
std::ofstream file(fs::path(directory) / "panda/board/obj" / filename, std::ios::binary);
|
||||
std::string content(256, value);
|
||||
file.write(content.data(), content.size());
|
||||
}
|
||||
fs::current_path(fs::path(directory) / "work/a/b");
|
||||
using Type = cereal::PandaState::PandaType;
|
||||
CHECK(FirmwarePanda(Type::DOS, 0x11).up_to_date());
|
||||
CHECK(!FirmwarePanda(Type::DOS, 0x22).up_to_date());
|
||||
for (auto type : {Type::RED_PANDA, Type::RED_PANDA_V2, Type::TRES, Type::CUATRO}) {
|
||||
CHECK(FirmwarePanda(type, 0x22).up_to_date());
|
||||
CHECK(!FirmwarePanda(type, 0x11).up_to_date());
|
||||
}
|
||||
CHECK(!FirmwarePanda(Type::UNKNOWN, 0x11).up_to_date());
|
||||
CHECK(!FirmwarePanda(Type::UNKNOWN, 0x22).up_to_date());
|
||||
fs::remove(fs::path(directory) / "panda/board/obj/panda.bin.signed");
|
||||
CHECK(!FirmwarePanda(Type::DOS, 0x11).up_to_date());
|
||||
}
|
||||
|
||||
|
||||
class RoutingPanda : public Panda {
|
||||
public:
|
||||
explicit RoutingPanda(uint32_t offset) : Panda(offset) {}
|
||||
using Panda::pack_can_buffer;
|
||||
using Panda::unpack_can_buffer;
|
||||
using Panda::calculate_checksum;
|
||||
};
|
||||
|
||||
void test_disjoint_bus_blocks_and_return_flags() {
|
||||
MessageBuilder message;
|
||||
auto list = message.initEvent().initSendcan(12);
|
||||
for (uint32_t bus = 0; bus < 12; ++bus) {
|
||||
list[bus].setAddress(0x100 + bus);
|
||||
list[bus].setSrc(bus);
|
||||
const std::array<uint8_t, 8> data{(uint8_t)bus, 1, 2, 3, 4, 5, 6, 7};
|
||||
list[bus].setDat(kj::arrayPtr(data.data(), data.size()));
|
||||
}
|
||||
for (uint32_t offset : {0U, 4U, 8U}) {
|
||||
RoutingPanda panda(offset);
|
||||
std::vector<uint8_t> bytes;
|
||||
panda.pack_can_buffer(list.asReader(), [&](uint8_t *data, size_t size) { bytes.insert(bytes.end(), data, data + size); });
|
||||
const size_t packet_size = sizeof(can_header) + 8;
|
||||
CHECK(bytes.size() == 4 * packet_size);
|
||||
for (uint32_t local = 0; local < 4; ++local) {
|
||||
can_header header;
|
||||
memcpy(&header, bytes.data() + local * packet_size, sizeof(header));
|
||||
CHECK(header.bus == local && header.addr == 0x100 + offset + local);
|
||||
}
|
||||
for (uint32_t flags : {0U, CAN_RETURNED_BUS_OFFSET, CAN_REJECTED_BUS_OFFSET}) {
|
||||
auto encoded = bytes;
|
||||
for (uint32_t local = 0; local < 4; ++local) {
|
||||
can_header header;
|
||||
auto *packet = encoded.data() + local * packet_size;
|
||||
memcpy(&header, packet, sizeof(header));
|
||||
header.returned = flags == CAN_RETURNED_BUS_OFFSET;
|
||||
header.rejected = flags == CAN_REJECTED_BUS_OFFSET;
|
||||
header.checksum = 0;
|
||||
memcpy(packet, &header, sizeof(header));
|
||||
header.checksum = panda.calculate_checksum(packet, packet_size);
|
||||
memcpy(packet, &header, sizeof(header));
|
||||
}
|
||||
uint32_t size = encoded.size(); std::vector<can_frame> frames;
|
||||
CHECK(panda.unpack_can_buffer(encoded.data(), size, frames));
|
||||
CHECK(frames.size() == 4);
|
||||
for (uint32_t local = 0; local < 4; ++local) {
|
||||
CHECK(frames[local].src == offset + local + flags);
|
||||
CHECK(frames[local].address == 0x100 + offset + local);
|
||||
CHECK((uint8_t)frames[local].dat[0] == offset + local);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void test_can_protocol() {
|
||||
test_disjoint_bus_blocks_and_return_flags();
|
||||
test_firmware_selection();
|
||||
for (auto hw_type : {cereal::PandaState::PandaType::DOS, cereal::PandaState::PandaType::RED_PANDA}) {
|
||||
for (uint32_t offset : {0U, 4U, 8U}) {
|
||||
for (int can_list_size : {1, 3, 5, 9, 10, 18, 19, 20, 30, 60, 100, 200}) {
|
||||
PandaTest send_test(can_list_size, hw_type, offset);
|
||||
send_test.test_can_send();
|
||||
|
||||
PandaTest receive_test(can_list_size, hw_type, offset);
|
||||
receive_test.test_can_recv();
|
||||
|
||||
PandaTest chunked_receive_test(can_list_size, hw_type, offset);
|
||||
for (uint32_t chunk_size : {1U, 63U, 64U, 65U}) chunked_receive_test.test_can_recv(chunk_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
return run_native_test(test_can_protocol);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
"""Portable regression checks of the current hardware transition and power branches."""
|
||||
|
||||
import ast
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace as NS
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from openpilot.starpilot.drive_state.resolver import Mode
|
||||
from openpilot.system.hardware.power_monitoring import PowerMonitoring
|
||||
from openpilot.starpilot.power.offroad_preferences import PowerPolicy
|
||||
|
||||
ROOT = Path(__file__).parents[4]
|
||||
|
||||
|
||||
def hardware_body():
|
||||
tree = ast.parse((ROOT / 'openpilot/system/hardware/hardwared.py').read_text())
|
||||
function = next(node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == 'hardware_thread')
|
||||
return next(node for node in function.body if isinstance(node, ast.While)).body
|
||||
|
||||
|
||||
def executable(nodes):
|
||||
return compile(ast.fix_missing_locations(ast.Module(body=nodes, type_ignores=[])), 'current-hardware', 'exec')
|
||||
|
||||
|
||||
def test_forced_modes_poll_at_nominal_cadence_without_repeated_edges_and_observe_physical_changes():
|
||||
from openpilot.starpilot.drive_state.resolver import effective_onroad
|
||||
|
||||
body = hardware_body()
|
||||
start = next(i for i, node in enumerate(body) if isinstance(node, ast.Assign) and ast.unparse(node.targets[0]) == 'physical_ignition')
|
||||
end = next(i for i, node in enumerate(body) if isinstance(node, ast.Assign) and ast.unparse(node.targets[0]) == 'ign_edge')
|
||||
code = executable(body[start : end + 1])
|
||||
for mode, started in ((Mode.ONROAD, True), (Mode.OFFROAD, False)):
|
||||
reads = []
|
||||
owner = NS(snapshot=lambda mode=mode, reads=reads: reads.append(True) or NS(mode=mode))
|
||||
values = {
|
||||
'Mode': Mode,
|
||||
'drive_mode': mode,
|
||||
'drive_state': owner,
|
||||
'physical_ignition_prev': None,
|
||||
'requested_onroad_prev': None,
|
||||
'started_ts': 1 if started else None,
|
||||
'DT_HW': 0.5,
|
||||
'SERVICE_LIST': {'pandaStates': NS(frequency=10)},
|
||||
'effective_onroad': effective_onroad,
|
||||
}
|
||||
for frame in range(1, 101):
|
||||
values.update(sm=NS(frame=frame, updated={'pandaStates': True}), pandaStates=[object()], onroad_conditions={'ignition': False, 'not_onroad_cycle': True})
|
||||
exec(code, values)
|
||||
assert not values['ign_edge']
|
||||
assert len(reads) == 20
|
||||
# Raw changes between scheduled polls are processed once, including timeout loss and recovery.
|
||||
for frame, ignition, updated, expected in (
|
||||
(101, True, True, True),
|
||||
(102, True, False, False),
|
||||
(103, False, False, True),
|
||||
(104, False, False, False),
|
||||
(106, True, True, True),
|
||||
):
|
||||
values.update(
|
||||
sm=NS(frame=frame, updated={'pandaStates': updated}), pandaStates=[object()], onroad_conditions={'ignition': ignition, 'not_onroad_cycle': True}
|
||||
)
|
||||
exec(code, values)
|
||||
assert values['physical_edge'] == expected
|
||||
assert values['ign_edge'] == expected
|
||||
|
||||
|
||||
def power_branch():
|
||||
body = hardware_body()
|
||||
index = next(i for i, node in enumerate(body) if isinstance(node, ast.If) and ast.unparse(node.test) == 'should_start')
|
||||
return executable(body[index - 1 : index + 1])
|
||||
|
||||
|
||||
def test_physical_shutdown_episode_survives_force_auto_and_clears_only_real_ignition():
|
||||
values = {
|
||||
'Mode': Mode,
|
||||
'off_ts': 100.0,
|
||||
'physical_off_ts': None,
|
||||
'forced_power_episode': False,
|
||||
'started_ts': None,
|
||||
'started_seen': False,
|
||||
'startup_blocked_ts': None,
|
||||
'startup_conditions': {},
|
||||
'startup_conditions_prev': {},
|
||||
'cloudlog': Mock(),
|
||||
}
|
||||
code = power_branch()
|
||||
|
||||
def step(now, mode, ignition, started, edge=False):
|
||||
values.update(time=NS(monotonic=lambda: now), drive_mode=mode, onroad_conditions={'ignition': ignition}, should_start=started, physical_edge=edge)
|
||||
exec(code, values)
|
||||
|
||||
step(110, Mode.ONROAD, False, True)
|
||||
assert values['off_ts'] is None and values['physical_off_ts'] == 100 and values['forced_power_episode']
|
||||
step(120, Mode.OFFROAD, False, False)
|
||||
step(130, Mode.AUTO, False, False)
|
||||
assert values['physical_off_ts'] == 100 and values['forced_power_episode']
|
||||
step(130, Mode.ONROAD, False, True) # Established MONOTONIC clock after suspend; no mode reset.
|
||||
assert values['physical_off_ts'] == 100
|
||||
step(140, Mode.ONROAD, True, True, True)
|
||||
assert values['physical_off_ts'] is None and not values['forced_power_episode']
|
||||
step(150, Mode.OFFROAD, True, False)
|
||||
step(300, Mode.OFFROAD, True, False)
|
||||
assert values['physical_off_ts'] is None and not values['forced_power_episode']
|
||||
step(301, Mode.OFFROAD, False, False, True)
|
||||
assert values['physical_off_ts'] == 301
|
||||
|
||||
|
||||
def test_real_shutdown_cutoff_and_timeout_remain_effective_with_physical_clock():
|
||||
monitor = object.__new__(PowerMonitoring)
|
||||
monitor._power_policy = lambda now: PowerPolicy()
|
||||
monitor.params = NS(get_bool=lambda key: False)
|
||||
monitor.car_battery_capacity_uWh = 100000
|
||||
monitor.car_voltage_mV = 11000
|
||||
with patch('openpilot.system.hardware.power_monitoring.time.monotonic', return_value=1000):
|
||||
assert monitor.should_shutdown(False, True, 100, True)
|
||||
assert not monitor.should_shutdown(True, True, 100, True)
|
||||
assert not monitor.should_shutdown(False, True, None, True)
|
||||
monitor.car_voltage_mV = 13000
|
||||
assert monitor.should_shutdown(False, True, -200000, True)
|
||||
|
||||
|
||||
def test_default_auto_clock_and_explicit_startup_gates_remain_stock():
|
||||
from openpilot.starpilot.drive_state.resolver import should_start
|
||||
|
||||
assert should_start(Mode.AUTO, {'ignition': True, 'device_temp_good': True}, {'terms': True}, already_started=False)
|
||||
assert not should_start(Mode.AUTO, {'ignition': False, 'device_temp_good': True}, {'terms': True}, already_started=False)
|
||||
assert not should_start(Mode.AUTO, {'ignition': True, 'device_temp_good': False}, {'terms': True}, already_started=True)
|
||||
assert not should_start(Mode.AUTO, {'ignition': True, 'device_temp_good': True}, {'terms': False}, already_started=False)
|
||||
assert should_start(Mode.AUTO, {'ignition': True, 'device_temp_good': True}, {'terms': False}, already_started=True)
|
||||
clock = Mock(return_value=120)
|
||||
values = {
|
||||
'Mode': Mode,
|
||||
'off_ts': 100,
|
||||
'physical_off_ts': None,
|
||||
'forced_power_episode': False,
|
||||
'started_ts': None,
|
||||
'started_seen': False,
|
||||
'startup_blocked_ts': None,
|
||||
'startup_conditions': {},
|
||||
'startup_conditions_prev': {},
|
||||
'cloudlog': Mock(),
|
||||
'time': NS(monotonic=clock),
|
||||
'drive_mode': Mode.AUTO,
|
||||
'onroad_conditions': {'ignition': False},
|
||||
'should_start': False,
|
||||
'physical_edge': False,
|
||||
}
|
||||
exec(power_branch(), values)
|
||||
assert values['off_ts'] == 100 and not values['forced_power_episode']
|
||||
clock.assert_not_called()
|
||||
|
||||
|
||||
def test_manager_missing_native_key_registry_fails_closed_without_aborting_normal_startup():
|
||||
from openpilot.common.params import UnknownKeyName
|
||||
|
||||
tree = ast.parse((ROOT / 'openpilot/system/manager/manager.py').read_text())
|
||||
function = next(node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == 'manager_init')
|
||||
boundary = next(
|
||||
node
|
||||
for node in function.body
|
||||
if isinstance(node, ast.Try) and any(isinstance(child, ast.Attribute) and child.attr == 'initialize_manager' for child in ast.walk(node))
|
||||
)
|
||||
owner = NS(initialize_manager=Mock(side_effect=UnknownKeyName('DriveStateRequest')))
|
||||
logger = Mock()
|
||||
values = {'drive_state': owner, 'cloudlog': logger, 'UnknownKeyName': UnknownKeyName}
|
||||
exec(executable([boundary]), values)
|
||||
logger.warning.assert_called_once_with('Force drive state is unavailable')
|
||||
|
||||
|
||||
def test_effective_onroad_disables_power_save_before_started_publication_without_changing_auto():
|
||||
body = hardware_body()
|
||||
power_index = next(i for i, node in enumerate(body) if isinstance(node, ast.Assign) and ast.unparse(node.targets[0]) == 'should_pwrsave')
|
||||
started_index = next(i for i, node in enumerate(body) if isinstance(node, ast.Assign) and ast.unparse(node.targets[0]) == 'msg.deviceState.started')
|
||||
send_index = next(i for i, node in enumerate(body) if isinstance(node, ast.Expr) and ast.unparse(node.value) == "pm.send('deviceState', msg)")
|
||||
assert power_index + 2 < started_index < send_index
|
||||
code = executable(body[power_index:power_index + 3] + [body[started_index], body[send_index]])
|
||||
|
||||
def step(ignition, started, brightness, previous, count=1):
|
||||
events = []
|
||||
msg = NS(deviceState=NS(screenBrightnessPercent=brightness, started=False))
|
||||
values = {
|
||||
'onroad_conditions': {'ignition': ignition}, 'should_start': started,
|
||||
'started_ts': 1 if started else None, 'msg': msg, 'pwrsave': previous, 'count': count,
|
||||
'HARDWARE': NS(set_power_save=lambda enabled: events.append(('power_save', enabled))),
|
||||
'pm': NS(send=lambda service, message: events.append((service, message.deviceState.started))),
|
||||
}
|
||||
exec(code, values)
|
||||
return values['pwrsave'], events
|
||||
|
||||
# Asleep forced Onroad must enable CPUs before manager sees started=True.
|
||||
assert step(False, True, 0, True) == (False, [('power_save', False), ('deviceState', True)])
|
||||
# Holding the same state adds no repeated hardware power transition.
|
||||
assert step(False, True, 0, False) == (False, [('deviceState', True)])
|
||||
assert step(False, False, 0, False) == (True, [('power_save', True), ('deviceState', False)])
|
||||
# Physical ignition retains its authority even when forcing the pipeline off.
|
||||
assert step(True, False, 0, True) == (False, [('power_save', False), ('deviceState', False)])
|
||||
for ignition, started in ((False, False), (True, False), (True, True)):
|
||||
for brightness in (0, 0.0009, 0.001, 50):
|
||||
expected = not ignition and brightness < 1e-3
|
||||
power_save, events = step(ignition, started, brightness, not expected, count=0)
|
||||
assert power_save == expected # Exact ordinary Auto equation, including blocked startup.
|
||||
assert events[0] == ('power_save', expected)
|
||||
|
||||
|
||||
def test_forced_onroad_startup_blocked_uses_nominal_hardware_cadence_and_keeps_edges():
|
||||
from openpilot.starpilot.drive_state.resolver import effective_onroad, should_start
|
||||
|
||||
body = hardware_body()
|
||||
start = next(i for i, node in enumerate(body) if isinstance(node, ast.Assign) and ast.unparse(node.targets[0]) == 'physical_ignition')
|
||||
end = next(i for i, node in enumerate(body) if isinstance(node, ast.Assign) and ast.unparse(node.targets[0]) == 'ign_edge')
|
||||
code = executable(body[start:end + 1])
|
||||
reads = []
|
||||
values = {
|
||||
'Mode': Mode, 'drive_mode': Mode.ONROAD,
|
||||
'drive_state': NS(snapshot=lambda: reads.append(True) or NS(mode=Mode.ONROAD)),
|
||||
'physical_ignition_prev': None, 'requested_onroad_prev': None,
|
||||
'started_ts': None, 'DT_HW': 0.5,
|
||||
'SERVICE_LIST': {'pandaStates': NS(frequency=10)}, 'effective_onroad': effective_onroad,
|
||||
}
|
||||
full_frames = []
|
||||
for frame in range(1, 101):
|
||||
conditions = {'ignition': False, 'not_onroad_cycle': True, 'device_temp_good': True}
|
||||
values.update(sm=NS(frame=frame, updated={'pandaStates': True}), pandaStates=[object()], onroad_conditions=conditions)
|
||||
exec(code, values)
|
||||
if frame % 5 == 0 or values['ign_edge']:
|
||||
full_frames.append(frame)
|
||||
assert not should_start(Mode.ONROAD, conditions, {'terms': False}, already_started=False)
|
||||
assert full_frames == list(range(5, 101, 5))
|
||||
assert len(reads) == 20
|
||||
# Onroad-cycle/thermal eligibility and physical changes still bypass throttle once.
|
||||
for frame, ignition, ready, expected in (
|
||||
(101, False, False, True), (102, False, False, False),
|
||||
(103, False, True, True), (104, False, True, False),
|
||||
(106, True, True, True), (107, True, True, False),
|
||||
(108, False, True, True), (109, False, True, False),
|
||||
):
|
||||
values.update(sm=NS(frame=frame, updated={'pandaStates': True}), pandaStates=[object()],
|
||||
onroad_conditions={'ignition': ignition, 'not_onroad_cycle': ready, 'device_temp_good': True})
|
||||
exec(code, values)
|
||||
assert values['ign_edge'] == expected
|
||||
assert not should_start(Mode.ONROAD, values['onroad_conditions'], {'terms': False}, already_started=False)
|
||||
assert should_start(Mode.ONROAD, values['onroad_conditions'], {'terms': True}, already_started=False)
|
||||
@@ -0,0 +1 @@
|
||||
"""Saved, optional parked-power policy shared by hardwared and the native UI."""
|
||||
@@ -0,0 +1,93 @@
|
||||
"""Strict optional parked-power document; invalid or absent means stock policy."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
from typing import Any
|
||||
|
||||
|
||||
KEY = "OffroadPowerPreferences"
|
||||
MAX_BYTES = 512
|
||||
STOCK_HOURS = 30
|
||||
STOCK_VOLTS_TENTHS = 118
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PowerPolicy:
|
||||
enabled: bool = False
|
||||
delay_hours: int = STOCK_HOURS
|
||||
cutoff_tenths: int = STOCK_VOLTS_TENTHS
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SavedPower:
|
||||
raw: bytes | None
|
||||
policy: PowerPolicy = PowerPolicy()
|
||||
valid: bool = True
|
||||
readable: bool = True
|
||||
|
||||
|
||||
def _unique_object(pairs: list[tuple[str, object]]) -> dict[str, object]:
|
||||
value: dict[str, object] = {}
|
||||
for key, item in pairs:
|
||||
if key in value:
|
||||
raise ValueError("duplicate parked-power field")
|
||||
value[key] = item
|
||||
return value
|
||||
|
||||
|
||||
def decode(raw: bytes) -> PowerPolicy | None:
|
||||
if len(raw) > MAX_BYTES:
|
||||
return None
|
||||
try:
|
||||
data = json.loads(raw, parse_constant=lambda _value: None, object_pairs_hook=_unique_object)
|
||||
except (UnicodeDecodeError, ValueError, TypeError, RecursionError, OverflowError):
|
||||
return None
|
||||
if not isinstance(data, dict) or set(data) != {"version", "enabled", "delayHours", "cutoffTenths"}:
|
||||
return None
|
||||
version, enabled = data["version"], data["enabled"]
|
||||
hours, tenths = data["delayHours"], data["cutoffTenths"]
|
||||
if (type(version) is not int or version != 1 or type(enabled) is not bool or
|
||||
type(hours) is not int or not 1 <= hours <= 30 or
|
||||
type(tenths) is not int or not 118 <= tenths <= 125):
|
||||
return None
|
||||
return PowerPolicy(enabled, hours, tenths)
|
||||
|
||||
|
||||
def encode(policy: PowerPolicy) -> bytes:
|
||||
return json.dumps(to_value(policy), sort_keys=True, separators=(",", ":")).encode()
|
||||
|
||||
|
||||
def to_value(policy: PowerPolicy) -> dict[str, int | bool]:
|
||||
if (type(policy.enabled) is not bool or type(policy.delay_hours) is not int or
|
||||
type(policy.cutoff_tenths) is not int or not 1 <= policy.delay_hours <= 30 or
|
||||
not 118 <= policy.cutoff_tenths <= 125):
|
||||
raise ValueError("invalid parked-power policy")
|
||||
return {"version": 1, "enabled": policy.enabled, "delayHours": policy.delay_hours,
|
||||
"cutoffTenths": policy.cutoff_tenths}
|
||||
|
||||
|
||||
def read_saved(params: Any) -> SavedPower:
|
||||
try:
|
||||
fd = os.open(params.get_param_path(KEY), os.O_RDONLY | os.O_NONBLOCK | os.O_NOFOLLOW)
|
||||
except FileNotFoundError:
|
||||
return SavedPower(None)
|
||||
except (AttributeError, OSError, TypeError, ValueError):
|
||||
return SavedPower(None, valid=False, readable=False)
|
||||
try:
|
||||
if not stat.S_ISREG(os.fstat(fd).st_mode):
|
||||
return SavedPower(None, valid=False, readable=False)
|
||||
raw = os.read(fd, MAX_BYTES + 1)
|
||||
except OSError:
|
||||
return SavedPower(None, valid=False, readable=False)
|
||||
finally:
|
||||
os.close(fd)
|
||||
if len(raw) > MAX_BYTES:
|
||||
return SavedPower(raw, valid=False, readable=False)
|
||||
policy = decode(raw)
|
||||
return SavedPower(raw, policy if policy is not None else PowerPolicy(), valid=policy is not None)
|
||||
|
||||
|
||||
def effective(saved: SavedPower) -> PowerPolicy:
|
||||
return saved.policy if saved.readable and saved.valid and saved.policy.enabled else PowerPolicy()
|
||||
+38
-14
@@ -102,7 +102,7 @@ def build_project(project_name, project, main, extra_flags):
|
||||
CFLAGS=flags,
|
||||
ASFLAGS=flags,
|
||||
LINKFLAGS=flags,
|
||||
CPPPATH=[Dir("./"), "./board/stm32h7/inc", opendbc.INCLUDE_PATH],
|
||||
CPPPATH=[Dir("./"), project["INCLUDE_PATH"], opendbc.INCLUDE_PATH],
|
||||
ASCOM="$AS $ASFLAGS -o $TARGET -c $SOURCES",
|
||||
BUILDERS={
|
||||
'Objcopy': Builder(generator=objcopy, suffix='.bin', src_suffix='.elf')
|
||||
@@ -134,8 +134,17 @@ def build_project(project_name, project, main, extra_flags):
|
||||
|
||||
|
||||
|
||||
base_project_f4 = {
|
||||
"STARTUP_FILE": "./board/stm32f4/startup_stm32f413xx.s",
|
||||
"LINKER_SCRIPT": "./board/stm32f4/stm32f4_flash.ld",
|
||||
"APP_START_ADDRESS": "0x8004000",
|
||||
"INCLUDE_PATH": "./board/stm32f4/inc",
|
||||
"FLAGS": ["-mcpu=cortex-m4", "-mhard-float", "-DSTM32F4", "-DSTM32F413xx", "-mfpu=fpv4-sp-d16"],
|
||||
}
|
||||
|
||||
base_project_h7 = {
|
||||
"STARTUP_FILE": "./board/stm32h7/startup_stm32h7x5xx.s",
|
||||
"INCLUDE_PATH": "./board/stm32h7/inc",
|
||||
"LINKER_SCRIPT": "./board/stm32h7/stm32h7x5_flash.ld",
|
||||
"APP_START_ADDRESS": "0x8020000",
|
||||
"FLAGS": [
|
||||
@@ -149,20 +158,22 @@ base_project_h7 = {
|
||||
}
|
||||
|
||||
# Common autogenerated includes
|
||||
with open("board/obj/gitversion.h", "w") as f:
|
||||
version = get_version(BUILDER, BUILD_TYPE)
|
||||
f.write("#pragma once\n\n")
|
||||
f.write(f'extern const uint8_t gitversion[{len(version)+1}];\n')
|
||||
f.write(f'const uint8_t gitversion[{len(version)+1}] = "{version}";\n')
|
||||
|
||||
with open("board/obj/version", "w") as f:
|
||||
f.write(f'{get_version(BUILDER, BUILD_TYPE)}')
|
||||
|
||||
version = get_version(BUILDER, BUILD_TYPE)
|
||||
gitversion_header = ("#pragma once\n\n" +
|
||||
f'extern const uint8_t gitversion[{len(version)+1}];\n' +
|
||||
f'const uint8_t gitversion[{len(version)+1}] = "{version}";\n')
|
||||
certs = [get_key_header(n) for n in ["debug", "release"]]
|
||||
with open("board/obj/cert.h", "w") as f:
|
||||
f.write("#pragma once\n\n")
|
||||
for cert in certs:
|
||||
f.write("\n".join(cert) + "\n")
|
||||
cert_header = "#pragma once\n\n" + "".join("\n".join(cert) + "\n" for cert in certs)
|
||||
|
||||
|
||||
def write_generated(target, source, env):
|
||||
Path(target[0].abspath).write_text(str(source[0]))
|
||||
return 0
|
||||
|
||||
|
||||
generation_env = Environment(tools=[])
|
||||
for name, payload in (("gitversion.h", gitversion_header), ("version", version), ("cert.h", cert_header)):
|
||||
generation_env.Command(File(f"./board/obj/{name}"), Value(payload), write_generated)
|
||||
|
||||
# Packet version defines: SHA hash of the struct header files
|
||||
def version_hash(path):
|
||||
@@ -173,8 +184,21 @@ hh, ch = version_hash("board/health.h"), version_hash(os.path.join(opendbc.INCLU
|
||||
common_flags += [f"-DHEALTH_PACKET_VERSION=0x{hh:08X}U", f"-DCAN_PACKET_VERSION_HASH=0x{ch:08X}U"]
|
||||
|
||||
# panda fw
|
||||
build_project("panda", base_project_f4, "./board/main.c", [])
|
||||
build_project("panda_h7", base_project_h7, "./board/main.c", [])
|
||||
|
||||
for variant, flags in (
|
||||
("remote", ["-DPANDA_GM_REMOTE_START_C9"]),
|
||||
("hkg_remote", ["-DPANDA_HKG_REMOTE_START"]),
|
||||
("tesla_wake", ["-DPANDA_TESLA_WAKE_ON_CAN"]),
|
||||
("can_ignition_only", ["-DPANDA_IGNORE_IGNITION_LINE"]),
|
||||
("remote_can_ignition_only", ["-DPANDA_GM_REMOTE_START_C9", "-DPANDA_IGNORE_IGNITION_LINE"]),
|
||||
("hkg_remote_can_ignition_only", ["-DPANDA_HKG_REMOTE_START", "-DPANDA_IGNORE_IGNITION_LINE"]),
|
||||
("tesla_wake_can_ignition_only", ["-DPANDA_TESLA_WAKE_ON_CAN", "-DPANDA_IGNORE_IGNITION_LINE"]),
|
||||
):
|
||||
build_project(f"panda_{variant}", base_project_f4, "./board/main.c", flags)
|
||||
build_project(f"panda_h7_{variant}", base_project_h7, "./board/main.c", flags)
|
||||
|
||||
# body fw
|
||||
build_project("body_h7", base_project_h7, "./board/body/main.c", ["-DPANDA_BODY"])
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ struct board {
|
||||
// ******************* Definitions ********************
|
||||
// These should match the enums in cereal/log.capnp and __init__.py
|
||||
#define HW_TYPE_UNKNOWN 0U
|
||||
#define HW_TYPE_DOS 6U
|
||||
#define HW_TYPE_RED_PANDA 7U
|
||||
#define HW_TYPE_TRES 9U
|
||||
#define HW_TYPE_CUATRO 10U
|
||||
@@ -55,6 +56,7 @@ struct board {
|
||||
#define CAN_MODE_NORMAL 0U
|
||||
#define CAN_MODE_OBD_CAN2 1U
|
||||
|
||||
extern struct board board_dos;
|
||||
extern struct board board_tres;
|
||||
extern struct board board_cuatro;
|
||||
extern struct board board_red;
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
#pragma once
|
||||
|
||||
#include "board_declarations.h"
|
||||
|
||||
// /////////////////////// //
|
||||
// Dos (STM32F4) + Harness //
|
||||
// /////////////////////// //
|
||||
|
||||
static void dos_enable_can_transceiver(uint8_t transceiver, bool enabled) {
|
||||
switch (transceiver){
|
||||
case 1U:
|
||||
set_gpio_output(GPIOC, 1, !enabled);
|
||||
break;
|
||||
case 2U:
|
||||
set_gpio_output(GPIOC, 13, !enabled);
|
||||
break;
|
||||
case 3U:
|
||||
set_gpio_output(GPIOA, 0, !enabled);
|
||||
break;
|
||||
case 4U:
|
||||
set_gpio_output(GPIOB, 10, !enabled);
|
||||
break;
|
||||
default:
|
||||
print("Invalid CAN transceiver ("); puth(transceiver); print("): enabling failed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dos_set_bootkick(BootState state) {
|
||||
set_gpio_output(GPIOC, 4, state != BOOT_BOOTKICK);
|
||||
}
|
||||
|
||||
static void dos_set_can_mode(uint8_t mode) {
|
||||
dos_enable_can_transceiver(2U, false);
|
||||
dos_enable_can_transceiver(4U, false);
|
||||
switch (mode) {
|
||||
case CAN_MODE_NORMAL:
|
||||
case CAN_MODE_OBD_CAN2:
|
||||
if ((bool)(mode == CAN_MODE_NORMAL) != (bool)(harness.status == HARNESS_STATUS_FLIPPED)) {
|
||||
// B12,B13: disable OBD mode
|
||||
set_gpio_mode(GPIOB, 12, MODE_INPUT);
|
||||
set_gpio_mode(GPIOB, 13, MODE_INPUT);
|
||||
|
||||
// B5,B6: normal CAN2 mode
|
||||
set_gpio_alternate(GPIOB, 5, GPIO_AF9_CAN2);
|
||||
set_gpio_alternate(GPIOB, 6, GPIO_AF9_CAN2);
|
||||
dos_enable_can_transceiver(2U, true);
|
||||
} else {
|
||||
// B5,B6: disable normal CAN2 mode
|
||||
set_gpio_mode(GPIOB, 5, MODE_INPUT);
|
||||
set_gpio_mode(GPIOB, 6, MODE_INPUT);
|
||||
|
||||
// B12,B13: OBD mode
|
||||
set_gpio_alternate(GPIOB, 12, GPIO_AF9_CAN2);
|
||||
set_gpio_alternate(GPIOB, 13, GPIO_AF9_CAN2);
|
||||
dos_enable_can_transceiver(4U, true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print("Tried to set unsupported CAN mode: "); puth(mode); print("\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dos_set_ir_power(uint8_t percentage){
|
||||
pwm_set(TIM4, 2, percentage);
|
||||
}
|
||||
|
||||
static void dos_set_fan_enabled(bool enabled){
|
||||
set_gpio_output(GPIOA, 1, enabled);
|
||||
}
|
||||
|
||||
static void dos_set_siren(bool enabled){
|
||||
set_gpio_output(GPIOC, 12, enabled);
|
||||
}
|
||||
|
||||
static uint32_t dos_read_voltage_mV(void){
|
||||
return adc_get_mV(&(const adc_signal_t) ADC_CHANNEL_DEFAULT(ADC1, 12)) * 11U;
|
||||
}
|
||||
|
||||
static bool dos_read_som_gpio (void){
|
||||
return (get_gpio_input(GPIOC, 2) != 0);
|
||||
}
|
||||
|
||||
static void dos_init(void) {
|
||||
common_init_gpio();
|
||||
|
||||
// A8,A15: normal CAN3 mode
|
||||
set_gpio_alternate(GPIOA, 8, GPIO_AF11_CAN3);
|
||||
set_gpio_alternate(GPIOA, 15, GPIO_AF11_CAN3);
|
||||
|
||||
// C8: FAN PWM aka TIM3_CH3
|
||||
set_gpio_alternate(GPIOC, 8, GPIO_AF2_TIM3);
|
||||
|
||||
// C2: SOM GPIO used as input (fan control at boot)
|
||||
set_gpio_mode(GPIOC, 2, MODE_INPUT);
|
||||
set_gpio_pullup(GPIOC, 2, PULL_DOWN);
|
||||
|
||||
// Initialize IR PWM and set to 0%
|
||||
set_gpio_alternate(GPIOB, 7, GPIO_AF2_TIM4);
|
||||
pwm_init(TIM4, 2);
|
||||
dos_set_ir_power(0U);
|
||||
|
||||
// Bootkick
|
||||
dos_set_bootkick(BOOT_BOOTKICK);
|
||||
|
||||
// Init clock source (camera strobe) using PWM
|
||||
clock_source_init(false);
|
||||
}
|
||||
|
||||
static harness_configuration dos_harness_config = {
|
||||
.GPIO_SBU1 = GPIOC,
|
||||
.GPIO_SBU2 = GPIOC,
|
||||
.GPIO_relay_SBU1 = GPIOC,
|
||||
.GPIO_relay_SBU2 = GPIOC,
|
||||
.pin_SBU1 = 0,
|
||||
.pin_SBU2 = 3,
|
||||
.pin_relay_SBU1 = 10,
|
||||
.pin_relay_SBU2 = 11,
|
||||
.adc_signal_SBU1 = ADC_CHANNEL_DEFAULT(ADC1, 10),
|
||||
.adc_signal_SBU2 = ADC_CHANNEL_DEFAULT(ADC1, 13),
|
||||
};
|
||||
|
||||
board board_dos = {
|
||||
.harness_config = &dos_harness_config,
|
||||
.has_spi = false,
|
||||
.has_fan = true,
|
||||
.avdd_mV = 3300U,
|
||||
.fan_enable_cooldown_time = 3U,
|
||||
.init = dos_init,
|
||||
.enable_can_transceiver = dos_enable_can_transceiver,
|
||||
.led_GPIO = {GPIOC, GPIOC, GPIOC},
|
||||
.led_pin = {9, 7, 6},
|
||||
.set_can_mode = dos_set_can_mode,
|
||||
.read_voltage_mV = dos_read_voltage_mV,
|
||||
.read_current_mA = unused_read_current,
|
||||
.set_fan_enabled = dos_set_fan_enabled,
|
||||
.set_ir_power = dos_set_ir_power,
|
||||
.set_siren = dos_set_siren,
|
||||
.set_bootkick = dos_set_bootkick,
|
||||
.read_som_gpio = dos_read_som_gpio,
|
||||
.set_amp_enabled = unused_set_amp_enabled
|
||||
};
|
||||
@@ -19,6 +19,8 @@
|
||||
// platform includes
|
||||
#ifdef STM32H7
|
||||
#include "board/stm32h7/stm32h7_config.h"
|
||||
#elif defined(STM32F4)
|
||||
#include "board/stm32f4/stm32f4_config.h"
|
||||
#else
|
||||
// TODO: uncomment this, cppcheck complains
|
||||
// building for tests
|
||||
|
||||
@@ -4,16 +4,17 @@
|
||||
|
||||
bool bootkick_reset_triggered = false;
|
||||
|
||||
void bootkick_tick(bool ignition, bool recent_heartbeat) {
|
||||
void bootkick_tick(bool ignition, bool recent_heartbeat, bool wake) {
|
||||
static uint8_t waiting_to_boot_countdown = 0;
|
||||
static uint8_t boot_reset_countdown = 0;
|
||||
static uint8_t bootkick_harness_status_prev = HARNESS_STATUS_NC;
|
||||
static bool bootkick_ign_prev = false;
|
||||
static bool bootkick_wake_prev = false;
|
||||
static BootState boot_state = BOOT_BOOTKICK;
|
||||
BootState boot_state_prev = boot_state;
|
||||
const bool harness_inserted = (harness.status != bootkick_harness_status_prev) && (harness.status != HARNESS_STATUS_NC);
|
||||
|
||||
if ((ignition && !bootkick_ign_prev) || harness_inserted) {
|
||||
if ((ignition && !bootkick_ign_prev) || harness_inserted || (wake && !bootkick_wake_prev && !ignition)) {
|
||||
// bootkick on rising edge of ignition or harness insertion
|
||||
boot_state = BOOT_BOOTKICK;
|
||||
} else if (recent_heartbeat) {
|
||||
@@ -56,6 +57,7 @@ void bootkick_tick(bool ignition, bool recent_heartbeat) {
|
||||
|
||||
// update state
|
||||
bootkick_ign_prev = ignition;
|
||||
bootkick_wake_prev = wake;
|
||||
bootkick_harness_status_prev = harness.status;
|
||||
if (waiting_to_boot_countdown > 0U) {
|
||||
waiting_to_boot_countdown--;
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
#include "bxcan_declarations.h"
|
||||
|
||||
// IRQs: CAN1_TX, CAN1_RX0, CAN1_SCE
|
||||
// CAN2_TX, CAN2_RX0, CAN2_SCE
|
||||
// CAN3_TX, CAN3_RX0, CAN3_SCE
|
||||
|
||||
CAN_TypeDef *cans[CAN_ARRAY_SIZE] = {CAN1, CAN2, CAN3};
|
||||
uint8_t can_irq_number[CAN_IRQS_ARRAY_SIZE][CAN_IRQS_ARRAY_SIZE] = {
|
||||
{ CAN1_TX_IRQn, CAN1_RX0_IRQn, CAN1_SCE_IRQn },
|
||||
{ CAN2_TX_IRQn, CAN2_RX0_IRQn, CAN2_SCE_IRQn },
|
||||
{ CAN3_TX_IRQn, CAN3_RX0_IRQn, CAN3_SCE_IRQn },
|
||||
};
|
||||
|
||||
bool can_set_speed(uint8_t can_number) {
|
||||
bool ret = true;
|
||||
CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number);
|
||||
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number);
|
||||
|
||||
ret &= llcan_set_speed(
|
||||
CANx,
|
||||
bus_config[bus_number].can_speed,
|
||||
can_loopback,
|
||||
can_silent
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void can_clear_send(CAN_TypeDef *CANx, uint8_t can_number) {
|
||||
can_health[can_number].can_core_reset_cnt += 1U;
|
||||
llcan_clear_send(CANx);
|
||||
}
|
||||
|
||||
void update_can_health_pkt(uint8_t can_number, uint32_t ir_reg) {
|
||||
CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number);
|
||||
uint32_t esr_reg = CANx->ESR;
|
||||
|
||||
can_health[can_number].bus_off = ((esr_reg & CAN_ESR_BOFF) >> CAN_ESR_BOFF_Pos);
|
||||
can_health[can_number].bus_off_cnt += can_health[can_number].bus_off;
|
||||
can_health[can_number].error_warning = ((esr_reg & CAN_ESR_EWGF) >> CAN_ESR_EWGF_Pos);
|
||||
can_health[can_number].error_passive = ((esr_reg & CAN_ESR_EPVF) >> CAN_ESR_EPVF_Pos);
|
||||
|
||||
can_health[can_number].last_error = ((esr_reg & CAN_ESR_LEC) >> CAN_ESR_LEC_Pos);
|
||||
if ((can_health[can_number].last_error != 0U) && (can_health[can_number].last_error != 7U)) {
|
||||
can_health[can_number].last_stored_error = can_health[can_number].last_error;
|
||||
}
|
||||
|
||||
can_health[can_number].receive_error_cnt = ((esr_reg & CAN_ESR_REC) >> CAN_ESR_REC_Pos);
|
||||
can_health[can_number].transmit_error_cnt = ((esr_reg & CAN_ESR_TEC) >> CAN_ESR_TEC_Pos);
|
||||
|
||||
can_health[can_number].irq0_call_rate = interrupts[can_irq_number[can_number][0]].call_rate;
|
||||
can_health[can_number].irq1_call_rate = interrupts[can_irq_number[can_number][1]].call_rate;
|
||||
can_health[can_number].irq2_call_rate = interrupts[can_irq_number[can_number][2]].call_rate;
|
||||
|
||||
if (ir_reg != 0U) {
|
||||
can_health[can_number].total_error_cnt += 1U;
|
||||
|
||||
// RX message lost due to FIFO overrun
|
||||
if ((CANx->RF0R & (CAN_RF0R_FOVR0)) != 0U) {
|
||||
can_health[can_number].total_rx_lost_cnt += 1U;
|
||||
CANx->RF0R &= ~(CAN_RF0R_FOVR0);
|
||||
}
|
||||
can_clear_send(CANx, can_number);
|
||||
}
|
||||
}
|
||||
|
||||
// ***************************** CAN *****************************
|
||||
// CANx_SCE IRQ Handler
|
||||
static void can_sce(uint8_t can_number) {
|
||||
update_can_health_pkt(can_number, 1U);
|
||||
}
|
||||
|
||||
// CANx_TX IRQ Handler
|
||||
void process_can(uint8_t can_number) {
|
||||
if (can_number != 0xffU) {
|
||||
|
||||
ENTER_CRITICAL();
|
||||
|
||||
CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number);
|
||||
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number);
|
||||
|
||||
// check for empty mailbox
|
||||
CANPacket_t to_send;
|
||||
if ((CANx->TSR & (CAN_TSR_TERR0 | CAN_TSR_ALST0)) != 0U) { // last TX failed due to error arbitration lost
|
||||
can_health[can_number].total_tx_lost_cnt += 1U;
|
||||
CANx->TSR |= (CAN_TSR_TERR0 | CAN_TSR_ALST0);
|
||||
}
|
||||
if ((CANx->TSR & CAN_TSR_TME0) == CAN_TSR_TME0) {
|
||||
// add successfully transmitted message to my fifo
|
||||
if ((CANx->TSR & CAN_TSR_RQCP0) == CAN_TSR_RQCP0) {
|
||||
if ((CANx->TSR & CAN_TSR_TXOK0) == CAN_TSR_TXOK0) {
|
||||
CANPacket_t to_push;
|
||||
to_push.fd = 0U;
|
||||
to_push.returned = 1U;
|
||||
to_push.rejected = 0U;
|
||||
to_push.extended = (CANx->sTxMailBox[0].TIR >> 2) & 0x1U;
|
||||
to_push.addr = (to_push.extended != 0U) ? (CANx->sTxMailBox[0].TIR >> 3) : (CANx->sTxMailBox[0].TIR >> 21);
|
||||
to_push.data_len_code = CANx->sTxMailBox[0].TDTR & 0xFU;
|
||||
to_push.bus = bus_number;
|
||||
WORD_TO_BYTE_ARRAY(&to_push.data[0], CANx->sTxMailBox[0].TDLR);
|
||||
WORD_TO_BYTE_ARRAY(&to_push.data[4], CANx->sTxMailBox[0].TDHR);
|
||||
can_set_checksum(&to_push);
|
||||
|
||||
rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U;
|
||||
}
|
||||
|
||||
// clear interrupt
|
||||
// careful, this can also be cleared by requesting a transmission
|
||||
CANx->TSR |= CAN_TSR_RQCP0;
|
||||
}
|
||||
|
||||
if (can_pop(can_queues[bus_number], &to_send)) {
|
||||
if ((to_send.fd != 0U) || (to_send.data_len_code > 8U)) {
|
||||
can_health[can_number].total_tx_lost_cnt += 1U;
|
||||
} else if (can_check_checksum(&to_send)) {
|
||||
can_health[can_number].total_tx_cnt += 1U;
|
||||
// only send if we have received a packet
|
||||
CANx->sTxMailBox[0].TIR = ((to_send.extended != 0U) ? (to_send.addr << 3) : (to_send.addr << 21)) | (to_send.extended << 2);
|
||||
CANx->sTxMailBox[0].TDTR = to_send.data_len_code;
|
||||
BYTE_ARRAY_TO_WORD(CANx->sTxMailBox[0].TDLR, &to_send.data[0]);
|
||||
BYTE_ARRAY_TO_WORD(CANx->sTxMailBox[0].TDHR, &to_send.data[4]);
|
||||
// Send request TXRQ
|
||||
CANx->sTxMailBox[0].TIR |= 0x1U;
|
||||
} else {
|
||||
can_health[can_number].total_tx_checksum_error_cnt += 1U;
|
||||
}
|
||||
|
||||
refresh_can_tx_slots_available();
|
||||
}
|
||||
}
|
||||
|
||||
EXIT_CRITICAL();
|
||||
}
|
||||
}
|
||||
|
||||
// CANx_RX0 IRQ Handler
|
||||
// blink blue when we are receiving CAN messages
|
||||
void can_rx(uint8_t can_number) {
|
||||
CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number);
|
||||
uint8_t bus_number = BUS_NUM_FROM_CAN_NUM(can_number);
|
||||
|
||||
while ((CANx->RF0R & CAN_RF0R_FMP0) != 0U) {
|
||||
can_health[can_number].total_rx_cnt += 1U;
|
||||
|
||||
// add to my fifo
|
||||
CANPacket_t to_push;
|
||||
|
||||
to_push.fd = 0U;
|
||||
to_push.returned = 0U;
|
||||
to_push.rejected = 0U;
|
||||
to_push.extended = (CANx->sFIFOMailBox[0].RIR >> 2) & 0x1U;
|
||||
to_push.addr = (to_push.extended != 0U) ? (CANx->sFIFOMailBox[0].RIR >> 3) : (CANx->sFIFOMailBox[0].RIR >> 21);
|
||||
to_push.data_len_code = CANx->sFIFOMailBox[0].RDTR & 0xFU;
|
||||
if (to_push.data_len_code > 8U) {
|
||||
can_health[can_number].total_rx_lost_cnt += 1U;
|
||||
CANx->RF0R |= CAN_RF0R_RFOM0;
|
||||
continue;
|
||||
}
|
||||
to_push.bus = bus_number;
|
||||
WORD_TO_BYTE_ARRAY(&to_push.data[0], CANx->sFIFOMailBox[0].RDLR);
|
||||
WORD_TO_BYTE_ARRAY(&to_push.data[4], CANx->sFIFOMailBox[0].RDHR);
|
||||
can_set_checksum(&to_push);
|
||||
|
||||
// forwarding (panda only)
|
||||
int bus_fwd_num = safety_fwd_hook(bus_number, to_push.addr);
|
||||
if (bus_fwd_num != -1) {
|
||||
CANPacket_t to_send;
|
||||
|
||||
to_send.fd = 0U;
|
||||
to_send.returned = 0U;
|
||||
to_send.rejected = 0U;
|
||||
to_send.extended = to_push.extended; // TXRQ
|
||||
to_send.addr = to_push.addr;
|
||||
to_send.bus = to_push.bus;
|
||||
to_send.data_len_code = to_push.data_len_code;
|
||||
(void)memcpy(to_send.data, to_push.data, dlc_to_len[to_push.data_len_code]);
|
||||
can_set_checksum(&to_send);
|
||||
|
||||
can_send(&to_send, bus_fwd_num, true);
|
||||
can_health[can_number].total_fwd_cnt += 1U;
|
||||
}
|
||||
|
||||
safety_rx_invalid += safety_rx_hook(&to_push) ? 0U : 1U;
|
||||
ignition_can_hook(&to_push);
|
||||
|
||||
led_set(LED_BLUE, true);
|
||||
rx_buffer_overflow += can_push(&can_rx_q, &to_push) ? 0U : 1U;
|
||||
|
||||
// next
|
||||
CANx->RF0R |= CAN_RF0R_RFOM0;
|
||||
}
|
||||
}
|
||||
|
||||
static void CAN1_TX_IRQ_Handler(void) { process_can(0); }
|
||||
static void CAN1_RX0_IRQ_Handler(void) { can_rx(0); }
|
||||
static void CAN1_SCE_IRQ_Handler(void) { can_sce(0); }
|
||||
|
||||
static void CAN2_TX_IRQ_Handler(void) { process_can(1); }
|
||||
static void CAN2_RX0_IRQ_Handler(void) { can_rx(1); }
|
||||
static void CAN2_SCE_IRQ_Handler(void) { can_sce(1); }
|
||||
|
||||
static void CAN3_TX_IRQ_Handler(void) { process_can(2); }
|
||||
static void CAN3_RX0_IRQ_Handler(void) { can_rx(2); }
|
||||
static void CAN3_SCE_IRQ_Handler(void) { can_sce(2); }
|
||||
|
||||
bool can_init(uint8_t can_number) {
|
||||
bool ret = false;
|
||||
|
||||
REGISTER_INTERRUPT(CAN1_TX_IRQn, CAN1_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
|
||||
REGISTER_INTERRUPT(CAN1_RX0_IRQn, CAN1_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
|
||||
REGISTER_INTERRUPT(CAN1_SCE_IRQn, CAN1_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_1)
|
||||
REGISTER_INTERRUPT(CAN2_TX_IRQn, CAN2_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
|
||||
REGISTER_INTERRUPT(CAN2_RX0_IRQn, CAN2_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
|
||||
REGISTER_INTERRUPT(CAN2_SCE_IRQn, CAN2_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_2)
|
||||
REGISTER_INTERRUPT(CAN3_TX_IRQn, CAN3_TX_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
|
||||
REGISTER_INTERRUPT(CAN3_RX0_IRQn, CAN3_RX0_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
|
||||
REGISTER_INTERRUPT(CAN3_SCE_IRQn, CAN3_SCE_IRQ_Handler, CAN_INTERRUPT_RATE, FAULT_INTERRUPT_RATE_CAN_3)
|
||||
|
||||
if (can_number != 0xffU) {
|
||||
CAN_TypeDef *CANx = CANIF_FROM_CAN_NUM(can_number);
|
||||
ret &= can_set_speed(can_number);
|
||||
ret &= llcan_init(CANx);
|
||||
// in case there are queued up messages
|
||||
process_can(can_number);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -19,25 +19,52 @@ bool can_loopback = false;
|
||||
extern can_ring can_##x; \
|
||||
can_ring can_##x = { .w_ptr = 0, .r_ptr = 0, .fifo_size = (size), .elems = (CANPacket_t *)&(elems_##x) };
|
||||
|
||||
#ifdef STM32F4
|
||||
#define CAN_RX_BUFFER_SIZE 1024U
|
||||
#define CAN_TX_BUFFER_SIZE 256U
|
||||
_Static_assert(sizeof(CANPacket_t) * (CAN_RX_BUFFER_SIZE + (3U * CAN_TX_BUFFER_SIZE)) <= (128U * 1024U), "F4 CAN queues exceed high SRAM1");
|
||||
#else
|
||||
#define CAN_RX_BUFFER_SIZE 4096U
|
||||
#define CAN_TX_BUFFER_SIZE 416U
|
||||
#endif
|
||||
|
||||
#ifdef STM32H7
|
||||
// ITCM RAM and DTCM RAM are the fastest for Cortex-M7 core access
|
||||
__attribute__((section(".axisram"))) can_buffer(rx_q, CAN_RX_BUFFER_SIZE)
|
||||
__attribute__((section(".itcmram"))) can_buffer(tx1_q, CAN_TX_BUFFER_SIZE)
|
||||
__attribute__((section(".itcmram"))) can_buffer(tx2_q, CAN_TX_BUFFER_SIZE)
|
||||
#elif defined(STM32F4)
|
||||
__attribute__((section(".canram"))) can_buffer(rx_q, CAN_RX_BUFFER_SIZE)
|
||||
__attribute__((section(".canram"))) can_buffer(tx1_q, CAN_TX_BUFFER_SIZE)
|
||||
__attribute__((section(".canram"))) can_buffer(tx2_q, CAN_TX_BUFFER_SIZE)
|
||||
#else // kept for PC
|
||||
can_buffer(rx_q, CAN_RX_BUFFER_SIZE)
|
||||
can_buffer(tx1_q, CAN_TX_BUFFER_SIZE)
|
||||
can_buffer(tx2_q, CAN_TX_BUFFER_SIZE)
|
||||
#endif
|
||||
#ifdef STM32F4
|
||||
__attribute__((section(".canram"))) can_buffer(tx3_q, CAN_TX_BUFFER_SIZE)
|
||||
#else
|
||||
can_buffer(tx3_q, CAN_TX_BUFFER_SIZE)
|
||||
#endif
|
||||
|
||||
// FIXME:
|
||||
// cppcheck-suppress misra-c2012-9.3
|
||||
can_ring *can_queues[PANDA_CAN_CNT] = {&can_tx1_q, &can_tx2_q, &can_tx3_q};
|
||||
|
||||
#ifdef STM32F4
|
||||
static void can_init_buffers(void) {
|
||||
(void)memset(can_rx_q.elems, 0, can_rx_q.fifo_size * sizeof(CANPacket_t));
|
||||
can_rx_q.w_ptr = 0U;
|
||||
can_rx_q.r_ptr = 0U;
|
||||
for (uint32_t i = 0U; i < PANDA_CAN_CNT; i++) {
|
||||
(void)memset(can_queues[i]->elems, 0, can_queues[i]->fifo_size * sizeof(CANPacket_t));
|
||||
can_queues[i]->w_ptr = 0U;
|
||||
can_queues[i]->r_ptr = 0U;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// ********************* interrupt safe queue *********************
|
||||
bool can_pop(can_ring *q, CANPacket_t *elem) {
|
||||
bool ret = false;
|
||||
|
||||
@@ -49,7 +49,7 @@ void set_gpio_output_type(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int out
|
||||
void set_gpio_alternate(GPIO_TypeDef *GPIO, unsigned int pin, unsigned int mode) {
|
||||
ENTER_CRITICAL();
|
||||
uint32_t tmp = GPIO->AFR[pin >> 3U];
|
||||
tmp &= ~(0xFU << ((pin & 7U) * 4U));
|
||||
tmp &= ~((uint32_t)0xFU << ((pin & 7U) * 4U));
|
||||
tmp |= mode << ((pin & 7U) * 4U);
|
||||
register_set(&(GPIO->AFR[pin >> 3]), tmp, 0xFFFFFFFFU);
|
||||
set_gpio_mode(GPIO, pin, MODE_ALTERNATE);
|
||||
|
||||
@@ -30,3 +30,7 @@ uint32_t microsecond_timer_get(void) {
|
||||
}
|
||||
|
||||
typedef uint32_t GPIO_TypeDef;
|
||||
typedef uint32_t ADC_TypeDef;
|
||||
typedef uint32_t IRQn_Type;
|
||||
#define NUM_INTERRUPTS 163U
|
||||
#include "stm32h7/lladc_declarations.h"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
// from the linker script
|
||||
#ifdef STM32F4
|
||||
#define APP_START_ADDRESS 0x8004000U
|
||||
#else
|
||||
#define APP_START_ADDRESS 0x8020000U
|
||||
#endif
|
||||
|
||||
// flasher state variables
|
||||
uint32_t *prog_ptr = NULL;
|
||||
|
||||
+32
-3
@@ -16,13 +16,18 @@
|
||||
|
||||
#include "board/drivers/can_common.h"
|
||||
|
||||
#ifdef STM32H7
|
||||
#include "board/drivers/fdcan.h"
|
||||
#else
|
||||
#include "board/drivers/bxcan.h"
|
||||
#endif
|
||||
|
||||
#include "board/sys/power_saving.h"
|
||||
|
||||
#include "board/obj/gitversion.h"
|
||||
|
||||
#include "board/can_comms.h"
|
||||
|
||||
#include "board/main_comms.h"
|
||||
|
||||
// ****************************** safety mode ******************************
|
||||
@@ -153,8 +158,11 @@ static void tick_handler(void) {
|
||||
const bool recent_heartbeat = heartbeat_counter == 0U;
|
||||
|
||||
// tick drivers at 1Hz
|
||||
bool started = harness_check_ignition() || ignition_can;
|
||||
bootkick_tick(started, recent_heartbeat);
|
||||
bool started = panda_ignition_line() || ignition_can;
|
||||
#ifdef PANDA_HKG_REMOTE_START
|
||||
started = started || hkg_remote_climate_wake;
|
||||
#endif
|
||||
bootkick_tick(started, recent_heartbeat, wake_on_can);
|
||||
|
||||
// increase heartbeat counter and cap it at the uint32 limit
|
||||
if (heartbeat_counter < UINT32_MAX) {
|
||||
@@ -233,10 +241,23 @@ static void tick_handler(void) {
|
||||
ignition_can = false;
|
||||
}
|
||||
|
||||
if (wake_on_can_cnt > 2U) {
|
||||
wake_on_can = false;
|
||||
}
|
||||
#ifdef PANDA_HKG_REMOTE_START
|
||||
if (hkg_remote_climate_wake_cnt > 2U) {
|
||||
hkg_remote_climate_wake = false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// on to the next one
|
||||
uptime_cnt += 1U;
|
||||
safety_mode_cnt += 1U;
|
||||
ignition_can_cnt += 1U;
|
||||
wake_on_can_cnt += 1U;
|
||||
#ifdef PANDA_HKG_REMOTE_START
|
||||
hkg_remote_climate_wake_cnt += 1U;
|
||||
#endif
|
||||
|
||||
// synchronous safety check
|
||||
safety_tick();
|
||||
@@ -255,6 +276,10 @@ int main(void) {
|
||||
// shouldn't have interrupts here, but just in case
|
||||
disable_interrupts();
|
||||
|
||||
#ifdef STM32F4
|
||||
can_init_buffers();
|
||||
#endif
|
||||
|
||||
// init early devices
|
||||
clock_init();
|
||||
peripherals_init();
|
||||
@@ -264,7 +289,9 @@ int main(void) {
|
||||
led_set(LED_RED, true);
|
||||
led_set(LED_GREEN, true);
|
||||
adc_init(ADC1);
|
||||
#ifdef STM32H7
|
||||
dts_init();
|
||||
#endif
|
||||
|
||||
// print hello
|
||||
print("\n\n\n************************ MAIN START ************************\n");
|
||||
@@ -329,7 +356,7 @@ int main(void) {
|
||||
|
||||
// LED should keep on blinking all the time
|
||||
while (true) {
|
||||
#ifdef ALLOW_DEBUG
|
||||
#if defined(ALLOW_DEBUG) && defined(STM32H7)
|
||||
if (stop_mode_requested) {
|
||||
enter_stop_mode();
|
||||
}
|
||||
@@ -350,11 +377,13 @@ int main(void) {
|
||||
delay((MAX_LED_FADE - fade) >> 4);
|
||||
}
|
||||
} else {
|
||||
#ifdef STM32H7
|
||||
if ((hw_type == HW_TYPE_CUATRO) && !current_board->read_som_gpio()) {
|
||||
assert_fatal(current_safety_mode == SAFETY_SILENT, "Error: Entering low power mode while not in SAFETY_SILENT. Hanging\n");
|
||||
enter_stop_mode(); // deep sleep, wakes on CAN or SBU activity
|
||||
assert_fatal(false, "Error: enter_stop_mode returned after system reset. Hanging\n");
|
||||
}
|
||||
#endif
|
||||
// cppcheck-suppress misra-c2012-17.3 ; CMSIS __WFI macro expands to inline asm
|
||||
__WFI();
|
||||
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
|
||||
|
||||
@@ -7,6 +7,9 @@ uint8_t hw_type = 0;
|
||||
board *current_board;
|
||||
uint32_t uptime_cnt = 0;
|
||||
uint16_t reset_flags = 0U;
|
||||
#ifdef STM32F4
|
||||
uint16_t sound_output_level = 0U;
|
||||
#endif
|
||||
|
||||
// heartbeat state
|
||||
uint32_t heartbeat_counter = 0;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
// ///////////////////////////////////////////////////////////// //
|
||||
// Hardware abstraction layer for all different supported boards //
|
||||
// ///////////////////////////////////////////////////////////// //
|
||||
#include "board/boards/board_declarations.h"
|
||||
#include "board/boards/unused_funcs.h"
|
||||
|
||||
// ///// Board definition and detection ///// //
|
||||
#include "board/stm32f4/lladc.h"
|
||||
#include "board/drivers/harness.h"
|
||||
#include "board/drivers/fan.h"
|
||||
#include "board/stm32f4/llfan.h"
|
||||
#include "board/drivers/clock_source.h"
|
||||
#include "board/boards/dos.h"
|
||||
|
||||
void detect_board_type(void) {
|
||||
set_gpio_output(GPIOC, 14, 1);
|
||||
set_gpio_output(GPIOC, 5, 1);
|
||||
if (!detect_with_pull(GPIOB, 1, PULL_UP) && !detect_with_pull(GPIOB, 7, PULL_UP)) {
|
||||
hw_type = HW_TYPE_DOS;
|
||||
current_board = &board_dos;
|
||||
}
|
||||
|
||||
// Return A13 to the alt mode to fix SWD
|
||||
set_gpio_alternate(GPIOA, 13, GPIO_AF0_SWJ);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
void clock_init(void) {
|
||||
// enable external oscillator
|
||||
register_set_bits(&(RCC->CR), RCC_CR_HSEON);
|
||||
while ((RCC->CR & RCC_CR_HSERDY) == 0U);
|
||||
|
||||
// divide things
|
||||
// AHB = 96MHz
|
||||
// APB1 = 48MHz
|
||||
// APB2 = 48MHz
|
||||
register_set(&(RCC->CFGR), RCC_CFGR_HPRE_DIV1 | RCC_CFGR_PPRE2_DIV2 | RCC_CFGR_PPRE1_DIV2, 0xFF7FFCF3U);
|
||||
|
||||
// 16MHz crystal
|
||||
// PLLM: 8
|
||||
// PLLN: 96
|
||||
// PLLP: 2
|
||||
// PLLQ: 4
|
||||
// P output: 96MHz
|
||||
// Q output: 48MHz
|
||||
register_set(&(RCC->PLLCFGR), RCC_PLLCFGR_PLLQ_2 | RCC_PLLCFGR_PLLM_3 | RCC_PLLCFGR_PLLN_6 | RCC_PLLCFGR_PLLN_5 | RCC_PLLCFGR_PLLSRC_HSE, 0x7F437FFFU);
|
||||
|
||||
// start PLL
|
||||
register_set_bits(&(RCC->CR), RCC_CR_PLLON);
|
||||
while ((RCC->CR & RCC_CR_PLLRDY) == 0U);
|
||||
|
||||
// Configure Flash prefetch, Instruction cache, Data cache and wait state
|
||||
// *** without this, it breaks ***
|
||||
register_set(&(FLASH->ACR), FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS, 0x1F0FU);
|
||||
|
||||
// switch to PLL
|
||||
register_set_bits(&(RCC->CFGR), RCC_CFGR_SW_PLL);
|
||||
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
|
||||
|
||||
// *** running on PLL ***
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/**************************************************************************//**
|
||||
* @file cmsis_compiler.h
|
||||
* @brief CMSIS compiler generic header file
|
||||
* @version V5.1.0
|
||||
* @date 09. October 2018
|
||||
******************************************************************************/
|
||||
/*
|
||||
* Copyright (c) 2009-2018 Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __CMSIS_COMPILER_H
|
||||
#define __CMSIS_COMPILER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Arm Compiler 4/5
|
||||
*/
|
||||
#if defined ( __CC_ARM )
|
||||
#include "cmsis_armcc.h"
|
||||
|
||||
|
||||
/*
|
||||
* Arm Compiler 6.6 LTM (armclang)
|
||||
*/
|
||||
#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050) && (__ARMCC_VERSION < 6100100)
|
||||
#include "cmsis_armclang_ltm.h"
|
||||
|
||||
/*
|
||||
* Arm Compiler above 6.10.1 (armclang)
|
||||
*/
|
||||
#elif defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6100100)
|
||||
#include "cmsis_armclang.h"
|
||||
|
||||
|
||||
/*
|
||||
* GNU Compiler
|
||||
*/
|
||||
#elif defined ( __GNUC__ )
|
||||
#include "cmsis_gcc.h"
|
||||
|
||||
|
||||
/*
|
||||
* IAR Compiler
|
||||
*/
|
||||
#elif defined ( __ICCARM__ )
|
||||
#include <cmsis_iccarm.h>
|
||||
|
||||
|
||||
/*
|
||||
* TI Arm Compiler
|
||||
*/
|
||||
#elif defined ( __TI_ARM__ )
|
||||
#include <cmsis_ccs.h>
|
||||
|
||||
#ifndef __ASM
|
||||
#define __ASM __asm
|
||||
#endif
|
||||
#ifndef __INLINE
|
||||
#define __INLINE inline
|
||||
#endif
|
||||
#ifndef __STATIC_INLINE
|
||||
#define __STATIC_INLINE static inline
|
||||
#endif
|
||||
#ifndef __STATIC_FORCEINLINE
|
||||
#define __STATIC_FORCEINLINE __STATIC_INLINE
|
||||
#endif
|
||||
#ifndef __NO_RETURN
|
||||
#define __NO_RETURN __attribute__((noreturn))
|
||||
#endif
|
||||
#ifndef __USED
|
||||
#define __USED __attribute__((used))
|
||||
#endif
|
||||
#ifndef __WEAK
|
||||
#define __WEAK __attribute__((weak))
|
||||
#endif
|
||||
#ifndef __PACKED
|
||||
#define __PACKED __attribute__((packed))
|
||||
#endif
|
||||
#ifndef __PACKED_STRUCT
|
||||
#define __PACKED_STRUCT struct __attribute__((packed))
|
||||
#endif
|
||||
#ifndef __PACKED_UNION
|
||||
#define __PACKED_UNION union __attribute__((packed))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32 /* deprecated */
|
||||
struct __attribute__((packed)) T_UINT32 { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_WRITE
|
||||
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
|
||||
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void*)(addr))->v) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_READ
|
||||
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
|
||||
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_WRITE
|
||||
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_READ
|
||||
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
|
||||
#endif
|
||||
#ifndef __ALIGNED
|
||||
#define __ALIGNED(x) __attribute__((aligned(x)))
|
||||
#endif
|
||||
#ifndef __RESTRICT
|
||||
#define __RESTRICT __restrict
|
||||
#endif
|
||||
#ifndef __COMPILER_BARRIER
|
||||
#warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.
|
||||
#define __COMPILER_BARRIER() (void)0
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* TASKING Compiler
|
||||
*/
|
||||
#elif defined ( __TASKING__ )
|
||||
/*
|
||||
* The CMSIS functions have been implemented as intrinsics in the compiler.
|
||||
* Please use "carm -?i" to get an up to date list of all intrinsics,
|
||||
* Including the CMSIS ones.
|
||||
*/
|
||||
|
||||
#ifndef __ASM
|
||||
#define __ASM __asm
|
||||
#endif
|
||||
#ifndef __INLINE
|
||||
#define __INLINE inline
|
||||
#endif
|
||||
#ifndef __STATIC_INLINE
|
||||
#define __STATIC_INLINE static inline
|
||||
#endif
|
||||
#ifndef __STATIC_FORCEINLINE
|
||||
#define __STATIC_FORCEINLINE __STATIC_INLINE
|
||||
#endif
|
||||
#ifndef __NO_RETURN
|
||||
#define __NO_RETURN __attribute__((noreturn))
|
||||
#endif
|
||||
#ifndef __USED
|
||||
#define __USED __attribute__((used))
|
||||
#endif
|
||||
#ifndef __WEAK
|
||||
#define __WEAK __attribute__((weak))
|
||||
#endif
|
||||
#ifndef __PACKED
|
||||
#define __PACKED __packed__
|
||||
#endif
|
||||
#ifndef __PACKED_STRUCT
|
||||
#define __PACKED_STRUCT struct __packed__
|
||||
#endif
|
||||
#ifndef __PACKED_UNION
|
||||
#define __PACKED_UNION union __packed__
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32 /* deprecated */
|
||||
struct __packed__ T_UINT32 { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_WRITE
|
||||
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
|
||||
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_READ
|
||||
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
|
||||
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_WRITE
|
||||
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_READ
|
||||
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
|
||||
#endif
|
||||
#ifndef __ALIGNED
|
||||
#define __ALIGNED(x) __align(x)
|
||||
#endif
|
||||
#ifndef __RESTRICT
|
||||
#warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.
|
||||
#define __RESTRICT
|
||||
#endif
|
||||
#ifndef __COMPILER_BARRIER
|
||||
#warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.
|
||||
#define __COMPILER_BARRIER() (void)0
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* COSMIC Compiler
|
||||
*/
|
||||
#elif defined ( __CSMC__ )
|
||||
#include <cmsis_csm.h>
|
||||
|
||||
#ifndef __ASM
|
||||
#define __ASM _asm
|
||||
#endif
|
||||
#ifndef __INLINE
|
||||
#define __INLINE inline
|
||||
#endif
|
||||
#ifndef __STATIC_INLINE
|
||||
#define __STATIC_INLINE static inline
|
||||
#endif
|
||||
#ifndef __STATIC_FORCEINLINE
|
||||
#define __STATIC_FORCEINLINE __STATIC_INLINE
|
||||
#endif
|
||||
#ifndef __NO_RETURN
|
||||
// NO RETURN is automatically detected hence no warning here
|
||||
#define __NO_RETURN
|
||||
#endif
|
||||
#ifndef __USED
|
||||
#warning No compiler specific solution for __USED. __USED is ignored.
|
||||
#define __USED
|
||||
#endif
|
||||
#ifndef __WEAK
|
||||
#define __WEAK __weak
|
||||
#endif
|
||||
#ifndef __PACKED
|
||||
#define __PACKED @packed
|
||||
#endif
|
||||
#ifndef __PACKED_STRUCT
|
||||
#define __PACKED_STRUCT @packed struct
|
||||
#endif
|
||||
#ifndef __PACKED_UNION
|
||||
#define __PACKED_UNION @packed union
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32 /* deprecated */
|
||||
@packed struct T_UINT32 { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32(x) (((struct T_UINT32 *)(x))->v)
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_WRITE
|
||||
__PACKED_STRUCT T_UINT16_WRITE { uint16_t v; };
|
||||
#define __UNALIGNED_UINT16_WRITE(addr, val) (void)((((struct T_UINT16_WRITE *)(void *)(addr))->v) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT16_READ
|
||||
__PACKED_STRUCT T_UINT16_READ { uint16_t v; };
|
||||
#define __UNALIGNED_UINT16_READ(addr) (((const struct T_UINT16_READ *)(const void *)(addr))->v)
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_WRITE
|
||||
__PACKED_STRUCT T_UINT32_WRITE { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32_WRITE(addr, val) (void)((((struct T_UINT32_WRITE *)(void *)(addr))->v) = (val))
|
||||
#endif
|
||||
#ifndef __UNALIGNED_UINT32_READ
|
||||
__PACKED_STRUCT T_UINT32_READ { uint32_t v; };
|
||||
#define __UNALIGNED_UINT32_READ(addr) (((const struct T_UINT32_READ *)(const void *)(addr))->v)
|
||||
#endif
|
||||
#ifndef __ALIGNED
|
||||
#warning No compiler specific solution for __ALIGNED. __ALIGNED is ignored.
|
||||
#define __ALIGNED(x)
|
||||
#endif
|
||||
#ifndef __RESTRICT
|
||||
#warning No compiler specific solution for __RESTRICT. __RESTRICT is ignored.
|
||||
#define __RESTRICT
|
||||
#endif
|
||||
#ifndef __COMPILER_BARRIER
|
||||
#warning No compiler specific solution for __COMPILER_BARRIER. __COMPILER_BARRIER is ignored.
|
||||
#define __COMPILER_BARRIER() (void)0
|
||||
#endif
|
||||
|
||||
|
||||
#else
|
||||
#error Unknown compiler.
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* __CMSIS_COMPILER_H */
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
/**************************************************************************//**
|
||||
* @file cmsis_version.h
|
||||
* @brief CMSIS Core(M) Version definitions
|
||||
* @version V5.0.3
|
||||
* @date 24. June 2019
|
||||
******************************************************************************/
|
||||
/*
|
||||
* Copyright (c) 2009-2019 ARM Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined ( __ICCARM__ )
|
||||
#pragma system_include /* treat file as system include file for MISRA check */
|
||||
#elif defined (__clang__)
|
||||
#pragma clang system_header /* treat file as system include file */
|
||||
#endif
|
||||
|
||||
#ifndef __CMSIS_VERSION_H
|
||||
#define __CMSIS_VERSION_H
|
||||
|
||||
/* CMSIS Version definitions */
|
||||
#define __CM_CMSIS_VERSION_MAIN ( 5U) /*!< [31:16] CMSIS Core(M) main version */
|
||||
#define __CM_CMSIS_VERSION_SUB ( 3U) /*!< [15:0] CMSIS Core(M) sub version */
|
||||
#define __CM_CMSIS_VERSION ((__CM_CMSIS_VERSION_MAIN << 16U) | \
|
||||
__CM_CMSIS_VERSION_SUB ) /*!< CMSIS Core(M) version number */
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,273 @@
|
||||
/******************************************************************************
|
||||
* @file mpu_armv7.h
|
||||
* @brief CMSIS MPU API for Armv7-M MPU
|
||||
* @version V5.1.0
|
||||
* @date 08. March 2019
|
||||
******************************************************************************/
|
||||
/*
|
||||
* Copyright (c) 2017-2019 Arm Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the License); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if defined ( __ICCARM__ )
|
||||
#pragma system_include /* treat file as system include file for MISRA check */
|
||||
#elif defined (__clang__)
|
||||
#pragma clang system_header /* treat file as system include file */
|
||||
#endif
|
||||
|
||||
#ifndef ARM_MPU_ARMV7_H
|
||||
#define ARM_MPU_ARMV7_H
|
||||
|
||||
#define ARM_MPU_REGION_SIZE_32B ((uint8_t)0x04U) ///!< MPU Region Size 32 Bytes
|
||||
#define ARM_MPU_REGION_SIZE_64B ((uint8_t)0x05U) ///!< MPU Region Size 64 Bytes
|
||||
#define ARM_MPU_REGION_SIZE_128B ((uint8_t)0x06U) ///!< MPU Region Size 128 Bytes
|
||||
#define ARM_MPU_REGION_SIZE_256B ((uint8_t)0x07U) ///!< MPU Region Size 256 Bytes
|
||||
#define ARM_MPU_REGION_SIZE_512B ((uint8_t)0x08U) ///!< MPU Region Size 512 Bytes
|
||||
#define ARM_MPU_REGION_SIZE_1KB ((uint8_t)0x09U) ///!< MPU Region Size 1 KByte
|
||||
#define ARM_MPU_REGION_SIZE_2KB ((uint8_t)0x0AU) ///!< MPU Region Size 2 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_4KB ((uint8_t)0x0BU) ///!< MPU Region Size 4 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_8KB ((uint8_t)0x0CU) ///!< MPU Region Size 8 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_16KB ((uint8_t)0x0DU) ///!< MPU Region Size 16 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_32KB ((uint8_t)0x0EU) ///!< MPU Region Size 32 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_64KB ((uint8_t)0x0FU) ///!< MPU Region Size 64 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_128KB ((uint8_t)0x10U) ///!< MPU Region Size 128 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_256KB ((uint8_t)0x11U) ///!< MPU Region Size 256 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_512KB ((uint8_t)0x12U) ///!< MPU Region Size 512 KBytes
|
||||
#define ARM_MPU_REGION_SIZE_1MB ((uint8_t)0x13U) ///!< MPU Region Size 1 MByte
|
||||
#define ARM_MPU_REGION_SIZE_2MB ((uint8_t)0x14U) ///!< MPU Region Size 2 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_4MB ((uint8_t)0x15U) ///!< MPU Region Size 4 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_8MB ((uint8_t)0x16U) ///!< MPU Region Size 8 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_16MB ((uint8_t)0x17U) ///!< MPU Region Size 16 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_32MB ((uint8_t)0x18U) ///!< MPU Region Size 32 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_64MB ((uint8_t)0x19U) ///!< MPU Region Size 64 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_128MB ((uint8_t)0x1AU) ///!< MPU Region Size 128 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_256MB ((uint8_t)0x1BU) ///!< MPU Region Size 256 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_512MB ((uint8_t)0x1CU) ///!< MPU Region Size 512 MBytes
|
||||
#define ARM_MPU_REGION_SIZE_1GB ((uint8_t)0x1DU) ///!< MPU Region Size 1 GByte
|
||||
#define ARM_MPU_REGION_SIZE_2GB ((uint8_t)0x1EU) ///!< MPU Region Size 2 GBytes
|
||||
#define ARM_MPU_REGION_SIZE_4GB ((uint8_t)0x1FU) ///!< MPU Region Size 4 GBytes
|
||||
|
||||
#define ARM_MPU_AP_NONE 0U ///!< MPU Access Permission no access
|
||||
#define ARM_MPU_AP_PRIV 1U ///!< MPU Access Permission privileged access only
|
||||
#define ARM_MPU_AP_URO 2U ///!< MPU Access Permission unprivileged access read-only
|
||||
#define ARM_MPU_AP_FULL 3U ///!< MPU Access Permission full access
|
||||
#define ARM_MPU_AP_PRO 5U ///!< MPU Access Permission privileged access read-only
|
||||
#define ARM_MPU_AP_RO 6U ///!< MPU Access Permission read-only access
|
||||
|
||||
/** MPU Region Base Address Register Value
|
||||
*
|
||||
* \param Region The region to be configured, number 0 to 15.
|
||||
* \param BaseAddress The base address for the region.
|
||||
*/
|
||||
#define ARM_MPU_RBAR(Region, BaseAddress) \
|
||||
(((BaseAddress) & MPU_RBAR_ADDR_Msk) | \
|
||||
((Region) & MPU_RBAR_REGION_Msk) | \
|
||||
(MPU_RBAR_VALID_Msk))
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attributes
|
||||
*
|
||||
* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
|
||||
* \param IsShareable Region is shareable between multiple bus masters.
|
||||
* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache.
|
||||
* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
|
||||
*/
|
||||
#define ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable) \
|
||||
((((TypeExtField) << MPU_RASR_TEX_Pos) & MPU_RASR_TEX_Msk) | \
|
||||
(((IsShareable) << MPU_RASR_S_Pos) & MPU_RASR_S_Msk) | \
|
||||
(((IsCacheable) << MPU_RASR_C_Pos) & MPU_RASR_C_Msk) | \
|
||||
(((IsBufferable) << MPU_RASR_B_Pos) & MPU_RASR_B_Msk))
|
||||
|
||||
/**
|
||||
* MPU Region Attribute and Size Register Value
|
||||
*
|
||||
* \param DisableExec Instruction access disable bit, 1= disable instruction fetches.
|
||||
* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode.
|
||||
* \param AccessAttributes Memory access attribution, see \ref ARM_MPU_ACCESS_.
|
||||
* \param SubRegionDisable Sub-region disable field.
|
||||
* \param Size Region size of the region to be configured, for example 4K, 8K.
|
||||
*/
|
||||
#define ARM_MPU_RASR_EX(DisableExec, AccessPermission, AccessAttributes, SubRegionDisable, Size) \
|
||||
((((DisableExec) << MPU_RASR_XN_Pos) & MPU_RASR_XN_Msk) | \
|
||||
(((AccessPermission) << MPU_RASR_AP_Pos) & MPU_RASR_AP_Msk) | \
|
||||
(((AccessAttributes) & (MPU_RASR_TEX_Msk | MPU_RASR_S_Msk | MPU_RASR_C_Msk | MPU_RASR_B_Msk))) | \
|
||||
(((SubRegionDisable) << MPU_RASR_SRD_Pos) & MPU_RASR_SRD_Msk) | \
|
||||
(((Size) << MPU_RASR_SIZE_Pos) & MPU_RASR_SIZE_Msk) | \
|
||||
(((MPU_RASR_ENABLE_Msk))))
|
||||
|
||||
/**
|
||||
* MPU Region Attribute and Size Register Value
|
||||
*
|
||||
* \param DisableExec Instruction access disable bit, 1= disable instruction fetches.
|
||||
* \param AccessPermission Data access permissions, allows you to configure read/write access for User and Privileged mode.
|
||||
* \param TypeExtField Type extension field, allows you to configure memory access type, for example strongly ordered, peripheral.
|
||||
* \param IsShareable Region is shareable between multiple bus masters.
|
||||
* \param IsCacheable Region is cacheable, i.e. its value may be kept in cache.
|
||||
* \param IsBufferable Region is bufferable, i.e. using write-back caching. Cacheable but non-bufferable regions use write-through policy.
|
||||
* \param SubRegionDisable Sub-region disable field.
|
||||
* \param Size Region size of the region to be configured, for example 4K, 8K.
|
||||
*/
|
||||
#define ARM_MPU_RASR(DisableExec, AccessPermission, TypeExtField, IsShareable, IsCacheable, IsBufferable, SubRegionDisable, Size) \
|
||||
ARM_MPU_RASR_EX(DisableExec, AccessPermission, ARM_MPU_ACCESS_(TypeExtField, IsShareable, IsCacheable, IsBufferable), SubRegionDisable, Size)
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attribute for strongly ordered memory.
|
||||
* - TEX: 000b
|
||||
* - Shareable
|
||||
* - Non-cacheable
|
||||
* - Non-bufferable
|
||||
*/
|
||||
#define ARM_MPU_ACCESS_ORDERED ARM_MPU_ACCESS_(0U, 1U, 0U, 0U)
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attribute for device memory.
|
||||
* - TEX: 000b (if shareable) or 010b (if non-shareable)
|
||||
* - Shareable or non-shareable
|
||||
* - Non-cacheable
|
||||
* - Bufferable (if shareable) or non-bufferable (if non-shareable)
|
||||
*
|
||||
* \param IsShareable Configures the device memory as shareable or non-shareable.
|
||||
*/
|
||||
#define ARM_MPU_ACCESS_DEVICE(IsShareable) ((IsShareable) ? ARM_MPU_ACCESS_(0U, 1U, 0U, 1U) : ARM_MPU_ACCESS_(2U, 0U, 0U, 0U))
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attribute for normal memory.
|
||||
* - TEX: 1BBb (reflecting outer cacheability rules)
|
||||
* - Shareable or non-shareable
|
||||
* - Cacheable or non-cacheable (reflecting inner cacheability rules)
|
||||
* - Bufferable or non-bufferable (reflecting inner cacheability rules)
|
||||
*
|
||||
* \param OuterCp Configures the outer cache policy.
|
||||
* \param InnerCp Configures the inner cache policy.
|
||||
* \param IsShareable Configures the memory as shareable or non-shareable.
|
||||
*/
|
||||
#define ARM_MPU_ACCESS_NORMAL(OuterCp, InnerCp, IsShareable) ARM_MPU_ACCESS_((4U | (OuterCp)), IsShareable, ((InnerCp) & 2U), ((InnerCp) & 1U))
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attribute non-cacheable policy.
|
||||
*/
|
||||
#define ARM_MPU_CACHEP_NOCACHE 0U
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attribute write-back, write and read allocate policy.
|
||||
*/
|
||||
#define ARM_MPU_CACHEP_WB_WRA 1U
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attribute write-through, no write allocate policy.
|
||||
*/
|
||||
#define ARM_MPU_CACHEP_WT_NWA 2U
|
||||
|
||||
/**
|
||||
* MPU Memory Access Attribute write-back, no write allocate policy.
|
||||
*/
|
||||
#define ARM_MPU_CACHEP_WB_NWA 3U
|
||||
|
||||
|
||||
/**
|
||||
* Struct for a single MPU Region
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t RBAR; //!< The region base address register value (RBAR)
|
||||
uint32_t RASR; //!< The region attribute and size register value (RASR) \ref MPU_RASR
|
||||
} ARM_MPU_Region_t;
|
||||
|
||||
/** Enable the MPU.
|
||||
* \param MPU_Control Default access permissions for unconfigured regions.
|
||||
*/
|
||||
__STATIC_INLINE void ARM_MPU_Enable(uint32_t MPU_Control)
|
||||
{
|
||||
MPU->CTRL = MPU_Control | MPU_CTRL_ENABLE_Msk;
|
||||
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
|
||||
SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk;
|
||||
#endif
|
||||
__DSB();
|
||||
__ISB();
|
||||
}
|
||||
|
||||
/** Disable the MPU.
|
||||
*/
|
||||
__STATIC_INLINE void ARM_MPU_Disable(void)
|
||||
{
|
||||
__DMB();
|
||||
#ifdef SCB_SHCSR_MEMFAULTENA_Msk
|
||||
SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk;
|
||||
#endif
|
||||
MPU->CTRL &= ~MPU_CTRL_ENABLE_Msk;
|
||||
}
|
||||
|
||||
/** Clear and disable the given MPU region.
|
||||
* \param rnr Region number to be cleared.
|
||||
*/
|
||||
__STATIC_INLINE void ARM_MPU_ClrRegion(uint32_t rnr)
|
||||
{
|
||||
MPU->RNR = rnr;
|
||||
MPU->RASR = 0U;
|
||||
}
|
||||
|
||||
/** Configure an MPU region.
|
||||
* \param rbar Value for RBAR register.
|
||||
* \param rsar Value for RSAR register.
|
||||
*/
|
||||
__STATIC_INLINE void ARM_MPU_SetRegion(uint32_t rbar, uint32_t rasr)
|
||||
{
|
||||
MPU->RBAR = rbar;
|
||||
MPU->RASR = rasr;
|
||||
}
|
||||
|
||||
/** Configure the given MPU region.
|
||||
* \param rnr Region number to be configured.
|
||||
* \param rbar Value for RBAR register.
|
||||
* \param rsar Value for RSAR register.
|
||||
*/
|
||||
__STATIC_INLINE void ARM_MPU_SetRegionEx(uint32_t rnr, uint32_t rbar, uint32_t rasr)
|
||||
{
|
||||
MPU->RNR = rnr;
|
||||
MPU->RBAR = rbar;
|
||||
MPU->RASR = rasr;
|
||||
}
|
||||
|
||||
/** Memcopy with strictly ordered memory access, e.g. for register targets.
|
||||
* \param dst Destination data is copied to.
|
||||
* \param src Source data is copied from.
|
||||
* \param len Amount of data words to be copied.
|
||||
*/
|
||||
__STATIC_INLINE void ARM_MPU_OrderedMemcpy(volatile uint32_t* dst, const uint32_t* __RESTRICT src, uint32_t len)
|
||||
{
|
||||
uint32_t i;
|
||||
for (i = 0U; i < len; ++i)
|
||||
{
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
|
||||
/** Load the given number of MPU regions from a table.
|
||||
* \param table Pointer to the MPU configuration table.
|
||||
* \param cnt Amount of regions to be configured.
|
||||
*/
|
||||
__STATIC_INLINE void ARM_MPU_Load(ARM_MPU_Region_t const* table, uint32_t cnt)
|
||||
{
|
||||
const uint32_t rowWordSize = sizeof(ARM_MPU_Region_t)/4U;
|
||||
while (cnt > MPU_TYPE_RALIASES) {
|
||||
ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), MPU_TYPE_RALIASES*rowWordSize);
|
||||
table += MPU_TYPE_RALIASES;
|
||||
cnt -= MPU_TYPE_RALIASES;
|
||||
}
|
||||
ARM_MPU_OrderedMemcpy(&(MPU->RBAR), &(table->RBAR), cnt*rowWordSize);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32f4xx.h
|
||||
* @author MCD Application Team
|
||||
* @version V2.6.0
|
||||
* @date 04-November-2016
|
||||
* @brief CMSIS STM32F4xx Device Peripheral Access Layer Header File.
|
||||
*
|
||||
* The file is the unique include file that the application programmer
|
||||
* is using in the C source code, usually in main.c. This file contains:
|
||||
* - Configuration section that allows to select:
|
||||
* - The STM32F4xx device used in the target application
|
||||
* - To use or not the peripheral’s drivers in application code(i.e.
|
||||
* code will be based on direct access to peripheral’s registers
|
||||
* rather than drivers API), this option is controlled by
|
||||
* "#define USE_HAL_DRIVER"
|
||||
*
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** @addtogroup CMSIS
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup stm32f4xx
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef __STM32F4xx_H
|
||||
#define __STM32F4xx_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/** @addtogroup Library_configuration_section
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief STM32 Family
|
||||
*/
|
||||
#if !defined (STM32F4)
|
||||
#define STM32F4
|
||||
#endif /* STM32F4 */
|
||||
|
||||
/* Uncomment the line below according to the target STM32 device used in your
|
||||
application
|
||||
*/
|
||||
/* #if !defined (STM32F405xx) && !defined (STM32F415xx) && !defined (STM32F407xx) && !defined (STM32F417xx) && \
|
||||
!defined (STM32F427xx) && !defined (STM32F437xx) && !defined (STM32F429xx) && !defined (STM32F439xx) && \
|
||||
!defined (STM32F401xC) && !defined (STM32F401xE) && !defined (STM32F410Tx) && !defined (STM32F410Cx) && \
|
||||
!defined (STM32F410Rx) && !defined (STM32F411xE) && !defined (STM32F446xx) && !defined (STM32F469xx) && \
|
||||
!defined (STM32F479xx) && !defined (STM32F412Cx) && !defined (STM32F412Rx) && !defined (STM32F412Vx) && \
|
||||
!defined (STM32F412Zx) && !defined (STM32F413xx) && !defined (STM32F423xx) */
|
||||
/* #define STM32F405xx */ /*!< STM32F405RG, STM32F405VG and STM32F405ZG Devices */
|
||||
/* #define STM32F415xx */ /*!< STM32F415RG, STM32F415VG and STM32F415ZG Devices */
|
||||
/* #define STM32F407xx */ /*!< STM32F407VG, STM32F407VE, STM32F407ZG, STM32F407ZE, STM32F407IG and STM32F407IE Devices */
|
||||
/* #define STM32F417xx */ /*!< STM32F417VG, STM32F417VE, STM32F417ZG, STM32F417ZE, STM32F417IG and STM32F417IE Devices */
|
||||
/* #define STM32F427xx */ /*!< STM32F427VG, STM32F427VI, STM32F427ZG, STM32F427ZI, STM32F427IG and STM32F427II Devices */
|
||||
/* #define STM32F437xx */ /*!< STM32F437VG, STM32F437VI, STM32F437ZG, STM32F437ZI, STM32F437IG and STM32F437II Devices */
|
||||
/* #define STM32F429xx */ /*!< STM32F429VG, STM32F429VI, STM32F429ZG, STM32F429ZI, STM32F429BG, STM32F429BI, STM32F429NG,
|
||||
STM32F439NI, STM32F429IG and STM32F429II Devices */
|
||||
/* #define STM32F439xx */ /*!< STM32F439VG, STM32F439VI, STM32F439ZG, STM32F439ZI, STM32F439BG, STM32F439BI, STM32F439NG,
|
||||
STM32F439NI, STM32F439IG and STM32F439II Devices */
|
||||
/* #define STM32F401xC */ /*!< STM32F401CB, STM32F401CC, STM32F401RB, STM32F401RC, STM32F401VB and STM32F401VC Devices */
|
||||
/* #define STM32F401xE */ /*!< STM32F401CD, STM32F401RD, STM32F401VD, STM32F401CE, STM32F401RE and STM32F401VE Devices */
|
||||
/* #define STM32F410Tx */ /*!< STM32F410T8 and STM32F410TB Devices */
|
||||
/* #define STM32F410Cx */ /*!< STM32F410C8 and STM32F410CB Devices */
|
||||
/* #define STM32F410Rx */ /*!< STM32F410R8 and STM32F410RB Devices */
|
||||
/* #define STM32F411xE */ /*!< STM32F411CC, STM32F411RC, STM32F411VC, STM32F411CE, STM32F411RE and STM32F411VE Devices */
|
||||
/* #define STM32F446xx */ /*!< STM32F446MC, STM32F446ME, STM32F446RC, STM32F446RE, STM32F446VC, STM32F446VE, STM32F446ZC,
|
||||
and STM32F446ZE Devices */
|
||||
/* #define STM32F469xx */ /*!< STM32F469AI, STM32F469II, STM32F469BI, STM32F469NI, STM32F469AG, STM32F469IG, STM32F469BG,
|
||||
STM32F469NG, STM32F469AE, STM32F469IE, STM32F469BE and STM32F469NE Devices */
|
||||
/* #define STM32F479xx */ /*!< STM32F479AI, STM32F479II, STM32F479BI, STM32F479NI, STM32F479AG, STM32F479IG, STM32F479BG
|
||||
and STM32F479NG Devices */
|
||||
/* #define STM32F412Cx */ /*!< STM32F412CEU and STM32F412CGU Devices */
|
||||
/* #define STM32F412Zx */ /*!< STM32F412ZET, STM32F412ZGT, STM32F412ZEJ and STM32F412ZGJ Devices */
|
||||
/* #define STM32F412Vx */ /*!< STM32F412VET, STM32F412VGT, STM32F412VEH and STM32F412VGH Devices */
|
||||
/* #define STM32F412Rx */ /*!< STM32F412RET, STM32F412RGT, STM32F412REY and STM32F412RGY Devices */
|
||||
/* #define STM32F413xx */ /*!< STM32F413CH, STM32F413MH, STM32F413RH, STM32F413VH, STM32F413ZH, STM32F413CG, STM32F413MG,
|
||||
STM32F413RG, STM32F413VG and STM32F413ZG Devices */
|
||||
/* #define STM32F423xx */ /*!< STM32F423CH, STM32F423RH, STM32F423VH and STM32F423ZH Devices */
|
||||
//#endif
|
||||
|
||||
/* Tip: To avoid modifying this file each time you need to switch between these
|
||||
devices, you can define the device in your toolchain compiler preprocessor.
|
||||
*/
|
||||
#if !defined (USE_HAL_DRIVER)
|
||||
/**
|
||||
* @brief Comment the line below if you will not use the peripherals drivers.
|
||||
In this case, these drivers will not be included and the application code will
|
||||
be based on direct access to peripherals registers
|
||||
*/
|
||||
/*#define USE_HAL_DRIVER */
|
||||
#endif /* USE_HAL_DRIVER */
|
||||
|
||||
/**
|
||||
* @brief CMSIS version number V2.6.0
|
||||
*/
|
||||
#define __STM32F4xx_CMSIS_VERSION_MAIN (0x02U) /*!< [31:24] main version */
|
||||
#define __STM32F4xx_CMSIS_VERSION_SUB1 (0x06U) /*!< [23:16] sub1 version */
|
||||
#define __STM32F4xx_CMSIS_VERSION_SUB2 (0x00U) /*!< [15:8] sub2 version */
|
||||
#define __STM32F4xx_CMSIS_VERSION_RC (0x00U) /*!< [7:0] release candidate */
|
||||
#define __STM32F4xx_CMSIS_VERSION ((__STM32F4xx_CMSIS_VERSION_MAIN << 24)\
|
||||
|(__STM32F4xx_CMSIS_VERSION_SUB1 << 16)\
|
||||
|(__STM32F4xx_CMSIS_VERSION_SUB2 << 8 )\
|
||||
|(__STM32F4xx_CMSIS_VERSION))
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup Device_Included
|
||||
* @{
|
||||
*/
|
||||
|
||||
// #if defined(STM32F405xx)
|
||||
// #include "stm32f405xx.h"
|
||||
// #elif defined(STM32F415xx)
|
||||
// #include "stm32f415xx.h"
|
||||
// #elif defined(STM32F407xx)
|
||||
// #include "stm32f407xx.h"
|
||||
// #elif defined(STM32F417xx)
|
||||
// #include "stm32f417xx.h"
|
||||
// #elif defined(STM32F427xx)
|
||||
// #include "stm32f427xx.h"
|
||||
// #elif defined(STM32F437xx)
|
||||
// #include "stm32f437xx.h"
|
||||
// #elif defined(STM32F429xx)
|
||||
// #include "stm32f429xx.h"
|
||||
// #elif defined(STM32F439xx)
|
||||
// #include "stm32f439xx.h"
|
||||
// #elif defined(STM32F401xC)
|
||||
// #include "stm32f401xc.h"
|
||||
// #elif defined(STM32F401xE)
|
||||
// #include "stm32f401xe.h"
|
||||
// #elif defined(STM32F410Tx)
|
||||
// #include "stm32f410tx.h"
|
||||
// #elif defined(STM32F410Cx)
|
||||
// #include "stm32f410cx.h"
|
||||
// #elif defined(STM32F410Rx)
|
||||
// #include "stm32f410rx.h"
|
||||
// #elif defined(STM32F411xE)
|
||||
// #include "stm32f411xe.h"
|
||||
// #elif defined(STM32F446xx)
|
||||
// #include "stm32f446xx.h"
|
||||
// #elif defined(STM32F469xx)
|
||||
// #include "stm32f469xx.h"
|
||||
// #elif defined(STM32F479xx)
|
||||
// #include "stm32f479xx.h"
|
||||
// #elif defined(STM32F412Cx)
|
||||
// #include "stm32f412cx.h"
|
||||
// #elif defined(STM32F412Zx)
|
||||
// #include "stm32f412zx.h"
|
||||
// #elif defined(STM32F412Rx)
|
||||
// #include "stm32f412rx.h"
|
||||
// #elif defined(STM32F412Vx)
|
||||
// #include "stm32f412vx.h"
|
||||
#if defined(STM32F413xx)
|
||||
#include "stm32f413xx.h"
|
||||
#elif defined(STM32F423xx)
|
||||
#include "stm32f423xx.h"
|
||||
#else
|
||||
#error "Please select first the target STM32F4xx device used in your application (in stm32f4xx.h file)"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup Exported_types
|
||||
* @{
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
RESET = 0U,
|
||||
SET = !RESET
|
||||
} FlagStatus, ITStatus;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
DISABLE = 0U,
|
||||
ENABLE = !DISABLE
|
||||
} FunctionalState;
|
||||
#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
|
||||
|
||||
typedef enum
|
||||
{
|
||||
ERROR = 0U,
|
||||
SUCCESS = !ERROR
|
||||
} ErrorStatus;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/** @addtogroup Exported_macro
|
||||
* @{
|
||||
*/
|
||||
#define SET_BIT(REG, BIT) ((REG) |= (BIT))
|
||||
|
||||
#define CLEAR_BIT(REG, BIT) ((REG) &= ~(BIT))
|
||||
|
||||
#define READ_BIT(REG, BIT) ((REG) & (BIT))
|
||||
|
||||
#define CLEAR_REG(REG) ((REG) = (0x0))
|
||||
|
||||
#define WRITE_REG(REG, VAL) ((REG) = (VAL))
|
||||
|
||||
#define READ_REG(REG) ((REG))
|
||||
|
||||
#define MODIFY_REG(REG, CLEARMASK, SETMASK) WRITE_REG((REG), (((READ_REG(REG)) & (~(CLEARMASK))) | (SETMASK)))
|
||||
|
||||
#define POSITION_VAL(VAL) (__CLZ(__RBIT(VAL)))
|
||||
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#if defined (USE_HAL_DRIVER)
|
||||
#include "stm32f4xx_hal.h"
|
||||
#endif /* USE_HAL_DRIVER */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __STM32F4xx_H */
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@@ -0,0 +1,214 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file stm32f4xx_hal_def.h
|
||||
* @author MCD Application Team
|
||||
* @version V1.6.0
|
||||
* @date 04-November-2016
|
||||
* @brief This file contains HAL common defines, enumeration, macros and
|
||||
* structures definitions.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/* Define to prevent recursive inclusion -------------------------------------*/
|
||||
#ifndef __STM32F4xx_HAL_DEF
|
||||
#define __STM32F4xx_HAL_DEF
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Includes ------------------------------------------------------------------*/
|
||||
#include "stm32f4xx.h"
|
||||
//#include "Legacy/stm32_hal_legacy.h"
|
||||
//#include <stdio.h>
|
||||
|
||||
/* Exported types ------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief HAL Status structures definition
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
HAL_OK = 0x00U,
|
||||
HAL_ERROR = 0x01U,
|
||||
HAL_BUSY = 0x02U,
|
||||
HAL_TIMEOUT = 0x03U
|
||||
} HAL_StatusTypeDef;
|
||||
|
||||
/**
|
||||
* @brief HAL Lock structures definition
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
HAL_UNLOCKED = 0x00U,
|
||||
HAL_LOCKED = 0x01U
|
||||
} HAL_LockTypeDef;
|
||||
|
||||
/* Exported macro ------------------------------------------------------------*/
|
||||
#define HAL_MAX_DELAY 0xFFFFFFFFU
|
||||
|
||||
#define HAL_IS_BIT_SET(REG, BIT) (((REG) & (BIT)) != RESET)
|
||||
#define HAL_IS_BIT_CLR(REG, BIT) (((REG) & (BIT)) == RESET)
|
||||
|
||||
#define __HAL_LINKDMA(__HANDLE__, __PPP_DMA_FIELD__, __DMA_HANDLE__) \
|
||||
do{ \
|
||||
(__HANDLE__)->__PPP_DMA_FIELD__ = &(__DMA_HANDLE__); \
|
||||
(__DMA_HANDLE__).Parent = (__HANDLE__); \
|
||||
} while(0)
|
||||
|
||||
#define UNUSED(x) ((void)(x))
|
||||
|
||||
/** @brief Reset the Handle's State field.
|
||||
* @param __HANDLE__: specifies the Peripheral Handle.
|
||||
* @note This macro can be used for the following purpose:
|
||||
* - When the Handle is declared as local variable; before passing it as parameter
|
||||
* to HAL_PPP_Init() for the first time, it is mandatory to use this macro
|
||||
* to set to 0 the Handle's "State" field.
|
||||
* Otherwise, "State" field may have any random value and the first time the function
|
||||
* HAL_PPP_Init() is called, the low level hardware initialization will be missed
|
||||
* (i.e. HAL_PPP_MspInit() will not be executed).
|
||||
* - When there is a need to reconfigure the low level hardware: instead of calling
|
||||
* HAL_PPP_DeInit() then HAL_PPP_Init(), user can make a call to this macro then HAL_PPP_Init().
|
||||
* In this later function, when the Handle's "State" field is set to 0, it will execute the function
|
||||
* HAL_PPP_MspInit() which will reconfigure the low level hardware.
|
||||
* @retval None
|
||||
*/
|
||||
#define __HAL_RESET_HANDLE_STATE(__HANDLE__) ((__HANDLE__)->State = 0U)
|
||||
|
||||
#if (USE_RTOS == 1)
|
||||
/* Reserved for future use */
|
||||
#error "USE_RTOS should be 0 in the current HAL release"
|
||||
#else
|
||||
#define __HAL_LOCK(__HANDLE__) \
|
||||
do{ \
|
||||
if((__HANDLE__)->Lock == HAL_LOCKED) \
|
||||
{ \
|
||||
return HAL_BUSY; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
(__HANDLE__)->Lock = HAL_LOCKED; \
|
||||
} \
|
||||
}while (0)
|
||||
|
||||
#define __HAL_UNLOCK(__HANDLE__) \
|
||||
do{ \
|
||||
(__HANDLE__)->Lock = HAL_UNLOCKED; \
|
||||
}while (0)
|
||||
#endif /* USE_RTOS */
|
||||
|
||||
#if defined ( __GNUC__ )
|
||||
#ifndef __weak
|
||||
#define __weak __attribute__((weak))
|
||||
#endif /* __weak */
|
||||
#ifndef __packed
|
||||
#define __packed __attribute__((__packed__))
|
||||
#endif /* __packed */
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
|
||||
/* Macro to get variable aligned on 4-bytes, for __ICCARM__ the directive "#pragma data_alignment=4" must be used instead */
|
||||
#if defined (__GNUC__) /* GNU Compiler */
|
||||
#ifndef __ALIGN_END
|
||||
#define __ALIGN_END __attribute__ ((aligned (4)))
|
||||
#endif /* __ALIGN_END */
|
||||
#ifndef __ALIGN_BEGIN
|
||||
#define __ALIGN_BEGIN
|
||||
#endif /* __ALIGN_BEGIN */
|
||||
#else
|
||||
#ifndef __ALIGN_END
|
||||
#define __ALIGN_END
|
||||
#endif /* __ALIGN_END */
|
||||
#ifndef __ALIGN_BEGIN
|
||||
#if defined (__CC_ARM) /* ARM Compiler */
|
||||
#define __ALIGN_BEGIN __align(4)
|
||||
#elif defined (__ICCARM__) /* IAR Compiler */
|
||||
#define __ALIGN_BEGIN
|
||||
#endif /* __CC_ARM */
|
||||
#endif /* __ALIGN_BEGIN */
|
||||
#endif /* __GNUC__ */
|
||||
|
||||
|
||||
/**
|
||||
* @brief __RAM_FUNC definition
|
||||
*/
|
||||
#if defined ( __CC_ARM )
|
||||
/* ARM Compiler
|
||||
------------
|
||||
RAM functions are defined using the toolchain options.
|
||||
Functions that are executed in RAM should reside in a separate source module.
|
||||
Using the 'Options for File' dialog you can simply change the 'Code / Const'
|
||||
area of a module to a memory space in physical RAM.
|
||||
Available memory areas are declared in the 'Target' tab of the 'Options for Target'
|
||||
dialog.
|
||||
*/
|
||||
#define __RAM_FUNC HAL_StatusTypeDef
|
||||
|
||||
#elif defined ( __ICCARM__ )
|
||||
/* ICCARM Compiler
|
||||
---------------
|
||||
RAM functions are defined using a specific toolchain keyword "__ramfunc".
|
||||
*/
|
||||
#define __RAM_FUNC __ramfunc HAL_StatusTypeDef
|
||||
|
||||
#elif defined ( __GNUC__ )
|
||||
/* GNU Compiler
|
||||
------------
|
||||
RAM functions are defined using a specific toolchain attribute
|
||||
"__attribute__((section(".RamFunc")))".
|
||||
*/
|
||||
#define __RAM_FUNC HAL_StatusTypeDef __attribute__((section(".RamFunc")))
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief __NOINLINE definition
|
||||
*/
|
||||
#if defined ( __CC_ARM ) || defined ( __GNUC__ )
|
||||
/* ARM & GNUCompiler
|
||||
----------------
|
||||
*/
|
||||
#define __NOINLINE __attribute__ ( (noinline) )
|
||||
|
||||
#elif defined ( __ICCARM__ )
|
||||
/* ICCARM Compiler
|
||||
---------------
|
||||
*/
|
||||
#define __NOINLINE _Pragma("optimize = no_inline")
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ___STM32F4xx_HAL_DEF */
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file system_stm32f4xx.h
|
||||
* @author MCD Application Team
|
||||
* @version V2.6.0
|
||||
* @date 04-November-2016
|
||||
* @brief CMSIS Cortex-M4 Device System Source File for STM32F4xx devices.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** @addtogroup CMSIS
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup stm32f4xx_system
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Define to prevent recursive inclusion
|
||||
*/
|
||||
#ifndef __SYSTEM_STM32F4XX_H
|
||||
#define __SYSTEM_STM32F4XX_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @addtogroup STM32F4xx_System_Includes
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
/** @addtogroup STM32F4xx_System_Exported_types
|
||||
* @{
|
||||
*/
|
||||
/* This variable is updated in three ways:
|
||||
1) by calling CMSIS function SystemCoreClockUpdate()
|
||||
2) by calling HAL API function HAL_RCC_GetSysClockFreq()
|
||||
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
|
||||
Note: If you use this function to configure the system clock; then there
|
||||
is no need to call the 2 first functions listed above, since SystemCoreClock
|
||||
variable is updated automatically.
|
||||
*/
|
||||
extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */
|
||||
|
||||
extern const uint8_t AHBPrescTable[16]; /*!< AHB prescalers table values */
|
||||
extern const uint8_t APBPrescTable[8]; /*!< APB prescalers table values */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F4xx_System_Exported_Constants
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F4xx_System_Exported_Macros
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F4xx_System_Exported_Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
extern void SystemInit(void);
|
||||
extern void SystemCoreClockUpdate(void);
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /*__SYSTEM_STM32F4XX_H */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@@ -0,0 +1,98 @@
|
||||
// ********************* Bare interrupt handlers *********************
|
||||
// Only implemented the STM32F413 interrupts for now
|
||||
|
||||
void WWDG_IRQHandler(void) {handle_interrupt(WWDG_IRQn);}
|
||||
void PVD_IRQHandler(void) {handle_interrupt(PVD_IRQn);}
|
||||
void TAMP_STAMP_IRQHandler(void) {handle_interrupt(TAMP_STAMP_IRQn);}
|
||||
void RTC_WKUP_IRQHandler(void) {handle_interrupt(RTC_WKUP_IRQn);}
|
||||
void FLASH_IRQHandler(void) {handle_interrupt(FLASH_IRQn);}
|
||||
void RCC_IRQHandler(void) {handle_interrupt(RCC_IRQn);}
|
||||
void EXTI0_IRQHandler(void) {handle_interrupt(EXTI0_IRQn);}
|
||||
void EXTI1_IRQHandler(void) {handle_interrupt(EXTI1_IRQn);}
|
||||
void EXTI2_IRQHandler(void) {handle_interrupt(EXTI2_IRQn);}
|
||||
void EXTI3_IRQHandler(void) {handle_interrupt(EXTI3_IRQn);}
|
||||
void EXTI4_IRQHandler(void) {handle_interrupt(EXTI4_IRQn);}
|
||||
void DMA1_Stream0_IRQHandler(void) {handle_interrupt(DMA1_Stream0_IRQn);}
|
||||
void DMA1_Stream1_IRQHandler(void) {handle_interrupt(DMA1_Stream1_IRQn);}
|
||||
void DMA1_Stream2_IRQHandler(void) {handle_interrupt(DMA1_Stream2_IRQn);}
|
||||
void DMA1_Stream3_IRQHandler(void) {handle_interrupt(DMA1_Stream3_IRQn);}
|
||||
void DMA1_Stream4_IRQHandler(void) {handle_interrupt(DMA1_Stream4_IRQn);}
|
||||
void DMA1_Stream5_IRQHandler(void) {handle_interrupt(DMA1_Stream5_IRQn);}
|
||||
void DMA1_Stream6_IRQHandler(void) {handle_interrupt(DMA1_Stream6_IRQn);}
|
||||
void ADC_IRQHandler(void) {handle_interrupt(ADC_IRQn);}
|
||||
void CAN1_TX_IRQHandler(void) {handle_interrupt(CAN1_TX_IRQn);}
|
||||
void CAN1_RX0_IRQHandler(void) {handle_interrupt(CAN1_RX0_IRQn);}
|
||||
void CAN1_RX1_IRQHandler(void) {handle_interrupt(CAN1_RX1_IRQn);}
|
||||
void CAN1_SCE_IRQHandler(void) {handle_interrupt(CAN1_SCE_IRQn);}
|
||||
void EXTI9_5_IRQHandler(void) {handle_interrupt(EXTI9_5_IRQn);}
|
||||
void TIM1_BRK_TIM9_IRQHandler(void) {handle_interrupt(TIM1_BRK_TIM9_IRQn);}
|
||||
void TIM1_UP_TIM10_IRQHandler(void) {handle_interrupt(TIM1_UP_TIM10_IRQn);}
|
||||
void TIM1_TRG_COM_TIM11_IRQHandler(void) {handle_interrupt(TIM1_TRG_COM_TIM11_IRQn);}
|
||||
void TIM1_CC_IRQHandler(void) {handle_interrupt(TIM1_CC_IRQn);}
|
||||
void TIM2_IRQHandler(void) {handle_interrupt(TIM2_IRQn);}
|
||||
void TIM3_IRQHandler(void) {handle_interrupt(TIM3_IRQn);}
|
||||
void TIM4_IRQHandler(void) {handle_interrupt(TIM4_IRQn);}
|
||||
void I2C1_EV_IRQHandler(void) {handle_interrupt(I2C1_EV_IRQn);}
|
||||
void I2C1_ER_IRQHandler(void) {handle_interrupt(I2C1_ER_IRQn);}
|
||||
void I2C2_EV_IRQHandler(void) {handle_interrupt(I2C2_EV_IRQn);}
|
||||
void I2C2_ER_IRQHandler(void) {handle_interrupt(I2C2_ER_IRQn);}
|
||||
void SPI1_IRQHandler(void) {handle_interrupt(SPI1_IRQn);}
|
||||
void SPI2_IRQHandler(void) {handle_interrupt(SPI2_IRQn);}
|
||||
void USART1_IRQHandler(void) {handle_interrupt(USART1_IRQn);}
|
||||
void USART2_IRQHandler(void) {handle_interrupt(USART2_IRQn);}
|
||||
void USART3_IRQHandler(void) {handle_interrupt(USART3_IRQn);}
|
||||
void EXTI15_10_IRQHandler(void) {handle_interrupt(EXTI15_10_IRQn);}
|
||||
void RTC_Alarm_IRQHandler(void) {handle_interrupt(RTC_Alarm_IRQn);}
|
||||
void OTG_FS_WKUP_IRQHandler(void) {handle_interrupt(OTG_FS_WKUP_IRQn);}
|
||||
void TIM8_BRK_TIM12_IRQHandler(void) {handle_interrupt(TIM8_BRK_TIM12_IRQn);}
|
||||
void TIM8_UP_TIM13_IRQHandler(void) {handle_interrupt(TIM8_UP_TIM13_IRQn);}
|
||||
void TIM8_TRG_COM_TIM14_IRQHandler(void) {handle_interrupt(TIM8_TRG_COM_TIM14_IRQn);}
|
||||
void TIM8_CC_IRQHandler(void) {handle_interrupt(TIM8_CC_IRQn);}
|
||||
void DMA1_Stream7_IRQHandler(void) {handle_interrupt(DMA1_Stream7_IRQn);}
|
||||
void FSMC_IRQHandler(void) {handle_interrupt(FSMC_IRQn);}
|
||||
void SDIO_IRQHandler(void) {handle_interrupt(SDIO_IRQn);}
|
||||
void TIM5_IRQHandler(void) {handle_interrupt(TIM5_IRQn);}
|
||||
void SPI3_IRQHandler(void) {handle_interrupt(SPI3_IRQn);}
|
||||
void UART4_IRQHandler(void) {handle_interrupt(UART4_IRQn);}
|
||||
void UART5_IRQHandler(void) {handle_interrupt(UART5_IRQn);}
|
||||
void TIM6_DAC_IRQHandler(void) {handle_interrupt(TIM6_DAC_IRQn);}
|
||||
void TIM7_IRQHandler(void) {handle_interrupt(TIM7_IRQn);}
|
||||
void DMA2_Stream0_IRQHandler(void) {handle_interrupt(DMA2_Stream0_IRQn);}
|
||||
void DMA2_Stream1_IRQHandler(void) {handle_interrupt(DMA2_Stream1_IRQn);}
|
||||
void DMA2_Stream2_IRQHandler(void) {handle_interrupt(DMA2_Stream2_IRQn);}
|
||||
void DMA2_Stream3_IRQHandler(void) {handle_interrupt(DMA2_Stream3_IRQn);}
|
||||
void DMA2_Stream4_IRQHandler(void) {handle_interrupt(DMA2_Stream4_IRQn);}
|
||||
void CAN2_TX_IRQHandler(void) {handle_interrupt(CAN2_TX_IRQn);}
|
||||
void CAN2_RX0_IRQHandler(void) {handle_interrupt(CAN2_RX0_IRQn);}
|
||||
void CAN2_RX1_IRQHandler(void) {handle_interrupt(CAN2_RX1_IRQn);}
|
||||
void CAN2_SCE_IRQHandler(void) {handle_interrupt(CAN2_SCE_IRQn);}
|
||||
void OTG_FS_IRQHandler(void) {handle_interrupt(OTG_FS_IRQn);}
|
||||
void DMA2_Stream5_IRQHandler(void) {handle_interrupt(DMA2_Stream5_IRQn);}
|
||||
void DMA2_Stream6_IRQHandler(void) {handle_interrupt(DMA2_Stream6_IRQn);}
|
||||
void DMA2_Stream7_IRQHandler(void) {handle_interrupt(DMA2_Stream7_IRQn);}
|
||||
void USART6_IRQHandler(void) {handle_interrupt(USART6_IRQn);}
|
||||
void I2C3_EV_IRQHandler(void) {handle_interrupt(I2C3_EV_IRQn);}
|
||||
void I2C3_ER_IRQHandler(void) {handle_interrupt(I2C3_ER_IRQn);}
|
||||
void DFSDM1_FLT0_IRQHandler(void) {handle_interrupt(DFSDM1_FLT0_IRQn);}
|
||||
void DFSDM1_FLT1_IRQHandler(void) {handle_interrupt(DFSDM1_FLT1_IRQn);}
|
||||
void CAN3_TX_IRQHandler(void) {handle_interrupt(CAN3_TX_IRQn);}
|
||||
void CAN3_RX0_IRQHandler(void) {handle_interrupt(CAN3_RX0_IRQn);}
|
||||
void CAN3_RX1_IRQHandler(void) {handle_interrupt(CAN3_RX1_IRQn);}
|
||||
void CAN3_SCE_IRQHandler(void) {handle_interrupt(CAN3_SCE_IRQn);}
|
||||
void RNG_IRQHandler(void) {handle_interrupt(RNG_IRQn);}
|
||||
void FPU_IRQHandler(void) {handle_interrupt(FPU_IRQn);}
|
||||
void UART7_IRQHandler(void) {handle_interrupt(UART7_IRQn);}
|
||||
void UART8_IRQHandler(void) {handle_interrupt(UART8_IRQn);}
|
||||
void SPI4_IRQHandler(void) {handle_interrupt(SPI4_IRQn);}
|
||||
void SPI5_IRQHandler(void) {handle_interrupt(SPI5_IRQn);}
|
||||
void SAI1_IRQHandler(void) {handle_interrupt(SAI1_IRQn);}
|
||||
void UART9_IRQHandler(void) {handle_interrupt(UART9_IRQn);}
|
||||
void UART10_IRQHandler(void) {handle_interrupt(UART10_IRQn);}
|
||||
void QUADSPI_IRQHandler(void) {handle_interrupt(QUADSPI_IRQn);}
|
||||
void FMPI2C1_EV_IRQHandler(void) {handle_interrupt(FMPI2C1_EV_IRQn);}
|
||||
void FMPI2C1_ER_IRQHandler(void) {handle_interrupt(FMPI2C1_ER_IRQn);}
|
||||
void LPTIM1_IRQHandler(void) {handle_interrupt(LPTIM1_IRQn);}
|
||||
void DFSDM2_FLT0_IRQHandler(void) {handle_interrupt(DFSDM2_FLT0_IRQn);}
|
||||
void DFSDM2_FLT1_IRQHandler(void) {handle_interrupt(DFSDM2_FLT1_IRQn);}
|
||||
void DFSDM2_FLT2_IRQHandler(void) {handle_interrupt(DFSDM2_FLT2_IRQn);}
|
||||
void DFSDM2_FLT3_IRQHandler(void) {handle_interrupt(DFSDM2_FLT3_IRQn);}
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "lladc_declarations.h"
|
||||
|
||||
void adc_init(ADC_TypeDef *adc) {
|
||||
register_set(&(ADC->CCR), ADC_CCR_TSVREFE | ADC_CCR_VBATE, 0xC30000U);
|
||||
register_set(&(adc->CR2), ADC_CR2_ADON, 0xFF7F0F03U);
|
||||
}
|
||||
|
||||
static uint16_t adc_get_raw(const adc_signal_t *signal) {
|
||||
// sample time
|
||||
if (signal->channel < 10U) {
|
||||
signal->adc->SMPR2 = ((uint32_t) signal->sample_time << (signal->channel * 3U));
|
||||
} else {
|
||||
signal->adc->SMPR1 = ((uint32_t) signal->sample_time << ((signal->channel - 10U) * 3U));
|
||||
}
|
||||
|
||||
// select channel
|
||||
signal->adc->JSQR = ((uint32_t) signal->channel << 15U);
|
||||
|
||||
// start conversion
|
||||
signal->adc->SR &= ~(ADC_SR_JEOC);
|
||||
signal->adc->CR2 |= ADC_CR2_JSWSTART;
|
||||
while (!(signal->adc->SR & ADC_SR_JEOC));
|
||||
|
||||
return signal->adc->JDR1;
|
||||
}
|
||||
|
||||
uint16_t adc_get_mV(const adc_signal_t *signal) {
|
||||
return (adc_get_raw(signal) * current_board->avdd_mV) / 4095U;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
#include "llbxcan_declarations.h"
|
||||
|
||||
// kbps multiplied by 10
|
||||
const uint32_t speeds[SPEEDS_ARRAY_SIZE] = {100U, 200U, 500U, 1000U, 1250U, 2500U, 5000U, 10000U};
|
||||
const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE] = {0U}; // No separate data speed, dummy
|
||||
|
||||
bool llcan_set_speed(CAN_TypeDef *CANx, uint32_t speed, bool loopback, bool silent) {
|
||||
bool ret = true;
|
||||
|
||||
// initialization mode
|
||||
register_set(&(CANx->MCR), CAN_MCR_TTCM | CAN_MCR_INRQ, 0x180FFU);
|
||||
uint32_t timeout_counter = 0U;
|
||||
while((CANx->MSR & CAN_MSR_INAK) != CAN_MSR_INAK){
|
||||
// Delay for about 1ms
|
||||
delay(10000);
|
||||
timeout_counter++;
|
||||
|
||||
if(timeout_counter >= CAN_INIT_TIMEOUT_MS){
|
||||
print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out (1)!\n");
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret){
|
||||
// set time quanta from defines
|
||||
register_set(&(CANx->BTR), ((CAN_BTR_TS1_0 * (CAN_SEQ1-1U)) |
|
||||
(CAN_BTR_TS2_0 * (CAN_SEQ2-1U)) |
|
||||
(CAN_BTR_SJW_0 * (CAN_SJW-1U)) |
|
||||
(can_speed_to_prescaler(speed) - 1U)), 0xC37F03FFU);
|
||||
|
||||
// silent loopback mode for debugging
|
||||
if (loopback) {
|
||||
register_set_bits(&(CANx->BTR), CAN_BTR_SILM | CAN_BTR_LBKM);
|
||||
}
|
||||
if (silent) {
|
||||
register_set_bits(&(CANx->BTR), CAN_BTR_SILM);
|
||||
}
|
||||
|
||||
// reset
|
||||
register_set(&(CANx->MCR), CAN_MCR_TTCM | CAN_MCR_ABOM, 0x180FFU);
|
||||
|
||||
timeout_counter = 0U;
|
||||
while(((CANx->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)) {
|
||||
// Delay for about 1ms
|
||||
delay(10000);
|
||||
timeout_counter++;
|
||||
|
||||
if(timeout_counter >= CAN_INIT_TIMEOUT_MS){
|
||||
print(CAN_NAME_FROM_CANIF(CANx)); print(" set_speed timed out (2)!\n");
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void llcan_irq_disable(const CAN_TypeDef *CANx) {
|
||||
if (CANx == CAN1) {
|
||||
NVIC_DisableIRQ(CAN1_TX_IRQn);
|
||||
NVIC_DisableIRQ(CAN1_RX0_IRQn);
|
||||
NVIC_DisableIRQ(CAN1_SCE_IRQn);
|
||||
} else if (CANx == CAN2) {
|
||||
NVIC_DisableIRQ(CAN2_TX_IRQn);
|
||||
NVIC_DisableIRQ(CAN2_RX0_IRQn);
|
||||
NVIC_DisableIRQ(CAN2_SCE_IRQn);
|
||||
} else if (CANx == CAN3) {
|
||||
NVIC_DisableIRQ(CAN3_TX_IRQn);
|
||||
NVIC_DisableIRQ(CAN3_RX0_IRQn);
|
||||
NVIC_DisableIRQ(CAN3_SCE_IRQn);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
void llcan_irq_enable(const CAN_TypeDef *CANx) {
|
||||
if (CANx == CAN1) {
|
||||
NVIC_EnableIRQ(CAN1_TX_IRQn);
|
||||
NVIC_EnableIRQ(CAN1_RX0_IRQn);
|
||||
NVIC_EnableIRQ(CAN1_SCE_IRQn);
|
||||
} else if (CANx == CAN2) {
|
||||
NVIC_EnableIRQ(CAN2_TX_IRQn);
|
||||
NVIC_EnableIRQ(CAN2_RX0_IRQn);
|
||||
NVIC_EnableIRQ(CAN2_SCE_IRQn);
|
||||
} else if (CANx == CAN3) {
|
||||
NVIC_EnableIRQ(CAN3_TX_IRQn);
|
||||
NVIC_EnableIRQ(CAN3_RX0_IRQn);
|
||||
NVIC_EnableIRQ(CAN3_SCE_IRQn);
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
bool llcan_init(CAN_TypeDef *CANx) {
|
||||
bool ret = true;
|
||||
|
||||
// Enter init mode
|
||||
register_set_bits(&(CANx->FMR), CAN_FMR_FINIT);
|
||||
|
||||
// Wait for INAK bit to be set
|
||||
uint32_t timeout_counter = 0U;
|
||||
while(((CANx->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)) {
|
||||
// Delay for about 1ms
|
||||
delay(10000);
|
||||
timeout_counter++;
|
||||
|
||||
if(timeout_counter >= CAN_INIT_TIMEOUT_MS){
|
||||
print(CAN_NAME_FROM_CANIF(CANx)); print(" initialization timed out!\n");
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ret){
|
||||
// no mask
|
||||
// For some weird reason some of these registers do not want to set properly on CAN2 and CAN3. Probably something to do with the single/dual mode and their different filters.
|
||||
CANx->sFilterRegister[0].FR1 = 0U;
|
||||
CANx->sFilterRegister[0].FR2 = 0U;
|
||||
CANx->sFilterRegister[14].FR1 = 0U;
|
||||
CANx->sFilterRegister[14].FR2 = 0U;
|
||||
CANx->FA1R |= 1U | (1UL << 14);
|
||||
|
||||
// Exit init mode, do not wait
|
||||
register_clear_bits(&(CANx->FMR), CAN_FMR_FINIT);
|
||||
|
||||
// enable certain CAN interrupts
|
||||
register_set_bits(&(CANx->IER), CAN_IER_TMEIE | CAN_IER_FMPIE0 | CAN_IER_ERRIE | CAN_IER_LECIE | CAN_IER_BOFIE | CAN_IER_EPVIE | CAN_IER_EWGIE | CAN_IER_FOVIE0 | CAN_IER_FFIE0);
|
||||
|
||||
// clear overrun flag on init
|
||||
CANx->RF0R &= ~(CAN_RF0R_FOVR0);
|
||||
|
||||
llcan_irq_enable(CANx);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void llcan_clear_send(CAN_TypeDef *CANx) {
|
||||
CANx->TSR |= CAN_TSR_ABRQ0; // Abort message transmission on error interrupt
|
||||
CANx->MSR |= CAN_MSR_ERRI; // Clear error interrupt
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
// SAE 2284-3 : minimum 16 tq, SJW 3, sample point at 81.3%
|
||||
#define CAN_QUANTA 16U
|
||||
#define CAN_SEQ1 12U
|
||||
#define CAN_SEQ2 3U
|
||||
#define CAN_SJW 3U
|
||||
|
||||
#define CAN_PCLK 48000U
|
||||
// 333 = 33.3 kbps
|
||||
// 5000 = 500 kbps
|
||||
#define can_speed_to_prescaler(x) (CAN_PCLK / CAN_QUANTA * 10U / (x))
|
||||
|
||||
#define CAN_NAME_FROM_CANIF(CAN_DEV) (((CAN_DEV)==CAN1) ? "CAN1" : (((CAN_DEV) == CAN2) ? "CAN2" : "CAN3"))
|
||||
|
||||
// kbps multiplied by 10
|
||||
#define SPEEDS_ARRAY_SIZE 8
|
||||
extern const uint32_t speeds[SPEEDS_ARRAY_SIZE];
|
||||
#define DATA_SPEEDS_ARRAY_SIZE 1
|
||||
extern const uint32_t data_speeds[DATA_SPEEDS_ARRAY_SIZE]; // No separate data speed, dummy
|
||||
|
||||
bool llcan_set_speed(CAN_TypeDef *CANx, uint32_t speed, bool loopback, bool silent);
|
||||
void llcan_irq_disable(const CAN_TypeDef *CANx);
|
||||
void llcan_irq_enable(const CAN_TypeDef *CANx);
|
||||
bool llcan_init(CAN_TypeDef *CANx);
|
||||
void llcan_clear_send(CAN_TypeDef *CANx);
|
||||
@@ -0,0 +1,23 @@
|
||||
// TACH interrupt handler
|
||||
static void EXTI2_IRQ_Handler(void) {
|
||||
volatile unsigned int pr = EXTI->PR & (1U << 2);
|
||||
if ((pr & (1U << 2)) != 0U) {
|
||||
fan_state.tach_counter++;
|
||||
}
|
||||
EXTI->PR = (1U << 2);
|
||||
}
|
||||
|
||||
void llfan_init(void) {
|
||||
// 5000RPM * 4 tach edges / 60 seconds
|
||||
REGISTER_INTERRUPT(EXTI2_IRQn, EXTI2_IRQ_Handler, 700U, FAULT_INTERRUPT_RATE_TACH)
|
||||
|
||||
// Init PWM speed control
|
||||
pwm_init(TIM3, 3);
|
||||
|
||||
// Init TACH interrupt
|
||||
register_set(&(SYSCFG->EXTICR[0]), SYSCFG_EXTICR1_EXTI2_PD, 0xF00U);
|
||||
register_set_bits(&(EXTI->IMR), (1U << 2));
|
||||
register_set_bits(&(EXTI->RTSR), (1U << 2));
|
||||
register_set_bits(&(EXTI->FTSR), (1U << 2));
|
||||
NVIC_EnableIRQ(EXTI2_IRQn);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
bool flash_is_locked(void) {
|
||||
return (FLASH->CR & FLASH_CR_LOCK);
|
||||
}
|
||||
|
||||
void flash_unlock(void) {
|
||||
FLASH->KEYR = 0x45670123;
|
||||
FLASH->KEYR = 0xCDEF89AB;
|
||||
}
|
||||
|
||||
bool flash_erase_sector(uint8_t sector, bool unlocked) {
|
||||
// don't erase the bootloader(sector 0)
|
||||
if (sector != 0 && sector < 12 && unlocked) {
|
||||
FLASH->CR = (sector << 3) | FLASH_CR_SER;
|
||||
FLASH->CR |= FLASH_CR_STRT;
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void flash_write_word(void *prog_ptr, uint32_t data) {
|
||||
uint32_t *pp = prog_ptr;
|
||||
FLASH->CR = FLASH_CR_PSIZE_1 | FLASH_CR_PG;
|
||||
*pp = data;
|
||||
while (FLASH->SR & FLASH_SR_BSY);
|
||||
}
|
||||
|
||||
void flush_write_buffer(void) { }
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "llusb_declarations.h"
|
||||
|
||||
USB_OTG_GlobalTypeDef *USBx = USB_OTG_FS;
|
||||
|
||||
static void OTG_FS_IRQ_Handler(void) {
|
||||
NVIC_DisableIRQ(OTG_FS_IRQn);
|
||||
//__disable_irq();
|
||||
usb_irqhandler();
|
||||
//__enable_irq();
|
||||
NVIC_EnableIRQ(OTG_FS_IRQn);
|
||||
}
|
||||
|
||||
void usb_init(void) {
|
||||
REGISTER_INTERRUPT(OTG_FS_IRQn, OTG_FS_IRQ_Handler, 1500000U, FAULT_INTERRUPT_RATE_USB) //TODO: Find out a better rate limit for USB. Now it's the 1.5MB/s rate
|
||||
|
||||
// full speed PHY, do reset and remove power down
|
||||
/*puth(USBx->GRSTCTL);
|
||||
print(" resetting PHY\n");*/
|
||||
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_AHBIDL) == 0U);
|
||||
//print("AHB idle\n");
|
||||
|
||||
// reset PHY here
|
||||
USBx->GRSTCTL |= USB_OTG_GRSTCTL_CSRST;
|
||||
while ((USBx->GRSTCTL & USB_OTG_GRSTCTL_CSRST) == USB_OTG_GRSTCTL_CSRST);
|
||||
//print("reset done\n");
|
||||
|
||||
// internal PHY, force device mode
|
||||
USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_FDMOD;
|
||||
|
||||
// slowest timings
|
||||
USBx->GUSBCFG |= ((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT);
|
||||
|
||||
// power up the PHY
|
||||
USBx->GCCFG = USB_OTG_GCCFG_PWRDWN;
|
||||
|
||||
//USBx->GCCFG |= USB_OTG_GCCFG_VBDEN | USB_OTG_GCCFG_SDEN |USB_OTG_GCCFG_PDEN | USB_OTG_GCCFG_DCDEN;
|
||||
|
||||
/* B-peripheral session valid override enable*/
|
||||
USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOVAL;
|
||||
USBx->GOTGCTL |= USB_OTG_GOTGCTL_BVALOEN;
|
||||
|
||||
// be a device, slowest timings
|
||||
//USBx->GUSBCFG = USB_OTG_GUSBCFG_FDMOD | USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL;
|
||||
//USBx->GUSBCFG |= (uint32_t)((USBD_FS_TRDT_VALUE << 10) & USB_OTG_GUSBCFG_TRDT);
|
||||
//USBx->GUSBCFG = USB_OTG_GUSBCFG_PHYSEL | USB_OTG_GUSBCFG_TRDT | USB_OTG_GUSBCFG_TOCAL;
|
||||
|
||||
// **** for debugging, doesn't seem to work ****
|
||||
//USBx->GUSBCFG |= USB_OTG_GUSBCFG_CTXPKT;
|
||||
|
||||
// reset PHY clock
|
||||
USBx_PCGCCTL = 0;
|
||||
|
||||
// enable the fancy OTG things
|
||||
// DCFG_FRAME_INTERVAL_80 is 0
|
||||
//USBx->GUSBCFG |= USB_OTG_GUSBCFG_HNPCAP | USB_OTG_GUSBCFG_SRPCAP;
|
||||
USBx_DEVICE->DCFG |= USB_OTG_SPEED_FULL | USB_OTG_DCFG_NZLSOHSK;
|
||||
|
||||
//USBx_DEVICE->DCFG = USB_OTG_DCFG_NZLSOHSK | USB_OTG_DCFG_DSPD;
|
||||
//USBx_DEVICE->DCFG = USB_OTG_DCFG_DSPD;
|
||||
|
||||
// clear pending interrupts
|
||||
USBx->GINTSTS = 0xBFFFFFFFU;
|
||||
|
||||
// setup USB interrupts
|
||||
// all interrupts except TXFIFO EMPTY
|
||||
//USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM | USB_OTG_GINTSTS_SOF | USB_OTG_GINTSTS_EOPF);
|
||||
//USBx->GINTMSK = 0xFFFFFFFF & ~(USB_OTG_GINTMSK_NPTXFEM | USB_OTG_GINTMSK_PTXFEM);
|
||||
USBx->GINTMSK = USB_OTG_GINTMSK_USBRST | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_OTGINT |
|
||||
USB_OTG_GINTMSK_RXFLVLM | USB_OTG_GINTMSK_GONAKEFFM | USB_OTG_GINTMSK_GINAKEFFM |
|
||||
USB_OTG_GINTMSK_OEPINT | USB_OTG_GINTMSK_IEPINT | USB_OTG_GINTMSK_USBSUSPM |
|
||||
USB_OTG_GINTMSK_CIDSCHGM | USB_OTG_GINTMSK_SRQIM | USB_OTG_GINTMSK_MMISM | USB_OTG_GINTMSK_EOPFM;
|
||||
|
||||
USBx->GAHBCFG = USB_OTG_GAHBCFG_GINT;
|
||||
|
||||
// DCTL startup value is 2 on new chip, 0 on old chip
|
||||
USBx_DEVICE->DCTL = 0;
|
||||
|
||||
// enable the IRQ
|
||||
NVIC_EnableIRQ(OTG_FS_IRQn);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
extern USB_OTG_GlobalTypeDef *USBx;
|
||||
|
||||
#define USBx_DEVICE ((USB_OTG_DeviceTypeDef *)((uint32_t)USBx + USB_OTG_DEVICE_BASE))
|
||||
#define USBx_INEP(i) ((USB_OTG_INEndpointTypeDef *)((uint32_t)USBx + USB_OTG_IN_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE)))
|
||||
#define USBx_OUTEP(i) ((USB_OTG_OUTEndpointTypeDef *)((uint32_t)USBx + USB_OTG_OUT_ENDPOINT_BASE + ((i) * USB_OTG_EP_REG_SIZE)))
|
||||
#define USBx_DFIFO(i) *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_FIFO_BASE + ((i) * USB_OTG_FIFO_SIZE))
|
||||
#define USBx_PCGCCTL *(__IO uint32_t *)((uint32_t)USBx + USB_OTG_PCGCCTL_BASE)
|
||||
|
||||
#define USBD_FS_TRDT_VALUE 5UL
|
||||
#define USB_OTG_SPEED_FULL 3UL
|
||||
|
||||
|
||||
void usb_irqhandler(void);
|
||||
void usb_init(void);
|
||||
@@ -0,0 +1,91 @@
|
||||
#ifdef BOOTSTUB
|
||||
void gpio_usb_init(void) {
|
||||
#else
|
||||
static void gpio_usb_init(void) {
|
||||
#endif
|
||||
// A11,A12: USB
|
||||
set_gpio_alternate(GPIOA, 11, GPIO_AF10_OTG_FS);
|
||||
set_gpio_alternate(GPIOA, 12, GPIO_AF10_OTG_FS);
|
||||
GPIOA->OSPEEDR = GPIO_OSPEEDER_OSPEEDR11 | GPIO_OSPEEDER_OSPEEDR12;
|
||||
}
|
||||
|
||||
void gpio_spi_init(void) {
|
||||
}
|
||||
|
||||
#ifdef BOOTSTUB
|
||||
void gpio_usart2_init(void) {
|
||||
// A2,A3: USART 2 for debugging
|
||||
set_gpio_alternate(GPIOA, 2, GPIO_AF7_USART2);
|
||||
set_gpio_alternate(GPIOA, 3, GPIO_AF7_USART2);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Common GPIO initialization
|
||||
void common_init_gpio(void) {
|
||||
// enable OTG out tied to ground
|
||||
GPIOA->ODR = 0;
|
||||
GPIOB->ODR = 0;
|
||||
GPIOA->PUPDR = 0;
|
||||
GPIOB->AFR[0] = 0;
|
||||
GPIOB->AFR[1] = 0;
|
||||
|
||||
// C2: Voltage sense line
|
||||
set_gpio_mode(GPIOC, 2, MODE_ANALOG);
|
||||
|
||||
gpio_usb_init();
|
||||
|
||||
// B8,B9: CAN 1
|
||||
set_gpio_alternate(GPIOB, 8, GPIO_AF8_CAN1);
|
||||
set_gpio_alternate(GPIOB, 9, GPIO_AF8_CAN1);
|
||||
}
|
||||
|
||||
#ifdef BOOTSTUB
|
||||
void flasher_peripherals_init(void) {
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
|
||||
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Peripheral initialization
|
||||
void peripherals_init(void) {
|
||||
// enable GPIO(A,B,C,D)
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN;
|
||||
|
||||
// Supplemental
|
||||
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // for RTC config
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
|
||||
|
||||
// Connectivity
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
|
||||
RCC->AHB2ENR |= RCC_AHB2ENR_OTGFSEN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_UART5EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN1EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN2EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_CAN3EN;
|
||||
|
||||
// Analog
|
||||
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_DACEN;
|
||||
|
||||
// Timers
|
||||
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // clock source timer
|
||||
RCC->APB2ENR |= RCC_APB2ENR_TIM8EN; // clock source slave timer
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // main counter
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // pedal and fan PWM
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM4EN; // IR PWM
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM5EN; // k-line init
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // interrupt timer
|
||||
RCC->APB2ENR |= RCC_APB2ENR_TIM9EN; // slow loop
|
||||
}
|
||||
|
||||
void enable_interrupt_timer(void) {
|
||||
register_set_bits(&(RCC->APB1ENR), RCC_APB1ENR_TIM6EN); // Enable interrupt timer peripheral
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* @file startup_stm32f413xx.s
|
||||
* @author MCD Application Team
|
||||
* @version V2.6.0
|
||||
* @date 04-November-2016
|
||||
* @brief STM32F413xx Devices vector table for GCC based toolchains.
|
||||
* This module performs:
|
||||
* - Set the initial SP
|
||||
* - Set the initial PC == Reset_Handler,
|
||||
* - Set the vector table entries with the exceptions ISR address
|
||||
* - Branches to main in the C library (which eventually
|
||||
* calls main()).
|
||||
* After Reset the Cortex-M4 processor is in Thread mode,
|
||||
* priority is Privileged, and the Stack is set to Main.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT 2016 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m4
|
||||
.fpu softvfp
|
||||
.thumb
|
||||
|
||||
.global g_pfnVectors
|
||||
.global Default_Handler
|
||||
|
||||
/* start address for the initialization values of the .data section.
|
||||
defined in linker script */
|
||||
.word _sidata
|
||||
/* start address for the .data section. defined in linker script */
|
||||
.word _sdata
|
||||
/* end address for the .data section. defined in linker script */
|
||||
.word _edata
|
||||
/* start address for the .bss section. defined in linker script */
|
||||
.word _sbss
|
||||
/* end address for the .bss section. defined in linker script */
|
||||
.word _ebss
|
||||
/* stack used for SystemInit_ExtMemCtl; always internal RAM used */
|
||||
|
||||
/**
|
||||
* @brief This is the code that gets called when the processor first
|
||||
* starts execution following a reset event. Only the absolutely
|
||||
* necessary set is performed, after which the application
|
||||
* supplied main() routine is called.
|
||||
* @param None
|
||||
* @retval : None
|
||||
*/
|
||||
|
||||
.section .text.Reset_Handler
|
||||
.weak Reset_Handler
|
||||
.type Reset_Handler, %function
|
||||
Reset_Handler:
|
||||
ldr sp, =_estack /* set stack pointer */
|
||||
bl __initialize_hardware_early
|
||||
|
||||
/* Copy the data segment initializers from flash to SRAM */
|
||||
movs r1, #0
|
||||
b LoopCopyDataInit
|
||||
|
||||
CopyDataInit:
|
||||
ldr r3, =_sidata
|
||||
ldr r3, [r3, r1]
|
||||
str r3, [r0, r1]
|
||||
adds r1, r1, #4
|
||||
|
||||
LoopCopyDataInit:
|
||||
ldr r0, =_sdata
|
||||
ldr r3, =_edata
|
||||
adds r2, r0, r1
|
||||
cmp r2, r3
|
||||
bcc CopyDataInit
|
||||
ldr r2, =_sbss
|
||||
b LoopFillZerobss
|
||||
/* Zero fill the bss segment. */
|
||||
FillZerobss:
|
||||
movs r3, #0
|
||||
str r3, [r2], #4
|
||||
|
||||
LoopFillZerobss:
|
||||
ldr r3, = _ebss
|
||||
cmp r2, r3
|
||||
bcc FillZerobss
|
||||
|
||||
/* Call the clock system intitialization function.*/
|
||||
/* bl SystemInit */
|
||||
/* Call static constructors */
|
||||
/* bl __libc_init_array */
|
||||
/* Call the application's entry point.*/
|
||||
bl main
|
||||
bx lr
|
||||
.size Reset_Handler, .-Reset_Handler
|
||||
|
||||
/**
|
||||
* @brief This is the code that gets called when the processor receives an
|
||||
* unexpected interrupt. This simply enters an infinite loop, preserving
|
||||
* the system state for examination by a debugger.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
.section .text.Default_Handler,"ax",%progbits
|
||||
Default_Handler:
|
||||
Infinite_Loop:
|
||||
b Infinite_Loop
|
||||
.size Default_Handler, .-Default_Handler
|
||||
/******************************************************************************
|
||||
*
|
||||
* The minimal vector table for a Cortex M3. Note that the proper constructs
|
||||
* must be placed on this to ensure that it ends up at physical address
|
||||
* 0x0000.0000.
|
||||
*
|
||||
*******************************************************************************/
|
||||
.section .isr_vector,"a",%progbits
|
||||
.type g_pfnVectors, %object
|
||||
.size g_pfnVectors, .-g_pfnVectors
|
||||
|
||||
g_pfnVectors:
|
||||
.word _estack
|
||||
.word Reset_Handler
|
||||
.word NMI_Handler
|
||||
.word HardFault_Handler
|
||||
.word MemManage_Handler
|
||||
.word BusFault_Handler
|
||||
.word UsageFault_Handler
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word SVC_Handler
|
||||
.word DebugMon_Handler
|
||||
.word 0
|
||||
.word PendSV_Handler
|
||||
.word SysTick_Handler
|
||||
|
||||
/* External Interrupts */
|
||||
.word WWDG_IRQHandler /* Window WatchDog */
|
||||
.word PVD_IRQHandler /* PVD through EXTI Line detection */
|
||||
.word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */
|
||||
.word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */
|
||||
.word FLASH_IRQHandler /* FLASH */
|
||||
.word RCC_IRQHandler /* RCC */
|
||||
.word EXTI0_IRQHandler /* EXTI Line0 */
|
||||
.word EXTI1_IRQHandler /* EXTI Line1 */
|
||||
.word EXTI2_IRQHandler /* EXTI Line2 */
|
||||
.word EXTI3_IRQHandler /* EXTI Line3 */
|
||||
.word EXTI4_IRQHandler /* EXTI Line4 */
|
||||
.word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */
|
||||
.word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */
|
||||
.word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */
|
||||
.word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */
|
||||
.word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */
|
||||
.word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */
|
||||
.word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */
|
||||
.word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */
|
||||
.word CAN1_TX_IRQHandler /* CAN1 TX */
|
||||
.word CAN1_RX0_IRQHandler /* CAN1 RX0 */
|
||||
.word CAN1_RX1_IRQHandler /* CAN1 RX1 */
|
||||
.word CAN1_SCE_IRQHandler /* CAN1 SCE */
|
||||
.word EXTI9_5_IRQHandler /* External Line[9:5]s */
|
||||
.word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */
|
||||
.word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */
|
||||
.word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */
|
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
|
||||
.word TIM2_IRQHandler /* TIM2 */
|
||||
.word TIM3_IRQHandler /* TIM3 */
|
||||
.word TIM4_IRQHandler /* TIM4 */
|
||||
.word I2C1_EV_IRQHandler /* I2C1 Event */
|
||||
.word I2C1_ER_IRQHandler /* I2C1 Error */
|
||||
.word I2C2_EV_IRQHandler /* I2C2 Event */
|
||||
.word I2C2_ER_IRQHandler /* I2C2 Error */
|
||||
.word SPI1_IRQHandler /* SPI1 */
|
||||
.word SPI2_IRQHandler /* SPI2 */
|
||||
.word USART1_IRQHandler /* USART1 */
|
||||
.word USART2_IRQHandler /* USART2 */
|
||||
.word USART3_IRQHandler /* USART3 */
|
||||
.word EXTI15_10_IRQHandler /* External Line[15:10]s */
|
||||
.word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */
|
||||
.word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */
|
||||
.word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */
|
||||
.word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */
|
||||
.word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */
|
||||
.word TIM8_CC_IRQHandler /* TIM8 Capture Compare */
|
||||
.word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */
|
||||
.word FSMC_IRQHandler /* FSMC */
|
||||
.word SDIO_IRQHandler /* SDIO */
|
||||
.word TIM5_IRQHandler /* TIM5 */
|
||||
.word SPI3_IRQHandler /* SPI3 */
|
||||
.word UART4_IRQHandler /* UART4 */
|
||||
.word UART5_IRQHandler /* UART5 */
|
||||
.word TIM6_DAC_IRQHandler /* TIM6, DAC1 and DAC2 */
|
||||
.word TIM7_IRQHandler /* TIM7 */
|
||||
.word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */
|
||||
.word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */
|
||||
.word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */
|
||||
.word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */
|
||||
.word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */
|
||||
.word DFSDM1_FLT0_IRQHandler /* DFSDM1 Filter0 */
|
||||
.word DFSDM1_FLT1_IRQHandler /* DFSDM1 Filter1 */
|
||||
.word CAN2_TX_IRQHandler /* CAN2 TX */
|
||||
.word CAN2_RX0_IRQHandler /* CAN2 RX0 */
|
||||
.word CAN2_RX1_IRQHandler /* CAN2 RX1 */
|
||||
.word CAN2_SCE_IRQHandler /* CAN2 SCE */
|
||||
.word OTG_FS_IRQHandler /* USB OTG FS */
|
||||
.word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */
|
||||
.word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */
|
||||
.word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */
|
||||
.word USART6_IRQHandler /* USART6 */
|
||||
.word I2C3_EV_IRQHandler /* I2C3 event */
|
||||
.word I2C3_ER_IRQHandler /* I2C3 error */
|
||||
.word CAN3_TX_IRQHandler /* CAN3 TX */
|
||||
.word CAN3_RX0_IRQHandler /* CAN3 RX0 */
|
||||
.word CAN3_RX1_IRQHandler /* CAN3 RX1 */
|
||||
.word CAN3_SCE_IRQHandler /* CAN3 SCE */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word RNG_IRQHandler /* RNG */
|
||||
.word FPU_IRQHandler /* FPU */
|
||||
.word UART7_IRQHandler /* UART7 */
|
||||
.word UART8_IRQHandler /* UART8 */
|
||||
.word SPI4_IRQHandler /* SPI4 */
|
||||
.word SPI5_IRQHandler /* SPI5 */
|
||||
.word 0 /* Reserved */
|
||||
.word SAI1_IRQHandler /* SAI1 */
|
||||
.word UART9_IRQHandler /* UART9 */
|
||||
.word UART10_IRQHandler /* UART10 */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word QUADSPI_IRQHandler /* QuadSPI */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word FMPI2C1_EV_IRQHandler /* FMPI2C1 Event */
|
||||
.word FMPI2C1_ER_IRQHandler /* FMPI2C1 Error */
|
||||
.word LPTIM1_IRQHandler /* LPTIM1 */
|
||||
.word DFSDM2_FLT0_IRQHandler /* DFSDM2 Filter0 */
|
||||
.word DFSDM2_FLT1_IRQHandler /* DFSDM2 Filter1 */
|
||||
.word DFSDM2_FLT2_IRQHandler /* DFSDM2 Filter2 */
|
||||
.word DFSDM2_FLT3_IRQHandler /* DFSDM2 Filter3 */
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Provide weak aliases for each Exception handler to the Default_Handler.
|
||||
* As they are weak aliases, any function with the same name will override
|
||||
* this definition.
|
||||
*
|
||||
*******************************************************************************/
|
||||
.weak NMI_Handler
|
||||
.thumb_set NMI_Handler,Default_Handler
|
||||
|
||||
.weak HardFault_Handler
|
||||
.thumb_set HardFault_Handler,Default_Handler
|
||||
|
||||
.weak MemManage_Handler
|
||||
.thumb_set MemManage_Handler,Default_Handler
|
||||
|
||||
.weak BusFault_Handler
|
||||
.thumb_set BusFault_Handler,Default_Handler
|
||||
|
||||
.weak UsageFault_Handler
|
||||
.thumb_set UsageFault_Handler,Default_Handler
|
||||
|
||||
.weak SVC_Handler
|
||||
.thumb_set SVC_Handler,Default_Handler
|
||||
|
||||
.weak DebugMon_Handler
|
||||
.thumb_set DebugMon_Handler,Default_Handler
|
||||
|
||||
.weak PendSV_Handler
|
||||
.thumb_set PendSV_Handler,Default_Handler
|
||||
|
||||
.weak SysTick_Handler
|
||||
.thumb_set SysTick_Handler,Default_Handler
|
||||
|
||||
.weak WWDG_IRQHandler
|
||||
.thumb_set WWDG_IRQHandler,Default_Handler
|
||||
|
||||
.weak PVD_IRQHandler
|
||||
.thumb_set PVD_IRQHandler,Default_Handler
|
||||
|
||||
.weak TAMP_STAMP_IRQHandler
|
||||
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
|
||||
|
||||
.weak RTC_WKUP_IRQHandler
|
||||
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
|
||||
|
||||
.weak FLASH_IRQHandler
|
||||
.thumb_set FLASH_IRQHandler,Default_Handler
|
||||
|
||||
.weak RCC_IRQHandler
|
||||
.thumb_set RCC_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI0_IRQHandler
|
||||
.thumb_set EXTI0_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI1_IRQHandler
|
||||
.thumb_set EXTI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI2_IRQHandler
|
||||
.thumb_set EXTI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI3_IRQHandler
|
||||
.thumb_set EXTI3_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI4_IRQHandler
|
||||
.thumb_set EXTI4_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream0_IRQHandler
|
||||
.thumb_set DMA1_Stream0_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream1_IRQHandler
|
||||
.thumb_set DMA1_Stream1_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream2_IRQHandler
|
||||
.thumb_set DMA1_Stream2_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream3_IRQHandler
|
||||
.thumb_set DMA1_Stream3_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream4_IRQHandler
|
||||
.thumb_set DMA1_Stream4_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream5_IRQHandler
|
||||
.thumb_set DMA1_Stream5_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream6_IRQHandler
|
||||
.thumb_set DMA1_Stream6_IRQHandler,Default_Handler
|
||||
|
||||
.weak ADC_IRQHandler
|
||||
.thumb_set ADC_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_TX_IRQHandler
|
||||
.thumb_set CAN1_TX_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_RX0_IRQHandler
|
||||
.thumb_set CAN1_RX0_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_RX1_IRQHandler
|
||||
.thumb_set CAN1_RX1_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN1_SCE_IRQHandler
|
||||
.thumb_set CAN1_SCE_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI9_5_IRQHandler
|
||||
.thumb_set EXTI9_5_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_BRK_TIM9_IRQHandler
|
||||
.thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_UP_TIM10_IRQHandler
|
||||
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_TRG_COM_TIM11_IRQHandler
|
||||
.thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_CC_IRQHandler
|
||||
.thumb_set TIM1_CC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM2_IRQHandler
|
||||
.thumb_set TIM2_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM3_IRQHandler
|
||||
.thumb_set TIM3_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM4_IRQHandler
|
||||
.thumb_set TIM4_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C1_EV_IRQHandler
|
||||
.thumb_set I2C1_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C1_ER_IRQHandler
|
||||
.thumb_set I2C1_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C2_EV_IRQHandler
|
||||
.thumb_set I2C2_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C2_ER_IRQHandler
|
||||
.thumb_set I2C2_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI1_IRQHandler
|
||||
.thumb_set SPI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI2_IRQHandler
|
||||
.thumb_set SPI2_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART1_IRQHandler
|
||||
.thumb_set USART1_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART2_IRQHandler
|
||||
.thumb_set USART2_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART3_IRQHandler
|
||||
.thumb_set USART3_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI15_10_IRQHandler
|
||||
.thumb_set EXTI15_10_IRQHandler,Default_Handler
|
||||
|
||||
.weak RTC_Alarm_IRQHandler
|
||||
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_FS_WKUP_IRQHandler
|
||||
.thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_BRK_TIM12_IRQHandler
|
||||
.thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_UP_TIM13_IRQHandler
|
||||
.thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_TRG_COM_TIM14_IRQHandler
|
||||
.thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM8_CC_IRQHandler
|
||||
.thumb_set TIM8_CC_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Stream7_IRQHandler
|
||||
.thumb_set DMA1_Stream7_IRQHandler,Default_Handler
|
||||
|
||||
.weak FSMC_IRQHandler
|
||||
.thumb_set FSMC_IRQHandler,Default_Handler
|
||||
|
||||
.weak SDIO_IRQHandler
|
||||
.thumb_set SDIO_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM5_IRQHandler
|
||||
.thumb_set TIM5_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI3_IRQHandler
|
||||
.thumb_set SPI3_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART4_IRQHandler
|
||||
.thumb_set UART4_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART5_IRQHandler
|
||||
.thumb_set UART5_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM6_DAC_IRQHandler
|
||||
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM7_IRQHandler
|
||||
.thumb_set TIM7_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream0_IRQHandler
|
||||
.thumb_set DMA2_Stream0_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream1_IRQHandler
|
||||
.thumb_set DMA2_Stream1_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream2_IRQHandler
|
||||
.thumb_set DMA2_Stream2_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream3_IRQHandler
|
||||
.thumb_set DMA2_Stream3_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream4_IRQHandler
|
||||
.thumb_set DMA2_Stream4_IRQHandler,Default_Handler
|
||||
|
||||
.weak DFSDM1_FLT0_IRQHandler
|
||||
.thumb_set DFSDM1_FLT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak DFSDM1_FLT1_IRQHandler
|
||||
.thumb_set DFSDM1_FLT1_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_TX_IRQHandler
|
||||
.thumb_set CAN2_TX_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_RX0_IRQHandler
|
||||
.thumb_set CAN2_RX0_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_RX1_IRQHandler
|
||||
.thumb_set CAN2_RX1_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN2_SCE_IRQHandler
|
||||
.thumb_set CAN2_SCE_IRQHandler,Default_Handler
|
||||
|
||||
.weak OTG_FS_IRQHandler
|
||||
.thumb_set OTG_FS_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream5_IRQHandler
|
||||
.thumb_set DMA2_Stream5_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream6_IRQHandler
|
||||
.thumb_set DMA2_Stream6_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA2_Stream7_IRQHandler
|
||||
.thumb_set DMA2_Stream7_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART6_IRQHandler
|
||||
.thumb_set USART6_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C3_EV_IRQHandler
|
||||
.thumb_set I2C3_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C3_ER_IRQHandler
|
||||
.thumb_set I2C3_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN3_TX_IRQHandler
|
||||
.thumb_set CAN3_TX_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN3_RX0_IRQHandler
|
||||
.thumb_set CAN3_RX0_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN3_RX1_IRQHandler
|
||||
.thumb_set CAN3_RX1_IRQHandler,Default_Handler
|
||||
|
||||
.weak CAN3_SCE_IRQHandler
|
||||
.thumb_set CAN3_SCE_IRQHandler,Default_Handler
|
||||
|
||||
.weak RNG_IRQHandler
|
||||
.thumb_set RNG_IRQHandler,Default_Handler
|
||||
|
||||
.weak FPU_IRQHandler
|
||||
.thumb_set FPU_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART7_IRQHandler
|
||||
.thumb_set UART7_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART8_IRQHandler
|
||||
.thumb_set UART8_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI4_IRQHandler
|
||||
.thumb_set SPI4_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI5_IRQHandler
|
||||
.thumb_set SPI5_IRQHandler,Default_Handler
|
||||
|
||||
.weak SAI1_IRQHandler
|
||||
.thumb_set SAI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART9_IRQHandler
|
||||
.thumb_set UART9_IRQHandler,Default_Handler
|
||||
|
||||
.weak UART10_IRQHandler
|
||||
.thumb_set UART10_IRQHandler,Default_Handler
|
||||
|
||||
.weak QUADSPI_IRQHandler
|
||||
.thumb_set QUADSPI_IRQHandler,Default_Handler
|
||||
|
||||
.weak FMPI2C1_EV_IRQHandler
|
||||
.thumb_set FMPI2C1_EV_IRQHandler,Default_Handler
|
||||
|
||||
.weak FMPI2C1_ER_IRQHandler
|
||||
.thumb_set FMPI2C1_ER_IRQHandler,Default_Handler
|
||||
|
||||
.weak LPTIM1_IRQHandler
|
||||
.thumb_set LPTIM1_IRQHandler,Default_Handler
|
||||
|
||||
.weak DFSDM2_FLT0_IRQHandler
|
||||
.thumb_set DFSDM2_FLT0_IRQHandler,Default_Handler
|
||||
|
||||
.weak DFSDM2_FLT1_IRQHandler
|
||||
.thumb_set DFSDM2_FLT1_IRQHandler,Default_Handler
|
||||
|
||||
.weak DFSDM2_FLT2_IRQHandler
|
||||
.thumb_set DFSDM2_FLT2_IRQHandler,Default_Handler
|
||||
|
||||
.weak DFSDM2_FLT3_IRQHandler
|
||||
.thumb_set DFSDM2_FLT3_IRQHandler,Default_Handler
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "stm32f4xx.h"
|
||||
#include "stm32f4xx_hal_gpio_ex.h"
|
||||
#define MCU_IDCODE 0x463U
|
||||
|
||||
#define CORE_FREQ 96U // in MHz
|
||||
#define APB1_FREQ (CORE_FREQ/2U)
|
||||
#define APB1_TIMER_FREQ (APB1_FREQ*2U) // APB1 is multiplied by 2 for the timer peripherals
|
||||
#define APB2_FREQ (CORE_FREQ/2U)
|
||||
#define APB2_TIMER_FREQ (APB2_FREQ*2U) // APB2 is multiplied by 2 for the timer peripherals
|
||||
|
||||
#define BOOTLOADER_ADDRESS 0x1FFF0004U
|
||||
|
||||
// Around (1Mbps / 8 bits/byte / 12 bytes per message)
|
||||
#define CAN_INTERRUPT_RATE 12000U
|
||||
|
||||
#define MAX_LED_FADE 8192U
|
||||
|
||||
#define NUM_INTERRUPTS 102U // There are 102 external interrupt sources (see stm32f413.h)
|
||||
|
||||
#define TICK_TIMER_IRQ TIM1_BRK_TIM9_IRQn
|
||||
#define TICK_TIMER TIM9
|
||||
|
||||
#define MICROSECOND_TIMER TIM2
|
||||
|
||||
#define INTERRUPT_TIMER_IRQ TIM6_DAC_IRQn
|
||||
#define INTERRUPT_TIMER TIM6
|
||||
|
||||
#define IND_WDG IWDG
|
||||
|
||||
#define PROVISION_CHUNK_ADDRESS 0x1FFF79E0U
|
||||
#define DEVICE_SERIAL_NUMBER_ADDRESS 0x1FFF79C0U
|
||||
|
||||
#include "board/can.h"
|
||||
#include "board/comms_definitions.h"
|
||||
|
||||
#ifndef BOOTSTUB
|
||||
#include "board/main_definitions.h"
|
||||
#else
|
||||
#include "board/bootstub_declarations.h"
|
||||
#endif
|
||||
|
||||
#include "board/libc.h"
|
||||
#include "board/sys/critical.h"
|
||||
#include "board/sys/faults.h"
|
||||
#include "board/utils.h"
|
||||
|
||||
#include "board/drivers/registers.h"
|
||||
#include "board/drivers/interrupts.h"
|
||||
#include "board/drivers/gpio.h"
|
||||
#include "board/stm32f4/peripherals.h"
|
||||
#include "board/stm32f4/interrupt_handlers.h"
|
||||
#include "board/drivers/timers.h"
|
||||
#include "board/stm32f4/board.h"
|
||||
#include "board/stm32f4/clock.h"
|
||||
|
||||
#if !defined(BOOTSTUB)
|
||||
#include "board/drivers/debug.h"
|
||||
#endif
|
||||
|
||||
#ifdef BOOTSTUB
|
||||
#include "board/stm32f4/llflash.h"
|
||||
#else
|
||||
#include "board/stm32f4/llbxcan.h"
|
||||
#endif
|
||||
|
||||
#include "board/stm32f4/llusb.h"
|
||||
|
||||
// unused
|
||||
uint16_t spi_error_count = 0U;
|
||||
void spi_init(void) {}
|
||||
void sound_tick(void) {};
|
||||
void can_tx_comms_resume_spi(void) {};
|
||||
|
||||
void early_gpio_float(void) {
|
||||
RCC->AHB1ENR = RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
|
||||
|
||||
GPIOB->MODER = 0; GPIOC->MODER = 0;
|
||||
GPIOA->ODR = 0; GPIOB->ODR = 0; GPIOC->ODR = 0;
|
||||
GPIOA->PUPDR = 0; GPIOB->PUPDR = 0; GPIOC->PUPDR = 0;
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
******************************************************************************
|
||||
**
|
||||
|
||||
** File : LinkerScript.ld
|
||||
**
|
||||
** Author : Auto-generated by System Workbench for STM32
|
||||
**
|
||||
** Abstract : Linker script for STM32H735ZGTx series
|
||||
** 1024Kbytes FLASH and 560Kbytes RAM
|
||||
**
|
||||
** Set heap size, stack size and stack location according
|
||||
** to application requirements.
|
||||
**
|
||||
** Set memory bank area and size if external memory is used.
|
||||
**
|
||||
** Target : STMicroelectronics STM32
|
||||
**
|
||||
** Distribution: The file is distributed “as is,” without any warranty
|
||||
** of any kind.
|
||||
**
|
||||
*****************************************************************************
|
||||
** @attention
|
||||
**
|
||||
** <h2><center>© COPYRIGHT(c) 2019 STMicroelectronics</center></h2>
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without modification,
|
||||
** are permitted provided that the following conditions are met:
|
||||
** 1. Redistributions of source code must retain the above copyright notice,
|
||||
** this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
** this list of conditions and the following disclaimer in the documentation
|
||||
** and/or other materials provided with the distribution.
|
||||
** 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
** may be used to endorse or promote products derived from this software
|
||||
** without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**
|
||||
*****************************************************************************
|
||||
*/
|
||||
|
||||
/* Entry Point */
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
/* Highest address of the user mode stack */
|
||||
enter_bootloader_mode = 0x2001FFFC;
|
||||
fault_cookie = 0x20000000;
|
||||
|
||||
_estack = 0x2001FFFC; /* end of RAM */
|
||||
_app_start = 0x08004000;
|
||||
|
||||
/* Generate a link error if heap and stack don't fit into RAM */
|
||||
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||
|
||||
/* Specify the memory areas */
|
||||
MEMORY
|
||||
{
|
||||
RAM (xrw) : ORIGIN = 0x20000004, LENGTH = 128K - 8
|
||||
CANRAM (xrw) : ORIGIN = 0x20020000, LENGTH = 128K
|
||||
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1536K
|
||||
}
|
||||
|
||||
/* Define output sections */
|
||||
SECTIONS
|
||||
{
|
||||
/* The startup code goes first into FLASH */
|
||||
.isr_vector :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* The program code and other data goes into FLASH */
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.glue_7) /* glue arm to thumb code */
|
||||
*(.glue_7t) /* glue thumb to arm code */
|
||||
*(.eh_frame)
|
||||
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .; /* define a global symbols at end of code */
|
||||
} >FLASH
|
||||
|
||||
/* Constant data goes into FLASH */
|
||||
.rodata :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
|
||||
.ARM : {
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} >FLASH
|
||||
|
||||
.preinit_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||
KEEP (*(.preinit_array*))
|
||||
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||
} >FLASH
|
||||
.init_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__init_array_start = .);
|
||||
KEEP (*(SORT(.init_array.*)))
|
||||
KEEP (*(.init_array*))
|
||||
PROVIDE_HIDDEN (__init_array_end = .);
|
||||
} >FLASH
|
||||
.fini_array :
|
||||
{
|
||||
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||
KEEP (*(SORT(.fini_array.*)))
|
||||
KEEP (*(.fini_array*))
|
||||
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||
} >FLASH
|
||||
|
||||
/* used by the startup to initialize data */
|
||||
_sidata = LOADADDR(.data);
|
||||
|
||||
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||
.data :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .; /* create a global symbol at data start */
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
|
||||
. = ALIGN(4);
|
||||
_edata = .; /* define a global symbol at data end */
|
||||
} >RAM AT> FLASH
|
||||
|
||||
|
||||
/* Uninitialized data section */
|
||||
. = ALIGN(4);
|
||||
.bss :
|
||||
{
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .; /* define a global symbol at bss start */
|
||||
__bss_start__ = _sbss;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .; /* define a global symbol at bss end */
|
||||
__bss_end__ = _ebss;
|
||||
} >RAM
|
||||
|
||||
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||
._user_heap_stack :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( end = . );
|
||||
PROVIDE ( _end = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
} >RAM
|
||||
|
||||
.canram (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.canram*)
|
||||
} >CANRAM
|
||||
|
||||
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||
}
|
||||
|
||||
ASSERT(_ebss + _Min_Heap_Size + _Min_Stack_Size <= _estack, "F4 data/heap/stack overlap reserved boot flag")
|
||||
ASSERT(SIZEOF(.canram) <= LENGTH(CANRAM), "F4 CAN queues exceed upper SRAM1")
|
||||
@@ -8,7 +8,7 @@
|
||||
// Low power state "stop mode" is only entered from SAFETY_SILENT when no safety function is active and exited via reset which is a safe state.
|
||||
|
||||
bool power_save_enabled = false;
|
||||
#ifdef ALLOW_DEBUG
|
||||
#if defined(ALLOW_DEBUG) && defined(STM32H7)
|
||||
volatile bool stop_mode_requested = false;
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,12 @@ void enable_can_transceivers(bool enabled) {
|
||||
// Leave main CAN always on for CAN-based ignition detection
|
||||
uint8_t main_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 3U : 1U;
|
||||
for(uint8_t i=1U; i<=4U; i++){
|
||||
current_board->enable_can_transceiver(i, (i == main_bus) || enabled);
|
||||
bool transceiver_enabled = (i == main_bus) || enabled;
|
||||
#ifdef PANDA_HKG_REMOTE_START
|
||||
uint8_t hkg_bus = (harness.status == HARNESS_STATUS_FLIPPED) ? 4U : 2U;
|
||||
transceiver_enabled = transceiver_enabled || (i == hkg_bus);
|
||||
#endif
|
||||
current_board->enable_can_transceiver(i, transceiver_enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +36,9 @@ void set_power_save_state(bool enable) {
|
||||
} else {
|
||||
llcan_irq_disable(cans[2]);
|
||||
}
|
||||
#ifndef PANDA_HKG_REMOTE_START
|
||||
llcan_irq_disable(cans[1]);
|
||||
#endif
|
||||
} else {
|
||||
print("disable power savings\n");
|
||||
|
||||
@@ -54,6 +61,7 @@ void set_power_save_state(bool enable) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef STM32H7
|
||||
static void enter_stop_mode(void) {
|
||||
// the SPI CS interrupt shares an IRQ with the FDCAN3 RX wakeup line, and doesn't clear its pending bit
|
||||
NVIC_DisableIRQ(EXTI15_10_IRQn);
|
||||
@@ -161,3 +169,4 @@ static void enter_stop_mode(void) {
|
||||
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -129,6 +129,7 @@ class Panda:
|
||||
|
||||
# from https://github.com/commaai/openpilot/blob/103b4df18cbc38f4129555ab8b15824d1a672bdf/cereal/log.capnp#L648
|
||||
HW_TYPE_UNKNOWN = b'\x00'
|
||||
HW_TYPE_DOS = b'\x06'
|
||||
HW_TYPE_RED_PANDA = b'\x07'
|
||||
HW_TYPE_TRES = b'\x09'
|
||||
HW_TYPE_CUATRO = b'\x0a'
|
||||
@@ -150,9 +151,10 @@ class Panda:
|
||||
HEALTH_FLAG_HARDFAULT_RESET = 1 << 8
|
||||
|
||||
H7_DEVICES = [HW_TYPE_RED_PANDA, HW_TYPE_TRES, HW_TYPE_CUATRO, HW_TYPE_BODY]
|
||||
SUPPORTED_DEVICES = H7_DEVICES
|
||||
F4_DEVICES = [HW_TYPE_DOS]
|
||||
SUPPORTED_DEVICES = H7_DEVICES + F4_DEVICES
|
||||
|
||||
INTERNAL_DEVICES = (HW_TYPE_TRES, HW_TYPE_CUATRO)
|
||||
INTERNAL_DEVICES = (HW_TYPE_DOS, HW_TYPE_TRES, HW_TYPE_CUATRO)
|
||||
|
||||
HARNESS_STATUS_NC = 0
|
||||
HARNESS_STATUS_NORMAL = 1
|
||||
@@ -408,7 +410,9 @@ class Panda:
|
||||
apps_sectors_cumsum = accumulate(mcu_type.config.sector_sizes[1:])
|
||||
last_sector = next((i + 1 for i, v in enumerate(apps_sectors_cumsum) if v > len(code)), -1)
|
||||
assert last_sector >= 1, "Binary too small? No sector to erase."
|
||||
assert last_sector < 7, "Binary too large! Risk of overwriting provisioning chunk."
|
||||
assert last_sector < len(mcu_type.config.sector_sizes), "Binary too large! Risk of overwriting provisioning chunk."
|
||||
if mcu_type == McuType.F4:
|
||||
assert last_sector < 12, "DOS bootstubs support sectors 1..11"
|
||||
|
||||
# unlock flash
|
||||
logger.info("flash: unlocking")
|
||||
@@ -440,7 +444,7 @@ class Panda:
|
||||
return
|
||||
|
||||
if not fn:
|
||||
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn)
|
||||
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn)
|
||||
assert os.path.isfile(fn)
|
||||
logger.debug("flash: main version is %s", self.get_version())
|
||||
if not self.bootstub:
|
||||
@@ -455,7 +459,7 @@ class Panda:
|
||||
logger.debug("flash: bootstub version is %s", self.get_version())
|
||||
|
||||
# do flash
|
||||
Panda.flash_static(self._handle, code, mcu_type=McuType.H7)
|
||||
Panda.flash_static(self._handle, code, mcu_type=self.get_mcu_type())
|
||||
|
||||
# reconnect
|
||||
if reconnect:
|
||||
@@ -506,7 +510,7 @@ class Panda:
|
||||
def up_to_date(self, fn=None) -> bool:
|
||||
current = self.get_signature()
|
||||
if fn is None:
|
||||
fn = os.path.join(FW_PATH, McuType.H7.config.app_fn)
|
||||
fn = os.path.join(FW_PATH, self.get_mcu_type().config.app_fn)
|
||||
expected = Panda.get_signature_from_firmware(fn)
|
||||
return (current == expected)
|
||||
|
||||
@@ -639,8 +643,16 @@ class Panda:
|
||||
"""
|
||||
return self._serial
|
||||
|
||||
def get_mcu_type(self) -> McuType:
|
||||
hw_type = self.get_type()
|
||||
if hw_type in self.F4_DEVICES:
|
||||
return McuType.F4
|
||||
if hw_type in self.H7_DEVICES:
|
||||
return McuType.H7
|
||||
raise ValueError(f"Unknown HW: {hw_type}")
|
||||
|
||||
def get_dfu_serial(self):
|
||||
return PandaDFU.st_serial_to_dfu_serial(self._serial, McuType.H7)
|
||||
return PandaDFU.st_serial_to_dfu_serial(self._serial, self.get_mcu_type())
|
||||
|
||||
def get_uid(self):
|
||||
"""
|
||||
|
||||
@@ -31,6 +31,13 @@ class McuConfig(NamedTuple):
|
||||
return self.bootstub_address + sum(self.sector_sizes[:i])
|
||||
|
||||
|
||||
F4Config = McuConfig(
|
||||
"STM32F4", 0x463,
|
||||
[0x4000 for _ in range(4)] + [0x10000] + [0x20000 for _ in range(11)],
|
||||
16, 0x1FFF7A10, 0x800, 0x1FFF79C0, 0x8004000,
|
||||
"panda.bin.signed", 0x8000000, "bootstub.panda.bin",
|
||||
)
|
||||
|
||||
H7Config = McuConfig(
|
||||
"STM32H7",
|
||||
0x483,
|
||||
@@ -48,6 +55,7 @@ H7Config = McuConfig(
|
||||
|
||||
@enum.unique
|
||||
class McuType(enum.Enum):
|
||||
F4 = F4Config
|
||||
H7 = H7Config
|
||||
|
||||
@property
|
||||
|
||||
@@ -104,6 +104,8 @@ class PandaDFU:
|
||||
uid_base = struct.unpack("H" * 6, bytes.fromhex(st))
|
||||
if mcu_type == McuType.H7:
|
||||
return binascii.hexlify(struct.pack("!HHH", uid_base[1] + uid_base[5], uid_base[0] + uid_base[4], uid_base[3])).upper().decode("utf-8")
|
||||
if mcu_type == McuType.F4:
|
||||
return binascii.hexlify(struct.pack("!HHH", uid_base[1] + uid_base[5], uid_base[0] + uid_base[4] + 0xA, uid_base[3])).upper().decode("utf-8")
|
||||
except struct.error:
|
||||
return None
|
||||
|
||||
|
||||
@@ -4,6 +4,209 @@ Cppcheck checkers list from test_misra.sh:
|
||||
|
||||
|
||||
|
||||
TEST variant options:
|
||||
--enable=all --disable=unusedFunction --addon=misra -DSTM32F4 -DSTM32F413xx -I /board/stm32f4/inc/ /board/main.c
|
||||
|
||||
|
||||
Critical errors
|
||||
---------------
|
||||
No critical errors encountered.
|
||||
Note: There might still have been non-critical bailouts which might lead to false negatives.
|
||||
|
||||
|
||||
Open source checkers
|
||||
--------------------
|
||||
Yes Check64BitPortability::pointerassignment
|
||||
Yes CheckAssert::assertWithSideEffects
|
||||
Yes CheckAutoVariables::assignFunctionArg
|
||||
Yes CheckAutoVariables::autoVariables
|
||||
Yes CheckAutoVariables::checkVarLifetime
|
||||
No CheckBool::checkAssignBoolToFloat require:style,c++
|
||||
Yes CheckBool::checkAssignBoolToPointer
|
||||
Yes CheckBool::checkBitwiseOnBoolean
|
||||
Yes CheckBool::checkComparisonOfBoolExpressionWithInt
|
||||
No CheckBool::checkComparisonOfBoolWithBool require:style,c++
|
||||
No CheckBool::checkComparisonOfBoolWithInt require:warning,c++
|
||||
No CheckBool::checkComparisonOfFuncReturningBool require:style,c++
|
||||
Yes CheckBool::checkIncrementBoolean
|
||||
Yes CheckBool::pointerArithBool
|
||||
Yes CheckBool::returnValueOfFunctionReturningBool
|
||||
Yes CheckBufferOverrun::analyseWholeProgram
|
||||
Yes CheckBufferOverrun::argumentSize
|
||||
Yes CheckBufferOverrun::arrayIndex
|
||||
Yes CheckBufferOverrun::arrayIndexThenCheck
|
||||
Yes CheckBufferOverrun::bufferOverflow
|
||||
Yes CheckBufferOverrun::negativeArraySize
|
||||
Yes CheckBufferOverrun::objectIndex
|
||||
Yes CheckBufferOverrun::pointerArithmetic
|
||||
No CheckBufferOverrun::stringNotZeroTerminated require:warning,inconclusive
|
||||
Yes CheckClass::analyseWholeProgram
|
||||
No CheckClass::checkConst require:style,inconclusive
|
||||
No CheckClass::checkConstructors require:style,warning
|
||||
No CheckClass::checkCopyConstructors require:warning
|
||||
No CheckClass::checkDuplInheritedMembers require:warning
|
||||
No CheckClass::checkExplicitConstructors require:style
|
||||
No CheckClass::checkMemset
|
||||
No CheckClass::checkMissingOverride require:style,c++03
|
||||
No CheckClass::checkReturnByReference require:performance
|
||||
No CheckClass::checkSelfInitialization
|
||||
No CheckClass::checkThisUseAfterFree require:warning
|
||||
No CheckClass::checkUnsafeClassRefMember require:warning,safeChecks
|
||||
No CheckClass::checkUselessOverride require:style
|
||||
No CheckClass::checkVirtualFunctionCallInConstructor require:warning
|
||||
No CheckClass::initializationListUsage require:performance
|
||||
No CheckClass::initializerListOrder require:style,inconclusive
|
||||
No CheckClass::operatorEqRetRefThis require:style
|
||||
No CheckClass::operatorEqToSelf require:warning
|
||||
No CheckClass::privateFunctions require:style
|
||||
No CheckClass::thisSubtraction require:warning
|
||||
No CheckClass::virtualDestructor
|
||||
Yes CheckCondition::alwaysTrueFalse
|
||||
Yes CheckCondition::assignIf
|
||||
Yes CheckCondition::checkAssignmentInCondition
|
||||
Yes CheckCondition::checkBadBitmaskCheck
|
||||
Yes CheckCondition::checkCompareValueOutOfTypeRange
|
||||
Yes CheckCondition::checkDuplicateConditionalAssign
|
||||
Yes CheckCondition::checkIncorrectLogicOperator
|
||||
Yes CheckCondition::checkInvalidTestForOverflow
|
||||
Yes CheckCondition::checkModuloAlwaysTrueFalse
|
||||
Yes CheckCondition::checkPointerAdditionResultNotNull
|
||||
Yes CheckCondition::clarifyCondition
|
||||
Yes CheckCondition::comparison
|
||||
Yes CheckCondition::duplicateCondition
|
||||
Yes CheckCondition::multiCondition
|
||||
Yes CheckCondition::multiCondition2
|
||||
No CheckExceptionSafety::checkCatchExceptionByValue require:style
|
||||
No CheckExceptionSafety::checkRethrowCopy require:style
|
||||
No CheckExceptionSafety::deallocThrow require:warning
|
||||
No CheckExceptionSafety::destructors require:warning
|
||||
No CheckExceptionSafety::nothrowThrows
|
||||
No CheckExceptionSafety::rethrowNoCurrentException
|
||||
No CheckExceptionSafety::unhandledExceptionSpecification require:style,inconclusive
|
||||
Yes CheckFunctions::checkIgnoredReturnValue
|
||||
Yes CheckFunctions::checkMathFunctions
|
||||
Yes CheckFunctions::checkMissingReturn
|
||||
Yes CheckFunctions::checkProhibitedFunctions
|
||||
Yes CheckFunctions::invalidFunctionUsage
|
||||
Yes CheckFunctions::memsetInvalid2ndParam
|
||||
Yes CheckFunctions::memsetZeroBytes
|
||||
No CheckFunctions::returnLocalStdMove require:performance,c++11
|
||||
Yes CheckFunctions::useStandardLibrary
|
||||
No CheckIO::checkCoutCerrMisusage require:c
|
||||
Yes CheckIO::checkFileUsage
|
||||
Yes CheckIO::checkWrongPrintfScanfArguments
|
||||
Yes CheckIO::invalidScanf
|
||||
Yes CheckLeakAutoVar::check
|
||||
No CheckMemoryLeakInClass::check
|
||||
Yes CheckMemoryLeakInFunction::checkReallocUsage
|
||||
Yes CheckMemoryLeakNoVar::check
|
||||
No CheckMemoryLeakNoVar::checkForUnsafeArgAlloc
|
||||
Yes CheckMemoryLeakStructMember::check
|
||||
Yes CheckNullPointer::analyseWholeProgram
|
||||
Yes CheckNullPointer::arithmetic
|
||||
Yes CheckNullPointer::nullConstantDereference
|
||||
Yes CheckNullPointer::nullPointer
|
||||
No CheckOther::checkAccessOfMovedVariable require:c++11,warning
|
||||
Yes CheckOther::checkCastIntToCharAndBack
|
||||
Yes CheckOther::checkCharVariable
|
||||
Yes CheckOther::checkComparePointers
|
||||
Yes CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse
|
||||
Yes CheckOther::checkConstPointer
|
||||
No CheckOther::checkConstVariable require:style,c++
|
||||
No CheckOther::checkDuplicateBranch require:style,inconclusive
|
||||
Yes CheckOther::checkDuplicateExpression
|
||||
Yes CheckOther::checkEvaluationOrder
|
||||
Yes CheckOther::checkFuncArgNamesDifferent
|
||||
No CheckOther::checkIncompleteArrayFill require:warning,portability,inconclusive
|
||||
Yes CheckOther::checkIncompleteStatement
|
||||
No CheckOther::checkInterlockedDecrement require:windows-platform
|
||||
Yes CheckOther::checkInvalidFree
|
||||
Yes CheckOther::checkKnownArgument
|
||||
Yes CheckOther::checkKnownPointerToBool
|
||||
No CheckOther::checkMisusedScopedObject require:style,c++
|
||||
Yes CheckOther::checkModuloOfOne
|
||||
Yes CheckOther::checkNanInArithmeticExpression
|
||||
Yes CheckOther::checkNegativeBitwiseShift
|
||||
Yes CheckOther::checkOverlappingWrite
|
||||
No CheckOther::checkPassByReference require:performance,c++
|
||||
Yes CheckOther::checkRedundantAssignment
|
||||
No CheckOther::checkRedundantCopy require:c++,performance,inconclusive
|
||||
Yes CheckOther::checkRedundantPointerOp
|
||||
Yes CheckOther::checkShadowVariables
|
||||
Yes CheckOther::checkSignOfUnsignedVariable
|
||||
No CheckOther::checkSuspiciousCaseInSwitch require:warning,inconclusive
|
||||
No CheckOther::checkSuspiciousSemicolon require:warning,inconclusive
|
||||
Yes CheckOther::checkUnionZeroInit
|
||||
Yes CheckOther::checkUnreachableCode
|
||||
Yes CheckOther::checkUnusedLabel
|
||||
Yes CheckOther::checkVarFuncNullUB
|
||||
Yes CheckOther::checkVariableScope
|
||||
Yes CheckOther::checkZeroDivision
|
||||
Yes CheckOther::clarifyCalculation
|
||||
Yes CheckOther::clarifyStatement
|
||||
Yes CheckOther::invalidPointerCast
|
||||
Yes CheckOther::redundantBitwiseOperationInSwitch
|
||||
Yes CheckOther::suspiciousFloatingPointCast
|
||||
No CheckOther::warningDangerousTypeCast require:warning,c++
|
||||
Yes CheckOther::warningIntToPointerCast
|
||||
No CheckOther::warningOldStylePointerCast require:style,c++
|
||||
No CheckPostfixOperator::postfixOperator require:performance
|
||||
Yes CheckSizeof::checkSizeofForArrayParameter
|
||||
Yes CheckSizeof::checkSizeofForNumericParameter
|
||||
Yes CheckSizeof::checkSizeofForPointerSize
|
||||
Yes CheckSizeof::sizeofCalculation
|
||||
Yes CheckSizeof::sizeofFunction
|
||||
Yes CheckSizeof::sizeofVoid
|
||||
Yes CheckSizeof::sizeofsizeof
|
||||
No CheckSizeof::suspiciousSizeofCalculation require:warning,inconclusive
|
||||
No CheckStl::checkDereferenceInvalidIterator require:warning
|
||||
No CheckStl::checkDereferenceInvalidIterator2
|
||||
No CheckStl::checkFindInsert require:performance
|
||||
No CheckStl::checkMutexes require:warning
|
||||
No CheckStl::erase
|
||||
No CheckStl::eraseIteratorOutOfBounds
|
||||
No CheckStl::if_find require:warning,performance
|
||||
No CheckStl::invalidContainer
|
||||
No CheckStl::iterators
|
||||
No CheckStl::knownEmptyContainer require:style
|
||||
No CheckStl::misMatchingContainerIterator
|
||||
No CheckStl::misMatchingContainers
|
||||
No CheckStl::missingComparison require:warning
|
||||
No CheckStl::negativeIndex
|
||||
No CheckStl::outOfBounds
|
||||
No CheckStl::outOfBoundsIndexExpression
|
||||
No CheckStl::redundantCondition require:style
|
||||
No CheckStl::size require:performance,c++03
|
||||
No CheckStl::stlBoundaries
|
||||
No CheckStl::stlOutOfBounds
|
||||
No CheckStl::string_c_str
|
||||
No CheckStl::useStlAlgorithm require:style
|
||||
No CheckStl::uselessCalls require:performance,warning
|
||||
Yes CheckString::checkAlwaysTrueOrFalseStringCompare
|
||||
Yes CheckString::checkIncorrectStringCompare
|
||||
Yes CheckString::checkSuspiciousStringCompare
|
||||
Yes CheckString::overlappingStrcmp
|
||||
Yes CheckString::sprintfOverlappingData
|
||||
Yes CheckString::strPlusChar
|
||||
Yes CheckString::stringLiteralWrite
|
||||
Yes CheckType::checkFloatToIntegerOverflow
|
||||
Yes CheckType::checkIntegerOverflow
|
||||
Yes CheckType::checkLongCast
|
||||
Yes CheckType::checkSignConversion
|
||||
Yes CheckType::checkTooBigBitwiseShift
|
||||
Yes CheckUninitVar::analyseWholeProgram
|
||||
Yes CheckUninitVar::check
|
||||
Yes CheckUninitVar::valueFlowUninit
|
||||
No CheckUnusedFunctions::check require:unusedFunction
|
||||
Yes CheckUnusedVar::checkFunctionVariableUsage
|
||||
Yes CheckUnusedVar::checkStructMemberUsage
|
||||
Yes CheckVaarg::va_list_usage
|
||||
Yes CheckVaarg::va_start_argument
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
TEST variant options:
|
||||
--enable=all --disable=unusedFunction --addon=misra -DSTM32H7 -DSTM32H725xx -I /board/stm32h7/inc/ /board/main.c
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ cppcheck() {
|
||||
|
||||
PANDA_OPTS="--enable=all --disable=unusedFunction --addon=misra"
|
||||
|
||||
printf "\n${GREEN}** PANDA F4 CODE **${NC}\n"
|
||||
cppcheck $PANDA_OPTS -DSTM32F4 -DSTM32F413xx -I $PANDA_DIR/board/stm32f4/inc/ $PANDA_DIR/board/main.c
|
||||
|
||||
printf "\n${GREEN}** PANDA H7 CODE **${NC}\n"
|
||||
cppcheck $PANDA_OPTS -DSTM32H7 -DSTM32H725xx -I $PANDA_DIR/board/stm32h7/inc/ $PANDA_DIR/board/main.c
|
||||
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
import ast
|
||||
import binascii
|
||||
import importlib.util
|
||||
from itertools import accumulate
|
||||
import os
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def constants():
|
||||
spec = importlib.util.spec_from_file_location('tested_panda_constants', ROOT / 'panda/python/constants.py')
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def production_panda(tmp_path):
|
||||
tree = ast.parse((ROOT / 'panda/python/__init__.py').read_text())
|
||||
original = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == 'Panda')
|
||||
selected = {'get_mcu_type', 'is_internal', 'get_dfu_serial', 'up_to_date', 'flash', 'flash_static'}
|
||||
body = [node for node in original.body if isinstance(node, ast.Assign) or isinstance(node, ast.FunctionDef) and node.name in selected]
|
||||
cls = ast.ClassDef(name='Panda', bases=[], keywords=[], body=body, decorator_list=[])
|
||||
mcu = constants().McuType
|
||||
env = {'accumulate': accumulate, 'McuType': mcu, 'compute_version_hash': lambda _: 1, 'opendbc': SimpleNamespace(INCLUDE_PATH=''),
|
||||
'os': os, 'BASEDIR': str(tmp_path), '_parse_c_struct': lambda *args: None, 'struct': struct,
|
||||
'FW_PATH': str(tmp_path), 'logger': Mock(), 'PandaDFU': Mock(),
|
||||
'usb1': SimpleNamespace(ENDPOINT_IN=1, TYPE_VENDOR=2, RECIPIENT_DEVICE=3, ENDPOINT_OUT=4)}
|
||||
exec(compile(ast.fix_missing_locations(ast.Module(body=[cls], type_ignores=[])), '<current-panda>', 'exec'), env)
|
||||
return env['Panda'], env, mcu
|
||||
|
||||
|
||||
@pytest.mark.parametrize('hardware,expected', [(b'\x06', 'F4'), (b'\x07', 'H7'), (b'\x09', 'H7'), (b'\x0a', 'H7')])
|
||||
def test_detected_hardware_selects_exact_firmware_and_dfu_config(tmp_path, hardware, expected):
|
||||
cls, env, mcu = production_panda(tmp_path)
|
||||
panda = cls()
|
||||
panda.get_type = lambda: hardware
|
||||
panda._serial = '010002000300040005000600'
|
||||
assert panda.get_mcu_type() == getattr(mcu, expected)
|
||||
panda.get_dfu_serial()
|
||||
env['PandaDFU'].st_serial_to_dfu_serial.assert_called_once_with(panda._serial, getattr(mcu, expected))
|
||||
assert panda.is_internal() == (hardware != b'\x07')
|
||||
|
||||
|
||||
@pytest.mark.parametrize('hardware', [b'\x00', b'\x03', b'\xff'])
|
||||
def test_unknown_hardware_never_defaults_to_h7(tmp_path, hardware):
|
||||
cls, _, _ = production_panda(tmp_path)
|
||||
panda = cls()
|
||||
panda.get_type = lambda: hardware
|
||||
with pytest.raises(ValueError, match='Unknown HW'):
|
||||
panda.get_mcu_type()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('hardware', [b'\x06', b'\x09'])
|
||||
def test_default_flash_uses_detected_mcu_image_and_static_flash_target(tmp_path, hardware):
|
||||
cls, env, _ = production_panda(tmp_path)
|
||||
panda = cls()
|
||||
panda.get_type = lambda: hardware
|
||||
target = panda.get_mcu_type()
|
||||
image = tmp_path / target.config.app_fn
|
||||
image.write_bytes(b'exact configured image')
|
||||
panda.up_to_date = lambda **kwargs: False
|
||||
panda.bootstub = True
|
||||
panda.get_version = lambda: 'bootstub'
|
||||
panda._handle = object()
|
||||
panda.reconnect = Mock()
|
||||
cls.flash_static = Mock()
|
||||
panda.flash()
|
||||
cls.flash_static.assert_called_once_with(panda._handle, image.read_bytes(), mcu_type=target)
|
||||
panda.reconnect.assert_called_once()
|
||||
captured = []
|
||||
cls.get_signature_from_firmware = lambda path: captured.append(path) or b'correct'
|
||||
panda.get_signature = lambda: b'correct'
|
||||
assert cls.up_to_date(panda)
|
||||
assert captured == [str(image)]
|
||||
|
||||
|
||||
def test_pandad_signature_binds_detected_mcu_and_rejects_wrong_packet_hashes(tmp_path):
|
||||
tree = ast.parse((ROOT / 'openpilot/selfdrive/pandad/pandad.py').read_text())
|
||||
functions = [node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name in {'get_expected_signature', 'flash_panda'}]
|
||||
mcu = constants().McuType
|
||||
for target in (mcu.F4, mcu.H7):
|
||||
for versions in ((11, 22), (0, 0), (11, 23)):
|
||||
device = SimpleNamespace(get_mcu_type=lambda: target, is_internal=lambda: True, bootstub=False,
|
||||
get_version=lambda: 'current', get_signature=lambda: b'correct',
|
||||
get_packets_versions=lambda: versions, close=Mock(), flash=Mock())
|
||||
factory = Mock(return_value=device)
|
||||
factory.HEALTH_PACKET_VERSION, factory.CAN_PACKET_VERSION = 11, 22
|
||||
factory.get_signature_from_firmware = Mock(return_value=b'correct')
|
||||
class Mismatch(Exception):
|
||||
pass
|
||||
env = {'os': os, 'FW_PATH': str(tmp_path), 'McuType': mcu, 'Panda': factory,
|
||||
'PandaProtocolMismatch': Mismatch, 'cloudlog': Mock(), 'HARDWARE': Mock()}
|
||||
exec(compile(ast.fix_missing_locations(ast.Module(body=functions, type_ignores=[])), '<current-wrapper>', 'exec'), env)
|
||||
if versions == (11, 22):
|
||||
env['flash_panda']('serial')
|
||||
else:
|
||||
with pytest.raises(Mismatch, match='protocol hash mismatch'):
|
||||
env['flash_panda']('serial')
|
||||
factory.get_signature_from_firmware.assert_called_once_with(str(tmp_path / target.config.app_fn))
|
||||
device.flash.assert_not_called()
|
||||
device.close.assert_called_once()
|
||||
|
||||
|
||||
def test_dfu_serial_uses_f4_rom_offset_and_h7_unchanged():
|
||||
tree = ast.parse((ROOT / 'panda/python/dfu.py').read_text())
|
||||
original = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == 'PandaDFU')
|
||||
method = next(node for node in original.body if isinstance(node, ast.FunctionDef) and node.name == 'st_serial_to_dfu_serial')
|
||||
cls = ast.ClassDef(name='PandaDFU', bases=[], keywords=[], body=[method], decorator_list=[])
|
||||
mcu = constants().McuType
|
||||
env = {'McuType': mcu, 'binascii': binascii, 'struct': struct}
|
||||
exec(compile(ast.fix_missing_locations(ast.Module(body=[cls], type_ignores=[])), '<current-dfu>', 'exec'), env)
|
||||
convert = env['PandaDFU'].st_serial_to_dfu_serial
|
||||
assert convert('010002000300040005000600', mcu.F4) == '000800100004'
|
||||
assert convert('010002000300040005000600', mcu.H7) == '000800060004'
|
||||
assert convert(None, mcu.F4) is None
|
||||
|
||||
|
||||
def test_f4_sector12_is_rejected_before_any_erase_or_write(tmp_path):
|
||||
cls, _, mcu = production_panda(tmp_path)
|
||||
cls.flasher_present = lambda handle: True
|
||||
handle = SimpleNamespace(controlWrite=Mock(), bulkWrite=Mock())
|
||||
code = bytes(sum(mcu.F4.config.sector_sizes[1:12]))
|
||||
with pytest.raises(AssertionError, match='DOS bootstubs support sectors 1..11'):
|
||||
cls.flash_static(handle, code, mcu.F4)
|
||||
handle.controlWrite.assert_not_called()
|
||||
handle.bulkWrite.assert_not_called()
|
||||
Reference in New Issue
Block a user