diff --git a/MMC5983MA_I2C/README.md b/MMC5983MA_I2C/README.md new file mode 100644 index 0000000..a3d7b0d --- /dev/null +++ b/MMC5983MA_I2C/README.md @@ -0,0 +1,71 @@ +# STM32 C++ Driver for MMC5983MA (I2C) + +### Description +This is a high-performance C++ driver for the MEMSIC MMC5983MA 3-axis magnetometer, designed for STM32 microcontrollers using the HAL library. + +**Key Feature:** This driver uses a **Stateless I2C Wrapper**, making it thread-safe. + +--- + +### Features +* **Stateless Architecture:** Wrapper stores no state, preventing race conditions in preemptive environments. +* **Standard I2C Support:** Communicates via the standard 2-wire I2C interface. +* **Core Functionality:** + * Read Product ID (Validation). + * Trigger Magnetic Field Measurements. + * Perform SET and RESET operations (De-gaussing). + * Read and scale 18-bit X, Y, Z magnetic data (in Gauss). + +--- + +### Project Structure +* `mmc5983ma_i2c.hpp` / `.cpp`: The main driver class. Handles register logic and data conversion. +* `i2c_wrapper.hpp` / `.cpp`: A lightweight abstraction layer for STM32 HAL I2C functions. +* `mmc5983ma_regs.hpp`: Register map and bit definitions. +* `main_read_test.cpp`: Example implementation. + +--- + +### Dependencies +* **STM32 HAL Library:** Requires `stm32f4xx_hal.h` (or equivalent for your F1/H7/L4 series). +* **I2C Handle:** You must initialize an `I2C_HandleTypeDef` (e.g., `hi2c1`) in your `main.c`. + +--- + +### How to Use + +1. **Include Headers:** + ```cpp + #include "i2c_wrapper.hpp" + #include "mmc5983ma_i2c.hpp" + ``` + +2. **Instantiate Objects:** + Since the driver is stateless, you create the wrapper and pass it by pointer to the driver, along with the device address. + ```cpp + // 1. Create Wrapper (Pass the HAL Handle) + I2C_Wrapper i2cBus(&hi2c1); + + // 2. Create Driver (Pass Wrapper Pointer + I2C Address) + // Standard Address: 0x30 (0110000 shifted left by 1 is 0x60, handled internally) + MMC5983MA mag(&i2cBus, 0x30); + ``` + +3. **Initialization & Reading:** + ```cpp + if (mag.begin()) { + // Init success + } + + // In your loop: + mag.triggerMeasurement(); + HAL_Delay(10); // Wait for conversion (~8ms) + + MagData data; + if (mag.readData(data)) { + // Use data.scaledX, data.scaledY, data.scaledZ + } + ``` + +### Address Note +The MMC5983MA 7-bit address is `0x30`. The driver automatically handles the left-shift required by the HAL library. \ No newline at end of file diff --git a/MMC5983MA_I2C/i2c_wrapper.cpp b/MMC5983MA_I2C/i2c_wrapper.cpp new file mode 100644 index 0000000..051c943 --- /dev/null +++ b/MMC5983MA_I2C/i2c_wrapper.cpp @@ -0,0 +1,22 @@ +/* + * i2c_wrapper.cpp + */ +#include "i2c_wrapper.hpp" + +I2C_Wrapper::I2C_Wrapper(I2C_HandleTypeDef* hi2c) : _hi2c(hi2c) { +} + +bool I2C_Wrapper::writeByte(std::uint8_t devAddr, std::uint8_t regAddr, std::uint8_t data) { + // HAL_I2C_Mem_Write handles Reg Addr + Data Sequence + return HAL_I2C_Mem_Write(_hi2c, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100) == HAL_OK; +} + +std::uint8_t I2C_Wrapper::readByte(std::uint8_t devAddr, std::uint8_t regAddr) { + std::uint8_t data = 0; + // HAL_I2C_Mem_Read handles Reg Addr + Data Sequence + return HAL_I2C_Mem_Read(_hi2c, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100); +} + +bool I2C_Wrapper::readBytes(std::uint8_t devAddr, std::uint8_t regAddr, std::uint8_t *data, std::uint8_t len) { + return HAL_I2C_Mem_Read(_hi2c, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, data, len, 100) == HAL_OK; +} \ No newline at end of file diff --git a/MMC5983MA_I2C/i2c_wrapper.hpp b/MMC5983MA_I2C/i2c_wrapper.hpp new file mode 100644 index 0000000..ed81c1b --- /dev/null +++ b/MMC5983MA_I2C/i2c_wrapper.hpp @@ -0,0 +1,53 @@ +/* + * i2c_wrapper.hpp + */ + +#ifndef I2C_WRAPPER_HPP +#define I2C_WRAPPER_HPP + +#include + +extern "C" { + #include "stm32f4xx_hal.h" +} + +class I2C_Wrapper { +public: + /** + * @brief Constructor. + * @param hi2c Pointer to the HAL I2C handle + */ + + I2C_Wrapper(I2C_HandleTypeDef* hi2c); + + /** + * @brief Writes a single byte to a specific register. + * @param devAddr The 8-bit device address (left-shifted) + * @param regAddr The register address to write to. + * @param data The byte to write. + * @return True if successful (HAL_OK), false otherwise. + */ + bool writeByte(std::uint8_t devAddr, std::uint8_t regAddr, std::uint8_t data); + + /** + * @brief Reads a single byte from a specific register. + * @param devAddr The 8-bit device address (left-shifted) + * @param regAddr The register address to read from. + * @return 1 if successful, 0 otherwise. + */ + std::uint8_t readByte(std::uint8_t devAddr, std::uint8_t regAddr); + + /** + * @brief Reads Multiple bytes from a specific register. + * @param devAddr The 8-bit device address (left-shifted) + * @param regAddr The register address to read from. + * @param data Pointer to the buffer to store read data. + * @param len Number of bytes to read. + * @return True if successful (HAL_OK), false otherwise. + */ + bool readBytes(std::uint8_t devAddr, std::uint8_t regAddr, std::uint8_t data[], std::uint8_t len); +private: + I2C_HandleTypeDef* _hi2c; +}; + +#endif // I2C_WRAPPER_HPP \ No newline at end of file diff --git a/MMC5983MA_I2C/main_read_test.cpp b/MMC5983MA_I2C/main_read_test.cpp new file mode 100644 index 0000000..d3bba7b --- /dev/null +++ b/MMC5983MA_I2C/main_read_test.cpp @@ -0,0 +1,85 @@ +/* + * main_read_test.cpp + * + * Test application for MMC5983MA I2C Driver. + */ + +extern "C" { + #include "main.h" // Contains I2C handle declaration (hi2c1) +} + +#include "i2c_wrapper.hpp" +#include "mmc5983ma_i2c.hpp" // Ensure this matches your actual header filename + +#include + +// ################################################################## +// ## Hardware Configuration ## +// ################################################################## + +// 1. Define your I2C Handle (e.g., hi2c1, hi2c2) +extern I2C_HandleTypeDef hi2c1; +#define MMC_I2C_HANDLE &hi2c1 + +// 2. Define Sensor Address +// Datasheet (Pg 9) specifies 7-bit address: 0110000 (0x30) +#define MMC_I2C_ADDR 0x30 + +// ################################################################## + +// 1. Create the I2C Wrapper instance +I2C_Wrapper i2cBus(MMC_I2C_HANDLE); + +// 2. Create the MMC5983MA Driver instance +// Pass pointer to wrapper and the I2C address +MMC5983MA magnetometer(&i2cBus, MMC_I2C_ADDR); + +// 3. Data container +MagData magData; + +/** + * @brief The application entry point. + */ +int main(void) +{ + // --- HAL Init (Usually done in main.c) --- + // HAL_Init(); + // SystemClock_Config(); + // MX_I2C1_Init(); + + printf("--- MMC5983MA I2C Test ---\r\n"); + + // 4. Initialize the sensor + if (magnetometer.begin()) { + printf("Sensor Initialized. Product ID Verified.\r\n"); + } else { + printf("Sensor Init Failed! Check wiring and pull-ups.\r\n"); + while (1) { + HAL_Delay(1000); // Trap on failure + } + } + + // --- Main Loop --- + while (1) + { + // 5. Trigger Measurement + magnetometer.triggerMeasurement(); + + // 6. Wait for measurement to complete + // Default bandwidth (100Hz) takes ~8ms + HAL_Delay(10); + + // 7. Read Data + if (magnetometer.readData(magData)) { + // Print scaled values + printf("X: %.4f G | Y: %.4f G | Z: %.4f G\r\n", + magData.scaledX, + magData.scaledY, + magData.scaledZ); + } else { + printf("Data not ready yet.\r\n"); + } + + HAL_Delay(500); // Read every 500ms + } +} \ No newline at end of file diff --git a/MMC5983MA_I2C/mmc5983ma_i2c.cpp b/MMC5983MA_I2C/mmc5983ma_i2c.cpp new file mode 100644 index 0000000..b53b9f6 --- /dev/null +++ b/MMC5983MA_I2C/mmc5983ma_i2c.cpp @@ -0,0 +1,100 @@ +/* + * mmc5983ma.cpp + * + * Implementation of the MMC5983MA driver. + */ + +#include "mmc5983ma_i2c.hpp" +#include "mmc5983ma_regs.hpp" +#include "i2c_wrapper.hpp" +#include + +using std::uint8_t; +using std::uint16_t; +using std::uint32_t; + + +// Constructor +MMC5983MA::MMC5983MA(I2C_Wrapper* i2cBus, uint8_t adress) : _i2c(i2cBus) { + _address = (adress << 1); // left shift for HAL compatibility +} + +bool MMC5983MA::begin(){ + uint8_t productID = getProductID(); + + return (productID == MMC5983MA_PRODUCT_ID_VALUE); +} + +uint8_t MMC5983MA::getProductID(){ + // (P ID at 0x2F) + return (readRegister(MMC5983MA_P_ID)); +} + + +void MMC5983MA::triggerMeasurement(){ + writeRegister(MMC5983MA_IT_CONTROL0, MMC5983MA_TM_M); +} + +void MMC5983MA::performSet(){ + writeRegister(MMC5983MA_IT_CONTROL0, MMC5983MA_SET); +} + + +void MMC5983MA::performReset(){ + writeRegister(MMC5983MA_IT_CONTROL0, MMC5983MA_RESET); +} + + + + + +bool MMC5983MA::readData(MagData& data) { + // check Status reg + uint8_t status = readRegister(MMC5983MA_STATUS); + if (!(status & MMC5983MA_MEAS_M_DONE)) { + return false; // Data not ready + } + + // Read 7 measurement regs at once. + uint8_t buffer[7]; + readRegisters(MMC5983MA_XOUT0, buffer, 7); + + // Combine bytes into raw 18-bit values + data.rawX = ((uint32_t)buffer[0] << 10) | + ((uint32_t)buffer[1] << 2) | + ((uint32_t)(buffer[6] & 0xC0) >> 6); + + data.rawY = ((uint32_t)buffer[2] << 10) | + ((uint32_t)buffer[3] << 2) | + ((uint32_t)(buffer[6] & 0x30) >> 4); + + data.rawZ = ((uint32_t)buffer[4] << 10) | + ((uint32_t)buffer[5] << 2) | + ((uint32_t)(buffer[6] & 0x0C) >> 2); + + + // Apply scaling factors (Gauss) + data.scaledX = ((float)data.rawX - _nullFieldOffset) / _countsPerGauss; + data.scaledY = ((float)data.rawY - _nullFieldOffset) / _countsPerGauss; + data.scaledZ = ((float)data.rawZ - _nullFieldOffset) / _countsPerGauss; + + return true; +} + + + +/* ------------- PRIVATE HELPERS ------------- */ + +void MMC5983MA::writeRegister(uint8_t reg, uint8_t value) { + _i2c->writeByte(_address, reg, value); +} + +uint8_t MMC5983MA::readRegister(uint8_t reg) { + return _i2c->readByte(_address, reg); +} + +void MMC5983MA::readRegisters(uint8_t reg, uint8_t* buffer, uint8_t len) { + _i2c->readBytes(_address, reg, buffer, len); +} + +/* MMC5983MA_CPP */ \ No newline at end of file diff --git a/MMC5983MA_I2C/mmc5983ma_i2c.hpp b/MMC5983MA_I2C/mmc5983ma_i2c.hpp new file mode 100644 index 0000000..fda9672 --- /dev/null +++ b/MMC5983MA_I2C/mmc5983ma_i2c.hpp @@ -0,0 +1,97 @@ +/* + * mmc5983ma.hpp + * + * C++ driver for the MMC5983MA magnetometer. + */ + +#ifndef MMC5983MA_HPP +#define MMC5983MA_HPP + +#include "mmc5983ma_regs.hpp" +#include "i2c_wrapper.hpp" +#include + +struct MagData { + std::uint32_t rawX; + std::uint32_t rawY; + std::uint32_t rawZ; + float scaledX; + float scaledY; + float scaledZ; +}; + +class MMC5983MA { +public: + /** + * @brief Constructor + * @param i2cBus Pointer to an initialized I2C wrapper instance. + * @param address The 7Bit I2C address of the sensor. + */ + MMC5983MA(I2C_Wrapper* i2cBus, std::uint8_t address); + + /** + * @brief Initializes the sensor. + * @return True on success (e.g., product ID matches), false otherwise. + */ + bool begin(); + + /** + * @brief Triggers a new MF measurement. + */ + void triggerMeasurement(); + + /** + * @brief Reads the latest magnetic field data from the sensor. + * @return True if data is ready and read, false otherwise. + */ + bool readData(MagData& data); + + /** + * @brief Performs a SET operation. + */ + void performSet(); + + /** + * @brief Performs a RESET operation. + */ + void performReset(); + + /** + * @brief Reads the product ID register. + * @return The 8-bit product ID, or 0 on failure. + */ + std::uint8_t getProductID(); + +private: + /** + * @brief Writes a single byte to a sensor register. + * @param reg The register address. + * @param value The 8-bit value to write. + */ + void writeRegister(std::uint8_t reg, std::uint8_t value); + + /** + * @brief Reads a single byte from a sensor register. + * @param reg The register address. + * @return The 8-bit value read. + */ + std::uint8_t readRegister(std::uint8_t reg); + + /** + * @brief Reads multiple bytes from the sensor starting at a register. + * @param reg The starting register address. + * @param buffer Pointer to the buffer to store read data. + * @param len Number of bytes to read. + */ + void readRegisters(std::uint8_t reg, std::uint8_t* buffer, std::uint8_t len); + + // Dependencies + I2C_Wrapper* _i2c; + std::uint8_t _address; + + // Constants for scaling data + const float _countsPerGauss = 16384.0f; + const float _nullFieldOffset = 131072.0f; +}; + +#endif // MMC5983MA_HPP \ No newline at end of file diff --git a/MMC5983MA_I2C/mmc5983ma_regs.hpp b/MMC5983MA_I2C/mmc5983ma_regs.hpp new file mode 100644 index 0000000..e3e7aa8 --- /dev/null +++ b/MMC5983MA_I2C/mmc5983ma_regs.hpp @@ -0,0 +1,86 @@ +/* + * mmc5983ma_regs.hpp + * WIP | Feat mmc5983ma_regs - Part 1 + * Register definitions and constants for the MMC5983MA magnetometer. + */ +#ifndef MMC5983MA_REGS_HPP +#define MMC5983MA_REGS_HPP + +// Register Map +#define MMC5983MA_XOUT0 0x00 // Xout0 Register addresses +#define MMC5983MA_XOUT1 0x01 // Xout1 Register addresses +#define MMC5983MA_YOUT0 0x02 // Yout0 Register addresses +#define MMC5983MA_YOUT1 0x03 // Yout1 Register addresses +#define MMC5983MA_ZOUT0 0x04 // Zout0 Register addresses +#define MMC5983MA_ZOUT1 0x05 // Zout1 Register addresses +#define MMC5983MA_XYZOUT 0x06 // XYZout Register addresses +#define MMC5983MA_TOUT 0x07 // Temperature Out Unsigned [ (-75~125C); LSB == -75 ]Register addresses +#define MMC5983MA_STATUS 0x08 // Status Register addresses +#define MMC5983MA_IT_CONTROL0 0x09 // Register addresses +#define MMC5983MA_IT_CONTROL1 0x0A // Register addresses +#define MMC5983MA_IT_CONTROL2 0x0B // Register addresses +#define MMC5983MA_IT_CONTROL3 0x0C // Register addresses +#define MMC5983MA_P_ID 0x2F // Register addresses + +// Bit Masks for Status Register (0x08) +#define MMC5983MA_MEAS_M_DONE (1 << 0) // Magnetic Field Data Ready Bit Mask +#define MMC5983MA_MEAS_T_DONE (1 << 1) // Temperature Data Ready Bit Mask +#define MMC5983MA_OTP_RD_DONE (1 << 4) // Memory Read Successful Bit Mask + +// Bit Masks for IT_Control0 Register (0x09) +#define MMC5983MA_TM_M (1 << 0) // MF Measurement Bit Mask +#define MMC5983MA_TM_T (1 << 1) // T Measurement Bit Mask +#define MMC5983MA_INT_MEAS_DONE_EN (1 << 2) // Interrupt Measurement Done Enable Bit Mask +#define MMC5983MA_SET (1 << 3) // Setting Operation Bit Mask +#define MMC5983MA_RESET (1 << 4) // Soft Reset Bit Mask +#define MMC5983MA_AUTO_SR_EN (1 << 5) // Auto Self-Reset Enable Bit Mask +#define MMC5983MA_OTP_READ (1 << 6) // One-Time Programmable Read Bit Mask + +// Bit Masks for IT_Control1 Register (0x0A) +// Bandwidth (BW[1:0]) settings: measurement time and data output rate +#define MMC5983MA_BW_100HZ (0x00) // 00: 8ms, 100Hz +#define MMC5983MA_BW_200HZ (0x01) // 01: 4ms, 200Hz +#define MMC5983MA_BW_400HZ (0x02) // 10: 2ms, 400Hz +#define MMC5983MA_BW_800HZ (0x03) // 11: 0.5ms, 800Hz +// Channel inhibits +#define MMC5983MA_X_INHIBIT (1 << 3) // Disable X channel +#define MMC5983MA_Y_INHIBIT ((1 << 4) | (1 << 5)) // Disable Y and Z channels +// Software Reset +#define MMC5983MA_SW_RST (1 << 7) // Software Reset + + +// Bit Masks for IT_Control2 Register (0x0B) +// Continuous Measurement Frequency (CM_Freq[2:0]) +#define MMC5983MA_CM_FREQ_OFF (0x00) // 000: Continuous Mode Off +#define MMC5983MA_CM_FREQ_1Z (0x01) // 001: 1Hz +#define MMC5983MA_CM_FREQ_10Z (0x02) // 010: 10Hz +#define MMC5983MA_CM_FREQ_20Z (0x03) // 011: 20Hz +#define MMC5983MA_CM_FREQ_50Z (0x04) // 100: 50Hz +#define MMC5983MA_CM_FREQ_100Z (0x05) // 101: 100Hz +#define MMC5983MA_CM_FREQ_200Z (0x06) // 110: 200Hz (BW=01) +#define MMC5983MA_CM_FREQ_1000Z (0x07) // 111: 1000Hz (BW=11) +// Continuous Measurement Enable +#define MMC5983MA_CMM_EN (1 << 3) // Enable Continuous Mode +// Periodic Set (Prd_set[2:0]) - shifted to bits [6:4] +#define MMC5983MA_PRD_SET_1 (0x00 << 4) // 000: Every 1 measurement +#define MMC5983MA_PRD_SET_25 (0x01 << 4) // 001: Every 25 measurements +#define MMC5983MA_PRD_SET_75 (0x02 << 4) // 010: Every 75 measurements +#define MMC5983MA_PRD_SET_100 (0x03 << 4) // 011: Every 100 measurements +#define MMC5983MA_PRD_SET_250 (0x04 << 4) // 100: Every 250 measurements +#define MMC5983MA_PRD_SET_500 (0x05 << 4) // 101: Every 500 measurements +#define MMC5983MA_PRD_SET_1000 (0x06 << 4) // 110: Every 1000 measurements +#define MMC5983MA_PRD_SET_2000 (0x07 << 4) // 111: Every 2000 measurements +// Periodic Set Enable +#define MMC5983MA_EN_PRD_SET (1 << 7) // Enable Periodic Set + + +// Bit Masks for IT_Control3 Register (0x0C) +#define MMC5983MA_ST_ENP (1 << 2) // Self-test positive current +#define MMC5983MA_ST_ENM (1 << 3) // Self-test negative current +#define MMC5983MA_SPI_3W (1 << 6) // Enable 3-wire SPI mode + + +// P_ID Register (0x2F) +#define MMC5983MA_PRODUCT_ID_VALUE 0x28 // Default value of Product ID reg + +#endif // MMC5983MA_REGS_HPP \ No newline at end of file diff --git a/NAU7802/ExampleUsage.cpp b/NAU7802/ExampleUsage.cpp new file mode 100644 index 0000000..498bd55 --- /dev/null +++ b/NAU7802/ExampleUsage.cpp @@ -0,0 +1,56 @@ +/* Includes ------------------------------------------------------------------*/ +#include "main.h" +#include "I2C_Wrapper.hpp" +#include "NAU7802.hpp" + +/* Private variables ---------------------------------------------------------*/ +extern I2C_HandleTypeDef hi2c1; + +/* Function Prototypes -------------------------------------------------------*/ +void SystemClock_Config(void); +static void MX_GPIO_Init(void); +static void MX_I2C1_Init(void); + +int main(void) +{ + /* MCU Configuration--------------------------------------------------------*/ + HAL_Init(); + SystemClock_Config(); + MX_GPIO_Init(); + MX_I2C1_Init(); + + /* --- SETUP STAGE --- */ + // 1. Create wrapper and config structs + I2C_Wrapper i2c_bus_1(&hi2c1); + NAU7802_PARAMS adc_config; + NAU7802_OUT adc_data; + + // 3. Configure parameters + adc_config.initialGain = NAU7802_GAIN_128X; + + // 4. Create the driver object + NAU7802 adc(adc_config, i2c_bus_1); + // 5. Check if initialization was successful + if (!adc.get_isInitialized()) + { + // ADC not found or failed to configure + Error_Handler(); + } + + /* --- MAIN LOOP --- */ + while (1) + { + // 7. Check if data is ready + if (adc.isReady()) + { + // 8. Read data + if (adc.readSensor(&adc_data) == HAL_OK) + { + // Successfully read data + //Example Debug Print: + // printf("Raw ADC Value: %ld\r\n", adc_data.raw_reading); + } + } + HAL_Delay(100); + } +} \ No newline at end of file diff --git a/NAU7802/I2C_Wrapper.cpp b/NAU7802/I2C_Wrapper.cpp new file mode 100644 index 0000000..a9715f9 --- /dev/null +++ b/NAU7802/I2C_Wrapper.cpp @@ -0,0 +1,49 @@ +/* + * i2c_wrapper.cpp + */ + +#include "I2C_Wrapper.hpp" + +/** + * @brief Writes data to a register on the I2C device. + * @note Uses HAL_I2C_Mem_Write + */ +uint8_t I2C_Wrapper::writeReg() { + if (numBytes != 1) { + return HAL_ERROR; + } + + HAL_StatusTypeDef status = HAL_I2C_Mem_Write( + hi2c, + deviceAddress, + registerAddress, + I2C_MEMADD_SIZE_8BIT, + sendData.data(), + numBytes, + 100 // timeout + ); + + return (uint8_t)status; +} + +/** + * @brief Reads data from a register on the I2C device. + * @note Uses HAL_I2C_Mem_Read + */ +uint8_t I2C_Wrapper::readReg() { + HAL_StatusTypeDef status = HAL_I2C_Mem_Read( + hi2c, + deviceAddress, + registerAddress, + I2C_MEMADD_SIZE_8BIT, + receiveData.data(), + numBytes, + 100 // timeout + ); + + return (uint8_t)status; +} + +void I2C_Wrapper::updDeviceAddr(uint8_t newAddress) { + deviceAddress = newAddress; +} \ No newline at end of file diff --git a/NAU7802/I2C_Wrapper.hpp b/NAU7802/I2C_Wrapper.hpp new file mode 100644 index 0000000..27316a2 --- /dev/null +++ b/NAU7802/I2C_Wrapper.hpp @@ -0,0 +1,38 @@ +/* + * i2c_wrapper.hpp + */ + +#ifndef I2C_WRAPPER_HPP_ +#define I2C_WRAPPER_HPP_ + +#include + +extern "C" { + #include "stm32f4xx_hal.h" +} + +#define COMM_ERROR 5 + +class I2C_Wrapper { +public: + // Constructor + I2C_Wrapper(I2C_HandleTypeDef* i2c) + : hi2c(i2c), deviceAddress(0) {} + + // Variables + uint8_t registerAddress; + uint8_t numBytes; + std::array sendData; + std::array receiveData; + + // Functions + uint8_t writeReg(); + uint8_t readReg(); + void updDeviceAddr(uint8_t newAddress); + +private: + I2C_HandleTypeDef* hi2c; + uint8_t deviceAddress; +}; + +#endif /* I2C_WRAPPER_HPP_ */ \ No newline at end of file diff --git a/NAU7802/NAU7802.cpp b/NAU7802/NAU7802.cpp new file mode 100644 index 0000000..bd78f5c --- /dev/null +++ b/NAU7802/NAU7802.cpp @@ -0,0 +1,131 @@ +/* + * nau7802.cpp + * + * Implementation of the NAU7802 driver. + */ + +#include "NAU7802.hpp" +#include "stm32f4xx_hal.h" // Only needed for HAL_Delay and HAL_GetTick + +// Begin NAU7802 Startup Sequence +NAU7802::NAU7802(NAU7802_PARAMS configs, I2C_Wrapper& i2c_pointer) + : i2c(i2c_pointer), parameters(configs) +{ + isInitialized = false; + i2c.updDeviceAddr(NAU7802_I2C_ADDRESS); + + // 1. Send a reset command + if (!reset()) { + return; // Failed to reset + } + HAL_Delay(10); + + // 2. Power up the analog and digital sections + i2c.registerAddress = NAU7802_REG_PU_CTRL; + i2c.sendData[0] = NAU7802_PU_CTRL_PUD | NAU7802_PU_CTRL_PUA; + i2c.numBytes = 1; + if (i2c.writeReg() != HAL_OK) { + return; // Failed to write + } + + // 3. Wait for the Power Up Ready bit + uint32_t startTime = HAL_GetTick(); + while (HAL_GetTick() - startTime < 100) { // 100ms timeout + i2c.registerAddress = NAU7802_REG_PU_CTRL; + i2c.numBytes = 1; + if (i2c.readReg() != HAL_OK) { + return; // Failed to read + } + + if ((i2c.receiveData[0] & NAU7802_PU_CTRL_PUR) != 0) { + // Ready! Now set the initial gain. + if (!setGain(parameters.initialGain)) { + return; // Failed to set gain + } + + isInitialized = true; // Success! + return; + } + HAL_Delay(1); + } + + // Timeout occurred +} + +bool NAU7802::isReady() { + i2c.registerAddress = NAU7802_REG_PU_CTRL; + i2c.numBytes = 1; + if (i2c.readReg() != HAL_OK) { + return false; + } + return (i2c.receiveData[0] & NAU7802_PU_CTRL_CR) != 0; +} + +uint8_t NAU7802::readSensor(NAU7802_OUT *dest) { + i2c.registerAddress = NAU7802_REG_ADC_B2; // Start reading from MSB + i2c.numBytes = 3; // Read 3 bytes + uint8_t status = i2c.readReg(); + + if (status != HAL_OK) { + dest->raw_reading = 0; + return status; + } + + // Combine the three bytes from the wrapper's receiveData + int32_t value = ((int32_t)i2c.receiveData[0] << 16) | \ + ((int32_t)i2c.receiveData[1] << 8) | \ + (i2c.receiveData[2]); + + // Sign-extend the 24-bit value to a 32-bit integer + if (value & 0x00800000) { + value |= 0xFF000000; + } + + dest->raw_reading = value; + return status; +} + +bool NAU7802::reset() { + i2c.registerAddress = NAU7802_REG_PU_CTRL; + i2c.sendData[0] = NAU7802_PU_CTRL_RR; + i2c.numBytes = 1; + if (i2c.writeReg() != HAL_OK) return false; + + HAL_Delay(1); + + i2c.sendData[0] = 0x00; // Clear reset bit + if (i2c.writeReg() != HAL_OK) return false; + + return true; +} + +bool NAU7802::setGain(uint8_t gain) { + if (gain > NAU7802_GAIN_128X) { + return false; // Invalid gain setting + } + + // Read the current value of CTRL1 + i2c.registerAddress = NAU7802_REG_CTRL1; + i2c.numBytes = 1; + if (i2c.readReg() != HAL_OK) { + return false; + } + + uint8_t ctrl1_value = i2c.receiveData[0]; + ctrl1_value &= 0b11111000; // Clear the gain bits (bits 0, 1, 2) + ctrl1_value |= gain; // Set the new gain bits + + // Write the modified value back + i2c.registerAddress = NAU7802_REG_CTRL1; + i2c.sendData[0] = ctrl1_value; + i2c.numBytes = 1; + if (i2c.writeReg() != HAL_OK) { + return false; + } + + return true; +} + +bool NAU7802::get_isInitialized(void) { + return isInitialized; +} \ No newline at end of file diff --git a/NAU7802/NAU7802.hpp b/NAU7802/NAU7802.hpp new file mode 100644 index 0000000..2d4e9f0 --- /dev/null +++ b/NAU7802/NAU7802.hpp @@ -0,0 +1,58 @@ +/* + * nau7802.hpp + * + * Driver for the NAU7802 24-bit ADC. + */ +#ifndef NAU7802_HPP +#define NAU7802_HPP + +#include +#include "I2C_Wrapper.hpp" +#include "NAU7802_regs.hpp" + +/** + * @brief Configuration parameters for the NAU7802 driver. + */ +typedef struct NAU7802_DRIVER_PARAMETER { + uint8_t initialGain; // Use one of the NAU7802_GAIN_xxx macros +} NAU7802_PARAMS; + +/** + * @brief Output data structure for the NAU7802. + */ +typedef struct NAU7802_OUTPUT { + int32_t raw_reading; +} NAU7802_OUT; + + +class NAU7802 { +public: + /** + * @brief Constructs the NAU7802 Driver. + * @param configs Configuration settings for the sensor. + * @param i2c_pointer I2C Wrapper for communication. + */ + NAU7802(NAU7802_PARAMS configs, I2C_Wrapper& i2c_pointer); + + // Check if conversion is ready + bool isReady(); + + // Read 24-bit signed ADC value + uint8_t readSensor(NAU7802_OUT *dest); + + // Software reset + bool reset(); + + // Set PGA gain + bool setGain(uint8_t gain); + + // Check if the driver is initialized + bool get_isInitialized(void); + +private: + I2C_Wrapper& i2c; // Reference to the I2C wrapper + bool isInitialized; + NAU7802_PARAMS parameters; // Store the configs +}; + +#endif // NAU7802_HPP \ No newline at end of file diff --git a/NAU7802/NAU7802_regs.hpp b/NAU7802/NAU7802_regs.hpp new file mode 100644 index 0000000..d392847 --- /dev/null +++ b/NAU7802/NAU7802_regs.hpp @@ -0,0 +1,40 @@ +/* + * nau7802_regs.hpp + * + * Register definitions and constants for the NAU7802 ADC. + */ +#ifndef NAU7802_REGS_HPP +#define NAU7802_REGS_HPP + +// Fixed 7-bit I2C address of the NAU7802. [left-shifted for HAL functions] +#define NAU7802_I2C_ADDRESS (0x2A << 1) + +// Register Map +#define NAU7802_REG_PU_CTRL 0x00 +#define NAU7802_REG_CTRL1 0x01 +#define NAU7802_REG_CTRL2 0x02 +#define NAU7802_REG_ADC_B2 0x12 // ADC Result MSB +#define NAU7802_REG_ADC_B1 0x13 // ADC Result Mid-byte +#define NAU7802_REG_ADC_B0 0x14 // ADC Result LSB +#define NAU7802_REG_REVISION_ID 0x1F + +// Gain [in] settings for register +#define NAU7802_GAIN_1X 0b000 +#define NAU7802_GAIN_2X 0b001 +#define NAU7802_GAIN_4X 0b010 +#define NAU7802_GAIN_8X 0b011 +#define NAU7802_GAIN_16X 0b100 +#define NAU7802_GAIN_32X 0b101 +#define NAU7802_GAIN_64X 0b110 +#define NAU7802_GAIN_128X 0b111 + +// PU_CTRL Register Bits +#define NAU7802_PU_CTRL_RR (1 << 0) // Register Reset +#define NAU7802_PU_CTRL_PUD (1 << 1) // Power Up Digital +#define NAU7802_PU_CTRL_PUA (1 << 2) // Power Up Analog +#define NAU7802_PU_CTRL_PUR (1 << 3) // Power Up Ready (Read Only) +#define NAU7802_PU_CTRL_CR (1 << 5) // Cycle Ready (Read Only) +#define NAU7802_PU_CTRL_OSCS (1 << 6) // Select clock source +#define NAU7802_PU_CTRL_AVDDS (1 << 7) // Select internal LDO + +#endif // NAU7802_REGS_HPP \ No newline at end of file diff --git a/NAU7802/README.md b/NAU7802/README.md new file mode 100644 index 0000000..25ab6a4 --- /dev/null +++ b/NAU7802/README.md @@ -0,0 +1,90 @@ + +# STM32 C++ Driver for NAU7802 + +This is a simple, lightweight C++ driver for the Nuvoton NAU7802 24-bit ADC, designed to be used with the STM32 HAL library in a C++ environment (for example, STM32CubeIDE). + +### Features + +- Handles sensor initialization and power-up sequence +- Software reset +- Sets PGA gain (from 1x to 128x) +- Checks if new conversion data is ready +- Reads the 24-bit signed ADC result + +### Project Structure + +The driver is split into logical components to make it easy to understand and port: + +- `I2C_Wrapper.hpp` / `.cpp` + - A small C++ class that wraps the C-style STM32 HAL I2C functions (`HAL_I2C_Mem_Write` / `HAL_I2C_Mem_Read`). + - Acts as the low-level communication handler for register reads/writes. +- `NAU7802_regs.hpp` + - Register addresses, bit masks, and gain values for the NAU7802 (datasheet-based definitions). +- `NAU7802.hpp` / `.cpp` + - The main driver with logic for communicating with the sensor. + - Defines `NAU7802_PARAMS` for configuration and `NAU7802_OUT` for output data. + - Uses `I2C_Wrapper` to perform all register operations. +- `main_read_test.cpp` / `ExampleUsage.cpp` + - Example `main()` files demonstrating driver initialization and reading. + +### How to use + +Follow these steps in your project (assumes HAL and `hi2c1` are available): + +1. Include the headers in your `main.cpp` or equivalent: + +```c +#include "main.h" +#include "I2C_Wrapper.hpp" +#include "NAU7802.hpp" + +// Ensure hi2c1 is declared (defined in main.c) +extern I2C_HandleTypeDef hi2c1; +``` + +2. Initialize the driver (after `MX_I2C1_Init()`): + +```c +// Create the I2C wrapper and config/data structs +I2C_Wrapper i2c_bus_1(&hi2c1); +NAU7802_PARAMS adc_config; +NAU7802_OUT adc_data; + +// Configure parameters +adc_config.initialGain = NAU7802_GAIN_1X; // choose desired gain + +// Create the driver object (constructor will initialize the sensor) +NAU7802 adc(adc_config, i2c_bus_1); + +// Check initialization +if (!adc.get_isInitialized()) { + Error_Handler(); +} +``` + +3. Read data in your main loop: + +```c +while (1) { + if (adc.isReady()) { + if (adc.readSensor(&adc_data) == HAL_OK) { + // Use adc_data.raw_reading (32-bit signed) + // e.g. printf("Raw ADC: %ld\r\n", adc_data.raw_reading); + } + } + HAL_Delay(100); // 100 ms is reasonable for default sample rate +} +``` + +### Complete example + +See `main_read_test.cpp` or `ExampleUsage.cpp` in this driver folder for a complete example that initializes the sensor at 1x gain and continuously reads raw ADC values. + +### Dependencies + +- STM32 HAL (driver uses HAL I2C functions) +- C++11 or newer (used for small utilities like `std::array` in `I2C_Wrapper`) + +### Datasheet + +For full register details and calibration notes, consult the NAU7802 datasheet (e.g. en-us--DS_NAU7802_DataSheet_EN_Rev2.6.pdf). \ No newline at end of file diff --git a/NAU7802/en-us--DS_NAU7802_DataSheet_EN_Rev2.6.pdf b/NAU7802/en-us--DS_NAU7802_DataSheet_EN_Rev2.6.pdf new file mode 100644 index 0000000..8a8a37a Binary files /dev/null and b/NAU7802/en-us--DS_NAU7802_DataSheet_EN_Rev2.6.pdf differ diff --git a/NAU7802/main_read_test.cpp b/NAU7802/main_read_test.cpp new file mode 100644 index 0000000..a6d9ac8 --- /dev/null +++ b/NAU7802/main_read_test.cpp @@ -0,0 +1,73 @@ +/* + * main_nau7802_test.cpp + * + * Example usage file for the refactored NAU7802 driver. + * This example initializes the sensor with 1x gain + * and continuously reads the raw ADC value. + */ + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" +#include "I2C_Wrapper.hpp" +#include "NAU7802.hpp" +#include // For printf debugging + +/* Private variables ---------------------------------------------------------*/ +// Assume hi2c1 is generated by STM32CubeIDE and initialized in main() +extern I2C_HandleTypeDef hi2c1; // Declaration of hi2c1 + +/* Function Prototypes -------------------------------------------------------*/ +// These are assumed to be in main.h or defined in main.c +void SystemClock_Config(void); +static void MX_GPIO_Init(void); +static void MX_I2C1_Init(void); +extern void Error_Handler(void); // Assuming this is defined in main.c + +/** + * @brief The application entry point. + */ +int main(void) +{ + /* MCU Configuration--------------------------------------------------------*/ + HAL_Init(); + SystemClock_Config(); + MX_GPIO_Init(); + MX_I2C1_Init(); // This initializes hi2c1 + + /* --- SETUP STAGE --- */ + // 1. create wrapper and config structs + I2C_Wrapper i2c_bus_1(&hi2c1); + NAU7802_PARAMS adc_config; + NAU7802_OUT adc_data; + // 3. Configure parameters + adc_config.initialGain = NAU7802_GAIN_1X; // Set to 1x gain as requested + + // 4. Create the driver object + NAU7802 adc(adc_config, i2c_bus_1); + + // 5. Check if initialization was successful + if (!adc.get_isInitialized()) + { + // ADC not found or failed to configure + // printf("NAU7802 Initialization Failed!\r\n"); + Error_Handler(); + } + + // printf("NAU7802 Initialized. Gain set to 1x.\r\n"); + + /* --- MAIN LOOP --- */ + while (1) + { + // 7. Check if data is ready (Step 6 is creating the output struct, done globally) + if (adc.isReady()) + { + // 8. Read data + if (adc.readSensor(&adc_data) == HAL_OK) + { + // You UART debug print here: + // printf("Raw ADC Value (Gain 1x): %ld\r\n", adc_data.raw_reading); + } + } + HAL_Delay(100); // Wait 100ms before checking again + } +} \ No newline at end of file