vdghtkpm 发表于 2026-1-2 12:27:35

学习心得-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:30:10

vdghtkpm 发表于 2026-1-2 12:27
学习心得-6运用定时器执行双灯轮巡点亮




执行代码后现象

vdghtkpm 发表于 2026-1-2 12:59:34

学习心得-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;
                }
         }   
    }

}

vdghtkpm 发表于 2026-1-2 13:10:42

核心板 学习心得-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);
}

vdghtkpm 发表于 2026-1-2 13:21:28

学习心得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的亮灭状态
        }
}

vdghtkpm 发表于 2026-1-2 13:27:52

学习心得-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端口
        }
}

神农鼎 发表于 2026-1-2 13:51:13

【Ai8051U】擎天柱核心板学习记录贴 | 已有部分开源程序发布 - 传统89C52单片机学习板,升级到强大的 Ai8051U,管脚兼容穿越到32位的性能,SDCC实战 国芯人工智能技术交流网站 - AI32位8051交流社区

vdghtkpm 发表于 2026-1-2 17:04:32

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);
        }
}

vdghtkpm 发表于 2026-1-2 17:12:01

学习心得-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);
        }
}

代码注释中有关键代码注释,有意思。

vdghtkpm 发表于 2026-1-2 17:18:04

学习心得-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);
        }
}
页: 1 [2] 3 4
查看完整版本: AI8051U学习心得-1