C - 赫兹到秒以及如何获得适当的延迟时间?

C - Hertz to Seconds and how to get the proper time for a delay?

我正在玩 PIC 24,目前我在将赫兹转换为秒然后将其用作发送到压电件(蜂鸣器、缩放器、扬声器等)的信号延迟时遇到了一些问题) 我想让它播放某些音符。

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

这是我遇到问题的代码:

我的赫兹到整数的转换:

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

你需要一个循环!

repeat for note length:
   on
   delay
   off
   delay

还可以考虑离线计算每个音符所需的周期,并将其输入为整数而不是赫兹 - CPU 不会在浮点计算中浪费时间

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

  • 你的计算有偏差;和
  • 一毫秒的计时器分辨率对于此应用来说太粗略了。

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

首先,您正在尝试使用方波来产生正弦波。所以如果你用定时器专门针对目标频率,听起来总是有点不对劲。制作一个 440Hz 方波,它不像 440Hz 正弦波那样 "sound"。也许物理会把它四舍五入,但我打赌不会像你想要的那么多。

如果你有速度,你可以。做 90 年代或任何时候的一位 DAC 事情。如果你能让你的方波比扬声器的物理移动速度更快,你可以说在一段时间内撒上比零更多的东西,比如以受控的速度将扬声器推出一点,然后在一段时间内让扬声器更多的是零。扬声器变成了物理低通滤波器。您或许可以使用微控制器中的 PWM 来帮助解决这个问题。但是您需要动态更改它,这是 PIC,因此您可能会 运行 资源不足,然后才能编写大量 table 以获得干净的声音。

要做方波,你需要改变一半频率的输出引脚。不要在 运行 时间进行计算,然后在您的计算器上进行计算或让工具链进行计算。假设您 运行 将 processor/timer 设置为 1Mhz,并且您希望引脚上的频率为 440Hz。您需要周期为 1/440。 hz 是每秒循环数,因此倒置为每个循环秒数。每个周期或周期 0.00227272(重复)秒,所以你需要一半高一半低(反之亦然,无所谓)所以 这意味着 0.00113636... 在输出状态变化之间。如果您的计时器是 1Mhz,即每个周期 1/1 百万秒。或者一微秒。 0.00113636 ... 1136 中有多少微秒。因此,每 1136 个计时器滴答作响,您就会更改状态,您阅读计时器上的文档并让它倒数或倒数或任何 1136 计数(通常这些是基于零的数字,所以 1135 然后计算零,然后是中断或状态标志或其他)。您也可以轮询一个计数器,该计数器计数 to/from 所有 1 to/from 所有零并翻转,从现在减去然后用正在计数的位数掩盖它,差异就是时间。使用 16 位计数器(现在开始)&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 供电的速度,预先计算你需要在一个周期内发送的值的数量,然后制作一个 table 并循环发送它们,直到你完成该频率。

所有这些都回到您的代码中。您正在尝试使用一些我猜是毫秒的库函数吗?所以可以说这就是你想做的。

我们看到的 440Hz 是半个周期的 0.00113636 秒,所以这将是 1 毫秒然后关闭 1 毫秒,您的代码应该这样做

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

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

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

Middle C根据google是261.6Hz,对错让运行用它。即 3823 微秒。给你的音符函数 255 喂食,我假设它的定义是给 1.9 我假设毫秒。就毫秒而言,这是正确的。得到 t运行cated 到 1 毫秒或 2000 微秒,即 500hz,这当然是偏离的。该定义应该是 261 或 262,而不是 255,对吗?

嗯,所以你想先发出一个高脉冲,然后再发出一个固定长度的 200 毫秒低脉冲?因此,如果您给它喂食注意 E 并假设代码没有时间到 运行。它会高 2 毫秒,然后低 200,假设你重复这是 1% 的占空比,频率为 1/201 毫秒或 4.97...赫兹,钢琴频率

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

不显示 5hz 的音符。我敢肯定它接近那里的一些谐波,但非常低。

A 将是 3ms 高 200 低或 1/203ms 或 4.9hz 的频率

除了数学之外,运行在图片(或任何没有 fpu 的地方)上进行时间浮点运算非常昂贵,仅数学一项就可能比整个周期花费更长的时间。绝对没有理由计算那个 运行 时间。你可以很容易地用数学来定义,或者使用你的计算器,然后用硬编码的手算数字来定义。它仍然无法使用固定的低周期,尤其是与您需要的数字相比非常大的低周期。假设代码 运行 立即延迟毫秒。开启延迟 1ms 关闭延迟 1ms 为 500hz。开,延时2,关延时2,250hz,延时3是166,延时4是125等等。假设代码 运行 立即出现,并且使用毫秒延迟,你不会打出很多真正的音符。

要产生您想要的压力波,基本上是让扬声器从静止状态推出半个周期,然后从静止状态吸回半个周期,理想情况下以正弦方式进行,这样它就会慢慢消失出来然后回来然后拉进然后出去。从重置状态变为仅退出状态肯定会起作用,但是您仍然需要正确的占空比才能接近方波。因此,了解您拥有的计时器,通过一张图片,您可以轻松地编写一些循环来燃烧时钟周期,因为它们是我记忆中的确定性。从处理器的频率开始,它是什么?您的音符的一个周期有多少个处理器周期?与上面的 440hz 到 1mhz 数学相同。 0.0011363636 次 100 万(每半周期秒数乘以每秒滴答数,秒抵消,你得到每半周期滴答数就像数字一样计算单位)如果你的 mcu 运行 以每秒 200 万个时钟滴答数计算,那么它是200 万次 0.001136363636...

然后弄清楚如何打开它等待 cpu 个时钟然后关闭并等待 cpu 个时钟。将其输入您的压电或其他设备,看看它的声音如何。

如果你有 16 位寄存器,我打赌你没有,但假设你有一个 1mhz 的时钟

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

当然是在汇编中。假设一个时钟周期用于减法并比较每个 b运行ch 的两个时钟周期可以说每个循环有四个,所以 1136/4 = 284。用预先计算的值 284.[=19 加载寄存器=]

手动编写一些汇编代码

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 与计数到 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; 
}