386 lines
14 KiB
C
386 lines
14 KiB
C
/**
|
||
* @file ADC.c
|
||
* @brief Сбор аналоговых отсчётов и первичная обработка измерительных каналов.
|
||
*
|
||
* Модуль настраивает АЦП TMS320F28335 и обслуживает прерывание окончания
|
||
* преобразования. Отсчёты накапливаются в ADC_table и adc_table_lem, после
|
||
* чего передаются измерительному контуру. Обработчик работает в контексте
|
||
* прерывания, поэтому его время выполнения ограничено частотой дискретизации,
|
||
* а разделяемые таблицы должны читаться основным циклом согласованно.
|
||
*/
|
||
#include "DSP2833x_Device.h" // DSP281x Headerfile Include File
|
||
#include "DSP2833x_Examples.h" // DSP281x Examples Include File
|
||
#include "DSP2833x_SWPrioritizedIsrLevels.h"
|
||
|
||
#include "ADC.h"
|
||
|
||
#include "log_to_mem.h"
|
||
#include "RS485.h"
|
||
#include "filter_bat2.h"
|
||
|
||
#include "measure.h"
|
||
#include "message.h"
|
||
#include "package.h"
|
||
|
||
#include "peripher.h"
|
||
|
||
/** @brief Сырые отсчёты четырёх силовых каналов, передаваемые Current_count(). */
|
||
Uint16 adc_table_lem[4];
|
||
/** @brief Фильтрованные значения всех рабочих и калибровочных каналов. */
|
||
Uint16 ADC_table[28]; // Потому что +4 калибр
|
||
/** @brief Число допустимых отсчётов прошлого окна для контроля выбросов. */
|
||
Uint16 prev_ok[28];
|
||
|
||
/** @brief Параметры временного мультиплексирования термоканалов в тиках АЦП. */
|
||
unsigned int COUNT_ONE_CANAL;
|
||
unsigned int COUNT_DISCHARGE;
|
||
unsigned int COUNT_TRANSICIA;
|
||
unsigned int FILTER_CLIP;
|
||
|
||
/** @brief Счётчик прогрева измерений и его начальная длительность. */
|
||
long WAKE, WAKE_TIME;
|
||
|
||
// Prototype statements for functions found within this file.
|
||
/** @brief ISR конца последовательности ADC SEQ1. */
|
||
interrupt void adc_isr(void);
|
||
|
||
/**
|
||
* @brief Полностью настраивает АЦП, ePWM-триггер и расписание каналов платы.
|
||
*
|
||
* @details По Desk выбирается физическая последовательность входов ADCINA/B.
|
||
* ePWM1 затем запускает SEQ1 с частотой ADC_FREQ. Для мультиплексированных
|
||
* термоканалов рассчитываются интервалы разряда, переходного процесса и окна
|
||
* накопления; WAKE подавляет использование неустановившихся фильтров при старте.
|
||
*
|
||
* @pre Desk и признаки TermoAD/TermoSW определены функцией get_Mode().
|
||
* @post ADCINT подключён к adc_isr(), SEQ1 и ePWM1 запущены.
|
||
*/
|
||
void setup_adc()
|
||
{
|
||
long CLKdiv,HSPCLKdiv,Rate;
|
||
int i;
|
||
|
||
#if (CPU_FRQ_150MHZ) // Default - 150 MHz SYSCLKOUT
|
||
#define ADC_MODCLK 0x3 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 150/(2*3) = 25.0 MHz
|
||
#endif
|
||
#if (CPU_FRQ_100MHZ)
|
||
#define ADC_MODCLK 0x2 // HSPCLK = SYSCLKOUT/2*ADC_MODCLK2 = 100/(2*2) = 25.0 MHz
|
||
#endif
|
||
|
||
// Specific clock setting for this example:
|
||
// EALLOW;
|
||
// SysCtrlRegs.HISPCP.all = ADC_MODCLK; // HSPCLK = SYSCLKOUT/ADC_MODCLK
|
||
// EDIS;
|
||
|
||
// Interrupts that are used in this example are re-mapped to
|
||
// ISR functions found within this file.
|
||
EALLOW; // This is needed to write to EALLOW protected register
|
||
PieVectTable.ADCINT = &adc_isr;
|
||
EDIS; // This is needed to disable write to EALLOW protected registers
|
||
|
||
InitAdc(); // For this example, init the ADC
|
||
|
||
// Enable ADCINT in PIE
|
||
PieCtrlRegs.PIEIER1.bit.INTx6 = 1;
|
||
IER |= M_INT1; // Enable CPU Interrupt 1
|
||
// EINT; // Enable Global interrupt INTM
|
||
// ERTM; // Enable Global realtime interrupt DBGM
|
||
|
||
/* Исключаем использование остаточных данных до первого полного цикла АЦП. */
|
||
for(i=0;i<28;i++)
|
||
ADC_table[i]=
|
||
prev_ok[i] = 0;
|
||
|
||
/* Каждая роль платы имеет собственную разводку входов и длину sequencer. */
|
||
// Configure ADC
|
||
if(Desk==dsk_LOAD)
|
||
{
|
||
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x0005; // Setup 2 conv's on SEQ1
|
||
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x3; // температура (которой нет)
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; // омега
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x5; // напруга 1
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x4; // напруга 2
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x7; // ток 1
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x2; // ток 2
|
||
}
|
||
|
||
if(Desk==dsk_BKST)
|
||
{
|
||
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x0002; // Setup 2 conv's on SEQ1
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; //0x5;//0x7; // Setup ADCINA3 as 1st SEQ1 conv.
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x1; //0x4;// Setup ADCINA2 as 2nd SEQ1 conv.
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x2; //0x4;// Setup ADCINA2 as 2nd SEQ1 conv.
|
||
}
|
||
|
||
if(Desk==dsk_COMM)
|
||
{
|
||
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x0006; // Setup 2 conv's on SEQ1
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x5; // сначала тоже будут температуры
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x4; // Setup ADCINA2 as 2nd SEQ1 conv.
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x7; // Setup ADCINA2 as 2nd SEQ1 conv.
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0x2; // Setup ADCINA2 as 2nd SEQ1 conv.
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0x3; // Setup ADCINA2 as 2nd SEQ1 conv.
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0x6; // Setup ADCINA2 as 2nd SEQ1 conv.
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x1; // Setup ADCINA2 as 2nd SEQ1 conv.
|
||
}
|
||
|
||
if(Desk==dsk_BKSD)
|
||
{
|
||
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x0000; // Setup 2 conv's on SEQ1
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x0; // Setup ADCINA3 as 1st SEQ1 conv.
|
||
}
|
||
|
||
if(Desk==dsk_SHKF)
|
||
{
|
||
AdcRegs.ADCMAXCONV.bit.MAX_CONV1 = 0x000F; // Setup 2 conv's on SEQ1
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV00 = 0x2; // 380В Ф1
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV01 = 0x3; // 380В Ф2
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV02 = 0x6; // 31В Ф1
|
||
AdcRegs.ADCCHSELSEQ1.bit.CONV03 = 0xC; // 31В Ф2 ?
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV04 = 0xA; // 31В UC1
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV05 = 0xB; // 31В UC2
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV06 = 0x7; // 24В ПУ
|
||
AdcRegs.ADCCHSELSEQ2.bit.CONV07 = 0x4; // 24В ПУ
|
||
|
||
AdcRegs.ADCCHSELSEQ3.bit.CONV08 = 0x5; // 24В ПК было 5
|
||
AdcRegs.ADCCHSELSEQ3.bit.CONV09 = 0x1; // 15В Др
|
||
AdcRegs.ADCCHSELSEQ3.bit.CONV10 = 0xE; // +24В Дт
|
||
AdcRegs.ADCCHSELSEQ3.bit.CONV11 = 0x0; // +24В Дт
|
||
AdcRegs.ADCCHSELSEQ4.bit.CONV12 = 0x8; // -24В Дт было 8
|
||
|
||
AdcRegs.ADCCHSELSEQ4.bit.CONV13 = 0xF;//0xF; // ДТ° 1
|
||
AdcRegs.ADCCHSELSEQ4.bit.CONV14 = 0xD;//0xD; // ДТ° 2
|
||
AdcRegs.ADCCHSELSEQ4.bit.CONV15 = 0x9;//0x9;
|
||
}
|
||
|
||
AdcRegs.ADCTRL2.bit.EPWM_SOCA_SEQ1 = 1;// Enable SOCA from ePWM to start SEQ1
|
||
AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 = 1; // Enable SEQ1 interrupt (every EOS)
|
||
AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit
|
||
|
||
AdcRegs.ADCTRL1.bit.SEQ_CASC = 1; // 1 Cascaded mode
|
||
|
||
AdcRegs.ADCTRL1.bit.CONT_RUN=0;
|
||
AdcRegs.ADCTRL1.bit.ACQ_PS = 15;
|
||
|
||
//AdcRegs.ADCTRL1.bit.CPS=1;
|
||
|
||
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1
|
||
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE
|
||
|
||
// Assumes ePWM1 clock is already enabled in InitSysCtrl();
|
||
EPwm1Regs.ETSEL.bit.SOCAEN = 1; // Enable SOC on A group
|
||
EPwm1Regs.ETSEL.bit.SOCASEL = 4; // Select SOC from from CPMA on upcount
|
||
EPwm1Regs.ETPS.bit.SOCAPRD = 1; // Generate pulse on 1st event
|
||
EPwm1Regs.CMPA.half.CMPA = 0x0080; // Set compare A value
|
||
|
||
EPwm1Regs.TBCTL.bit.HSPCLKDIV = CLKMULT;
|
||
EPwm1Regs.TBCTL.bit.CLKDIV=2;
|
||
|
||
CLKdiv = 1<<EPwm1Regs.TBCTL.bit.CLKDIV;
|
||
if(EPwm1Regs.TBCTL.bit.HSPCLKDIV) HSPCLKdiv = 2*EPwm1Regs.TBCTL.bit.HSPCLKDIV;
|
||
else HSPCLKdiv = 1;
|
||
|
||
/* Период ePWM получается из реальной частоты TBCLK после обоих делителей. */
|
||
Rate = (SYSCLKOUT/(HSPCLKdiv*CLKdiv))/ADC_FREQ;
|
||
|
||
EPwm1Regs.TBPRD = Rate; // Set period for ePWM1
|
||
EPwm1Regs.TBCTL.bit.CTRMODE = 0; // count up and start
|
||
|
||
WAKE_TIME =2L * ADC_FREQ; // dsk_LOAD;
|
||
|
||
if(Desk==dsk_BKSD)
|
||
{
|
||
COUNT_ONE_CANAL = ADC_FREQ/250; // 15
|
||
COUNT_DISCHARGE = 4;
|
||
COUNT_TRANSICIA = 9;
|
||
FILTER_CLIP = 20;//40;
|
||
WAKE_TIME =7L * ADC_FREQ;
|
||
}
|
||
if(Desk==dsk_BKST)
|
||
{
|
||
COUNT_ONE_CANAL = ADC_FREQ/350; // 10
|
||
COUNT_DISCHARGE = 1;
|
||
COUNT_TRANSICIA = 4;
|
||
FILTER_CLIP = 20;//40;
|
||
WAKE_TIME =3L * ADC_FREQ;
|
||
}
|
||
if(TermoAD)
|
||
{
|
||
COUNT_ONE_CANAL = ADC_FREQ/15; // 250;
|
||
COUNT_DISCHARGE = ADC_FREQ/145; // 25;
|
||
COUNT_TRANSICIA = ADC_FREQ/25; // 150;
|
||
FILTER_CLIP = 20;//200;
|
||
WAKE_TIME =3L * ADC_FREQ;
|
||
}
|
||
if(Desk==dsk_LOAD)
|
||
{
|
||
WAKE_TIME =10L * ADC_FREQ;
|
||
}
|
||
if(Desk==dsk_SHKF)
|
||
{
|
||
WAKE_TIME =1L * ADC_FREQ;
|
||
}
|
||
|
||
WAKE = WAKE_TIME;
|
||
}
|
||
|
||
/**
|
||
* @brief Забирает результаты SEQ1, фильтрует их и запускает расчёт величин.
|
||
*
|
||
* @details Сначала разрешает вложенные прерывания согласно программному
|
||
* приоритету PIE. В зависимости от аппаратной роли обрабатывает силовые каналы,
|
||
* нагрузочный аналоговый вход, шкафные каналы либо мультиплексированные
|
||
* термодатчики. Выброс принимается только при прохождении FILTER_CLIP; во время
|
||
* WAKE фильтр обучается, но наружу выдаётся непосредственный отсчёт.
|
||
*
|
||
* @note На общем выходе fin всегда сбрасывает SEQ1, подтверждает PIE и
|
||
* восстанавливает сохранённую маску PIEIER1, включая путь раннего выхода.
|
||
*/
|
||
interrupt void adc_isr(void)
|
||
{
|
||
static int cownt_one_canal=0;
|
||
static int cownt_cans=0;
|
||
int code_tpl_canal=0;
|
||
float Temper,Filter;
|
||
int i,n;
|
||
|
||
static int ok, cwnt_ok[2]={0,0};
|
||
|
||
// Set interrupt priority:
|
||
volatile Uint16 TempPIEIER = PieCtrlRegs.PIEIER1.all;
|
||
|
||
IER |= M_INT1;
|
||
IER &= MINT1; // Set "global" priority
|
||
PieCtrlRegs.PIEIER1.all &= MG11;//16; // Set "group" priority
|
||
PieCtrlRegs.PIEACK.all = 0xFFFF; // Enable PIE interrupts
|
||
EINT;
|
||
|
||
/* До READY обслуживается только обязательное завершение аппаратного цикла. */
|
||
if(WAKE) WAKE--;
|
||
if(!READY) goto fin;
|
||
|
||
if(Caliber_time)
|
||
{
|
||
if(!--Caliber_time)
|
||
{
|
||
cTermoCal = 0;
|
||
cSaveParam = 1;
|
||
} }
|
||
|
||
if(Kurrent)
|
||
{
|
||
for(i=0;i<4;i++)
|
||
{
|
||
adc_table_lem[i] = *((&AdcRegs.ADCRESULT0)+i+2) >>4; Current_count(i);
|
||
} }
|
||
|
||
if(Desk==dsk_LOAD)
|
||
{
|
||
Temper = AdcRegs.ADCRESULT1 >> 4;
|
||
modbus[0x63] = Temper;
|
||
Temper = filterbat(&adc_filter[0],Temper);
|
||
modbus[0x1D] = (Temper-DAC_04)/(DAC_20-DAC_04)*(200-40)+40;
|
||
}
|
||
|
||
if(Desk==dsk_SHKF)
|
||
{
|
||
for(i=0;i<CLKMULT*3;)
|
||
{
|
||
if (++cownt_cans>=tpl_cans) cownt_cans=0;
|
||
|
||
// Именно здесь нет дыр
|
||
// if(sens_type[cownt_cans])
|
||
{
|
||
i++;
|
||
Temper= *(&AdcRegs.ADCRESULT0 + cownt_cans) >>4;
|
||
ADC_table[cownt_cans]=filterbat(&adc_filter[cownt_cans],Temper);
|
||
if(sens_type[cownt_cans]==TERMO_AD) Temper_count(cownt_cans,0);
|
||
else Power_count(cownt_cans);
|
||
} } }
|
||
|
||
/* Программный мультиплексор: переключение, выдержка, накопление и расчёт. */
|
||
if(TermoSW)
|
||
{
|
||
if(cownt_one_canal==COUNT_DISCHARGE)
|
||
{
|
||
code_tpl_canal = cownt_cans;
|
||
|
||
if(TermoAD)
|
||
{
|
||
if(cownt_cans == TPL_CANS ) code_tpl_canal = TERMOPAIR-1;
|
||
if(cownt_cans == TPL_CANS+1) code_tpl_canal = TERMOPAIR-2; // потому что 300 и 400 наоборот
|
||
}
|
||
select_tpl_canal(code_tpl_canal);
|
||
}
|
||
|
||
/* Отсчёты до окончания переходного процесса намеренно отбрасываются. */
|
||
if(cownt_one_canal > COUNT_TRANSICIA)
|
||
for(i=0;i<TermoSW;i++)
|
||
{
|
||
n = TermoSW*cownt_cans+i;
|
||
|
||
Temper = *(&AdcRegs.ADCRESULT0 + i) >>4;
|
||
|
||
/* Ограничитель защищает рекурсивный фильтр от одиночного выброса. */
|
||
ok = abs(ADC_table[n]-Temper) < FILTER_CLIP;
|
||
|
||
if(ok) cwnt_ok[i]++;
|
||
|
||
if(ok|!prev_ok[n])
|
||
{
|
||
Filter = filterbat(&adc_filter[n],Temper);
|
||
|
||
if(WAKE)ADC_table[n] = Temper;
|
||
else ADC_table[n] = Filter;
|
||
}
|
||
}
|
||
/*
|
||
if(cownt_cans == TPL_CANS)
|
||
{
|
||
Test_mem_limit(8);
|
||
|
||
modbus[0x2F]=Log.Adres;
|
||
|
||
for(i=0;i<1;i++)
|
||
{
|
||
Log_to_mem(*(&AdcRegs.ADCRESULT0 + i) >>4);
|
||
Log_to_mem(ADC_table[2*cownt_cans+i]);
|
||
}
|
||
}
|
||
*/
|
||
if(++cownt_one_canal>=COUNT_ONE_CANAL)
|
||
{ cownt_one_canal=0;
|
||
|
||
select_tpl_255();
|
||
|
||
for(i=0;i<TermoSW;i++)
|
||
{
|
||
n = TermoSW*cownt_cans+i;
|
||
Temper_count(n,0);
|
||
prev_ok[n] = cwnt_ok[i];
|
||
cwnt_ok[i] = 0;
|
||
}
|
||
|
||
if(++cownt_cans >= TPL_CANS+2)
|
||
{ cownt_cans=0;
|
||
|
||
for(i=0;i<Owen;i++)
|
||
Temper_count(tpl_cans+i,1);
|
||
} } }
|
||
|
||
fin:
|
||
|
||
/* Единая эпилоговая секция обязательна и после goto при READY == 0. */
|
||
// Reinitialize for next ADC sequence
|
||
AdcRegs.ADCTRL2.bit.RST_SEQ1 = 1; // Reset SEQ1
|
||
AdcRegs.ADCST.bit.INT_SEQ1_CLR = 1; // Clear INT SEQ1 bit
|
||
PieCtrlRegs.PIEACK.all = PIEACK_GROUP1; // Acknowledge interrupt to PIE
|
||
|
||
// Restore registers saved:
|
||
DINT;
|
||
PieCtrlRegs.PIEIER1.all = TempPIEIER;
|
||
|
||
return;
|
||
}
|