📄 vblank.c
字号:
/*
$Workfile: vblank.c $
$Revision: 1.17 $
$Date: Sep 19 2005 04:15:06 $
*/
#define __D_DDC2BI_C__
//******************************************************************************
//
// Copyright (C) 2002. GENESIS MICROCHIP INC.
// All rights reserved. No part of this program may be reproduced.
//
// Genesis Microchip Inc., 165 Commerce Valley Dr. West
// Thornhill, Ontario, Canada, L3T 7V8
//
//==============================================================================
//
// MODULE: vblank.c
//
// USAGE: This module contains functions running functions indide the
// vertical blanking time.
//
//******************************************************************************
//******************************************************************************
// I N C L U D E F I L E S
//*****************************************************************************
#include "inc\all.h"
#include "system\mcu186.h"
#include "math.h"
#include "mem.h"
//******************************************************************************
// L O C A L D E F I N I T I O N S
//******************************************************************************
#if (USE_ADC_CALIBRATION_ISR)
#define ADC_CALIBRATION_ISR_IN_RAM 1 // 0: The code will be in ROM
#define DEBUG_VBLANK_ISR 0
#if DEBUG_VBLANK_ISR && DEBUG_MSG
#define msg(a,b) gm_Print((const char far *)a,b)
#else
#define msg(a,b)
#endif
//******************************************************************************
// F U N C T I O N S
//******************************************************************************
//void InitCalValues(void)
// Calucultates rgb_cal[] values and stores in NVRAM.
//void InitADCCalibration(BYTE integrity) based on 'integrity' either gets
// rgb_cal[] values from NVRAM or calls gm_AutoAdcInitMain and then calls
// InitCalVaues() above.
//void ADCCalibration(void)
// Background routine which adjusts OFFSET2.
//void far ADCCalibrationISR(void)
// Main body of ISR, collects rgb_tot[] values.
//void interrupt ext_VBlankISR(void)
// New IRQ4 interrupt, it calls DDC2Bi is (old irq4) or ADCCalibrationISR
// based on interrupt status.
//void InitADCCalibrationISR(BYTE integrity)
// Main initialization, it sets up the new IRQ4 vector and calls
// InitADCCalibration() above.
//******************************************************************************
// S T A T I C F U N C T I O N P R O T O T Y P E S
//******************************************************************************
static void SaveOffset1Cal(void);
static gmt_RET_STAT LoadOffset1Cal(void);
//******************************************************************************
// C O D E
//******************************************************************************
// Arrays below are for the offset1 moving average. Just adjust
// OFFSET_ARRAY_SIZE to whatever average size you like.
#define OFFSET2_ARRAY_SIZE 64
// ADC_READINGS_PER_INTERRUPT is how many ADC readings will take place in
// one interrupt. If this value is set equal to OFFSET2_ARRAY_SIZE, then
// all the readings will take place in one interrupt. If the value is set
// to half of OFFSET2_ARRAY_SIZE, then it will take two interrups to
// get all the data.
#define ADC_READINGS_PER_INTERRUPT 1
//The threshold value is how many counts the ADC value has to change
//before it adjusts the offset1 value.
#define ADC_THRESHOLD ((OFFSET2_ARRAY_SIZE * 8) / 10)
// DDC2Bi holdoff time is used to avoid servicing the VBlanking ADCCalibration
// routine if DDC2Bi traffic is found. This will free up more processing time
// for the DDC2Bi handler which takes a big hit by having the IRQ4 hander
// out in external ROM. Normally the DDC2Bi handler is in IROM.
// The units are in VBlank interrupts, a value of 100 means ADCCalibraion
// won't be called for 100 VBlanking intervals if DDC2Bi traffic is found.
#define VBLANK_DDC2BI_HOLDOFF 100
// Glitch threshold is used when reading the ADC directly. The routines
// will read the ADC twice for each reading, and if the two readings
// are within the glitch threshold, then that value is considered good.
#define ADC_GLITCH_THRESHOLD 3
// ADC_GLITCH_RETRY is how many times to read the ADC channels if a glitch
// is encountered. To high a value and you may spend to much time in the
// ISR resulting in garbage on the display. The number is the combined
// reading of all 3 ADCs.
#define ADC_GLITCH_RETRY 9
// Values below are DAC, OFFSET2 and GAIN values to be programmed in when
// reading the ADC during blanking. These values are held constant so we
// can monitor the ADC drift and adjust OFFSET1 accordingly.
#define ADC_TESTDAC_VALUE 0x18 //0x80...ideally its better not to have this on edge for large transistions (7F -80)
WORD rgb_cal[3];
SWORD rgb_tot[3];
volatile BYTE rgb_index;
//******************************************************************
// FUNCTION : void InitCalValues
// USAGE : This is where the actual computation for the
// calibration values is performed. This function
// Is called during initialization if there is no
// calibration values in NVRAM.
// **NOTE** This function should be called whenever
// an gm_AutoColorBalance is performed. The Auto
// Color balance routine will calculate new OFFSET2
// and Gain values for each ADC. If this function isn't
// called to get new calibration values, then the ISR
// will just 'adjust' the OFFSET2 values back.
//******************************************************************
void InitCalValues(void)
{
BYTE i,j,color,one,two,SysMask,Fas2;
WORD W_Timeout;
DWORD timeout;
// turn off calibration ISR
SysMask = gm_ReadRegByte(MISC_OCMMASK);
// if rgb_index is zero, then routine is running, but background ADCCalibration
// hasn't executed yet and interrupt will be off, so turn it back on.
if(rgb_index == 0x00)
SysMask |= D_VBLANK;
gm_ClearRegBitsByte(MISC_OCMMASK, D_VBLANK);
rgb_cal[0] = 0;
rgb_cal[1] = 0;
rgb_cal[2] = 0;
for(j=0;j<OFFSET2_ARRAY_SIZE;j+= ADC_READINGS_PER_INTERRUPT)
{
timeout = 0UL;
// clear vblank status bit.
gm_WriteRegByte(DISPLAY_STATUS, D_VBLANK);
// wait for vblank, otherwise you'll see a glitch in the display as this
// calibration routine runs.
do
{
timeout++;
if(timeout >= 0x500UL) //timeout within about 20ms
break;
}while((gm_ReadRegByte(DISPLAY_STATUS) & D_VBLANK) == 0);
gm_SetRegBitsByte(ADC_CONTROL, AC_COUPLE_EN | CLAMP_EN | ADC_ENABLE | ( gmvb_SogSensitivity << SOG_SENS_SHIFT));
// set up DAC
gm_WriteRegByte(ADC_TESTDAC, RED_ST | GRN_ST | BLU_ST); // Enable ADC Self Test for all RGB channels.
gm_WriteRegByte(ADC_DAC_DATA, ADC_TESTDAC_VALUE);
#ifdef ADC_B_PHASE
Fas2 = gm_ReadRegByte(ADC_B_PHASE);
gm_WriteRegByte(ADC_B_PHASE, 0x00); // Reduced the BW before calibration.
#else
Fas2 = gm_ReadRegByte(ADC_FAS2);
gm_WriteRegByte(ADC_FAS2, 0x00); // Reduced the BW before calibration.
#endif
for(color=0; color < 3;color++)
{
i = ADC_READINGS_PER_INTERRUPT;
W_Timeout = (ADC_READINGS_PER_INTERRUPT * 4);
while(i && W_Timeout)
{
one = gm_ReadRegByte(ADC_DATA_RED + color);
two = gm_ReadRegByte(ADC_DATA_RED + color);
if(abs(one-two) < ADC_GLITCH_THRESHOLD)
{
rgb_cal[color] += (WORD)two;
i--;
}
W_Timeout--;
}
// if timeout occurs, better to set calibration value to ADC_TESTDAC_VALUE than 0x00, it will
// likely produce less color shift if all else is working fine.
if(W_Timeout == 0)
{
msg("InitCalValues: timeout!!",0);
rgb_cal[color] += (ADC_TESTDAC_VALUE * ADC_READINGS_PER_INTERRUPT);
}
// Round up! (add 0.5)
#if (OFFSET2_ARRAY_SIZE > 1)
rgb_cal[color] += (ADC_READINGS_PER_INTERRUPT / 2);
#endif
}
// restore DAC
// Turn off test ADC
gm_WriteRegByte(ADC_TESTDAC, 0);
// Restore BW
#ifdef ADC_B_PHASE
gm_WriteRegByte(ADC_B_PHASE, Fas2);
#else
gm_WriteRegByte(ADC_FAS2, Fas2);
#endif
}
msg("**r_cal %x",(WORD)rgb_cal[0]);
msg("**g_cal %x",(WORD)rgb_cal[1]);
msg("**b_cal %x",(WORD)rgb_cal[2]);
SaveOffset1Cal();
// turn on calibration ISR
rgb_index = OFFSET2_ARRAY_SIZE;
rgb_tot[0] = rgb_cal[0];
rgb_tot[1] = rgb_cal[1];
rgb_tot[2] = rgb_cal[2];
gm_WriteRegByte(DISPLAY_STATUS, D_VBLANK);
gm_WriteRegByte(MISC_OCMMASK, SysMask);
}
//******************************************************************
// FUNCTION : void InitADCCalibration
// USAGE : Based on 'integrity' the routine will either
// Read the calibration values from NVRAM or
// compute new values. If generating new
// calibration values, then first multiple
// OFFSET1 calibrations are perfromed (gm_AutoADCInitMain())
// and averaged together to get a real 'average'
// OFFSET1 value.
//******************************************************************
void InitADCCalibration(BYTE integrity)
{
BYTE i;
WORD red, grn, blu;
// First see if OFFSET1 values are saved in NVRAM.
if(integrity && (LoadOffset1Cal() == gmd_OK))
{
msg("offset1 values restored from NVRAM",0);
msg("red %x",(WORD)gm_ReadRegByte(RED_OFFSET1));
msg("grn %x",(WORD)gm_ReadRegByte(GRN_OFFSET1));
msg("blu %x",(WORD)gm_ReadRegByte(BLU_OFFSET1));
msg("r_cal %x",rgb_cal[0]);
msg("g_cal %x",rgb_cal[1]);
msg("b_cal %x",rgb_cal[2]);
gm_SetRegBitsByte(ADC_MODULATION,CAL_TRIG);
gm_ClearRegBitsByte(ADC_MODULATION,CAL_TRIG);
return;
}
else
{
#define AUTO_ADC_INIT_AVG 16
red = grn = blu = 0;
for(i=0;i<AUTO_ADC_INIT_AVG;i++)
{
gm_AutoADCInitMain();
red += gm_ReadRegByte(RED_OFFSET1);
grn += gm_ReadRegByte(GRN_OFFSET1);
blu += gm_ReadRegByte(BLU_OFFSET1);
}
red += AUTO_ADC_INIT_AVG / 2;
grn += AUTO_ADC_INIT_AVG / 2;
blu += AUTO_ADC_INIT_AVG / 2;
red /= AUTO_ADC_INIT_AVG;
grn /= AUTO_ADC_INIT_AVG;
blu /= AUTO_ADC_INIT_AVG;
}
gm_WriteRegByte(RED_OFFSET1,red);
gm_WriteRegByte(GRN_OFFSET1,grn);
gm_WriteRegByte(BLU_OFFSET1,blu);
InitCalValues();
}
//******************************************************************
// FUNCTION : void ADCQuickCalibrate
// USAGE : Function performs the same actions the calibration
// ISR and ADCCalibrate() perform, except it counts
// on the display being disabled, and thus doesn't
// have to wait for the vblanking time, it can
// run continousely until offset2 stabalizes.
//******************************************************************
void ADCQuickCalibrate(void)
{
BYTE color, Fas2;
BYTE B_Adc[3];
BYTE B_rgboff2[3];
WORD W_Done = (OFFSET2_ARRAY_SIZE * 0x3F); // maximum timeout.
#define STABLE_DONE_COUNT 3 // how many times offset2 has to be stable to be declared finished.
BYTE B_StableDone = STABLE_DONE_COUNT;
// set up DAC
gm_WriteRegWord(ADC_TESTDAC,(((WORD)ADC_TESTDAC_VALUE << 8) | RED_ST | GRN_ST | BLU_ST));
#ifdef ADC_B_PHASE
Fas2 = gm_ReadRegByte(ADC_B_PHASE);
gm_WriteRegByte(ADC_B_PHASE, 0x00); // Reduced the BW before calibration.
#else
Fas2 = gm_ReadRegByte(ADC_FAS2);
gm_WriteRegByte(ADC_FAS2, 0x00); // Reduced the BW before calibration.
#endif
rgb_tot[0] = rgb_cal[0];
rgb_tot[1] = rgb_cal[1];
rgb_tot[2] = rgb_cal[2];
do
{
B_Adc[0] = gm_ReadRegByte(ADC_DATA_RED);
if(abs(B_Adc[0] - gm_ReadRegByte(ADC_DATA_RED)) > ADC_GLITCH_THRESHOLD)
continue;
B_Adc[1] = gm_ReadRegByte(ADC_DATA_GRN);
if(abs(B_Adc[1] - gm_ReadRegByte(ADC_DATA_GRN)) > ADC_GLITCH_THRESHOLD)
continue;
B_Adc[2] = gm_ReadRegByte(ADC_DATA_BLU);
if(abs(B_Adc[2] - gm_ReadRegByte(ADC_DATA_BLU)) > ADC_GLITCH_THRESHOLD)
continue;
// if no glitch adjust the rgb_tot values.
rgb_tot[0] -= (SWORD)B_Adc[0];
rgb_tot[1] -= (SWORD)B_Adc[1];
rgb_tot[2] -= (SWORD)B_Adc[2];
rgb_index--;
if(rgb_index == 0x00)
{// when reach end run ADCCalibration()
// get current offset2 values.
B_rgboff2[0] = gm_ReadRegByte(RED_OFFSET2);
B_rgboff2[1] = gm_ReadRegByte(GRN_OFFSET2);
B_rgboff2[2] = gm_ReadRegByte(BLU_OFFSET2);
// adjust offset2.
// if new total is greater than the calibrated value, decrease the offset2,
// else increase it. rgb_tot[0] and red_cal haven't been divided by
// OFFSET2_ARRAY_SIZE to scale them back down, so instead ADC_THRESHOLD
// if multipied by OFFSET2_ARRAY_SIZE so we don't have to do the division
// (or shift)
for(color=0;color<3;color++)
{
BYTE adj;
// Round up! (add 0.5)
#if (OFFSET2_ARRAY_SIZE > 1)
rgb_tot[color] -= (OFFSET2_ARRAY_SIZE / 2);
#endif
// use a proportional control to speedup the calibration.
adj = abs(rgb_tot[color]) / ADC_THRESHOLD;
if(rgb_tot[color] < -(ADC_THRESHOLD))
{
B_StableDone = STABLE_DONE_COUNT; // if any change, reset stable done count
B_rgboff2[color]-= adj;
// check we didn't wrap around.
if(B_rgboff2[color] > 0x7F)
B_rgboff2[color] = 0x00;
}
if(rgb_tot[color] > (ADC_THRESHOLD))
{
B_StableDone = STABLE_DONE_COUNT; // if any change, reset stable done count.
B_rgboff2[color]+= adj;
// check we didn't wrap around.
if(B_rgboff2[color] > 0x7F)
B_rgboff2[color] = 0x7F;
}
// reset rgb_tot
rgb_tot[color] = rgb_cal[color];
}// for color
// restore offset2
gm_WriteRegByte(RED_OFFSET2,B_rgboff2[0]);
gm_WriteRegByte(GRN_OFFSET2,B_rgboff2[1]);
gm_WriteRegByte(BLU_OFFSET2,B_rgboff2[2]);
gm_WriteRegByte(HOST_CONTROL, IPFORCE_UPDATE);
rgb_index = OFFSET2_ARRAY_SIZE;
if((B_StableDone--) == 0)
break;
}// if index == 0
}while(W_Done--);
// restore DAC
gm_WriteRegWord(ADC_TESTDAC,0x00);
// Restore BW
#ifdef ADC_B_PHASE
gm_WriteRegByte(ADC_B_PHASE, Fas2);
#else
gm_WriteRegByte(ADC_FAS2, Fas2);
#endif
}
//******************************************************************
// FUNCTION : void ADCCalibration
// USAGE : Function looks at the rgb_tot values compared
// to saved ADC calibration values (rgb_cal), if
// the values are different, then the offset2
// registers should be adjusted to make these values
// equal.
//******************************************************************
void ADCCalibration(void)
{
BYTE color;
BYTE B_rgboff2[3];
WORD adj;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -