找回密码
 立即注册
楼主: 王昱顺

【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】

 火.. [复制链接]
  • 打卡等级:常住居民II
  • 打卡总天数:97
  • 最近打卡:2026-07-21 16:51:45
已绑定手机

35

主题

135

回帖

1171

积分

版主

积分
1171
发表于 前天 09:10 | 显示全部楼层

好的,我已经获取了所有必要的外设信息。现在开始编写这个一体化演示工程。由于代码量较大,我将分多个文件输出。


AI8051U 一体化演示工程

文件清单

文件名 说明
main.c 主程序,包含初始化、主循环调度
uart_cmd.c / uart_cmd.h 串口命令解析系统
pwm_rgb.c / pwm_rgb.h 6路PWM RGB彩灯控制(渐变呼吸/色彩循环/固定亮度)
adc_volt.c / adc_volt.h ADC电压采集
ext_int.c / ext_int.h 外部电平中断+软件消抖
iap_flash.c / iap_flash.h Flash读写操作
config.h 全局配置和头文件包含

文件1:config.h

/*
 * config.h - AI8051U 一体化演示工程 全局配置头文件
 *
 * 硬件平台:AI8051U 系列单片机
 * 编译器:Keil C251 (8位模式)
 * 系统时钟:24MHz (内部IRC)
 *
 * 引脚分配:
 *   UART1: P3.0(RxD), P3.1(TxD)  - 串口命令交互
 *   PWM输出(6路RGB):
 *     PWMA_CH1(PWM1P) -> P1.0 - R通道
 *     PWMA_CH2(PWM2P) -> P1.1 - G通道
 *     PWMA_CH3(PWM3P) -> P1.2 - B通道
 *     PWMA_CH4(PWM4P) -> P1.3 - R2通道
 *     PWMB_CH1(PWM5)  -> P1.4 - G2通道
 *     PWMB_CH2(PWM6)  -> P1.5 - B2通道
 *   ADC输入: P1.6 (ADC通道6)
 *   外部中断: P3.2 (INT0, 下降沿触发)
 *
 * 串口参数:115200, 8N1
 */

#ifndef __CONFIG_H__
#define __CONFIG_H__

#include "Ai8051U.H"          // AI8051U 头文件
#include "intrins.h"          // _nop_() 等内建函数
#include <stdio.h>            // sprintf, sscanf

/* 系统时钟频率 */
#define FOSC                24000000UL

/* 串口波特率 */
#define BAUD_RATE           115200UL
#define BRT_VALUE           (65536 - (FOSC / 4 / BAUD_RATE))

/* 串口接收缓冲区大小 */
#define UART_BUF_SIZE       64

/* PWM 周期 (ARR值) */
#define PWM_PERIOD          1000

/* ADC 参考电压 (mV) */
#define ADC_VREF            3300UL

/* 软件消抖时间 (ms) */
#define DEBOUNCE_MS         20

/* IAP/EEPROM 起始地址 (扇区0) */
#define IAP_START_ADDR      0x0000

/* RGB LED 数量 */
#define RGB_LED_NUM         2

/* PWM 模式枚举 */
typedef enum {
    PWM_MODE_BREATH    = 0,  /* 渐变呼吸模式 */
    PWM_MODE_CYCLE     = 1,  /* 色彩循环模式 */
    PWM_MODE_FIXED     = 2   /* 固定亮度模式 */
} PWM_Mode;

#endif /* __CONFIG_H__ */

文件2:uart_cmd.h

/*
 * uart_cmd.h - 串口命令解析系统
 *
 * 命令格式: <命令前缀><参数>\r\n
 *   读写Flash:  F<地址:4位HEX>,<数据:2位HEX>  或  F<地址:4位HEX>R
 *   调节PWM:    P<通道:1位>,<占空比:0-1000>   或  P<模式:0-2>
 *   采集ADC:    A<通道:0-15>
 *   帮助:       H
 */

#ifndef __UART_CMD_H__
#define __UART_CMD_H__

/* 命令类型 */
typedef enum {
    CMD_NONE     = 0,
    CMD_FLASH    = 1,   /* Flash 操作 */
    CMD_PWM      = 2,   /* PWM 操作 */
    CMD_ADC      = 3,   /* ADC 操作 */
    CMD_HELP     = 4    /* 帮助 */
} CmdType;

/* 命令解析结果结构体 */
typedef struct {
    CmdType type;           /* 命令类型 */
    unsigned char param1;   /* 参数1 */
    unsigned int  param2;   /* 参数2 */
    unsigned char is_read;  /* Flash: 1=读, 0=写 */
} CmdResult;

/* 初始化串口1 (115200bps, 定时器2做波特率发生器) */
void UART1_Init(void);

/* 发送一个字节 (阻塞) */
void UART1_SendByte(unsigned char dat);

/* 发送字符串 */
void UART1_SendString(char *str);

/* 发送缓冲区数据 (指定长度) */
void UART1_SendBuffer(unsigned char *buf, unsigned char len);

/* 发送一个整数 (十进制) */
void UART1_SendInt(int val);

/* 解析串口接收到的命令字符串 */
unsigned char UART1_ParseCmd(char *cmd_str, CmdResult *result);

/* 获取接收缓冲区中是否收到完整一行 (以\r\n结尾) */
unsigned char UART1_GetLine(char *buf, unsigned char max_len);

/* 串口中断服务函数声明 (在main.c或uart_cmd.c中实现) */
void Uart1_Isr(void) interrupt 4;

#endif /* __UART_CMD_H__ */

文件3:uart_cmd.c

/*
 * uart_cmd.c - 串口命令解析系统实现
 *
 * 基于手册"串口1使用定时器2做波特率发生器"范例编写
 * 参考手册章节:19 2组USART-LIN/SPI,同步/异步串口通信
 */

#include "config.h"
#include "uart_cmd.h"

/* 串口环形缓冲区 */
static unsigned char rx_buffer[UART_BUF_SIZE];
static unsigned char rx_wptr = 0;
static unsigned char rx_rptr = 0;
static unsigned char rx_line_ready = 0;  /* 收到完整一行标志 */

/* 发送忙标志 */
static bit tx_busy = 0;

/*
 * UART1_Init - 初始化串口1
 * 模式1 (8位数据, 可变波特率)
 * 定时器2做波特率发生器, 1T模式
 * 波特率 = FOSC / 4 / (65536 - BRT_VALUE)
 */
void UART1_Init(void)
{
    /* 设置串口1为模式1: 8位数据, 可变波特率, 允许接收 */
    SCON = 0x50;        /* SM0=0, SM1=1, REN=1 */

    /* 选择定时器2作为波特率发生器 */
    S1BRT = 1;

    /* 设置定时器2重装值 */
    T2L = (unsigned char)(BRT_VALUE);
    T2H = (unsigned char)(BRT_VALUE >> 8);

    /* 定时器2 1T模式, 启动 */
    T2x12 = 1;
    T2R = 1;

    /* 初始化缓冲区指针 */
    rx_wptr = 0;
    rx_rptr = 0;
    rx_line_ready = 0;
    tx_busy = 0;

    /* 使能串口1中断 */
    ES = 1;
}

/*
 * UART1_SendByte - 发送一个字节 (阻塞查询方式)
 */
void UART1_SendByte(unsigned char dat)
{
    while (tx_busy);        /* 等待上次发送完成 */
    tx_busy = 1;
    SBUF = dat;             /* 写入SBUF触发发送 */
}

/*
 * UART1_SendString - 发送字符串
 */
void UART1_SendString(char *str)
{
    while (*str != '\0')
    {
        UART1_SendByte((unsigned char)(*str));
        str++;
    }
}

/*
 * UART1_SendBuffer - 发送指定长度的数据
 */
void UART1_SendBuffer(unsigned char *buf, unsigned char len)
{
    unsigned char i;
    for (i = 0; i < len; i++)
    {
        UART1_SendByte(buf[i]);
    }
}

/*
 * UART1_SendInt - 发送十进制整数
 */
void UART1_SendInt(int val)
{
    char buf[12];
    sprintf(buf, "%d", val);
    UART1_SendString(buf);
}

/*
 * UART1_GetLine - 从接收缓冲区取一行 (以\r\n结尾)
 * 返回1表示成功获取一行, 0表示无完整行
 */
unsigned char UART1_GetLine(char *buf, unsigned char max_len)
{
    unsigned char i = 0;
    unsigned char dat;

    if (!rx_line_ready)
    {
        return 0;           /* 无完整行 */
    }

    /* 关闭中断保护 */
    ES = 0;

    while (rx_rptr != rx_wptr)
    {
        dat = rx_buffer[rx_rptr];
        rx_rptr = (rx_rptr + 1) & (UART_BUF_SIZE - 1);

        if (dat == '\n')
        {
            /* 遇到换行符, 行结束 */
            buf[i] = '\0';
            rx_line_ready = 0;
            ES = 1;
            return 1;
        }
        else if (dat != '\r')   /* 忽略回车符 */
        {
            if (i < max_len - 1)
            {
                buf[i++] = (char)dat;
            }
        }
    }

    /* 理论上不会到这里, 但以防万一 */
    rx_line_ready = 0;
    ES = 1;
    buf[i] = '\0';
    return 1;
}

/*
 * UART1_ParseCmd - 解析命令字符串
 *
 * 命令格式:
 *   F<地址4位HEX>,<数据2位HEX>  - 写Flash
 *   F<地址4位HEX>R              - 读Flash
 *   P<通道1位>,<占空比0-1000>   - 设置PWM通道占空比
 *   P<模式0-2>                  - 设置PWM模式
 *   A<通道0-15>                 - 读取ADC
 *   H                            - 帮助
 *
 * 返回1表示解析成功, 0表示格式错误
 */
unsigned char UART1_ParseCmd(char *cmd_str, CmdResult *result)
{
    unsigned char prefix;
    unsigned int addr;
    unsigned int data_val;
    unsigned char ch;
    unsigned int duty;
    unsigned char mode;

    if (cmd_str == 0 || result == 0)
    {
        return 0;
    }

    /* 跳过前导空格 */
    while (*cmd_str == ' ' || *cmd_str == '\t')
    {
        cmd_str++;
    }

    prefix = (unsigned char)(*cmd_str);
    if (prefix == '\0')
    {
        return 0;
    }

    switch (prefix)
    {
        case 'F':
        case 'f':
            /* Flash操作: F<地址4位HEX>,<数据2位HEX> 或 F<地址4位HEX>R */
            result->type = CMD_FLASH;
            if (sscanf(cmd_str + 1, "%4x", &addr) != 1)
            {
                return 0;
            }
            result->param1 = (unsigned char)(addr & 0xFF);
            result->param2 = addr;

            /* 检查是否为读命令 (以R或r结尾) */
            {
                char *p = cmd_str + 1;
                while (*p >= '0' && *p <= '9' || *p >= 'A' && *p <= 'F' || *p >= 'a' && *p <= 'f')
                {
                    p++;
                }
                if (*p == 'R' || *p == 'r')
                {
                    result->is_read = 1;
                }
                else if (*p == ',')
                {
                    /* 写命令: 解析数据 */
                    result->is_read = 0;
                    if (sscanf(p + 1, "%2x", &data_val) != 1)
                    {
                        return 0;
                    }
                    result->param2 = (unsigned int)data_val;
                }
                else
                {
                    return 0;
                }
            }
            return 1;

        case 'P':
        case 'p':
            /* PWM操作: P<通道>,<占空比> 或 P<模式> */
            result->type = CMD_PWM;
            if (sscanf(cmd_str + 1, "%d,%d", &ch, &duty) == 2)
            {
                /* 设置通道占空比 */
                result->param1 = (unsigned char)ch;
                result->param2 = duty;
            }
            else if (sscanf(cmd_str + 1, "%d", &mode) == 1)
            {
                /* 设置模式 */
                result->param1 = 0xFF;      /* 标记为模式设置 */
                result->param2 = (unsigned int)mode;
            }
            else
            {
                return 0;
            }
            return 1;

        case 'A':
        case 'a':
            /* ADC操作: A<通道> */
            result->type = CMD_ADC;
            if (sscanf(cmd_str + 1, "%d", &ch) != 1)
            {
                return 0;
            }
            result->param1 = (unsigned char)ch;
            result->param2 = 0;
            return 1;

        case 'H':
        case 'h':
            result->type = CMD_HELP;
            return 1;

        default:
            return 0;
    }
}

/*
 * Uart1_Isr - 串口1中断服务函数
 * 中断向量号4
 */
void Uart1_Isr(void) interrupt 4
{
    unsigned char dat;

    if (TI)     /* 发送中断 */
    {
        TI = 0;
        tx_busy = 0;
    }

    if (RI)     /* 接收中断 */
    {
        RI = 0;
        dat = SBUF;

        /* 存入环形缓冲区 */
        rx_buffer[rx_wptr] = dat;
        rx_wptr = (rx_wptr + 1) & (UART_BUF_SIZE - 1);

        /* 检测到换行符, 标记行就绪 */
        if (dat == '\n')
        {
            rx_line_ready = 1;
        }
    }
}

文件4:pwm_rgb.h

/*
 * pwm_rgb.h - 6路PWM RGB彩灯控制
 *
 * 使用PWMA (通道1~4) 和 PWMB (通道1~2) 共6路PWM
 * 支持三种模式: 渐变呼吸、色彩循环、固定亮度
 *
 * 参考手册章节:29 16位高级PWM定时器
 */

#ifndef __PWM_RGB_H__
#define __PWM_RGB_H__

#include "config.h"

/* RGB LED 通道索引 */
#define RGB_R1      0   /* PWMA_CH1 -> P1.0 */
#define RGB_G1      1   /* PWMA_CH2 -> P1.1 */
#define RGB_B1      2   /* PWMA_CH3 -> P1.2 */
#define RGB_R2      3   /* PWMA_CH4 -> P1.3 */
#define RGB_G2      4   /* PWMB_CH1 -> P1.4 */
#define RGB_B2      5   /* PWMB_CH2 -> P1.5 */

