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
トップ:
ボトム:
部品購入先
- 0805抵抗サンプル帳:https://s.click.taobao.com/oWjIgGu
- 0805コンデンササンプル帳:https://s.click.taobao.com/r9ea1Hu
- コモンカソード4桁7セグ:https://u.jd.com/1ir7YWC
- TM1650チップ:https://s.click.taobao.com/pYveTFu
- DS18B20チップ:https://s.click.taobao.com/zaNQqFu
部品購入は立創商城がおすすめ。優惠登録リンク: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
その他のオープンソースプロジェクトおすすめ
- MSP430F149最小システムボードを描いてオープン化:https://blog.zeruns.com/archives/713.html
- STM32F030C8T6最小システムボードと流水灯(回路図とPCB):https://blog.zeruns.com/archives/715.html
- SY8205同期降圧可調DCDC電源モジュール(回路図とPCB):https://blog.zeruns.com/archives/717.html
- 2011年全国電子コンテスト問題・スイッチング電源モジュール並列給電システム:https://blog.zeruns.com/archives/718.html
- 2007年電源問題:30~36V可調昇圧DCDCモジュール(UC3843):https://oshwhub.com/zeruns/36v-sheng-ya-dcdc-mo-kuai-uc3842
おすすめ記事
- コストパフォーマンスに優れた安価なVPS/クラウドサーバーおすすめ: https://blog.vpszj.cn/archives/41.html
- 個人ブログの構築方法:https://blog.zeruns.com/archives/218.html
- マイクラサーバー構築チュートリアル:https://blog.zeruns.com/tag/mc/
- STM32でSHT3xシリーズ温湿度センサーを読み取る:https://blog.zeruns.com/archives/700.html
- VSCodeでKeilの代わりにSTM32・51マイコン開発を実現:https://blog.zeruns.com/archives/690.html
- 指点云 日本VPS 性能レビュー、1Core 1G 10M 5G防御 月29.4元:https://blog.vpszj.cn/archives/1749.html
- 雨雲 宿遷 Gold 6146 高防クラウドサーバー性能評価:https://blog.vpszj.cn/archives/1725.html




