From 0e8e1e4db65166f51c07112247033757027dc22a Mon Sep 17 00:00:00 2001 From: Janosch Date: Mon, 10 Oct 2022 11:46:30 +0100 Subject: [PATCH] initial commit for tests --- include/anain_prj.h | 20 ++ include/chargercan.h | 33 +++ include/digio_prj.h | 26 ++ include/errormessage_prj.h | 11 + include/functions.h | 15 ++ include/hwdefs.h | 14 + include/hwinit.h | 40 +++ include/param_prj.h | 172 ++++++++++++ src/chargercan.cpp | 102 +++++++ src/hwinit.cpp | 148 +++++++++++ src/main.cpp | 530 +++++++++++++++++++++++++++++++++++++ src/terminal_prj.cpp | 253 ++++++++++++++++++ test/digio_mock.cpp | 10 + test/digio_mock.h | 39 +++ test/test.c | 26 ++ test/test.h | 28 ++ test/test.out | Bin 0 -> 42576 bytes test/test_can.h | 8 + test/test_common.h | 5 + test/test_state.h | 35 +++ 20 files changed, 1515 insertions(+) create mode 100644 include/anain_prj.h create mode 100644 include/chargercan.h create mode 100644 include/digio_prj.h create mode 100644 include/errormessage_prj.h create mode 100644 include/functions.h create mode 100644 include/hwdefs.h create mode 100644 include/hwinit.h create mode 100644 include/param_prj.h create mode 100644 src/chargercan.cpp create mode 100644 src/hwinit.cpp create mode 100644 src/main.cpp create mode 100644 src/terminal_prj.cpp create mode 100644 test/digio_mock.cpp create mode 100644 test/digio_mock.h create mode 100644 test/test.c create mode 100644 test/test.h create mode 100755 test/test.out create mode 100644 test/test_can.h create mode 100644 test/test_common.h create mode 100644 test/test_state.h 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 0000000000000000000000000000000000000000..29e27f3a7fc390d61e85c8356cac8a844fd7cfb6 GIT binary patch literal 42576 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 +#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); +}