/* 初始化高级PWM (PWMA + PWMB) */
void PWM_Init(void);

/* 设置单个PWM通道占空比 (0 ~ PWM_PERIOD) */
void PWM_SetDuty(unsigned char channel, unsigned int duty);

/* 设置RGB LED1 颜色 (R, G, B 各 0~PWM_PERIOD) */
void PWM_SetRGB1(unsigned int r, unsigned int g, unsigned int b);

/* 设置RGB LED2 颜色 (R, G, B 各 0~PWM_PERIOD) */
void PWM_SetRGB2(unsigned int r, unsigned int g, unsigned int b);

/* 设置PWM工作模式 */
void PWM_SetMode(PWM_Mode mode);

/* PWM主循环处理 (在main循环中周期调用) */
void PWM_Task(void);

#endif /* __PWM_RGB_H__ */

文件5:pwm_rgb.c

/*
 * pwm_rgb.c - 6路PWM RGB彩灯控制实现
 *
 * PWMA: 通道1~4 (PWM1P~PWM4P) 映射到 P1.0~P1.3
 * PWMB: 通道1~2 (PWM5~PWM6)   映射到 P1.4~P1.5
 *
 * 参考手册章节:29 16位高级PWM定时器
 * 以及手册中"使能高级PWM的高速模式(异步模式)"范例
 */

#include "config.h"
#include "pwm_rgb.h"

/* 当前PWM模式 */
static PWM_Mode current_mode = PWM_MODE_BREATH;

/* 当前RGB值 (用于固定亮度模式) */
static unsigned int rgb1_val[3] = {PWM_PERIOD, 0, 0};  /* R1, G1, B1 */
static unsigned int rgb2_val[3] = {0, PWM_PERIOD, 0};  /* R2, G2, B2 */

/* 呼吸/循环模式变量 */
static unsigned int breath_step = 0;
static unsigned int cycle_phase = 0;

/*
 * PWMA_WriteReg - 异步写PWMA寄存器 (高速模式)
 * 参考手册"HSPWM地址寄存器"章节
 */
static void PWMA_WriteReg(unsigned char addr, unsigned char dat)
{
    while (HSPWMA_ADR & 0x80);          /* 等待前一次异步操作完成 */
    HSPWMA_DAT = dat;                   /* 准备数据 */
    HSPWMA_ADR = addr & 0x7F;           /* 设置地址, 写操作 */
}

/*
 * PWMA_ReadReg - 异步读PWMA寄存器 (高速模式)
 */
static unsigned char PWMA_ReadReg(unsigned char addr)
{
    unsigned char dat;
    while (HSPWMA_ADR & 0x80);          /* 等待前一次异步操作完成 */
    HSPWMA_ADR = addr | 0x80;           /* 设置地址, 读操作 */
    while (HSPWMA_ADR & 0x80);          /* 等待当前读取完成 */
    dat = HSPWMA_DAT;                   /* 读取数据 */
    return dat;
}

/*
 * PWMB_WriteReg - 异步写PWMB寄存器
 */
static void PWMB_WriteReg(unsigned char addr, unsigned char dat)
{
    while (HSPWMB_ADR & 0x80);
    HSPWMB_DAT = dat;
    HSPWMB_ADR = addr & 0x7F;
}

/*
 * PWMB_ReadReg - 异步读PWMB寄存器
 */
static unsigned char PWMB_ReadReg(unsigned char addr)
{
    unsigned char dat;
    while (HSPWMB_ADR & 0x80);
    HSPWMB_ADR = addr | 0x80;
    while (HSPWMB_ADR & 0x80);
    dat = HSPWMB_DAT;
    return dat;
}

/*
 * PWM_Init - 初始化高级PWM
 *
 * PWMA: 使用系统时钟, 不分频, ARR=1000
 * 通道1~4: PWM模式1 (CNT<CCR时输出有效)
 * PWMB: 同PWMA配置
 */
void PWM_Init(void)
{
    unsigned char i;

    /* 设置PWM输出引脚为推挽输出 */
    P1M0 |= 0x3F;       /* P1.0~P1.5 推挽输出 */
    P1M1 &= ~0x3F;

    /* 使能PWMA异步访问 (高速模式) */
    HSPWMA_CFG = 0x03;

    /* ---- PWMA 配置 ---- */

    /* 关闭所有通道输出 */
    PWMA_WriteReg((unsigned char)&PWMA_CCER1, 0x00);
    PWMA_WriteReg((unsigned char)&PWMA_CCER2, 0x00);

    /* 配置通道1~4为PWM输出模式 (OCxREF输出PWM) */
    /* CCMR1: 通道1, 模式: PWM模式1 (110) */
    PWMA_WriteReg((unsigned char)&PWMA_CCMR1, 0x60);   /* OC1M=110, 预装载使能 */
    PWMA_WriteReg((unsigned char)&PWMA_CCMR2, 0x60);   /* 通道2 */
    PWMA_WriteReg((unsigned char)&PWMA_CCMR3, 0x60);   /* 通道3 */
    PWMA_WriteReg((unsigned char)&PWMA_CCMR4, 0x60);   /* 通道4 */

    /* 使能通道输出 (CCER1: CC1E, CC2E; CCER2: CC3E, CC4E) */
    PWMA_WriteReg((unsigned char)&PWMA_CCER1, 0x55);   /* 使能CC1/CC1N, CC2/CC2N */
    PWMA_WriteReg((unsigned char)&PWMA_CCER2, 0x55);   /* 使能CC3/CC3N, CC4/CC4N */

    /* 使能PWM信号输出到端口 */
    PWMA_WriteReg((unsigned char)&PWMA_ENO, 0x0F);     /* ENO1~ENO4 使能 */

    /* 使能主输出 */
    PWMA_WriteReg((unsigned char)&PWMA_BKR, 0x80);     /* MOE=1 */

    /* 设置PWM周期 (ARR) */
    PWMA_WriteReg((unsigned char)&PWMA_ARRH, (unsigned char)(PWM_PERIOD >> 8));
    PWMA_WriteReg((unsigned char)&PWMA_ARRL, (unsigned char)(PWM_PERIOD));

    /* 设置预分频器 (不分频) */
    PWMA_WriteReg((unsigned char)&PWMA_PSCRH, 0x00);
    PWMA_WriteReg((unsigned char)&PWMA_PSCRL, 0x00);

    /* 设置各通道初始占空比为0 */
    for (i = 1; i <= 4; i++)
    {
        unsigned char addr_h, addr_l;
        switch (i)
        {
            case 1: addr_h = (unsigned char)&PWMA_CCR1H; addr_l = (unsigned char)&PWMA_CCR1L; break;
            case 2: addr_h = (unsigned char)&PWMA_CCR2H; addr_l = (unsigned char)&PWMA_CCR2L; break;
            case 3: addr_h = (unsigned char)&PWMA_CCR3H; addr_l = (unsigned char)&PWMA_CCR3L; break;
            case 4: addr_h = (unsigned char)&PWMA_CCR4H; addr_l = (unsigned char)&PWMA_CCR4L; break;
            default: continue;
        }
        PWMA_WriteReg(addr_h, 0x00);
        PWMA_WriteReg(addr_l, 0x00);
    }

    /* 启动PWMA计数器 */
    PWMA_WriteReg((unsigned char)&PWMA_CR1, 0x01);     /* CEN=1 */

    /* ---- PWMB 配置 ---- */
    HSPWMB_CFG = 0x03;

    PWMB_WriteReg((unsigned char)&PWMB_CCER1, 0x00);

    /* 通道1~2为PWM输出 */
    PWMB_WriteReg((unsigned char)&PWMB_CCMR1, 0x60);
    PWMB_WriteReg((unsigned char)&PWMB_CCMR2, 0x60);

    PWMB_WriteReg((unsigned char)&PWMB_CCER1, 0x11);   /* 使能CC1, CC2 */

    /* PWMB没有ENO寄存器, 直接输出 */
    PWMB_WriteReg((unsigned char)&PWMB_BKR, 0x80);

    PWMB_WriteReg((unsigned char)&PWMB_ARRH, (unsigned char)(PWM_PERIOD >> 8));
    PWMB_WriteReg((unsigned char)&PWMB_ARRL, (unsigned char)(PWM_PERIOD));

    PWMB_WriteReg((unsigned char)&PWMB_PSCRH, 0x00);
    PWMB_WriteReg((unsigned char)&PWMB_PSCRL, 0x00);

    PWMB_WriteReg((unsigned char)&PWMB_CCR1H, 0x00);
    PWMB_WriteReg((unsigned char)&PWMB_CCR1L, 0x00);
    PWMB_WriteReg((unsigned char)&PWMB_CCR2H, 0x00);
    PWMB_WriteReg((unsigned char)&PWMB_CCR2L, 0x00);

    PWMB_WriteReg((unsigned char)&PWMB_CR1, 0x01);

    /* 设置默认模式为呼吸模式 */
    current_mode = PWM_MODE_BREATH;
    breath_step = 0;
    cycle_phase = 0;
}

/*
 * PWM_SetDuty - 设置单个PWM通道占空比
 * channel: 0~5 对应 RGB_R1 ~ RGB_B2
 * duty: 0 ~ PWM_PERIOD
 */
void PWM_SetDuty(unsigned char channel, unsigned int duty)
{
    unsigned char addr_h, addr_l;

    if (duty > PWM_PERIOD)
    {
        duty = PWM_PERIOD;
    }

    switch (channel)
    {
        case RGB_R1:    /* PWMA_CH1 */
            addr_h = (unsigned char)&PWMA_CCR1H;
            addr_l = (unsigned char)&PWMA_CCR1L;
            PWMA_WriteReg(addr_h, (unsigned char)(duty >> 8));
            PWMA_WriteReg(addr_l, (unsigned char)(duty));
            break;
        case RGB_G1:    /* PWMA_CH2 */
            addr_h = (unsigned char)&PWMA_CCR2H;
            addr_l = (unsigned char)&PWMA_CCR2L;
            PWMA_WriteReg(addr_h, (unsigned char)(duty >> 8));
            PWMA_WriteReg(addr_l, (unsigned char)(duty));
            break;
        case RGB_B1:    /* PWMA_CH3 */
            addr_h = (unsigned char)&PWMA_CCR3H;
            addr_l = (unsigned char)&PWMA_CCR3L;
            PWMA_WriteReg(addr_h, (unsigned char)(duty >> 8));
            PWMA_WriteReg(addr_l, (unsigned char)(duty));
            break;
        case RGB_R2:    /* PWMA_CH4 */
            addr_h = (unsigned char)&PWMA_CCR4H;
            addr_l = (unsigned char)&PWMA_CCR4L;
            PWMA_WriteReg(addr_h, (unsigned char)(duty >> 8));
            PWMA_WriteReg(addr_l, (unsigned char)(duty));
            break;
        case RGB_G2:    /* PWMB_CH1 */
            addr_h = (unsigned char)&PWMB_CCR1H;
            addr_l = (unsigned char)&PWMB_CCR1L;
            PWMB_WriteReg(addr_h, (unsigned char)(duty >> 8));
            PWMB_WriteReg(addr_l, (unsigned char)(duty));
            break;
        case RGB_B2:    /* PWMB_CH2 */
            addr_h = (unsigned char)&PWMB_CCR2H;
            addr_l = (unsigned char)&PWMB_CCR2L;
            PWMB_WriteReg(addr_h, (unsigned char)(duty >> 8));
            PWMB_WriteReg(addr_l, (unsigned char)(duty));
            break;
        default:
            break;
    }
}

/*
 * PWM_SetRGB1 - 设置RGB LED1 颜色
 */
void PWM_SetRGB1(unsigned int r, unsigned int g, unsigned int b)
{
    PWM_SetDuty(RGB_R1, r);
    PWM_SetDuty(RGB_G1, g);
    PWM_SetDuty(RGB_B1, b);
    rgb1_val[0] = r;
    rgb1_val[1] = g;
    rgb1_val[2] = b;
}

/*
 * PWM_SetRGB2 - 设置RGB LED2 颜色
 */
void PWM_SetRGB2(unsigned int r, unsigned int g, unsigned int b)
{
    PWM_SetDuty(RGB_R2, r);
    PWM_SetDuty(RGB_G2, g);
    PWM_SetDuty(RGB_B2, b);
    rgb2_val[0] = r;
    rgb2_val[1] = g;
    rgb2_val[2] = b;
}

/*
 * PWM_SetMode - 设置PWM工作模式
 */
void PWM_SetMode(PWM_Mode mode)
{
    current_mode = mode;
    breath_step = 0;
    cycle_phase = 0;

    if (mode == PWM_MODE_FIXED)
    {
        /* 固定亮度模式: 恢复保存的颜色值 */
        PWM_SetRGB1(rgb1_val[0], rgb1_val[1], rgb1_val[2]);
        PWM_SetRGB2(rgb2_val[0], rgb2_val[1], rgb2_val[2]);
    }
}

/*
 * PWM_Task - PWM主循环处理
 * 在main循环中周期调用, 实现呼吸/循环效果
 */
