矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 17047|回复: 34

LTC2400+MAX515,自校准的(直流)电压发生器

  [复制链接]
     
发表于 2012-6-23 12:43:59 | |阅读模式
本帖最后由 washu 于 2012-6-23 12:49 编辑

方案来自 Linear 的 An86:
AN86 - A Standards Lab Grade 20-Bit DAC with 0.1ppm/° Drift: The Dedicated Art of Digitizing One Part Per Million


cb551.jpg

设计理念:用 LTC2400 在一个闭环周期中测试一次“参考基准”,这里是 LM399,当然也可以是更加可靠的比如 LTZ1000,然后再测试一次 DAC 的输出,计算两者的比例差异并调整 DAC 使得两者保持一个恒定的比例,从而尽可能消除 DAC 的温度系数和老化的影响,将输出电压尽可能纠正到这个参考基准上。

cb553.jpg

设计目标:
1、6 位半的电压设定和回读分辨率 *
2、10-25ppm 的年稳
3、曲线拟合纠正后 10ppm 的线性误差

*实际上两个 DAC 各自具有 12bit 分辨率,合成步进可以达到 24bit


任意电压设定


10V
cb591.jpg

9V
cb593.jpg

8V
cb594.jpg

7V
cb595.jpg

6V
cb596.jpg

5V
cb597.jpg

4V
cb598.jpg

3V
cb599.jpg

9.876V
cb592.jpg
     
 楼主| 发表于 2012-6-23 12:48:13 |
强悍的温度自纠正能力

34 度时:
cb571.jpg

25 度时
cb573.jpg


22 度时
cb574.jpg

一个晚上之后,第二天重复测试
cb576.jpg



这个强悍的性能其实来源于内部参考 LM399 具有极好的温度系数,MAX515-LTC2400 的 Closeloop 将电路(主要是电阻)因为温度变化产生的误差很好纠正了

34 度时的参考电压
cb572.jpg

25 度时的参考电压
cb575.jpg
     
 楼主| 发表于 2012-6-23 12:51:23 |
电路图和程序

cb581.jpg

cb582.jpg

cb583.jpg

cb584.jpg
     
 楼主| 发表于 2012-6-23 12:52:22 |
本帖最后由 washu 于 2012-6-24 22:39 编辑

单片机上的源程序,加入了简要的注释,一些英文注释是我写程序的时候写给自己看的,语法什么的就不要深究了

$Device= m16                        '声明使用的 IC
$Stack = 32                               
$Clock = 8                        '声明单片机的频率
$Timer2 = PWM, 8, Normal        '使用定时器 2 作为 PWM,这是为了给 12864 LCD 提供负压
$GLCD HD61202, Data=PORTC, Ctrl=PORTA, 128, 64, 3         '声明使用一个 12864 LCD,其驱动内置于编译器中
$Gctrl EN=0, WR=2, DI=1, CS1=5, CS2=4                         '声明显示器使用的 IO
$ShiftIn Data=PORTA.6, Clock=PORTA.7, Msb                 '声明一个软 SPI 输入端口,由编译器驱动
$ShiftOut Data=PORTB.0, Clock=PORTB.1, Msb                '声明一个软 SPI 输出端口,由编译器驱动
$LeadChar="0", Format(2,5)
$Baud = 1200,n,8,1                                        '声明硬件串口的设置

Const Reference = &b00000001                                   '声明模拟开关对应常量
Const ExternaIN = &b00000010
Const GndOffset = &b00000100
Const Synthesis = &b00001000

Dim F2HD As Flash Byte                                        '声明软字体,由编译器提供
Dim F0HD As Flash Byte

Declare Function Setdac(MastDAC As Word, SlavDAC As Word) As Byte        '声明用户函数 设置 DAC
Declare Function SetVoltage(fVoltage As Float) As Byte                        '声明用户函数 设置电压
Declare Function SetChannel(Channel As Byte) As Byte                        '声明用户函数 设置模拟开关通道
Declare Function Getinlerr(fVoltage As Float) As Word                        '声明用户函数 获得非线性误差
Declare Interrupt Urxc()                                                '声明使用串口破中断

Enable Interrupts
Enable Urxc

Dim i As Byte
Dim iSmsetp As Byte
Dim iBusystp As Byte
Dim iTrd As Byte
Dim iDraw As Word
Dim CurrentChannel As Byte
Dim itm As Byte
Dim baCalChannel As Bit
Dim bUartset As Bit

Dim tTxt As String *15
Dim tm As String *7
Dim tCmd As String *4

Dim iDa As Byte
Dim iDb As Byte
Dim iDc As Byte
Dim iDd As Byte

Dim iTa As Byte
Dim iTb As Byte
Dim iTc As Byte
Dim iTd As Byte

