STC12C5A60S2最小系统板/51单片机温度显示和温度控制风扇

STC12C5A60S2 Minimum System Board / 51 MCU Temperature Display and Temperature-Controlled Fan, onboard DS18B20 and TM1650 + 4-digit 7-segment LED.

Electronics / MCU Technical Exchange Group: 2169025065

Project Introduction

Microcontroller course project: build an intelligent temperature-controlled fan that starts when the temperature reaches the set lower limit, outputs a proportional PWM duty cycle to control the fan speed when the temperature is between the lower and upper limits, and runs the fan at full speed when the temperature exceeds the upper limit.
Uses STC12C5A60S2 MCU, DS18B20 temperature sensor, and TM1650 chip to drive the 7-segment display via I2C.
This open-source project can also be used directly as an STC12C5A60S2 minimum system; all I/O pins are broken out.

STC12C5A60S2 Introduction

The STC12C5A60S2 series is a single-clock/machine-cycle (1T) MCU produced by STC. It is a high-speed, low-power, ultra-interference-resistant next-generation 8051 microcontroller, fully instruction-compatible with traditional 8051 but 8–12× faster. It integrates an internal MAX810 dedicated reset circuit, 2 PWM channels, 8-channel high-speed 10-bit ADC (250 kS/s), ideal for motor control and high-interference environments.

TM1650

TM1650 is a dedicated LED (7-segment display) driver with keypad-scan interface. It integrates MCU digital I/O control, data latches, LED drivers, key scanning, and brightness adjustment. TM1650 is stable, reliable, and highly interference-resistant, suitable for 24-hour continuous operation.

  • Two display modes: 8-segment × 4-digit and 7-segment × 4-digit
  • Supports single keys 7×4 bit (28 keys) and combo keys (4 keys)
  • 8-level brightness adjustment
  • Segment drive current >25 mA, digit drive current >150 mA
  • High-speed 2-wire serial interface (CLK, DAT)
  • Built-in RC oscillator
  • Built-in power-on reset circuit
  • Built-in data latch circuit
  • 3–5.5 V supply voltage
  • Strong anti-interference ability
  • Available in DIP16 and SOP16 packages

Physical Photos


Schematic

PCB

Top layer:

Bottom layer:

Component Purchase Links

Recommended component store: LCSC. Register with discount link: https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html

Code & Documentation

Full project and datasheets download: https://url.zeruns.com/AkHGU Extraction code: 6gzf

LCSC Open-Source Project Link: https://url.zeruns.com/46y43

main.c

#include <STC12C5A60S2.H>
#include<intrins.h>
#include "TM1650.h"
#include "DS18B20.h"
#include "Key.h"

sbit LED2 = P2 ^ 3;
sbit LED3 = P2 ^ 4;
sbit LED4 = P2 ^ 5;
sbit FAN = P4 ^ 2;

// Define TM1650 display array
unsigned char code dig1[11] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x40}; // 0,1,2,3,4,5,6,7,8,9,- // no decimal point
unsigned char code dig2[11] = {0xbf, 0x86, 0xdb, 0xcf, 0xe6, 0xed, 0xfd, 0x87, 0xff, 0xef, 0x40}; // 0,1,2,3,4,5,6,7,8,9,- // with decimal point
unsigned char code dig3[3] = {0x76, 0x38, 0x40};                                                  // H, L, -

// Define counter variables
unsigned int count = 0, count1 = 0; // counter values

// Temperature upper and lower limits
uint8_t H_Set = 50;
uint8_t L_Set = 25;

// Define display mode enumeration
typedef enum
{
    H_mode = 0, // upper limit temperature setting
    L_mode,     // lower limit temperature setting
    T_mode,     // temperature display
} Display_MODE;

Display_MODE Display_mode = T_mode;

uint16_t temp;

