寒假第七讲(内存管理与NE555频率测量)

:bullseye: IAP15单片机内存管理与NE555频率测量笔记

作者:777
最后更新:2026-02-03


:pushpin: 一、内存管理指南

1.1 四种存储区域(Figure 3)

IAP15单片机主要有以下四种存储区域:

区域类型 关键字 地址范围 特点说明
data 默认(Small模型) 0x00-0x7F 内部RAM,访问速度最快
idata idata 0x80-0xFF 间接寻址内部RAM
pdata pdata 分页外部RAM 256字节页,速度较快
xdata xdata 外部RAM 容量大但速度慢
  1. 特别注意 :warning:
    使用串口功能(如printfsscanf )时会占用较多内存,建议合理分配变量到不同存储区。

:satellite_antenna: 二、NE555频率测量

2.1 测量原理

频率 = 1秒内脉冲上升沿的个数
通过计数器对脉冲进行计数,1秒读取一次计数值。

接线方法(Figure 4)

  • 使用跳线帽连接 Net_SIGSIG_OUT (对应P34引脚)
  • 引脚对应关系:P34 ←→ 脉冲信号
  • 重要提醒
    :cross_mark: 不能使用定时器1作为计数器
    定时器0的C/T位必须置1(计数模式)。

2.3 程序实现

2.3.1 定时器0初始化(16位计数器)

void Timer0_Init(void)      // 计数器模式@12.000MHz
{
    AUXR &= 0x7F;           // 定时器时钟12T模式
    TMOD &= 0xF0;           // 清除定时器0模式位
    TMOD |= 0x05;           // 设置为计数器,16位不自动重载
//这边要自己修改
    TL0 = 0x00;             // 计数器从0开始
    TH0 = 0x00;
    TF0 = 0;                // 清除溢出标志
    TR0 = 1;                // 启动计数器
}

2.3.2 定时器1初始化(1ms定时器)

unsigned int time_all_1s;    // 注意:必须用unsigned int!
                            // unsigned char最大255,这里需要1000

void Timer1_Init(void)      // 1毫秒@12.000MHz
{
    AUXR &= 0xBF;           // 定时器时钟12T模式
    TMOD &= 0x0F;           // 清除定时器1模式位
    TL1 = 0x18;             // 定时初值(1ms)
    TH1 = 0xFC;
    TF1 = 0;
    TR1 = 1;                // 启动定时器
    ET1 = 1;                // 使能定时器1中断
    EA = 1;                 // 开总中断
}

2.3.3 定时器1中断服务程序

void Timer1_Isr(void) interrupt 3
{
    if(++time_all_1s == 1000)  // 累计1000ms = 1s
    {
        time_all_1s = 0;
        // 读取计数值:高8位左移8位 + 低8位
        Freq = (TH0 << 8) | TL0;
        TH0 = TL0 = 0;          // 计数器清零
    }
}

注:取的时候是TH0和TL0都要拿出来,TH0是高八位,数据取完后,我们就可以将TL0和TH0归0

:warning: 三、常见问题与解决方案

3.1 接线问题

  • 问题 :测量不到频率
  • 解决 :确认跳线帽正确连接 P34SIGNAL 线

3.2 按键干扰问题

矩阵键盘的最后一列使用P34,会导致测量误差:

unsigned char Key_Read()
{
    unsigned char temp = 0; 
    // 原代码:P44=0;P42=1;P35=1;P34=1;
    // 修改:注释掉P34相关操作
    P44 = 0; P42 = 1; P35 = 1;  // P34=1;  // 这行被注释
    // ... 其余按键扫描代码
    
    /* 完全注释掉使用P34的按键扫描部分
    P44=1;P42=1;P35=1;P34=0;
    if(P33==0) temp=16;
    ... // 其他按键判断
    */
    return temp;
}

3.3 初始化顺序问题

必须严格按照以下顺序初始化

void main()
{
    Timer0_Init();  // 先初始化计数器
    Timer1_Init();  // 再初始化定时器
    
    while(1)
    {
        // 主循环代码
    }
}

3.4 第一次测量误差问题

使用标志位延迟显示,避免第一次测量值不准确:

bit show_flag_first = 0;  // 显示标志位

void Seg_Proc()  // 数码管显示处理
{
    if(show_flag_first)  // 只有标志位为1才显示
    {
        // 进行频率显示
    }
}

void Timer1_Isr(void) interrupt 3
{
    if(++time_all_1s == 1000)
    {
        show_flag_first = 1;  // 第一次1秒后置1,之后一直为1
        time_all_1s = 0;
        Freq = TH0 << 8 | TL0;
        TH0 = TL0 = 0;
    }
}

:memo: 四、总结要点

  1. 内存管理
    * 优先使用Small模型
    * 关键变量放data区
    * 大数组可放xdata区
  2. 频率测量
    * 定时器0:计数器模式(TMOD |= 0x05)
    * 定时器1:1ms定时器产生1秒基准
    * 注意屏蔽按键对P34的干扰
  3. 易错点
    * 初始化顺序:Timer0 → Timer1
    * 第一次测量需要延迟显示
    * 按键扫描要避开P34引脚