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

STC12C5A60S2最小システムボード/51マイコン温度表示・温度制御ファン、基板上にDS18B20とTM1650+4桁7セグLED。

電子・マイコン技術交流グループ:2169025065

プロジェクト概要

マイコン課題設計で、スマート温度制御ファンを制作。設定下限温度に達するとファン始動、上下限間では比例PWMデューティで制御、上限超えで全速運転。
MCUはSTC12C5A60S2、温度センサはDS18B20、7セグはTM1650チップでI²C通信。
このオープンソースプロジェクトはそのままSTC12C5A60S2最小システムとしても使え、全IOピンを引出済み。

STC12C5A60S2概要

STC12C5A60S2シリーズは宏晶科技製シングルクロック/マシンサイクル(1T)MCU。高速・低消費電力・高耐ノイズな次世代8051で、命令コードは従来8051と完全互換、速度は8〜12倍高速。内部にMAX810専用リセット回路、2ch PWM、8ch高速10bit A/D変換(250kSPS)を統合し、モータ制御・強ノイズ環境に最適。

TM1650

TM1650はキースキャン付きLED(発光ダイオードディスプレイ)駆動専用IC。内部にMCU入出力デジタルインターフェース、データラッチ、LEDドライバ、キースキャン、輝度調整回路を集積。性能が安定し品質が高く、24時間連続運転にも対応。

  • 表示モード:8セグ×4桁/7セグ×4桁
  • 単独キー7×4bit(28キー)+コンビネーションキー(4キー)対応
  • 8段階輝度調整
  • セグメント駆動電流≥25mA、桁駆動電流≥150mA
  • 高速2線シリアル(CLK,DAT)
  • 発振:内蔵RC
  • 内蔵POR回路
  • 内蔵データラッチ回路
  • 動作電源:3〜5.5V
  • 高耐ノイズ
  • パッケージ:DIP16/SOP16

実物写真


回路図

PCB

トップ:

ボトム:

部品購入先

部品購入は立創商城がおすすめ。優惠登録リンク:https://activity.szlcsc.com/invite/D03E5B9CEAAE70A4.html

コード・資料

完成プロジェクト+各チップデータシートダウンロード:https://url.zeruns.com/AkHGU 抽出コード:6gzf

立創オープンプラットフォームリンク:https://url.zeruns.com/46y43

main.c

#include <STC12C5A60S2.H>
#in```含める<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;

// TM1650表示用配列の定義
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、-//小数点なし
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、-//小数点あり
unsigned char code dig3[3] = {0x76, 0x38, 0x40};                                                  // H、L、-

// カウンタ変数の定義
unsigned int count = 0, count1 = 0; // カウント値

// 温度上下限値
uint8_t H_Set = 50;
uint8_t L_Set = 25;

// 表示モードの列挙型定義
typedef enum
{
    H_mode = 0, // 上限温度設定
    L_mode,     // 下限温度設定
    T_mode,     // 温度表示
} Display_MODE;

Display_MODE Display_mode = T_mode;

uint16_t temp;

// タイマ/カウンタ初期化
void Timer_Init()
{
    EA = 1;       // 全割り込み許可
    AUXR |= 0x80; // タイマ0クロック1Tモード
    TMOD &= 0xF0; // 下位4ビットクリア、16ビットカウンタモード設定
    TMOD |= 0x01; // 上位4ビットをタイマモード0に設定
    TL0 = 0xCD;   // タイマ初期値設定
    TH0 = 0xD4;   // タイマ初期値設定
    TF0 = 0;      // TF0フラグクリア
    TR0 = 1;      // タイマ0開始
    ET0 = 1;      // タイマ0割り込み許可

    AUXR &= 0xBF; // タイマ1クロック12Tモード
    TMOD &= 0x0F; // タイマモード設定
    TMOD |= 0x10; // タイマモード設定
    TL1 = 0x00;   // タイマ初期値設定
    TH1 = 0xB8;   // タイマ初期値設定
    TF1 = 0;      // TF1フラグクリア
    TR1 = 1;      // タイマ1開始
    ET1 = 1;      // タイマ1割り込み許可
}