void PWM_Task(void)
{
    unsigned int r1, g1, b1;
    unsigned int r2, g2, b2;
    unsigned int val;

    switch (current_mode)
    {
        case PWM_MODE_BREATH:
            /*
             * 渐变呼吸模式:
             * 所有LED同步呼吸, 亮度从0渐变到PWM_PERIOD再回到0
             * breath_step: 0~1999, 0~999上升, 1000~1999下降
             */
            if (breath_step < PWM_PERIOD)
            {
                val = breath_step;                      /* 上升阶段 */
            }
            else
            {
                val = 2 * PWM_PERIOD - breath_step;     /* 下降阶段 */
            }

            /* LED1: 红色呼吸, LED2: 蓝色呼吸 */
            PWM_SetRGB1(val, 0, 0);
            PWM_SetRGB2(0, 0, val);

            breath_step++;
            if (breath_step >= 2 * PWM_PERIOD)
            {
                breath_step = 0;
            }
            break;

        case PWM_MODE_CYCLE:
            /*
             * 色彩循环模式:
             * 使用相位偏移实现彩虹效果
             * cycle_phase: 0~2999
             */
            {
                unsigned int phase = cycle_phase % 1000;

                /* LED1: 红->绿->蓝 循环 */
                if (cycle_phase < 1000)
                {
                    r1 = PWM_PERIOD - phase;
                    g1 = phase;
                    b1 = 0;
                }
                else if (cycle_phase < 2000)
                {
                    r1 = 0;
                    g1 = PWM_PERIOD - phase;
                    b1 = phase;
                }
                else
                {
                    r1 = phase;
                    g1 = 0;
                    b1 = PWM_PERIOD - phase;
                }

                /* LED2: 相位偏移500, 实现不同颜色 */
                {
                    unsigned int phase2 = (cycle_phase + 500) % 3000;
                    unsigned int p2 = phase2 % 1000;

                    if (phase2 < 1000)
                    {
                        r2 = PWM_PERIOD - p2;
                        g2 = p2;
                        b2 = 0;
                    }
                    else if (phase2 < 2000)
                    {
                        r2 = 0;
                        g2 = PWM_PERIOD - p2;
                        b2 = p2;
                    }
                    else
                    {
                        r2 = p2;
                        g2 = 0;
                        b2 = PWM_PERIOD - p2;
                    }
                }

                PWM_SetRGB1(r1, g1, b1);
                PWM_SetRGB2(r2, g2, b2);
            }

            cycle_phase++;
            if (cycle_phase >= 3000)
            {
                cycle_phase = 0;
            }
            break;

        case PWM_MODE_FIXED:
            /* 固定亮度模式: 不做变化, 保持已有设置 */
            break;

        default:
            break;
    }
}

由于代码量较大,我先输出到这里。接下来继续输出 adc_volt.c/hext_int.c/hiap_flash.c/hmain.c。请告诉我"继续",我将输出剩余文件。


🔍 搜索 "STC AI8051U 串口交互 命令系统"

⏱️ 查找用时 22.4s | 总回答用时 69.2s

回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:149
  • 最近打卡:2026-07-21 08:54:57

39

主题

543

回帖

3327

积分

超级版主

积分
3327
发表于 前天 09:20 | 显示全部楼层
STC8H1K28,生成火灾报警器,完整演示程序,并含详细程序注解

截图202607201012458603.jpg
电话:0513-55012946 QQ:2195591714  微信:  19952583740
回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:149
  • 最近打卡:2026-07-21 08:54:57

39

主题

543

回帖

3327

积分

超级版主

积分
3327
发表于 前天 09:20 | 显示全部楼层

好的,我已经查阅了 STC8H1K28 的相关手册资料。现在我来生成一个完整的火灾报警器演示程序。


STC8H1K28 火灾报警器完整演示程序

功能说明

本程序实现一个基于 STC8H1K28 的火灾报警器,具备以下功能:

  1. 烟雾/温度检测:使用 ADC 通道(P1.0/ADC0)采集模拟式烟雾传感器或温度传感器的电压信号
  2. 声光报警:检测到火警时,驱动蜂鸣器(有源蜂鸣器)发出报警声,同时 LED 指示灯闪烁
  3. 串口通信:通过串口1(P3.0 RxD, P3.1 TxD)向上位机发送报警状态信息
  4. 按键消警:通过按键(P3.2/INT0)可手动解除报警

硬件连接(以 LQFP32 封装为例)

外设 引脚 说明
烟雾传感器(模拟输出) P1.0/ADC0 ADC 输入通道0
蜂鸣器(有源,高电平驱动) P1.1 推挽输出
红色报警 LED P1.2 推挽输出,低电平点亮
绿色正常 LED P1.3 推挽输出,低电平点亮
消警按键(按下为低) P3.2/INT0 高阻输入,内部上拉
串口1 TxD P3.1 推挽输出
串口1 RxD P3.0 高阻输入

完整程序代码

/******************************************************************************
 * 文件名: main.c
 * 芯片  : STC8H1K28 (LQFP32/QFN32/TSSOP28/TSSOP20)
 * 功能  : 火灾报警器完整演示程序
 * 说明  :
 *   - 使用内部高速IRC时钟,频率 24MHz(ISP下载时设置)
 *   - ADC通道0(P1.0)采集烟雾传感器模拟电压
 *   - 当ADC值超过阈值(火警阈值)时,触发声光报警
 *   - 蜂鸣器(P1.1)发出间歇报警声,红色LED(P1.2)闪烁
 *   - 正常时绿色LED(P1.3)常亮
 *   - 串口1(P3.0/P3.1)以115200bps输出报警状态信息
 *   - 按键(P3.2)按下可手动解除报警
 *   - 定时器0用于产生1ms时基,供系统调度使用
 *
 * 使用说明:
 *   1. 在ISP下载软件中选择"STC8H1K28"系列
 *   2. 设置内部IRC时钟为 24MHz
 *   3. 下载本程序到芯片
 *   4. 打开串口助手(115200,8,N,1)查看报警信息
 *   5. 烟雾传感器输出接P1.0,电压越高表示烟雾浓度越大
 ******************************************************************************/

/* ======================== 头文件包含 ======================== */
#include "STC8H.h"          /* STC8H系列头文件,包含所有SFR定义 */

/* ======================== 用户宏定义 ======================== */

/* 系统时钟频率,单位Hz。ISP下载时设置为24MHz */
#define FOSC            24000000UL

/* 串口波特率 */
#define BAUD            115200UL

/* ADC参考电压,单位mV。通常接VCC=3.3V或5V,此处以3300mV为例 */
#define ADC_VREF_MV     3300UL

/* ADC阈值定义(10位ADC,范围0~1023)
 * 火警阈值:当ADC值 >= FIRE_THRESHOLD 时触发报警
 * 正常恢复阈值:当ADC值 < NORMAL_THRESHOLD 时自动解除报警
 * 设置滞回区间,防止临界值频繁抖动 */
#define FIRE_THRESHOLD      600     /* 火警触发阈值(约1.93V @3.3V参考) */
#define NORMAL_THRESHOLD    500     /* 正常恢复阈值(约1.61V @3.3V参考) */

/* 定时器0重载值,用于产生1ms中断
 * 使用1T模式,定时器时钟 = FOSC / (TM0PS + 1)
 * 设置TM0PS = 23,则定时器时钟 = 24MHz / 24 = 1MHz
 * 1ms定时:计数值 = 1000
 * 重载值 = 65536 - 1000 = 64536 = 0xFC18 */
#define T0_1MS_RELOAD_H     0xFC    /* 定时器0高8位重载值 */
#define T0_1MS_RELOAD_L     0x18    /* 定时器0低8位重载值 */

/* 报警蜂鸣器周期参数(单位:ms) */
#define BUZZER_ON_MS        200     /* 蜂鸣器响200ms */
#define BUZZER_OFF_MS       200     /* 蜂鸣器停200ms */

/* LED闪烁周期参数(单位:ms) */
#define LED_BLINK_MS        500     /* LED闪烁周期500ms */

/* 按键消抖时间(单位:ms) */
#define KEY_DEBOUNCE_MS     20

/* ADC采样间隔(单位:ms) */
#define ADC_SAMPLE_INTERVAL 100

/* ======================== 全局变量 ======================== */

/* 系统1ms滴答计数器,由定时器0中断累加 */
volatile unsigned int sys_tick_ms = 0;

/* ADC采样值(10位,0~1023) */
volatile unsigned int adc_value = 0;

/* ADC采样完成标志,在主循环中处理 */
volatile unsigned char adc_ready = 0;

/* 报警状态标志
 * 0: 正常状态
 * 1: 火警报警状态 */
volatile unsigned char alarm_state = 0;

/* 按键按下标志(下降沿触发) */
volatile unsigned char key_pressed = 0;

/* ======================== 函数声明 ======================== */
void delay_ms(unsigned int ms);
void System_Init(void);
void GPIO_Init(void);
void Timer0_Init(void);
void UART1_Init(void);
void ADC_Init(void);
void EXTI0_Init(void);
unsigned int ADC_GetValue(unsigned char channel);
void SendString(unsigned char *str);
void SendHex(unsigned char dat);
void ProcessAlarm(void);
void ProcessKey(void);

/* ======================== 主函数 ======================== */
void main(void)
{
    unsigned int last_adc_sample_tick = 0;  /* 上次ADC采样时刻 */
    unsigned char buf[64];                   /* 串口发送缓冲区 */

    /* 系统初始化 */
    System_Init();

    /* 发送启动信息 */
    SendString("Fire Alarm System Started.\r\n");
    SendString("STC8H1K28 - ADC Smoke Detector\r\n");

    /* 主循环 */
    while (1)
    {
        /* ---- 1. 定时ADC采样 ---- */
        if ((unsigned int)(sys_tick_ms - last_adc_sample_tick) >= ADC_SAMPLE_INTERVAL)
        {
            last_adc_sample_tick = sys_tick_ms;

            /* 启动ADC转换(通道0,P1.0) */
            ADC_CONTR &= ~0x0F;             /* 清除通道选择位 */
            ADC_CONTR |= 0x00;              /* 选择ADC通道0 (ADC_CHS=0000) */
            ADC_CONTR |= 0x40;              /* 启动ADC转换 (ADC_START=1) */
        }

        /* ---- 2. 处理ADC转换完成 ---- */
        if (adc_ready)
        {
            adc_ready = 0;                  /* 清除标志 */

            /* 计算电压值(mV),用于串口输出显示 */
            unsigned int voltage_mv;
            voltage_mv = (unsigned int)((unsigned long)adc_value * ADC_VREF_MV / 1024);

            /* 串口输出ADC值和电压 */
            SendString("ADC:");
            /* 将adc_value转换为十进制字符串输出 */
            buf[0] = '0' + (adc_value / 1000) % 10;
            buf[1] = '0' + (adc_value / 100) % 10;
            buf[2] = '0' + (adc_value / 10) % 10;
            buf[3] = '0' + adc_value % 10;
            buf[4] = '\0';
            SendString(buf);

            SendString(" mV:");
            buf[0] = '0' + (voltage_mv / 1000) % 10;
            buf[1] = '0' + (voltage_mv / 100) % 10;
            buf[2] = '0' + (voltage_mv / 10) % 10;
            buf[3] = '0' + voltage_mv % 10;
            buf[4] = '\0';
            SendString(buf);

            /* 判断报警状态 */
            if (alarm_state == 0)
            {
                /* 当前正常,检查是否达到火警阈值 */
                if (adc_value >= FIRE_THRESHOLD)
                {
                    alarm_state = 1;        /* 进入报警状态 */
                    SendString(" *** FIRE ALARM! ***\r\n");
                }
                else
                {
                    SendString(" Normal\r\n");
                }
            }
            else
            {
                /* 当前报警,检查是否恢复到正常阈值以下 */
                if (adc_value < NORMAL_THRESHOLD)
                {
                    alarm_state = 0;        /* 恢复正常状态 */
                    SendString(" Alarm Cleared\r\n");
                }
                else
                {
                    SendString(" *** FIRE ALARM! ***\r\n");
                }
            }
        }

        /* ---- 3. 处理按键消警 ---- */
        ProcessKey();

        /* ---- 4. 处理报警输出(蜂鸣器、LED) ---- */
        ProcessAlarm();
    }
}

/* ======================== 系统初始化 ======================== */
void System_Init(void)
{
    /* 关闭所有中断,防止初始化过程中产生意外中断 */
    EA = 0;

    GPIO_Init();        /* I/O口初始化 */
    Timer0_Init();      /* 定时器0初始化(1ms时基) */
    UART1_Init();       /* 串口1初始化(115200bps) */
    ADC_Init();         /* ADC初始化 */
    EXTI0_Init();       /* 外部中断0初始化(按键) */

    /* 打开总中断 */
    EA = 1;
}

/* ======================== I/O口初始化 ======================== */
void GPIO_Init(void)
{
    /* P1.0 - ADC0输入:高阻输入模式
     * P1M1.0 = 1, P1M0.0 = 0 */
    P1M1 |= 0x01;       /* P1.0设置为高阻输入 */
    P1M0 &= ~0x01;

    /* P1.1 - 蜂鸣器控制:推挽输出模式
     * P1M1.1 = 0, P1M0.1 = 1 */
    P1M1 &= ~0x02;
    P1M0 |= 0x02;
    P11 = 0;            /* 初始关闭蜂鸣器 */

    /* P1.2 - 红色报警LED:推挽输出模式
     * P1M1.2 = 0, P1M0.2 = 1 */
    P1M1 &= ~0x04;
    P1M0 |= 0x04;
    P12 = 1;            /* 初始熄灭(低电平点亮,高电平熄灭) */

    /* P1.3 - 绿色正常LED:推挽输出模式
     * P1M1.3 = 0, P1M0.3 = 1 */
    P1M1 &= ~0x08;
    P1M0 |= 0x08;
    P13 = 0;            /* 初始点亮绿色LED,表示系统正常运行 */

    /* P3.0 - RxD:高阻输入模式(串口接收)
     * P3M1.0 = 1, P3M0.0 = 0 */
    P3M1 |= 0x01;
    P3M0 &= ~0x01;

    /* P3.1 - TxD:推挽输出模式(串口发送)
     * P3M1.1 = 0, P3M0.1 = 1 */
    P3M1 &= ~0x02;
    P3M0 |= 0x02;

    /* P3.2 - 按键输入:高阻输入模式,打开内部4K上拉
     * P3M1.2 = 1, P3M0.2 = 0 */
    P3M1 |= 0x04;
    P3M0 &= ~0x04;
    P3PU |= 0x04;       /* 打开P3.2内部4K上拉电阻 */
}