Dim bREGas(33) As Byte
Dim bREGbs(33) As Byte
Dim bREGcs(33) As Byte
Dim bREGds(33) As Byte

Dim bREGar(33) As Byte
Dim bREGbr(33) As Byte
Dim bREGcr(33) As Byte
Dim bREGdr(33) As Byte

Dim fTrn As Float
Dim fOut As Float
Dim fReference As Float
Dim fSynthesis As Float
Dim fCalsacles As Float
Dim fCalfSynth As Float

Dim fVoltage As Float
Dim fError As Float
Dim fVset As Float
Dim fVtrn As Float
Dim Vseth As Word
Dim Vsetl As Word

'Dim uVseth As Word
'Dim uVsetl As Word

Dim fSetting As Float
Dim fCurerr As Float
Dim fPreerr As Float
Dim fIerr As Float
Dim fDerr As Float
Dim iPv As Word
Dim iIv As Word
Dim iDv As Word

Dim wErr As Word

Pwm2 = 3
Set DDRD.7 'Setting the VLCD Pwm Output
Set DDRB.2 'Setting the CD4094 Strobe Output
Set DDRA.3 'Setting the LCD 12864 PowerUp Reset Pin
Start Timer2

PORTB.2 = 0   'Setting the CD4094 Strobe Basical Status

' Reset and Init. the Graph LCD
PORTA.3 = 0
WaitMs 200
PORTA.3 = 1
GLcdInit
Gcls

FontSet F2HD
GLcd(0, 0), "Program Init"
Wait 1
Gcls


fVoltage = 9900000
stt:                                                        '程序重新开始执行的断点,设置新的电压值后会转跳到此重新执行
Gcls
' ******************** Init Variable ********************

CurrentChannel = &b01101000
baCalChannel = 0

fReference = 0
fSynthesis = 0
fCalsacles = 6880731

iPv = 100000
iIv = 400
iDv = 10

fPreerr = 0
fError = 0
fIerr = 0

bUartset = 0

For i = 0 To 33
    bREGas(i) = 0
    bREGbs(i) = 0
    bREGcs(i) = 0
    bREGds(i) = 0
    bREGar(i) = 0
    bREGbr(i) = 0
    bREGcr(i) = 0
    bREGdr(i) = 0   
Next i

' ******************** Init Variable ********************

Vseth = ReadEE(0)                                        '读取 EEPROM 中的校准数据
Vsetl = ReadEE(10)
fCalsacles = Vseth * 10000 + Vsetl


FontSet F0HD
Format(Scientific,6)
GLcd(0, 7), fCalsacles                                        '这是为了方便调试加入的,将 EEPROM 中的校准数据显示在 LCD 上


SetVoltage(fVoltage)
WaitMs 500

FontSet F2HD
Format(2, 5)
GLcd(0, 0), "Set"; fVoltage / 1000000; "VDC"

itm = 0
iSmsetp = 0

