| #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); |
| |