WIP: pocket sysctl firmware initial
This commit is contained in:
parent
3cf67eb2dc
commit
0ca71b816d
|
@ -0,0 +1,24 @@
|
|||
cmake_minimum_required(VERSION 3.13)
|
||||
|
||||
# initialize the SDK based on PICO_SDK_PATH
|
||||
# note: this must happen before project()
|
||||
include(pico_sdk_import.cmake)
|
||||
|
||||
project(my_project)
|
||||
|
||||
# initialize the Raspberry Pi Pico SDK
|
||||
pico_sdk_init()
|
||||
|
||||
# rest of your project
|
||||
|
||||
add_executable(sysctl
|
||||
sysctl.c
|
||||
)
|
||||
|
||||
target_link_libraries(sysctl pico_stdlib hardware_i2c)
|
||||
|
||||
pico_enable_stdio_usb(sysctl 1)
|
||||
pico_enable_stdio_uart(sysctl 0)
|
||||
|
||||
pico_add_extra_outputs(sysctl)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
export PICO_SDK_PATH=$PWD/../../pico-sdk
|
||||
|
||||
mkdir -p build
|
||||
cd build
|
||||
cmake ..
|
||||
|
||||
make
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
#!/bin/bash
|
||||
|
||||
picotool load ./build/sysctl.uf2
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_FUSB302B_H
|
||||
#define PDB_FUSB302B_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pdb_msg.h"
|
||||
|
||||
/* Device ID register */
|
||||
#define FUSB_DEVICE_ID 0x01
|
||||
#define FUSB_DEVICE_ID_VERSION_ID_SHIFT 4
|
||||
#define FUSB_DEVICE_ID_VERSION_ID (0xF << FUSB_DEVICE_ID_VERSION_ID_SHIFT)
|
||||
#define FUSB_DEVICE_ID_PRODUCT_ID_SHIFT 2
|
||||
#define FUSB_DEVICE_ID_PRODUCT_ID (0x3 << FUSB_DEVICE_ID_PRODUCT_ID_SHIFT)
|
||||
#define FUSB_DEVICE_ID_REVISION_ID_SHIFT 0
|
||||
#define FUSB_DEVICE_ID_REVISION_ID (0x3 << FUSB_DEVICE_ID_REVISION_ID_SHIFT)
|
||||
|
||||
/* Switches0 register */
|
||||
#define FUSB_SWITCHES0 0x02
|
||||
#define FUSB_SWITCHES0_PU_EN2 (1 << 7)
|
||||
#define FUSB_SWITCHES0_PU_EN1 (1 << 6)
|
||||
#define FUSB_SWITCHES0_VCONN_CC2 (1 << 5)
|
||||
#define FUSB_SWITCHES0_VCONN_CC1 (1 << 4)
|
||||
#define FUSB_SWITCHES0_MEAS_CC2 (1 << 3)
|
||||
#define FUSB_SWITCHES0_MEAS_CC1 (1 << 2)
|
||||
#define FUSB_SWITCHES0_PDWN_2 (1 << 1)
|
||||
#define FUSB_SWITCHES0_PDWN_1 1
|
||||
|
||||
/* Switches1 register */
|
||||
#define FUSB_SWITCHES1 0x03
|
||||
#define FUSB_SWITCHES1_POWERROLE (1 << 7)
|
||||
#define FUSB_SWITCHES1_SPECREV_SHIFT 5
|
||||
#define FUSB_SWITCHES1_SPECREV (0x3 << FUSB_SWITCHES1_SPECREV_SHIFT)
|
||||
#define FUSB_SWITCHES1_DATAROLE (1 << 4)
|
||||
#define FUSB_SWITCHES1_AUTO_CRC (1 << 2)
|
||||
#define FUSB_SWITCHES1_TXCC2 (1 << 1)
|
||||
#define FUSB_SWITCHES1_TXCC1 1
|
||||
|
||||
/* Measure register */
|
||||
#define FUSB_MEASURE 0x04
|
||||
#define FUSB_MEASURE_MEAS_VBUS (1 << 6)
|
||||
#define FUSB_MEASURE_MDAC_SHIFT 0
|
||||
#define FUSB_MEASURE_MDAC (0x3F << FUSB_MEASURE_MDAC_SHIFT)
|
||||
|
||||
/* Slice register */
|
||||
#define FUSB_SLICE 0x05
|
||||
#define FUSB_SLICE_SDAC_HYS_SHIFT 6
|
||||
#define FUSB_SLICE_SDAC_HYS (0x3 << FUSB_SLICE_SDAC_HYS_SHIFT)
|
||||
#define FUSB_SLICE_SDAC_SHIFT 0
|
||||
#define FUSB_SLICE_SDAC (0x3F << FUSB_SLICE_SDAC_SHIFT)
|
||||
|
||||
/* Control0 register */
|
||||
#define FUSB_CONTROL0 0x06
|
||||
#define FUSB_CONTROL0_TX_FLUSH (1 << 6)
|
||||
#define FUSB_CONTROL0_INT_MASK (1 << 5)
|
||||
#define FUSB_CONTROL0_HOST_CUR_SHIFT 2
|
||||
#define FUSB_CONTROL0_HOST_CUR (0x3 << FUSB_CONTROL0_HOST_CUR_SHIFT)
|
||||
#define FUSB_CONTROL0_AUTO_PRE (1 << 1)
|
||||
#define FUSB_CONTROL0_TX_START 1
|
||||
|
||||
/* Control1 register */
|
||||
#define FUSB_CONTROL1 0x07
|
||||
#define FUSB_CONTROL1_ENSOP2DB (1 << 6)
|
||||
#define FUSB_CONTROL1_ENSOP1DB (1 << 5)
|
||||
#define FUSB_CONTROL1_BIST_MODE2 (1 << 4)
|
||||
#define FUSB_CONTROL1_RX_FLUSH (1 << 2)
|
||||
#define FUSB_CONTROL1_ENSOP2 (1 << 1)
|
||||
#define FUSB_CONTROL1_ENSOP1 1
|
||||
|
||||
/* Control2 register */
|
||||
#define FUSB_CONTROL2 0x08
|
||||
#define FUSB_CONTROL2_TOG_SAVE_PWR_SHIFT 6
|
||||
#define FUSB_CONTROL2_TOG_SAVE_PWR (0x3 << FUSB_CONTROL2_TOG_SAVE_PWR)
|
||||
#define FUSB_CONTROL2_TOG_RD_ONLY (1 << 5)
|
||||
#define FUSB_CONTROL2_WAKE_EN (1 << 3)
|
||||
#define FUSB_CONTROL2_MODE_SHIFT 1
|
||||
#define FUSB_CONTROL2_MODE (0x3 << FUSB_CONTROL2_MODE_SHIFT)
|
||||
#define FUSB_CONTROL2_TOGGLE 1
|
||||
|
||||
/* Control3 register */
|
||||
#define FUSB_CONTROL3 0x09
|
||||
#define FUSB_CONTROL3_SEND_HARD_RESET (1 << 6)
|
||||
#define FUSB_CONTROL3_BIST_TMODE (1 << 5)
|
||||
#define FUSB_CONTROL3_AUTO_HARDRESET (1 << 4)
|
||||
#define FUSB_CONTROL3_AUTO_SOFTRESET (1 << 3)
|
||||
#define FUSB_CONTROL3_N_RETRIES_SHIFT 1
|
||||
#define FUSB_CONTROL3_N_RETRIES (0x3 << FUSB_CONTROL3_N_RETRIES_SHIFT)
|
||||
#define FUSB_CONTROL3_AUTO_RETRY 1
|
||||
|
||||
/* Mask1 register */
|
||||
#define FUSB_MASK1 0x0A
|
||||
#define FUSB_MASK1_M_VBUSOK (1 << 7)
|
||||
#define FUSB_MASK1_M_ACTIVITY (1 << 6)
|
||||
#define FUSB_MASK1_M_COMP_CHNG (1 << 5)
|
||||
#define FUSB_MASK1_M_CRC_CHK (1 << 4)
|
||||
#define FUSB_MASK1_M_ALERT (1 << 3)
|
||||
#define FUSB_MASK1_M_WAKE (1 << 2)
|
||||
#define FUSB_MASK1_M_COLLISION (1 << 1)
|
||||
#define FUSB_MASK1_M_BC_LVL (1 << 0)
|
||||
|
||||
/* Power register */
|
||||
#define FUSB_POWER 0x0B
|
||||
#define FUSB_POWER_PWR3 (1 << 3)
|
||||
#define FUSB_POWER_PWR2 (1 << 2)
|
||||
#define FUSB_POWER_PWR1 (1 << 1)
|
||||
#define FUSB_POWER_PWR0 1
|
||||
|
||||
/* Reset register */
|
||||
#define FUSB_RESET 0x0C
|
||||
#define FUSB_RESET_PD_RESET (1 << 1)
|
||||
#define FUSB_RESET_SW_RES 1
|
||||
|
||||
/* OCPreg register */
|
||||
#define FUSB_OCPREG 0x0D
|
||||
#define FUSB_OCPREG_OCP_RANGE (1 << 3)
|
||||
#define FUSB_OCPREG_OCP_CUR_SHIFT 0
|
||||
#define FUSB_OCPREG_OCP_CUR (0x7 << FUSB_OCPREG_OCP_CUR_SHIFT)
|
||||
|
||||
/* Maska register */
|
||||
#define FUSB_MASKA 0x0E
|
||||
#define FUSB_MASKA_M_OCP_TEMP (1 << 7)
|
||||
#define FUSB_MASKA_M_TOGDONE (1 << 6)
|
||||
#define FUSB_MASKA_M_SOFTFAIL (1 << 5)
|
||||
#define FUSB_MASKA_M_RETRYFAIL (1 << 4)
|
||||
#define FUSB_MASKA_M_HARDSENT (1 << 3)
|
||||
#define FUSB_MASKA_M_TXSENT (1 << 2)
|
||||
#define FUSB_MASKA_M_SOFTRST (1 << 1)
|
||||
#define FUSB_MASKA_M_HARDRST 1
|
||||
|
||||
/* Maskb register */
|
||||
#define FUSB_MASKB 0x0F
|
||||
#define FUSB_MASKB_M_GCRCSENT 1
|
||||
|
||||
/* Control4 register */
|
||||
#define FUSB_CONTROL4 0x10
|
||||
#define FUSB_CONTROL4_TOG_EXIT_AUD 1
|
||||
|
||||
/* Status0a register */
|
||||
#define FUSB_STATUS0A 0x3C
|
||||
#define FUSB_STATUS0A_SOFTFAIL (1 << 5)
|
||||
#define FUSB_STATUS0A_RETRYFAIL (1 << 4)
|
||||
#define FUSB_STATUS0A_POWER3 (1 << 3)
|
||||
#define FUSB_STATUS0A_POWER2 (1 << 2)
|
||||
#define FUSB_STATUS0A_SOFTRST (1 << 1)
|
||||
#define FUSB_STATUS0A_HARDRST 1
|
||||
|
||||
/* Status1a register */
|
||||
#define FUSB_STATUS1A 0x3D
|
||||
#define FUSB_STATUS1A_TOGSS_SHIFT 3
|
||||
#define FUSB_STATUS1A_TOGSS (0x7 << FUSB_STATUS1A_TOGSS_SHIFT)
|
||||
#define FUSB_STATUS1A_RXSOP2DB (1 << 2)
|
||||
#define FUSB_STATUS1A_RXSOP1DB (1 << 1)
|
||||
#define FUSB_STATUS1A_RXSOP 1
|
||||
|
||||
/* Interrupta register */
|
||||
#define FUSB_INTERRUPTA 0x3E
|
||||
#define FUSB_INTERRUPTA_I_OCP_TEMP (1 << 7)
|
||||
#define FUSB_INTERRUPTA_I_TOGDONE (1 << 6)
|
||||
#define FUSB_INTERRUPTA_I_SOFTFAIL (1 << 5)
|
||||
#define FUSB_INTERRUPTA_I_RETRYFAIL (1 << 4)
|
||||
#define FUSB_INTERRUPTA_I_HARDSENT (1 << 3)
|
||||
#define FUSB_INTERRUPTA_I_TXSENT (1 << 2)
|
||||
#define FUSB_INTERRUPTA_I_SOFTRST (1 << 1)
|
||||
#define FUSB_INTERRUPTA_I_HARDRST 1
|
||||
|
||||
/* Interruptb register */
|
||||
#define FUSB_INTERRUPTB 0x3F
|
||||
#define FUSB_INTERRUPTB_I_GCRCSENT 1
|
||||
|
||||
/* Status0 register */
|
||||
#define FUSB_STATUS0 0x40
|
||||
#define FUSB_STATUS0_VBUSOK (1 << 7)
|
||||
#define FUSB_STATUS0_ACTIVITY (1 << 6)
|
||||
#define FUSB_STATUS0_COMP (1 << 5)
|
||||
#define FUSB_STATUS0_CRC_CHK (1 << 4)
|
||||
#define FUSB_STATUS0_ALERT (1 << 3)
|
||||
#define FUSB_STATUS0_WAKE (1 << 2)
|
||||
#define FUSB_STATUS0_BC_LVL_SHIFT 0
|
||||
#define FUSB_STATUS0_BC_LVL (0x3 << FUSB_STATUS0_BC_LVL_SHIFT)
|
||||
|
||||
/* Status1 register */
|
||||
#define FUSB_STATUS1 0x41
|
||||
#define FUSB_STATUS1_RXSOP2 (1 << 7)
|
||||
#define FUSB_STATUS1_RXSOP1 (1 << 6)
|
||||
#define FUSB_STATUS1_RX_EMPTY (1 << 5)
|
||||
#define FUSB_STATUS1_RX_FULL (1 << 4)
|
||||
#define FUSB_STATUS1_TX_EMPTY (1 << 3)
|
||||
#define FUSB_STATUS1_TX_FULL (1 << 2)
|
||||
#define FUSB_STATUS1_OVRTEMP (1 << 1)
|
||||
#define FUSB_STATUS1_OCP 1
|
||||
|
||||
/* Interrupt register */
|
||||
#define FUSB_INTERRUPT 0x42
|
||||
#define FUSB_INTERRUPT_I_VBUSOK (1 << 7)
|
||||
#define FUSB_INTERRUPT_I_ACTIVITY (1 << 6)
|
||||
#define FUSB_INTERRUPT_I_COMP_CHNG (1 << 5)
|
||||
#define FUSB_INTERRUPT_I_CRC_CHK (1 << 4)
|
||||
#define FUSB_INTERRUPT_I_ALERT (1 << 3)
|
||||
#define FUSB_INTERRUPT_I_WAKE (1 << 2)
|
||||
#define FUSB_INTERRUPT_I_COLLISION (1 << 1)
|
||||
#define FUSB_INTERRUPT_I_BC_LVL 1
|
||||
|
||||
/* FIFOs register */
|
||||
#define FUSB_FIFOS 0x43
|
||||
|
||||
#define FUSB_FIFO_TX_TXON 0xA1
|
||||
#define FUSB_FIFO_TX_SOP1 0x12
|
||||
#define FUSB_FIFO_TX_SOP2 0x13
|
||||
#define FUSB_FIFO_TX_SOP3 0x1B
|
||||
#define FUSB_FIFO_TX_RESET1 0x15
|
||||
#define FUSB_FIFO_TX_RESET2 0x16
|
||||
#define FUSB_FIFO_TX_PACKSYM 0x80
|
||||
#define FUSB_FIFO_TX_JAM_CRC 0xFF
|
||||
#define FUSB_FIFO_TX_EOP 0x14
|
||||
#define FUSB_FIFO_TX_TXOFF 0xFE
|
||||
|
||||
#define FUSB_FIFO_RX_TOKEN_BITS 0xE0
|
||||
#define FUSB_FIFO_RX_SOP 0xE0
|
||||
#define FUSB_FIFO_RX_SOP1 0xC0
|
||||
#define FUSB_FIFO_RX_SOP2 0xA0
|
||||
#define FUSB_FIFO_RX_SOP1DB 0x80
|
||||
#define FUSB_FIFO_RX_SOP2DB 0x60
|
||||
|
||||
|
||||
/*
|
||||
* FUSB status union
|
||||
*
|
||||
* Provides a nicer structure than just an array of uint8_t for working with
|
||||
* the FUSB302B status and interrupt flags.
|
||||
*/
|
||||
union fusb_status {
|
||||
uint8_t bytes[7];
|
||||
struct {
|
||||
uint8_t status0a;
|
||||
uint8_t status1a;
|
||||
uint8_t interrupta;
|
||||
uint8_t interruptb;
|
||||
uint8_t status0;
|
||||
uint8_t status1;
|
||||
uint8_t interrupt;
|
||||
};
|
||||
};
|
||||
|
||||
#endif /* PDB_FUSB302B_H */
|
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_PD_H
|
||||
#define PDB_PD_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* Macros for working with USB Power Delivery messages.
|
||||
*
|
||||
* This file is mostly written from the PD Rev. 2.0 spec, but the header is
|
||||
* written from the Rev. 3.0 spec.
|
||||
*/
|
||||
|
||||
/*
|
||||
* PD Header
|
||||
*/
|
||||
#define PD_HDR_MSGTYPE_SHIFT 0
|
||||
#define PD_HDR_MSGTYPE (0x1F << PD_HDR_MSGTYPE_SHIFT)
|
||||
#define PD_HDR_DATAROLE_SHIFT 5
|
||||
#define PD_HDR_DATAROLE (0x1 << PD_HDR_DATAROLE_SHIFT)
|
||||
#define PD_HDR_SPECREV_SHIFT 6
|
||||
#define PD_HDR_SPECREV (0x3 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_HDR_POWERROLE_SHIFT 8
|
||||
#define PD_HDR_POWERROLE (1 << PD_HDR_POWERROLE_SHIFT)
|
||||
#define PD_HDR_MESSAGEID_SHIFT 9
|
||||
#define PD_HDR_MESSAGEID (0x7 << PD_HDR_MESSAGEID_SHIFT)
|
||||
#define PD_HDR_NUMOBJ_SHIFT 12
|
||||
#define PD_HDR_NUMOBJ (0x7 << PD_HDR_NUMOBJ_SHIFT)
|
||||
#define PD_HDR_EXT (1 << 15)
|
||||
|
||||
/* Message types */
|
||||
#define PD_MSGTYPE_GET(msg) (((msg)->hdr & PD_HDR_MSGTYPE) >> PD_HDR_MSGTYPE_SHIFT)
|
||||
/* Control Message */
|
||||
#define PD_MSGTYPE_GOODCRC 0x01
|
||||
#define PD_MSGTYPE_GOTOMIN 0x02
|
||||
#define PD_MSGTYPE_ACCEPT 0x03
|
||||
#define PD_MSGTYPE_REJECT 0x04
|
||||
#define PD_MSGTYPE_PING 0x05
|
||||
#define PD_MSGTYPE_PS_RDY 0x06
|
||||
#define PD_MSGTYPE_GET_SOURCE_CAP 0x07
|
||||
#define PD_MSGTYPE_GET_SINK_CAP 0x08
|
||||
#define PD_MSGTYPE_DR_SWAP 0x09
|
||||
#define PD_MSGTYPE_PR_SWAP 0x0A
|
||||
#define PD_MSGTYPE_VCONN_SWAP 0x0B
|
||||
#define PD_MSGTYPE_WAIT 0x0C
|
||||
#define PD_MSGTYPE_SOFT_RESET 0x0D
|
||||
#define PD_MSGTYPE_NOT_SUPPORTED 0x10
|
||||
#define PD_MSGTYPE_GET_SOURCE_CAP_EXTENDED 0x11
|
||||
#define PD_MSGTYPE_GET_STATUS 0x12
|
||||
#define PD_MSGTYPE_FR_SWAP 0x13
|
||||
#define PD_MSGTYPE_GET_PPS_STATUS 0x14
|
||||
#define PD_MSGTYPE_GET_COUNTRY_CODES 0x15
|
||||
/* Data Message */
|
||||
#define PD_MSGTYPE_SOURCE_CAPABILITIES 0x01
|
||||
#define PD_MSGTYPE_REQUEST 0x02
|
||||
#define PD_MSGTYPE_BIST 0x03
|
||||
#define PD_MSGTYPE_SINK_CAPABILITIES 0x04
|
||||
#define PD_MSGTYPE_BATTERY_STATUS 0x05
|
||||
#define PD_MSGTYPE_ALERT 0x06
|
||||
#define PD_MSGTYPE_GET_COUNTRY_INFO 0x07
|
||||
#define PD_MSGTYPE_VENDOR_DEFINED 0x0F
|
||||
/* Extended Message */
|
||||
#define PD_MSGTYPE_SOURCE_CAPABILITIES_EXTENDED 0x01
|
||||
#define PD_MSGTYPE_STATUS 0x02
|
||||
#define PD_MSGTYPE_GET_BATTERY_CAP 0x03
|
||||
#define PD_MSGTYPE_GET_BATTERY_STATUS 0x04
|
||||
#define PD_MSGTYPE_BATTERY_CAPABILITIES 0x05
|
||||
#define PD_MSGTYPE_GET_MANUFACTURER_INFO 0x06
|
||||
#define PD_MSGTYPE_MANUFACTURER_INFO 0x07
|
||||
#define PD_MSGTYPE_SECURITY_REQUEST 0x08
|
||||
#define PD_MSGTYPE_SECURITY_RESPONSE 0x09
|
||||
#define PD_MSGTYPE_FIRMWARE_UPDATE_REQUEST 0x0A
|
||||
#define PD_MSGTYPE_FIRMWARE_UPDATE_RESPONSE 0x0B
|
||||
#define PD_MSGTYPE_PPS_STATUS 0x0C
|
||||
#define PD_MSGTYPE_COUNTRY_INFO 0x0D
|
||||
#define PD_MSGTYPE_COUNTRY_CODES 0x0E
|
||||
|
||||
/* Data roles */
|
||||
#define PD_DATAROLE_UFP (0x0 << PD_HDR_DATAROLE_SHIFT)
|
||||
#define PD_DATAROLE_DFP (0x1 << PD_HDR_DATAROLE_SHIFT)
|
||||
|
||||
/* Specification revisions */
|
||||
#define PD_SPECREV_1_0 (0x0 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_SPECREV_2_0 (0x1 << PD_HDR_SPECREV_SHIFT)
|
||||
#define PD_SPECREV_3_0 (0x2 << PD_HDR_SPECREV_SHIFT)
|
||||
|
||||
/* Port power roles */
|
||||
#define PD_POWERROLE_SINK (0x0 << PD_HDR_POWERROLE_SHIFT)
|
||||
#define PD_POWERROLE_SOURCE (0x1 << PD_HDR_POWERROLE_SHIFT)
|
||||
|
||||
/* Message ID */
|
||||
#define PD_MESSAGEID_GET(msg) (((msg)->hdr & PD_HDR_MESSAGEID) >> PD_HDR_MESSAGEID_SHIFT)
|
||||
|
||||
/* Number of data objects */
|
||||
#define PD_NUMOBJ(n) (((n) << PD_HDR_NUMOBJ_SHIFT) & PD_HDR_NUMOBJ)
|
||||
#define PD_NUMOBJ_GET(msg) (((msg)->hdr & PD_HDR_NUMOBJ) >> PD_HDR_NUMOBJ_SHIFT)
|
||||
|
||||
|
||||
/*
|
||||
* PD Extended Message Header
|
||||
*/
|
||||
#define PD_EXTHDR_DATA_SIZE_SHIFT 0
|
||||
#define PD_EXTHDR_DATA_SIZE (0x1FF << PD_EXTHDR_DATA_SIZE_SHIFT)
|
||||
#define PD_EXTHDR_REQUEST_CHUNK_SHIFT 10
|
||||
#define PD_EXTHDR_REQUEST_CHUNK (1 << PD_EXTHDR_REQUEST_CHUNK_SHIFT)
|
||||
#define PD_EXTHDR_CHUNK_NUMBER_SHIFT 11
|
||||
#define PD_EXTHDR_CHUNK_NUMBER (0xF << PD_EXTHDR_CHUNK_NUMBER_SHIFT)
|
||||
#define PD_EXTHDR_CHUNKED_SHIFT 15
|
||||
#define PD_EXTHDR_CHUNKED (1 << PD_EXTHDR_CHUNKED_SHIFT)
|
||||
|
||||
/* Data size */
|
||||
#define PD_DATA_SIZE(n) (((n) << PD_EXTHDR_DATA_SIZE_SHIFT) & PD_EXTHDR_DATA_SIZE)
|
||||
#define PD_DATA_SIZE_GET(msg) (((msg)->exthdr & PD_EXTHDR_DATA_SIZE) >> PD_EXTHDR_DATA_SIZE_SHIFT)
|
||||
|
||||
/* Chunk number */
|
||||
#define PD_CHUNK_NUMBER(n) (((n) << PD_EXTHDR_CHUNK_NUMBER_SHIFT) & PD_EXTHDR_CHUNK_NUMBER)
|
||||
#define PD_CHUNK_NUMBER_GET(msg) (((msg)->exthdr & PD_EXTHDR_CHUNK_NUMBER) >> PD_EXTHDR_CHUNK_NUMBER_SHIFT)
|
||||
|
||||
|
||||
/*
|
||||
* PD Power Data Object
|
||||
*/
|
||||
#define PD_PDO_TYPE_SHIFT 30
|
||||
#define PD_PDO_TYPE (0x3 << PD_PDO_TYPE_SHIFT)
|
||||
|
||||
/* PDO types */
|
||||
#define PD_PDO_TYPE_FIXED ((unsigned) (0x0 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_BATTERY ((unsigned) (0x1 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_VARIABLE ((unsigned) (0x2 << PD_PDO_TYPE_SHIFT))
|
||||
#define PD_PDO_TYPE_AUGMENTED ((unsigned) (0x3 << PD_PDO_TYPE_SHIFT))
|
||||
|
||||
#define PD_APDO_TYPE_SHIFT 28
|
||||
#define PD_APDO_TYPE (0x3 << PD_APDO_TYPE_SHIFT)
|
||||
|
||||
/* APDO types */
|
||||
#define PD_APDO_TYPE_PPS (0x0 << PD_APDO_TYPE_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO */
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT 29
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT 28
|
||||
#define PD_PDO_SRC_FIXED_USB_SUSPEND (1 << PD_PDO_SRC_FIXED_USB_SUSPEND_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT 27
|
||||
#define PD_PDO_SRC_FIXED_UNCONSTRAINED (1 << PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_USB_COMMS_SHIFT 26
|
||||
#define PD_PDO_SRC_FIXED_USB_COMMS (1 << PD_PDO_SRC_FIXED_USB_COMMS_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT 25
|
||||
#define PD_PDO_SRC_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SRC_FIXED_DUAL_ROLE_DATA_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT 24
|
||||
#define PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG (1 << PD_PDO_SRC_FIXED_UNCHUNKED_EXT_MSG_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT 20
|
||||
#define PD_PDO_SRC_FIXED_PEAK_CURRENT (0x3 << PD_PDO_SRC_FIXED_PEAK_CURRENT_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE_SHIFT 10
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE (0x3FF << PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
|
||||
#define PD_PDO_SRC_FIXED_CURRENT_SHIFT 0
|
||||
#define PD_PDO_SRC_FIXED_CURRENT (0x3FF << PD_PDO_SRC_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO current */
|
||||
#define PD_PDO_SRC_FIXED_CURRENT_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_CURRENT) >> PD_PDO_SRC_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Source Fixed PDO voltage */
|
||||
#define PD_PDO_SRC_FIXED_VOLTAGE_GET(pdo) (((pdo) & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT)
|
||||
|
||||
/* PD Programmable Power Supply APDO */
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_SHIFT 17
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE (0xFF << PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_SHIFT 8
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE (0xFF << PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_CURRENT_SHIFT 0
|
||||
#define PD_APDO_PPS_CURRENT (0x7F << PD_APDO_PPS_CURRENT_SHIFT)
|
||||
|
||||
/* PD Programmable Power Supply APDO voltages */
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MAX_VOLTAGE) >> PD_APDO_PPS_MAX_VOLTAGE_SHIFT)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_GET(pdo) (((pdo) & PD_APDO_PPS_MIN_VOLTAGE) >> PD_APDO_PPS_MIN_VOLTAGE_SHIFT)
|
||||
|
||||
#define PD_APDO_PPS_MAX_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MAX_VOLTAGE_SHIFT) & PD_APDO_PPS_MAX_VOLTAGE)
|
||||
#define PD_APDO_PPS_MIN_VOLTAGE_SET(v) (((v) << PD_APDO_PPS_MIN_VOLTAGE_SHIFT) & PD_APDO_PPS_MIN_VOLTAGE)
|
||||
|
||||
/* PD Programmable Power Supply APDO current */
|
||||
#define PD_APDO_PPS_CURRENT_GET(pdo) ((uint8_t) (((pdo) & PD_APDO_PPS_CURRENT) >> PD_APDO_PPS_CURRENT_SHIFT))
|
||||
|
||||
#define PD_APDO_PPS_CURRENT_SET(i) (((i) << PD_APDO_PPS_CURRENT_SHIFT) & PD_APDO_PPS_CURRENT)
|
||||
|
||||
/* TODO: other types of source PDO */
|
||||
|
||||
/* PD Sink Fixed PDO */
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT 29
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_PWR (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_PWR_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT 28
|
||||
#define PD_PDO_SNK_FIXED_HIGHER_CAP (1 << PD_PDO_SNK_FIXED_HIGHER_CAP_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT 27
|
||||
#define PD_PDO_SNK_FIXED_UNCONSTRAINED (1 << PD_PDO_SNK_FIXED_UNCONSTRAINED_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_USB_COMMS_SHIFT 26
|
||||
#define PD_PDO_SNK_FIXED_USB_COMMS (1 << PD_PDO_SNK_FIXED_USB_COMMS_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT 25
|
||||
#define PD_PDO_SNK_FIXED_DUAL_ROLE_DATA (1 << PD_PDO_SNK_FIXED_DUAL_ROLE_DATA_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE_SHIFT 10
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE (0x3FF << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT)
|
||||
#define PD_PDO_SNK_FIXED_CURRENT_SHIFT 0
|
||||
#define PD_PDO_SNK_FIXED_CURRENT (0x3FF << PD_PDO_SNK_FIXED_CURRENT_SHIFT)
|
||||
|
||||
/* PD Sink Fixed PDO current */
|
||||
#define PD_PDO_SNK_FIXED_CURRENT_SET(i) (((i) << PD_PDO_SNK_FIXED_CURRENT_SHIFT) & PD_PDO_SNK_FIXED_CURRENT)
|
||||
|
||||
/* PD Sink Fixed PDO voltage */
|
||||
#define PD_PDO_SNK_FIXED_VOLTAGE_SET(v) (((v) << PD_PDO_SNK_FIXED_VOLTAGE_SHIFT) & PD_PDO_SNK_FIXED_VOLTAGE)
|
||||
|
||||
/* TODO: other types of sink PDO */
|
||||
|
||||
|
||||
/*
|
||||
* PD Request Data Object
|
||||
*/
|
||||
#define PD_RDO_OBJPOS_SHIFT 28
|
||||
#define PD_RDO_OBJPOS (0x7 << PD_RDO_OBJPOS_SHIFT)
|
||||
#define PD_RDO_GIVEBACK_SHIFT 27
|
||||
#define PD_RDO_GIVEBACK (1 << PD_RDO_GIVEBACK_SHIFT)
|
||||
#define PD_RDO_CAP_MISMATCH_SHIFT 26
|
||||
#define PD_RDO_CAP_MISMATCH (1 << PD_RDO_CAP_MISMATCH_SHIFT)
|
||||
#define PD_RDO_USB_COMMS_SHIFT 25
|
||||
#define PD_RDO_USB_COMMS (1 << PD_RDO_USB_COMMS_SHIFT)
|
||||
#define PD_RDO_NO_USB_SUSPEND_SHIFT 24
|
||||
#define PD_RDO_NO_USB_SUSPEND (1 << PD_RDO_NO_USB_SUSPEND_SHIFT)
|
||||
#define PD_RDO_UNCHUNKED_EXT_MSG_SHIFT 23
|
||||
#define PD_RDO_UNCHUNKED_EXT_MSG (1 << PD_RDO_UNCHUNKED_EXT_MSG_SHIFT)
|
||||
|
||||
#define PD_RDO_OBJPOS_SET(i) (((i) << PD_RDO_OBJPOS_SHIFT) & PD_RDO_OBJPOS)
|
||||
#define PD_RDO_OBJPOS_GET(msg) (((msg)->obj[0] & PD_RDO_OBJPOS) >> PD_RDO_OBJPOS_SHIFT)
|
||||
|
||||
/* Fixed and Variable RDO, no GiveBack support */
|
||||
#define PD_RDO_FV_CURRENT_SHIFT 10
|
||||
#define PD_RDO_FV_CURRENT (0x3FF << PD_RDO_FV_CURRENT_SHIFT)
|
||||
#define PD_RDO_FV_MAX_CURRENT_SHIFT 0
|
||||
#define PD_RDO_FV_MAX_CURRENT (0x3FF << PD_RDO_FV_MAX_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_FV_CURRENT_SET(i) (((i) << PD_RDO_FV_CURRENT_SHIFT) & PD_RDO_FV_CURRENT)
|
||||
#define PD_RDO_FV_MAX_CURRENT_SET(i) (((i) << PD_RDO_FV_MAX_CURRENT_SHIFT) & PD_RDO_FV_MAX_CURRENT)
|
||||
|
||||
/* Fixed and Variable RDO with GiveBack support */
|
||||
#define PD_RDO_FV_MIN_CURRENT_SHIFT 0
|
||||
#define PD_RDO_FV_MIN_CURRENT (0x3FF << PD_RDO_FV_MIN_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_FV_MIN_CURRENT_SET(i) (((i) << PD_RDO_FV_MIN_CURRENT_SHIFT) & PD_RDO_FV_MIN_CURRENT)
|
||||
|
||||
/* TODO: Battery RDOs */
|
||||
|
||||
/* Programmable RDO */
|
||||
#define PD_RDO_PROG_VOLTAGE_SHIFT 9
|
||||
#define PD_RDO_PROG_VOLTAGE (0x7FF << PD_RDO_PROG_VOLTAGE_SHIFT)
|
||||
#define PD_RDO_PROG_CURRENT_SHIFT 0
|
||||
#define PD_RDO_PROG_CURRENT (0x7F << PD_RDO_PROG_CURRENT_SHIFT)
|
||||
|
||||
#define PD_RDO_PROG_VOLTAGE_SET(i) (((i) << PD_RDO_PROG_VOLTAGE_SHIFT) & PD_RDO_PROG_VOLTAGE)
|
||||
#define PD_RDO_PROG_CURRENT_SET(i) (((i) << PD_RDO_PROG_CURRENT_SHIFT) & PD_RDO_PROG_CURRENT)
|
||||
|
||||
|
||||
/*
|
||||
* Time values
|
||||
*
|
||||
* Where a range is specified, the middle of the range (rounded down to the
|
||||
* nearest millisecond) is used.
|
||||
*/
|
||||
#define PD_T_CHUNKING_NOT_SUPPORTED TIME_MS2I(45)
|
||||
#define PD_T_HARD_RESET_COMPLETE TIME_MS2I(4)
|
||||
#define PD_T_PS_TRANSITION TIME_MS2I(500)
|
||||
#define PD_T_SENDER_RESPONSE TIME_MS2I(27)
|
||||
#define PD_T_SINK_REQUEST TIME_MS2I(100)
|
||||
#define PD_T_TYPEC_SINK_WAIT_CAP TIME_MS2I(465)
|
||||
#define PD_T_PPS_REQUEST TIME_S2I(10)
|
||||
/* This is actually from Type-C, not Power Delivery, but who cares? */
|
||||
#define PD_T_PD_DEBOUNCE TIME_MS2I(15)
|
||||
|
||||
|
||||
/*
|
||||
* Counter maximums
|
||||
*/
|
||||
#define PD_N_HARD_RESET_COUNT 2
|
||||
|
||||
|
||||
/*
|
||||
* Value parameters
|
||||
*/
|
||||
#define PD_MAX_EXT_MSG_LEN 260
|
||||
#define PD_MAX_EXT_MSG_CHUNK_LEN 26
|
||||
#define PD_MAX_EXT_MSG_LEGACY_LEN 26
|
||||
|
||||
|
||||
/*
|
||||
* Unit conversions
|
||||
*
|
||||
* V: volt
|
||||
* CV: centivolt
|
||||
* MV: millivolt
|
||||
* PRV: Programmable RDO voltage unit (20 mV)
|
||||
* PDV: Power Delivery voltage unit (50 mV)
|
||||
* PAV: PPS APDO voltage unit (100 mV)
|
||||
*
|
||||
* A: ampere
|
||||
* CA: centiampere
|
||||
* MA: milliampere
|
||||
* PDI: Power Delivery current unit (10 mA)
|
||||
* PAI: PPS APDO current unit (50 mA)
|
||||
*
|
||||
* W: watt
|
||||
* CW: centiwatt
|
||||
* MW: milliwatt
|
||||
*
|
||||
* O: ohm
|
||||
* CO: centiohm
|
||||
* MO: milliohm
|
||||
*/
|
||||
#define PD_MV2PRV(mv) ((mv) / 20)
|
||||
#define PD_MV2PDV(mv) ((mv) / 50)
|
||||
#define PD_MV2PAV(mv) ((mv) / 100)
|
||||
#define PD_PRV2MV(prv) ((prv) * 20)
|
||||
#define PD_PDV2MV(pdv) ((pdv) * 50)
|
||||
#define PD_PAV2MV(pav) ((pav) * 100)
|
||||
|
||||
#define PD_MA2CA(ma) (((ma) + 10 - 1) / 10)
|
||||
#define PD_MA2PDI(ma) (((ma) + 10 - 1) / 10)
|
||||
#define PD_MA2PAI(ma) (((ma) + 50 - 1) / 50)
|
||||
#define PD_CA2PAI(ca) (((ca) + 5 - 1) / 5)
|
||||
#define PD_PDI2MA(pdi) ((pdi) * 10)
|
||||
#define PD_PAI2MA(pai) ((pai) * 50)
|
||||
#define PD_PAI2CA(pai) ((pai) * 5)
|
||||
|
||||
#define PD_MW2CW(mw) ((mw) / 10)
|
||||
|
||||
#define PD_MO2CO(mo) ((mo) / 10)
|
||||
|
||||
/* Get portions of a voltage in more normal units */
|
||||
#define PD_MV_V(mv) ((mv) / 1000)
|
||||
#define PD_MV_MV(mv) ((mv) % 1000)
|
||||
|
||||
#define PD_PDV_V(pdv) ((pdv) / 20)
|
||||
#define PD_PDV_CV(pdv) (5 * ((pdv) % 20))
|
||||
|
||||
#define PD_PAV_V(pav) ((pav) / 10)
|
||||
#define PD_PAV_CV(pav) (10 * ((pav) % 10))
|
||||
|
||||
/* Get portions of a PD current in more normal units */
|
||||
#define PD_PDI_A(pdi) ((pdi) / 100)
|
||||
#define PD_PDI_CA(pdi) ((pdi) % 100)
|
||||
|
||||
#define PD_PAI_A(pai) ((pai) / 20)
|
||||
#define PD_PAI_CA(pai) (5 * ((pai) % 20))
|
||||
|
||||
/* Get portions of a power in more normal units */
|
||||
#define PD_CW_W(cw) ((cw) / 100)
|
||||
#define PD_CW_CW(cw) ((cw) % 100)
|
||||
|
||||
/* Get portions of a resistance in more normal units */
|
||||
#define PD_CO_O(co) ((co) / 100)
|
||||
#define PD_CO_CO(co) ((co) % 100)
|
||||
|
||||
/*
|
||||
* Unit constants
|
||||
*/
|
||||
#define PD_MV_MIN 0
|
||||
#define PD_MV_MAX 21000
|
||||
#define PD_PDV_MIN PD_MV2PDV(PD_MV_MIN)
|
||||
#define PD_PDV_MAX PD_MV2PDV(PD_MV_MAX)
|
||||
|
||||
#define PD_MA_MIN 0
|
||||
#define PD_MA_MAX 5000
|
||||
#define PD_CA_MIN PD_MA2CA(PD_MA_MIN)
|
||||
#define PD_CA_MAX PD_MA2CA(PD_MA_MAX)
|
||||
#define PD_PDI_MIN PD_MA2PDI(PD_MA_MIN)
|
||||
#define PD_PDI_MAX PD_MA2PDI(PD_MA_MAX)
|
||||
|
||||
#define PD_MW_MIN 0
|
||||
#define PD_MW_MAX 100000
|
||||
|
||||
#define PD_MO_MIN 500
|
||||
#define PD_MO_MAX 655350
|
||||
|
||||
|
||||
#endif /* PDB_PD_H */
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* PD Buddy Firmware Library - USB Power Delivery for everyone
|
||||
* Copyright 2017-2018 Clayton G. Hobbs
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef PDB_MSG_H
|
||||
#define PDB_MSG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
* PD message union
|
||||
*
|
||||
* This can be safely read from or written to in any form without any
|
||||
* transformations because everything in the system is little-endian.
|
||||
*
|
||||
* Two bytes of padding are required at the start to prevent problems due to
|
||||
* alignment. Specifically, without the padding, &obj[0] != &bytes[2], making
|
||||
* the statement in the previous paragraph invalid.
|
||||
*/
|
||||
union pd_msg {
|
||||
struct {
|
||||
uint8_t _pad1[2];
|
||||
uint8_t bytes[30];
|
||||
} __attribute__((packed));
|
||||
struct {
|
||||
uint8_t _pad2[2];
|
||||
uint16_t hdr;
|
||||
union {
|
||||
uint32_t obj[7];
|
||||
struct {
|
||||
uint16_t exthdr;
|
||||
uint8_t data[26];
|
||||
};
|
||||
};
|
||||
} __attribute__((packed));
|
||||
};
|
||||
|
||||
|
||||
#endif /* PDB_MSG_H */
|
|
@ -0,0 +1,62 @@
|
|||
# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
|
||||
|
||||
# This can be dropped into an external project to help locate this SDK
|
||||
# It should be include()ed prior to project()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
|
||||
set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
|
||||
message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
|
||||
set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
|
||||
endif ()
|
||||
|
||||
if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
|
||||
message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
|
||||
set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
|
||||
set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
|
||||
|
||||
if (NOT PICO_SDK_PATH)
|
||||
if (PICO_SDK_FETCH_FROM_GIT)
|
||||
include(FetchContent)
|
||||
set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
|
||||
if (PICO_SDK_FETCH_FROM_GIT_PATH)
|
||||
get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
|
||||
endif ()
|
||||
FetchContent_Declare(
|
||||
pico_sdk
|
||||
GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
|
||||
GIT_TAG master
|
||||
)
|
||||
if (NOT pico_sdk)
|
||||
message("Downloading Raspberry Pi Pico SDK")
|
||||
FetchContent_Populate(pico_sdk)
|
||||
set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
|
||||
endif ()
|
||||
set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
|
||||
else ()
|
||||
message(FATAL_ERROR
|
||||
"SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
|
||||
if (NOT EXISTS ${PICO_SDK_PATH})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
|
||||
if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
|
||||
message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
|
||||
endif ()
|
||||
|
||||
set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
|
||||
|
||||
include(${PICO_SDK_INIT_CMAKE_FILE})
|
|
@ -0,0 +1,969 @@
|
|||
#include <stdio.h>
|
||||
#include "pico/stdlib.h"
|
||||
#include "pico/binary_info.h"
|
||||
#include "hardware/i2c.h"
|
||||
#include "hardware/irq.h"
|
||||
#include "fusb302b.h"
|
||||
#include "pd.h"
|
||||
|
||||
#define FW_REV "PREF1SYS D1 20220824"
|
||||
|
||||
#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_SOM_WAKE 19
|
||||
#define PIN_1V1_ENABLE 23
|
||||
#define PIN_3V3_ENABLE 24
|
||||
#define PIN_5V_ENABLE 25
|
||||
#define PIN_USB_SRC_ENABLE 28
|
||||
|
||||
// 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
|
||||
// MP2762A charger
|
||||
// https://www.monolithicpower.com/en/documentview/productdocument/index/version/2/document_type/Datasheet/lang/en/sku/MP2762AGV/document_id/9073/
|
||||
#define MPS_ADDR 0x5c
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// https://git.clarahobbs.com/pd-buddy/pd-buddy-firmware/src/branch/master/lib/src/fusb302b.c
|
||||
|
||||
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. */
|
||||
if ((fusb_read_byte(FUSB_FIFOS) & 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(uint32_t pdo) {
|
||||
int tmp;
|
||||
|
||||
/* Dual-role power */
|
||||
tmp = (pdo & PD_PDO_SRC_FIXED_DUAL_ROLE_PWR) >> PD_PDO_SRC_FIXED_DUAL_ROLE_PWR_SHIFT;
|
||||
if (tmp) {
|
||||
printf("\tdual_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("\tusb_suspend: %d\n", tmp);
|
||||
}
|
||||
|
||||
/* Unconstrained Power */
|
||||
tmp = (pdo & PD_PDO_SRC_FIXED_UNCONSTRAINED) >> PD_PDO_SRC_FIXED_UNCONSTRAINED_SHIFT;
|
||||
if (tmp) {
|
||||
printf("\tunconstrained_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("\tusb_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("\tdual_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("\tunchunked_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("\tpeak_i: %d\n", tmp);
|
||||
}
|
||||
|
||||
/* Voltage */
|
||||
tmp = (pdo & PD_PDO_SRC_FIXED_VOLTAGE) >> PD_PDO_SRC_FIXED_VOLTAGE_SHIFT;
|
||||
printf("\tv: %d.%02d V\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("\ti: %d.%02d A\n", PD_PDI_A(tmp), PD_PDI_CA(tmp));
|
||||
|
||||
return voltage;
|
||||
}
|
||||
|
||||
int charger_dump() {
|
||||
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);
|
||||
|
||||
printf("[charger info]\n");
|
||||
printf(" status: %x\n", status);
|
||||
printf(" fault: %x\n ------------\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 ------------\n", adc_ntc_v);
|
||||
|
||||
printf(" input_c_limit: %d\n", input_c_limit);
|
||||
printf(" input_v_limit: %d\n", input_v_limit);
|
||||
printf(" charge_c: %d\n", charge_c);
|
||||
printf(" precharge_c: %d\n", precharge_c);
|
||||
printf(" bat_full_v: %d\n ============\n", bat_full_v);
|
||||
|
||||
if (adc_input_v < 11) {
|
||||
// renegotiate PD
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void max_dump() {
|
||||
// disable write protection (CommStat)
|
||||
max_write_word(0x61, 0x0000);
|
||||
max_write_word(0x61, 0x0000);
|
||||
// fixme set thermistor config
|
||||
max_write_word_100(0xca, 0x58ef);
|
||||
// set pack cfg: 2 cells (0), 1 thermistor, 6v charge pump (?), no aoldo, btpken off (??)
|
||||
max_write_word_100(0xb5, (1<11)|(0<<8)|(0<<2)|0);
|
||||
|
||||
uint16_t comm_stat = max_read_word(0x61);
|
||||
uint16_t status = max_read_word(0x00);
|
||||
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));
|
||||
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);
|
||||
|
||||
printf("[pack info]\n");
|
||||
|
||||
printf(" comm_stat: %04x\n", comm_stat);
|
||||
|
||||
printf(" status: %04x\n", status);
|
||||
if (status & 0x8000) {
|
||||
printf(" `-- prot alert\n");
|
||||
}
|
||||
if (status & 0x0002) {
|
||||
printf(" `-- POR, clearing\n");
|
||||
max_write_word(0x61, 0x0000);
|
||||
max_write_word(0x61, 0x0000);
|
||||
max_write_word(0x00, status & (~0x0002));
|
||||
}
|
||||
printf(" prot_alert: %04x\n", prot_alert);
|
||||
printf(" prot_cfg2: %04x\n", prot_cfg2);
|
||||
printf(" therm_cfg: %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: %04x\n", prot_status);
|
||||
if (prot_status & (1<<14)) {
|
||||
printf(" `-- too hot\n");
|
||||
}
|
||||
if (prot_status & (1<<13)) {
|
||||
printf(" `-- full\n");
|
||||
}
|
||||
if (prot_status & (1<<12)) {
|
||||
printf(" `-- too cold for charge\n");
|
||||
}
|
||||
if (prot_status & (1<<11)) {
|
||||
printf(" `-- overvoltage\n");
|
||||
}
|
||||
if (prot_status & (1<<10)) {
|
||||
printf(" `-- overcharge current\n");
|
||||
}
|
||||
if (prot_status & (1<<9)) {
|
||||
printf(" `-- qoverflow\n");
|
||||
}
|
||||
if (prot_status & (1<<8)) {
|
||||
printf(" `-- prequal timeout\n");
|
||||
}
|
||||
if (prot_status & (1<<7)) {
|
||||
printf(" `-- imbalance\n");
|
||||
}
|
||||
if (prot_status & (1<<6)) {
|
||||
printf(" `-- perm fail\n");
|
||||
}
|
||||
if (prot_status & (1<<5)) {
|
||||
printf(" `-- die hot\n");
|
||||
}
|
||||
if (prot_status & (1<<4)) {
|
||||
printf(" `-- too hot for discharge\n");
|
||||
}
|
||||
if (prot_status & (1<<3)) {
|
||||
printf(" `-- undervoltage\n");
|
||||
}
|
||||
if (prot_status & (1<<2)) {
|
||||
printf(" `-- overdischarge current\n");
|
||||
}
|
||||
if (prot_status & (1<<1)) {
|
||||
printf(" `-- resdfault\n");
|
||||
}
|
||||
if (prot_status & (1<<0)) {
|
||||
printf(" `-- ship\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 ============\n", vpack);
|
||||
}
|
||||
|
||||
void turn_som_power_on() {
|
||||
gpio_put(PIN_LED_B, 1);
|
||||
|
||||
printf("[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_5V_ENABLE, 1);
|
||||
|
||||
sleep_ms(10);
|
||||
gpio_put(PIN_DISP_EN, 1);
|
||||
sleep_ms(10);
|
||||
gpio_put(PIN_DISP_RESET, 1); // not connected?
|
||||
}
|
||||
|
||||
void turn_som_power_off() {
|
||||
gpio_put(PIN_LED_B, 0);
|
||||
|
||||
printf("[turn_som_power_off]\n");
|
||||
gpio_put(PIN_DISP_RESET, 0);
|
||||
gpio_put(PIN_DISP_EN, 0);
|
||||
|
||||
gpio_put(PIN_5V_ENABLE, 0);
|
||||
sleep_ms(10);
|
||||
gpio_put(PIN_3V3_ENABLE, 0);
|
||||
sleep_ms(10);
|
||||
gpio_put(PIN_1V1_ENABLE, 0);
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
|
||||
#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
|
||||
|
||||
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) {
|
||||
printf("rx: [%c]\n", 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",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') {
|
||||
// TODO
|
||||
// wake SoC
|
||||
}
|
||||
else if (remote_cmd == 'c') {
|
||||
// TODO
|
||||
// get status of cells, current, voltage, fuel gauge
|
||||
sprintf(uart_buffer, "%d\r\n", 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_uart_rx() {
|
||||
while (uart_is_readable(UART_ID)) {
|
||||
handle_commands(uart_getc(UART_ID));
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
stdio_init_all();
|
||||
|
||||
//sleep_ms(2000);
|
||||
// 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;
|
||||
//irq_set_exclusive_handler(UART_IRQ, on_uart_rx);
|
||||
//irq_set_enabled(UART_IRQ, true);
|
||||
//uart_set_irq_enables(UART_ID, true, false); // bool rx_has_data, bool tx_needs_data
|
||||
|
||||
printf("uart setup\n");
|
||||
|
||||
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_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_put(PIN_LED_R, 0);
|
||||
gpio_put(PIN_LED_G, 0);
|
||||
gpio_put(PIN_LED_B, 0);
|
||||
|
||||
unsigned int t = 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;
|
||||
|
||||
// https://www.reclaimerlabs.com/blog/2017/2/1/usb-c-for-engineers-part-3
|
||||
|
||||
printf("main loop\n");
|
||||
|
||||
while (true) {
|
||||
sleep_ms(1);
|
||||
|
||||
while (uart_is_readable(UART_ID)) {
|
||||
handle_commands(uart_getc(UART_ID));
|
||||
}
|
||||
|
||||
if (state == 0) {
|
||||
gpio_put(PIN_LED_R, 0);
|
||||
power_objects = 0;
|
||||
|
||||
//printf("[pocket-sysctl] 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
|
||||
// TODO: figure out CC direction?
|
||||
|
||||
printf("[pocket-sysctl] FUSB probed.\n");
|
||||
|
||||
fusb_write_byte(FUSB_RESET, FUSB_RESET_SW_RES);
|
||||
// turn on all power
|
||||
fusb_write_byte(FUSB_POWER, 0x0F);
|
||||
fusb_write_byte(FUSB_CONTROL3, 0x07);
|
||||
fusb_write_byte(FUSB_CONTROL1, FUSB_CONTROL1_RX_FLUSH);
|
||||
|
||||
/* Measure CC1 */
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x07);
|
||||
sleep_us(250);
|
||||
uint8_t cc1 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
|
||||
|
||||
printf("[pocket-sysctl] CC1: %d\n", cc1);
|
||||
|
||||
/* Measure CC2 */
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x0B);
|
||||
sleep_us(250);
|
||||
uint8_t cc2 = fusb_read_byte(FUSB_STATUS0) & FUSB_STATUS0_BC_LVL;
|
||||
|
||||
printf("[pocket-sysctl] CC2: %d\n", cc2);
|
||||
|
||||
if (cc1 > cc2) {
|
||||
fusb_write_byte(FUSB_SWITCHES1, 0x25);
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x07);
|
||||
} else {
|
||||
fusb_write_byte(FUSB_SWITCHES1, 0x26);
|
||||
fusb_write_byte(FUSB_SWITCHES0, 0x0B);
|
||||
}
|
||||
|
||||
printf("[pocket-sysctl] switches set\n");
|
||||
|
||||
//fusb_write_byte(FUSB_RESET, FUSB_RESET_PD_RESET);
|
||||
t = 0;
|
||||
state = 1;
|
||||
} else {
|
||||
if (t > 1000) {
|
||||
printf("[pocket-sysctl] state 0: fusb timeout\n");
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (state == 1) {
|
||||
//printf("[pocket-sysctl] state 2\n");
|
||||
|
||||
if (t>2000) {
|
||||
printf("[pocket-sysctl] state 2, timeout\n");
|
||||
|
||||
// issue hard reset
|
||||
fusb_write_byte(FUSB_CONTROL3, (1<<6) | 0x07);
|
||||
sleep_ms(1);
|
||||
fusb_write_byte(FUSB_CONTROL3, 0x07);
|
||||
|
||||
request_sent = 0;
|
||||
state = 0;
|
||||
t = 0;
|
||||
}
|
||||
|
||||
int res = fusb_read_message(&rx_msg);
|
||||
|
||||
if (!res) {
|
||||
uint8_t msgtype = PD_MSGTYPE_GET(&rx_msg);
|
||||
uint8_t numobj = PD_NUMOBJ_GET(&rx_msg);
|
||||
printf(" msg type: %x numobj: %d\n", msgtype, numobj);
|
||||
if (msgtype == PD_MSGTYPE_SOURCE_CAPABILITIES) {
|
||||
int 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(pdo);
|
||||
if (voltage > max_voltage && voltage <= 20) {
|
||||
power_objects = i+1;
|
||||
max_voltage = voltage;
|
||||
}
|
||||
} else {
|
||||
printf("not a fixed PDO: %08x\n", pdo);
|
||||
}
|
||||
}
|
||||
if (!request_sent) {
|
||||
state = 2;
|
||||
t = 0;
|
||||
}
|
||||
} else if (msgtype == PD_MSGTYPE_PS_RDY) {
|
||||
// power supply is ready
|
||||
printf("[pocket-sysctl] power supply ready!\n");
|
||||
request_sent = 0;
|
||||
t = 0;
|
||||
state = 3;
|
||||
}
|
||||
}
|
||||
} else if (state == 2) {
|
||||
printf("[pocket-sysctl] state 2, requesting PO %d\n", power_objects);
|
||||
|
||||
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("[pocket-sysctl] request sent.\n");
|
||||
|
||||
tx_id_count++;
|
||||
|
||||
request_sent = 1;
|
||||
state = 1;
|
||||
} else if (state == 3) {
|
||||
gpio_put(PIN_LED_R, 1);
|
||||
|
||||
// running
|
||||
if (t>2000) {
|
||||
printf("[pocket-sysctl] state 3\n");
|
||||
//i2c_scan();
|
||||
|
||||
int renegotiate = charger_dump();
|
||||
max_dump();
|
||||
|
||||
if (renegotiate) {
|
||||
state = 0;
|
||||
}
|
||||
|
||||
t = 0;
|
||||
}
|
||||
}
|
||||
|
||||
t++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue