驱动一个AIP650、数码管、按键、LED、红外、蜂鸣器控制板

如果一个控制板买了很多年而没有去理会,大概是因为太忙而无暇顾及。如果一旦闲下来,还是要翻出去来研究一下,算是对板子和自己一个交代(对,就是那个胶带)。

这个控制板主控是一个8脚的MCU,已打磨不知道型号,无法再利用了。

板上的数码管、按键、3个LED都是AIP650驱动。

还有红外接收和蜂鸣器。

这个板子驱动难度不大,但输入输出比较丰富,通用性很强,研究一下还是很有意义。

花了一些时间将主要的引脚引了出来,如图:

硬件已经OK,下面用STC8051U来驱动这个板子。

一、简单的调度系统

从STC论坛上学到的:

1、任务TASK定义

复制代码
typedef struct 
{
	u8 Run;               //任务状态:Run/Stop
	u16 TIMCount;         //定时计数器
	u16 TRITime;          //重载计数器
	void (*TaskHook) (void); //任务函数
} TASK_COMPONENTS;   

每个任务主要定义4个要素:

Run任务状态:0为不满足条件不被调用,1为即将被调用

TIMCount定时计数器:在定时器每次回调中计数器递减,直到为0时设置Run为1,等待执行。

TRITime重载计数器:在Task任务函数被执行后,将TIMCount重置为TRITime,开启下一次调度循环。

void (*TaskHook) (void);任务函数:Task被执行时运行的函数

复制代码
static TASK_COMPONENTS Task_Comps[]=
{
//状态  计数  周期  函数
 {0,10,10,key_Task},
 {0,10,10,irrx_Task},
};

u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps[0]);

这里定义了2个Task,其中key_Task为按键相关处理,irrx_Task为红外接收相关处理

2、任务调度回调函数

主要实现在每个调度最小时间单位时对各Task的计数做减一操作。当计数为0时重载计数

复制代码
void Task_Marks_Handler_Callback(void)
{
    u8 i;
    for(i=0; i<Tasks_Max; i++)
    {
        if(Task_Comps[i].TIMCount)      /* If the time is not 0 */
        {
            Task_Comps[i].TIMCount--;   /* Time counter decrement */
            if(Task_Comps[i].TIMCount == 0) /* If time arrives */
            {
                /*Resume the timer value and try again */
                Task_Comps[i].TIMCount = Task_Comps[i].TRITime;  
                Task_Comps[i].Run = 1;      /* The task can be run */
            }
        }
    }
}

此函数需要在定时器中断处理函数中调用

复制代码
void timer1_int (void) interrupt 3
{
	Task_Marks_Handler_Callback();
}

3、TASK函数执行

当Run为1时,执行TASK中定义的对应回调函数

复制代码
void Task_Pro_Handler_Callback(void)
{
    u8 i;
    for(i=0; i<Tasks_Max; i++)
    {
        if(Task_Comps[i].Run) /* If task can be run */
        {
            Task_Comps[i].Run = 0;      /* Flag clear 0 */
            Task_Comps[i].TaskHook();   /* Run task */
        }
    }
}

此函数需要在main文件while(1)中调用

复制代码
	  while(1){
		   Task_Pro_Handler_Callback();	
		}

4、Timer1初始化

应为调用处理中用到了Timer1,补充一下Timer1初始化:

复制代码
#define Timer1_Reload   (MAIN_Fosc / 2000)      //Timer 1 中断频率, 2000次/秒

void Timer1_init(void)
{
        TR1 = 0;    //停止计数

    #if (Timer1_Reload < 64)    // 如果用户设置值不合适, 则不启动定时器
        #error "Timer1设置的中断过快!"

    #elif ((Timer1_Reload/12) < 65536UL)    // 如果用户设置值不合适, 则不启动定时器
        ET1 = 1;    //允许中断
    //  PT1 = 1;    //高优先级中断
        TMOD &= ~0x30;
        TMOD |= (0 << 4);   //工作模式, 0: 16位自动重装, 1: 16位定时/计数, 2: 8位自动重装
    //  T1_CT = 1;  //计数
        T1_CT = 0;  //定时
    //  T1CLKO = 1; //输出时钟
        T1CLKO = 0; //不输出时钟

        #if (Timer1_Reload < 65536UL)
            T1x12 = 1;  //1T mode
            TH1 = (u8)((65536UL - Timer1_Reload) / 256);
            TL1 = (u8)((65536UL - Timer1_Reload) % 256);
        #else
            T1x12 = 0;  //12T mode
            TH1 = (u8)((65536UL - Timer1_Reload/12) / 256);
            TL1 = (u8)((65536UL - Timer1_Reload/12) % 256);
        #endif

        TR1 = 1;    //开始运行

    #else
        #error "Timer1设置的中断过慢!"
    #endif
}

5、key_Task

复制代码
void key_Task(void)
{
	static uint16_t num=0;
	//static unsigned char i=0;
	unsigned char key_value;
	key_value=aip650_scan_key();
	if(0xFF!=key_value)
	{
		delay_ms(100);
		key_value=aip650_scan_key();
		if(key_value==0x6c){
			delay_ms(100);
			key_value=aip650_scan_key();
			if(key_value==0x6c)	aip650_display_number(++num);
		}else if(key_value==0x4c){
			delay_ms(100);
			key_value=aip650_scan_key();
			if(key_value==0x4c) aip650_display_number(--num);
		}else if(key_value==0x64){
			delay_ms(100);
			key_value=aip650_scan_key();
			if(key_value==0x64){
	
    			//Buzzer_Control(1000, 500);  // 1kHz,响0.5秒
				//delay_ms(1000);
			}
		}


	}
}

二、AIP650驱动

AIP650通常用于共阴极数码管和矩阵键盘的驱动。接口类似与I2C,但是没有I2C从地址。关于AIP650的原理和介绍,AIP659数据手册有详细的描述。这里只侧重于驱动这个板子上的数码管、按键、LED。

1、基本定义

复制代码
// 端口定义 - 使用P3.2和P3.3
sbit AIP650_SCL = P3^2;  // 时钟线
sbit AIP650_SDA = P3^3;  // 数据线

根据这个板子的硬件连接情况,总结的真值表:

复制代码
unsigned char code seg_table[] = {
    0xFC, // 0
    0x24, // 1
    0xBA, // 2
    0xAE, // 3
    0x66, // 4
    0xCE, // 5
    0xDE, // 6
    0xA4, // 7
    0xFE, // 8
    0xEE, // 9
    0xF6, // A
    0x5E, // b
    0xD8, // C
    0x3E, // d
    0xDA, // E
    0xD2, // F
    0x80, // 小数点单独
    0x00  // 全灭
};

2、软件I2C

I2C起始信号

复制代码
// I2C起始信号
static void i2c_start(void)
{
    AIP650_SDA = 1;
    AIP650_SCL = 1;
    delay_us(5);
    AIP650_SDA = 0;
    delay_us(5);
    AIP650_SCL = 0;
}

I2C停止信号

复制代码
// I2C停止信号
static void i2c_stop(void)
{
    AIP650_SDA = 0;
    AIP650_SCL = 1;
    delay_us(5);
    AIP650_SDA = 1;
    delay_us(5);
}

I2C发送一个字节

复制代码
// I2C发送一个字节,返回ACK(0:应答,1:非应答)
static unsigned char i2c_write_byte(unsigned char dat)
{
    unsigned char i, ack;
    
    for(i = 0; i < 8; i++)
    {
        if(dat & 0x80)
            AIP650_SDA = 1;
        else
            AIP650_SDA = 0;
        
        dat <<= 1;
        delay_us(2);
        AIP650_SCL = 1;
        delay_us(5);
        AIP650_SCL = 0;
        delay_us(2);
    }
    
    // 释放SDA,读取ACK
    AIP650_SDA = 1;
    delay_us(2);
    AIP650_SCL = 1;
    delay_us(5);
    ack = AIP650_SDA;  // 读取应答位
    AIP650_SCL = 0;
    
    return ack;
}

