- 打卡等级:偶尔看看I
- 打卡总天数:15
- 最近打卡:2026-05-30 11:13:22
已绑定手机
注册会员
- 积分
- 133
|
STC8H4K32TL单片机,原控制板用的蜂鸣器,现需要加一个简单的语音提示功能,改用ad116H桥芯片推喇叭,
在测试版上用的两个单独p通道测试正常后移到项目控制板上,因为项目控制板上没有p通道了,就用了两个独立的n通道修改模式后代替p通道。现在的问题是一样的参数蜂鸣器模式和音乐模式声音都变小了。试了很多方法都没有找到原因,难道n通道不能输出全量程幅度吗。
以下是两个p通道版本。
#include <intrins.h> // 包含_nop_()函数
#include "music-8bit-8K-wave.h"
#include <STC8H.H> // 包含STC8H系列单片机的寄存器定义,如P4、AUXR、TMOD等
typedef bit u2; //给逻辑值起个好听的名字,
typedef unsigned char u8; //给短整数起个好听的名字,
typedef unsigned int u16; //给中整数起个好听的名字,
typedef unsigned long u32; //给长整数起个好听的名字,
//喇叭pwma控制,上电状态在蜂鸣器模式,每次需要播放语音时切换到音乐模式,播放音频文件,结束后切换到蜂鸣器模式。
//蜂鸣器响或播放音乐时不能切换状态,且蜂鸣器优先。
// 全局变量
u32 Music_i = 0; //音频播放位置
u2 BeepMode = 0; // 0:蜂鸣器模式,1:音乐模式
u2 LastKeyState_P03 = 1; // P0.3上一次状态
u2 LastKeyState_P02 = 1; // P0.2上一次状态
u8 d1; // 上个值
u8 d2; // 下个值
u2 czzt = 0; // 插值一次状态,0当前位置正常采集点,1当前位置插值点,
u8 KeyDebounceCnt_P03 = 0; // P0.3消抖计数器
u8 KeyDebounceCnt_P02 = 0; // P0.2消抖计数器
// 声明外部数组
extern const unsigned char code Music1[];
extern const unsigned char code Music2[];
extern const unsigned char code Music3[];
extern const unsigned char code Music4[];
u8 *Music; // 当前播放哪首歌的指针
u16 MUSIC_LEN; // 当前歌曲长度
#define MUSIC_LEN1 sizeof(Music1); //取数组长度备用
#define MUSIC_LEN2 sizeof(Music2); //取数组长度备用
#define MUSIC_LEN3 sizeof(Music3); //取数组长度备用
#define MUSIC_LEN4 sizeof(Music4); //取数组长度备用
// ================= 延时函数(用于消抖) =================
void DelayMs(unsigned int ms)
{
unsigned int i, j;
for(i = 0; i < ms; i++) {
for(j = 0; j < 1200; j++) {
_nop_();
}
}
}
// ================= PWM 初始化(音乐模式) =================
void PWMA_Music(void)
{
P_SW2 |= 0x80;
// 1. 边沿对齐模式(向上计数)
PWMA_CR1 = 0x00; // 默认就是边沿对齐
// 2. 8位PWM(0~255)
PWMA_PSCR = 2; // 3分频,33MHz/3=11MHz
PWMA_ARR = 255; // 8位分辨率,PWM频率 = 11MHz / 151 ≈ 72.8kHz
// 3. 通道3 (P2.4) - 上桥
PWMA_CCMR3 = 0x68; // PWM模式1
PWMA_CCR3 = 0; // 占空比0
PWMA_CCER2 = 0x01; // 开启通道3,正极性
// 4. 通道1 (P1.0) - 下桥(用P通道,不用N通道)
PWMA_CCMR1 = 0x68; // PWM模式1
PWMA_CCR1 = 0; // 占空比0
PWMA_CCER1 = 0x01; // 开启通道1,正极性(不用N通道)
// 5. 关闭死区(因为不用互补,不需要死区)
PWMA_DTR = 0;
// 6. 引脚映射
PWMA_PS = (0x01 << 4) | (0x00 << 0); // PWM3P->P2.4, PWM1P->P1.0
// 7. 输出使能
PWMA_ENO = (1 << 4) | (1 << 0); // 开启 PWM3P (P2.4) 和 PWM1P (P1.0)
// 8. 启动
PWMA_BKR = 0x80;
PWMA_CR1 |= 0x01;
BeepMode = 1; //修改喇叭模式为音乐状态
}
// ================= PWM 初始化(蜂鸣器模式) =================
void PWMA_Beep(void)
{
P_SW2 |= 0x80;
// 1. 边沿对齐模式(向上计数)
PWMA_CR1 = 0x00; // 默认就是边沿对齐
// 2. 8位PWM(0~255)
PWMA_PSCR = 48; // // 48分频,33MHz/128=687kHz
PWMA_ARR = 255; // 2.69kHz PWM频率(687kHz/255≈2.69kHz)
PWMA_DTR = 0; //死区(因为下桥取反互补)
// 3. 通道3 (P2.4) - 上桥
PWMA_CCMR3 = 0x68; // PWM模式1
PWMA_CCR3 = 0; // 10%占空比(92×40%=36.8≈37,但我们用40),占空比大喇叭超电流,而且播放声音和凤鸣时音量不一样,
PWMA_CCER2 = 0x01; // 开启通道3,正极性
// 4. 通道1 (P1.0) - 下桥(取反输出,带死区时间)
PWMA_CCMR1 = 0x68; // PWM模式1
PWMA_CCR1 = 0; // 下桥占空比 =0
PWMA_CCER1 = 0x01; // 开启通道1,正极性(不用N通道)
// 5. 引脚映射
PWMA_PS = (0x01 << 4) | (0x00 << 0); // PWM3P->P2.4, PWM1P->P1.0
// 6. 输出使能
PWMA_ENO = (1 << 4) | (1 << 0); // 开启 PWM3P (P2.4) 和 PWM1P (P1.0)
// 7. 启动
PWMA_BKR = 0x80;
PWMA_CR1 |= 0x01;
BeepMode = 0; //修改喇叭模式为蜂鸣器状态
}
// ================= 定时器0 初始化 =================
void Timer0_Init(void)
{
AUXR |= 0x80; // 1T模式
TMOD &= 0xF0; // 16位自动重装
TL0 = 0x24; // 22kHz初值低8位 (64036 & 0xFF)
TH0 = 0xFA; // 22kHz初值高8位 (64036 >> 8)
TF0 = 0; // 清除定时器0溢出标志
ET0 = 0; // 打开定时器0中断,需要播放音乐才打开。
TR0 = 1; // 启动定时器0开始计数
}
// ================= 定时器0 中断服务函数 =================
void Timer0_ISR(void) interrupt 1
{
u8 pwm_val; // 8位PWM值
u16 sum; // 插值
u2 bridge_select; // 上下桥选择位
if (Music_i < MUSIC_LEN) // 使用宏定义 MUSIC_LEN
{
if (czzt == 1) //插值位置
{
czzt = 0; //插值状态
sum=d1+d2;
if(sum>=256) //加法后面跟随大于等于256时编译器优化时有99%可能用加法的溢出位判断,这样可以加速程序。
{
bridge_select = 1;// 提取上下桥标识位
} else {
bridge_select = 0;// 提取上下桥标识位
}
pwm_val = sum; //16位转8位,51单片机直接等于只取低8位,其他单片机可能要通过位与运算才能获得低8位。
}else { //正常播放
czzt = 1; //插值状态
bridge_select = (d1 >> 7) & 0x01; // 提取上下桥标识位(d1的bit3)
pwm_val = d1 <<1; // 提取音频数据,并放大2倍,右移放大并挤出标志位。
d2=d1; //存储本次数据
d1 = Music[Music_i++]; //准备下个数据
}
//修改通道占空比,输出数据
if (bridge_select) {
// 上半周:P通道工作,N通道静音
PWMA_CCR3 = pwm_val; // P通道输出PWM占空比
PWMA_CCR1 = 0; // N通道静音:全高
} else {
// 下半周:N通道工作,P通道静音
PWMA_CCR3 = 0; // P通道静音:全低
PWMA_CCR1 = 255 - pwm_val; // N通道输出PWM占空比
}
} else {
// 播放完毕,
Music_i=0; //播放位置置零,1.切换到蜂鸣器模式,在主循环里表示蜂鸣器模式时检测这个值,如果为零则播放完毕,切换回蜂鸣器模式,播放时要插值需提前读取一个值,起始位就是1了,
PWMA_CCR3 = 0; // P通道静音:全低
PWMA_CCR1 = 0; // p通道静音:全高
ET0 = 0; // 关闭中断,关闭定时器中断节省cpu开支。
}
}
// ================= 按钮初始化 =================
void Key_Init(void)
{
// P0.3和P0.2设置为上拉输入
P0M0 &= ~0x0C; // P0.3和P0.2准双向模式
P0M1 &= ~0x0C;
// 启用上拉电阻
P0PU |= 0x0C; // 启用P0.3和P0.2的上拉电阻
}
// ================= 读取按钮状态 =================
bit ReadKey_P03(void)
{
return (P0 & 0x08) ? 1 : 0; // P0.3为高电平返回1,低电平返回0
}
bit ReadKey_P02(void)
{
return (P0 & 0x04) ? 1 : 0; // P0.2为高电平返回1,低电平返回0
}
// ================= 播放哪一个声音 排水、脱水、洗涤、换洗 =================
void StartMusic(unsigned char i)
{
if(i == 1)
{
Music = Music1;
MUSIC_LEN = MUSIC_LEN1
}
// else if(i == 2)
// {
// Music = Music2;
// MUSIC_LEN = MUSIC_LEN2
// }
// else if(i == 3)
// {
// Music = Music3;
// MUSIC_LEN = MUSIC_LEN3
// }
// else if(i == 4)
// {
// Music = Music4;
// MUSIC_LEN = MUSIC_LEN4
// }
Music_i=1; //置第一个采样点位置
d1 = Music[Music_i]; //插值要预读第一个值。
czzt = 0; //初始化插值状态
PWMA_Music(); //直接切换到音乐模式
ET0 = 1; // 打开定时器0中断
}
// ================= 主函数 =================
void main(void)
{
EA = 1;
// 初始化
Key_Init();
PWMA_Beep(); // 默认蜂鸣器模式
Timer0_Init();
while(1) {
bit current_key_p03, current_key_p02;
if (1==BeepMode) //在音乐模式,检查状态播放完了立马切换回蜂鸣器模式。
{
if (0==Music_i)
{
PWMA_Beep(); // 切换回蜂鸣器模式
}
}
// 读取当前按钮状态
current_key_p03 = ReadKey_P03();
current_key_p02 = ReadKey_P02();
// ========== P0.3按钮处理(蜂鸣器) ==========
if (current_key_p03 != LastKeyState_P03) {
// 状态变化,开始消抖
KeyDebounceCnt_P03++;
if (KeyDebounceCnt_P03 >= 5) { // 消抖5次
KeyDebounceCnt_P03 = 0;
if (current_key_p03 == 0) { // 按下(低电平)
// 在蜂鸣器模式打开蜂鸣器,不在则忽略,
if (0==BeepMode) {
PWMA_CCR1 = 20; // 启动PWM蜂鸣器。
}
DelayMs(10); // 消抖延时
} else { // 松开(高电平)
// 在蜂鸣器模式打开蜂鸣器,不在则忽略,
if (0==BeepMode) {
PWMA_CCR1 = 0; // 停止PWM,// 关闭蜂鸣器
}
DelayMs(10); // 消抖延时
}
LastKeyState_P03 = current_key_p03;
}
} else {
KeyDebounceCnt_P03 = 0; // 状态稳定,重置计数器
}
// ========== P0.2按钮处理(播放音乐) ==========
if (current_key_p02 != LastKeyState_P02) {
// 状态变化,开始消抖
KeyDebounceCnt_P02++;
if (KeyDebounceCnt_P02 >= 5) { // 消抖5次
KeyDebounceCnt_P02 = 0;
if (current_key_p02 == 0) { // 按下(低电平)
// 切换到音乐模式并播放
StartMusic(1);
DelayMs(10); // 消抖延时
}
LastKeyState_P02 = current_key_p02;
}
} else {
KeyDebounceCnt_P02 = 0; // 状态稳定,重置计数器
}
// 短暂延时,避免CPU占用过高
_nop_();
_nop_();
_nop_();
}
}
|
|