commit 0e8e1e4db65166f51c07112247033757027dc22a Author: Janosch Date: Mon Oct 10 11:46:30 2022 +0100 initial commit for tests diff --git a/include/anain_prj.h b/include/anain_prj.h new file mode 100644 index 0000000..058bda9 --- /dev/null +++ b/include/anain_prj.h @@ -0,0 +1,20 @@ +#ifndef ANAIN_PRJ_H_INCLUDED +#define ANAIN_PRJ_H_INCLUDED + +#include "hwdefs.h" + +/* Here we specify how many samples are combined into one filtered result. Following values are possible: +* - NUM_SAMPLES = 1: Most recent raw value is returned +* - NUM_SAMPLES = 3: Median of last 3 values is returned +* - NUM_SAMPLES = 9: Median of last 3 medians is returned +* - NUM_SAMPLES = 12: Average of last 4 medians is returned +*/ +#define NUM_SAMPLES 12 +#define SAMPLE_TIME ADC_SMPR_SMP_239DOT5CYC //Sample&Hold time for each pin. Increases sample time, might increase accuracy + +//Here you specify a list of analog inputs, see main.cpp on how to use them +#define ANA_IN_LIST \ + ANA_IN_ENTRY(cablelim, GPIOA, 0) \ + ANA_IN_ENTRY(uaux, GPIOA, 5) \ + +#endif // ANAIN_PRJ_H_INCLUDED diff --git a/include/chargercan.h b/include/chargercan.h new file mode 100644 index 0000000..441ec84 --- /dev/null +++ b/include/chargercan.h @@ -0,0 +1,33 @@ +/* + * This file is part of the tumanako_vc project. + * + * Copyright (C) 2018 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef CHARGERCAN_H +#define CHARGERCAN_H +#include "stm32_can.h" + +class ChargerCAN +{ + public: + static void MapMessages(Can* can); + + protected: + + private: +}; + +#endif // CHARGERCAN_H diff --git a/include/digio_prj.h b/include/digio_prj.h new file mode 100644 index 0000000..7ac7a13 --- /dev/null +++ b/include/digio_prj.h @@ -0,0 +1,26 @@ +#ifndef PinMode_PRJ_H_INCLUDED +#define PinMode_PRJ_H_INCLUDED + +#include "hwdefs.h" + +/* Here you specify generic IO pins, i.e. digital input or outputs. + * Inputs can be floating (INPUT_FLT), have a 30k pull-up (INPUT_PU) + * or pull-down (INPUT_PD) or be an output (OUTPUT) +*/ + +#define DIG_IO_LIST \ + DIG_IO_ENTRY(enable_in, GPIOB, GPIO3, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(threep_in, GPIOB, GPIO4, PinMode::INPUT_FLT) \ + DIG_IO_ENTRY(led_out, GPIOC, GPIO13, PinMode::OUTPUT) \ + DIG_IO_ENTRY(hvena_out, GPIOA, GPIO2, PinMode::OUTPUT) \ + DIG_IO_ENTRY(acpres_out, GPIOA, GPIO3, PinMode::OUTPUT) \ + DIG_IO_ENTRY(d3_out, GPIOA, GPIO4, PinMode::OUTPUT) \ + DIG_IO_ENTRY(evseact_out, GPIOB, GPIO15, PinMode::OUTPUT) \ + DIG_IO_ENTRY(ch1ena_out, GPIOB, GPIO14, PinMode::OUTPUT) \ + DIG_IO_ENTRY(ch2ena_out, GPIOB, GPIO13, PinMode::OUTPUT) \ + DIG_IO_ENTRY(ch3ena_out, GPIOB, GPIO12, PinMode::OUTPUT) \ + DIG_IO_ENTRY(ch1act_out, GPIOB, GPIO5, PinMode::OUTPUT) \ + DIG_IO_ENTRY(ch2act_out, GPIOB, GPIO6, PinMode::OUTPUT) \ + DIG_IO_ENTRY(ch3act_out, GPIOB, GPIO7, PinMode::OUTPUT) \ + +#endif // PinMode_PRJ_H_INCLUDED diff --git a/include/errormessage_prj.h b/include/errormessage_prj.h new file mode 100644 index 0000000..bab3b9e --- /dev/null +++ b/include/errormessage_prj.h @@ -0,0 +1,11 @@ +#ifndef ERRORMESSAGE_PRJ_H_INCLUDED +#define ERRORMESSAGE_PRJ_H_INCLUDED + +#define ERROR_BUF_SIZE 4 + +#define ERROR_MESSAGE_LIST \ + ERROR_MESSAGE_ENTRY(CHARGERCAN, ERROR_STOP) \ + ERROR_MESSAGE_ENTRY(CHARGERFAULT, ERROR_STOP) \ + ERROR_MESSAGE_ENTRY(EXTCAN, ERROR_STOP) \ + +#endif // ERRORMESSAGE_PRJ_H_INCLUDED diff --git a/include/functions.h b/include/functions.h new file mode 100644 index 0000000..d7003c9 --- /dev/null +++ b/include/functions.h @@ -0,0 +1,15 @@ +#include "../libopeninv/include/params.h" + +static bool IsEvseInput(); +static void DisableAll(); + +static bool IsEvseInput() +{ + enum inputs input = (enum inputs)Param::GetInt(Param::inputype); + return input == INP_TYPE1 || input == INP_TYPE2 || input == INP_TYPE2_3P || input == INP_TYPE2_AUTO; +} + +static bool CheckUnplugged() +{ + return IsEvseInput() && !Param::GetBool(Param::proximity); +} diff --git a/include/hwdefs.h b/include/hwdefs.h new file mode 100644 index 0000000..fac71ba --- /dev/null +++ b/include/hwdefs.h @@ -0,0 +1,14 @@ +#ifndef HWDEFS_H_INCLUDED +#define HWDEFS_H_INCLUDED + + +#define RCC_CLOCK_SETUP rcc_clock_setup_in_hse_8mhz_out_72mhz + +//Address of parameter block in flash +#define FLASH_PAGE_SIZE 1024 +#define PARAM_BLKSIZE FLASH_PAGE_SIZE +#define PARAM_BLKNUM 1 //last block of 1k +#define CAN1_BLKNUM 2 +#define CAN2_BLKNUM 4 + +#endif // HWDEFS_H_INCLUDED diff --git a/include/hwinit.h b/include/hwinit.h new file mode 100644 index 0000000..5645dc0 --- /dev/null +++ b/include/hwinit.h @@ -0,0 +1,40 @@ +/* + * This file is part of the stm32-template project. + * + * Copyright (C) 2020 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HWINIT_H_INCLUDED +#define HWINIT_H_INCLUDED + + +#ifdef __cplusplus +extern "C" +{ +#endif + +void clock_setup(void); +void usart_setup(void); +void nvic_setup(void); +void rtc_setup(void); +void tim_setup(void); +void write_bootloader_pininit(); + +#ifdef __cplusplus +} +#endif + +#endif // HWINIT_H_INCLUDED diff --git a/include/param_prj.h b/include/param_prj.h new file mode 100644 index 0000000..abd3edd --- /dev/null +++ b/include/param_prj.h @@ -0,0 +1,172 @@ +/* + * This file is part of the stm32-template project. + * + * Copyright (C) 2020 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file contains all parameters used in your project + * See main.cpp on how to access them. + * If a parameters unit is of format "0=Choice, 1=AnotherChoice" etc. + * It will be displayed as a dropdown in the web interface + * If it is a spot value, the decimal is translated to the name, i.e. 0 becomes "Choice" + * If the enum values are powers of two, they will be displayed as flags, example + * "0=None, 1=Flag1, 2=Flag2, 4=Flag3, 8=Flag4" and the value is 5. + * It means that Flag1 and Flag3 are active -> Display "Flag1 | Flag3" + * + * Every parameter/value has a unique ID that must never change. This is used when loading parameters + * from flash, so even across firmware versions saved parameters in flash can always be mapped + * back to our list here. If a new value is added, it will receive its default value + * because it will not be found in flash. + * The unique ID is also used in the CAN module, to be able to recover the CAN map + * no matter which firmware version saved it to flash. + * Make sure to keep track of your ids and avoid duplicates. Also don't re-assign + * IDs from deleted parameters because you will end up loading some random value + * into your new parameter! + * IDs are 16 bit, so 65535 is the maximum + */ + +//Define a version string of your firmware here +#define VER 1.17.R + +/* Entries must be ordered as follows: + 1. Saveable parameters (id != 0) + 2. Temporary parameters (id = 0) + 3. Display values + */ +//Next param id (increase when adding new parameter!): 23 +//Next value Id: 2051 +/* category name unit min max default id */ +#define PARAM_LIST \ + PARAM_ENTRY(CAT_CHARGER, idclim, "A", 0, 45, 45, 3 ) \ + PARAM_ENTRY(CAT_CHARGER, iaclim, "A", 0, 72, 16, 10 ) \ + PARAM_ENTRY(CAT_CHARGER, idcspnt, "A", 0, 45, 45, 9 ) \ + PARAM_ENTRY(CAT_CHARGER, chargerena, CHARGERS, 1, 7, 7, 4 ) \ + PARAM_ENTRY(CAT_CHARGER, udcspnt, "V", 50, 420, 403, 5 ) \ + PARAM_ENTRY(CAT_CHARGER, udclim, "V", 50, 420, 398, 6 ) \ + PARAM_ENTRY(CAT_CHARGER, timelim, "minutes", -1, 10000, -1, 16 ) \ + PARAM_ENTRY(CAT_CHARGER, timedly, "minutes", -1, 10000, -1, 22 ) \ + PARAM_ENTRY(CAT_CHARGER, inputype, INPUTS, 0, 5, 1, 17 ) \ + PARAM_ENTRY(CAT_CHARGER, cancontrol, OFFON, 0, 1, 0, 14 ) \ + PARAM_ENTRY(CAT_CHARGER, enablepol, POLARITIES,0, 1, 0, 18 ) \ + PARAM_ENTRY(CAT_CHARGER, idckp, "", 0, 10000, 1, 20 ) \ + PARAM_ENTRY(CAT_CHARGER, idcki, "", 0, 10000, 10, 21 ) \ + VALUE_ENTRY(state, STATES, 2043 ) \ + VALUE_ENTRY(uptime, "s", 2048 ) \ + VALUE_ENTRY(lasterr, errorListString, 2002 ) \ + VALUE_ENTRY(uaux, "V", 2049 ) \ + VALUE_ENTRY(aclim, "A", 2042 ) \ + VALUE_ENTRY(cablelim, "A", 2038 ) \ + VALUE_ENTRY(evselim, "A", 2039 ) \ + VALUE_ENTRY(idc, "A", 2041 ) \ + VALUE_ENTRY(udc, "V", 2047 ) \ + VALUE_ENTRY(soc, "%", 2046 ) \ + VALUE_ENTRY(proximity, OFFON, 2040 ) \ + VALUE_ENTRY(enable, OFFON, 2005 ) \ + VALUE_ENTRY(canenable, OFFON, 2045 ) \ + VALUE_ENTRY(cpuload, "%", 2004 ) \ + VALUE_ENTRY(version, VERSTR, 2001 ) \ + VALUE_ENTRY(opmode, OPMODES, 2000 ) \ + VALUE_ENTRY(hwaclim, "A", 2050 ) \ + VALUE_ENTRY(c1stt, "", 2007 ) \ + VALUE_ENTRY(c1flag, CHFLAGS, 2008 ) \ + VALUE_ENTRY(c1tmp1, "°C", 2010 ) \ + VALUE_ENTRY(c1tmp2, "°C", 2011 ) \ + VALUE_ENTRY(c1tmpin, "°C", 2012 ) \ + VALUE_ENTRY(c1uac, "V", 2013 ) \ + VALUE_ENTRY(c1iac, "A", 2014 ) \ + VALUE_ENTRY(c1udc, "V", 2015 ) \ + VALUE_ENTRY(c1idc, "A", 2016 ) \ + VALUE_ENTRY(c2stt, "", 2017 ) \ + VALUE_ENTRY(c2flag, CHFLAGS, 2018 ) \ + VALUE_ENTRY(c2tmp1, "°C", 2020 ) \ + VALUE_ENTRY(c2tmp2, "°C", 2021 ) \ + VALUE_ENTRY(c2tmpin, "°C", 2022 ) \ + VALUE_ENTRY(c2uac, "V", 2023 ) \ + VALUE_ENTRY(c2iac, "A", 2024 ) \ + VALUE_ENTRY(c2udc, "V", 2025 ) \ + VALUE_ENTRY(c2idc, "A", 2026 ) \ + VALUE_ENTRY(c3stt, "", 2027 ) \ + VALUE_ENTRY(c3flag, CHFLAGS, 2028 ) \ + VALUE_ENTRY(c3tmp1, "°C", 2030 ) \ + VALUE_ENTRY(c3tmp2, "°C", 2031 ) \ + VALUE_ENTRY(c3tmpin, "°C", 2032 ) \ + VALUE_ENTRY(c3uac, "V", 2033 ) \ + VALUE_ENTRY(c3iac, "A", 2034 ) \ + VALUE_ENTRY(c3udc, "V", 2035 ) \ + VALUE_ENTRY(c3idc, "A", 2036 ) \ + + +/***** Enum String definitions *****/ +#define OPMODES "0=Off, 1=Run" +#define CHARGERS "1=Charger1, 2=Charger2, 4=Charger3" +#define OFFON "0=Off, 1=On" +#define CHFLAGS "0=None, 1=Enabled, 2=Fault, 4=CheckAlive" +#define STATES "0=Off, 1=WaitStart, 2=Enable, 3=Activate, 4=Run, 5=Stop" +#define INPUTS "0=Type2, 1=Type2-3P, 2=Type1, 3=Manual, 4=Manual-3P, 5=Type2-Auto" +#define POLARITIES "0=ActiveHigh, 1=ActiveLow" +#define CAT_TEST "Testing" +#define CAT_CHARGER "Charger" +#define CAT_COMM "Communication" + +#define VERSTR STRINGIFY(4=VER) + +/***** enums ******/ + +enum inputs +{ + INP_TYPE2, + INP_TYPE2_3P, + INP_TYPE1, + INP_MANUAL, + INP_MANUAL_3P, + INP_TYPE2_AUTO +}; + +enum states +{ + OFF, + WAITSTART, + ENABLE, + ACTIVATE, + EVSEACTIVATE, + STOP +}; + +enum _canspeeds +{ + CAN_PERIOD_100MS = 0, + CAN_PERIOD_10MS, + CAN_PERIOD_LAST +}; + +enum _modes +{ + MOD_OFF = 0, + MOD_RUN, + MOD_LAST +}; + +enum _chflags +{ + FLAG_NONE = 0, + FLAG_ENABLED = 1, + FLAG_FAULT = 2, + FLAG_CHECK = 4 +}; + +//Generated enum-string for possible errors +extern const char* errorListString; + diff --git a/src/chargercan.cpp b/src/chargercan.cpp new file mode 100644 index 0000000..527c82c --- /dev/null +++ b/src/chargercan.cpp @@ -0,0 +1,102 @@ +/* + * This file is part of the stm32-... project. + * + * Copyright (C) 2021 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "chargercan.h" + +void ChargerCAN::MapMessages(Can* can) +{ + can->AddRecv(Param::hwaclim, 0x207, 32, 9, 0.06666f); //gain 0.06666 + can->AddRecv(Param::hwaclim, 0x209, 32, 9, 0.06666f); //gain 0.06666 + can->AddRecv(Param::hwaclim, 0x20B, 32, 9, 0.06666f); //gain 0.06666 + can->AddRecv(Param::c1iac, 0x207, 41, 9, 0.06666f); //gain 0.06666 + can->AddRecv(Param::c2iac, 0x209, 41, 9, 0.06666f); //gain 0.06666 + can->AddRecv(Param::c3iac, 0x20B, 41, 9, 0.06666f); //gain 0.06666 + can->AddRecv(Param::c1uac, 0x207, 8, 8, 1); + can->AddRecv(Param::c2uac, 0x209, 8, 8, 1); + can->AddRecv(Param::c3uac, 0x20B, 8, 8, 1); + can->AddRecv(Param::c1flag, 0x207, 17, 2, 1); + can->AddRecv(Param::c2flag, 0x209, 17, 2, 1); + can->AddRecv(Param::c3flag, 0x20B, 17, 2, 1); + can->AddRecv(Param::c1stt, 0x217, 0, 8, 1); + can->AddRecv(Param::c2stt, 0x219, 0, 8, 1); + can->AddRecv(Param::c3stt, 0x21B, 0, 8, 1); + can->AddRecv(Param::c1idc, 0x227, 32, 16, 0.000839233f); //gain 0.000839233 + can->AddRecv(Param::c2idc, 0x229, 32, 16, 0.000839233f); //gain 0.000839233 + can->AddRecv(Param::c3idc, 0x22B, 32, 16, 0.000839233f); //gain 0.000839233 + can->AddRecv(Param::c1udc, 0x227, 16, 16, 0.01052856f); //gain 0.01052856 + can->AddRecv(Param::c2udc, 0x229, 16, 16, 0.01052856f); //gain 0.01052856 + can->AddRecv(Param::c3udc, 0x22B, 16, 16, 0.01052856f); //gain 0.01052856 + can->AddRecv(Param::c1tmp1, 0x237, 0, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c2tmp1, 0x239, 0, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c3tmp1, 0x23B, 0, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c1tmp2, 0x237, 8, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c2tmp2, 0x239, 8, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c3tmp2, 0x23B, 8, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c1tmpin, 0x237, 40, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c2tmpin, 0x239, 40, 8, 1, -40); //offset -40°C + can->AddRecv(Param::c3tmpin, 0x23B, 40, 8, 1, -40); //offset -40°C + //We don't have enough space for all messages, so we discard these + //can->AddRecv(Param::c1tmplim, 0x247, 0, 8, 7); //gain 0.234375 + //can->AddRecv(Param::c2tmplim, 0x249, 0, 8, 7); //gain 0.234375 + //can->AddRecv(Param::c3tmplim, 0x24B, 0, 8, 7); //gain 0.234375 + /***** CHAdeMO RX *****/ + can->AddRecv(Param::canenable, 0x102, 40, 1, 1); + can->AddRecv(Param::idcspnt, 0x102, 24, 8, 1); + can->AddRecv(Param::udclim, 0x102, 8, 16, 1); + can->AddRecv(Param::soc, 0x102, 48, 8, 1); + + /***** Charger TX ******/ + can->AddSend(Param::udcspnt, 0x45c, 0, 16, 100); //gain 100 + //Send 0xe14 when opmode is 0 and 0x2e14 when opmode=1 + can->AddSend(Param::opmode, 0x45c, 24, 8, 32, 0xe); //opmode is 1 when running + //constant value 0x14, version just used as dummy, multiplied by 0 + can->AddSend(Param::version, 0x45c, 16, 8, 0, 0x14); + //same as above + can->AddSend(Param::version, 0x45c, 48, 8, 0, (int8_t)0x90); + can->AddSend(Param::version, 0x45c, 56, 8, 0, (int8_t)0x8c); + + can->AddSend(Param::version, 0x42c, 0, 8, 0, 0x42); + can->AddSend(Param::version, 0x42c, 8, 8, 0, (int8_t)0xBB); //TODO: AC voltage + can->AddSend(Param::aclim, 0x42c, 16, 16, 1500); + can->AddSend(Param::opmode, 0x42c, 32, 8, 154, 100); + + can->AddSend(Param::version, 0x43c, 0, 8, 0, 0x42); + can->AddSend(Param::version, 0x43c, 8, 8, 0, (int8_t)0xBB); //TODO: AC voltage + can->AddSend(Param::aclim, 0x43c, 16, 16, 1500); + can->AddSend(Param::opmode, 0x43c, 32, 8, 154, 100); + + can->AddSend(Param::version, 0x44c, 0, 8, 0, 0x42); + can->AddSend(Param::version, 0x44c, 8, 8, 0, (int8_t)0xBB); //TODO: AC voltage + can->AddSend(Param::aclim, 0x44c, 16, 16, 1500); + can->AddSend(Param::opmode, 0x44c, 32, 8, 154, 100); + + can->AddSend(Param::version, 0x368, 0, 8, 0, 0x03); + can->AddSend(Param::version, 0x368, 8, 8, 0, 0x49); + can->AddSend(Param::version, 0x368, 16, 8, 0, 0x29); + can->AddSend(Param::version, 0x368, 24, 8, 0, 0x11); + can->AddSend(Param::version, 0x368, 40, 8, 0, 0x0c); + can->AddSend(Param::version, 0x368, 48, 8, 0, 0x40); + can->AddSend(Param::version, 0x368, 56, 8, 0, (int8_t)0xff); + + /***** CHAdeMO TX *****/ + can->AddSend(Param::version, 0x108, 8, 16, 107); //output 428V max = 4*107 + can->AddSend(Param::idclim, 0x108, 24, 8, 1); + can->AddSend(Param::udc, 0x109, 8, 16, 1); + can->AddSend(Param::idc, 0x109, 24, 16, 1); + can->AddSend(Param::opmode, 0x109, 40, 3, 5); //Set charging and connlock at once +} diff --git a/src/hwinit.cpp b/src/hwinit.cpp new file mode 100644 index 0000000..27d0653 --- /dev/null +++ b/src/hwinit.cpp @@ -0,0 +1,148 @@ +/* + * This file is part of the stm32-template project. + * + * Copyright (C) 2020 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hwdefs.h" +#include "hwinit.h" +#include "stm32_loader.h" +#include "my_string.h" + +/** +* Start clocks of all needed peripherals +*/ +void clock_setup(void) +{ + RCC_CLOCK_SETUP(); + + //The reset value for PRIGROUP (=0) is not actually a defined + //value. Explicitly set 16 preemtion priorities + SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_PRIGROUP_GROUP16_NOSUB; + + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOB); + rcc_periph_clock_enable(RCC_GPIOC); + rcc_periph_clock_enable(RCC_GPIOD); + rcc_periph_clock_enable(RCC_USART1); + rcc_periph_clock_enable(RCC_USART3); + rcc_periph_clock_enable(RCC_TIM2); //Scheduler + rcc_periph_clock_enable(RCC_TIM3); //Proximity PWM sense + rcc_periph_clock_enable(RCC_DMA1); //ADC and UART + rcc_periph_clock_enable(RCC_ADC1); + rcc_periph_clock_enable(RCC_CRC); + rcc_periph_clock_enable(RCC_AFIO); //CAN + rcc_periph_clock_enable(RCC_CAN1); //CAN +} + +/* Some pins should never be left floating at any time + * Since the bootloader delays firmware startup by a few 100ms + * We need to tell it which pins we want to initialize right + * after startup + */ +void write_bootloader_pininit() +{ + uint32_t flashSize = desig_get_flash_size(); + uint32_t pindefAddr = FLASH_BASE + flashSize * 1024 - PINDEF_BLKNUM * PINDEF_BLKSIZE; + const struct pincommands* flashCommands = (struct pincommands*)pindefAddr; + + struct pincommands commands; + + memset32((int*)&commands, 0, PINDEF_NUMWORDS); + + //!!! Customize this to match your project !!! + //Here we specify that PC13 be initialized to ON + //AND PB1 AND PB2 be initialized to OFF + commands.pindef[0].port = GPIOC; + commands.pindef[0].pin = GPIO13; + commands.pindef[0].inout = PIN_OUT; + commands.pindef[0].level = 1; + commands.pindef[1].port = GPIOB; + commands.pindef[1].pin = GPIO1 | GPIO2; + commands.pindef[1].inout = PIN_OUT; + commands.pindef[1].level = 0; + + crc_reset(); + uint32_t crc = crc_calculate_block(((uint32_t*)&commands), PINDEF_NUMWORDS); + commands.crc = crc; + + if (commands.crc != flashCommands->crc) + { + flash_unlock(); + flash_erase_page(pindefAddr); + + //Write flash including crc, therefor <= + for (uint32_t idx = 0; idx <= PINDEF_NUMWORDS; idx++) + { + uint32_t* pData = ((uint32_t*)&commands) + idx; + flash_program_word(pindefAddr + idx * sizeof(uint32_t), *pData); + } + flash_lock(); + } +} + +/** +* Enable Timer refresh and break interrupts +*/ +void nvic_setup(void) +{ + nvic_enable_irq(NVIC_TIM2_IRQ); //Scheduler + nvic_set_priority(NVIC_TIM2_IRQ, 0xe << 4); //second lowest priority +} + +void rtc_setup() +{ + //Base clock is HSE/128 = 8MHz/128 = 62.5kHz + //62.5kHz / (62499 + 1) = 1Hz + rtc_auto_awake(RCC_HSE, 62499); //1000ms tick + rtc_set_counter_val(0); +} + +/** +* Setup timer for measuring 1 Khz Pilot dutycycle +*/ +void tim_setup() +{ + timer_set_prescaler(TIM3, 71); //run at 1 MHz + timer_set_period(TIM3, 65535); + timer_direction_up(TIM3); + timer_slave_set_mode(TIM3, TIM_SMCR_SMS_RM); + timer_slave_set_polarity(TIM3, TIM_ET_FALLING); + timer_slave_set_trigger(TIM3, TIM_SMCR_TS_TI1FP1); + timer_ic_set_filter(TIM3, TIM_IC1, TIM_IC_DTF_DIV_32_N_8); + timer_ic_set_filter(TIM3, TIM_IC2, TIM_IC_DTF_DIV_32_N_8); + timer_ic_set_input(TIM3, TIM_IC1, TIM_IC_IN_TI1);//measures frequency + timer_ic_set_input(TIM3, TIM_IC2, TIM_IC_IN_TI1);//measure duty cycle + timer_set_oc_polarity_high(TIM3, TIM_OC1); + timer_set_oc_polarity_low(TIM3, TIM_OC2); + timer_ic_enable(TIM3, TIM_IC1); + timer_ic_enable(TIM3, TIM_IC2); + timer_generate_event(TIM3, TIM_EGR_UG); + timer_enable_counter(TIM3); +} + diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..ce2c34c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,530 @@ +/* + * This file is part of the stm32-template project. + * + * Copyright (C) 2020 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include +#include "stm32_can.h" +#include "terminal.h" +#include "params.h" +#include "hwdefs.h" +#include "digio.h" +#include "hwinit.h" +#include "anain.h" +#include "param_save.h" +#include "my_math.h" +#include "errormessage.h" +#include "printf.h" +#include "stm32scheduler.h" +#include "picontroller.h" +#include "chargercan.h" +#include "functions.h" + +static Stm32Scheduler* scheduler; +static Can* can; +static PiController dcCurController; +static uint32_t startTime; + +static void EvseRead() +{ + const int threshProxType1 = 2200; + const int threshProx = 3700; + const int thresh13A = 3200; + const int thresh20A = 2800; + const int thresh32A = 1800; + const int thresh63A = 1000; + int val = AnaIn::cablelim.Get(); + + if (timer_get_flag(TIM3, TIM_SR_CC2IF)) + { + //The relationship between duty cycle and maximum current is linear + //until 85% = 51A. Above that it becomes non-linear but that is not + //relevant for our 10kW charger. + float evselim = timer_get_ic_value(TIM3, TIM_IC2) / 10; + evselim *= 0.666666f; + Param::SetFloat(Param::evselim, evselim); + } + else + { + //If no PWM detected, set limit to 0 + Param::SetInt(Param::evselim, 0); + } + + if (Param::GetInt(Param::inputype) == INP_TYPE2 || + Param::GetInt(Param::inputype) == INP_TYPE2_3P || + Param::GetInt(Param::inputype) == INP_TYPE2_AUTO) + { + if (val > threshProx) + { + Param::SetInt(Param::proximity, 0); + Param::SetInt(Param::cablelim, 0); + } + else + { + Param::SetInt(Param::proximity, 1); + + if (val > thresh13A) + { + Param::SetInt(Param::cablelim, 13); + } + else if (val > thresh20A) + { + Param::SetInt(Param::cablelim, 20); + } + else if (val > thresh32A) + { + Param::SetInt(Param::cablelim, 32); + } + else if (val > thresh63A) + { + Param::SetInt(Param::cablelim, 63); + } + } + } + else if (Param::GetInt(Param::inputype) == INP_TYPE1) + { + if (val > threshProxType1) + { + Param::SetInt(Param::proximity, 0); + Param::SetInt(Param::cablelim, 0); + } + else + { + Param::SetInt(Param::proximity, 1); + Param::SetInt(Param::cablelim, 40); + } + } + else + { + Param::SetInt(Param::proximity, 0); + Param::SetInt(Param::cablelim, 32); + } +} + +static void DisableAll() +{ + DigIo::hvena_out.Clear(); + DigIo::acpres_out.Clear(); + DigIo::evseact_out.Clear(); + DigIo::ch1act_out.Clear(); + DigIo::ch2act_out.Clear(); + DigIo::ch2act_out.Clear(); + DigIo::ch1ena_out.Clear(); + DigIo::ch2ena_out.Clear(); + DigIo::ch3ena_out.Clear(); +} + + +static bool CheckStartCondition() +{ + return (IsEvseInput() && Param::GetBool(Param::proximity) && Param::Get(Param::cablelim) > FP_FROMFLT(1.4) && Param::GetBool(Param::enable)) || + (!IsEvseInput() && Param::GetBool(Param::enable)); +} + +static bool CheckVoltage() +{ + static int timeout = 0; + + if (Param::Get(Param::udc) > Param::Get(Param::udclim)) + { + timeout++; + } + else + { + timeout = 0; + } + + return timeout > 10; +} + +static bool CheckChargerFaults() +{ + const int acPresentThresh = 70; + const int timeout = 20; + static int counters[3] = { timeout, timeout, timeout }; + int configuredChargers = Param::GetInt(Param::chargerena); + bool timeouts[3]; + bool active1 = (configuredChargers & 1) && (Param::GetInt(Param::c1uac) > acPresentThresh); + bool active2 = (configuredChargers & 2) && (Param::GetInt(Param::c2uac) > acPresentThresh); + bool active3 = (configuredChargers & 4) && (Param::GetInt(Param::c3uac) > acPresentThresh); + + timeouts[0] = (Param::GetInt(Param::c1flag) & FLAG_CHECK) != 0; + timeouts[1] = (Param::GetInt(Param::c2flag) & FLAG_CHECK) != 0; + timeouts[2] = (Param::GetInt(Param::c3flag) & FLAG_CHECK) != 0; + + for (int i = 0; i < 3; i++) + { + if (timeouts[i]) + { + if (counters[i] > 0) + { + counters[i]--; + timeouts[i] = false; + } + else + { + ErrorMessage::Post(ERR_CHARGERCAN); + } + } + else + { + counters[i] = timeout; + } + } + + //Set check flag. By the next call this should be deleted by the CAN module + Param::SetInt(Param::c1flag, Param::GetInt(Param::c1flag) | FLAG_CHECK); + Param::SetInt(Param::c2flag, Param::GetInt(Param::c2flag) | FLAG_CHECK); + Param::SetInt(Param::c3flag, Param::GetInt(Param::c3flag) | FLAG_CHECK); + + return (active1 && ((Param::GetInt(Param::c1flag) & FLAG_FAULT) || timeouts[0])) || + (active2 && ((Param::GetInt(Param::c2flag) & FLAG_FAULT) || timeouts[1])) || + (active3 && ((Param::GetInt(Param::c3flag) & FLAG_FAULT) || timeouts[2])); +} + +static bool CheckUnplugged() +{ + return IsEvseInput() && !Param::GetBool(Param::proximity); +} + +static bool CheckTimeout() +{ + uint32_t now = rtc_get_counter_val(); + uint32_t timeout = Param::GetInt(Param::timelim); + + timeout *= 60; + + return timeout > 0 && (now - startTime) > timeout; +} + +static bool CheckDelay() +{ + uint32_t now = rtc_get_counter_val(); + uint32_t start = Param::GetInt(Param::timedly) * 60; + + return start <= 0 || (now - startTime) > start; +} + +static void CalcAcCurrentLimit() +{ + int configuredChargers = Param::GetInt(Param::chargerena); + float iacLim = Param::GetFloat(Param::iaclim); + float hwaclim = Param::GetFloat(Param::hwaclim); + float evseLim = Param::GetFloat(Param::evselim); + float cableLim = Param::GetFloat(Param::cablelim); + int activeModules = ((configuredChargers & 1) > 0) + ((configuredChargers & 2) > 0) + ((configuredChargers & 4) > 0); + + if (IsEvseInput()) + { + iacLim = MIN(iacLim, MIN(evseLim, cableLim)); + } + + if (Param::GetInt(Param::opmode) == 0) + { + dcCurController.ResetIntegrator(); + iacLim = 0; + } + else + { + dcCurController.SetMinMaxY(0, iacLim); + iacLim = dcCurController.Run(Param::Get(Param::idc)); + } + + if (Param::GetInt(Param::inputype) == INP_MANUAL || + Param::GetInt(Param::inputype) == INP_TYPE1 || + Param::GetInt(Param::inputype) == INP_TYPE2 || + (Param::GetInt(Param::inputype) == INP_TYPE2_AUTO && !DigIo::threep_in.Get())) + { + iacLim /= (float)activeModules; + } + + iacLim = MIN(iacLim, hwaclim); + + Param::SetFloat(Param::aclim, iacLim); +} + +static void ChargerStateMachine() +{ + static states state = OFF; + int configuredChargers = Param::GetInt(Param::chargerena); + + if (!Param::GetBool(Param::enable)) + { + state = OFF; + } + + switch (state) + { + default: + case OFF: + Param::SetInt(Param::opmode, 0); + DisableAll(); + + if (CheckStartCondition()) + { + startTime = rtc_get_counter_val(); + state = WAITSTART; + } + break; + case WAITSTART: + if (CheckDelay()) + state = ENABLE; + break; + case ENABLE: + DigIo::hvena_out.Set(); + if (configuredChargers & 1) + DigIo::ch1ena_out.Set(); + if (configuredChargers & 2) + DigIo::ch2ena_out.Set(); + if (configuredChargers & 4) + DigIo::ch3ena_out.Set(); + state = ACTIVATE; + break; + case ACTIVATE: + Param::SetInt(Param::opmode, 1); + + if (configuredChargers & 1) + DigIo::ch1act_out.Set(); + if (configuredChargers & 2) + DigIo::ch2act_out.Set(); + if (configuredChargers & 4) + DigIo::ch3act_out.Set(); + + startTime = rtc_get_counter_val(); + state = EVSEACTIVATE; + break; + case EVSEACTIVATE: + DigIo::evseact_out.Set(); + DigIo::acpres_out.Set(); + + if (CheckVoltage() || CheckTimeout()) + state = STOP; + if (CheckUnplugged()) + { + DigIo::acpres_out.Clear(); + DigIo::evseact_out.Clear(); + state = OFF; + } + if (CheckChargerFaults()) + { + DigIo::acpres_out.Clear(); + state = OFF; + } + break; + case STOP: + DisableAll(); + Param::SetInt(Param::opmode, 0); + + if (CheckUnplugged()) + state = OFF; + break; + } + + Param::SetInt(Param::state, state); +} + +static void CalcEnable() +{ + static int recheckCan = 10; + bool enablePol = Param::GetBool(Param::enablepol); + bool enable = DigIo::enable_in.Get() ^ enablePol; + + enable &= !Param::GetBool(Param::cancontrol) || Param::GetBool(Param::canenable); + + if (Param::GetBool(Param::cancontrol)) + { + if (recheckCan == 0) + { + if (Param::GetInt(Param::canenable) == 3) + { + Param::SetInt(Param::canenable, 0); + ErrorMessage::Post(ERR_EXTCAN); + } + else + { + Param::SetInt(Param::canenable, 3); //Must be overwritten by CAN message within the next second + } + recheckCan = 10; + } + + recheckCan--; + } + + Param::SetInt(Param::enable, enable); +} + +static void CalcTotals() +{ + s32fp totalCurrent = Param::Get(Param::c1idc) + Param::Get(Param::c2idc) + Param::Get(Param::c3idc); + Param::SetFixed(Param::idc, totalCurrent); + s32fp u1 = Param::Get(Param::c1udc); + s32fp u2 = Param::Get(Param::c2udc); + s32fp u3 = Param::Get(Param::c3udc); + + s32fp udcmax = MAX(u1, MAX(u2, u3)); + Param::SetFixed(Param::udc, udcmax); +} + +static void ResetValuesInOffMode() +{ + if (Param::GetInt(Param::state) == OFF) + { + for (int i = Param::c1stt; i <= Param::c3idc; i++) + { + Param::SetInt((Param::PARAM_NUM)i, 0); + } + } +} + +//sample 100ms task +static void Ms100Task(void) +{ + DigIo::led_out.Toggle(); + //The boot loader enables the watchdog, we have to reset it + //at least every 2s or otherwise the controller is hard reset. + iwdg_reset(); + //Calculate CPU load. Don't be surprised if it is zero. + float cpuLoad = scheduler->GetCpuLoad() / 10.0f; + //This sets a fixed point value WITHOUT calling the parm_Change() function + Param::SetFloat(Param::cpuload, cpuLoad); + //Set timestamp of error message + ErrorMessage::SetTime(rtc_get_counter_val()); + Param::SetInt(Param::uptime, rtc_get_counter_val()); + Param::SetFloat(Param::uaux, AnaIn::uaux.Get() / 223.418f); + + ResetValuesInOffMode(); + CalcTotals(); + CalcEnable(); + CalcAcCurrentLimit(); + ChargerStateMachine(); + + EvseRead(); + + can->SendAll(); +} + +static void MapChargerMessages() +{ + uint32_t dummyId; + uint8_t dummyOfs, dummyLen; + float dummyGain; + bool dummyrx; + + //check sample value, if it is mapped assume valid CAN map + if (can->FindMap(Param::hwaclim, dummyId, dummyOfs, dummyLen, dummyGain, dummyrx)) return; + + can->Clear(); + + ChargerCAN::MapMessages(can); + + can->Save(); +} + +/** This function is called when the user changes a parameter */ +void Param::Change(Param::PARAM_NUM paramNum) +{ + s32fp spnt; + + switch (paramNum) + { + case Param::idckp: + case Param::idcki: + dcCurController.SetGains(Param::GetInt(Param::idckp), Param::GetInt(Param::idcki)); + break; + case Param::idclim: + case Param::idcspnt: + spnt = MIN(Param::Get(Param::idcspnt), Param::Get(Param::idclim)); + dcCurController.SetRef(spnt); + break; + default: + //Handle general parameter changes here. Add paramNum labels for handling specific parameters + break; + } +} + +//Whichever timer(s) you use for the scheduler, you have to +//implement their ISRs here and call into the respective scheduler +extern "C" void tim2_isr(void) +{ + scheduler->Run(); +} + +extern "C" int main(void) +{ + extern const TERM_CMD termCmds[]; + + clock_setup(); //Must always come first + rtc_setup(); + ANA_IN_CONFIGURE(ANA_IN_LIST); + DIG_IO_CONFIGURE(DIG_IO_LIST); + AnaIn::Start(); //Starts background ADC conversion via DMA + write_bootloader_pininit(); //Instructs boot loader to initialize certain pins + gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, AFIO_MAPR_CAN1_REMAP_PORTB); + tim_setup(); //Use timer3 for sampling pilot PWM + nvic_setup(); //Set up some interrupts + parm_load(); //Load stored parameters + Param::Change(Param::idckp); //Call callback once for parameter propagation + Param::Change(Param::idclim); //Call callback once for parameter propagation + + Stm32Scheduler s(TIM2); //We never exit main so it's ok to put it on stack + scheduler = &s; + //Initialize CAN1, including interrupts. Clock must be enabled in clock_setup() + Can c(CAN1, Can::Baud500, true); + c.SetNodeId(5); + //store a pointer for easier access + can = &c; + Terminal t3(USART3, termCmds); + Terminal t1(USART1, termCmds); + + MapChargerMessages(); + dcCurController.SetCallingFrequency(10); + + //Up to four tasks can be added to each timer scheduler + //AddTask takes a function pointer and a calling interval in milliseconds. + //The longest interval is 655ms due to hardware restrictions + //You have to enable the interrupt (int this case for TIM2) in nvic_setup() + //There you can also configure the priority of the scheduler over other interrupts + s.AddTask(Ms100Task, 100); + + //backward compatibility, version 4 was the first to support the "stream" command + Param::SetInt(Param::version, 4); + + //In version 1.11 this changed from mV to V + if (Param::GetInt(Param::udcspnt) > 420) + { + Param::SetFloat(Param::udcspnt, Param::GetFloat(Param::udcspnt) / 1000); + } + + //Now all our main() does is running the terminal + //All other processing takes place in the scheduler or other interrupt service routines + //The terminal has lowest priority, so even loading it down heavily will not disturb + //our more important processing routines. + while(1) + { + t1.Run(); + t3.Run(); + } + + + return 0; +} + diff --git a/src/terminal_prj.cpp b/src/terminal_prj.cpp new file mode 100644 index 0000000..be95ea0 --- /dev/null +++ b/src/terminal_prj.cpp @@ -0,0 +1,253 @@ +/* + * This file is part of the stm32-template project. + * + * Copyright (C) 2020 Johannes Huebner + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* This file contains a standard set of commands that are used by the + * esp8266 web interface. + * You can add your own commands if needed + */ +#include +#include +#include "hwdefs.h" +#include "terminal.h" +#include "params.h" +#include "my_string.h" +#include "my_fp.h" +#include "printf.h" +#include "param_save.h" +#include "errormessage.h" +#include "stm32_can.h" +#include "terminalcommands.h" +#include "chargercan.h" + +static void LoadDefaults(Terminal* term, char *arg); +static void PrintParamsJson(Terminal* term, char *arg); +static void PrintSerial(Terminal* term, char *arg); +static void MapCan(Terminal* term, char *arg); +static void PrintErrors(Terminal* term, char *arg); + +static Terminal* curTerm = NULL; + +extern "C" const TERM_CMD termCmds[] = +{ + { "set", TerminalCommands::ParamSet }, + { "get", TerminalCommands::ParamGet }, + { "flag", TerminalCommands::ParamFlag }, + { "stream", TerminalCommands::ParamStream }, + { "defaults", LoadDefaults }, + { "save", TerminalCommands::SaveParameters }, + { "load", TerminalCommands::LoadParameters }, + { "json", PrintParamsJson }, + { "can", MapCan }, + { "serial", PrintSerial }, + { "errors", PrintErrors }, + { "reset", TerminalCommands::Reset }, + { NULL, NULL } +}; + +static void PrintCanMap(Param::PARAM_NUM param, uint32_t canid, uint8_t offset, uint8_t length, float gain, bool rx) +{ + const char* name = Param::GetAttrib(param)->name; + fprintf(curTerm, "can "); + + if (rx) + fprintf(curTerm, "rx "); + else + fprintf(curTerm, "tx "); + fprintf(curTerm, "%s %d %d %d %f\r\n", name, canid, offset, length, FP_FROMFLT(gain)); +} + +//cantx param id offset len gain +static void MapCan(Terminal* term, char *arg) +{ + Param::PARAM_NUM paramIdx = Param::PARAM_INVALID; + int values[4]; + int result; + char op; + char *ending; + const int numArgs = 4; + + arg = my_trim(arg); + + if (arg[0] == 'p') + { + while (curTerm != NULL); //lock + curTerm = term; + Can::GetInterface(0)->IterateCanMap(PrintCanMap); + curTerm = NULL; + return; + } + + if (arg[0] == 'c') + { + Can::GetInterface(0)->Clear(); + ChargerCAN::MapMessages(Can::GetInterface(0)); + fprintf(term, "Default CAN mapping restored\r\n"); + return; + } + + op = arg[0]; + arg = (char *)my_strchr(arg, ' '); + + if (0 == *arg) + { + fprintf(term, "Missing argument\r\n"); + return; + } + + arg = my_trim(arg); + ending = (char *)my_strchr(arg, ' '); + + if (*ending == 0 && op != 'd') + { + fprintf(term, "Missing argument\r\n"); + return; + } + + *ending = 0; + paramIdx = Param::NumFromString(arg); + arg = my_trim(ending + 1); + + if (Param::PARAM_INVALID == paramIdx) + { + fprintf(term, "Unknown parameter\r\n"); + return; + } + + if (op == 'd') + { + if (paramIdx >= Param::version) + { + fprintf(term, "Internal CAN map can not be deleted\r\n"); + return; + } + result = Can::GetInterface(0)->Remove(paramIdx); + fprintf(term, "%d entries removed\r\n", result); + return; + } + + for (int i = 0; i < numArgs; i++) + { + ending = (char *)my_strchr(arg, ' '); + + if (0 == *ending && i < (numArgs - 1)) + { + fprintf(term, "Missing argument\r\n"); + return; + } + + *ending = 0; + int iVal = my_atoi(arg); + + //allow gain values < 1 and re-interpret them + if (i == (numArgs - 1) && iVal == 0) + { + values[i] = fp_atoi(arg, 16); + } + else + { + values[i] = iVal; + } + + arg = my_trim(ending + 1); + } + + if (op == 't') + { + result = Can::GetInterface(0)->AddSend(paramIdx, values[0], values[1], values[2], values[3] / 65536.0f); + } + else + { + result = Can::GetInterface(0)->AddRecv(paramIdx, values[0], values[1], values[2], values[3] / 65536.0f); + } + + switch (result) + { + case CAN_ERR_INVALID_ID: + fprintf(term, "Invalid CAN Id %x\r\n", values[0]); + break; + case CAN_ERR_INVALID_OFS: + fprintf(term, "Invalid Offset %d\r\n", values[1]); + break; + case CAN_ERR_INVALID_LEN: + fprintf(term, "Invalid length %d\r\n", values[2]); + break; + case CAN_ERR_MAXITEMS: + fprintf(term, "Cannot map anymore items to CAN id %d\r\n", values[0]); + break; + case CAN_ERR_MAXMESSAGES: + fprintf(term, "Max message count reached\r\n"); + break; + default: + fprintf(term, "CAN map successful, %d message%s active\r\n", result, result > 1 ? "s" : ""); + } +} + +static void PrintParamsJson(Terminal* term, char *arg) +{ + arg = my_trim(arg); + + const Param::Attributes *pAtr; + char comma = ' '; + bool printHidden = arg[0] == 'h'; + + fprintf(term, "{"); + for (uint32_t idx = 0; idx < Param::PARAM_LAST; idx++) + { + pAtr = Param::GetAttrib((Param::PARAM_NUM)idx); + + if ((Param::GetFlag((Param::PARAM_NUM)idx) & Param::FLAG_HIDDEN) == 0 || printHidden) + { + fprintf(term, "%c\r\n \"%s\": {\"unit\":\"%s\",\"value\":%f,",comma, pAtr->name, pAtr->unit, Param::Get((Param::PARAM_NUM)idx)); + + if (Param::IsParam((Param::PARAM_NUM)idx)) + { + fprintf(term, "\"isparam\":true,\"minimum\":%f,\"maximum\":%f,\"default\":%f,\"category\":\"%s\",\"i\":%d}", + pAtr->min, pAtr->max, pAtr->def, pAtr->category, idx); + } + else + { + fprintf(term, "\"isparam\":false}"); + } + comma = ','; + } + } + fprintf(term, ",\r\n \"serial\": {\"unit\":\"\",\"value\":\"%X:%X:%X\",\"isparam\":false}", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0); + fprintf(term, "\r\n}\r\n"); +} + + +static void LoadDefaults(Terminal* term, char *arg) +{ + arg = arg; + Param::LoadDefaults(); + fprintf(term, "Defaults loaded\r\n"); +} + +static void PrintErrors(Terminal* term, char *arg) +{ + term = term; + arg = arg; + ErrorMessage::PrintAllErrors(); +} + +static void PrintSerial(Terminal* term, char *arg) +{ + arg = arg; + fprintf(term, "%X:%X:%X\r\n", DESIG_UNIQUE_ID2, DESIG_UNIQUE_ID1, DESIG_UNIQUE_ID0); +} diff --git a/test/digio_mock.cpp b/test/digio_mock.cpp new file mode 100644 index 0000000..69e67a0 --- /dev/null +++ b/test/digio_mock.cpp @@ -0,0 +1,10 @@ +#include "digio_mock.h" +#undef DIG_IO_ENTRY +#define DIG_IO_ENTRY(name, port, pin, mode) DigIo DigIo::name; +DIG_IO_LIST + +#undef ANA_IN_ENTRY +#define ANA_IN_ENTRY(name, port, pin) AnaIn AnaIn::name; +ANA_IN_LIST +#undef ANA_IN_ENTRY + diff --git a/test/digio_mock.h b/test/digio_mock.h new file mode 100644 index 0000000..2a2ed02 --- /dev/null +++ b/test/digio_mock.h @@ -0,0 +1,39 @@ +#include "../libopeninv/include/my_fp.h" +#include +#include "../include/digio_prj.h" +#include "../include/anain_prj.h" +#ifndef TEST_MOCK_H +#define TEST_MOCK_H +class Can { + public: + void Send(uint32_t canId, uint32_t data[2]) { Send(canId, data, 8); } + void Send(uint32_t canId, uint32_t data[2], uint8_t len) {} ; +}; +//void Can::Send(uint32_t canId, uint32_t data[2], uint8_t len){ }; + + +class DigIo{ + public: + #define DIG_IO_ENTRY(name, port, pin, mode) static DigIo name; + DIG_IO_LIST + #undef DIG_IO_ENTRY + + bool val = 0; + + bool Get() { return val; } + void Set() { val = 1;} + void Clear() { val = 0;} +}; + +class AnaIn{ + public: + #define ANA_IN_ENTRY(name, port, pin) static AnaIn name; + ANA_IN_LIST + #undef ANA_IN_ENTRY + + bool Get() { return 1; } + void Set() {} +}; + + +#endif diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..7cb21a4 --- /dev/null +++ b/test/test.c @@ -0,0 +1,26 @@ +#include "../libopeninv/include/my_fp.h" +#include "../libopeninv/include/my_math.h" +#include "../libopeninv/include/params.h" +#include "test.h" +#include "test_can.h" +#include "test_state.h" + +extern void Param::Change(Param::PARAM_NUM ParamNum){ }; + +// To add a test: +// 1) write void function test_x (see existing includes) +// 2) register function & name to test_cases below + +int main() { + //Test Case register + TestCase tests[] = { + (TestCase){"test_is_evse_input", test_is_evse_input}, + (TestCase){"test_check_unplugged", test_check_unplugged}, + // Example: + //(TestCase){"Your tests name", function_name_without_brackets} + //... + }; + + run_suite(tests, sizeof(tests)/sizeof(TestCase)); + return 0; +} diff --git a/test/test.h b/test/test.h new file mode 100644 index 0000000..7a8ddaf --- /dev/null +++ b/test/test.h @@ -0,0 +1,28 @@ +#include +#include + +typedef struct{ + char name[50]; + void (*func)(void); +} TestCase; + + +void run_suite(TestCase tests[], int num_cases){ + for(int i = 0; i < num_cases; i++){ + tests[i].func(); + printf("\r%d \033[0;32m\tPASSED\t%s\e[0m\n",i,tests[i].name); + } + printf("\033[0;32m ALL %d CASES PASSED\e[0m\n",num_cases); +} + +void run_case(TestCase tests[], int num_cases, char name[50]){ + printf("\rRUNNING INDIVIDUAL TEST \033[0;32m\t\t\e[0m\n"); + for(int i = 0; i < num_cases; i++){ + int res = strcmp(tests[i].name, name); + if(0 == res) + { + tests[i].func(); + printf("\r%d \033[0;32m\tPASSED\t%s\e[0m\n",i,tests[i].name); + } + } +} diff --git a/test/test.out b/test/test.out new file mode 100755 index 0000000..29e27f3 Binary files /dev/null and b/test/test.out differ diff --git a/test/test_can.h b/test/test_can.h new file mode 100644 index 0000000..048e88f --- /dev/null +++ b/test/test_can.h @@ -0,0 +1,8 @@ +#ifndef TEST_CAN_H +#define TEST_CAN_H +#endif + +#include +#include "test_common.h" +#include "../libopeninv/include/my_fp.h" + diff --git a/test/test_common.h b/test/test_common.h new file mode 100644 index 0000000..70ae60b --- /dev/null +++ b/test/test_common.h @@ -0,0 +1,5 @@ + +#ifndef TEST_COMMON_H +#define TEST_COMMON_H + +#endif diff --git a/test/test_state.h b/test/test_state.h new file mode 100644 index 0000000..2ca8f1d --- /dev/null +++ b/test/test_state.h @@ -0,0 +1,35 @@ +#ifndef TEST_STATE_H +#define TEST_STATE_H +#endif +#include +#include "test_common.h" +#include "functions.h" + +void test_is_evse_input() +{ + + bool res = false; + + res = IsEvseInput(); + assert(res); + + Param::SetInt(Param::inputype, INP_MANUAL); + res = IsEvseInput(); + assert(!res); +} + +void test_check_unplugged() +{ + bool res = false; + + Param::SetInt(Param::inputype, INP_TYPE1); + //TODO: surprising behaviour with Param::Set(Param::proximity, true); // as it expects s32fp + + Param::SetInt(Param::proximity, 1); + res = CheckUnplugged(); + assert(!res); + + Param::SetInt(Param::proximity, 0); + res = CheckUnplugged(); + assert(res); +}