I2C读取一个字节

复制代码
// I2C读取一个字节,ack=0发送应答,ack=1发送非应答
static unsigned char i2c_read_byte(unsigned char ack)
{
    unsigned char i, dat = 0;
    
    AIP650_SDA = 1;  // 释放总线
    
    for(i = 0; i < 8; i++)
    {
        dat <<= 1;
        AIP650_SCL = 1;
        delay_us(5);
        if(AIP650_SDA)
            dat |= 1;
        AIP650_SCL = 0;
        delay_us(2);
    }
    
    // 发送应答/非应答
    if(ack)
        AIP650_SDA = 1;  // 非应答
    else
        AIP650_SDA = 0;  // 应答
    
    delay_us(2);
    AIP650_SCL = 1;
    delay_us(5);
    AIP650_SCL = 0;
    AIP650_SDA = 1;  // 释放
    
    return dat;
}

3、AIP650控制

AIP650初始化

复制代码
// AIP650初始化
void aip650_init(void)
{
    AIP650_SCL = 1;
    AIP650_SDA = 1;
    
    delay_us(10);
    
    // 开启显示
    aip650_display_on();
    // 设置亮度为6
    aip650_set_brightness(6);
}

开启关闭显示、设置亮度

复制代码
// 开启显示
void aip650_display_on(void)
{
    i2c_start();
    //i2c_write_byte(0xC0);  // 设备地址+写
    i2c_write_byte(0x48);   // 亮度寄存器(示例地址)
    i2c_write_byte(0x01);   // 开启显示
    i2c_stop();
}

// 关闭显示
void aip650_display_off(void)
{
    i2c_start();
    //i2c_write_byte(0xC0);
    i2c_write_byte(0x48);
    i2c_write_byte(0x00);   // 关闭显示
    i2c_stop();
}

// 设置亮度 (0-7)
void aip650_set_brightness(unsigned char level)
{
    if(level > 7) level = 7;
    
    i2c_start();
   // i2c_write_byte(0xC0);
    i2c_write_byte(0x48);   // 亮度控制寄存器
		i2c_write_byte(0x01|level<<4);
    //i2c_write_byte(0x88 | level);  // 开启显示+亮度
    i2c_stop();
}

4、显示0-f字符

复制代码
void aip650_display_char(unsigned char pos, unsigned char ascii_char)
{
    unsigned char seg_data = 0x00;
    unsigned char i;
    
    // 判断是否有小数点
    if(ascii_char == '.')
    {
        seg_data = 0x80;  // 只显示小数点
    }
    else if(ascii_char >= '0' && ascii_char <= '9')
    {
        seg_data = seg_table[ascii_char - '0'];
    }
    else if(ascii_char >= 'A' && ascii_char <= 'F')
    {
        seg_data = seg_table[10 + (ascii_char - 'A')];
    }
    else if(ascii_char >= 'a' && ascii_char <= 'f')
    {
        seg_data = seg_table[10 + (ascii_char - 'a')];
    }
    else
    {
        seg_data = seg_table[17];  // 全灭
    }
    
    // 发送到AIP650
    i2c_start();

		switch(pos)
		{
			case 0:
				i2c_write_byte(0x68);
				break;
			case 1:
				i2c_write_byte(0x6C);
				AIP_VAL[1]=seg_data|(AIP_VAL[1]&0x01);
				break;
			case 2:
				i2c_write_byte(0x6A);
				AIP_VAL[2]=seg_data|(AIP_VAL[2]&0x01);
				break;
			case 3:
				i2c_write_byte(0x6E);
				AIP_VAL[3]=seg_data|(AIP_VAL[3]&0x01);
				break;
		}
		

    i2c_write_byte(seg_data);
    i2c_stop();
}

5、显示数字

