/** * @file adc_sensor.c * @brief Implementation of the ADC sensor library. * * This file contains the implementation for initializing and reading from ADC * sensors. It currently provides simulated values for voltage and current, with * placeholders for real hardware ADC implementation including GPIO control. */ #include #include #include #include #include #include LOG_MODULE_REGISTER(adc_sensor, LOG_LEVEL_INF); // Simulated values #define SIMULATED_VOLTAGE_MV 12000 #define SIMULATED_CURRENT_MA 45 // Devicetree node checks #define VOLTAGE_SENSOR_NODE DT_NODELABEL(supply_voltage) #define CURRENT_SENSOR_NODE DT_NODELABEL(motor_current) #define SENSOR_MUX_NODE DT_NODELABEL(vnd7050aj_mux) #ifndef CONFIG_ADC_SENSOR_SIMULATED // ADC device reference from centralized mux node #if DT_NODE_EXISTS(SENSOR_MUX_NODE) #define ADC_NODE DT_PHANDLE(SENSOR_MUX_NODE, io_channels) #define ADC_CHANNEL DT_PHA(SENSOR_MUX_NODE, io_channels, input) #define ADC_RESOLUTION 12 #define ADC_REFERENCE_MV DT_PROP(SENSOR_MUX_NODE, reference_mv) // Sensor-specific properties #if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) #define VOLTAGE_DIVIDER_RATIO \ DT_PROP(VOLTAGE_SENSOR_NODE, voltage_divider_ratio) #define VOLTAGE_MUX_CHANNEL DT_PROP(VOLTAGE_SENSOR_NODE, mux_channel) #define VOLTAGE_DELAY_MS DT_PROP(VOLTAGE_SENSOR_NODE, measurement_delay_ms) #endif #if DT_NODE_EXISTS(CURRENT_SENSOR_NODE) #define CURRENT_SENSE_RESISTOR_MOHM \ DT_PROP(CURRENT_SENSOR_NODE, current_sense_resistor_mohm) #define CURRENT_MUX_CHANNEL DT_PROP(CURRENT_SENSOR_NODE, mux_channel) #define CURRENT_DELAY_MS DT_PROP(CURRENT_SENSOR_NODE, measurement_delay_ms) #endif static const struct device *adc_dev; static struct adc_channel_cfg adc_channel_cfg = { .gain = ADC_GAIN_1, .reference = ADC_REF_INTERNAL, .acquisition_time = ADC_ACQ_TIME_DEFAULT, .channel_id = ADC_CHANNEL, .differential = 0}; static struct adc_sequence adc_sequence = { .channels = BIT(ADC_CHANNEL), .buffer_size = sizeof(uint16_t), .resolution = ADC_RESOLUTION, }; static uint16_t adc_buffer; #endif #endif static bool initialized = false; #ifndef CONFIG_ADC_SENSOR_SIMULATED // GPIO specs from centralized mux node #if DT_NODE_EXISTS(SENSOR_MUX_NODE) static const struct gpio_dt_spec sen_gpio = GPIO_DT_SPEC_GET(SENSOR_MUX_NODE, sen_gpios); static const struct gpio_dt_spec s0_gpio = GPIO_DT_SPEC_GET(SENSOR_MUX_NODE, s0_gpios); static const struct gpio_dt_spec s1_gpio = GPIO_DT_SPEC_GET(SENSOR_MUX_NODE, s1_gpios); #endif /** * @brief Configure GPIO pins for ADC sensor multiplexer control */ static int configure_sensor_gpios(void) { int ret = 0; #if DT_NODE_EXISTS(SENSOR_MUX_NODE) // Configure sensor multiplexer GPIOs if (gpio_is_ready_dt(&sen_gpio)) { ret = gpio_pin_configure_dt(&sen_gpio, GPIO_OUTPUT_INACTIVE); if (ret < 0) { LOG_ERR("Failed to configure SEN GPIO: %d", ret); return ret; } } if (gpio_is_ready_dt(&s0_gpio)) { ret = gpio_pin_configure_dt(&s0_gpio, GPIO_OUTPUT_INACTIVE); if (ret < 0) { LOG_ERR("Failed to configure S0 GPIO: %d", ret); return ret; } } if (gpio_is_ready_dt(&s1_gpio)) { ret = gpio_pin_configure_dt(&s1_gpio, GPIO_OUTPUT_INACTIVE); if (ret < 0) { LOG_ERR("Failed to configure S1 GPIO: %d", ret); return ret; } } #endif return 0; } /** * @brief Set multiplexer channel for sensor selection * @param enable Enable/disable the sensor * @param channel Multiplexer channel (0-3) * @param delay_ms Delay after setting GPIOs */ static int set_mux_channel(bool enable, uint8_t channel, uint32_t delay_ms) { #if DT_NODE_EXISTS(SENSOR_MUX_NODE) if (gpio_is_ready_dt(&sen_gpio)) { gpio_pin_set_dt(&sen_gpio, enable ? 1 : 0); } if (gpio_is_ready_dt(&s0_gpio)) { gpio_pin_set_dt(&s0_gpio, (channel & 0x01) ? 1 : 0); } if (gpio_is_ready_dt(&s1_gpio)) { gpio_pin_set_dt(&s1_gpio, (channel & 0x02) ? 1 : 0); } // Delay for GPIO settling if (delay_ms > 0) { k_msleep(delay_ms); } #endif return 0; } #endif /* !CONFIG_ADC_SENSOR_SIMULATED */ #ifndef CONFIG_ADC_SENSOR_SIMULATED /** * @brief Read ADC value and convert to millivolts (for voltage sensor) * @return ADC reading in millivolts, or 0 on error */ static uint16_t read_adc_voltage_mv(void) { #if DT_NODE_EXISTS(SENSOR_MUX_NODE) && DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) int ret = adc_read(adc_dev, &adc_sequence); if (ret < 0) { LOG_ERR("ADC read failed: %d", ret); return 0; } // Convert ADC reading to millivolts // ADC reading is 12-bit (0-4095) representing 0 to ADC_REFERENCE_MV uint32_t adc_value = adc_buffer; uint32_t voltage_mv = (adc_value * ADC_REFERENCE_MV) / 4095; // Apply voltage divider scaling voltage_mv *= VOLTAGE_DIVIDER_RATIO; LOG_DBG("ADC raw: %u, voltage: %u mV", adc_value, (uint16_t)voltage_mv); return (uint16_t)voltage_mv; #else return 0; #endif } /** * @brief Read ADC value and convert to milliamps (for current sensor) * @return ADC reading in milliamps, or 0 on error */ static uint16_t read_adc_current_ma(void) { #if DT_NODE_EXISTS(SENSOR_MUX_NODE) && DT_NODE_EXISTS(CURRENT_SENSOR_NODE) int ret = adc_read(adc_dev, &adc_sequence); if (ret < 0) { LOG_ERR("ADC read failed: %d", ret); return 0; } // Convert ADC reading to millivolts first uint32_t adc_value = adc_buffer; uint32_t voltage_mv = (adc_value * ADC_REFERENCE_MV) / 4095; // Convert voltage to current based on sense resistor // I = V / R, where R is in milliohms and V is in millivolts // Result is in milliamps uint32_t current_ma = (voltage_mv * 1000) / CURRENT_SENSE_RESISTOR_MOHM; LOG_DBG("ADC raw: %u, current: %u mA", adc_value, (uint16_t)current_ma); return (uint16_t)current_ma; #else return 0; #endif } #endif int adc_sensor_init(void) { if (initialized) { return 0; } #ifdef CONFIG_ADC_SENSOR_SIMULATED LOG_INF("ADC sensor initialized (simulated mode)"); LOG_INF("Simulated values: %dmV, %dmA", SIMULATED_VOLTAGE_MV, SIMULATED_CURRENT_MA); #else // Initialize GPIO pins for sensor control int ret = configure_sensor_gpios(); if (ret < 0) { LOG_ERR("Failed to configure sensor GPIOs: %d", ret); return ret; } // Initialize ADC hardware #if DT_NODE_EXISTS(SENSOR_MUX_NODE) adc_dev = DEVICE_DT_GET(ADC_NODE); if (!device_is_ready(adc_dev)) { LOG_ERR("ADC device not ready"); return -ENODEV; } adc_sequence.buffer = &adc_buffer; ret = adc_channel_setup(adc_dev, &adc_channel_cfg); if (ret < 0) { LOG_ERR("Failed to setup ADC channel: %d", ret); return ret; } LOG_INF("ADC device ready: %s", adc_dev->name); #endif LOG_INF("ADC sensor initialized (real ADC mode with centralized mux)"); #if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) LOG_INF("Voltage sensor: channel %d, divider ratio %d", VOLTAGE_MUX_CHANNEL, VOLTAGE_DIVIDER_RATIO); #endif #if DT_NODE_EXISTS(CURRENT_SENSOR_NODE) LOG_INF("Current sensor: channel %d, sense resistor %d mOhm", CURRENT_MUX_CHANNEL, CURRENT_SENSE_RESISTOR_MOHM); #endif #endif initialized = true; return 0; } uint16_t adc_sensor_get_voltage_mv(void) { if (!initialized) { LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()"); adc_sensor_init(); } #ifdef CONFIG_ADC_SENSOR_SIMULATED return SIMULATED_VOLTAGE_MV; #else // Set multiplexer to voltage channel #if DT_NODE_EXISTS(VOLTAGE_SENSOR_NODE) set_mux_channel(true, VOLTAGE_MUX_CHANNEL, VOLTAGE_DELAY_MS); // Read real ADC value for voltage uint16_t voltage = read_adc_voltage_mv(); // Disable sensor after measurement to save power set_mux_channel(false, 0, 0); return voltage; #else return 0; #endif #endif } uint16_t adc_sensor_get_current_ma(void) { if (!initialized) { LOG_WRN("ADC sensor not initialized, calling adc_sensor_init()"); adc_sensor_init(); } #ifdef CONFIG_ADC_SENSOR_SIMULATED return SIMULATED_CURRENT_MA; #else // Set multiplexer to current channel #if DT_NODE_EXISTS(CURRENT_SENSOR_NODE) set_mux_channel(true, CURRENT_MUX_CHANNEL, CURRENT_DELAY_MS); // Read real ADC value for current uint16_t current = read_adc_current_ma(); // Disable sensor after measurement to save power set_mux_channel(false, 0, 0); return current; #else return 0; #endif #endif }