最近使用PIC16F18323设计一个电源的控制软件,主要功能有:检测输入电压,NTC贴片电阻温度监测,电路输出开环检测及输出过压保护锁死等。
PIC16F18323系列单片机有256字节内存,1K字节ROM空间,使用的是高速内部振荡器(锁相环之后时钟频率是32MHz),软件需求如下:
引脚 | 引脚特性 | 网络名 | 功能 | 功能要求 |
1 | / | VDD | 供电脚 | |
2 | A/D采样 | VIN | 输入电压检测 |
|
3 | / | 空脚 | ||
4 | / | MCLR | 复位 | |
5 | 输出 | V1_ON | -12V开机信号 | 开机:输出低电平; 关机:输出高电平,默认值为高电平 |
6 | 输出 | IN_OK | 输入状态信号 | 输入正常:输出高电平; 输入异常(欠压、过压):输出低电平,默认值为低电平 |
7 | 输出 | 无 | 输入继电器控制 | 继电器开:输出高电平; 继电器关:输出低电平,默认值为低电平 |
8 | 输出 | 无 | 50V开机信号 | 开机:输出低电平; 关机:输出高电平,默认值为高电平 |
9 | 输出 | V2_ON | +12V开机信号 | 开机:输出低电平; 关机:输出高电平,默认值为高电平 |
10 | A/D采样 | COMP2 | +12V开环检测 | +12V开环保护:此脚电压≥2.5V时(持续40mS),9脚输出高电平,持续3S,3S之后重新检测10脚电压,如果电压<2.2V,那么9脚输出低电平,反之则输出高电平。开环保护模式:+12V开40mS,关3S,打嗝模式 |
11 | A/D采样 | COMP1 | -12V开环检测 | -12V开环保护:此脚电压≥2.5V时(持续40mS),5脚输出高电平,持续3S,3S之后重新检测11脚电压,如果电压<2.2V,那么5脚输出低电平,反之则输出高电平。开环保护模式:-12V开40mS,关3S,打嗝模式 |
12 | A/D采样 | OTP | 温度检测 | 1、NTC型号:APR-CWF104F3950FA55A; 2、过温保护:此电压≥3.5V时(持续20mS,3.5V对应温度96℃),5、8、9脚同时输出高电平(关闭3路输出); 3、过温保护恢复:过温保护之后当电压≤2.8V时,5、9脚同时输出低电平(开-12V、+12V),延时100mS,8脚输出低电平(开50V)。 |
13 | 输入 | +12V_OVP | +12V输出过压检测 | +12V输出过压保护:此脚检测到高电平时(持续5-10mS),9脚持续输出高电平(过压锁死) |
14 | / | GND | 地 | |
开机时序: 第1步:检测输入正常,延时500mS,7脚输出高电平(开继电器); 第2步:延时100mS,5、9脚同时输出低电平(开-12V、+12V); 第3步:延时100mS,8脚输出低电平(开50V),6脚输出高电平(输入告警解除) |
单片机原理图部分:
其实程序逻辑比较简单,就是检测后进行各种控制,我的主要思路是:先进行AD检测,然后设置各个标志位,再根据标志位进行各个输出设备的开关控制。
主程序如下:
头文件main.h
/*
* File: main.h
* Author: PD821
*
* Created on September 8, 2023, 5:33 PM
*/
#ifndef MAIN_H
#define MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
//typedef unsigned char uint8_t;
//typedef unsigned int uint16_t;
//Configuration bits: selected in the GUI
//CONFIG1
#pragma config FEXTOSC = OFF // FEXTOSC External Oscillator mode Selection bits->Oscillator not enabled
#pragma config RSTOSC = HFINT32 // Power-up default value for COSC bits->HFINTOSC with 2x PLL (32MHz)
#pragma config CLKOUTEN = OFF // Clock Out Enable bit->CLKOUT function is disabled; I/O or oscillator function on OSC2
#pragma config CSWEN = ON // Clock Switch Enable bit->Writing to NOSC and NDIV is allowed
#pragma config FCMEN = ON // Fail-Safe Clock Monitor Enable->Fail-Safe Clock Monitor is enabled
//CONFIG2
#pragma config MCLRE = ON // Master Clear Enable bit->MCLR/VPP pin function is MCLR; Weak pull-up enabled
#pragma config PWRTE = OFF // Power-up Timer Enable bit->PWRT disabled
#pragma config WDTE = OFF // Watchdog Timer Enable bits->WDT disabled; SWDTEN is ignored
#pragma config LPBOREN = OFF // Low-power BOR enable bit->ULPBOR disabled
#pragma config BOREN = ON // Brown-out Reset Enable bits->Brown-out Reset enabled, SBOREN bit ignored
#pragma config BORV = LOW // Brown-out Reset Voltage selection bit->Brown-out voltage (Vbor) set to 2.45V
#pragma config PPS1WAY = ON // PPSLOCK bit One-Way Set Enable bit->The PPSLOCK bit can be cleared and set only once; PPS registers remain locked after one clear/set cycle
#pragma config STVREN = ON // Stack Overflow/Underflow Reset Enable bit->Stack Overflow or Underflow will cause a Reset
#pragma config DEBUG = OFF // Debugger enable bit->Background debugger disabled
//CONFIG3
#pragma config WRT = OFF // User NVM self-write protection bits->Write protection off
#pragma config LVP = ON // Low Voltage Programming Enable bit->Low Voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignored.
//CONFIG4
#pragma config CP = OFF // User NVM Program Memory Code Protection bit->User NVM code protection disabled
#pragma config CPD = OFF // Data NVM Memory Code Protection bit->Data NVM code protection disabled
//CPU clock define
#define _XTAL_FREQ 32000000
#define ACQ_US_DELAY 1
/*
* bit 7-2 CHS<5:0>: Analog Channel Select bits
111111 = FVR (Fixed Voltage Reference)(2)
111110 = DAC1 output(1)
111101 = Temperature Indicator(3)
111100 =VSS
111011 = Reserved. No channel connected. •
010101 = ANC5(4)
010100 = ANC4(4)
010011 = ANC3(4)
010010 = ANC2(4)
010001 = ANC1(4)
010000 = ANC0(4)
001011 = Reserved. No channel connected. •
000101 = ANA5
000100 = ANA4
000011 = Reserved. No channel connected.
000010 = ANA2
000001 = ANA1
000000 = ANA0
*/
#define nVIN 5 // RA5/ANA5
#define nTMP1 1 // RA1/ANA1
#define nCOMP1 2 // RA2/ANA2
#define nCOMP2 16 // RC0/ANC0
#define SAMPLE_CNT 32 // ADC sampling count
#define PV12_OVP PORTAbits.RA0 //+12V OVP check
#define NV12_OVP PORTAbits.RA1 //-12V OVP check
#define RLY_RDY_TIMEOUT 500 //If VDC input is normal, we need to delay 500ms before turning on RELAY
#define V12OUT_RDY_TIMEOUT 100 //If relay is ready, we delay 100ms before turning on the +12V and -12V output
#define V50OUT_RDY_TIMEOUT 100 //If +12 and -12V are both ready, we delay 100ms before turning on the 50V output
#define IO_FILTER_CNT 10 //IO filter count
#define IO_FILTER_VALID_CNT 10 //IO valid count of filter
#define OP_FILTER_CNT 20 //OPEN-LOOP filter count
#define OP_FILTER_VALID_CNT 20 //OPEN-LOOP valid count of filter
#define HICCUP_OFF_TIME 3000 //turn OFF about 3 seconds
#define HICCUP_ON_TIME 50 //turn ON about 50 milli-seconds
#define RELAY_ALWAYS_ON 0 //Is the relay always ON once powered-on? 0: OFF, 1: ON
#define LOCK_NO_DELAY 1 //Do we need to lock the output once OVP occurs? 1: turn off the output immediately, 0: delay sometime to turn off the output
#define LOCK_TIMEOUT 5000 //If the LOCK_NO_DELAY is set to 0, we delay sometime to check the OVP
#define DELAY_TURN_OFF_TIME 50 //delay some time to turn OFF the output once the IN_OK is abnormal
#define WARNING_FLAG_VP12 0x0A13
#define WARNING_FLAG_VN12 0x1813
//Relay
#define RELAY_ON() do { LATCbits.LATC3 = 1; } while(0)
#define RELAY_OFF() do { LATCbits.LATC3 = 0; } while(0)
//VDC
#define IN_OK() do { LATCbits.LATC4 = 1; } while(0)
#define IN_NOT_OK() do { LATCbits.LATC4 = 0; } while(0)
//-12V
#define VN12_ON() do { LATCbits.LATC5 = 0; } while(0)
#define VN12_OFF() do { LATCbits.LATC5 = 1; } while(0)
//+12V
#define VP12_ON() do { LATCbits.LATC1 = 0; } while(0)
#define VP12_OFF() do { LATCbits.LATC1 = 1; } while(0)
//50V
#define V50_ON() do { LATCbits.LATC2 = 0; } while(0)
#define V50_OFF() do { LATCbits.LATC2 = 1; } while(0)
struct ALARM
{
unsigned VDC_UVP : 1; //VDC under-voltage
unsigned VDC_OVP : 1; //VDC over-voltage
unsigned VDC_READY : 1; //VDC is ready
unsigned RLY_READY : 1; //the relay is ready
unsigned OTP : 1; //over temperature protection
unsigned VN12_OVP : 1; //-12V output is OVP
unsigned VP12_EN : 1; //+12V is ready to open
unsigned VP12_ON : 1; //+12V output is on or off
unsigned VP12_OVP : 1; //+12V output is OVP
unsigned VP12_OPEN_LOOP : 1; //+12V open-loop
unsigned IN_OK : 1; //IN-OK signal
unsigned OUT_LOCKED : 1; //lock the output permanently
unsigned VN12_OPEN_LOOP : 1; //-12V open-loop
unsigned VN12_EN : 1; //-12V is ready to open
unsigned VN12_ON : 1; //-12V output is on or off
unsigned V50_EN : 1; //50V output is ready to open
};
union ALARM_STATUS_UNION
{
struct ALARM alarm;
uint16_t allbits;
};
typedef union ALARM_STATUS_UNION ALARM_STATUS;
#define ADCFS 1024.0 //the maximum AD value of 10-bit ADC
#define ADCREF 5.0 //5V reference voltage
#define ADC_DIV_REF (float)(ADCFS/ADCREF)
//===========================================================================
//
// DC under/over voltage macros
//
//===========================================================================
#define VIN_DIV_RUP (51.0 + 51.0) //up and down resistors
#define VIN_DIV_RLOW 5.1
#define VIN_SENSE_FULLSCALE ((1.0 + VIN_DIV_RUP/VIN_DIV_RLOW) * ADCREF) //Maximum input voltage 105V
#define VIN_DIV_GAIN ((float)(VIN_DIV_RLOW/(VIN_DIV_RLOW + VIN_DIV_RUP)))//gain: 5.1/(5.1+102)
#define VDC_UV_OFF 32.5 //DC under voltage alarm
#define VDC_UV_ON 35.0 //DC under voltage recovered
#define VDC_OV_OFF 78.0 //DC over voltage alarm
#define VDC_OV_ON 75.0 //DC over voltage recovered
#define VDC_UV_OFF_AD (uint16_t)(VDC_UV_OFF * VIN_DIV_GAIN * ADC_DIV_REF)
#define VDC_UV_ON_AD (uint16_t)(VDC_UV_ON * VIN_DIV_GAIN * ADC_DIV_REF)
#define VDC_OV_OFF_AD (uint16_t)(VDC_OV_OFF * VIN_DIV_GAIN * ADC_DIV_REF)
#define VDC_OV_ON_AD (uint16_t)(VDC_OV_ON * VIN_DIV_GAIN * ADC_DIV_REF)
//===========================================================================
//
// OTP macros
//
//===========================================================================
//#define OTP1_OFF 3.35 //NTC=3.89893K, temperature: 105
//#define OTP1_RECOVER 2.84 //NTC=6.60883K, temperature: 90
//#define OTP1_OFF_AD 687 //(0x2AF)//105C, ((uint16_t)(((OTP1_OFF)/(ADCREF)) * (ADCFS)))
//#define OTP1_RECOVER_AD 581 //(0x246)//90C, ((uint16_t)(((OTP1_RECOVER)/(ADCREF)) * (ADCFS)))
#define OTP1_OFF 3.5 //temperature: 96
#define OTP1_RECOVER 2.8 //NTC=6.60883K, temperature: 90
#define OTP1_OFF_AD 717 //(0x2AF)//105C, ((uint16_t)(((OTP1_OFF)/(ADCREF)) * (ADCFS)))
#define OTP1_RECOVER_AD 573 //(0x246)//90C, ((uint16_t)(((OTP1_RECOVER)/(ADCREF)) * (ADCFS)))
//===========================================================================
//
// OPEN-LOOP macros
//
//===========================================================================
#define OP_VP12_OFF 2.5
#define OP_VP12_RECOVER 2.2
#define OP_VP12_OFF_AD (uint16_t)(OP_VP12_OFF * ADC_DIV_REF)
#define OP_VP12_RECOVER_AD (uint16_t)(OP_VP12_RECOVER * ADC_DIV_REF)
#define OP_VN12_OFF 2.5
#define OP_VN12_RECOVER 2.2
#define OP_VN12_OFF_AD (uint16_t)(OP_VN12_OFF * ADC_DIV_REF)
#define OP_VN12_RECOVER_AD (uint16_t)(OP_VN12_RECOVER * ADC_DIV_REF)
#ifdef __cplusplus
}
#endif
#endif /* MAIN_H */
C文件main.c
#include "xc.h"
#include "main.h"
//#define PORTBIT(ADR,BIT_LOC) ((unsigned)(&ADR)*8+(BIT_LOC))
//static bit in_SDA @PORTBIT(PORTC,1);//SDA data
volatile uint16_t timer1ReloadVal;
uint16_t rly_rdy_timeout = 0; //once VDC is normal, we delay some time to turn ON the relay
uint16_t v12_rdy_timeout = 0;
uint16_t v50_rdy_timeout = 0;
uint8_t v12_off_timeout = 0; //delay some time to turn off the +12V output
uint16_t cHiccup1OnTime = 0; //the ON time that the +12V output lasts after delaying 3 seconds
uint16_t cHiccup1OffTime = 0; //hiccup OFF time counter
uint8_t cHiccup1OnFlag = 0; //flag indicating that hiccup OFF time has reached the designated time and needs to reopen the output for a short time
uint16_t cHiccup2OnTime = 0; //the ON time that the -12V output lasts after delaying 3 seconds
uint16_t cHiccup2OffTime = 0; //hiccup OFF time counter
uint8_t cHiccup2OnFlag = 0; //flag indicating that hiccup OFF time has reached the designated time and needs to reopen the output for a short time
uint8_t cFlag_1ms = 0;
uint8_t cFlag_2ms = 0; //2 milli-seconds timeout for ADC to start the conversion
uint8_t cSample_cnt = 0;
uint8_t cSample_flag = 0; //flag indicating that all the four channels are sampled and the next conversion is ready to start
uint16_t ovp1_time_cnt = 0; //once output is OVP, we can decide whether it is necessary to delay some time or immediately to turn OFF the output
uint16_t ovp2_time_cnt = 0;
volatile uint8_t cchannel = 5; //default channel is VIN
volatile uint8_t c2mS_Nct = 0; //2 milli-seconds counter
volatile uint8_t io_cnt = 0;
uint8_t adc_filter_cnt = 0; //ADC filter counter
uint8_t op_filter_cnt = 0; //OPEN-LOOP filter counter
//ADC filter and OPEN-LOOP filter
uint8_t vdc_ovp_off = 0,vdc_ovp_recovered = 0;
uint8_t vdc_uvp_off = 0,vdc_uvp_recovered = 0;
uint8_t otp_off = 0,otp_recovered = 0;
uint8_t op_vp12_off = 0,op_vp12_recovered = 0;
uint8_t op_vn12_off = 0,op_vn12_recovered = 0;
//Unless stated otherwise, the following variables are similar to those that VDC uses
volatile uint16_t iSample_ad = 0;
volatile uint16_t iAd_data[4] ={0};
uint8_t vp12_ovp_H = 0; //OVP check
uint8_t vp12_ovp_L = 0;
ALARM_STATUS AlarmStatus;
/******************************************************
name: Init_OSC
brief: configure the system oscillator
input: none
output: none
*****************************************************/
void Init_OSC(void)
{
//NOSC HFINTOSC; NDIV 1;
OSCCON1 = 0x60;
//CSWHOLD may proceed; SOSCPWR Low power; SOSCBE crystal oscillator;
OSCCON3 = 0x00;
//LFOEN disabled; ADOEN disabled; SOSCEN disabled; EXTOEN disabled; HFOEN disabled;
OSCEN = 0x00;
//HFFRQ 32_MHz;
OSCFRQ = 0x07;
//HFTUN 0;
OSCTUNE = 0x00;
}
/******************************************************
name: Init_Timer
brief: configure the timer period, 1 milli-second
input: none
output: none
*****************************************************/
void Init_Timer(void)
{
//Set the Timer to the options selected in the GUI
//T1GSS T1G_pin; TMR1GE disabled; T1GTM disabled; T1GPOL low; T1GGO_nDONE done; T1GSPM disabled;
T1GCON = 0x00;
//TMR1H 224;
TMR1H = 0xE0;
//TMR1L 192;
TMR1L = 0xC0;
// Clearing IF flag before enabling the interrupt.
PIR1bits.TMR1IF = 0;
// Load the TMR value to reload variable
timer1ReloadVal=(uint16_t)((TMR1H << 8) | TMR1L);
// Enabling TMR1 interrupt.
PIE1bits.TMR1IE = 1;
// Set Default Interrupt Handler
//TMR1_SetInterruptHandler(TMR1_DefaultInterruptHandler);
// T1CKPS 1:1; T1SOSC T1CKI_enabled; T1SYNC synchronize; TMR1CS FOSC/4; TMR1ON enabled;
//bit7-bit6: TMR1CS<1:0>: Timer1 Clock Source Select bits
//00 = Timer1 clock source is instruction clock (FOSC/4)
T1CON = 0x01;//0b0000 0001
}
/******************************************************
name: Init_ADC
brief: configure the ADC module
input: none
output: none
*****************************************************/
void Init_ADC(void)
{
//ADFM right; ADNREF VSS; ADPREF VDD; ADCS FOSC/32 = 1M clock frequency;
/*
* bit 7 ADFM: ADC Result Format Select bit
* bit 6-4 ADCS<2:0>: ADC Conversion Clock Select bits
111 = ADCRC (dedicated RC oscillator)
110 =FOSC/64
101 =FOSC/16
100 =FOSC/4
011 = ADCRC (dedicated RC oscillator)
010 =FOSC/32
001 =FOSC/8
000 =FOSC/2
*/
ADCON1 = 0xA0; //1010, right justified, FOSC/32
//ADACT no_auto_trigger;
ADACT = 0x00;
//ADRESL 0;
ADRESL = 0x00;
//ADRESH 0;
ADRESH = 0x00;
//set the default ADC channel
cchannel = nVIN;
ADCON0bits.CHS = cchannel;
//ADGO stop; ADON enabled; CHS ANA0;
//bit 1 GO/DONE: ADC Conversion Status bit
//bit 0 ADON: ADC Enable bi
ADCON0 = 0x01;
}
/******************************************************
name: Init_GPIO
brief: configure the all pins needed
input: none
output: none
*****************************************************/
void Init_GPIO(void)
{
//LATx registers, set default pin value
//RC1-V2-ON: 1: OFF
//RC2-Alarm: 0: OFF
//RC3-Relay: 0: OFF
//RC4-IN-OK: 0: OFF
//RC5-V1-ON: 1: OFF
LATA = 0x00; // 0000 0000
LATC = 0x22; // 0010 0010
//TRISx registers, RA0-RA5 are all input mode
TRISA = 0x3F; // 0011 1111
TRISC = 0x01; // 0000 0001, RC0 is input, RC1-RC5 are output mode
//ANSELx registers, the following pins are configured as analog input
//RA5: VIN
//RA1: OTP
//RA2: COMP1
//RC0: COMP2
ANSELC = 0x01; // 0000 0001
ANSELA = 0x26; // 0010 0110
//WPUx registers, wake pull-up register, this is mainly used for input pins
//WPUA = 0x08; // 0000 1000
//WPUC = 0x36; // 0011 0110
//ODx registers, open-drain control
//bit 5-4 ODCA<5:4>: PORTA Open-Drain Enable bits
//For RA<5:4> pins, respectively
//1 = Port pin operates as open-drain drive (sink current only)
//0 = Port pin operates as standard push-pull drive (source and sink current)
ODCONA = 0x00;
ODCONC = 0x00;
//SLRCONx registers for RA and RC, slew rate control
SLRCONA = 0x37;//0011 0111
/*
bit 5-0 SLRC<5:0>: PORTC Slew Rate Enable bits
For RC<5:0> pins, respectively
1 = Port pin slew rate is limited
0 = Port pin slews at maximum rate
*/
SLRCONC = 0x3F;//0011 1111,
//INLVLx registers, input threshold control
/*
* The INLVLA register (Register 12-8) controls the input
voltage threshold for each of the available PORTA input
pins. A selection between the Schmitt Trigger CMOS or
the TTL Compatible thresholds is available. The input
threshold is important in determining the value of a read
of the PORTA register and also the level at which an
interrupt-on-change occurs, if that feature is enabled.
* bit 5-0 INLVLA<5:0>: PORTA Input Level Select bits
For RA<5:0> pins, respectively
1 = ST input used for PORT reads and interrupt-on-change
0 = TTL input used for PORT reads and interrupt-on-change
*/
INLVLA = 0x3F;
INLVLC = 0x3F;
//bit 4-0 RxyPPS<4:0>: Pin Rxy Output Source Selection bits
//10100(0x14) = Rxy source is EUSART TC/CK
//RC4PPS = 0x14; //RC4->EUSART:TX; //use RC4 as the EUSART TX pin
//set the default IO status of all output pins
RELAY_OFF();
IN_NOT_OK();
VP12_OFF();
VN12_OFF();
V50_OFF();
}
/*
* interrupt service routine
* here we mainly process the timer1's overflow event
*/
void __interrupt() ISR(void)
{
//interrupt handler
if(INTCONbits.PEIE == 1)
{
if(PIR1bits.TMR1IF == 1) // 1000 us
{
cFlag_1ms = 1;
if(++c2mS_Nct>=2)
{
cFlag_2ms = 1;
c2mS_Nct = 0;
}
//Clear the TMR1 interrupt flag
PIR1bits.TMR1IF = 0;
//TMR1_WriteTimer(timer1ReloadVal);
TMR1H = (uint8_t)(timer1ReloadVal >> 8);
TMR1L = (uint8_t)timer1ReloadVal;
}
}
}
/**************************************************************************************************
name:ADC_GetConversion
description:get the AD conversion result
input: channel: the channel to be converted
output: the 16-bit result
***************************************************************************************************/
uint16_t ADC_GetConversion(uint8_t channel)
{
// select the A/D channel
ADCON0bits.CHS = channel;
// Turn on the ADC module
ADCON0bits.ADON = 1;
// Acquisition time delay
__delay_us(ACQ_US_DELAY);
// Start the conversion
ADCON0bits.ADGO = 1;
// Wait for the conversion to finish
while (ADCON0bits.ADGO);
// Conversion finished, return the result
return ((uint16_t)((ADRESH << 8) + ADRESL));
}
/**************************************************************************************************
name:void ADC_Sample(void)
description:ADC sample, convert ADC per 2 milli-seconds
input: None
output: iAd_data[0]: VDC voltage;iAd_data[1]: OTP;iAd_data[2]: COMP1;iAd_data[3]: COMP2
***************************************************************************************************/
void ADC_Sample(void)
{
uint16_t iTemp;
if(cSample_flag == 0)
{
if (cSample_cnt == 0)
{
cSample_cnt = 1;
return;
}
switch(cchannel)
{
case nVIN: //VDC
{
//GO_nDONE = 1; // A2D start conversion
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(++cSample_cnt > SAMPLE_CNT)
{
cSample_cnt = 0;
iAd_data[0] = iSample_ad>>5;
iSample_ad = 0;
cchannel = nTMP1;
ADCON0bits.CHS = cchannel;
}
break;
}
case nTMP1: //temperature
{
//GO_nDONE = 1;
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(++cSample_cnt > SAMPLE_CNT)
{
cSample_cnt = 0;
iAd_data[1] = iSample_ad>>5;
iSample_ad = 0;
cchannel = nCOMP1;
ADCON0bits.CHS = cchannel;
}
break;
}
case nCOMP1: //COMP1, -12V OPEN-LOOP
{
//GO_nDONE = 1;
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(++cSample_cnt > SAMPLE_CNT)
{
cSample_cnt = 0;
iAd_data[2] = iSample_ad>>5;
iSample_ad = 0;
cchannel = nCOMP2;
ADCON0bits.CHS = cchannel;
}
break;
}
case nCOMP2: //COMP2, +12V OPEN-LOOP
{
//GO_nDONE = 1;
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(++cSample_cnt > SAMPLE_CNT)
{
cSample_cnt = 0;
iAd_data[3] = iSample_ad>>5;
iSample_ad = 0;
cchannel = nVIN;
ADCON0bits.CHS = cchannel;
cSample_flag = 1;
}
break;
}
default:
{
cSample_cnt = 0;
iSample_ad = 0;
cchannel = nVIN;
ADCON0bits.CHS = cchannel;
break;
}
}
}
}
/******************************************************
name: ADC_Process
brief: just call the ADC_Sample function and clear
* cSamle_flag if it is set.
* This function is called by main function.
input: none
output: none
*****************************************************/
void ADC_Process(void)
{
ADC_Sample();
if(cSample_flag == 1)
{
cSample_flag = 0;
}
}
/*
void ADC_Sample(void)
{
uint16_t iTemp;
if(cFlag_2ms == 1)
{
switch(cchannel)
{
case nVIN: //VDC
{
//GO_nDONE = 1; // A2D start conversion
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(cSample_cnt>=15)
{
cSample_cnt = 0;
iAd_data[0] = iSample_ad>>4;
iSample_ad = 0;
cchannel = nTMP1;
ADCON0bits.CHS = cchannel;
}
else
{
cSample_cnt++;
}
break;
}
case nTMP1: //temperature
{
//GO_nDONE = 1;
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(cSample_cnt>=15)
{
cSample_cnt = 0;
iAd_data[1] = iSample_ad>>4;
iSample_ad = 0;
cchannel = nCOMP1;
ADCON0bits.CHS = cchannel;
}
else
{
cSample_cnt++;
}
break;
}
case nCOMP1: //COMP1, -12V OPEN-LOOP
{
//GO_nDONE = 1;
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(cSample_cnt>=15)
{
cSample_cnt = 0;
iAd_data[2] = iSample_ad>>4;
iSample_ad = 0;
cchannel = nCOMP2;
ADCON0bits.CHS = cchannel;
}
else
{
cSample_cnt++;
}
break;
}
case nCOMP2: //COMP2, +12V OPEN-LOOP
{
//GO_nDONE = 1;
ADCON0bits.ADGO = 1;
__delay_us(ACQ_US_DELAY);
while (ADCON0bits.ADGO);
iTemp = ADRESH<<8;
iTemp = iTemp + ADRESL;
iSample_ad = iSample_ad + iTemp;
if(cSample_cnt>=15)
{
cSample_cnt = 0;
iAd_data[3] = iSample_ad>>4;
iSample_ad = 0;
cchannel = nVIN;
ADCON0bits.CHS = cchannel;
}
else
{
cSample_cnt++;
}
break;
}
default:
{
cSample_cnt = 0;
iSample_ad = 0;
cchannel = nVIN;
ADCON0bits.CHS = cchannel;
break;
}
}
if(PV12_OVP == 1)//+12V
{
vp12_ovp_H++;
}
else
{
vp12_ovp_L++;
}
if(io_cnt>=IO_FILTER_CNT)//set or clear OVP flag
{
if(vp12_ovp_H>=IO_FILTER_VALID_CNT)
{
AlarmStatus.alarm.VP12_OVP = 1;
}
if(vp12_ovp_L>=IO_FILTER_VALID_CNT)
{
AlarmStatus.alarm.VP12_OVP = 0;//no need to clear this bit
}
vp12_ovp_H = 0;
vp12_ovp_L = 0;
vn12_ovp_H = 0;
vn12_ovp_L = 0;
io_cnt = 0;
}
else
{
io_cnt++;
}
//c2mS_Nct = 0;
cFlag_2ms = 0;
}
}
*/
/******************************************************
name: Fault_Check
brief: set or clear all the flags, mainly the ADC result
period: 2 milli-second
input: none
output: none
*****************************************************/
static void Fault_Check(void)
{
if(cFlag_2ms == 1)
{
if(iAd_data[0]<=VDC_UV_OFF_AD) //DC under/over voltage filter
vdc_uvp_off++;
if(iAd_data[0]>=VDC_UV_ON_AD)
vdc_uvp_recovered++;
if(iAd_data[0]>=VDC_OV_OFF_AD)
vdc_ovp_off++;
if(iAd_data[0]<=VDC_OV_ON_AD)
vdc_ovp_recovered++;
if(iAd_data[1]>=OTP1_OFF_AD) //OTP
otp_off++;
if(iAd_data[1]<=OTP1_RECOVER_AD)
otp_recovered++;
if(adc_filter_cnt >= IO_FILTER_CNT)
{
if(vdc_uvp_off >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_UVP == 0) //VDC under voltage
{
AlarmStatus.alarm.VDC_UVP = 1;
}
else if(vdc_uvp_recovered >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_UVP == 1)
{
AlarmStatus.alarm.VDC_UVP = 0;
}
if(vdc_ovp_off >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_OVP == 0) //VDC over voltage
{
AlarmStatus.alarm.VDC_OVP = 1;
}
else if(vdc_ovp_recovered >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.VDC_OVP == 1)
{
AlarmStatus.alarm.VDC_OVP = 0;
}
if(otp_off >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.OTP == 0) //OTP
{
AlarmStatus.alarm.OTP = 1;
}
else if(otp_recovered >= IO_FILTER_VALID_CNT && AlarmStatus.alarm.OTP == 1)
{
AlarmStatus.alarm.OTP = 0;
}
adc_filter_cnt = 0;
vdc_uvp_off = 0;
vdc_uvp_recovered = 0;
vdc_ovp_off = 0;
vdc_ovp_recovered = 0;
otp_off = 0;
otp_recovered = 0;
}
else
{
adc_filter_cnt++;
}
//+12V and -12V OPEN-LOOP check
//OPEN-LOOP time check should ber longer to avoid the peak current while starting up
if(iAd_data[3] >= OP_VP12_OFF_AD)
op_vp12_off++;
if(iAd_data[3] <= OP_VP12_RECOVER_AD)
op_vp12_recovered++;
if(iAd_data[2] >= OP_VN12_OFF_AD)
op_vn12_off++;
if(iAd_data[2] <= OP_VN12_RECOVER_AD)
op_vn12_recovered++;
if(op_filter_cnt >= OP_FILTER_CNT)
{
if(op_vp12_off >= OP_FILTER_VALID_CNT && AlarmStatus.alarm.VP12_OPEN_LOOP == 0) //OPEN-LOOP of COMP2
{
AlarmStatus.alarm.VP12_OPEN_LOOP = 1;
}
//else if(op_vp12_recovered >= OP_FILTER_VALID_CNT && AlarmStatus.alarm.VP12_OPEN_LOOP == 1)
//{
//AlarmStatus.alarm.VP12_OPEN_LOOP = 0;
//}
if(op_vn12_off >= OP_FILTER_VALID_CNT && AlarmStatus.alarm.VN12_OPEN_LOOP == 0) //OPEN-LOOP of COMP1
{
AlarmStatus.alarm.VN12_OPEN_LOOP = 1;
}
//the warning flat can't be cleared here once it is set
//else if(op_vn12_recovered >= OP_FILTER_VALID_CNT && AlarmStatus.alarm.VN12_OPEN_LOOP == 1)
//{
//AlarmStatus.alarm.VN12_OPEN_LOOP = 0;
//}
op_filter_cnt = 0;
op_vp12_off = 0;
op_vp12_recovered = 0;
op_vn12_off = 0;
op_vn12_recovered = 0;
}
else
{
op_filter_cnt++;
}
cFlag_2ms = 0;
}
}
/******************************************************
name: Process_VP12
brief: process the +12V output
input: none
output: none
*****************************************************/
static void Process_VP12(void)
{
//POWER Dispatch. Under/over voltage protection OR output error OR over temperature, we need to deal with them respectively
//if((AlarmStatus.allbits & WARNING_FLAG_VP12)==0)//IN_OK must be set to 1
if(AlarmStatus.alarm.IN_OK == 1 && AlarmStatus.alarm.OUT_LOCKED == 0 && AlarmStatus.alarm.VP12_OPEN_LOOP == 0 && AlarmStatus.alarm.OTP == 0)
{
if(AlarmStatus.alarm.VP12_EN == 1)//only when all the conditions are ready can we turn on the +12V output
{
VP12_ON();
AlarmStatus.alarm.VP12_ON = 1;
}
else
{
VP12_OFF();
AlarmStatus.alarm.VP12_ON = 0;
}
cHiccup1OffTime = 0;
cHiccup1OnTime = 0;
cHiccup1OnFlag = 0;
}
else if(AlarmStatus.alarm.OUT_LOCKED == 1) //output is locked
{
VP12_OFF();
AlarmStatus.alarm.VP12_EN = 0;
AlarmStatus.alarm.VP12_ON = 0;
cHiccup1OffTime = 0;
cHiccup1OnTime = 0;
cHiccup1OnFlag = 0;
}
else if(AlarmStatus.alarm.VP12_OPEN_LOOP == 1)//OPEN-LOOP hiccup
{
if(++cHiccup1OffTime>=HICCUP_OFF_TIME)//OFF time
{
VN12_ON();
cHiccup1OffTime = HICCUP_OFF_TIME;
cHiccup1OnFlag = 1;
}
else
{
VP12_OFF();
cHiccup1OnTime = 0;
cHiccup1OnFlag = 0;
}
}
else //other exceptions processed in Task_Manager function
{
//TODO
}
/*
else//other exceptions, such as OVP, UVP or OTP or output is LOCKED
{
//rly_rdy_timeout=0;
if(AlarmStatus.alarm.IN_OK == 0)//we need to delay some to open the output, including -12V, +12V and 50V
{
v12_rdy_timeout = 0;
}
v50_rdy_timeout = 0;
cHiccup1OffTime = 0;
cHiccup1OnTime = 0;
cHiccup1OnFlag = 0;
#if(RELAY_ALWAYS_ON == 0)
{
AlarmStatus.alarm.RLY_READY = 0;
RELAY_OFF();
}
#endif
//VDC_OK=0;
if(AlarmStatus.alarm.IN_OK==0)//only after the IN_OK is set to 0 can set we turn OFF the +12V
{
if(++vp12_off_timeout>=DELAY_TURN_OFF_TIME)
{
vp12_off_timeout=0;
VP12_OFF();
}
}
else//OTHER exception rather than IN_OK error, turn OFF the output directly
{
VP12_OFF();
}
AlarmStatus.alarm.VP12_EN = 0;
AlarmStatus.alarm.VP12_ON = 0;
}*/
if(cHiccup1OnFlag == 1)//hiccup
{
if(++cHiccup1OnTime>=HICCUP_ON_TIME)//the 49V output is on for 50 milli-seconds
{
if(iAd_data[3]>=OP_VP12_OFF_AD)//if it is still in OPEN-LOOP state
{
cHiccup1OffTime = 0;
}
else
{
if(iAd_data[3]<OP_VP12_RECOVER_AD)//recovered, return difference is necessary
{
AlarmStatus.alarm.VP12_OPEN_LOOP = 0;
}
cHiccup1OffTime = 0;
}
cHiccup1OnTime = 0;
cHiccup1OnFlag = 0;
}
}
}
/******************************************************
name: Process_VN12
brief: process the -12V output
input: none
output: none
*****************************************************/
static void Process_VN12(void)
{
//POWER Dispatch. Under/over voltage protection OR output error OR over temperature, we need to deal with them respectively
//if((AlarmStatus.allbits & WARNING_FLAG_VN12) == 0)//IN_OK must be set to 1
if(AlarmStatus.alarm.IN_OK == 1 && AlarmStatus.alarm.VN12_OPEN_LOOP == 0 && AlarmStatus.alarm.OTP == 0)
{
if(AlarmStatus.alarm.VN12_EN == 1)//only when all the conditions are ready can we turn on the -12V output
{
VN12_ON();
AlarmStatus.alarm.VN12_ON = 1;
}
else
{
VN12_OFF();
AlarmStatus.alarm.VN12_ON = 0;
}
cHiccup2OffTime = 0;
cHiccup2OnTime = 0;
cHiccup2OnFlag = 0;
}
else if(AlarmStatus.alarm.VN12_OPEN_LOOP == 1)//OPEN-LOOP hiccup
{
if(++cHiccup2OffTime>=HICCUP_OFF_TIME)//OFF time
{
VN12_ON();
cHiccup2OffTime = HICCUP_OFF_TIME;
cHiccup2OnFlag = 1;
}
else
{
VN12_OFF();
cHiccup2OnTime = 0;
cHiccup2OnFlag = 0;
}
}
else //other exceptions processed in Task_Manager function
{
//TODO
}
/*
else//other exceptions, such as OVP, UVP or OTP
{
//rly_rdy_timeout=0;
v12_rdy_timeout=0;
cHiccup2OffTime=0;
cHiccup2OnTime=0;
cHiccup2OnFlag=0;
#if(RELAY_ALWAYS_ON == 0)
{
AlarmStatus.alarm.RLY_READY = 0;
RELAY_OFF();
}
#endif
//VDC_OK=0;
if(AlarmStatus.alarm.IN_OK == 0)//only after the IN_OK is set to 0 can set we turn OFF the -12V
{
if(++v12_off_timeout>=DELAY_TURN_OFF_TIME)
{
v12_off_timeout = 0;
VN12_OFF();
}
}
else//OTHER exception rather than IN_OK error, turn OFF the output directly
{
VN12_OFF();
}
AlarmStatus.alarm.VN12_EN = 0;
AlarmStatus.alarm.VN12_ON = 0;
}*/
if(cHiccup2OnFlag == 1)//hiccup
{
if(++cHiccup2OnTime>=HICCUP_ON_TIME)//the -12V output is on for 40 milli-seconds
{
if(iAd_data[2]>=OP_VN12_OFF_AD)//if it is still in OPEN-LOOP state
{
cHiccup2OffTime = 0;
}
else
{
if(iAd_data[2]<OP_VN12_RECOVER_AD)//recovered, return difference is necessary
{
AlarmStatus.alarm.VN12_OPEN_LOOP = 0;
}
cHiccup2OffTime = 0;
}
cHiccup2OnTime = 0;
cHiccup2OnFlag = 0;
}
}
}
/**
*@name: Task_Manager
*@description: switch all the states according to the power-supply status
e.g. if the VDC is over-voltage, we need to shut off the main output
At the same time, we turn ON/OFF the signals according to the status
*@params: none
*@return: none
*/
static void Task_Manager(void)
{
//===============================================================================
// ****** DC power-supply, following the DC powered-on sequences ******
// 1. DC_OK -->START
// 2. delay 50ms -->RELAY ON
// 6. delay 100ms -->49V ON
//===============================================================================
if(AlarmStatus.alarm.VDC_UVP == 0 && AlarmStatus.alarm.VDC_OVP == 0)
{
AlarmStatus.alarm.IN_OK = 1;//It is not until the VDC is normal that we set the IN_OK signal to 1.
v12_off_timeout = 0;
}
else
{
AlarmStatus.alarm.IN_OK = 0;
}
#if (RELAY_ALWAYS_ON == 1)
{
if(rly_rdy_timeout++ >= RLY_RDY_TIMEOUT)
{
rly_rdy_timeout = 0;
AlarmStatus.alarm.RLY_READY = 1;
}
}
#else
//if(AlarmStatus.alarm.IN_OK == 1 && AlarmStatus.alarm.OTP == 0)//delay some time before we open the relay
if(AlarmStatus.alarm.IN_OK == 1)
{
if(rly_rdy_timeout++ >= RLY_RDY_TIMEOUT) //ONLY when the first time the device is powered-on do we need to delay some time to turn ON the relay.
{
//rly_rdy_timeout=0;
rly_rdy_timeout = RLY_RDY_TIMEOUT;
AlarmStatus.alarm.RLY_READY = 1;
}
}
else
{
//rly_rdy_timeout=0;
AlarmStatus.alarm.RLY_READY = 0;
}
#endif
if(AlarmStatus.alarm.RLY_READY == 1) //prepare to open +12V AND -12V output
{
if(v12_rdy_timeout++>=V12OUT_RDY_TIMEOUT)//delay 100ms
{
//The reason why we don't clear variable v12_rdy_timeout is because we turn ON the -12V and +12V directly without any delay once the OTP is recovered.
//v12_rdy_timeout=0;
v12_rdy_timeout = V12OUT_RDY_TIMEOUT;
AlarmStatus.alarm.VP12_EN = 1;
AlarmStatus.alarm.VN12_EN = 1;
}
}
else
{
v12_rdy_timeout = 0;
AlarmStatus.alarm.VP12_EN = 0;
AlarmStatus.alarm.VN12_EN = 0;
}
//if(AlarmStatus.alarm.VP12_EN == 1 && AlarmStatus.alarm.VN12_EN == 1) //ONLY when the +12V or -12V is powered-on can we turn ON the 50V output
if(AlarmStatus.alarm.VP12_EN == 1 || AlarmStatus.alarm.VN12_EN == 1)
{
if(v50_rdy_timeout++>=V50OUT_RDY_TIMEOUT)//delay 100ms
{
v50_rdy_timeout = 0;
AlarmStatus.alarm.V50_EN = 1;
}
}
else//the 50V output is not affected by the 12V and -12V
{
//v50_rdy_timeout = 0;
//AlarmStatus.alarm.V50_EN = 0;
}
if(PV12_OVP == 1)//+12V OVP check
{
vp12_ovp_H++;
}
else
{
vp12_ovp_L++;
}
if(io_cnt>=IO_FILTER_CNT)//set or clear OVP flag
{
if(vp12_ovp_H>=IO_FILTER_VALID_CNT)
{
AlarmStatus.alarm.VP12_OVP = 1;
}
if(vp12_ovp_L>=IO_FILTER_VALID_CNT)
{
AlarmStatus.alarm.VP12_OVP = 0;//no need to clear this bit
}
vp12_ovp_H = 0;
vp12_ovp_L = 0;
io_cnt = 0;
}
else
{
io_cnt++;
}
if(AlarmStatus.alarm.VP12_OVP == 1)//ONLY when the OVP flag is set can we start counting
{
if(ovp1_time_cnt++>=LOCK_TIMEOUT)
{
ovp1_time_cnt = LOCK_TIMEOUT;
}
}
else
{
ovp1_time_cnt = 0;
}
if(AlarmStatus.alarm.IN_OK == 1)//VDC error information
{
IN_OK();
}
else
{
IN_NOT_OK();
}
if(AlarmStatus.alarm.RLY_READY == 1)//power on the relay
{
RELAY_ON();
}
else
{
RELAY_OFF();
}
if(AlarmStatus.alarm.V50_EN == 1)//turn ON the 50V output
{
V50_ON();
}
else
{
V50_OFF();
}
if(AlarmStatus.alarm.IN_OK == 0 || AlarmStatus.alarm.OTP == 1)//other exceptions, such as OVP, UVP or OTP
{
//rly_rdy_timeout=0;//open the delay directly from exception without any delay
if(AlarmStatus.alarm.IN_OK == 0)//Each time VDC is abnormal, we need to delay some to open the output, including -12V, +12V and 50V
{
v12_rdy_timeout = 0;
}
v50_rdy_timeout = 0;
cHiccup1OffTime = 0;
cHiccup1OnTime = 0;
cHiccup1OnFlag = 0;
cHiccup2OffTime = 0;
cHiccup2OnTime = 0;
cHiccup2OnFlag = 0;
AlarmStatus.alarm.VP12_EN = 0;
AlarmStatus.alarm.VP12_ON = 0;
AlarmStatus.alarm.VN12_EN = 0;
AlarmStatus.alarm.VN12_ON = 0;
AlarmStatus.alarm.V50_EN = 0;
VN12_OFF();
VP12_OFF();
V50_OFF();
#if(RELAY_ALWAYS_ON == 0)
{
if(AlarmStatus.alarm.IN_OK == 0)//don't need to turn OFF the relay once OTP flag is set
{
AlarmStatus.alarm.RLY_READY = 0;
RELAY_OFF();
}
}
#endif
/*
//VDC_OK=0;
if(AlarmStatus.alarm.IN_OK == 0)//only after the IN_OK is set to 0 can set we turn OFF the +12V
{
if(++v12_off_timeout>=DELAY_TURN_OFF_TIME)
{
v12_off_timeout=0;
VN12_OFF();
VP12_OFF();
}
}
else//OTHER exception rather than IN_OK error, turn OFF the output directly
{
VN12_OFF();
VP12_OFF();
}*/
}
}
/**
*@name: OVP_Lock_Check
*@description: output over voltage process
Here we need to lock the 12V output or delay some time then lock it.
*@params: none
*@return: none
*/
static void OVP_Lock_Check(void)
{
#if(LOCK_NO_DELAY == 1)//lock the output immediately once OVP occurs
{
//if(AlarmStatus.alarm.VP12_OVP == 1 || AlarmStatus.alarm.VN12_OVP == 1)
//Once the OVP flag is set, we'll never clear it.
if(AlarmStatus.alarm.VP12_OVP == 1)
{
AlarmStatus.alarm.OUT_LOCKED = 1;
}
else
{
//AlarmStatus.alarm.OUT_LOCKED=0;
}
}
#else
{
if(ovp1_time_cnt>=LOCK_TIMEOUT || ovp2_time_cnt>=LOCK_TIMEOUT)
{
AlarmStatus.alarm.OUT_LOCKED=1;
}
else
{
AlarmStatus.alarm.OUT_LOCKED=0;
}
}
#endif
}
/**
*@name: Process_1MS
*@description: 1 milli-second task polling
*@params: none
*@return: none
*/
void Process_1MS(void)
{
if(cFlag_1ms == 0)
{
return;
}
cFlag_1ms = 0;
Task_Manager();
Process_VP12();
Process_VN12();
}
/* main task */
int main(void)
{
Init_OSC();
Init_Timer();
Init_GPIO();
Init_ADC();
INTCONbits.PEIE = 1;
INTCONbits.GIE = 1;
while(1)
{
ADC_Process();
Fault_Check();
Process_1MS();
OVP_Lock_Check();
}
return 0;
}
我想请教下各位:关于AD检测,除了累加求和然后再求平均值外,还有没有其他更好的方法?如果AD值在进行了求平均值后还有没有必要再滤波一定的时间再次确定检测结果?原来程序这样做的目的是避免误操作,举个例子,在电压检测时有可能会有尖峰电压而且持续时间很短,如果不做滤波处理可能就会产生误动作。
麻烦大家对我的程序结构提出一些改善意见,特别是可以优化的地方,谢谢了!