Do                                               
   iSmsetp = iSmsetp + 1
   If iSmsetp > 67 Then iSmsetp = 67
   'GLcd(0, 7), "Smthsetp:"; iSmsetp                        '等待 ADC LTC2400 的忙碌信号,然后读取它
      
   iBusystp = 0
   Do
     iBusystp = iBusystp + 1
     WaitMs 1   
   Loop While PINA.6 = 1   
   'GLcd(0, 7), "iBusystp:"; iBusystp
   baCalChannel = Not baCalChannel
   If baCalChannel = 1 Then
      CurrentChannel = SetChannel(Synthesis)
   Else
      CurrentChannel = SetChannel(Reference)
   End If
   WaitMs 10

   iDa = ShiftIn
   iDb = ShiftIn
   iDc = ShiftIn
   iDd = ShiftIn   
     
   iDd = iDd And &b11110000
   iDd = Swap(iDd)
   iTrd = iDc And &b00001111
   iTrd = Swap(iTrd)
   iTd = iDd + iTrd
   
   iTrd = iDc And &b11110000
   iTc = Swap(iTrd)
   iTrd = iDb And &b00001111
   iTrd = Swap(iTrd)
   iTc = iTrd + iTc   
   
   iTrd = iDb And &b11110000
   iTb = Swap(iTrd)
   iTrd = iDa And &b00001111
   iTrd = Swap(iTrd)
   iTb = iTrd + iTb  

   iTrd = iDa And &b00110000
   iTa = Swap(iTrd)                                        ' 读取 LTC2400 的 32 个 bit 并处理成 24bit 格式
   

   fTrn = 5 * (16777216 * iTa + 65536 * iTb + 256 * iTc + iTd - 33554432) / 16777216
   'fTrn = fTrn * 2002676 / 1000000
   FontSet F0HD
   
   Format(Scientific,6)                                        ' 用两串数组先入先出存放转换的结果,这样做是因为编译器提供的支持有限,无法使用单精度浮点数组。
   If baCalChannel = 0 Then
      'GLcd(0, 2), "Syn:"; fTrn
      For i = 1 To 32
          bREGds(i) = bREGds(i + 1)
          bREGcs(i) = bREGcs(i + 1)
          bREGbs(i) = bREGbs(i + 1)
          bREGas(i) = bREGas(i + 1)                 
      Next i
      bREGds(32) = iTd
      bREGcs(32) = iTc
      bREGbs(32) = iTb
      bREGas(32) = iTa
      fTrn = 0
      fOut = 0
      For i = 1 To 32
          iTa = bREGas(i)
          iTb = bREGbs(i)
          iTc = bREGcs(i)
          iTd = bREGds(i)
          fTrn = 5 * (16777216 * iTa + 65536 * iTb + 256 * iTc + iTd - 33554432) / 16777216
          fOut = fOut + fTrn
      Next i
      fOut = fOut / 32
      fSynthesis = fOut
      'GLcd(0, 4), "sm_Syn:"; fSynthesis
   Else
      'GLcd(0, 3), "Ref:"; fTrn
      For i = 1 To 32
          bREGdr(i) = bREGdr(i + 1)
          bREGcr(i) = bREGcr(i + 1)
          bREGbr(i) = bREGbr(i + 1)
          bREGar(i) = bREGar(i + 1)                 
      Next i
      bREGdr(32) = iTd
      bREGcr(32) = iTc
      bREGbr(32) = iTb
      bREGar(32) = iTa
      fTrn = 0
      fOut = 0
      For i = 1 To 32
          iTa = bREGar(i)
          iTb = bREGbr(i)
          iTc = bREGcr(i)
          iTd = bREGdr(i)
          fTrn = 5 * (16777216 * iTa + 65536 * iTb + 256 * iTc + iTd - 33554432) / 16777216
          fOut = fOut + fTrn
      Next i
      fOut = fOut / 32
      fReference = fOut     
      'GLcd(0, 5), "sm_Ref:"; fReference        
   End If
   
   fCalfSynth = fSynthesis / (fReference / fCalsacles)                        '经过先入先出寄存器后的转换结果被平滑,然后根据校准数据和非线性校正数据计算得到正确的电压值
   fCalfSynth = fCalfSynth + Getinlerr(fCalfSynth/1000000)
   'GLcd(0, 7), Getinlerr(fCalfSynth/1000000)
   
   If iSmsetp > 66 Then                                                        '当寄存器满了才认为可以进行电压修正
      FontSet F2HD
      Format(2,5)
      GLcd(0, 2), "Red"; fCalfSynth / 1000000; "VDC"
      'fSetting = fVoltage
      fCalfSynth = fCalfSynth
      fError = fVoltage - fCalfSynth                                        '获得测量值和设置值的差
      FontSet F0HD
      Format(3,1)
      GLcd(0, 4), "Err:"; fError; " uV"
      
      fCurerr = fVoltage - fCalfSynth                                        '用这个差值作为 PID 算法的入口,得到校正值去重设 DAC
      fIerr = fIerr + fCurerr
      fDerr = fPreerr
      fError = iPv * fCurerr + iIv * fIerr + iDv * fDerr
      fPreerr = fCurerr
      Format(Scientific,6)
      'GLcd(0, 5), "PID:"; fError
      
      fError = fVoltage + fError / 10000
      GLcd(0, 5), "Adj:"; fError
      SetVoltage(fError)
   End If
   FontSet F0HD
   GLcd(96, 7), Hex(iSmsetp); "/43"
   'GLcd(0, 5), "USet: "; tTxt
   If bUartset = 1 Then

      'GLcd(0, 6), "Usetf:"; fSetting
      
      fVoltage = fSetting
      'SetVoltage(fVoltage)
      
      iSmsetp = 0
      bUartset = 0
      GoTo stt
   End If   
Loop


Function Setdac(MastDAC As Word, SlavDAC As Word) As Byte                '设置 DAC 的子函数,将入口值转换为 MAX515 适合的格式;MAX515 的 CS 接在移位寄存器 CD4094 上
Local bMastMSB As Byte
Local bMastLSB As Byte
Local bSlavMSB As Byte
Local bSlavLSB As Byte
Local Channelset As Byte

bMastMSB = Msb(MastDAC)
bMastLSB = MastDAC - bMastMSB * 256
bSlavMSB = Msb(SlavDAC)
bSlavLSB = SlavDAC - bSlavMSB * 256

Channelset = CurrentChannel And &b00001111
ShiftOut &b00000000; &b00000000; &b00000000; &b00000000; Channelset
PORTB.2 = 1 : WaitUs 100: PORTB.2 = 0
Channelset = &b01100000 + Channelset
ShiftOut bMastMSB; bMastLSB; bSlavMSB; bSlavLSB; Channelset
PORTB.2 = 1 : WaitUs 100: PORTB.2 = 0
Return 0
End Function