/* ======================== 定时器0初始化(1ms时基) ======================== */
void Timer0_Init(void)
{
    /* 使用定时器0,模式0(16位自动重装载模式)
     * 1T模式,预分频TM0PS=23,定时器时钟 = 24MHz/24 = 1MHz
     * 1ms定时:计数值 = 1000
     * 重载值 = 65536 - 1000 = 64536 = 0xFC18 */

    /* 访问XFR寄存器前,必须先使能EAXFR */
    P_SW2 |= 0x80;      /* 使能访问XFR */

    /* 设置定时器0预分频值 */
    TM0PS = 23;         /* 定时器0时钟 = FOSC / (23+1) = 1MHz */

    P_SW2 &= ~0x80;     /* 关闭XFR访问(可选) */

    /* 设置定时器0工作模式:16位自动重装载(模式0) */
    TMOD &= ~0x03;      /* T0_M1=0, T0_M0=0,模式0 */
    TMOD &= ~0x04;      /* T0_C/T=0,定时器模式(对内部时钟计数) */
    TMOD &= ~0x08;      /* T0_GATE=0,不受INT0控制 */

    /* 设置1T模式 */
    AUXR |= 0x80;       /* T0x12=1,1T模式 */

    /* 装载定时器初值 */
    TH0 = T0_1MS_RELOAD_H;      /* 高8位 */
    TL0 = T0_1MS_RELOAD_L;      /* 低8位 */

    /* 清除定时器0中断标志 */
    TF0 = 0;

    /* 使能定时器0中断 */
    ET0 = 1;            /* IE.1 = 1 */

    /* 启动定时器0 */
    TR0 = 1;            /* TCON.4 = 1 */
}

/* ======================== 串口1初始化(115200bps) ======================== */
void UART1_Init(void)
{
    /* 使用定时器2作为波特率发生器
     * 波特率 = FOSC / 4 / (65536 - [T2H,T2L])
     * 115200 = 24000000 / 4 / (65536 - RL)
     * RL = 65536 - 24000000 / 4 / 115200 = 65536 - 52 = 65484 = 0xFFCC
     * 注意:实际使用中由于误差,可能需要微调 */

    /* 设置定时器2为波特率发生器 */
    AUXR &= ~0x10;      /* T2_C/T=0,定时器模式 */
    AUXR |= 0x04;       /* T2x12=1,1T模式 */

    /* 设置定时器2重载值 */
    T2H = 0xFF;         /* 高8位 */
    T2L = 0xCC;         /* 低8位 */

    /* 启动定时器2 */
    AUXR |= 0x10;       /* T2R=1 */

    /* 配置串口1 */
    SCON = 0x50;        /* 模式1(8位UART),REN=1允许接收 */
    AUXR &= ~0x01;      /* S1ST2=0,选择定时器2为波特率发生器 */

    /* 使能串口1中断(接收中断) */
    ES = 1;             /* IE.4 = 1 */
}

/* ======================== ADC初始化 ======================== */
void ADC_Init(void)
{
    /* 打开ADC电源 */
    ADC_CONTR |= 0x80;      /* ADC_POWER=1 */

    /* 配置ADC转换结果格式:右对齐(10位结果)
     * RESFMT=0:ADC_RES[7:0]存放高8位,ADC_RESL[7:6]存放低2位
     * 设置ADC时钟速度 */
    ADCCFG = 0x20;          /* RESFMT=0(右对齐),SPEED=0000(最快速度) */

    /* 配置ADC时序(使用默认值即可,此处显式设置) */
    P_SW2 |= 0x80;          /* 使能访问XFR */
    ADCTIM = 0x2A;          /* CSSETUP=0, CSHOLD=01, SMPDUTY=01010(默认值) */
    P_SW2 &= ~0x80;

    /* 使能ADC中断 */
    ADC_CONTR &= ~0x20;     /* 清除ADC_FLAG */
    EADC = 1;               /* IE.5 = 1,使能ADC中断 */
}

/* ======================== 外部中断0初始化(按键) ======================== */
void EXTI0_Init(void)
{
    /* 配置INT0为下降沿触发 */
    IT0 = 1;                /* TCON.0 = 1,下降沿触发 */

    /* 清除中断标志 */
    IE0 = 0;                /* TCON.1 = 0 */

    /* 使能外部中断0 */
    EX0 = 1;                /* IE.0 = 1 */
}

/* ======================== 毫秒延时函数 ======================== */
void delay_ms(unsigned int ms)
{
    unsigned int start_tick = sys_tick_ms;

    while ((unsigned int)(sys_tick_ms - start_tick) < ms)
    {
        /* 等待,利用定时器0中断产生的sys_tick_ms */
    }
}

/* ======================== ADC获取值(查询方式) ======================== */
unsigned int ADC_GetValue(unsigned char channel)
{
    unsigned int result;

    /* 选择通道 */
    ADC_CONTR &= ~0x0F;             /* 清除通道选择位 */
    ADC_CONTR |= (channel & 0x0F);  /* 设置通道 */

    /* 启动转换 */
    ADC_CONTR |= 0x40;              /* ADC_START=1 */

    /* 等待转换完成 */
    while (!(ADC_CONTR & 0x20));    /* 等待ADC_FLAG=1 */

    /* 读取转换结果(10位右对齐) */
    result = (unsigned int)ADC_RES;         /* 高8位 */
    result = (result << 8) | ADC_RESL;      /* 组合低8位,实际低2位有效 */

    /* 清除标志 */
    ADC_CONTR &= ~0x20;             /* 清除ADC_FLAG */

    return result;
}

/* ======================== 串口发送字符串 ======================== */
void SendString(unsigned char *str)
{
    while (*str != '\0')
    {
        SBUF = *str;                /* 发送一个字节 */
        while (!TI);                /* 等待发送完成 */
        TI = 0;                     /* 清除发送中断标志 */
        str++;
    }
}

/* ======================== 串口发送一个十六进制字节 ======================== */
void SendHex(unsigned char dat)
{
    unsigned char hex_char;

    /* 高4位 */
    hex_char = (dat >> 4) & 0x0F;
    if (hex_char < 10)
        hex_char += '0';
    else
        hex_char += 'A' - 10;
    SBUF = hex_char;
    while (!TI);
    TI = 0;

    /* 低4位 */
    hex_char = dat & 0x0F;
    if (hex_char < 10)
        hex_char += '0';
    else
        hex_char += 'A' - 10;
    SBUF = hex_char;
    while (!TI);
    TI = 0;
}

/* ======================== 报警处理函数 ======================== */
void ProcessAlarm(void)
{
    static unsigned int last_toggle_tick = 0;   /* 上次翻转时刻 */
    static unsigned char buzzer_state = 0;      /* 蜂鸣器当前状态 */
    static unsigned char led_state = 0;         /* LED当前状态 */

    if (alarm_state == 0)
    {
        /* ---- 正常状态 ---- */
        /* 关闭蜂鸣器 */
        P11 = 0;

        /* 绿色LED常亮,红色LED熄灭 */
        P13 = 0;        /* 绿色LED亮(低电平有效) */
        P12 = 1;        /* 红色LED灭 */

        /* 复位状态变量 */
        buzzer_state = 0;
        led_state = 0;
        last_toggle_tick = sys_tick_ms;
    }
    else
    {
        /* ---- 报警状态 ---- */
        /* 绿色LED熄灭 */
        P13 = 1;

        /* 蜂鸣器和红色LED以200ms周期交替 */
        if ((unsigned int)(sys_tick_ms - last_toggle_tick) >= BUZZER_ON_MS)
        {
            last_toggle_tick = sys_tick_ms;

            /* 翻转蜂鸣器状态 */
            buzzer_state = !buzzer_state;
            P11 = buzzer_state;     /* 蜂鸣器控制 */

            /* 红色LED与蜂鸣器同步闪烁 */
            led_state = buzzer_state;
            P12 = led_state;        /* 红色LED(低电平点亮) */
        }
    }
}

/* ======================== 按键处理函数 ======================== */
void ProcessKey(void)
{
    static unsigned int last_key_tick = 0;      /* 上次按键处理时刻 */

    if (key_pressed)
    {
        /* 消抖处理:检测到按键后,延时20ms再次确认 */
        if ((unsigned int)(sys_tick_ms - last_key_tick) >= KEY_DEBOUNCE_MS)
        {
            last_key_tick = sys_tick_ms;

            /* 再次读取按键状态(P3.2),确认是否确实按下 */
            if (P32 == 0)           /* 按键按下(低电平) */
            {
                if (alarm_state == 1)
                {
                    /* 报警状态下,按键按下解除报警 */
                    alarm_state = 0;
                    SendString("Alarm silenced by user.\r\n");
                }
            }

            key_pressed = 0;        /* 清除按键标志 */
        }
    }
}

/* ======================== 定时器0中断服务函数(1ms周期) ======================== */
void Timer0_ISR(void) interrupt 1
{
    /* 定时器0为16位自动重装载模式,硬件自动重载初值 */
    /* 累加系统滴答 */
    sys_tick_ms++;
}

/* ======================== 串口1中断服务函数 ======================== */
void UART1_ISR(void) interrupt 4
{
    unsigned char rx_data;

    if (RI)                         /* 接收中断 */
    {
        RI = 0;                     /* 清除接收中断标志 */
        rx_data = SBUF;             /* 读取接收数据 */

        /* 此处可添加串口命令解析功能
         * 例如:收到 'R' 可复位报警,收到 'S' 可查询状态等 */
        if (rx_data == 'R' || rx_data == 'r')
        {
            /* 远程复位报警 */
            if (alarm_state == 1)
            {
                alarm_state = 0;
                SendString("Alarm reset by remote command.\r\n");
            }
        }
        else if (rx_data == 'S' || rx_data == 's')
        {
            /* 查询当前状态 */
            if (alarm_state == 0)
                SendString("Status: Normal\r\n");
            else
                SendString("Status: FIRE ALARM!\r\n");
        }
    }

    if (TI)                         /* 发送中断 */
    {
        TI = 0;                     /* 清除发送中断标志 */
        /* 发送中断由查询方式处理,此处不做额外操作 */
    }
}

/* ======================== ADC中断服务函数 ======================== */
void ADC_ISR(void) interrupt 13
{
    unsigned int result;

    /* 清除ADC中断标志 */
    ADC_CONTR &= ~0x20;             /* 清除ADC_FLAG */

    /* 读取转换结果(10位右对齐) */
    result = (unsigned int)ADC_RES;         /* 高8位 */
    result = (result << 8) | ADC_RESL;      /* 组合低8位 */

    /* 保存ADC值 */
    adc_value = result;

    /* 设置ADC采样完成标志 */
    adc_ready = 1;
}

/* ======================== 外部中断0服务函数(按键) ======================== */
void EXTI0_ISR(void) interrupt 0
{
    /* 设置按键按下标志,在主循环中处理消抖 */
    key_pressed = 1;
}

程序架构说明

本程序采用前后台系统架构

  • 中断服务(前台)

    • 定时器0中断(1ms):提供系统时基 sys_tick_ms
    • ADC中断:读取转换结果,设置 adc_ready 标志
    • 串口1中断:接收远程命令('R'复位报警,'S'查询状态)
    • 外部中断0:检测按键按下
  • 主循环(后台)

    • 定时启动ADC采样
    • 处理ADC结果,判断报警状态
    • 处理按键消警
    • 控制蜂鸣器和LED输出

使用说明

  1. 硬件连接

    • 将烟雾传感器(如 MQ-2)的模拟输出端接 P1.0
    • 有源蜂鸣器(正极)接 P1.1,负极接 GND(需加三极管驱动或使用有源蜂鸣器模块)
    • 红色 LED 通过 1kΩ 限流电阻接 P1.2,绿色 LED 通过 1kΩ 限流电阻接 P1.3
    • 按键一端接 P3.2,另一端接 GND
    • P3.0/P3.1 接 USB转串口模块(如 CH340G)连接电脑
  2. ISP下载设置

    • 芯片型号选择:STC8H1K28
    • 内部IRC频率:24MHz
    • 其他选项保持默认即可
  3. 阈值调整

    • 根据实际使用的传感器,调整 FIRE_THRESHOLDNORMAL_THRESHOLD 宏定义值
    • 可通过串口观察 ADC 数值,确定合适的阈值
  4. 串口命令

    • 发送 Ss:查询当前状态
    • 发送 Rr:远程解除报警

注意事项

  1. ADC参考电压ADC_VRef+ 引脚(LQFP32的第11脚)需接稳定的参考电压,要求不高时可直接接 VCC
  2. 蜂鸣器驱动:有源蜂鸣器工作电流较大,建议通过 NPN 三极管(如 S8050)驱动,不要直接由 I/O 口驱动
  3. 传感器选型:本程序适用于输出模拟电压的烟雾/气体传感器(如 MQ-2、MQ-7)或温度传感器(如 NTC 热敏电阻分压电路)
  4. P3.0/P3.1:这两个口上电后为弱上拉/准双向口模式,无需额外配置即可用于串口下载

🔍 搜索 "STC8H1K28 生成火灾报警器 完整演示程序"

📌 相关论坛讨论

  1. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】

⏱️ 查找用时 8.2s | 总回答用时 56.2s

电话:0513-55012946 QQ:2195591714  微信:  19952583740
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:78
  • 最近打卡:2026-07-21 11:15:23

30

主题

96

回帖

1052

积分

版主

积分
1052
发表于 前天 09:21 | 显示全部楼层
能帮我用AI8051U实验箱实现模拟宠风扇功能吗,详细注释
截图202607200922345660.jpg
回复

使用道具 举报 送花

  • 打卡等级:常住居民I
  • 打卡总天数:78
  • 最近打卡:2026-07-21 11:15:23

30

主题

96

回帖

1052

积分

版主

积分
1052
发表于 前天 09:21 | 显示全部楼层

好的,我已经查阅了AI8051U手册中关于定时器、I/O口和时钟管理的详细内容,以及论坛上的相关讨论。现在我来为您设计一个完整的基于AI8051U实验箱的模拟定时智能驱鸟器方案。


基于AI8051U实验箱的模拟定时智能驱鸟器