// Timer/counter initialization
void Timer_Init()
{
    EA = 1;       // enable global interrupt
    AUXR |= 0x80; // timer0 1T mode
    TMOD &= 0xF0; // clear low 4 bits, set 16-bit counter mode
    TMOD |= 0x01; // set high 4 bits to timer mode 0
    TL0 = 0xCD;   // set timer initial value
    TH0 = 0xD4;   // set timer initial value
    TF0 = 0;      // clear TF0 flag
    TR0 = 1;      // timer0 start
    ET0 = 1;      // enable timer0 interrupt

    AUXR &= 0xBF; // timer1 12T mode
    TMOD &= 0x0F; // set timer mode
    TMOD |= 0x10; // set timer mode
    TL1 = 0x00;   // set timer initial value
    TH1 = 0xB8;   // set timer initial value
    TF1 = 0;      // clear TF1 flag
    TR1 = 1;      // timer1 start
    ET1 = 1;      // enable timer1 interrupt
}

// Timer/counter 0 interrupt service routine
void Timer0_Isr(void) interrupt 1
{
    TL0 = 0xCD; // set timer initial value
    TH0 = 0xD4; // set timer initial value
    count++;    // increment every 1 ms
    count1++;
}

void Timer1_Isr(void) interrupt 3
{
    TL1 = 0x00; // set timer initial value
    TH1 = 0xB8; // set timer initial value
    key_status_check(0, KEY1);
    key_status_check(1, KEY2);
    key_status_check(2, KEY3);
    key_status_check(3, KEY4);
}

void PWMInit()
{
    // Configure PWM
    CCON = 0; // Initial PCA control register
              // PCA timer stop running
              // Clear CF flag
              // Clear all module interrupt flag
    CL = 0;   // Reset PCA base timer
    CH = 0;
    CMOD = 0x00;            // Set PCA timer clock to crystal/12, disable PCA overflow interrupt
    CCAP0H = CCAP0L = 0x80; // PWM0 port output 50% duty cycle square wave
    CCAPM0 = 0x42;          // Enable comparator, enable PWM0
    AUXR1 |= 0x40;          // Switch PWM output IO to P4

    CR = 1; // PCA timer start run
}
// https://blog.zeruns.com
void SetPwmDutyCycle(unsigned char dutyCycle)
{
    // dutyCycle range 0-100, representing 0% to 100% duty cycle.
    unsigned char newValue = ((100 - dutyCycle) * 255) / 100;
    CCAP0H = CCAP0L = newValue; // Update CCAP0L to change PWM duty cycle
}