// タイマ/カウンタ0割り込みサービスルーチン
void Timer0_Isr(void) interrupt 1
{
    TL0 = 0xCD; // タイマ初期値設定
    TH0 = 0xD4; // タイマ初期値設定
    count++;    // 1msごとにカウント増加
    count1++;
}

void Timer1_Isr(void) interrupt 3
{
    TL1 = 0x00; // タイマ初期値設定
    TH1 = 0xB8; // タイマ初期値設定
    key_status_check(0, KEY1);
    key_status_check(1, KEY2);
    key_status_check(2, KEY3);
    key_status_check(3, KEY4);
}

void PWMInit()
{
    // PWM設定
    CCON = 0; // PCA制御レジスタ初期化
              // PCAタイマ停止
              // CFフラグクリア
              // 全モジュール割り込みフラグクリア
    CL = 0;   // PCAベースタイマリセット
    CH = 0;
    CMOD = 0x00;            // PCAタイマクロックを水晶発振周波数/12に設定、PCAカウンタオーバーフロー割り込み無効
    CCAP0H = CCAP0L = 0x80; // PWM0ポート出力50%デューティ矩形波
    CCAPM0 = 0x42;          // コンパレータ機能有効、PWM0有効
    AUXR1 |= 0x40;          // PWM出力IOをP4に切り替え

    CR = 1; // PCAタイマ開始
}
// https://blog.zeruns.com
void SetPwmDutyCycle(unsigned char dutyCycle)
{
    // dutyCycleの範囲は0~100で、0%~100%のデューティ比を示す
    unsigned char newValue = ((100 - dutyCycle) * 255) / 100;
    CCAP0H = CCAP0L = newValue; // CCAP0Lを更新し、PWM信号デューティ比を変更
}