复制代码
void aip650_display_number(unsigned int num)
{
    unsigned char digits[4];
    
    digits[3] = (num / 100) % 10;
    digits[2] = (num / 10) % 10;
    digits[1] = num % 10;
    
	aip650_display_char(1, digits[1] + '0');
    aip650_display_char(2, digits[2] + '0');
    aip650_display_char(3, digits[3] + '0');
    
    //将显示真值保存下来	
	AIP_VAL[3]=seg_table[digits[3]];
	AIP_VAL[2]=seg_table[digits[2]];
	AIP_VAL[1]=seg_table[digits[1]];
}

6、扫描按键

复制代码
unsigned char aip650_scan_key(void)
{
    unsigned char key_value = 0xFF;
    
    // 起始信号
    i2c_start();
  
    // 发送读键命令 0x4F
    if(i2c_write_byte(0x4F))
    {
        i2c_stop();
        return 0xFF;
    }
   
    // 读取按键值(发送0x00提供时钟,最后发NACK)
    key_value = i2c_read_byte(1);
    
    // 停止
    i2c_stop();
    
    return key_value;
}

7、驱动3个LED

复制代码
void aip650_led(unsigned char pos,unsigned char on)
{
	i2c_start();
	switch(pos)
	{
		case 1:
			i2c_write_byte(0x6C);	//DIG2
			break;
		case 2:
			i2c_write_byte(0x6A);	//DIG1
			break;
		case 3:
			i2c_write_byte(0x6E);	//DIG3
			break;
	}

    //真值最后一位对应led,与数码管的真值取或,使2者同时显示
	if(on==1){
		i2c_write_byte(AIP_VAL[pos]|0x01);
	}else
	{
		i2c_write_byte(AIP_VAL[pos]&~0x01);
	}
	i2c_stop();
}

三、红外接收驱动

这部分应该时通用的:

复制代码
#include <STC8051U.h>
#include "main.h"
#include "aip650.h"

// 引脚定义
#define IR_INPUT P32      // 红外接收头连接到P3.2 (INT0)

// 全局变量
unsigned int ir_time;          // 存储时间测量值
unsigned char ir_state;         // 解码状态机状态
unsigned char ir_data[4];       // 存储4字节数据: [用户码, 用户反码, 按键码, 按键反码]
unsigned char ir_data_index;    // 数据位索引(0-31)
bit ir_data_ready;              // 数据接收完成标志
bit ir_repeat_flag;             // 重复码标志
unsigned char ir_address;       // 解码后的地址码
unsigned char ir_command;       // 解码后的命令码

// 定时器0初始化 - 用于测量时间
void Timer0_Init(void) {
    TMOD &= 0xF0;           // 清除定时器0设置
    TMOD |= 0x01;           // 模式1: 16位定时器
    TH0 = 0;
    TL0 = 0;
    TF0 = 0;
    TR0 = 0;                // 初始不启动
}

// 定时器0启动/停止控制
void Timer0_Run(bit flag) {
    TR0 = flag;
}

// 设置定时器计数值
void Timer0_SetCounter(unsigned int value) {
    TH0 = value >> 8;
    TL0 = value & 0xFF;
}

// 获取定时器当前计数值
unsigned int Timer0_GetCounter(void) {
    return (TH0 << 8) | TL0;
}




void PortInt_Init(void)
{
	P4INTE = 0x08;			//使能P4口中断
	P4IM0 = 0x00;			//设置P4口中断模式 (00:下降沿, 01:上升沿)
	P4IM1 = 0x00;			//设置P4口中断模式 (10:低电平, 11:高电平)
	P4WKUE = 0x00;			//设置P4口中断唤醒省电模式
}


// 红外接收初始化
void IR_Init(void) {
    Timer0_Init();
		PortInt_Init();
    ir_state = 0;
    ir_data_ready = 0;
    ir_repeat_flag = 0;
}

// 获取接收到的地址码
unsigned char IR_GetAddress(void) {
    return ir_address;
}

// 获取接收到的命令码
unsigned char IR_GetCommand(void) {
    return ir_command;
}

// 检查是否有新数据
bit IR_IsDataReady(void) {
    if(ir_data_ready) {
        ir_data_ready = 0;
        return 1;
    }
    return 0;
}

