C - Hertz to Seconds以及如何获得适当的延迟时间?

时间:2016-12-21 13:48:11

标签: c pic low-level low-level-io pic24

我正在玩PIC 24,目前我将hertz转换为秒有一些问题,然后将其用作延迟信号发送到压电片(蜂鸣器,变焦器,扬声器等)和我想让它发挥某些音符。

我想知道我是否正在进行从赫兹到秒的转换(在提供的代码中为m秒)以及我是否正在进行正确的信号处理。

以下是我感到困扰的代码:

我的hertz转换为int:

int16 note(int i)
{
   float time = (1.0/i);

   int fin = ((time/2) * 1000);

   return fin;
}

以下是我如何将信号发送到我正在使用的pic24:

void main()
{
InitMCU();

 output_high(PIN_D1);
 delay_ms(note(E));
 output_low(PIN_D1);
 delay_ms(200); 
 output_high(PIN_D1);
 delay_ms(note(E));
 output_low(PIN_D1);
 delay_ms(200);
}

以下是我如何定义笔记:

#define C 255 //do
#define D 227 //re
#define E 204 //mi
#define F 191
#define G 170
#define A 153
#define B 136
#define C2 127

3 个答案:

答案 0 :(得分:2)

你需要一个循环!

repeat for note length:
   on
   delay
   off
   delay

还要考虑离线计算每个音符的所需时段,并将其作为整数而不是以Hz为单位输入 - CPU不会在浮点计算中浪费时间

答案 1 :(得分:1)

中间A音符通常是440 Hz音调。如果您每秒打开和关闭压电扬声器440次,则连续切换之间的延迟为1/880秒,即1.13636363636 ms。这显示了两件事:

  • 你的计算已经过时了;和
  • 1毫秒的定时器分辨率对于此应用来说太粗糙了。

当然,你需要一个循环播放一段时间的音调。

答案 2 :(得分:1)

首先,你试图用方波来制作正弦波。因此,如果您使用计时器专门针对目标频率,那么总会听起来有点不对劲。做一个440Hz的方波,它不会发出声音"像一个440Hz的正弦波。也许物理学会将它完成,但是,我打赌的不是你想要的。

如果你有速度,你可以。从90年代开始做一位DAC的事情或者那时候。如果你可以使你的方波比扬声器可以物理移动更快,你可以说比一小时更多地撒上一些声音,然后用一个受控速率将扬声器推出一点,然后比一些扬声器更加零。扬声器成为物理低通滤波器。您可以使用微控制器中的PWM来帮助解决这个问题。但是你需要动态地改变它,这就是PIC,所以你可能会耗尽资源,然后才能编写很多表来获得干净的声音。

要做方波,你需要以一半的频率改变输出引脚。不要在运行时进行计算,然后在计算器上进行计算或让工具链进行计算。假设您正在以1Mhz运行处理器/定时器,并且您希望在引脚上使用440Hz。你需要的时间是1/440。 hz是每秒周期数,所以反转使得每个周期的秒数。 0.00227272(重复)每个周期或周期的秒数,所以你需要它高一半而低一半(反之亦然,无关紧要)所以 这意味着输出状态变化之间的0.00113636 ...如果你的计时器是1Mhz,每个周期是1 / 1M亿秒。或者一微秒。 0.00113636 ... 1136中有多少微秒。所以每个1136计时器都会改变你的状态,你读取计时器上的文档并让它倒数或向上计数或任何1136计数(通常这些是基于零的数字所以1135然后计算零,然后是中断或状态标志或其他)。您也可以轮询一个计数器,该计数器计入/从所有零到/来自全零并翻转,然后从现在减去并用计数的位数掩盖它,差值就是时间。 16位计数器(start-now)& 0xFFFF是差异,只要你想要的时间足够小于0xFFFF。肯定是1136。等到开始减去现在(如果它是向下计数器,或者现在如果向上计数器则开始分钟)。

预先计算您想要的每个频率的半个周期的计数时间。当你做出一个音调时,你必须以某种方式循环每个周期半个周期,关闭半个周期,开半个周期关闭一半。

根据发言人和您的频率,它应该分类工作。如果你想尝试一点事

https://en.wikipedia.org/wiki/1-bit_DAC

你可以从三角波开始,在1136微秒的1/4处执行66%的占空比,在1136微秒的1/2中占空比33%,最后1/4的占空比为66%。或者一次做1/4然后在一个工作周期做1/2,然后在另一个工作周期做1/2。您应该能够找到或写入低通滤波器,而不是您会知道扬声器属性是什么,但您可以了解如何在较高的波段内生成较慢的波。在三角形之后你可以尝试一个梯形。以某种速度上升,稍微上涨50%,然后下降,重复另一半时间。

出于实验目的,查找或预先计算覆盖整个时期的序列,您可以使用生成几百或几千行代码的代码,如果不完全确定,则可以是公平的X微秒可以通过执行正好Y个指令或正确混合Y指令(如

)来实现
on
on
off
on
off
on
on
off

然后最后一条指令跳到顶部,再次进行实验,但你可能会发现,如果你做得对,你会得到更清晰的声音,也许比方波更干净。

你可以添加一个简单的R / C滤波器,实际上是两个组件,将您的比特流转换为模拟波形,然后您可以为扬声器供电或放大然后输入扬声器,此处的功能是您可以在范围。

你可以采用廉价而简单的另一条路径是电阻梯,而不是一位输出基本上该周期中该位置的模拟值,并使用梯形电阻将其转换为模拟信号。

