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.
This commit is contained in:
2026-10-09 16:17:37 +03:00
parent c6c9128238
commit 5d9818291b
63 changed files with 2307 additions and 365 deletions

View File

@@ -1,5 +1,5 @@
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
#include "DSP2833x_Examples.h" // DSP281x Examples Include File
#include "DSP2833x_Examples.h" // DSP281x Examples Include File
#include "DSP2833x_SWPrioritizedIsrLevels.h"
#include "ADC.h"

View File

@@ -21,12 +21,110 @@ 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)
@@ -63,7 +161,7 @@ void Init_DAC(void)
toggle_RES_OUT_1();
}
void Anal_output(long vrot, long maxx)
static void anal_output(long vrot, long maxx, int calibration)
{
long out;
static int x;
@@ -72,7 +170,7 @@ void Anal_output(long vrot, long maxx)
x=0;// !x; òùåòíî áûòèå
if(cCalibrDac) out=DAC_cal();
if(calibration && cCalibrDac) out=DAC_cal();
out = out & 0xFFF;
@@ -99,79 +197,145 @@ Log_to_mem(modbus[0x1D]);
}
void Load_runner()
/* Ñîõðàí¸í ïóáëè÷íûé èíòåðôåéñ; çàùèòíûé ñáðîñ âûçûâàåò âíóòðåííþþ
* ôóíêöèþ ñ calibration=0, ÷òîáû ðó÷íàÿ êàëèáðîâêà íå ïîäìåíèëà 4 ìÀ. */
void Anal_output(long vrot, long maxx)
{
static unsigned int count_dac=0, count_load=0, x=1;
static unsigned int preStart=0;
static float dac_go,dac_stop;
float sinus=0;
int kod;
if (maxx > 0) anal_output(vrot, maxx, 1);
}
if(IMPowse) IMPowse--;
if(++count_dac >= period_dac)
{
count_dac=0;
void Load_runner(void)
{
unsigned int saved, commands, voltage, fresh, timeout;
unsigned int start, reset, active = 0;
float sinus = 0;
if(WAKEpowse)
{
--WAKEpowse;
/* 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--;
if( (WAKEpowse == DAC_FREQ/2*6)||
(WAKEpowse == DAC_FREQ/2*5)||
(WAKEpowse == DAC_FREQ/2*4)||
(WAKEpowse == DAC_FREQ/2*3)||
(WAKEpowse == DAC_FREQ/2*2)||
(WAKEpowse == DAC_FREQ/2*1) )
{
Init_DAC(); x=1;
} }
/* Îòñóòñòâèå êîìàíäû ïóñêà — íà÷àëî íîâîãî ñåàíñà, ñáðàñûâàåò îøèáêó.
* 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);
else
{
if(cUMPstart)
{
if(!preStart)
{
dac_go=DAC_go-4;
dac_stop=DAC_stop-4;
if(dac_go<0) dac_go=0;
if(dac_go>5) dac_go=5;
if(dac_stop>16) dac_go=16;
if(dac_stop<=dac_go) dac_stop=dac_go+1;
/* Äëÿ ëîãà ñîõðàíÿåì ðåàëüíûå îãðàíè÷åííûå óñòàâêè:
* âíóòðåííèå dac_go/stop îòñ÷èòûâàþòñÿ îò 4 ìÀ. */
ump_dac_go10 = (unsigned int)((dac_go + 4.0) * 10.0);
ump_dac_stop10 = (unsigned int)((dac_stop + 4.0) * 10.0);
}
/* Ïîâòîðíûé 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(count_load > (time_dac/2))count_load++;
if(++count_load > time_dac) count_load = time_dac;
sinus = ((dac_stop - dac_go) / 16.0 * count_load) + (dac_go / 16.0 * time_dac);
// sinus = (10.0 / 16.0 * count_load) + (2.0 / 16.0 * time_dac);
}
else
{ count_load = 0;
sinus=0;
x = (x+1)&0x3FF;
}
/* Ñíèìîê ñòàäèè ðàçãîíà äëÿ ñîïîñòàâëåíèÿ êîìàíäû CAN,
* îæèäàåìîãî òîêà è êîäà, äåéñòâèòåëüíî îòïðàâëåííîãî ÖÀÏ. */
ump_dac_ramp = count_load;
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;
preStart = cUMPstart;
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;
}
if(!x || cInitDac)
{
Init_DAC(); x=1; cInitDac=0;
}
else
{
kod = (int)sinus;
Anal_output(kod, time_dac);
} } } }
/* 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;
}

View File

@@ -6,3 +6,13 @@ void Setup_DAC_time(void);
/* Ñ÷¸ò÷èê ïóñêîâîãî èìïóëüñà è âûäåðæêè èíèöèàëèçàöèè äîñòóïíû ëîããåðó. */
extern unsigned long IMPowse, WAKEpowse;
extern unsigned int period_dac, time_dac;
/* Call once before IRQ; complete technological CAN packets update voltage. */
void Load_control_init(void);
void Load_voltage_can(unsigned int header, unsigned int a,
unsigned int b, unsigned int c);
/* 0x1300/8 words: signature, version, simulation, timeout, status, attempts,
* voltage, age. Only 0x1302 (simulation) and 0x1303 (timeout) are writable. */
unsigned int Load_protection_prepare(unsigned int function, unsigned int address,
unsigned int value, unsigned int *out, unsigned int *error);

