From 9d5a4e9dd9a3919fc764bc1946f697f0ae0d6db3 Mon Sep 17 00:00:00 2001 From: Armin Date: Tue, 20 Apr 2021 08:58:10 +0200 Subject: [PATCH] Modified TalkieVocabularyDemo. Fixed 12 bit output, closes #14. Removed clamp. --- README.md | 13 +-- .../TalkieVocabularyDemo.ino | 30 +++++-- .../USDistanceToVoice/USDistanceToVoice.ino | 4 +- .../_2_VoltmeterSayQ/_2_VoltmeterSayQ.ino | 9 +- src/Talkie.cpp | 85 ++++++++++--------- 5 files changed, 86 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 0a80963..74ebcd1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # [Talkie](https://github.com/ArminJo/Talkie) Available as Arduino library "Talkie" -### [Version 1.3.0](https://github.com/ArminJo/Talkie/releases) - work in progress +### [Version 1.3.0](https://github.com/ArminJo/Talkie/releases) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Installation instructions](https://www.ardu-badge.com/badge/Talkie.svg?)](https://www.ardu-badge.com/Talkie) @@ -33,12 +33,13 @@ YouTube Intoduction by [Gadget Reboot](https://www.youtube.com/channel/UCwiKHTeg - **ATmega328** as found on the **Uno** and **Nano** boards. - **ATmega2560** as found on the **MEGA 2560** board. - **ATmega32U4** as found on the **Leonardo** and **CircuitPlaygound** boards. - - **ARM0** (Tested for Arduino Zero) as found on the **SAMD**, **Teensy** and **Particle** boards. + - **ARM M0** (Tested for Arduino Zero) as found on the **SAMD**, **Teensy** and **Particle** boards. - **ESP32**. ESP8266 is theoretical possible using FRC2, but for now Arduino shares the FRC1 timer between interrupts and PWM. + - **ARM M3** (Tested for BluePill) for Roger Clarks as well as STM core. ## Pin mapping table for different platforms -| Platform | Normal | Inverted | 8kHz timer | PWM timer | -|-|-|-|-|-| +| Platform | Normal | Inverted | 8kHz timer | PWM timer | Remarks | +|-|-|-|-|-|-| | AVR (Uno and Nano) | 3 | 11 | 1 | 2 | | ATmega2560 | 6/PH3 | 7/PH4 | 1 | 4 | | Leonardo | 9/PB5 | 10/PB6 | 1 | 4 | @@ -46,6 +47,8 @@ YouTube Intoduction by [Gadget Reboot](https://www.youtube.com/channel/UCwiKHTeg | Esplora | 6/PD7 | % | 1 | 4 | | Zero (SAMD) | A0 | % | TC5 | DAC0 | | ESP32 | 25 | % | hw_timer_t | DAC0 | +| BluePill | 3 | % | timer3 | analogWrite | Roger Clarks core | +| BluePill | PA3 | % | timer4 | analogWrite | STM core | | Teensy | 12/14721 | % | IntervalTimer | analogWrite | ## Timer usage @@ -117,7 +120,7 @@ The 8 kHz interrupt handling requires **8 # Revision History ### Version 1.3.0 - Removed blocking wait for ATmega32U4 Serial in examples. -- 10 bit Coefficients are working noe, but they do not sound better :-(. +- 10 bit Coefficients are working now, but they do not sound better :-(. - Tested on an ESP32. - Tested on a BluePill. diff --git a/examples/TalkieVocabularyDemo/TalkieVocabularyDemo.ino b/examples/TalkieVocabularyDemo/TalkieVocabularyDemo.ino index dc37d96..f2a9faf 100644 --- a/examples/TalkieVocabularyDemo/TalkieVocabularyDemo.ino +++ b/examples/TalkieVocabularyDemo/TalkieVocabularyDemo.ino @@ -32,7 +32,8 @@ #include "Talkie.h" #include "Vocab_US_Large.h" #include "Vocab_Special.h" -#include "Vocab_Soundbites.h" +#include "Vocab_US_TI99.h" +#include "Vocab_US_Clock.h" /* * Voice PWM output pins for different ATmegas: @@ -83,10 +84,27 @@ void loop() { voice.say(sp2_ON); voice.say(sp2_FIRE); - // The strange soundbites demo + // US Clock words voice.say(spPAUSE2); - voice.say(spWHAT_IS_THY_BIDDING); - voice.say(spHASTA_LA_VISTA); - voice.say(spONE_SMALL_STEP); - voice.say(spHMMM_BEER); + voice.say(spc_GOOD); + voice.say(spc_EVENING); + voice.say(spPAUSE1); + voice.say(spc_THE); + voice.say(spc_TIME); + voice.say(spc_IS); + voice.say(spc_SEVEN); + voice.say(spc_FOURTY); + voice.say(spc_FIVE); + voice.say(spc_P_M_); + voice.say(spPAUSE1); + + // Ti99 words + voice.say(spPAUSE2); + voice.say(spt_CASSETTE); + voice.say(spt_COMPLETED); + voice.say(spPAUSE1); + voice.say(spt_BUT); + voice.say(spt_NOT); + voice.say(spt_FINISHED); + } diff --git a/examples/USDistanceToVoice/USDistanceToVoice.ino b/examples/USDistanceToVoice/USDistanceToVoice.ino index 2b21049..4847457 100644 --- a/examples/USDistanceToVoice/USDistanceToVoice.ino +++ b/examples/USDistanceToVoice/USDistanceToVoice.ino @@ -42,9 +42,11 @@ * Esplora 6/PD7 % 1 4 * Zero (SAMD) A0 % TC5 DAC0 * ESP32 25 % hw_timer_t DAC0 + * BluePill 3 % timer3 analogWrite Roger Clarks core + * BluePill PA3 % timer4 analogWrite STM core * Teensy 12/14721 % IntervalTimer analogWrite * - * As default both inverted and not inverted outputs are enabled to increase volume if speaker is attached between them. + * As default both inverted and not inverted outputs are enabled for AVR to increase volume if speaker is attached between them. * Use Talkie Voice(true, false); if you only need not inverted pin or if you want to use SPI on ATmega328 which needs pin 11. * * The outputs can drive headphones directly, or add a simple audio amplifier to drive a loudspeaker. diff --git a/examples/_2_VoltmeterSayQ/_2_VoltmeterSayQ.ino b/examples/_2_VoltmeterSayQ/_2_VoltmeterSayQ.ino index 67c8483..855f468 100644 --- a/examples/_2_VoltmeterSayQ/_2_VoltmeterSayQ.ino +++ b/examples/_2_VoltmeterSayQ/_2_VoltmeterSayQ.ino @@ -35,10 +35,15 @@ * ProMicro 5/PC6 % 1 4 - or Adafruit Circuit Playground Classic * Esplora 6/PD7 % 1 4 * Zero (SAMD) A0 % TC5 DAC0 - * ESP32 25 % timer1 analogWrite + * ESP32 25 % hw_timer_t DAC0 * BluePill 3 % timer3 analogWrite Roger Clarks core * BluePill PA3 % timer4 analogWrite STM core * Teensy 12/14721 % IntervalTimer analogWrite + * + * As default both inverted and not inverted outputs are enabled for AVR to increase volume if speaker is attached between them. + * Use Talkie Voice(true, false); if you only need not inverted pin or if you want to use SPI on ATmega328 which needs pin 11. + * + * The outputs can drive headphones directly, or add a simple audio amplifier to drive a loudspeaker. */ #include @@ -109,6 +114,8 @@ void setup() { Serial.print(F("Speech output at pin ")); #if defined(ARDUINO_ARCH_STM32) Serial.println("PA3"); // the internal pin numbers are crazy for the STM32 Boards library +#elif defined(ARDUINO_ARCH_SAMD) + Serial.println("A0"); // DAC0 is at PIN 14/A0 #else Serial.print(Voice.NonInvertedOutputPin); #endif diff --git a/src/Talkie.cpp b/src/Talkie.cpp index 7f56d9d..880f128 100644 --- a/src/Talkie.cpp +++ b/src/Talkie.cpp @@ -46,10 +46,16 @@ * Esplora 6/PD7 % 1 4 * Zero (SAMD) A0 % TC5 DAC0 * ESP32 25 % hw_timer_t DAC0 + * BluePill 3 % timer3 analogWrite Roger Clarks core + * BluePill PA3 % timer4 analogWrite STM core * Teensy 12/14721 % IntervalTimer analogWrite * + * As default both inverted and not inverted outputs are enabled for AVR to increase volume if speaker is attached between them. + * Use Talkie Voice(true, false); if you only need not inverted pin or if you want to use SPI on ATmega328 which needs pin 11. * - * Copyright (C) 2018 Armin Joachimsmeyer + * The outputs can drive headphones directly, or add a simple audio amplifier to drive a loudspeaker. + * + * Copyright (C) 2018-2021 Armin Joachimsmeyer * armin.joachimsmeyer@gmail.com * * This file is part of Talkie https://github.com/ArminJo/Talkie. @@ -118,7 +124,7 @@ IntervalTimer sIntervalTimer; #elif defined(ESP32) #include -static hw_timer_t *sESP32Timer = NULL; +static hw_timer_t *sTalkieSampleRateTimer = NULL; #elif defined(ARDUINO_ARCH_SAMD) // Zero static void tcStart(uint32_t sampleRate); // TC5 @@ -130,8 +136,9 @@ static void tcEnd(); * Use timer 3 as sample rate timer. * Timer 3 blocks PA6, PA7, PB0, PB1, so if you require one of them as tone() or Servo output, you must choose another timer. */ -HardwareTimer sSTM32Timer(3); -HardwareTimer sSTM32PWMTimer(2); +HardwareTimer sTalkieSampleRateTimer(3); +timer_dev *sTalkiePWMTimer; +uint8_t sTalkiePWMTimerChannel; #elif defined(STM32F1xx) || defined(ARDUINO_ARCH_STM32) #include // 4 timers and 3. timer is used for tone(), 2. for Servo @@ -140,9 +147,9 @@ HardwareTimer sSTM32PWMTimer(2); * Timer 4 blocks PB6, PB7, PB8, PB9, so if you require one of them as tone() or Servo output, you must choose another timer. */ # if defined(TIM4) -HardwareTimer sSTM32Timer(TIM4); +HardwareTimer sTalkieSampleRateTimer(TIM4); # else -HardwareTimer sSTM32Timer(TIM2); +HardwareTimer sTalkieSampleRateTimer(TIM2); # endif #endif @@ -271,7 +278,7 @@ void Talkie::initializeHardware() { #elif defined(ARDUINO_ARCH_SAMD) // Zero #define _10_BIT_OUTPUT // 10-bit, 350 ksps Digital-to-Analog Converter (DAC) -#define DAC_PIN DAC0 // PA02 + DAC1 on due +#define DAC_PIN DAC0 // pin 14/A0 for Zero. PA02 + DAC1 on due #define PWM_OUTPUT_FUNCTION(nextPwm) analogWrite(sPointerToTalkieForISR->NonInvertedOutputPin, nextPwm) # ifdef ARDUINO_SAMD_CIRCUITPLAYGROUND_EXPRESS static const int CPLAY_SPEAKER_SHUTDOWN= 11; @@ -311,10 +318,10 @@ void Talkie::initializeHardware() { #define PWM_OUTPUT_FUNCTION(nextPwm) dacWrite(sPointerToTalkieForISR->NonInvertedOutputPin, nextPwm) // Use Timer1 with 1 microsecond resolution, main APB clock is 80MHZ #define APB_FREQUENCY_DIVIDER 80 - sESP32Timer = timerBegin(1, APB_FREQUENCY_DIVIDER, true); - timerAttachInterrupt(sESP32Timer, timerInterrupt, true); - timerAlarmWrite(sESP32Timer, (getApbFrequency() / APB_FREQUENCY_DIVIDER) / SAMPLE_RATE, true); - timerAlarmEnable(sESP32Timer); + sTalkieSampleRateTimer = timerBegin(1, APB_FREQUENCY_DIVIDER, true); + timerAttachInterrupt(sTalkieSampleRateTimer, timerInterrupt, true); + timerAlarmWrite(sTalkieSampleRateTimer, (getApbFrequency() / APB_FREQUENCY_DIVIDER) / SAMPLE_RATE, true); + timerAlarmEnable(sTalkieSampleRateTimer); #if defined(DEBUG) && defined(ESP32) Serial.print("CPU frequency="); Serial.print(getCpuFrequencyMHz()); @@ -333,27 +340,29 @@ void Talkie::initializeHardware() { // http://dan.drown.org/stm32duino/package_STM32duino_index.json #define DAC_PIN PA3 // T2C4 #define _10_BIT_OUTPUT -#define PWM_OUTPUT_FUNCTION(nextPwm) sSTM32PWMTimer.setCompare(TIMER_CH4, nextPwm) +#define PWM_OUTPUT_FUNCTION(nextPwm) timer_set_compare(sTalkiePWMTimer, sTalkiePWMTimerChannel, nextPwm) /* * Prepare 10 bit PWM @ 72 MHz */ - pinMode(DAC_PIN, PWM); // this initializes the output pin and the timer mode - sSTM32PWMTimer.setPrescaleFactor(1); - sSTM32PWMTimer.setOverflow((1 << 10) - 1); - sSTM32PWMTimer.setCompare(TIMER_CH4, (1 << 9)); - sSTM32PWMTimer.resume(); // Start timer - sSTM32PWMTimer.refresh(); // Reset to start values + sTalkiePWMTimerChannel = PIN_MAP[sPointerToTalkieForISR->NonInvertedOutputPin].timer_channel; // set timer channel according to pin + sTalkiePWMTimer = PIN_MAP[sPointerToTalkieForISR->NonInvertedOutputPin].timer_device; // set timer according to pin + pinMode(sPointerToTalkieForISR->NonInvertedOutputPin, PWM); // this initializes the output pin and the timer mode + timer_set_prescaler(sTalkiePWMTimer, 0); // set prescaler to 1 + timer_set_reload(sTalkiePWMTimer, (1 << 10) - 1); // setOverflow() + timer_set_compare(sTalkiePWMTimer, sTalkiePWMTimerChannel, (1 << 9)); + timer_resume(sTalkiePWMTimer); // Start timer + timer_generate_update(sTalkiePWMTimer); // Reset to start values /* * Set timer for interrupts at SAMPLE_RATE */ - sSTM32Timer.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); - sSTM32Timer.setPrescaleFactor(1); - sSTM32Timer.setOverflow(F_CPU / SAMPLE_RATE); - sSTM32Timer.attachInterrupt(TIMER_CH1, timerInterrupt); - sSTM32Timer.resume(); // Start timer - sSTM32Timer.refresh(); // Reset to start values + sTalkieSampleRateTimer.setMode(TIMER_CH1, TIMER_OUTPUT_COMPARE); + sTalkieSampleRateTimer.setPrescaleFactor(1); + sTalkieSampleRateTimer.setOverflow(F_CPU / SAMPLE_RATE); + sTalkieSampleRateTimer.attachInterrupt(TIMER_CH1, timerInterrupt); + sTalkieSampleRateTimer.resume(); // Start timer + sTalkieSampleRateTimer.refresh(); // Reset to start values #elif defined(STM32F1xx) || defined(ARDUINO_ARCH_STM32) // STM32duino by ST Microsystems. @@ -368,10 +377,10 @@ void Talkie::initializeHardware() { /* * Set timer for interrupts at SAMPLE_RATE */ - sSTM32Timer.setOverflow(1000000L / SAMPLE_RATE, MICROSEC_FORMAT); - sSTM32Timer.attachInterrupt(timerInterrupt); - sSTM32Timer.resume(); // Start timer - sSTM32Timer.refresh(); // Reset to start values + sTalkieSampleRateTimer.setOverflow(1000000L / SAMPLE_RATE, MICROSEC_FORMAT); + sTalkieSampleRateTimer.attachInterrupt(timerInterrupt); + sTalkieSampleRateTimer.resume(); // Start timer + sTalkieSampleRateTimer.refresh(); // Reset to start values #endif // AVR #ifdef MEASURE_TIMING @@ -427,13 +436,13 @@ void Talkie::terminateHardware() { tcEnd(); #elif defined(ESP32) - timerAlarmDisable(sESP32Timer); + timerAlarmDisable(sTalkieSampleRateTimer); #elif defined(TEENSYDUINO) sIntervalTimer.end(); #elif defined(__STM32F1__) || defined(ARDUINO_ARCH_STM32F1) || defined(STM32F1xx) || defined(ARDUINO_ARCH_STM32) - sSTM32Timer.pause(); + sTalkieSampleRateTimer.pause(); #endif // defined(__AVR__) @@ -712,6 +721,7 @@ extern "C" { * 50 microseconds with 4 simple optimizations (change ">>15" to "<<1) >>16") * 8 us for a BluePill with Roger Clarks core * 12 us for a BluePill with official STM core + * 15 us for Arduino Zero @ 48 MHz */ #if defined(ESP32) IRAM_ATTR @@ -770,7 +780,7 @@ void timerInterrupt() { } // Lattice filter forward path -> fill temporary variables -// rescale by shifting >> 7 because we have signed values here (for unsigned it would need >> 8) +// rescale by shifting >> 7 because we have signed values here (for unsigned it would require >> 8) #if !defined(USE_10_BIT_KOEFFICIENT_VALUES) u9 = u10 - (((int16_t) synthK10 * x9) >> 7); u8 = u9 - (((int16_t) synthK9 * x8) >> 7); @@ -802,15 +812,6 @@ void timerInterrupt() { u0 = u1 - ((synthK1 * x0) >> 9); #endif -// Output clamp -// Yes this happens generally seldom, but for "spt_BUT" it happens very often, which makes this sound unusable - if (u0 > 511) { - u0 = 511; - } - if (u0 < -512) { - u0 = -512; - } - // Lattice filter reverse path -> compute next x* values #if !defined(USE_10_BIT_KOEFFICIENT_VALUES) x9 = x8 + (((int16_t) synthK9 * u8) >> 7); @@ -839,12 +840,12 @@ void timerInterrupt() { x1 = x0 + ((synthK1 * u0) >> 9); #endif - x0 = u0; + x0 = u0; // 10 bit value -512 to +511 #if defined(_10_BIT_OUTPUT) nextPwm = (u0) + 0x200; #elif defined(_12_BIT_OUTPUT) - nextPwm = (u0 * 2) + 0x800; + nextPwm = (u0 * 4) + 0x800; #else // 8 bit is default nextPwm = (u0 >> 2) + 0x80;