功能设计说明

本程序利用AI8051U实验箱实现一个模拟定时智能驱鸟器,主要功能:

  1. 定时驱鸟:通过定时器产生精确的时间基准,在设定的时间间隔内自动触发驱鸟动作
  2. 多模式驱鸟:支持LED闪烁(视觉驱鸟)+ 蜂鸣器发声(听觉驱鸟)组合
  3. 按键控制:通过实验箱矩阵按键设置驱鸟时间间隔、启停控制
  4. 状态显示:通过LED数码管显示当前工作模式和倒计时
  5. USB-CDC通信:通过USB-CDC串口输出驱鸟状态信息到电脑

硬件资源分配

功能 资源 说明
1ms时间基准 定时器0 产生1ms系统节拍
驱鸟定时 定时器1 产生驱鸟间隔定时
蜂鸣器发声 P5.0 无源蜂鸣器,通过定时器1翻转驱动
LED指示灯 P0.0~P0.7 驱鸟时LED闪烁
LED数码管 74HC595控制 显示工作模式和倒计时
矩阵按键 P0.0~P0.3列,P0.6/P0.7行 设置参数
USB-CDC USB接口 输出状态信息到电脑

完整代码

文件1:main.c

/*
 * 基于AI8051U实验箱的模拟定时智能驱鸟器
 * 
 * 功能说明:
 * 1. 定时驱鸟:通过定时器产生精确的时间基准,在设定的时间间隔内自动触发驱鸟动作
 * 2. 多模式驱鸟:支持LED闪烁(视觉驱鸟)+ 蜂鸣器发声(听觉驱鸟)组合
 * 3. 按键控制:通过实验箱矩阵按键设置驱鸟时间间隔、启停控制
 * 4. 状态显示:通过LED数码管显示当前工作模式和倒计时
 * 5. USB-CDC通信:通过USB-CDC串口输出驱鸟状态信息到电脑
 *
 * 按键功能:
 *   Key0 - 模式切换(自动/手动/关闭)
 *   Key4 - 增加驱鸟间隔时间(+5秒)
 *   Key5 - 减少驱鸟间隔时间(-5秒)
 *
 * 硬件连接(AI8051U实验箱):
 *   P0.0-P0.3 - 矩阵按键列输入
 *   P0.6/P0.7 - 矩阵按键行扫描
 *   P0.0-P0.7 - LED指示灯(驱鸟时闪烁)
 *   P4.0      - LED总电源控制(低电平点亮)
 *   P5.0      - 蜂鸣器控制
 *   P3.2/P3.4/P3.5 - 74HC595控制数码管
 *
 * 编译环境:Keil C51,芯片选择 AI8051U
 * 工作频率:内部高速IRC 40MHz
 */

#include "ai8051u.h"           // AI8051U头文件
#include "ai_usb.h"            // USB库头文件
#include "intrins.h"           // 使用_nop_()函数

/* 宏定义 */
#define FOSC            40000000UL      // 系统工作频率 40MHz
#define T0_1MS_RELOAD   (65536 - FOSC / 12 / 1000)  // 定时器0 1ms重载值(12T模式)
#define LED_ON          0               // LED点亮(低电平有效)
#define LED_OFF         1               // LED熄灭

/* 工作模式定义 */
#define MODE_AUTO       0               // 自动驱鸟模式
#define MODE_MANUAL     1               // 手动驱鸟模式
#define MODE_STOP       2               // 停止模式

/* 数码管段码表(共阴极) */
unsigned char code LED_PATTERN[10] = {
    0x3F,   // 0
    0x06,   // 1
    0x5B,   // 2
    0x4F,   // 3
    0x66,   // 4
    0x6D,   // 5
    0x7D,   // 6
    0x07,   // 7
    0x7F,   // 8
    0x6F    // 9
};

/* 全局变量 */
unsigned char g_mode;               // 当前工作模式
unsigned int  g_interval;           // 驱鸟间隔时间(秒)
unsigned int  g_countdown;          // 倒计时(秒)
unsigned char g_led_buf[8];         // LED数码管显示缓冲区
unsigned char g_show_index;         // 数码管扫描索引
bit g_flag_1ms;                     // 1ms标志位
bit g_flag_1s;                      // 1秒标志位
bit g_flag_bird_scare;              // 驱鸟触发标志
unsigned int g_ms_counter;          // 毫秒计数器
unsigned char g_bird_scare_time;    // 驱鸟持续时间计数

/* 按键相关变量 */
unsigned char g_key_code;           // 按键键码
unsigned char g_key_debounce;       // 按键去抖动计数器
bit g_key_ok;                       // 按键有效标志

/* 74HC595控制引脚 */
sbit DAT = P3^4;                    // 74HC595串行数据输入
sbit SCK = P3^2;                    // 74HC595串行时钟
sbit RCK = P3^5;                    // 74HC595并行输出时钟

/* 函数声明 */
void SYS_Init(void);
void Timer0_Init(void);
void Timer1_Init(void);
void ScanKey(void);
void KeyProcess(void);
void Led_Init(void);
void ScanLed(void);
void SetLed(unsigned char index, unsigned char num, bit show_point);
void Shift_74HC595(unsigned char dat);
void Output_74HC595(void);
void BirdScare_Start(void);
void BirdScare_Stop(void);
void UpdateDisplay(void);

/* 主函数 */
void main(void)
{
    SYS_Init();                     // 系统初始化
    Timer0_Init();                  // 定时器0初始化(1ms基准)
    Timer1_Init();                  // 定时器1初始化(蜂鸣器驱动)
    Led_Init();                     // LED数码管初始化
    usb_init();                     // USB初始化
  
    EA = 1;                         // 开启总中断
    P40 = 0;                        // 打开LED总电源
  
    /* 初始状态:自动模式,间隔30秒 */
    g_mode = MODE_AUTO;
    g_interval = 30;
    g_countdown = g_interval;
    g_bird_scare_time = 0;
  
    /* 通过USB-CDC输出启动信息 */
    printf_usb("=== 智能驱鸟器启动 ===\r\n");
    printf_usb("模式: 自动, 间隔: %d秒\r\n", (int)g_interval);
  
    while (1)
    {
        /* 1ms事件处理 */
        if (g_flag_1ms)
        {
            g_flag_1ms = 0;
            ScanKey();              // 按键扫描
            ScanLed();              // 数码管扫描
        }
      
        /* 1秒事件处理 */
        if (g_flag_1s)
        {
            g_flag_1s = 0;
          
            /* 自动模式下倒计时 */
            if (g_mode == MODE_AUTO)
            {
                if (g_countdown > 0)
                {
                    g_countdown--;
                }
                else
                {
                    /* 倒计时到,触发驱鸟 */
                    g_countdown = g_interval;
                    g_flag_bird_scare = 1;
                    g_bird_scare_time = 5;  // 驱鸟持续5秒
                    printf_usb("[驱鸟] 触发! 间隔:%ds\r\n", (int)g_interval);
                }
            }
          
            UpdateDisplay();        // 更新数码管显示
        }
      
        /* 驱鸟动作处理 */
        if (g_flag_bird_scare)
        {
            if (g_bird_scare_time > 0)
            {
                /* 驱鸟持续期间:LED闪烁 + 蜂鸣器发声 */
                P0 = ~P0;           // LED闪烁
                g_bird_scare_time--;
            }
            else
            {
                /* 驱鸟结束 */
                g_flag_bird_scare = 0;
                P0 = 0xFF;          // 关闭所有LED
                BirdScare_Stop();   // 关闭蜂鸣器
                printf_usb("[驱鸟] 结束\r\n");
            }
        }
      
        /* 按键事件处理 */
        if (g_key_ok)
        {
            g_key_ok = 0;
            KeyProcess();
        }
    }
}

/* 系统初始化函数 */
void SYS_Init(void)
{
    P_SW2 |= 0x80;                  // 允许访问扩展的特殊寄存器XFR
  
    WTST = 0;                       // 设置取程序代码等待时间,0表示不等待
    CKCON = 0;                      // 设置访问片内xdata速度,0表示最快
  
    /* 设置所有I/O口为准双向口模式 */
    P0M0 = 0x00; P0M1 = 0x00;
    P1M0 = 0x00; P1M1 = 0x00;
    P2M0 = 0x00; P2M1 = 0x00;
    P3M0 = 0x00; P3M1 = 0x00;
    P4M0 = 0x00; P4M1 = 0x00;
    P5M0 = 0x00; P5M1 = 0x00;
    P6M0 = 0x00; P6M1 = 0x00;
    P7M0 = 0x00; P7M1 = 0x00;
  
    P0 = 0xFF;                      // 初始关闭所有LED
}

/* 定时器0初始化 - 产生1ms时间基准 */
void Timer0_Init(void)
{
    /* 定时器0模式0:16位自动重装载模式 */
    TMOD &= 0xF0;                   // 清除T0模式位
    TMOD |= 0x00;                   // 模式0:16位自动重载
  
    AUXR &= 0x7F;                   // T0x12=0,12T模式
  
    /* 计算1ms定时重载值 */
    /* 定时周期 = (65536 - [TH0,TL0]) * 12 / FOSC */
    /* [TH0,TL0] = 65536 - FOSC / 12 / 1000 */
    TL0 = (unsigned char)(T0_1MS_RELOAD);       // 低8位
    TH0 = (unsigned char)(T0_1MS_RELOAD >> 8);  // 高8位
  
    TF0 = 0;                        // 清除溢出标志
    TR0 = 1;                        // 启动定时器0
    ET0 = 1;                        // 使能定时器0中断
}

/* 定时器1初始化 - 用于驱动蜂鸣器发声 */
void Timer1_Init(void)
{
    /* 定时器1模式0:16位自动重装载模式 */
    TMOD &= 0x0F;                   // 清除T1模式位
    TMOD |= 0x00;                   // 模式0:16位自动重载
  
    AUXR &= 0xBF;                   // T1x12=0,12T模式
  
    TL1 = 0x00;                     // 初始值
    TH1 = 0x00;
  
    TF1 = 0;                        // 清除溢出标志
    TR1 = 0;                        // 先不启动
    ET1 = 1;                        // 使能定时器1中断
}

/* 定时器0中断服务程序 - 1ms中断 */
void Timer0_Isr(void) interrupt 1
{
    g_flag_1ms = 1;                 // 设置1ms标志
  
    /* 累计1秒 */
    g_ms_counter++;
    if (g_ms_counter >= 1000)
    {
        g_ms_counter = 0;
        g_flag_1s = 1;              // 设置1秒标志
    }
}

/* 定时器1中断服务程序 - 蜂鸣器驱动 */
void Timer1_Isr(void) interrupt 3
{
    /* 翻转P5.0产生方波驱动蜂鸣器 */
    P50 = ~P50;
}

/* 按键扫描函数 - 每1ms执行一次 */
void ScanKey(void)
{
    unsigned char key;
  
    key = 0xFF;
  
    /* 扫描第1行(P0.6) */
    P0 = 0xFF;
    P06 = 0;
    P07 = 1;
    _nop_();
    _nop_();
  
    if (!P00) key = 0;              // Key0
    else if (!P01) key = 1;         // Key1
    else if (!P02) key = 2;         // Key2
    else if (!P03) key = 3;         // Key3
    else
    {
        /* 扫描第2行(P0.7) */
        P07 = 0;
        P06 = 1;
        _nop_();
        _nop_();
      
        if (!P00) key = 4;          // Key4
        else if (!P01) key = 5;     // Key5
        else if (!P02) key = 6;     // Key6
        else if (!P03) key = 7;     // Key7
    }
  
    P0 = 0xFF;                      // 恢复P0口
  
    /* 按键去抖动处理 */
    if (key != g_key_code)
    {
        g_key_ok = 0;
        g_key_code = key;
        g_key_debounce = 20;        // 20ms去抖动
    }
    else
    {
        if (g_key_debounce != 0)
        {
            g_key_debounce--;
            if (g_key_debounce == 0)
            {
                g_key_ok = 1;       // 按键有效
            }
        }
    }
}

/* 按键处理函数 */
void KeyProcess(void)
{
    switch (g_key_code)
    {
        case 0:                     // Key0:模式切换
            g_mode++;
            if (g_mode > MODE_STOP)
            {
                g_mode = MODE_AUTO;
            }
          
            /* 根据新模式调整状态 */
            if (g_mode == MODE_AUTO)
            {
                g_countdown = g_interval;
                printf_usb("[模式] 自动驱鸟, 间隔:%ds\r\n", (int)g_interval);
            }
            else if (g_mode == MODE_MANUAL)
            {
                printf_usb("[模式] 手动驱鸟, 按Key4触发\r\n");
            }
            else
            {
                BirdScare_Stop();
                g_flag_bird_scare = 0;
                P0 = 0xFF;
                printf_usb("[模式] 停止\r\n");
            }
            break;
          
        case 4:                     // Key4:增加间隔 / 手动触发
            if (g_mode == MODE_AUTO)
            {
                g_interval += 5;
                if (g_interval > 300)
                {
                    g_interval = 300;
                }
                g_countdown = g_interval;
                printf_usb("[设置] 间隔:%ds\r\n", (int)g_interval);
            }
            else if (g_mode == MODE_MANUAL)
            {
                /* 手动触发驱鸟 */
                g_flag_bird_scare = 1;
                g_bird_scare_time = 5;
                BirdScare_Start();
                printf_usb("[手动] 触发驱鸟!\r\n");
            }
            break;
          
        case 5:                     // Key5:减少间隔
            if (g_mode == MODE_AUTO)
            {
                if (g_interval > 5)
                {
                    g_interval -= 5;
                }
                else
                {
                    g_interval = 5;
                }
                g_countdown = g_interval;
                printf_usb("[设置] 间隔:%ds\r\n", (int)g_interval);
            }
            break;
          
        default:
            break;
    }
}