View File

@@ -10,8 +10,8 @@
4876, 5005, 4876, 4847, 1898, 1873, 2535, 2502, // Áîðò 1
// ÓÌÏ UA UC IA IC 20mA O 4mA O 20mA I 4mA I
3820, 3790, 3505, 3380, 1736, 683, 854, 14, // Αξπς 0 1062 1980
3835, 3785, 3522, 3332, 1745, 689, 860, 17, // Αξπς 1 1720 2405
7580, 7540, 2310, 2420, 1736, 683, 854, 14, // Αξπς 0 1062 1980
7580, 7540, 2310, 2420, 1745, 689, 860, 17, // Αξπς 1 1720 2405
// ÁÊÑÑÄ 100 Ohm 150 Ohm
1281, 1272, 0, 0, 622, 2263, 0, 0,

View File

@@ -0,0 +1,15 @@
#ifndef BALSAM167_EEPROM_CONFIG_H
#define BALSAM167_EEPROM_CONFIG_H
/* Build-time checkbox: 0 = AT25256B (32 KiB), 1 = M95M02-DR (256 KiB).
* This must match the soldered part BEFORE startup loads saved parameters.
* CCS: change the value below, then Project > Clean and Rebuild.
* Command-line builds may override EEPROM_M95M02 for that build only.
* A GUI checkbox cannot change SPI ROM boot or startup initialization.
*/
#ifndef EEPROM_M95M02
#define EEPROM_M95M02 0
#endif
#if EEPROM_M95M02 != 0 && EEPROM_M95M02 != 1
#error EEPROM_M95M02_must_be_0_or_1
#endif
#endif

View File

@@ -0,0 +1,28 @@
#ifndef BALSAM167_GAS_H
#define BALSAM167_GAS_H
#include "eeprom_config.h"
#include <stdint.h>
/* Board profile 167/v1, word addresses, independent of the wire transport.
* The original register and UMP addresses remain valid in both builds.
* Only the physically fitted M95M02 version exposes the additional regions. */
#if EEPROM_M95M02
#define GAS167_REG_BASE 0x5000U
#define GAS167_REG_COUNT 128U
#define GAS167_INFO_BASE 0x5F00U
#define GAS167_INFO_WORDS 8U
#define GAS167_MAX_WORDS 124U
#define GAS167_PROFILE_VERSION 1U
#define GAS167_CAPABILITIES 7U /* bit 0 RTU, bit 1 service CAN, bit 2 FC06 */
/* Called by the main-loop transports under their snapshot IRQ lock.
* Capacity: at least GAS167_MAX_WORDS uint16_t words.
* RTU reads: 1..124 words; paired service CAN reads: 2..124, even.
* Successful reads return their count. Writes/errors return address/value.
* error uses Modbus codes 0 (success), 1 (function), 2 (address), 3 (value).
* No EEPROM I/O, allocation, or changes to the existing logger. */
int gas167_contains(uint16_t address);
unsigned gas167_prepare(unsigned function, uint16_t address, uint16_t value,
int can, uint16_t *reply, unsigned *error);
#endif
#endif

View File

@@ -95,6 +95,11 @@
#define DAC_go modbus[0x50] // íà÷àëî çàðàäà
#define DAC_stop modbus[0x51] // êîíåö çàðàäà
/* LOAD-only persistent CAN timeout (ms), read-only status and attempts. */
#define LOAD_CAN_TIMEOUT modbus[0x52]
#define LOAD_STATUS modbus[0x7C]
#define LOAD_ATTEMPTS modbus[0x7D]
#define Cancount (modbus+0x60) // ïàóçà ìåæäó I ïîñûëêàìè CAN
#define Bright (modbus+0x62) // àðêîñòü ñèãíàëüíûõ ëàìïî÷åê
#define Brightness modbus[0x62] // àðêîñòü ñèãíàëüíûõ ëàìïî÷åê

View File

