Skip to main content

Platform Porting

All hardware-specific code is isolated in Src/npz_hal.c. Reference implementations are currently available for the STM32L0, Nordic Semiconductor nRF and Microchip PIC32 and PIC16 series. To port the driver to other MCU families, implement the three functions below using your platform's I2C API.

HAL API

npz_hal_init()

Must configure and enable the MCU I2C peripheral so it is ready to communicate with the nPZero at address 0x7A (= 0x3D shifted left by 1) at ≤ 100 kbit/s.

Returns OK on success, ERR on failure.

npz_hal_read()

Must perform a blocking I2C register read:

  1. Write slave_register to the device to set the read pointer.
  2. Read size bytes from the device into pData.
npz_status_e npz_hal_read(uint8_t slave_address, uint8_t slave_register,
uint8_t *pData, uint16_t size, uint32_t timeout);

npz_hal_write()

Must perform a blocking I2C write. pData[0] is the register address; subsequent bytes are data.

npz_status_e npz_hal_write(uint8_t slave_address, uint8_t *pData,
uint16_t size, uint32_t timeout);

STM32 (Reference Implementation)

The shipped npz_hal.c uses HAL_I2C_Mem_Read and HAL_I2C_Master_Transmit on an I2C1 instance configured for 7-bit addressing at standard speed.

// Excerpt — actual file targets STM32L0xx
npz_status_e npz_hal_read(uint8_t slave_address, uint8_t slave_register,
uint8_t *pData, uint16_t size, uint32_t timeout) {
HAL_I2C_Mem_Read(&m_hi2c1, slave_address, slave_register, 1, pData, size, timeout);
return OK;
}

npz_status_e npz_hal_write(uint8_t slave_address, uint8_t *pData,
uint16_t size, uint32_t timeout) {
if (HAL_I2C_Master_Transmit(&m_hi2c1, slave_address, pData, size, timeout) != HAL_OK)
return ERR;
return OK;
}

nRF52 / Zephyr

#include <zephyr/drivers/i2c.h>

static const struct device *i2c_dev;
#define NPZ_I2C_ADDR 0x3D

npz_status_e npz_hal_init(void) {
i2c_dev = DEVICE_DT_GET(DT_NODELABEL(i2c0));
return device_is_ready(i2c_dev) ? OK : ERR;
}

npz_status_e npz_hal_read(uint8_t slave_address, uint8_t slave_register,
uint8_t *pData, uint16_t size, uint32_t timeout) {
return i2c_write_read(i2c_dev, NPZ_I2C_ADDR,
&slave_register, 1, pData, size) == 0 ? OK : ERR;
}

npz_status_e npz_hal_write(uint8_t slave_address, uint8_t *pData,
uint16_t size, uint32_t timeout) {
return i2c_write(i2c_dev, pData, size, NPZ_I2C_ADDR) == 0 ? OK : ERR;
}

ESP32 (ESP-IDF)

#include "driver/i2c.h"

#define I2C_PORT I2C_NUM_0
#define NPZ_I2C_ADDR 0x3D

npz_status_e npz_hal_init(void) {
i2c_config_t conf = {
.mode = I2C_MODE_MASTER,
.sda_io_num = GPIO_NUM_21,
.scl_io_num = GPIO_NUM_22,
.sda_pullup_en = GPIO_PULLUP_ENABLE,
.scl_pullup_en = GPIO_PULLUP_ENABLE,
.master.clk_speed = 100000,
};
i2c_param_config(I2C_PORT, &conf);
return i2c_driver_install(I2C_PORT, I2C_MODE_MASTER, 0, 0, 0) == ESP_OK ? OK : ERR;
}

npz_status_e npz_hal_read(uint8_t slave_address, uint8_t slave_register,
uint8_t *pData, uint16_t size, uint32_t timeout) {
return i2c_master_write_read_device(I2C_PORT, NPZ_I2C_ADDR,
&slave_register, 1, pData, size,
pdMS_TO_TICKS(timeout)) == ESP_OK ? OK : ERR;
}

npz_status_e npz_hal_write(uint8_t slave_address, uint8_t *pData,
uint16_t size, uint32_t timeout) {
return i2c_master_write_to_device(I2C_PORT, NPZ_I2C_ADDR, pData, size,
pdMS_TO_TICKS(timeout)) == ESP_OK ? OK : ERR;
}

Platform Checklist

Before testing your port, verify:

  • I2C peripheral initialized at 100 kbit/s.
  • slave_address passed into HAL functions equals NPZ_I2C_ADDRESS (0x7A).
  • Reads implement a write-then-read (set register pointer, then read data).
  • Both functions are blocking for the duration of timeout ms.
  • External pull-up resistors fitted on SDA/SCL (or I2C_PULL_AUTO set in npz_device_config_s to use internal pull-ups).
  • npz_hal_init() returns OK before any other driver call is made.