/* 启动驱鸟(开启蜂鸣器) */
void BirdScare_Start(void)
{
    /* 设置定时器1产生约1KHz方波驱动蜂鸣器 */
    /* 定时周期 = (65536 - [TH1,TL1]) * 12 / FOSC */
    /* 目标频率1KHz,翻转模式,所以定时周期=0.5ms */
    /* [TH1,TL1] = 65536 - FOSC / 12 / 2000 */
    TL1 = (unsigned char)(65536 - FOSC / 12 / 2000);
    TH1 = (unsigned char)((65536 - FOSC / 12 / 2000) >> 8);
  
    TR1 = 1;                        // 启动定时器1
}

/* 停止驱鸟(关闭蜂鸣器) */
void BirdScare_Stop(void)
{
    TR1 = 0;                        // 停止定时器1
    P50 = 0;                        // 关闭蜂鸣器
}

/* LED数码管初始化 */
void Led_Init(void)
{
    unsigned char i;
  
    DAT = 0;
    RCK = 0;
    SCK = 0;
  
    for (i = 0; i < 8; i++)
    {
        g_led_buf[i] = 0x00;        // 关闭所有数码管
    }
  
    g_show_index = 0;
}

/* 74HC595串行移入一个字节 */
void Shift_74HC595(unsigned char dat)
{
    unsigned char i;
  
    for (i = 0; i < 8; i++)
    {
        dat <<= 1;
        DAT = CY;                   // 将最高位移出到CY
        SCK = 0;
        _nop_();
        _nop_();
        SCK = 1;                    // 上升沿移入数据
    }
}

/* 74HC595并行输出 */
void Output_74HC595(void)
{
    RCK = 0;
    _nop_();
    _nop_();
    RCK = 1;                        // 上升沿输出数据
}

/* LED数码管扫描 - 每1ms执行一次 */
void ScanLed(void)
{
    Shift_74HC595(g_led_buf[g_show_index]);         // 段码
    Shift_74HC595(~(1 << g_show_index));            // 位选
    Output_74HC595();
  
    g_show_index++;
    if (g_show_index >= 8)
    {
        g_show_index = 0;
    }
}

/* 设置数码管显示 */
void SetLed(unsigned char index, unsigned char num, bit show_point)
{
    if (index < 8)
    {
        if (num < 10)
        {
            g_led_buf[index] = LED_PATTERN[num];
        }
        else
        {
            g_led_buf[index] = 0x00;    // 关闭
        }
      
        if (show_point)
        {
            g_led_buf[index] |= 0x80;   // 显示小数点
        }
    }
}

/* 更新数码管显示内容 */
void UpdateDisplay(void)
{
    unsigned char d1, d2, d3, d4;
  
    /* 数码管显示格式:模式 + 间隔/倒计时 */
    /* 第0位:模式标识(A-自动, M-手动, S-停止) */
    switch (g_mode)
    {
        case MODE_AUTO:
            SetLed(0, 0, 0);        // 显示0表示自动
            break;
        case MODE_MANUAL:
            SetLed(0, 1, 0);        // 显示1表示手动
            break;
        case MODE_STOP:
            SetLed(0, 2, 0);        // 显示2表示停止
            break;
    }
  
    /* 第1位:显示间隔时间或倒计时 */
    if (g_mode == MODE_AUTO)
    {
        /* 显示倒计时 */
        d1 = g_countdown / 100;
        d2 = (g_countdown % 100) / 10;
        d3 = g_countdown % 10;
        SetLed(1, d1, 0);
        SetLed(2, d2, 0);
        SetLed(3, d3, 0);
    }
    else if (g_mode == MODE_MANUAL)
    {
        SetLed(1, 0, 0);
        SetLed(2, 0, 0);
        SetLed(3, 0, 0);
    }
    else
    {
        SetLed(1, 0, 0);
        SetLed(2, 0, 0);
        SetLed(3, 0, 0);
    }
  
    /* 第4-7位:显示间隔设定值 */
    d1 = g_interval / 100;
    d2 = (g_interval % 100) / 10;
    d3 = g_interval % 10;
    SetLed(5, d1, 0);
    SetLed(6, d2, 0);
    SetLed(7, d3, 1);              // 带小数点表示设定值
}

使用说明

  1. 编译下载:使用Keil C51编译,芯片选择AI8051U,将生成的HEX文件通过AiCube-ISP下载到AI8051U实验箱

  2. 操作方式

    • Key0:切换工作模式(自动→手动→停止→自动循环)
    • Key4:自动模式下增加驱鸟间隔(+5秒,最大300秒);手动模式下触发一次驱鸟
    • Key5:自动模式下减少驱鸟间隔(-5秒,最小5秒)
  3. 显示说明

    • 数码管第0位:0=自动模式,1=手动模式,2=停止模式
    • 数码管第1-3位:自动模式下显示倒计时(秒)
    • 数码管第5-7位(带小数点):显示当前设定的间隔时间(秒)
  4. 驱鸟动作

    • P0口LED闪烁(视觉驱鸟)
    • P5.0蜂鸣器发出约1KHz声音(听觉驱鸟)
    • 每次驱鸟持续约5秒
    • 通过USB-CDC串口输出状态信息到电脑
  5. USB-CDC通信:连接电脑后会出现一个USB-CDC串口,打开串口助手即可看到驱鸟器的状态信息


如果您需要调整驱鸟频率、声音音调或增加更多功能(如光敏传感器检测、RTC定时等),请告诉我,我可以为您进一步扩展代码。


🔍 搜索 "AI8051U 实验箱 模拟定时 智能驱鸟"

📌 相关论坛讨论

  1. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】
  2. 一起学 AiCube@Ai8051U 定时器0/1,INT0/INT1,中断组合应用实验打卡

⏱️ 查找用时 10.4s | 总回答用时 46.9s

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2026-07-21 09:58:23
已绑定手机

10

主题

52

回帖

273

积分

中级会员

积分
273
发表于 前天 09:37 | 显示全部楼层
STC32G144K246-LQFP100是否支持 CAN-FD?请给出寄存器配置代码

截图202607200936546428.jpg
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:54
  • 最近打卡:2026-07-21 09:58:23
已绑定手机

10

主题

52

回帖

273

积分

中级会员

积分
273
发表于 前天 09:37 | 显示全部楼层

现在我已经获取了足够的信息。让我来回答您的问题。


回答:STC32G144K246-LQFP100 支持 CAN-FD

是的,STC32G144K246 系列支持 CAN-FD。 根据手册"CAN_FD,兼容CAN2.0B/A"章节的表格,STC32G144K246系列明确标注支持 CAN-FD(●)。该芯片内置 2 路 CAN-FD 控制器(CAN1 和 CAN2),支持 CAN2.0A/B(最大8字节有效载荷)和 CAN-FD(最高64字节有效载荷),CAN-FD 最高通信波特率可达 8Mbit/s


LQFP100 封装 CAN 引脚分配

CAN 通道 信号 LQFP100 引脚 复用功能
CAN1 CAN_RX 95 (P0.0) 默认
CAN_TX 96 (P0.1) 默认
CAN_RX_2 50 (P5.0) 可选
CAN_TX_2 51 (P5.1) 可选
CAN_RX_3 69 (P4.2) 可选
CAN_TX_3 93 (P4.5) 可选
CAN_RX_4 55 (P7.0) 可选
CAN_TX_4 56 (P7.1) 可选
CAN_RX_5 45 (P3.0) 可选
CAN_TX_5 46 (P3.1) 可选
CAN_RX_6 53 (P3.6) 可选
CAN_TX_6 54 (P3.7) 可选
CAN_RX_7 21 (P1.4) 可选
CAN_TX_7 22 (P1.5) 可选
CAN_RX_8 70 (P4.3) 可选
CAN_TX_8 71 (P4.4) 可选
CAN2 CAN2_RX 97 (P0.2) 默认
CAN2_TX 98 (P0.3) 默认
CAN2_RX_2 100 (P5.2) 可选
CAN2_TX_2 1 (P5.3) 可选
CAN2_RX_3 94 (P4.6) 可选
CAN2_TX_3 20 (P4.7) 可选
CAN2_RX_4 57 (P7.2) 可选
CAN2_TX_4 58 (P7.3) 可选
CAN2_RX_5 18 (P1.0) 可选
CAN2_TX_5 19 (P1.1) 可选
CAN2_RX_6 13 (P8.4) 可选
CAN2_TX_6 14 (P8.5) 可选
CAN2_RX_7 59 (PA.0) 可选
CAN2_TX_7 60 (PA.1) 可选

中断号: CAN1 中断向量地址 FF:00E3H,中断号 28;CAN2 中断向量地址 FF:00EBH,中断号 29。


CAN-FD 寄存器配置代码(以 CAN1 为例,使用默认引脚 P0.0/P0.1)

以下代码使用 Keil C251 编译器,配置 CAN1 以 500kbps(仲裁段)/ 2Mbps(数据段)工作,发送一帧 CAN-FD 扩展帧数据。

/******************************************************************************
 * 文件名: CAN_FD_Demo.c
 * 芯片: STC32G144K246-LQFP100
 * 编译器: Keil C251
 * 
 * 功能: 演示 CAN-FD 基本配置与发送
 *       使用 CAN1 默认引脚 P0.0(CAN_RX), P0.1(CAN_TX)
 *       仲裁段波特率: 500kbps (系统时钟40MHz)
 *       数据段波特率: 2Mbps   (系统时钟40MHz)
 *       发送一帧 CAN-FD 扩展帧数据 (ID=0x123, 64字节数据)
 * 
 * 接线: P0.0 -> CAN收发器 RX
 *       P0.1 -> CAN收发器 TX
 *       推荐使用 TJA1040/TJA1050 等 5V CAN 收发器
 ******************************************************************************/

#include "stc32g.h"     // STC32G 系列头文件

// 定义 CAN 寄存器基地址 (XFR 区域)
#define CAN1_BASE   0x7EF400

// 定义常用寄存器地址宏
#define CAN_CFG_STAT  (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA0))
#define CAN_TCMD      (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA1))
#define CAN_TCTRL     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA2))
#define CAN_RCTRL     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA3))
#define CAN_RTIE      (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA4))
#define CAN_RTIF      (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA5))
#define CAN_ERRINT    (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA6))
#define CAN_LIMIT     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA7))
#define CAN_S_SEG1    (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA8))
#define CAN_S_SEG2    (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xA9))
#define CAN_S_SJW     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xAA))
#define CAN_S_PRESC   (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xAB))
#define CAN_F_SEG1    (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xAC))
#define CAN_F_SEG2    (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xAD))
#define CAN_F_SJW     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xAE))
#define CAN_F_PRESC   (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xAF))
#define CAN_EALCAP    (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB0))
#define CAN_TDC       (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB1))
#define CAN_RECNT     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB2))
#define CAN_TECNT     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB3))
#define CAN_ACFCTRL   (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB4))
#define CAN_TIMECFG   (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB5))
#define CAN_ACF_EN0   (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB6))
#define CAN_ACF_EN1   (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB7))
#define CAN_ACF0      (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB8))
#define CAN_ACF1      (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xB9))
#define CAN_ACF2      (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xBA))
#define CAN_ACF3      (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xBB))
#define CAN_TTCFG     (*(volatile unsigned char volatile xdata *)(CAN1_BASE + 0xBF))

// 发送缓冲区 (PTB)
#define CAN_TBUF_ADDR ((volatile unsigned char volatile xdata *)(CAN1_BASE + 0x50))

// 接收缓冲区 (RBUF)
#define CAN_RBUF_ADDR ((volatile unsigned char volatile xdata *)(CAN1_BASE + 0x00))

// 延时函数
void delay_ms(unsigned int ms)
{
    unsigned int i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 12000; j++);
}