@@ -2,8 +2,19 @@
#define __SPISE2P_H__
/* Physical EEPROM layout shared with the linker and SETGUI. */
#include "eeprom_config.h"
#include "include/balsam167_eeprom_layout.h"
// ¨ìêîñòü ïàìàòè â áàéòàõ
#define SEEPROM_LEN 0x10000
/* Preserve compact 16-bit arithmetic for the original 32-KiB build.
* Only the large part needs 32-bit word addresses and transfer lengths. */
#if EEPROM_M95M02
typedef unsigned long SE2P_SIZE;
#else
typedef unsigned int SE2P_SIZE;
#endif
#define SEEPROM_LEN BALSAM167_EEPROM_BYTES
#define NULL 0
@@ -58,8 +69,10 @@ typedef struct {
/* Object declaration */
typedef struct {
SE2P_DATA *msgPtr;
unsigned int csr; /* control/status register */
/* Main loop publishes the descriptor before setting csr; Timer2 consumes it.
* Both accesses are volatile so optimization cannot publish the request first. */
SE2P_DATA * volatile msgPtr;
volatile unsigned int csr; /* control/status register */
void (*init)(void *);
void (*tick)(void *);
void (*csset)(void);
@@ -85,12 +98,12 @@ void spiSe2pWrite(SPISE2P_DRV *se2p, SE2P_DATA *data);
void spiSe2pRead(SPISE2P_DRV *se2p, SE2P_DATA *data);
#if(SPISE2P_DATA_WIDTH==SIXTEEN_BIT)
#define PROM_LEN 0x8000
#define PAGE_LEN 0x20
#define PROM_LEN BALSAM167_EEPROM_WORDS
#define PAGE_LEN (BALSAM167_EEPROM_PAGE_BYTES / 2)
#define WORD_LEN 2
#else
#define PROM_LEN 0x4000
#define PAGE_LEN 0x40
#define PROM_LEN BALSAM167_EEPROM_BYTES
#define PAGE_LEN BALSAM167_EEPROM_PAGE_BYTES
#define WORD_LEN 1
#endif
@@ -105,7 +118,7 @@ void InitSeeprom(void);
** adres = 0..0x4000, åñëè äëèíà ñëîâà 16 áèò **
** buf - óêàçàòåëü íà ïàìàòü, îòêóäà ïèñàòü. **
** size - äëèíà áëîêà â áàéòàõ. Ïî-ëþáîìó â áàéòàõ! */
void Seeprom_write(unsigned int adres, unsigned int buf[], unsigned int size);
void Seeprom_write(SE2P_SIZE adres, unsigned int buf[], SE2P_SIZE size);
/* ×òåíèå áëîêà èç SEEPROM. Ïàðàìåòðû òàêîâû: **
** adres - àäðåñ â åïðîìêå, îòêóäà ÷èòàòü. **
@@ -113,7 +126,7 @@ void Seeprom_write(unsigned int adres, unsigned int buf[], unsigned int size);
** adres = 0..0x4000, åñëè äëèíà ñëîâà 16 áèò **
** buf - óêàçàòåëü íà ïàìàòü, êóäà ÷èòàòü. **
** size - äëèíà áëîêà â áàéòàõ. Ïî-ëþáîìó â áàéòàõ! */
void Seeprom_read(unsigned int adres, unsigned int buf[], unsigned int size);
void Seeprom_read(SE2P_SIZE adres, unsigned int buf[], SE2P_SIZE size);
#endif

View File

@@ -398,7 +398,16 @@ void extendbios(RS_DATA *rs_arr)
switch ( code )
{
// Ïðîøèâàåì EPROM Èç RAM
case 4: Seeprom_write(Address1,(unsigned int*)Address2,Length);
#if EEPROM_M95M02
/* 0x44 is intentionally rejected by old firmware. Conversely this build
* rejects legacy code 4, preventing writes with the wrong GUI profile. */
case 0x44:
#else
case 4:
#endif
if (!Length || Address1 >= BALSAM167_EEPROM_FIRMWARE_BYTES/2UL ||
Length > BALSAM167_EEPROM_FIRMWARE_BYTES-Address1*2UL) return;
Seeprom_write(Address1,(unsigned int*)Address2,Length);
break;
// ×èòàåì èç EPROM â RAM
case 5: Seeprom_read(Address1,(unsigned int*)Address2,Length);

View File

@@ -12,7 +12,9 @@
#include "message.h"
#include "peripher.h"
#include "log_to_mem.h"
#include "DAC.h"
#include "ump_can_protocol.h"
#include "gas167.h"
// Prototype statements for functions found within this file.
@@ -234,11 +236,18 @@ void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
Data[1] = (loword>>16) & 0xffff;
Data[2] = (loword ) & 0xffff;
if(bit[0]) if(adr < ANSWER_LEN) modbus[adr] = Data[0]; adr++;
if(bit[1]) if(adr < ANSWER_LEN) modbus[adr] = Data[1]; adr++;
if(bit[2]) if(adr < ANSWER_LEN) modbus[adr] = Data[2];
if(bit[0]) if(adr < ANSWER_LEN &&
!(Desk == dsk_LOAD && (adr == 0x7CU || adr == 0x7DU))) modbus[adr] = Data[0]; adr++;
if(bit[1]) if(adr < ANSWER_LEN &&
!(Desk == dsk_LOAD && (adr == 0x7CU || adr == 0x7DU))) modbus[adr] = Data[1]; adr++;
if(bit[2]) if(adr < ANSWER_LEN &&
!(Desk == dsk_LOAD && (adr == 0x7CU || adr == 0x7DU))) modbus[adr] = Data[2];
/* Ôèêñèðóåì ïðèìåí¸ííóþ êîìàíäó äî âûõîäà èç îáðàáîò÷èêà CAN. */
/* A short frame cannot confirm voltage freshness. Service frames
* UMP_MARKER return above and do not refresh this timeout. */
if (ECanMboxes->MBOX1.MSGCTRL.bit.DLC == 8)
Load_voltage_can((unsigned int)(hiword >> 16), Data[0], Data[1], Data[2]);
ump_log_can((unsigned int)(hiword >> 16), Data[0], Data[1], Data[2]);
led2_toggle();
}
@@ -316,7 +325,13 @@ void UMP_CAN_service(void)
saved = __disable_interrupts();
tag = ump_can_tag; address = ump_can_address; value = ump_can_value;
ump_can_pending = 0;
ump_can_length = ump_can_prepare(tag & 0xFF, address, value, ump_can_reply, &error);
if (address >= 0x1300U && address < 0x1308U)
ump_can_length = Load_protection_prepare(tag & 0xFF, address, value, ump_can_reply, &error);
#if EEPROM_M95M02
else if (gas167_contains(address))
ump_can_length = gas167_prepare(tag & 0xFF, address, value, 1, ump_can_reply, &error);
#endif
else ump_can_length = ump_can_prepare(tag & 0xFF, address, value, ump_can_reply, &error);
ump_can_reply_tag = (tag & 0xFF00) | error;
ump_can_index = 0;
__restore_interrupts(saved);

63
Source/Internal/gas167.c Normal file
View File

@@ -0,0 +1,63 @@
#include "gas167.h"
#if EEPROM_M95M02
#include "package.h"
extern int modbus[];
extern int Desk;
extern void ump_log_rs(void);
static const uint16_t gas167_info[GAS167_INFO_WORDS] = {
0x4741U, 0x5331U, GAS167_PROFILE_VERSION, 167U,
GAS167_REG_BASE, GAS167_REG_COUNT, GAS167_CAPABILITIES, 1U
};
int gas167_contains(uint16_t address)
{
return (uint16_t)(address - GAS167_REG_BASE) < GAS167_REG_COUNT ||
(uint16_t)(address - GAS167_INFO_BASE) < GAS167_INFO_WORDS;
}
static unsigned gas167_write(uint16_t offset, uint16_t value)
{
/* Match the existing native Modbus write policy. These status words
* cannot be overwritten and an invalid timeout cannot bypass protection. */
if (Desk == dsk_LOAD && (offset == 0x7CU || offset == 0x7DU)) return 2;
if (Desk == dsk_LOAD && offset == 0x52U &&
(value == 0U || value > 60000U)) return 3;
/* C28x int is one register word; reads always expose the raw uint16_t. */
modbus[offset] = (int)value;
if (offset == 127U) ump_log_rs();
return 0;
}
unsigned gas167_prepare(unsigned function, uint16_t address, uint16_t value,
int can, uint16_t *reply, unsigned *error)
{
uint16_t offset;
unsigned i, available;
int info;
if (!reply || !error) return 0;
*error = 0;
info = (uint16_t)(address - GAS167_INFO_BASE) < GAS167_INFO_WORDS;
offset = (uint16_t)(address - (info ? GAS167_INFO_BASE : GAS167_REG_BASE));
available = info ? GAS167_INFO_WORDS : GAS167_REG_COUNT;
if (function != 3U && function != 6U) *error = 1;
else if (offset >= available) *error = 2;
else if (function == 6U) {
*error = info ? 2U : gas167_write(offset, value);
} else if (!value || value > GAS167_MAX_WORDS || (can && (value & 1U))) {
*error = 3;
} else if (value > available - offset) {
*error = 2;
} else {
/* Validate the entire block before reading. A block cannot cross a
* region boundary, wrap at 0xFFFF, or leak the hidden parameter word. */
for (i = 0; i < value; ++i)
reply[i] = info ? gas167_info[offset+i] : (uint16_t)modbus[offset+i];
return value;
}
reply[0] = address;
reply[1] = value;
return 2;
}
#endif

View File

@@ -31,17 +31,15 @@ void clear_mem()
for (i=Log.Start; i<Log.Finis; i++)
*(volatile int *)i = 0;
}
/* Два банка используют уже выделенную внешнюю RAM .logg (64000 слов).
* Активный банк кольцевой. Через 1000 мс после команды отключения он
* становится архивом, а запись продолжается во втором банке. Переключение
* занимает постоянное время: копирования 30 тысяч слов под запретом IRQ нет.
* Архив переживает разрыв RS-связи, но НЕ исчезновение питания платы. */
/* Two RAM banks: freeze after 10% of bank capacity is recorded after stop. */
static volatile unsigned int ump_state, ump_count, ump_cursor, ump_next;
static volatile unsigned int ump_period = 20, ump_divider;
static volatile unsigned long ump_time, ump_sequence;
static unsigned int ump_bank, ump_archive_bank = 1;
static unsigned int ump_archive_count, ump_archive_next, ump_generation;
static unsigned int ump_source, ump_post, ump_post_dropped;
static unsigned int ump_source, ump_post;
#define UMP_POST_POINTS (UMP_LOG_CAPACITY / 10U)
static void ump_freeze(void);
static unsigned int ump_can_header, ump_can_data[3], ump_can_count;
/* Latest PM67 sums: register 306 (index 305), register 9 (index 8). */
static unsigned int ump_pm67_voltage[2];
@@ -62,7 +60,7 @@ void ump_log_init(void)
ump_time = 0; ump_sequence = 0;
ump_bank = 0; ump_archive_bank = 1;
ump_archive_count = 0; ump_archive_next = 0; ump_generation = 0;
ump_source = 0; ump_post = 0; ump_post_dropped = 0;
ump_source = 0; ump_post = 0;
ump_can_header = 0; ump_can_count = 0;
for (i = 0; i < 3; i++) ump_can_data[i] = 0;
for (i = 0; i < 2; i++) ump_pm67_voltage[i] = 0;
@@ -95,32 +93,30 @@ static unsigned int ump_outputs(void)
static void ump_capture(unsigned int reason)
{
volatile unsigned int *p;
unsigned int i, commands;
unsigned int i, commands, inputs, outputs;
if (ump_state != 1 || Desk != dsk_LOAD || !READY) return;
if (no_write || never_write) { ump_state = 3; return; }
commands = Commands;
/* Отключение: фронт cUMPreset (бит 6) либо спад cUMPstart (бит 7).
* Повторные команды не сдвигают конец уже начатого постинтервала. */
if (!ump_post && (((commands & 0x40) && !(ump_prev_commands & 0x40)) ||
(!(commands & 0x80) && (ump_prev_commands & 0x80)))) {
/* Половину банка резервируем под постинтервал. Даже при периоде
* 1 мс или шквале CAN событие отключения не вытесняется кольцом. */
if (ump_count >= UMP_LOG_CAPACITY/2) ump_count = UMP_LOG_CAPACITY/2-1;
ump_post_dropped = 0;
ump_post = 1000;
inputs = ump_inputs(); outputs = ump_outputs();
/* Freeze only an actual running-to-stopped output transition.
* STOP is set before RES_OUT_2 is cleared in main; an interrupt can
* observe these writes separately, so require its level, not its edge.
* Repeated reset/STOP pulses while already stopped leave the archive. */
if (!ump_post && (ump_prev_outputs & 4U) && !(outputs & 4U) &&
(outputs & 2U)) {
/* Keep 90% including the trigger; reserve 10% for later records. */
if (ump_count >= UMP_LOG_CAPACITY - UMP_POST_POINTS)
ump_count = UMP_LOG_CAPACITY - UMP_POST_POINTS - 1;
ump_post = UMP_POST_POINTS;
reason |= 0x100;
}
/* Последнее место оставляем для снимка конца постинтервала. При
* переполнении честно отмечаем усечение битом 0x200 в конечной записи. */
if (ump_post && ump_count >= UMP_LOG_CAPACITY-1) {
ump_post_dropped = 1;
return;
}
if (reason == 5 && ump_post_dropped) reason |= 0x200;
ump_prev_commands = commands; ump_prev_inputs = inputs; ump_prev_outputs = outputs;
/* Every stored point counts, including CAN and I/O, except the trigger. */
if (ump_post && !(reason & 0x100) && --ump_post == 0) reason = 5;
p = logs_block + ((unsigned long)ump_bank * UMP_LOG_CAPACITY + ump_next) * UMP_LOG_WORDS;
p[0] = (unsigned int)ump_time; p[1] = (unsigned int)(ump_time >> 16);
p[2] = reason; p[3] = commands;
p[4] = ump_inputs(); p[5] = ump_outputs();
p[4] = inputs; p[5] = outputs;
p[6] = (unsigned int)IMPowse; p[7] = (unsigned int)WAKEpowse;
p[8] = modbus[0x7B]; p[9] = modbus[0x1C];
for (i = 0; i < 4; i++) { p[10+i] = sens_data[i]; p[14+i] = modbus[i]; }
@@ -136,11 +132,11 @@ static void ump_capture(unsigned int reason)
p[33] = ump_dac_go10; p[34] = ump_dac_stop10; p[35] = ump_dac_position;
for (i = 0; i < 4; i++) p[36+i] = ADC_table[i];
p[40] = ump_pm67_voltage[0]; p[41] = ump_pm67_voltage[1];
ump_prev_commands = commands; ump_prev_inputs = p[4]; ump_prev_outputs = p[5];
ump_next++;
if (ump_next == UMP_LOG_CAPACITY) ump_next = 0;
if (ump_count < UMP_LOG_CAPACITY) ump_count++;
ump_sequence++;
if (reason == 5) ump_freeze();
}
/* Çàâåðø¸ííûé áàíê áîëüøå íå èçìåíÿåòñÿ äî ñëåäóþùåãî îòêëþ÷åíèÿ.
@@ -160,10 +156,6 @@ void ump_log_tick(void)
if (ump_state == 1) {
ump_time++;
if (++ump_divider >= ump_period) { ump_divider = 0; ump_capture(1); }
if (ump_post && --ump_post == 0) {
ump_capture(5);
ump_freeze();
}
}
__restore_interrupts(saved);
}
@@ -224,6 +216,7 @@ int ump_log_write(unsigned int address, unsigned int value)
ump_count = 0; ump_next = 0; ump_cursor = 0; ump_divider = 0; ump_post = 0;
ump_source = 0;
ump_prev_commands = Commands;
ump_prev_inputs = ump_inputs(); ump_prev_outputs = ump_outputs();
no_write = 0; never_write = 0;
ump_state = 1;
ump_capture(1);
@@ -274,7 +267,11 @@ unsigned int ump_log_read(unsigned int address)
case UMP_LOG_BASE+11: return (unsigned int)READY_FREQ;
case UMP_LOG_BASE+12: return ump_generation;
case UMP_LOG_BASE+13: return ump_archive_count;
case UMP_LOG_BASE+14: return ump_post;
/* Preserve the legacy millisecond status field as an upper estimate.
* Additional event records may finish the post interval sooner. */
case UMP_LOG_BASE+14:
first = (unsigned long)ump_post * ump_period;
return first > 65535UL ? 65535U : (unsigned int)first;
case UMP_LOG_BASE+15: return ump_state;
}
if (address >= UMP_LOG_DATA && address < UMP_LOG_DATA+120 && (ump_source || ump_state != 1)) {

View File

@@ -39,8 +39,6 @@ void main()
{
int i,j,mask;
static unsigned int cancount[2], cancell[2], rscount;
static int preCstart=1,preCstop=1;
int umpstart,umpstop;
RS_DATA * rs;
int presbyt=0;
@@ -83,6 +81,7 @@ void main()
clear_mem();
ump_log_init();
Load_control_init(); /* Explicit initialization before IRQ. */
set_cntrl_addr(Mode,16);
timer_Init();
EnableInterrupts();
@@ -178,27 +177,8 @@ void main()
presbyt = bTermoCal;
}
if(Desk==dsk_LOAD)
{
umpstart = cUMPstart;
umpstop = cUMPreset;
if(umpstart & !preCstart)
{
set_START_DPTB(); set_RES_OUT_2();
IMPowse = READY_FREQ;
}
preCstart = umpstart;
if(umpstop & !preCstop)
{
set_STOP_DPTB(); clear_RES_OUT_2();
IMPowse = READY_FREQ;
}
preCstop = umpstop;
if(!IMPowse) { clear_START_DPTB(); clear_STOP_DPTB(); }
}
/* Timer1 owns START/STOP/permission and the DAC ramp together.
* Main must not re-enable the output during a protected reload. */
/* Ñíèìîê ïîñëå îáíîâëåíèÿ âõîäîâ è ôîðìèðîâàíèÿ ïóñêîâûõ âûõîäîâ. */
ump_log_io();

View File

@@ -417,7 +417,7 @@ void Init_packMask()
if(Desk==dsk_LOAD)
{ Maska[m_SLOW][7]|= 0x0030; // Êàëèáðîâêà ÖÀÏ
Maska[m_SLOW][5]|= 0x0003; // Íà÷àëî è êîíåö çàðàäà
Maska[m_FAST][7]|= 0xE000; // Дублировать команды
Maska[m_FAST][7]|= 0xF000; /* Protection status 0x7C + attempts 0x7D. */ // Äóáëèðîâàòü êîìàíäû
Maska[m_FAST][1]|= 0x3000; // Óïðàâëàþùèé òîê ÑÈÔÓ
} }

View File

@@ -1,3 +1,5 @@
#include "eeprom_config.h"
#include "include/balsam167_eeprom_block.h"
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
#include "package.h"
#include "RS485.h"
@@ -18,7 +20,13 @@
#include "caliber.h"
#include "log_to_mem.h"
#include "DAC.h"
#include "ump_tms28335.h"
#include "gas167.h"
#include "include/balsam167_eeprom_layout.h"
#if (ANSWER_LEN + 1) != BALSAM167_EEPROM_PARAM_WORDS
#error EEPROM_parameter_reservation_must_match_ANSWER_LEN
#endif
int modbus[ANSWER_LEN+1];
unsigned int param[ANSWER_LEN+1];
@@ -126,6 +134,7 @@ void Default_params()
// Ïðèøëî âðåìà!
DAC_go = 6; // íà÷àëî çàðàäà
LOAD_CAN_TIMEOUT = 2000; /* ms without fresh ZPT: freeze ramp. */
DAC_stop= 16; // êîíåö çàðàäà
}
@@ -141,7 +150,7 @@ void Load_params()
{
unsigned int i,crc;
Seeprom_read(0x3FFF-(ANSWER_LEN+1), param, (ANSWER_LEN+1)*2);
Seeprom_read(BALSAM167_EEPROM_PARAMS_START_WORD, param, (ANSWER_LEN+1)*2);
crc = get_crc16(param,ANSWER_LEN);
if( (crc==param[ANSWER_LEN]) &&
(crc !=0xFFFF) &&
@@ -174,7 +183,7 @@ void Save_params()
if(dif)
{
param[ANSWER_LEN] = get_crc16(param,ANSWER_LEN);
Seeprom_write(0x3FFF-(ANSWER_LEN+1),param,(ANSWER_LEN+1)*2);
Seeprom_write(BALSAM167_EEPROM_PARAMS_START_WORD,param,(ANSWER_LEN+1)*2);
} }
/***************************************************************/
@@ -206,9 +215,39 @@ static int receive_ump(RS_DATA *rs_arr)
address = (rs_arr->RS_Header[2] << 8) | rs_arr->RS_Header[3];
if (address < UMP_BASE) return 0;
value = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
/* SPI driver advances in Timer2 ISR: NEVER mask interrupts here.
* Reuse the existing snapshot buffer; the main loop owns this transaction.
* The shared validator rejects writes, wraparound and out-of-device reads. */
if (address >= 0x1300U && address < 0x1308U) {
count = Load_protection_prepare(rs_arr->RS_Header[1], address, value, words, &error);
} else if (address >= BALSAM167_EEPROM_RTU_BASE) {
error = 2;
#if EEPROM_M95M02
/* Exception 3 prevents old AT-profile clients from falling back to
* legacy PEEK after a profile mismatch. No SPI transaction is issued. */
if (!(value & BALSAM167_EEPROM_M95_MARKER)) error = 3;
#endif
count = 0;
if (balsam167_eeprom_block_valid(rs_arr->RS_Header[1], address, value)) {
#if EEPROM_M95M02
count = value & 255U;
Seeprom_read(balsam167_eeprom_block_word(address, value), words, count * 2);
#else
count = value;
Seeprom_read(address - BALSAM167_EEPROM_RTU_BASE, words, count * 2);
#endif
error = 0;
}
} else {
saved = __disable_interrupts();
#if EEPROM_M95M02
if (gas167_contains(address))
count = gas167_prepare(rs_arr->RS_Header[1], address, value, 0, words, &error);
else
#endif
count = ump_tms28335_capture(rs_arr->RS_Header[1], address, value, words, &error);
__restore_interrupts(saved);
}
length = ump_rtu_encode(CNTRL_ADDR, rs_arr->RS_Header[1], error,
words, count, rs_arr->buffer, MAX_SEND_LENGTH);
if (length) {
@@ -289,6 +328,13 @@ void ReceiveCommandModbus6(RS_DATA *rs_arr)
// ïîëó÷èëè ñëîâî äàííûõ
Data_MB = (rs_arr->RS_Header[4] << 8) | rs_arr->RS_Header[5];
if (Desk == dsk_LOAD && (Address_MB == 0x7CU || Address_MB == 0x7DU)) {
modbus_exception(rs_arr, CMD_MODBUS_6, 2); return;
}
if (Desk == dsk_LOAD && Address_MB == 0x52U &&
(Data_MB == 0U || Data_MB > 60000U)) {
modbus_exception(rs_arr, CMD_MODBUS_6, 3); return;
}
if (Address_MB < ANSWER_LEN) {
Modbus[Address_MB].all = Data_MB;
if (Address_MB == 127) ump_log_rs();

View File

@@ -1,3 +1,7 @@
/* Build the canonical sources directly; do not copy their implementations. */
#include "../firmware-info/src/firmware_info_static.c"
/* Bind only this PM35 application's identity. The shared transport still owns
* range checks, read-only access, CRC and CAN fragment packing. */
#define UMP_FIRMWARE_INFO_READ firmware_info_static_read
#include "src/ump_protocol.c"
#include "ports/tms320f28335/ump_tms28335.c"

View File

@@ -19,12 +19,18 @@ int ccc=0;
/******* SPI bus Serial EEPROM driver Initialization routine ********/
/********************************************************************/
void Seeprom_write( unsigned int adres,
void Seeprom_write( SE2P_SIZE adres,
unsigned int buf[],
unsigned int size)
SE2P_SIZE size)
{
unsigned int len;
#if EEPROM_M95M02
/* Bound physical accesses before byte-to-word rounding. Legacy 32-KiB
* callers retain their compact driver; EXTEND is checked independently. */
if (adres >= PROM_LEN || !size || size > (PROM_LEN-adres)*2) return;
#endif
CpuTimer2.InterruptCount=0;
CpuTimer2Regs.TCR.all = 0x4020; // Use write-only instruction to set TSS bit = 0
@@ -50,12 +56,18 @@ void Seeprom_write( unsigned int adres,
CpuTimer2Regs.TCR.all = 0x4010; // Use write-only instruction to set TSS bit = 1
}
void Seeprom_read( unsigned int adres,
void Seeprom_read( SE2P_SIZE adres,
unsigned int buf[],
unsigned int size)
SE2P_SIZE size)
{
unsigned int len;
#if EEPROM_M95M02
/* Bound physical accesses before byte-to-word rounding. Legacy 32-KiB
* callers retain their compact driver; EXTEND is checked independently. */
if (adres >= PROM_LEN || !size || size > (PROM_LEN-adres)*2) return;
#endif
CpuTimer2.InterruptCount=0;
CpuTimer2Regs.TCR.all = 0x4020; // Use write-only instruction to set TSS bit = 0
@@ -244,6 +256,15 @@ void SPISE2P_DRV_tick(SPISE2P_DRV *eeprom)
case 5:
/* Send Address */
#if EEPROM_M95M02
SpiaRegs.SPICCR.all=SPISE2P_TFR8BIT;
SpiaRegs.SPITXBUF=(unsigned int)(eeprom->msgPtr->se2pAddr >> 16) << 8;
step=20;
break;
case 20:
if (!SpiaRegs.SPISTS.bit.INT_FLAG) break;
dummy=SpiaRegs.SPIRXBUF;
#endif
#if(SPISE2P_ADDR_WIDTH==SIXTEEN_BIT)
SpiaRegs.SPICCR.all= SPISE2P_TFR16BIT;
SpiaRegs.SPITXBUF=eeprom->msgPtr->se2pAddr;
@@ -374,7 +395,16 @@ void SPISE2P_DRV_tick(SPISE2P_DRV *eeprom)
case 15:
/* Send Address */
#if(SPISE2P_ADDR_WIDTH==SIXTEEN_BIT)
#if EEPROM_M95M02
SpiaRegs.SPICCR.all=SPISE2P_TFR8BIT;
SpiaRegs.SPITXBUF=(unsigned int)(eeprom->msgPtr->se2pAddr >> 16) << 8;
step=21;
break;
case 21:
if (!SpiaRegs.SPISTS.bit.INT_FLAG) break;
dummy=SpiaRegs.SPIRXBUF;
#endif
#if(SPISE2P_ADDR_WIDTH==SIXTEEN_BIT)
SpiaRegs.SPICCR.all= SPISE2P_TFR16BIT;
SpiaRegs.SPITXBUF=eeprom->msgPtr->se2pAddr;
#endif