Files
Balsam_167/Source/Internal/ADC.c

386 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @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;
}