pocket-reform/pocket-reform-sysctl-fw/sysctl.c

1429 lines
39 KiB
C

/*
SPDX-License-Identifier: GPL-3.0-or-later
MNT Pocket Reform System Controller Firmware for RP2040
Copyright 2023-2024 MNT Research GmbH
fusb_read/write functions based on:
https://git.clarahobbs.com/pd-buddy/pd-buddy-firmware/src/branch/master/lib/src/fusb302b.c
*/
#include <stdio.h>
#include <string.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "pico/sleep.h"
#include "hardware/i2c.h"
#include "hardware/spi.h"
#include "hardware/irq.h"
#include "hardware/rtc.h"
#include "fusb302b.h"
#include "pd.h"
#define FW_STRING1 "PREF1SYS"
#define FW_STRING2 "R1"
#define FW_STRING3 "20240416"
#define FW_REV FW_STRING1 FW_STRING2 FW_STRING3
#define PIN_SDA 0
#define PIN_SCL 1
#define PIN_DISP_RESET 2
#define PIN_FLIGHTMODE 3
#define PIN_KBD_UART_TX 4
#define PIN_KBD_UART_RX 5
#define PIN_WOWWAN 6
#define PIN_DISP_EN 7
#define PIN_SOM_MOSI 8
#define PIN_SOM_SS0 9
#define PIN_SOM_SCK 10
#define PIN_SOM_MISO 11
#define PIN_SOM_UART_TX 12
#define PIN_SOM_UART_RX 13
#define PIN_FUSB_INT 14
#define PIN_LED_B 15
#define PIN_LED_R 16
#define PIN_LED_G 17
#define PIN_MODEM_POWER 18
#define PIN_SOM_WAKE 19
#define PIN_MODEM_RESET 20
#define PIN_1V1_ENABLE 23
#define PIN_3V3_ENABLE 24
#define PIN_5V_ENABLE 25
#define PIN_PHONE_DPR 27
#define PIN_USB_SRC_ENABLE 28
#define PIN_PWREN_LATCH 29
// FUSB302B USB-PD controller
#define FUSB_ADDR 0x22
// MAX17320 protector/balancer
// https://datasheets.maximintegrated.com/en/ds/MAX17320.pdf
#define MAX_ADDR1 0x36
#define MAX_ADDR2 0x0b
// MP2650 charger
// https://www.monolithicpower.com/en/documentview/productdocument/index/version/2/document_type/Datasheet/lang/en/sku/MP2650GV/document_id/9664/
#define MPS_ADDR 0x5c
#define I2C_TIMEOUT (1000*500)
#define UART_ID uart1
#define BAUD_RATE 115200
#define DATA_BITS 8
#define STOP_BITS 1
#define PARITY UART_PARITY_NONE
// battery information
// TODO: turn into a struct
// 4.8A x 3600 seconds/hour (per cell)
#define MAX_CAPACITY (4.0)*3600.0
float report_capacity_max_ampsecs = MAX_CAPACITY;
float report_capacity_accu_ampsecs = MAX_CAPACITY;
float report_capacity_min_ampsecs = 0;
int report_capacity_percentage = 0;
float report_volts = 0;
float report_current = 0;
float report_cells_v[8] = {0,0,0,0,0,0,0,0};
bool reached_full_charge = true; // FIXME
bool som_is_powered = false;
bool print_pack_info = false;
void i2c_scan() {
printf("\nI2C Scan\n");
printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F\n");
for (int addr = 0; addr < (1 << 7); ++addr) {
if (addr % 16 == 0) {
printf("%02x ", addr);
}
int ret;
uint8_t rxdata;
ret = i2c_read_blocking(i2c0, addr, &rxdata, 1, false);
printf(ret < 0 ? "." : "@");
printf(addr % 16 == 15 ? "\n" : " ");
}
}
uint8_t max_read_byte(uint8_t addr)
{
uint8_t buf;
i2c_write_blocking(i2c0, MAX_ADDR1, &addr, 1, true);
i2c_read_blocking(i2c0, MAX_ADDR1, &buf, 1, false);
return buf;
}
uint16_t max_read_word(uint8_t addr)
{
uint8_t buf[2];
i2c_write_blocking(i2c0, MAX_ADDR1, &addr, 1, true);
i2c_read_blocking(i2c0, MAX_ADDR1, buf, 2, false);
uint16_t result = ((uint16_t)buf[1]<<8) | (uint16_t)buf[0];
return result;
}
uint16_t max_read_word_100(uint8_t addr)
{
uint8_t buf[2];
i2c_write_blocking(i2c0, MAX_ADDR2, &addr, 1, true);
i2c_read_blocking(i2c0, MAX_ADDR2, buf, 2, false);
uint16_t result = ((uint16_t)buf[1]<<8) | (uint16_t)buf[0];
return result;
}
void max_read_buf(uint8_t addr, uint8_t size, uint8_t *buf)
{
i2c_write_blocking(i2c0, MAX_ADDR1, &addr, 1, true);
i2c_read_blocking(i2c0, MAX_ADDR1, buf, size, false);
}
void max_write_byte(uint8_t addr, uint8_t byte)
{
uint8_t buf[2] = {addr, byte};
i2c_write_blocking(i2c0, MAX_ADDR1, buf, 2, false);
}
void max_write_word(uint8_t addr, uint16_t word)
{
uint8_t buf[3] = {addr, word&0xff, word>>8};
int res = i2c_write_blocking(i2c0, MAX_ADDR1, buf, 3, false);
}
void max_write_word_100(uint8_t addr, uint16_t word)
{
uint8_t buf[3] = {addr, word&0xff, word>>8};
int res = i2c_write_blocking(i2c0, MAX_ADDR2, buf, 3, false);
}
void max_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf)
{
uint8_t txbuf[size + 1];
txbuf[0] = addr;
for (int i = 0; i < size; i++) {
txbuf[i + 1] = buf[i];
}
i2c_write_blocking(i2c0, MAX_ADDR1, txbuf, size + 1, false);
}
float max_word_to_mv(uint16_t w)
{
float result = ((float)w)*0.078125;
return result;
}
float max_word_to_pack_mv(uint16_t w)
{
float result = ((float)w)*0.3125;
return result;
}
float max_word_to_ma(uint16_t w)
{
float result = ((float)w)*0.3125;
return result;
}
float max_word_to_time(uint16_t w)
{
float result = ((float)w)*5.625;
return result;
}
float max_word_to_cap(uint16_t w)
{
float result = ((float)w)*1.0; // depends on Rsense, 1.0 @ 5mohms. otherwise 5.0μVh / Rsense
return result;
}
float max_word_to_percentage(uint16_t w)
{
// TODO: cap to 100%
float result = ((float)w)*0.00390625;
return result;
}
uint8_t mps_read_byte(uint8_t addr)
{
uint8_t buf;
i2c_write_blocking(i2c0, MPS_ADDR, &addr, 1, true);
i2c_read_blocking(i2c0, MPS_ADDR, &buf, 1, false);
return buf;
}
uint16_t mps_read_word(uint8_t addr)
{
uint8_t buf[2];
i2c_write_blocking(i2c0, MPS_ADDR, &addr, 1, true);
i2c_read_blocking(i2c0, MPS_ADDR, buf, 2, false);
uint16_t result = ((uint16_t)buf[1]<<8) | (uint16_t)buf[0];
return result;
}
float mps_word_to_ntc(uint16_t w)
{
float result = (float)(w&0xfff)*1.6/4096.0;
return result;
}
float mps_word_to_3200(uint16_t w)
{
float result = (w>>10)*100
+ ((w&(1<<9))>>9)*50
+ ((w&(1<<8))>>8)*25
+ ((w&(1<<7))>>7)*12.5
+ ((w&(1<<6))>>6)*6.25;
return result;
}
float mps_word_to_6400(uint16_t w)
{
float result = (w>>9)*100
+ ((w&(1<<8))>>8)*50
+ ((w&(1<<7))>>7)*25
+ ((w&(1<<6))>>6)*12.5;
return result;
}
float mps_word_to_12800(uint16_t w)
{
float result = (w>>8)*100
+ ((w&(1<<7))>>7)*50
+ ((w&(1<<6))>>6)*25;
return result;
}
float mps_word_to_w(uint16_t w)
{
float result = (w>>9)
+ ((w&(1<<8))>>8)*0.5
+ ((w&(1<<7))>>7)*0.25
+ ((w&(1<<6))>>6)*0.125;
return result;
}
// tj=903-2.578*t
// tj-903=-2.578*t
// (tj-903)/-2.578=t
float mps_word_to_temp(uint16_t w)
{
float result = (float)(w>>6);
result = (result - 903) / -2.578;
//result = 903-2.578*result;
return result;
}
void mps_read_buf(uint8_t addr, uint8_t size, uint8_t *buf)
{
i2c_write_blocking(i2c0, MPS_ADDR, &addr, 1, true);
i2c_read_blocking(i2c0, MPS_ADDR, buf, size, false);
}
void mps_write_byte(uint8_t addr, uint8_t byte)
{
uint8_t buf[2] = {addr, byte};
i2c_write_blocking(i2c0, MPS_ADDR, buf, 2, false);
}
void mps_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf)
{
uint8_t txbuf[size + 1];
txbuf[0] = addr;
for (int i = 0; i < size; i++) {
txbuf[i + 1] = buf[i];
}
i2c_write_blocking(i2c0, MPS_ADDR, txbuf, size + 1, false);
}
uint8_t fusb_read_byte(uint8_t addr)
{
uint8_t buf;
i2c_write_blocking(i2c0, FUSB_ADDR, &addr, 1, true);
i2c_read_blocking(i2c0, FUSB_ADDR, &buf, 1, false);
return buf;
}
void fusb_read_buf(uint8_t addr, uint8_t size, uint8_t *buf)
{
i2c_write_blocking(i2c0, FUSB_ADDR, &addr, 1, true);
i2c_read_blocking(i2c0, FUSB_ADDR, buf, size, false);
}
void fusb_write_byte(uint8_t addr, uint8_t byte)
{
uint8_t buf[2] = {addr, byte};
i2c_write_blocking(i2c0, FUSB_ADDR, buf, 2, false);
}
void fusb_write_buf(uint8_t addr, uint8_t size, const uint8_t *buf)
{
uint8_t txbuf[size + 1];
txbuf[0] = addr;
for (int i = 0; i < size; i++) {
txbuf[i + 1] = buf[i];
}
i2c_write_blocking(i2c0, FUSB_ADDR, txbuf, size + 1, false);
}
void fusb_send_message(const union pd_msg *msg)
{
/* Token sequences for the FUSB302B */
static uint8_t sop_seq[5] = {
FUSB_FIFO_TX_SOP1,
FUSB_FIFO_TX_SOP1,
FUSB_FIFO_TX_SOP1,
FUSB_FIFO_TX_SOP2,
FUSB_FIFO_TX_PACKSYM
};
static uint8_t eop_seq[4] = {
FUSB_FIFO_TX_JAM_CRC,
FUSB_FIFO_TX_EOP,
FUSB_FIFO_TX_TXOFF,
FUSB_FIFO_TX_TXON
};
/* Get the length of the message: a two-octet header plus NUMOBJ four-octet
* data objects */
uint8_t msg_len = 2 + 4 * PD_NUMOBJ_GET(msg);
/* Set the number of bytes to be transmitted in the packet */
sop_seq[4] = FUSB_FIFO_TX_PACKSYM | msg_len;
/* Write all three parts of the message to the TX FIFO */
fusb_write_buf(FUSB_FIFOS, 5, sop_seq);
fusb_write_buf(FUSB_FIFOS, msg_len, msg->bytes);
fusb_write_buf(FUSB_FIFOS, 4, eop_seq);
}
uint8_t fusb_read_message(union pd_msg *msg)
{
uint8_t garbage[4];
uint8_t numobj;
/* If this isn't an SOP message, return error.
* Because of our configuration, we should be able to assume this means the
* buffer is empty, and not try to read past a non-SOP message. */
uint8_t rxb = fusb_read_byte(FUSB_FIFOS);
if (rxb!=0) printf("# [fusb] rx = 0x%02x\n", rxb);
if ((rxb & FUSB_FIFO_RX_TOKEN_BITS)
!= FUSB_FIFO_RX_SOP) {
return 1;
}
/* Read the message header into msg */
fusb_read_buf(FUSB_FIFOS, 2, msg->bytes);
/* Get the number of data objects */
numobj = PD_NUMOBJ_GET(msg);
/* If there is at least one data object, read the data objects */
if (numobj > 0) {
fusb_read_buf(FUSB_FIFOS, numobj * 4, msg->bytes + 2);
}
/* Throw the CRC32 in the garbage, since the PHY already checked it. */
fusb_read_buf(FUSB_FIFOS, 4, garbage);
return 0;
}
// returns voltage
int print_src_fixed_pdo(int number, uint32_t pdo) {
int tmp;
printf("[pd_src_fixed_pdo]\n");
printf("number = %d\n", number);
/* Dual-role power */
tmp = (pdo & PD_PDO_SRC_FIXED_DUAL_ROLE_PWR) >> PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT;
if (tmp) {
printf("dual_role_pwr = %d\n", tmp);
}
/* USB Suspend Supported */
tmp = (pdo & PD_PDO_SRC_FIXED_USB_SUSPEND) >> PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT;
if (tmp) {
printf("usb_suspend = %d\n", tmp);
}
/* Unconstrained Power */
tmp = (pdo & PD_PDO_SRC_FIXED_UNCONSTRAINED) >> PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT;
if (tmp) {
printf("unconstrained_pwr = %d\n", tmp);
}
/* USB Communications Capable */
tmp = (pdo & PD_PDO_SRC_FIXED_USB_COMMS) >> PD_PDO_SRC_FIXED_USB_COMMS_SHIFT;
if (tmp) {
printf("usb_comms = %d\n", tmp);
}
/* Dual-Role Data */
tmp = (pdo & PD_PDO_SRC_FIXED_DUAL_ROLE_DATA) >> PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT;
if (tmp) {
printf("dual_role_data = %d\n", tmp);
}
/* Unchunked Extended Messages Supported */
tmp = (pdo & PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG) >> PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT;
if (tmp) {
printf("unchunked_ext_msg = %d\n", tmp);
}
/* Peak Current */
tmp = (pdo & PD_PDO_SRC_FIXED_PEAK_CURRENT) >> PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT;
if (tmp) {
printf("peak_i = %d\n", tmp);
}
/* Voltage */
tmp = (pdo & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT;
printf("v = %d.%02d\n", PD_PDV_V(tmp), PD_PDV_CV(tmp));
int voltage = (int)PD_PDV_V(tmp);
/* Maximum Current */
tmp = (pdo & PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT;
printf("i_a: %d.%02d\n", PD_PDI_A(tmp), PD_PDI_CA(tmp));
return voltage;
}
int charger_configure() {
// TODO: check all MP2650 registers, esp. 4, 7, b
// set input current limit to 2000mA
mps_write_byte(0x00, (1<<5)|(1<<3));
// set input voltage limit to 6V (above 5V USB voltage)
mps_write_byte(0x01, (1<<6));
// set charge current limit to 2000mA (1600+400)
mps_write_byte(0x02, (1<<5)|(1<<3));
}
float charger_dump() {
// TODO: if max reports overvoltage (dysbalanced cells),
// can we lower the charging voltage temporarily?
// alternatively, the current
uint8_t status = mps_read_byte(0x13);
uint8_t fault = mps_read_byte(0x14);
float adc_bat_v = mps_word_to_6400(mps_read_word(0x16))/1000.0;
float adc_sys_v = mps_word_to_6400(mps_read_word(0x18))/1000.0;
float adc_charge_c = mps_word_to_6400(mps_read_word(0x1a))/1000.0;
float adc_input_v = mps_word_to_12800(mps_read_word(0x1c))/1000.0;
float adc_input_c = mps_word_to_3200(mps_read_word(0x1e))/1000.0;
float adc_temp = mps_word_to_temp(mps_read_word(0x24));
float adc_sys_pwr = mps_word_to_w(mps_read_word(0x26));
float adc_discharge_c = mps_word_to_6400(mps_read_word(0x28))/1000.0;
float adc_ntc_v = mps_word_to_ntc(mps_read_word(0x40))/1000.0;
uint8_t input_c_limit = mps_read_byte(0x00);
uint8_t input_v_limit = mps_read_byte(0x01);
uint8_t charge_c = mps_read_byte(0x02);
uint8_t precharge_c = mps_read_byte(0x03);
uint8_t bat_full_v = mps_read_byte(0x04);
// carry over to globals for SPI reporting
report_current = -(adc_input_c - adc_discharge_c);
report_volts = adc_sys_v;
if (print_pack_info) {
printf("[charger_info]\n");
printf("status = 0x%x\n", status);
printf("fault = 0x%x\n", fault);
printf("adc_bat_v = %f\n", adc_bat_v);
printf("adc_sys_v = %f\n", adc_sys_v);
printf("adc_charge_c = %f\n", adc_charge_c);
printf("adc_input_v = %f\n", adc_input_v);
printf("adc_input_c = %f\n", adc_input_c);
printf("adc_temp = %f\n", adc_temp);
printf("adc_sys_pwr = %f\n", adc_sys_pwr);
printf("adc_discharge_c = %f\n", adc_discharge_c);
printf("adc_ntc_v = %f\n", adc_ntc_v);
printf("input_c_limit = 0x%x\n", input_c_limit);
printf("input_v_limit = 0x%x\n", input_v_limit);
printf("charge_c = 0x%x\n", charge_c);
printf("precharge_c = 0x%x\n", precharge_c);
printf("bat_full_v = 0x%d\n", bat_full_v);
}
return adc_input_v;
}
void max_dump() {
// disable write protection (CommStat)
max_write_word(0x61, 0x0000);
max_write_word(0x61, 0x0000);
// set pack cfg: 2 cells (0), 1+1 thermistor, 6v charge pump, 11:thtype=10k, btpken on, no aoldo
max_write_word_100(0xb5, (0<<14)|(1<<13)|(0<<11)|(0<<8)|(2<<2)|0);
// enable balancing (zener)
// nBalCfg
max_write_word(0x61, 0x0000);
max_write_word(0x61, 0x0000);
max_write_word_100(0xd4, (1<<13)|(3<<10)|(3<<5));
uint16_t comm_stat = max_read_word(0x61);
uint16_t status = max_read_word(0x00);
uint16_t packcfg = max_read_word_100(0xb5);
uint16_t prot_status = max_read_word(0xd9);
uint16_t prot_alert = max_read_word(0xaf);
uint16_t prot_cfg2 = max_read_word_100(0xf1);
uint16_t therm_cfg = max_read_word_100(0xca);
float vcell = max_word_to_mv(max_read_word(0x1a));
float avg_vcell = max_word_to_mv(max_read_word(0x19));
float cell1 = max_word_to_mv(max_read_word(0xd8));
float cell2 = max_word_to_mv(max_read_word(0xd7));
float cell3 = max_word_to_mv(max_read_word(0xd6));
float cell4 = max_word_to_mv(max_read_word(0xd5));
// this value looks good (checked with inducing voltages w/ power supply)
float vpack = max_word_to_pack_mv(max_read_word(0xda));
float temp = ((float)((int16_t)max_read_word(0x1b)))*(1.0/256.0);
float die_temp = ((float)((int16_t)max_read_word(0x34)))*(1.0/256.0);
float temp1 = ((float)((int16_t)max_read_word_100(0x3a)))*(1.0/256.0);
float temp2 = ((float)((int16_t)max_read_word_100(0x39)))*(1.0/256.0);
float temp3 = ((float)((int16_t)max_read_word_100(0x38)))*(1.0/256.0);
float temp4 = ((float)((int16_t)max_read_word_100(0x37)))*(1.0/256.0);
float rep_capacity = max_word_to_cap(max_read_word(0x05));
float rep_percentage = max_word_to_percentage(max_read_word(0x06));
float rep_age = max_word_to_percentage(max_read_word(0x07));
float rep_full_capacity = max_word_to_cap(max_read_word(0x10));
float rep_time_to_empty = max_word_to_time(max_read_word(0x11));
float rep_time_to_full = max_word_to_time(max_read_word(0x20));
// carry over to globals
report_capacity_percentage = (int)rep_percentage;
// charger mostly doesn't charge to >98%
if (report_capacity_percentage >= 98) report_capacity_percentage = 100;
report_cells_v[0] = cell1;
report_cells_v[1] = cell2;
if (print_pack_info) {
printf("[pack_info]\n");
printf("comm_stat = 0x%04x\n", comm_stat);
printf("packcfg = 0x%04x\n", packcfg);
printf("status = 0x%04x\n", status);
printf("status_prot_alert = %d\n", (status & 0x8000) ? 1 : 0);
printf("prot_alert = 0x%04x\n", prot_alert);
printf("prot_cfg2 = 0x%04x\n", prot_cfg2);
printf("therm_cfg = 0x%04x\n", therm_cfg);
printf("temp = %f\n", temp);
printf("die temp = %f\n", die_temp);
printf("temp1 = %f\n", temp1);
printf("temp2 = %f\n", temp2);
printf("temp3 = %f\n", temp3);
printf("temp4 = %f\n", temp4);
printf("prot_status = 0x%04x\n", prot_status);
printf("prot_status_meaning = \"");
if (prot_status & (1<<14)) {
printf("too hot, ");
}
if (prot_status & (1<<13)) {
printf("full, ");
}
if (prot_status & (1<<12)) {
printf("too cold for charge, ");
}
if (prot_status & (1<<11)) {
printf("overvoltage, ");
}
if (prot_status & (1<<10)) {
printf("overcharge current, ");
}
if (prot_status & (1<<9)) {
printf("qoverflow, ");
}
if (prot_status & (1<<8)) {
printf("prequal timeout, ");
}
if (prot_status & (1<<7)) {
printf("imbalance, ");
}
if (prot_status & (1<<6)) {
printf("perm fail, ");
}
if (prot_status & (1<<5)) {
printf("die hot, ");
}
if (prot_status & (1<<4)) {
printf("too hot for discharge, ");
}
if (prot_status & (1<<3)) {
printf("undervoltage, ");
}
if (prot_status & (1<<2)) {
printf("overdischarge current, ");
}
if (prot_status & (1<<1)) {
printf("resdfault, ");
}
if (prot_status & (1<<0)) {
printf("ship, ");
}
printf("\"\n");
printf("vcell = %f\n", vcell);
printf("avg_vcell = %f\n", avg_vcell);
printf("cell1 = %f\n", cell1);
printf("cell2 = %f\n", cell2);
printf("cell3 = %f\n", cell3);
printf("cell4 = %f\n", cell4);
printf("vpack = %f\n", vpack);
printf("rep_capacity_mah = %f\n", rep_capacity);
printf("rep_percentage = %f\n", rep_percentage);
printf("rep_age_percentage = %f\n", rep_age);
printf("rep_full_capacity_mah = %f\n", rep_full_capacity);
printf("rep_time_to_empty_sec = %f\n", rep_time_to_empty);
printf("rep_time_to_full_sec = %f\n", rep_time_to_full);
}
if (status & 0x0002) {
printf("# POR, clearing status\n");
max_write_word(0x61, 0x0000);
max_write_word(0x61, 0x0000);
max_write_word(0x00, status & (~0x0002));
}
}
void init_spi_client();
void turn_som_power_on() {
init_spi_client();
// latch
gpio_put(PIN_PWREN_LATCH, 1);
gpio_put(PIN_LED_B, 1);
printf("# [action] turn_som_power_on\n");
gpio_put(PIN_1V1_ENABLE, 1);
sleep_ms(10);
gpio_put(PIN_3V3_ENABLE, 1);
sleep_ms(10);
/*gpio_put(PIN_SOM_MOSI, 1);
gpio_put(PIN_SOM_SS0, 1);
gpio_put(PIN_SOM_SCK, 1);
gpio_put(PIN_SOM_MISO, 1);*/
gpio_put(PIN_5V_ENABLE, 1);
// MODEM
gpio_put(PIN_FLIGHTMODE, 1); // active low
gpio_put(PIN_MODEM_RESET, 0); // active low (?)
gpio_put(PIN_MODEM_POWER, 1); // active high
gpio_put(PIN_PHONE_DPR, 1); // active high
sleep_ms(10);
gpio_put(PIN_DISP_EN, 1);
sleep_ms(10);
gpio_put(PIN_DISP_RESET, 1);
// MODEM
gpio_put(PIN_MODEM_RESET, 1); // active low
// done with latching
gpio_put(PIN_PWREN_LATCH, 0);
som_is_powered = true;
}
void turn_som_power_off() {
init_spi_client();
// latch
gpio_put(PIN_PWREN_LATCH, 1);
// FIXME spi test
/*gpio_put(PIN_SOM_MOSI, 0);
gpio_put(PIN_SOM_SS0, 0);
gpio_put(PIN_SOM_SCK, 0);
gpio_put(PIN_SOM_MISO, 0);*/
gpio_put(PIN_LED_B, 0);
printf("# [action] turn_som_power_off\n");
gpio_put(PIN_DISP_RESET, 0);
gpio_put(PIN_DISP_EN, 0);
// MODEM
gpio_put(PIN_FLIGHTMODE, 0); // active low
gpio_put(PIN_MODEM_RESET, 0); // active low
gpio_put(PIN_MODEM_POWER, 0); // active high
gpio_put(PIN_PHONE_DPR, 0); // active high
gpio_put(PIN_5V_ENABLE, 0);
sleep_ms(10);
gpio_put(PIN_3V3_ENABLE, 0);
sleep_ms(10);
gpio_put(PIN_1V1_ENABLE, 0);
// done with latching
gpio_put(PIN_PWREN_LATCH, 0);
som_is_powered = false;
}
void som_wake() {
uart_puts(uart0, "wake\r\n");
}
#define ST_EXPECT_DIGIT_0 0
#define ST_EXPECT_DIGIT_1 1
#define ST_EXPECT_DIGIT_2 2
#define ST_EXPECT_DIGIT_3 3
#define ST_EXPECT_CMD 4
#define ST_SYNTAX_ERROR 5
#define ST_EXPECT_RETURN 6
#define ST_EXPECT_MAGIC 7
char remote_cmd = 0;
uint8_t remote_arg = 0;
unsigned char cmd_state = ST_EXPECT_DIGIT_0;
unsigned int cmd_number = 0;
int cmd_echo = 0;
char uart_buffer[255] = {0};
// chr: input character
void handle_commands(char chr) {
if (cmd_echo) {
sprintf(uart_buffer, "%c", chr);
uart_puts(UART_ID, uart_buffer);
}
// states:
// 0-3 digits of optional command argument
// 4 command letter expected
// 5 syntax error (unexpected character)
// 6 command letter entered
if (cmd_state>=ST_EXPECT_DIGIT_0 && cmd_state<=ST_EXPECT_DIGIT_3) {
// read number or command
if (chr >= '0' && chr <= '9') {
cmd_number*=10;
cmd_number+=(chr-'0');
cmd_state++;
} else if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) {
// command entered instead of digit
remote_cmd = chr;
cmd_state = ST_EXPECT_RETURN;
} else if (chr == '\n' || chr == ' ') {
// ignore newlines or spaces
} else if (chr == '\r') {
sprintf(uart_buffer, "error:syntax\r\n");
uart_puts(UART_ID, uart_buffer);
cmd_state = ST_EXPECT_DIGIT_0;
cmd_number = 0;
} else {
// syntax error
cmd_state = ST_SYNTAX_ERROR;
}
}
else if (cmd_state == ST_EXPECT_CMD) {
// read command
if ((chr >= 'a' && chr <= 'z') || (chr >= 'A' && chr <= 'Z')) {
remote_cmd = chr;
cmd_state = ST_EXPECT_RETURN;
} else {
cmd_state = ST_SYNTAX_ERROR;
}
}
else if (cmd_state == ST_SYNTAX_ERROR) {
// syntax error
if (chr == '\r') {
sprintf(uart_buffer, "error:syntax\r\n");
uart_puts(UART_ID, uart_buffer);
cmd_state = ST_EXPECT_DIGIT_0;
cmd_number = 0;
}
}
else if (cmd_state == ST_EXPECT_RETURN) {
if (chr == '\n' || chr == ' ') {
// ignore newlines or spaces
}
else if (chr == '\r') {
if (cmd_echo) {
// FIXME
sprintf(uart_buffer,"\n");
uart_puts(UART_ID, uart_buffer);
}
// execute
if (remote_cmd == 'p') {
// toggle system power and/or reset imx
if (cmd_number == 0) {
turn_som_power_off();
sprintf(uart_buffer,"system: off\r\n");
uart_puts(UART_ID, uart_buffer);
} else if (cmd_number == 2) {
//reset_som();
sprintf(uart_buffer,"system: reset\r\n");
uart_puts(UART_ID, uart_buffer);
} else {
turn_som_power_on();
sprintf(uart_buffer,"system: on\r\n");
uart_puts(UART_ID, uart_buffer);
}
}
else if (remote_cmd == 'a') {
// TODO
// get system current (mA)
sprintf(uart_buffer,"%d\r\n",0);
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 'v' && cmd_number>=0 && cmd_number<=0) {
// TODO
// get cell voltage
sprintf(uart_buffer,"%d\r\n",0);
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 'V') {
// TODO
// get system voltage
sprintf(uart_buffer,"%d\r\n",0);
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 's') {
// TODO
sprintf(uart_buffer,FW_REV"normal,%d,%d,%d\r\n",0,0,0);
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 'u') {
// TODO
// turn reporting to i.MX on or off
}
else if (remote_cmd == 'w') {
// wake SoC
som_wake();
sprintf(uart_buffer,"system: wake\r\n");
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 'c') {
// get status of cells, current, voltage, fuel gauge
int mA = (int)(report_current*1000.0);
char mA_sign = ' ';
if (mA<0) {
mA = -mA;
mA_sign = '-';
}
int mV = (int)(report_volts*1000.0);
sprintf(uart_buffer,"%02d %02d %02d %02d %02d %02d %02d %02d mA%c%04dmV%05d %3d%% P%d\r\n",
(int)(report_cells_v[0]/100),
(int)(report_cells_v[1]/100),
(int)(report_cells_v[2]/100),
(int)(report_cells_v[3]/100),
(int)(report_cells_v[4]/100),
(int)(report_cells_v[5]/100),
(int)(report_cells_v[6]/100),
(int)(report_cells_v[7]/100),
mA_sign,
mA,
mV,
report_capacity_percentage,
som_is_powered?1:0);
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 'S') {
// TODO
// get charger system cycles in current state
sprintf(uart_buffer, "%d\r\n", 0);
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 'C') {
// TODO
// get battery capacity (mAh)
sprintf(uart_buffer,"%d/%d/%d\r\n",0,0,0);
uart_puts(UART_ID, uart_buffer);
}
else if (remote_cmd == 'e') {
// toggle serial echo
cmd_echo = cmd_number?1:0;
}
else {
sprintf(uart_buffer, "error:command\r\n");
uart_puts(UART_ID, uart_buffer);
}
cmd_state = ST_EXPECT_DIGIT_0;
cmd_number = 0;
} else {
cmd_state = ST_SYNTAX_ERROR;
}
}
}
#define SPI_BUF_LEN 0x8
uint8_t spi_buf[SPI_BUF_LEN];
unsigned char spi_cmd_state = ST_EXPECT_MAGIC;
unsigned char spi_command = '\0';
uint8_t spi_arg1 = 0;
void init_spi_client() {
gpio_set_function(PIN_SOM_MOSI, GPIO_FUNC_SPI);
gpio_set_function(PIN_SOM_MISO, GPIO_FUNC_SPI);
gpio_set_function(PIN_SOM_SS0, GPIO_FUNC_SPI);
gpio_set_function(PIN_SOM_SCK, GPIO_FUNC_SPI);
spi_init(spi1, 400 * 1000);
// we don't appreciate the wording, but it's the API we are given
spi_set_slave(spi1, true);
spi_set_format(spi1, 8, SPI_CPOL_0, SPI_CPHA_1, SPI_MSB_FIRST);
printf("# [spi] init_spi_client done\n");
}
/**
* @brief SPI command from imx poll function
*
* Ported from MNT Reform reform2-lpc-fw.
*/
void handle_spi_commands() {
int len = 0;
int all_zeroes = 1;
while (spi_is_readable(spi1) && len < SPI_BUF_LEN) {
// 0x00 is "repeated tx data"
spi_read_blocking(spi1, 0x00, &spi_buf[len], 1);
if (spi_buf[len] != 0) all_zeroes = 0;
len++;
}
if (len == 0) {
return;
}
//printf("# [spi] rx (len = %d): %02x %02x %02x %02x %02x %02x %02x %02x\n", len, spi_buf[0], spi_buf[1], spi_buf[2], spi_buf[3], spi_buf[4], spi_buf[5], spi_buf[6], spi_buf[7]);
// states:
// 0 arg1 byte expected
// 4 command byte expected
// 6 execute command
// 7 magic byte expected
for (uint8_t s = 0; s < len; s++) {
if (spi_cmd_state == ST_EXPECT_MAGIC) {
// magic byte found, prevents garbage data
// in the bus from triggering a command
if (spi_buf[s] == 0xb5) {
spi_cmd_state = ST_EXPECT_CMD;
}
}
else if (spi_cmd_state == ST_EXPECT_CMD) {
// read command
spi_command = spi_buf[s];
spi_cmd_state = ST_EXPECT_DIGIT_0;
}
else if (spi_cmd_state == ST_EXPECT_DIGIT_0) {
// read arg1 byte
spi_arg1 = spi_buf[s];
spi_cmd_state = ST_EXPECT_RETURN;
}
//printf("# [spi] after 0x%02x (pos %d): state %d cmd %c (%02x) arg %d\n", spi_buf[s], s, spi_cmd_state, spi_command, spi_command, spi_arg1);
}
if (spi_cmd_state == ST_EXPECT_MAGIC && !all_zeroes) {
// reset SPI0 block
// this is a workaround for confusion with
// software spi from BPI-CM4 where we get
// bit-shifted bytes
init_spi_client();
spi_cmd_state = ST_EXPECT_MAGIC;
spi_command = 0;
spi_arg1 = 0;
return;
}
if (spi_cmd_state != ST_EXPECT_RETURN) {
// waiting for more data
return;
}
printf("# [spi] exec: '%c' 0x%02x\n", spi_command, spi_arg1);
// clear receive buffer, reuse as send buffer
memset(spi_buf, 0, SPI_BUF_LEN);
// execute power state command
if (spi_command == 'p') {
// toggle system power and/or reset imx
if (spi_arg1 == 1) {
turn_som_power_off();
}
if (spi_arg1 == 2) {
turn_som_power_on();
}
if (spi_arg1 == 3) {
// TODO
//reset_som();
}
spi_buf[0] = som_is_powered;
}
// return firmware version and api info
else if (spi_command == 'f') {
if(spi_arg1 == 0) {
memcpy(spi_buf, FW_STRING1, 8);
}
else if(spi_arg1 == 1) {
memcpy(spi_buf, FW_STRING2, 2);
}
else {
memcpy(spi_buf, FW_STRING3, 8);
}
}
// execute status query command
else if (spi_command == 'q') {
uint8_t percentage = (uint8_t)report_capacity_percentage;
int16_t voltsInt = (int16_t)(report_volts*1000.0);
int16_t currentInt = (int16_t)(report_current*1000.0);
spi_buf[0] = (uint8_t)voltsInt;
spi_buf[1] = (uint8_t)(voltsInt >> 8);
spi_buf[2] = (uint8_t)currentInt;
spi_buf[3] = (uint8_t)(currentInt >> 8);
spi_buf[4] = (uint8_t)percentage;
spi_buf[5] = (uint8_t)0; // TODO "state" not implemented
spi_buf[6] = (uint8_t)0;
}
// get cell voltage
else if (spi_command == 'v') {
uint16_t volts = 0;
uint8_t cell1 = 0;
if (spi_arg1 == 1) {
cell1 = 4;
}
for (uint8_t c = 0; c < 4; c++) {
volts = report_cells_v[c + cell1];
spi_buf[c*2] = (uint8_t)volts;
spi_buf[(c*2)+1] = (uint8_t)(volts >> 8);
}
}
// get calculated capacity
else if (spi_command == 'c') {
uint16_t cap_accu = (uint16_t) report_capacity_max_ampsecs / 3.6;
uint16_t cap_min = (uint16_t) report_capacity_min_ampsecs / 3.6;
uint16_t cap_max = (uint16_t) report_capacity_max_ampsecs / 3.6;
spi_buf[0] = (uint8_t)cap_accu;
spi_buf[1] = (uint8_t)(cap_accu >> 8);
spi_buf[2] = (uint8_t)cap_min;
spi_buf[3] = (uint8_t)(cap_min >> 8);
spi_buf[4] = (uint8_t)cap_max;
spi_buf[5] = (uint8_t)(cap_max >> 8);
}
else if (spi_command == 'u') {
// not implemented
}
// FIXME: if we don't reset, SPI wants to transact the amount of bytes
// that we read above for unknown reasons
init_spi_client();
if (som_is_powered) {
spi_write_blocking(spi1, spi_buf, SPI_BUF_LEN);
}
spi_cmd_state = ST_EXPECT_MAGIC;
spi_command = 0;
spi_arg1 = 0;
return;
}
void on_uart_rx() {
while (uart_is_readable(UART_ID)) {
handle_commands(uart_getc(UART_ID));
}
}
int main() {
//set_sys_clock_48mhz();
stdio_init_all();
init_spi_client();
// UART to keyboard
uart_init(UART_ID, BAUD_RATE);
uart_set_format(UART_ID, DATA_BITS, STOP_BITS, PARITY);
uart_set_hw_flow(UART_ID, false, false);
uart_set_fifo_enabled(UART_ID, true);
gpio_set_function(PIN_KBD_UART_TX, GPIO_FUNC_UART);
gpio_set_function(PIN_KBD_UART_RX, GPIO_FUNC_UART);
int UART_IRQ = UART_ID == uart0 ? UART0_IRQ : UART1_IRQ;
// UART to som
uart_init(uart0, BAUD_RATE);
uart_set_format(uart0, DATA_BITS, STOP_BITS, PARITY);
uart_set_hw_flow(uart0, false, false);
uart_set_fifo_enabled(uart0, true);
gpio_set_function(PIN_SOM_UART_TX, GPIO_FUNC_UART);
gpio_set_function(PIN_SOM_UART_RX, GPIO_FUNC_UART);
gpio_set_function(PIN_SDA, GPIO_FUNC_I2C);
gpio_set_function(PIN_SCL, GPIO_FUNC_I2C);
bi_decl(bi_2pins_with_func(PIN_SDA, PIN_SCL, GPIO_FUNC_I2C));
i2c_init(i2c0, 100 * 1000);
gpio_init(PIN_LED_R);
gpio_init(PIN_LED_G);
gpio_init(PIN_LED_B);
gpio_set_dir(PIN_LED_R, 1);
gpio_set_dir(PIN_LED_G, 1);
gpio_set_dir(PIN_LED_B, 1);
gpio_init(PIN_1V1_ENABLE);
gpio_init(PIN_3V3_ENABLE);
gpio_init(PIN_5V_ENABLE);
gpio_set_dir(PIN_1V1_ENABLE, 1);
gpio_set_dir(PIN_3V3_ENABLE, 1);
gpio_set_dir(PIN_5V_ENABLE, 1);
gpio_put(PIN_1V1_ENABLE, 0);
gpio_put(PIN_3V3_ENABLE, 0);
gpio_put(PIN_5V_ENABLE, 0);
gpio_init(PIN_PWREN_LATCH);
gpio_set_dir(PIN_PWREN_LATCH, 1);
gpio_put(PIN_PWREN_LATCH, 0);
gpio_init(PIN_DISP_RESET);
gpio_init(PIN_DISP_EN);
gpio_set_dir(PIN_DISP_EN, 1);
gpio_set_dir(PIN_DISP_RESET, 1);
gpio_put(PIN_DISP_EN, 0);
gpio_put(PIN_DISP_RESET, 0);
gpio_init(PIN_FLIGHTMODE);
gpio_init(PIN_MODEM_POWER);
gpio_init(PIN_MODEM_RESET);
gpio_init(PIN_PHONE_DPR);
gpio_set_dir(PIN_FLIGHTMODE, 1);
gpio_set_dir(PIN_MODEM_POWER, 1);
gpio_set_dir(PIN_MODEM_RESET, 1);
gpio_set_dir(PIN_PHONE_DPR, 1);
gpio_put(PIN_FLIGHTMODE, 0); // active low
gpio_put(PIN_MODEM_POWER, 0); // active high
gpio_put(PIN_MODEM_RESET, 0); // active low (?)
gpio_put(PIN_PHONE_DPR, 0); // active high // causes 0.146W power use when high in off state!
gpio_put(PIN_LED_R, 0);
gpio_put(PIN_LED_G, 0);
gpio_put(PIN_LED_B, 0);
gpio_init(PIN_USB_SRC_ENABLE);
gpio_set_dir(PIN_USB_SRC_ENABLE, 1);
gpio_put(PIN_USB_SRC_ENABLE, 0);
// latch the PWR and display pins
gpio_put(PIN_PWREN_LATCH, 1);
gpio_put(PIN_PWREN_LATCH, 0);
unsigned int t = 0;
unsigned int t_report = 0;
int state = 0;
int request_sent = 0;
uint8_t rxdata[2];
union pd_msg tx;
int tx_id_count = 0;
union pd_msg rx_msg;
int power_objects = 0;
int max_voltage = 0;
sleep_ms(5000);
#ifdef FACTORY_MODE
// in factory mode, turn on power immediately to be able to flash
// the keyboard
turn_som_power_on();
#endif
printf("# [pocket_sysctl] entering main loop.\n");
while (true) {
// handle commands from keyboard
while (uart_is_readable(UART_ID)) {
handle_commands(uart_getc(UART_ID));
}
handle_spi_commands();
#ifdef ACM_ENABLED
// handle commands over usb serial
int usb_c = getchar_timeout_us(0);
if (usb_c != PICO_ERROR_TIMEOUT) {
printf("# [acm_command] '%c'\n", usb_c);
if (usb_c == '1') {
turn_som_power_on();
}
else if (usb_c == '0') {
turn_som_power_off();
}
else if (usb_c == 'p') {
print_pack_info = !print_pack_info;
}
}
#endif
if (state == 0) {
gpio_put(PIN_LED_R, 0);
power_objects = 0;
request_sent = 0;
// by default, we output 5V on VUSB
gpio_put(PIN_USB_SRC_ENABLE, 1);
//printf("# [pd] state 0\n");
// probe FUSB302BMPX
if (i2c_read_timeout_us(i2c0, FUSB_ADDR, rxdata, 1, false, I2C_TIMEOUT)) {
// 1. set auto GoodCRC
// AUTO_CRC in Switches1
// Address: 03h; Reset Value: 0b0010_0000
//printf("# [pd] FUSB probed.\n");
fusb_write_byte(FUSB_RESET, FUSB_RESET_SW_RES);
sleep_us(10);
// turn on all power
fusb_write_byte(FUSB_POWER, 0x0F);
// automatic retransmission
//fusb_write_byte(FUSB_CONTROL3, 0x07);
// AUTO_HARDRESET | AUTO_SOFTRESET | 3 retries | AUTO_RETRY
fusb_write_byte(FUSB_CONTROL3, (1<<4) | (1<<3) | (3<<1) | 1);
// flush rx buffer
fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
// pdwn means pulldown. 0 = no pull down
/* Measure CC1 */
fusb_write_byte(FUSB_SWITCHES0, 4|2|1); // MEAS_CC1|PDWN2 |PDWN1
sleep_us(250);
uint8_t cc1 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
//printf("# [pd] CC1: %d\n", cc1);
/* Measure CC2 */
fusb_write_byte(FUSB_SWITCHES0, 8|2|1); // MEAS_CC2|PDWN2 |PDWN1
sleep_us(250);
uint8_t cc2 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
//printf("# [pd] CC2: %d\n", cc2);
// detect orientation
if (cc1 > cc2) {
fusb_write_byte(FUSB_SWITCHES1, 4|1); // |AUTO_CRC|TXCC1
fusb_write_byte(FUSB_SWITCHES0, 4|2|1); // MEAS_CC1|PDWN2 |PDWN1
} else {
fusb_write_byte(FUSB_SWITCHES1, 4|2); // |AUTO_CRC|TXCC2
fusb_write_byte(FUSB_SWITCHES0, 8|2|1); // MEAS_CC2|PDWN2 |PDWN1
}
//printf("# [pd] switches set.\n");
fusb_write_byte(FUSB_RESET, FUSB_RESET_PD_RESET);
// automatic soft reset
// FIXME disturbs PD with some supplies
//fusb_write_byte(FUSB_CONTROL3, (1<<6) | (1<<4) | (1<<3) | (3<<1) | 1);
//sleep_ms(1);
//fusb_write_byte(FUSB_CONTROL3, (1<<4) | (1<<3) | (3<<1) | 1);
//printf("# [pd] auto hard/soft reset and retries set.\n");
t = 0;
state = 1;
} else {
if (t > 1000) {
printf("# [pd] state 0: fusb timeout.\n");
t = 0;
}
}
} else if (state == 1) {
//printf("[pocket-sysctl] state 1\n");
if (t>3000) {
printf("# [pd] state 1, timeout.\n");
float input_voltage = charger_dump();
max_dump();
t = 0;
// without batteries, the system dies here (brownout?)
// but the charger might have set up the requested voltage anyway
if (input_voltage < 6) {
fusb_write_byte(FUSB_CONTROL3, (1<<6) | (1<<4) | (1<<3) | (3<<1) | 1);
//sleep_ms(1);
fusb_write_byte(FUSB_CONTROL3, (1<<4) | (1<<3) | (3<<1) | 1);
state = 0;
} else {
state = 3;
}
}
int res = fusb_read_message(&rx_msg);
if (!res) {
//printf("# [pd] s1: charger responds, turning off USB_SRC\n");
// if a charger is responding, turn off our 5V output
gpio_put(PIN_USB_SRC_ENABLE, 0);
uint8_t msgtype = PD_MSGTYPE_GET(&rx_msg);
uint8_t numobj = PD_NUMOBJ_GET(&rx_msg);
if (msgtype == PD_MSGTYPE_SOURCE_CAPABILITIES) {
max_voltage = 0;
for (int i=0; i<numobj; i++) {
uint32_t pdo = rx_msg.obj[i];
if ((pdo & PD_PDO_TYPE) == PD_PDO_TYPE_FIXED) {
int voltage = print_src_fixed_pdo(i+1, pdo);
if (voltage > max_voltage && voltage <= 20) {
power_objects = i+1;
max_voltage = voltage;
}
} else {
printf("# [pd] state 1, not a fixed PDO: 0x%08x\n", pdo);
}
}
if (!request_sent) {
state = 2;
t = 0;
}
} else if (msgtype == PD_MSGTYPE_PS_RDY) {
// power supply is ready
printf("# [pd] state 1, power supply ready.\n");
request_sent = 0;
t = 0;
state = 3;
} else {
printf("# [pd] state 1, msg type: 0x%x numobj: %d\n", msgtype, numobj);
}
} else {
//sleep_ms(1);
//printf("# [pd] state 1, no message\n");
}
} else if (state == 2) {
printf("# [pd] state 2, requesting PO %d, %d V\n", power_objects, max_voltage);
tx.hdr = PD_MSGTYPE_REQUEST | PD_NUMOBJ(1) | PD_DATAROLE_UFP | PD_POWERROLE_SINK | PD_SPECREV_2_0;
tx.hdr &= ~PD_HDR_MESSAGEID;
tx.hdr |= (tx_id_count % 8) << PD_HDR_MESSAGEID_SHIFT;
int current = 1;
tx.obj[0] = PD_RDO_FV_MAX_CURRENT_SET(current)
| PD_RDO_FV_CURRENT_SET(current)
| PD_RDO_NO_USB_SUSPEND | PD_RDO_OBJPOS_SET(power_objects); // FIXME
fusb_send_message(&tx);
printf("# [pd] state 2, request sent.\n");
tx_id_count++;
t = 0;
request_sent = 1;
state = 1;
} else if (state == 3) {
gpio_put(PIN_LED_R, 1);
gpio_put(PIN_USB_SRC_ENABLE, 0);
charger_configure();
// charging
sleep_ms(1);
// running
if (t>2000) {
printf("# [pd] state 3.\n");
float input_voltage = charger_dump();
max_dump();
if (input_voltage < 6) {
printf("# [pd] input voltage below 6v, renegotiate.\n");
state = 0;
}
t = 0;
}
}
t++;
t_report++;
}
return 0;
}