// 检查是否收到重复码
bit IR_IsRepeat(void) {
    if(ir_repeat_flag) {
        ir_repeat_flag = 0;
        return 1;
    }
    return 0;
}


void Port4_Isr(void) interrupt 41{
    // 24MHz主频下,定时器每计数1代表0.5μs
    // 因为12T模式下,一个机器周期 = 12/24MHz = 0.5μs
		P50=~P50;
    P4INTE = 0x00;          // 禁用所有P4口中断
		if(P4INTF & 0x08) {     // 判断是否是P4.3中断
        P4INTF &= ~0x08;    // 清除P4.3的中断标志位
     switch(ir_state) {
        case 0:  // 空闲状态,等待第一个下降沿
            Timer0_SetCounter(0);
            Timer0_Run(1);
            ir_state = 1;
            break;
            
        case 1:  // 等待引导码结束或检测重复码
            ir_time = Timer0_GetCounter();
            Timer0_SetCounter(0);
            
            // 检测引导码(9ms低+4.5ms高 = 13.5ms)
            // 13.5ms / 0.5μs = 27000
            if(ir_time > 26500 && ir_time < 27500) {
                ir_state = 2;           // 开始接收数据
                ir_data_index = 0;
            }
            // 检测重复码(9ms低+2.25ms高 = 11.25ms)
            // 11.25ms / 0.5μs = 22500
            else if(ir_time > 22000 && ir_time < 23000) {
                ir_repeat_flag = 1;     // 设置重复码标志
                Timer0_Run(0);
                ir_state = 0;
            }
            else {
                ir_state = 1;           // 无效信号,继续等待
            }
            break;
            
        case 2:  // 接收数据位
            ir_time = Timer0_GetCounter();
            Timer0_SetCounter(0);
            
            // 判断是"0"还是"1"
            // 逻辑0: 560μs低 + 560μs高 = 1120μs
            // 1120μs / 0.5μs = 2240
            if(ir_time > 2000 && ir_time < 2500) {
                // 收到"0",对应位清0
                ir_data[ir_data_index / 8] &= ~(0x01 << (ir_data_index % 8));
                ir_data_index++;
            }
            // 逻辑1: 560μs低 + 1680μs高 = 2240μs
            // 2240μs / 0.5μs = 4480
            else if(ir_time > 4200 && ir_time < 4800) {
                // 收到"1",对应位置1
                ir_data[ir_data_index / 8] |= (0x01 << (ir_data_index % 8));
                ir_data_index++;
            }
            else {
                // 时间异常,重新开始
                ir_data_index = 0;
                ir_state = 1;
            }
            
            // 接收完32位数据
            if(ir_data_index >= 32) {
                // 校验数据:第2字节应该是第1字节的反码
                // 第4字节应该是第3字节的反码
                if((ir_data[1] == (unsigned char)~ir_data[0]) && 
                   (ir_data[3] == (unsigned char)~ir_data[2])) {
                    // 数据有效
                    ir_address = ir_data[0];    // 地址码
                    ir_command = ir_data[2];     // 命令码
                    ir_data_ready = 1;
                }
                // 无论是否有效,都重置状态机
                Timer0_Run(0);
                ir_data_index = 0;
                ir_state = 0;
            }
            break;
    }
		 P4INTE = 0x08;          // 重新使能P4.3中断
}
}


