Expose the existing registers through Modbus and service CAN using one GAS provider only in M95M02 builds. Include the EEPROM layout/driver, LOAD protection and calibration, firmware identity and shared CCS build workflow. Add transport/profile and SCI8 validation tests, documentation and the checked M95M02 firmware image.
342 lines
12 KiB
C
342 lines
12 KiB
C
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
|
|
#include "DSP2833x_SWPrioritizedIsrLevels.h"
|
|
#include "filter_bat2.h"
|
|
#include "measure.h"
|
|
|
|
#include "RS485.h"
|
|
#include "message.h"
|
|
#include "DAC.h"
|
|
#include "package.h"
|
|
#include "tools.h"
|
|
|
|
#include "peripher.h"
|
|
#include "log_to_mem.h"
|
|
|
|
#define dCLK_1() GpioDataRegs.GPBSET.bit.GPIO63=1
|
|
#define dCLK_0() GpioDataRegs.GPBCLEAR.bit.GPIO63=1
|
|
#define dCS_1() GpioDataRegs.GPBSET.bit.GPIO59=1
|
|
#define dCS_0() GpioDataRegs.GPBCLEAR.bit.GPIO59=1
|
|
|
|
unsigned long WAKEpowse;
|
|
unsigned long IMPowse;
|
|
unsigned int period_dac, time_dac;
|
|
|
|
/* Âñå âûõîäû ÓÌÏ è ðàìïà ïðèíàäëåæàò îäíîìó îáðàáîò÷èêó Timer1.
|
|
* Îñíîâíîé öèêë áîëüøå íå âûäà¸ò START: èíà÷å îí ìîæåò óâèäåòü êîðîòêèé
|
|
* ñïàä êîìàíäû, êîòîðûé ïðîïóñòèò ðàñ÷¸ò ÖÀÏ ñ ïåðèîäîì 5 ìñ. */
|
|
#define LOAD_IDLE 0U
|
|
#define LOAD_RUN 1U
|
|
#define LOAD_RELOAD 2U
|
|
#define LOAD_FAULT 3U
|
|
#define LOAD_MAX_ATTEMPTS 5U
|
|
#define LOAD_VOLTAGE_MIN 30U
|
|
#define LOAD_PULSE_MS 1000U
|
|
static unsigned int load_state, load_attempts, load_inhibit;
|
|
static unsigned int load_count_dac, load_count_ramp, load_init_count;
|
|
static unsigned int load_zero_sent, load_stop_asserted, load_initialized;
|
|
static float load_go, load_end;
|
|
/* 16-áèòíûå ïîëÿ îáùèå ñ CAN ISR. Ñíèìîê è óâåëè÷åíèå âîçðàñòà âûïîëíÿþòñÿ
|
|
* ïîä êîðîòêîé áëîêèðîâêîé IRQ; îòïðàâêà SPI ïîä áëîêèðîâêîé íå âûïîëíÿåòñÿ. */
|
|
static volatile unsigned int load_voltage, load_voltage_age, load_voltage_seen;
|
|
static volatile unsigned int load_simulation;
|
|
|
|
void Load_control_init(void)
|
|
{
|
|
load_state = LOAD_IDLE; load_attempts = 0; load_inhibit = 0;
|
|
load_count_dac = 0; load_count_ramp = 0; load_init_count = 1;
|
|
load_zero_sent = 0; load_stop_asserted = 0; load_initialized = 0;
|
|
load_go = 0; load_end = 0;
|
|
load_voltage = 0; load_voltage_age = 0; load_voltage_seen = 0;
|
|
load_simulation = 0;
|
|
IMPowse = 0; WAKEpowse = 0; period_dac = 0; time_dac = 0;
|
|
}
|
|
|
|
/* Âîçðàñò ñáðàñûâàåò òîëüêî íóæíîå íàïðÿæåíèå, à íå ëþáîé CAN-ïàêåò.
|
|
* ÓÌÏ 5: ÇÏÒ1, èíäåêñ 305; ÓÌÏ 6: ÇÏÒ2, èíäåêñ 8. Ìàñøòàá 1 Â/åä.
|
|
* Ìàñêè ñëîòîâ ñîõðàíÿþò àäðåñà äàæå äëÿ ïðîïóùåííûõ ñëîâ êàäðà. */
|
|
void Load_voltage_can(unsigned int header, unsigned int a,
|
|
unsigned int b, unsigned int c)
|
|
{
|
|
unsigned int address = header & 0x1FFFU;
|
|
unsigned int selected, i, mask = 0x8000U, data[3];
|
|
if (Desk != dsk_LOAD || (load_simulation & 1U)) return;
|
|
if (Mode == adr_LOA1) selected = 305U;
|
|
else if (Mode == adr_LOA2) selected = 8U;
|
|
else return;
|
|
data[0] = a; data[1] = b; data[2] = c;
|
|
for (i = 0; i < 3; i++, address++, mask >>= 1) {
|
|
if ((header & mask) && address == selected) {
|
|
load_voltage = data[i];
|
|
load_voltage_age = 0;
|
|
load_voltage_seen = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* STOP óäåðæèâàåòñÿ âåñü ñáðîñ, âêëþ÷àÿ îòïðàâêó êîäà 4 ìÀ â ÖÀÏ.
|
|
* Ïîâòîðíûé ïóñê ðàçðåø¸í òîëüêî ïîñëå ýòîé îòïðàâêè è 1000 ìñ STOP.
|
|
* Ýòî ïîäòâåðæäåíèå ïðîãðàììíîé îòïðàâêè, íå èçìåðåíèå ôèçè÷åñêîãî òîêà. */
|
|
static void load_stop(unsigned int state)
|
|
{
|
|
clear_START_DPTB();
|
|
set_STOP_DPTB();
|
|
clear_RES_OUT_2();
|
|
load_stop_asserted = 1;
|
|
load_state = state;
|
|
load_count_ramp = 0;
|
|
load_zero_sent = 0;
|
|
IMPowse = LOAD_PULSE_MS;
|
|
}
|
|
|
|
static void load_start(void)
|
|
{
|
|
/* Óñòàâêè ôèêñèðóþòñÿ äëÿ êàæäîé ïîïûòêè. Âíóòðåííèé íîëü = 4 ìÀ.
|
|
* Èñïðàâëåíà îïå÷àòêà ñòàðîãî îãðàíè÷åíèÿ: îãðàíè÷èâàåì êîíåö ðàìïû,
|
|
* à íå çàìåíÿåì íà÷àëüíóþ óñòàâêó íà 16 (÷òî äàâàëî ñêà÷îê äî 20 ìÀ). */
|
|
load_go = (float)DAC_go - 4.0;
|
|
load_end = (float)DAC_stop - 4.0;
|
|
if (load_go < 0) load_go = 0;
|
|
if (load_go > 5) load_go = 5;
|
|
if (load_end > 16) load_end = 16;
|
|
if (load_end <= load_go) load_end = load_go + 1;
|
|
ump_dac_go10 = (unsigned int)((load_go + 4.0) * 10.0);
|
|
ump_dac_stop10 = (unsigned int)((load_end + 4.0) * 10.0);
|
|
load_count_ramp = 0;
|
|
load_attempts++;
|
|
/* STOP îòïóñêàåì íåïîñðåäñòâåííî ïåðåä START, ðàçðåøåíèå — ïîñëå íåãî.
|
|
* Çàäàíèå îñòà¸òñÿ 4 ìÀ äî ïåðâîãî øàãà íîâîé ðàìïû. */
|
|
clear_STOP_DPTB();
|
|
load_stop_asserted = 0;
|
|
set_START_DPTB();
|
|
set_RES_OUT_2();
|
|
IMPowse = LOAD_PULSE_MS;
|
|
load_state = LOAD_RUN;
|
|
}
|
|
|
|
void Setup_DAC_time(void)
|
|
{
|
|
WAKEpowse = 3L * DAC_FREQ +1;
|
|
|
|
period_dac = READY_FREQ / DAC_FREQ;
|
|
time_dac = LOAD_TIME * DAC_FREQ;
|
|
/* Ñòàðûå EEPROM èìåþò íîëü â íîâîì ïàðàìåòðå: èñïîëüçóåì 600 ìñ.
|
|
* 1..60000 ìñ ìîæíî ìåíÿòü ÷åðåç ðåãèñòð 0x52 è ñîõðàíÿòü øòàòíî. */
|
|
if ((unsigned int)LOAD_CAN_TIMEOUT == 0U ||
|
|
(unsigned int)LOAD_CAN_TIMEOUT > 60000U) LOAD_CAN_TIMEOUT = 600;
|
|
LOAD_STATUS = 0; LOAD_ATTEMPTS = 0;
|
|
load_initialized = 1;
|
|
}
|
|
|
|
void dOUT(int x)
|
|
{
|
|
if(x) GpioDataRegs.GPBSET.bit.GPIO60=1;
|
|
else GpioDataRegs.GPBCLEAR.bit.GPIO60=1;
|
|
}
|
|
|
|
void wast()
|
|
{
|
|
int i;
|
|
|
|
for(i=0;i<25;i++);
|
|
}
|
|
|
|
void dSEND(unsigned int word)
|
|
{
|
|
int i;
|
|
|
|
dCS_0(); DSP28x_usDelay(1L);
|
|
|
|
for(i=0;i<16;i++)
|
|
{
|
|
dCLK_1();
|
|
dOUT((word & 0x8000)?1:0); word = word<<1; DSP28x_usDelay(1L);
|
|
dCLK_0(); DSP28x_usDelay(1L);
|
|
}
|
|
dCS_1();
|
|
}
|
|
|
|
void Init_DAC(void)
|
|
{
|
|
dSEND(0x9002|0x0000);
|
|
toggle_RES_OUT_1();
|
|
}
|
|
|
|
static void anal_output(long vrot, long maxx, int calibration)
|
|
{
|
|
long out;
|
|
static int x;
|
|
|
|
out = DAC_min() + vrot * DAC_max() / maxx - vrot * DAC_min() / maxx ;
|
|
|
|
x=0;// !x; òùåòíî áûòèå
|
|
|
|
if(calibration && cCalibrDac) out=DAC_cal();
|
|
|
|
out = out & 0xFFF;
|
|
|
|
if(x) dSEND(out|0x0000|0x0000);
|
|
else dSEND(out|0x8000|0x0000);
|
|
|
|
modbus[123] = out;
|
|
|
|
modbus[0x1C] = 40L + vrot * 200 / maxx - vrot * 40 / maxx ;
|
|
|
|
/*
|
|
if(!no_write)
|
|
{
|
|
Test_mem_limit(4);
|
|
|
|
Log_to_mem(modbus[0x7B]);
|
|
Log_to_mem(modbus[0x63]);
|
|
Log_to_mem(modbus[0x1C]);
|
|
Log_to_mem(modbus[0x1D]);
|
|
|
|
}
|
|
*/
|
|
|
|
|
|
}
|
|
|
|
/* Ñîõðàí¸í ïóáëè÷íûé èíòåðôåéñ; çàùèòíûé ñáðîñ âûçûâàåò âíóòðåííþþ
|
|
* ôóíêöèþ ñ calibration=0, ÷òîáû ðó÷íàÿ êàëèáðîâêà íå ïîäìåíèëà 4 ìÀ. */
|
|
void Anal_output(long vrot, long maxx)
|
|
{
|
|
if (maxx > 0) anal_output(vrot, maxx, 1);
|
|
}
|
|
|
|
void Load_runner(void)
|
|
{
|
|
unsigned int saved, commands, voltage, fresh, timeout;
|
|
unsigned int start, reset, active = 0;
|
|
float sinus = 0;
|
|
|
|
/* ISR ðàáîòàåò 1 êÃö, íî GPIO/ÖÀÏ íåëüçÿ îáñëóæèâàòü äî çàãðóçêè
|
|
* ïàðàìåòðîâ. Ýòî òàêæå èñêëþ÷àåò ïðåæíåå äåëåíèå íà time_dac=0. */
|
|
if (!load_initialized || !READY || !period_dac || !time_dac) return;
|
|
saved = __disable_interrupts();
|
|
commands = (unsigned int)Commands;
|
|
voltage = (load_simulation & 2U) ? 0U : load_voltage;
|
|
timeout = (unsigned int)LOAD_CAN_TIMEOUT;
|
|
if (!timeout || timeout > 60000U) timeout = 600U;
|
|
/* Äî ïåðâîãî ïàêåòà çíà÷åíèå 0 íå ñ÷èòàåòñÿ ñâåæèì èçìåðåíèåì.
|
|
* Íàñûùåíèå âîçðàñòà èñêëþ÷àåò ëîæíîå âîññòàíîâëåíèå ïîñëå ïåðåïîëíåíèÿ. */
|
|
fresh = load_voltage_seen && load_voltage_age < timeout;
|
|
if (load_voltage_age < 65535U) load_voltage_age++;
|
|
__restore_interrupts(saved);
|
|
start = commands & 0x80U; reset = commands & 0x40U;
|
|
if (IMPowse) IMPowse--;
|
|
|
|
/* Îòñóòñòâèå êîìàíäû ïóñêà — íà÷àëî íîâîãî ñåàíñà, ñáðàñûâàåò îøèáêó.
|
|
* RESET âñåãäà ñòàðøå START; îòïóñêàíèå RESET ïðè îñòàâøåìñÿ START
|
|
* íå äà¸ò íåîæèäàííîãî ïîâòîðíîãî ïóñêà: ñíà÷àëà òðåáóåòñÿ START=0. */
|
|
if (!start) {
|
|
if (load_state != LOAD_IDLE) load_stop(LOAD_IDLE);
|
|
load_attempts = 0; load_inhibit = 0;
|
|
} else if (reset) {
|
|
if (load_state != LOAD_FAULT && load_state != LOAD_IDLE)
|
|
load_stop(LOAD_IDLE);
|
|
load_inhibit = 1;
|
|
}
|
|
if (reset && !load_stop_asserted) load_stop(LOAD_IDLE);
|
|
|
|
/* Ïîâòîðíûé START äîïóñêàåòñÿ ïîñëå ñáðîñà ÖÀÏ, îêîí÷àíèÿ STOP è
|
|
* âîññòàíîâëåíèÿ íóæíîãî CAN-íàïðÿæåíèÿ. Òàéìàóò ñàì íå òðàòèò ïîïûòêó. */
|
|
if (start && !reset && !load_inhibit && !WAKEpowse && fresh &&
|
|
load_zero_sent && !IMPowse &&
|
|
(load_state == LOAD_IDLE || load_state == LOAD_RELOAD)) load_start();
|
|
if (load_state == LOAD_RUN && !IMPowse) clear_START_DPTB();
|
|
|
|
if (++load_count_dac >= period_dac) {
|
|
load_count_dac = 0;
|
|
if (WAKEpowse) {
|
|
--WAKEpowse;
|
|
if (WAKEpowse && !(WAKEpowse % (DAC_FREQ / 2))) {
|
|
Init_DAC(); load_init_count = 1;
|
|
}
|
|
} else {
|
|
active = load_state == LOAD_RUN;
|
|
if (active) {
|
|
/* Ïðè òàéìàóòå ñîõðàíÿåì ïîëîæåíèå È îòïðàâëåííîå çàäàíèå,
|
|
* íå äåëàåì STOP è íå ïðîäîëæàåì ðàçãîí áåç îáðàòíîé ñâÿçè. */
|
|
if (fresh) {
|
|
if (load_count_ramp > time_dac / 2) load_count_ramp++;
|
|
if (++load_count_ramp > time_dac) load_count_ramp = time_dac;
|
|
}
|
|
sinus = ((load_end - load_go) / 16.0 * load_count_ramp) +
|
|
(load_go / 16.0 * time_dac);
|
|
/* Ïðîâåðÿåì ñëåäóþùèé øàã ÄÎ ïåðåäà÷è ÖÀÏ. 6 ìÀ äîïóñòèìû,
|
|
* ñòðîãî >6 ìÀ è <30  òðåáóþò íåìåäëåííîãî STOP.
|
|
* Ïåðâàÿ ïîïûòêà âõîäèò â ëèìèò 5; øåñòîãî START íå áóäåò. */
|
|
if (fresh && voltage < LOAD_VOLTAGE_MIN &&
|
|
sinus * 16.0 > 2.0 * time_dac) {
|
|
load_stop(load_attempts >= LOAD_MAX_ATTEMPTS ?
|
|
LOAD_FAULT : LOAD_RELOAD);
|
|
active = 0; sinus = 0;
|
|
}
|
|
} else {
|
|
load_count_ramp = 0;
|
|
load_init_count = (load_init_count + 1) & 0x3FFU;
|
|
}
|
|
/* Âî âðåìÿ çàùèòû è ïóñêà ðó÷íîé êîä êàëèáðîâêè çàïðåù¸í.
|
|
* Êàëèáðîâêà ñîõðàíåíà òîëüêî ïðè ñíÿòîì START è ñâîáîäíîì STOP.
|
|
* Íóëåâîé êîä çàäàíèÿ çäåñü îçíà÷àåò òîê 4 ìÀ ïî DAC_min(). */
|
|
ump_dac_ramp = load_count_ramp;
|
|
ump_dac_duration = time_dac;
|
|
ump_dac_position = (unsigned int)sinus;
|
|
if (active && !fresh) {
|
|
/* Óäåðæèâàåì ÖÀÏ. Äàæå cInitDac íå ìåíÿåò òîê â ïàóçå CAN. */
|
|
} else if ((!load_init_count || cInitDac) && !load_stop_asserted) {
|
|
Init_DAC(); load_init_count = 1; cInitDac = 0;
|
|
} else {
|
|
anal_output((int)sinus, time_dac,
|
|
!start && !load_stop_asserted);
|
|
/* A manual calibration frame is not a confirmed 4 mA reload.
|
|
* START must wait for a forced baseline frame after it. */
|
|
if (!active) load_zero_sent = !(!start && !load_stop_asserted && cCalibrDac);
|
|
}
|
|
}
|
|
}
|
|
/* Äèàãíîñòèêà äîñòóïíà îáû÷íûì Modbus/CAN, íåçàâèñèìî îò ëîããåðà:
|
|
* áèò 0 — îøèáêà ïÿòè ïóñêîâ; áèò 1 — íåò ñâåæåãî íàïðÿæåíèÿ;
|
|
* áèò 2 — ñáðîñ ïåðåä ïîâòîðîì; áèò 3 — ðàáîòà ðàìïû.
|
|
* Îøèáêà óäåðæèâàåòñÿ ñî STOP, ïîêà îïåðàòîð íå ñíèìåò START. */
|
|
LOAD_STATUS = (load_state == LOAD_FAULT ? 1U : 0U) |
|
|
(!fresh ? 2U : 0U) |
|
|
(load_state == LOAD_RELOAD ? 4U : 0U) |
|
|
(load_state == LOAD_RUN ? 8U : 0U) |
|
|
(load_simulation << 4);
|
|
LOAD_ATTEMPTS = load_attempts;
|
|
}
|
|
|
|
/* Local versioned block, shared by RTU and service CAN. Probe its signature
|
|
* before writes: old firmware returns exception 2 instead of starting tests.
|
|
* Simulation is RAM-only, zeroed at boot, and never copied into EEPROM.
|
|
* bit 0 ignores ZPT reception (age reaches the configured timeout);
|
|
* bit 1 substitutes 0 V only for protection, leaving real logged CAN intact. */
|
|
unsigned int Load_protection_prepare(unsigned int function, unsigned int address,
|
|
unsigned int value, unsigned int *out, unsigned int *error)
|
|
{
|
|
unsigned int saved = __disable_interrupts();
|
|
*error = 2;
|
|
if (Desk == dsk_LOAD && address == 0x1300U && function == 3 && value == 8) {
|
|
out[0] = 0x5550U; out[1] = 1;
|
|
out[2] = load_simulation; out[3] = (unsigned int)LOAD_CAN_TIMEOUT;
|
|
out[4] = (unsigned int)LOAD_STATUS; out[5] = load_attempts;
|
|
out[6] = load_voltage; out[7] = load_voltage_seen ? load_voltage_age : 65535U;
|
|
*error = 0;
|
|
__restore_interrupts(saved);
|
|
return 8;
|
|
}
|
|
if (Desk == dsk_LOAD && function == 6) {
|
|
if (address == 0x1302U && value <= 3U) {
|
|
load_simulation = value;
|
|
*error = 0;
|
|
} else if (address == 0x1303U && value >= 1U && value <= 60000U) {
|
|
LOAD_CAN_TIMEOUT = value; *error = 0;
|
|
} else if (address == 0x1302U || address == 0x1303U) *error = 3;
|
|
}
|
|
out[0] = address; out[1] = value;
|
|
__restore_interrupts(saved);
|
|
return 2;
|
|
}
|