PDA

View Full Version : MikroBasic to PBP Pro: Boost mode SMPS



jmgelba
- 10th October 2011, 18:25
Hi group,

I found an instructables article on creating a boost mode power supply based on a PIC. Its programmed in MikroBasic so I thought Id try and convert it to PBP PRO.

There are a couple of questions I have and a few issues. I'd also like to add the ability to have pushbutton up and down dimming and soft start/soft stop too.

Anyway, here is the original program:




dim temp as word
dim final_period, final_duty, high_duty, low_duty, last_adjust as byte
dim supply_multiplier, period_multiplier as float
dim v_feedback, v_supply, t_rise, SMPS_duty, t_period, SMPS_period as float
dim EE_pointer, EE_data as byte


'set values
const v_ref as float=3.8 'float 5.1 in the example
const supply_ratio as float=11.35 'float
const osc_freq as float=8 'integer or float
const L_Ipeak as float=67 'float
const fb_value as word=443 'word 225 in the example


sub procedure PRE_CALC
'precalculations
supply_multiplier=v_ref*supply_ratio
period_multiplier=(4/osc_freq)
end sub




sub procedure UPDATE_SMPS
'take Vss reading
temp=0
temp=ADC_read(ANS0) ' ADC conversion
'convert to voltage
v_feedback=temp 'put ADC in float
v_supply=v_feedback/1024 'find supply % of vref
v_supply=v_supply*supply_multiplier 'find supply volts
'calculate rise time
t_rise=L_Ipeak/v_supply 'find rise time @ supply volts for L/Ipeak
'calculate CCPR1L:CCP1CON<5:4>
SMPS_duty=t_rise*osc_freq 'find duty cycle value
final_duty=SMPS_duty 'convert to byte
'calculate period
t_period=(t_rise*1.33) 'dutycycle = .75period
'calculate PR2
SMPS_period=(t_period/period_multiplier) 'find period value
final_period=SMPS_period-1.0 'put in byte


'put in register masks
high_duty=(final_duty>>2) 'high 6 bits in CCPR1L
low_duty=(final_duty<<6) 'low two bits for CCP1CON
low_duty=(low_duty>>2) 'shift back to CCP1CON<5:4>
low_duty.3=1 'PWM configuration bit
low_duty.2=1 'PWM configuration bit


'LOG TO EEPROM
'only write if we measure somthing,
'prevents false writes durring programming b/c MCLR is disabled
if temp > 0 then
EE_data=word(temp>>8) 'high 8 bits first address
Eeprom_Write(EE_pointer, EE_data)
EE_pointer=EE_pointer+1
EE_data=temp 'low 8 bit second address
Eeprom_Write(EE_pointer, EE_data)
EE_pointer=EE_pointer+1
'put final_duty in eeprom
Eeprom_Write(EE_pointer, final_duty)
EE_pointer=EE_pointer+1
'put final_period in eeprom
Eeprom_Write(EE_pointer, final_period)
EE_pointer=EE_pointer+1
'reset write pointer if needed
if EE_pointer > 197 then EE_pointer=1 end if
'update the write pointer
Eeprom_write(0, EE_pointer)
end if


end sub




main:


'set ADC
GPIO = 0
CMCON0 = 7
TRISIO = 0 ' designate gpio as output
TRISIO.ANS0 = 1 ' pin ANS0 as input (Supply feedback)
TRISIO.ANS1 = 1 ' pin ANS1 as input (HV-feedback)
ANSEL.ANS1=1
ANSEL.ANS0=1
ADCON0.VCFG = 0 ' Vdd as Vref


'Low Voltage Indicator Light
GPIO.4=1 'indicator light on


'delay and take Vss reading
EE_pointer=Eeprom_read(0)
delay_ms(1000)
PRE_CALC
UPDATE_SMPS


'setup PWM
PR2=final_period 'sets PWM frequency
CCPR1L=0 '8 most sig. duty cycle bits =0
CCP1CON.5=0 'least significant bits = 0
CCP1CON.4=0 'least significant bits = 0
CCP1CON=12 'turns on PWM (set CCP1CON=0 at any time to turn off PWM)
T2CON=4 'turns on TIMER2 module (required for PWM timing)
last_adjust=0