void irrx_Task(void)
{
	
	unsigned char last_cmd = 0;
	if(IR_IsRepeat()) {
            // 按键被长按,可以执行连续动作
            // 例如:音量连续增加
           // P1_0 = ~P1_0;  // LED闪烁提示
        }
        
        // 检查是否有新的按键数据
        if(IR_IsDataReady()) {
            unsigned char addr = IR_GetAddress();
            unsigned char cmd = IR_GetCommand();
            P27=~P27;
            
            // 根据按键码执行不同操作
            if(cmd != last_cmd) {
                last_cmd = cmd;
							printf("cmd:%d\r\n",cmd);
							
							//显示键值
						if(((cmd&0xf0)>>4)<10)
						{
							aip650_display_char(2,((cmd&0xF0)>>4)+'0');	
						}else
						{
							aip650_display_char(2,((cmd&0xF0)>>4)-10+'a');
						}
						
						if(((cmd&0x0f)<10))
						{
							aip650_display_char(1,(cmd&0x0F)+'0');
						}else
						{
							aip650_display_char(1,(cmd&0x0F)-10+'a');
						}
						
						Buzzer_Control(1000, 500); 
						delay_ms(500);
						
						switch(cmd)
						{
							case 0x07:
								//aip650_led(3,0);
								aip650_led(1,1);
								break;
							case 0x03:
								//aip650_led(3,0);
								aip650_led(2,1);
								break;
							case 0x13:
								aip650_led(3,1);
								break;
							case 0x0b:
								P04=~P04;
								break;
							case 0x50:
								P05=~P05;
								break;
							case 0x0f:
								P06=~P06;
								break;
						}
                // 这里添加你的控制逻辑
                // 例如:控制LED、电机、显示等
               // P1 = ~cmd;  // 示例:在P1口显示按键码的反码
            }
        }

}

四、蜂鸣器驱动

这部分应该时通用的:

复制代码
#include <STC8051U.h>
#include "main.h"

// 蜂鸣器引脚定义(根据实际连接修改)
#define BUZZER_PIN P40 // 假设蜂鸣器连接到P1.0



// 全局变量
unsigned int buzzer_frequency = 0;  // 当前频率(Hz)
unsigned int buzzer_duration = 0;    // 持续时间(ms)
bit buzzer_enable = 0;               // 蜂鸣器使能标志
unsigned int buzzer_count = 0;        // 定时器计数(用于持续时间控制)
unsigned int buzzer_timer_reload = 0; // 定时器重载值



// Timer3初始化(用于蜂鸣器PWM输出)
void Timer3_Init_Buzzer(void) {
		T3R = 0;    //停止计数
		ET3 = 1;    //允许中断
		T3_CT = 0;  //定时
		T3CLKO = 0; //不输出时钟
		T3R = 1;    //开始运行
}

// 启动Timer3
void Timer3_Start(void) {
    T3R = 1;    //开始运行
}

// 停止Timer3
void Timer3_Stop(void) {
    T3R = 0;    //开始运行
}

// 设置Timer3重载值
void Timer3_SetReload(unsigned int value) {
		T3x12 = 1; 
    T3H = value >> 8;   // 高8位
    T3L = value & 0xFF; // 低8位
}

// 计算定时器重载值(用于蜂鸣器频率)
unsigned int CalculateBuzzerTimerValue(unsigned int freq) {
    unsigned long timer_count;
    
    if (freq == 0) return 0;
    
    // 计算公式:定时器计数 = 主频 / (12 * 频率 * 2)
    // 24MHz主频,12T模式,产生方波需要翻转两次
    timer_count = 24000000UL / (12UL * freq * 2);
    
    // 转换为定时器重载值(65536 - 计数值)
    return 65536 - timer_count;
}

// 蜂鸣器控制函数
// freq: 频率(Hz),0表示关闭
// duration: 持续时间(ms),0表示持续响
void Buzzer_Control(unsigned int freq, unsigned int duration) {
		unsigned int timer_value;
    if (freq == 0) {
        // 关闭蜂鸣器
        buzzer_enable = 0;
        Timer3_Stop();          // 停止Timer3
        BUZZER_PIN = 0;         // 拉低引脚(假设低电平不响)
        return;
    }
    
    // 计算并设置定时器重载值
    timer_value = CalculateBuzzerTimerValue(freq);
    if (timer_value == 0 || timer_value >= 65536) return;
    
    // 保存重载值和参数
    buzzer_timer_reload = timer_value;
    buzzer_frequency = freq;
    buzzer_duration = duration;
    buzzer_count = 0;
    
    // 设置定时器初值并启动
    Timer3_SetReload(timer_value);
    Timer3_Start();
    buzzer_enable = 1;
}