// Main function
void main()
{
    TM_WrCmd(0x21); // Set TM1650 to 8-segment×4-digit mode, turn on display, brightness level 2
    Timer_Init();   // initialize timer
    Key_Init();     // initialize key state machine
    P4M0 = 0x04;    // set P4.2 as push-pull output
    P4M1 = 0x00;
    PWMInit();          // initialize PWM
    SetPwmDutyCycle(0); // set PWM duty cycle to 0
    temp = GetTemp();
// https://blog.zeruns.com
    while (1) // infinite loop
    {
        if (count >= 100) // every 100 ms
        {
            count = 0;
            temp = GetTemp();           // read temperature
            if (Display_mode == T_mode) // temperature display mode
            {
                TM_WrDat(0x68, dig1[temp / 1000]);     // write digit 1 data
                TM_WrDat(0x6a, dig2[temp / 100 % 10]); // write digit 2 data
                TM_WrDat(0x6c, dig1[temp / 10 % 10]);  // write digit 3 data
                TM_WrDat(0x6e, dig1[temp % 10]);       // write digit 4 data
            }
            if (Display_mode == H_mode) // upper limit setting
            {
                TM_WrDat(0x68, dig3[0]);          // digit 1 show H
                TM_WrDat(0x6a, dig3[2]);          // digit 2 show -
                TM_WrDat(0x6c, dig1[H_Set / 10]); // write digit 3 data
                TM_WrDat(0x6e, dig1[H_Set % 10]); // write digit 4 data
            }
            else if (Display_mode == L_mode) // lower limit setting
            {
                TM_WrDat(0x68, dig3[1]);          // digit 1 show L
                TM_WrDat(0x6a, dig3[2]);          // digit 2 show -
                TM_WrDat(0x6c, dig1[L_Set / 10]); // write digit 3 data
                TM_WrDat(0x6e, dig1[L_Set % 10]); // write digit 4 data
            }

            if (temp / 100 >= L_Set && temp / 100 < H_Set) // when temperature between lower and upper limits, set fan PWM duty cycle accordingly
            {
                uint8_t pwm_set = (uint8_t)((temp / 100.0 - (float)L_Set) / ((H_Set - L_Set) / 55.0) + 45.0 + 0.5);
                SetPwmDutyCycle(pwm_set);
            }
            else if (temp / 100 >= H_Set) // when temperature above upper limit, fan full speed
            {
                SetPwmDutyCycle(100); // set duty cycle 100%
            }
            else if (temp / 100 < L_Set) // when temperature below lower limit, fan off
            {
                SetPwmDutyCycle(0);
            }

            LED2 = ~LED2;
        }
        if (count1 >= 500) // every 500 ms
        {
            count1 = 0;
            LED3 = ~LED3;
        }
        if (key[0] == 1) // SW3 mode switch
        {
            if (Display_mode != 2)
            {
                Display_mode++;
            }
            else
            {
                Display_mode = 0;
            }
            key[0] = 0;
        }
        if (key[1] == 1) // SW4 up key
        {
            if (Display_mode == H_mode)
            {
                if (H_Set < 99)
                {
                    H_Set++;
                }
            }
            else if (Display_mode == L_mode)
            {
                if (L_Set < 99)
                {
                    L_Set++;
                }
            }
            key[1] = 0;
        }
        if (key[2] == 1) // SW5 down key
        {
            if (Display_mode == H_mode)
            {
                if (H_Set > 0)
                {
                    H_Set--;
                }
            }
            else if (Display_mode == L_mode)
            {
                if (L_Set > 0)
                {
                    L_Set--;
                }
            }
            key[2] = 0;
        }
        LED4 = ~LED4;
    }
}

TM1650.c

#include "TM1650.h"
#include <STC12C5A60S2.H>
#include <intrins.h>

// https://blog.zeruns.com

// Define TM1650 pins
sbit SCL_T = P2 ^ 0; // serial clock
sbit SDA_T = P2 ^ 1; // serial data

// Define delay functions
void Delay5us_TM() //@11.0592MHz
{
    unsigned char i;

    _nop_();
    _nop_();
    _nop_();
    i = 10;
    while (--i)
        ;
}
void Delay1us_TM() //@11.0592MHz
{
    _nop_();
}

// TM1650 start bit
void TM_Start()
{
    SCL_T = 1;
    SDA_T = 1;
    Delay5us_TM();
    SDA_T = 0;
}

// TM1650 stop bit
void TM_Stop()
{
    SCL_T = 1;
    SDA_T = 0;
    Delay5us_TM();
    SDA_T = 1;
}

// TM1650 acknowledge signal
void TM_Ack()
{
    unsigned char timeout = 1;
    SCL_T = 1;
    Delay5us_TM();
    SCL_T = 0;
    while ((SDA_T) && (timeout <= 100))
    {
        timeout++;
    }
    Delay5us_TM();
    SCL_T = 0;
}

// Write one byte via bus
void Write_TM_Byte(unsigned char TM_Byte)
{
    unsigned char i;
    SCL_T = 0;
    Delay1us_TM();
    for (i = 0; i < 8; i++)
    {
        if (TM_Byte & 0x80)
            SDA_T = 1;
        else
            SDA_T = 0;
        SCL_T = 0;
        Delay5us_TM();
        SCL_T = 1;
        Delay5us_TM();
        SCL_T = 0;
        TM_Byte <<= 1;
    }
}

