找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 6|回复: 0
收起左侧

实验8位单片机STC8H8K64U数字音频带通滤波器

[复制链接]
ID:1136941 发表于 2026-5-25 16:42 | 显示全部楼层 |阅读模式
主体程序及算法由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(&params);
    Filter_UpdateCoefficients(&bandpass, &params);
    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(&params);
        Filter_UpdateCoefficients(&bandpass, &params);
        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;
    }
}

回复

使用道具 举报

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

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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