initial commit for tests
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the tumanako_vc project.
|
||||
*
|
||||
* Copyright (C) 2018 Johannes Huebner <dev@johanneshuebner.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef CHARGERCAN_H
|
||||
#define CHARGERCAN_H
|
||||
#include "stm32_can.h"
|
||||
|
||||
class ChargerCAN
|
||||
{
|
||||
public:
|
||||
static void MapMessages(Can* can);
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#endif // CHARGERCAN_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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* This file is part of the stm32-template project.
|
||||
*
|
||||
* Copyright (C) 2020 Johannes Huebner <dev@johanneshuebner.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* This file is part of the stm32-template project.
|
||||
*
|
||||
* Copyright (C) 2020 Johannes Huebner <dev@johanneshuebner.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of the stm32-... project.
|
||||
*
|
||||
* Copyright (C) 2021 Johannes Huebner <dev@johanneshuebner.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#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
|
||||
}
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file is part of the stm32-template project.
|
||||
*
|
||||
* Copyright (C) 2020 Johannes Huebner <dev@johanneshuebner.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <libopencm3/cm3/common.h>
|
||||
#include <libopencm3/cm3/nvic.h>
|
||||
#include <libopencm3/cm3/scb.h>
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
#include <libopencm3/stm32/rcc.h>
|
||||
#include <libopencm3/stm32/usart.h>
|
||||
#include <libopencm3/stm32/adc.h>
|
||||
#include <libopencm3/stm32/timer.h>
|
||||
#include <libopencm3/stm32/dma.h>
|
||||
#include <libopencm3/stm32/rtc.h>
|
||||
#include <libopencm3/stm32/crc.h>
|
||||
#include <libopencm3/stm32/flash.h>
|
||||
#include <libopencm3/stm32/desig.h>
|
||||
#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);
|
||||
}
|
||||
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
/*
|
||||
* This file is part of the stm32-template project.
|
||||
*
|
||||
* Copyright (C) 2020 Johannes Huebner <dev@johanneshuebner.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <libopencm3/stm32/usart.h>
|
||||
#include <libopencm3/stm32/timer.h>
|
||||
#include <libopencm3/stm32/rtc.h>
|
||||
#include <libopencm3/stm32/can.h>
|
||||
#include <libopencm3/stm32/iwdg.h>
|
||||
#include <libopencm3/stm32/crc.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* This file is part of the stm32-template project.
|
||||
*
|
||||
* Copyright (C) 2020 Johannes Huebner <dev@johanneshuebner.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* 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 <libopencm3/cm3/scb.h>
|
||||
#include <libopencm3/stm32/usart.h>
|
||||
#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);
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#include "../libopeninv/include/my_fp.h"
|
||||
#include <stdio.h>
|
||||
#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
|
||||
+26
@@ -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;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,8 @@
|
||||
#ifndef TEST_CAN_H
|
||||
#define TEST_CAN_H
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include "test_common.h"
|
||||
#include "../libopeninv/include/my_fp.h"
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
#ifndef TEST_COMMON_H
|
||||
#define TEST_COMMON_H
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,35 @@
|
||||
#ifndef TEST_STATE_H
|
||||
#define TEST_STATE_H
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user