#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include "1KF01.h"
#include "ad7705.h"
#include "HC595.h"
#include "main.h"
#include "crc16.h"
#include "usart.h"
volatile uint16_t eep_cali_data_addr;//计算校准系数存放地址的变量
extern volatile uint8_t timer0out;//主函数定义的超时标志
extern volatile MODULE_INFO module_info;//主函数定义的模块信息结构体
extern volatile ALL_CALI_DATA all_cali_data;//主函数定义的读取EEPROM校准数据的结构体
extern volatile CHN_CALI_DATA module_cali_data[CALI_VALUE_NUM];//主函数定义的全局校准系数数组
//----------------------------------------------------------------------------
//函数:reset_AD7705
//功能:AD7705串行接口失步后将其复位。复位后要延时500us再访问
//----------------------------------------------------------------------------
void reset_AD7705(void)
{
uint8_t i;
SBI_AD_SDI;
for( i=0; i<36; i++ )
{
CBI_AD_SCK;
asm("nop");
asm("nop");
asm("nop");
asm("nop");
SBI_AD_SCK;
asm("nop");
asm("nop");
asm("nop");
asm("nop");
}
for( i = 0; i<10; i++)
_delay_us(50);
}
//------------------------------------------------------------------------------------------
//函数:read_AD7705_word
//功能:从AD7705读一个字的数据,共16bit
//------------------------------------------------------------------------------------------
uint16_t read_AD7705_word(void)
{
uint16_t data = 0;
for( uint8_t i = 0; i<16; i++)
{
CBI_AD_SCK;
asm("nop");
asm("nop");
data <<= 1;
if(!AD7705_DO)
data++;
SBI_AD_SCK;
asm("nop");
asm("nop");
asm("nop");
}
return data;
}
//------------------------------------------------------------------------------------------
//函数:read_AD7705_dword
//功能:从AD7705读一个24的数据
//------------------------------------------------------------------------------------------
uint32_t read_AD7705_dword(void)
{
uint32_t data = 0;
for( uint8_t i = 0; i<24; i++)
{
CBI_AD_SCK;
asm("nop");
asm("nop");
data <<= 1;
if(!AD7705_DO)
data++;
SBI_AD_SCK;
asm("nop");
asm("nop");
asm("nop");
}
return data;
}
//------------------------------------------------------------------------------------------
//函数:write_AD7705_byte
//功能:往AD7705写8位数据
//参数:IN - uint8_t data,要写入AD7705的数据
//------------------------------------------------------------------------------------------
void write_AD7705_byte( uint8_t data )
{
for( uint8_t i = 0; i<8; i++)
{
CBI_AD_SCK;
if(data&0x80)
SBI_AD_SDI;
else
CBI_AD_SDI;
data <<= 1;
asm("nop");
asm("nop");
SBI_AD_SCK;
asm("nop");
asm("nop");
asm("nop");
}
SBI_AD_SDI;//在空闲器件保持输入数据线为高
}
//------------------------------------------------------------------------------------------
//函数:write_AD7705_dword
//功能:往AD7705写24位数据,因为AD7705是24位的器件
//------------------------------------------------------------------------------------------
void write_AD7705_dword( uint32_t data )
{
for( uint8_t i = 0; i<24; i++)
{
CBI_AD_SCK;
if(data&0x800000)
SBI_AD_SDI;
else
CBI_AD_SDI;
data <<= 1;
asm("nop");
asm("nop");
SBI_AD_SCK;
asm("nop");
asm("nop");
asm("nop");
}
SBI_AD_SDI;//在空闲器件保持输入数据线为高
}
//------------------------------------------------------------------------------------------
//函数:AD7705_calibration
//
// EEPROM数据块格式为:
// 系统0校正值(4byte), 系统满量程校正值(4byte), 系统0校正标志(1byte),系统满量程校正标志
// (1byte),CRC16校验值(2byte),共12byte。
//
//参数:IN - uint8_t board, 0 - 对主板进行校正,1-对副板进行校正
// IN - uint8_t range, 需要校正的量程
// IN - uint8_t cali_type, 校正类型,ZERO_CALIBRATION- 0校正;
// FULL_CALIBRATION - 满量程校正
//
//返回:返回-1表示校准失败,非0表示校正成功,并返回相应的索引值
//------------------------------------------------------------------------------------------
int8_t AD7705_calibration( uint8_t const board, uint8_t const range, uint8_t const cali_type )
{
uint8_t setup_reg;//AD7705设置寄存器内容变量
int8_t index;//校准系数偏移位置
if(board > 1)//主副板标志,0-主板,1-副板
return -1;//返回错误指示
if(cali_type > 1)//校准类型,0-零校准,1-满量程校准
return -1;//返回错误指示
SBI_AD_CS;//操作595的时候要重新将7705的CS置无效
_delay_ms(1);//光耦延时,至少要等待30us左右
switch( range )
{
case B5V:
case S1VTo5V:
case S0VTo10V:
case B10V:
case S0TO20MA:
case S4TO20MA:
case B20MA:
if(board == 0)
{ //主板校准控制字设置,选择第5通道
send_byte_to_595(0,0x30);//副板无效
send_byte_to_595(0,0x00);//主板第一通道
send_byte_to_595(1,0x89);//模拟开关设置
}
else
{ //副板校准控制字设置,选择第1通道
send_byte_to_595(0,0x0a);//副板第一通道
send_byte_to_595(0,0x30);//主板无效
send_byte_to_595(1,0x89);//模拟开关设置
}
setup_reg = PGA_2|_BV(BUF);//大电压校准7705的设置,增益为2
index = 0;//校准系数偏移为0
break;
case B1V:
case B500MV:
if(board == 0)
{ // 第5通道
send_byte_to_595(0,0x30);
send_byte_to_595(0,0x20);
send_byte_to_595(1,0x83);//模拟开关设置
}
else
{ // 第1通道
send_byte_to_595(0,0x2a);
send_byte_to_595(0,0x30);
send_byte_to_595(1,0x83);//模拟开关设置
}
setup_reg = PGA_2|_BV(BUF);//中电压校准7705的设置,增益为2
index = 1;//校准系数偏移为1
break;
case B50MV:
if(board == 0)
{ // 第5通道
send_byte_to_595(0,0x30);
send_byte_to_595(0,0x20);
send_byte_to_595(1,0x83);//模拟开关设置
}
else
{ // 第1通道
send_byte_to_595(0,0x2a);
send_byte_to_595(0,0x30);
send_byte_to_595(1,0x83);//模拟开关设置
}
setup_reg = PGA_32|_BV(BUF);//小电压校准7705的设置,增益为32
index = 2;//校准系数偏移为2
break;
case R600: // 0.255ma
if(board == 0)
{ // 第5通道
send_byte_to_595(0,0x30);
send_byte_to_595(0,0x00);
send_byte_to_595(1,0x36);//第一次测量模拟开关设置
}
else
{ // 第1通道
send_byte_to_595(0,0x0a);
send_byte_to_595(0,0x30);
send_byte_to_595(1,0x36);//第一次模拟开关设置
}
setup_reg = PGA_4|_BV(BUF);
index = 3;//校准系数偏移为3,index=4为第二次校准存放位置
break;
case R6000: // 0.255ma
if(board == 0)
{ // 第5通道
send_byte_to_595(0,0x30);
send_byte_to_595(0,0x00);
send_byte_to_595(1,0x36);//第一次模拟开关设置
}
else
{ // 第1通道
send_byte_to_595(0,0x0a);
send_byte_to_595(0,0x30);
send_byte_to_595(1,0x36);//第一次模拟开关设置
}
setup_reg = PGA_1|_BV(BUF);
index = 5;//校准系数偏移为5,index=6为第二次校准存放位置
break;
default:
return -1;//量程不存在则返回错误指示
}
//计算校准系数在EEPROM中的存放地址,副板要跨越12*14=168个字节
eep_cali_data_addr = 168*board+index*CALI_DATA_LEN;
for( uint8_t resistor = 0; resistor<2; resistor++ )//电阻要进行两次校准
{
CBI_AD_CS;//使能7705的数字接口
_delay_ms(1);//光耦延时,至少要等待30us左右
reset_AD7705();//7705复位
for( uint8_t freq = 0; freq < 2; freq++ )//AD7705两次不同频率的校准,0-50HZ校准,1-60HZ校准
{
//写7705的CLOCK寄存器
write_AD7705_byte( SEL_WR_CLOCK_REG );
if( 0 == freq )
write_AD7705_byte( F_50HZ|_BV(CLK) );//select 50HZ
else
write_AD7705_byte( F_60HZ|_BV(CLK) );//select 60HZ
_delay_ms(10);//延时
eep_cali_data_addr += 84*freq;//60HZ频率校准系数存放的位置还要偏移12*7=84个字节
if( ZERO_CALIBRATION == cali_type )
{
//AD7705系统零校准
write_AD7705_byte( SEL_WR_SETUP_REG );
write_AD7705_byte( setup_reg|SYS_ZERO_CALI );//把设置内容写入7705的设置寄存器
start_timer0(50);
_delay_ms(2);
while(!AD7705_RDY)
{
if( 1 == timer0out)
{ // 内部0校正出错
SBI_AD_CS;//将7705片选置为无效,由于光耦延时,至少要延时30us
_delay_us(66);
STOP_TIMER0;//超时返回前先关闭定时器中断
return -1;//超时返回
}
}
STOP_TIMER0;
// 如果系统0校正正确,读OFFSET寄存器
write_AD7705_byte(SEL_RD_OFFSET_REG);
all_cali_data.data_frame.ZS.dword_data = read_AD7705_dword();//保存到校准系数结构体变量的ZS变量
/*
put_c( 0x80 );//监控零校正数据
put_c( resistor );
put_c( freq );
put_c( all_cali_data.data_frame.ZS.byte_data[0]);
put_c( all_cali_data.data_frame.ZS.byte_data[1]);
put_c( all_cali_data.data_frame.ZS.byte_data[2]);
put_c( all_cali_data.data_frame.ZS.byte_data[3]);
*/
all_cali_data.data_frame.flag[0] = 0x55;//设置零校准正确标志
//计算校验和
all_cali_data.data_frame.crc_val = checksum( (unsigned char*)(&all_cali_data.data[0]), CALI_DATA_LEN-2 );
//保存校正值到EEPROM
eeprom_busy_wait();
eeprom_write_block( (unsigned char*)(&all_cali_data.data[0]), (void*)eep_cali_data_addr, CALI_DATA_LEN );
}
else//满量程校正,零校准在满量程校准之前进行
{
eeprom_busy_wait();//读取EEPROM的零校准系数
eeprom_read_block( (unsigned char*)(&all_cali_data.data[0]), (void*)eep_cali_data_addr,CALI_DATA_LEN);
//计算校验和,并验证是否可用:零校准标志和校验和两个条件中任何一个不正确都将认为是错误
if( checksum( (unsigned char*)(&all_cali_data.data[0]), CALI_DATA_LEN-2 ) != all_cali_data.data_frame.crc_val
|| 0x55 != all_cali_data.data_frame.flag[0] )
{
SBI_AD_CS;//AD7705的数字接口置为无效
_delay_us(66);//由于光耦延时,至少要延时30us左右
return -1;//如果零校准系数不可用则返回错误只是-1
}
//将ZS写入offset寄存器
write_AD7705_byte(SEL_WR_OFFSET_REG);
write_AD7705_dword(all_cali_data.data_frame.ZS.dword_data);
write_AD7705_byte(SEL_WR_SETUP_REG);//设置7705的设置寄存器
write_AD7705_byte(setup_reg|SYS_FULL_CALI);
start_timer0(50);
_delay_ms(2);
while(!AD7705_RDY)
{
if( 1 == timer0out)
{ // 系统满量程校正出错
SBI_AD_CS;//AD7705的数字接口置为无效
_delay_us(66);//由于光耦延时,至少要延时30us左右
STOP_TIMER0;
return -1;
}
}
STOP_TIMER0;
// 如果内部满量程校正正确,读FULL SCALE寄存器
write_AD7705_byte(SEL_RD_FULL_REG);
all_cali_data.data_frame.GS.dword_data = read_AD7705_dword();//保存到校准系数结构体变量的GS变量
/*
put_c( 0x81 );//监控满量程校正数据
put_c( resistor );
put_c( freq );
put_c( all_cali_data.data_frame.GS.byte_data[0]);
put_c( all_cali_data.data_frame.GS.byte_data[1]);
put_c( all_cali_data.data_frame.GS.byte_data[2]);
put_c( all_cali_data.data_frame.GS.byte_data[3]);
*/
all_cali_data.data_frame.flag[1] = 0xaa;//设置满量程校准正确标志
all_cali_data.data_frame.crc_val = checksum( (unsigned char*)(&all_cali_data.data[0]),CALI_DATA_LEN-2 );
// 保存校正值到EEPROM第一个地址
eeprom_busy_wait();
eeprom_write_block( (unsigned char*)(&all_cali_data.data[0]), (void*)eep_cali_data_addr, CALI_DATA_LEN );
}
}
if( 0==resistor && (R600==range || R6000==range) )//如果是电阻校准则要进行两次校准
{
SBI_AD_CS;//操作595的时候要重新将7705的CS置无效
_delay_ms(1);//由于光耦延时,至少要延时30us左右
if(0==board)
{ // 第5通道
send_byte_to_595(0,0x30);
send_byte_to_595(0,0x20);
send_byte_to_595(1,0x32);//第二次电阻校准模拟开关设置
}
else
{//第1通道
send_byte_to_595(0,0x2a);
send_byte_to_595(0,0x30);
send_byte_to_595(1,0x32);//第二次电阻校准模拟开关设置
}
eep_cali_data_addr -= 72; //第二次电阻校准时,校准系数存放地址从60HZ第一次测量的地址跳转到50HZ第二次测量的地址
}
else
{
break;//非电阻量程只校准一次便退出。
}
}
SBI_AD_CS;//将AD7705的CS置无效
_delay_us(66);//因为光耦延时,至少要延时30us左右
return index;//返回校准系数偏移位置
}
//------------------------------------------------------------------------------------------
//函数:start_AD7705
//功能:先写offset寄存器,再写full scale寄存器,然后启动7705进行单次转换
//第二次电阻测量的校准系数存放位置要加1,由cali标识
//------------------------------------------------------------------------------------------
void start_AD7705(uint8_t const channel, uint8_t const cali)
{
//cli();//设置7705的过程中关闭中断,防止被干扰导致设置错误
//写OFFSET寄存器
write_AD7705_byte(SEL_WR_OFFSET_REG);
write_AD7705_dword( module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sys0.dword_data);
//write_AD7705_dword( module_cali_data[ module_info.channel_info[channel].cali_index-cali ].cali_data_sys0.dword_data);
/*
put_c(cali);
put_c(0x00);//监控量程的的零校正系数
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sys0.byte_data[0]);
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sys0.byte_data[1]);
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sys0.byte_data[2]);
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sys0.byte_data[3]);
*/
//写满量程校准寄存器
write_AD7705_byte(SEL_WR_FULL_REG);
write_AD7705_dword( module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sysf.dword_data);
//write_AD7705_dword( module_cali_data[ module_info.channel_info[channel].cali_index-cali ].cali_data_sysf.dword_data);
/*
put_c(0x11);//监控量程的的满校正系数
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sysf.byte_data[0]);
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sysf.byte_data[1]);
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sysf.byte_data[2]);
put_c(module_cali_data[ module_info.channel_info[channel].cali_index+cali ].cali_data_sysf.byte_data[3]);
*/
write_AD7705_byte(SEL_WR_SETUP_REG);//写配置信息
write_AD7705_byte(module_info.channel_info[channel].setup_reg);
write_AD7705_byte(SEL_WR_CLOCK_REG);//写时钟信息
write_AD7705_byte(module_info.clock_reg);
//sei();//设置7705结束后重新打开中断
}
//------------------------------------------------------------------------------------------
//函数:start_and_wait_AD7705
//功能:启动7705进行单次转换,并等待AD转换结束
//------------------------------------------------------------------------------------------
uint8_t start_and_wait_AD7705(uint8_t const channel, uint8_t const cali)
{
start_AD7705(channel, cali);//设置AD7705
start_timer0(6);
_delay_ms(2);
while(!AD7705_RDY)
{
if( 1 == timer0out)
{
STOP_TIMER0;
return 0;
}
}
STOP_TIMER0;
return 1;
}
|