STC89C52RC
芯片手册
原理图
扩展版原理图
功能示例
LED灯
LED灯的常亮效果
# include <STC89C5xRC.H>
void main(){
//将LED连接的P00端口设置为0
P00 =0;
while(1);
}
LED灯的闪烁
/*----------------------------------------------------------
* 文件名:LED_FlowLight.c
* 功能:STC89C52RC单片机控制的LED流水灯程序(单向左移循环)
* 硬件连接:P0口接8个共阳LED,P4.6控制蜂鸣器
* 作者:[您的名字]
* 日期:[创建日期]
*---------------------------------------------------------*/
#include <STC89C5xRC.H> // 包含STC89C52RC系列单片机寄存器定义头文件
#include <INTRINS.H> // 包含内部函数库(提供_nop_()空指令)
/* 类型重定义(增强可读性)*/
typedef unsigned char u8; // 定义无符号8位数据类型(范围0~255)
typedef unsigned int u16; // 定义无符号16位数据类型(范围0~65535)
/* 函数声明 */
void Delayms(u16 count); // 毫秒级延时函数声明
/*----------------------------------------------------------
* 主函数
*---------------------------------------------------------*/
void main()
{
// 变量初始化
u8 temp = 0x01; // 初始化LED位置(二进制00000001,对应最右侧LED)
// 注:实际是P0.0对应第一个LED,P0.7对应第八个LED
P46 = 0; // 关闭蜂鸣器(硬件设计缺陷,P4.6低电平关闭蜂鸣器)
// 如果不设置,上电时可能产生噪音
while (1) // 主循环(单片机程序必须包含无限循环)
{
/* LED显示控制 */
P0 = ~temp; // 输出到P0口控制LED:
// - 取反操作是因为采用共阳接法(端口输出0时LED亮)
// - 例如temp=0x01(00000001),取反后=0xFE(11111110),
// 即P0.0输出0,对应LED点亮
/* 更新LED位置 */
temp <<= 1; // 左移一位,实现LED流水效果
// 例如:0x01→0x02→0x04→...→0x80
/* 循环检测 */
if (temp == 0) // 当左移超出8位时(0x80<<1会变成0x00)
{
temp = 0x01; // 重新从最右侧开始
}
/* 延时控制流水速度 */
Delayms(100); // 延时100ms(控制LED移动速度)
}
}
/*----------------------------------------------------------
* 函数名称:Delayms
* 功能:实现毫秒级延时
* 参数:count - 需要延时的毫秒数
* 说明:针对12MHz晶振校准,其他频率需调整参数
*---------------------------------------------------------*/
void Delayms(u16 count)
{
/* 变量定义(使用data关键字将变量存储在内部RAM,提高访问速度)*/
u8 data i, j;
while (count--) // 外层循环(控制总延时毫秒数)
{
_nop_(); // 空指令(消耗1个机器周期,12MHz下=1us)
// 用于微调延时精度
/* 双重循环实现精确延时 */
i = 2;
j = 199;
do
{
while (--j); // 内层循环1(约199×3个机器周期)
} while (--i); // 内层循环2(外层循环2次)
/*
* 延时计算(12MHz时钟):
* - 1机器周期=1us
* - 内层循环:199×3 = 597us
* - 外层循环:2×597 = 1194us ≈ 1ms
* - 总延时:count × 1ms
*/
}
}
LED灯的跑马灯效果:从左到右,从右到左
#include <STC89C5xRC.H> // 包含STC89C52RC系列单片机头文件
#include <INTRINS.H> // 包含 intrinsics 函数(如_nop_)
typedef unsigned char u8; // 定义无符号8位数据类型(0~255)
typedef unsigned int u16; // 定义无符号16位数据类型(0~65535)
// 函数声明
void Delayms(u16 count); // 毫秒级延时函数声明
void main()
{
// 变量初始化
u8 temp = 0x01; // 初始灯位(00000001,最右侧LED亮)
bit is_left = 1; // 方向标志(1=左移,0=右移)
while (1) // 主循环
{
P0 = ~temp; // 输出到P0口(取反因为LED共阳接法)
// 根据移动方向更新灯位
if (is_left) {
temp <<= 1; // 左移一位(LED向左移动)
} else {
temp >>= 1; // 右移一位(LED向右移动)
}
// 检测边界条件并改变方向
if (temp == 0x80) { // 当移动到最左端(10000000)
is_left = 0; // 改为右移方向
}
if (temp == 0x01) { // 当移动到最右端(00000001)
is_left = 1; // 改为左移方向
}
Delayms(100); // 延时100ms控制移动速度
}
}
/**
* @brief 毫秒级延时函数
* @param count 延时毫秒数
* @note 针对12MHz晶振校准,其他频率需要调整参数
*/
void Delayms(u16 count)
{
u8 data i, j; // 使用data关键字将变量存储在内部RAM
while (count--) // 外层循环(毫秒级)
{
_nop_(); // 空指令(4个时钟周期)
i = 2;
j = 199;
do // 内层循环(微秒级)
{
while (--j); // 约100us
} while (--i); // 组合成约1ms延时
}
}
数码管
静态数码管
#include <STC89C5xRC.H> // 包含STC89C52RC系列单片机头文件
#include <INTRINS.H> // 包含 intrinsics 函数(如_nop_)
typedef unsigned char u8; // 定义无符号8位数据类型
typedef unsigned int u16; // 定义无符号16位数据类型
typedef unsigned long u32; // 定义无符号32位数据类型
// 函数声明
void DigitalTube_setBuffer(u32 number); // 设置数码管显示缓冲区
void DigitalTube_Single(u8 pos, u8 number); // 控制单个数码管显示
void DigitalTube_Refresh(); // 刷新整个数码管显示
static void Delayms(u16 count); // 毫秒级延时函数(static限制作用域)
// 数码管段选码(共阴数码管0-9,对应a~dp段)
// 编码格式:gfedcba(P0.0~P0.6),最高位P0.7为小数点
const u8 number_codes[10] = {
0x3F, // 0 - 00111111
0x06, // 1 - 00000110
0x5B, // 2 - 01011011
0x4F, // 3 - 01001111
0x66, // 4 - 01100110
0x6D, // 5 - 01101101
0x7D, // 6 - 01111101
0x07, // 7 - 00000111
0x7F, // 8 - 01111111
0x6F // 9 - 01101111
};
u8 digital_buffer[8]; // 数码管显示缓冲区(存储8位数码管的段选值)
void main()
{
// 初始化IO口
// P0 = 0x00; // 段选初始化为全灭(注释掉,实际在刷新函数中处理)
// P1 = 0xC7; // 位选初始化为全灭(P1.3-P1.5控制位选,11000111)
P46 = 0; // 可能的总使能信号(低电平有效)
P36 = 0; // 数码管使能信号(低电平有效)
P34 = 1; // 关闭流水灯(高电平关闭)
DigitalTube_setBuffer(99998888); // 设置初始显示值为250
while (1)
{
DigitalTube_Refresh(); // 持续刷新数码管显示
}
}
/**
* @brief 设置数码管显示缓冲区内容
* @param number 要显示的数字(最大支持8位数)
* @note 数字将右对齐显示,不显示前导零
*/
void DigitalTube_setBuffer(u32 number)
{
u8 i;
// 1. 清空缓冲区(全部显示空白)
for (i = 0; i < 8; i++) {
digital_buffer[i] = 0; // 0表示不显示任何段
}
// 2. 从最右侧开始填充数字(右对齐)
for (i = 7; i >= 0; i--) {
digital_buffer[i] = number_codes[number % 10]; // 获取当前位的段码
number /= 10; // 移除已处理的最低位
if (number == 0) break; // 数字已处理完毕则退出
}
}
/**
* @brief 控制单个数码管显示
* @param pos 数码管位置(0-7对应位选)
* @param number 要显示的段码值
* @note 使用P1.3-P1.5控制3-8译码器选择位选
*/
void DigitalTube_Single(u8 pos, u8 number)
{
// 1. 位选控制(通过P1.3-P1.5)
pos <<= 3; // 左移3位,将0-7映射到P1.3-P1.5
P1 &= 0xC7; // 11000111 - 清除位选位(P1.3-P1.5)
P1 |= pos; // 设置新的位选
// 2. 段选输出
P0 = number; // 输出段码值到P0口
}
/**
* @brief 刷新整个数码管显示(动态扫描)
* @note 采用循环扫描方式,每位显示1ms
*/
void DigitalTube_Refresh()
{
u8 i = 0;
while (i <= 7) // 扫描0-7共8位数码管
{
DigitalTube_Single(i, digital_buffer[i]); // 显示当前位
Delayms(1); // 保持显示1ms
i++;
}
}
/**
* @brief 毫秒级延时函数
* @param count 延时毫秒数
* @note 使用static限制只在本文件使用,防止命名冲突
*/
static void Delayms(u16 count)
{
u8 data i, j;
while (count--)
{
_nop_(); // 空指令,用于精确延时
i = 2;
j = 199;
do
{
while (--j); // 内层循环延时
} while (--i); // 外层循环延时
}
}
数码管计数
mian.c
#include ".\Com\Com_Util.h"
#include ".\Int\Int_DigitalTube.h"
// 主函数
void main()
{
u8 num = 100;
u8 count = 0;
u8 i=0;
P46 = 0; // 关闭蜂鸣器
// 初始化数码管
Int_DigitalTube_Init();
//设置要显示的数字
Int_DigitalTube_setBuffer(num);
//死循环
while (1)
{
Int_DigitalTube_setBuffer(num);
while (count <= 100)
{
Int_DigitalTube_Refresh();
count++;
}
if (num > 0)
{
num--;
count=0;
}else if (num == 0)
{
while (1)
{
Int_DigitalTube_setBuffer(0);
}
}
}
}
App.c
App.h
Com.c
#include "Com_Util.h"
// 延时函数,指定延时多少毫秒
void Delayms(u16 count)
{
u8 data i, j;
while (count)
{
_nop_();
i = 2;
j = 199;
do
{
while (--j)
;
} while (--i);
count--;
}
}
Com.h
#ifndef __COM_UTIL_H__
#define __COM_UTIL_H__
#include <STC89C5xRC.H>
#include <INTRINS.H>
// 类型别名
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
/**
* @brief 延时函数,指定延时多少毫秒
*
* @param count 指定多少毫秒
*/
void Delayms(u16 count);
#endif
Dir.c
Dir.h
Int.c
#include "Int_DigitalTube.h"
// 定义数组,保存每个数字的段选信息
static u8 s_number_codes[10] = {
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
// 定义数组,8个元素,对应数码管8个位置; 每个元素存储数字的段选信息
static u8 s_digital_buffer[8];
/**
* @brief 数码管初始化
*/
void Int_DigitalTube_Init()
{
// 打开数码管开关
P36 = 0;
// 关闭流水灯
P34 = 0;
}
/**
* @brief 将指定的整数设置到数码管显示缓存中(s_digital_buffer数组)
*
* @param number
*/
void Int_DigitalTube_setBuffer(u32 number)
{
u8 i;
// 1. 清空之前的显示内容
for (i = 0; i < 8; i++)
{
s_digital_buffer[i] = 0x00;
}
// 2. 依次取出number中每位上的数,将其段选信息存储数组;最低位存入最后一个元素
for (i = 7;; i--)
{
// 取出当前位上的数,将其段选信息存入数组指定位置
s_digital_buffer[i] = s_number_codes[number % 10];
// 处理number,去掉最低位
number /= 10;
// 如果number变为0,说明数字已经取完,停止循环
if (number == 0 || i == 0)
{
break;
}
}
}
/**
* @brief 数码管指定位置指定数字
*
* @param pos 位置,使用数字0~7分别表示从左边数第1到到第8个
* @param code 数字的段选信息
*/
void Int_DigitalTube_Single(u8 pos, u8 number_code)
{
// 1. 位选 -------------------------------
// 1.1 pos 左移3位, 与P15、P14、P13 对齐
pos <<= 3;
// 1.2 将P1的P15、P14、P13三位置0,其他位保持不变, P1 & 0b11000111
P1 &= 0xC7;
// 1.3 将pos上的三位有效数, 赋值到 P15、P14、P13 位置上
P1 |= pos;
// 2. 段选 --------------------------------
P0 = number_code;
}
/**
* @brief 刷新数码管
*
*/
void Int_DigitalTube_Refresh()
{
// 循环0到7
u8 i;
for (i = 0; i <= 7; i++)
{
Int_DigitalTube_Single(i, s_digital_buffer[i]);
Delayms(1);
}
}
Int.h
#ifndef __INT_DIGITALTUBE_H__
#define __INT_DIGITALTUBE_H__
#include "..\Com\Com_Util.h"
/**
* @brief 数码管初始化
*/
void Int_DigitalTube_Init();
/**
* @brief 将指定的整数设置到数码管显示缓存中(digital_buffer数组)
*
* @param number
*/
void Int_DigitalTube_setBuffer(u32 number);
/**
* @brief 数码管指定位置指定数字
*
* @param pos 位置,使用数字0~7分别表示从左边数第1到到第8个
* @param code 数字的段选信息
*/
void Int_DigitalTube_Single(u8 pos, u8 number_code);
/**
* @brief 刷新数码管
*
*/
void Int_DigitalTube_Refresh();
#endif