主体程序及算法由AI生成,我加入了PWM音频输出及按键调节,LCD1602显示。DIY了实际电路,经实验输入夹杂噪音的音频信号,调节Q值(带宽同步变化)变化明显,但是调节中心频率变化不正常,不知道是不是8位单片机性能太低的结果,请有经验的老师指点一下。
/***************************************************
* 8位单片机数字音频带通滤波器 - 可调版本
* 采样率:8kHz (125us中断)
* 功能:实时修改中心频率(500Hz-3kHz)和带宽(Q值1-10)
* 控制:按键+LCD显示
***************************************************/
#include <STC8H.H>
#include <intrins.h>
// 类型定义
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
typedef signed int s16;
typedef signed long s32;
#define uchar unsigned char
#define uint unsigned int
typedef signed int q15; // Q15定点数格式
typedef signed long q31; // Q31定点数格式
//------------------------------------------
// 双二阶滤波器结构体
typedef struct {
// 延迟线
q15 x1, x2; // 输入延迟:x[n-1], x[n-2]
q15 y1, y2; // 输出延迟:y[n-1], y[n-2]
// 滤波器系数(Q15格式)
q15 b0, b1, b2; // 前向系数
q15 a1, a2; // 反馈系数(实际存储 -a1, -a2)
} BiquadFilter;
// 滤波器参数结构体
typedef struct {
float fc; // 中心频率 (Hz)
float q; // 品质因数
float fs; // 采样率 (Hz)
// 计算出的系数
float b0, b1, b2;
float a0, a1, a2;
} FilterParams;
//-------定义PWM输出引脚------------
#define PWM1_0 0X00 //P:P1.0,N:P1.1
#define PWM1_1 0X01 //P:P2.0,N:P2.1
#define PWM1_2 0X02 //P:P6.0,N:P6.1
#define PWM2_0 0X00 //P:P1.2/P5.4,N:P1.3
#define PWM2_1 0X04 //P:P2.2,N:P2.3
#define PWM2_2 0X08 //P:P6.2,N:P6.3
#define PWM3_0 0X00 //P:P1.4,N:P1.5
#define PWM3_1 0X10 //P:P2.4,N:P2.5
#define PWM3_2 0X20 //P:P6.4,N:P6.5
#define PWM4_0 0X00 //P:P1.6,N:P1.7
#define PWM4_1 0X40 //P:P2.6,N:P2.7
#define PWM4_2 0X80 //P:P6.6,N:P6.7
#define PWM4_3 0XC0 //P:P3.4,N:P3.3
#define ENO1P 0X01
#define ENO1N 0X02
#define ENO2P 0X04
#define ENO2N 0X08
#define ENO3P 0X10
#define ENO3N 0X20
#define ENO4P 0X40
#define ENO4N 0X80
/***************全局变量 ***************/
static BiquadFilter bandpass; // 带通滤波器
static FilterParams params; // 滤波器参数
static unsigned char need_update = 0; //0 系数更新标志
static q15 volume = 32767; //16384 音量系数 0.5 (范围0-32767)
unsigned char Table[]="0123456789";
unsigned char freq_num[4];// 频率
unsigned char q_num[2] ;// Q值
unsigned char bw_num[4];//带宽
u16 center_freq=1000;//默认1000HZ
u16 center_q=1;//默认Q值1
bit flag=1;
//-------------------------------------------
// 按键定义
sbit KEY_FREQ_UP = P3^0; // 频率增加
sbit KEY_FREQ_DOWN = P3^1; // 频率减少
sbit KEY_Q_UP = P3^2; // Q值增加
sbit KEY_Q_DOWN = P3^3; // Q值减少
sbit RS=P2^5; //定义rs端口
// RW 接地
sbit EN=P2^3; //定义en端口
sbit LCD_D4=P4^2;
sbit LCD_D5=P4^1;
sbit LCD_D6=P3^7;
sbit LCD_D7=P3^6;
//-------------函数声明------------------------
void gpio_init(void);
void Timer0_Init(void);
void ADC_Init(void);
u16 ADC_Read(u8 ch);
//----------------------
void audio_setup(void);
void Filter_ResetFloat(void);
float Filter_ProcessFloat(float input);
q15 Filter_Process(q15 input);
void Filter_ApplyUpdate(void);
void Filter_SetQ(float q_value);
void Filter_SetFrequency(float freq_hz);
void Filter_Init(float sample_rate);
static void Filter_ClearDelay(BiquadFilter *f);
static void Filter_UpdateCoefficients(BiquadFilter *f, FilterParams *p);
void Filter_CalculateCoefficients(FilterParams *p);
static q15 q15_add(q15 a, q15 b);
static q15 q15_mul(q15 a, q15 b);
float cosf(float x);
static float cosf_optimized_positive_range(float x);
float sinf(float x);
//----------------------
void KeyScan(void);
void DisplayStatus(void);
void pwm_init();
void pwm_out(u16 PWM1_Duty);
//--------------------------
void display_Bm(unsigned char x, unsigned char y);
void display_F(unsigned char x, unsigned char y);
void display_Q(unsigned char x, unsigned char y);
void LCD_en_write(void);
void LCD_by(uchar abc);
void LCD_set_xy( uchar x, uchar y );
void LCD_write_str(uchar X,uchar Y,uchar *s);
void LCD_init(void);
void delay_nus(uint n);
void delay_5us(void);
void del_ms(uint n);
void Delay1ms(void);
//--------------------------------------------------
float sinf(float x) {
// 快速角度归一化
const float two_pi = 6.28318530717958647692f;
const float inv_two_pi = 0.15915494309189533576f;
int sign;
float x2;
float result;
// 获取倍数
int n = (int)(x * inv_two_pi);
x = x - n * two_pi;
// 保持在 [-π, π] 范围内
if (x > 3.14159265358979323846f) x -= two_pi;
if (x < -3.14159265358979323846f) x += two_pi;
// 使用奇偶对称性减少计算范围到 [0, π/2]
sign = 1;
if (x < 0) {
x = -x;
sign = -1;
}
if (x > 1.57079632679489661923f) { // x > π/2
x = 3.14159265358979323846f - x;
}
// 泰勒级数计算,重新计算符号
x2 = x * x;
result = x * (1.0f - x2 / 6.0f * (1.0f - x2 / 20.0f * (1.0f - x2 / 42.0f)));
return sign * result;
}
// 内部辅助函数,只处理 [0, π/2] 范围
static float cosf_optimized_positive_range(float x) {
float x2 = x * x;
// 使用霍纳法则优化多项式计算
// cos(x) ≈ 1 - x2/2! + x?/4! - x?/6! + x?/8!
return 1.0f + x2 * (-0.5f + x2 * (0.041666666666666664f +
x2 * (-0.001388888888888889f + x2 * 0.0000248015873015873f)));
}
float cosf(float x) {
// 快速角度归一化
const float two_pi = 6.28318530717958647692f;
const float inv_two_pi = 0.15915494309189533576f;
// 获取倍数并归一化到 [-π, π]
int n = (int)(x * inv_two_pi);
x = x - n * two_pi;
if (x > 3.14159265358979323846f) x -= two_pi;
if (x < -3.14159265358979323846f) x += two_pi;
// 利用 cos(-x) = cos(x),将 x 转为正值
if (x < 0) x = -x;
// 利用 cos(π-x) = -cos(x) 减少计算范围
if (x > 1.57079632679489661923f) { // x > π/2
x = 3.14159265358979323846f - x;
return -cosf_optimized_positive_range(x);
}
return cosf_optimized_positive_range(x);
}
// Q15乘法:a * b,返回Q15格式(带饱和)
static q15 q15_mul(q15 a, q15 b) {
q31 result = (q31)a * (q31)b;
result = result >> 15;
// 饱和处理
if (result > 32767) result = 32767;
if (result < -32768) result = -32768;
return (q15)result;
}
// Q15加法(带饱和)
static q15 q15_add(q15 a, q15 b) {
q31 sum = (q31)a + (q31)b;
if (sum > 32767) return 32767;
if (sum < -32768) return -32768;
return (q15)sum;
}
// 计算双二阶带通滤波器系数
// 公式来源:RBJ音频均衡器滤波器设计
void Filter_CalculateCoefficients(FilterParams *p) {
float w0 = 2.0f * 3.14159265359f * p->fc / p->fs;
float cos_w0 = cosf(w0);
float sin_w0 = sinf(w0);
float alpha = sin_w0 / (2.0f * p->q);
// 带通滤波器系数
p->b0 = alpha;
p->b1 = 0.0f;
p->b2 = -alpha;
p->a0 = 1.0f + alpha;
p->a1 = -2.0f * cos_w0;
p->a2 = 1.0f - alpha;
// 归一化(除以a0)
p->b0 /= p->a0;
p->b1 /= p->a0;
p->b2 /= p->a0;
p->a1 /= p->a0;
p->a2 /= p->a0;
}
// 将浮点系数转换为Q15定点格式并更新滤波器
static void Filter_UpdateCoefficients(BiquadFilter *f, FilterParams *p) {
// 浮点转Q15(范围-1到1映射到-32768到32767)
f->b0 = (q15)(p->b0 * 32767.0f);
f->b1 = (q15)(p->b1 * 32767.0f);
f->b2 = (q15)(p->b2 * 32767.0f);
// 注意:差分方程使用 y[n] = b0*x[n] + ... - a1*y[n-1] - a2*y[n-2]
// 所以这里存储的是 -a1 和 -a2
f->a1 = (q15)((-p->a1) * 32767.0f);
f->a2 = (q15)((-p->a2) * 32767.0f);
}
// 清空滤波器延迟线(切换频率时防止爆音)
static void Filter_ClearDelay(BiquadFilter *f) {
f->x1 = 0;
f->x2 = 0;
f->y1 = 0;
f->y2 = 0;
}
// 初始化滤波器
void Filter_Init(float sample_rate) {
params.fs = sample_rate;
params.fc = 1000.0f; // 默认1kHz
params.q = 1.0f; // 默认Q=1
// 计算初始系数
Filter_CalculateCoefficients(¶ms);
Filter_UpdateCoefficients(&bandpass, ¶ms);
Filter_ClearDelay(&bandpass);
need_update = 0;
}
// 设置中心频率(Hz)
void Filter_SetFrequency(float freq_hz) {
if (freq_hz < 20.0f) freq_hz = 20.0f;
if (freq_hz > params.fs / 2.0f) freq_hz = params.fs / 2.0f;
if (params.fc != freq_hz) {
params.fc = freq_hz;
need_update = 1;
}
}
// 设置Q值
void Filter_SetQ(float q_value) {
if (q_value < 0.5f) q_value = 0.5f;
if (q_value > 20.0f) q_value = 20.0f;
if (params.q != q_value) {
params.q = q_value;
need_update = 1;
}
}
// 执行滤波器系数更新(主循环调用)
void Filter_ApplyUpdate(void) {
if (need_update) {
Filter_CalculateCoefficients(¶ms);
Filter_UpdateCoefficients(&bandpass, ¶ms);
Filter_ClearDelay(&bandpass); // 清空延迟防爆音
need_update = 0;
}
}
// 处理一个采样点(定点Q15版本)
q15 Filter_Process(q15 input) {
q15 output;
// 直接II型双二阶滤波器
// y[n] = b0*x[n] + b1*x[n-1] + b2*x[n-2] + a1*y[n-1] + a2*y[n-2]
output = q15_mul(bandpass.b0, input);
output = q15_add(output, q15_mul(bandpass.b1, bandpass.x1));
output = q15_add(output, q15_mul(bandpass.b2, bandpass.x2));
output = q15_add(output, q15_mul(bandpass.a1, bandpass.y1));
output = q15_add(output, q15_mul(bandpass.a2, bandpass.y2));
// 更新延迟线
bandpass.x2 = bandpass.x1;
bandpass.x1 = input;
bandpass.y2 = bandpass.y1;
bandpass.y1 = output;
return output;
}
// 浮点版本处理函数(精度高但速度慢)
float Filter_ProcessFloat(float input) {
static float x1 = 0, x2 = 0, y1 = 0, y2 = 0;
float output;
output = params.b0 * input + params.b1 * x1 + params.b2 * x2
- params.a1 * y1 - params.a2 * y2;
x2 = x1;
x1 = input;
y2 = y1;
y1 = output;
return output;
}
// 重置浮点版本状态
void Filter_ResetFloat(void) {
static float x1 = 0, x2 = 0, y1 = 0, y2 = 0;
x1 = x2 = y1 = y2 = 0;
}
//----------------------------------
// 音频处理循环(中断服务函数)
void audio_isr(void) interrupt 1 {
unsigned int adc_raw;
q15 input, output;
u16 temp;
// 读取ADC
adc_raw = ADC_Read(0);//端口P1^0
// ADC转Q15
input = ((q15)adc_raw - 2048) << 8;
// 滤波
output = Filter_Process(input);
// 应用音量控制
temp = ((q31)output * volume) >> 14;
output = (q15)temp;
// 输出
temp = (u16)output + 32768;
temp = temp >> 8;
pwm_out((u8)temp);
}
//----------------------------------
// 初始化
void audio_setup(void) {
Filter_Init(8000.0f); // 采样率8kHz
// Filter_SetFrequency(1000); // 中心频率1kHz
// Filter_SetQ(2.0f); // Q=2
}
//----------------------------------
void gpio_init(void){
P0M0 = 0x00; P0M1 = 0x00;
P1M0 = 0x00; P1M1 = 0x01;
P2M0 = 0x01; P2M1 = 0x00;
P3M0 = 0x00; P3M1 = 0x00;
P4M0 = 0x00; P4M1 = 0x00;
P5M0 = 0x00; P5M1 = 0x00;
}
//----------------频率------------------------
void display_F(unsigned char x, unsigned char y)
{
unsigned char i;
LCD_set_xy( x, y );
RS=1;
for(i=0;i<4;i++)
{
LCD_by(Table[freq_num[i]]);
}
}
//-----------------Q值----------------------------
void display_Q(unsigned char x, unsigned char y)
{
unsigned char i;
LCD_set_xy( x, y );
RS=1;
for(i=0;i<2;i++)
{
LCD_by(Table[q_num[i]]);
}
}
//------------------带宽-----------------------
void display_Bm(unsigned char x, unsigned char y)
{
unsigned char i;
LCD_set_xy( x, y );
RS=1;
for(i=0;i<4;i++)
{
LCD_by(Table[bw_num[i]]);
}
}
//---------------------------------------------
void LCD_en_write(void) //液晶使能
{
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
EN=1;
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
delay_5us();
EN=0;
}
//---------------------------------------
void LCD_by(uchar abc)//写字节
{
delay_nus(1000);
if(((abc<<0)&0x80)==0)
LCD_D7=0;
else LCD_D7=1;
if(((abc<<1)&0x80)==0)
LCD_D6=0;
else LCD_D6=1;
if(((abc<<2)&0x80)==0)
LCD_D5=0;
else LCD_D5=1;
if(((abc<<3)&0x80)==0)
LCD_D4=0;
else LCD_D4=1;
LCD_en_write();
if(((abc<<4)&0x80)==0)
LCD_D7=0;
else LCD_D7=1;
if(((abc<<5)&0x80)==0)
LCD_D6=0;
else LCD_D6=1;
if(((abc<<6)&0x80)==0)
LCD_D5=0;
else LCD_D5=1;
if(((abc<<7)&0x80)==0)
LCD_D4=0;
else LCD_D4=1;
LCD_en_write();
}
//----------------------------------------------
void LCD_set_xy( uchar x, uchar y )//写地址函数
{
uchar address;
if (y == 0) address = 0x80 + x;
else
address = 0xc0 + x;
RS=0;
LCD_by(address);
}
//---------------------------------------------
void LCD_write_str(uchar X,uchar Y,uchar *s)//写字符串
{
LCD_set_xy(X,Y);
RS=1;
while(*s)
{
LCD_by(*s);
s++;
}
}
//------------------------------------
void LCD_init(void) //液晶初始化
{
RS=0;
del_ms(500);
LCD_by(0x30);
del_ms(60);
LCD_by(0x30);
del_ms(10);
LCD_by(0x30);
del_ms(10);
LCD_by(0x02);
del_ms(10);
LCD_by(0x28);//4bit test显示模式设置(不检测忙信号)
del_ms(10);
LCD_by(0x08);// 显示关闭
del_ms(10);
LCD_by(0x01);// 显示清屏
del_ms(10);
LCD_by(0x06);// 显示光标不移动设置//移动0x04
del_ms(10);
LCD_by(0x0C);// 显示开及光标设置
del_ms(100);
}
//----------------------------------------------
void delay_nus(uint n)//N us延时函数
{
uint i=0;
for (i=0;i<n;i++){_nop_();_nop_();_nop_();}
}
//------------------------------
void delay_5us(void)
{
unsigned char data i;
_nop_();
i = 11;
while (--i);
}
//------------------------------
void Delay1ms(void)
{
unsigned char data i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
//------------------------------
void del_ms(uint n)//ms延时函数
{
uint j;
for(j=0;j<n;j++){Delay1ms();}
}
//------------------------------------
void pwm_init()
{
P_SW2 |=0X80;
PWMA_CCER1 = 0X00;
PWMA_CCER2 = 0X00;
PWMA_CCMR1 = 0X60;
PWMA_CCMR2 = 0X60;
PWMA_CCMR3 = 0X60;
PWMA_CCMR4 = 0X60;
PWMA_CCER1 = 0X55;
PWMA_CCER2 = 0X55;
// PWMA_ARRH = 0X02; //设置周期24000000hz/32000-1=749
// PWMA_ARRL = 0XED; //749/32KHZ
PWMA_ARRH = 0X04; //设置周期40000000hz/32000-1=1249
PWMA_ARRL = 0XE1; //1249/32KHZ
PWMA_ENO = 0X00;
PWMA_ENO |= ENO1P; //使能输出
// PWMA_ENO |= ENO2P; //使能输出
PWMA_PS = 0X00; //PWM通道输出引脚选择位
PWMA_PS |= PWM1_1; //选择PWM1_1通道/P2.0
// PWMA_PS |= PWM2_1; //选择PWM2_1通道/P2.2
PWMA_BKR = 0X80; //使能主输出
PWMA_CR1 |= 0X01; //开始计时
P_SW2 &= 0X7F;
}
//---------------------------------
void pwm_out(u16 PWM1_Duty)//端口P2^0
{
P_SW2 |=0X80;
PWMA_CCR1H = (u8)(PWM1_Duty >> 8);
PWMA_CCR1L = (u8)(PWM1_Duty);
P_SW2 &= 0X7F;
}
/******************************/
void ADC_Init(void) {
ADCCFG = ADCCFG | 0X20;
ADC_CONTR = 0X80;
}
u16 ADC_Read(u8 ch) {
ADC_RES = 0;
ADC_RESL = 0;
ADC_CONTR = (ADC_CONTR & 0XE0) | 0X40 | ch;
_nop_();
_nop_();
_nop_();
_nop_();
while((ADC_CONTR & 0X20) == 0);
ADC_CONTR &= ~0X20;
return (((unsigned int)ADC_RES<<8) | ADC_RESL);
}
/*************** 按键扫描 ***************/
void KeyScan(void) {
// 频率增加
if(KEY_FREQ_UP == 0) {
del_ms(50);
center_freq=center_freq+100;
if(center_freq>=3000)center_freq=3000;
while(KEY_FREQ_UP==0);
flag=1;
}
// 频率减少
else if(KEY_FREQ_DOWN == 0) {
del_ms(50);
center_freq=center_freq-100;
if(center_freq<=500)center_freq=500;
while(KEY_FREQ_DOWN==0);
flag=1;
}
// Q值增加
if(KEY_Q_UP == 0) {
del_ms(50);
center_q=center_q+1;
if(center_q>=10)center_q=10;
while(KEY_Q_UP==0);
flag=1;
}
// Q值减少
else if(KEY_Q_DOWN == 0) {
del_ms(50);
center_q=center_q-1;
if(center_q<=1)center_q=1;
while(KEY_Q_DOWN == 0);
flag=1;
}
}
/*************** 状态显示 ***************/
void DisplayStatus(void) {
u16 center_bm=0;
freq_num[0] = center_freq%10000/1000;
freq_num[1] = center_freq%1000/100;
freq_num[2] = center_freq%100/10;
freq_num[3] = center_freq%10;
q_num[0] = center_q%100/10;
q_num[1] = center_q%10;
center_bm = center_freq/center_q;//频率/Q=带宽
bw_num[0] = center_bm%10000/1000;
bw_num[1] = center_bm%1000/100;
bw_num[2] = center_bm%100/10;
bw_num[3] = center_bm%10;
if(flag==1)
{
Filter_SetFrequency(center_freq);// 中心频率
Filter_SetQ(center_q); // Q
Filter_ApplyUpdate();
// 更新频率显示
LCD_write_str(0,0,"Freq:");
display_F(7,0);
LCD_write_str(14,0,"Hz");
// 更新Q值显示
LCD_write_str(0,1,"Q:");
display_Q(2,1);
// 更新带宽显示
LCD_write_str(6,1,"Bw:");
display_Bm(9,1);
LCD_write_str(14,1,"Hz");
flag=0;
}
}
/************************************/
void Timer0_Init(void) {
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x78; //设置定时初始值125us/8khz@40mhz
TH0 = 0xEC; //设置定时初始值
// TL0 = 0x48; //设置定时初始值125us/8khz@24mhz
// TH0 = 0xF4; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
/*************** 主函数 ***************/
void main(void) {
gpio_init();
pwm_init();
ADC_Init();
Timer0_Init();
LCD_init();
del_ms(500);
audio_setup();
EA=1;
while(1) {
KeyScan();
DisplayStatus();
P00=~P00;
}
}
|