// メイン関数
void main()
{
    TM_WrCmd(0x21); // TM1650を8セグ×4桁モードに設定、表示をオン、輝度レベル2
    Timer_Init();   // タイマ初期化
    Key_Init();     // キーステートマシン初期化
    P4M0 = 0x04;    // P4.2をプッシュプル出力に設定
    P4M1 = 0x00;
    PWMInit();          // PWM初期化
    SetPwmDutyCycle(0); // PWMデューティ比を0に設定
    temp = GetTemp();
// https://blog.zeruns.com
    while (1) // 無限ループ、以下を繰り返し実行
    {
        if (count >= 100) // 100msごとに実行
        {
            count = 0;
            temp = GetTemp();           // 温度読み取り
            if (Display_mode == T_mode) // 温度表示モード
            {
                TM_WrDat(0x68, dig1[temp / 1000]);     // 第1桁表示データ書き込み
                TM_WrDat(0x6a, dig2[temp / 100 % 10]); // 第2桁表示データ書き込み
                TM_WrDat(0x6c, dig1[temp / 10 % 10]);  // 第3桁表示データ書き込み
                TM_WrDat(0x6e, dig1[temp % 10]);       // 第4桁表示データ書き込み
            }
            if (Display_mode == H_mode) // 上限温度設定
            {
                TM_WrDat(0x68, dig3[0]);          // 第1桁にH表示
                TM_WrDat(0x6a, dig3[2]);          // 第2桁に-表示
                TM_WrDat(0x6c, dig1[H_Set / 10]); // 第3桁表示データ書き込み
                TM_WrDat(0x6e, dig1[H_Set % 10]); // 第4桁表示データ書き込み
            }
            else if (Display_mode == L_mode) // 下限温度設定
            {
                TM_WrDat(0x68, dig3[1]);          // 第1桁にL表示
                TM_WrDat(0x6a, dig3[2]);          // 第2桁に-表示
                TM_WrDat(0x6c, dig1[L_Set / 10]); // 第3桁表示データ書き込み
                TM_WrDat(0x6e, dig1[L_Set % 10]); // 第4桁表示データ書き込み
            }

            if (temp / 100 >= L_Set && temp / 100 < H_Set) // 温度が下限と上限の間の場合、温度に応じてファンPWMデューティ比を設定
            {
                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) // 温度が上限以上の場合ファン全速
            {
                SetPwmDutyCycle(100); // デューティ比100%設定
            }
            else if (temp / 100 < L_Set) // 温度が下限未満の場合ファン停止
            {
                SetPwmDutyCycle(0);
            }

            LED2 = ~LED2;
        }
        if (count1 >= 500) // 500msごとに実行
        {
            count1 = 0;
            LED3 = ~LED3;
        }
        if (key[0] == 1) // SW3モード切替
        {
            if (Display_mode != 2)
            {
                Display_mode++;
            }
            else
            {
                Display_mode = 0;
            }
            key[0] = 0;
        }
        if (key[1] == 1) // SW4上キー
        {
            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下キー
        {
            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

// TM1650ピン定義
sbit SCL_T = P2 ^ 0; // シリアルクロック
sbit SDA_T = P2 ^ 1; // シリアルデータ

// 遅延関数定義
void Delay5us_TM() //@11.0592MHz
{
    unsigned char i;

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

// TM1650スタートビット
void TM_Start()
{
    SCL_T = 1;
    SDA_T = 1;
    Delay5us_TM();
    SDA_T = 0;
}

// TM1650ストップビット
void TM_Stop()
{
    SCL_T = 1;
    SDA_T = 0;
    Delay5us_TM();
    SDA_T = 1;
}

// TM1650アクノリッジ信号
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;
}

// バスに1バイト書き込み
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データ書き込み
void TM_WrDat(unsigned char add, unsigned char dat)
{
    TM_Start();
    Write_TM_Byte(add); // 表示メモリ アドレス
    TM_Ack();
    Write_TM_Byte(dat); // 表示データ
    TM_Ack();
    TM_Stop();
}

// TM1650コマンド書き込み
void TM_WrCmd(unsigned char Bri)
{
    TM_Start();
    Write_TM_Byte(0x48); // 表示モード
    TM_Ack();
    Write_TM_Byte(Bri); // 輝度制御
    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のデータピン
sbit DS = P2 ^ 2;

// 遅延関数、単位はマイクロ秒
void delay_us(uchar us)
{
    while (us--)
    {
        _nop_();
    }
}

// https://blog.zeruns.com

// DS18B20を初期化、0で成功、1で失敗
bit DS18B20_Init()
{
    bit i;
    DS = 1; // バスを解放
    _nop_();
    DS = 0; // バスを少なくとも480us引き下げ、DS18B20をリセット
    delay_us(480);
    DS = 1;       // バスを解放
    delay_us(20); // 15~60us待機
    i = DS;       // DS18B20の存在信号を読み取り、0で存在、1で不存在
    delay_us(70); // 60~240us待機
    DS = 1;       // バスを解放
    _nop_();
    _nop_();
    return (i);
}

// DS18B20に1バイト書き込む
void DSWriteByte(uchar dat)
{
    uchar i;
    for (i = 0; i < 8; i++)
    {
        DS = 0; // バスを引き下げて書き込みタイミングを生成
        _nop_();
        _nop_();
        DS = dat & 0x01; // 最下位ビットを書き込む
        delay_us(60);    // 書き込みタイミングを少なくとも60us維持
        DS = 1;          // バスを解放
        _nop_();
        _nop_();
        dat >>= 1; // 1ビット右シフト、次のビットを準備
    }
}

// DS18B20から1バイト読み取る
uchar DSReadByte()
{
    uchar i, dat, j;
    for (i = 0; i < 8; i++)
    {
        DS = 0; // バスを引き下げて読み取りタイミングを生成
        _nop_();
        _nop_();
        DS = 1; // バスを解放
        _nop_();
        _nop_();
        j = DS;       // 最下位ビットを読み取る
        delay_us(60); // 読み取りタイミングを少なくとも60us維持
        DS = 1;       // バスを解放
        _nop_();
        _nop_();
        dat = (j << 7) | (dat >> 1); // 1ビット左シフト、読み取ったビットをdatの最上位に格納
    }
    return (dat);
}

// DS18B20の温度データを取得、戻り値は温度に100を掛けた値、単位は摂氏
int GetTemp()
{
    uchar L, H;
    int temp;
    DS18B20_Init();    // DS18B20を初期化
    DSWriteByte(0xcc); // ROMスキップコマンドを送信、アドレス照合を無視
    DSWriteByte(0x44); // 温度変換コマンドを送信、温度測定を開始し結果をスクラッチパッドに保存
    DS18B20_Init();    // DS18B20を初期化
    DSWriteByte(0xcc); // ROMスキップコマンドを送信
    DSWriteByte(0xbe); // スクラッチパッド読み取りコマンドを送信、温度データ読み取り準備
    L = DSReadByte();  // 下位バイトを先に読み取る
    H = DSReadByte();  // 上位バイトを次に読み取る
    temp = H;
    temp <<= 8;
    temp |= L;                        // 上位・下位バイトを結合、16ビットバイナリを得る
    temp = temp * 0.0625 * 100 + 0.5; // バイナリを10進数に変換、分解能0.0625℃を掛け、小数第1位を保持し四捨五入
    return (temp);                    // 温度データを返す、例:25.6℃→256
}

DS18B20.h

#ifndef __DS18B20_H_
#define __DS18B20_H_

bit DS18B20_Init();
int GetTemp();

#endif

Key.c

#include "Key.h"

// キー状態の列挙型定義
typedef enum
{
	KS_RELEASE = 0, // キー離れ
	KS_SHAKE,		// キーチャタリング
	KS_PRESS,		// 安定押下
} KEY_STATUS;

// 現在のループ終了時の状態機械状態
#define g_keyStatus 0
// 現在の状態(毎ループ後にg_keyStatusと一致)
#define g_nowKeyStatus 1
// 前回の状態(状態遷移元判別用)
#define g_lastKeyStatus 2

uint8_t KEY_Status[4][3]; // 各キーの状態記録
uint8_t key[4];			  // 各キーが安定押下されたか、1で押下、0で未押下

// 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;
	} // キー状態機械を全て離れ状態に初期化
	KEY1 = KEY2 = KEY3 = KEY4 = 1;
}

// キー状態機械プログラム
void key_status_check(uint8_t key_num, uint8_t KEY)
{
	switch (KEY_Status[key_num][g_keyStatus])
	{
	// キー離れ(初期状態)
	case KS_RELEASE:
	{
		// ローレベル検出でチャタリング処理へ
		if (KEY == 0)
		{
			KEY_Status[key_num][g_keyStatus] = KS_SHAKE;
		}
	}
	break;

	// チャタリング
	case KS_SHAKE:
	{
		if (KEY == 1)
		{
			KEY_Status[key_num][g_keyStatus] = KS_RELEASE;
		}
		else
		{
			KEY_Status[key_num][g_keyStatus] = KS_PRESS;
		}
	}
	break;

	// 安定短押し
	case KS_PRESS:
	{
		// ハイレベル検出でチャタリング処理へ
		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])
	{
		// 現在離れ かつ 前回押下
		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>

/*キー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]; // 各キーの状態記録
extern uint8_t key[4];           // 各キーが安定押下されたか、1で押下、0で未押下

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

#endif

その他のオープンソースプロジェクトおすすめ

おすすめ記事