diff options
Diffstat (limited to 'firmware')
42 files changed, 8401 insertions, 0 deletions
| diff --git a/firmware/e300/rev_b/Makefile b/firmware/e300/rev_b/Makefile new file mode 100644 index 000000000..0f13dc60b --- /dev/null +++ b/firmware/e300/rev_b/Makefile @@ -0,0 +1,76 @@ +# +# Copyright 2009 Ettus Research LLC +# + +################################################## +# Compiler +################################################## +CC = avr-gcc +OBJCOPY = avr-objcopy +STRIP = avr-strip +OBJDUMP = avr-objdump +SREC = srec_cat +CFLAGS = -Os -std=gnu99 -Wall -fshort-enums -pedantic-errors -Wl,--gc-sections \ +	-Wstrict-prototypes -Wmissing-prototypes -Wcast-align -Wshadow \ +	-DENABLE_SERIAL -DCHARGER_TI -DLED_POLARITY -DDEBUG_VOID +# -DENABLE_SERIAL	: Output serial debug +# -DCHARGER_TI		: Use TI charger (rev B) instead of LTC (rev A) +# -DLED_POLARITY	: Dual-polarity LED on rev B +# -DDDR3L		: Lower DDR voltage (rev B R-divider changed, so disable this to get back into nominal range) +# -DDEBUG_VOID		: Use (void) debug function replacements instead of inline NOOP (use if .text overflows) +# -DI2C_REWORK		: Rev A only +# -DDEBUG		: Enable debug routines (LED blinks, etc) +# -DDEBUG_SAFETY	: Extra debug prints +# -DATTINY88_DIP	: ATTINY88 DIP testing on STK600 +# -DHARDWIRE_ENABLE : LTC3675 dedicated enable lines - don't use + +#-Werror +#-D IO_DEBUG + +################################################## +# Files +################################################## +HDRS = +SRCS =  main.c io.c power.c ltc3675.c i2c.c debug.c bq24190.c +TARGET = main + +################################################## +# Device +################################################## +MMCU = attiny88 +#PROGRAMMER = avrisp2 +PROGRAMMER = stk600 +PORT = usb +AVRDUDE = avrdude -p $(MMCU) -c $(PROGRAMMER) -P $(PORT) + +################################################## +# Global Targets +################################################## +all: $(TARGET).hex + +clean: +	$(RM) *.o *.elf *.hex + +install: all +	$(AVRDUDE) -U flash:w:$(TARGET).hex:i + +################################################## +# Dependency Targets +################################################## +fuses.hex: $(TARGET).elf +	$(OBJCOPY) -j .fuse -O ihex $< $@ --change-section-lma .fuse=0 + +lfuse.hex: fuses.hex +	$(SREC) $< -Intel -crop 0x00 0x01 -offset  0x00 -O $@ -Intel + +hfuse.hex: fuses.hex +	$(SREC) $< -Intel -crop 0x01 0x02 -offset -0x01 -O $@ -Intel + +$(TARGET).hex: $(TARGET).elf +	$(OBJCOPY) -R .eeprom -R .fuse -O ihex $< $@ + +$(TARGET).elf: $(SRCS:.c=.o) +	$(CC) -mmcu=$(MMCU) $^ -o $@ + +%.o: %.c $(HDRS) Makefile +	$(CC) -mmcu=$(MMCU) -c $< -o $@ $(CFLAGS) diff --git a/firmware/e300/rev_b/PMC.atsln b/firmware/e300/rev_b/PMC.atsln new file mode 100644 index 000000000..e9f4ffc79 --- /dev/null +++ b/firmware/e300/rev_b/PMC.atsln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Atmel Studio Solution File, Format Version 11.00 +Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "PMC", "PMC.cproj", "{A379D421-4236-44AF-A711-52B7BDA29919}" +EndProject +Global +	GlobalSection(SolutionConfigurationPlatforms) = preSolution +		Debug|AVR = Debug|AVR +		Release (DDR3L)|AVR = Release (DDR3L)|AVR +		Release (DDR3L, Ti)|AVR = Release (DDR3L, Ti)|AVR +		Release (Dev)|AVR = Release (Dev)|AVR +		Release|AVR = Release|AVR +	EndGlobalSection +	GlobalSection(ProjectConfigurationPlatforms) = postSolution +		{A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.ActiveCfg = Debug|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.Build.0 = Debug|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.ActiveCfg = Release (DDR3L)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.Build.0 = Release (DDR3L)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.ActiveCfg = Release (DDR3L, Ti)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.Build.0 = Release (DDR3L, Ti)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.ActiveCfg = Release (Dev)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.Build.0 = Release (Dev)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.ActiveCfg = Release|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.Build.0 = Release|AVR +	EndGlobalSection +	GlobalSection(SolutionProperties) = preSolution +		HideSolutionNode = FALSE +	EndGlobalSection +EndGlobal diff --git a/firmware/e300/rev_b/PMC.cproj b/firmware/e300/rev_b/PMC.cproj new file mode 100644 index 000000000..e3784d10b --- /dev/null +++ b/firmware/e300/rev_b/PMC.cproj @@ -0,0 +1,288 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +  <PropertyGroup> +    <SchemaVersion>2.0</SchemaVersion> +    <ProjectVersion>6.0</ProjectVersion> +    <ToolchainName>com.Atmel.AVRGCC8</ToolchainName> +    <ProjectGuid>{a379d421-4236-44af-a711-52b7bda29919}</ProjectGuid> +    <avrdevice>ATtiny88</avrdevice> +    <avrdeviceseries>none</avrdeviceseries> +    <OutputType>Executable</OutputType> +    <Language>C</Language> +    <OutputFileName>$(MSBuildProjectName)</OutputFileName> +    <OutputFileExtension>.elf</OutputFileExtension> +    <OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory> +    <AssemblyName>PMC</AssemblyName> +    <Name>PMC</Name> +    <RootNamespace>PMC</RootNamespace> +    <ToolchainFlavour>Native</ToolchainFlavour> +    <KeepTimersRunning>true</KeepTimersRunning> +    <OverrideVtor>false</OverrideVtor> +    <OverrideVtorValue /> +    <eraseonlaunchrule>0</eraseonlaunchrule> +    <AsfVersion>3.1.3</AsfVersion> +    <avrtool>com.atmel.avrdbg.tool.stk600</avrtool> +    <avrtoolinterface>ISP</avrtoolinterface> +    <com_atmel_avrdbg_tool_stk600> +      <ToolType>com.atmel.avrdbg.tool.stk600</ToolType> +      <ToolName>STK600</ToolName> +      <ToolNumber>004A8D68669B</ToolNumber> +      <KeepTimersRunning>true</KeepTimersRunning> +      <OverrideVtor>false</OverrideVtor> +      <OverrideVtorValue> +      </OverrideVtorValue> +      <Channel> +        <host>127.0.0.1</host> +        <port>49172</port> +        <ssl>False</ssl> +      </Channel> +      <ToolOptions> +        <InterfaceName>ISP</InterfaceName> +        <InterfaceProperties> +          <JtagDbgClock>249000</JtagDbgClock> +          <JtagProgClock>1000000</JtagProgClock> +          <IspClock>25000</IspClock> +          <JtagInChain>false</JtagInChain> +          <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession> +          <JtagDevicesBefore>0</JtagDevicesBefore> +          <JtagDevicesAfter>0</JtagDevicesAfter> +          <JtagInstrBitsBefore>0</JtagInstrBitsBefore> +          <JtagInstrBitsAfter>0</JtagInstrBitsAfter> +        </InterfaceProperties> +      </ToolOptions> +    </com_atmel_avrdbg_tool_stk600> +    <com_atmel_avrdbg_tool_ispmk2> +      <ToolType>com.atmel.avrdbg.tool.ispmk2</ToolType> +      <ToolName>AVRISP mkII</ToolName> +      <ToolNumber>000200136505</ToolNumber> +      <KeepTimersRunning>true</KeepTimersRunning> +      <OverrideVtor>false</OverrideVtor> +      <OverrideVtorValue> +      </OverrideVtorValue> +      <Channel> +        <host>127.0.0.1</host> +        <port>49228</port> +        <ssl>False</ssl> +      </Channel> +      <ToolOptions> +        <InterfaceName>ISP</InterfaceName> +        <InterfaceProperties> +          <JtagDbgClock>249000</JtagDbgClock> +          <JtagProgClock>1000000</JtagProgClock> +          <IspClock>8000</IspClock> +          <JtagInChain>false</JtagInChain> +          <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession> +          <JtagDevicesBefore>0</JtagDevicesBefore> +          <JtagDevicesAfter>0</JtagDevicesAfter> +          <JtagInstrBitsBefore>0</JtagInstrBitsBefore> +          <JtagInstrBitsAfter>0</JtagInstrBitsAfter> +        </InterfaceProperties> +      </ToolOptions> +    </com_atmel_avrdbg_tool_ispmk2> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>I2C_REWORK</Value> +            <Value>ENABLE_SERIAL</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +      </AvrGcc> +    </ToolchainSettings> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>ATTINY88_DIP</Value> +            <Value>DEBUG</Value> +            <Value>I2C_REWORK</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +        <avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel> +      </AvrGcc> +    </ToolchainSettings> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L)' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>I2C_REWORK</Value> +            <Value>ENABLE_SERIAL</Value> +            <Value>DDR3L</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +      </AvrGcc> +    </ToolchainSettings> +    <OutputPath>bin\Release (DDR3)\</OutputPath> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release (Dev)' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>I2C_REWORK</Value> +            <Value>ENABLE_SERIAL</Value> +            <Value>ATTINY88_DIP</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection>True</avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +        <avrgcc.linker.optimization.GarbageCollectUnusedSections>True</avrgcc.linker.optimization.GarbageCollectUnusedSections> +      </AvrGcc> +    </ToolchainSettings> +    <OutputPath>bin\Release (Dev)\</OutputPath> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L, Ti)' "> +    <ToolchainSettings> +      <AvrGcc> +  <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +  <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +  <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +  <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +  <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +  <avrgcc.compiler.symbols.DefSymbols> +    <ListValues> +      <Value>I2C_REWORK_disabled</Value> +      <Value>DDR3L_disabled_revB_R_FB_network_changed</Value> +      <Value>CHARGER_TI</Value> +      <Value>ENABLE_SERIAL_disabled</Value> +      <Value>LED_POLARITY</Value> +    </ListValues> +  </avrgcc.compiler.symbols.DefSymbols> +  <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +  <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +  <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +  <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +  <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +  <avrgcc.linker.libraries.Libraries> +    <ListValues> +      <Value>m</Value> +    </ListValues> +  </avrgcc.linker.libraries.Libraries> +</AvrGcc> +    </ToolchainSettings> +    <OutputPath>bin\Release (DDR3L, Ti)\</OutputPath> +  </PropertyGroup> +  <ItemGroup> +    <Compile Include="bq24190.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="bq24190.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="config.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="debug.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="debug.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="error.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="global.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="i2c.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="i2c.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="io.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="io.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc3675.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc3675.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc4155.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc4155.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="main.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="power.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="power.h"> +      <SubType>compile</SubType> +    </Compile> +  </ItemGroup> +  <Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" /> +</Project>
\ No newline at end of file diff --git a/firmware/e300/rev_b/bq24190.c b/firmware/e300/rev_b/bq24190.c new file mode 100644 index 000000000..029beacf6 --- /dev/null +++ b/firmware/e300/rev_b/bq24190.c @@ -0,0 +1,335 @@ +/* + * bq24190.c + * + * Created: 11/12/2012 4:58:12 PM + *  Author: Balint Seeber + */ + +#ifdef CHARGER_TI + +#include "config.h" +#include "bq24190.h" + +#include <util/delay.h> + +#include "io.h" +#include "i2c.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +#ifndef I2C_REWORK +#include "power.h" +#endif // I2C_REWORK + +static io_pin_t USBPM_IRQ	= IO_PB(1); + +#ifdef ATTINY88_DIP + +static io_pin_t CHRG_SDA     = IO_PC(2); +static io_pin_t CHRG_SCL     = IO_PC(3); + +#else + +#ifdef I2C_REWORK + +static io_pin_t CHRG_SDA     = IO_PC(4); +static io_pin_t CHRG_SCL     = IO_PC(5); + +#else + +#define CHRG_SDA	PWR_SDA +#define CHRG_SCL	PWR_SCL + +#endif // I2C_REWORK + +#endif // ATTINY88_DIP + +const bool _bq24190_pull_up = false; + +#define BQ24190_BASE_ADDRESS    (0x6B << 1) +#define BQ24190_WRITE_ADDRESS   (BQ24190_BASE_ADDRESS + 0) +#define BQ24190_READ_ADDRESS    (BQ24190_BASE_ADDRESS + 1) + +enum BQ24190Registers +{ +	BQ24190_REG_INPUT_SOURCE_CTL= 0, +	BQ24190_REG_PWR_ON_CONFIG	= 1, +	BQ24190_REG_CHARGE_CURRENT	= 2, +	BQ24190_REG_PRE_TERM_CURRENT= 3, +	BQ24190_REG_CHARGE_VOLTAGE	= 4, +	BQ24190_REG_TIMER_CONTROL	= 5, +	BQ24190_REG_SYSTEM_STATUS	= 8, +	BQ24190_REG_FAULT			= 9 +}; +/* +enum BQ24190TimerControl +{ +	 +}; +*/ +enum BQ24190Shifts +{ +	BQ24190_SHIFTS_CHARGER_CONFIG	= 4, +	BQ24190_SHIFTS_I2C_WATCHDOG		= 4, +	BQ24190_SHIFTS_CHARGER_STATUS	= 4, +	BQ24190_SHIFTS_CHARGER_FAULT	= 4, +}; + +enum BQ24190VBusStatus +{ +	BQ24190_VBUS_UNKNOWN, +	BQ24190_VBUS_USB, +	BQ24190_VBUS_ADAPTER, +	BQ24190_VBUS_OTG +}; + +enum BQ24190ChargerStatus +{ +	BQ24190_CHRG_STAT_NOT_CHARGING, +	BQ24190_CHRG_STAT_PRE_CHARGE, +	BQ24190_CHRG_STAT_FAST_CHARGING, +	BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE, +	BQ24190_CHRG_STAT_MASK = BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE +}; + +enum BQ24190SystemStatus +{ +	BQ24190_STATUS_DPM					= 0x08, +	BQ24190_STATUS_POWER_GOOD			= 0x04, +	BQ24190_STATUS_THERMAL_REGULATION	= 0x02, +	BQ24190_STATUS_VSYSMIN_REGULATION	= 0x01 +}; + +enum BQ24190Faults +{ +	BQ24190_FAULT_WATCHDOG_EXPIRED	= 0x80, +	BQ24190_FAULT_VBUS_OVERLOADED	= 0x40, +	BQ24190_FAULT_BATOVP			= 0x08 +}; + +enum BQ24190ChargerFaults +{ +	BQ24190_CHRGFAULT_NORMAL, +	BQ24190_CHRGFAULT_INPUT, +	BQ24190_CHRGFAULT_THERMAL, +	BQ24190_CHRGFAULT_SAFETY_TIMER +}; + +enum BQ24190NTCFaults +{ +	BQ24190_NTCFAULT_NORMAL, +	BQ24190_NTCFAULT_TS1_COLD, +	BQ24190_NTCFAULT_TS1_HOT, +	BQ24190_NTCFAULT_TS2_COLD, +	BQ24190_NTCFAULT_TS2_HOT, +	BQ24190_NTCFAULT_BOTH_COLD, +	BQ24190_NTCFAULT_BOTH_HOT +}; + +bool bq24190_toggle_charger(bool on) +{ +	uint8_t config = 0; +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQPC ", false); +	debug_log_hex(config); +	 +	config &= ~(0x3 << BQ24190_SHIFTS_CHARGER_CONFIG); +	if (on) +		config |= (0x01 << BQ24190_SHIFTS_CHARGER_CONFIG);	// Enable charger +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, config, _bq24190_pull_up) == false) +		return false; +	 +	//////// +/* +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQPC ", false); +	debug_log_hex(config); +*/ +	//////// +	 +	return true; +} + +bool bq24190_init(bool disable_charger) +{ +#ifdef I2C_REWORK +	i2c_init_ex(CHRG_SDA, CHRG_SCL, _bq24190_pull_up); +#endif // I2C_REWORK +	io_input_pin(USBPM_IRQ); +#if !defined(DEBUG) && !defined(ATTINY88_DIP) +	//io_set_pin(USBPM_IRQ);	// [Enable pull-up for Open Drain] AVR pull-up not enough +#endif // DEBUG +	if (disable_charger) +	{ +		if (bq24190_toggle_charger(false) == false) +			return false; +	} +	 +	/////////////////////////////////// +	 +	uint8_t timer_control = 0; +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_TIMER_CONTROL, &timer_control, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQTC ", false); +	debug_log_hex(timer_control); +	 +	timer_control &= ~(0x3 << BQ24190_SHIFTS_I2C_WATCHDOG); +	timer_control |= (0x00 << BQ24190_SHIFTS_I2C_WATCHDOG);	// Disable I2C watch dog +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_TIMER_CONTROL, timer_control, _bq24190_pull_up) == false) +		return false; +	 +	/////////////////////////////////// +	 +	//BQ24190_REG_PWR_ON_CONFIG +	// Minimum System Voltage Limit: (default) 101 3.5V +	 +	//BQ24190_REG_CHARGE_CURRENT +	// Fast Charge Current Limit: (default) 011000 2048mA +	 +	//BQ24190_REG_PRE_TERM_CURRENT +	// Pre-charge current limit: (default) 0001 256mA +	// Termination current limit: (default) 0001 256mA +	 +	//BQ24190_REG_CHARGE_VOLTAGE +	// Charge voltage limit: (default) 101100 4.208V +	 +	/////////////////////////////////// +	 +	uint8_t input_src_ctl = 0; +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, &input_src_ctl, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQIS ", false); +	debug_log_hex(input_src_ctl); +	 +	// Input voltage limit: (default) 0110 4.36V +	 +	//input_src_ctl &= ~(0x07); +	input_src_ctl |= (0x07);	// Set 3A limit +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, input_src_ctl, _bq24190_pull_up) == false) +		return false; +	 +	return true; +} + +bool bq24190_has_interrupt(void) +{ +	//bool state = io_test_pin(USBPM_IRQ); +	//debug_log_ex("BQIRQ", false); +	//debug_log_byte(state); +	//return (state != 1); +	return (io_test_pin(USBPM_IRQ) == false); +} + +static uint8_t _bq24190_last_status, _bq24190_last_fault; + +bool _bq24190_handle_irq(void) +{ +	uint8_t val = 0x00; +	bool result = false; +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	debug_log_ex("BQST ", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	_bq24190_last_status = val; + +	debug_log_ex("BQST ", false); +	debug_log_hex(val); +	 +	/*if (val & LTC4155_WALLSNS_GOOD) +	{ +		uint8_t wall_state = 0; +		if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false) +			goto _bq24190_handle_fail; +		 +		wall_state &= ~0x1E; +		wall_state |= 0x0E; +		 +		if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false) +			goto _bq24190_handle_fail; +		 +		debug_log("I+"); +	}*/ +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	debug_log_ex("BQF  ", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	_bq24190_last_fault = val; +	 +	debug_log_ex("BQF  ", false); +	debug_log_hex(val); +	 +	val = (_bq24190_last_status >> BQ24190_SHIFTS_CHARGER_STATUS) & BQ24190_CHRG_STAT_MASK; +	 +	if (_state.blink_error == BlinkError_None) +	{ +		switch (val) +		{ +			case BQ24190_CHRG_STAT_PRE_CHARGE: +			case BQ24190_CHRG_STAT_FAST_CHARGING: +			//case BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE: +			{ +				if ((_state.battery_not_present == false)/* && +					(_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD))*/) +				{ +					//charge_set_led(true); +					charge_notify(true); +					break; +				} +			} +			//case BQ24190_CHRG_STAT_NOT_CHARGING: +			default: +				//charge_set_led(false); +				charge_notify(false); +		} +	} +	 +//	bq24190_dump(); +	 +	result = true; +_bq24190_handle_fail: +	return result; +} + +bool bq24190_handle_irq(void)	// IRQ is pulsed (not held) +{ +	pmc_mask_irqs(true); +	 +	//_delay_ms(250);	// [Wait for registers to update] +	 +	bool result = _bq24190_handle_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +//void bq24190_dump(void) +/* +bool bq24190_set_charge_current_limit(uint8_t deciamps) +{ +	return true; +} +*/ +#endif // CHARGER_TI diff --git a/firmware/e300/rev_b/bq24190.h b/firmware/e300/rev_b/bq24190.h new file mode 100644 index 000000000..dc20ca796 --- /dev/null +++ b/firmware/e300/rev_b/bq24190.h @@ -0,0 +1,26 @@ +/* + * bq24190.h + * + * Created: 11/12/2012 4:58:23 PM + *  Author: Balint Seeber + */  + + +#ifndef BQ24190_H_ +#define BQ24190_H_ + +#include <stdbool.h> +#include <stdint.h> + +#ifdef CHARGER_TI + +bool bq24190_init(bool disable_charger); +bool bq24190_has_interrupt(void); +bool bq24190_handle_irq(void); +//void bq24190_dump(void); +//bool bq24190_set_charge_current_limit(uint8_t deciamps); +bool bq24190_toggle_charger(bool on); + +#endif // !CHARGER_TI + +#endif /* BQ24190_H_ */ diff --git a/firmware/e300/rev_b/config.h b/firmware/e300/rev_b/config.h new file mode 100644 index 000000000..2547f65f0 --- /dev/null +++ b/firmware/e300/rev_b/config.h @@ -0,0 +1,2 @@ +#define __DELAY_BACKWARD_COMPATIBLE__   // Avoid compile-time arg error to '__builtin_avr_delay_cycles' +#define F_CPU   1000000UL // 1 MHz (8MHz / 8) diff --git a/firmware/e300/rev_b/debug.c b/firmware/e300/rev_b/debug.c new file mode 100644 index 000000000..ff7d09445 --- /dev/null +++ b/firmware/e300/rev_b/debug.c @@ -0,0 +1,297 @@ +/* + * debug.c + */ + +#include "config.h" +#include "debug.h" + +#include <util/delay.h> +#include <avr/io.h> +#include <avr/interrupt.h> + +#include "io.h" +#include "power.h" +#include "global.h" + +#define DEBUG_BLINK_DELAY	250	// ms + +#ifdef ATTINY88_DIP + +#define SERIAL_DEBUG_INDEX			6 +#define SERIAL_DEBUG_PORT			PORTD +static io_pin_t SERIAL_DEBUG      = IO_PD(SERIAL_DEBUG_INDEX); + +#else +/* +#ifdef I2C_REWORK +//static io_pin_t SERIAL_DEBUG      = IO_PC(1);	// EN1 +#else +//static io_pin_t SERIAL_DEBUG      = EN4; +#endif // I2C_REWORK +*/ +// No good: PWR_EN4 trace still connected to LTC3675 +//#define SERIAL_DEBUG_INDEX			1 +//#define SERIAL_DEBUG_PORT			PORTA +//static io_pin_t SERIAL_DEBUG      = IO_PA(SERIAL_DEBUG_INDEX); + +// AVR_MISO +#define SERIAL_DEBUG_INDEX			4 +#define SERIAL_DEBUG_PORT			PORTB +static io_pin_t SERIAL_DEBUG      = IO_PB(SERIAL_DEBUG_INDEX); + +#endif // ATTINY88_DIP +/* +#ifdef DEBUG + +#else + +#endif // DEBUG +*/ +#ifdef DEBUG + +#ifdef ATTINY88_DIP +static io_pin_t DEBUG_1 = IO_PB(6); +static io_pin_t DEBUG_2	= IO_PB(7); +#endif // ATTINY88_DIP + +void debug_init() +{ +	io_output_pin(DEBUG_1); +	io_output_pin(DEBUG_2); +	 +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +#ifdef ENABLE_SERIAL	 +	io_set_pin(SERIAL_DEBUG); +	io_output_pin(SERIAL_DEBUG); +#endif // ENABLE_SERIAL +} + +#else + +void debug_init() +{ +#ifdef ENABLE_SERIAL +	io_set_pin(SERIAL_DEBUG); +	io_output_pin(SERIAL_DEBUG); +#endif // ENABLE_SERIAL +} + +#endif // DEBUG + +#if defined(DEBUG) && !defined(DEBUG_VOID) + +void debug_set(io_pin_t pin, bool enable) +{ +	io_enable_pin(pin, !enable); +} + +void debug_blink(uint8_t count) +{ +	io_enable_pin(DEBUG_1, false); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); + +	for (; count > 0; count--) { +		io_enable_pin(DEBUG_2, false); +		_delay_ms(DEBUG_BLINK_DELAY); +		io_enable_pin(DEBUG_2, true); +		_delay_ms(DEBUG_BLINK_DELAY); +	} + +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); +} + +void debug_blink_rev(uint8_t count) +{ +	io_enable_pin(DEBUG_2, false); +	io_enable_pin(DEBUG_1, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); + +	for (; count > 0; count--) { +		io_enable_pin(DEBUG_1, false); +		_delay_ms(DEBUG_BLINK_DELAY); +		io_enable_pin(DEBUG_1, true); +		_delay_ms(DEBUG_BLINK_DELAY); +	} + +	io_enable_pin(DEBUG_2, true); +	io_enable_pin(DEBUG_1, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); +} + +void debug_blink2(uint8_t count) +{ +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); + +	bool b = false; +	for (; count > 0; count--) { +		io_enable_pin(DEBUG_1, b); +		io_enable_pin(DEBUG_2, b); +		_delay_ms(DEBUG_BLINK_DELAY); +		b = !b; +	} + +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); +} + +void debug_wait(void) +{ +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	 +	bool b = false; +	while (true) +	{ +		io_enable_pin(DEBUG_1, b); +		io_enable_pin(DEBUG_2, !b); +		 +		_delay_ms(DEBUG_BLINK_DELAY); +		 +		b = !b; +	} +	 +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +} + +#else + +#ifndef DEBUG_VOID + +void debug_blink_rev(uint8_t count) +{ +	charge_set_led(true); +	_delay_ms(DEBUG_BLINK_DELAY * 4); + +	for (; count > 0; count--) { +		charge_set_led(false); +		_delay_ms(DEBUG_BLINK_DELAY); +		charge_set_led(true); +		_delay_ms(DEBUG_BLINK_DELAY * 2); +	} + +	_delay_ms(DEBUG_BLINK_DELAY * 2); +	charge_set_led(false); +	_delay_ms(DEBUG_BLINK_DELAY * 4); +} +#endif // DEBUG_VOID + +#endif // DEBUG + +#ifdef ENABLE_SERIAL + +static void _serial_tx(uint8_t* buffer) +{ +	//uint8_t time_fix = 0; +	// 3333/2 - 10 +	// 650 +	//	[-2 for DEV] -20 works (perhaps different USB-Serial converter) +	//	[-20 for PRD] Which board? +	//	+20 Board #5 (-0: 3.592, -10: 3.280) +	const uint16_t delay = 650+20; +	uint16_t countdown; +	 +	for (uint8_t j = 0; j < 10; ++j) +	{ +		if (buffer[j]) +		SERIAL_DEBUG_PORT |= _BV(SERIAL_DEBUG_INDEX); +		else +		SERIAL_DEBUG_PORT &= ~_BV(SERIAL_DEBUG_INDEX); +		 +		countdown = delay; +		while (--countdown) +		__asm("nop"); +	} +} + +static void _serial_tx_char(char c) +{ +	uint8_t buffer[10]; +	uint8_t i = 0; +	 +	buffer[i++] = 0;	// START +	for (int idx = 0; idx < 8; ++idx) +	buffer[i++] = (((uint8_t)(c) & ((uint8_t)1<<((idx)))) ? 0x01 : 0x00);	// Endianness: 7- +	buffer[i++] = 1;	// STOP +	 +	_serial_tx(buffer); +} + +void debug_log_ex_P(const char* message, bool new_line) +{ +	char c = pgm_read_byte(message); +	if (c == '\0') +		return; +	 +	pmc_mask_irqs(true); + +	do +	{ +		_serial_tx_char(c); +		c = pgm_read_byte(++message); +	} while (c != '\0'); +	 +	if (new_line) +		_serial_tx_char('\n'); + +	io_set_pin(SERIAL_DEBUG); +	 +	pmc_mask_irqs(false); +} + +void _debug_log_ex(const char* message, bool new_line) +{ +	if (message[0] == '\0') +	return; +	 +	pmc_mask_irqs(true); + +	do +	{ +		_serial_tx_char(*message); +	} while (*(++message) != '\0'); +	 +	if (new_line) +	_serial_tx_char('\n'); + +	io_set_pin(SERIAL_DEBUG); +	 +	pmc_mask_irqs(false); +} + +void debug_log_byte_ex(uint8_t n, bool new_line) +{ +	char ch[4]; +	ch[0] = '0' + (n / 100); +	ch[1] = '0' + ((n % 100) / 10); +	ch[2] = '0' + (n % 10); +	ch[3] = '\0'; +	_debug_log_ex(ch, new_line); +} + +void debug_log_hex_ex(uint8_t n, bool new_line) +{ +	char ch[4]; +	ch[0] = 'x'; +	uint8_t _n = n >> 4; +	if (_n < 10) +		ch[1] = '0' + _n; +	else +		ch[1] = 'A' + (_n - 10); +	n &= 0x0F; +	if (n < 10) +		ch[2] = '0' + n; +	else +		ch[2] = 'A' + (n - 10); +	ch[3] = '\0'; +	_debug_log_ex(ch, new_line); +} + +#endif // ENABLE_SERIAL diff --git a/firmware/e300/rev_b/debug.h b/firmware/e300/rev_b/debug.h new file mode 100644 index 000000000..5e6435972 --- /dev/null +++ b/firmware/e300/rev_b/debug.h @@ -0,0 +1,90 @@ +/* + * debug.h + */  + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +#include <stdint.h> +#include <stdbool.h> +#include <avr/pgmspace.h> + +#include "io.h" + +#ifdef DEBUG +#define DEBUG_INLINE +#define DEBUG_NOOP	; +#define LED_ON		false +#define LED_OFF		true +#else +#define DEBUG_INLINE inline +#define DEBUG_NOOP	{} +#define LED_ON		true +#define LED_OFF		false +#endif // DEBUG + +//#define DEBUG_VOID +#define DEBUG_SAFETY + +#ifdef DEBUG_VOID + +//#define debug_init	(void) +#define debug_set	(void) +#define debug_blink	(void) +#define debug_blink_rev	(void) +#define debug_blink2 (void) +#define debug_wait	(void) + +#else + +//DEBUG_INLINE void debug_init(void) DEBUG_NOOP +DEBUG_INLINE void debug_set(io_pin_t pin, bool enable) DEBUG_NOOP +DEBUG_INLINE void debug_blink(uint8_t count) DEBUG_NOOP +//DEBUG_INLINE void debug_blink_rev(uint8_t count) DEBUG_NOOP +void debug_blink_rev(uint8_t count); +DEBUG_INLINE void debug_blink2(uint8_t count) DEBUG_NOOP +DEBUG_INLINE void debug_wait(void) DEBUG_NOOP + +#endif // DEBUG_VOID + +#if defined(DEBUG) && !defined(ENABLE_SERIAL) +#define ENABLE_SERIAL +#endif // DEBUG && !ENABLE_SERIAL + +/*DEBUG_INLINE */void debug_init(void)/* DEBUG_NOOP*/; + +#ifdef ENABLE_SERIAL + +void debug_log_ex_P(const char* message, bool new_line); +void debug_log_hex_ex(uint8_t n, bool new_line); +void debug_log_byte_ex(uint8_t n, bool new_line); +void _debug_log_ex(const char* message, bool new_line); + +// Prototypes to silence avr-gcc +inline void debug_log_P(const char* message); +inline void debug_log_hex(uint8_t n); +inline void debug_log_byte(uint8_t n); +inline void _debug_log(const char* message); + +inline void debug_log_P(const char* message) { debug_log_ex_P(message, true); } +inline void debug_log_hex(uint8_t n) { debug_log_hex_ex(n, true); } +inline void debug_log_byte(uint8_t n) { debug_log_byte_ex(n, true); } +inline void _debug_log(const char* message) { _debug_log_ex(message, true); } + +#else + +inline void debug_log_ex_P		(const char* message, bool new_line) {}; +inline void debug_log_hex_ex	(uint8_t n, bool new_line) {}; +inline void debug_log_byte_ex	(uint8_t n, bool new_line) {}; +inline void _debug_log_ex		(const char* message, bool new_line) {}; + +#define debug_log_P			(void) +#define debug_log_hex		(void) +#define debug_log_byte		(void) +#define _debug_log			(void) +#endif // ENABLE_SERIAL + +#define debug_log(x)		debug_log_P(PSTR(x)) +#define debug_log_ex(x,nl)	debug_log_ex_P(PSTR(x), nl) + +#endif /* DEBUG_H_ */ diff --git a/firmware/e300/rev_b/error.h b/firmware/e300/rev_b/error.h new file mode 100644 index 000000000..82a8f0aca --- /dev/null +++ b/firmware/e300/rev_b/error.h @@ -0,0 +1,31 @@ +/* + * error.h + * + * Created: 4/09/2012 6:25:53 PM + *  Author: Balint Seeber + */  + + +#ifndef ERROR_H_ +#define ERROR_H_ + +enum ErrorBlinkCount	// Lower number = higher priority +{ +	BlinkError_None, +	// Low power/battery +	BlinkError_LowVoltage, +	BlinkError_LTC3675_UnderVoltage = BlinkError_LowVoltage, +	BlinkError_LTC4155_UnderVoltage = BlinkError_LowVoltage,	// FIXME: This does not work when checking status +	// Should match power boot steps +	BlinkError_FPGA_Power, +	BlinkError_DRAM_Power, +	BlinkError_1_8V_Peripherals_Power, +	BlinkError_3_3V_Peripherals_Power, +	BlinkError_TX_Power, +	// LTC3675 +	BlinkError_LTC3675_OverTemperature, +	// LTC4155 +	BlinkError_LTC4155_BadCell +}; + +#endif /* ERROR_H_ */ diff --git a/firmware/e300/rev_b/global.h b/firmware/e300/rev_b/global.h new file mode 100644 index 000000000..50fab581d --- /dev/null +++ b/firmware/e300/rev_b/global.h @@ -0,0 +1,49 @@ +/* + * global.h + * + * Created: 31/08/2012 8:47:14 PM + *  Author: Balint Seeber + */  + +#ifndef GLOBAL_H_ +#define GLOBAL_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <avr/pgmspace.h> + +typedef struct State +{ +	bool interrupts_enabled; +	uint8_t interrupt_depth; +	//bool timers_running; +	uint8_t active_timers; +	bool powered; +	bool battery_not_present; +	bool battery_charging; +	bool wake_up; +	bool power_off; +	bool core_power_bad; +	bool ltc3675_irq; +#ifdef CHARGER_TI +	bool bq24190_irq; +#else +	bool ltc4155_irq; +#endif // CHARGER_TI +	//bool low_battery; +	uint8_t blink_error; +	uint8_t blinker_state; +	uint8_t blink_loops; +	uint8_t blink_last_loop; +	bool blink_stop; +} STATE; + +//extern volatile bool _timers_running; +extern volatile STATE _state; + +void pmc_set_blink_error(uint8_t count); +uint8_t pmc_get_blink_error(void); + +bool pmc_mask_irqs(bool mask); + +#endif /* GLOBAL_H_ */ diff --git a/firmware/e300/rev_b/i2c.c b/firmware/e300/rev_b/i2c.c new file mode 100644 index 000000000..70a28e61a --- /dev/null +++ b/firmware/e300/rev_b/i2c.c @@ -0,0 +1,518 @@ +#include "config.h" +#include "i2c.h" + +#include <util/delay.h> + +#include "io.h" +#include "debug.h" + +/* +	- Reset bus on failure (lack of ACK, etc) +	- Clock stretching +	- In pull-up mode, much code was commented out to ever avoid driving the bus (for a fleeting moment) as this was visible on the scope as short peaks (instead the line will briefly go Hi Z). +*/ + +volatile bool _i2c_disable_ack_check = false; + +// FIXME: Follow magic numbers should be in a struct that is passed into each function + +#define I2C_DEFAULT_RETRY_DELAY     1   // us MAGIC +#define I2C_DEFAULT_MAX_ACK_RETRIES 10  // * I2C_DEFAULT_RETRY_DELAY us + +#define I2C_DEFAULT_BUS_WAIT		10	// us MAGIC +#define I2C_DEFAULT_MAX_BUS_RETRIES	10 + +#define I2C_DEFAULT_SCL_LOW_PERIOD  2   // 1.3 us +#define I2C_DEFAULT_SCL_HIGH_PERIOD 1   // 0.6 us +#define I2C_DEFAULT_BUS_FREE_TIME   2   // 1.3 us +#define I2C_DEFAULT_STOP_TIME       1   // 0.6 us + +#define I2C_DELAY	_delay_us	// _delay_ms + +static bool _i2c_start_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +	// Assumes: SDA/SCL are both inputs + +	uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; +	while ((io_test_pin(sda) == false) || (io_test_pin(scl) == false)) +	{ +		I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +		if (retries-- == 0) +		{ +debug_log("I2C:S1"); +			return false; +		}			 +	} +	 +	// START condition +//	if (pull_up == false) +		io_clear_pin(sda);	// Set LOW before switching to output +	io_output_pin(sda); +//	if (pull_up) +//		io_clear_pin(sda); +	I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);  // Thd, sta +	 +	retries = I2C_DEFAULT_MAX_BUS_RETRIES; +	while (io_test_pin(scl) == false)	// SCL should remain high +	{ +		I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +		if (retries-- == 0) +		{ +			io_input_pin(sda); +debug_log_ex("I2C:S2", false); +debug_log_hex(scl); +			return false; +		}			 +	} + +//	if (pull_up == false) +		io_clear_pin(scl); +	io_output_pin(scl); +//	if (pull_up) +//		io_clear_pin(scl); +	I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD / 2);   // MAGIC + +	return true; +} + +static bool _i2c_stop_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +	// Assumes: +	//	SCL is output & LOW +	//	SDA is input (Hi-Z, or pull-up enabled) +	 +	// Assuming pull-up already enabled +	//if (pull_up) +	//	io_set_pin(sda); +	 +	bool result = true; +	 +	// SDA should be HIGH after ACK has been clocked away +//	bool skip_drive = false; +	uint8_t retries = 0; +	while (io_test_pin(sda) == false) +	{ +		if (retries == I2C_DEFAULT_MAX_ACK_RETRIES) +		{ +			debug_log_ex("I2C:STP ", false); +			debug_log_hex(sda); +			debug_blink_rev(4); +			 +//			skip_drive = true; +			result = false; +			break;	// SDA is being held low?! +		} + +		++retries; +		I2C_DELAY(I2C_DEFAULT_RETRY_DELAY); +	} +	 +	// STOP condition +//	if ((pull_up == false) || (skip_drive)) +		io_clear_pin(sda);	// Don't tri-state if internal pull-up is used +//	//else +//	// Pin will now be driven, but having checked SDA is HIGH above means slave's SDA should be Open Collector (i.e. it won't blow up) +	io_output_pin(sda);	// Drive LOW +//	if (pull_up) +//		io_clear_pin(sda); + +	/////////////////////////////////// +	 +//	if (pull_up) +//		io_set_pin(scl);	// Don't tri-state if internal pull-up is used. Line will be driven, but assuming this is the only master on the clock line (i.e. no one else will pull it low). +	io_input_pin(scl); +	if (pull_up) +		io_set_pin(scl); +	I2C_DELAY(I2C_DEFAULT_STOP_TIME); +	 +	/////////////////////////////////// + +//	if ((pull_up) && (skip_drive == false)) +//		io_set_pin(sda);	// Don't tri-state if internal pull-up is used +	io_input_pin(sda); +//	if ((pull_up) && (skip_drive)) +		io_set_pin(sda); +	I2C_DELAY(I2C_DEFAULT_BUS_FREE_TIME); +	 +	return result; +} +/* +static void _i2c_stop(io_pin_t sda, io_pin_t scl) +{ +	_i2c_stop_ex(sda, scl, false); +} +*//* +static void _i2c_abort_safe_ex(io_pin_t pin, bool pull_up) +{ +	if (io_is_output(pin)) +	{ +		if (io_is_pin_set(pin))	// This is bad - hope no slave is pulling down the line +		{ +			io_input_pin(pin);	// Pull-up already enabled +			 +			if (pull_up == false) +				io_clear_pin(pin);	// Doing this after changing direction ensures the line is not brought down +		} +		else	// Currently pulling line down +		{ +			io_input_pin(pin);	// Hi-Z +			 +			if (pull_up)	// There will be a moment where the line will float (better than driving the line though...) +			{ +				io_set_pin(pin); +			} +		} +	} +	else	// Already an input +	{ +		if (pull_up) +		{ +			io_set_pin(pin);	// Enable pull-ups +		} +		else +		{ +			io_clear_pin(pin);	// Disable pull-ups +		} +	} +	 +	// Normally: pin will be Hi-Z input +	// With internal pull-up: pin will be input with pull-up enabled +} +*/ +static void _i2c_abort_safe(io_pin_t pin, bool pull_up) +{ +	if (pull_up == false) +		io_clear_pin(pin);	// Should never be output/HIGH, could be input/<was outputting HIGH> so disable pull-ups +	 +	io_input_pin(pin); +	 +	if (pull_up) +		io_set_pin(pin);	// Enable pull-up +} + +static void _i2c_abort_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +/*	if (pull_up == false) +	{ +		io_clear_pin(sda); +		io_clear_pin(scl); +	} +	 +	io_input_pin(scl); +	io_input_pin(sda); +	 +	if (pull_up) +	{ +		io_set_pin(sda); +		io_set_pin(scl); +	} +*/ +	_i2c_abort_safe(scl, pull_up); +	_i2c_abort_safe(sda, pull_up); + +	//_i2c_abort_safe_ex(scl, pull_up); +	//_i2c_abort_safe_ex(sda, pull_up); +} +/* +static void _i2c_abort(io_pin_t sda, io_pin_t scl) +{ +	_i2c_abort_ex(sda, scl, false); +} +*/ +static bool _i2c_write_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t value, bool pull_up) +{ +    // Assumes: +    //  SDA output is LOW +    //  SCL output is LOW + +    for (uint8_t i = 0; i < 8; ++i) +    { +		bool b = ((value & (0x01 << (7 - i))) != 0x00);	// MSB first +		 +		if (b) +		{ +			if (pull_up) +			{ +//				io_set_pin(sda);	// This is bad (will drive line for a moment), but more stable than letting line float +				io_input_pin(sda); +				io_set_pin(sda); +			}				 +			else +				io_input_pin(sda);	// Release HIGH +			 +			if (io_test_pin(sda) == false) +			{ +				debug_log("I2C:WR "); +				debug_log_hex(sda); +				debug_blink_rev(1); +				return false; +			}			 +		}			 +		else +		{ +			if (pull_up) +			{ +//				if (io_is_output(sda)) +					io_clear_pin(sda); +//				else +//				{ +					io_output_pin(sda);	// [This is bad (will drive line for a moment), but more stable than letting line float] +//					io_clear_pin(sda); +//				} +			} +			else +			{ +				io_enable_pin(sda, false); +				io_output_pin(sda);	// Drive LOW +			}				 +		} +		 +		/////////////////////////////// + +        io_input_pin(scl);	// Release HIGH +		if (pull_up) +			io_set_pin(scl); +        I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); +#ifdef I2C_ALLOW_CLOCK_STRETCH +		uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; +		while (io_test_pin(scl) == false)	// Clock stretch requested? +		{ +			I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +			if (--retries == 0) +			{ +				io_input_pin(sda);	// Release HIGH +				if (pull_up) +					io_set_pin(sda); +				 +				debug_log_ex("I2C:STRTCH ", false); +				debug_log_hex(scl); +				debug_blink_rev(2); +				return false; +			} +		} +#endif // I2C_ALLOW_CLOCK_STRETCH +		if (pull_up) +			io_clear_pin(scl); +        io_output_pin(scl);	// Drive LOW +        I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); +    } + +    io_input_pin(sda);	// Release HIGH +	if (pull_up) +		io_set_pin(sda);	// Assuming letting line float won't confuse slave when pulling line LOW for ACK +    I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); + +    uint8_t retries = 0; +    while ((_i2c_disable_ack_check == false) && (io_test_pin(sda))) +    { +        if (retries == I2C_DEFAULT_MAX_ACK_RETRIES) +		{ +			debug_log_ex("I2C:ACK ", false); +			debug_log_hex_ex(sda, false); +			debug_log_hex(value); +			debug_blink_rev(3); +            return false;	// Will abort and not release bus - done by caller +		} + +        ++retries; +        I2C_DELAY(I2C_DEFAULT_RETRY_DELAY); +    } + +    // Clock away acknowledge +//	if (pull_up) +//		io_set_pin(scl); +    io_input_pin(scl);	// Release HIGH +	if (pull_up) +		io_set_pin(scl); +    I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); + +	if (pull_up) +		io_clear_pin(scl); +    io_output_pin(scl);	// Drive LOW +//	if (pull_up) +//		io_clear_pin(scl); +    I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); + +    return true; +} + +static bool _i2c_read_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t* value, bool pull_up) +{ +    // Assumes: +    //  SDA output is LOW +    //  SCL output is LOW +	 +	io_input_pin(sda); +	if (pull_up) +		io_set_pin(sda);	// OK to leave line floating for a moment (better not to drive as slave will be pulling it to ground) + +    (*value) = 0x00; + +    for (uint8_t i = 0; i < 8; ++i) +    { +//		if (pull_up) +//			io_set_pin(scl);	// [Not ideal with pull-up] +        io_input_pin(scl);	// Release HIGH +		if (pull_up) +			io_set_pin(scl); +        I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); +#ifdef I2C_ALLOW_CLOCK_STRETCH +		uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; +		while (io_test_pin(scl) == false)	// Clock stretch requested? +		{ +			I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +			if (--retries == 0) +			{ +				debug_log_ex("I2C:R "); +				debug_log_hex(scl); +				debug_blink_rev(5); +				return false; +			}				 +		} +#endif // I2C_ALLOW_CLOCK_STRETCH +        (*value) |= ((io_test_pin(sda) ? 0x1 : 0x0) << (7 - i));   // MSB first + +		if (pull_up) +			io_clear_pin(scl); +        io_output_pin(scl);	// Drive LOW (not ideal with pull-up) +//		if (pull_up) +//			io_clear_pin(scl); +        I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); +    } + +    // Not necessary to ACK since it's only this one byte + +    return true; +} + +bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up) +{ +	if (_i2c_start_ex(sda, scl, pull_up) == false) +		return false; + +	if (_i2c_write_byte_ex(sda, scl, addr & ~0x01, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R21:", false); +		debug_log("R21"); +		//debug_log_hex(addr); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} + +	if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R22:", false); +		debug_log("R22"); +		//debug_log_hex(subaddr); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} +	 +	io_input_pin(scl); +	if (pull_up) +		io_set_pin(scl); +	I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +	 +	if (_i2c_start_ex(sda, scl, pull_up) == false) +	{ +		return false; +	} +	 +	if (_i2c_write_byte_ex(sda, scl, addr | 0x01, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R23:", false); +		debug_log("R23"); +		//debug_log_hex(addr); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} + +	if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R24:", false); +		debug_log("R24"); +		//debug_log_hex(*value); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} +	 +	if (_i2c_stop_ex(sda, scl, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		debug_log("R25"); +#endif // I2C_EXTRA_DEBUGGING +	} + +	return true; +i2c_read2_fail: +	_i2c_abort_ex(sda, scl, pull_up); +	return false; +} + +bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up) +{ +	if (_i2c_start_ex(sda, scl, pull_up) == false) +		return false; + +    if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false) +        goto i2c_write_fail; + +    if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false) +        goto i2c_write_fail; + +    if (_i2c_write_byte_ex(sda, scl, value, pull_up) == false) +        goto i2c_write_fail; + +    _i2c_stop_ex(sda, scl, pull_up); + +    return true; +i2c_write_fail: +	_i2c_abort_ex(sda, scl, pull_up); +	return false; +} + +bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value) +{ +	return i2c_write_ex(sda, scl, addr, subaddr, value, false); +} + +bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up) +{ +    if (_i2c_start_ex(sda, scl, pull_up) == false) +		return false; + +    if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false) +        goto i2c_read_fail; + +    if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false) +        goto i2c_read_fail; + +    if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false) +        goto i2c_read_fail; + +    _i2c_stop_ex(sda, scl, pull_up); + +    return true; +i2c_read_fail: +	_i2c_abort_ex(sda, scl, pull_up); +	return false; +} + +bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value) +{ +	return i2c_read_ex(sda, scl, addr, subaddr, value, false); +} + +void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +	_i2c_abort_ex(sda, scl, pull_up); +} + +void i2c_init(io_pin_t sda, io_pin_t scl) +{ +	i2c_init_ex(sda, scl, false); +} diff --git a/firmware/e300/rev_b/i2c.h b/firmware/e300/rev_b/i2c.h new file mode 100644 index 000000000..5898e7e43 --- /dev/null +++ b/firmware/e300/rev_b/i2c.h @@ -0,0 +1,17 @@ +#ifndef I2C_H +#define I2C_H + +#include "io.h" + +void i2c_init(io_pin_t sda, io_pin_t scl); +bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value); +bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value); + +void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up); +bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up); +bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up); +bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up); + +extern volatile bool _i2c_disable_ack_check; + +#endif // I2C_H diff --git a/firmware/e300/rev_b/io.c b/firmware/e300/rev_b/io.c new file mode 100644 index 000000000..0256181c6 --- /dev/null +++ b/firmware/e300/rev_b/io.c @@ -0,0 +1,75 @@ +/* + * Copyright 2009-2012 Ettus Research LLC + */ + +#include "io.h" +#include <avr/io.h> + +#define _GET_PIN(pin)           ((pin) & 0xf) +#define _GET_MASK(pin)          (_BV(_GET_PIN(pin))) +#define _GET_REG(pin, reg_x)    (*reg_x[pin >> 4]) + +#ifndef IO_DEBUG +static volatile uint8_t *ddr_x[] = {&DDRA, &DDRB, &DDRC, &DDRD};		// 0: input, 1: output +static volatile uint8_t *port_x[] = {&PORTA, &PORTB, &PORTC, &PORTD};	// Port contents (will appear at output if direction is set to output. If input, '1' enables pull-ups, '0' set tri-state) +static volatile uint8_t *pin_x[] = {&PINA, &PINB, &PINC, &PIND};		// Port contents (input) If output, will return value on PORT +#endif + +void io_output_pin(io_pin_t pin){ +#ifndef IO_DEBUG +	_GET_REG(pin, ddr_x) |= _GET_MASK(pin); +#endif +} + +void io_input_pin(io_pin_t pin){ +#ifndef IO_DEBUG +	_GET_REG(pin, ddr_x) &= ~_GET_MASK(pin); +#endif +} + +bool io_is_output(io_pin_t pin){ +#ifndef IO_DEBUG +	return bit_is_set(_GET_REG(pin, ddr_x), _GET_PIN(pin)); +#else +	return 0; +#endif +} + +bool io_is_input(io_pin_t pin){ +	return !io_is_output(pin); +} + +void io_set_pin(io_pin_t pin){	// In input mode, will enable pull-ups +#ifndef IO_DEBUG +	_GET_REG(pin, port_x) |= _GET_MASK(pin); +#endif +} + +void io_clear_pin(io_pin_t pin){	// In input mode, will disable pull-ups +#ifndef IO_DEBUG +	_GET_REG(pin, port_x) &= ~_GET_MASK(pin); +#endif +} + +bool io_is_pin_set(io_pin_t pin){ +#ifndef IO_DEBUG +	return bit_is_set(_GET_REG(pin, port_x), _GET_PIN(pin)); +#else +	return 0; +#endif +} + +void io_enable_pin(io_pin_t pin, bool enable){ +    if (enable) +        io_set_pin(pin); +    else +        io_clear_pin(pin); +} + +bool io_test_pin(io_pin_t pin){ +#ifndef IO_DEBUG +	return bit_is_set(_GET_REG(pin, pin_x), _GET_PIN(pin)); +#else +	return 0; +#endif +} diff --git a/firmware/e300/rev_b/io.h b/firmware/e300/rev_b/io.h new file mode 100644 index 000000000..7eea8f0a3 --- /dev/null +++ b/firmware/e300/rev_b/io.h @@ -0,0 +1,31 @@ +/* + * Copyright 2009 Ettus Research LLC + */ + +#ifndef IO_H +#define IO_H + +#include <stdint.h> +#include <stdbool.h> + +#define IO_PX(port, pin) ((uint8_t)(((port - 'A') << 4) + pin)) +#define IO_PA(pin) IO_PX('A', pin) +#define IO_PB(pin) IO_PX('B', pin) +#define IO_PC(pin) IO_PX('C', pin) +#define IO_PD(pin) IO_PX('D', pin) + +typedef const uint8_t io_pin_t; + +void io_output_pin(io_pin_t pin); +void io_input_pin(io_pin_t pin); +bool io_is_output(io_pin_t pin); +bool io_is_input(io_pin_t pin); + +void io_set_pin(io_pin_t pin); +void io_clear_pin(io_pin_t pin); +void io_enable_pin(io_pin_t pin, bool enable); +bool io_is_pin_set(io_pin_t pin); + +bool io_test_pin(io_pin_t pin); + +#endif /* IO_H */ diff --git a/firmware/e300/rev_b/ltc3675.c b/firmware/e300/rev_b/ltc3675.c new file mode 100644 index 000000000..0f85ec7e5 --- /dev/null +++ b/firmware/e300/rev_b/ltc3675.c @@ -0,0 +1,525 @@ +/* + * Copyright 2012 Ettus Research LLC + */ + +/* +    ? STOP condition after writing address on read +    - Default buck/boost register values are OK +*/ + +#include "config.h" +#include "ltc3675.h" + +//#include <stdio.h> +#include <util/delay.h> +#include <avr/interrupt.h> + +#include "io.h" +#include "i2c.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +#ifndef I2C_REWORK +#include "power.h" +#endif // I2C_REWORK + +const bool _ltc3675_pull_up = +#ifdef I2C_REWORK +	true +#else +	false +#endif // I2C_REWORK +; + +volatile ltc3675_reg_helper_fn _ltc3675_reg_helper; + +//#define HARDWIRE_ENABLE	// Use hardware enable pins instead of I2C on regulators that support it + +#ifdef ATTINY88_DIP + +#ifdef HARDWIRE_ENABLE +static io_pin_t PWR_EN1     = IO_PC(7);	// Not routed by card +static io_pin_t PWR_EN2     = IO_PA(0);	// Not available on DIP +static io_pin_t PWR_EN3     = IO_PA(1);	// Not available on DIP +static io_pin_t PWR_EN4     = IO_PB(6);	// Instead of FTDI_BCD +static io_pin_t PWR_EN5     = IO_PB(7);	// Instead of FTDI_PWREN2 +#endif // HARDWIRE_ENABLE + +//static io_pin_t PWR_SDA     = IO_PC(4); +//static io_pin_t PWR_SCL     = IO_PC(5); + +#else + +#ifdef HARDWIRE_ENABLE +static io_pin_t PWR_EN1     = IO_PC(1); +//static io_pin_t PWR_EN2     = IO_PC(2);	// Now used by I2C for charge controller +//static io_pin_t PWR_EN3     = IO_PC(3);	// Now used by I2C for charge controller +static io_pin_t PWR_EN4     = IO_PA(1); +static io_pin_t PWR_EN5     = IO_PA(2); +#endif // HARDWIRE_ENABLE + +#ifdef I2C_REWORK +static io_pin_t PWR_SDA     = IO_PC(2);		// Instead of EN5 +static io_pin_t PWR_SCL     = IO_PA(2);		// Instead of EN2 +#endif // I2C_REWORK + +#endif // ATTINY88_DIP + +static io_pin_t PWR_IRQ     = IO_PD(0); +static io_pin_t WAKEUP      = IO_PD(2); +static io_pin_t ONSWITCH_DB = IO_PD(3); +static io_pin_t PWR_RESET   = IO_PD(4); + +#define LTC3675_BASE_ADDRESS    0x12 +#define LTC3675_WRITE_ADDRESS   (LTC3675_BASE_ADDRESS + 0) +#define LTC3675_READ_ADDRESS    (LTC3675_BASE_ADDRESS + 1) + +#define LTC3675_RETRY_DELAY     1   // us MAGIC +#define LTC3675_MAX_ACK_RETRIES 10  // * LTC3675_RETRY_DELAY us + +#define LTC3675_SCL_LOW_PERIOD  2   // 1.3 us +#define LTC3675_SCL_HIGH_PERIOD 1   // 0.6 us +#define LTC3675_BUS_FREE_TIME   2   // 1.3 us +#define LTC3675_STOP_TIME       1   // 0.6 us + +#define LTC3675_REGULATOR_ENABLE_DELAY	10	// 50	// ms (some arbitrary value so that the external power supply can settle) + +enum LTC3675Registers +{ +	LTC3675_REG_NONE			= 0x00, +	LTC3675_REG_BUCK1			= 0x01, +	LTC3675_REG_BUCK2			= 0x02, +	LTC3675_REG_BUCK3			= 0x03, +	LTC3675_REG_BUCK4			= 0x04, +	LTC3675_REG_BOOST			= 0x05, +	LTC3675_REG_BUCK_BOOST		= 0x06, +	LTC3675_REG_LED_CONFIG		= 0x07, +	LTC3675_REG_LED_DAC			= 0x08, +	LTC3675_REG_UVOT			= 0x09, +	LTC3675_REG_RSTB			= 0xA0, +	LTC3675_REG_IRQB_MASK		= 0x0B, +	LTC3675_REG_REALTIME_STATUS	= 0x0C, +	LTC3675_REG_LATCHED_STATUS	= 0x0D, +	LTC3675_REG_CLEAR_IRQ		= 0x0F +}; + +enum LTC3675StatusBits +{ +	LTC3675_UnderVoltage	= 1 << 7, +	LTC3675_OverTemperature	= 1 << 6, +	LTC3675_BuckBoost_PGood	= 1 << 5, +	LTC3675_Boost_PGood		= 1 << 4, +	LTC3675_Buck4_PGood		= 1 << 3, +	LTC3675_Buck3_PGood		= 1 << 2, +	LTC3675_Buck2_PGood		= 1 << 1, +	LTC3675_Buck1_PGood		= 1 << 0 +}; + +#define LTC3675_DEFAULT_BUCK_REG_VAL		0x6F +#define LTC3675_DEFAULT_BOOST_REG_VAL		0x0F +#define LTC3675_DEFAULT_BUCK_BOOST_REG_VAL	0x0F + +#define LTC3675_ENABLE_REGISTER_BIT			0x80 + +// Max I2C rate = 400kHz + +static void _ltc3675_clear_irq() +{ +	// Two-stage clear +	i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_CLEAR_IRQ, 0x00, _ltc3675_pull_up); +	i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_NONE, 0x00, _ltc3675_pull_up); +} + +volatile uint8_t _ltc3675_last_status = 0x00; + +uint8_t ltc3675_get_last_status(void) +{ +	return _ltc3675_last_status; +} + +uint8_t ltc3675_reg_status_to_error(uint8_t val) +{ +	if (((val & LTC3675_BuckBoost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_6))) +		return BlinkError_3_3V_Peripherals_Power; +	 +	if (((val & LTC3675_Boost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_5))) +		return BlinkError_TX_Power; +	 +	//if (((val & LTC3675_Buck4_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_4))) +	 +	if (((val & LTC3675_Buck3_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_3))) +		return BlinkError_1_8V_Peripherals_Power; +	 +	//if (((val & LTC3675_Buck2_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_2))) +	 +	if (((val & LTC3675_Buck1_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_1))) +		return BlinkError_DRAM_Power; +	 +	return BlinkError_None; +} + +bool ltc3675_is_power_good(uint8_t val) +{ +	return (ltc3675_reg_status_to_error(val) == BlinkError_None); +} + +uint8_t ltc3675_status_to_error(uint8_t val) +{ +	if (val & LTC3675_UnderVoltage) +		return BlinkError_LTC3675_UnderVoltage; +	 +	if (val & LTC3675_OverTemperature) +		return BlinkError_LTC3675_OverTemperature; +	 +	uint8_t reg_error = ltc3675_reg_status_to_error(val); +	if (reg_error != BlinkError_None) +		return reg_error; +	 +	return BlinkError_None; +} + +bool _ltc3675_handle_irq(void) +{ +	uint8_t val = 0x00; +	bool result = false; +	 +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_LATCHED_STATUS, &val, _ltc3675_pull_up)) +	{ +		debug_log_ex("3675LTCH ", false); +		debug_log_hex(val); +	} +	 +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, /*LTC3675_REG_LATCHED_STATUS*/LTC3675_REG_REALTIME_STATUS, &val, _ltc3675_pull_up))	// No point acting on latched because could have been resolved +	{ +		//debug_log_ex("3675LTCH ", false); +		debug_log_ex("3675RT ", false); +		debug_log_hex(val); +		 +		_ltc3675_last_status = val; +		 +		uint8_t error = ltc3675_status_to_error(val); +		 +		/*if (val & LTC3675_UnderVoltage) +		{ +			pmc_set_blink_error(BlinkError_LTC3675_UnderVoltage); +			//_state.low_battery = true; +		}*/ +		 +		if (error) +		{ +			pmc_set_blink_error(error); +			 +			/*_i2c_disable_ack_check = true; +			uint8_t chk = 0x00; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_6) << 0; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_5) << 1; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_3) << 2; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_1) << 3; +			i2c_write_ex(PWR_SDA, PWR_SCL, 0xFE, 0xFF, chk, _ltc3675_pull_up); +			_i2c_disable_ack_check = false;*/ +		}			 +		 +		result = true; +	} +	 +	_ltc3675_clear_irq(); +	 +	return result; +} + +static bool _ltc3675_get_realtime_status(uint8_t* val) +{ +	//cli(); +	 +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_REALTIME_STATUS, val, _ltc3675_pull_up) == false) +		return false; +	 +	debug_log_ex("3675RT ", false); +	debug_log_hex(*val); +	 +	//sei(); +	 +	return true; +} + +int8_t ltc3675_check_status(void) +{ +	uint8_t val = 0x00; +	 +	pmc_mask_irqs(true); +	 +	bool result = _ltc3675_get_realtime_status(&val); +	 +	pmc_mask_irqs(false); +	 +	if (result == false) +		return -1; +	 +	//_ltc3675_last_status = val; +	 +	/*if (val & LTC3675_UnderVoltage) +		return BlinkError_LTC3675_UnderVoltage; +	 +	if (val & LTC3675_OverTemperature) +		return BlinkError_LTC3675_OverTemperature; +	 +	return BlinkError_None;*/ +	 +	return ltc3675_status_to_error(val); +} + +bool ltc3675_handle_irq(void) +{ +	pmc_mask_irqs(true); +	 +	/*uint8_t*/bool result = _ltc3675_handle_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +static bool _ltc3675_default_reg_helper(uint8_t address) +{ +	uint8_t val = 0x00; +	i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, address, &val, _ltc3675_pull_up); +	return ((val & LTC3675_ENABLE_REGISTER_BIT) == LTC3675_ENABLE_REGISTER_BIT); +} + +bool ltc3675_init(ltc3675_reg_helper_fn helper) +{ +	if (helper) +		_ltc3675_reg_helper = helper; +	else +		_ltc3675_reg_helper = _ltc3675_default_reg_helper; +#ifdef HARDWIRE_ENABLE +    io_output_pin(PWR_EN1); +    io_output_pin(PWR_EN2); +    io_output_pin(PWR_EN3); +    io_output_pin(PWR_EN4); +    io_output_pin(PWR_EN5); +#endif // HARDWIRE_ENABLE + + /*	io_output_pin(PWR_SDA); +    io_output_pin(PWR_SCL); + +    // Must remain HIGH when idle +    io_set_pin(PWR_SDA); +    io_set_pin(PWR_SCL); +*/ +#ifdef I2C_REWORK +	i2c_init_ex(PWR_SDA, PWR_SCL, _ltc3675_pull_up); +#endif // I2C_REWORK +    io_input_pin(PWR_IRQ); +#if !defined(DEBUG) && !defined(ATTINY88_DIP) +	io_set_pin(PWR_IRQ);	// Enable pull-up for Open Drain +#endif // DEBUG +	 +    io_input_pin(WAKEUP); +	io_set_pin(WAKEUP);	// Enable pull-up for Open Drain +	 +    io_input_pin(ONSWITCH_DB); +	io_set_pin(ONSWITCH_DB);	// Enable pull-up for Open Drain +	 +    io_input_pin(PWR_RESET); +	io_set_pin(PWR_RESET);	// Enable pull-up for Open Drain +	 +	_ltc3675_clear_irq();	// Clear old interrupt - state might have changed (e.g. undervoltage might have been resolved) + +    if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_IRQB_MASK, 0xFF, _ltc3675_pull_up) == false)	// Any PGOOD fault will pull IRQB low +		return false; +	 +	if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_UVOT, 0x70, _ltc3675_pull_up) == false)	// 3.4V UV +		return false; +	 +	if (ltc3675_has_interrupt()) +		_ltc3675_handle_irq(); +	 +	// Non-maskable: +	//	UV warning threshold (default): 2.7V +	//	Over temp warning threshold (default): 10 degrees below + +    return true; +} + +bool ltc3675_is_waking_up(void) +{ +	return io_test_pin(WAKEUP); +} + +static bool _ltc3675_is_pgood(uint8_t reg) +{ +	uint8_t val = 0x00; +	if (_ltc3675_get_realtime_status(&val) == false) +		return false; +	return ((reg & val) == reg); +} + +static bool _ltc3675_toggle_reg(uint8_t addr, uint8_t def_reg, bool on) +{ +	bool result = true; +	 +	//cli(); +	 +	uint8_t val = 0x00 | def_reg; +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, addr, &val, _ltc3675_pull_up) == false) +		return false; +	 +	val &= ~LTC3675_ENABLE_REGISTER_BIT; +	 +	if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, addr, /*def_reg*/val | (on ? LTC3675_ENABLE_REGISTER_BIT : 0x00), _ltc3675_pull_up) == false) +		//return true; +		result = false; +	 +	if (on) +	{ +		_delay_ms(LTC3675_REGULATOR_ENABLE_DELAY); +	} +	 +	//sei(); + +	return result; +	//return true; +} + +bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on) +{ +	//debug_blink2(reg + 1); +	debug_log_ex("3675 ", false); +	debug_log_byte_ex(reg, true); +	 +	// Sub-address: index of regulator +	// Data: <default reg contents> | <enable> +	 +	bool result = false; +	 +    switch (reg) +    { +        case LTC3675_REG_1: // Master +        case LTC3675_REG_2: // Slave +#ifdef HARDWIRE_ENABLE +            io_enable_pin(PWR_EN1, on); +			//break; +#else +			//debug_blink2(reg + 1); +			if (_ltc3675_toggle_reg(LTC3675_REG_BUCK1, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false) { +				//debug_blink2(reg + 1); +				return false; +			} +			//debug_blink2(reg + 1); +#endif // HARDWIRE_ENABLE +			result = (_ltc3675_is_pgood(LTC3675_Buck1_PGood) == on); +			break; +        case LTC3675_REG_3: // Master +        case LTC3675_REG_4: // Slave +#ifdef HARDWIRE_ENABLE +            io_enable_pin(PWR_EN3, on); +            //break; +#else +			if (_ltc3675_toggle_reg(LTC3675_REG_BUCK3, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false) +				return false; +#endif // HARDWIRE_ENABLE +			result = (_ltc3675_is_pgood(LTC3675_Buck3_PGood) == on); +			break; +        case LTC3675_REG_5: // I2C only +            if (_ltc3675_toggle_reg(LTC3675_REG_BOOST, LTC3675_DEFAULT_BOOST_REG_VAL, on) == false)    // (Boost address, Default reg contents | Enable) +				return false; +			result = (_ltc3675_is_pgood(LTC3675_Boost_PGood) == on); +			break; +        case LTC3675_REG_6: // Single +#ifdef HARDWIRE_ENABLE +            io_enable_pin(PWR_EN5, on); +            //break; +#else +			if (_ltc3675_toggle_reg(LTC3675_REG_BUCK_BOOST, LTC3675_DEFAULT_BUCK_BOOST_REG_VAL, on) == false) +				return false; +#endif // HARDWIRE_ENABLE +			result = (_ltc3675_is_pgood(LTC3675_BuckBoost_PGood) == on); +			break; +        //default: +		//	return false; +    } +	 +	_debug_log((result ? "+" : "-")); + +    return result; +} + +bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage) +{ +    // Not necessary due to R-bridges and default DAC registers + +    // VRAM will be 1.3579 - a little high? (re-program DAC reference) +    //  No: minimum FB step will put Vout < 1.35 +	 +	uint16_t max_voltage = 0; +	uint8_t reg_subaddr = 0; +	 +	switch (reg) +	{ +		case LTC3675_REG_1:	// 1A Buck +		case LTC3675_REG_2:	// 1A Buck +			max_voltage = 1500; +			reg_subaddr = LTC3675_REG_BUCK1; +			break; +		case LTC3675_REG_3:	// 500mA Buck +		case LTC3675_REG_4:	// 500mA Buck +			max_voltage = 1800; +			reg_subaddr = LTC3675_REG_BUCK3; +			break; +		case LTC3675_REG_5:	// 1A Boost +			max_voltage = 5000; +			reg_subaddr = LTC3675_REG_BOOST; +			break; +		case LTC3675_REG_6:	// 1A Buck-Boost +			max_voltage = 3300; +			reg_subaddr = LTC3675_REG_BUCK_BOOST; +			break; +	} +	 +	if (voltage > max_voltage) +		return false; +	 +	//uint32_t rMax = ((uint32_t)voltage * 1000) / (uint32_t)max_voltage; +	//uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800; +	uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800;	// 800mV full-scale feedback voltage +	uint32_t r = ((uint32_t)voltage * 1000) / (uint32_t)rFB; +	if (r < 450) +		return false; +	 +	uint16_t rDAC = (16 * ((uint16_t)r - 450)) / (800 - 450); +	 +	debug_log_ex("Vr ", false); +	debug_log_byte_ex(reg, false); +	debug_log_ex("=", false); +	debug_log_byte_ex((uint8_t)rDAC, false); +	 +	uint8_t val = 0x00; +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, reg_subaddr, &val, _ltc3675_pull_up) == false) +	{ +		debug_log("-"); +		return false; +	}		 +	 +	val = (val & 0xF0) | (uint8_t)rDAC; +	if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, reg_subaddr, val, _ltc3675_pull_up) == false) +	{ +		debug_log("-"); +		return false; +	} +	 +	debug_log("+"); + +	return true; +} + +bool ltc3675_is_power_button_depressed(void) +{ +	return (io_test_pin(ONSWITCH_DB) == false); +} + +bool ltc3675_has_interrupt(void) +{ +	return (io_test_pin(PWR_IRQ) == false); +} diff --git a/firmware/e300/rev_b/ltc3675.h b/firmware/e300/rev_b/ltc3675.h new file mode 100644 index 000000000..d7c4baa59 --- /dev/null +++ b/firmware/e300/rev_b/ltc3675.h @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Ettus Research LLC + */ + +#ifndef LTC3675_H +#define LTC3675_H + +//#include "types.h" +#include <stdbool.h> +#include <stdint.h> + +typedef bool (*ltc3675_reg_helper_fn)(uint8_t address); + +bool ltc3675_init(ltc3675_reg_helper_fn helper); + +typedef enum ltc3675_regulators { +    LTC3675_REG_1,  // 1A Buck +    LTC3675_REG_2,  // 1A Buck +    LTC3675_REG_3,  // 500mA Buck +    LTC3675_REG_4,  // 500mA Buck +    LTC3675_REG_5,  // 1A Boost +    LTC3675_REG_6   // 1A Buck-Boost +    // LED Boost +} ltc3675_regulator_t; + +bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on); +bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage); +bool ltc3675_is_power_button_depressed(void); +bool ltc3675_has_interrupt(void); +bool ltc3675_handle_irq(void); +int8_t ltc3675_check_status(void); +uint8_t ltc3675_get_last_status(void); +uint8_t ltc3675_status_to_error(uint8_t val); +bool ltc3675_is_power_good(uint8_t val); +bool ltc3675_is_waking_up(void); + +#endif /* LTC3675_H */ diff --git a/firmware/e300/rev_b/ltc4155.c b/firmware/e300/rev_b/ltc4155.c new file mode 100644 index 000000000..5f404e651 --- /dev/null +++ b/firmware/e300/rev_b/ltc4155.c @@ -0,0 +1,402 @@ +/* + * ltc4155.c + */  + +#ifndef CHARGER_TI + +#include "config.h" +#include "ltc4155.h" + +#include <util/delay.h> + +#include "io.h" +#include "i2c.h" +#include "power.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +static io_pin_t USBPM_IRQ	= IO_PB(1); + +#ifdef ATTINY88_DIP + +static io_pin_t CHRG_SDA     = IO_PC(2); +static io_pin_t CHRG_SCL     = IO_PC(3); + +#else + +#ifdef I2C_REWORK + +static io_pin_t CHRG_SDA     = IO_PC(4); +static io_pin_t CHRG_SCL     = IO_PC(5); + +#else + +#define CHRG_SDA	PWR_SDA +#define CHRG_SCL	PWR_SCL + +#endif // I2C_REWORK + +#endif // ATTINY88_DIP + +const bool _ltc4155_pull_up = false; + +#define LTC4155_BASE_ADDRESS    0x12 +#define LTC4155_WRITE_ADDRESS   (LTC4155_BASE_ADDRESS + 0) +#define LTC4155_READ_ADDRESS    (LTC4155_BASE_ADDRESS + 1) +/* +#define LTC4155_RETRY_DELAY     1   // us MAGIC +#define LTC4155_MAX_ACK_RETRIES 10  // * LTC4155_RETRY_DELAY us + +#define LTC4155_SCL_LOW_PERIOD  2   // 1.3 us +#define LTC4155_SCL_HIGH_PERIOD 1   // 0.6 us +#define LTC4155_BUS_FREE_TIME   2   // 1.3 us +#define LTC4155_STOP_TIME       1   // 0.6 us +*/ +enum LTC4155Registers +{ +	LTC4155_REG_USB			= 0x00,	// W/R +	LTC4155_REG_WALL		= 0x01,	// W/R +	LTC4155_REG_CHARGE		= 0x02,	// W/R +	LTC4155_REG_STATUS		= 0x03,	// R +	LTC4155_REG_GOOD		= 0x04,	// R +	LTC4155_REG_THERMISTOR	= 0x05,	// R +	LTC4155_REG_ENABLE		= 0x06,	// W/R +	LTC4155_REG_ARM_AND_SHIP= 0x07	// W +}; + +enum LTC4155InterruptMasks	// LTC4155_REG_ENABLE +{ +	LTC4155_ENABLE_USB_OTG	= 1 << 1, +	 +	LTC4155_INT_UVCL	= 1 << 2, +	LTC4155_INT_ILIMIT	= 1 << 3, +	LTC4155_INT_USB_OTG	= 1 << 4, +	LTC4155_INT_EXT_PWR	= 1 << 5, +	LTC4155_INT_FAULT	= 1 << 6, +	LTC4155_INT_CHARGER	= 1 << 7 +}; + +enum LTC4155Options	// LTC4155_REG_USB +{ +	LTC4155_USB_OTG_LOCKOUT				= 1 << 5, +	LTC4155_ENABLE_BATTERY_CONDITIONER	= 1 << 6, +	LTC4155_DISABLE_INPUT_UVCL			= 1 << 7 +}; + +enum LTC4155Shifts +{ +	LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT	= 4, +	LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE	= 2, +	LTC4155_SHIFTS_WALL_PRIORITY		= 7, +	LTC4155_SHIFTS_WALL_SAFETY_TIMER	= 5 +}; + +enum LTC4155Statuses	// LTC4155_REG_STATUS +{ +	LTC4155_LOW_BATTERY		= 1 << 0, +	LTC4155_BOOST_ENABLE	= 1 << 3, +	LTC4155_ID_PIN_DETECT	= 1 << 4, +}; + +enum LTC4155Goods	// LTC4155_REG_GOOD +{ +	LTC4155_BAD_CELL_FAULT		= 1 << 0, +	LTC4155_OTG_FAULT			= 1 << 1, +	LTC4155_OVP_ACTIVE			= 1 << 2, +	LTC4155_INPUT_UVCL_ACTIVE	= 1 << 3, +	LTC4155_INPUT_CURRENT_LIMIT_ACTIVE = 1 << 4, +	LTC4155_WALLSNS_GOOD		= 1 << 5, +	LTC4155_USBSNS_GOOD			= 1 << 6, +	LTC4155_EXTERNAL_POWER_GOOD	= 1 << 7 +}; + +enum LTC4155BatteryChargerStatues +{ +	LTC4155_CHARGER_OFF, +	LTC4155_CHARGER_LOW_BATTERY_VOLTAGE, +	LTC4155_CHARGER_CONSTANT_CURRENT, +	LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX, +	LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX, +	LTC4155_CHARGER_NTC_TOO_WARM, +	LTC4155_CHARGER_NTC_TOO_COLD, +	LTC4155_CHARGER_NTC_HOT +}; + +enum LTC4155ThermistorStatuses +{ +	LTC4155_NTC_NORMAL, +	LTC4155_NTC_TOO_COLD, +	LTC4155_NTC_TOO_WARM, +	LTC4155_NTC_FAULT +}; + +static const uint8_t _ltc4155_interrupt_mask = +//	LTC4155_ENABLE_USB_OTG |// Enable +5V on USB connector	// Is this causing the chip to power off the output?! +	LTC4155_INT_UVCL | +	LTC4155_INT_ILIMIT | +	LTC4155_INT_USB_OTG | +	LTC4155_INT_EXT_PWR |	// Turn up current limit +	LTC4155_INT_FAULT |		// Blink error +	LTC4155_INT_CHARGER;	// Illuminate charge LED + +static bool _ltc4155_clear_irq(void) +{ +	return i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, _ltc4155_interrupt_mask, _ltc4155_pull_up); +} + +bool ltc4155_clear_irq(void) +{ +	pmc_mask_irqs(true); +	 +	bool result = _ltc4155_clear_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +static uint8_t _ltc4155_last_good, _ltc4155_last_status; + +bool _ltc4155_handle_irq(void) +{ +	_ltc4155_clear_irq();	// Clear frozen registers to get the real-time ones +	 +	_delay_ms(50);	// Wait for registers to clear/update +	 +	////////////////// +	 +	uint8_t val = 0x00; +	bool result = false; +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false) +		goto _ltc4155_handle_fail; +	 +	//if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false) +	//	goto _ltc4155_handle_fail; +	 +	debug_log_ex("4155GO ", false); +	debug_log_hex(val); +	 +	if (val & LTC4155_WALLSNS_GOOD) +	{ +		uint8_t wall_state = 0; +		if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false) +			goto _ltc4155_handle_fail; +		 +		wall_state &= ~0x1E; +		wall_state |= 0x0E; +		 +		if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false) +			goto _ltc4155_handle_fail; +		 +		debug_log("I+"); +	} +	 +	_ltc4155_last_good = val; +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false) +		goto _ltc4155_handle_fail; +	 +	//if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false) +	//	goto _ltc4155_handle_fail; +	 +	debug_log_ex("4155ST ", false); +	debug_log_hex(val); +	 +	_ltc4155_last_status = val; +	 +	val >>= 5; +	 +	if (_state.blink_error == BlinkError_None) +	{ +		switch (val) +		{ +			case LTC4155_CHARGER_CONSTANT_CURRENT: +			case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX: +			case LTC4155_CHARGER_LOW_BATTERY_VOLTAGE:	// If this persists for more than 1/2hr, BAD_CELL_FAULT is enabled and FAULT interrupt is generated +			{ +				if ((_state.battery_not_present == false) && +					(_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD))) +				{ +					//charge_set_led(true); +					charge_notify(true); +					break; +				}						 +			} +			case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX:	// Small amount of current still charging the battery but below Vc/x threshold +			//case LTC4155_CHARGER_NTC_TOO_WARM: +			//case LTC4155_CHARGER_NTC_TOO_COLD: +			//case LTC4155_CHARGER_NTC_HOT: +			//	break; +			//case LTC4155_CHARGER_OFF: +			default: +				//charge_set_led(false); +				charge_notify(false); +		} +	} +	 +//	ltc4155_dump(); +	 +	result = true; +_ltc4155_handle_fail: +	_ltc4155_clear_irq();	// Even though it happens first above, this is necessary otherwise future IRQs won't be detected +	 +	return result; +} + +#define LTC4155_CHARGE_CURRENT_LIMIT	/*0xF*/0x7	// [100%] 50% + +bool ltc4155_set_charge_current_limit(uint8_t percentage) +{ +	uint8_t val = 0; +	uint8_t limit = 0; +	 +	if (percentage > 100) +		return false; +	else if (percentage == 100) +		percentage = 0xF; +	else if (percentage > 12)	// 0..88 -> 0..8800 +	{ +		uint16_t l = (((uint16_t)percentage - 12) * 100) / 586; +		limit = (uint8_t)l; +	} +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, &val, _ltc4155_pull_up) == false) +		return false; +	 +	val &= ((0x1 << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT) - 1); +	//val |= (LTC4155_CHARGE_CURRENT_LIMIT << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT); +	val |= (limit << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT); +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, val, _ltc4155_pull_up) == false) +		return false; +	 +//ltc4155_dump(); +	 +	return true; +} + +bool ltc4155_init(bool disable_charger) +{ +	io_input_pin(USBPM_IRQ); +#if !defined(DEBUG) && !defined(ATTINY88_DIP) +	io_set_pin(USBPM_IRQ);	// Enable pull-up for Open Drain +#endif // DEBUG +#ifdef I2C_REWORK +	i2c_init_ex(CHRG_SDA, CHRG_SCL, _ltc4155_pull_up); +#endif // I2C_REWORK +	if (/*_ltc4155_clear_irq()*/_ltc4155_handle_irq() == false)	// Will set interrupt masks	// FIXME: Why does this cause instability?! +		return false; + +	const uint8_t charge_state = +		(disable_charger ? 0x0 : LTC4155_CHARGE_CURRENT_LIMIT) << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT |	// Battery charger I limit = 100% +		0x3 << LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE |	// FIXME: Vbatt float = 4.05V - 4.2V for LiPo (default 0x00) +		0x0;	// Full capacity charge threshold = 10% +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, charge_state, _ltc4155_pull_up) == false) +		return false; + +	const uint8_t wall_state = +		0x0 << LTC4155_SHIFTS_WALL_PRIORITY | +		0x0 << LTC4155_SHIFTS_WALL_SAFETY_TIMER |	// Charge safety timer = 4hr	// FIXME: 8hr or Vc/x +		0xE;	// 3 amps, 0x1F - CLPROG1 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false) +		return false; + +	// FIXME: +	// Disable ID pin detection & autonomous startup +	// Enable OTG +	//i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_USB, LTC4155_USB_OTG_LOCKOUT, _ltc4155_pull_up);	// Disable autonomous startup +	//i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, LTC4155_ENABLE_USB_OTG, _ltc4155_pull_up);	// Enable OTG +	 +	if (_ltc4155_handle_irq() == false)	// One more time (IRQ LED stays lit in dev setup) +		return false; +	 +	return true; +} + +bool ltc4155_has_interrupt(void) +{ +	//bool state = io_test_pin(USBPM_IRQ); +	//debug_log_ex("4155IRQ", false); +	//debug_log_byte(state); +	//return (state != 1); +	return (io_test_pin(USBPM_IRQ) == false); +} + +bool ltc4155_handle_irq(void) +{ +	pmc_mask_irqs(true); +	 +	bool result = _ltc4155_handle_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +bool ltc4155_arm_ship_and_store(void) +{ +	return true; +} + +bool ltc4155_get_thermistor(uint8_t* val, bool* warning) +{ +	bool result = false; +	uint8_t _val = 0; +	 +	pmc_mask_irqs(true); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_THERMISTOR, &_val, _ltc4155_pull_up) == false) +		goto ltc4155_get_thermistor_fail; +	 +	if (val) +		(*val) = _val >> 1; +	 +	if (warning) +		(*warning) = ((_val & 0x01) != 0x00); +	 +	result = true; +ltc4155_get_thermistor_fail: +	pmc_mask_irqs(false); +	return result; +} + +void ltc4155_dump(void) +{ +	pmc_mask_irqs(true); +	 +	uint8_t val = 0x00; +	bool warning = false; +	 +	if (ltc4155_get_thermistor(&val, &warning) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\tTHRM", false); +	if (warning) +		debug_log_ex("!", false); +	debug_log_byte(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &val, _ltc4155_pull_up) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\tWALL", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\t4155GO ", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\t4155ST ", false); +	debug_log_hex(val); +	 +ltc4155_dump_fail: +	pmc_mask_irqs(false); +} + +#endif // !CHARGER_TI diff --git a/firmware/e300/rev_b/ltc4155.h b/firmware/e300/rev_b/ltc4155.h new file mode 100644 index 000000000..7e8e3751d --- /dev/null +++ b/firmware/e300/rev_b/ltc4155.h @@ -0,0 +1,25 @@ +/* + * ltc4155.h + * + * Created: 17/08/2012 8:09:43 PM + *  Author: Balint Seeber + */  + + +#ifndef LTC4155_H_ +#define LTC4155_H_ + +#include <stdbool.h> +#include <stdint.h> + +#ifndef CHARGER_TI + +bool ltc4155_init(bool disable_charger); +bool ltc4155_has_interrupt(void); +bool ltc4155_handle_irq(void); +void ltc4155_dump(void); +bool ltc4155_set_charge_current_limit(uint8_t percentage); + +#endif // !CHARGER_TI + +#endif /* LTC4155_H_ */ diff --git a/firmware/e300/rev_b/main.c b/firmware/e300/rev_b/main.c new file mode 100644 index 000000000..1e065ffef --- /dev/null +++ b/firmware/e300/rev_b/main.c @@ -0,0 +1,385 @@ +/* + * Copyright 2009 Ettus Research LLC + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <avr/io.h> +#include <util/delay.h> +#include <avr/sleep.h> +#include <avr/interrupt.h> + +#include "global.h" +#include "power.h" +#include "debug.h" +#include "error.h" +#include "ltc3675.h" +#ifdef CHARGER_TI +#include "bq24190.h" +#else +#include "ltc4155.h" +#endif // CHARGER_TI + +#define AUTO_POWER_ON + +#define INITIAL_DELAY	250	// ms + +FUSES = {	// FIXME: & FUSE_CKSEL1 for low power 128 kHz clock +	.low = (FUSE_CKSEL0 & FUSE_SUT0 & FUSE_CKDIV8),	// Internal 8MHz Oscillator, Slowly rising power (start-up time), Divide Clock by 8 +	.high = (FUSE_EESAVE & FUSE_SPIEN),	// Save EEPROM between flashes	// FIXME: Leave SPIEN for programming enabled? +};	// Not using watchdog as it runs during sleep and consumes power + +volatile STATE _state; + +/* +    - Main/shared variables must be volatile +	- Port pins are tri-stated on reset +	* AVR_IRQ PD(5) +	- Enable pull-ups on all O.D. outputs from regulator chip +	* PS_POR/SRST should be driven HIGH by ATTiny? +	- AVR_RESET -> RESET pin - don't configure fuse (this would disable this functionality and prohibit serial programming) +	* Ship-and-store mode for charge controller? +	* cli before I2C calls +	* PS_TX +	- en5-clk, en2-data +	* Instruction following SEI is executed before interrupts +	* LTC3675 real-time status doesn't contain UV/OT +	* LTC3675 PGOOD -> power down (no point in checking blink state) +	* On WALL, use TX, on battery use OTG switcher +	* PRR - Power Reduction Register (p40) +	- 100% -> 50% battery charge limit +	* Check latched status for UV/OT in 3675 +	* If blink error reset, get latest charge status from 4155 +	* Fix UV status check from 3675/4155 as they currently share the same error  +	* Use charger termination at 8hr or Vc/x +	* Check PGood on all regs after power on before setting powered=true +	* Re-init 4155 on soft-power on +	- Re-set 3A limit in 4155 after external power connection +	- Removing power when running on battery, 4155GO 0xA0 - but WALL has been removed +	- Why is charger reporting Constant Current when power is removed +	* ltc3675_is_power_button_depressed: check if any reg is on, otherwise value will be invalid +	* When e.g. 3.3V doesn't come up, blink code is correctly 4 but there's a very short blink before re-starting the sequence +	- Vprog<Vc/x +*/ + +bool pmc_mask_irqs(bool mask) +{ +	if (_state.interrupts_enabled == false) +		return false; +	 +	if (mask) +	{ +		if (_state.interrupt_depth == 0) +			cli(); +		++_state.interrupt_depth; +	}		 +	else +	{ +		if (_state.interrupt_depth == 0) +			return false; +		 +		--_state.interrupt_depth; +		if (_state.interrupt_depth == 0) +			sei(); +	} +	 +	return true; +} + +int main(void) +{ +	_delay_ms(INITIAL_DELAY); +	 +	/////////////////////////////////////////////////////////////////////////// +	 +	memset((void*)&_state, 0x00, sizeof(STATE)); +	 +	debug_init(); +	debug_blink(1); +	 +	//debug_log("#");	// Will not boot if this is 21 chars long?! +	debug_log("Hello world"); +	 +    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // SLEEP_MODE_PWR_SAVE combination is documented as Reserved +	 +//ltc4155_dump(); + +    // FIXME: Init as SPI slave (FPGA is master) +	 +	// 8-bit timer for blinking errors on charge LED +	TCCR0A = _BV(CTC0);		// CTC mode +	OCR0A = 244;			// 250ms with 1024 prescale +	TIMSK0 = _BV(OCIE0A);	// Enable CTC on Timer 0 + +	bool init_result = power_init(); +	debug_log_ex("Init", false); +	_debug_log(init_result ? "+" : "-"); +	debug_blink(2); +	//debug_blink_rev(6); +	 +	/////////////////////////////////// +#ifdef AUTO_POWER_ON +	power_on(); // Turn on immediately. Need to de-press power button to turn off. +	debug_log("Power"); +	debug_blink(3); +	//debug_blink_rev(10); + +	//debug_wait(); +	 +//ltc4155_dump(); +#endif // AUTO_POWER_ON +	_state.interrupts_enabled = true; +	sei();	// Enable interrupts + +	asm("nop"); + +	_state.wake_up = false;	// This will fire the first time the regs are turned on +	 +	bool one_more = false; +	 +	while (true) +	{ +		one_more = false; +#ifdef CHARGER_TI +		if (_state.bq24190_irq) +		{ +			bq24190_handle_irq(); +			 +			_state.bq24190_irq = false; +		} +#else +		if ((_state.ltc4155_irq)/* || ltc4155_has_interrupt()*/)	// [Don't know why PCINT ISR misses LTC4155 IRQ on power up, so double-check state of line] +		{ +			ltc4155_handle_irq(); +//ltc4155_dump(); +			_state.ltc4155_irq = false; +		} +#endif // !CHARGER_TI +		if (_state.core_power_bad)	// FIXME: Check whether it's supposed to be on +		{ +			if (power_is_subsys_on(PS_FPGA)) +			{ +				_delay_ms(1);	// Seeing weird 120us drop in PGOOD during boot from flash (no apparent drop in 1.0V though) +				 +				if (tps54478_is_power_good() == false) +				{ +					debug_log("ML:FPGA!"); +			 +					//power_off(); +					_state.power_off = true; +			 +					/*while (_state.wake_up == false) +					{ +						blink_error_sequence(1); +					}*/ +					pmc_set_blink_error(BlinkError_FPGA_Power);	// [If a blink error was set in power_off, this will supercede it] +				} +			}			 +			 +			_state.core_power_bad = false; +		} +		 +		if ((_state.ltc3675_irq)/* || ltc3675_has_interrupt()*/)	// This is fired on initial power up +		{ +			debug_log("ML:3675+"); +			 +			ltc3675_handle_irq(); +			 +			if (ltc3675_is_power_good(ltc3675_get_last_status()) == false) +			{ +				debug_log("ML:3675!"); +				 +				//power_off(); +				_state.power_off = true; +			} +			 +			_state.ltc3675_irq = false; +		} +		 +		if (_state.power_off) +		{ +			debug_log("ML:Off.."); +			 +			power_off(); +			 +			_state.power_off = false; +			_state.wake_up = false; +		} +		else if (_state.wake_up) +		{ +			_delay_ms(1);	// Tapping 3.1 ohm load ing 4155 in dev setup causes transient on this line and causes power on sequence to begin again +			 +			//if (_state.powered == false)	// Don't check in case button is held long enough to force LTC3675 shutdown (will not change 'powered' value) +			if (ltc3675_is_waking_up()) +			{ +				debug_log("ML:On.."); +				 +				power_on(); +			} +			 +			_state.wake_up = false; +		} +		 +		// Check to see if the error state has resolved itself at the end of each sequence of the current blink error +		 +		if ((_state.blink_error != BlinkError_None) && (_state.blink_last_loop != _state.blink_loops)) +		{ +			// [Check IRQs periodically] +			 +			bool ltc3675_use_last_status = false; +			/*if (ltc3675_has_interrupt()) +			{ +				//debug_set(IO_PB(6), ((_state.blink_loops % 2) == 0)); +				ltc3675_use_last_status = true; +				ltc3675_handle_irq(); +			}*/ +			 +			/////////////////////////// +			 +			switch (_state.blink_error) +			{ +				case BlinkError_LTC3675_UnderVoltage: +				case BlinkError_LTC3675_OverTemperature: +				case BlinkError_DRAM_Power: +				case BlinkError_3_3V_Peripherals_Power: +				case BlinkError_1_8V_Peripherals_Power: +				case BlinkError_TX_Power: +					if (((ltc3675_use_last_status) && (ltc3675_status_to_error(ltc3675_get_last_status()) != BlinkError_None)) ||  +						((ltc3675_use_last_status == false) && (ltc3675_check_status() != BlinkError_None))) +						break; +					debug_log("BE:3675-"); +					goto cancel_blink_error; +				case BlinkError_FPGA_Power: +					if (tps54478_is_power_good() == false) +						break; +					debug_log("BE:FPGA-"); +					goto cancel_blink_error; +				default: +cancel_blink_error:				 +					//debug_set(IO_PB(7), true); +					pmc_set_blink_error(BlinkError_None); +			} +			 +			//////////////////////////////////// +			 +			// More periodic checks +			// Need to do this has some interrupts are on PCINT, and while GIE is disabled, might change & change back +			//	E.g. LTC3675 IRQ due to UV, reset IRQ, re-asserts UV +#ifndef CHARGER_TI +			if (ltc4155_has_interrupt()) +			{ +				debug_log("BE:4155"); +				 +				_state.ltc4155_irq = true; +				one_more = true; +			} +#endif // !CHARGER_TI +			if (ltc3675_has_interrupt()) +			{ +				debug_log("BE:3675"); +				 +				_state.ltc3675_irq = true; +				one_more = true; +			} +			 +			if (power_is_subsys_on(PS_FPGA)) +			{ +				if (tps54478_is_power_good() == false) +				{ +					debug_log("BE:FPGA!"); +				 +					_state.core_power_bad = true; +					one_more = true; +				} +			} +			 +			//////////////////////////////////// +			 +			_state.blink_last_loop = _state.blink_loops; +		} +		 +		//if (_state.timers_running == false) +		if ((_state.active_timers == 0) && (one_more == false)) +		{ +			debug_log("^"); +			sleep_mode(); +			debug_log("$"); +		}			 +	} + +	return 0; +} + +uint8_t pmc_get_blink_error(void) +{ +	return _state.blink_error; +} + +void pmc_set_blink_error(uint8_t count) +{ +	if ((_state.blink_error != BlinkError_None) && (count /*> _state.blink_error*/!= BlinkError_None))	// [Prioritise] Always keep first sequence running +		return; +	else if (_state.blink_error == count)	// Don't restart if the same +		return; +	 +	if (count == BlinkError_None) +	{ +		debug_log("BLNK-"); +		_state.blink_stop = true; +		return; +	} +	 +	//char msg[25]; +	//sprintf(msg, "Blink code = %i\n", count); +	//debug_log(msg); +	debug_log_ex("BLNK ", false); +	debug_log_byte(count); +	 +	_state.blink_error = count; +	_state.blink_loops = 0; +	_state.blink_last_loop = 0; +	_state.blinker_state = 0; +	_state.blink_stop = false; + +	charge_set_led(false); +	 +	TCNT0 = 0; +	if ((TCCR0A & 0x07) == 0x00)	// Might already be active with existing error +		_state.active_timers++; +	TCCR0A |= 0x05;	// Start with 1024 prescale +} + +ISR(TIMER0_COMPA_vect)	// Blink the sequence, and leave one slot at the beginning and end where the LED is off so one can get a sense of how many blinks occurred +{ +	pmc_mask_irqs(true); +	 +	if (_state.blinker_state < (2 * _state.blink_error + 1)) +		charge_set_led((_state.blinker_state % 2) == 1); +	 +	_state.blinker_state++; +	 +	if (_state.blinker_state == (2 * _state.blink_error + 1 + 1)) +	{ +		_state.blinker_state = 0; +		 +		if (_state.blink_stop) +		{ +			if ((TCCR0A & 0x07) != 0x00) +				_state.active_timers--; +			TCCR0A &= ~0x07; +			 +			_state.blink_error = BlinkError_None; +			 +			debug_log("BLNK."); +		} +		else +		{ +			_state.blink_loops++; +		} +	} +	 +	pmc_mask_irqs(false); +} diff --git a/firmware/e300/rev_b/power.c b/firmware/e300/rev_b/power.c new file mode 100644 index 000000000..79864f817 --- /dev/null +++ b/firmware/e300/rev_b/power.c @@ -0,0 +1,909 @@ +/* +	* Test battery voltage code +	* Charger error blinking uses busy wait - will drain battery if encounters error while unattended? Surely rest of H/W will pull more current. +*/ +#include "config.h" +#include "power.h" + +#include <string.h> +#include <util/delay.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/sleep.h> + +#include "io.h" +#include "i2c.h" +#include "ltc3675.h" +#include "ltc4155.h" +#include "bq24190.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +#define BLINK_ERROR_DELAY		250  // ms + +#define POWER_DEFAULT_DELAY     50  // ms +#define POWER_DEFAULT_RETRIES   10 + +#define BATT_MIN_VOLTAGE		2000	// mV + +#define ARRAY_SIZE(a)			(sizeof(a)/sizeof(a[0])) +#define ZERO_MEMORY(s)			memset(&s, 0x00, sizeof(s)) + +#ifndef I2C_REWORK +io_pin_t PWR_SDA     = IO_PC(4); +io_pin_t PWR_SCL     = IO_PC(5); + +io_pin_t USBHUB_RESET= IO_PA(2); +#endif // I2C_REWORK + +//volatile bool powered = false; + +#ifdef DDR3L +#define DRAM_VOLTAGE	1350 +#else +#define DRAM_VOLTAGE	0	// Hardware default +#endif // DDR3 + +struct reg_config { +    int16_t voltage;    // mV +	uint8_t device; +    uint8_t address;    // Device specific +    bool powered; +} default_reg_config[] = {        // Index maps to 'power_subsystem_t', 0 volts means leave at hardware default +	{ 0000, REG_UNKNOWN, 0/*, true*/ },				// PS_UNKNOWN +	{ 1000, REG_TPS54478, 0/*, true*/ },			// PS_FPGA +	{ DRAM_VOLTAGE, REG_LTC3675, LTC3675_REG_1 },	// PS_VDRAM +	{ /*1800*/0, REG_LTC3675, LTC3675_REG_3 },		// PS_PERIPHERALS_1_8 +	{ /*3300*/0, REG_LTC3675, LTC3675_REG_6 },		// PS_PERIPHERALS_3_3 +	{ /*5000*/0, REG_LTC3675, LTC3675_REG_5 }		// PS_TX +}; +/* +int8_t power_get_regulator_index(uint8_t device, uint8_t address) +{ +	for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i) +	{ +		struct reg_config* reg = default_reg_config + i; +		if ((reg->device == device) && (reg->address == address)) +			return i; +	} +	 +	return -1; +} +*/ +bool power_is_subsys_on(power_subsystem_t index) +{ +	if ((index <= PS_UNKNOWN) || (index >= PS_MAX)) +		return false; +	 +	return default_reg_config[index].powered; +} + +static bool ltc3675_reg_helper(uint8_t address) +{ +	for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i) +	{ +		struct reg_config* reg = default_reg_config + i; +		if ((reg->device == REG_LTC3675) && (reg->address == address)) +			return reg->powered; +	} +#ifdef DEBUG_SAFETY +	debug_log_ex("!3675HLP ", false); +	debug_log_hex(address); +#endif // DEBUG_SAFETY +	return false; +	//return power_is_subsys_on(power_get_regulator_index(REG_LTC3675, address) - 1); +} + +static io_pin_t AVR_CS      = IO_PB(2); +static io_pin_t AVR_MOSI    = IO_PB(3); +static io_pin_t AVR_MISO    = IO_PB(4); +static io_pin_t AVR_SCK     = IO_PB(5); + +#ifndef ATTINY88_DIP +static io_pin_t FTDI_BCD    = IO_PB(6); +static io_pin_t FTDI_PWREN2 = IO_PB(7); +#endif // ATTINY88_DIP + +static io_pin_t AVR_RESET   = IO_PC(6); +static io_pin_t AVR_IRQ     = IO_PD(5); + +/////////////////////////////////////////////////////////////////////////////// + +#define TPS54478_START_DELAY	10	// 50 (safety)	// 3 (per spec)	// ms (some arbitrary value so that the external power supply can settle) + +#ifdef ATTINY88_DIP +static io_pin_t CORE_PWR_EN = IO_PC(1);	// IO_PC(7) not routed by card, using PWER_EN1 instead +#else +static io_pin_t CORE_PWR_EN = IO_PA(3); +#endif // ATTINY88_DIP +static io_pin_t CORE_PGOOD = IO_PB(0); + +void tps54478_init(bool enable) +{ +	tps54478_set_power(enable); +	io_clear_pin(CORE_PWR_EN); +	 +    io_input_pin(CORE_PGOOD); +#if !defined(DEBUG) && !defined(ATTINY88_DIP)	// Don't enable pull-up when connected to a pulled-up switch +	io_set_pin(CORE_PGOOD);	// Enable pull-up for Open Drain +#endif // DEBUG +//#ifdef DEBUG +//	io_enable_pin(CORE_PWR_EN, false); +//#endif // DEBUG +//_delay_ms(2500); +} + +void tps54478_set_power(bool on) +{ +	debug_log_ex("54478", false); +	 +	// Assumes: Hi-Z input/LOW output +	 +	if (on) +	{ +		io_input_pin(CORE_PWR_EN); +		_delay_ms(TPS54478_START_DELAY); +		 +		debug_log("+"); +	}		 +	else +	{ +		io_output_pin(CORE_PWR_EN); +		// Don't delay here as we can't detect its state anyway +		 +		debug_log("-"); +	}		 +	 +	//io_enable_pin(CORE_PWR_EN, on); +} + +bool tps54478_is_power_good(void) +{ +    return io_test_pin(CORE_PGOOD);	// This doesn't necessarily mean it's good - the chip might be malfunctioning (or switched off) +} + +/////////////////////////////////////////////////////////////////////////////// + +static io_pin_t CHARGE      = IO_PD(1); + +#if !defined(ATTINY88_DIP) && defined(LED_POLARITY) +static io_pin_t POWER_LED	= IO_PC(7); + +void power_set_led_ex(bool on, bool swap) +{ +	if (swap) +	{ +		if ((on == false) && (/*io_is_pin_set(CHARGE)*/_state.battery_charging))	// If charging and turning off, don't change charge light +		{ +			charge_set_led(true);	// Force it again just in case +			return; +		} +	}		 +	 +	io_clear_pin(CHARGE); +	io_enable_pin(POWER_LED, on); +} + +void power_set_led(bool on) +{ +	power_set_led_ex(on, true); +} +#endif // !ATTINY88_DIP && LED_POLARITY + +void charge_set_led_ex(bool on, bool swap) +{ +#ifdef ATTINY88_DIP +	// +#else + +#ifdef LED_POLARITY +	io_clear_pin(POWER_LED); +#endif // LED_POLARITY + +#endif // ATTINY88_DIP + +#ifdef ATTINY88_DIP +	io_enable_pin(CHARGE, !on); +#else +    io_enable_pin(CHARGE, on); + +#ifdef LED_POLARITY +	if (swap) +	{ +		if ((on == false) && (_state.powered))	// If no longer charging, turn power light back on +			power_set_led(true); +	}			 +#endif // LED_POLARITY + +#endif // ATTINY88_DIP +} + +void charge_set_led(bool on) +{ +	charge_set_led_ex(on, true); +} + +void charge_notify(bool charging) +{ +	_state.battery_charging = charging; +	 +	charge_set_led(charging); +} + +/////////////////////////////////////////////////////////////////////////////// + +void usbhub_reset(void) +{ +#ifndef I2C_REWORK +	io_clear_pin(USBHUB_RESET); +	 +	_delay_us(1 * 10);	// Minimum active low pulse is 1us +	 +	io_set_pin(USBHUB_RESET); +#endif // I2C_REWORK +} + +/////////////////////////////////////////////////////////////////////////////// + +void power_signal_interrupt(void) +{ +    io_set_pin(AVR_IRQ);	// FIXME: Active low? +} + +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(DEBUG) && !(defined(ENABLE_SERIAL) && defined(ATTINY88_DIP)) +static io_pin_t PS_POR      = IO_PD(6); +#define PS_POR_AVAILABLE +#endif // DEBUG +static io_pin_t PS_SRST     = IO_PD(7); + +#define FPGA_RESET_DELAY    10  // ms   // MAGIC + +void fpga_reset(bool delay) +{ +#ifdef PS_POR_AVAILABLE +    io_clear_pin(PS_POR); +#endif // PS_POR_AVAILABLE +    io_clear_pin(PS_SRST); + +    if (delay) +        _delay_ms(FPGA_RESET_DELAY); +#ifdef PS_POR_AVAILABLE +    io_enable_pin(PS_POR, true); +#endif // PS_POR_AVAILABLE +    io_enable_pin(PS_SRST, true); +} + +/////////////////////////////////////////////////////////////////////////////// + +static io_pin_t VBAT        = IO_PC(0); + +void battery_init(void) +{ +    //io_input_pin(VBAT); +    DIDR0 |= 0x1;           // Digital input disable PC0 (ADC0) + +    ADMUX = (1 << REFS0)    // AVcc reference +          | (0 << ADLAR)    // Left-aligned result +          | (0 << MUX0);    // ADC0 + +    ADCSRA = (0x7 << ADPS0);// Prescale clock by 128 +} + +uint16_t battery_get_voltage(void) +{ +    // Vout = (357k / (274k + 357k)) * Vbat +    // Vbat = (Vout * (274k + 357k)) / 357k + +    // ADC = (Vin * 1024) / Vref +    // Vin = (ADC * Vref) / 1024 +    // Vref = 3.3 + +    // Vbat(mV) = 1000 * (((ADC * 3.3) / 1024) * (274k + 357k)) / 357k +    // Vbat(mV) ~= ADC * 5.70 + +    ADCSRA |= (1 << ADEN);        // FIXME: Turn on ADC (or leave on all the time?) + +    ADCSRA |= (1 << ADSC);  // Start conversion + +    while (ADCSRA & (1 << ADSC));   // Wait for End of Conversion + +    /*uint16_t*/uint32_t voltage = (ADCH << 8) | (ADCL << 0); +#ifdef ATTINY88_DIP +	voltage = (voltage * 32227) / 10000;	// ~3.22265625 +#else +    voltage = (voltage * 56961) / 10000;	// ~5.69606748 +#endif // ATTINY88_DIP +    ADCSRA &= ~(1 << ADEN);         // FIXME: Turn off ADC (or leave on all the time?) + +    return (uint16_t)voltage; +} + +/////////////////////////////////////////////////////////////////////////////// + +void blink_error_sequence(uint8_t len) +{ +    charge_set_led(false); +    _delay_ms(BLINK_ERROR_DELAY * 2); + +    for (; len > 0; len--) { +        charge_set_led(true); +        _delay_ms(BLINK_ERROR_DELAY); +        charge_set_led(false); +        _delay_ms(BLINK_ERROR_DELAY); +    } + +    //for (len = 2; len > 0; len--)   // Could have *2 on delay, but so as never to overflow 8-bit argument +    //    _delay_ms(BLINK_ERROR_DELAY); +} + +typedef struct power_params { +    power_subsystem_t subsys; +    bool enable; +    uint8_t retry; +    //uint16_t opaque; +} power_params_t; + +static bool _power_up_fpga(power_params_t* params) +{ +    if (params->subsys != PS_FPGA) +        return false; + +    if (params->enable == false) +    { +        //if (tps54478_is_power_good() == false)  // Already off +		//	return true; + +        if (params->retry == 0) +		{ +			io_clear_pin(PS_SRST);	// FIXME: Hold it low to stop +#ifdef PS_POR_AVAILABLE +			io_clear_pin(PS_POR);	// Prepare it for shutdown, and then the potential next power cycle +#endif // PS_POR_AVAILABLE			 +            tps54478_set_power(false); +		} + +        //return (tps54478_is_power_good() == false); +		return true; +    } + +    //bool fpga_power_good = tps54478_is_power_good();  // TODO: Can it ever already be good? + +    if (params->retry == 0) +        tps54478_set_power(true); + +    return tps54478_is_power_good(); +} + +static bool _power_up_reg(power_params_t* params) +{ +    if ((params->subsys > PS_TX) || (params->subsys < PS_VDRAM)) +        return false; + +    struct reg_config* cfg = default_reg_config + params->subsys; + +    if (params->enable == false) +        return ltc3675_enable_reg(cfg->address, false); + +	if (cfg->voltage > 0) +	{ +		if (ltc3675_set_voltage(cfg->address, cfg->voltage) == false) +			return false; +	}	 + +    return ltc3675_enable_reg(cfg->address, true); +} + +static bool _power_enable_subsys(power_params_t* params) +{ +    switch (params->subsys) +    { +        case PS_FPGA: +            return _power_up_fpga(params); +        //case PS_: +        //    break; +        default: +            return _power_up_reg(params); +    } + +    return false;   // Should never get here +} + +bool power_enable(power_subsystem_t subsys, bool on) +{ +    power_params_t params; +    ZERO_MEMORY(params); +    params.subsys = subsys; +    params.enable = on; + +    return _power_enable_subsys(¶ms); +} + +typedef bool (*boot_function_t)(power_params_t*); + +struct boot_step { +    power_subsystem_t subsys; +	//boot_function_t fn; +	//uint8_t delay; +	//uint8_t retries; +    //uint16_t opaque; +	//bool powered; +} boot_steps[] = {  // MAGIC: Retries/delays +	{ PS_FPGA,				/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 7..8						// 3..4 +	{ PS_VDRAM,				/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 9..10					// 5..6 +	{ PS_PERIPHERALS_1_8,	/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 11..12					// 7..8 +	{ PS_PERIPHERALS_3_3,	/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 13..14					// 9..10 +	{ PS_TX,				/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }  // CHECK: Leaving TX off +}; +/* +bool power_is_subsys_on(int8_t index) +{ +	if ((index < 0) || (index >= ARRAY_SIZE(boot_steps))) +		return false; +	 +	struct boot_step* step = boot_steps + index; +	 +	return step->powered; +} +*/ +bool power_init(void) +{ +	io_output_pin(CHARGE); +#ifdef LED_POLARITY +    io_output_pin(POWER_LED); +#endif // LED_POLARITY + +	charge_set_led(true); +	 +	battery_init(); +	 +    tps54478_init(true);	// Will keep EN float (keep power on) +#ifndef I2C_REWORK +	i2c_init(PWR_SDA, PWR_SCL); +	 +	io_output_pin(USBHUB_RESET); +#endif // I2C_REWORK +#ifdef CHARGER_TI +	if (bq24190_init(true) == false) +		return false; +#else +	if (ltc4155_init(/*_state.battery_not_present*/true/*false*/) == false) +		return false; +#endif // CHARGER_TI +#ifdef CHARGER_TI +	_delay_ms(1000);	// Still at 1.4V on dev board +#else +	_delay_ms(25);	// Wait for charge current to stop (Vbatt to fall to 0V) +#endif // CHARGER_TI +	uint16_t batt_voltage = battery_get_voltage(); +	debug_log_ex("Vb ", false); +	debug_log_byte((uint8_t)(batt_voltage / 100)); +	//debug_log_hex_ex(batt_voltage >> 8, false); +	//debug_log_hex(batt_voltage & 0xFF); +	if (batt_voltage < BATT_MIN_VOLTAGE) +	{ +		_state.battery_not_present = true; +		 +		//debug_log("NoBatt"); +	} +	else +	{ +#ifdef CHARGER_TI +		bq24190_toggle_charger(true); +#else +		ltc4155_set_charge_current_limit(50); +#endif // CHARGER_TI +	} + +    if (ltc3675_init(ltc3675_reg_helper) == false) +		return false; +#ifdef PS_POR_AVAILABLE +	io_output_pin(PS_POR); +#endif // PS_POR_AVAILABLE +    io_output_pin(PS_SRST); +    // Hold low until power is stable +#ifdef PS_POR_AVAILABLE +    io_clear_pin(PS_POR); +#endif // PS_POR_AVAILABLE +    io_clear_pin(PS_SRST); +/* +    AVR_CS +    AVR_MOSI +    AVR_MISO +    AVR_SCK + +    FTDI_BCD +    FTDI_PWREN2 +*/ +	io_input_pin(AVR_RESET);	// Has external pull-up (won't do anything because this is configured at the hardware RESET pin) +	 +	//io_output_pin(AVR_IRQ);		// Output here, input to FPGA +    io_input_pin(AVR_IRQ); +	//io_set_pin(AVR_IRQ);	// FIXME: Active low? +	 +	/////////////// +	 +	EICRA = _BV(ISC01) | _BV(ISC00) | _BV(ISC10)/* | _BV(ISC11)*/;	// Rising edge for INT0 (WAKEUP). [Falling for INT1.] Any logical change INT1 (ONSWITCH_DB) +	//EIMSK = _BV(INT0);	// [Turn on WAKEUP interrupt] Don't do this, as unit will turn on anyway +	EIMSK = _BV(INT1) | _BV(INT0);	// Turn on ONSWITCH_DB and WAKEUP +	 +	PCMSK0 = _BV(PCINT1) | _BV(PCINT0);	// USBPM_IRQ | CORE_PGOOD +	PCMSK2 = _BV(PCINT16)/* | _BV(PCINT20)*/;	// PWR_IRQ/* | PWR_RESET*/ +	PCICR = _BV(PCIE2) | _BV(PCIE0); + +	/////////////// +/* +	TCNT0; +	OCR0A = 0x; +	TCCR0A = _BV(CTC0); +	TIFR0; +	TIMSK0; +	TCCR0A |= 0x05;	// Switch on with 1024 prescaler +*/ +	TCCR1B = _BV(WGM12);	// CTC mode +	OCR1A = 15624 * 2;		// Hold button for 2 seconds to switch off +	TIMSK1 = _BV(OCIE1A);	// Enable CTC on Timer 1 +	 +	charge_set_led(false); +	 +	return true; +} + +bool power_on(void) +{ +	pmc_mask_irqs(true); +	 +    //charge_set_led(false); +	 +	bool last_power_led_state = /*true*/false; +	 +	//if ((ARRAY_SIZE(boot_steps) % 2) == 0)	// Should end with 'true' +	//	last_power_led_state = false; +	 +	power_set_led(last_power_led_state); +	 +	uint8_t step_count, retry; +	for (step_count = 0; step_count < ARRAY_SIZE(boot_steps); step_count++) +	{ +		last_power_led_state = !last_power_led_state; +		power_set_led(last_power_led_state); +		 +//		debug_blink(step_count); +//		debug_blink(3 + (step_count * 2) + 0); +//		debug_blink_rev(7 + (step_count * 2) + 0); +		 +	    struct boot_step* step = boot_steps + step_count; +	    if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN)) +            continue; +		 +		debug_log_ex("PWR ", false); +		debug_log_byte_ex(step->subsys, true); + +        power_params_t params; + +	    for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++) +		{ +	        ZERO_MEMORY(params); +            params.subsys = step->subsys; +            params.enable = true; +	        params.retry = retry; + +	        if ((/*(step->fn != NULL) && (step->fn(¶ms))) || +				((step->fn == NULL) && */(_power_enable_subsys(¶ms)))) +			{ +				//step->powered = true; +				default_reg_config[step->subsys].powered = true; +				 +				debug_log("+"); +//				debug_blink(3 + (step_count * 2) + 1); +//				debug_blink_rev(7 + (step_count * 2) + 1); + +//ltc4155_dump(); + +                break; +	        } +			 +			debug_log("?"); + +            if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/) +                _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY); +	    } +		 +//		debug_blink(step_count); + +	    if (retry == /*step->retries*/POWER_DEFAULT_RETRIES) +	        break; +    } + +    if (step_count != ARRAY_SIZE(boot_steps)) +	{ +		debug_log("x"); +		 +		//sei();	// For button press detection + +        /*while (_state.powered == false) { +            blink_error_sequence(step_count + BlinkError_FPGA_Power); +        }*/ +		pmc_set_blink_error(step_count + BlinkError_FPGA_Power); + +		pmc_mask_irqs(false); + +        return false; +    } +	 +	/////////////////////////////////// +	 +	//bool was_hub_in_reset = (io_is_pin_set(USBHUB_RESET) == false); +	 +	usbhub_reset(); +	 +	/*if (was_hub_in_reset) +	{ +		_delay_ms(10); +		usbhub_reset(); +	}*/ +	 +	fpga_reset(false);  // Power has been brought up, so let FPGA run +	 +	/////////////////////////////////// +	 +//	ltc4155_dump(); +	 +	// Turn off WAKEUP interrupt, enable ONSWITCH_DB +	//EIMSK = _BV(INT1); +	 +	_state.powered = true; +//debug_blink_rev(1); +//_delay_ms(1000);	// Wait for FPGA PGOOD to stabilise +	pmc_mask_irqs(false); +	 +	power_set_led(true); +	 +	if (_state.battery_charging) +	{ +		_delay_ms(500*2); +		charge_set_led(true); +	}		 +	 +    return true; +} + +uint8_t power_off(void) +{ +	pmc_mask_irqs(true); +	 +	io_clear_pin(PS_SRST);	// FIXME: Hold it low to stop FPGA running +	 +	bool last_power_led_state = /*false*/true; +	 +	//if ((ARRAY_SIZE(boot_steps) % 2) == 0)	// Should end with 'false' +	//	last_power_led_state = true; +	 +	//power_set_led(last_power_led_state); +	 +	/////////////////////////////////// +	 +	int8_t step_count, retry; +	for (step_count = ARRAY_SIZE(boot_steps) - 1; step_count >= 0; step_count--) +	{ +		last_power_led_state = !last_power_led_state; +		power_set_led(last_power_led_state); +		 +//		debug_blink(step_count); +		 +	    struct boot_step* step = boot_steps + step_count; +	    if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN)) +            continue; + +        power_params_t params; + +	    for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++) +		{ +	        ZERO_MEMORY(params); +            params.subsys = step->subsys; +            params.enable = false; +	        params.retry = retry; +			 +			if ((/*(step->fn != NULL) && (step->fn(¶ms))) || +				((step->fn == NULL) && */(_power_enable_subsys(¶ms)))) +			{ +				//step->powered = false; +				default_reg_config[step->subsys].powered = false; +				break; +			} +			 +            if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/) +                _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY); +	    } +		 +//		debug_blink(step_count); + +	    if (retry == /*step->retries*/POWER_DEFAULT_RETRIES) +	        break; +    } + +    if (step_count != -1) +	{ +		/*pmc_mask_irqs(false); +		 +        while (_state.powered) { +            blink_error_sequence(step_count + BlinkError_FPGA_Power); +        }*/ +		if (pmc_get_blink_error() == BlinkError_None)	// Only set blink error if no existing error +			pmc_set_blink_error(step_count + BlinkError_FPGA_Power); +		 +		pmc_mask_irqs(false); + +        return (step_count + 1); +    } +	 +	/////////////////////////////////// +	 +	// Turn off WAKEUP interrupt, enable ONSWITCH_DB +	//EIMSK = _BV(INT1); + +	_state.powered = false; +	 +	pmc_mask_irqs(false); +	 +	power_set_led_ex(false, false); +	_delay_ms(500*2); +	 +	power_set_led(false);	// Will turn on charger LED if battery is charging +	 +	return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUG + +#ifdef ATTINY88_DIP +static io_pin_t DEBUG_1 = IO_PB(6); +static io_pin_t DEBUG_2	= IO_PB(7); +#endif // ATTINY88_DIP + +#endif // DEBUG + +ISR(INT0_vect)	// PD(2) WAKEUP: Rising edge +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	//power_on(); +	debug_log("\nINT0\n"); +	_state.wake_up = true; +	 +	//sei(); +	pmc_mask_irqs(false); +} + +ISR(INT1_vect)	// PD(3) ONSWITCH_DB (PB_STAT): Any change +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	if (ltc3675_is_power_button_depressed()) +	{ +		debug_log("PWRBTN+"); +		 +		TCNT1 = 0; +		if ((TCCR1B & 0x07) == 0x00) +		{ +			_state.active_timers++; +			debug_log("TIMER1+"); +		} +		TCCR1B |= /*0x5*/0x3;	// [1024] 64 prescaler +		//_state.timers_running = true; +		 +		//debug_set(DEBUG_1, true); +		//debug_set(DEBUG_2, false); +	} +	else +	{ +		debug_log("PWRBTN-"); +		 +		//if (TIMSK1 & _BV(OCIE1A))	// If letting go of button and still running, stop timer +		{ +			//TIMSK1 &= ~_BV(OCIE1A); +			if ((TCCR1B & 0x07) != 0x00) +			{ +				_state.active_timers--; +				debug_log("TIMER1-"); +			} +			TCCR1B &= ~0x7;	// Disable timer +			//_state.timers_running = false; +			 +			//debug_set(DEBUG_1, false); +		} +	} +	 +	//sei(); +	pmc_mask_irqs(false); +} + +ISR(TIMER1_COMPA_vect) +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	debug_log("TIMER1"); + +	//TIMSK1 &= ~_BV(OCIE1A);	// Turn off timer +	TCCR1B &= ~0x7;	// Disable timer +	//_state.timers_running = false; +	_state.active_timers--; +	 +	if (_state.powered) +	{ +		debug_log("PWROFF"); +		 +		_state.power_off = true; +	}		 +	 +	//debug_set(DEBUG_2, true); +	 +	//power_off(); +	 +	//sei(); +	pmc_mask_irqs(false); +	 +	//sleep_mode(); +} + +ISR(PCINT0_vect) +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	//debug_log("PCINT0"); +	 +	// CORE_PGOOD +	//	Assert low: power problem -> shutdown +	// USBPM_IRQ +	//	Charge status change? -> update LED +	//	Power problem:	battery -> blink charge LED +	//					major -> shutdown +	 +	if (/*(_state.powered) && */(/*io_test_pin(CORE_PGOOD)*/tps54478_is_power_good() == false)) +	{ +		_state.core_power_bad = true; +	} +#ifdef CHARGER_TI +	if (bq24190_has_interrupt()) +	{ +		_state.bq24190_irq = true; +	} +#else +	if (ltc4155_has_interrupt()) +	{ +		_state.ltc4155_irq = true; +	} +#endif // CHARGER_TI +	//sei(); +	pmc_mask_irqs(false); +} + +ISR(PCINT2_vect) +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	//debug_log("PCINT2"); +	 +	// PWR_IRQ +	//	Regulator problem: shutdown +	// PWR_RESET +	//	Ignored +	 +	if (ltc3675_has_interrupt()) +	{ +		//debug_set(IO_PB(6), true); +		_state.ltc3675_irq = true; +	} +	 +	//sei(); +	pmc_mask_irqs(false); +} diff --git a/firmware/e300/rev_b/power.h b/firmware/e300/rev_b/power.h new file mode 100644 index 000000000..453633414 --- /dev/null +++ b/firmware/e300/rev_b/power.h @@ -0,0 +1,58 @@ +#ifndef POWER_H +#define POWER_H + +#include <stdbool.h> +#include <stdint.h> + +void tps54478_init(bool enable); +void tps54478_set_power(bool on);   // Zynq core power (1.0V for FPGA) +bool tps54478_is_power_good(void); + +void charge_set_led(bool on);		// Here for error blink codes +void charge_notify(bool charging); + +void power_signal_interrupt(void); + +void fpga_reset(bool delay); + +typedef enum power_subsystems { +    PS_UNKNOWN, +    PS_FPGA, +    PS_VDRAM, +    PS_PERIPHERALS_1_8, +    PS_PERIPHERALS_3_3, +    PS_TX, +	PS_MAX +} power_subsystem_t; + +enum Regulators +{ +	REG_UNKNOWN, +	REG_TPS54478, +	REG_LTC3675 +}; + +bool power_enable(power_subsystem_t subsys, bool on); + +void battery_init(void); +uint16_t battery_get_voltage(void);  // mV + +bool power_init(void); +bool power_on(void); +uint8_t power_off(void); + +//bool power_is_subsys_on(int8_t index); +bool power_is_subsys_on(power_subsystem_t index); +//int8_t power_get_regulator_index(uint8_t device, uint8_t address); +//bool ltc3675_reg_helper(uint8_t address); + +void usbhub_reset(void); + +#ifndef I2C_REWORK +#include "io.h" + +extern io_pin_t PWR_SDA; +extern io_pin_t PWR_SCL; +#endif // I2C_REWORK + +#endif // POWER_H diff --git a/firmware/e300/rev_c/Makefile b/firmware/e300/rev_c/Makefile new file mode 100644 index 000000000..0f13dc60b --- /dev/null +++ b/firmware/e300/rev_c/Makefile @@ -0,0 +1,76 @@ +# +# Copyright 2009 Ettus Research LLC +# + +################################################## +# Compiler +################################################## +CC = avr-gcc +OBJCOPY = avr-objcopy +STRIP = avr-strip +OBJDUMP = avr-objdump +SREC = srec_cat +CFLAGS = -Os -std=gnu99 -Wall -fshort-enums -pedantic-errors -Wl,--gc-sections \ +	-Wstrict-prototypes -Wmissing-prototypes -Wcast-align -Wshadow \ +	-DENABLE_SERIAL -DCHARGER_TI -DLED_POLARITY -DDEBUG_VOID +# -DENABLE_SERIAL	: Output serial debug +# -DCHARGER_TI		: Use TI charger (rev B) instead of LTC (rev A) +# -DLED_POLARITY	: Dual-polarity LED on rev B +# -DDDR3L		: Lower DDR voltage (rev B R-divider changed, so disable this to get back into nominal range) +# -DDEBUG_VOID		: Use (void) debug function replacements instead of inline NOOP (use if .text overflows) +# -DI2C_REWORK		: Rev A only +# -DDEBUG		: Enable debug routines (LED blinks, etc) +# -DDEBUG_SAFETY	: Extra debug prints +# -DATTINY88_DIP	: ATTINY88 DIP testing on STK600 +# -DHARDWIRE_ENABLE : LTC3675 dedicated enable lines - don't use + +#-Werror +#-D IO_DEBUG + +################################################## +# Files +################################################## +HDRS = +SRCS =  main.c io.c power.c ltc3675.c i2c.c debug.c bq24190.c +TARGET = main + +################################################## +# Device +################################################## +MMCU = attiny88 +#PROGRAMMER = avrisp2 +PROGRAMMER = stk600 +PORT = usb +AVRDUDE = avrdude -p $(MMCU) -c $(PROGRAMMER) -P $(PORT) + +################################################## +# Global Targets +################################################## +all: $(TARGET).hex + +clean: +	$(RM) *.o *.elf *.hex + +install: all +	$(AVRDUDE) -U flash:w:$(TARGET).hex:i + +################################################## +# Dependency Targets +################################################## +fuses.hex: $(TARGET).elf +	$(OBJCOPY) -j .fuse -O ihex $< $@ --change-section-lma .fuse=0 + +lfuse.hex: fuses.hex +	$(SREC) $< -Intel -crop 0x00 0x01 -offset  0x00 -O $@ -Intel + +hfuse.hex: fuses.hex +	$(SREC) $< -Intel -crop 0x01 0x02 -offset -0x01 -O $@ -Intel + +$(TARGET).hex: $(TARGET).elf +	$(OBJCOPY) -R .eeprom -R .fuse -O ihex $< $@ + +$(TARGET).elf: $(SRCS:.c=.o) +	$(CC) -mmcu=$(MMCU) $^ -o $@ + +%.o: %.c $(HDRS) Makefile +	$(CC) -mmcu=$(MMCU) -c $< -o $@ $(CFLAGS) diff --git a/firmware/e300/rev_c/PMC.atsln b/firmware/e300/rev_c/PMC.atsln new file mode 100644 index 000000000..e9f4ffc79 --- /dev/null +++ b/firmware/e300/rev_c/PMC.atsln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Atmel Studio Solution File, Format Version 11.00 +Project("{54F91283-7BC4-4236-8FF9-10F437C3AD48}") = "PMC", "PMC.cproj", "{A379D421-4236-44AF-A711-52B7BDA29919}" +EndProject +Global +	GlobalSection(SolutionConfigurationPlatforms) = preSolution +		Debug|AVR = Debug|AVR +		Release (DDR3L)|AVR = Release (DDR3L)|AVR +		Release (DDR3L, Ti)|AVR = Release (DDR3L, Ti)|AVR +		Release (Dev)|AVR = Release (Dev)|AVR +		Release|AVR = Release|AVR +	EndGlobalSection +	GlobalSection(ProjectConfigurationPlatforms) = postSolution +		{A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.ActiveCfg = Debug|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Debug|AVR.Build.0 = Debug|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.ActiveCfg = Release (DDR3L)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L)|AVR.Build.0 = Release (DDR3L)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.ActiveCfg = Release (DDR3L, Ti)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (DDR3L, Ti)|AVR.Build.0 = Release (DDR3L, Ti)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.ActiveCfg = Release (Dev)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release (Dev)|AVR.Build.0 = Release (Dev)|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.ActiveCfg = Release|AVR +		{A379D421-4236-44AF-A711-52B7BDA29919}.Release|AVR.Build.0 = Release|AVR +	EndGlobalSection +	GlobalSection(SolutionProperties) = preSolution +		HideSolutionNode = FALSE +	EndGlobalSection +EndGlobal diff --git a/firmware/e300/rev_c/PMC.cproj b/firmware/e300/rev_c/PMC.cproj new file mode 100644 index 000000000..e3784d10b --- /dev/null +++ b/firmware/e300/rev_c/PMC.cproj @@ -0,0 +1,288 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +  <PropertyGroup> +    <SchemaVersion>2.0</SchemaVersion> +    <ProjectVersion>6.0</ProjectVersion> +    <ToolchainName>com.Atmel.AVRGCC8</ToolchainName> +    <ProjectGuid>{a379d421-4236-44af-a711-52b7bda29919}</ProjectGuid> +    <avrdevice>ATtiny88</avrdevice> +    <avrdeviceseries>none</avrdeviceseries> +    <OutputType>Executable</OutputType> +    <Language>C</Language> +    <OutputFileName>$(MSBuildProjectName)</OutputFileName> +    <OutputFileExtension>.elf</OutputFileExtension> +    <OutputDirectory>$(MSBuildProjectDirectory)\$(Configuration)</OutputDirectory> +    <AssemblyName>PMC</AssemblyName> +    <Name>PMC</Name> +    <RootNamespace>PMC</RootNamespace> +    <ToolchainFlavour>Native</ToolchainFlavour> +    <KeepTimersRunning>true</KeepTimersRunning> +    <OverrideVtor>false</OverrideVtor> +    <OverrideVtorValue /> +    <eraseonlaunchrule>0</eraseonlaunchrule> +    <AsfVersion>3.1.3</AsfVersion> +    <avrtool>com.atmel.avrdbg.tool.stk600</avrtool> +    <avrtoolinterface>ISP</avrtoolinterface> +    <com_atmel_avrdbg_tool_stk600> +      <ToolType>com.atmel.avrdbg.tool.stk600</ToolType> +      <ToolName>STK600</ToolName> +      <ToolNumber>004A8D68669B</ToolNumber> +      <KeepTimersRunning>true</KeepTimersRunning> +      <OverrideVtor>false</OverrideVtor> +      <OverrideVtorValue> +      </OverrideVtorValue> +      <Channel> +        <host>127.0.0.1</host> +        <port>49172</port> +        <ssl>False</ssl> +      </Channel> +      <ToolOptions> +        <InterfaceName>ISP</InterfaceName> +        <InterfaceProperties> +          <JtagDbgClock>249000</JtagDbgClock> +          <JtagProgClock>1000000</JtagProgClock> +          <IspClock>25000</IspClock> +          <JtagInChain>false</JtagInChain> +          <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession> +          <JtagDevicesBefore>0</JtagDevicesBefore> +          <JtagDevicesAfter>0</JtagDevicesAfter> +          <JtagInstrBitsBefore>0</JtagInstrBitsBefore> +          <JtagInstrBitsAfter>0</JtagInstrBitsAfter> +        </InterfaceProperties> +      </ToolOptions> +    </com_atmel_avrdbg_tool_stk600> +    <com_atmel_avrdbg_tool_ispmk2> +      <ToolType>com.atmel.avrdbg.tool.ispmk2</ToolType> +      <ToolName>AVRISP mkII</ToolName> +      <ToolNumber>000200136505</ToolNumber> +      <KeepTimersRunning>true</KeepTimersRunning> +      <OverrideVtor>false</OverrideVtor> +      <OverrideVtorValue> +      </OverrideVtorValue> +      <Channel> +        <host>127.0.0.1</host> +        <port>49228</port> +        <ssl>False</ssl> +      </Channel> +      <ToolOptions> +        <InterfaceName>ISP</InterfaceName> +        <InterfaceProperties> +          <JtagDbgClock>249000</JtagDbgClock> +          <JtagProgClock>1000000</JtagProgClock> +          <IspClock>8000</IspClock> +          <JtagInChain>false</JtagInChain> +          <JtagEnableExtResetOnStartSession>false</JtagEnableExtResetOnStartSession> +          <JtagDevicesBefore>0</JtagDevicesBefore> +          <JtagDevicesAfter>0</JtagDevicesAfter> +          <JtagInstrBitsBefore>0</JtagInstrBitsBefore> +          <JtagInstrBitsAfter>0</JtagInstrBitsAfter> +        </InterfaceProperties> +      </ToolOptions> +    </com_atmel_avrdbg_tool_ispmk2> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>I2C_REWORK</Value> +            <Value>ENABLE_SERIAL</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +      </AvrGcc> +    </ToolchainSettings> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>ATTINY88_DIP</Value> +            <Value>DEBUG</Value> +            <Value>I2C_REWORK</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize (-O1)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.optimization.DebugLevel>Default (-g2)</avrgcc.compiler.optimization.DebugLevel> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +        <avrgcc.assembler.debugging.DebugLevel>Default (-Wa,-g)</avrgcc.assembler.debugging.DebugLevel> +      </AvrGcc> +    </ToolchainSettings> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L)' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>I2C_REWORK</Value> +            <Value>ENABLE_SERIAL</Value> +            <Value>DDR3L</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +      </AvrGcc> +    </ToolchainSettings> +    <OutputPath>bin\Release (DDR3)\</OutputPath> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release (Dev)' "> +    <ToolchainSettings> +      <AvrGcc> +        <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +        <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +        <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +        <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +        <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +        <avrgcc.compiler.symbols.DefSymbols> +          <ListValues> +            <Value>I2C_REWORK</Value> +            <Value>ENABLE_SERIAL</Value> +            <Value>ATTINY88_DIP</Value> +          </ListValues> +        </avrgcc.compiler.symbols.DefSymbols> +        <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +        <avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection>True</avrgcc.compiler.optimization.PrepareFunctionsForGarbageCollection> +        <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +        <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +        <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +        <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +        <avrgcc.linker.libraries.Libraries> +          <ListValues> +            <Value>m</Value> +          </ListValues> +        </avrgcc.linker.libraries.Libraries> +        <avrgcc.linker.optimization.GarbageCollectUnusedSections>True</avrgcc.linker.optimization.GarbageCollectUnusedSections> +      </AvrGcc> +    </ToolchainSettings> +    <OutputPath>bin\Release (Dev)\</OutputPath> +  </PropertyGroup> +  <PropertyGroup Condition=" '$(Configuration)' == 'Release (DDR3L, Ti)' "> +    <ToolchainSettings> +      <AvrGcc> +  <avrgcc.common.outputfiles.hex>True</avrgcc.common.outputfiles.hex> +  <avrgcc.common.outputfiles.lss>True</avrgcc.common.outputfiles.lss> +  <avrgcc.common.outputfiles.eep>True</avrgcc.common.outputfiles.eep> +  <avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned>True</avrgcc.compiler.general.ChangeDefaultCharTypeUnsigned> +  <avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned>True</avrgcc.compiler.general.ChangeDefaultBitFieldUnsigned> +  <avrgcc.compiler.symbols.DefSymbols> +    <ListValues> +      <Value>I2C_REWORK_disabled</Value> +      <Value>DDR3L_disabled_revB_R_FB_network_changed</Value> +      <Value>CHARGER_TI</Value> +      <Value>ENABLE_SERIAL_disabled</Value> +      <Value>LED_POLARITY</Value> +    </ListValues> +  </avrgcc.compiler.symbols.DefSymbols> +  <avrgcc.compiler.optimization.level>Optimize for size (-Os)</avrgcc.compiler.optimization.level> +  <avrgcc.compiler.optimization.PackStructureMembers>True</avrgcc.compiler.optimization.PackStructureMembers> +  <avrgcc.compiler.optimization.AllocateBytesNeededForEnum>True</avrgcc.compiler.optimization.AllocateBytesNeededForEnum> +  <avrgcc.compiler.warnings.AllWarnings>True</avrgcc.compiler.warnings.AllWarnings> +  <avrgcc.compiler.warnings.WarningsAsErrors>True</avrgcc.compiler.warnings.WarningsAsErrors> +  <avrgcc.linker.libraries.Libraries> +    <ListValues> +      <Value>m</Value> +    </ListValues> +  </avrgcc.linker.libraries.Libraries> +</AvrGcc> +    </ToolchainSettings> +    <OutputPath>bin\Release (DDR3L, Ti)\</OutputPath> +  </PropertyGroup> +  <ItemGroup> +    <Compile Include="bq24190.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="bq24190.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="config.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="debug.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="debug.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="error.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="global.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="i2c.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="i2c.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="io.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="io.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc3675.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc3675.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc4155.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="ltc4155.h"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="main.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="power.c"> +      <SubType>compile</SubType> +    </Compile> +    <Compile Include="power.h"> +      <SubType>compile</SubType> +    </Compile> +  </ItemGroup> +  <Import Project="$(AVRSTUDIO_EXE_PATH)\\Vs\\Compiler.targets" /> +</Project>
\ No newline at end of file diff --git a/firmware/e300/rev_c/bq24190.c b/firmware/e300/rev_c/bq24190.c new file mode 100644 index 000000000..029beacf6 --- /dev/null +++ b/firmware/e300/rev_c/bq24190.c @@ -0,0 +1,335 @@ +/* + * bq24190.c + * + * Created: 11/12/2012 4:58:12 PM + *  Author: Balint Seeber + */ + +#ifdef CHARGER_TI + +#include "config.h" +#include "bq24190.h" + +#include <util/delay.h> + +#include "io.h" +#include "i2c.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +#ifndef I2C_REWORK +#include "power.h" +#endif // I2C_REWORK + +static io_pin_t USBPM_IRQ	= IO_PB(1); + +#ifdef ATTINY88_DIP + +static io_pin_t CHRG_SDA     = IO_PC(2); +static io_pin_t CHRG_SCL     = IO_PC(3); + +#else + +#ifdef I2C_REWORK + +static io_pin_t CHRG_SDA     = IO_PC(4); +static io_pin_t CHRG_SCL     = IO_PC(5); + +#else + +#define CHRG_SDA	PWR_SDA +#define CHRG_SCL	PWR_SCL + +#endif // I2C_REWORK + +#endif // ATTINY88_DIP + +const bool _bq24190_pull_up = false; + +#define BQ24190_BASE_ADDRESS    (0x6B << 1) +#define BQ24190_WRITE_ADDRESS   (BQ24190_BASE_ADDRESS + 0) +#define BQ24190_READ_ADDRESS    (BQ24190_BASE_ADDRESS + 1) + +enum BQ24190Registers +{ +	BQ24190_REG_INPUT_SOURCE_CTL= 0, +	BQ24190_REG_PWR_ON_CONFIG	= 1, +	BQ24190_REG_CHARGE_CURRENT	= 2, +	BQ24190_REG_PRE_TERM_CURRENT= 3, +	BQ24190_REG_CHARGE_VOLTAGE	= 4, +	BQ24190_REG_TIMER_CONTROL	= 5, +	BQ24190_REG_SYSTEM_STATUS	= 8, +	BQ24190_REG_FAULT			= 9 +}; +/* +enum BQ24190TimerControl +{ +	 +}; +*/ +enum BQ24190Shifts +{ +	BQ24190_SHIFTS_CHARGER_CONFIG	= 4, +	BQ24190_SHIFTS_I2C_WATCHDOG		= 4, +	BQ24190_SHIFTS_CHARGER_STATUS	= 4, +	BQ24190_SHIFTS_CHARGER_FAULT	= 4, +}; + +enum BQ24190VBusStatus +{ +	BQ24190_VBUS_UNKNOWN, +	BQ24190_VBUS_USB, +	BQ24190_VBUS_ADAPTER, +	BQ24190_VBUS_OTG +}; + +enum BQ24190ChargerStatus +{ +	BQ24190_CHRG_STAT_NOT_CHARGING, +	BQ24190_CHRG_STAT_PRE_CHARGE, +	BQ24190_CHRG_STAT_FAST_CHARGING, +	BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE, +	BQ24190_CHRG_STAT_MASK = BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE +}; + +enum BQ24190SystemStatus +{ +	BQ24190_STATUS_DPM					= 0x08, +	BQ24190_STATUS_POWER_GOOD			= 0x04, +	BQ24190_STATUS_THERMAL_REGULATION	= 0x02, +	BQ24190_STATUS_VSYSMIN_REGULATION	= 0x01 +}; + +enum BQ24190Faults +{ +	BQ24190_FAULT_WATCHDOG_EXPIRED	= 0x80, +	BQ24190_FAULT_VBUS_OVERLOADED	= 0x40, +	BQ24190_FAULT_BATOVP			= 0x08 +}; + +enum BQ24190ChargerFaults +{ +	BQ24190_CHRGFAULT_NORMAL, +	BQ24190_CHRGFAULT_INPUT, +	BQ24190_CHRGFAULT_THERMAL, +	BQ24190_CHRGFAULT_SAFETY_TIMER +}; + +enum BQ24190NTCFaults +{ +	BQ24190_NTCFAULT_NORMAL, +	BQ24190_NTCFAULT_TS1_COLD, +	BQ24190_NTCFAULT_TS1_HOT, +	BQ24190_NTCFAULT_TS2_COLD, +	BQ24190_NTCFAULT_TS2_HOT, +	BQ24190_NTCFAULT_BOTH_COLD, +	BQ24190_NTCFAULT_BOTH_HOT +}; + +bool bq24190_toggle_charger(bool on) +{ +	uint8_t config = 0; +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQPC ", false); +	debug_log_hex(config); +	 +	config &= ~(0x3 << BQ24190_SHIFTS_CHARGER_CONFIG); +	if (on) +		config |= (0x01 << BQ24190_SHIFTS_CHARGER_CONFIG);	// Enable charger +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, config, _bq24190_pull_up) == false) +		return false; +	 +	//////// +/* +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_PWR_ON_CONFIG, &config, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQPC ", false); +	debug_log_hex(config); +*/ +	//////// +	 +	return true; +} + +bool bq24190_init(bool disable_charger) +{ +#ifdef I2C_REWORK +	i2c_init_ex(CHRG_SDA, CHRG_SCL, _bq24190_pull_up); +#endif // I2C_REWORK +	io_input_pin(USBPM_IRQ); +#if !defined(DEBUG) && !defined(ATTINY88_DIP) +	//io_set_pin(USBPM_IRQ);	// [Enable pull-up for Open Drain] AVR pull-up not enough +#endif // DEBUG +	if (disable_charger) +	{ +		if (bq24190_toggle_charger(false) == false) +			return false; +	} +	 +	/////////////////////////////////// +	 +	uint8_t timer_control = 0; +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_TIMER_CONTROL, &timer_control, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQTC ", false); +	debug_log_hex(timer_control); +	 +	timer_control &= ~(0x3 << BQ24190_SHIFTS_I2C_WATCHDOG); +	timer_control |= (0x00 << BQ24190_SHIFTS_I2C_WATCHDOG);	// Disable I2C watch dog +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_TIMER_CONTROL, timer_control, _bq24190_pull_up) == false) +		return false; +	 +	/////////////////////////////////// +	 +	//BQ24190_REG_PWR_ON_CONFIG +	// Minimum System Voltage Limit: (default) 101 3.5V +	 +	//BQ24190_REG_CHARGE_CURRENT +	// Fast Charge Current Limit: (default) 011000 2048mA +	 +	//BQ24190_REG_PRE_TERM_CURRENT +	// Pre-charge current limit: (default) 0001 256mA +	// Termination current limit: (default) 0001 256mA +	 +	//BQ24190_REG_CHARGE_VOLTAGE +	// Charge voltage limit: (default) 101100 4.208V +	 +	/////////////////////////////////// +	 +	uint8_t input_src_ctl = 0; +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, &input_src_ctl, _bq24190_pull_up) == false) +		return false; +	 +	debug_log_ex("BQIS ", false); +	debug_log_hex(input_src_ctl); +	 +	// Input voltage limit: (default) 0110 4.36V +	 +	//input_src_ctl &= ~(0x07); +	input_src_ctl |= (0x07);	// Set 3A limit +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, BQ24190_WRITE_ADDRESS, BQ24190_REG_INPUT_SOURCE_CTL, input_src_ctl, _bq24190_pull_up) == false) +		return false; +	 +	return true; +} + +bool bq24190_has_interrupt(void) +{ +	//bool state = io_test_pin(USBPM_IRQ); +	//debug_log_ex("BQIRQ", false); +	//debug_log_byte(state); +	//return (state != 1); +	return (io_test_pin(USBPM_IRQ) == false); +} + +static uint8_t _bq24190_last_status, _bq24190_last_fault; + +bool _bq24190_handle_irq(void) +{ +	uint8_t val = 0x00; +	bool result = false; +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	debug_log_ex("BQST ", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_SYSTEM_STATUS, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	_bq24190_last_status = val; + +	debug_log_ex("BQST ", false); +	debug_log_hex(val); +	 +	/*if (val & LTC4155_WALLSNS_GOOD) +	{ +		uint8_t wall_state = 0; +		if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false) +			goto _bq24190_handle_fail; +		 +		wall_state &= ~0x1E; +		wall_state |= 0x0E; +		 +		if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false) +			goto _bq24190_handle_fail; +		 +		debug_log("I+"); +	}*/ +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	debug_log_ex("BQF  ", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, BQ24190_READ_ADDRESS, BQ24190_REG_FAULT, &val, _bq24190_pull_up) == false) +		goto _bq24190_handle_fail; +	 +	_bq24190_last_fault = val; +	 +	debug_log_ex("BQF  ", false); +	debug_log_hex(val); +	 +	val = (_bq24190_last_status >> BQ24190_SHIFTS_CHARGER_STATUS) & BQ24190_CHRG_STAT_MASK; +	 +	if (_state.blink_error == BlinkError_None) +	{ +		switch (val) +		{ +			case BQ24190_CHRG_STAT_PRE_CHARGE: +			case BQ24190_CHRG_STAT_FAST_CHARGING: +			//case BQ24190_CHRG_STAT_CHARGE_TERMINATION_DONE: +			{ +				if ((_state.battery_not_present == false)/* && +					(_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD))*/) +				{ +					//charge_set_led(true); +					charge_notify(true); +					break; +				} +			} +			//case BQ24190_CHRG_STAT_NOT_CHARGING: +			default: +				//charge_set_led(false); +				charge_notify(false); +		} +	} +	 +//	bq24190_dump(); +	 +	result = true; +_bq24190_handle_fail: +	return result; +} + +bool bq24190_handle_irq(void)	// IRQ is pulsed (not held) +{ +	pmc_mask_irqs(true); +	 +	//_delay_ms(250);	// [Wait for registers to update] +	 +	bool result = _bq24190_handle_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +//void bq24190_dump(void) +/* +bool bq24190_set_charge_current_limit(uint8_t deciamps) +{ +	return true; +} +*/ +#endif // CHARGER_TI diff --git a/firmware/e300/rev_c/bq24190.h b/firmware/e300/rev_c/bq24190.h new file mode 100644 index 000000000..dc20ca796 --- /dev/null +++ b/firmware/e300/rev_c/bq24190.h @@ -0,0 +1,26 @@ +/* + * bq24190.h + * + * Created: 11/12/2012 4:58:23 PM + *  Author: Balint Seeber + */  + + +#ifndef BQ24190_H_ +#define BQ24190_H_ + +#include <stdbool.h> +#include <stdint.h> + +#ifdef CHARGER_TI + +bool bq24190_init(bool disable_charger); +bool bq24190_has_interrupt(void); +bool bq24190_handle_irq(void); +//void bq24190_dump(void); +//bool bq24190_set_charge_current_limit(uint8_t deciamps); +bool bq24190_toggle_charger(bool on); + +#endif // !CHARGER_TI + +#endif /* BQ24190_H_ */ diff --git a/firmware/e300/rev_c/config.h b/firmware/e300/rev_c/config.h new file mode 100644 index 000000000..2547f65f0 --- /dev/null +++ b/firmware/e300/rev_c/config.h @@ -0,0 +1,2 @@ +#define __DELAY_BACKWARD_COMPATIBLE__   // Avoid compile-time arg error to '__builtin_avr_delay_cycles' +#define F_CPU   1000000UL // 1 MHz (8MHz / 8) diff --git a/firmware/e300/rev_c/debug.c b/firmware/e300/rev_c/debug.c new file mode 100644 index 000000000..ff7d09445 --- /dev/null +++ b/firmware/e300/rev_c/debug.c @@ -0,0 +1,297 @@ +/* + * debug.c + */ + +#include "config.h" +#include "debug.h" + +#include <util/delay.h> +#include <avr/io.h> +#include <avr/interrupt.h> + +#include "io.h" +#include "power.h" +#include "global.h" + +#define DEBUG_BLINK_DELAY	250	// ms + +#ifdef ATTINY88_DIP + +#define SERIAL_DEBUG_INDEX			6 +#define SERIAL_DEBUG_PORT			PORTD +static io_pin_t SERIAL_DEBUG      = IO_PD(SERIAL_DEBUG_INDEX); + +#else +/* +#ifdef I2C_REWORK +//static io_pin_t SERIAL_DEBUG      = IO_PC(1);	// EN1 +#else +//static io_pin_t SERIAL_DEBUG      = EN4; +#endif // I2C_REWORK +*/ +// No good: PWR_EN4 trace still connected to LTC3675 +//#define SERIAL_DEBUG_INDEX			1 +//#define SERIAL_DEBUG_PORT			PORTA +//static io_pin_t SERIAL_DEBUG      = IO_PA(SERIAL_DEBUG_INDEX); + +// AVR_MISO +#define SERIAL_DEBUG_INDEX			4 +#define SERIAL_DEBUG_PORT			PORTB +static io_pin_t SERIAL_DEBUG      = IO_PB(SERIAL_DEBUG_INDEX); + +#endif // ATTINY88_DIP +/* +#ifdef DEBUG + +#else + +#endif // DEBUG +*/ +#ifdef DEBUG + +#ifdef ATTINY88_DIP +static io_pin_t DEBUG_1 = IO_PB(6); +static io_pin_t DEBUG_2	= IO_PB(7); +#endif // ATTINY88_DIP + +void debug_init() +{ +	io_output_pin(DEBUG_1); +	io_output_pin(DEBUG_2); +	 +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +#ifdef ENABLE_SERIAL	 +	io_set_pin(SERIAL_DEBUG); +	io_output_pin(SERIAL_DEBUG); +#endif // ENABLE_SERIAL +} + +#else + +void debug_init() +{ +#ifdef ENABLE_SERIAL +	io_set_pin(SERIAL_DEBUG); +	io_output_pin(SERIAL_DEBUG); +#endif // ENABLE_SERIAL +} + +#endif // DEBUG + +#if defined(DEBUG) && !defined(DEBUG_VOID) + +void debug_set(io_pin_t pin, bool enable) +{ +	io_enable_pin(pin, !enable); +} + +void debug_blink(uint8_t count) +{ +	io_enable_pin(DEBUG_1, false); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); + +	for (; count > 0; count--) { +		io_enable_pin(DEBUG_2, false); +		_delay_ms(DEBUG_BLINK_DELAY); +		io_enable_pin(DEBUG_2, true); +		_delay_ms(DEBUG_BLINK_DELAY); +	} + +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); +} + +void debug_blink_rev(uint8_t count) +{ +	io_enable_pin(DEBUG_2, false); +	io_enable_pin(DEBUG_1, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); + +	for (; count > 0; count--) { +		io_enable_pin(DEBUG_1, false); +		_delay_ms(DEBUG_BLINK_DELAY); +		io_enable_pin(DEBUG_1, true); +		_delay_ms(DEBUG_BLINK_DELAY); +	} + +	io_enable_pin(DEBUG_2, true); +	io_enable_pin(DEBUG_1, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); +} + +void debug_blink2(uint8_t count) +{ +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); + +	bool b = false; +	for (; count > 0; count--) { +		io_enable_pin(DEBUG_1, b); +		io_enable_pin(DEBUG_2, b); +		_delay_ms(DEBUG_BLINK_DELAY); +		b = !b; +	} + +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	_delay_ms(DEBUG_BLINK_DELAY * 2); +} + +void debug_wait(void) +{ +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +	 +	bool b = false; +	while (true) +	{ +		io_enable_pin(DEBUG_1, b); +		io_enable_pin(DEBUG_2, !b); +		 +		_delay_ms(DEBUG_BLINK_DELAY); +		 +		b = !b; +	} +	 +	io_enable_pin(DEBUG_1, true); +	io_enable_pin(DEBUG_2, true); +} + +#else + +#ifndef DEBUG_VOID + +void debug_blink_rev(uint8_t count) +{ +	charge_set_led(true); +	_delay_ms(DEBUG_BLINK_DELAY * 4); + +	for (; count > 0; count--) { +		charge_set_led(false); +		_delay_ms(DEBUG_BLINK_DELAY); +		charge_set_led(true); +		_delay_ms(DEBUG_BLINK_DELAY * 2); +	} + +	_delay_ms(DEBUG_BLINK_DELAY * 2); +	charge_set_led(false); +	_delay_ms(DEBUG_BLINK_DELAY * 4); +} +#endif // DEBUG_VOID + +#endif // DEBUG + +#ifdef ENABLE_SERIAL + +static void _serial_tx(uint8_t* buffer) +{ +	//uint8_t time_fix = 0; +	// 3333/2 - 10 +	// 650 +	//	[-2 for DEV] -20 works (perhaps different USB-Serial converter) +	//	[-20 for PRD] Which board? +	//	+20 Board #5 (-0: 3.592, -10: 3.280) +	const uint16_t delay = 650+20; +	uint16_t countdown; +	 +	for (uint8_t j = 0; j < 10; ++j) +	{ +		if (buffer[j]) +		SERIAL_DEBUG_PORT |= _BV(SERIAL_DEBUG_INDEX); +		else +		SERIAL_DEBUG_PORT &= ~_BV(SERIAL_DEBUG_INDEX); +		 +		countdown = delay; +		while (--countdown) +		__asm("nop"); +	} +} + +static void _serial_tx_char(char c) +{ +	uint8_t buffer[10]; +	uint8_t i = 0; +	 +	buffer[i++] = 0;	// START +	for (int idx = 0; idx < 8; ++idx) +	buffer[i++] = (((uint8_t)(c) & ((uint8_t)1<<((idx)))) ? 0x01 : 0x00);	// Endianness: 7- +	buffer[i++] = 1;	// STOP +	 +	_serial_tx(buffer); +} + +void debug_log_ex_P(const char* message, bool new_line) +{ +	char c = pgm_read_byte(message); +	if (c == '\0') +		return; +	 +	pmc_mask_irqs(true); + +	do +	{ +		_serial_tx_char(c); +		c = pgm_read_byte(++message); +	} while (c != '\0'); +	 +	if (new_line) +		_serial_tx_char('\n'); + +	io_set_pin(SERIAL_DEBUG); +	 +	pmc_mask_irqs(false); +} + +void _debug_log_ex(const char* message, bool new_line) +{ +	if (message[0] == '\0') +	return; +	 +	pmc_mask_irqs(true); + +	do +	{ +		_serial_tx_char(*message); +	} while (*(++message) != '\0'); +	 +	if (new_line) +	_serial_tx_char('\n'); + +	io_set_pin(SERIAL_DEBUG); +	 +	pmc_mask_irqs(false); +} + +void debug_log_byte_ex(uint8_t n, bool new_line) +{ +	char ch[4]; +	ch[0] = '0' + (n / 100); +	ch[1] = '0' + ((n % 100) / 10); +	ch[2] = '0' + (n % 10); +	ch[3] = '\0'; +	_debug_log_ex(ch, new_line); +} + +void debug_log_hex_ex(uint8_t n, bool new_line) +{ +	char ch[4]; +	ch[0] = 'x'; +	uint8_t _n = n >> 4; +	if (_n < 10) +		ch[1] = '0' + _n; +	else +		ch[1] = 'A' + (_n - 10); +	n &= 0x0F; +	if (n < 10) +		ch[2] = '0' + n; +	else +		ch[2] = 'A' + (n - 10); +	ch[3] = '\0'; +	_debug_log_ex(ch, new_line); +} + +#endif // ENABLE_SERIAL diff --git a/firmware/e300/rev_c/debug.h b/firmware/e300/rev_c/debug.h new file mode 100644 index 000000000..5e6435972 --- /dev/null +++ b/firmware/e300/rev_c/debug.h @@ -0,0 +1,90 @@ +/* + * debug.h + */  + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +#include <stdint.h> +#include <stdbool.h> +#include <avr/pgmspace.h> + +#include "io.h" + +#ifdef DEBUG +#define DEBUG_INLINE +#define DEBUG_NOOP	; +#define LED_ON		false +#define LED_OFF		true +#else +#define DEBUG_INLINE inline +#define DEBUG_NOOP	{} +#define LED_ON		true +#define LED_OFF		false +#endif // DEBUG + +//#define DEBUG_VOID +#define DEBUG_SAFETY + +#ifdef DEBUG_VOID + +//#define debug_init	(void) +#define debug_set	(void) +#define debug_blink	(void) +#define debug_blink_rev	(void) +#define debug_blink2 (void) +#define debug_wait	(void) + +#else + +//DEBUG_INLINE void debug_init(void) DEBUG_NOOP +DEBUG_INLINE void debug_set(io_pin_t pin, bool enable) DEBUG_NOOP +DEBUG_INLINE void debug_blink(uint8_t count) DEBUG_NOOP +//DEBUG_INLINE void debug_blink_rev(uint8_t count) DEBUG_NOOP +void debug_blink_rev(uint8_t count); +DEBUG_INLINE void debug_blink2(uint8_t count) DEBUG_NOOP +DEBUG_INLINE void debug_wait(void) DEBUG_NOOP + +#endif // DEBUG_VOID + +#if defined(DEBUG) && !defined(ENABLE_SERIAL) +#define ENABLE_SERIAL +#endif // DEBUG && !ENABLE_SERIAL + +/*DEBUG_INLINE */void debug_init(void)/* DEBUG_NOOP*/; + +#ifdef ENABLE_SERIAL + +void debug_log_ex_P(const char* message, bool new_line); +void debug_log_hex_ex(uint8_t n, bool new_line); +void debug_log_byte_ex(uint8_t n, bool new_line); +void _debug_log_ex(const char* message, bool new_line); + +// Prototypes to silence avr-gcc +inline void debug_log_P(const char* message); +inline void debug_log_hex(uint8_t n); +inline void debug_log_byte(uint8_t n); +inline void _debug_log(const char* message); + +inline void debug_log_P(const char* message) { debug_log_ex_P(message, true); } +inline void debug_log_hex(uint8_t n) { debug_log_hex_ex(n, true); } +inline void debug_log_byte(uint8_t n) { debug_log_byte_ex(n, true); } +inline void _debug_log(const char* message) { _debug_log_ex(message, true); } + +#else + +inline void debug_log_ex_P		(const char* message, bool new_line) {}; +inline void debug_log_hex_ex	(uint8_t n, bool new_line) {}; +inline void debug_log_byte_ex	(uint8_t n, bool new_line) {}; +inline void _debug_log_ex		(const char* message, bool new_line) {}; + +#define debug_log_P			(void) +#define debug_log_hex		(void) +#define debug_log_byte		(void) +#define _debug_log			(void) +#endif // ENABLE_SERIAL + +#define debug_log(x)		debug_log_P(PSTR(x)) +#define debug_log_ex(x,nl)	debug_log_ex_P(PSTR(x), nl) + +#endif /* DEBUG_H_ */ diff --git a/firmware/e300/rev_c/error.h b/firmware/e300/rev_c/error.h new file mode 100644 index 000000000..82a8f0aca --- /dev/null +++ b/firmware/e300/rev_c/error.h @@ -0,0 +1,31 @@ +/* + * error.h + * + * Created: 4/09/2012 6:25:53 PM + *  Author: Balint Seeber + */  + + +#ifndef ERROR_H_ +#define ERROR_H_ + +enum ErrorBlinkCount	// Lower number = higher priority +{ +	BlinkError_None, +	// Low power/battery +	BlinkError_LowVoltage, +	BlinkError_LTC3675_UnderVoltage = BlinkError_LowVoltage, +	BlinkError_LTC4155_UnderVoltage = BlinkError_LowVoltage,	// FIXME: This does not work when checking status +	// Should match power boot steps +	BlinkError_FPGA_Power, +	BlinkError_DRAM_Power, +	BlinkError_1_8V_Peripherals_Power, +	BlinkError_3_3V_Peripherals_Power, +	BlinkError_TX_Power, +	// LTC3675 +	BlinkError_LTC3675_OverTemperature, +	// LTC4155 +	BlinkError_LTC4155_BadCell +}; + +#endif /* ERROR_H_ */ diff --git a/firmware/e300/rev_c/global.h b/firmware/e300/rev_c/global.h new file mode 100644 index 000000000..50fab581d --- /dev/null +++ b/firmware/e300/rev_c/global.h @@ -0,0 +1,49 @@ +/* + * global.h + * + * Created: 31/08/2012 8:47:14 PM + *  Author: Balint Seeber + */  + +#ifndef GLOBAL_H_ +#define GLOBAL_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <avr/pgmspace.h> + +typedef struct State +{ +	bool interrupts_enabled; +	uint8_t interrupt_depth; +	//bool timers_running; +	uint8_t active_timers; +	bool powered; +	bool battery_not_present; +	bool battery_charging; +	bool wake_up; +	bool power_off; +	bool core_power_bad; +	bool ltc3675_irq; +#ifdef CHARGER_TI +	bool bq24190_irq; +#else +	bool ltc4155_irq; +#endif // CHARGER_TI +	//bool low_battery; +	uint8_t blink_error; +	uint8_t blinker_state; +	uint8_t blink_loops; +	uint8_t blink_last_loop; +	bool blink_stop; +} STATE; + +//extern volatile bool _timers_running; +extern volatile STATE _state; + +void pmc_set_blink_error(uint8_t count); +uint8_t pmc_get_blink_error(void); + +bool pmc_mask_irqs(bool mask); + +#endif /* GLOBAL_H_ */ diff --git a/firmware/e300/rev_c/i2c.c b/firmware/e300/rev_c/i2c.c new file mode 100644 index 000000000..70a28e61a --- /dev/null +++ b/firmware/e300/rev_c/i2c.c @@ -0,0 +1,518 @@ +#include "config.h" +#include "i2c.h" + +#include <util/delay.h> + +#include "io.h" +#include "debug.h" + +/* +	- Reset bus on failure (lack of ACK, etc) +	- Clock stretching +	- In pull-up mode, much code was commented out to ever avoid driving the bus (for a fleeting moment) as this was visible on the scope as short peaks (instead the line will briefly go Hi Z). +*/ + +volatile bool _i2c_disable_ack_check = false; + +// FIXME: Follow magic numbers should be in a struct that is passed into each function + +#define I2C_DEFAULT_RETRY_DELAY     1   // us MAGIC +#define I2C_DEFAULT_MAX_ACK_RETRIES 10  // * I2C_DEFAULT_RETRY_DELAY us + +#define I2C_DEFAULT_BUS_WAIT		10	// us MAGIC +#define I2C_DEFAULT_MAX_BUS_RETRIES	10 + +#define I2C_DEFAULT_SCL_LOW_PERIOD  2   // 1.3 us +#define I2C_DEFAULT_SCL_HIGH_PERIOD 1   // 0.6 us +#define I2C_DEFAULT_BUS_FREE_TIME   2   // 1.3 us +#define I2C_DEFAULT_STOP_TIME       1   // 0.6 us + +#define I2C_DELAY	_delay_us	// _delay_ms + +static bool _i2c_start_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +	// Assumes: SDA/SCL are both inputs + +	uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; +	while ((io_test_pin(sda) == false) || (io_test_pin(scl) == false)) +	{ +		I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +		if (retries-- == 0) +		{ +debug_log("I2C:S1"); +			return false; +		}			 +	} +	 +	// START condition +//	if (pull_up == false) +		io_clear_pin(sda);	// Set LOW before switching to output +	io_output_pin(sda); +//	if (pull_up) +//		io_clear_pin(sda); +	I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD);  // Thd, sta +	 +	retries = I2C_DEFAULT_MAX_BUS_RETRIES; +	while (io_test_pin(scl) == false)	// SCL should remain high +	{ +		I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +		if (retries-- == 0) +		{ +			io_input_pin(sda); +debug_log_ex("I2C:S2", false); +debug_log_hex(scl); +			return false; +		}			 +	} + +//	if (pull_up == false) +		io_clear_pin(scl); +	io_output_pin(scl); +//	if (pull_up) +//		io_clear_pin(scl); +	I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD / 2);   // MAGIC + +	return true; +} + +static bool _i2c_stop_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +	// Assumes: +	//	SCL is output & LOW +	//	SDA is input (Hi-Z, or pull-up enabled) +	 +	// Assuming pull-up already enabled +	//if (pull_up) +	//	io_set_pin(sda); +	 +	bool result = true; +	 +	// SDA should be HIGH after ACK has been clocked away +//	bool skip_drive = false; +	uint8_t retries = 0; +	while (io_test_pin(sda) == false) +	{ +		if (retries == I2C_DEFAULT_MAX_ACK_RETRIES) +		{ +			debug_log_ex("I2C:STP ", false); +			debug_log_hex(sda); +			debug_blink_rev(4); +			 +//			skip_drive = true; +			result = false; +			break;	// SDA is being held low?! +		} + +		++retries; +		I2C_DELAY(I2C_DEFAULT_RETRY_DELAY); +	} +	 +	// STOP condition +//	if ((pull_up == false) || (skip_drive)) +		io_clear_pin(sda);	// Don't tri-state if internal pull-up is used +//	//else +//	// Pin will now be driven, but having checked SDA is HIGH above means slave's SDA should be Open Collector (i.e. it won't blow up) +	io_output_pin(sda);	// Drive LOW +//	if (pull_up) +//		io_clear_pin(sda); + +	/////////////////////////////////// +	 +//	if (pull_up) +//		io_set_pin(scl);	// Don't tri-state if internal pull-up is used. Line will be driven, but assuming this is the only master on the clock line (i.e. no one else will pull it low). +	io_input_pin(scl); +	if (pull_up) +		io_set_pin(scl); +	I2C_DELAY(I2C_DEFAULT_STOP_TIME); +	 +	/////////////////////////////////// + +//	if ((pull_up) && (skip_drive == false)) +//		io_set_pin(sda);	// Don't tri-state if internal pull-up is used +	io_input_pin(sda); +//	if ((pull_up) && (skip_drive)) +		io_set_pin(sda); +	I2C_DELAY(I2C_DEFAULT_BUS_FREE_TIME); +	 +	return result; +} +/* +static void _i2c_stop(io_pin_t sda, io_pin_t scl) +{ +	_i2c_stop_ex(sda, scl, false); +} +*//* +static void _i2c_abort_safe_ex(io_pin_t pin, bool pull_up) +{ +	if (io_is_output(pin)) +	{ +		if (io_is_pin_set(pin))	// This is bad - hope no slave is pulling down the line +		{ +			io_input_pin(pin);	// Pull-up already enabled +			 +			if (pull_up == false) +				io_clear_pin(pin);	// Doing this after changing direction ensures the line is not brought down +		} +		else	// Currently pulling line down +		{ +			io_input_pin(pin);	// Hi-Z +			 +			if (pull_up)	// There will be a moment where the line will float (better than driving the line though...) +			{ +				io_set_pin(pin); +			} +		} +	} +	else	// Already an input +	{ +		if (pull_up) +		{ +			io_set_pin(pin);	// Enable pull-ups +		} +		else +		{ +			io_clear_pin(pin);	// Disable pull-ups +		} +	} +	 +	// Normally: pin will be Hi-Z input +	// With internal pull-up: pin will be input with pull-up enabled +} +*/ +static void _i2c_abort_safe(io_pin_t pin, bool pull_up) +{ +	if (pull_up == false) +		io_clear_pin(pin);	// Should never be output/HIGH, could be input/<was outputting HIGH> so disable pull-ups +	 +	io_input_pin(pin); +	 +	if (pull_up) +		io_set_pin(pin);	// Enable pull-up +} + +static void _i2c_abort_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +/*	if (pull_up == false) +	{ +		io_clear_pin(sda); +		io_clear_pin(scl); +	} +	 +	io_input_pin(scl); +	io_input_pin(sda); +	 +	if (pull_up) +	{ +		io_set_pin(sda); +		io_set_pin(scl); +	} +*/ +	_i2c_abort_safe(scl, pull_up); +	_i2c_abort_safe(sda, pull_up); + +	//_i2c_abort_safe_ex(scl, pull_up); +	//_i2c_abort_safe_ex(sda, pull_up); +} +/* +static void _i2c_abort(io_pin_t sda, io_pin_t scl) +{ +	_i2c_abort_ex(sda, scl, false); +} +*/ +static bool _i2c_write_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t value, bool pull_up) +{ +    // Assumes: +    //  SDA output is LOW +    //  SCL output is LOW + +    for (uint8_t i = 0; i < 8; ++i) +    { +		bool b = ((value & (0x01 << (7 - i))) != 0x00);	// MSB first +		 +		if (b) +		{ +			if (pull_up) +			{ +//				io_set_pin(sda);	// This is bad (will drive line for a moment), but more stable than letting line float +				io_input_pin(sda); +				io_set_pin(sda); +			}				 +			else +				io_input_pin(sda);	// Release HIGH +			 +			if (io_test_pin(sda) == false) +			{ +				debug_log("I2C:WR "); +				debug_log_hex(sda); +				debug_blink_rev(1); +				return false; +			}			 +		}			 +		else +		{ +			if (pull_up) +			{ +//				if (io_is_output(sda)) +					io_clear_pin(sda); +//				else +//				{ +					io_output_pin(sda);	// [This is bad (will drive line for a moment), but more stable than letting line float] +//					io_clear_pin(sda); +//				} +			} +			else +			{ +				io_enable_pin(sda, false); +				io_output_pin(sda);	// Drive LOW +			}				 +		} +		 +		/////////////////////////////// + +        io_input_pin(scl);	// Release HIGH +		if (pull_up) +			io_set_pin(scl); +        I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); +#ifdef I2C_ALLOW_CLOCK_STRETCH +		uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; +		while (io_test_pin(scl) == false)	// Clock stretch requested? +		{ +			I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +			if (--retries == 0) +			{ +				io_input_pin(sda);	// Release HIGH +				if (pull_up) +					io_set_pin(sda); +				 +				debug_log_ex("I2C:STRTCH ", false); +				debug_log_hex(scl); +				debug_blink_rev(2); +				return false; +			} +		} +#endif // I2C_ALLOW_CLOCK_STRETCH +		if (pull_up) +			io_clear_pin(scl); +        io_output_pin(scl);	// Drive LOW +        I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); +    } + +    io_input_pin(sda);	// Release HIGH +	if (pull_up) +		io_set_pin(sda);	// Assuming letting line float won't confuse slave when pulling line LOW for ACK +    I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); + +    uint8_t retries = 0; +    while ((_i2c_disable_ack_check == false) && (io_test_pin(sda))) +    { +        if (retries == I2C_DEFAULT_MAX_ACK_RETRIES) +		{ +			debug_log_ex("I2C:ACK ", false); +			debug_log_hex_ex(sda, false); +			debug_log_hex(value); +			debug_blink_rev(3); +            return false;	// Will abort and not release bus - done by caller +		} + +        ++retries; +        I2C_DELAY(I2C_DEFAULT_RETRY_DELAY); +    } + +    // Clock away acknowledge +//	if (pull_up) +//		io_set_pin(scl); +    io_input_pin(scl);	// Release HIGH +	if (pull_up) +		io_set_pin(scl); +    I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); + +	if (pull_up) +		io_clear_pin(scl); +    io_output_pin(scl);	// Drive LOW +//	if (pull_up) +//		io_clear_pin(scl); +    I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); + +    return true; +} + +static bool _i2c_read_byte_ex(io_pin_t sda, io_pin_t scl, uint8_t* value, bool pull_up) +{ +    // Assumes: +    //  SDA output is LOW +    //  SCL output is LOW +	 +	io_input_pin(sda); +	if (pull_up) +		io_set_pin(sda);	// OK to leave line floating for a moment (better not to drive as slave will be pulling it to ground) + +    (*value) = 0x00; + +    for (uint8_t i = 0; i < 8; ++i) +    { +//		if (pull_up) +//			io_set_pin(scl);	// [Not ideal with pull-up] +        io_input_pin(scl);	// Release HIGH +		if (pull_up) +			io_set_pin(scl); +        I2C_DELAY(I2C_DEFAULT_SCL_HIGH_PERIOD); +#ifdef I2C_ALLOW_CLOCK_STRETCH +		uint8_t retries = I2C_DEFAULT_MAX_BUS_RETRIES; +		while (io_test_pin(scl) == false)	// Clock stretch requested? +		{ +			I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +			if (--retries == 0) +			{ +				debug_log_ex("I2C:R "); +				debug_log_hex(scl); +				debug_blink_rev(5); +				return false; +			}				 +		} +#endif // I2C_ALLOW_CLOCK_STRETCH +        (*value) |= ((io_test_pin(sda) ? 0x1 : 0x0) << (7 - i));   // MSB first + +		if (pull_up) +			io_clear_pin(scl); +        io_output_pin(scl);	// Drive LOW (not ideal with pull-up) +//		if (pull_up) +//			io_clear_pin(scl); +        I2C_DELAY(I2C_DEFAULT_SCL_LOW_PERIOD); +    } + +    // Not necessary to ACK since it's only this one byte + +    return true; +} + +bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up) +{ +	if (_i2c_start_ex(sda, scl, pull_up) == false) +		return false; + +	if (_i2c_write_byte_ex(sda, scl, addr & ~0x01, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R21:", false); +		debug_log("R21"); +		//debug_log_hex(addr); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} + +	if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R22:", false); +		debug_log("R22"); +		//debug_log_hex(subaddr); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} +	 +	io_input_pin(scl); +	if (pull_up) +		io_set_pin(scl); +	I2C_DELAY(I2C_DEFAULT_BUS_WAIT); +	 +	if (_i2c_start_ex(sda, scl, pull_up) == false) +	{ +		return false; +	} +	 +	if (_i2c_write_byte_ex(sda, scl, addr | 0x01, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R23:", false); +		debug_log("R23"); +		//debug_log_hex(addr); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} + +	if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		//debug_log_ex("R24:", false); +		debug_log("R24"); +		//debug_log_hex(*value); +#endif // I2C_EXTRA_DEBUGGING +		goto i2c_read2_fail; +	} +	 +	if (_i2c_stop_ex(sda, scl, pull_up) == false) +	{ +#ifdef I2C_EXTRA_DEBUGGING +		debug_log("R25"); +#endif // I2C_EXTRA_DEBUGGING +	} + +	return true; +i2c_read2_fail: +	_i2c_abort_ex(sda, scl, pull_up); +	return false; +} + +bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up) +{ +	if (_i2c_start_ex(sda, scl, pull_up) == false) +		return false; + +    if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false) +        goto i2c_write_fail; + +    if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false) +        goto i2c_write_fail; + +    if (_i2c_write_byte_ex(sda, scl, value, pull_up) == false) +        goto i2c_write_fail; + +    _i2c_stop_ex(sda, scl, pull_up); + +    return true; +i2c_write_fail: +	_i2c_abort_ex(sda, scl, pull_up); +	return false; +} + +bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value) +{ +	return i2c_write_ex(sda, scl, addr, subaddr, value, false); +} + +bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up) +{ +    if (_i2c_start_ex(sda, scl, pull_up) == false) +		return false; + +    if (_i2c_write_byte_ex(sda, scl, addr, pull_up) == false) +        goto i2c_read_fail; + +    if (_i2c_write_byte_ex(sda, scl, subaddr, pull_up) == false) +        goto i2c_read_fail; + +    if (_i2c_read_byte_ex(sda, scl, value, pull_up) == false) +        goto i2c_read_fail; + +    _i2c_stop_ex(sda, scl, pull_up); + +    return true; +i2c_read_fail: +	_i2c_abort_ex(sda, scl, pull_up); +	return false; +} + +bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value) +{ +	return i2c_read_ex(sda, scl, addr, subaddr, value, false); +} + +void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up) +{ +	_i2c_abort_ex(sda, scl, pull_up); +} + +void i2c_init(io_pin_t sda, io_pin_t scl) +{ +	i2c_init_ex(sda, scl, false); +} diff --git a/firmware/e300/rev_c/i2c.h b/firmware/e300/rev_c/i2c.h new file mode 100644 index 000000000..5898e7e43 --- /dev/null +++ b/firmware/e300/rev_c/i2c.h @@ -0,0 +1,17 @@ +#ifndef I2C_H +#define I2C_H + +#include "io.h" + +void i2c_init(io_pin_t sda, io_pin_t scl); +bool i2c_read(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value); +bool i2c_write(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value); + +void i2c_init_ex(io_pin_t sda, io_pin_t scl, bool pull_up); +bool i2c_read_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up); +bool i2c_read2_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t* value, bool pull_up); +bool i2c_write_ex(io_pin_t sda, io_pin_t scl, uint8_t addr, uint8_t subaddr, uint8_t value, bool pull_up); + +extern volatile bool _i2c_disable_ack_check; + +#endif // I2C_H diff --git a/firmware/e300/rev_c/io.c b/firmware/e300/rev_c/io.c new file mode 100644 index 000000000..0256181c6 --- /dev/null +++ b/firmware/e300/rev_c/io.c @@ -0,0 +1,75 @@ +/* + * Copyright 2009-2012 Ettus Research LLC + */ + +#include "io.h" +#include <avr/io.h> + +#define _GET_PIN(pin)           ((pin) & 0xf) +#define _GET_MASK(pin)          (_BV(_GET_PIN(pin))) +#define _GET_REG(pin, reg_x)    (*reg_x[pin >> 4]) + +#ifndef IO_DEBUG +static volatile uint8_t *ddr_x[] = {&DDRA, &DDRB, &DDRC, &DDRD};		// 0: input, 1: output +static volatile uint8_t *port_x[] = {&PORTA, &PORTB, &PORTC, &PORTD};	// Port contents (will appear at output if direction is set to output. If input, '1' enables pull-ups, '0' set tri-state) +static volatile uint8_t *pin_x[] = {&PINA, &PINB, &PINC, &PIND};		// Port contents (input) If output, will return value on PORT +#endif + +void io_output_pin(io_pin_t pin){ +#ifndef IO_DEBUG +	_GET_REG(pin, ddr_x) |= _GET_MASK(pin); +#endif +} + +void io_input_pin(io_pin_t pin){ +#ifndef IO_DEBUG +	_GET_REG(pin, ddr_x) &= ~_GET_MASK(pin); +#endif +} + +bool io_is_output(io_pin_t pin){ +#ifndef IO_DEBUG +	return bit_is_set(_GET_REG(pin, ddr_x), _GET_PIN(pin)); +#else +	return 0; +#endif +} + +bool io_is_input(io_pin_t pin){ +	return !io_is_output(pin); +} + +void io_set_pin(io_pin_t pin){	// In input mode, will enable pull-ups +#ifndef IO_DEBUG +	_GET_REG(pin, port_x) |= _GET_MASK(pin); +#endif +} + +void io_clear_pin(io_pin_t pin){	// In input mode, will disable pull-ups +#ifndef IO_DEBUG +	_GET_REG(pin, port_x) &= ~_GET_MASK(pin); +#endif +} + +bool io_is_pin_set(io_pin_t pin){ +#ifndef IO_DEBUG +	return bit_is_set(_GET_REG(pin, port_x), _GET_PIN(pin)); +#else +	return 0; +#endif +} + +void io_enable_pin(io_pin_t pin, bool enable){ +    if (enable) +        io_set_pin(pin); +    else +        io_clear_pin(pin); +} + +bool io_test_pin(io_pin_t pin){ +#ifndef IO_DEBUG +	return bit_is_set(_GET_REG(pin, pin_x), _GET_PIN(pin)); +#else +	return 0; +#endif +} diff --git a/firmware/e300/rev_c/io.h b/firmware/e300/rev_c/io.h new file mode 100644 index 000000000..7eea8f0a3 --- /dev/null +++ b/firmware/e300/rev_c/io.h @@ -0,0 +1,31 @@ +/* + * Copyright 2009 Ettus Research LLC + */ + +#ifndef IO_H +#define IO_H + +#include <stdint.h> +#include <stdbool.h> + +#define IO_PX(port, pin) ((uint8_t)(((port - 'A') << 4) + pin)) +#define IO_PA(pin) IO_PX('A', pin) +#define IO_PB(pin) IO_PX('B', pin) +#define IO_PC(pin) IO_PX('C', pin) +#define IO_PD(pin) IO_PX('D', pin) + +typedef const uint8_t io_pin_t; + +void io_output_pin(io_pin_t pin); +void io_input_pin(io_pin_t pin); +bool io_is_output(io_pin_t pin); +bool io_is_input(io_pin_t pin); + +void io_set_pin(io_pin_t pin); +void io_clear_pin(io_pin_t pin); +void io_enable_pin(io_pin_t pin, bool enable); +bool io_is_pin_set(io_pin_t pin); + +bool io_test_pin(io_pin_t pin); + +#endif /* IO_H */ diff --git a/firmware/e300/rev_c/ltc3675.c b/firmware/e300/rev_c/ltc3675.c new file mode 100644 index 000000000..0f85ec7e5 --- /dev/null +++ b/firmware/e300/rev_c/ltc3675.c @@ -0,0 +1,525 @@ +/* + * Copyright 2012 Ettus Research LLC + */ + +/* +    ? STOP condition after writing address on read +    - Default buck/boost register values are OK +*/ + +#include "config.h" +#include "ltc3675.h" + +//#include <stdio.h> +#include <util/delay.h> +#include <avr/interrupt.h> + +#include "io.h" +#include "i2c.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +#ifndef I2C_REWORK +#include "power.h" +#endif // I2C_REWORK + +const bool _ltc3675_pull_up = +#ifdef I2C_REWORK +	true +#else +	false +#endif // I2C_REWORK +; + +volatile ltc3675_reg_helper_fn _ltc3675_reg_helper; + +//#define HARDWIRE_ENABLE	// Use hardware enable pins instead of I2C on regulators that support it + +#ifdef ATTINY88_DIP + +#ifdef HARDWIRE_ENABLE +static io_pin_t PWR_EN1     = IO_PC(7);	// Not routed by card +static io_pin_t PWR_EN2     = IO_PA(0);	// Not available on DIP +static io_pin_t PWR_EN3     = IO_PA(1);	// Not available on DIP +static io_pin_t PWR_EN4     = IO_PB(6);	// Instead of FTDI_BCD +static io_pin_t PWR_EN5     = IO_PB(7);	// Instead of FTDI_PWREN2 +#endif // HARDWIRE_ENABLE + +//static io_pin_t PWR_SDA     = IO_PC(4); +//static io_pin_t PWR_SCL     = IO_PC(5); + +#else + +#ifdef HARDWIRE_ENABLE +static io_pin_t PWR_EN1     = IO_PC(1); +//static io_pin_t PWR_EN2     = IO_PC(2);	// Now used by I2C for charge controller +//static io_pin_t PWR_EN3     = IO_PC(3);	// Now used by I2C for charge controller +static io_pin_t PWR_EN4     = IO_PA(1); +static io_pin_t PWR_EN5     = IO_PA(2); +#endif // HARDWIRE_ENABLE + +#ifdef I2C_REWORK +static io_pin_t PWR_SDA     = IO_PC(2);		// Instead of EN5 +static io_pin_t PWR_SCL     = IO_PA(2);		// Instead of EN2 +#endif // I2C_REWORK + +#endif // ATTINY88_DIP + +static io_pin_t PWR_IRQ     = IO_PD(0); +static io_pin_t WAKEUP      = IO_PD(2); +static io_pin_t ONSWITCH_DB = IO_PD(3); +static io_pin_t PWR_RESET   = IO_PD(4); + +#define LTC3675_BASE_ADDRESS    0x12 +#define LTC3675_WRITE_ADDRESS   (LTC3675_BASE_ADDRESS + 0) +#define LTC3675_READ_ADDRESS    (LTC3675_BASE_ADDRESS + 1) + +#define LTC3675_RETRY_DELAY     1   // us MAGIC +#define LTC3675_MAX_ACK_RETRIES 10  // * LTC3675_RETRY_DELAY us + +#define LTC3675_SCL_LOW_PERIOD  2   // 1.3 us +#define LTC3675_SCL_HIGH_PERIOD 1   // 0.6 us +#define LTC3675_BUS_FREE_TIME   2   // 1.3 us +#define LTC3675_STOP_TIME       1   // 0.6 us + +#define LTC3675_REGULATOR_ENABLE_DELAY	10	// 50	// ms (some arbitrary value so that the external power supply can settle) + +enum LTC3675Registers +{ +	LTC3675_REG_NONE			= 0x00, +	LTC3675_REG_BUCK1			= 0x01, +	LTC3675_REG_BUCK2			= 0x02, +	LTC3675_REG_BUCK3			= 0x03, +	LTC3675_REG_BUCK4			= 0x04, +	LTC3675_REG_BOOST			= 0x05, +	LTC3675_REG_BUCK_BOOST		= 0x06, +	LTC3675_REG_LED_CONFIG		= 0x07, +	LTC3675_REG_LED_DAC			= 0x08, +	LTC3675_REG_UVOT			= 0x09, +	LTC3675_REG_RSTB			= 0xA0, +	LTC3675_REG_IRQB_MASK		= 0x0B, +	LTC3675_REG_REALTIME_STATUS	= 0x0C, +	LTC3675_REG_LATCHED_STATUS	= 0x0D, +	LTC3675_REG_CLEAR_IRQ		= 0x0F +}; + +enum LTC3675StatusBits +{ +	LTC3675_UnderVoltage	= 1 << 7, +	LTC3675_OverTemperature	= 1 << 6, +	LTC3675_BuckBoost_PGood	= 1 << 5, +	LTC3675_Boost_PGood		= 1 << 4, +	LTC3675_Buck4_PGood		= 1 << 3, +	LTC3675_Buck3_PGood		= 1 << 2, +	LTC3675_Buck2_PGood		= 1 << 1, +	LTC3675_Buck1_PGood		= 1 << 0 +}; + +#define LTC3675_DEFAULT_BUCK_REG_VAL		0x6F +#define LTC3675_DEFAULT_BOOST_REG_VAL		0x0F +#define LTC3675_DEFAULT_BUCK_BOOST_REG_VAL	0x0F + +#define LTC3675_ENABLE_REGISTER_BIT			0x80 + +// Max I2C rate = 400kHz + +static void _ltc3675_clear_irq() +{ +	// Two-stage clear +	i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_CLEAR_IRQ, 0x00, _ltc3675_pull_up); +	i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_NONE, 0x00, _ltc3675_pull_up); +} + +volatile uint8_t _ltc3675_last_status = 0x00; + +uint8_t ltc3675_get_last_status(void) +{ +	return _ltc3675_last_status; +} + +uint8_t ltc3675_reg_status_to_error(uint8_t val) +{ +	if (((val & LTC3675_BuckBoost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_6))) +		return BlinkError_3_3V_Peripherals_Power; +	 +	if (((val & LTC3675_Boost_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_5))) +		return BlinkError_TX_Power; +	 +	//if (((val & LTC3675_Buck4_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_4))) +	 +	if (((val & LTC3675_Buck3_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_3))) +		return BlinkError_1_8V_Peripherals_Power; +	 +	//if (((val & LTC3675_Buck2_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_2))) +	 +	if (((val & LTC3675_Buck1_PGood) == 0) && ((_ltc3675_reg_helper)(LTC3675_REG_1))) +		return BlinkError_DRAM_Power; +	 +	return BlinkError_None; +} + +bool ltc3675_is_power_good(uint8_t val) +{ +	return (ltc3675_reg_status_to_error(val) == BlinkError_None); +} + +uint8_t ltc3675_status_to_error(uint8_t val) +{ +	if (val & LTC3675_UnderVoltage) +		return BlinkError_LTC3675_UnderVoltage; +	 +	if (val & LTC3675_OverTemperature) +		return BlinkError_LTC3675_OverTemperature; +	 +	uint8_t reg_error = ltc3675_reg_status_to_error(val); +	if (reg_error != BlinkError_None) +		return reg_error; +	 +	return BlinkError_None; +} + +bool _ltc3675_handle_irq(void) +{ +	uint8_t val = 0x00; +	bool result = false; +	 +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_LATCHED_STATUS, &val, _ltc3675_pull_up)) +	{ +		debug_log_ex("3675LTCH ", false); +		debug_log_hex(val); +	} +	 +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, /*LTC3675_REG_LATCHED_STATUS*/LTC3675_REG_REALTIME_STATUS, &val, _ltc3675_pull_up))	// No point acting on latched because could have been resolved +	{ +		//debug_log_ex("3675LTCH ", false); +		debug_log_ex("3675RT ", false); +		debug_log_hex(val); +		 +		_ltc3675_last_status = val; +		 +		uint8_t error = ltc3675_status_to_error(val); +		 +		/*if (val & LTC3675_UnderVoltage) +		{ +			pmc_set_blink_error(BlinkError_LTC3675_UnderVoltage); +			//_state.low_battery = true; +		}*/ +		 +		if (error) +		{ +			pmc_set_blink_error(error); +			 +			/*_i2c_disable_ack_check = true; +			uint8_t chk = 0x00; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_6) << 0; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_5) << 1; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_3) << 2; +			chk |= (_ltc3675_reg_helper)(LTC3675_REG_1) << 3; +			i2c_write_ex(PWR_SDA, PWR_SCL, 0xFE, 0xFF, chk, _ltc3675_pull_up); +			_i2c_disable_ack_check = false;*/ +		}			 +		 +		result = true; +	} +	 +	_ltc3675_clear_irq(); +	 +	return result; +} + +static bool _ltc3675_get_realtime_status(uint8_t* val) +{ +	//cli(); +	 +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, LTC3675_REG_REALTIME_STATUS, val, _ltc3675_pull_up) == false) +		return false; +	 +	debug_log_ex("3675RT ", false); +	debug_log_hex(*val); +	 +	//sei(); +	 +	return true; +} + +int8_t ltc3675_check_status(void) +{ +	uint8_t val = 0x00; +	 +	pmc_mask_irqs(true); +	 +	bool result = _ltc3675_get_realtime_status(&val); +	 +	pmc_mask_irqs(false); +	 +	if (result == false) +		return -1; +	 +	//_ltc3675_last_status = val; +	 +	/*if (val & LTC3675_UnderVoltage) +		return BlinkError_LTC3675_UnderVoltage; +	 +	if (val & LTC3675_OverTemperature) +		return BlinkError_LTC3675_OverTemperature; +	 +	return BlinkError_None;*/ +	 +	return ltc3675_status_to_error(val); +} + +bool ltc3675_handle_irq(void) +{ +	pmc_mask_irqs(true); +	 +	/*uint8_t*/bool result = _ltc3675_handle_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +static bool _ltc3675_default_reg_helper(uint8_t address) +{ +	uint8_t val = 0x00; +	i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, address, &val, _ltc3675_pull_up); +	return ((val & LTC3675_ENABLE_REGISTER_BIT) == LTC3675_ENABLE_REGISTER_BIT); +} + +bool ltc3675_init(ltc3675_reg_helper_fn helper) +{ +	if (helper) +		_ltc3675_reg_helper = helper; +	else +		_ltc3675_reg_helper = _ltc3675_default_reg_helper; +#ifdef HARDWIRE_ENABLE +    io_output_pin(PWR_EN1); +    io_output_pin(PWR_EN2); +    io_output_pin(PWR_EN3); +    io_output_pin(PWR_EN4); +    io_output_pin(PWR_EN5); +#endif // HARDWIRE_ENABLE + + /*	io_output_pin(PWR_SDA); +    io_output_pin(PWR_SCL); + +    // Must remain HIGH when idle +    io_set_pin(PWR_SDA); +    io_set_pin(PWR_SCL); +*/ +#ifdef I2C_REWORK +	i2c_init_ex(PWR_SDA, PWR_SCL, _ltc3675_pull_up); +#endif // I2C_REWORK +    io_input_pin(PWR_IRQ); +#if !defined(DEBUG) && !defined(ATTINY88_DIP) +	io_set_pin(PWR_IRQ);	// Enable pull-up for Open Drain +#endif // DEBUG +	 +    io_input_pin(WAKEUP); +	io_set_pin(WAKEUP);	// Enable pull-up for Open Drain +	 +    io_input_pin(ONSWITCH_DB); +	io_set_pin(ONSWITCH_DB);	// Enable pull-up for Open Drain +	 +    io_input_pin(PWR_RESET); +	io_set_pin(PWR_RESET);	// Enable pull-up for Open Drain +	 +	_ltc3675_clear_irq();	// Clear old interrupt - state might have changed (e.g. undervoltage might have been resolved) + +    if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_IRQB_MASK, 0xFF, _ltc3675_pull_up) == false)	// Any PGOOD fault will pull IRQB low +		return false; +	 +	if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, LTC3675_REG_UVOT, 0x70, _ltc3675_pull_up) == false)	// 3.4V UV +		return false; +	 +	if (ltc3675_has_interrupt()) +		_ltc3675_handle_irq(); +	 +	// Non-maskable: +	//	UV warning threshold (default): 2.7V +	//	Over temp warning threshold (default): 10 degrees below + +    return true; +} + +bool ltc3675_is_waking_up(void) +{ +	return io_test_pin(WAKEUP); +} + +static bool _ltc3675_is_pgood(uint8_t reg) +{ +	uint8_t val = 0x00; +	if (_ltc3675_get_realtime_status(&val) == false) +		return false; +	return ((reg & val) == reg); +} + +static bool _ltc3675_toggle_reg(uint8_t addr, uint8_t def_reg, bool on) +{ +	bool result = true; +	 +	//cli(); +	 +	uint8_t val = 0x00 | def_reg; +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, addr, &val, _ltc3675_pull_up) == false) +		return false; +	 +	val &= ~LTC3675_ENABLE_REGISTER_BIT; +	 +	if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, addr, /*def_reg*/val | (on ? LTC3675_ENABLE_REGISTER_BIT : 0x00), _ltc3675_pull_up) == false) +		//return true; +		result = false; +	 +	if (on) +	{ +		_delay_ms(LTC3675_REGULATOR_ENABLE_DELAY); +	} +	 +	//sei(); + +	return result; +	//return true; +} + +bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on) +{ +	//debug_blink2(reg + 1); +	debug_log_ex("3675 ", false); +	debug_log_byte_ex(reg, true); +	 +	// Sub-address: index of regulator +	// Data: <default reg contents> | <enable> +	 +	bool result = false; +	 +    switch (reg) +    { +        case LTC3675_REG_1: // Master +        case LTC3675_REG_2: // Slave +#ifdef HARDWIRE_ENABLE +            io_enable_pin(PWR_EN1, on); +			//break; +#else +			//debug_blink2(reg + 1); +			if (_ltc3675_toggle_reg(LTC3675_REG_BUCK1, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false) { +				//debug_blink2(reg + 1); +				return false; +			} +			//debug_blink2(reg + 1); +#endif // HARDWIRE_ENABLE +			result = (_ltc3675_is_pgood(LTC3675_Buck1_PGood) == on); +			break; +        case LTC3675_REG_3: // Master +        case LTC3675_REG_4: // Slave +#ifdef HARDWIRE_ENABLE +            io_enable_pin(PWR_EN3, on); +            //break; +#else +			if (_ltc3675_toggle_reg(LTC3675_REG_BUCK3, LTC3675_DEFAULT_BUCK_REG_VAL, on) == false) +				return false; +#endif // HARDWIRE_ENABLE +			result = (_ltc3675_is_pgood(LTC3675_Buck3_PGood) == on); +			break; +        case LTC3675_REG_5: // I2C only +            if (_ltc3675_toggle_reg(LTC3675_REG_BOOST, LTC3675_DEFAULT_BOOST_REG_VAL, on) == false)    // (Boost address, Default reg contents | Enable) +				return false; +			result = (_ltc3675_is_pgood(LTC3675_Boost_PGood) == on); +			break; +        case LTC3675_REG_6: // Single +#ifdef HARDWIRE_ENABLE +            io_enable_pin(PWR_EN5, on); +            //break; +#else +			if (_ltc3675_toggle_reg(LTC3675_REG_BUCK_BOOST, LTC3675_DEFAULT_BUCK_BOOST_REG_VAL, on) == false) +				return false; +#endif // HARDWIRE_ENABLE +			result = (_ltc3675_is_pgood(LTC3675_BuckBoost_PGood) == on); +			break; +        //default: +		//	return false; +    } +	 +	_debug_log((result ? "+" : "-")); + +    return result; +} + +bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage) +{ +    // Not necessary due to R-bridges and default DAC registers + +    // VRAM will be 1.3579 - a little high? (re-program DAC reference) +    //  No: minimum FB step will put Vout < 1.35 +	 +	uint16_t max_voltage = 0; +	uint8_t reg_subaddr = 0; +	 +	switch (reg) +	{ +		case LTC3675_REG_1:	// 1A Buck +		case LTC3675_REG_2:	// 1A Buck +			max_voltage = 1500; +			reg_subaddr = LTC3675_REG_BUCK1; +			break; +		case LTC3675_REG_3:	// 500mA Buck +		case LTC3675_REG_4:	// 500mA Buck +			max_voltage = 1800; +			reg_subaddr = LTC3675_REG_BUCK3; +			break; +		case LTC3675_REG_5:	// 1A Boost +			max_voltage = 5000; +			reg_subaddr = LTC3675_REG_BOOST; +			break; +		case LTC3675_REG_6:	// 1A Buck-Boost +			max_voltage = 3300; +			reg_subaddr = LTC3675_REG_BUCK_BOOST; +			break; +	} +	 +	if (voltage > max_voltage) +		return false; +	 +	//uint32_t rMax = ((uint32_t)voltage * 1000) / (uint32_t)max_voltage; +	//uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800; +	uint32_t rFB = ((uint32_t)max_voltage * 1000) / (uint32_t)800;	// 800mV full-scale feedback voltage +	uint32_t r = ((uint32_t)voltage * 1000) / (uint32_t)rFB; +	if (r < 450) +		return false; +	 +	uint16_t rDAC = (16 * ((uint16_t)r - 450)) / (800 - 450); +	 +	debug_log_ex("Vr ", false); +	debug_log_byte_ex(reg, false); +	debug_log_ex("=", false); +	debug_log_byte_ex((uint8_t)rDAC, false); +	 +	uint8_t val = 0x00; +	if (i2c_read2_ex(PWR_SDA, PWR_SCL, LTC3675_READ_ADDRESS, reg_subaddr, &val, _ltc3675_pull_up) == false) +	{ +		debug_log("-"); +		return false; +	}		 +	 +	val = (val & 0xF0) | (uint8_t)rDAC; +	if (i2c_write_ex(PWR_SDA, PWR_SCL, LTC3675_WRITE_ADDRESS, reg_subaddr, val, _ltc3675_pull_up) == false) +	{ +		debug_log("-"); +		return false; +	} +	 +	debug_log("+"); + +	return true; +} + +bool ltc3675_is_power_button_depressed(void) +{ +	return (io_test_pin(ONSWITCH_DB) == false); +} + +bool ltc3675_has_interrupt(void) +{ +	return (io_test_pin(PWR_IRQ) == false); +} diff --git a/firmware/e300/rev_c/ltc3675.h b/firmware/e300/rev_c/ltc3675.h new file mode 100644 index 000000000..d7c4baa59 --- /dev/null +++ b/firmware/e300/rev_c/ltc3675.h @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Ettus Research LLC + */ + +#ifndef LTC3675_H +#define LTC3675_H + +//#include "types.h" +#include <stdbool.h> +#include <stdint.h> + +typedef bool (*ltc3675_reg_helper_fn)(uint8_t address); + +bool ltc3675_init(ltc3675_reg_helper_fn helper); + +typedef enum ltc3675_regulators { +    LTC3675_REG_1,  // 1A Buck +    LTC3675_REG_2,  // 1A Buck +    LTC3675_REG_3,  // 500mA Buck +    LTC3675_REG_4,  // 500mA Buck +    LTC3675_REG_5,  // 1A Boost +    LTC3675_REG_6   // 1A Buck-Boost +    // LED Boost +} ltc3675_regulator_t; + +bool ltc3675_enable_reg(ltc3675_regulator_t reg, bool on); +bool ltc3675_set_voltage(ltc3675_regulator_t reg, uint16_t voltage); +bool ltc3675_is_power_button_depressed(void); +bool ltc3675_has_interrupt(void); +bool ltc3675_handle_irq(void); +int8_t ltc3675_check_status(void); +uint8_t ltc3675_get_last_status(void); +uint8_t ltc3675_status_to_error(uint8_t val); +bool ltc3675_is_power_good(uint8_t val); +bool ltc3675_is_waking_up(void); + +#endif /* LTC3675_H */ diff --git a/firmware/e300/rev_c/ltc4155.c b/firmware/e300/rev_c/ltc4155.c new file mode 100644 index 000000000..5f404e651 --- /dev/null +++ b/firmware/e300/rev_c/ltc4155.c @@ -0,0 +1,402 @@ +/* + * ltc4155.c + */  + +#ifndef CHARGER_TI + +#include "config.h" +#include "ltc4155.h" + +#include <util/delay.h> + +#include "io.h" +#include "i2c.h" +#include "power.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +static io_pin_t USBPM_IRQ	= IO_PB(1); + +#ifdef ATTINY88_DIP + +static io_pin_t CHRG_SDA     = IO_PC(2); +static io_pin_t CHRG_SCL     = IO_PC(3); + +#else + +#ifdef I2C_REWORK + +static io_pin_t CHRG_SDA     = IO_PC(4); +static io_pin_t CHRG_SCL     = IO_PC(5); + +#else + +#define CHRG_SDA	PWR_SDA +#define CHRG_SCL	PWR_SCL + +#endif // I2C_REWORK + +#endif // ATTINY88_DIP + +const bool _ltc4155_pull_up = false; + +#define LTC4155_BASE_ADDRESS    0x12 +#define LTC4155_WRITE_ADDRESS   (LTC4155_BASE_ADDRESS + 0) +#define LTC4155_READ_ADDRESS    (LTC4155_BASE_ADDRESS + 1) +/* +#define LTC4155_RETRY_DELAY     1   // us MAGIC +#define LTC4155_MAX_ACK_RETRIES 10  // * LTC4155_RETRY_DELAY us + +#define LTC4155_SCL_LOW_PERIOD  2   // 1.3 us +#define LTC4155_SCL_HIGH_PERIOD 1   // 0.6 us +#define LTC4155_BUS_FREE_TIME   2   // 1.3 us +#define LTC4155_STOP_TIME       1   // 0.6 us +*/ +enum LTC4155Registers +{ +	LTC4155_REG_USB			= 0x00,	// W/R +	LTC4155_REG_WALL		= 0x01,	// W/R +	LTC4155_REG_CHARGE		= 0x02,	// W/R +	LTC4155_REG_STATUS		= 0x03,	// R +	LTC4155_REG_GOOD		= 0x04,	// R +	LTC4155_REG_THERMISTOR	= 0x05,	// R +	LTC4155_REG_ENABLE		= 0x06,	// W/R +	LTC4155_REG_ARM_AND_SHIP= 0x07	// W +}; + +enum LTC4155InterruptMasks	// LTC4155_REG_ENABLE +{ +	LTC4155_ENABLE_USB_OTG	= 1 << 1, +	 +	LTC4155_INT_UVCL	= 1 << 2, +	LTC4155_INT_ILIMIT	= 1 << 3, +	LTC4155_INT_USB_OTG	= 1 << 4, +	LTC4155_INT_EXT_PWR	= 1 << 5, +	LTC4155_INT_FAULT	= 1 << 6, +	LTC4155_INT_CHARGER	= 1 << 7 +}; + +enum LTC4155Options	// LTC4155_REG_USB +{ +	LTC4155_USB_OTG_LOCKOUT				= 1 << 5, +	LTC4155_ENABLE_BATTERY_CONDITIONER	= 1 << 6, +	LTC4155_DISABLE_INPUT_UVCL			= 1 << 7 +}; + +enum LTC4155Shifts +{ +	LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT	= 4, +	LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE	= 2, +	LTC4155_SHIFTS_WALL_PRIORITY		= 7, +	LTC4155_SHIFTS_WALL_SAFETY_TIMER	= 5 +}; + +enum LTC4155Statuses	// LTC4155_REG_STATUS +{ +	LTC4155_LOW_BATTERY		= 1 << 0, +	LTC4155_BOOST_ENABLE	= 1 << 3, +	LTC4155_ID_PIN_DETECT	= 1 << 4, +}; + +enum LTC4155Goods	// LTC4155_REG_GOOD +{ +	LTC4155_BAD_CELL_FAULT		= 1 << 0, +	LTC4155_OTG_FAULT			= 1 << 1, +	LTC4155_OVP_ACTIVE			= 1 << 2, +	LTC4155_INPUT_UVCL_ACTIVE	= 1 << 3, +	LTC4155_INPUT_CURRENT_LIMIT_ACTIVE = 1 << 4, +	LTC4155_WALLSNS_GOOD		= 1 << 5, +	LTC4155_USBSNS_GOOD			= 1 << 6, +	LTC4155_EXTERNAL_POWER_GOOD	= 1 << 7 +}; + +enum LTC4155BatteryChargerStatues +{ +	LTC4155_CHARGER_OFF, +	LTC4155_CHARGER_LOW_BATTERY_VOLTAGE, +	LTC4155_CHARGER_CONSTANT_CURRENT, +	LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX, +	LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX, +	LTC4155_CHARGER_NTC_TOO_WARM, +	LTC4155_CHARGER_NTC_TOO_COLD, +	LTC4155_CHARGER_NTC_HOT +}; + +enum LTC4155ThermistorStatuses +{ +	LTC4155_NTC_NORMAL, +	LTC4155_NTC_TOO_COLD, +	LTC4155_NTC_TOO_WARM, +	LTC4155_NTC_FAULT +}; + +static const uint8_t _ltc4155_interrupt_mask = +//	LTC4155_ENABLE_USB_OTG |// Enable +5V on USB connector	// Is this causing the chip to power off the output?! +	LTC4155_INT_UVCL | +	LTC4155_INT_ILIMIT | +	LTC4155_INT_USB_OTG | +	LTC4155_INT_EXT_PWR |	// Turn up current limit +	LTC4155_INT_FAULT |		// Blink error +	LTC4155_INT_CHARGER;	// Illuminate charge LED + +static bool _ltc4155_clear_irq(void) +{ +	return i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, _ltc4155_interrupt_mask, _ltc4155_pull_up); +} + +bool ltc4155_clear_irq(void) +{ +	pmc_mask_irqs(true); +	 +	bool result = _ltc4155_clear_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +static uint8_t _ltc4155_last_good, _ltc4155_last_status; + +bool _ltc4155_handle_irq(void) +{ +	_ltc4155_clear_irq();	// Clear frozen registers to get the real-time ones +	 +	_delay_ms(50);	// Wait for registers to clear/update +	 +	////////////////// +	 +	uint8_t val = 0x00; +	bool result = false; +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false) +		goto _ltc4155_handle_fail; +	 +	//if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false) +	//	goto _ltc4155_handle_fail; +	 +	debug_log_ex("4155GO ", false); +	debug_log_hex(val); +	 +	if (val & LTC4155_WALLSNS_GOOD) +	{ +		uint8_t wall_state = 0; +		if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &wall_state, _ltc4155_pull_up) == false) +			goto _ltc4155_handle_fail; +		 +		wall_state &= ~0x1E; +		wall_state |= 0x0E; +		 +		if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false) +			goto _ltc4155_handle_fail; +		 +		debug_log("I+"); +	} +	 +	_ltc4155_last_good = val; +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false) +		goto _ltc4155_handle_fail; +	 +	//if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false) +	//	goto _ltc4155_handle_fail; +	 +	debug_log_ex("4155ST ", false); +	debug_log_hex(val); +	 +	_ltc4155_last_status = val; +	 +	val >>= 5; +	 +	if (_state.blink_error == BlinkError_None) +	{ +		switch (val) +		{ +			case LTC4155_CHARGER_CONSTANT_CURRENT: +			case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_GT_VCX: +			case LTC4155_CHARGER_LOW_BATTERY_VOLTAGE:	// If this persists for more than 1/2hr, BAD_CELL_FAULT is enabled and FAULT interrupt is generated +			{ +				if ((_state.battery_not_present == false) && +					(_ltc4155_last_good & (LTC4155_WALLSNS_GOOD | LTC4155_USBSNS_GOOD))) +				{ +					//charge_set_led(true); +					charge_notify(true); +					break; +				}						 +			} +			case LTC4155_CHARGER_CONSTANT_VOLTAGE_VPROG_LT_VCX:	// Small amount of current still charging the battery but below Vc/x threshold +			//case LTC4155_CHARGER_NTC_TOO_WARM: +			//case LTC4155_CHARGER_NTC_TOO_COLD: +			//case LTC4155_CHARGER_NTC_HOT: +			//	break; +			//case LTC4155_CHARGER_OFF: +			default: +				//charge_set_led(false); +				charge_notify(false); +		} +	} +	 +//	ltc4155_dump(); +	 +	result = true; +_ltc4155_handle_fail: +	_ltc4155_clear_irq();	// Even though it happens first above, this is necessary otherwise future IRQs won't be detected +	 +	return result; +} + +#define LTC4155_CHARGE_CURRENT_LIMIT	/*0xF*/0x7	// [100%] 50% + +bool ltc4155_set_charge_current_limit(uint8_t percentage) +{ +	uint8_t val = 0; +	uint8_t limit = 0; +	 +	if (percentage > 100) +		return false; +	else if (percentage == 100) +		percentage = 0xF; +	else if (percentage > 12)	// 0..88 -> 0..8800 +	{ +		uint16_t l = (((uint16_t)percentage - 12) * 100) / 586; +		limit = (uint8_t)l; +	} +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, &val, _ltc4155_pull_up) == false) +		return false; +	 +	val &= ((0x1 << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT) - 1); +	//val |= (LTC4155_CHARGE_CURRENT_LIMIT << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT); +	val |= (limit << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT); +	 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, val, _ltc4155_pull_up) == false) +		return false; +	 +//ltc4155_dump(); +	 +	return true; +} + +bool ltc4155_init(bool disable_charger) +{ +	io_input_pin(USBPM_IRQ); +#if !defined(DEBUG) && !defined(ATTINY88_DIP) +	io_set_pin(USBPM_IRQ);	// Enable pull-up for Open Drain +#endif // DEBUG +#ifdef I2C_REWORK +	i2c_init_ex(CHRG_SDA, CHRG_SCL, _ltc4155_pull_up); +#endif // I2C_REWORK +	if (/*_ltc4155_clear_irq()*/_ltc4155_handle_irq() == false)	// Will set interrupt masks	// FIXME: Why does this cause instability?! +		return false; + +	const uint8_t charge_state = +		(disable_charger ? 0x0 : LTC4155_CHARGE_CURRENT_LIMIT) << LTC4155_SHIFTS_CHARGE_CURRENT_LIMIT |	// Battery charger I limit = 100% +		0x3 << LTC4155_SHIFTS_CHARGE_FLOAT_VOLTAGE |	// FIXME: Vbatt float = 4.05V - 4.2V for LiPo (default 0x00) +		0x0;	// Full capacity charge threshold = 10% +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_CHARGE, charge_state, _ltc4155_pull_up) == false) +		return false; + +	const uint8_t wall_state = +		0x0 << LTC4155_SHIFTS_WALL_PRIORITY | +		0x0 << LTC4155_SHIFTS_WALL_SAFETY_TIMER |	// Charge safety timer = 4hr	// FIXME: 8hr or Vc/x +		0xE;	// 3 amps, 0x1F - CLPROG1 +	if (i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_WALL, wall_state, _ltc4155_pull_up) == false) +		return false; + +	// FIXME: +	// Disable ID pin detection & autonomous startup +	// Enable OTG +	//i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_USB, LTC4155_USB_OTG_LOCKOUT, _ltc4155_pull_up);	// Disable autonomous startup +	//i2c_write_ex(CHRG_SDA, CHRG_SCL, LTC4155_WRITE_ADDRESS, LTC4155_REG_ENABLE, LTC4155_ENABLE_USB_OTG, _ltc4155_pull_up);	// Enable OTG +	 +	if (_ltc4155_handle_irq() == false)	// One more time (IRQ LED stays lit in dev setup) +		return false; +	 +	return true; +} + +bool ltc4155_has_interrupt(void) +{ +	//bool state = io_test_pin(USBPM_IRQ); +	//debug_log_ex("4155IRQ", false); +	//debug_log_byte(state); +	//return (state != 1); +	return (io_test_pin(USBPM_IRQ) == false); +} + +bool ltc4155_handle_irq(void) +{ +	pmc_mask_irqs(true); +	 +	bool result = _ltc4155_handle_irq(); +	 +	pmc_mask_irqs(false); +	 +	return result; +} + +bool ltc4155_arm_ship_and_store(void) +{ +	return true; +} + +bool ltc4155_get_thermistor(uint8_t* val, bool* warning) +{ +	bool result = false; +	uint8_t _val = 0; +	 +	pmc_mask_irqs(true); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_THERMISTOR, &_val, _ltc4155_pull_up) == false) +		goto ltc4155_get_thermistor_fail; +	 +	if (val) +		(*val) = _val >> 1; +	 +	if (warning) +		(*warning) = ((_val & 0x01) != 0x00); +	 +	result = true; +ltc4155_get_thermistor_fail: +	pmc_mask_irqs(false); +	return result; +} + +void ltc4155_dump(void) +{ +	pmc_mask_irqs(true); +	 +	uint8_t val = 0x00; +	bool warning = false; +	 +	if (ltc4155_get_thermistor(&val, &warning) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\tTHRM", false); +	if (warning) +		debug_log_ex("!", false); +	debug_log_byte(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_WALL, &val, _ltc4155_pull_up) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\tWALL", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_GOOD, &val, _ltc4155_pull_up) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\t4155GO ", false); +	debug_log_hex(val); +	 +	if (i2c_read2_ex(CHRG_SDA, CHRG_SCL, LTC4155_READ_ADDRESS, LTC4155_REG_STATUS, &val, _ltc4155_pull_up) == false) +		goto ltc4155_dump_fail; +	 +	debug_log_ex("\t4155ST ", false); +	debug_log_hex(val); +	 +ltc4155_dump_fail: +	pmc_mask_irqs(false); +} + +#endif // !CHARGER_TI diff --git a/firmware/e300/rev_c/ltc4155.h b/firmware/e300/rev_c/ltc4155.h new file mode 100644 index 000000000..7e8e3751d --- /dev/null +++ b/firmware/e300/rev_c/ltc4155.h @@ -0,0 +1,25 @@ +/* + * ltc4155.h + * + * Created: 17/08/2012 8:09:43 PM + *  Author: Balint Seeber + */  + + +#ifndef LTC4155_H_ +#define LTC4155_H_ + +#include <stdbool.h> +#include <stdint.h> + +#ifndef CHARGER_TI + +bool ltc4155_init(bool disable_charger); +bool ltc4155_has_interrupt(void); +bool ltc4155_handle_irq(void); +void ltc4155_dump(void); +bool ltc4155_set_charge_current_limit(uint8_t percentage); + +#endif // !CHARGER_TI + +#endif /* LTC4155_H_ */ diff --git a/firmware/e300/rev_c/main.c b/firmware/e300/rev_c/main.c new file mode 100644 index 000000000..1e065ffef --- /dev/null +++ b/firmware/e300/rev_c/main.c @@ -0,0 +1,385 @@ +/* + * Copyright 2009 Ettus Research LLC + */ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <avr/io.h> +#include <util/delay.h> +#include <avr/sleep.h> +#include <avr/interrupt.h> + +#include "global.h" +#include "power.h" +#include "debug.h" +#include "error.h" +#include "ltc3675.h" +#ifdef CHARGER_TI +#include "bq24190.h" +#else +#include "ltc4155.h" +#endif // CHARGER_TI + +#define AUTO_POWER_ON + +#define INITIAL_DELAY	250	// ms + +FUSES = {	// FIXME: & FUSE_CKSEL1 for low power 128 kHz clock +	.low = (FUSE_CKSEL0 & FUSE_SUT0 & FUSE_CKDIV8),	// Internal 8MHz Oscillator, Slowly rising power (start-up time), Divide Clock by 8 +	.high = (FUSE_EESAVE & FUSE_SPIEN),	// Save EEPROM between flashes	// FIXME: Leave SPIEN for programming enabled? +};	// Not using watchdog as it runs during sleep and consumes power + +volatile STATE _state; + +/* +    - Main/shared variables must be volatile +	- Port pins are tri-stated on reset +	* AVR_IRQ PD(5) +	- Enable pull-ups on all O.D. outputs from regulator chip +	* PS_POR/SRST should be driven HIGH by ATTiny? +	- AVR_RESET -> RESET pin - don't configure fuse (this would disable this functionality and prohibit serial programming) +	* Ship-and-store mode for charge controller? +	* cli before I2C calls +	* PS_TX +	- en5-clk, en2-data +	* Instruction following SEI is executed before interrupts +	* LTC3675 real-time status doesn't contain UV/OT +	* LTC3675 PGOOD -> power down (no point in checking blink state) +	* On WALL, use TX, on battery use OTG switcher +	* PRR - Power Reduction Register (p40) +	- 100% -> 50% battery charge limit +	* Check latched status for UV/OT in 3675 +	* If blink error reset, get latest charge status from 4155 +	* Fix UV status check from 3675/4155 as they currently share the same error  +	* Use charger termination at 8hr or Vc/x +	* Check PGood on all regs after power on before setting powered=true +	* Re-init 4155 on soft-power on +	- Re-set 3A limit in 4155 after external power connection +	- Removing power when running on battery, 4155GO 0xA0 - but WALL has been removed +	- Why is charger reporting Constant Current when power is removed +	* ltc3675_is_power_button_depressed: check if any reg is on, otherwise value will be invalid +	* When e.g. 3.3V doesn't come up, blink code is correctly 4 but there's a very short blink before re-starting the sequence +	- Vprog<Vc/x +*/ + +bool pmc_mask_irqs(bool mask) +{ +	if (_state.interrupts_enabled == false) +		return false; +	 +	if (mask) +	{ +		if (_state.interrupt_depth == 0) +			cli(); +		++_state.interrupt_depth; +	}		 +	else +	{ +		if (_state.interrupt_depth == 0) +			return false; +		 +		--_state.interrupt_depth; +		if (_state.interrupt_depth == 0) +			sei(); +	} +	 +	return true; +} + +int main(void) +{ +	_delay_ms(INITIAL_DELAY); +	 +	/////////////////////////////////////////////////////////////////////////// +	 +	memset((void*)&_state, 0x00, sizeof(STATE)); +	 +	debug_init(); +	debug_blink(1); +	 +	//debug_log("#");	// Will not boot if this is 21 chars long?! +	debug_log("Hello world"); +	 +    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // SLEEP_MODE_PWR_SAVE combination is documented as Reserved +	 +//ltc4155_dump(); + +    // FIXME: Init as SPI slave (FPGA is master) +	 +	// 8-bit timer for blinking errors on charge LED +	TCCR0A = _BV(CTC0);		// CTC mode +	OCR0A = 244;			// 250ms with 1024 prescale +	TIMSK0 = _BV(OCIE0A);	// Enable CTC on Timer 0 + +	bool init_result = power_init(); +	debug_log_ex("Init", false); +	_debug_log(init_result ? "+" : "-"); +	debug_blink(2); +	//debug_blink_rev(6); +	 +	/////////////////////////////////// +#ifdef AUTO_POWER_ON +	power_on(); // Turn on immediately. Need to de-press power button to turn off. +	debug_log("Power"); +	debug_blink(3); +	//debug_blink_rev(10); + +	//debug_wait(); +	 +//ltc4155_dump(); +#endif // AUTO_POWER_ON +	_state.interrupts_enabled = true; +	sei();	// Enable interrupts + +	asm("nop"); + +	_state.wake_up = false;	// This will fire the first time the regs are turned on +	 +	bool one_more = false; +	 +	while (true) +	{ +		one_more = false; +#ifdef CHARGER_TI +		if (_state.bq24190_irq) +		{ +			bq24190_handle_irq(); +			 +			_state.bq24190_irq = false; +		} +#else +		if ((_state.ltc4155_irq)/* || ltc4155_has_interrupt()*/)	// [Don't know why PCINT ISR misses LTC4155 IRQ on power up, so double-check state of line] +		{ +			ltc4155_handle_irq(); +//ltc4155_dump(); +			_state.ltc4155_irq = false; +		} +#endif // !CHARGER_TI +		if (_state.core_power_bad)	// FIXME: Check whether it's supposed to be on +		{ +			if (power_is_subsys_on(PS_FPGA)) +			{ +				_delay_ms(1);	// Seeing weird 120us drop in PGOOD during boot from flash (no apparent drop in 1.0V though) +				 +				if (tps54478_is_power_good() == false) +				{ +					debug_log("ML:FPGA!"); +			 +					//power_off(); +					_state.power_off = true; +			 +					/*while (_state.wake_up == false) +					{ +						blink_error_sequence(1); +					}*/ +					pmc_set_blink_error(BlinkError_FPGA_Power);	// [If a blink error was set in power_off, this will supercede it] +				} +			}			 +			 +			_state.core_power_bad = false; +		} +		 +		if ((_state.ltc3675_irq)/* || ltc3675_has_interrupt()*/)	// This is fired on initial power up +		{ +			debug_log("ML:3675+"); +			 +			ltc3675_handle_irq(); +			 +			if (ltc3675_is_power_good(ltc3675_get_last_status()) == false) +			{ +				debug_log("ML:3675!"); +				 +				//power_off(); +				_state.power_off = true; +			} +			 +			_state.ltc3675_irq = false; +		} +		 +		if (_state.power_off) +		{ +			debug_log("ML:Off.."); +			 +			power_off(); +			 +			_state.power_off = false; +			_state.wake_up = false; +		} +		else if (_state.wake_up) +		{ +			_delay_ms(1);	// Tapping 3.1 ohm load ing 4155 in dev setup causes transient on this line and causes power on sequence to begin again +			 +			//if (_state.powered == false)	// Don't check in case button is held long enough to force LTC3675 shutdown (will not change 'powered' value) +			if (ltc3675_is_waking_up()) +			{ +				debug_log("ML:On.."); +				 +				power_on(); +			} +			 +			_state.wake_up = false; +		} +		 +		// Check to see if the error state has resolved itself at the end of each sequence of the current blink error +		 +		if ((_state.blink_error != BlinkError_None) && (_state.blink_last_loop != _state.blink_loops)) +		{ +			// [Check IRQs periodically] +			 +			bool ltc3675_use_last_status = false; +			/*if (ltc3675_has_interrupt()) +			{ +				//debug_set(IO_PB(6), ((_state.blink_loops % 2) == 0)); +				ltc3675_use_last_status = true; +				ltc3675_handle_irq(); +			}*/ +			 +			/////////////////////////// +			 +			switch (_state.blink_error) +			{ +				case BlinkError_LTC3675_UnderVoltage: +				case BlinkError_LTC3675_OverTemperature: +				case BlinkError_DRAM_Power: +				case BlinkError_3_3V_Peripherals_Power: +				case BlinkError_1_8V_Peripherals_Power: +				case BlinkError_TX_Power: +					if (((ltc3675_use_last_status) && (ltc3675_status_to_error(ltc3675_get_last_status()) != BlinkError_None)) ||  +						((ltc3675_use_last_status == false) && (ltc3675_check_status() != BlinkError_None))) +						break; +					debug_log("BE:3675-"); +					goto cancel_blink_error; +				case BlinkError_FPGA_Power: +					if (tps54478_is_power_good() == false) +						break; +					debug_log("BE:FPGA-"); +					goto cancel_blink_error; +				default: +cancel_blink_error:				 +					//debug_set(IO_PB(7), true); +					pmc_set_blink_error(BlinkError_None); +			} +			 +			//////////////////////////////////// +			 +			// More periodic checks +			// Need to do this has some interrupts are on PCINT, and while GIE is disabled, might change & change back +			//	E.g. LTC3675 IRQ due to UV, reset IRQ, re-asserts UV +#ifndef CHARGER_TI +			if (ltc4155_has_interrupt()) +			{ +				debug_log("BE:4155"); +				 +				_state.ltc4155_irq = true; +				one_more = true; +			} +#endif // !CHARGER_TI +			if (ltc3675_has_interrupt()) +			{ +				debug_log("BE:3675"); +				 +				_state.ltc3675_irq = true; +				one_more = true; +			} +			 +			if (power_is_subsys_on(PS_FPGA)) +			{ +				if (tps54478_is_power_good() == false) +				{ +					debug_log("BE:FPGA!"); +				 +					_state.core_power_bad = true; +					one_more = true; +				} +			} +			 +			//////////////////////////////////// +			 +			_state.blink_last_loop = _state.blink_loops; +		} +		 +		//if (_state.timers_running == false) +		if ((_state.active_timers == 0) && (one_more == false)) +		{ +			debug_log("^"); +			sleep_mode(); +			debug_log("$"); +		}			 +	} + +	return 0; +} + +uint8_t pmc_get_blink_error(void) +{ +	return _state.blink_error; +} + +void pmc_set_blink_error(uint8_t count) +{ +	if ((_state.blink_error != BlinkError_None) && (count /*> _state.blink_error*/!= BlinkError_None))	// [Prioritise] Always keep first sequence running +		return; +	else if (_state.blink_error == count)	// Don't restart if the same +		return; +	 +	if (count == BlinkError_None) +	{ +		debug_log("BLNK-"); +		_state.blink_stop = true; +		return; +	} +	 +	//char msg[25]; +	//sprintf(msg, "Blink code = %i\n", count); +	//debug_log(msg); +	debug_log_ex("BLNK ", false); +	debug_log_byte(count); +	 +	_state.blink_error = count; +	_state.blink_loops = 0; +	_state.blink_last_loop = 0; +	_state.blinker_state = 0; +	_state.blink_stop = false; + +	charge_set_led(false); +	 +	TCNT0 = 0; +	if ((TCCR0A & 0x07) == 0x00)	// Might already be active with existing error +		_state.active_timers++; +	TCCR0A |= 0x05;	// Start with 1024 prescale +} + +ISR(TIMER0_COMPA_vect)	// Blink the sequence, and leave one slot at the beginning and end where the LED is off so one can get a sense of how many blinks occurred +{ +	pmc_mask_irqs(true); +	 +	if (_state.blinker_state < (2 * _state.blink_error + 1)) +		charge_set_led((_state.blinker_state % 2) == 1); +	 +	_state.blinker_state++; +	 +	if (_state.blinker_state == (2 * _state.blink_error + 1 + 1)) +	{ +		_state.blinker_state = 0; +		 +		if (_state.blink_stop) +		{ +			if ((TCCR0A & 0x07) != 0x00) +				_state.active_timers--; +			TCCR0A &= ~0x07; +			 +			_state.blink_error = BlinkError_None; +			 +			debug_log("BLNK."); +		} +		else +		{ +			_state.blink_loops++; +		} +	} +	 +	pmc_mask_irqs(false); +} diff --git a/firmware/e300/rev_c/power.c b/firmware/e300/rev_c/power.c new file mode 100644 index 000000000..36bc81456 --- /dev/null +++ b/firmware/e300/rev_c/power.c @@ -0,0 +1,900 @@ +/* +	* Test battery voltage code +	* Charger error blinking uses busy wait - will drain battery if encounters error while unattended? Surely rest of H/W will pull more current. +*/ +#include "config.h" +#include "power.h" + +#include <string.h> +#include <util/delay.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <avr/sleep.h> + +#include "io.h" +#include "i2c.h" +#include "ltc3675.h" +#include "ltc4155.h" +#include "bq24190.h" +#include "debug.h" +#include "global.h" +#include "error.h" + +#define BLINK_ERROR_DELAY		250  // ms + +#define POWER_DEFAULT_DELAY     50  // ms +#define POWER_DEFAULT_RETRIES   10 + +#define BATT_MIN_VOLTAGE		2000	// mV + +#define ARRAY_SIZE(a)			(sizeof(a)/sizeof(a[0])) +#define ZERO_MEMORY(s)			memset(&s, 0x00, sizeof(s)) + +#ifndef I2C_REWORK +io_pin_t PWR_SDA     = IO_PC(4); +io_pin_t PWR_SCL     = IO_PC(5); + +io_pin_t USB_RESETn= IO_PA(2); +#endif // I2C_REWORK + +//volatile bool powered = false; + +#ifdef DDR3L +#define DRAM_VOLTAGE	1350 // TODO: Misleading, actual DRAM voltage is 1.5V. This sets the regular voltage. +#else +#define DRAM_VOLTAGE	0	// Hardware default +#endif // DDR3 + +struct reg_config { +    int16_t voltage;    // mV +	uint8_t device; +    uint8_t address;    // Device specific +    bool powered; +} default_reg_config[] = {        // Index maps to 'power_subsystem_t', 0 volts means leave at hardware default +	{ 0000, REG_UNKNOWN, 0/*, true*/ },				// PS_UNKNOWN +	{ 1000, REG_TPS54478, 0/*, true*/ },			// PS_FPGA +	{ DRAM_VOLTAGE, REG_LTC3675, LTC3675_REG_1 },	// PS_VDRAM +	{ /*1800*/0, REG_LTC3675, LTC3675_REG_3 },		// PS_PERIPHERALS_1_8 +	{ /*3300*/0, REG_LTC3675, LTC3675_REG_6 },		// PS_PERIPHERALS_3_3 +	{ /*5000*/0, REG_LTC3675, LTC3675_REG_5 }		// PS_TX +}; +/* +int8_t power_get_regulator_index(uint8_t device, uint8_t address) +{ +	for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i) +	{ +		struct reg_config* reg = default_reg_config + i; +		if ((reg->device == device) && (reg->address == address)) +			return i; +	} +	 +	return -1; +} +*/ +bool power_is_subsys_on(power_subsystem_t index) +{ +	if ((index <= PS_UNKNOWN) || (index >= PS_MAX)) +		return false; +	 +	return default_reg_config[index].powered; +} + +static bool ltc3675_reg_helper(uint8_t address) +{ +	for (int8_t i = 0; i < ARRAY_SIZE(default_reg_config); ++i) +	{ +		struct reg_config* reg = default_reg_config + i; +		if ((reg->device == REG_LTC3675) && (reg->address == address)) +			return reg->powered; +	} +#ifdef DEBUG_SAFETY +	debug_log_ex("!3675HLP ", false); +	debug_log_hex(address); +#endif // DEBUG_SAFETY +	return false; +	//return power_is_subsys_on(power_get_regulator_index(REG_LTC3675, address) - 1); +} + +static io_pin_t AVR_CS      = IO_PB(2); +static io_pin_t AVR_MOSI    = IO_PB(3); +static io_pin_t AVR_MISO    = IO_PB(4); +static io_pin_t AVR_SCK     = IO_PB(5); + +static io_pin_t FTDI_RESETn = IO_PB(6); +static io_pin_t FTDI_CBUS3 = IO_PB(7); +static io_pin_t USB_CLK_EN = IO_PA(1); + +static io_pin_t AVR_RESET   = IO_PC(6); +static io_pin_t AVR_IRQ     = IO_PD(5); + +/////////////////////////////////////////////////////////////////////////////// + +#define TPS54478_START_DELAY	10	// 50 (safety)	// 3 (per spec)	// ms (some arbitrary value so that the external power supply can settle) + +#ifdef ATTINY88_DIP +static io_pin_t CORE_PWR_EN = IO_PC(1);	// IO_PC(7) not routed by card, using PWER_EN1 instead +#else +static io_pin_t CORE_PWR_EN = IO_PA(3); +#endif // ATTINY88_DIP +static io_pin_t CORE_PGOOD = IO_PB(0); + +void tps54478_init(bool enable) +{ +	tps54478_set_power(enable); +	io_clear_pin(CORE_PWR_EN); +	 +    io_input_pin(CORE_PGOOD); +#if !defined(DEBUG) && !defined(ATTINY88_DIP)	// Don't enable pull-up when connected to a pulled-up switch +	io_set_pin(CORE_PGOOD);	// Enable pull-up for Open Drain +#endif // DEBUG +//#ifdef DEBUG +//	io_enable_pin(CORE_PWR_EN, false); +//#endif // DEBUG +//_delay_ms(2500); +} + +void tps54478_set_power(bool on) +{ +	debug_log_ex("54478", false); +	 +	// Assumes: Hi-Z input/LOW output +	 +	if (on) +	{ +		io_input_pin(CORE_PWR_EN); +		_delay_ms(TPS54478_START_DELAY); +		 +		debug_log("+"); +	}		 +	else +	{ +		io_output_pin(CORE_PWR_EN); +		// Don't delay here as we can't detect its state anyway +		 +		debug_log("-"); +	}		 +	 +	//io_enable_pin(CORE_PWR_EN, on); +} + +bool tps54478_is_power_good(void) +{ +    return io_test_pin(CORE_PGOOD);	// This doesn't necessarily mean it's good - the chip might be malfunctioning (or switched off) +} + +/////////////////////////////////////////////////////////////////////////////// + +static io_pin_t CHARGE      = IO_PD(1); + +#if !defined(ATTINY88_DIP) && defined(LED_POLARITY) +static io_pin_t POWER_LED	= IO_PC(7); + +void power_set_led_ex(bool on, bool swap) +{ +	if (swap) +	{ +		if ((on == false) && (/*io_is_pin_set(CHARGE)*/_state.battery_charging))	// If charging and turning off, don't change charge light +		{ +			charge_set_led(true);	// Force it again just in case +			return; +		} +	}		 +	 +	io_clear_pin(CHARGE); +	io_enable_pin(POWER_LED, on); +} + +void power_set_led(bool on) +{ +	power_set_led_ex(on, true); +} +#endif // !ATTINY88_DIP && LED_POLARITY + +void charge_set_led_ex(bool on, bool swap) +{ +#ifdef ATTINY88_DIP +	// +#else + +#ifdef LED_POLARITY +	io_clear_pin(POWER_LED); +#endif // LED_POLARITY + +#endif // ATTINY88_DIP + +#ifdef ATTINY88_DIP +	io_enable_pin(CHARGE, !on); +#else +    io_enable_pin(CHARGE, on); + +#ifdef LED_POLARITY +	if (swap) +	{ +		if ((on == false) && (_state.powered))	// If no longer charging, turn power light back on +			power_set_led(true); +	}			 +#endif // LED_POLARITY + +#endif // ATTINY88_DIP +} + +void charge_set_led(bool on) +{ +	charge_set_led_ex(on, true); +} + +void charge_notify(bool charging) +{ +	_state.battery_charging = charging; +	 +	charge_set_led(charging); +} + +/////////////////////////////////////////////////////////////////////////////// + +void power_signal_interrupt(void) +{ +    io_set_pin(AVR_IRQ);	// FIXME: Active low? +} + +/////////////////////////////////////////////////////////////////////////////// + +#if !defined(DEBUG) && !(defined(ENABLE_SERIAL) && defined(ATTINY88_DIP)) +static io_pin_t PS_POR      = IO_PD(6); +#define PS_POR_AVAILABLE +#endif // DEBUG +static io_pin_t PS_SRST     = IO_PD(7); + +#define FPGA_RESET_DELAY    10  // ms   // MAGIC + +void fpga_reset(bool delay) +{ +#ifdef PS_POR_AVAILABLE +    io_clear_pin(PS_POR); +#endif // PS_POR_AVAILABLE +    io_clear_pin(PS_SRST); + +    if (delay) +        _delay_ms(FPGA_RESET_DELAY); +#ifdef PS_POR_AVAILABLE +    io_enable_pin(PS_POR, true); +#endif // PS_POR_AVAILABLE +    io_enable_pin(PS_SRST, true); +} + +/////////////////////////////////////////////////////////////////////////////// + +static io_pin_t VBAT        = IO_PC(0); + +void battery_init(void) +{ +    //io_input_pin(VBAT); +    DIDR0 |= 0x1;           // Digital input disable PC0 (ADC0) + +    ADMUX = (1 << REFS0)    // AVcc reference +          | (0 << ADLAR)    // Left-aligned result +          | (0 << MUX0);    // ADC0 + +    ADCSRA = (0x7 << ADPS0);// Prescale clock by 128 +} + +uint16_t battery_get_voltage(void) +{ +    // Vout = (357k / (274k + 357k)) * Vbat +    // Vbat = (Vout * (274k + 357k)) / 357k + +    // ADC = (Vin * 1024) / Vref +    // Vin = (ADC * Vref) / 1024 +    // Vref = 3.3 + +    // Vbat(mV) = 1000 * (((ADC * 3.3) / 1024) * (274k + 357k)) / 357k +    // Vbat(mV) ~= ADC * 5.70 + +    ADCSRA |= (1 << ADEN);        // FIXME: Turn on ADC (or leave on all the time?) + +    ADCSRA |= (1 << ADSC);  // Start conversion + +    while (ADCSRA & (1 << ADSC));   // Wait for End of Conversion + +    /*uint16_t*/uint32_t voltage = (ADCH << 8) | (ADCL << 0); +#ifdef ATTINY88_DIP +	voltage = (voltage * 32227) / 10000;	// ~3.22265625 +#else +    voltage = (voltage * 56961) / 10000;	// ~5.69606748 +#endif // ATTINY88_DIP +    ADCSRA &= ~(1 << ADEN);         // FIXME: Turn off ADC (or leave on all the time?) + +    return (uint16_t)voltage; +} + +/////////////////////////////////////////////////////////////////////////////// + +void blink_error_sequence(uint8_t len) +{ +    charge_set_led(false); +    _delay_ms(BLINK_ERROR_DELAY * 2); + +    for (; len > 0; len--) { +        charge_set_led(true); +        _delay_ms(BLINK_ERROR_DELAY); +        charge_set_led(false); +        _delay_ms(BLINK_ERROR_DELAY); +    } + +    //for (len = 2; len > 0; len--)   // Could have *2 on delay, but so as never to overflow 8-bit argument +    //    _delay_ms(BLINK_ERROR_DELAY); +} + +typedef struct power_params { +    power_subsystem_t subsys; +    bool enable; +    uint8_t retry; +    //uint16_t opaque; +} power_params_t; + +static bool _power_up_fpga(power_params_t* params) +{ +    if (params->subsys != PS_FPGA) +        return false; + +    if (params->enable == false) +    { +        //if (tps54478_is_power_good() == false)  // Already off +		//	return true; + +        if (params->retry == 0) +		{ +			io_clear_pin(PS_SRST);	// FIXME: Hold it low to stop +#ifdef PS_POR_AVAILABLE +			io_clear_pin(PS_POR);	// Prepare it for shutdown, and then the potential next power cycle +#endif // PS_POR_AVAILABLE			 +            tps54478_set_power(false); +		} + +        //return (tps54478_is_power_good() == false); +		return true; +    } + +    //bool fpga_power_good = tps54478_is_power_good();  // TODO: Can it ever already be good? + +    if (params->retry == 0) +        tps54478_set_power(true); + +    return tps54478_is_power_good(); +} + +static bool _power_up_reg(power_params_t* params) +{ +    if ((params->subsys > PS_TX) || (params->subsys < PS_VDRAM)) +        return false; + +    struct reg_config* cfg = default_reg_config + params->subsys; + +    if (params->enable == false) +        return ltc3675_enable_reg(cfg->address, false); + +	if (cfg->voltage > 0) +	{ +		if (ltc3675_set_voltage(cfg->address, cfg->voltage) == false) +			return false; +	}	 + +    return ltc3675_enable_reg(cfg->address, true); +} + +static bool _power_enable_subsys(power_params_t* params) +{ +    switch (params->subsys) +    { +        case PS_FPGA: +            return _power_up_fpga(params); +        //case PS_: +        //    break; +        default: +            return _power_up_reg(params); +    } + +    return false;   // Should never get here +} + +bool power_enable(power_subsystem_t subsys, bool on) +{ +    power_params_t params; +    ZERO_MEMORY(params); +    params.subsys = subsys; +    params.enable = on; + +    return _power_enable_subsys(¶ms); +} + +typedef bool (*boot_function_t)(power_params_t*); + +struct boot_step { +    power_subsystem_t subsys; +	//boot_function_t fn; +	//uint8_t delay; +	//uint8_t retries; +    //uint16_t opaque; +	//bool powered; +} boot_steps[] = {  // MAGIC: Retries/delays +	{ PS_FPGA,				/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 7..8						// 3..4 +	{ PS_VDRAM,				/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 9..10					// 5..6 +	{ PS_PERIPHERALS_1_8,	/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 11..12					// 7..8 +	{ PS_PERIPHERALS_3_3,	/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ },	// 13..14					// 9..10 +	{ PS_TX,				/*NULL, POWER_DEFAULT_DELAY, POWER_DEFAULT_RETRIES*/ }  // CHECK: Leaving TX off +}; +/* +bool power_is_subsys_on(int8_t index) +{ +	if ((index < 0) || (index >= ARRAY_SIZE(boot_steps))) +		return false; +	 +	struct boot_step* step = boot_steps + index; +	 +	return step->powered; +} +*/ +bool power_init(void) +{ +	io_output_pin(CHARGE); +#ifdef LED_POLARITY +    io_output_pin(POWER_LED); +#endif // LED_POLARITY + +	charge_set_led(true); +	 +	battery_init(); +	 +    tps54478_init(true);	// Will keep EN float (keep power on) +#ifndef I2C_REWORK +	i2c_init(PWR_SDA, PWR_SCL); +	 + +	 +#endif // I2C_REWORK +	io_input_pin(USB_RESETn);   +	io_output_pin(FTDI_RESETn);  +	io_output_pin(USB_CLK_EN); +	io_input_pin(FTDI_CBUS3); +	 +#ifdef CHARGER_TI +	if (bq24190_init(true) == false) +		return false; +#else +	if (ltc4155_init(/*_state.battery_not_present*/true/*false*/) == false) +		return false; +#endif // CHARGER_TI +#ifdef CHARGER_TI +	_delay_ms(1000);	// Still at 1.4V on dev board +#else +	_delay_ms(25);	// Wait for charge current to stop (Vbatt to fall to 0V) +#endif // CHARGER_TI +	uint16_t batt_voltage = battery_get_voltage(); +	debug_log_ex("Vb ", false); +	debug_log_byte((uint8_t)(batt_voltage / 100)); +	//debug_log_hex_ex(batt_voltage >> 8, false); +	//debug_log_hex(batt_voltage & 0xFF); +	if (batt_voltage < BATT_MIN_VOLTAGE) +	{ +		_state.battery_not_present = true; +		 +		//debug_log("NoBatt"); +	} +	else +	{ +#ifdef CHARGER_TI +		bq24190_toggle_charger(true); +#else +		ltc4155_set_charge_current_limit(50); +#endif // CHARGER_TI +	} + +    if (ltc3675_init(ltc3675_reg_helper) == false) +		return false; +#ifdef PS_POR_AVAILABLE +	io_output_pin(PS_POR); +#endif // PS_POR_AVAILABLE +    io_output_pin(PS_SRST); +    // Hold low until power is stable +#ifdef PS_POR_AVAILABLE +    io_clear_pin(PS_POR); +#endif // PS_POR_AVAILABLE +    io_clear_pin(PS_SRST); +/* +    AVR_CS +    AVR_MOSI +    AVR_MISO +    AVR_SCK + +    FTDI_BCD +    FTDI_PWREN2 +*/ +	io_input_pin(AVR_RESET);	// Has external pull-up (won't do anything because this is configured at the hardware RESET pin) +	 +	//io_output_pin(AVR_IRQ);		// Output here, input to FPGA +    io_input_pin(AVR_IRQ); +	//io_set_pin(AVR_IRQ);	// FIXME: Active low? +	 +	/////////////// +	 +	EICRA = _BV(ISC01) | _BV(ISC00) | _BV(ISC10)/* | _BV(ISC11)*/;	// Rising edge for INT0 (WAKEUP). [Falling for INT1.] Any logical change INT1 (ONSWITCH_DB) +	//EIMSK = _BV(INT0);	// [Turn on WAKEUP interrupt] Don't do this, as unit will turn on anyway +	EIMSK = _BV(INT1) | _BV(INT0);	// Turn on ONSWITCH_DB and WAKEUP +	 +	PCMSK0 = _BV(PCINT1) | _BV(PCINT0);	// USBPM_IRQ | CORE_PGOOD +	PCMSK2 = _BV(PCINT16)/* | _BV(PCINT20)*/;	// PWR_IRQ/* | PWR_RESET*/ +	PCICR = _BV(PCIE2) | _BV(PCIE0); + +	/////////////// +/* +	TCNT0; +	OCR0A = 0x; +	TCCR0A = _BV(CTC0); +	TIFR0; +	TIMSK0; +	TCCR0A |= 0x05;	// Switch on with 1024 prescaler +*/ +	TCCR1B = _BV(WGM12);	// CTC mode +	OCR1A = 15624 * 2;		// Hold button for 2 seconds to switch off +	TIMSK1 = _BV(OCIE1A);	// Enable CTC on Timer 1 +	 +	charge_set_led(false); +	 +	return true; +} + +bool power_on(void) +{ +	pmc_mask_irqs(true); +	 +    //charge_set_led(false); +	 +	bool last_power_led_state = /*true*/false; +	 +	//if ((ARRAY_SIZE(boot_steps) % 2) == 0)	// Should end with 'true' +	//	last_power_led_state = false; +	 +	power_set_led(last_power_led_state); +	 +	fpga_reset(true); +	 +	uint8_t step_count, retry; +	for (step_count = 0; step_count < ARRAY_SIZE(boot_steps); step_count++) +	{ +		last_power_led_state = !last_power_led_state; +		power_set_led(last_power_led_state); +		 +//		debug_blink(step_count); +//		debug_blink(3 + (step_count * 2) + 0); +//		debug_blink_rev(7 + (step_count * 2) + 0); +		 +	    struct boot_step* step = boot_steps + step_count; +	    if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN)) +            continue; +		 +		debug_log_ex("PWR ", false); +		debug_log_byte_ex(step->subsys, true); + +        power_params_t params; + +	    for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++) +		{ +	        ZERO_MEMORY(params); +            params.subsys = step->subsys; +            params.enable = true; +	        params.retry = retry; + +	        if ((/*(step->fn != NULL) && (step->fn(¶ms))) || +				((step->fn == NULL) && */(_power_enable_subsys(¶ms)))) +			{ +				//step->powered = true; +				default_reg_config[step->subsys].powered = true; +				 +				debug_log("+"); +//				debug_blink(3 + (step_count * 2) + 1); +//				debug_blink_rev(7 + (step_count * 2) + 1); + +//ltc4155_dump(); + +                break; +	        } +			 +			debug_log("?"); + +            if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/) +                _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY); +	    } +		 +//		debug_blink(step_count); + +	    if (retry == /*step->retries*/POWER_DEFAULT_RETRIES) +	        break; +    } + +    if (step_count != ARRAY_SIZE(boot_steps)) +	{ +		debug_log("x"); +		 +		//sei();	// For button press detection + +        /*while (_state.powered == false) { +            blink_error_sequence(step_count + BlinkError_FPGA_Power); +        }*/ +		pmc_set_blink_error(step_count + BlinkError_FPGA_Power); + +		pmc_mask_irqs(false); + +        return false; +    } +	 +	/////////////////////////////////// +	 +	io_set_pin(USB_CLK_EN); +	_delay_ms(200); +	io_set_pin(FTDI_RESETn); +	fpga_reset(false);  // Power has been brought up, so let FPGA run +	_delay_ms(100); +	 +	/////////////////////////////////// +	 +//	ltc4155_dump(); +	 +	// Turn off WAKEUP interrupt, enable ONSWITCH_DB +	//EIMSK = _BV(INT1); +	 +	_state.powered = true; +//debug_blink_rev(1); +//_delay_ms(1000);	// Wait for FPGA PGOOD to stabilise +	pmc_mask_irqs(false); +	 +	power_set_led(true); +	 +	if (_state.battery_charging) +	{ +		_delay_ms(500*2); +		charge_set_led(true); +	}		 +	 +    return true; +} + +uint8_t power_off(void) +{ +	pmc_mask_irqs(true); +	 +	io_clear_pin(PS_SRST);	// FIXME: Hold it low to stop FPGA running +	 +	fpga_reset(true); +	io_clear_pin(USB_CLK_EN); +	 +	bool last_power_led_state = /*false*/true; +	 +	//if ((ARRAY_SIZE(boot_steps) % 2) == 0)	// Should end with 'false' +	//	last_power_led_state = true; +	 +	//power_set_led(last_power_led_state); +	 +	/////////////////////////////////// +	 +	int8_t step_count, retry; +	for (step_count = ARRAY_SIZE(boot_steps) - 1; step_count >= 0; step_count--) +	{ +		last_power_led_state = !last_power_led_state; +		power_set_led(last_power_led_state); +		 +//		debug_blink(step_count); +		 +	    struct boot_step* step = boot_steps + step_count; +	    if (/*(step->fn == NULL) && */(step->subsys == PS_UNKNOWN)) +            continue; + +        power_params_t params; + +	    for (retry = 0; retry < /*step->retries*/POWER_DEFAULT_RETRIES; retry++) +		{ +	        ZERO_MEMORY(params); +            params.subsys = step->subsys; +            params.enable = false; +	        params.retry = retry; +			 +			if ((/*(step->fn != NULL) && (step->fn(¶ms))) || +				((step->fn == NULL) && */(_power_enable_subsys(¶ms)))) +			{ +				//step->powered = false; +				default_reg_config[step->subsys].powered = false; +				break; +			} +			 +            if ((retry < /*step->retries*/POWER_DEFAULT_RETRIES)/* && (step->delay > 0)*/) +                _delay_ms(/*step->delay*/POWER_DEFAULT_DELAY); +	    } +		 +//		debug_blink(step_count); + +	    if (retry == /*step->retries*/POWER_DEFAULT_RETRIES) +	        break; +    } + +    if (step_count != -1) +	{ +		/*pmc_mask_irqs(false); +		 +        while (_state.powered) { +            blink_error_sequence(step_count + BlinkError_FPGA_Power); +        }*/ +		if (pmc_get_blink_error() == BlinkError_None)	// Only set blink error if no existing error +			pmc_set_blink_error(step_count + BlinkError_FPGA_Power); +		 +		pmc_mask_irqs(false); + +        return (step_count + 1); +    } +	 +	/////////////////////////////////// +	 +	// Turn off WAKEUP interrupt, enable ONSWITCH_DB +	//EIMSK = _BV(INT1); + +	_state.powered = false; +	 +	pmc_mask_irqs(false); +	 +	power_set_led_ex(false, false); +	_delay_ms(500*2); +	 +	power_set_led(false);	// Will turn on charger LED if battery is charging +	 +	return 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +#ifdef DEBUG + +#ifdef ATTINY88_DIP +static io_pin_t DEBUG_1 = IO_PB(6); +static io_pin_t DEBUG_2	= IO_PB(7); +#endif // ATTINY88_DIP + +#endif // DEBUG + +ISR(INT0_vect)	// PD(2) WAKEUP: Rising edge +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	//power_on(); +	debug_log("\nINT0\n"); +	_state.wake_up = true; +	 +	//sei(); +	pmc_mask_irqs(false); +} + +ISR(INT1_vect)	// PD(3) ONSWITCH_DB (PB_STAT): Any change +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	if (ltc3675_is_power_button_depressed()) +	{ +		debug_log("PWRBTN+"); +		 +		TCNT1 = 0; +		if ((TCCR1B & 0x07) == 0x00) +		{ +			_state.active_timers++; +			debug_log("TIMER1+"); +		} +		TCCR1B |= /*0x5*/0x3;	// [1024] 64 prescaler +		//_state.timers_running = true; +		 +		//debug_set(DEBUG_1, true); +		//debug_set(DEBUG_2, false); +	} +	else +	{ +		debug_log("PWRBTN-"); +		 +		//if (TIMSK1 & _BV(OCIE1A))	// If letting go of button and still running, stop timer +		{ +			//TIMSK1 &= ~_BV(OCIE1A); +			if ((TCCR1B & 0x07) != 0x00) +			{ +				_state.active_timers--; +				debug_log("TIMER1-"); +			} +			TCCR1B &= ~0x7;	// Disable timer +			//_state.timers_running = false; +			 +			//debug_set(DEBUG_1, false); +		} +	} +	 +	//sei(); +	pmc_mask_irqs(false); +} + +ISR(TIMER1_COMPA_vect) +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	debug_log("TIMER1"); + +	//TIMSK1 &= ~_BV(OCIE1A);	// Turn off timer +	TCCR1B &= ~0x7;	// Disable timer +	//_state.timers_running = false; +	_state.active_timers--; +	 +	if (_state.powered) +	{ +		debug_log("PWROFF"); +		 +		_state.power_off = true; +	}		 +	 +	//debug_set(DEBUG_2, true); +	 +	//power_off(); +	 +	//sei(); +	pmc_mask_irqs(false); +	 +	//sleep_mode(); +} + +ISR(PCINT0_vect) +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	//debug_log("PCINT0"); +	 +	// CORE_PGOOD +	//	Assert low: power problem -> shutdown +	// USBPM_IRQ +	//	Charge status change? -> update LED +	//	Power problem:	battery -> blink charge LED +	//					major -> shutdown +	 +	if (/*(_state.powered) && */(/*io_test_pin(CORE_PGOOD)*/tps54478_is_power_good() == false)) +	{ +		_state.core_power_bad = true; +	} +#ifdef CHARGER_TI +	if (bq24190_has_interrupt()) +	{ +		_state.bq24190_irq = true; +	} +#else +	if (ltc4155_has_interrupt()) +	{ +		_state.ltc4155_irq = true; +	} +#endif // CHARGER_TI +	//sei(); +	pmc_mask_irqs(false); +} + +ISR(PCINT2_vect) +{ +	//cli(); +	pmc_mask_irqs(true); +	 +	//debug_log("PCINT2"); +	 +	// PWR_IRQ +	//	Regulator problem: shutdown +	// PWR_RESET +	//	Ignored +	 +	if (ltc3675_has_interrupt()) +	{ +		//debug_set(IO_PB(6), true); +		_state.ltc3675_irq = true; +	} +	 +	//sei(); +	pmc_mask_irqs(false); +} diff --git a/firmware/e300/rev_c/power.h b/firmware/e300/rev_c/power.h new file mode 100644 index 000000000..453633414 --- /dev/null +++ b/firmware/e300/rev_c/power.h @@ -0,0 +1,58 @@ +#ifndef POWER_H +#define POWER_H + +#include <stdbool.h> +#include <stdint.h> + +void tps54478_init(bool enable); +void tps54478_set_power(bool on);   // Zynq core power (1.0V for FPGA) +bool tps54478_is_power_good(void); + +void charge_set_led(bool on);		// Here for error blink codes +void charge_notify(bool charging); + +void power_signal_interrupt(void); + +void fpga_reset(bool delay); + +typedef enum power_subsystems { +    PS_UNKNOWN, +    PS_FPGA, +    PS_VDRAM, +    PS_PERIPHERALS_1_8, +    PS_PERIPHERALS_3_3, +    PS_TX, +	PS_MAX +} power_subsystem_t; + +enum Regulators +{ +	REG_UNKNOWN, +	REG_TPS54478, +	REG_LTC3675 +}; + +bool power_enable(power_subsystem_t subsys, bool on); + +void battery_init(void); +uint16_t battery_get_voltage(void);  // mV + +bool power_init(void); +bool power_on(void); +uint8_t power_off(void); + +//bool power_is_subsys_on(int8_t index); +bool power_is_subsys_on(power_subsystem_t index); +//int8_t power_get_regulator_index(uint8_t device, uint8_t address); +//bool ltc3675_reg_helper(uint8_t address); + +void usbhub_reset(void); + +#ifndef I2C_REWORK +#include "io.h" + +extern io_pin_t PWR_SDA; +extern io_pin_t PWR_SCL; +#endif // I2C_REWORK + +#endif // POWER_H | 
