- 打卡等级:初来乍到
- 打卡总天数:9
- 最近打卡:2026-04-08 08:28:01
已绑定手机
注册会员
- 积分
- 80
|
写了一个电子温度计程序,一次点亮成功,喜欢的同学拿去用。程序还可以扩展,下个帖子扩展几个功能,支持的坛友点赞关注一下!
STC32G DS18B20温度测量与数码管显示程序
硬件:
MCU: STC32G12K128
温度传感器: DS18B20
显示: 4位共阴数码管
引脚连接:
DS18B20数据线: P1.7
数码管段选A-H: P2.0-P2.7
数码管位选1-4: P3.0-P3.3
#include "STC32G.h"
#include <intrins.h>
/*============================================================================
* 引脚定义
============================================================================*/
// DS18B20数据引脚定义
sbit DS18B20_DQ = P1^7; // DS18B20数据线
// 数码管段选引脚定义 (P2端口)
sbit SEG_A = P2^0; // 数码管A段
sbit SEG_B = P2^1; // 数码管B段
sbit SEG_C = P2^2; // 数码管C段
sbit SEG_D = P2^3; // 数码管D段
sbit SEG_E = P2^4; // 数码管E段
sbit SEG_F = P2^5; // 数码管F段
sbit SEG_G = P2^6; // 数码管G段
sbit SEG_DP = P2^7; // 数码管小数点
// 数码管位选引脚定义 (P3端口)
sbit DIG1 = P3^0; // 第1位数码管
sbit DIG2 = P3^1; // 第2位数码管
sbit DIG3 = P3^2; // 第3位数码管
sbit DIG4 = P3^3; // 第4位数码管
/*============================================================================
* 常量定义
============================================================================*/
// 数码管编码表 (共阴极,0-9,带特殊字符)
// 格式:DP G F E D C B A
unsigned char code SEG_CODE[] = {
0x3F, // 0: 00111111
0x06, // 1: 00000110
0x5B, // 2: 01011011
0x4F, // 3: 01001111
0x66, // 4: 01100110
0x6D, // 5: 01101101
0x7D, // 6: 01111101
0x07, // 7: 00000110
0x7F, // 8: 01111111
0x6F, // 9: 01101111
0x40, // -: 01000000 (负号)
0x00, // 空: 00000000
0x39, // C: 00111001
0x76, // H: 01110110
0x00 // 灭: 00000000
};
// 数码管位选控制码
unsigned char code DIGIT_CODE[] = {
0xFE, // 11111110 - 第1位
0xFD, // 11111101 - 第2位
0xFB, // 11111011 - 第3位
0xF7 // 11110111 - 第4位
};
/*============================================================================
* 全局变量
============================================================================*/
unsigned int temp_value; // 温度值(放大10倍,如25.5度存储为255)
signed char temp_sign = 1; // 温度符号:1为正,-1为负
unsigned char display_flag = 0; // 显示更新标志
unsigned int time_count = 0; // 时间计数器
// 显示缓冲区(存放4位数码管要显示的数字)
unsigned char display_buf[4] = {0, 0, 0, 0};
unsigned char dot_position = 0; // 小数点位置(0:无小数点,1:第1位,2:第2位,3:第3位)
/*============================================================================
* 延时函数
============================================================================*/
/**
* @brief 微秒级延时
* @param us 微秒数
*/
void Delay_us(unsigned int us)
{
while(us--)
{
_nop_(); _nop_(); _nop_(); _nop_();
_nop_(); _nop_(); _nop_(); _nop_();
}
}
/**
* @brief 毫秒级延时
* @param ms 毫秒数
*/
void Delay_ms(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++)
{
for(j = 0; j < 1140; j++)
{
_nop_();
}
}
}
/*============================================================================
* 系统初始化函数
============================================================================*/
/**
* @brief GPIO引脚初始化
*/
void GPIO_Init(void)
{
// P2端口配置为推挽输出(数码管段选)
P2M0 = 0xFF; // 设置为推挽输出模式
P2M1 = 0x00;
P2 = 0x00; // 初始输出低电平,关闭所有段
// P3.0-P3.3配置为推挽输出(数码管位选)
P3M0 = 0x0F; // P3.0-P3.3推挽输出
P3M1 = 0x00;
P3 |= 0x0F; // 初始输出高电平,关闭所有位
// P1.7配置为准双向口(DS18B20)
P1M0 &= ~0x80;
P1M1 &= ~0x80;
P1 |= 0x80; // 初始输出高电平
// 其他端口保持默认
P0M0 = 0x00;
P0M1 = 0x00;
P4M0 = 0x00;
P4M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;
}
/**
* @brief 定时器0初始化(用于数码管扫描)
*/
void Timer0_Init(void)
{
AUXR &= 0x7F; // 定时器时钟12T模式
TMOD &= 0xF0; // 设置定时器模式
TMOD |= 0x01; // 定时器0模式1,16位定时器
TL0 = 0x00; // 设置定时初值
TH0 = 0xEE; // 1ms定时(12MHz晶振)
TF0 = 0; // 清除TF0标志
TR0 = 1; // 定时器0开始计时
ET0 = 1; // 允许定时器0中断
EA = 1; // 开总中断
}
/*============================================================================
* DS18B20驱动程序
============================================================================*/
/**
* @brief DS18B20初始化
* @return 0:初始化失败 1:初始化成功
*/
unsigned char DS18B20_Init(void)
{
unsigned char flag = 0;
// 主机拉低总线480-960us
DS18B20_DQ = 0;
Delay_us(600);
DS18B20_DQ = 1;
// 释放总线,等待15-60us
Delay_us(30);
// 检测从机响应脉冲
if(DS18B20_DQ == 0)
{
flag = 1; // DS18B20存在
}
else
{
flag = 0; // DS18B20不存在
}
// 等待从机释放总线
while(!DS18B20_DQ);
return flag;
}
/**
* @brief DS18B20写一个字节
* @param dat 要写入的数据
*/
void DS18B20_WriteByte(unsigned char dat)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
// 开始写时序
DS18B20_DQ = 0;
_nop_(); _nop_(); // 延时约2us
// 写入1位数据
DS18B20_DQ = dat & 0x01;
Delay_us(60); // 保持60us
// 释放总线
DS18B20_DQ = 1;
_nop_(); _nop_(); // 延时约2us
// 准备下一位
dat >>= 1;
}
// 写入完成后延时
Delay_us(2);
}
/**
* @brief DS18B20读一个字节
* @return 读取到的数据
*/
unsigned char DS18B20_ReadByte(void)
{
unsigned char i, dat = 0;
for(i = 0; i < 8; i++)
{
// 主机拉低总线开始读时序
DS18B20_DQ = 0;
_nop_(); _nop_(); // 延时约2us
// 释放总线
DS18B20_DQ = 1;
_nop_(); _nop_(); // 延时约2us
// 读取数据
dat >>= 1;
if(DS18B20_DQ)
{
dat |= 0x80;
}
// 保持读时序
Delay_us(60);
// 释放总线等待下一位
DS18B20_DQ = 1;
}
return dat;
}
/**
* @brief 读取温度值
* @return 温度值(实际值×10,如25.5返回255)
*/
signed int DS18B20_Read_Temperature(void)
{
unsigned char tempL, tempH;
signed int temp_value;
float temperature;
// 初始化DS18B20
if(DS18B20_Init() == 0)
{
return 0x7FFF; // 读取失败,返回特殊值
}
// 跳过ROM指令
DS18B20_WriteByte(0xCC);
// 启动温度转换
DS18B20_WriteByte(0x44);
// 延时等待转换完成(最大750ms)
Delay_ms(750);
// 重新初始化DS18B20
if(DS18B20_Init() == 0)
{
return 0x7FFF; // 读取失败
}
// 跳过ROM指令
DS18B20_WriteByte(0xCC);
// 读取温度寄存器指令
DS18B20_WriteByte(0xBE);
// 读取温度值(低字节、高字节)
tempL = DS18B20_ReadByte();
tempH = DS18B20_ReadByte();
// 合成16位温度值
temp_value = tempH;
temp_value <<= 8;
temp_value |= tempL;
// 判断温度正负
if(temp_value & 0xF800) // 高5位为1表示负数
{
temp_sign = -1; // 负温度
temp_value = ~temp_value + 1; // 取补码
}
else
{
temp_sign = 1; // 正温度
}
// 计算实际温度值(保留1位小数)
temperature = temp_value * 0.0625;
temp_value = (signed int)(temperature * 10);
return temp_value;
}
/*============================================================================
* 数码管显示函数
============================================================================*/
/**
* @brief 设置显示缓冲区
* @param value 要显示的值(实际值×10)
* @param sign 符号:1为正,-1为负
*/
void Set_Display_Buffer(signed int value, signed char sign)
{
unsigned int abs_value;
// 取绝对值
if(value < 0)
{
abs_value = -value;
}
else
{
abs_value = value;
}
// 分离各位数字
display_buf[0] = abs_value % 10; // 十分位(小数第一位)
display_buf[1] = (abs_value / 10) % 10; // 个位
display_buf[2] = (abs_value / 100) % 10; // 十位
display_buf[3] = (abs_value / 1000) % 10; // 百位
// 设置小数点位置(在个位后)
dot_position = 1;
// 符号处理
if(sign < 0)
{
// 负号显示在最高位
display_buf[3] = 10; // 显示"-"
}
else
{
// 正数,如果百位为0则不显示
if(display_buf[3] == 0)
{
display_buf[3] = 11; // 不显示
// 如果十位也为0则不显示
if(display_buf[2] == 0)
{
display_buf[2] = 11; // 不显示
}
}
}
}
/**
* @brief 数码管扫描显示函数(定时器中断调用)
*/
void Display_Scan(void)
{
static unsigned char digit = 0; // 当前显示的位
unsigned char seg_data;
// 关闭所有数码管(消隐)
DIG1 = 1;
DIG2 = 1;
DIG3 = 1;
DIG4 = 1;
// 获取当前位要显示的数字编码
seg_data = SEG_CODE[display_buf[digit]];
// 如果需要显示小数点
if(dot_position == digit)
{
seg_data |= 0x80; // 点亮小数点
}
// 设置段选数据
P2 = seg_data;
// 选择要显示的位
switch(digit)
{
case 0: DIG1 = 0; break; // 第1位(个位/十分位)
case 1: DIG2 = 0; break; // 第2位(十位/个位)
case 2: DIG3 = 0; break; // 第3位(百位/十位)
case 3: DIG4 = 0; break; // 第4位(符号/百位)
}
// 切换到下一位
digit++;
if(digit >= 4)
{
digit = 0;
}
}
/*============================================================================
* 定时器0中断服务函数
============================================================================*/
void Timer0_ISR(void) interrupt 1
{
static unsigned int temp_count = 0;
static unsigned int display_count = 0;
// 重装定时器初值(1ms)
TL0 = 0x00;
TH0 = 0xEE;
// 数码管扫描(每1ms扫描1位)
Display_Scan();
// 温度读取计数(每500ms读取一次温度)
if(temp_count++ >= 500)
{
temp_count = 0;
display_flag = 1; // 设置温度读取标志
}
// 显示刷新计数
if(display_count++ >= 1000)
{
display_count = 0;
// 可以在这里添加其他定时任务
}
}
/*============================================================================
* 主函数
============================================================================*/
void main(void)
{
signed int current_temp = 0;
signed int last_temp = 0;
// 系统初始化
GPIO_Init(); // GPIO引脚初始化
Timer0_Init(); // 定时器0初始化
// 初始显示"----"
display_buf[0] = 10; // "-"
display_buf[1] = 10; // "-"
display_buf[2] = 10; // "-"
display_buf[3] = 10; // "-"
dot_position = 0; // 无小数点
// 首次温度读取
current_temp = DS18B20_Read_Temperature();
if(current_temp != 0x7FFF)
{
Set_Display_Buffer(current_temp, temp_sign);
}
// 主循环
while(1)
{
// 检查是否需要读取温度
if(display_flag)
{
display_flag = 0; // 清除标志
// 读取温度
current_temp = DS18B20_Read_Temperature();
// 如果读取成功,更新显示
if(current_temp != 0x7FFF)
{
// 温度有变化才更新显示,避免闪烁
if(current_temp != last_temp)
{
last_temp = current_temp;
Set_Display_Buffer(current_temp, temp_sign);
}
}
else
{
// 读取失败,显示错误
display_buf[0] = 13; // "H"
display_buf[1] = 12; // "C"
display_buf[2] = 10; // "-"
display_buf[3] = 10; // "-"
dot_position = 0;
}
}
// 可以在这里添加其他任务
// 如按键检测、报警处理等
}
}
|
|