Testing
With most of the interfaces to the Cypress PSoC previously tested, any MOSFET losses at full load were of most interest. The datasheet for the MOSFET shows that the drain to source resistance is 13.3mR for a 4.5V gate drive.
Instead of testing with the fan and needing to handle the commutation caused by the fan itself, a 1R 1% resistor was used in place of the fan. The current limit on the power supply was set to 1.2A and the voltage measured across the 1R resistor, approximately 2.22V and across the MOSFET 0.056V. Gate voltage was 2.50V.
For the 0.056V drop across the MOSFET the resistance drain to source was around 47mR. No issue with heat dissipation at such low current through the device.
The PSoC used in the schematic was the same as the development board, a CY8C4245AXI-483. Using the Design Wide Resources window from PSoC Creator as a reference connections were drafted in the schematic.
|
PSoC Creator Design Wide Resources for DC Fan Controller |
Connections to the PSoC are shown in the capture below.
|
PSoC Connections |
The above image shows the connections for the digital and analogue power, programming header for a MiniProg and option resistors.
|
PSoC Input Protection |
For the prototype board some basic input protection was added by means of a series 10R resistor and rail to rail steering diodes as shown in the above capture.
|
PSoC Filtered Power |
To complete the PSoC portion of the design, the capture above shows the filtered 5VDC power for the digital and analogue supplies. This type of filtering and decoupling of the main supply is essential considering that a switch mode is being used for the main 5V supply rail.
|
Comair Fan Connections |
The above capture shows the external connections for the Comair Rotron DC fan. Pins 1 and 6 are the main 24VDC power supplying the controller board and the remaining pins 2 to 5 are the connections to the Comair fan. There is a reason that the 0VDC for the fan and the main 0V supply are side by side on the connector which has to do with the routing of the circuit board.
|
One Wire Connection |
One Wire connection is shown in the capture above.
|
I2C Bus Driver Connection |
I2C connections is optional on this design and the capture above is a datasheet implementation for testing. Protection could be added to the line facing side of the PCA9615 by means of TVS diodes and even small series resistance.
|
Schematic Top Sheet |
Finally for this Altium design, which uses a top sheet to connect all lower level sheets, the capture above illustrates the connections between sheets with power connections being global to all sheets.
Controller Enclosure
In selecting an enclosure for the controller the two requirements were the price and size, cheap and small as possible.
|
Altronics enclosure H9404 |
A supplier, Altronics, has some very affordable snap together enclosures and out of this selection the H9404 model looked fit for the purpose. Inside the enclosure are four posts to mount the controller circuit board then the removable lid snaps back on.
PCB Design
Beginning with a blank layout in the PCB layout software, the datasheet for the plastic enclosure was consulted to determine the dimensions of the circuit board to be mounted inside. Instead of using the actual dimensions specified by the datasheet, the circuit board was reduced in size in both dimensions by 1mm to account for changes in the enclosure during manufacturing or inaccuracies when the circuit board is cut, although the latter is rare for most manufacturers.
|
Circuit board dimensions with guides |
The above capture shows the circuit board with dimensions of 64 x 44 mm and work guides for setting the mounting post locations. For such as small circuit board work guides are hardly required, however if they are available in the circuit board layout software it can be good practice to use guides.
|
Circuit board unrouted with double sided placement |
To simplify the part placement on the controller circuit board the design was made double sided. The spread of components between top and bottom layer follows a rule often applied to double reflow designs which is one side for heavy components and the opposite side for light. Use of this layout may be suitable for home reflow ovens.
|
Circuit board routed with double sided placement |
Shown above is the routed four layer prototype board. The top and bottom are the signal layers with the two middle planes utilised for power.
PCB Population
Back from manufacture at PCBWay the unpopulated circuit board is shown below.
|
Blank DC fan controller board |
To begin with, only the power supply section was fitted to the board to ensure the 5VDC supply was stable.
|
Fan DC power supply section |
After powering up the board to 25VDC the on-board supply was confirmed to be working at 5.05VDC.
The Cypress PSoC and associated components were fitted to the board for initial testing with the One Wire sensor. Utilising a circuit board holder is an efficient tool well when board under test required probing on either side.
|
DC fan controller bench testing |
Draft Code
With the OneWire code tested, a first draft of the controllers code was compiled and programmed. Initially a lower temperature was set to trigger the fan. The draft code is listed below.
//==============================================================================
//
// OneWite temperature controlled DC Fan
// 10/10/2017 Greg L Initial write for functionality testing
//
//==============================================================================
#include <project.h>
#include <stdio.h> /* Required for sprintf */
#include <stdbool.h>
/* Global Defines */
#define debug 1 /* Debug via serial port */
#define Fan_Hyst_Compare 60u
#define Temp_Low_Thres 2700u /* Temperature deg C div 100u */
#define Temp_High_Thres 2900u /* Temperature deg C div 100u */
/* Global Variables */
static volatile CYBIT flag_Timer = false;
volatile uint8 tacho_data_ready = false; /* Tacho data ready flag - modified by ISR make volatile */
volatile uint16 Tacho_Counts = 0; /* Made global to share with ReportData function */
uint16 Last_Temp_Read = 0;
/* Structure */
struct {
uint8
Fan_run : 1, /* Start after power cycle */
Over_temp_trig : 1, /* Over temp triggered */
Fan_array_full : 1, /* Fan array of measured spped is loaded */
Fan_sample_fast : 1; /* Fan samples fast at 500msec, if off 10 sec */
} System;
/* ISR Prototypes */
CY_ISR_PROTO(isr_Timer);
CY_ISR_PROTO(isr_CounterResult);
/* Function Prototypes */
void Initialize(void);
void ReportData(void); /* Temperature to deg C and fan speed send thru UART */
//==============================================================================
// Timer Interrupt
//==============================================================================
CY_ISR(isr_Timer) /* ISR Timer to report temperature at regular intervals */
{
flag_Timer = true;
isr_Timer_ClearPending();
PWM_1_ClearInterrupt(PWM_1_INTR_MASK_CC_MATCH);
}
//==============================================================================
// Timer Interrupt
//==============================================================================
CY_ISR (isr_CounterResult)
{
tacho_data_ready = true;
Timer_3_ClearInterrupt(Timer_3_INTR_MASK_CC_MATCH);
isr_Counter_ClearPending();
}
//==============================================================================
// Init PSoC
//==============================================================================
void Initialize(void)
{
UART_Start(); /* UART start */
OneWire_Start(); /* OneWire start */
#ifdef debug
UART_UartPutString("\033[2J\033[HTemperature sensor Maxim DS18B20:\r\n"); /* Clear screen and cursor home */
if (OneWire_CheckPresence() == 0x1) {
UART_UartPutString("Sensor detected\r\n");
}
#endif
isr_Timer_StartEx(isr_Timer); /* Attach handler */
PWM_1_Start(); /* One second window timer for tacho measurement */
Timer_3_Start(); /* Pulse counter for tacho, 2PPR */
isr_Counter_StartEx(isr_CounterResult);
System.Fan_sample_fast = true; /* Set fan samples to fast */
if (System.Fan_run == false) {
PWR_Fan_Write(true);
Timer_2_WriteCompare(0x2000); /* Max PWM */
Timer_2_Start(); /* Fan PWM control */
CyDelay(5000); /* Run fan on start */
PWR_Fan_Write(false);
Timer_2_Stop();
}
}
//==============================================================================
// Convert temperature code to degC and
// Send result to Terminal using UART
//==============================================================================
void ReportData(void)
{
char buf[8] = {0};
sprintf(buf, "\033[3;0H%d.%02d\t", Last_Temp_Read/100, Last_Temp_Read%100); /* Reset cursor to start of third line */
UART_UartPutString(buf);
sprintf(buf, "%04d ", Tacho_Counts);
UART_UartPutString(buf); /* Output UART buffer */
}
//==============================================================================
// Main
//==============================================================================
int main()
{
const uint16 Fan_Time_Hyst = 300u; /* Time in seconds temp must be outside of thresholds */
const uint16 Fan_Time_Max = 600u; /* Time in seconds for max continuous fan run time */
uint32 Tmr2_Comp_Value = 200u;
uint16 Fan_Hyst_Counter = 0;
uint16 Fan_Run_Counter = 0;
uint16 Fan_Speed[10]; /* Record mutliple fan readings to average */
uint32 Fan_Speed_Temp = 0; /* Working fan speed temp register */
uint8 Fan_Speed_Index = 0; /* Index for the array */
uint8 Index_Temp = 0;
CyGlobalIntEnable; /* Enable global interrupts */
Initialize();
for(;;)
{
if(flag_Timer) /* read DS18B20 on timer, interval of 1sec */
{
flag_Timer = false;
OneWire_SendTemperatureRequest();
/* Normal state, no alarm, rising temperature */
if ((System.Over_temp_trig == false) && (Last_Temp_Read > Temp_High_Thres)) {
Fan_Hyst_Counter++;
}
/* Alarm state, falling temperature */
if ((System.Over_temp_trig == true) && (Last_Temp_Read < Temp_Low_Thres)) {
Fan_Hyst_Counter--;
}
/* Alarm state, rising temperature */
if ((System.Over_temp_trig == true) && (Last_Temp_Read > Temp_High_Thres)) {
Fan_Run_Counter++;
}
}
if (OneWire_DataReady) { /* DS18xx completed temperature measurement - begin read data */
OneWire_ReadTemperature();
Last_Temp_Read = OneWire_GetTemperatureAsInt100(0);
}
/* Handle temperature */
/* Exceeded the overtemp for more than specified duration */
if (Fan_Hyst_Counter >= Fan_Time_Hyst) {
System.Over_temp_trig = true;
PWR_Fan_Write(true);
}
/* Exceeded the over time for more than specified duration */
if ((Fan_Run_Counter >= Fan_Time_Max) && (System.Over_temp_trig = true)) {
System.Over_temp_trig = false;
Fan_Run_Counter = 0;
Fan_Hyst_Counter = 0;
PWR_Fan_Write(false);
Timer_2_WriteCompare(0u);
Timer_2_Stop();
}
/* Exceeded the undertemp for more than specified duration */
if (Fan_Hyst_Counter == 0u) {
System.Over_temp_trig = false;
PWR_Fan_Write(false);
Fan_Run_Counter = 0;
Timer_2_WriteCompare(0u);
Timer_2_Stop();
}
/* Handle fan PWM */
/* Fan speed is adjustable from 50% to 100% using PWM over a range of 1 to 10deg C */
if (System.Over_temp_trig == true) {
if (Last_Temp_Read > Temp_Low_Thres) {
Tmr2_Comp_Value = (Last_Temp_Read - Temp_Low_Thres) * 2; /* Scale result for PWM */
if (Tmr2_Comp_Value > 2000u) {
Tmr2_Comp_Value = 2000u; /* Timer 2 total period can't be exceeded */
}
Timer_2_Start();
Timer_2_WriteCompare(Tmr2_Comp_Value);
}
}
/* Handle tacho feedback (reporting only) for sample period - indicative measurement */
if ((tacho_data_ready == true) && (System.Fan_sample_fast == true)) {
Tacho_Counts = (Timer_3_ReadCaptureBuf())*30u; /* Read counts over 1sec, Hz to RPM, 2 pulses per rotation */
tacho_data_ready = false;
#ifdef debug
ReportData();
#endif
}
/* Handle tacho feedback (reporting only) for slow sampling (Untested code) */
if ((tacho_data_ready == true) && (System.Fan_sample_fast == false)) {
Fan_Speed[Fan_Speed_Index] = Timer_3_ReadCaptureBuf();
Fan_Speed_Index++;
if ((Fan_Speed_Index < 10u) && (System.Fan_array_full == false)) {
Fan_Speed_Index = 0;
System.Fan_array_full = true;
for (Index_Temp = 0; Index_Temp < Fan_Speed_Index; Index_Temp++) {
Fan_Speed_Temp += Fan_Speed[Index_Temp];
}
Fan_Speed_Temp = Fan_Speed_Temp / Fan_Speed_Index;
}
if ((Fan_Speed_Index < 10u) && (System.Fan_array_full == true)) {
Fan_Speed_Index = 0;
for (Index_Temp = 0; Index_Temp < 11; Index_Temp++) {
Fan_Speed_Temp += Fan_Speed[Index_Temp];
}
Fan_Speed_Temp = Fan_Speed_Temp / 10;
}
Tacho_Counts = Fan_Speed_Temp * 30u;
tacho_data_ready = false;
#ifdef debug
ReportData();
#endif
}
}
}
/* END */
Fan Installation
In order to collect some operational data, the fan and the associated controller was earmarked for installation in a hot environment such as a vehicle garage.
|
Haron 320mm sq vent |
A 320mm square vent from a local supplier was selected for use with the fan, no round vents with the required diameter were available at the time so the project ended up becoming a proverbial round fan in a square hole. The manufacturer (importer) of the fan was Haron International.
Instead of using plasterboard, 12mm plywood was utilised. This wood was cut to size including the vent cut out, sealed using sealer binder then coated with ceiling white. The vent and temperature sensor were mounted in the plywood.
|
Haron vent mounting in plywood |
The plastic spacer, provided for mounting on the read of the vent, was modified by removing the mounting posts. This allowed the spacer to mount flat on the rear of the vent. A suitable hole for the Comair Rotron fan was fashioned with a die grinder.
|
Mounted Comair Rotron DC fan |
To secure the fan all eight mounting holes were used to attach to the plastic spacer plate. All bolts were given a treatment of Loctite 222 threadlocker.
|
Comair Rotron DC fan mounting bolts |
To mount the plastic spacer plate with fan onto the vent a generous application of clear silicone and four bolts were used.