// CAN-FD 初始化函数
// 系统时钟: 40MHz
// 仲裁段: 500kbps (TQ=100ns, 位时间=20TQ)
// 数据段: 2Mbps   (TQ=50ns,  位时间=10TQ)
void CAN_FD_Init(void)
{
    unsigned char i;
  
    // 第一步: 使能访问 XFR 扩展寄存器
    P_SW2 |= 0x80;
  
    // 第二步: 进入复位模式 (RESET=1)
    CAN_CFG_STAT |= 0x80;
  
    // 第三步: 配置波特率 (只能在复位模式下配置)
    // 仲裁段: 500kbps @ 40MHz
    // TQ = (S_PRESC+1) / f_can_clk = (1+1)/40MHz = 50ns
    // 位时间 = (S_SEG1+2) + (S_SEG2+1) = (13+2)+(4+1)=20TQ = 1us -> 1MHz... 
    // 重新计算: 目标500kbps = 2us/bit
    // TQ = 100ns, PRESC=3 -> (3+1)/40MHz = 100ns
    // 位时间 = 20TQ = 2us -> 500kbps
    // S_SEG1+2=15 -> S_SEG1=13, S_SEG2+1=4 -> S_SEG2=3
    CAN_S_PRESC = 3;        // 预分频: (3+1)=4, TQ=100ns
    CAN_S_SEG1  = 13;       // S_SEG1=13, 实际段1时间=15TQ
    CAN_S_SEG2  = 0x03;     // S_SEG2=3,  实际段2时间=4TQ
    CAN_S_SJW   = 0x03;     // SJW=3TQ
  
    // 数据段: 2Mbps @ 40MHz
    // TQ = (F_PRESC+1) / f_can_clk = (0+1)/40MHz = 25ns
    // 位时间 = (F_SEG1+2)+(F_SEG2+1) = (5+2)+(2+1)=10TQ = 250ns -> 4MHz
    // 重新计算: 目标2Mbps = 500ns/bit
    // TQ = 50ns, PRESC=1 -> (1+1)/40MHz = 50ns
    // 位时间 = 10TQ = 500ns -> 2Mbps
    // F_SEG1+2=7 -> F_SEG1=5, F_SEG2+1=2 -> F_SEG2=1
    CAN_F_PRESC = 1;        // 预分频: (1+1)=2, TQ=50ns
    CAN_F_SEG1  = 0x05;     // F_SEG1=5, 实际段1时间=7TQ
    CAN_F_SEG2  = 0x01;     // F_SEG2=1, 实际段2时间=2TQ
    CAN_F_SJW   = 0x01;     // F_SJW=1TQ
  
    // 第四步: 配置 CAN-FD ISO 模式 (推荐使用 ISO 11898-1:2015)
    CAN_TCTRL |= 0x80;      // FD_ISO=1, 选择 ISO CAN-FD 模式
  
    // 第五步: 使能发送器延迟补偿 (TDC) - CAN-FD 高速数据段必需
    // SSPOFF 建议设为与 F_SEG1 相同值
    CAN_TDC = 0x80 | 0x05;  // TDCEN=1, SSPOFF=5
  
    // 第六步: 配置接收筛选器 - 接收所有帧 (全部通过)
    CAN_ACF_EN0 = 0x00;     // 先关闭所有筛选器
    CAN_ACF_EN1 = 0x00;
  
    // 使能筛选器组0, 接收所有ID
    CAN_ACFCTRL = 0x00;     // SELMASK=0, ACFADR=0 -> 指向ACF_1的CODE
    CAN_ACF0 = 0x00;        // CODE[7:0] = 0x00
    CAN_ACF1 = 0x00;        // CODE[15:8] = 0x00
    CAN_ACF2 = 0x00;        // CODE[23:16] = 0x00
    CAN_ACF3 = 0x00;        // AIDEE=0, AIDE=0, CODE[28:24]=0
  
    CAN_ACFCTRL = 0x20;     // SELMASK=1, ACFADR=0 -> 指向ACF_1的MASK
    CAN_ACF0 = 0xFF;        // MASK[7:0] = 0xFF (全部不比较)
    CAN_ACF1 = 0xFF;        // MASK[15:8] = 0xFF
    CAN_ACF2 = 0xFF;        // MASK[23:16] = 0xFF
    CAN_ACF3 = 0x1F;        // MASK[28:24]=11111, 全部不比较
  
    CAN_ACF_EN0 = 0x01;     // 使能筛选器组0 (AE_0=1)
  
    // 第七步: 配置接收BUF将满警告值
    CAN_LIMIT = 0x10;       // AFWL=1 (至少1帧即触发), EWL=0 (错误警告值=8)
  
    // 第八步: 配置中断 (可选)
    CAN_RTIE = 0x80;        // RIE=1, 使能接收中断
    // CAN_RTIE = 0x88;     // RIE=1, TPIE=1, 使能接收和PTB发送中断
  
    // 第九步: 退出复位模式 (RESET=0), 进入正常模式
    CAN_CFG_STAT &= ~0x80;
}

// 发送一帧 CAN-FD 扩展帧数据 (使用 PTB)
// id: 29位扩展ID
// data: 数据缓冲区指针
// len: 数据长度 (1~64)
// brs: 1=数据段使用快速波特率, 0=整帧使用仲裁段波特率
unsigned char CAN_FD_Send_Frame(unsigned long id, unsigned char *data, 
                                unsigned char len, unsigned char brs)
{
    unsigned char i;
    unsigned char dlc;
    unsigned char *tb = CAN_TBUF_ADDR;
  
    // 检查PTB是否空闲
    if(CAN_TCMD & 0x10)     // TPE=1, 正在发送
        return 0;           // 发送忙
  
    // 选择 PTB
    CAN_TCMD &= ~0x80;      // TBSEL=0
  
    // 计算 DLC 值 (CAN-FD 编码)
    if(len <= 8)
        dlc = len;
    else if(len <= 12)
        dlc = 0x09;
    else if(len <= 16)
        dlc = 0x0A;
    else if(len <= 20)
        dlc = 0x0B;
    else if(len <= 24)
        dlc = 0x0C;
    else if(len <= 32)
        dlc = 0x0D;
    else if(len <= 48)
        dlc = 0x0E;
    else
        dlc = 0x0F;
  
    // 填充发送缓冲区 (扩展帧格式)
    tb[0] = (unsigned char)(id & 0xFF);         // ID[7:0]
    tb[1] = (unsigned char)((id >> 8) & 0xFF);  // ID[15:8]
    tb[2] = (unsigned char)((id >> 16) & 0xFF); // ID[23:16]
    tb[3] = (unsigned char)((id >> 24) & 0x1F); // ID[28:24]
  
    // 控制字节: IDE=1(扩展帧), RTR=0(数据帧), FDF=1(CAN-FD), BRS, DLC
    tb[4] = 0x80 | 0x40;    // IDE=1, RTR=0
    tb[4] |= 0x20;          // FDF=1 (CAN-FD帧)
    if(brs)
        tb[4] |= 0x10;      // BRS=1 (数据段快速波特率)
    tb[4] |= (dlc & 0x0F);  // DLC
  
    // 填充数据
    for(i = 0; i < len; i++)
        tb[8 + i] = data[i];
  
    // 启动 PTB 发送
    CAN_TCMD |= 0x10;       // TPE=1
  
    return 1;               // 发送请求已提交
}

// 检查并读取接收到的数据
// 返回值: 0=无数据, 1=有数据已读取
unsigned char CAN_FD_Check_Receive(void)
{
    unsigned char status;
    unsigned char *rb = CAN_RBUF_ADDR;
    unsigned char i;
  
    // 检查接收中断标志
    if(!(CAN_RTIF & 0x80))  // RIF=0, 无接收数据
        return 0;
  
    // 读取接收数据 (此处仅做示例, 实际应用中需根据需求处理)
    // 扩展帧格式: rb[0~3]=ID, rb[4]=控制字节, rb[5]=状态, rb[6~7]=时间戳
    // rb[8]开始为数据
  
    // 清除接收中断标志 (写1清零)
    CAN_RTIF |= 0x80;       // 清除RIF
  
    // 释放接收缓冲区, 指向下一个SLOT
    CAN_RCTRL |= 0x10;      // RREL=1
  
    return 1;
}

// CAN1 中断服务函数
void CAN1_ISR(void) interrupt 28
{
    unsigned char rtif;
    unsigned char erint;
  
    rtif = CAN_RTIF;
    erint = CAN_ERRINT;
  
    // 接收中断
    if(rtif & 0x80)
    {
        // 处理接收数据 (在主循环中轮询处理, 中断中仅做标记)
        CAN_RTIF |= 0x80;   // 清除RIF
    }
  
    // PTB发送完成中断
    if(rtif & 0x08)
    {
        CAN_RTIF |= 0x08;   // 清除TPIF
    }
  
    // 错误中断
    if(rtif & 0x02)
    {
        CAN_RTIF |= 0x02;   // 清除EIF
    }
}

// 主函数
void main(void)
{
    unsigned char test_data[64];
    unsigned char i;
  
    // 初始化测试数据
    for(i = 0; i < 64; i++)
        test_data[i] = i;
  
    // 初始化 CAN-FD
    CAN_FD_Init();
  
    // 使能总中断
    EA = 1;
  
    // 主循环
    while(1)
    {
        delay_ms(1000);     // 每秒发送一帧
      
        // 发送一帧 CAN-FD 扩展帧, ID=0x123, 64字节数据, 使用快速波特率
        CAN_FD_Send_Frame(0x123, test_data, 64, 1);
      
        // 在主循环中轮询接收
        CAN_FD_Check_Receive();
    }
}

关键配置说明

  1. 波特率计算(系统时钟 40MHz):

    • 仲裁段 500kbpsS_PRESC=3 → TQ=100ns,S_SEG1=13, S_SEG2=3 → 位时间=20TQ=2μs
    • 数据段 2MbpsF_PRESC=1 → TQ=50ns,F_SEG1=5, F_SEG2=1 → 位时间=10TQ=500ns
  2. CAN-FD 关键寄存器

    • CAN_TCTRL.FD_ISO:选择 ISO 11898-1:2015 标准 CAN-FD 模式
    • CAN_TDC.TDCEN:使能发送器延迟补偿(CAN-FD 高速数据段必需)
    • CAN_TDC.SSPOFF:辅助采样点偏移,建议设为与 F_SEG1 相同值
  3. 发送缓冲区格式(扩展帧 CAN-FD):

    • TBUF[0~3]:29位扩展ID
    • TBUF[4]:控制字节(IDE=1, RTR=0, FDF=1, BRS, DLC[3:0])
    • TBUF[8~71]:数据(最多64字节)
  4. 接收筛选器:示例中配置为接收所有帧(MASK全1=不比较),实际应用中请根据需求设置具体的ID过滤。

  5. 外部硬件:CAN_TX/CAN_RX 引脚需连接 CAN 收发器(如 TJA1050/TJA1040),收发器再连接 CAN 总线。


🔍 搜索 "STC32G144K246 CAN-FD 寄存器配置"

📌 相关论坛讨论

  1. 自行激光炮@雁过留痕组,飞檐走壁组,全国大学生智能汽车竞赛@STC32G144K246
  2. LVGL@STC32G144K246,Hello World ! 51的 LVGL,GUI
  3. 核心板-STC32G144K246-LQFP100, 11/11版,不要使用,有新的版本开源

⏱️ 查找用时 12.6s | 总回答用时 47.5s

回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:344
  • 最近打卡:2026-07-22 10:08:37

16

主题

866

回帖

3999

积分

超级版主

积分
3999
发表于 前天 10:15 | 显示全部楼层
基于STC8H8K64U自动写一个串口1和串口2同时中断收发的演示程序,并含详细程序注解
截图202607201026007870.jpg
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:344
  • 最近打卡:2026-07-22 10:08:37

16

主题

866

回帖

3999

积分

超级版主

积分
3999
发表于 前天 10:15 | 显示全部楼层

好的,信息已经足够充分了。现在我来编写完整的演示程序。

/*
 * ============================================================
 * 程序名称:STC8H8K64U 串口1和串口2 同时中断收发演示程序
 * 硬件平台:STC8H8K64U
 * 编译环境:Keil C51
 * 系统时钟:11.0592MHz(内部IRC)
 * 串口1:波特率115200,8N1,使用定时器2作为波特率发生器
 *        引脚:P3.0(RxD) / P3.1(TxD)  —— 默认位置
 * 串口2:波特率115200,8N1,使用定时器2作为波特率发生器
 *        引脚:P1.0(RxD2) / P1.1(TxD2) —— 默认位置
 *
 * 功能说明:
 *   1. 串口1和串口2均工作在中断收发模式
 *   2. 串口1收到数据后,将收到的数据原样回发(回环测试)
 *   3. 串口2收到数据后,将收到的数据原样回发(回环测试)
 *   4. 主循环中每500ms通过串口1发送一次 "UART1 Hello!\r\n"
 *   5. 主循环中每500ms通过串口2发送一次 "UART2 Hello!\r\n"
 *   6. 串口1和串口2的发送和接收均使用中断方式
 *
 * 接线说明:
 *   串口1:P3.0(RxD) - USB转串口TXD
 *          P3.1(TxD) - USB转串口RXD
 *   串口2:P1.0(RxD2) - 外部设备TXD
 *          P1.1(TxD2) - 外部设备RXD
 *
 * 使用步骤:
 *   1. 用 STC-ISP 软件下载程序,选择内部IRC 11.0592MHz
 *   2. 打开两个串口助手,分别连接串口1和串口2
 *   3. 串口1和串口2均设置为 115200bps, 8N1
 *   4. 向串口1发送数据,会收到相同数据的回显
 *   5. 向串口2发送数据,会收到相同数据的回显
 *   6. 串口1和串口2会周期性收到 "UARTx Hello!" 消息
 * ============================================================
 */

#include "STC8H.h"      // STC8H系列头文件

/* ======================== 宏定义 ======================== */
#define FOSC            11059200UL      // 系统时钟频率 11.0592MHz
#define BAUDRATE        115200          // 波特率 115200

/* ======================== 串口缓冲区定义 ======================== */
#define UART1_RX_MAX    64              // 串口1接收缓冲区大小
#define UART2_RX_MAX    64              // 串口2接收缓冲区大小

/* 串口1接收环形缓冲区 */
unsigned char xdata UART1_RX_BUF[UART1_RX_MAX];  // 接收数据缓冲区
unsigned char UART1_RX_CNT;                      // 已接收字节数

/* 串口2接收环形缓冲区 */
unsigned char xdata UART2_RX_BUF[UART2_RX_MAX];  // 接收数据缓冲区
unsigned char UART2_RX_CNT;                      // 已接收字节数

/* 串口发送标志 */
volatile bit UART1_TX_BUSY;     // 串口1发送忙标志(1=正在发送)
volatile bit UART2_TX_BUSY;     // 串口2发送忙标志(1=正在发送)

/* 主循环定时计数 */
unsigned int main_loop_cnt;     // 主循环计数,用于定时发送

/* ======================== 函数声明 ======================== */
void Delay500ms(void);          // 软件延时500ms
void UART1_Config(void);        // 串口1初始化配置
void UART2_Config(void);        // 串口2初始化配置
void UART1_SendByte(unsigned char dat);     // 串口1发送单字节
void UART2_SendByte(unsigned char dat);     // 串口2发送单字节
void UART1_SendString(unsigned char *p);    // 串口1发送字符串
void UART2_SendString(unsigned char *p);    // 串口2发送字符串
void UART1_ProcessData(void);   // 串口1数据处理(回显)
void UART2_ProcessData(void);   // 串口2数据处理(回显)

