进程1:收到零件
功放DIY的零件今天终于收到了。先晒一下:
进程3:代码下载
代码下载使用RS232串口方式,手头刚好有1块RS232转换板可利用,连接方法见下图。转接板的5V供电直接取自功放板的+5V,所以那只R21电阻就不再是NC(未连接),而是要短接,5V供电才能输出。点击STC-ISP-15XX-V6.28H.EXE,在”打开程序“栏里输入版主提供的TEST.HEX,点击”下载/编程“,然后接通功放板电源。代码很顺利地下载到板上。
图1 下载器原理图
图2 连接图
图3 下载成功
进程4:按键音量控制
使用MDK4-C51开始写代码,第一阶段实现按键音量控制,为后续的红外线遥控打好基础:
#include "config.h"
#include "GPIO.h"
#include "delay.h"
static struct {
unsigned Key:1;
unsigned Mute:1
} Flag;
#define BIT0 0x01
#define BIT1 0x02
#define BIT2 0x04
#define BIT3 0x08
#define BIT4 0x10
#define BIT5 0x20
#define BIT6 0x40
#define BIT7 0x80
#define Get_CHECK ((P1 & BIT0)^BIT0)
#define Get_MUTE ((P1 & BIT1)^BIT1)
#define Get_Dn ((P1 & BIT2)^BIT2)
#define Get_Up ((P1 & BIT3)^BIT3)
#define MUTE_H P1 |= BIT4
#define MUTE_L P1 &= ~BIT4
#define CLOCK_H P3 |= BIT3
#define CLOCK_L P3 &=~BIT3
void GPIO_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_Pin_5|GPIO_Pin_4;
GPIO_InitStructure.Mode = GPIO_OUT_PP;
GPIO_Inilize(GPIO_P1,&GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_Pin_3|GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitStructure.Mode = GPIO_OUT_PP;
GPIO_Inilize(GPIO_P3,&GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_Pin_2;
GPIO_InitStructure.Mode = GPIO_PullUp;
GPIO_Inilize(GPIO_P3,&GPIO_InitStructure);
GPIO_InitStructure.Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.Mode = GPIO_PullUp;
GPIO_Inilize(GPIO_P1,&GPIO_InitStructure);
}
void Volume_Dn(void)
{ P1 &= ~BIT5;
P3 &=~BIT6;
CLOCK_H;
delay_ms(100);
CLOCK_L;
P1 |=BIT5;
}
void Volume_Up(void)
{ P1 &= ~BIT5;
P3 |=BIT6;
CLOCK_H;
delay_ms(100);
CLOCK_L;
P1 |=BIT5;
}
void Scan_Key(void)
{ if(Get_Dn||Get_Up||Get_MUTE)
{
delay_ms(20);
if(Get_Dn||Get_Up||Get_MUTE)
Flag.Key=1;
}
}
void Key_Server(void)
{
Flag.Key=0;
if(Get_Dn) Volume_Dn();
if(Get_Up) Volume_Up();
if(Get_MUTE)
{
Flag.Mute++;
if(Flag.Mute)
MUTE_H;
else
MUTE_L;
}
}
void main(void)
{ unsigned char i;
GPIO_config();
while(1)
{
Scan_Key();
if(Flag.Key)
Key_Server();
}
}
按键处理过程采用软件延时方式去除干扰,每按一次按键,红色LED闪亮一次。以上代码实现了按键控制和静音功能。
进程5 EEPROM存储数据
利用STC15W204S内部EEPROM存储音量控制数据。对EEPROM的读写并不复杂,STC推出的例程稍加修改即可。写入数据前要先进行删除才能写入。
EEPROM_SectorErase(IAP_Address);
EEPROM_write_n(IAP_Address,buffer,2);
读出数据:
EEPROM_read_n(IAP_Address,buffer,2);
底层代码:
#include "config.h"
#include "eeprom.h"
//========================================================================
// 函数: void ISP_Disable(void)
// 描述: 禁止访问ISP/IAP.
// 参数: non.
// 返回: non.
// 版本: V1.0, 2012-10-22
//========================================================================
void DisableEEPROM(void)
{
ISP_CONTR = 0; //禁止ISP/IAP操作
ISP_CMD = 0; //去除ISP/IAP命令
ISP_TRIG = 0; //防止ISP/IAP命令误触发
ISP_ADDRH = 0xff; //清0地址高字节
ISP_ADDRL = 0xff; //清0地址低字节,指向非EEPROM区,防止误操作
}
//========================================================================
// 函数: void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u16 number)
// 描述: 从指定EEPROM首地址读出n个字节放指定的缓冲.
// 参数: EE_address: 读出EEPROM的首地址.
// DataAddress: 读出数据放缓冲的首地址.
// number: 读出的字节长度.
// 返回: non.
// 版本: V1.0, 2012-10-22
//========================================================================
void EEPROM_read_n(u16 EE_address,u8 *DataAddress,u16 number)
{
EA = 0; //禁止中断
ISP_CONTR = (ISP_EN + ISP_WAIT_FREQUENCY); //设置等待时间,允许ISP/IAP操作,送一次就够
ISP_READ(); //送字节读命令,命令不需改变时,不需重新送命令
do
{
ISP_ADDRH = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
ISP_ADDRL = EE_address % 256; //送地址低字节
ISP_TRIG(); //先送5AH,再送A5H到ISP/IAP触发寄存器,每次都需要如此
//送完A5H后,ISP/IAP命令立即被触发启动
//CPU等待IAP完成后,才会继续执行程序。
_nop_();
*DataAddress = ISP_DATA; //读出的数据送往
EE_address++;
DataAddress++;
}while(--number);
DisableEEPROM();
EA = 1; //重新允许中断
}
//========================================================================
// 函数: void EEPROM_SectorErase(u16 EE_address)
// 描述: 把指定地址的EEPROM扇区擦除.
// 参数: EE_address: 要擦除的扇区EEPROM的地址.
// 返回: non.
// 版本: V1.0, 2013-5-10
//========================================================================
void EEPROM_SectorErase(u16 EE_address)
{
EA = 0; //禁止中断
//只有扇区擦除,没有字节擦除,512字节/扇区。
//扇区中任意一个字节地址都是扇区地址。
ISP_ADDRH = EE_address / 256; //送扇区地址高字节(地址需要改变时才需重新送地址)
ISP_ADDRL = EE_address % 256; //送扇区地址低字节
ISP_CONTR = (ISP_EN + ISP_WAIT_FREQUENCY); //设置等待时间,允许ISP/IAP操作,送一次就够
ISP_ERASE(); //送扇区擦除命令,命令不需改变时,不需重新送命令
ISP_TRIG();
_nop_();
DisableEEPROM();
EA = 1; //重新允许中断
}
//========================================================================
// 函数: void EEPROM_write_n(u16 EE_address,u8 *DataAddress,u16 number)
// 描述: 把缓冲的n个字节写入指定首地址的EEPROM.
// 参数: EE_address: 写入EEPROM的首地址.
// DataAddress: 写入源数据的缓冲的首地址.
// number: 写入的字节长度.
// 返回: non.
// 版本: V1.0, 2012-10-22
//========================================================================
void EEPROM_write_n(u16 EE_address,u8 *DataAddress,u16 number)
{
EA = 0; //禁止中断
ISP_CONTR = (ISP_EN + ISP_WAIT_FREQUENCY); //设置等待时间,允许ISP/IAP操作,送一次就够
ISP_WRITE(); //送字节写命令,命令不需改变时,不需重新送命令
do
{
ISP_ADDRH = EE_address / 256; //送地址高字节(地址需要改变时才需重新送地址)
ISP_ADDRL = EE_address % 256; //送地址低字节
ISP_DATA = *DataAddress; //送数据到ISP_DATA,只有数据改变时才需重新送
ISP_TRIG();
_nop_();
EE_address++;
DataAddress++;
}while(--number);
DisableEEPROM();
EA = 1; //重新允许中断
}
进程6:红外线遥控
红外线遥控确实是有一定难度的进程了。由于红外接收对延时精度要求很高,且DIY板没有显示外设,调试工作难度很大。
参考了STC官方例程,决定采用STC-ISP-15XX-V6.83.EXE自带的串口助手来调试。我的下载器是通过RS232电平转换器和PC机连接的,正好可以利用,硬件也无需做任何修改,就直接通过下载线返回串口助手。误打误着。
这部分代码是STC官方发布的,拿来就用,希望对你也有帮助:
#include "reg52.h"
extern void BitTime(void);
sfr INT_CLKO = 0x8F;
sbit P_TXD1 = P3^1;
void Tx1Send(unsigned char dat);
//模拟串口发送
void Tx1Send(unsigned char dat) //9600,N,8,1 发送一个字节
{
unsigned char i;
P_TXD1 = 0;
BitTime();
i = 8;
do
{
dat >>= 1;
P_TXD1 = CY;
BitTime();
}while(--i);
P_TXD1 = 1;
BitTime(); //stop bit
BitTime(); //stop bit
}
调用方法就是:Tx1Send( dat);
红外解码:
#include "Exti.h"
#define Get_INT0 ((P3 & 0x04)^0x04)
#define testbit(var,bit) ((var)&(1<<(bit)))
#define setbit(var,bit) ((var)|=(1<<(bit)))
#define clrbit(var,bit) ((var)&=~(1<<(bit)))
sbit IRIN = P3^2;
sbit led = P1^5;
unsigned char IRCOM[4]=0;
bit flag=0;
bit flagcom=0;
void delay014ms(unsigned char x);
void IR_init(void);
void delay014ms(unsigned char x)
{
unsigned char i;
while(x--)
{
for (i = 0; i<125; i++) //13
{;}
}
}
void IR_init(void)
{
EA=1;
EX0=1; //允许总中断中断,使能 INT0 外部中断
IT0=1; //触发方式为脉冲负边沿触发
IRIN=1; //I/O口初始化
}
void IR_CODE(void) interrupt 0
{
unsigned char j,k,N=0;
EX0 = 0;
delay014ms(15);
if (IRIN==1)
{ EX0 =1;
return;
}
while (!IRIN)
{delay014ms(1);}
for (j=0;j<4;j++)
{
for (k=0;k<8;k++)
{
while (IRIN)
{delay014ms(1);}
while (!IRIN)
{delay014ms(1);}
while (IRIN)
{
delay014ms(1);
N++;
if (N>=30)
{ EX0=1;
return;}
}
IRCOM[j]=IRCOM[j] >> 1;
if (N>=8) {IRCOM[j] = IRCOM[j] | 0x80;}
N=0;
}
}
if (IRCOM[2]!=~IRCOM[3])
{
IRCOM[4]=0;
EX0=1;
return;
}
flagcom = 1;
flag = !flag;//pro();
EX0=1;
return;
}
主程序调用红外接收数据:
if(flagcom!=0)
{ if( IRCOM[2]==0x01)Volume_Up();
if( IRCOM[2]==0x07)Volume_Dn();
if( IRCOM[2]==0x17)Volume_Mute();
Tx1Send(IRCOM[0]); //顺便把接收到的4Byte数据回显到串口助手
Tx1Send(IRCOM[1]);
Tx1Send(IRCOM[2]);
Tx1Send(IRCOM[3]);
flagcom=0;
}
我的遥控器见下图,音量加的代码是:0X01,音量减的代码是0X07,静音的代码是0X17。
EEPROM存储了音量和静音2个数据,如果当前状态是静音,关机后将保持静音状态。
点击下载DIY.rar!
进程7:试听 TPA3110 PK TDA2030
今天开始试听。为了比较效果,使用功放DIY板 PK TDA2030功放板,耳机采用发烧级HD650,片源选择CD唱片电影《泰坦尼克号》配乐之1:《NEVER AN ABSOLUTION》。
PK
HD650发烧耳机
先试听功放DIY板,0:51“时出现的低音鼓声浑厚有劲。接下来的中音感觉圆浑,1:16”后的女高声清脆透亮,不足之处是歌手吸气时的口音略带细沙似的爆裂声。这可能与数字功放在处理ADC和DAC时转换速率不足有关吧。另外在播放过程调整音量时有时会出现点爆裂声响,这可能与LM4811数字音量控制有关吧?
再听听TDA2030这款传统模拟功放的效果,它的低频力度感觉比TPA3110要差点,中音感觉平平,但是歌手吸气时的口音明显就没有那个爆裂声了。
小结:
TPA3110 数字功放效率高,虽然未装散热器,但温升很小,在低中频时力度不错,高音略显不足。
TDA2030模拟功放效率低,没有散热片根本无法工作,低中频力度稍差,但高频时的失真明显要小些。
水平有限,以上只是个人观点,不当之处请指正。
视频中的声音只能是手机录制来自音箱的声音效果,与HD650耳机的听音效果是无法相比的。
有奖活动 | |
---|---|
【有奖活动】分享技术经验,兑换京东卡 | |
话不多说,快进群! | |
请大声喊出:我要开发板! | |
【有奖活动】EEPW网站征稿正在进行时,欢迎踊跃投稿啦 | |
奖!发布技术笔记,技术评测贴换取您心仪的礼品 | |
打赏了!打赏了!打赏了! |
打赏帖 | |
---|---|
vscode+cmake搭建雅特力AT32L021开发环境被打赏30分 | |
【换取逻辑分析仪】自制底板并驱动ArduinoNanoRP2040ConnectLCD扩展板被打赏47分 | |
【分享评测,赢取加热台】RISC-V GCC 内嵌汇编使用被打赏38分 | |
【换取逻辑分析仪】-基于ADI单片机MAX78000的简易MP3音乐播放器被打赏48分 | |
我想要一部加热台+树莓派PICO驱动AHT10被打赏38分 | |
【换取逻辑分析仪】-硬件SPI驱动OLED屏幕被打赏36分 | |
换逻辑分析仪+上下拉与多路选择器被打赏29分 | |
Let'sdo第3期任务合集被打赏50分 | |
换逻辑分析仪+Verilog三态门被打赏27分 | |
换逻辑分析仪+Verilog多输出门被打赏24分 |