学习心得-6运用定时器执行双灯轮巡点亮#include "ai8051u.h" //调用头文件
#include "stc32_stc8_usb.h" //调用头文件
#include "intrins.h" //d调用头文件
//注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,按键通用,本节课程的其余内容均通用!
#define u8unsigned char //8位无符号变量(0-255)
#define u16 unsigned int //16位无符号变量(0-65535)
u8 state = 0; //初始状态
u8 Run_State = 0; //运行状态
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void Delay20ms(void) //@24.000MHzDelay20ms();
{
unsigned long edata i;
_nop_();
_nop_();
i = 119998UL;
while (i) i--;
}
void Timer0_Init(void); //3秒@24.000MHz //函数声明
void main(void)
{
int count=1; //按键计数变量
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
P6M1 = 0x00; P6M0 = 0x00;
P7M1 = 0x00; P7M0 = 0x00;
usb_init(); //USB CDC 接口配置
IE2 |= 0x80; //使能USB中断
// Timer0_Init(); //定时器初始化
EA = 1; //IE |= 0X80;
P40 = 0;
while (DeviceState != DEVSTATE_CONFIGURED); //等待USB完成配置
while(1)
{
if (bUsbOutReady) //如果接收到了数据
{
//USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
usb_OUT_done(); //
}
//任务1:
// if( P32 == 0 ) //判断P32按钮是否按下
// {
// Delay20ms(); //延时20ms消抖
// if( P32 == 0 )
// {
// printf("按键按下次数\xfd:%d 次\r\n",(int)count);
// count++;
//
// while( P32 == 0 ); //等待P32松开
//
// }
// }
// //任务2:灯按一下点亮三秒后熄灭。
if( P32 == 0 ) //判断P32按钮是否按下
{
Delay20ms(); //延时20ms消抖
if( P32 == 0 )
{
// printf("按键按下次数\xfd:%d 次\r\n",(int)count);
// count++;
P20 = 0;
Timer0_Init();
while( P32 == 0 ); //等待P32松开
}
}
//
//任务3:救护车灯控制器,按下报警按钮,红蓝交替闪烁(LED1和LED2 表示红和蓝灯),再按一下报警按钮,红蓝灯停止。
if( P32 == 0 ) //判断P32按钮是否按下
{
Delay20ms(); //延时20ms消抖
if( P32 == 0 )
{
Run_State = !Run_State; //运行状态取反
if( Run_State==1 ) //运行
{
Timer0_Init();
}
else
{
TR0 = 0; //关闭定时器
P20 = 1;
P21 = 1;
}
while( P32 == 0 ); //等待P32松开
}
}
}
}
//void Timer0_Init(void) //3秒@24.000MHz 函数定义
//{
// TM0PS = 0x5B; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
// AUXR &= 0x7F; //定时器时钟12T模式
// TMOD &= 0xF0; //设置定时器模式
// TL0 = 0x3F; //设置定时初始值
// TH0 = 0x01; //设置定时初始值
// TF0 = 0; //清除TF0标志
// TR0 = 1; //定时器0开始计时
// ET0 = 1; //使能定时器0中断
//
// //TM0PS = 91
// //12T /12
// // THO-TL0 = 319
//
//}
void Timer0_Init(void) //500毫秒@24.000MHz
{
TM0PS = 0x0F; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xDC; //设置定时初始值
TH0 = 0x0B; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void Timer0_Isr(void) interrupt 1 //3秒执行一次
{
state = !state;
P20 = state;
P21 = !state;
}
可以模拟救护车车灯。学习了按键防抖的基础代码,同时学习了中断的控制代码。
vdghtkpm 发表于 2026-1-2 12:27
学习心得-6运用定时器执行双灯轮巡点亮
执行代码后现象
学习心得-7 数码管模拟测试,成功点亮数码管
#include "io.h"
u8 SEG_NUM[]=
{
0x3F, /*'0', 0*/
0x06, /*'1', 1*/
0x5B, /*'2', 2*/
0x4F, /*'3', 3*/
0x66, /*'4', 4*/
0x6D, /*'5', 5*/
0x7D, /*'6', 6*/
0x07, /*'7', 7*/
0x7F, /*'8', 8*/
0x6F, /*'9', 9*/
0x77, /*'A', 10*/
0x7C, /*'B', 11*/
0x39, /*'C', 12*/
0x5E, /*'D', 13*/
0x79, /*'E', 14*/
0x71, /*'F', 15*/
0x40, /*'-', 16*/
0x00, /*' ', 17*/
0x80, /*'.', 18*/
};
u8 T_NUM =
{
0X01,0X02,0X04,0X08,0X10,0X20,0X40,0X80
};
u8 State1 = 0; //LED1初始状态
u8 State2 = 0; //LED2初始状态
u8 State3 = 0; //LED3初始状态
u16 Key_Vol ; //按键按下持续时间
u8 Key_flag = 0;
u8 cnt;
void LED0_Blink(void)
{
State1 = !State1;
P20 = State1;
}
void LED1_Blink(void)
{
State2 = !State2;
P21 = State2;
}
void LED2_Blink(void)
{
State3 = !State3;
P22 = State3;
}
void KEY_Task(void)
{
if( P32 == 0 )
{
Key_Vol++;
if( Key_Vol==5 )
{
//按键按下的任务
Key_flag = !Key_flag;
if(cnt)
{
cnt = 0;
cnt = 0;
}
printf( "按键单击\r\n" );
}
}
else
{
Key_Vol = 0;
}
}
u8 shi=0;
u8 fen=0;
u8 miao =0;
//void TIMECOUNT_Task(void)
//{
// miao ++;
// if( miao>59 )
// {
// miao = 0;
// fen++;
// if( fen>59 )
// {
// fen = 0;
// shi ++;
// if( shi>23 )
// shi = 0;
// }
// }
//}
u8 state_now = 0;
void PLED_40(void)
{
u8 cod;
cod = 0x0f; //表示开启P0-P3
cod = 0X01; //P0端口
cod = 0X01; //P1
cod = ~T_NUM; //P2
cod = 0X01; //P3
LED40_SendData( cod,5 );
P2 = ~T_NUM;
state_now++;
if( state_now>7 )
state_now = 0;
}
void SEG_PC( void )
{
u8 cod;
cod = SEG_NUM; //小时的十位数的数码管段码
cod = SEG_NUM;
cod =SEG_NUM; //数码管刷段码和位码
cod = SEG_NUM; //分钟
cod = SEG_NUM;
cod =SEG_NUM; //数码管刷段码和位码
cod = SEG_NUM; //分钟
cod = SEG_NUM;
SEG7_ShowCode(cod);
miao ++;
if( miao>59 )
{
miao = 0;
fen++;
if( fen>59 )
{
fen = 0;
shi ++;
if( shi>23 )
shi = 0;
}
}
}
// 课后小练:简易免单器
void SEGLED_PC(void)
{
u8 cod;
if(Key_flag)
{
cod = SEG_NUM;
cod = SEG_NUM|SEG_NUM;
cod = SEG_NUM;
cod = SEG_NUM;
cod = SEG_NUM/10];
cod = SEG_NUM%10]|SEG_NUM;
cod = SEG_NUM/10];
cod = SEG_NUM%10];
}
else
{
cod = SEG_NUM;
cod = SEG_NUM|SEG_NUM;
cod = SEG_NUM;
cod = SEG_NUM;
cod = SEG_NUM/10];
cod = SEG_NUM%10]|SEG_NUM;
cod = SEG_NUM/10];
cod = SEG_NUM%10];
}
SEG7_ShowCode(cod);
if(!cnt && Key_flag)
{
cnt++;
if(cnt > 59)
{
cnt = 0;
cnt++;
if( cnt>9 )
{
cnt = 1;
}
}
}
}
核心板 学习心得-8PWM输出 p20-p23输出不同位数的占空比,led点亮成功。看实物有呼吸灯效果。
#include "..\..\comm\AI8051U.h"
#include "intrins.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
#define MAIN_Fosc 24000000UL// 定义主时钟
#define Timer0_Reload (65536UL - (MAIN_Fosc / 12 / 400)) // Timer0中断频率400Hz
#define PCA0 0
#define PCA1 1
#define PCA2 2
#define PCA3 3
bit B_PWM0_Dir, B_PWM1_Dir, B_PWM2_Dir, B_PWM3_Dir; // 方向标志
u16 pwm0, pwm1, pwm2, pwm3;
void Timer0_config(void);
void PWM_config(void);
void UpdatePwm(u8 PCA_id, u16 pwm_value);
void main() {
WTST = 0;// 最快指令执行速度
EAXFR = 1; // 扩展寄存器访问使能
CKCON = 0; // 提高XRAM访问速度
// 端口初始化为准双向口
P2M1 = P2M0 = 0x00;
P2M1 = P2M1 = 0x00;
P2 &= 0x00; // 清除P2口输出
Timer0_config();
PWM_config();
EA = 1; // 开启总中断
// 初始化PWM值及方向
pwm0 = 128; B_PWM0_Dir = 0; UpdatePwm(PCA0, pwm0);
pwm1 = 512; B_PWM1_Dir = 0; UpdatePwm(PCA1, pwm1);
pwm2 = 32;B_PWM2_Dir = 0; UpdatePwm(PCA2, pwm2);
pwm3 = 256; B_PWM3_Dir = 0; UpdatePwm(PCA3, pwm3);
while (1);
}
// Timer0初始化:12T模式,自动重载
void Timer0_config(void) {
AUXR &= ~0x80; // 清除第7位,设置Timer0为12T模式
TH0 = (u8)(Timer0_Reload >> 8);
TL0 = (u8)(Timer0_Reload & 0xFF);
ET0 = 1; // 使能Timer0中断
TR0 = 1; // 启动Timer0
}
// PWM初始化配置
void PWM_config(void) {
CMOD = (CMOD & ~0xC0) | (2 << 5); // 选择PCA通道映射到P2.0-P2.3
CCON |= 0x40; // 启动PCA定时器
// PCA0: 8位PWM,P2.0
CCAPM0 = 0x42; // PWM模式
PCA_PWM0 = 0x08; // 8位PWM,ECOMD=0, TOGBIT=0
CCAP0H = 0x80; // 初始占空比50%
// PCA1: 10位PWM,P2.1
CCAPM1 = 0x42; // PWM模式
PCA_PWM1 = 0x0A; // 10位PWM,ECOMD=0, TOGBIT=0
CCAP1H = 0x80 >> 2; // 高8位初始值 (1024/4=256)
CCAP1L = (0x80 & 0x03) << 6; // 低2位初始值
// PCA2: 6位PWM,P2.2
CCAPM2 = 0x42; // PWM模式
PCA_PWM2 = 0x09; // 6位PWM,ECOMD=0, TOGBIT=0
CCAP2H = 0x20; // 初始占空比 (64/64=31.25%)
// PCA3: 8位PWM,P2.3
CCAPM3 = 0x42; // PWM模式
PCA_PWM3 = 0x08; // 8位PWM,ECOMD=0, TOGBIT=0
CCAP3H = 0x80; // 初始占空比50%
}
// 更新PWM占空比
void UpdatePwm(u8 PCA_id, u16 pwm_value) {
switch (PCA_id) {
case PCA0: // 8位PWM
CCAP0H = (u8)pwm_value;
break;
case PCA1: // 10位PWM
CCAP1H = (u8)(pwm_value >> 2);// 高8位
CCAP1L = (u8)((pwm_value & 0x03) << 6); // 低2位左移6位
break;
case PCA2: // 6位PWM
CCAP2H = (u8)(pwm_value & 0x3F); // 低6位
break;
case PCA3: // 8位PWM
CCAP3H = (u8)pwm_value;
break;
}
}
// Timer0中断服务例程
void timer0(void) interrupt 1 {
// PCA0: 8位PWM
if (B_PWM0_Dir) {
if (--pwm0 <= 1) B_PWM0_Dir = 0;
} else {
if (++pwm0 >= 255) B_PWM0_Dir = 1;
}
UpdatePwm(PCA0, pwm0);
// PCA1: 10位PWM
if (B_PWM1_Dir) {
if (--pwm1 <= 1) B_PWM1_Dir = 0;
} else {
if (++pwm1 >= 1023) B_PWM1_Dir = 1;
}
UpdatePwm(PCA1, pwm1);
// PCA2: 6位PWM
if (B_PWM2_Dir) {
if (--pwm2 <= 1) B_PWM2_Dir = 0;
} else {
if (++pwm2 >= 63) B_PWM2_Dir = 1;
}
UpdatePwm(PCA2, pwm2);
// PCA3: 8位PWM
if (B_PWM3_Dir) {
if (--pwm3 <= 1) B_PWM3_Dir = 0;
} else {
if (++pwm3 >= 255) B_PWM3_Dir = 1;
}
UpdatePwm(PCA3, pwm3);
}
学习心得9-p2端口流水灯,测试成功
/**************************************************************
* 程序功能:STC擎天柱核心板 P2端口 流水灯
* 描述:P2端口的8个LED灯依次循环点亮,形成流水效果。
**************************************************************/
#include <AI8051U.H> // 包含擎天柱核心板(AI8051U)的寄存器定义头文件
#include <intrins.h> // 包含C51编译器的内部函数库,例如_crol_循环移位函数
// 定义主时钟频率,用于精确计算延时
#define MAIN_Fosc 24000000UL// 定义主时钟频率为24MHz (UL表示无符号长整型)
/**
* @brief 毫秒级延时函数 (软件延时)
* @param ms 需要延时的毫秒数
* @note 此函数的延时精度依赖于CPU主时钟频率(MAIN_Fosc)。
* 当时钟频率改变时,需要重新计算或调整延时参数。
*/
void delay_ms(unsigned char ms)
{
unsigned int i;
// 外层循环,控制总的毫秒数
do
{
// 内层循环,实现大约1毫秒的延时
// MAIN_Fosc / 6000: 这个计算基于标准8051架构,一个机器周期 = 12个时钟周期。
// 但STC15/STC32系列单片机可以设置为1T模式,一个机器周期 = 1个时钟周期。
// 这里的计算方式比较特殊,可能是在特定时钟模式下的经验值,目的是让内部while循环执行时间约为1ms。
i = MAIN_Fosc / 6000;
while(--i); // 空循环,通过消耗CPU时间来实现延时
}
while(--ms); // 外层循环,每执行一次,ms减1,直到ms为0
}
/**
* @brief 系统初始化函数
*/
void Init(void)
{
WTST = 0; // 设置程序指令等待参数,0为最快速度,不等待
EAXFR = 1; // 使能访问扩展RAM(XFR),这是操作P4-P7端口的前提
CKCON = 0; // 设置外部RAM访问时序,0为标准时序
// 将所有GPIO端口设置为准双向口模式,这是最通用的I/O模式
P0M1 = 0x00; P0M0 = 0x00; // P0口设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; // P1口设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; // P2口设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; // P3口设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; // P4口设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; // P5口设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; // P6口设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; // P7口设置为准双向口
}
/**
* @brief 主函数,程序入口
*/
void main(void)
{
unsigned char temp; // 定义一个临时变量,用于存储要输出到P2端口的LED状态模式
Init(); // 调用初始化函数,配置系统和端口
temp = 0xFE; // 初始化LED模式。0xFE的二进制是 1111 1110。
// 假设LED是低电平点亮,那么P20引脚将输出低电平,第一个LED点亮。
while(1) // 无限循环,让流水灯效果持续进行
{
// 使用_crol_函数对temp变量进行循环左移
// _crol_(temp, 1) 表示将temp的所有位向左移动1位,
// 最高位移出的位会循环进入最低位。
// 例如: 1111 1110 -> 1111 1101 -> 1111 1011 ...
temp = _crol_(temp, 1);
delay_ms(100); // 延时100毫秒,控制流水灯的移动速度
P2 = temp; // 将更新后的模式写入P2端口,改变LED的亮灭状态
}
}
学习心得-10右移流水灯,实现成功/**************************************************************
* 程序功能:STC擎天柱核心板 P2端口 右移流水灯
* 描述:P2端口的8个LED灯从右到左依次循环点亮,形成流水效果。
**************************************************************/
#include <AI8051U.H> // 包含擎天柱核心板(AI8051U)的寄存器定义头文件
#include <intrins.h> // 包含C51编译器的内部函数库,例如_cror_循环移位函数
// 定义主时钟频率,用于精确计算延时
#define MAIN_Fosc 24000000UL// 定义主时钟频率为24MHz
/**
* @brief 毫秒级延时函数 (软件延时)
* @param ms 需要延时的毫秒数
*/
void delay_ms(unsigned char ms)
{
unsigned int i;
do
{
i = MAIN_Fosc / 6000;
while(--i); // 空循环,通过消耗CPU时间来实现延时
}
while(--ms);
}
/**
* @brief 系统初始化函数
*/
void Init(void)
{
WTST = 0; // 设置程序指令等待参数,0为最快速度
EAXFR = 1; // 使能访问扩展RAM(XFR),操作P4-P7端口的前提
CKCON = 0; // 设置外部RAM访问时序
// 将所有GPIO端口设置为准双向口模式
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
P6M1 = 0x00; P6M0 = 0x00;
P7M1 = 0x00; P7M0 = 0x00;
}
/**
* @brief 主函数,程序入口
*/
void main(void)
{
unsigned char temp; // 定义一个临时变量,用于存储要输出到P2端口的LED状态模式
Init(); // 调用初始化函数
// --- 主要修改点 1: 修改初始值 ---
// 原来的初始值是 0xFE (二进制 1111 1110),从P20开始亮。
// 现在我们从P27开始亮,所以初始值应该是 0x7F (二进制 0111 1111)。
temp = 0x7F;
while(1) // 无限循环
{
// --- 主要修改点 2: 将循环左移改为循环右移 ---
// _cror_(temp, 1) 表示将temp的所有位向右移动1位,
// 最低位移出的位会循环进入最高位。
// 例如: 0111 1111 -> 1011 1111 -> 1101 1111 ...
temp = _cror_(temp, 1);
delay_ms(100); // 延时100毫秒
P2 = temp; // 将更新后的模式写入P2端口
}
}
【Ai8051U】擎天柱核心板学习记录贴 | 已有部分开源程序发布 - 传统89C52单片机学习板,升级到强大的 Ai8051U,管脚兼容穿越到32位的性能,SDCC实战 国芯人工智能技术交流网站 - AI32位8051交流社区
AI8051uU学习心得-11
追逐灯 p2口看以下程序
#include <AI8051U.H>
#include <intrins.h>
#define MAIN_Fosc 24000000UL
void delay_ms(unsigned char ms)
{
unsigned int i;
do
{
i = MAIN_Fosc / 6000;
while(--i);
}
while(--ms);
}
void Init(void)
{
WTST = 0;
EAXFR = 1;
CKCON = 0;
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
P6M1 = 0x00; P6M0 = 0x00;
P7M1 = 0x00; P7M0 = 0x00;
}
void main(void)
{
unsigned char temp;
Init();
// --- 主要修改点 1: 修改初始值 ---
// 0x03 的二进制是 0000 0011。
// 如果LED是低电平点亮,这会使P20和P21同时点亮。
temp = 0x03;
while(1)
{
P2 = temp; // 先将当前模式输出到P2端口
delay_ms(100);// 延时,让眼睛能看清
// --- 主要修改点 2: 修改移位逻辑 ---
// 使用_crol_函数将模式整体向左移动一位。
// 例如: 0000 0011 -> 0000 0110 -> 0000 1100 ...
temp = _crol_(temp, 1);
}
}
学习心得-12简陋版本涟漪灯
#include <AI8051U.H>
#include <intrins.h>
#define MAIN_Fosc 24000000UL
void delay_ms(unsigned char ms)
{
unsigned int i;
do
{
i = MAIN_Fosc / 6000;
while(--i);
}
while(--ms);
}
void Init(void)
{
WTST = 0;
EAXFR = 1;
CKCON = 0;
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
P6M1 = 0x00; P6M0 = 0x00;
P7M1 = 0x00; P7M0 = 0x00;
}
void main(void)
{
unsigned char i;
Init();
while(1)
{
// --- 效果一:扩散 (从中间向两边) ---
// 我们从中间的P23和P24开始亮
// i=0: 00011000 (0x18) -> P23, P24 亮
// i=1: 00111100 (0x3C) -> P22, P23, P24, P25 亮
// i=2: 01111110 (0x7E) -> P21-P26 亮
// i=3: 11111111 (0xFF) -> P20-P27 亮
for(i = 0; i < 4; i++)
{
// 使用位操作来点亮对称的LED
// (0x08 << i) 负责点亮中间偏左的LED
// (0x10 << i) 负责点亮中间偏右的LED
// | (或运算) 将它们合并
P2 = ~((0x08 << i) | (0x10 << i));
delay_ms(150);
}
// --- 效果二:收缩 (从两边向中间) ---
// 这个循环与上面的扩散循环正好相反
// i=3: 11111111 (0xFF) -> P20-P27 亮
// i=2: 01111110 (0x7E) -> P21-P26 亮
// i=1: 00111100 (0x3C) -> P22-P25 亮
// i=0: 00011000 (0x18) -> P23-P24 亮
for(i = 3; i > 0; i--)
{
P2 = ~((0x08 << i) | (0x10 << i));
delay_ms(150);
}
// 注意:为了避免在收缩到最小时有一个明显的停顿,
// 第二个循环的条件是 i > 0。循环结束后,P2的状态是 i=1 时的状态 (0x3C)。
// 我们可以再加一步,让它收缩到最小点并延时。
P2 = ~(0x08 | 0x10); // 收缩到最小点 (0x18)
delay_ms(150);
}
}
代码注释中有关键代码注释,有意思。
学习心得-13对冲-绽放灯
#include <AI8051U.H> // 包含擎天柱核心板寄存器定义头文件
#include <intrins.h> // 包含内部函数库 (虽然本程序没用,但通常会包含)
#define MAIN_Fosc 24000000UL// 定义主时钟频率为24MHz
/**
* @brief 毫秒级延时函数 (软件延时)
* @param ms 需要延时的毫秒数
*/
void delay_ms(unsigned char ms)
{
unsigned int i;
do
{
i = MAIN_Fosc / 6000;
while(--i);
}
while(--ms);
}
/**
* @brief 系统初始化函数
*/
void Init(void)
{
WTST = 0; // 设置程序指令等待参数,0为最快速度
EAXFR = 1; // 使能访问扩展RAM(XFR)
CKCON = 0; // 设置外部RAM访问时序
// 将所有GPIO端口设置为准双向口模式
P0M1 = 0x00; P0M0 = 0x00;
P1M1 = 0x00; P1M0 = 0x00;
P2M1 = 0x00; P2M0 = 0x00;
P3M1 = 0x00; P3M0 = 0x00;
P4M1 = 0x00; P4M0 = 0x00;
P5M1 = 0x00; P5M0 = 0x00;
P6M1 = 0x00; P6M0 = 0x00;
P7M1 = 0x00; P7M0 = 0x00;
}
/**
* @brief 主函数,程序入口
*/
void main(void)
{
unsigned char i;
unsigned char pattern; // 用于存储LED亮灭模式的变量
Init(); // 调用初始化函数
while(1) // 无限循环,让效果持续进行
{
// --- 效果一:收缩 (从两端向中间点亮) ---
pattern = 0x00; // 初始模式为全灭
for(i = 0; i < 4; i++)
{
// 核心逻辑:使用位操作来点亮对称的LED
// (0x01 << i) : 点亮从左边数第i个LED (P20, P21, P22, P23)
// (0x80 >> i) : 点亮从右边数第i个LED (P27, P26, P25, P24)
// pattern |= ... : 将新点亮的LED状态合并到当前模式中
pattern |= (0x01 << i) | (0x80 >> i);
P2 = ~pattern; // 将模式输出到P2端口 (低电平点亮,所以需要取反)
delay_ms(200); // 延时,控制动画速度
}
// --- 效果二:绽放 (从中间向两端点亮) ---
// 这个循环与上面的收缩循环正好相反
for(i = 3; i > 0; i--)
{
// 核心逻辑:使用位操作来熄灭对称的LED
// ~((0x01 << i) | (0x80 >> i)) : 创建一个掩码,将要熄灭的LED位置设为0
// pattern &= ... : 使用与运算,将pattern中对应的位清零
pattern &= ~((0x01 << i) | (0x80 >> i));
P2 = ~pattern; // 将更新后的模式输出到P2端口
delay_ms(200); // 延时
}
// 为了让动画更流畅,在绽放结束后,可以再灭一次最中间的灯
pattern = 0x00;
P2 = ~pattern;
delay_ms(200);
}
}