zoukankan      html  css  js  c++  java
  • USB ISP(ICSP) Open Programmer < PWM ADC HV PID >

    http://sourceforge.net/projects/openprogrammer/?source=navbar

    Open Programmer

    http://openprog.altervista.org/OP_eng.html#Quick

    Open Programmer v0.8.x

    Quick facts

    • Completely free and Open Source (including firmware)
    • Programs PIC10-12-16-18-24, dsPIC30-33,
      EEPROMs type 24xxxx (I2C), 25xxx (SPI), 93xx6 (MicroWire),
      DS24xx (OneWire), 11xxx (UNIO),
      some ATMEL micros, communicates with generic
      I2C & SPI devices (see supported devices)
    • Can work as ICD debugger
    • USB 2.0 Full Speed interface, HID class (same as keyboards, mice, etc.)
    • Self powered
    • Doesn't need drivers
    • Built from easy to find components (estimated cost ~10€)
    • Hardware generated timings for maximum speed and reliability
      (writes a 18F2550 in 15s, 8s under Linux)
    • Doesn't saturate your CPU and doesn't suffer
      when other programs are running
    • Open source control programs for Linux and Windows
    • It's not another PicKit clone

    The circuit (v1.7)

    The project is based on a 28 pin 18F2550,
    but only about a third of the memory and 0% of eeprom was used,
    so it will fit confortably into the smaller 2455.

    The 2458 and 2553 have a 12 bit ADC, so only recompilation is required.
    With some modifications I adapted the code to the 2450;
    since this model lacks the MSSP module I used a software implementation of I2C and SPI;
    it also lacks the second PWM channel, therefore it can't generate the clock for Atmel chips
    (for those that are configured with external clock);
    in this case RB3 can be used to turn on an external oscillator
    (which would be inserted in a modified Atmel expansion board).

    The use of the corresponding 40 pin devices (4450, 4455, 4458, 4550, 4553)
    requires modification of the PCB.

    In order to implement an USB pheripheral with a PIC micro we need very few components:
    the main microcontroller, a quartz, some capacitors, and a USB type B receptacle,
    exactly as written in application notes from Microchip.

    To be able to program PIC devices we need two digital lines for clock and data and
    two supply voltages, VCC and VPP, which are controlled using three transistors;
    VPP comes from a 
    switching voltage regulator formed by Q4, L1, D3 which is described later.

    In order to use SPI Flash memories it's necessary to modify the EEPROM adapter
    to lower the supply voltage to 3.3V; below is the schematic diagram:
     

    The ICSP-IN connector is used to program the main micro without extracting it,
    by means of another programmer.

    http://ww1.microchip.com/downloads/en/appnotes/00258a.pdf

    Low Cost USB Microcontroller Programmer

    Switching voltage regulator

    18F2550

    Timer2 (if DCDC on): 90kHz, no prescaler, no postscaler

    Timer3 (if DCDC on): synchronous counter, no prescaler, source for CCP2
    (after CLOCK_GEN command): 16 bit, source for CCP1 & CCP2, no prescaler

    CCP1 (if DCDC on): PWM mode, clock from timer2, 90 kHz, to DCDC converter
    (after CLOCK_GEN command): compare mode, reset timer3 on match (RC2)

    CCP2 (if DCDC on): compare mode, trigger ADC every 250us
    (after CLOCK_GEN command): compare mode, toggle on match, clock to external devices

    ADC: acquires Vreg*12k/34k on AN0(FB), FOSC/64, triggered by CCP2, generates interrupt

    If compiled for 18F2450:
    Timer1 (if DCDC on): interrupt every 250us, ADC started by interrupt routine;
    no CCP2

    18F2458

    #define ADC12//12 bit ADC requires a different algorithm for DCDC control

    #define Vcost 72.282 //(12/(12+22)/5*1024) //Voltage feedback constant

    #define ADC12   //12 bit ADC requires a different algorithm for DCDC control
    #define Vcost 72.282 //(12/(12+22)/5*1024) //Voltage feedback constant
    int pwm = 0;
    byte pwm_maxH = 100, T1 = 1;
    int volatile err = 0, errz = 0, vreg = 13.0 * Vcost;
    
    void init( void )
    {
      err = errz = pwm = 0;
    #if !defined(NO_CCP2)
      CCP1CON = CCP2CON = 0;
    #else
      CCP1CON=0;
    #endif  
    }
    
    /******************************************************************************
     * Function:        void TXins(byte)
     * PreCondition:    None
     * Input:           None
     * Output:          None
     * Side Effects:    None
     * Overview:    Write to output queue
     *****************************************************************************/
    void TXins( byte x )
    {
    }
    
    void loop( void )
    {
      switch ( receive_buffer[ RXptr ] )
      {
    
        case CLOCK_GEN:       //
          TXins( CLOCK_GEN );
          if ( RXptr + 1 < number_of_bytes_read )
          {
            i = receive_buffer[ ++RXptr ];
    #if !defined(NO_CCP2)       //use CCP1-1-timer3
            if ( i == 0xff )
            {
              CCP1CON = CCP2CON = 0;
              LATBbits.LATB3 = 0;
              T3CON = 0;
            }
            else
            {
              TRISCbits.TRISC2 = 1;     //PWM1 disable output
              TRISBbits.TRISB3 = 0;     //PWM2 enable output
              TMR3L = TMR3H = 0;
              T3CON = 0b11000001; //16 bit, source for CCP1 & CCP2, no prescaler
              CCPR1H = CCPR2H = 0x00;
              //CCPR1=N clock=12MHz/2(N+1)
              if ( i == 0 )
                CCPR1L = 0x3B;   //100KHz
              else if ( i == 1 )
                CCPR1L = 0x1D;  //200KHz
              else if ( i == 2 )
                CCPR1L = 0xB; //500KHz
              else if ( i == 3 )
                CCPR1L = 0x5; //1 MHz
              else if ( i == 4 )
                CCPR1L = 0x2; //2 MHz
              else if ( i == 5 )
                CCPR1L = 0x1; //3 MHz
              else if ( i == 6 )
                CCPR1L = 0x0; //6 MHz
              CCPR2L = 1;
              CCP1CON = 0x0B;   //reset timer3 on match
              CCP2CON = 0x02;   //toggle on timer3 match
            }
    #else         //18F2450 (software mode)
            if (i==0xff)
            {
              LATBbits.LATB3=0;
            }
            else
            {
              TRISBbits.TRISB3=0;
              LATBbits.LATB3=1;
            }
    #endif
          }
          else
          {
            TXins( RX_ERR );
            receive_buffer[ RXptr + 1 ] = FLUSH;
          }
          break;
    
        case VREG_EN:     //enable DCDC [5us]
          TXins( VREG_EN );
          err = errz = pwm = 0;
          PR2 = 0x84;     //PWM period=11us f=90kHz
          T2CON = 4;      //timer2 ON
    #if defined(NO_CCP2)        //use timer1 if CCP2 is not present
            PIR1bits.TMR1IF=0;
            PIE1bits.TMR1IE=1;//enable Timer1 interrupt
            TMR1H=0xF4;//64K-3000 @48MHz = 250 us
            TMR1L=0x48;
            T1CON=0b10000001;//timer1, 16 bit, no prescaler
    #else               //else use CCP2
          CCPR2H = 0x0B;    //CCPR2=3000 (0xBB8) 12MHz/3000= 4KHz = 250us
          CCPR2L = 0xB8;
          PIR1bits.ADIF = 0;
          PIE1bits.ADIE = 1;  //enable ADC interrupt
          TMR3L = TMR3H = 0;
          T3CON = 0b10001001; //timer3, 16 bit, linked to CCP2, no prescaler
          CCP2CON = 0x0B;   //enable compare mode with ADC trigger
    #endif
          CCPR1L = 0;     //SetDCPWM1(0);
          CCP1CON = 0x0C;   //enable pwm
          TRISCbits.TRISC2 = 0; //PWM1 out
          INTCON = 0b11000000;  //enable interrupt
          HLVDCON = 0b00011110; //enable power supply comparator @ 4.5V
          break;
        case VREG_DIS:      //disable DCDC
          TXins( VREG_DIS );
    #if defined(NO_CCP2)
          PIE1bits.TMR1IE=0;  //disable Timer1 interrupt
          CCP1CON=T1CON=0;//timer1 off, pwm off
    #else               //others use CCP2
          PIE1bits.ADIE = 0;  //ADC interrupt
          CCP1CON = CCP2CON = 0;  //pwm off
    #endif
          HLVDCON = 0b00001110; //diable power supply comparator
          break;
        case SET_VPP:
          //set vpp (X x 0.1V) and wait for it to stabilize to +- 0.2V
          //return X if successful, else INS_ERR
          TXins( SET_VPP );
          if ( RXptr + 1 < number_of_bytes_read )
          {
    #if !defined(NO_CCP2)
            if ( !PIE1bits.ADIE || !HLVDCONbits.IVRST )
              TXins( INS_ERR ); //Check interrupt and HLVD
    #else 
                if(!PIE1bits.TMR1IE||!HLVDCONbits.IVRST) TXins(INS_ERR); //Check interrupt and HLVD
    #endif
            else
            {
              PIR2bits.HLVDIF = 0;
              d = (unsigned char) receive_buffer[ ++RXptr ];
              vreg = d * 72 / 10; //7;
              i = 15;     //15 ms
              do
              {
                for ( d = 1000; d; d-- )
                {      //to update err
                  Nop( );
                  Nop( );
                  Nop( );
                  Nop( );
                }
                i--;
              }while ( ( err > 14 || err < -14 ) && i );
              if ( PIR2bits.HLVDIF )
                TXins( 0 );
              else if ( err > 14 || err < -14 )
                TXins( INS_ERR );
              else
                TXins( receive_buffer[ RXptr ] );
            }
          }
          else
          {
            TXins( RX_ERR );
            receive_buffer[ RXptr + 1 ] = FLUSH;
          }
          break;
        case EN_VPP_VCC:
          //controls VPP and VCC, bit 0 VCC, bit 2 VPP
          TXins( EN_VPP_VCC );
          if ( RXptr + 1 < number_of_bytes_read )
          {
            i = receive_buffer[ ++RXptr ];
            vcc_bit = ( i & 0x01 ) ? 0 : 1;
            vcc_dir = ( i & 0x02 ) ? 1 : 0;
            vpp_bit = ( i & 0x04 ) ? 1 : 0;
            vpp_dir = ( i & 0x08 ) ? 1 : 0;
          }
          else
          {
            TXins( RX_ERR );
            receive_buffer[ RXptr + 1 ] = FLUSH;
          }
          break;
    
      }
    }
    
    /******************************************************************************
     * Function:        void timer_isr(void)
     * PreCondition:    None
     * Input:           None
     * Output:          None
     * Side Effects:    None
     * Overview:    DCDC control function
     * Note:            None
     *****************************************************************************/
    #pragma interruptlow timer_isr
    void timer_isr (void)
    {
    #if defined(NO_CCP2)      //no autostart by CCP2  
      ADCON0bits.GO=1;      //start conversion
      TMR1H=0xF4;         //64K-3000 @48MHz = 250 us
      TMR1L=0x48;
      while(ADCON0bits.GO);
      PIR1bits.TMR1IF = 0;
    #else
      while(ADCON0bits.GO);   //in case ADC is not finished
      PIR1bits.ADIF = 0;
    #endif
      errz=err;
      HIBYTE(err)=ADRESH;
      LOBYTE(err)=ADRESL;
    #if defined(ADC12)      //must shift if 12 bit result 
      err>>=2;
    #endif
      err-=vreg;
      //pwm+=errz*225
      //pwm-=err*228
    #define F 1
    #define W 0
      _asm            //arg2=errz arg1=225
      
      MOVLW   225
      MULWF   errz,0        // ARG1L * ARG2L -> PRODH:PRODL
      MOVFF   PRODH, RES1
      MOVFF   PRODL, RES0
      MOVLW   225
      MULWF   errz+1,0      // ARG1L * ARG2H -> PRODH:PRODL
      MOVF  PRODL, W,0
      ADDWF   RES1, F,0       // Add cross
      MOVF  RES0,W,0      //pwm+=errz*225
      ADDWF pwm,F,0
      MOVF  RES1,W,0
      ADDWFC  pwm+1,F,0
      MOVLW   228
      MULWF   err,0         // ARG1L * ARG2L -> PRODH:PRODL
      MOVFF   PRODH, RES1
      MOVFF   PRODL, RES0
      MOVLW   228
      MULWF   err+1,0       // ARG1L * ARG2H -> PRODH:PRODL
      MOVF  PRODL, W,0
      ADDWF   RES1, F,0       // Add cross
      MOVF  RES0,W,0      //pwm-=err*228
      SUBWF pwm,F,0
      MOVF  RES1,W,0
      SUBWFB  pwm+1,F,0
    
      _endasm
    
      if(HIBYTE(pwm)<0) pwm=0;
      if(HIBYTE(pwm)>pwm_maxH) HIBYTE(pwm)=pwm_maxH;
      CCPR1L=HIBYTE(pwm);
      CCP1CON = (CCP1CON & 0xCF) | ((LOBYTE(pwm) >> 2) & 0x30);
    }
    #pragma code

    In order to generate a voltage higher than 5V we need a boost switching converter.
    On the market there are thousands of single chip solutions,
    but I used instead the microcontroller itself and a few external components.

    The width of output pulses will vary to keep the output voltage stable over all operating conditions.
    In practice this is a digitally controlled regulator, as shown in the following diagram:

    he ADC converter presently uses the 5V supply as a reference, 

    so the output voltage will follow it; it is possible to connect
    an external reference to RA3 to improve the overall precision.

    Switching frequency is 90 kHz, which is well over
    the cutoff frequency of the output LC filter (~2,3 kHz).

    The performance is limited by losses due to the transistor, diode, inductor,
    but since the load is very low we can use low-cost (even recycled) components;
    to improve load capability switch to a better transistor,
    a Shottky diode, a higher rated inductor.

    Anyways, in order to design a suitable regulator (block C above)
    it's necessary to work in s domain and model the converter itself;
    this has fortunately been done already, some info is available for example 

    http://www.ti.com/lit/an/slva061/slva061.pdf

    With present component values the boost converter operates in dicontinuous mode;
    critical current is: 

    Icrit=Vu*T/(16*L)=86 mA
    well over expected load, supposed to be 1 mA.
    Other parameters:
    Vi=5
    Vu=12.5
    D=(Vu-Vi)/Vo
    L=100e-6
    C=47e-6
    I=1e-3 
    R=12/I 
    Rl=1.6 (inductor series resistance)
    T=1/90e3

    vu            1                    vu    M-1          Vu         2M -1   
    --- = Gdo ---------- where Gdo = 2 --- ------- , M = ---, wp = ----------
     D         1 + s/wp                 D   2M -1         Vi        (M-1) RC 

    Transfer function results to be:

    vu       127.58    
    -- = ------------- 
    D    0.22031 s + 1 
    Which has the following Bode diagram:
     
     

    It seems that the system, in closed loop, would be stable even by itself;
    however it would have a steady state error of 1/DCgain. 

    It's better to use a controller with a pole on the origin
    and a zero to stabilize everything, for example the following controller:
     

         D    0.25 (s + 50) 
    C = --- = ------------- 
        err         s   

    Overall transfer function would be:

    vu   144.77 s + 7238.4  
    -- = -----------------  
    vi     s2 + 4.539 s 

    The system is stable, with a phase margin of ~75º.
    Since we operate in the digital domain we must choose the sampling frequency.
    It can't be too high because of execution speed;
    if too low it limits the regulator bandwidth; a period of 250 us was a good compromise. 

    The various transfer functions are converted to z domain using bilinear transformation: 
    

    vu   0.018199 z2+ 0.00022607 z - 0.017973
    -- = ------------------------------------
    vi         z2 - 1.9989 z + 0.99887
    
    
    The controller is:
    
    
         D    0.25156*z - 0.24844   C1 - C2 z-1
    C = --- = ------------------- = -----------
    err z - 1 1 -z-1
    
    


    Remember that z-1 represents a delay of one clock cycle.
    Next we must deal with quantization and calculation errors.
    A/D converter is 10 bits wide, and is triggered by timer2;
    at the end of conversion an interrupt calls the regulation routine,
    which calculates the new duty cycle for the PWM peripheral, also 10 bits wide.

    On the feedback path it's necessary to include a voltage divider in order
    to limit ADC input voltage to [0,5V]; R1 and R2 do this.

    So the block diagram is modified as follows:    

    a=12/34
    
    Vu=C'H(Vi-aVu)
    
    Vu    C'H   
    -- = ------ 
    Vi   1+aC'H 

    To compare with the previous model we can multiply both terms by a;
    simply remembering to change the set point we can decide that
    the new input is Vi/a, and equate with the previous expression:

     Vu     aC'H     CH   
    ---- = ------ = ----  
    Vi/a   1+aC'H   1+CH  
    
    
    aC'=C
    
    
         aC1' - aC2' z-1        C1 - C2 z-1
    aC'= --------------- = C = ------------
           1 - z-1                1 - z-1
    
    
    aC1'=C1
    aC2'=C2

    Since the hardware works with 10 bit digital data we can go from D/err to pwm/[err]:

    [err]=err*1024/5
    pwm=D*1024
    
    
         D      pwm/1024       pwm     C1' - C2'z-1
    C'= --- = ------------ = ------- = ------------
        err   [err]/1024*5   [err]*5      1 -z-1
    
    
    pwm(1 - z-1)=[err](5*C1/a - 5*C2/a z-1)=[err](3.564 - 3.52 z-1)

    It's clear that integer multiplications can't be used with these coefficients;
    the easiest solution is to work with fractional values
    (i.e. divide output by 2N and multiply coefficients accordingly);
    considering that pwm output is 10 bits wide and left-aligned,
    we can easily work with values divided by 64.

    pwm(1 - z-1 )=[err]( k1 - k2 z-1 )/64
    
    k1=5C1/a*64=228.12 ~ 228
    k2=5C2/a*64=225.25 ~ 225

    Following are step responses of continuous-time system (blue),
    discrete-time system (red), discrete-time system with approximate cefficients (green);
    As you can see they're almost coincident.

    For all calculations I used Octave, an open source mathematical modeling tool; version 3 has just been released,
    and it can be used also under the famous windows (almost)operating system.

    If someone is interested these are the modeling scripts I used.
    The real code for the control function was written in assembly; this is necessary for performance reasons.
    In fact our C compiler calls a library function to perform multiplications,
    so it has to save many variables on the stack causing a delay;
    in this case the resulting execution time had reached 50 us,
    which is a significant fraction of the sampling period.

    Instead, by avoiding function calls and manually coding the 16x8 bit multiplication
    (see k1 & k2), the execution time is down to 12 us.


    Some real waveforms:

    3

    Power-up transient, 50 ms/div

    Step response to load change (load on top trace, 
    AC coupled output on bottom tr.), 50ms/div

    Step response to set point change (from 11,5 to 12,5 V), 50 ms/div

    +5v to +13v Converter 

    http://www.romanblack.com/smps/conv.htm

    A cheap circuit to make +13v from a +5v supply
    Roman Black - orig Sep 2002 - updated Aug 2006


    This is a simple smps voltage converter, 
    it makes an output voltage of +13.3v from a "standard" +5v supply as used with logic chips. 


    I was contacted by hobby PIC programmer legend Myke Predko about designing a simple and
    cheap circuit to produce the 13v Vpp voltage needed to program PIC chips
    from a standard 5v regulated supply. This would eliminate the need for an smps 13v converter chip. 


    The circuit I settled on worked out very cheap, using a cheap 470uH "RF choke" type inductor
    and a very cheap 1N4148 small signal diode as the rectifier.
    The two transistor types are not critical.
    Measured efficiency was 72% which is quite reasonable from such a simple low power circuit. 


    The inductor can be a cheap small "RF choke" type.

    • Tiny cheap RF type inductor
    • Very cheap circuit (under 50 cents in parts!)
    • Reliable self-starting
    • 72% efficiency
    • Input: 5v regulated
    • Output: 13.3v @ 12mA maximum

    How it works

    The circuit regulates around peak inductor current, 
    and since input voltage remains constant it also regulates input current and hence input power. 


    The regulation mechanism is R2, by ensuring that if R2 volts
    exceeds 0.6v main switch Q1 will turn off. The input current path is via L1, Q1 and R2. 


    Oscillation duty cycle is close to 50:50 so with an R2 of 6.8 ohms,
    peak inductor current is about 90mA and the average input current is about 46mA.
    Total current requirements can be reduced by increasing R2. 

    Notes

    DO NOT run the circuit with no zener, and do not remove the zener when the circuit is running! 
    Like any boost converter it may produce high voltages without the zener output clamp,
    and may damage transistors or the diode etc. 


    The circuit oscillates mainly based on inductor characteristics,
    and the cap C1 is not really a tuning cap, instead it provides some stability of oscillation waveform
    at a frequency determined mainly by inductance and the total load. 


    This booster is a CONSTANT POWER regulator. 
    It always draws 46mA from the 5v supply (230mW),
    and delivers this power to the load at about 72% efficiency.
    So the load always gets about 13.3v and 12mA.
    If the output is overloaded at currents greater than 12mA the output voltage drops,
    providing a crude current regulation.
    If the output voltage is further overloaded to the point where it drops below 5v
    the diode starts passing current directly from the 5v supply and input current rises a LOT. 

    Overloads like this should be avoided!

    Important Notes!

    The 13.3v output can be turned on or off by attaching the R1 resistor to +5v (as shown)
    or to 0v to turn it off. R1 can be attached to a PIC output pin to control the circuit. 

    When the circuit is OFF the output is about 4.3v and total current consumption is about 0.4mA. 

    When the circuit is ON the output is 13.3v and total current consumption is about 46mA. 

    For a PIC programmer application; the MCLR must rise very quickly to 13v to initiate programming.
    Use a 3k3 resistor in series with the output, going to the MCLR pin.
    Then use an additional NPN transistor to hold MCLR low as an open-collector pull-down.
    This transistor can then be switched and will switch the output voltage very quickly
    from 0v to 13v. (This is necessary with the smps 13v generator chips also.) 

    High Power / High Voltage?

    Note! 
    This circuit can be quite usable for higher power applications,
    and will oscillate reliably with a larger (high power) version of Q1
    like a TO-220 darlington type. Remember it regulates INPUT current,
    and will provide a fairly regulated amount of power to the high voltage output load.
    It can be used as a simple 4 watt fluoro tube driver with a 12 volt input (for example).
    If you try this, try these values; 

        Q1 = TIP122 darlington (see note)
          L1 = 470uH 500mA ferrite core inductor

     

          D1 = 1N4937 600v 1A fast rectifier

     

          R1 = 4k7

     

          R2 = 3.3 ohm

     

          R4 = 56k

     

        CLOAD = 0.1uF (not critical) but must be 630 volt type

    With a 12v input this will run about 2 watts and make a usable light from the fluoro tube.
    Remove the zener and connect the output to a 4 watt (5 inch) fluoro tube like a F4T5 type.
    You may need a heatsink for Q1. I used a TIP-122 as it was lying around,
    but a high voltage darlington (>300v) is better.

    Remember this produces high voltages and you MUST always have the tube connected.
    Have fun and 
    work safely with high voltages! 

    Low Cost USB Microcontroller Programmer

    http://ww1.microchip.com/downloads/en/appnotes/00258a.pdf

    THE BOOST POWER SUPPLY

    A boost power supply is needed on the PICkit 1 programmer board in order
    to create the programming voltage (VPP) called for in the PIC12F629/675
    programming specification. The boost power supply is a combination of hardware and firmware design.

    Implementation: Hardware

    The hardware developed for the PICkit 1 FLASH Starter Kit intentionally
    uses as few components as necessary to keep the cost low.
    Most of the components on the PICkit 1 programmer board make up the boost power supply.
    The boost power supply, shown in Figure 3, creates the 13V voltage needed
    for programming Microchip's low pin count FLASH devices.

    CCP1 runs in PWM mode to create the input to the boost circuit.
    Timer2 sets the period of the PWM.
    An A/D channel (RA1) provides feedback to the PIC16C745.  


    VPP is switched on and off by pin RA0.
    Similarly, RB7 switches power to VDD.

    Finally, RC6 and RC7 bit bang programming commands to the device.

    Details on how a boost circuit works are discussed in Technical Brief TB053.

    http://ww1.microchip.com/downloads/en/appnotes/91053b.pdf

    What happens briefly is that when Q2 is turned on, inductor L1 charges.
    When Q2 is switched off, the energy stored in L1 flows through D13 to the storage capacitor (C4) and load.
    Q3 and Q4 make up a switch for turning VPP on and off to the FLASH device.
    The switch is toggled on and off by RA0.
     Assuming the resistive load is the held constant,VPP will change with respect to the energy released from L1.
    L1 stores energy during the high phase of the PWM and releases energy during the low phase of the PWM.
    This boost circuit runs in Discontinuous mode, meaning L1 will always discharge fully during the low phase of the PWM.
    VPP increases as the duty cycle of the PWM increases. The stability of VPP is achieved with a firmware PID.

    Implementation: Firmware

    The boost circuit routines are located in two linked files: 

    pid.asm and VPPCntl.asm.

    VPPCntl.asm contains the functions for initializing the PIC16C745 peripherals used
    to generate the boost circuit, turning VPP and VDD on and off, and clamping VPP high or low.
    This file also contains the DoSwitcher function which is called from the ISR when Timer2 interrupts.

    Timer2 is set up to overflow at a frequency of 93.75 kHz.

    The Timer2 postscaler is 1:16, therefore the Timer2 interrupt occurs at a frequency of 5.86 kHz.
    DoSwitcher reads the A/D value generated on the boost circuit feedback pin.
    DoSwitcher then transfers this value to the PID function where a correction factor is calculated.
    Several parameters are defined at the head of VPPCntl.asm.
    They are described in Table 1.

    Any increase or decrease in the duty cycle causes a respective rise or fall in VPP.
    The variable governing the duty cycle is the control variable (CV).
    The control variable is calculated for the next cycle based on the proportional,
    integral and differential analysis of the feedback. Equation 1 shows this relation.

    pid.asm

        extern    doPID
        extern    InitPID
        extern    SetTarget
    
        extern    K1
        extern    K2
        extern    K3
    
    ;    File : PID.asm
    ;
    ;    Proportional, Integral, Differential control loop utility.
    
        GLOBAL    doPID, InitPID, SetTarget
        GLOBAL    K1,K2,K3,Target,DeltaV
    
    #include    <P16C745.inc>
    
        errorlevel  -302              ; suppress message 302 from list file
        
    #define MaximumTarget    H'C2'
    #define TargetADC    MaximumTarget
    
    #define subwl    sublw        ; Ward's Secret Macro
    
    Kp    equ    D'230'         ; Proportional error multiplier
    Ki     equ    D'26'        ; Integral error multiplier
    Kd    equ    D'128'        ; Differential error multiplier
    
    ;*****************************************************************************************
    ;*
    ;* Define RAM variables
    ;*
    
    bank0    udata
    
    multcnd        res    1    ; 8 bit multiplicand
    multplr        res    1    ; 8 bit multiplier
    xCount        res    1    ; counter workspace used by Multiply and Tacq
    H_Byte        res    1    ; High byte of the 16 bit multiply result
    L_Byte        res    1    ; Low byte of the 16 bit multiply result
    K1        res    1    ; P multiplier
    K2        res    1    ; I multiplier
    K3        res    1    ; D multiplier
    Target        res    1    ; Target boost voltage ADC
    MaxTarget    res    1    ; Maximum target boost voltage
    DeltaV        res    1    ; difference between target and measured output voltage
    LastDeltaV     res    1    ; previous DeltaV
    H_Integral     res    1    ; ms byte of 16 bit error integrator
    L_Integral     res    1    ; ls byte of 16 bit error integrator
    H_PIDSum     res    1    ; ms byte of 16 bit PID sum
    L_PIDSum     res    1    ; ls byte of 16 bit PID sum
    PIDTemp        res    1    ; temporary work space
    
        global    K1
        global    K2
        global    K3
    
    PROG1    code
    
    doPID
        ; use value passed through W as latest input for PID
    
        movwf    PIDTemp
    
    CtrlLoop    
        banksel    DeltaV
        movf    DeltaV,w            ; get most recent error voltage
        movwf    LastDeltaV        ; save as last
    
        bcf    STATUS,C            ; assure zero in msb after rotate
        rrf    PIDTemp,w        ; get latest input (7 bit signed)
        subwf    Target,w            ; compute error voltage
        movwf    DeltaV            ; save
    
    ; Proportional error
    
        movf    K1,w                ; prepare for multiply by pre-placing...
        movwf    multplr            ; ...Proportional multiplier and...
        movf    DeltaV,w            ; ...error voltage...
        movwf    multcnd            ; ...in multiply workspace
        call    Multiply            ; multiply Proportional error by K1
        movf    H_Byte,w            ; accumulate first of PID offsets
        movwf    H_PIDSum            ;
        movf    L_Byte,w            ;
        movwf    L_PIDSum            ;
    
    ; Integral error
    
        movf    K2,w                ; prepare for multiply by pre-placing...
        movwf    multplr            ; ...Integral multiplier
        movf    DeltaV,w            ; compute Integral Deltas
        movwf    multcnd            ; save integral in multiply workspace
        call    Multiply            ; multiply Integral error by K2
        call    Integrate        ; integrate and accumulate
    
    ; Differential error
    
        movf    K3,w                ; prepare for multiply by pre-placing...
        movwf    multplr            ; ...Differential multiplier
        movf    LastDeltaV,w            ; compute Differential voltage...
        subwf    DeltaV,w    ; ...Differential = Delta - LastDelta
        movwf    multcnd            ; save Differential in multiply workspace
        call    Multiply            ; multiply Diffential error by K3
        call    Accumulate        ; add to previous result
    
    ; return corrected control byte
    
        movf    H_PIDSum,w    ; recover control byte into W
        return                ; return the byte
        
    
    Multiply
    ;*****************************************************************
    ;
    ;       8 x 8 multiplier routine
    ;       enter with two 8 bit numbers in multcnd and multplr
    ;       16 bit result is returned in H_Byte and L_Byte
    ;       multplr is altered in the process.
    ;    This is a semi-signed multiply which means that if the
    ;    multcnd is negative the result will be negative. The
    ;    multplr is always unsigned.
    ;
    ;*****************************************************************
    ;
        clrf    H_Byte        ; clear workspace (L_Byte is self clearing)
        movlw   8        ; multiply is 8 accumulate/rotates
        movwf   xCount        ; set accumulate/rotate counter
        movf    multcnd,W    ; restore multicand
        btfsc    multcnd,7    ; is multicand negative?
        subwl    D'0'        ; yes - make it positive by two's complimenting
    MU10    
        rrf     multplr,f    ; test multiplier LSB
        skpnc                    ; skip accumulate if LSB was zero
        addwf   H_Byte,f    ; not zero - add multicand
        rrf     H_Byte,f    ; right shift 16 bit result 1 place
        rrf     L_Byte,f    ; 
        decfsz  xCount,f    ; all 8 accumulate/rotates performed?
        goto    MU10        ; no - repeat for next bit
    
        btfss    multcnd,7    ; was multicand negative?
        return            ; no - no further processing
    
        comf    L_Byte,f    ; make positive result negative by two's complimenting
        comf    H_Byte,f    ; invert all 16 bits...
        movlw    1        ; ...and...
        addwf    L_Byte,f    ; ...add 1
        skpnc                ; adjust MS byte if carry from LS byte
        incf    H_Byte,f    ;
    
        return
    
    Integrate
    ;*****************************************************************
    ;
    ;    Sum the 16 bit result of K2 multiply result with
    ;    all previous results and add this integration to the PIDSum.
    ;
    ;    The integral is always positive but other addends are signed.
    ;       Determine overflow/underfow by comparing the msb's of the 
    ;       integral, addend, and addition result as follows:
    ;
    ;    Integral, Addend, Result - Conclusion - Action
    ;        1       0       0      Overflow     Force Max
    ;        0       1       1      Underflow    Force Min
    ;
    ;*****************************************************************
    
        movf    H_Integral,w    ; get ms result
        movwf    PIDTemp        ; save
        movf    H_Byte,w    ; restore ms multiplication result
        addwf    H_Integral,f    ; 16 bit accumulate    
        movf    L_Byte,w    ; get ls result
        addwf    L_Integral,f    ; 16 bit accumulate
        btfsc    STATUS,C    ; lower byte addition carry?
        incf    H_Integral,f    ; yes - adjust ms byte
    
    ; test for addition overflow
    
        btfss    PIDTemp,7    ; test for pre-addition integral msb
        goto    IntegUnder    ; zero - check for underflow
        
        btfsc    H_Byte,7    ; is addend negative?
        goto    IGAccum        ; yes - no overflow
    
        btfsc    H_Integral,7    ; is result positive?
        goto    IGAccum        ; no - no overflow
        
        movlw    0xff        ; overflow detected...
        movwf    H_Integral    ; ... force maximum
        movwf    L_Integral
        goto    IGAccum
        
    ; Test for addition underflow
    
    IntegUnder
        btfss    H_Byte,7    ; is addend positive?
        goto    IGAccum        ; yes - no underflow
    
        btfss    H_Integral,7    ; is result negative?
        goto    IGAccum        ; no - no underflow
        
        clrf    H_Integral    ; underflow detected...
        clrf    L_Integral    ; ... force minimum
    
    IGAccum    
    ; the integral is always positive 
    ;  accumulate integral and sum then check for over/under flow
    ;  the accumulation result must be positive
    
        movf    H_PIDSum,w    ; get ms addend
        movwf    PIDTemp        ; save
        movf    H_Integral,w    ; restore ms integral
        addwf    H_PIDSum,f    ; accumulate with sum
        movf    L_Integral,w    ; restore ls byte of integral
        addwf    L_PIDSum,f    ; sum with ls PIDSum
        btfsc    STATUS,C    ; lower byte addition carry?
        incf    H_PIDSum,f    ; yes - adjust ms byte
    
    ; test for addition overflow
    
        btfss    H_Integral,7    ; test integral msb
        goto    IAUnder        ; zero - check for underflow
        
        btfsc    PIDTemp,7    ; is addend negative?
        goto    IntegExit    ; yes - no overflow
    
        btfsc    H_PIDSum,7    ; is result positive?
        goto    IntegExit    ; no - no overflow
        
        movlw    0xff        ; overflow detected...
        movwf    H_PIDSum    ; ... force maximum
        movwf    L_PIDSum
        goto    IntegExit
        
    ; Test for addition underflow
    
    IAUnder
        btfss    PIDTemp,7    ; is addend positive?
        goto    IntegExit    ; yes - no underflow
    
        btfss    H_PIDSum,7    ; is result negative?
        goto    IntegExit    ; no - no underflow
        
        clrf    H_PIDSum    ; underflow detected...
        clrf    L_PIDSum    ; ... force minimum
    
    IntegExit
        return
    
    Accumulate    
    ;*****************************************************************
    ;
    ;    Accumulate the 16 bit result of K3 PID multiply result
    ;
    ;    The sum accumulator (PIDSum) is assumed always positive
    ;
    ;*****************************************************************
    
        movf    H_PIDSum,w    ; get pre-addition sum ms byte
        movwf    PIDTemp        ; save
        movf    H_Byte,w    ; get ms multiplication result
        addwf    H_PIDSum,f    ; 16 bit accumulate
        movf    L_Byte,w    ; get ls result
        addwf    L_PIDSum,f    ; 16 bit accumulate
        btfsc    STATUS,C    ; lower byte addition carry?
        incf    H_PIDSum,f    ; yes - adjust ms byte
        
    ; test for accumulation overflow
    
        btfss    PIDTemp,7    ; test pre-addition msb
        goto    ACUnder        ; zero - check for underflow
        
        btfsc    H_Byte,7    ; is addend negative?
        goto    AccumExit    ; yes - no overflow
    
        btfsc    H_PIDSum,7    ; is result positive?
        goto    AccumExit    ; no - no overflow
        
        movlw    0xff        ; overflow detected...
        movwf    H_PIDSum    ; ... force maximum
        movwf    L_PIDSum
        goto    AccumExit
        
    ; Test for addition underflow
    
    ACUnder
        btfss    H_Byte,7    ; is addend positive?
        goto    AccumExit    ; yes - no underflow
    
        btfss    H_PIDSum,7    ; is result negative?
        goto    AccumExit    ; no - no underflow
        
        clrf    H_PIDSum    ; underflow detected...
        clrf    L_PIDSum    ; ... force minimum
    
    AccumExit
        return
    
    InitPID
    ;************************************************************************************
    ;*
    ;* Initialize PID registers
    ;*
        movlw    Kp        ; initialize PID multipliers
        movwf    K1
        movlw    Ki
        movwf    K2
        movlw    Kd
        movwf    K3
        clrf    DeltaV        ; clear accumulators
        clrf    H_Integral
        clrf    L_Integral
        clrf    H_PIDSum
        clrf    L_PIDSum
        return
    
    SetTarget
    ;************************************************************************
    ;*
    ;* take value passed in W and configure the target
    ;*
    
        movwf    Target        ; set target
        bcf    STATUS,C    ; clear carry for rotate
        rrf    Target,f    ; convert to 7 bit signed
        
        return
        
        end

    vppcntrl.asm

    ;###############################################################################
    ; filename:    VppCntrl.inc
    ;        Vpp Control functions
    ;
    ; This file implements a basic interrupt service routine and shows how the
    ; USB interrupt would be serviced, and also how InitUSB and PutUSB
    ; should be called.  It may be used as a reference, or as a starting point 
    ; from which to build an application.  
    ;
    ;###############################################################################
        extern    VppInit
        extern    VppHigh
        extern    VppLow
        extern    VppOff
        extern    VppOn
        extern    VddOn
        extern    VddOff
        extern    DoSwitcher
        extern    PWMOn
        extern    PWMOff
    
    ;###############################################################################
    ; filename:    VppCntrl.ASM
    ;        Vpp Control functions
    ;
    ; This file implements a regulated boost power supply.
    ; The following resources are used by this module:
    ;     CCP
    ;     ADC channel
    ;     Timer 
    ;
    ;###############################################################################
    
        GLOBAL    lastADRES
    
    #include <P16C745.inc>
    #include "pid.inc"
    
        errorlevel  -302              ; suppress message 302 from list file
    
    bank0    udata
    
    PowerState    res    1    ; Power Status Flag
    pwm        res    1    ; temporary value for CCP loader
    Result        res    2    ; final calculation
    skip        res    1
    lastADRES    res    1
    
    #define    PowerGood    PowerState,0
    #define    PowerHigh    PowerState,1
    #define    PowerLow    PowerState,2
    #define    PowerON        PowerState,7
    
    ; Set Points for voltage regulator.
    V5V        equ    .92    ; counts for 5v from Regulator
    VCHECK        equ    .220    ; regulator setpoint (11.7v ideal)
    ;VCHECK        equ    .230    ; regulator setpoint (12v)  maybe too high
    PWM_MAX        equ    .130    ; 75% PWM Maximum
    PWM_MIN        equ    .13    ; 5% PWM Minimum
    skip_reload    equ    .10
    
    PROG1    code
    
    VppInit
        global    VppInit
        call    InitPID
        movlw    VCHECK
        call    SetTarget
        clrf    STATUS        ; Bank0
        return
        
    VppLow
        global    VppLow
        ; cause Vpp to be 5v
        banksel    PowerState
        bcf    PowerON
        banksel    CCP1CON
        clrf    CCP1CON    ; turn off CCP
        clrf    STATUS        ; Bank0
        return
    
    VppHigh
        global    VppHigh
        ; cause Vpp to be 13v
        ; does not return until regulator is running
        ; setup ADC
        banksel    ADCON1
        movlw    0x04
        movwf    ADCON1
        banksel    ADCON0
        movlw    0x89        ; configure ADC for Fint/32 and AN1.
        movwf    ADCON0        ; Turn ADC on
        banksel    PORTC
        clrf    PORTC
        banksel    TRISC
        bcf    TRISC,2        ; set RC2 as output
        bsf    TRISA,1
    
    ; setup CCP
        banksel    PR2
        movlw    .63        ; 93.75khz Timer 2
        movwf    PR2
        banksel    T2CON
        movlw    0x7C        ; T2 postscale 1/16
        movwf    T2CON
        movlw    PWM_MAX
        pagesel    updateCCP
        call    updateCCP
        banksel    PowerState
        bcf    PowerGood
        bcf    INTCON,GIE    ; Disable Global IRQ for a little bit
        banksel    TMR2
        clrf    TMR2        ; Clear T2 Counter
        banksel    PIR1
        bcf    PIR1,TMR2IF    ; Clear T2 Pending IRQ
        banksel    PIE1
        bsf    PIE1,TMR2IE    ; Enable T2 IRQ
        banksel    INTCON
        bsf    INTCON,PEIE    ; Enable Peripheral IRQ
        bsf    INTCON,GIE    ; Enable Global IRQ
        bsf    ADCON0,GO
        banksel    PowerState
        bsf    PowerON
        clrf    STATUS        ; Bank0
        return
    
    VddOff
        global    VddOff
        banksel    PORTB
        movf    PORTB,W
        iorlw    0x80
        movwf    PORTB
        ; turns on the Vdd transistor
        clrf    STATUS        ; Bank0
        return
    
    VddOn
        global    VddOn
        ; turns off the Vdd transistor.
        banksel    PORTB
        movf    PORTB,w
        andlw    0x7F
        movwf    PORTB
        clrf    STATUS        ; Bank0
        return
    
    VppOn
        global    VppOn
        banksel    PORTA        ; make RA0 high
        movf    PORTA,W
        iorlw    0x01
        movwf    PORTA    
        clrf    STATUS        ; Bank0
        return
    
    VppOff
        global    VppOff
        ; make RA0 low
        banksel    PORTA
        movf    PORTA,W
        andlw    0xFE
        movwf    PORTA
        clrf    STATUS        ; Bank0
        return
    
    updateCCP
    ; place W value in CCPL & CCPCON<5:4>
        banksel    pwm
        movwf    pwm
        sublw    PWM_MAX
        btfsc    STATUS,C
        goto    uc
        movlw    PWM_MAX
        movwf    pwm
    
    uc
        swapf    pwm,w
        andlw    0x30
        iorlw    0x0F
        banksel    CCP1CON
        movwf    CCP1CON
        banksel    pwm
        rrf    pwm,f
        rrf    pwm,w
        andlw    0x3F
        banksel    CCPR1L
        movwf    CCPR1L
        return
    
    DoSwitcher
        global    DoSwitcher
        banksel    skip
        decfsz    skip,f
        goto    switcherdone
        movlw    skip_reload
        movwf    skip
        banksel    ADRES
        movf    ADRES,w
        ; start next ADC
        bsf    ADCON0,GO
        banksel    lastADRES
        movwf    lastADRES
        banksel    PowerState
        btfss    PowerON
        goto    switcherdone
        pagesel    doPID
        call    doPID
        pagesel    updateCCP
        call    updateCCP
    
    switcherdone
    
        banksel    PIR1
        bcf    PIR1,TMR2IF
        banksel    PORTB
        return
        
    PWMOn
        global    PWMOn
        bsf    STATUS,RP0    ; Bank1
        bcf    TRISC,1        ; make RC1 an output
        movlw    .74
        movwf    PR2
        bcf    STATUS,RP0    ; Bank0
        return
        
    PWMOff
        global    PWMOff
        bsf    STATUS,RP0    ; Bank1
        bsf    TRISC,1        ; make RC1 an input
        movlw    .63
        movwf    PR2
        bcf    STATUS,RP0    ; Bank0
        return
    
        end
  • 相关阅读:
    [转]Linq to SQL中的实体继承
    [转贴][WCF Security] 3. X509 身份验证
    [转贴]十大网站设计错误
    [转载].NET设计模式(1):开篇
    [转自JeffreyZhao]不妨来做个尝试:UpdatePanel for ASP.NET MVC
    [转]如何快速生成100万不重复的8位均匀分布的随机编号?
    [转贴]X.509 & RSA
    [c#]Webservice中如何实现方法重载(overload)以及如何传送不能序列化的对象作参数
    (Head First 设计模式)学习笔记(2) 观察者模式(气象站实例)
    (Head First 设计模式)学习笔记(1)
  • 原文地址:https://www.cnblogs.com/shangdawei/p/3145270.html
Copyright © 2011-2022 走看看