或者只是使用真正的dac。根据您提供dac的速度,预先计算一段时间内需要发送的值的数量,然后创建一个表并直接发送它们,直到完成该频率为止。

所以回到你的代码。你试图使用一些我猜毫秒库函数?所以,让我们说这就是你想要做的。

我们看到的440Hz是半个周期的0.00113636秒,因此在1毫秒关闭时将是1毫秒,你的代码应该这样做

for(i=0;i<nperiods;i++)
{
on
delay_ms(1)
off
delay_ms(1)
}

那里的任何其他延误只是让它错了......对于方波。对于其他人,我怀疑你会有硬编码延迟。

因此上面存在许多问题,首先关闭一毫秒延迟是为了减慢您正在尝试的需要微秒延迟的方法,并且您需要了解循环中的开销需要多长时间,我们的对于440hz,数学显示1136微秒,有些截断精度。但是执行延迟的代码,尤其是像这样的慢速mcu需要很多时钟周期,如果这是C代码而不是asm那么多,再加上打开和关闭gpio引脚的代码,你必须减去/调出那些。范围将有所帮助,如果您从1136开始并且示波器显示的是3000us而不是2272.7的时间段,那么您需要将其减去并重试。所以延迟772而不是1136.那种事情。

根据谷歌,中间C是261.6Hz,对或错让我们运行它。那是3823微秒。提供你的音符函数255,我假设它是定义的给出1.9我假设毫秒。这是正确的毫秒。它被截断为1毫秒或2000微秒,即500hz,这当然是关闭的。那个定义应该是261还是262而不是255呢?

嗯,所以你试图制作一个高脉冲然后是200ms的固定长度低脉冲?所以,如果你喂它注意E并假设代码没有时间运行。它会高2 ms然后低200,假设你重复1 / 201ms或4.97 ... Hz的1%占空比,钢琴频率

https://en.wikipedia.org/wiki/Piano_key_frequencies

不要在5hz显示音符。我确信它接近一些谐波,但非常低。

A为3ms高200低或频率为1 / 203ms或4.9hz

除了数学之外,在pic上(或没有fpu的任何地方)执行运行时浮点是非常昂贵的,单独的数学运算可能需要比整个周期更长的时间。绝对没有理由计算那个运行时。你可以轻松地用数学制作定义或使用你的计算器并用硬编码的手工计算数字制作定义。它仍然不适用于固定的低周期esp,它与你需要的数字相比非常大。假设代码立即运行,具有毫秒延迟。延迟1ms关闭延迟1ms是500hz。 on,延迟2,关闭延迟2,250hz,延迟3为166,延迟4为125,依此类推。假设代码立即运行并且使用毫秒延迟,那么你不会打很多真正的笔记。

使你想要的压力波基本上让扬声器从静止状态推出半个周期并从静止状态吸回半个周期,理想情况下是正弦方式,以便它慢慢地熄灭回来然后拉进去。从重置状态到外出只能确保工作,但你仍然需要正确的工作周期来接近方波。因此,了解你拥有的计时器,你可以轻松地手动编写一些循环来刻录时钟周期,因为它们是我记忆中的确定性。从处理器的频率开始,它是什么?一段笔记的处理周期是多少?与上面的440hz到1mhz数学相同。 0.0011363636乘以1百万(每半秒的秒数乘以秒秒,秒数取消,如果你的mcu以每秒2百万的时钟速率运行,那么你的每半周数学运算数就像数字一样),那么它是2百万次0.001136363636 ..

然后弄清楚如何打开它等待cpu时钟的数量然后关闭并等待cpu时钟的数量。把它喂进你的压电或其他,看看它听起来如何。

如果你有16位寄存器我打赌你不要假设1mhz时钟你会

load reg with some number
top
subtract reg,1
compare with zero
branch to top

当然是装配。假设减去一个时钟周期并比较每个,那么对于分支,可以说每个循环为4,因此1136/4 = 284.用预先计算的值284加载寄存器。

手工编写一些程序集

top:
gpio on
load reg,284
one:
sub reg,1
cmp reg,0
bne one
gpio off
load reg,284
two:
sub reg,1
cmp reg,0
bne two
jmp top
原油,但它会让你开始沿着这条路走下去。

如果你没有16位寄存器而是8位而是1mhz 1136 / 0x100 = 4余数112,如果这个处理器每个循环需要4个时钟就会非常好,正如我上面所幻想的那样。每个延迟都是

mov reg,0xFF
A:
sub reg,1
cmp reg,0
bne A
mov reg,28
B:
sub reg,1
cmp reg,0
bne B

毫无疑问,这里和其他地方有无数资源描述PIC系列成员的延迟循环。

你可以甩它,看到声音改变

#define DELX 300
volatile unsigned int x;
while(1)
{
    output_high(PIN_D1);
    for(x=0;x<DELX;x++) continue; 
    output_low(PIN_D1);
    for(x=0;x<DELX;x++) continue; 
}

并使用不同的数字进行定义。语气应该改变,质量可能不会比你现在的质量好或者好得多,但是如果听起来那就应该改变。你可能会碰到悬崖我认为它是一个8位处理器,所以数到200是WAYY不同然后计数到300,它不会是低一倍半的频率。可能它不是线性的,可能取决于编译器,但它可能是带有扭结的线性段在这里和那里。 100到200可能是线性的,300到400但可能不是200到300。

这可能是线性的

volatile unsigned int x;
unsigned int y,z;
for(z=0;z<1000;z++)
for(y=0;y<100;y++)
{
    output_high(PIN_D1);
    for(x=0;x<z;x++) continue; 
    output_low(PIN_D1);
    for(x=0;x<z;x++) continue; 
}