Thursday, December 23, 2010

Digital thermometer with LM35 and PIC18F452

This can be a complete project on its own - a simple DIY digital thermometer with LCD display and only a handful of parts - the PIC18F452, LM35 and a small number of resistors and capacitors running off a regulated 5v supply.
Temperature range - 0 to 150'C
Display type - LCD (can be 16x1, 16x2 or anything larger)
Controller: PIC18F452
Programming Language: BASIC
Compiler: mikroBASIC PRO for PIC v3.20


'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
program thermometer452

'Programmer: Syed Tahmid Mahbub
'Compiler: mikroBASIC PRO for PIC v3.20
'Target PIC: 18F452 - 40pin DIP
'Configuration: Check the project in the download link for the configuration bits

dim LCD_RS as sbit at RB4_bit
    LCD_EN as sbit at RB5_bit
    LCD_D4 as sbit at RB0_bit
    LCD_D5 as sbit at RB1_bit
    LCD_D6 as sbit at RB2_bit
    LCD_D7 as sbit at RB3_bit

    LCD_RS_Direction as sbit at TRISB4_bit
    LCD_EN_Direction as sbit at TRISB5_bit
    LCD_D4_Direction as sbit at TRISB0_bit
    LCD_D5_Direction as sbit at TRISB1_bit
    LCD_D6_Direction as sbit at TRISB2_bit
    LCD_D7_Direction as sbit at TRISB3_bit

dim ADCResult as longword
dim value as word[3]
dim vstring as string[3]

sub procedure GlobInit
    TRISB = 0
    PORTB = 0
    TRISA = 1
    ADCON1 = $4E
    LCD_Init
    LCD_Cmd(_LCD_CLEAR)
    LCD_Cmd(_LCD_CURSOR_OFF)
    LCD_Out(1, 1, "Temp:")
    LCD_Out(1, 15, "'C")
end sub

main:
     GlobInit
     while true
           ADCResult = (ADC_Read(0) * 500) >> 10
           value[0] = ADCResult div 100
           value[1] = (ADCResult div 10) mod 10
           value[2] = ADCResult mod 10
           vstring[0] = value[0] + 48
           vstring[1] = value[1] + 48
           vstring[2] = value[2] + 48
           LCD_Out(1, 10, vstring)
           delay_ms(50)
     wend
end.
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Schematic:
 

Hex file + Schematic + Code freely available for download:
http://www.4shared.com/file/wrT3PgNJ/hex__schematic__code.html

The code can be freely used and there is no copyright restrictions or anything as such. Only credit the author where necessary.

Digital thermometer with LM35 and PIC16F88

This can be a complete project on its own - a simple DIY digital thermometer with 7-segment displays and only a handful of parts - the PIC16F88, LM35 and a small number of resistors and capacitors running off a regulated 5v supply.
Temperature range - 0 to 150'C
Display type - 3 digit multiplexed 7 segment or 3 individual 7 segments
Controller: PIC16F88
Programming Language: BASIC
Compiler: mikroBASIC PRO for PIC v3.20

Complete code:

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

program DigThermo

'Programmer: Syed Tahmid Mahbub
'Compiler: mikroBASIC PRO for PIC v3.20
'Target PIC: 16F88 - 18pin DIP
'Configuration: XT OSC, PWRTE OFF, WDT OFF, RA5 - MCLR OFF, BOR OFF, LVP OFF, CP OFF, CPD OFF, DEBUG OFF, CKMNE OFF, INTEXTSW OFF

dim data7, num7 as byte
dim temperature as longword
dim digit as byte[3]

sub procedure send7 (dim value, seven as byte) 'Common Anode Displays (2 7segments)
    PORTA = 0
    select case value
           case 0 data7 = $C0
           case 1 data7 = $F9
           case 2 data7 = $A4
           case 3 data7 = $B0
           case 4 data7 = $99
           case 5 data7 = $92
           case 6 data7 = $82
           case 7 data7 = $F8
           case 8 data7 = $80
           case 9 data7 = $90
    end select
    PORTB = data7 'send temperature
    select case seven
           case 1 num7 = 2
           case 2 num7 = 4
           case 3 num7 = 8
    end select
    PORTA = num7 'choose which 7segment
end sub

