tone (), notone ()

Эниф
Пт 14 августа 2015 г. 13:26
Когда я пытаюсь перенести мою маленькую программу «Blink Blink Sound Booster» в Maple Mini, я понял, что нет Tone () и notone () Функции, доступные еще в библиотеке STM32 -Arduino - или, по крайней мере, я не смог их найти...

Таким образом, вот моя первая попытка реализовать Arduino, совместимые с Tone () и notone () функции. Будьте осторожны, что это моя первая попытка справиться с таймерами STM32, поэтому код все еще может быть далеко не оптимальным. Тем не менее, это, кажется, работает в моей программе, и он также хорошо проверялся с Arduino Tonemelody.INO Пример:
/////////////////////////////////////////////////////////////////////// // // tone(pin,frequency[,duration]) generate a tone on a given pin // // noTone(pin) switch of the tone on the pin // /////////////////////////////////////////////////////////////////////// #include "Arduino.h" #include #ifndef TONE_TIMER #define TONE_TIMER 2 #endif HardwareTimer tone_timer(TONE_TIMER); bool tone_state = true; // last pin state for toggling short tone_pin = -1; // pin for outputting sound short tone_freq = 444; // tone frequency (0=pause) unsigned tone_micros = 500000/444; // tone have wave time in usec int tone_counts = 0; // tone duration in units of half waves // timer hander for tone with no duration specified, // will keep going until noTone() is called void tone_handler_1(void) { tone_state = !tone_state; digitalWrite(tone_pin,tone_state); } // timer hander for tone with a specified duration, // will stop automatically when duration time is up. void tone_handler_2(void) { // check duration if(tone_freq>0){ tone_state = !tone_state; digitalWrite(tone_pin,tone_state); } if(!--tone_counts){ tone_timer.pause(); pinMode(tone_pin, INPUT); } } // play a tone on given pin with given frequency and optional duration in msec void tone(uint8_t pin, unsigned short freq, unsigned duration = 0) { tone_pin = pin; tone_freq = freq; tone_micros = 500000/(freq>0?freq:1000); tone_counts = 0; tone_timer.pause(); if(freq >= 0){ if(duration > 0)tone_counts = ((long)duration)*1000/tone_micros; pinMode(tone_pin, OUTPUT); // set timer to half period in microseconds tone_timer.setPeriod(tone_micros); // Set up an interrupt on channel 1 tone_timer.setChannel1Mode(TIMER_OUTPUT_COMPARE); tone_timer.setCompare(TIMER_CH1, 1); // Interrupt 1 count after each update tone_timer.attachCompare1Interrupt(tone_counts?tone_handler_2:tone_handler_1); // Refresh the tone timer tone_timer.refresh(); // Start the timer counting tone_timer.resume(); } else pinMode(tone_pin, INPUT); } // disable tone on specified pin, if any void noTone(uint8_t pin){ tone(pin,-1); }

Rogerclark
Пт 14 августа 2015 г., 20:57
@enif

Спасибо

После того, как мы узнаем, что это стабильно, я буду интегрировать его в ядро.

victor_pv
Сб 15 августа 2015 2:10
Enif, половина контактов в Maple Mini может быть установлена ​​в качестве вывода ШИМ в один из таймеров. Некоторые не могут, хотя и предпочли бы понадобиться реализация программного обеспечения, чтобы получить тон на них. Но для тех, которые могут быть установлены как выход канала таймера, их может быть проще, чтобы они напрямую генерировали частоту напрямую.

Посмотрите на документацию по таймеру аппаратного обеспечения Leaflabs для этого.

Если вы хотите найти, какое устройство таймера и канал таймера соответствует определенному выводу, вы можете использовать PIN_MAP, например:

Для устройства таймера:
timer_dev * mytimer = pin_map [pin].timer_device;

И для номера канала:
uint8 mychannel = pin_map [pin].timer_channel;

Затем вы можете напрямую манипулировать регистрами, или вы можете создать объект Hardwaretimer, соответствующий этому timer_device, с чем -то вроде:
uint8 timer_num = (mytimer - timer1) + 1;
Hardwaretimer my_timer_object (timer_num);

Таким образом, вы устанавливаете вывод в виде вывода ШИМ, а затем манипулируйте настройками таймера и timer_channel (или объекта аппаратного таймера), соответствующих этому таймеру, чтобы настроить частоту, которую вы хотите.
Я считаю, что документация Leaflabs может иметь какой -то пример создания определенной частоты на PIN.

Для любого пинга, для которого PIN_MAP возвращает NULL для Timer_Device или Timer_Channel, означает, что с ним не связан аппаратного таймера.
Вы можете полагаться на использование, чтобы проверить это или, возможно, проверить это в своем коде.

Эниф
Сб 15 августа 2015 г., 6:42
Большое спасибо, Виктор. Я смутно осознавал связь между таймерами и булавками ШИМ, но это помогает иметь фактическое кодирование, как получить доступ к соответствующим таймерам и каналам из программы.

Моя реализация действительно просто быстрый+грязный подход, по крайней мере, получить мой нано -эскиз, работая на Maple Mini. Но я думал, что все еще может быть полезно опубликовать его под «кодовыми снарядами», так как это также может помочь другим, пока не станет официальной версией Tone ()/Notone ()...

Я уверен, что генерирование тона непосредственно с помощью таймера/канала, связанного с данным выводом, будет более эффективной. Тем не менее, я боюсь, что проблема останется, что даже в этом случае использование Tone () все равно нарушит выход ШИМ на других 3 контактах, разделяя один и тот же таймер. Так что мое беспокойство меньше получаю наиболее эффективный код, а скорее способен минимизировать помехи, которые он вызывает с другими видами использования таймеров. Таким образом, моей идеей было, по крайней мере, иметь некоторую возможность использовать альтернативный таймер (с #define tone_timer), если таймер по умолчанию нарушает штучки PWM, необходимые для программы.

Как будет реализовать Tone () На данном таймере/канале без ущерба для ШИН на других булавках, связанных с этим таймером?

victor_pv
Сб 15 августа 2015 г. 13:01
Эниф написал:Большое спасибо, Виктор. Я смутно осознавал связь между таймерами и булавками ШИМ, но это помогает иметь фактическое кодирование, как получить доступ к соответствующим таймерам и каналам из программы.

Моя реализация действительно просто быстрый+грязный подход, по крайней мере, получить мой нано -эскиз, работая на Maple Mini. Но я думал, что все еще может быть полезно опубликовать его под «кодовыми снарядами», так как это также может помочь другим, пока не станет официальной версией Tone ()/Notone ()...

Я уверен, что генерирование тона непосредственно с помощью таймера/канала, связанного с данным выводом, будет более эффективной. Тем не менее, я боюсь, что проблема останется, что даже в этом случае использование Tone () все равно нарушит выход ШИМ на других 3 контактах, разделяя один и тот же таймер. Так что мое беспокойство меньше получаю наиболее эффективный код, а скорее способен минимизировать помехи, которые он вызывает с другими видами использования таймеров. Таким образом, моей идеей было, по крайней мере, иметь некоторую возможность использовать альтернативный таймер (с #define tone_timer), если таймер по умолчанию нарушает штучки PWM, необходимые для программы.

Как будет реализовать Tone () На данном таймере/канале без ущерба для ШИН на других булавках, связанных с этим таймером?

Rogerclark
Сб 15 августа 2015 г. 9:10 вечера
Афик

Порты имеют 4 регистра

Регулярный режим, чтобы установить режим PIN
ODR, реестр выходных данных (только более 16 бит)
IDR, регистр входных данных (опять же, только более низкие 16 бит)
BSSR, который может устанавливать и сбросить отдельные биты (его разделение на верхние и нижние 16 битов. Я не могу вспомнить, что в макушке головы, что 16 бит является установленным шаблоном, а какой 16 бит - сброс (низкий) шаблон. Но использование BSSR - это самый быстрый способ установить или сбросить отдельные биты, так как он не требует от кода для чтения существующего шаблона бита перед вами или в (или и в) новой шаблона. Следовательно, это примерно в 3 раза быстрее.
Поэтому я бы порекомендовал, если вы делаете генератор тона на основе ISR, просто имейте одну переменную с шаблоном BSSR, чтобы установить бит, и другой VAR, который хранит шаблон сброса, и установите их, когда тон запускается, и используйте их в вашем ISR, чтобы установить или сбросить рассматриваемый бит.

victor_pv
Солнце 16 августа 2015 2:16
Эниф написал:... Так что мое беспокойство меньше получаю наиболее эффективный код, а скорее способен минимизировать помехи, которые он вызывает с другими видами использования таймеров. Таким образом, моей идеей было, по крайней мере, иметь некоторую возможность использовать альтернативный таймер (с #define tone_timer), если таймер по умолчанию нарушает штучки PWM, необходимые для программы.

Как будет реализовать Tone () На данном таймере/канале без ущерба для ШИН на других булавках, связанных с этим таймером?

Эниф
Солнце 16 августа 2015 г., 19:15
Спасибо за ваш отзыв и вклад, Виктор и Роджер!

Я больше подумал об этом и в итоге получила другую реализацию, которая использует только один канал одного таймера и не влияет на работу ШИМ -пин, разделяющих один и тот же таймер. Идея состоит в том, чтобы сохранить сам таймер неизменным, я.эн. Не изменяя свой период, но оставляя его свободным, работая в 16 -битном реестре счетов (что использует PWM). Частота генерируется путем изменения реестра сравнения канала на лету внутри обработчика прерываний, что -то вроде «прыгания» от полуволны, чтобы вдвое снизить волну. Это становится немного более сложным, когда частота составляет менее 550 Гц (= 36000000/65536), так как тогда более одного прерывания должно пройти до того, как вывод будет переключен, поэтому период одной половины волны делится в июне менее 65536// 36000000 с.

Используя информацию, представленную Victor, текущий код будет использовать соответствующий таймер/канал ШИМ для штифтов PWM и таймер/канал по умолчанию для других выводов. Этот по умолчанию теперь установлено на Timer4/Channel4, так как этот не используется каким -либо шпином ШИМ. Однако,
Я вообще не знаю, используется ли этот канал для чего -то другого (вы знаете??). Если это не так, мы могли бы так же хорошо использовать Timer4/Channel4 для всех контактов... В любом случае, я также добавил функцию SettoneTimerChannel (таймер, канал) в необязательно принудительное с помощью конкретного комбинации таймера/канала.

Я также внедрил запись BSRR Register, как предложено Роджером. Если определяется макропоспорт use_bsrr, используется BSRR, в противном случае используется DigitalWrite () - который может быть полезен для тестирования и сравнительного анализа.

/////////////////////////////////////////////////////////////////////// // // tone(pin,frequency[,duration]) generate a tone on a given pin // // noTone(pin) switch off the tone on the pin // // setToneTimerChannel(timer,channel) force use of given timer/channel // /////////////////////////////////////////////////////////////////////// #include "Arduino.h" #include // define default timer and channel #ifndef TONE_TIMER #define TONE_TIMER 4 #endif #ifndef TONE_CHANNEL #define TONE_CHANNEL 4 #endif #define PinTimer(pin) (PIN_MAP[pin].timer_device->clk_id-RCC_TIMER1+1) #define PinChannel(pin) (PIN_MAP[pin].timer_channel) // if USE_PIN_TIMER is set, the PWM timer/channel is used for PWM pins #define USE_PIN_TIMER // if USE_BSRR is set the tone pin will be written via the fast BSRR register // instead of using the slow digitalWrite() function in the interrupt handler #define USE_BSRR // construct static timer array ( HardwareTimer TTimer1(1), TTimer2(2), TTimer3(3), TTimer4(4); #ifdef STM32_HIGH_DENSITY HardwareTimer TTimer5(5), TTimer6(6), TTimer7(7), TTimer8(8); #endif HardwareTimer *TTimer[4] { &TTimer1,&TTimer2,&TTimer3,&TTimer4 #ifdef STM32_HIGH_DENSITY ,&TTimer5,&TTimer6,&TTimer7,&TTimer8 #endif }; uint8_t tone_force_channel = 0; // forced timer channel uint8_t tone_force_ntimer = 0; // forced timer HardwareTimer *tone_timer = TTimer[TONE_TIMER]; // timer used to generate frequency uint8_t tone_channel = TONE_CHANNEL; // timer channel used to generate frequency uint8_t tone_ntimer = TONE_TIMER; // timer used to generate frequency bool tone_state = true; // last pin state for toggling short tone_pin = -1; // pin for outputting sound short tone_freq = 444; // tone frequency (0=pause) uint32_t tone_nhw = 0; // tone duration in number of half waves uint16_t tone_tcount = 0; // time between handler calls in 1/36 usec uint16_t tone_ncount = 0; // handler call between toggling uint16_t tone_n = 0; // remaining handler calls before toggling uint32_t tone_next = 0; // counter value of next interrupt #ifdef USE_BSRR volatile uint32_t *tone_bsrr; // BSRR set register (lower 16 bits) uint32_t tone_smask=0; // BSRR set bitmask uint32_t tone_rmask=0; // BSRR reset bitmask #endif //////////////////////////////////////////////////////////////////////////////// // timer hander for tone with no duration specified, // will keep going until noTone() is called void tone_handler_1(void) { tone_next += tone_tcount; // comparator value for next interrupt tone_timer->setCompare(tone_channel, tone_next); // and install it if(--tone_n == 0){ tone_state = !tone_state; // toggle tone output #ifdef USE_BSRR if(tone_state) *tone_bsrr = tone_smask; else *tone_bsrr = tone_rmask; #else digitalWrite(tone_pin,tone_state);// and output it #endif tone_n = tone_ncount; // reset interrupt counter } } //////////////////////////////////////////////////////////////////////////////// // timer hander for tone with a specified duration, // will stop automatically when duration time is up. void tone_handler_2(void) { tone_next += tone_tcount; tone_timer->setCompare(tone_channel, tone_next); if(--tone_n == 0){ if(tone_freq>0){ // toggle pin tone_state = !tone_state; #ifdef USE_BSRR if(tone_state) *tone_bsrr = tone_smask; else *tone_bsrr = tone_rmask; #else digitalWrite(tone_pin,tone_state);// and output it #endif } tone_n = tone_ncount; if(!--tone_nhw){ // check if tone duration has finished tone_timer->pause(); // disable timer pinMode(tone_pin, INPUT); // disable tone pin } } } //////////////////////////////////////////////////////////////////////////////// // play a tone on given pin with given frequency and optional duration in msec void tone(uint8_t pin, short freq, unsigned duration = 0) { tone_pin = pin; #ifdef USE_PIN_TIMER // if the pin has a PWM timer/channel, use it (unless the timer/channel are forced) if(PinChannel(tone_pin) && !tone_force_channel){ tone_channel = PinChannel(tone_pin); tone_ntimer = PinTimer(tone_pin); } else #endif { // set timer and channel to default resp values forced with setToneTimerChannel tone_ntimer = tone_force_channel?tone_force_ntimer:TONE_TIMER; tone_channel = tone_force_channel?tone_force_channel:TONE_CHANNEL; } tone_timer = TTimer[tone_ntimer-1]; tone_freq = freq; tone_nhw = 0; tone_next = 0; tone_timer->pause(); if(freq > 0 || duration >0 ){ uint32_t count = 18000000/freq; // timer counts per half wave tone_ncount = tone_n = (count>>16)+1; // number of 16-bit count chunk tone_tcount = count/tone_ncount; // size of count chunk if(duration > 0) // number of half waves to be generated tone_nhw = ((duration*(freq>0?freq:100))/1000)<<1; else // no duration specified, continuous sound until noTone() called tone_nhw = 0; pinMode(tone_pin, PWM); // configure output pin pinMode(tone_pin, OUTPUT); // configure output pin #ifdef USE_BSRR // Set up BSRR register values for fast ISR tone_bsrr = &((PIN_MAP[tone_pin].gpio_device)->regs->BSRR); tone_smask = (BIT(PIN_MAP[tone_pin].gpio_bit)); tone_rmask = tone_smask<<16; #endif // Set up an interrupt on given timer and channel tone_next = tone_tcount; // prepare channel compare register tone_timer->setMode(tone_channel,TIMER_OUTPUT_COMPARE); tone_timer->setCompare(tone_channel,tone_next); // attach corresponding handler routine tone_timer->attachInterrupt(tone_channel,tone_nhw?tone_handler_2:tone_handler_1); // Refresh the tone timer tone_timer->refresh(); // Start the timer counting tone_timer->resume(); } else { // detach handler routine tone_timer->detachInterrupt(tone_channel); // disactive pin by configuring it as input pinMode(tone_pin, INPUT); } } //////////////////////////////////////////////////////////////////////////////// // disable tone on specified pin, if any void noTone(uint8_t pin){ tone(pin,-1); // it's all handled in tone() } //////////////////////////////////////////////////////////////////////////////// // set timer and channel to some different value // must be called before calling tone() or after noTone() was called void setToneTimerChannel(uint8_t ntimer, uint8_t channel){ tone_force_ntimer = ntimer; tone_force_channel = channel; }

victor_pv
Солнце 16 августа 2015 г., 8:25 вечера
Этот канал переходит в PB9, который используется для обнаружения в Maple Mini. Так что это достаточно безопасно в Mini, так как этот штифт никогда не должен использоваться для PWM на этой плате, но на других платах он может быть использован.
Вы можете захотеть поместить предупреждающий комментарий или что -то в этом роде, для людей, использующих другой клен, или одну из многих других доступных досок STM32, но приятно, что вы уже нашли способ повлиять только на канал таймера :)

Ekawahyu
Пн 11 июля 2016 г. 16:00
@Enif: Каков статус этой работы? Доступно ли где -то на GitHub и стабильно?

Эниф
Солнце 17 июля 2016 г. 15:00
@ekawahyu: Нет, это все еще просто «кодовая снайплетка», которую вы видите в моем сообщении выше. Я использую его в некоторых из моих программ и не сталкиваюсь с какими -либо проблемами - пока...

Ахулл
Сб 19 ноября 2016 г., 22:39
Я добавил пару рабочих примеров - см. Эта ветка -> http: // www.STM32duino.com/viewtopic.PHP ... 202#P20202

@Roger, не уверен, насколько легко было бы добавить tone () notone () в ядро ​​arduino на основе кода Энифа, но я думаю, что это было бы полезное дополнение.

Rogerclark
Сб 19 ноября 2016 г., 23:26
Спасибо, Энди

Я согласен, что Tone () и Notone () должен быть добавлен.

Я быстро посмотрел на эту реализацию, и я думаю, что это казалось в порядке.

И как новый файл, он не собирается сломать ничего, пока он компилируется, поэтому я могу также добавить его ;-)

Rogerclark
Солнце 20 ноября 2016 г., 7:25 утра
Экавахю написал:@Enif: Каков статус этой работы? Доступно ли где -то на GitHub и стабильно?

Ореховый
Ср. 07 декабря 2016 г. 12:13
Хиас, так что я немного играл с тоном, у меня сейчас есть пьезо на моем проекте. И я хотел хороший маленький звук звукового сигнала для запуска и нажатия кнопки меню... Но! Я не могу получить приличные звуки от своего пьезо, используя тон. Я сделал небольшой код, чтобы масштабироваться через определенные ноты, и, хотя есть небольшой гармонический тон, поднимающийся по шкале, в основном искаженные громкие звуковые сигналы.

Я попробую что -нибудь записать, чтобы показать в какой -то момент.

Я питаю пьезо при 12 В и запускается из Maple Mini через стандартный транзистор 2N4001.

Пройти по спецификациям 12V должно быть в порядке. Я думаю, что это сказано 3-20В или что-то в этом роде.

На этом этапе я думаю, я не прошу помощи. Больше просто сказать мой опыт пока. Но если у вас есть идеи о том, как его улучшить, дайте мне знать.

Rogerclark
Ср. 07 декабря 2016 г. 12:23
Я не уверен, что вы когда -нибудь получите хороший звук от пьезо. Я знаю, что некоторые из них гармонично настроены на то, чтобы лучше всего работать на одной конкретной частоте (или гармоник этой частоты), поэтому они иногда имеют 2 пика в своем аудио -спектре.

Если вы едете через транзистор, вы можете посмотреть на альтернативное устройство, которое больше похоже на обычный динамик. Есть несколько маленьких звуков, которые не пьезо, которые вы могли бы использовать (но они часто принимают много тока - E.глин. От 50 до 100 мА и нужен водительский транзистор)

Даже тогда, если вы кормите квадратной волной, вы получите определенный тип звука, но вы можете попробовать добавить сеть RC между STM32 и транзистором, чтобы сформировать фильтр с низким проходом E.глин. Установлен до 10 кГц или, возможно, 5 кГц, так как это дало бы вещи более приятный звук.

Ахулл
Ср. 07 декабря 2016 г., 11:03
Если вам нужна только определенная частота «звукового сигнала», вы можете преобразовать квадратную волну в синусоидальную волну с пассивами.

http: // www.LearningAboutelectronics.компонент ... Ircuit.PHP

Вы также можете обнаружить, что PWM тона дает лучшие результаты, (контроль затухания для удаления может уменьшить худшее из искажения), но это будет включать в себя большую работу по функциям Tone () или написание собственного.

Вы, конечно, могли бы просто использовать звуковой сигнал 12 В, предназначенный для создания одного тон. Дайте нам знать, как вы поступаете.

Ореховый
Ср. 07 декабря 2016 г. 12:51
Спасибо за совет, ребята, просто чтобы бросить гаечный ключ в работы * плачет * Я перечитываю список. С тех пор, как я заказал зуммеры, списки были обновлены 3-5 В... Так что напряжение было перечислено неправильно раньше. И я поставляю 12 В до 5 В зуммера. Неудивительно, что это звучит искаженно.

О, хорошо, я могу жить с этим. Что -то, чтобы измениться на окончательном дизайне.

Ахулл
Ср. 07 декабря 2016 г. 15:06
Черная писала:Спасибо за совет, ребята, просто чтобы бросить гаечный ключ в работы * плачет * Я перечитываю список. С тех пор, как я заказал зуммеры, списки были обновлены 3-5 В... Так что напряжение было перечислено неправильно раньше. И я поставляю 12 В до 5 В зуммера. Неудивительно, что это звучит искаженно.

О, хорошо, я могу жить с этим. Что -то, чтобы измениться на окончательном дизайне.

Ореховый
Ср. 07 декабря 2016 г., 16:02
я думаю, пьезо довольно прочные... Я имею в виду, хит один, он может генерировать тысячу вольт... Так что я думаю, это не курит это... Но просто искажает: P

Я не думаю, что есть какая -либо активная схема в этой вещи. Только что врезался в Peizo... Я мог бы взломать пьезо и подключить его к рельсу 5V... а не 12 В

Ореховый
Ср. 07 декабря 2016 г., 16:24
Хех взломал линию 5V на нее... Намного лучше : D Все еще не идеально, но лучше. Тогда я могу только догадываться, что 12V был просто чрезмерной загрузкой сигнала усиления: P

Aramperez
Вторник 8 августа 2017 г., 2:30
Я использую PA0 для подключения к пьезовидному зуммеру и PA1 в качестве сигнала ШИМ для управления двигателем (через чип L9110S). Всякий раз, когда я использую тон (PA0, XXX);, он испортит ШИМ на PA1. Кто -нибудь знает, почему это так?

Спасибо,
Арам

victor_pv
Вторник 8 августа 2017 г. 4:01
[Aramperez - Вторник 8 августа 2017 г. 2:30] - Я использую PA0 для подключения к пьезовидному зуммеру и PA1 в качестве сигнала ШИМ для управления двигателем (через чип L9110S). Всякий раз, когда я использую тон (PA0, XXX);, он испортит ШИМ на PA1. Кто -нибудь знает, почему это так?

Спасибо,
Арам
Пожалуйста, проверьте таблицу данных и документацию Libmaple на SWM для получения подробной информации, но в основном обусловлены оба штифта, имеющих каналы с одного и того же устройства таймера.

Rogerclark
Вторник 8 августа 2017 г., 4:51
В F103C есть только 4 таймера, они разделены между группами булавок.

Если вы посмотрите в Maple Mini Documentation, вы увидите, какие таймеры обмениваются через какие булавки

http: // docs.Leaflabs.com/static.Leaflab ... -мини.HTML

(Я уверен, что та же самая информация находится в справочном руководстве для STM32F103 (все 1000+ страниц), если вы хотите прочитать окончательную ссылку

Aramperez
Вторник 8 августа 2017 г. 18:04
Спасибо, Виктор и Роджер, я посмотрю на документацию и перемесчу один из сигналов.

/Арам

Супер простой RTC