PWM code for 4-pin brushless DC fan - help please!

Hi everyone,

I've been hanging around "back stage" for a while now, reading and learning. I wanted to bring something to the table before asking for help, and I noticed that there are a fair few posts (and loads of questions on the web) regarding PWM control of computer-type fans.

What I discovered after my first prototype didn't work — and now it seems obvious — is that the DC brushless motor in the fan has its own electronics which, don't like being switched on and off rapidly!! From what I understand, the ONLY way to do this (aside from having a very, very low PWM rate) is to use a 4-pin fan, where the 4th connection is a PWM input to the fan's own electronics.

I have created some code, which is a little messy, but it does work.

The part that I am really struggling with is the "boost" (I've heard the term 'goose' also) to overcome the starting inertia from being stationary. I don't know how to set parameters so that the boost kicks in if the speed is to increase, but not if the speed is to decrease. Would I have to use two registers to do this?

Thanks & kind regards,
Craig.

Code:
'    Temperature sensitive fan-speed controller for 4-pin brushless DC type fan


'    serout C.0,N2400,(254,1) 'clear screen
'    serout C.0,N2400,(254,192,"Temp ",#b1,%11010010,"C ")  ; transmit value in b1 to serial OLED


symbol fan = C.2     ; Pin 5 - the only PWM output on the 08M2 chip
symbol LED = C.1     ; Pin 6 


gosub blast        ; ensures the fan starts spinning until the temp is determined to be low enough


main:                    ; set an appropriate PWM value according to temperature
        
        gosub howhot        ; read the temperature from the DS18B20 into b1
        gosub display        ; output the temp to serial display for testing purposes
        select case b1         ; use the value of the temperature in b1
        case <20            ; if temp is less than 20ºC
            goto offFan        ; switch fan off off
        case 20 to 24                ; temperature range
;gosub blast        ; kickstarts the fan from still, BUT unfortunately also boosts when reducing the PWM level :(
            goto fiftyFan        ; quiet at 50%
        case 25 to 29                ; temperature range
            goto sixtysixFan        ; low vibration at 60% - try 66%
        case 30 to 34                ; temperature range
            goto seventysevenFan    ; little noisy at 77% - try 85% noise but no vibe, too blowy though
        case 35 to 39                ; temperature range
            goto ninetyfan    ; 90% duty mostly wind noise
        case >= 40                    ; temperature limit
            goto alarm        ; max output fan with audible & visible alerts
        Else
            high fan            ; fan on full
        endselect
                                
            
blast:
        pwmout 2, 39, 160             ; fan at full power briefly to break inertia
        pause 250             ; the shortness of the brieflyness 
        return    
            


howhot:
        pause 750            ; provide enough time to initialise and also pause between temp reads
        readtemp C.4,b1       ;read temperature value from Pin 3 into b1
        return


        
display:
        serout C.0,N2400,(254,1)     ;Pin 7 clear screen
        pause 30
        serout C.0,N2400,(254,192,"Temp ",#b1,%11010010,"C ")  
        ; transmit value to serial OLED with binary code for º symbol
        return        


offFan:    
            pwmout 2, 39, 0                ; fan off for such cases as the temp being below minimum
            goto main


fiftyFan:                                ; pwm duty output
            pwmout 2, 39, 80
            goto main
        
sixtysixFan:                            ; pwm duty output    
            pwmout 2, 39, 106
            goto main
                    
seventysevenFan:                        ; pwm duty output    
            pwmout 2, 39, 123
            goto main            
            
eightyfiveFan:                            ; pwm duty output
            pwmout 2, 39, 136
            goto main
            
eightyeightFan:                            ; pwm duty output    
            pwmout 2, 39, 141
            goto main            
            
ninetyFan:                                ; pwm duty output    
            pwmout 2, 39, 144
            goto main
            
fullFan:                                ; pwm FULL duty output    
            pwmout 2, 39, 160
            goto main
            
        
alarm:                        ; maximum temperature reached!
        ;sound C.4, (100, 100)     ; Pin 3 freq100, length 100 for future use
        pwmout 2, 39, 160        ; fan on FULL
        high LED                 ; switch the LED on C.1 high
        pause 250             ; wait 0.25 sec 
        low LED                 ; switch off the LED
        pause 100             ; wait 0.10 sec
        high LED                 ; switch LED back on
        pause 250             ; wait 0.25 sec
        low LED                 ; switch off the LED
        pause 400             ; wait 0.40 sec
        goto main            ; will stay at max until the temperature drops
                            


        
interrupt: return             ; helps with download errors
About Me as I'm new:
I'm late 30's and getting back into hobby electronics after a career change into photography for 16 years (I was initially a systems apprentice). I was put off 'micros' at college after spending HOURS typing in machine-code to achieve almost nothing, and always tending to stick to analogue electronics after that!
I'm currently studying Physics with the Open University, working part-time, and looking after the home & kids — so Picaxe fits into the small pockets of time that I manage to steal here and there..!
 

Hemi345

Senior Member
I think I would use a variable for current dutycycle (curPWMduty) and what it needs to change to (newPWMDuty) and then check that variable and if it's 0, then run the blast routine.

Like:
Code:
symbol curPWMduty = b10
symbol newPWMduty = b11
...
    select case b1
        case <20            ; if temp is less than 20ºC
            newPWMduty =  0        ; switch fan off off
...
        case 25 to 29                ; temperature range
            newPWMduty =  106       ; low vibration at 60% - try 66%
        case 30 to 34                ; temperature range
            newPWMduty = 123        ; little noisy at 77% - try 85% noise but no vibe, too blowy though
...
        endselect
        if curPWMduty = 0 then
          gosub Blast
        endif
        curPWMduty = newPWMdut
        pwmout 2, 39, curPWMduty
        goto main

Code:
 

PaulRB

Senior Member
I would have thought you would only need to boost/goose from stationary, Craig. Not that I've ever done what you're attempting....

Welcome to the forum.

Paul
 
Thanks Paul,

And you're right: it steps up the speed seemingly without any problems.

The fan itself does have some in-built function to kick-start it (after a good few seconds), but for completeness and also as a learning exercise I'd like to have the code do it!

Craig.
 

Lostmyed

New Member
Hi Craig.
I was just looking at trying to create this same circuit myself, unfortunately I have little to no experience in creating the circuit itself. I there any chance you could post the circuit ?
 

geoff07

Senior Member
There will be a pwm level that keeps the motor moving, albeit silently. Some fans claim to have a linear voltage/speed curve. This certainly isn't true for my fans under pwm control. For example, one fan has a curve thus:

PWM/1000 speed
0 0
16 1080
52 1680
211 2640
1000 2880

so for that fan the active range is the lowest 20% of the pwm scale.

A three pin fan can work fine. What you do is run it on the normal PWM signal from the mosfet driver that you probably use. Then, say once a second, stop the PWM, set the power to on, use pulsein to measure the pulse interval from the fan tacho, then revert the output to pwm. This happens very fast so the fan speed is unaffected, but the electronics is run on the full 12v for the duration of the measurement so the interval is valid and reads the speed experienced when under pwm control. You can convert easily enough to rpm from interval. This technique also prevents stalling, because if the fan isn't moving the pulsein will timeout, giving a 0.6S 'on' period at full voltage for the motor and keeping it spinning.

Signal conditioning for the tacho was a problem for me, so I use a BC548 in a common-emitter circuit, see attached, to clean up the pulses and get them into the right logic values. This is for a fan in a 'silent' pc that runs in the living room. The headers connect to a DB18S20 sensor attached to the cpu heat pipe, a pot to set the temperature set point, and the three-wire fan. It uses the PC 12v/gnd/gnd/5v supply. The driver is a MAX4427, and the 0R resistors are so I can make a single-sided board.

I found using separate threads ('starts') made the code very simple. For example, one thread simply runs the fan using a pwm value set elsewhere, and once a second measures the speed as described above, and saves it. Other threads process the set point and specify the required pwm setting, flash an led according to the temperature difference, and report data via sertxd when testing. Flags are used to communicate between the threads, to indicate new data is available.
 

Attachments

inglewoodpete

Senior Member
I have successfully used a PICAXE to vary the speed of a 3-wire brushless fan. Within reasonable limits, the output of the fan that I used was proportional to the average voltage across it. I used PWM and a reasonably large electrolytic capacitor to even out the PWM to a steady voltage.

The minimum PWM rate was determined during testing. If I need cooling below that speed, I turn the PWM on or off according to the heat level.

When starting the fan, use a higher PWM rate for the first 100-200 mS to ensure it starts quickly.
 

Hemi345

Senior Member
I just saw a bug in what I suggested above, this would probably work better (otherwise, what i suggested above would cause the blast routine to run on every loop when the fan should be stopped):
Code:
        if newPWMduty != 0 AND curPWMduty = 0 then
          gosub Blast
        endif
        curPWMduty = newPWMduty
        pwmout 2, 39, curPWMduty
        goto main
 
Top