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

 

posted @ 2013-06-20 01:44  IAmAProgrammer  阅读(3372)  评论(0编辑  收藏  举报