while true
temp=0
temp=ADC_read(ANS1) ' ADC conversion
if temp > fb_value then 'feed back OVER set voltage
if last_adjust=1 then 'if 0 then already off
CCPR1L=0
CCP1CON.5=0
CCP1CON.4=0
GPIO.5=1
GPIO.4=0
last_adjust=0
end if
else
if last_adjust=0 then
CCPR1L=high_duty
CCP1CON=low_duty
GPIO.5=0
GPIO.4=1
last_adjust=1
end if


end if
wend


end.


And here is my PBP PRO conversion so far:



define OSC 48temp VAR word
final_period var byte
final_duty var byte
high_duty var byte
low_duty var byte
last_adjust var byte
supply_multiplier var word 'as float
period_multiplier var word 'as float
v_feedback var word
v_supply var word
t_rise var word
SMPS_duty var word
t_period var word
SMPS_period var word
EE_pointer var byte
EE_data var byte
v_ref var byte
supply_ratio var byte
osc_freq var byte
L_Ipeak var byte
fb_value var word


'-------------------------------------------------------------------------------


'set values
v_ref = 38/10 'float 5.1 in the example, 3.8 here
supply_ratio = 1135/100 'float, 11.35 here
osc_freq = 8 'integer or float
L_Ipeak = 67 'float
fb_value = 443 'word 225 in the example


'-------------------------------------------------------------------------------


ADCON1 = %00001011
ADCON2 = %10000111


TRISA = %00011111
TRISB = %00000000
TRISC = %00000111
CMCON = 7


'-------------------------------------------------------------------------------


PRE_CALC: 'precalculations
supply_multiplier=v_ref*supply_ratio
period_multiplier = 4/osc_freq
return


'-------------------------------------------------------------------------------


UPDATE_SMPS:
'take Vss reading
temp=0
ADCIN 0, temp
'convert to voltage
v_feedback=temp 'put ADC in float
v_supply=v_feedback/1024 'find supply % of vref
v_supply=v_supply*supply_multiplier 'find supply volts
'calculate rise time
t_rise=L_Ipeak / v_supply 'find rise time @ supply volts for L/Ipeak
'calculate CCPR1L:CCP1CON<5:4>
SMPS_duty=t_rise*osc_freq 'find duty cycle value
final_duty=SMPS_duty 'convert to byte
'calculate period
t_period=t_rise*133/10 'dutycycle = .75period
'calculate PR2
SMPS_period=(t_period/period_multiplier) 'find period value
final_period=SMPS_period-1 'put in byte


'put in register masks
high_duty=(final_duty>>2) 'high 6 bits in CCPR1L
low_duty=(final_duty<<6) 'low two bits for CCP1CON
low_duty=(low_duty>>2) 'shift back to CCP1CON<5:4>
low_duty.3=1 'PWM configuration bit
low_duty.2=1 'PWM configuration bit


'LOG TO EEPROM
'only write if we measure somthing,
'prevents false writes durring programming b/c MCLR is disabled
if temp > 0 then
EE_data=(temp>>8) 'high 8 bits first address
Write 0,EE_pointer, 10,EE_data
EE_pointer=EE_pointer+1
EE_data=temp 'low 8 bit second address
Write 0,EE_pointer, 10,EE_data
EE_pointer=EE_pointer+1
'put final_duty in eeprom
Write 0,EE_pointer, 20,final_duty
EE_pointer=EE_pointer+1
'put final_period in eeprom
Write 0,EE_pointer, 30,final_period
EE_pointer=EE_pointer+1
'reset write pointer if needed
if EE_pointer > 197 then
EE_pointer=1
endif
'update the write pointer
write 0, EE_pointer
endif


return


'-------------------------------------------------------------------------------


main:


'set ADC
'GPIO = 0
'CMCON = 7
'TRISa = 0 ' designate gpio as output
'TRISa.1 = 1 ' pin ANS0 as input (Supply feedback)
'TRISa.2 = 1 ' pin ANS1 as input (HV-feedback)
'ANSEL.ANS1=1
'ANSEL.ANS0=1
'ADCON0.VCFG = 0 ' Vdd as Vref


