Для всех Mode передавать пакеты при обновлении данных и повторять каждый через 1300 мс от последнего CAN ACK. Отдельно отправлять heartbeat раз в секунду. Сохранить маски рассылки, отключение потоков нулевым периодом и неблокирующее восстановление после ошибок TX. Проверены границы периодов, все Mode, занятый mailbox, отмена без CANAA, отсутствие ACK, переполнение Timer1, первый снимок и справедливость потоков. Изменённые модули ecan.c и main.c скомпилированы компилятором TI C2000.
336 lines
14 KiB
C
336 lines
14 KiB
C
/* Команды и телеметрия eCAN. MBOX1 принимает кадры, MBOX0 передаёт
|
||
* технологические регистры, MBOX2 выделен под ответы логгера.
|
||
* Слово 127 проходит UMP_commands_write непосредственно в CAN ISR.
|
||
* Запрос архива ISR лишь ставит на обслуживание; фрагментация
|
||
* ответов выполняется UMP_CAN_service из фонового цикла.
|
||
*/
|
||
#include "DSP2833x_Device.h" // DSP2833x Headerfile Include File
|
||
#include "DSP2833x_SWPrioritizedIsrLevels.h"
|
||
|
||
#include "filter_bat2.h"
|
||
#include "measure.h"
|
||
#include "package.h" // DSP281x Headerfile Include File
|
||
|
||
#include "ecan.h" // DSP281x Headerfile Include File
|
||
#include "tools.h" // DSP281x Headerfile Include File
|
||
|
||
#include "RS485.h"
|
||
#include "message.h"
|
||
#include "peripher.h"
|
||
#include "log_to_mem.h"
|
||
#include "ump_can_protocol.h"
|
||
#include "DAC.h"
|
||
|
||
|
||
// Prototype statements for functions found within this file.
|
||
interrupt void CANa_handler(void);
|
||
interrupt void CANa_reset_err(void);
|
||
|
||
// Global variable for this example
|
||
Uint32 ErrorCount;
|
||
Uint32 MessageReceivedCount;
|
||
Uint32 MessageTransivedCount=0;
|
||
|
||
Uint32 TestMbox1 = 0;
|
||
Uint32 TestMbox2 = 0;
|
||
Uint32 TestMbox3 = 0;
|
||
|
||
int CanTimeOutErrorTR = 0;
|
||
|
||
int wait=0;
|
||
|
||
/* MBOX2 выделен под ответы регистратора; штатный MBOX0 продолжает
|
||
* передавать технологические данные независимо. ISR только ставит запрос,
|
||
* а снимок и отправка короткими шагами выполняются в основном цикле. */
|
||
static volatile unsigned int ump_can_pending;
|
||
static volatile unsigned int ump_can_tag, ump_can_address, ump_can_value;
|
||
static unsigned int ump_can_reply[UMP_CAN_MAX_WORDS];
|
||
static unsigned int ump_can_length, ump_can_index, ump_can_reply_tag;
|
||
static unsigned int ump_can_waiting;
|
||
static unsigned long ump_can_sent_at;
|
||
|
||
#include "can_telemetry_impl.h" /* Определяет heartbeat, подтверждённый кэш и повтор данных через 1,3 с после объявления состояния штатного TX. */
|
||
|
||
void InitCan(int Port, int DevNum)
|
||
{
|
||
struct ECAN_REGS ECanShadow;
|
||
|
||
volatile struct ECAN_REGS * ECanRegs;
|
||
volatile struct ECAN_MBOXES * ECanMboxes;
|
||
volatile struct MOTO_REGS * ECanMOTORegs;
|
||
|
||
long id = 0x80BA0000;
|
||
|
||
CAN_telemetry_init(); /* Сбрасывает кэш, очередь и секундный heartbeat до разрешения аппаратных прерываний. */
|
||
DevNum--;
|
||
if(DevNum<0)DevNum=0;
|
||
if(DevNum>15)DevNum=15;
|
||
|
||
// Configure CAN pins using GPIO regs here
|
||
EALLOW;
|
||
|
||
ECanRegs = &ECanaRegs;
|
||
ECanMboxes = &ECanaMboxes;
|
||
ECanMOTORegs = &ECanaMOTORegs;
|
||
|
||
GpioCtrlRegs.GPAMUX2.bit.GPIO30 = 1;
|
||
GpioCtrlRegs.GPAMUX2.bit.GPIO31 = 1;
|
||
|
||
// Configure the eCAN RX and TX pins for eCAN transmissions
|
||
ECanRegs->CANTIOC.all = 8; // only 3rd bit, TXFUNC, is significant
|
||
ECanRegs->CANRIOC.all = 8; // only 3rd bit, RXFUNC, is significant
|
||
|
||
// Specify that 8 bits will be sent/received
|
||
ECanMboxes->MBOX0.MSGCTRL.all = 0x00000008;
|
||
ECanMboxes->MBOX1.MSGCTRL.all = 0x00000008;
|
||
ECanMboxes->MBOX2.MSGCTRL.all = 0x00000008;
|
||
|
||
// Disable all Mailboxes
|
||
// Required before writing the MSGIDs
|
||
ECanRegs->CANME.all = 0;
|
||
|
||
// задаем адрес 0 ащикa на передачу
|
||
ECanMboxes->MBOX0.MSGID.all = id + 0x10 + DevNum;
|
||
|
||
// задаем адрес 1 ащикa на прием
|
||
ECanMboxes->MBOX1.MSGID.all = id + DevNum; //поменать!!!! 1 и 0!!!
|
||
|
||
// MBOX0 и MBOX2 передают данные; MBOX1 принимает команды.
|
||
ECanMboxes->MBOX2.MSGID.all = id + 0x20 + DevNum;
|
||
ECanRegs->CANMD.all = 0xFFFFFFFA;
|
||
|
||
// Включаем три mailbox: телеметрия, команды и ответы логгера.
|
||
ECanRegs->CANME.all = 0x00000007;
|
||
|
||
// Clear all TAn bits
|
||
ECanRegs->CANTA.all = 0xFFFFFFFF;
|
||
// Clear all RMPn bits
|
||
ECanRegs->CANRMP.all = 0xFFFFFFFF;
|
||
// Clear all interrupt flag bits
|
||
ECanRegs->CANGIF0.all = 0xFFFFFFFF;
|
||
ECanRegs->CANGIF1.all = 0xFFFFFFFF;
|
||
// Clear all error and status bits
|
||
ECanRegs->CANES.all=0xffffffff;
|
||
|
||
// Request permission to change the configuration registers
|
||
ECanShadow.CANMC.all = 0;
|
||
ECanShadow.CANMC.bit.MBCC = 1; // Mailbox timestamp counter clear bit
|
||
ECanShadow.CANMC.bit.TCC = 1; // Time stamp counter MSB clear bit
|
||
ECanShadow.CANMC.bit.SCB = 1; // eCAN mode (reqd to access 32 mailboxes)
|
||
ECanShadow.CANMC.bit.WUBA = 1; // Wake up on bus activity
|
||
ECanShadow.CANMC.bit.ABO = 1; // Auto bus on
|
||
ECanShadow.CANMC.bit.CCR = 1;
|
||
// ECanShadow.CANMC.bit.STM = 1; // self-test loop-back
|
||
ECanRegs->CANMC.all = ECanShadow.CANMC.all;
|
||
while(!ECanRegs->CANES.bit.CCE); // Wait for CCE bit to be set..
|
||
|
||
// настриваем скорость CAN
|
||
ECanShadow.CANBTC.all = ECanRegs->CANBTC.all;
|
||
ECanShadow.CANBTC.bit.SJWREG=1;
|
||
|
||
ECanShadow.CANBTC.bit.BRPREG = (CLKMULT * 3) - 1;
|
||
ECanShadow.CANBTC.bit.TSEG1REG = 15;
|
||
ECanShadow.CANBTC.bit.TSEG2REG = 2;
|
||
|
||
ECanRegs->CANBTC.all = ECanShadow.CANBTC.all;
|
||
ECanShadow.CANMC.bit.CCR = 0; // Set CCR = 0
|
||
ECanRegs->CANMC.all = ECanShadow.CANMC.all;
|
||
while(ECanRegs->CANES.bit.CCE); // Wait for CCE bit to be cleared..
|
||
|
||
// задаем таймауты дла ожиданиа отправки получениа посылки
|
||
ECanMOTORegs->MOTO0 = 550000;
|
||
ECanMOTORegs->MOTO1 = 550000;
|
||
|
||
ECanRegs->CANTOC.all = 1;
|
||
ECanRegs->CANTOS.all = 0; // clear all time-out flags
|
||
ECanRegs->CANTSC = 0; // clear time-out counter
|
||
|
||
ECanShadow.CANGIM.all = 0;
|
||
|
||
ECanRegs->CANMIM.all = 2; // Enable interrupts of box 1
|
||
ECanRegs->CANMIL.all = 0x00000000; // All mailbox interrupts are generated on interrupt line 0.
|
||
ECanShadow.CANGIM.bit.I0EN = 1;
|
||
|
||
ECanShadow.CANGIM.bit.MTOM = 1;
|
||
ECanShadow.CANGIM.bit.I1EN = 1;
|
||
ECanShadow.CANGIM.bit.GIL = 1;
|
||
ECanRegs->CANGIM.all = ECanShadow.CANGIM.all;
|
||
|
||
PieVectTable.ECAN0INTA = &CANa_handler;
|
||
PieCtrlRegs.PIEIER9.bit.INTx5=1; // PIE Group 9, INT6
|
||
PieVectTable.ECAN1INTA = &CANa_reset_err;
|
||
PieCtrlRegs.PIEIER9.bit.INTx6=1; // PIE Group 9, INT6
|
||
|
||
IER |= M_INT9; // Enable CPU INT
|
||
EDIS;
|
||
|
||
// завершили настройку CAN ащиков
|
||
|
||
MessageReceivedCount = 0;
|
||
ErrorCount = 0;
|
||
CanTimeOutErrorTR=0;
|
||
MessageTransivedCount=0;
|
||
}
|
||
|
||
/* При занятом MBOX0 возвращается без ожидания ACK в цикле.
|
||
* Это отличается от блокирующих UART/EEPROM операций main.
|
||
*/
|
||
/* CAN_send определён общим для этой единицы трансляции блоком can_telemetry_impl.h выше. */
|
||
|
||
|
||
void Handlai(volatile struct ECAN_MBOXES * ECanMboxes)
|
||
{
|
||
unsigned int adr;
|
||
unsigned int bit[3];
|
||
unsigned long hiword,loword;
|
||
int Data[3];
|
||
|
||
Lonely=0;
|
||
|
||
hiword = ECanMboxes->MBOX1.MDH.all;
|
||
loword = ECanMboxes->MBOX1.MDL.all;
|
||
|
||
/* Служебный запрос логгера не является командой УМП и сам не должен
|
||
* заполнять журнал CAN-событиями при онлайн-чтении. Короткие кадры
|
||
* отбрасываются: незаполненные байты mailbox могли остаться от прошлого. */
|
||
if ((unsigned int)(hiword >> 16) == UMP_CAN_MARKER) {
|
||
if (ECanMboxes->MBOX1.MSGCTRL.bit.DLC == 8) {
|
||
ump_can_tag = (unsigned int)hiword;
|
||
ump_can_address = (unsigned int)(loword >> 16);
|
||
ump_can_value = (unsigned int)loword;
|
||
ump_can_pending = 1;
|
||
}
|
||
return;
|
||
}
|
||
adr = (hiword >> 16);
|
||
|
||
bit[0] = adr & 0x8000;
|
||
bit[1] = adr & 0x4000;
|
||
bit[2] = adr & 0x2000;
|
||
|
||
adr &= 0x1fff;
|
||
|
||
Data[0] = (hiword ) & 0xffff;
|
||
Data[1] = (loword>>16) & 0xffff;
|
||
Data[2] = (loword ) & 0xffff;
|
||
|
||
/* Каждое разрешённое слово имеет собственный адрес. Не опрашиваем
|
||
* Commands после кадра безусловно: пакет телеметрии не должен
|
||
* превращать внутренний сброс параметров в командный фронт.
|
||
* Адрес 127 проходит через единый обработчик полного слова. */
|
||
{
|
||
unsigned int i;
|
||
for (i = 0; i < 3; i++, adr++) {
|
||
if (bit[i] && adr < ANSWER_LEN) {
|
||
if (adr == 127) UMP_commands_write((unsigned int)Data[i]);
|
||
else modbus[adr] = Data[i];
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Фиксируем применённую команду до выхода из обработчика CAN. */
|
||
ump_log_can((unsigned int)(hiword >> 16), Data[0], Data[1], Data[2]);
|
||
led2_toggle();
|
||
}
|
||
|
||
interrupt void CANa_handler(void)
|
||
{
|
||
// Set interrupt priority:
|
||
volatile Uint16 TempPIEIER = PieCtrlRegs.PIEIER9.all;
|
||
IER |= M_INT9;
|
||
IER &= MINT9; // Set "global" priority
|
||
PieCtrlRegs.PIEIER9.all &= MG95; // Set "group" priority
|
||
PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
|
||
EINT;
|
||
|
||
Handlai(&ECanaMboxes);
|
||
|
||
ECanaRegs.CANRMP.all = 2;
|
||
PieCtrlRegs.PIEACK.bit.ACK9 |= 1;
|
||
// Restore registers saved:
|
||
DINT;
|
||
PieCtrlRegs.PIEIER9.all = TempPIEIER;
|
||
}
|
||
|
||
interrupt void CANa_reset_err(void)
|
||
{
|
||
// Set interrupt priority:
|
||
volatile Uint16 TempPIEIER = PieCtrlRegs.PIEIER9.all;
|
||
IER |= M_INT9;
|
||
IER &= MINT9; // Set "global" priority
|
||
PieCtrlRegs.PIEIER9.all &= MG96; // Set "group" priority
|
||
PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
|
||
EINT;
|
||
|
||
/* Проверяет реальный тайм-аут mailbox 0: отложенное PIE-прерывание после успешного TX уже может не иметь причины. */
|
||
if (ECanaRegs.CANTOS.all & 1UL)
|
||
{ /* Обрабатывает только штатный передатчик; служебный mailbox 2 контролируется отдельно в UMP_CAN_service. */
|
||
EALLOW; /* Разрешает изменение управляющего регистра eCAN перед снятием источника прерывания. */
|
||
ECanaRegs.CANTOC.all &= ~1UL; /* Снимает timeout только mailbox 0, сохраняя контроль служебных передач. */ /* Сброс TOC снимает TOS и MTOF согласно SPRUI07, 13.8.18.3; одной отмены TX недостаточно. */
|
||
EDIS; /* Восстанавливает защиту регистров до выхода из ветви обработки тайм-аута. */
|
||
if (ECanaRegs.CANTRS.all & 1UL) /* Передача могла завершиться между чтением TOS и снятием контроля. */
|
||
ECanaRegs.CANTRR.all = 1UL; /* Запрашивает отмену только ещё занятого mailbox 0, не задерживая прерывание ожиданием CANAA. */
|
||
can_tx_failed = 1U; /* Не допускает записи повреждённого или отменённого кадра в подтверждённый кэш телеметрии. */
|
||
CanTimeOutErrorTR++; /* Считает обнаруженный тайм-аут один раз, исключая фантомные повторные входы PIE. */
|
||
} /* Следующая штатная передача сама заново включит TOC после сброса временного счётчика. */
|
||
|
||
PieCtrlRegs.PIEACK.bit.ACK9 |= 1;
|
||
|
||
// Restore registers saved:
|
||
DINT;
|
||
PieCtrlRegs.PIEIER9.all = TempPIEIER;
|
||
}
|
||
|
||
//===========================================================================
|
||
// No more.
|
||
//===========================================================================
|
||
|
||
|
||
/* Один вызов обслуживает максимум один кадр и никогда не ждёт ACK в цикле.
|
||
* Снимок фиксируется один раз на запрос: CAN-фрагменты не смешивают
|
||
* значения до/после переключения кольцевого банка. Новые запросы забираем
|
||
* после завершения предыдущего ответа. GUI держит только один запрос. */
|
||
void UMP_CAN_service(void)
|
||
{
|
||
unsigned int saved, tag, address, value, error;
|
||
unsigned long hiword, loword;
|
||
if (ump_can_waiting) {
|
||
if (ECanaRegs.CANTA.all & 4UL) {
|
||
ECanaRegs.CANTA.all = 4UL;
|
||
ump_can_waiting = 0;
|
||
} else if ((ECanaRegs.CANAA.all & 4UL) ||
|
||
((unsigned long)CpuTimer1.InterruptCount - ump_can_sent_at > 250UL)) {
|
||
/* Нет подтверждения шины: отменяем только служебный mailbox.
|
||
* GUI обнаружит неполный ответ по тайм-ауту; управление MBOX0
|
||
* и приём команд MBOX1 продолжаются. */
|
||
ECanaRegs.CANTRR.all = 4UL;
|
||
/* До фактического завершения отмены mailbox не переиспользуем. */
|
||
if (!(ECanaRegs.CANAA.all & 4UL)) return;
|
||
ECanaRegs.CANAA.all = 4UL;
|
||
ump_can_waiting = 0;
|
||
ump_can_length = 0;
|
||
} else return;
|
||
}
|
||
if (ump_can_index >= ump_can_length && ump_can_pending) {
|
||
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);
|
||
ump_can_reply_tag = (tag & 0xFF00) | error;
|
||
ump_can_index = 0;
|
||
__restore_interrupts(saved);
|
||
}
|
||
if (ump_can_index >= ump_can_length) return;
|
||
/* Перед следующим фрагментом уступаем штатной телеметрии MBOX0. */
|
||
if (ECanaRegs.CANTRS.all & 1UL) return;
|
||
hiword = ((unsigned long)(UMP_CAN_MARKER + ump_can_index/2) << 16) | ump_can_reply_tag;
|
||
loword = ((unsigned long)ump_can_reply[ump_can_index] << 16) | ump_can_reply[ump_can_index+1];
|
||
ECanaRegs.CANTA.all = 4UL; ECanaRegs.CANAA.all = 4UL;
|
||
ECanaMboxes.MBOX2.MDH.all = hiword;
|
||
ECanaMboxes.MBOX2.MDL.all = loword;
|
||
ump_can_sent_at = CpuTimer1.InterruptCount;
|
||
ECanaRegs.CANTRS.all = 4UL;
|
||
ump_can_waiting = 1;
|
||
ump_can_index += 2;
|
||
}
|