// TM1650 write data
void TM_WrDat(unsigned char add, unsigned char dat)
{
    TM_Start();
    Write_TM_Byte(add); // display memory address
    TM_Ack();
    Write_TM_Byte(dat); // display data
    TM_Ack();
    TM_Stop();
}

// TM1650 write command
void TM_WrCmd(unsigned char Bri)
{
    TM_Start();
    Write_TM_Byte(0x48); // display mode
    TM_Ack();
    Write_TM_Byte(Bri); // brightness control
    TM_Ack();
    TM_Stop();
}

TM1650.h

#ifndef __TM1650_H_
#define __TM1650_H_

void TM_WrDat(unsigned char add, unsigned char dat);
void TM_WrCmd(unsigned char Bri);

#endif

DS18B20.c

#include "DS18B20.h"
#include <STC12C5A60S2.h>
#include <intrins.h>

#define uchar unsigned char
#define uint unsigned int

// DS18B20 data pin
sbit DS = P2 ^ 2;

// Delay function, unit is microseconds
void delay_us(uchar us)
{
    while (us--)
    {
        _nop_();
    }
}

// https://blog.zeruns.com

// Initialize DS18B20, return 0 for success, return 1 for failure
bit DS18B20_Init()
{
    bit i;
    DS = 1; // Release bus
    _nop_();
    DS = 0; // Pull bus low for at least 480us to reset DS18B20
    delay_us(480);
    DS = 1;       // Release bus
    delay_us(20); // Wait 15~60us
    i = DS;       // Read presence signal from DS18B20, 0 means present, 1 means not present
    delay_us(70); // Wait 60~240us
    DS = 1;       // Release bus
    _nop_();
    _nop_();
    return (i);
}

// Write one byte to DS18B20
void DSWriteByte(uchar dat)
{
    uchar i;
    for (i = 0; i < 8; i++)
    {
        DS = 0; // Pull bus low to generate write time slot
        _nop_();
        _nop_();
        DS = dat & 0x01; // Write lowest bit
        delay_us(60);    // Maintain write time slot for at least 60us
        DS = 1;          // Release bus
        _nop_();
        _nop_();
        dat >>= 1; // Right shift one bit, prepare to write next bit
    }
}

// Read one byte from DS18B20
uchar DSReadByte()
{
    uchar i, dat, j;
    for (i = 0; i < 8; i++)
    {
        DS = 0; // Pull bus low to generate read time slot
        _nop_();
        _nop_();
        DS = 1; // Release bus
        _nop_();
        _nop_();
        j = DS;       // Read lowest bit
        delay_us(60); // Maintain read time slot for at least 60us
        DS = 1;       // Release bus
        _nop_();
        _nop_();
        dat = (j << 7) | (dat >> 1); // Left shift one bit, store read bit into highest bit of dat
    }
    return (dat);
}

// Get temperature data from DS18B20, return value is temperature multiplied by 100, unit is Celsius
int GetTemp()
{
    uchar L, H;
    int temp;
    DS18B20_Init();    // Initialize DS18B20
    DSWriteByte(0xcc); // Send skip ROM command, ignore address matching
    DSWriteByte(0x44); // Send temperature conversion command, start measuring temperature and store result in scratchpad
    DS18B20_Init();    // Initialize DS18B20
    DSWriteByte(0xcc); // Send skip ROM command, ignore address matching
    DSWriteByte(0xbe); // Send read scratchpad command, prepare to read temperature data
    L = DSReadByte();  // Read low byte data first
    H = DSReadByte();  // Then read high byte data
    temp = H;
    temp <<= 8;
    temp |= L;                        // Combine high and low byte data to get 16-bit binary number, every 4 bits corresponds to a hex number, e.g. 0000010110100000 corresponds to 01A0H
    temp = temp * 0.0625 * 100 + 0.5; // Convert binary number to decimal, multiply by resolution (default 0.0625°C), then multiply by 10 to keep one decimal place, and round
    return (temp);                    // Return temperature data, e.g. 25.6°C corresponds to 256
}

