blob: f4e2b11ddb4fdad7c705322a177aa7f6796f3ad9 [file] [log] [blame]
#include <app/zedmon/ina.h>
#include <app/zedmon/usb.h>
#include <assert.h>
#include <lk/err.h>
#include <dev/i2c.h>
#include <kernel/timer.h>
#include <lib/cbuf.h>
#include <lib/console.h>
#include <platform.h>
#include <stdint.h>
#include <stdio.h>
#include <target/gpioconfig.h>
#include "ina231.h"
#include "ina233.h"
typedef struct __attribute__((packed)) {
lk_bigtime_t time;
uint8_t type:2;
uint8_t channel:6;
} ina_alert_event_t;
enum event_type {
EVENT_TYPE_SAMPLE = 0x0,
EVENT_TYPE_HEARTBEAT = 0x1,
};
static cbuf_t ina_alert_events;
static timer_t ina_heartbeat_timer;
static bool (*ina_sample)(uint16_t *v_shunt, uint16_t *v_bus);
static bool ina_initialized = false;
static uint32_t ina_errors;
static uint32_t ina_samples;
static uint16_t ina_v_shunt;
static uint16_t ina_v_bus;
static uint16_t ina_samples_since_heartbeat;
// This exepects to be called from irq context.
bool send_irq_event(ina_alert_event_t *event) {
// This won't race because we're in irq context.
if (cbuf_space_avail(&ina_alert_events) >= sizeof(*event)) {
cbuf_write(&ina_alert_events, event, sizeof(*event), false);
return true;
}
return false;
}
bool ina_alert_irq(int index, uint64_t timestamp) {
// We only handle a single channel right now.
assert(index == 0);
if (!ina_initialized) {
return false;
}
ina_alert_event_t event = {
.type = EVENT_TYPE_SAMPLE,
.channel = 0,
.time = timestamp,
};
return send_irq_event(&event);
}
enum handler_return ina_heartbeat_handler(struct timer *timer, lk_time_t now, void *arg){
ina_alert_event_t event = {
.type = EVENT_TYPE_HEARTBEAT,
.channel = 0,
.time = 0,
};
return send_irq_event(&event);
}
void ina_init(void) {
cbuf_initialize(&ina_alert_events, 32);
if (ina233_init()) {
printf("ina233 found\n");
ina_sample = ina233_sample;
} else if (ina231_init()) {
printf("ina231 found\n");
ina_sample = ina231_sample;
}
timer_initialize(&ina_heartbeat_timer);
ina_initialized = true;
// call sample once to unstick the conversion ready flag.
uint16_t v_shunt;
uint16_t v_bus;
ina_sample(&v_shunt, &v_bus);
timer_set_periodic(&ina_heartbeat_timer, 1000, ina_heartbeat_handler, NULL);
}
void ina_loop(void) {
while (true) {
ina_alert_event_t event;
cbuf_read(&ina_alert_events, &event, sizeof(event), true);
if (ina_sample == NULL) {
continue;
}
uint16_t v_shunt;
uint16_t v_bus;
// If we miss an alert signal edge, we'll stop sampling. To protect
// against this, we do an extra sample if we've gotten no samples
// between two heartbeats.
if (event.type == EVENT_TYPE_HEARTBEAT) {
uint16_t samples = ina_samples_since_heartbeat;
ina_samples_since_heartbeat = 0;
if (samples > 0) {
continue;
}
// Kick off a read below to restart the pipeline.
}
if (!ina_sample(&v_shunt, &v_bus)) {
ina_errors++;
continue;
}
// Don't report heartbeat samples.
if (event.type == EVENT_TYPE_SAMPLE) {
ina_v_shunt = v_shunt;
ina_v_bus = v_bus;
ina_samples++;
ina_samples_since_heartbeat++;
zedmon_usb_add_sample(event.time, v_shunt, v_bus);
}
}
}
static int cmd_ina_status(int argc, const cmd_args *argv) {
float v_shunt = ina_v_shunt * 0.0025;
float v_bus = ina_v_bus * 0.00125;
printf("samples: %u\n", ina_samples);
printf("errors: %u\n", ina_errors);
printf("v_bus: %f V \n", v_bus);
printf("v_shunt: %f mV\n", v_shunt);
return 0;
}
STATIC_COMMAND_START
STATIC_COMMAND("ina_status", "ina status", &cmd_ina_status)
STATIC_COMMAND_END(ina);