Function SetVoltage(fVoltage As Float) As Byte                                '设置电压的子函数,根据电压合成公式逆运算得到两个 DAC 应当的设置值
' the addsch VOut = Gain * (Vmast * R2 + Vslave * R1) / (R1+R2), Fullscale of the Vm and Vs all = 5V, R1 = 1K ohm, R2 = 1M ohm, Gain = 2.01509
fVtrn = fVoltage
fVset = fVtrn
' Get the MastDAC Voltage Setting Value
fVtrn = fVtrn / 2015090                        'the 2015090 is the Buffamp Gain( x2.01509)
fVtrn = fVtrn * 1001000 - 5000        'the 1001000 is the MastRes 1K and the SlaveRes 1M
fVtrn = fVtrn * 4095 / 4995005        'the 4095 is the 12bit DAC Full scale; 4995005 scale by addsch.(added the MastDAC Fullscale Voltage)
Vseth = fVtrn

fVtrn = Vseth * 5000000 / 4095
fVtrn = fVtrn / 1001000
fVtrn = fVset - fVtrn * 2015090
fVtrn = fVtrn * 1000000 / 2015090
fVtrn = fVtrn * 4095 / 5000
Vsetl = fVtrn

Setdac(Vseth,Vsetl)
Return 0
End Function

Function SetChannel(Channel As Byte) As Byte                                '设置模拟开关的子函数
Local Channelset As Byte
Channelset = &b01100000 + Channel
ShiftOut &b00000000; &b00000000; &b00000000; &b00000000; Channelset
PORTB.2 = 1 : WaitUs 100: PORTB.2 = 0
Return Channelset
End Function

Function Getinlerr(fVoltage As Float) As Word                                '获得非线性校正数据,它是通过测量和拟合得到的一个二次三项式,不过这没有经过深入的验证
Local bInlerr As Word

fVtrn = fVoltage
fVset = fVtrn

fVtrn = fVtrn * fVtrn * 7381
fVset = (fVset * 12381 + 645120)
fVset = (fVset - fVtrn) / 1000

bInlerr =  fVset
Return bInlerr
End Function

Interrupt Urxc(),Save 1
Local uVseth As Word
Local uVsetl As Word
Local uMSBh As Byte
Local uLSBh As Byte
Local uMSBl As Byte
Local uLSBl As Byte

InputBin tTxt

tCmd = Mid(tTxt, 1, 3)
If tCmd = "STV" Then
   tm = Mid(tTxt,4,4)
   uVseth = Val(tm)
   tm = Mid(tTxt,8,4)
   uVsetl = Val(tm)
   fSetting = uVseth
   fSetting = fSetting * 10000
   fSetting = fSetting + uVsetl
   bUartset = 1
End If

If tCmd = "CAL" Then
   tm = Mid(tTxt,4,4)
   uVseth = Val(tm)
   tm = Mid(tTxt,8,4)
   uVsetl = Val(tm)
   
   'uMSBh = Msb(uVseth)
   'uMSBl = Msb(uVsetl)
   'uLSBh = uVseth - uMSBh * 256
   'uLSBl = uVsetl - uMSBl * 256
   WriteEE (0,uVseth)
   WriteEE (10,uVsetl)

   bUartset = 1  
End If


Enable Interrupts
End Interrupt


$Include "..\Help\samples\F2HD.bas"                                '编译器提供的软字体
$Include "..\Help\samples\F0HD.bas"
     
发表于 2012-6-23 12:55:03 |
提示: 作者被禁止或删除 内容自动屏蔽
     
发表于 2012-6-23 14:03:37 |
这下又有得玩了。。。。

     
发表于 2012-6-23 14:37:35 |
非常好!!!应该出点套件!!!!!!!!!!!!!!!!!!
     
发表于 2012-6-23 14:39:44 |
这个强大。。。。
发表于 2012-6-23 18:06:48 |
牛  逼!六位半啊
有焊好的半成品吗
     
发表于 2012-6-23 18:23:09 |
有套件吗?预定一套!很强大!
发表于 2012-6-23 18:52:18 |
支持出套件~~~
     
发表于 2012-6-24 15:09:12 来自手机 |
我也想要个套件
     
发表于 2012-6-24 17:46:23 |
支持出套件~~~
     
 楼主| 发表于 2012-6-24 22:41:03 |
回楼上几位坛友,我也想出套件,但是没时间精力呀,连画 PCB 都要四处托人 谁能帮我发行套件就好啦
     
发表于 2012-6-24 23:19:58 |
做得很棒!赞一个!

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2024-5-4 03:10

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表