// Timer3中断服务程序
void Timer3_ISR(void) interrupt 19 {
    static unsigned int ms_counter = 0;
    static unsigned int half_period_count = 0;
    

    
    if (!buzzer_enable) return;
    
    // 重新加载定时器值(自动重载模式下,硬件会自动重载)
    // 但为了确保,也可以手动重载
    // Timer3_SetReload(buzzer_timer_reload);
    
    // 翻转蜂鸣器引脚,产生方波
    BUZZER_PIN = ~BUZZER_PIN;
    
    // 持续时间控制
    if (buzzer_duration > 0) {
        half_period_count++;
        
        // 每两次翻转(一个完整周期)计数一次
        if (half_period_count >= 2) {
            half_period_count = 0;
            
            // 计算经过了多少毫秒
            // 每毫秒需要的周期数 = 频率 / 1000
            ms_counter++;
            if (ms_counter >= 1) {  // 每1ms检查一次
                ms_counter = 0;
                buzzer_count++;
                
                // 达到指定持续时间
                if (buzzer_count >= buzzer_duration) {
                    buzzer_enable = 0;
                    Timer3_Stop();
                    BUZZER_PIN = 0;  // 关闭蜂鸣器
                    buzzer_count = 0;
                }
            }
        }
    }
}

// 音阶频率定义
#define DO  262
#define RE  294
#define MI  330
#define FA  349
#define SOL 392
#define LA  440
#define SI  494

// 音阶表
unsigned int tone_freq[] = {
    DO, RE, MI, FA, SOL, LA, SI
};

// 播放提示音函数
// tone: 音调编号 0-6 (Do, Re, Mi, Fa, Sol, La, Si)
// duration: 持续时间(ms)
void Buzzer_PlayTone(unsigned char tone, unsigned int duration) {
    if (tone < 7) {
        Buzzer_Control(tone_freq[tone], duration);
    }
}

// 蜂鸣器初始化
void Buzzer_Init(void) {
    BUZZER_PIN = 0;          // 初始关闭
    Timer3_Init_Buzzer();    // 初始化Timer3
}


// 播放一段旋律(示例)
void Buzzer_PlayMelody(void) {
    // 播放"小星星"片段
    Buzzer_PlayTone(0, 400);  // Do
    delay_ms(100);
    
    Buzzer_PlayTone(0, 400);  // Do
    delay_ms(100);
    
    Buzzer_PlayTone(4, 400);  // Sol
    delay_ms(100);
    
    Buzzer_PlayTone(4, 400);  // Sol
    delay_ms(100);
    
    Buzzer_PlayTone(5, 800);  // La
    delay_ms(200);
    
    Buzzer_PlayTone(5, 800);  // La
    delay_ms(200);
    
    Buzzer_PlayTone(4, 1600); // Sol
    delay_ms(400);
}

五、实际运行效果

相关推荐
HalvmånEver3 小时前
Linux:基于socket套接字写的简易英译汉翻译服务器
单片机·嵌入式硬件
jianqiang.xue3 小时前
ESP32-P4 看门狗复位全解析:HP_SYS_HP_WDT_RESET 故障排查实战
单片机·mcu·esp32·idf
somi73 小时前
51单片机-04-DS18B20 数字温度传感器
单片机·嵌入式硬件·51单片机
至为芯3 小时前
PY32F003至为芯支持32位ARM内核的低成本MCU微控制器
单片机·集成电路·芯片
zjxtxdy3 小时前
STM32开发板简介
stm32·单片机·嵌入式硬件
【 STM32开发 】3 小时前
【STM32 + CubeMX 教程】RTC 实时时钟 之 闹钟 -- F407篇
stm32·单片机·嵌入式硬件
weiyvyy3 小时前
接口开发的完整流程:从需求到验证
驱动开发·嵌入式硬件·硬件架构·硬件工程
MC_J4 小时前
STM32+FMC驱动W9825G6 SDRAM程序以及遇到的问题讲解
stm32·单片机
少年潜行4 小时前
【开源】STM32驱动BH1750(附开源代码)
单片机