'Low Voltage Indicator Light
porta.4=1 'indicator light on


'delay and take Vss reading
Read 0, EE_pointer
'EE_pointer=Eeprom_read(0)
pause 1000
gosub PRE_CALC
gosub UPDATE_SMPS


'setup PWM
PR2=final_period 'sets PWM frequency
CCPR1L=0 '8 most sig. duty cycle bits =0
CCP1CON.5=0 'least significant bits = 0
CCP1CON.4=0 'least significant bits = 0
CCP1CON=12 'turns on PWM (set CCP1CON=0 at any time to turn off PWM)
T2CON=4 'turns on TIMER2 module (required for PWM timing)
last_adjust=0


while
temp=0
ADCIN 0, temp ' ADC conversion
if temp > fb_value then 'feed back OVER set voltage
if last_adjust=1 then 'if 0 then already off
CCPR1L=0
CCP1CON.5=0
CCP1CON.4=0
porta.5=1
porta.4=0
last_adjust=0
endif
else
if last_adjust=0 then
CCPR1L=high_duty
CCP1CON=low_duty
porta.5=0
porta.4=1
last_adjust=1
endif


endif
wend


end]

Ok, first thing, I have not set up the ADC or inputs/outputs yet, this isnt an issue at the moment. i have also not finished with the EEprom yet.

My first question is regarding the While - Wend loop.

If the program is stuck doing stuff in this loop it cant respond to any push button inputs to turn it on or off, or dim the brightness etc. This part of the program will have to be done at the same time as the buttons are monitored. So, do I have the buttons on an interrupt or does this loop run in the back ground somehow?

Heckler
- 10th October 2011, 20:56
I'll give this a go... even though there are definately better programmers around here than me:lemo:

With microcontrollers there is very few instances where things can "go on in the background". Acutally NONE as far as the main processor is concerned, it can only do one thing at a time. Most all PIC's include onboard peripherals like PWM channels or USART serial links, timers and counters, etc. (see the datasheet for your PIC of choice and it will list all the different functions that each pin can be set up for) these peripherals can be set up to do something in the background, like the PWM channel, where you define a given pulse width and it will continue to output that pulse at a given duration and frequency while your program continues on.

