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:
firestar5683
2026-09-25 14:53:20 -05:00
parent ab81464bd1
commit c1aeddbfea
56 changed files with 25939 additions and 62 deletions
+6 -1
View File
@@ -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;
}
+3 -2
View File
@@ -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;
}
+42 -1
View File
@@ -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();
+56 -8
View File
@@ -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 &params_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;
}
+146
View File
@@ -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;
}
+8 -3
View File
@@ -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_;
};
+9 -5
View File
@@ -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 &params;
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)
+1
View File
@@ -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
View File
@@ -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"])
+2
View File
@@ -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;
+143
View File
@@ -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
};
+2
View File
@@ -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 -2
View File
@@ -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--;
+226
View File
@@ -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;
}
+27
View File
@@ -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;
+1 -1
View File
@@ -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);
+4
View File
@@ -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"
+4
View File
@@ -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
View File
@@ -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;
+3
View File
@@ -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;
+25
View File
@@ -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);
}
+34
View File
@@ -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 ***
}
+284
View File
@@ -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
+40
View File
@@ -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
+273
View File
@@ -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
+271
View File
@@ -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>&copy; 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****/
+214
View 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>&copy; 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
+124
View File
@@ -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>&copy; 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****/
+98
View 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);}
+29
View File
@@ -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;
}
+140
View File
@@ -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);
+23
View File
@@ -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);
}
+28
View File
@@ -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) { }
+80
View File
@@ -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);
}
+16
View File
@@ -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);
+91
View File
@@ -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
}
+583
View File
@@ -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>&copy; 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****/
+80
View 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;
}
+192
View File
@@ -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>&copy; 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")
+11 -2
View File
@@ -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
+19 -7
View File
@@ -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):
"""
+8
View File
@@ -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
+2
View File
@@ -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
+203
View File
@@ -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
+3
View File
@@ -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
+133
View File
@@ -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()