/* ======================== 主函数 ======================== */
void main(void)
{
    unsigned char i;

    /* 关闭看门狗(如果使能了的话) */
    // WDT_CONTR = 0x00;   // 默认关闭,无需操作

    /* 设置系统时钟为内部IRC 11.0592MHz(默认即为内部IRC) */
    /* 注意:下载时需在STC-ISP软件中选择内部IRC 11.0592MHz */

    /* 初始化变量 */
    UART1_RX_CNT = 0;
    UART2_RX_CNT = 0;
    UART1_TX_BUSY = 0;
    UART2_TX_BUSY = 0;
    main_loop_cnt = 0;

    /* 清空接收缓冲区 */
    for (i = 0; i < UART1_RX_MAX; i++)
    {
        UART1_RX_BUF[i] = 0;
    }
    for (i = 0; i < UART2_RX_MAX; i++)
    {
        UART2_RX_BUF[i] = 0;
    }

    /* 配置串口1和串口2 */
    UART1_Config();     // 初始化串口1
    UART2_Config();     // 初始化串口2

    /* 打开总中断 */
    EA = 1;

    /* 主循环 */
    while (1)
    {
        /* ---- 串口1数据处理 ---- */
        if (UART1_RX_CNT > 0)
        {
            /* 关闭总中断保护,防止在读取过程中被中断修改 */
            EA = 0;
            _nop_(); _nop_(); _nop_(); _nop_();  // 4级流水线,插入4个NOP
            UART1_ProcessData();
            EA = 1;
        }

        /* ---- 串口2数据处理 ---- */
        if (UART2_RX_CNT > 0)
        {
            EA = 0;
            _nop_(); _nop_(); _nop_(); _nop_();  // 4级流水线,插入4个NOP
            UART2_ProcessData();
            EA = 1;
        }

        /* ---- 周期性发送测试消息(约每500ms一次) ---- */
        if (++main_loop_cnt >= 500)
        {
            main_loop_cnt = 0;

            /* 通过串口1发送测试消息 */
            UART1_SendString("UART1 Hello!\r\n");

            /* 通过串口2发送测试消息 */
            UART2_SendString("UART2 Hello!\r\n");
        }

        /* 简单延时,控制主循环频率 */
        Delay500ms();   // 实际延时约1ms量级,配合计数实现500ms周期
    }
}

/* ======================== 串口1初始化 ======================== */
void UART1_Config(void)
{
    /*
     * 串口1配置:模式1(8位UART,可变波特率)
     * 波特率:115200 @ 11.0592MHz
     * 使用定时器2作为波特率发生器(1T模式)
     * 引脚:P3.0(RxD) / P3.1(TxD) —— 默认位置(S1_S[1:0]=00)
     */

    /* 设置串口1引脚为默认位置:P3.0(RxD) / P3.1(TxD) */
    /* P_SW1 的 S1_S[1:0] 默认值为 00,无需修改 */

    /* 配置定时器2作为波特率发生器 */
    /* 定时器2重载值 = 65536 - FOSC / (4 * BAUDRATE)
     *              = 65536 - 11059200 / (4 * 115200)
     *              = 65536 - 24
     *              = 65512 = 0xFFE8
     */
    T2L = 0xE8;         // 定时器2低8位重载值
    T2H = 0xFF;         // 定时器2高8位重载值

    /* 配置AUXR寄存器 */
    AUXR &= ~0x04;      // T2x12 = 0,先清零(实际下面会置1)
    AUXR |= 0x14;       // T2R = 1(启动定时器2)
                        // T2x12 = 1(1T模式)
                        // 注意:T2_C/T保持为0(定时器模式)

    /* 配置串口1使用定时器2作为波特率发生器 */
    AUXR |= 0x01;       // S1ST2 = 1,选择定时器2作为串口1波特率发生器

    /* 配置串口1控制寄存器 SCON */
    SCON = 0x50;        // SM0=0, SM1=1(模式1:8位UART)
                        // SM2=0(非多机通信)
                        // REN=1(允许接收)
                        // TB8=0
                        // TI=0, RI=0

    /* 使能串口1中断 */
    ES = 1;             // 使能串口1中断
}

/* ======================== 串口2初始化 ======================== */
void UART2_Config(void)
{
    /*
     * 串口2配置:模式0(8位UART,可变波特率)
     * 波特率:115200 @ 11.0592MHz
     * 使用定时器2作为波特率发生器(1T模式)
     * 引脚:P1.0(RxD2) / P1.1(TxD2) —— 默认位置(S2_S=0)
     *
     * 注意:串口1和串口2共用定时器2作为波特率发生器,
     *       因此波特率必须相同。
     */

    /* 设置串口2引脚为默认位置:P1.0(RxD2) / P1.1(TxD2) */
    /* P_SW2 的 S2_S 默认值为 0,无需修改 */

    /* 定时器2已在串口1初始化中配置完成,此处不再重复配置 */

    /* 配置串口2控制寄存器 S2CON */
    S2CON = 0x10;       // S2SM0=0(模式0:8位UART)
                        // S2SM2=0(非多机通信)
                        // S2REN=1(允许接收)
                        // S2TB8=0
                        // S2TI=0, S2RI=0

    /* 使能串口2中断 */
    /* 注意:串口2中断使能位在 IE2 寄存器中 */
    IE2 |= 0x01;        // ES2 = 1,使能串口2中断
}

/* ======================== 串口1发送单字节 ======================== */
void UART1_SendByte(unsigned char dat)
{
    /*
     * 功能:通过串口1发送一个字节数据(中断方式)
     * 参数:dat - 要发送的数据
     * 说明:等待上一个字节发送完成后,将数据写入SBUF触发发送
     */
    while (UART1_TX_BUSY);      // 等待上一个字节发送完成
    UART1_TX_BUSY = 1;          // 设置发送忙标志
    SBUF = dat;                 // 写入SBUF,启动发送
}

/* ======================== 串口2发送单字节 ======================== */
void UART2_SendByte(unsigned char dat)
{
    /*
     * 功能:通过串口2发送一个字节数据(中断方式)
     * 参数:dat - 要发送的数据
     */
    while (UART2_TX_BUSY);      // 等待上一个字节发送完成
    UART2_TX_BUSY = 1;          // 设置发送忙标志
    S2BUF = dat;                // 写入S2BUF,启动发送
}

/* ======================== 串口1发送字符串 ======================== */
void UART1_SendString(unsigned char *p)
{
    /*
     * 功能:通过串口1发送字符串(中断方式,逐字节发送)
     * 参数:p - 指向要发送的字符串的指针
     */
    while (*p)                  // 遍历字符串直到结束符 '\0'
    {
        UART1_SendByte(*p++);   // 发送当前字符,指针后移
    }
}

/* ======================== 串口2发送字符串 ======================== */
void UART2_SendString(unsigned char *p)
{
    /*
     * 功能:通过串口2发送字符串(中断方式,逐字节发送)
     * 参数:p - 指向要发送的字符串的指针
     */
    while (*p)                  // 遍历字符串直到结束符 '\0'
    {
        UART2_SendByte(*p++);   // 发送当前字符,指针后移
    }
}

/* ======================== 串口1数据处理(回显) ======================== */
void UART1_ProcessData(void)
{
    /*
     * 功能:处理串口1接收到的数据
     *       将接收缓冲区中的数据原样回发(回环测试)
     */
    unsigned char i;
    unsigned char len;

    /* 获取当前接收到的字节数 */
    len = UART1_RX_CNT;

    /* 将接收到的数据原样回发 */
    for (i = 0; i < len; i++)
    {
        UART1_SendByte(UART1_RX_BUF[i]);
    }

    /* 清空接收计数 */
    UART1_RX_CNT = 0;
}

/* ======================== 串口2数据处理(回显) ======================== */
void UART2_ProcessData(void)
{
    /*
     * 功能:处理串口2接收到的数据
     *       将接收缓冲区中的数据原样回发(回环测试)
     */
    unsigned char i;
    unsigned char len;

    /* 获取当前接收到的字节数 */
    len = UART2_RX_CNT;

    /* 将接收到的数据原样回发 */
    for (i = 0; i < len; i++)
    {
        UART2_SendByte(UART2_RX_BUF[i]);
    }

    /* 清空接收计数 */
    UART2_RX_CNT = 0;
}

/* ======================== 软件延时(约1ms) ======================== */
void Delay500ms(void)
{
    /*
     * 功能:软件延时,约1ms @ 11.0592MHz
     * 说明:用于主循环中的简单延时,配合计数实现500ms周期
     *       实际延时精度不高,仅用于演示
     */
    unsigned char i, j;

    _nop_();
    _nop_();
    i = 11;
    j = 190;
    do
    {
        while (--j);
    } while (--i);
}

/* ================================================================
 * 串口1中断服务函数
 * 中断向量:0023H
 * 中断源:UART1(RI || TI 触发)
 * 说明:同时处理接收中断和发送中断
 * ================================================================ */
void UART1_ISR(void) interrupt 4
{
    /*
     * 进入中断后,需要判断是接收中断(RI)还是发送中断(TI)
     * 注意:STC8H有4级流水线,但中断服务函数中硬件已自动保护现场
     */

    /* ---- 接收中断处理 ---- */
    if (RI)                     // 判断是否为接收中断
    {
        RI = 0;                 // 软件清除接收中断标志

        /* 将接收到的数据存入缓冲区(防止溢出) */
        if (UART1_RX_CNT < UART1_RX_MAX)
        {
            UART1_RX_BUF[UART1_RX_CNT] = SBUF;  // 读取接收数据
            UART1_RX_CNT++;                     // 接收计数+1
        }
        else
        {
            /* 缓冲区已满,丢弃该数据(可在此添加溢出处理) */
            unsigned char temp = SBUF;  // 读取数据以清除SBUF
            temp = temp;                // 防止编译警告
        }
    }

    /* ---- 发送中断处理 ---- */
    if (TI)                     // 判断是否为发送中断
    {
        TI = 0;                 // 软件清除发送中断标志
        UART1_TX_BUSY = 0;     // 清除发送忙标志,允许发送下一个字节
    }
}

/* ================================================================
 * 串口2中断服务函数
 * 中断向量:0043H
 * 中断源:UART2(S2RI || S2TI 触发)
 * 说明:同时处理接收中断和发送中断
 * ================================================================ */
void UART2_ISR(void) interrupt 8
{
    /*
     * 串口2的中断标志位在 S2CON 寄存器中
     * S2RI(S2CON.0)—— 接收中断标志
     * S2TI(S2CON.1)—— 发送中断标志
     */

    /* ---- 接收中断处理 ---- */
    if (S2CON & 0x01)           // 判断 S2RI 是否为1
    {
        S2CON &= ~0x01;         // 软件清除接收中断标志 S2RI

        /* 将接收到的数据存入缓冲区(防止溢出) */
        if (UART2_RX_CNT < UART2_RX_MAX)
        {
            UART2_RX_BUF[UART2_RX_CNT] = S2BUF;  // 读取接收数据
            UART2_RX_CNT++;                      // 接收计数+1
        }
        else
        {
            /* 缓冲区已满,丢弃该数据 */
            unsigned char temp = S2BUF;
            temp = temp;
        }
    }

    /* ---- 发送中断处理 ---- */
    if (S2CON & 0x02)           // 判断 S2TI 是否为1
    {
        S2CON &= ~0x02;         // 软件清除发送中断标志 S2TI
        UART2_TX_BUSY = 0;     // 清除发送忙标志,允许发送下一个字节
    }
}

程序说明

一、程序功能概述

本程序实现了 STC8H8K64U串口1(UART1)串口2(UART2) 同时独立工作的中断收发演示:

功能 串口1 串口2
引脚位置 P3.0(RxD) / P3.1(TxD) P1.0(RxD2) / P1.1(TxD2)
波特率 115200 115200
数据格式 8N1 8N1
波特率发生器 定时器2(1T模式) 定时器2(1T模式,共用)
接收方式 中断接收,存入环形缓冲区 中断接收,存入环形缓冲区
发送方式 中断发送(忙标志控制) 中断发送(忙标志控制)
数据处理 收到数据原样回发(回环) 收到数据原样回发(回环)
周期性发送 每500ms发送 "UART1 Hello!\r\n" 每500ms发送 "UART2 Hello!\r\n"

二、关键设计要点

  1. 波特率发生器共用:串口1和串口2共用定时器2作为波特率发生器,因此两者波特率必须相同。如需不同波特率,串口1可使用定时器1,串口2使用定时器2。

  2. 中断收发机制

    • 接收:数据到达时触发接收中断(RI/S2RI),在中断服务函数中将数据存入缓冲区,主循环中轮询处理
    • 发送:将数据写入SBUF/S2BUF触发发送,发送完成后触发发送中断(TI/S2TI),清除忙标志
  3. 缓冲区保护:在读取接收计数时,先关闭总中断并插入4个NOP(适配STC8H的4级流水线),防止在读取过程中被中断修改数据。

  4. 中断服务函数简洁原则:中断中仅做数据存储和标志清除,数据处理放在主循环中,避免函数重入问题。

三、使用步骤

  1. STC-ISP 下载软件 中,选择芯片型号 STC8H8K64U
  2. 设置 内部IRC频率为 11.0592MHz
  3. 下载本程序到芯片
  4. 用两个串口助手分别连接:
    • 串口1:连接 P3.0/P3.1(USB转串口模块)
    • 串口2:连接 P1.0/P1.1(另一个USB转串口模块)
  5. 两个串口助手均设置为 115200bps, 8N1
  6. 向任一串口发送数据,会收到相同数据的回显
  7. 两个串口会周期性收到 "UARTx Hello!" 消息

🔍 搜索 "STC8H8K64U 串口1 串口2 中断收发"

📌 相关论坛讨论

  1. 【AI助手】@AiCube-ISP-V6.96Z中,【自动生成程序】+【查错高手】
  2. STC【AI 助手】帮 STC8H8K64U自动写一个完整的演示程序,要包含下面这些:

⏱️ 查找用时 11.5s | 总回答用时 44.8s

回复

使用道具 举报 送花

  • 打卡等级:常住居民III
  • 打卡总天数:105
  • 最近打卡:2026-07-21 13:30:21

38

主题

496

回帖

3341

积分

版主

积分
3341
QQ
发表于 前天 11:36 | 显示全部楼层
用STC8H1K28做电梯召唤盒,生成完整演示程序,并含详细程序注解
截图202607201138267415.jpg
QQ:1463035472,微信:19952583954
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2026-7-22 10:41 , Processed in 0.092103 second(s), 98 queries .

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表