Now, you could set up the pushbutton to be tied to an I/O pin that had "Interrupt On Change" capability, either rising edge or falling edge. Then when you pushed the button an INT would be generated and the Pic could jump to the INT handler(check out Darrell Taylors "instant interrupts" for the "best" way to impliment interrupts.

OR you could just include a check for when the button pin changed state within the while/wend loop.

Remember... "can only do one thing at a time" is a relative statement... ie. if you push a button and the contact is closed for 200 msec's then that gives the processor a LOT of time the do something else and still get around to seeing that you are pushing a button.

good luck!

jmgelba
- 11th October 2011, 00:10
Hi Heckler,

Yup, I am aware things are done one at a time. I was wondering if timer based pwm could run whilst an interrupt on change was being checked for.
I am looking at DT's instant interrupts right now actually :)

jmgelba
- 25th October 2011, 04:07
The code below will flash the LED but not create a pwm output. I'm guessing its because there are fractions and not integers to work with thus creating 0's and other odd results.
The convert to voltage portion works fine - I changed it, but the rest needs work still.



temp=0 ADCIN 0, temp
'write 40, temp

'convert to voltage
v_feedback=temp 'put ADC in float
'v_supply=v_feedback/1024
v_supply = 1024/v_feedback * 10 'find supply % of vref
'v_supply=v_supply*supply_multiplier 'find supply volts
v_supply = v_supply * supply_multiplier / 10
write 60, v_supply

'calculate rise time
t_rise=L_Ipeak / v_supply 'find rise time @ supply volts for L/Ipeak
' = 68/24 = 2.833uS (example = 24vin)
'calculate CCPR1L:CCP1CON<5:4>
SMPS_duty=t_rise*osc_freq 'find duty cycle value
'2.833*48 = 136
final_duty=SMPS_duty 'convert to byte

'calculate period
t_period=t_rise*133/100 'dutycycle = .75period
'2.833*133/100 = 3.767uS
'calculate PR2
SMPS_period=(t_period/period_multiplier) 'find period value
'3.767/(4/48) = 45.2
final_period=SMPS_period-1 'put in byte
' = 44.2
'put in register masks
high_duty=(final_duty>>2) 'high 6 bits in CCPR1L
low_duty=(final_duty<<6) 'low two bits for CCP1CON
low_duty=(low_duty>>2) 'shift back to CCP1CON<5:4>
low_duty.3=1 'PWM configuration bit
low_duty.2=1 'PWM configuration bit




main:
CCPR1L=high_duty 'flash LED to show alive
CCP1CON=low_duty
portc.0 = 1
pause 100
portc.0 = 0
pause 100
GOTO main

Ioannis
- 25th October 2011, 07:07
I see an error on your PBP program



define OSC 48temp VAR word


should be



DEFINE OSC 48
temp VAR word


though I had no time to check all of your code.

Ioannis

jmgelba
- 25th October 2011, 13:39
Oops - thats a copy/paste error!!

Basically, I need help setting up a CCP for PWM operation - I have never done it.

Searching for examples...

Ioannis
- 25th October 2011, 14:08
This is for 16F1827 chip I use:




CCP1CON = %00111100 'Control of CCPx modules
CCP2CON = %00000000
CCP3CON = %00000000
CCP4CON = %00000000
CM1CON0.7=0
CM2CON0.7=0

CPSCON0 = 0
CPSCON1 = 0

DEFINE CCP1_REG PORTB
DEFINE CCP1_BIT 3
ccptmrs=0
pr2=249
ccp1con=$0C
t2con=%00000110

DACCON0 = 0



HTH,
Ioannis

jmgelba
- 25th October 2011, 14:26
I didnt know you needed to add defines. I assume CCP1_BIT 3 = PORTB.3 in your example?
Is DACC0N0 = 0 the same command as CMCON = 7? (turn off all comparators) or is it turn off all ADC's?

jmgelba
- 25th October 2011, 17:29
Ok, I understand everything up until this:

ccp1con=$0C

The pwm is already turned on as per the CCP1CON line.

jmgelba
- 25th October 2011, 18:35
Frustrated. Not sure what I am missing now. The CCP1 pin is always high, LED flashes correctly.



DEFINE OSC 48
DEFINE ADC_BITS 10
DEFINE ADC_CLOCK 3
DEFINE ADC_SAMPLEUS 50
DEFINE CCP1_REG PORTC
DEFINE CCP1_BIT 2


final_period var byte
final_duty var byte
high_duty var byte
low_duty var byte


ADCON1 = 00001011
ADCON2 = 00000011


TRISA = 00001111
TRISB = 01110000
TRISC = 00000000
CMCON = 7


CCP1CON = 00111100
PR2 = 44
high_duty=(final_duty>>2) 'high 6 bits in CCPR1L
low_duty=(final_duty<<6) 'low two bits for CCP1CON
low_duty=(low_duty>>2) 'shift back to CCP1CON<5:4>
low_duty.3=1 'PWM configuration bit
low_duty.2=1 'PWM configuration bit
CCPR1L=high_duty
CCP1CON = $0C
T2CON = 4


main:
PORTC.0 = 1
pause 500
PORTC.0 = 0
Pause 500
goto main

jmgelba
- 25th October 2011, 18:56
Figured it out. Helps to have a value for final_duty to load!
final_duty = 136

This gives me exactly what my calculations showed. 3.76uS at 75% duty cycle. 265.90KHz

Splendid!!

Ioannis
- 25th October 2011, 22:00
Sorry I was not online earlier. Which PIC are you using?

Ioannis

jmgelba
- 26th October 2011, 02:23
I am using a 18F2550.

I seem to have it working at the moment however it is not very smooth.

When the on/off button is pressed I need to sweep from 0% duty cycle up to the set limit in 1 second. I can see this happening by writing the incremental counter results to EEPROM and reading them later, however, the duty cycle does not increase for every +1 increase of the duty cycle byte. I assume this is a resolution issue due to running at 48MHz?

I really need to smooth that out and give a very nice gradual increase in light over time.

Also, what is a good way to have the program wait for a pin to go high?

Currently I am using:

waitforon:
If PORTB.7 = 1 then 'on/off button pulled high = on
gosub rampup 'increments final_duty and outputs value to CCP1
endif
PORTC.0 = 1 'flash heartbeat LED
pause 100
PORTC.0 = 0
pause 100
If final_duty = limit then 'if returned to waitforon from rampup, then final_duty is at set limit, so move on to main program
goto main
endif
goto waitforon


Sometimes this will not move to rampup: or move on to main: but the heartbeat LED will always flash correctly.

jmgelba
- 26th October 2011, 02:58
This is the rampup subroutine.

rampup:
For final_duty = 1 to limit
delay = 1000 / limit
pause delay
CCP1CON = %00111100
PR2 = 44
high_duty=(final_duty>>2) 'high 6 bits in CCPR1L
low_duty=(final_duty<<6) 'low two bits for CCP1CON
low_duty=(low_duty>>2) 'shift back to CCP1CON<5:4>
low_duty.3=1 'PWM configuration bit
low_duty.2=1 'PWM configuration bit
CCPR1L=high_duty
CCP1CON = $0C
T2CON = 4
Next final_duty


return

jmgelba
- 26th October 2011, 06:45
Alright, last question of the day, I promise!

This is the main loop code. It works fine holding the current limit by pulse skipping. The current limit is set by comparing the ADC reading to a value stored in fb, 205 in this case, or about 175mA for this application.
The problem comes when the down button is pressed. Instead of lowering fb by 1 it goes to 100% duty cycle, and my current goes as high as the current limit is set on my bench power supply and I have to yank the power.
I thought by lowering, or raising the value in fb, I would be able to control the current limit? What is going on?


main:
If PORTB.6 = 1 then
fb = fb - 1
If fb => 205 then
fb = fb
endif
endif
adcin 1, temp
If temp > fb then
final_duty = final_duty - 1
IF final_duty = 0 then
final_duty = final_duty
endif
endif
If temp < fb Then
final_duty = final_duty + 1
If final_duty = 44 then
final_duty = final_duty
endif
endif




CCP1CON = %00111100
PR2 = 44
high_duty=(final_duty>>2) 'high 6 bits in CCPR1L
low_duty=(final_duty<<6) 'low two bits for CCP1CON
low_duty=(low_duty>>2) 'shift back to CCP1CON<5:4>
low_duty.3=1 'PWM configuration bit
low_duty.2=1 'PWM configuration bit
CCPR1L=high_duty
CCP1CON = $0C
T2CON = 4


goto main

Ioannis
- 26th October 2011, 10:46
I don't see where you write to the EEPROM. If you do, maybe the timing for EEPROM storing (~10 msec) is creating the problem.

Other than that it looks OK the ramp up.

Ioannis

jmgelba
- 26th October 2011, 17:36
I used it once for logging and removed it. I did compensate for the write period. I actually slowed it down a lot so I could see what was happening at individual increments.

I tried i = fb and incrementing the i byte and not the fb byte and it works. I get a smooth up and down output now. It will current limit at each brightness setting.

So, there is the last problem to resolve:

When ramping up I am using duty cycle to increase brightness from zero to full on regardless of where the current limit is set at. The problems are that when I save the new current limit during the main loop, I get flashing as the output goes off during the write command and the pause 10 after the write command. Therefore, I can either write to EEPROM and ramp up to the newly set current limit, or have no flashing and ramp up to full output, go to the main loop and drop straight back to the new current limit. Both options dont look very good.

I am not sure how to write to EEPROM and stop it from flashing.

cncmachineguy
- 26th October 2011, 18:47
I think you may need some logic in there to check for button release. As it looks now, it will run the fb = fb - 1 VERY fast, and every time through the loop when you press the button. Chances are you will not release the button before lots of main loop runs.

jmgelba
- 27th October 2011, 04:07
That is very true. I added a pause of 50mS in each button press if-endif routine.

The problem with that is the LED's flicker each time the button is pressed. Not sure why the pause should effect the PWM. I thought that the PWM was continuous unless you turned it off with CCP1CON = 0. That was my basic understanding anyway and the theory behind the idea. Run pwm continuously and vary the duty cycle based on a target current limit and the actual feedback results. Add or subtract by one the duty cycle number until the current limit is achieved.
Also the LED's flicker when ever there is a WRITE command so I created a counter that only writes the value every 25000 cycles.

Ioannis
- 27th October 2011, 10:03
Even with 25000 cycles EEPROM will wear out relative soon.

About the button,maybe you can try to wait for the button release before proceed.

Also the the idea of add or subtract one to the duty cycle in theory it will work, practically will have a lot of jitter. Here PID control may help (more complicate though).

Ioannis

jmgelba
- 27th October 2011, 13:57
I'd be willing to learn about PID's. It's something I wanted to do anyway. Have you worked with them Ioannis?

Ioannis
- 27th October 2011, 14:28
For something similar to what you try, but not succesfully. It just oscilated and could not fix it.

I will try again, but am busy right now.

Ioannis

jmgelba
- 4th November 2011, 14:04
After some tweaking I have this pretty much working now. I can get it to ramp up slowly to full brightness, maintain a set level, be adjusted up and down, have the new value write to EEPROM and next time it is powered up it will ramp up to the last saved level. All I want to add now is ramp down at power off. Not a problem.

I'd like to reduce code size though. I have a lot of IF THEN's and a lot of FOR NEXT's. They are used to increment and decrement a byte value based on which level output is set.


rampup:l = 0
for l = 0 to led
if L = 0 then
fb = 0
CCP1CON = 0
ramp = 0
portc.2 = 0
endif


IF L = 1 then
fb = 10
endif


if L = 2 then
fb = 45
endif


if L = 3 then
pause 25
fb = 90
endif


if L = 4 then
pause 40
fb = 130
endif


IF L = 5 then
pause 50
fb = 170
endif


if L = 6 then
pause 75
fb = 280
endif


if L = 7 then
pause 100
fb = 450
endif


adcin 1, temp
If temp > fb and L > 0 then
ramp = ramp - 1
IF ramp <= 0 then
CCP1CON = 0
ramp = 0
portc.2 = 0
endif
endif
If temp < fb Then
ramp = ramp + 1
If ramp >= 40 then
ramp = 40
endif
endif
If ramp > 41 then
ramp = 0
endif


CCP1CON = 111100
PR2 = 44
high_duty=(ramp>>2) 'high 6 bits in CCPR1L
low_duty=(ramp<<6) 'low two bits for CCP1CON
low_duty=(low_duty>>2) 'shift back to CCP1CON<5:4>
low_duty.3=1 'PWM configuration bit
low_duty.2=1 'PWM configuration bit
CCPR1L=high_duty
CCP1CON = $0C
T2CON = 4


pause 100
next l
goto main

This is the ramp up routine that seems to work well. The ramp down is similar. Any way to reduce this block of code in size?

sangaboy
- 16th November 2011, 09:27
I like you project but im new user icanot able to post prease can you give instruction how can i communicate with different people in this chart

boroko
- 19th November 2011, 09:35
Can I ask for the link to the original project on Instructables?

jmgelba
- 22nd November 2011, 13:22
Sorry, I downloaded the zip with all the files in it, so I dont have a link. I'm sure you could search for it on their site though.

Incidentally, I got this boost controller to work and have been adding features to it. It works quite well but I still need to clean up some noise on the output.

boroko
- 23rd November 2011, 01:28
A search revealed a number of them. I was just trying to narrow down which circuit you were dealing with.

Bo

sangaboy
- 24th November 2011, 14:53
Im a new use in this forum my project concerned about generation of 3 phase SPWM I have not know how to comunicate with different user in this link

Archangel
- 25th November 2011, 02:27
Im a new use in this forum my project concerned about generation of 3 phase SPWM I have not know how to communicate with different user in this link
If you have questions, ask them in open forum, create your own thread, and people most likely will help. PM messages from strangers are usually unwelcome, and the same person who might help you in your thread may be UNWILLING if you PM them. Additionally your questions and the answers posted in a thread may well help someone else who is having a similar problem. I myself have learned many things working on someone else's problems.