Files
Balsam_167/Source/Internal/DAC.c
Andrey 5d9818291b feat: add M95M02-only GAS and EEPROM profiles
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.
2026-10-09 16:17:37 +03:00

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;
}