DS18B20.h

#ifndef __DS18B20_H_
#define __DS18B20_H_

bit DS18B20_Init();
int GetTemp();

#endif

Key.c

#include "Key.h"

// Define enumeration variable type for key status
typedef enum
{
	KS_RELEASE = 0, // Key released
	KS_SHAKE,		// Key bouncing
	KS_PRESS,		// Stable press
} KEY_STATUS;

// Current state machine state at end of loop
#define g_keyStatus 0
// Current state (consistent with g_keyStatus after each loop)
#define g_nowKeyStatus 1
// Last state (used to distinguish state origin)
#define g_lastKeyStatus 2

uint8_t KEY_Status[4][3]; // Record status of each key
uint8_t key[4];		  // Record whether each key is stably pressed, 1 means pressed, 0 means not pressed

// https://blog.zeruns.com

void Key_Init(void)
{
	uint8_t i;
	for (i = 0; i < 4; i++)
	{
		KEY_Status[i][g_keyStatus] = KS_RELEASE;
		KEY_Status[i][g_nowKeyStatus] = KS_RELEASE;
		KEY_Status[i][g_lastKeyStatus] = KS_RELEASE;
		key[i] = 0;
	} // Initialize all key state machines to released state
	KEY1 = KEY2 = KEY3 = KEY4 = 1;
}

// Key state machine program
void key_status_check(uint8_t key_num, uint8_t KEY)
{
	switch (KEY_Status[key_num][g_keyStatus])
	{
	// Key release (initial state)
	case KS_RELEASE:
	{
		// Detect low level, first debounce
		if (KEY == 0)
		{
			KEY_Status[key_num][g_keyStatus] = KS_SHAKE;
		}
	}
	break;

	// Bouncing
	case KS_SHAKE:
	{
		if (KEY == 1)
		{
			KEY_Status[key_num][g_keyStatus] = KS_RELEASE;
		}
		else
		{
			KEY_Status[key_num][g_keyStatus] = KS_PRESS;
		}
	}
	break;

	// Stable short press
	case KS_PRESS:
	{
		// Detect high level, first debounce
		if (KEY == 1)
		{
			KEY_Status[key_num][g_keyStatus] = KS_SHAKE;
		}
	}
	break;

	default:
		break;
	}

	if (KEY_Status[key_num][g_keyStatus] != KEY_Status[key_num][g_nowKeyStatus])
	{
		// Current state is released and previous state was pressed
		if ((KEY_Status[key_num][g_keyStatus] == KS_RELEASE) && (KEY_Status[key_num][g_lastKeyStatus] == KS_PRESS))
		{
			key[key_num] = 1;
		}
		KEY_Status[key_num][g_lastKeyStatus] = KEY_Status[key_num][g_nowKeyStatus];
		KEY_Status[key_num][g_nowKeyStatus] = KEY_Status[key_num][g_keyStatus];
	}
}

Key.h

#ifndef __KEY_H
#define __KEY_H

#include <STC12C5A60S2.H>

/*Define key IO*/
sbit KEY1 = P3 ^ 2;
sbit KEY2 = P3 ^ 3;
sbit KEY3 = P3 ^ 4;
sbit KEY4 = P3 ^ 5;

typedef unsigned char uint8_t;
typedef unsigned int uint16_t;
typedef unsigned long uint32_t;

extern uint8_t KEY_Status[4][3]; // Record status of each key
extern uint8_t key[4];           // Record whether each key is stably pressed, 1 means pressed, 0 means not pressed

void Key_Init(void);
void key_status_check(uint8_t key_num, uint8_t KEY);

#endif

Other Open Source Project Recommendations

Recommended Reading