sub procedure GlobInit
    TRISA = 1
    TRISB = 0
    PORTA = 0
    PORTB = 0
    ANSEL = 1 'AN0 analog
    CCP1CON = 0 'PWM off
    CMCON = 7 'Comparator off
end sub

sub procedure delay2ms
    delay_ms(2)
end sub

main:
     GlobInit
     while true
           temperature = (ADC_Read(0) * 500) >> 10
           digit[0] = temperature div 100
           digit[1] = (temperature div 10) mod 10
           digit[2] = temperature  mod 10
           send7(digit[0],1)
           delay2ms()
           send7(digit[1],2)
           delay2ms()
           send7(digit[2],3)
           delay2ms()
     wend
end.

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''

Schematic:




The code can be freely used and there is no copyright restrictions or anything as such. Only credit the author where necessary.

Hex file can be freely downloaded from:
http://www.4shared.com/file/o-OBjgGB/DigThermo.html

Thursday, December 16, 2010

Single microcontroller based 12v to 230v inverter with intelligent battery charging

 Single microcontroller based 12v to 230v inverter with intelligent battery charging (Made by me)


Here I describe the circuit as:

*single microcontroller/controller (ATMEGA16/32)
*no op-amps, only chips are the micro, opto-couplers and regulator (7805)
*low-battery/overload/short-circuit protection
*thyristor controlled battery charger, using the MOSFET body diode as the AC-DC rectifier
*charger maintains the battery voltage (top) between 13.2-13.5v (adjustable) to maximize the battery life
*6-LED display
*Only 93 parts in entire control circuit
*Delay between switchover to prevent inrush current

The method used here, described for those who may benefit from it:
-Initialize all ports and peripherals[ADC, Timers, Compare Modules]
-Initialize interrupts for Timer0 and compare module for
-For PWM, use Timer1 and 16-bit Phase and Frequency Correct PWM mode so the PWM runs completely on the hardware level without need for interaction to keep it running
-The AVR senses whether mains is present or not using a standard opto (4N35).
-If mains present, check battery level
-If battery level < 13.5v (this voltage is set using a pot, so can be easily adjusted), charge at the set current(set with a pot)
-If battery level > 13.5v, stop charging
-While battery > 13.2v, stop charging
-If battery voltage drops instantly start charging again
-Triac based, uses Timer0 and compare module with interrupt for phase angle control for fast charge, never overcharges battery, battery hasn't ever heated up till now and 2 year old battery still gives good backup, so charging algorithm is good for battery life
-Check mains
-If mains absent, initialize Timer and start PWM
-Check battery voltage, stop PWM and indicate on LED when battery falls below 10.8v (this is also set with a pot), response time is fast so a short circuit that produces an instant voltage drop is detected
-Check load level, check against preset level (set with pot) and if too high, shut down and indicate
-Check output voltage, adjust as required
-Check mains

*Coding is done with mikroBASIC PRO for AVR

*All voltages mentioned, eg 13.2v, 13.5v, 10.8v, Overload voltage, etc are all adjustable and set with variable resistors































This is a quasi-sine wave inverter that I made since it was more demanding than the sine at the time. I have a project with sine as well with a PIC but it's not very organized.

IPS stands for "Intelligent Power Supply", in short the inverter. IPS on means inverter on.
No, they're power MOSFETs. The design here uses IRFZ44N x 5 on each leg for 800W. You can use other MOSFETs as well. I haven't tried though. There are 4 transistors for driving the MOSFETs, on the control board - 2xPN2222, 2xPN2907. There are 2 more transistors on the MOSFET board.
It's 12-30v with changing resistors. For upto 48v, you need to change the 7805 with an auxilary supply, that's the only change.
If battery increases while charging, then there is battery full charged indicator.
Output volt is adjusted to achieve 230V or 220V as required, that is for feedback voltage setting or output voltage setting when running in inverter mode. I set mine at 230V.
Battery max is for battery high cut voltage, to cut off charging when battery reaches a specific voltage. I set mine at around 13.5v.
Charging current is for setting the current at which battery is to be charged. I set mine for 12-15A for 70Ah battery for quick charge.
Overload is for setting the maximum load. A load (800W in this case) is applied while running in inverter mode, and the pot is adjusted slowly till at one point the inverter turns off and the LED shows overload.
Low battery is for setting battery low cut voltage. I set mine for 10.5v.
On the board, the pots are labeled "HI CUT, LO CUT, OVERLOAD, FEEDBACK, CHARGING CURRENT". The one for current is a pot that is adjustable in small units.

