diff --git a/docs/datasheets/ADS1115IDGSR-Texas-Instruments.pdf b/docs/datasheets/ADS1115IDGSR-Texas-Instruments.pdf new file mode 100644 index 0000000..9d24ac4 Binary files /dev/null and b/docs/datasheets/ADS1115IDGSR-Texas-Instruments.pdf differ diff --git a/docs/datasheets/STM32-STM32F4-STM32F401-STM32F401CCU6-pinout-high-resolution.png b/docs/datasheets/STM32-STM32F4-STM32F401-STM32F401CCU6-pinout-high-resolution.png new file mode 100644 index 0000000..9e84aef Binary files /dev/null and b/docs/datasheets/STM32-STM32F4-STM32F401-STM32F401CCU6-pinout-high-resolution.png differ diff --git a/docs/datasheets/STM32-STM32F401-line.jpg.webp b/docs/datasheets/STM32-STM32F401-line.jpg.webp new file mode 100644 index 0000000..8a719a3 Binary files /dev/null and b/docs/datasheets/STM32-STM32F401-line.jpg.webp differ diff --git a/docs/datasheets/TC74.pdf b/docs/datasheets/TC74.pdf new file mode 100644 index 0000000..0a124a8 Binary files /dev/null and b/docs/datasheets/TC74.pdf differ diff --git a/docs/datasheets/adafruit-4-channel-adc-breakouts.pdf b/docs/datasheets/adafruit-4-channel-adc-breakouts.pdf new file mode 100644 index 0000000..e194478 Binary files /dev/null and b/docs/datasheets/adafruit-4-channel-adc-breakouts.pdf differ diff --git a/docs/datasheets/ads1110_en.pdf b/docs/datasheets/ads1110_en.pdf new file mode 100644 index 0000000..d95eb69 Binary files /dev/null and b/docs/datasheets/ads1110_en.pdf differ diff --git a/rebel/AGENTS.md b/rebel/AGENTS.md new file mode 100644 index 0000000..00342ee --- /dev/null +++ b/rebel/AGENTS.md @@ -0,0 +1,50 @@ +# AGENTS Guidelines (Rebel) + +This document defines high-level context and engineering principles for AI agents working on Rebel. + +## Project Context + +- `Cheetah` is a smart remotely-controlled car platform. +- `Rebel` is a Cheetah subproject containing car hardware and software. + +## Architecture Boundaries + +- `cars/`: board-specific BSP-like hardware/software layer. +- `apps/`: hardware-agnostic application logic. +- `lib/`: cross-layer contracts and communication interfaces. + +Keep these boundaries strict: +- No board-specific logic in `apps/`. +- No app/business logic in `cars/`. +- Shared contracts must live in `lib/`. + +## Habilis Hardware Source Of Truth + +- MCU: `STM32F401CE`. +- Shared I2C bus: `I2C1` (`PB6=SCL`, `PB7=SDA`) with `4.7k` pull-ups on both lines. +- I2C1 devices: + - `PCA9685 @ 0x41` + - `ADS1110 @ 0x48` + - `24C04A @ 0x50` + - Proteus I2C debugger +- Virtual terminal: `USART1` (`PA9=TX`, `PA10=RX`). +- `L298` is connected to `LED0` and `LED1` outputs of `PCA9685`. + +Treat this hardware profile as authoritative unless explicitly updated by the user. + +## Engineering Principles + +- Prefer root-cause fixes over symptom suppression. +- Avoid ad-hoc hacks that depend on fragile simulator/toolchain side effects. +- Keep changes minimal, auditable, and reversible. +- Preserve interface compatibility unless a breaking change is explicitly requested. +- Make build and runtime behavior deterministic across environments. +- Keep board-specific simulation details local to board files; keep shared templates generic. +- State assumptions and tradeoffs clearly in task summaries. + +## Build And Compatibility Policy + +- Favor explicit, stable build contracts over implicit compiler defaults. +- Pin language semantics intentionally (via explicit standard settings), while allowing toolchain upgrades. +- Do not rely on undefined behavior, toolchain quirks, or parser-specific artifacts. +- When simulator behavior diverges from hardware expectations, validate against hardware datasheets and protocol-level evidence first. diff --git a/rebel/CMakeLists.txt b/rebel/CMakeLists.txt index 5ab85ed..f320794 100644 --- a/rebel/CMakeLists.txt +++ b/rebel/CMakeLists.txt @@ -3,10 +3,18 @@ cmake_minimum_required(VERSION 3.16) set(APPS_ROOT "${CMAKE_SOURCE_DIR}/apps") set(CARS_ROOT "${CMAKE_SOURCE_DIR}/cars") +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS ON) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS ON) + # Define the build type if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() +message("Build type: " ${CMAKE_BUILD_TYPE}) # ========== App setup ========== set(APP starter CACHE STRING "Application logic target") @@ -32,10 +40,6 @@ set(CMAKE_TOOLCHAIN_FILE ${CAR_DIR}/toolchain.cmake) message(STATUS "Building app: ${APP} for car: ${CAR}") message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) - project(rebel C CXX ASM) # ========== Directories ========== @@ -51,11 +55,23 @@ set(SRC ) # ========== Compiler and linker flags ========== -# Shared C flags (warnings, optimization) -add_compile_options(-Wall -Wextra -Os) +add_compile_options(-Wall -Wextra) + +# Centralized optimization/debug policy. +add_compile_options( + $<$,$>:-O0> + $<$,$>:-O0> + $<$,$>:-g3> + $<$,$>:-g3> + + $<$,$>:-Os> + $<$,$>:-Os> + $<$,$>:-g0> + $<$,$>:-g0> +) # ========== Build main executable ========== -add_executable(firmware.elf ${SRC}) +add_executable(firmware ${SRC}) # Libraries add_subdirectory(lib/toolkit) @@ -65,36 +81,36 @@ add_subdirectory(lib/app) add_subdirectory(${APP_DIR}) # Linker script -set_target_properties(firmware.elf PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) -set_target_properties(firmware.elf PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_PROJECT_NAME}.map) -target_link_libraries(firmware.elf PRIVATE car_${CAR} toolkit app_${APP}) -target_link_options(firmware.elf PRIVATE "-T${LINKER_SCRIPT}") +set_target_properties(firmware PROPERTIES LINK_DEPENDS ${LINKER_SCRIPT}) +set_target_properties(firmware PROPERTIES ADDITIONAL_CLEAN_FILES ${CMAKE_PROJECT_NAME}.map) +target_link_libraries(firmware PRIVATE car_${CAR} toolkit app_${APP}) +target_link_options(firmware PRIVATE "-T${LINKER_SCRIPT}") + +# ========== Print size ========== +add_custom_command(TARGET firmware POST_BUILD + COMMAND ${CMAKE_SIZE} firmware.elf + COMMENT "Firmware size" +) # ========== Generate HEX ========== -add_custom_command(TARGET firmware.elf POST_BUILD +add_custom_command(TARGET firmware POST_BUILD COMMAND ${CMAKE_OBJCOPY} -O ihex firmware.elf firmware.hex COMMENT "Generating firmware.hex from firmware.elf" ) +# ========== Clean target ========== +# Not needed: use `cmake --build . --target clean` + # ========== Generate emulation.resc ========== configure_file( ${CARS_ROOT}/emulation.resc.tpl ${CMAKE_BINARY_DIR}/emulation.resc ) -# ========== Clean target ========== -# Not needed: use `cmake --build . --target clean` - -# ========== Print size ========== -add_custom_command(TARGET firmware.elf POST_BUILD - COMMAND ${CMAKE_SIZE} firmware.elf - COMMENT "Firmware size" -) - # Add custom target to run Renode test add_custom_target(run_emulation - COMMAND renode ${CMAKE_BINARY_DIR}/emulation.resc - DEPENDS firmware.elf + COMMAND renode --console ${CMAKE_BINARY_DIR}/emulation.resc + DEPENDS firmware WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} COMMENT "Running Renode simulation for ${CAR}" ) diff --git a/rebel/cars/habilis/CMakeLists.txt b/rebel/cars/habilis/CMakeLists.txt index ba36cc5..7587492 100644 --- a/rebel/cars/habilis/CMakeLists.txt +++ b/rebel/cars/habilis/CMakeLists.txt @@ -1,52 +1,4 @@ -set(STM32CUBE_DIR "${CMAKE_SOURCE_DIR}/vendor/stm32cubef4") -# Only download if directory doesn't exist -if (NOT EXISTS "${STM32CUBE_DIR}") - message(STATUS "Downloading STM32CubeF4...") - execute_process( - COMMAND git clone --depth 1 --branch v1.28.3 --recurse-submodules - https://github.com/STMicroelectronics/STM32CubeF4.git - "${STM32CUBE_DIR}" - RESULT_VARIABLE GIT_RESULT - ) - if (NOT GIT_RESULT EQUAL "0") - message(FATAL_ERROR "Failed to clone STM32CubeF4") - endif () -endif () - -set(STM32_HAL_DIR "${STM32CUBE_DIR}/Drivers/STM32F4xx_HAL_Driver/Src") -file(GLOB STM32_HAL_SOURCES "${STM32_HAL_DIR}/stm32f4xx_hal*.c") -list(FILTER STM32_HAL_SOURCES EXCLUDE REGEX "_template\\.c$") - -set(CAR_HAL_DEFINES USE_HAL_DRIVER STM32F401xE) -set(CAR_HAL_INCLUDES - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${STM32CUBE_DIR}/Drivers/STM32F4xx_HAL_Driver/Inc - ${STM32CUBE_DIR}/Drivers/CMSIS/Include - ${STM32CUBE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Include -) -set(CAR_HAL_LOWLEVEL_SOURCES - ${STM32CUBE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c - src/isr.c - src/hal_msp.c - src/sysmem.c - src/syscalls.c - src/startup.s -) - -add_library(car_hal OBJECT) -target_compile_definitions(car_hal PRIVATE ${CAR_HAL_DEFINES}) -target_include_directories(car_hal PRIVATE ${CAR_HAL_INCLUDES}) -target_sources(car_hal PRIVATE - ${STM32_HAL_SOURCES} - ${CAR_HAL_LOWLEVEL_SOURCES} -) -target_compile_options(car_hal PRIVATE - -O0 - -mno-unaligned-access - # -fno-inline - -fno-tree-sra - -fno-ipa-sra -) +include(src/hal/CMakeLists.txt) add_library(car_habilis INTERFACE) target_compile_definitions(car_habilis INTERFACE ${CAR_HAL_DEFINES}) @@ -55,7 +7,8 @@ target_sources(car_habilis INTERFACE $ src/car/car_factory.cpp src/car/habilis.cpp - src/car/i2c.cpp + src/car/i2c_bus.cpp + src/car/i2c_device.cpp src/toolkit/store.cpp src/toolkit/usart_logger.cpp ) diff --git a/rebel/cars/habilis/README.md b/rebel/cars/habilis/README.md index 9829051..e908dd1 100644 --- a/rebel/cars/habilis/README.md +++ b/rebel/cars/habilis/README.md @@ -1,2 +1,2 @@ # Rebel - Habilis Car -Board is based on STM32F103C6 (Blue Pill) \ No newline at end of file +Board is based on STM32F401CE (Black Pill) \ No newline at end of file diff --git a/rebel/cars/habilis/board.resc b/rebel/cars/habilis/board.resc index 0f50bd6..f4e2ebb 100644 --- a/rebel/cars/habilis/board.resc +++ b/rebel/cars/habilis/board.resc @@ -1,7 +1,13 @@ machine LoadPlatformDescription @platforms/cpus/stm32f4.repl -machine LoadPlatformDescriptionFromString "motors: Mocks.DummyI2CSlave @ i2c1 0x40" +machine LoadPlatformDescriptionFromString "pca9685: Mocks.DummyI2CSlave @ i2c1 0x41" +machine LoadPlatformDescriptionFromString "ads1110: Mocks.DummyI2CSlave @ i2c1 0x48" +machine LoadPlatformDescriptionFromString "eeprom24c04a: Mocks.DummyI2CSlave @ i2c1 0x50" +include @cars/habilis/renode/peripherals.py + +mach set "habilis" sysbus LoadELF @build/habilis-starter/firmware.elf +python "setup_peripherals(monitor, 'pca9685', 'ads1110', 'eeprom24c04a')" -showAnalyzer usart1 \ No newline at end of file +showAnalyzer usart1 diff --git a/rebel/cars/habilis/include/rebel/habilis/car/habilis.hpp b/rebel/cars/habilis/include/rebel/habilis/car/habilis.hpp index 2fd1d95..e456dd1 100644 --- a/rebel/cars/habilis/include/rebel/habilis/car/habilis.hpp +++ b/rebel/cars/habilis/include/rebel/habilis/car/habilis.hpp @@ -7,7 +7,7 @@ #include #include -#include +#include #include namespace Rebel::Habilis::Car @@ -15,7 +15,7 @@ namespace Rebel::Habilis::Car class Habilis : public Rebel::Car::Car { public: - Habilis(Rebel::Habilis::Toolkit::Store& store, I2CBus& engine); + Habilis(Rebel::Habilis::Toolkit::Store& store, I2CBus& i2c); void Run() override; @@ -40,6 +40,10 @@ namespace Rebel::Habilis::Car private: Rebel::Habilis::Toolkit::Store& store; - I2CBus& engine; + I2CBus& i2c; + + protected: + void boot() const; + void scanDevices() const; }; } diff --git a/rebel/cars/habilis/include/rebel/habilis/car/i2c.hpp b/rebel/cars/habilis/include/rebel/habilis/car/i2c.hpp deleted file mode 100644 index 4e07d82..0000000 --- a/rebel/cars/habilis/include/rebel/habilis/car/i2c.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// -// Created by pouyan on 7/13/25. -// - -#pragma once - -#include -#include - -namespace Rebel::Habilis::Car -{ - class I2CBus - { - public: - explicit I2CBus(uint8_t address); - - I2CBus(I2C_TypeDef* instance, uint8_t address); - - uint32_t publish(const uint8_t* data, const size_t length) /* override */; - - // void subscribe(ReceiveCallback cb) override; - - // This would be called from the ISR or polling loop when data is received - // void on_data_received(const uint8_t* data) const; - private: - uint8_t address_; - - I2C_HandleTypeDef hi2c{}; - - void configure(I2C_TypeDef *instance); - }; -}; diff --git a/rebel/cars/habilis/include/rebel/habilis/car/i2c_bus.hpp b/rebel/cars/habilis/include/rebel/habilis/car/i2c_bus.hpp new file mode 100644 index 0000000..78351aa --- /dev/null +++ b/rebel/cars/habilis/include/rebel/habilis/car/i2c_bus.hpp @@ -0,0 +1,43 @@ +// +// Created by pouyan on 7/13/25. +// + +#pragma once + +#include +#include + +namespace Rebel::Habilis::Car +{ + class I2CDevice; + + class I2CBus + { + friend class I2CDevice; + + public: + explicit I2CBus(I2C_TypeDef* instance); + + I2CBus(const I2CBus&) = delete; + I2CBus& operator=(const I2CBus&) = delete; + I2CBus(I2CBus&&) = delete; + I2CBus& operator=(I2CBus&&) = delete; + + I2CDevice device(uint16_t address); + uint32_t error(); + + private: + I2C_HandleTypeDef hi2c; + + HAL_StatusTypeDef isDeviceReady(uint16_t address); + HAL_StatusTypeDef write(uint16_t address, const uint8_t* data, size_t length); + HAL_StatusTypeDef read(uint16_t address, uint8_t* data, size_t length); + + protected: + void configure(I2C_TypeDef *instance); + + void waitForReadiness(); + void recover(); + static uint16_t addressOnWire(uint16_t address); + }; +}; diff --git a/rebel/cars/habilis/include/rebel/habilis/car/i2c_device.hpp b/rebel/cars/habilis/include/rebel/habilis/car/i2c_device.hpp new file mode 100644 index 0000000..314bc60 --- /dev/null +++ b/rebel/cars/habilis/include/rebel/habilis/car/i2c_device.hpp @@ -0,0 +1,26 @@ +// +// Created by pouyan on 3/6/26. +// + +#pragma once + +#include + +namespace Rebel::Habilis::Car +{ + class I2CBus; + + class I2CDevice + { + public: + explicit I2CDevice(I2CBus* bus, uint16_t address); + + [[nodiscard]] HAL_StatusTypeDef isReady() const; + HAL_StatusTypeDef send(const uint8_t* data, size_t length) const; + HAL_StatusTypeDef receive(uint8_t* data, size_t length) const; + + private: + I2CBus* bus; + uint16_t address; + }; +}; diff --git a/rebel/cars/habilis/include/rebel/habilis/hal.h b/rebel/cars/habilis/include/rebel/habilis/hal.h index 5802b63..ae13e1d 100644 --- a/rebel/cars/habilis/include/rebel/habilis/hal.h +++ b/rebel/cars/habilis/include/rebel/habilis/hal.h @@ -2,8 +2,7 @@ // Created by pouyan on 10/11/25. // -#ifndef HAL_H -#define HAL_H +#pragma once #include @@ -23,5 +22,3 @@ Peripherals* GetPeripherals(); #ifdef __cplusplus } #endif - -#endif //HAL_H diff --git a/rebel/cars/habilis/include/rebel/habilis/toolkit/usart_logger.hpp b/rebel/cars/habilis/include/rebel/habilis/toolkit/usart_logger.hpp index 7882844..979b5d5 100644 --- a/rebel/cars/habilis/include/rebel/habilis/toolkit/usart_logger.hpp +++ b/rebel/cars/habilis/include/rebel/habilis/toolkit/usart_logger.hpp @@ -17,8 +17,9 @@ namespace Rebel::Habilis::Toolkit void Log(const char* message) override; private: - UART_HandleTypeDef huart{}; + UART_HandleTypeDef huart; + protected: void configure(USART_TypeDef* instance, uint32_t baudrate); }; } diff --git a/rebel/cars/habilis/include/stm32f4xx_hal_conf.h b/rebel/cars/habilis/include/stm32f4xx_hal_conf.h index 8e6d104..0aae8c2 100644 --- a/rebel/cars/habilis/include/stm32f4xx_hal_conf.h +++ b/rebel/cars/habilis/include/stm32f4xx_hal_conf.h @@ -35,7 +35,7 @@ */ #define HAL_MODULE_ENABLED - /* #define HAL_CRYP_MODULE_ENABLED */ +/* #define HAL_CRYP_MODULE_ENABLED */ /* #define HAL_ADC_MODULE_ENABLED */ /* #define HAL_CAN_MODULE_ENABLED */ /* #define HAL_CRC_MODULE_ENABLED */ diff --git a/rebel/cars/habilis/renode/peripherals.py b/rebel/cars/habilis/renode/peripherals.py new file mode 100644 index 0000000..d782eeb --- /dev/null +++ b/rebel/cars/habilis/renode/peripherals.py @@ -0,0 +1,195 @@ +""" +Simple behavioral I2C models for Renode Habilis board emulation. +Compatible with Renode's IronPython runtime. +""" + +import time +from System import Byte +from System import Array + +try: + _integer_types = (int, long) +except NameError: + _integer_types = (int,) + + +def _now_seconds(): + if hasattr(time, "monotonic"): + return time.monotonic() + return time.time() + + +def _to_bytes(data): + if data is None: + return [] + if isinstance(data, _integer_types): + return [int(data) & 0xFF] + try: + return [int(b) & 0xFF for b in data] + except Exception: + return [int(data) & 0xFF] + + +def _enqueue_bytes(dummy, data): + buf = [int(b) & 0xFF for b in data] + dummy.EnqueueResponseBytes(Array[Byte](buf)) + + +def _resolve_dummy(mon, path): + candidates = [path] + if not path.startswith("sysbus."): + candidates.append("sysbus.{0}".format(path)) + if not path.startswith("i2c1."): + candidates.append("i2c1.{0}".format(path)) + if not path.startswith("sysbus.i2c1."): + candidates.append("sysbus.i2c1.{0}".format(path)) + + if mon is not None and hasattr(mon, "Machine") and mon.Machine is not None: + for c in candidates: + try: + n = mon.Machine[c] + if n is not None: + return n + except Exception: + pass + + ext = globals().get("externals") + if ext is not None: + for c in candidates: + try: + n = ext[c] + if n is not None: + return n + except Exception: + pass + + return None + + +class PCA9685Model(object): + def __init__(self, dummy): + self.dummy = dummy + self.regs = [0] * 256 + self.regs[0x00] = 0x01 # MODE1 + self.regs[0x01] = 0x04 # MODE2 + self.ptr = 0 + + def _enqueue_from_ptr(self, count): + out = [] + p = self.ptr + for _ in range(count): + out.append(self.regs[p]) + p = (p + 1) & 0xFF + _enqueue_bytes(self.dummy, out) + + def write(self, data): + buf = _to_bytes(data) + if len(buf) == 0: + return + + self.ptr = buf[0] + for b in buf[1:]: + self.regs[self.ptr] = b + self.ptr = (self.ptr + 1) & 0xFF + + # Prime response FIFO for possible immediate read transaction. + self._enqueue_from_ptr(32) + + +class ADS1110Model(object): + def __init__(self, dummy): + self.dummy = dummy + self.config = 0x8C + self.sample = 0 + self.direction = 1 + self.last_update = _now_seconds() + self._refill(256) + + def _period_seconds(self): + dr = (self.config >> 2) & 0x3 + rates = [15.0, 30.0, 60.0, 240.0] + return 1.0 / rates[dr] + + def _advance(self): + now = _now_seconds() + elapsed = now - self.last_update + period = self._period_seconds() + if elapsed < period: + return + + steps = int(elapsed / period) + if steps < 1: + steps = 1 + + for _ in range(steps): + self.sample += self.direction * 73 + if self.sample >= 0x7FFF: + self.sample = 0x7FFF + self.direction = -1 + elif self.sample <= 0: + self.sample = 0 + self.direction = 1 + + self.last_update = now + + def _frame(self): + self._advance() + raw = self.sample & 0xFFFF + drdy = 1 if (_now_seconds() - self.last_update) <= self._period_seconds() else 0 + cfg = ((drdy & 0x1) << 7) | (self.config & 0x7F) + return [(raw >> 8) & 0xFF, raw & 0xFF, cfg] + + def _refill(self, frames): + out = [] + for _ in range(frames): + out.extend(self._frame()) + _enqueue_bytes(self.dummy, out) + + def write(self, data): + buf = _to_bytes(data) + if len(buf) > 0: + # ADS1110 writable config is low 7 bits. + self.config = buf[-1] & 0x7F + + # Top-up response FIFO after config writes. + self._refill(128) + + +class EEPROM24C04AModel(object): + def __init__(self, dummy): + self.dummy = dummy + self.mem = [0xFF] * 512 + self.ptr = 0 + + def _enqueue_from_ptr(self, count): + out = [] + p = self.ptr + for _ in range(count): + out.append(self.mem[p]) + p = (p + 1) % len(self.mem) + _enqueue_bytes(self.dummy, out) + + def write(self, data): + buf = _to_bytes(data) + if len(buf) == 0: + return + + self.ptr = buf[0] % len(self.mem) + for b in buf[1:]: + self.mem[self.ptr] = b + self.ptr = (self.ptr + 1) % len(self.mem) + + self._enqueue_from_ptr(64) + + +def setup_peripherals(_monitor, pca_path, ads_path, eep_path): + pca = _resolve_dummy(_monitor, pca_path) + ads = _resolve_dummy(_monitor, ads_path) + eep = _resolve_dummy(_monitor, eep_path) + + if pca is None or ads is None or eep is None: + raise RuntimeError("Cannot resolve one or more I2C dummy peripherals") + + pca.DataReceived += PCA9685Model(pca).write + ads.DataReceived += ADS1110Model(ads).write + eep.DataReceived += EEPROM24C04AModel(eep).write diff --git a/rebel/cars/habilis/schema/proteus/v0.pdsprj b/rebel/cars/habilis/schema/proteus/v0.pdsprj index 8b3146a..72417c3 100644 Binary files a/rebel/cars/habilis/schema/proteus/v0.pdsprj and b/rebel/cars/habilis/schema/proteus/v0.pdsprj differ diff --git a/rebel/cars/habilis/src/car/car_factory.cpp b/rebel/cars/habilis/src/car/car_factory.cpp index 8128ea3..7274afb 100644 --- a/rebel/cars/habilis/src/car/car_factory.cpp +++ b/rebel/cars/habilis/src/car/car_factory.cpp @@ -6,55 +6,59 @@ #include #include -#include +#include #include #include #include -namespace Rebel::Car -{ - void BoardInitOnce() - { +namespace Rebel::Car { + void initBoardOnce(const bool internalOsc) { static bool done = false; - if (done) - { + if (done) { return; } - HAL_Init(); - - __HAL_RCC_PWR_CLK_ENABLE(); - __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2); - RCC_OscInitTypeDef iosc = {}; - iosc.OscillatorType = RCC_OSCILLATORTYPE_HSI; - iosc.HSIState = RCC_HSI_ON; - iosc.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + RCC_ClkInitTypeDef iclk = {}; + uint32_t mco1Source = RCC_MCO1SOURCE_HSE; + + iosc.OscillatorType = RCC_OSCILLATORTYPE_HSE; + iosc.HSIState = RCC_HSE_ON; iosc.PLL.PLLState = RCC_PLL_NONE; - HAL_RCC_OscConfig(&iosc); - RCC_ClkInitTypeDef iclk = {}; iclk.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK - | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; - iclk.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; + | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2; + iclk.SYSCLKSource = RCC_SYSCLKSOURCE_HSE; iclk.AHBCLKDivider = RCC_SYSCLK_DIV1; iclk.APB1CLKDivider = RCC_HCLK_DIV1; iclk.APB2CLKDivider = RCC_HCLK_DIV1; - HAL_RCC_ClockConfig(&iclk, FLASH_LATENCY_0); - HAL_RCC_MCOConfig(RCC_MCO1, RCC_MCO1SOURCE_HSI, RCC_MCODIV_1); + if (internalOsc) { + iosc.OscillatorType = RCC_OSCILLATORTYPE_HSI; + iosc.HSIState = RCC_HSI_ON; + iosc.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; + iclk.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; + mco1Source = RCC_MCO1SOURCE_HSI; + } + + HAL_Init(); + HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE2); + HAL_RCC_OscConfig(&iosc); + HAL_RCC_ClockConfig(&iclk, FLASH_LATENCY_0); + HAL_RCC_MCOConfig(RCC_MCO1, mco1Source, RCC_MCODIV_1); SystemCoreClockUpdate(); + done = true; } - Car* CarFactory::Build() - { - BoardInitOnce(); + Car *CarFactory::Build() { + initBoardOnce(true); static Rebel::Habilis::Toolkit::UsartLogger logger{USART1, 115200}; static Rebel::Habilis::Toolkit::Store store{logger}; - static Rebel::Habilis::Car::I2CBus engine{I2C1, 0x41}; - static Rebel::Habilis::Car::Habilis car{store, engine}; + + static Rebel::Habilis::Car::I2CBus i2c1{I2C1}; + static Rebel::Habilis::Car::Habilis car{store, i2c1}; return &car; } diff --git a/rebel/cars/habilis/src/car/habilis.cpp b/rebel/cars/habilis/src/car/habilis.cpp index a9f94f4..b18e233 100644 --- a/rebel/cars/habilis/src/car/habilis.cpp +++ b/rebel/cars/habilis/src/car/habilis.cpp @@ -10,26 +10,45 @@ namespace Rebel::Habilis::Car { - Habilis::Habilis(Rebel::Habilis::Toolkit::Store& store, I2CBus& engine) : store(store), engine(engine) {} + Habilis::Habilis(Rebel::Habilis::Toolkit::Store& store, I2CBus& i2c) : store(store), i2c(i2c) {} void Habilis::Run() { - char log[32]; - this->store.GetLogger()->Log("Car is running\r\n"); + char log[64] = {}; + uint8_t cmd[10] = {}; - constexpr uint8_t cmd[] = {0x00, 0x20}; - this->engine.publish(cmd, sizeof(cmd)); + this->boot(); + + auto iPressure = this->i2c.device(0x48); + + auto status = iPressure.isReady(); + sprintf(log, "Probe sensor: status=%d, err=%lu\r\n", status, this->i2c.error()); + this->store.GetLogger()->Log(log); + HAL_Delay(400); + + cmd[0] = 0x8C; + status = iPressure.send(cmd, 1); + sprintf(log, "Configure sensor: status=%d, err=%lu\r\n", status, this->i2c.error()); + this->store.GetLogger()->Log(log); + HAL_Delay(400); float throttle = 0.1; + this->PushThrottle(throttle); + + uint8_t pressure[3] = {}; for (;;) { - this->PushThrottle(throttle); - HAL_Delay(5000); + status = iPressure.receive(pressure, 3); + sprintf(log, "Read sensor: value=%d, status=%d, err=%lu\r\n", (pressure[0] << 8) | pressure[1], status, this->i2c.error()); + this->store.GetLogger()->Log(log); throttle += 0.1; if (throttle > 1.0) { throttle = 0; } + + HAL_Delay(300); + this->PushThrottle(throttle); } } @@ -50,7 +69,7 @@ namespace Rebel::Habilis::Car { } - void Habilis::PushThrottle(float pressure) + void Habilis::PushThrottle(const float pressure) { char log[64]; sprintf(log, "Pushing throttle: %d%%\r\n", static_cast(pressure * 100)); @@ -61,13 +80,16 @@ namespace Rebel::Habilis::Car constexpr uint16_t on = 0; const auto off = static_cast(pressure * 4096); + const auto engine = this->i2c.device(0x41); uint8_t cmd[5]; cmd[0] = 0x06 + 4 * channel; cmd[1] = on & 0xFF; cmd[2] = on >> 8; cmd[3] = off & 0xFF; cmd[4] = off >> 8; - this->engine.publish(cmd, sizeof(cmd)); + auto status = engine.send(cmd, 5); + sprintf(log, "Write pwm: status=%d, err=%lu\r\n", status, this->i2c.error()); + this->store.GetLogger()->Log(log); } void Habilis::ReleaseThrottle() @@ -124,4 +146,35 @@ namespace Rebel::Habilis::Car return 0; } -} + void Habilis::boot() const + { + uint8_t cmd[10] = {}; + + this->store.GetLogger()->Log("Warming up the engine\r\n"); + HAL_Delay(500); + + // this->scanDevices(); + // this->i2c.device(0x01).isReady(); + + const auto engine = this->i2c.device(0x41); + cmd[0] = 0x00; + cmd[1] = 0x20; + engine.send(cmd, 2); + } + + void Habilis::scanDevices() const + { + char log[64]; + + sprintf(log, "Scanning for connected devices\r\n"); + this->store.GetLogger()->Log(log); + for (uint8_t addr = 0x08; addr < 0x78; addr++) + { + if (this->i2c.device(addr).isReady() == HAL_OK) + { + sprintf(log, "Found device at 0x%02X\r\n", addr); + this->store.GetLogger()->Log(log); + } + } + } +}; diff --git a/rebel/cars/habilis/src/car/i2c.cpp b/rebel/cars/habilis/src/car/i2c.cpp deleted file mode 100644 index b02844a..0000000 --- a/rebel/cars/habilis/src/car/i2c.cpp +++ /dev/null @@ -1,61 +0,0 @@ -// -// Created by pouyan on 7/13/25. -// - -#include - -namespace Rebel::Habilis::Car -{ - I2CBus::I2CBus(const uint8_t address) : address_(address) - { - } - - I2CBus::I2CBus(I2C_TypeDef* instance, const uint8_t address) : I2CBus(address) - { - this->configure(instance); - } - - uint32_t I2CBus::publish(const uint8_t* data, const size_t length) - { - if (HAL_I2C_GetState(&this->hi2c) == HAL_I2C_STATE_READY) - { - HAL_I2C_Master_Transmit(&this->hi2c, this->address_ << 1, const_cast(data), length, 100); - } - - return HAL_I2C_GetError(&this->hi2c); - } - - // void I2CBus::subscribe(const ReceiveCallback cb) - // { - // subscribers_.push_back(cb); - // } - - // void I2CBus::on_data_received(const uint8_t* data, const size_t length) const - // { - // for (auto& cb : subscribers_) - // { - // cb(data, length); - // } - // } - - void I2CBus::configure(I2C_TypeDef* instance) - { - this->hi2c.Instance = instance; - this->hi2c.Init.ClockSpeed = 100000; - this->hi2c.Init.DutyCycle = I2C_DUTYCYCLE_2; - this->hi2c.Init.OwnAddress1 = 0; - this->hi2c.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; - this->hi2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; - this->hi2c.Init.OwnAddress2 = 0; - this->hi2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; - this->hi2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; - if (HAL_I2C_Init(&this->hi2c) != HAL_OK) - { - // todo: log error - } - - auto peripherals = GetPeripherals(); - peripherals->hi2c = &this->hi2c; - SetPeripherals(peripherals); - } -} diff --git a/rebel/cars/habilis/src/car/i2c_bus.cpp b/rebel/cars/habilis/src/car/i2c_bus.cpp new file mode 100644 index 0000000..e3391db --- /dev/null +++ b/rebel/cars/habilis/src/car/i2c_bus.cpp @@ -0,0 +1,84 @@ +// +// Created by pouyan on 7/13/25. +// + +#include + +#include + +namespace Rebel::Habilis::Car { + I2CBus::I2CBus(I2C_TypeDef *instance) : hi2c{} { + this->configure(instance); + } + + I2CDevice I2CBus::device(const uint16_t address) { + return I2CDevice(this, address); + } + + HAL_StatusTypeDef I2CBus::isDeviceReady(const uint16_t address) { + this->waitForReadiness(); + + return HAL_I2C_IsDeviceReady(&this->hi2c, I2CBus::addressOnWire(address), 1, 1); + } + + HAL_StatusTypeDef I2CBus::write(const uint16_t address, const uint8_t *data, const size_t length) { + this->waitForReadiness(); + + const auto status = HAL_I2C_Master_Transmit(&this->hi2c, I2CBus::addressOnWire(address), + const_cast(data), + length, 100); + if (status != HAL_OK) { + this->recover(); + } + + return status; + } + + HAL_StatusTypeDef I2CBus::read(const uint16_t address, uint8_t *data, const size_t length) { + this->waitForReadiness(); + + const auto status = HAL_I2C_Master_Receive(&this->hi2c, I2CBus::addressOnWire(address), data, length, 100); + if (status != HAL_OK) { + this->recover(); + } + + return status; + } + + uint32_t I2CBus::error() { + return HAL_I2C_GetError(&this->hi2c); + } + + void I2CBus::configure(I2C_TypeDef *instance) { + this->hi2c.Instance = instance; + this->hi2c.Init.ClockSpeed = 100000; + this->hi2c.Init.DutyCycle = I2C_DUTYCYCLE_2; + this->hi2c.Init.OwnAddress1 = 0x00; + this->hi2c.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; + this->hi2c.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; + this->hi2c.Init.OwnAddress2 = 0x00; + this->hi2c.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; + this->hi2c.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; + if (HAL_I2C_Init(&this->hi2c) != HAL_OK) { + // todo: log error + } + + auto peripherals = GetPeripherals(); + peripherals->hi2c = &this->hi2c; + SetPeripherals(peripherals); + } + + void I2CBus::waitForReadiness() { + while (HAL_I2C_GetState(&this->hi2c) != HAL_I2C_STATE_READY) { + } + } + + void I2CBus::recover() { + // HAL_I2C_DeInit(&this->hi2c); + // this->configure(this->hi2c.Instance); + } + + uint16_t I2CBus::addressOnWire(const uint16_t address) { + return address << 1; + } +} diff --git a/rebel/cars/habilis/src/car/i2c_device.cpp b/rebel/cars/habilis/src/car/i2c_device.cpp new file mode 100644 index 0000000..6340a44 --- /dev/null +++ b/rebel/cars/habilis/src/car/i2c_device.cpp @@ -0,0 +1,26 @@ +// +// Created by pouyan on 3/6/26. +// + +#include + +namespace Rebel::Habilis::Car { + I2CDevice::I2CDevice(I2CBus* bus, const uint16_t address) : bus(bus), address(address) + { + } + + HAL_StatusTypeDef I2CDevice::isReady() const + { + return this->bus->isDeviceReady(this->address); + } + + HAL_StatusTypeDef I2CDevice::send(const uint8_t* data, const size_t length) const + { + return this->bus->write(this->address, data, length); + } + + HAL_StatusTypeDef I2CDevice::receive(uint8_t* data, const size_t length) const + { + return this->bus->read(this->address, data, length); + } +} \ No newline at end of file diff --git a/rebel/cars/habilis/src/car/sketch_avr.cpp b/rebel/cars/habilis/src/car/sketch_avr.cpp deleted file mode 100644 index bb7512e..0000000 --- a/rebel/cars/habilis/src/car/sketch_avr.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include - -#include -#include - -#include <../lib/car/include/rebel/car/car.hpp> -#include - -#define SPEED_CTRL_ADDR 0x40 -#define MOTOR_L_ADDR 0x06 -#define MOTOR_R_ADDR 0x0A - -void wakeUpPwmDriver(); -void lockOnSpeed(uint8_t motor, float ratio); - -struct pwm { - uint16_t on; - uint16_t off; - - void writeOnWire() const { - Wire.write(on & 0xFF); // Low Byte of ON - Wire.write((on >> 8) & 0xFF); // High Byte of ON - Wire.write(off & 0xFF); // Low Byte of OFF - Wire.write((off >> 8) & 0xFF); // High Byte of OFF - } -}; - -pwm speed(float ratio); - -void setup() { - Wire.begin(); - - Serial.begin(9600); - while (!Serial) {} - - wakeUpPwmDriver(); -} - -void loop() { - lockOnSpeed(MOTOR_L_ADDR, 0.75); - lockOnSpeed(MOTOR_R_ADDR, 0.25); - delay(20000); - - lockOnSpeed(MOTOR_L_ADDR, 0.1); - delay(5000); - - lockOnSpeed(MOTOR_L_ADDR, 0); - lockOnSpeed(MOTOR_R_ADDR, 0); - delay(10000); -} - -void wakeUpPwmDriver() { - Wire.beginTransmission(SPEED_CTRL_ADDR); - Wire.write(0x00); // Mode1 - Wire.write(0x20); // Mode2 - Wire.endTransmission(true); -} - -void lockOnSpeed(const uint8_t motor, const float ratio) { - Wire.beginTransmission(SPEED_CTRL_ADDR); - Wire.write(motor); // Which motor - speed(ratio).writeOnWire(); // Wire PWM details - Wire.endTransmission(true); -} - -pwm speed(float ratio) { - const pwm res = {0, static_cast(ratio * 4096)}; - - return res; -} diff --git a/rebel/cars/habilis/src/hal/CMakeLists.txt b/rebel/cars/habilis/src/hal/CMakeLists.txt new file mode 100644 index 0000000..0180eef --- /dev/null +++ b/rebel/cars/habilis/src/hal/CMakeLists.txt @@ -0,0 +1,68 @@ +set(STM32CUBE_DIR "${CMAKE_SOURCE_DIR}/vendor/stm32cubef4") +# Only download if directory doesn't exist. +if (NOT EXISTS "${STM32CUBE_DIR}") + message(STATUS "Downloading STM32CubeF4...") + execute_process( + COMMAND git clone --depth 1 --branch v1.28.3 --recurse-submodules + https://github.com/STMicroelectronics/STM32CubeF4.git + "${STM32CUBE_DIR}" + RESULT_VARIABLE GIT_RESULT + ) + if (NOT GIT_RESULT EQUAL "0") + message(FATAL_ERROR "Failed to clone STM32CubeF4") + endif () +endif () + +set(CAR_HAL_DEFINES + USE_HAL_DRIVER + STM32F401xE +) + +set(CAR_HAL_INCLUDES + ${CMAKE_CURRENT_LIST_DIR}/../../include + ${STM32CUBE_DIR}/Drivers/STM32F4xx_HAL_Driver/Inc + ${STM32CUBE_DIR}/Drivers/CMSIS/Include + ${STM32CUBE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Include +) + +set(STM32_HAL_DIR "${STM32CUBE_DIR}/Drivers/STM32F4xx_HAL_Driver/Src") +set(CAR_HAL_STM32_HAL_SOURCES + ${STM32CUBE_DIR}/Drivers/CMSIS/Device/ST/STM32F4xx/Source/Templates/system_stm32f4xx.c + ${STM32_HAL_DIR}/stm32f4xx_hal.c + ${STM32_HAL_DIR}/stm32f4xx_hal_cortex.c + ${STM32_HAL_DIR}/stm32f4xx_hal_dma.c + ${STM32_HAL_DIR}/stm32f4xx_hal_dma_ex.c + ${STM32_HAL_DIR}/stm32f4xx_hal_exti.c + ${STM32_HAL_DIR}/stm32f4xx_hal_flash.c + ${STM32_HAL_DIR}/stm32f4xx_hal_flash_ex.c + ${STM32_HAL_DIR}/stm32f4xx_hal_flash_ramfunc.c + ${STM32_HAL_DIR}/stm32f4xx_hal_gpio.c + ${STM32_HAL_DIR}/stm32f4xx_hal_i2c.c + ${STM32_HAL_DIR}/stm32f4xx_hal_i2c_ex.c + ${STM32_HAL_DIR}/stm32f4xx_hal_pwr.c + ${STM32_HAL_DIR}/stm32f4xx_hal_pwr_ex.c + ${STM32_HAL_DIR}/stm32f4xx_hal_rcc.c + ${STM32_HAL_DIR}/stm32f4xx_hal_rcc_ex.c + ${STM32_HAL_DIR}/stm32f4xx_hal_uart.c + ${STM32_HAL_DIR}/stm32f4xx_hal_usart.c +) + +set(CAR_HAL_LOWLEVEL_SOURCES + ${CMAKE_CURRENT_LIST_DIR}/isr.c + ${CMAKE_CURRENT_LIST_DIR}/hal_msp.c + ${CMAKE_CURRENT_LIST_DIR}/sysmem.c + ${CMAKE_CURRENT_LIST_DIR}/syscalls.c + ${CMAKE_CURRENT_LIST_DIR}/startup.s +) + +add_library(car_hal OBJECT) +target_compile_definitions(car_hal PRIVATE ${CAR_HAL_DEFINES}) +target_include_directories(car_hal PRIVATE ${CAR_HAL_INCLUDES}) +target_sources(car_hal PRIVATE + ${CAR_HAL_STM32_HAL_SOURCES} + ${CAR_HAL_LOWLEVEL_SOURCES} +) +target_link_libraries(car_hal PRIVATE m) + +set(CAR_HAL_DEFINES ${CAR_HAL_DEFINES} PARENT_SCOPE) +set(CAR_HAL_INCLUDES ${CAR_HAL_INCLUDES} PARENT_SCOPE) diff --git a/rebel/cars/habilis/src/hal_msp.c b/rebel/cars/habilis/src/hal/hal_msp.c similarity index 92% rename from rebel/cars/habilis/src/hal_msp.c rename to rebel/cars/habilis/src/hal/hal_msp.c index 1ac0a27..728f528 100644 --- a/rebel/cars/habilis/src/hal_msp.c +++ b/rebel/cars/habilis/src/hal/hal_msp.c @@ -28,14 +28,14 @@ void HAL_MspInit(void) __HAL_RCC_PWR_CLK_ENABLE(); /* System interrupt init*/ - HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(USART1_IRQn); + // HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); + // HAL_NVIC_EnableIRQ(USART1_IRQn); - HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); + // HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0); + // HAL_NVIC_EnableIRQ(I2C1_EV_IRQn); - HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0); - HAL_NVIC_EnableIRQ(I2C1_ER_IRQn); + // HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 0); + // HAL_NVIC_EnableIRQ(I2C1_ER_IRQn); } /** @@ -47,6 +47,10 @@ void HAL_MspInit(void) void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) { GPIO_InitTypeDef igpio = {0}; + igpio.Mode = GPIO_MODE_AF_OD; + igpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + igpio.Pull = GPIO_PULLUP; + if (hi2c->Instance == I2C1) { __HAL_RCC_GPIOB_CLK_ENABLE(); @@ -55,9 +59,6 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) PB7 ------> I2C1_SDA */ igpio.Pin = GPIO_PIN_6 | GPIO_PIN_7; - igpio.Mode = GPIO_MODE_AF_OD; - igpio.Pull = GPIO_PULLUP; - igpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; igpio.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &igpio); @@ -72,16 +73,10 @@ void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c) PB3 ------> I2C2_SDA */ igpio.Pin = GPIO_PIN_10; - igpio.Mode = GPIO_MODE_AF_OD; - igpio.Pull = GPIO_PULLUP; - igpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; igpio.Alternate = GPIO_AF4_I2C2; HAL_GPIO_Init(GPIOB, &igpio); igpio.Pin = GPIO_PIN_3; - igpio.Mode = GPIO_MODE_AF_OD; - igpio.Pull = GPIO_NOPULL; - igpio.Speed = GPIO_SPEED_FREQ_VERY_HIGH; igpio.Alternate = GPIO_AF9_I2C2; HAL_GPIO_Init(GPIOB, &igpio); @@ -307,4 +302,4 @@ void SetPeripherals(Peripherals *peripherals) Peripherals* GetPeripherals() { return connectedDevices; -} \ No newline at end of file +} diff --git a/rebel/cars/habilis/src/isr.c b/rebel/cars/habilis/src/hal/isr.c similarity index 100% rename from rebel/cars/habilis/src/isr.c rename to rebel/cars/habilis/src/hal/isr.c diff --git a/rebel/cars/habilis/src/startup.s b/rebel/cars/habilis/src/hal/startup.s similarity index 99% rename from rebel/cars/habilis/src/startup.s rename to rebel/cars/habilis/src/hal/startup.s index 222bf40..2d27d85 100644 --- a/rebel/cars/habilis/src/startup.s +++ b/rebel/cars/habilis/src/hal/startup.s @@ -26,7 +26,11 @@ .syntax unified .cpu cortex-m4 +#if defined(REBEL_FPU_HARD) || defined(REBEL_FPU_SOFTFP) .fpu fpv4-sp-d16 +#else + .fpu softvfp +#endif .thumb .global g_pfnVectors diff --git a/rebel/cars/habilis/src/syscalls.c b/rebel/cars/habilis/src/hal/syscalls.c similarity index 100% rename from rebel/cars/habilis/src/syscalls.c rename to rebel/cars/habilis/src/hal/syscalls.c diff --git a/rebel/cars/habilis/src/sysmem.c b/rebel/cars/habilis/src/hal/sysmem.c similarity index 100% rename from rebel/cars/habilis/src/sysmem.c rename to rebel/cars/habilis/src/hal/sysmem.c diff --git a/rebel/cars/habilis/src/system.c b/rebel/cars/habilis/src/hal/system.c similarity index 100% rename from rebel/cars/habilis/src/system.c rename to rebel/cars/habilis/src/hal/system.c diff --git a/rebel/cars/habilis/src/toolkit/usart_logger.cpp b/rebel/cars/habilis/src/toolkit/usart_logger.cpp index f56638b..729b35c 100644 --- a/rebel/cars/habilis/src/toolkit/usart_logger.cpp +++ b/rebel/cars/habilis/src/toolkit/usart_logger.cpp @@ -8,18 +8,17 @@ namespace Rebel::Habilis::Toolkit { - UsartLogger::UsartLogger(USART_TypeDef* instance, uint32_t baudrate) + UsartLogger::UsartLogger(USART_TypeDef* instance, uint32_t baudrate) : huart{} { this->configure(instance, baudrate); } void UsartLogger::Log(const char* message) { - if (HAL_UART_GetState(&this->huart) == HAL_UART_STATE_READY) - { - HAL_UART_Transmit(&this->huart, reinterpret_cast(const_cast(message)), + while (HAL_UART_GetState(&this->huart) != HAL_UART_STATE_READY) {} + + HAL_UART_Transmit(&this->huart, reinterpret_cast(const_cast(message)), std::strlen(message), 100); - } } void UsartLogger::configure(USART_TypeDef* instance, uint32_t baudrate) @@ -36,9 +35,5 @@ namespace Rebel::Habilis::Toolkit { // todo: log error } - - auto peripherals = GetPeripherals(); - peripherals->huart = &this->huart; - SetPeripherals(peripherals); } } diff --git a/rebel/cars/habilis/toolchain.cmake b/rebel/cars/habilis/toolchain.cmake index e866836..924db3d 100644 --- a/rebel/cars/habilis/toolchain.cmake +++ b/rebel/cars/habilis/toolchain.cmake @@ -4,28 +4,56 @@ set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR arm) +set(CMAKE_C_COMPILER_ID GNU) +set(CMAKE_CXX_COMPILER_ID GNU) + # Toolchain prefix set(CROSS_COMPILE arm-none-eabi-) # Compilers -set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc) -set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++) -set(CMAKE_ASM_COMPILER ${CROSS_COMPILE}gcc) -set(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy) -set(CMAKE_SIZE ${CROSS_COMPILE}size) +set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc) +set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++) +set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) +set(CMAKE_LINKER ${CROSS_COMPILE}g++) +set(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy) +set(CMAKE_SIZE ${CROSS_COMPILE}size) + +set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf") +set(CMAKE_EXECUTABLE_SUFFIX_C ".elf") +set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf") # Bypass default linker set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +# FPU mode selection. +# Allowed values: hard, softfp, soft +set(FPU_MODE "hard" CACHE STRING "Floating-point ABI mode for STM32F401") +set_property(CACHE FPU_MODE PROPERTY STRINGS hard softfp soft) + # Target CPU options -set(TARGET_CPU_FLAGS "-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16") +if(FPU_MODE STREQUAL "hard") + set(TARGET_FPU_FLAGS "-mfloat-abi=hard -mfpu=fpv4-sp-d16") + set(TARGET_ASM_FPU_DEFS "-DREBEL_FPU_HARD=1") +elseif(FPU_MODE STREQUAL "softfp") + set(TARGET_FPU_FLAGS "-mfloat-abi=softfp -mfpu=fpv4-sp-d16") + set(TARGET_ASM_FPU_DEFS "-DREBEL_FPU_SOFTFP=1") +elseif(FPU_MODE STREQUAL "soft") + set(TARGET_FPU_FLAGS "-mfloat-abi=soft") + set(TARGET_ASM_FPU_DEFS "-DREBEL_FPU_SOFT=1") +else() + message(FATAL_ERROR "Invalid FPU_MODE='${FPU_MODE}'. Use one of: hard, softfp, soft") +endif() + +set(TARGET_CPU_FLAGS "-mcpu=cortex-m4 ${TARGET_FPU_FLAGS}") +set(TARGET_COMPILE_FLAGS "-ffunction-sections -fdata-sections") # No standard libraries -set(CMAKE_C_FLAGS_INIT "${TARGET_CPU_FLAGS} -ffreestanding -fdata-sections -ffunction-sections") -set(CMAKE_CXX_FLAGS_INIT "${TARGET_CPU_FLAGS} -fno-rtti -fno-exceptions -fno-threadsafe-statics") -set(CMAKE_ASM_FLAGS_INIT "${TARGET_CPU_FLAGS} -x assembler-with-cpp -MMD -MP") +set(CMAKE_C_FLAGS_INIT "${TARGET_CPU_FLAGS} ${TARGET_COMPILE_FLAGS}") +set(CMAKE_CXX_FLAGS_INIT "${CMAKE_C_FLAGS_INIT} -fno-rtti -fno-exceptions -fno-threadsafe-statics") +set(CMAKE_ASM_FLAGS_INIT "${TARGET_CPU_FLAGS} ${TARGET_ASM_FPU_DEFS} -x assembler-with-cpp -MMD -MP") set(CMAKE_EXE_LINKER_FLAGS_INIT "${TARGET_CPU_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} --specs=nano.specs") -set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Wl,--gc-sections") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} -Wl,-Map=${CMAKE_PROJECT_NAME}.map") +set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} -Wl,--gc-sections") set(CMAKE_EXE_LINKER_FLAGS_INIT "${CMAKE_EXE_LINKER_FLAGS_INIT} -Wl,--print-memory-usage") diff --git a/rebel/lib/README b/rebel/lib/README deleted file mode 100644 index 2593a33..0000000 --- a/rebel/lib/README +++ /dev/null @@ -1,46 +0,0 @@ - -This directory is intended for project specific (private) libraries. -PlatformIO will compile them to static libraries and link into executable file. - -The source code of each library should be placed in an own separate directory -("lib/your_library_name/[here are source files]"). - -For example, see a structure of the following two libraries `Foo` and `Bar`: - -|--lib -| | -| |--Bar -| | |--docs -| | |--examples -| | |--src -| | |- Bar.c -| | |- Bar.h -| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html -| | -| |--Foo -| | |- Foo.c -| | |- Foo.h -| | -| |- README --> THIS FILE -| -|- platformio.ini -|--src - |- main.c - -and a contents of `src/main.c`: -``` -#include -#include - -int main (void) -{ - ... -} - -``` - -PlatformIO Library Dependency Finder will find automatically dependent -libraries scanning project source files. - -More information about PlatformIO Library Dependency Finder -- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/rebel/lib/README.md b/rebel/lib/README.md new file mode 100644 index 0000000..c2d9c1f --- /dev/null +++ b/rebel/lib/README.md @@ -0,0 +1,2 @@ +# Rebel - Library +Interfaces are declared here \ No newline at end of file diff --git a/rebel/lib/app/CMakeLists.txt b/rebel/lib/app/CMakeLists.txt index 9326110..88b1834 100644 --- a/rebel/lib/app/CMakeLists.txt +++ b/rebel/lib/app/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(app - src/main.cpp + src/virtual.cpp ) target_include_directories(app PUBLIC diff --git a/rebel/lib/app/src/main.cpp b/rebel/lib/app/src/main.cpp deleted file mode 100644 index 97eb91d..0000000 --- a/rebel/lib/app/src/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by pouyan on 7/21/25. -// - -#include - -namespace Rebel::App -{ - // __attribute__((weak)) void Main::Run() - // { - // } - // - // __attribute__((weak)) void OnThrottle(float pressure) - // { - // } - // - // __attribute__((weak)) void OnSteer(float angle) - // { - // } - // - // __attribute__((weak)) void OnBrake(float pressure) - // { - // } -} diff --git a/rebel/lib/app/src/virtual.cpp b/rebel/lib/app/src/virtual.cpp new file mode 100644 index 0000000..4167795 --- /dev/null +++ b/rebel/lib/app/src/virtual.cpp @@ -0,0 +1,3 @@ +// +// Created by pouyan on 7/21/25. +// diff --git a/rebel/lib/car/CMakeLists.txt b/rebel/lib/car/CMakeLists.txt index 55baf62..53933fa 100644 --- a/rebel/lib/car/CMakeLists.txt +++ b/rebel/lib/car/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(car - src/car_factory.cpp + src/virtual.cpp ) target_include_directories(car PUBLIC diff --git a/rebel/lib/car/src/car_factory.cpp b/rebel/lib/car/src/car_factory.cpp deleted file mode 100644 index b59001b..0000000 --- a/rebel/lib/car/src/car_factory.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by pouyan on 7/15/25. -// - -#include - -namespace Rebel::Car -{ - // __attribute__((weak)) Car* CarFactory::Build() - // { - // return nullptr; - // } -} diff --git a/rebel/lib/car/src/virtual.cpp b/rebel/lib/car/src/virtual.cpp new file mode 100644 index 0000000..5300822 --- /dev/null +++ b/rebel/lib/car/src/virtual.cpp @@ -0,0 +1,3 @@ +// +// Created by pouyan on 7/15/25. +// diff --git a/rebel/lib/toolkit/CMakeLists.txt b/rebel/lib/toolkit/CMakeLists.txt index 8c69dde..133f36b 100644 --- a/rebel/lib/toolkit/CMakeLists.txt +++ b/rebel/lib/toolkit/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(toolkit - src/toolkit.cpp + src/virtual.cpp ) target_include_directories(toolkit PUBLIC diff --git a/rebel/lib/toolkit/include/rebel/toolkit/bus.hpp b/rebel/lib/toolkit/include/rebel/toolkit/bus.hpp new file mode 100644 index 0000000..6a17872 --- /dev/null +++ b/rebel/lib/toolkit/include/rebel/toolkit/bus.hpp @@ -0,0 +1,21 @@ +// +// Created by pouyan on 3/6/26. +// + +#pragma once + +#include + +namespace Rebel::Toolkit +{ + template + class Bus + { + public: + virtual ~Bus() = default; + + virtual Status probe(Addr address) = 0; + virtual Status write(Addr address, const Word* data, size_t length) = 0; + virtual Status read(Addr address, Word* data, size_t length) = 0; + }; +}; \ No newline at end of file diff --git a/rebel/lib/toolkit/include/rebel/toolkit/logger.hpp b/rebel/lib/toolkit/include/rebel/toolkit/logger.hpp index 3160b17..f10458b 100644 --- a/rebel/lib/toolkit/include/rebel/toolkit/logger.hpp +++ b/rebel/lib/toolkit/include/rebel/toolkit/logger.hpp @@ -4,13 +4,13 @@ #pragma once -#include - namespace Rebel::Toolkit { class Logger { public: + virtual ~Logger() = default; + virtual void Log(const char* message) = 0; }; -} +}; diff --git a/rebel/lib/toolkit/include/rebel/toolkit/stdin.hpp b/rebel/lib/toolkit/include/rebel/toolkit/stdin.hpp new file mode 100644 index 0000000..c051e03 --- /dev/null +++ b/rebel/lib/toolkit/include/rebel/toolkit/stdin.hpp @@ -0,0 +1,16 @@ +// +// Created by pouyan on 3/6/26. +// + +#pragma once + +namespace Rebel::Toolkit +{ + class Stdin + { + public: + virtual ~Stdin() = default; + + virtual char getc() = 0; + }; +}; \ No newline at end of file diff --git a/rebel/lib/toolkit/include/rebel/toolkit/stdout.hpp b/rebel/lib/toolkit/include/rebel/toolkit/stdout.hpp new file mode 100644 index 0000000..4abbe96 --- /dev/null +++ b/rebel/lib/toolkit/include/rebel/toolkit/stdout.hpp @@ -0,0 +1,16 @@ +// +// Created by pouyan on 3/6/26. +// + +#pragma once + +namespace Rebel::Toolkit +{ + class Stdout + { + public: + virtual ~Stdout() = default; + + virtual void putc(char c) = 0; + }; +}; \ No newline at end of file diff --git a/rebel/lib/toolkit/include/rebel/toolkit/store.hpp b/rebel/lib/toolkit/include/rebel/toolkit/store.hpp index 118a029..babeb64 100644 --- a/rebel/lib/toolkit/include/rebel/toolkit/store.hpp +++ b/rebel/lib/toolkit/include/rebel/toolkit/store.hpp @@ -11,6 +11,8 @@ namespace Rebel::Toolkit class Store { public: + virtual ~Store() = default; + virtual Logger* GetLogger() = 0; }; -} +}; diff --git a/rebel/lib/toolkit/src/toolkit.cpp b/rebel/lib/toolkit/src/virtual.cpp similarity index 100% rename from rebel/lib/toolkit/src/toolkit.cpp rename to rebel/lib/toolkit/src/virtual.cpp