该项目使用基于STM32G031的测试测量训练平台,设计实现音频模拟放大电路特性分析
一. 项目需求
音频模拟放大电路特性分析
- 通过芯片的PWM + 板上LPF电路生成频率在DC到20KHz的扫频信号
- 将该信号通过Test端口连接到测试电路的输入端
- STM32G031采集运算放大器U4(LMV358)第7脚输出的信号,对其进行量化处理
- 在OLED上绘制该电路的幅频、相频特性图
二. 硬件介绍
本次基于STM32G031的口袋仪器训练平台,采用128*128 OLED显示,2个通道的模拟输入 + 一个通道的Micphone语音输入,并有一路信号输出。
STM32G031G8微控制器基于工作频率可达64 MHz的高性能Arm® Cortex®-M0+ 32位RISC内核。提供了多个标准通信接口(2个I2C、2个SPI/1个I2S和2个USART)、1个多达19通道的12位ADC (2.5 Msps),集成了DMA、丰富的系统功能、增强型I/O与外设。
三. 设计思路
- 通过定时器产生PWM信号,再通过另一个定时器在中断中控制pwm占空比 + 板上LPF电路获得正弦扫频信号
- 使用两个ADC通道分别采集放大电路输出的信号和原始信号,进行频率和相位差的测量
- 在OLED上绘制该电路的幅频、相频特性图
四. 软件流程及关键代码
本项目使用STM32CubeMX+Clion环境开发
4.1 SPWM生成正弦波
根据理论,高频pwm波经过LPF滤波后剩余一个根据占空比变化的直流分量,周期性改变其占空比就能得到不同频率的近似正弦波。这里我们采用定时器三生成PWM波,定时器一产生中断,在中断中改变PWM的占空比。占空比存储在初始化时生成的sin_table中,通过跳跃读取改变频率。
#define N 256
#define ARR_MAX 63
uint16_t sin_table[N];
// PWM频率=500kHz(保持固定)
TIM3->PSC = 0;
TIM3->ARR = ARR_MAX;
TIM3->CCR3 = (ARR_MAX+1)/2; // 初始占空比50%
// 中断频率=400kHz
TIM1->PSC = 32-1;
TIM1->ARR = 5-1;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim==&htim1) {
phase_acc += delta_theta; // 相位累加
indexx = phase_acc & (N-1);
TIM3->CCR3 = sin_table[indexx]; // 更新占空比
}
}
void set_SPWM_freq(uint16_t freq_khz) {
delta_theta = freq_khz; // 直接设置相位步进值
}
4.2 ADC采集
在ADC采集中,使用两个通道交替采集,并使用dma将数据存入结果数组,使用定时器TIM2控制采集频率
ADC设置中Continuous Conversion Mode和DMA Continuous Requests 都设为disabled,否则会导致一些问题。Number Of Conversion 设为2后即可启用Scan Conversion Mode
void cycle_adc(void) {
for(int i=1; i<=20;i++) {//扫频
float min_in=4096;
float max_in=0;
float min_out=4096;
float max_out=0;
uint32_t cross_in=0;
uint32_t cross_out=0;
uint32_t GATE=2048;
set_SPWM_freq(i);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
HAL_ADC_Start_DMA(&hadc1, adc_buffer, ADC_LENGTH*2);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
//数据处理
for (int j=1; j<ADC_LENGTH*2-2; j=j+2) {
if (adc_buffer[j]>max_in) {max_in=adc_buffer[j];}
if (adc_buffer[j]<min_in) {min_in=adc_buffer[j];}
if (adc_buffer[j]<=GATE&&adc_buffer[j+2]>=GATE&&cross_in==0) {
cross_in=j;
}
}
for (int j=0; j<ADC_LENGTH*2-2; j=j+2) {
if (adc_buffer[j]>max_out) {max_out=adc_buffer[j];}
if (adc_buffer[j]<min_out) {min_out=adc_buffer[j];}
if (adc_buffer[j]<=GATE&&adc_buffer[j+2]>=GATE&&cross_out==0) {
cross_out=j;
}
}
if (gain[i]==0) {
gain[i]=(max_out-min_out)/(max_in-min_in);
}
else {
gain[i]=((max_out-min_out)/(max_in-min_in)+gain[i])/2;
}
if (theta[i]==0) {
theta[i]=((float)cross_out-(float)cross_in)*i*360/80;
while (theta[i]>180) {theta[i]=theta[i]-360;}
while (theta[i]<-180) {theta[i]=theta[i]+360;}
//if (theta[i]<0) {theta[i]=-theta[i];}
}
else {
for(int j=0; j<=20;j++) {
theta_temp[j]=theta[j];
theta[j]=((float)cross_out-(float)cross_in)*j*360/80;
while (theta[j]>180) {theta[j]=theta[j]-360;}
while (theta[j]<-180) {theta[j]=theta[j]+360;}
//if (theta[j]<0) {theta[j]=-theta[j];}
theta[j]=(theta[j]+theta_temp[j])/2;
}
cycle_done=1;
}
五. 功能展示
可在OLED上显示幅频特性图,相频特性图,原始数据。使用板上的两个按键实现翻页和重新启动测量功能
六. 遇到的问题
本次使用的平台上碰到了一系列问题,大多是由性能问题引起的,这时需要寻找一些替代方法
1. 在中断中索引SPWM表时中本想使用取模运算indexx = phase_acc % N,结果发现中断时间过长导致跑飞。替换为按位与操作indexx = phase_acc & (N-1)后大大缩短了计算时间,此时N必须是2的幂
2. 由于ram不足,并且该MCU没有FPU,在处理数据时没有使用FFT,而是使用检测峰峰值实现增益计算,过零法实现相位差检测,精度较低。通过多次测量提高精度。
3. 由于flash不足无法使用stdio中的sprintf函数,Clion环境下也无法使用Microlib重定向printf,参考了https://www.eetree.cn/project/903中的浮点数转字符串的实现来解决该问题。
七. 感想与体会
本次项目中复习了信号生成,测量,处理的知识。积累了STM32外设使用和有限资源下开发的经验。未来期望再次参加活动,了解并学习更多的知识,掌握更多的技能,开拓电子芯片在生活中运用的视野。