The 4 transistors are for MOSFET driving, the other 2 are for fan and relay.
There are 2 optocouplers, one for mains sense, the other for zero crossing detection. (If you are interested in zero-crossing detection, go through this: http://tahmidmc.blogspot.com/2012/10/zero-crossing-detection-with-pic16f877a.html
I forgot to mention, this circuit requires NO AUXILIARY 9-12V TRANSFORMER.

The transformer I have is 12-0-12 primary, doesn't need to be accurate, since you can adjust the output voltage using the pot. What I meant is, say you wanted a 12-0-12 transformer, but you got some error, then you can just adjust the preset/pot to set output at 230v. No separate winding, feedback is done on board using diode/cap/resistor and micro. Charging is done using the same MOSFET board, no special capacitor or inductor, just a snubber on the board. Transformer primary is not strict. The one I used has 12-0-12 primary, secondary 0-140-280. 140 is the charger tapping, 280 is the output voltage that is adjusted.

In my circuit, I've tested it numerous time and micro never misfired. Moreover, if you notice the board, you should say a hell lot of .1u ceramic caps, ie, 104pF caps. So since decoupling is so well done, I don't think there will be firing problems. A snubber may be added IF NECESSARY. I've found that the snubber isn't always necessary and gets very hot, so I omitted it, however it may be necessary in some situations so could be added there. This is also one reason I used BTA26, huge triac so that it isn't usually shorted by other factors as I know heat and firing are not issues. All in all, this seems reliable and has run over the last few months under numerous tests.
Yes, MOSFET can be added for a nice design, but I omitted it as it was more demanding to have the MOSFET board separate, in case the MOSFETs burnt. Haven't had a situation till now, but it can easily be made into one nice PCB.

The transformer is rated at 800W power and is a standard transformer used for 800W inverters over here. I don't know about the details as I got it wound by an acquaintance who is an expert in making such transformers. The primary voltage is 12-0-12, secondary voltage is 0-140-280.
This inverter has short circuit protection. It uses the fact that during a short circuit, DC bus voltage significantly decreases. The microcontroller senses that and indicates short circuit.
Reverse voltage protection isn't provided as it's connected to the battery 24/7.


Yes, the battery charging part. My one isn't as complicated. It's a simple charger that doesn't charge the battery to absolutely full charge, but maximum charge so that it can provide optimum backup and good battery life.
The battery is charged at around 16A when near full discharged and charge current slowly decreases to about 10A when battery is near 13.5v, when battery charging is completely terminated to prevent gassing or oxidizing of the plates.
The battery charging is phase angle control based and the charging is stopped every 13 seconds when battery voltage is checked. Then charging is resumed again. I initially implemented trickle charging but later discarded it as I found it unnecessary in this case, because whenever battery voltage falls charging is again resumed for a few minutes to make sure battery is fully charged, or at least apparently fully charged.
I've had the test unit running for 8 months, but the final version as shown in the pictures has been running for about 6 months. The battery has never become hot. Moreover, it was about a year old when I started using this inverter. The battery hasn't inflated yet and provides good backup. I don't even have to replace the water frequently, so that shows that the water isn't vaporizing.
I'm using a 12v 70Ah battery. As mentioned earlier, I can run a 40W fluorescent lamp, 80W ceiling fan and computer (around 250W when not at 100% load) for about 2 hours before the unit indicates low battery. From there it takes about 6-7 hours to fully charge the battery and the charging termination to become stable. Usually after running the inverter with 80W fan and 40W lamp, it takes an hour to get it to full charge.
The unit is designed for 150-250v.

The charging current changes with battery rating. The values mentioned are for 70A battery with initial charging current set manually.

I will upload schematic and PCB soon.

Basic Theory on AVR Timer0


To start off with, the formulae relating duty cycle, frequency and time period is: f=1/T, D=t/T where D is duty cycle, T is time period, t is on time, f is frequency
Vo = D * Vin, where Vo = average voltage output from AVR pin, D = duty cycle, Vin = Supply Voltage, eg 5v
The output voltage here is in the form of pulses not a complete analog value.
Eg. When frequency is 100Hz, duty cycle is 50% and supply voltage to AVR is 5v, then output from AVR pin is 2.5v. However, this does not mean that the output will be 2.5v DC. The output will be a pulsating output with frequency = 100Hz, 50% on, 50% off, with high state or on state voltage = 5v, low state or off state voltage = 0v. Therefore output = 2.5v, but actually 5v with 50% on and 50% off. This CAN be converted to pure DC by placing an RC or LC filter, which will convert the pulsating DC to a clean fixed DC voltage.
I will refer to ATMEGA8 for example, as this seems to be one of the most popular AVRs.
There are 3 timers – Timer/Counter 0, Timer/Counter 1 and Timer/Counter 2. I will refer to Timer/Counter as TC, so TC0 means Timer/Counter 0.
TC0 and TC2 are 8-bit timers. This means that the timers will count from 0 to (2^8) – 1 = 255. TC2 has one associated compare module. TC1 is 16-bit, so it counts from 0 to (2^16)-1 = 65535. It has two compare modules attached and provides a rich set of PWM functions.
I’ll start off with TC0.
TC0 has 4 registers associated with it that we need to work with. The most important is Timer/Counter 0 Control Register – TCCR0.

 Take a look at the registers below:


The CS2, CS1 and CS0 bits determine the rate at which the value of the TC increments. Say we have a clock of 10MHz. Therefore we have a clock cycle = T = 1/F = 1/10MHz = 100ns. Prescaler extends this time by a specific amount. So, looking at TCCR0, if we write 0 to it, we have no clock source, this means the TC is stopped and will not increment. It retains its current value.
If we set CS[2…0] to 1, we have no prescaler, that means that the TC increments each 100ns, ie each clock cycle. If we assign prescaler 8, by writing 2 to CS[2…0], we extend the time at which TC increments by 8. With no prescaling it would take 1 clock cycle for each increment, therefore with a prescaler of 8, the time would be extended 8 times, ie, it would now take 8 clock cycles for each increment, ie 800 ns.
The same follows for prescaler values of 64, 256 and 1024.
Eg. With a clock cycle of 10MHz, prescaler = 1024, the time taken by TC0 for one increment is:
Time for 1 increment with no prescaling = 1 clock cycle = 1/10MHz = 100ns
Therefore, time for 1 increment with prescaler = 1024 = 1024 clock cycles = 1024*0.1us = 102.4 us
[us = microseconds, ns = nanoseconds]
I will not explain the external clock source as it is not required for PWM.

 Take a look at the registers below:

The second associated register is TCNT0 that holds the current value of TC0.
E.g. What will be the value in TCNT0 when CS[2…0] is cleared (=0) after a time of 10secs? Clock = 1MHz, prescaler = 8.
Solution:
Each clock cycle = 1/1MHz = 1us [us = microsecond]
TC0 is incremented (if prescaler = 0) each clock cycle, ie each us(microsecond)
Therefore TC0 is incremented (when prescaler = 0) each 8 clock cycles, ie each 8 us(microsecond)
10 seconds = 10,000,000 microseconds
TC0 counts to a maximum of 255. That’s 255 increments. After this it overflows, ie, it rolls over from TOP [TOP means the maximum value to which TC0 can increment], in this case 255, to 0.
Each increment takes 8 us.
Therefore 256 increments (255 increments + 1 increment to overflow to 0) takes 256 * 8us = 2048us = 2.048ms [ms = milliseconds] = 0.002048 seconds
Therefore in 10 seconds, number of overflows = (10/0.002048) = 4882.8125
That’s 4882 overflows. There’s a remainder, 0.8125, which means a remainder of 0.8125*255 = 207.1875
Therefore 207 is stored in TC0.

Take a look at the registers below:

The third associated register is TIMSK – Timer/Counter Interrupt Mask Register. Here bit 0 is what is associated with TC0. TOIE0 – Timer/Counter Overflow Interrupt Enable 0. Setting this bit enables setting TC0 interrupt. I’ll go into details about this later.

Take a look at the registers below:

The last associated register is TIFR – Timer/Counter Interrupt Flag Register. Here bit 0 is what is associated with TC0. TOV0 = Timer/Counter Overflow Flag 0. This flag is set whenever the timer overflows (this has been shown in the above example). Writing one to this bit clears it.

There's more to come about the Timer PWM modes and Timer1 and Timer2 and all PWM modes.
Hope this is helpful to all.

Registers being referred to: