diff --git a/inc/drivers/CalliopeMicrophone.h b/inc/drivers/CalliopeMicrophone.h new file mode 100644 index 00000000..a9be5f35 --- /dev/null +++ b/inc/drivers/CalliopeMicrophone.h @@ -0,0 +1,77 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Calliope gGmbH +This software is provided by utz (M. Neidel) by arrangement with Calliope gGmbH. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifdef TARGET_NRF51_CALLIOPE + +#ifndef CALLIOPE_MICROPHONE_H +#define CALLIOPE_MICROPHONE_H + +#include "mbed.h" +#include "MicroBitIO.h" +#include "MicroBitComponent.h" + + +#define CALLIOPE_DEFAULT_SAMPLE_RATE 8000 //default sample rate for PWM sampling +#define CALLIOPE_MAX_SAMPLE_RATE 11025 //max sample rate, limited by sample loop exec time +#define CALLIOPE_MIN_SAMPLE_RATE 1 //min sample rate +#define CALLIOPE_MIN_SAMPLE_BUFFER_SIZE 1 //min sample buffer size +#define CALLIOPE_MIC_DEFAULT_SENSITIVITY 48 //mic sensitivity level (0..77) +#define CALLIOPE_MIC_MAX_SENSITIVITY 77 +#define CALLIOPE_MIC_BASE_LEVEL 518 //mic input null level + +class CalliopeMicrophone : public MicroBitComponent +{ + public: + //constructor + CalliopeMicrophone(); + + //destructor + ~CalliopeMicrophone(); + + //function to initialize recording + static void recordSample(uint8_t* buffer, int16_t len, + uint16_t sensitivity = CALLIOPE_MIC_DEFAULT_SENSITIVITY, int16_t sample_rate = CALLIOPE_DEFAULT_SAMPLE_RATE); + //function to stop/abort recording + static void stopRecording(); + //interrupt service, do not call directly! + static void updateInput(); + //function check recording status + static bool isRecording(); + + private: + static uint8_t* rec_buffer; + static int16_t rec_len; + static int16_t rec_pos; + static uint8_t pwm_tick; + static uint16_t upper_threshold; + static uint16_t lower_threshold; + static AnalogIn micpin; + static mbed::Ticker rec_ticker; + static bool active; +}; + +#endif +#endif // TARGET_NRF51_CALLIOPE diff --git a/inc/drivers/CalliopeSoundMotor.h b/inc/drivers/CalliopeSoundMotor.h index 6877d2c4..86582911 100644 --- a/inc/drivers/CalliopeSoundMotor.h +++ b/inc/drivers/CalliopeSoundMotor.h @@ -3,7 +3,8 @@ The MIT License (MIT) Copyright (c) 2016 Calliope GbR This software is provided by DELTA Systems (Georg Sommer) - Thomas Kern -und Björn Eberhardt GbR by arrangement with Calliope GbR. +und Björn Eberhardt GbR by arrangement with Calliope GbR. Modifications +and additional PWM sample driver by Michael Neidel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -51,6 +52,12 @@ DEALINGS IN THE SOFTWARE. #define CALLIOPE_MAX_FREQUENCY_HZ_S 20000 //max human audible frequency #define CALLIOPE_BOARD_FREQUENCY 16000000 +//constants for sound sampling +#define CALLIOPE_DEFAULT_SAMPLE_RATE 8000 //default sample rate for PWM samples +#define CALLIOPE_MAX_SAMPLE_RATE 11025 //max sample rate (limited by sampling loop exec time) +#define CALLIOPE_MIN_SAMPLE_RATE 1 //min sample rate + + class CalliopeSoundMotor : public MicroBitComponent { //current settings @@ -62,9 +69,17 @@ class CalliopeSoundMotor : public MicroBitComponent static uint16_t frequency_sound_hz; static bool silent_mode; - //current use of the controller -> 0: off, 1: motor use, 2: dual motor use, 3: sound use + //current use of the controller -> 0: off, 1: motor use, 2: dual motor use, 3: sound use, 4: pwm sample playback static uint8_t mode; + //sample playback settings + static uint8_t* sample_buffer; + static uint16_t sample_len; + static uint16_t sample_pos; + static uint8_t sample_period_tick; + static bool sample_playing; + static mbed::Ticker sample_ticker; + public: //constructor CalliopeSoundMotor(); @@ -92,6 +107,11 @@ class CalliopeSoundMotor : public MicroBitComponent void soundOn(uint16_t frequency_hz = frequency_sound_hz); void setSoundSilentMode(bool on_off); void soundOff(); + + //functions for sample playback + static void playSample(uint8_t* buffer, uint16_t len, int16_t sample_rate = CALLIOPE_DEFAULT_SAMPLE_RATE); + static void stopSamplePlayback(); + static void updateSampleOutput(); //check fucntions bool motorIsOn(); diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 06003e80..aa85817e 100755 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -27,7 +27,6 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "drivers/MicroBitAccelerometer-bmx.cpp" "drivers/MicroBitButton.cpp" "drivers/MicroBitCompass.cpp" - "drivers/MicroBitCompass-bmx.cpp" "drivers/MicroBitCompassCalibrator.cpp" "drivers/MicroBitDisplay.cpp" "drivers/MicroBitI2C.cpp" @@ -36,7 +35,6 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "drivers/MicroBitMessageBus.cpp" "drivers/MicroBitMultiButton.cpp" "drivers/MicroBitPin.cpp" - "drivers/MicroBitQuadratureDecoder.cpp" "drivers/MicroBitRadio.cpp" "drivers/MicroBitRadioDatagram.cpp" "drivers/MicroBitRadioEvent.cpp" @@ -47,6 +45,7 @@ set(YOTTA_AUTO_MICROBIT-DAL_CPP_FILES "drivers/MicroBitFlash.cpp" "drivers/MicroBitFile.cpp" "drivers/MicroBitFileSystem.cpp" + "drivers/CalliopeMicrophone.cpp" "drivers/CalliopeSoundMotor.cpp" "drivers/CalliopeRGB.cpp" diff --git a/source/drivers/CalliopeMicrophone.cpp b/source/drivers/CalliopeMicrophone.cpp new file mode 100644 index 00000000..f717937c --- /dev/null +++ b/source/drivers/CalliopeMicrophone.cpp @@ -0,0 +1,126 @@ +/* +The MIT License (MIT) + +Copyright (c) 2017 Calliope gGmbH +This software is provided by utz (M. Neidel) by arrangement with Calliope gGmbH. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifdef TARGET_NRF51_CALLIOPE + +#include "CalliopeMicrophone.h" +#include "MicroBitSystemTimer.h" +#include "MicroBitPin.h" + +//define static members +uint8_t* CalliopeMicrophone::rec_buffer; +int16_t CalliopeMicrophone::rec_len; +int16_t CalliopeMicrophone::rec_pos; +uint8_t CalliopeMicrophone::pwm_tick; +uint16_t CalliopeMicrophone::upper_threshold; +uint16_t CalliopeMicrophone::lower_threshold; +AnalogIn CalliopeMicrophone::micpin(MIC); +mbed::Ticker CalliopeMicrophone::rec_ticker; +bool CalliopeMicrophone::active = false; + +//constructor +CalliopeMicrophone::CalliopeMicrophone() +{ + system_timer_add_component(this); +} + + +//destructor +CalliopeMicrophone::~CalliopeMicrophone() +{ + system_timer_remove_component(this); +} + + +//PWM sampling function: records sound from microphone and converts to 1-bit PWM data until buffer is full +void CalliopeMicrophone::recordSample(uint8_t* buffer, int16_t len, uint16_t sensitivity, int16_t sample_rate) +{ + //refuse to run if already recording + if (active) return; + + //return on invalid parameters + if (len < CALLIOPE_MIN_SAMPLE_BUFFER_SIZE || sample_rate > CALLIOPE_MAX_SAMPLE_RATE + || sample_rate < CALLIOPE_MIN_SAMPLE_RATE || sensitivity > CALLIOPE_MIC_MAX_SENSITIVITY) return; + + //set recording mode + active = true; + + //initialize recording parameters + rec_buffer = buffer; + rec_len = len; + rec_pos = 0; + pwm_tick = 1; + upper_threshold = CALLIOPE_MIC_BASE_LEVEL + (80 - sensitivity); + lower_threshold = CALLIOPE_MIC_BASE_LEVEL - (80 - sensitivity); + + //set up interrupt service + rec_ticker.attach_us(&updateInput, static_cast(1000000 / sample_rate)); +} + + +void CalliopeMicrophone::stopRecording() { + + //do nothing if not recording + if (!active) return; + + //disable interrupt + rec_ticker.detach(); + + //set recording status + active = false; +} + +//interrupt service routine for sample recording - do not call directly! +void CalliopeMicrophone::updateInput() +{ + //stop recording if buffer is full + if (rec_pos >= rec_len) + { + stopRecording(); + return; + } + + //read value analog input + uint16_t val = micpin.read_u16(); + + //update pwm period counter and write to buffer if input crossed threshold or counter wrapped + if (pwm_tick && ((!(rec_pos & 1) && val > lower_threshold) + || ((rec_pos & 1) && val < upper_threshold))) ++pwm_tick; + else + { + rec_buffer[rec_pos] = pwm_tick; + pwm_tick = 1; + ++rec_pos; + } +} + +//function for checking recording status +bool CalliopeMicrophone::isRecording() +{ + return active; +} + +#endif diff --git a/source/drivers/CalliopeSoundMotor.cpp b/source/drivers/CalliopeSoundMotor.cpp index 47cb2217..1c2129ba 100644 --- a/source/drivers/CalliopeSoundMotor.cpp +++ b/source/drivers/CalliopeSoundMotor.cpp @@ -3,7 +3,8 @@ The MIT License (MIT) Copyright (c) 2016 Calliope GbR This software is provided by DELTA Systems (Georg Sommer) - Thomas Kern -und Björn Eberhardt GbR by arrangement with Calliope GbR. +und Björn Eberhardt GbR by arrangement with Calliope GbR. Modifications +and additional PWM sample driver by Michael Neidel. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), @@ -41,6 +42,12 @@ uint8_t CalliopeSoundMotor::motor_AB_current_use; uint16_t CalliopeSoundMotor::frequency_sound_hz; bool CalliopeSoundMotor::silent_mode; uint8_t CalliopeSoundMotor::mode; +uint8_t* CalliopeSoundMotor::sample_buffer; +uint16_t CalliopeSoundMotor::sample_len; +uint16_t CalliopeSoundMotor::sample_pos; +uint8_t CalliopeSoundMotor::sample_period_tick; +bool CalliopeSoundMotor::sample_playing = false; +mbed::Ticker CalliopeSoundMotor::sample_ticker; //constructor @@ -145,8 +152,8 @@ void CalliopeSoundMotor::PWM_init() //functions to control the motor void CalliopeSoundMotor::motorOn(int8_t duty_percent) { - //if value is out of bounds, do nothing - if((duty_percent > 100) || (duty_percent < -100)) return; + //if value is out of bounds or PWM sample playback is active, do nothing + if((duty_percent > 100) || (duty_percent < -100) || mode > 3) return; //set mode to single motor use mode = 1; @@ -293,8 +300,8 @@ void CalliopeSoundMotor::motorSleep() void CalliopeSoundMotor::motorAOn(uint8_t duty_percent) { - //if value is out of bounds, do nothing - if(duty_percent > 100) return; + //if value is out of bounds or PWM sample playback is active, do nothing + if(duty_percent > 100 || mode > 3) return; //save current setting duty_motor_A_percent = duty_percent; @@ -356,8 +363,8 @@ void CalliopeSoundMotor::motorAOn(uint8_t duty_percent) void CalliopeSoundMotor::motorBOn(uint8_t duty_percent) { - //if value is out of bounds, do nothing - if(duty_percent > 100) return; + //if value is out of bounds or PWM sample playback is active, do nothing + if(duty_percent > 100 || mode > 3) return; //save current setting duty_motor_B_percent = duty_percent; @@ -393,7 +400,7 @@ void CalliopeSoundMotor::motorBOn(uint8_t duty_percent) motor_AB_current_use |= 0x02; //values for duty cycle 0 - if(uint8_t(duty_motor_B_percent/2) == 0 || (motor_AB_current_use == 0x02)) { + if(uint8_t(duty_motor_A_percent/2) == 0 || (motor_AB_current_use == 0x02)) { nrf_gpio_pin_clear(CALLIOPE_PIN_MOTOR_IN1); nrf_gpio_pin_clear(CALLIOPE_PIN_MOTOR_IN2); } @@ -594,8 +601,6 @@ void CalliopeSoundMotor::setSoundSilentMode(bool on_off) NRF_TIMER2->TASKS_START = 1; } - - void CalliopeSoundMotor::soundOff() { //use function only for sound use @@ -619,6 +624,100 @@ void CalliopeSoundMotor::soundOff() } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//SAMPLE PLAYBACK FUNCTIONS +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +void CalliopeSoundMotor::playSample(uint8_t* buffer, uint16_t len, int16_t sample_rate) +{ + + //refuse to run if motor is currently in use + if (mode != 0) return; + + //validate sampling rate + if (sample_rate > CALLIOPE_MAX_SAMPLE_RATE || sample_rate < CALLIOPE_MIN_SAMPLE_RATE) return; + + //set controller mode + mode = 4; + + //disable GPIOTE control of the pins + nrf_gpiote_task_disable(0); + nrf_gpiote_task_disable(1); + + //initialize playback parameters + sample_buffer = buffer; + sample_len = len; + sample_pos = 0; + sample_period_tick = 1; + sample_playing = true; + + //activate controller and init pins + nrf_gpio_pin_set(CALLIOPE_PIN_MOTOR_SLEEP); + + //set up interrupt service + sample_ticker.attach_us(&updateSampleOutput, static_cast(1000000 / sample_rate)); +} + + +//interrupt service routine for sample playback - do not call directly! +void CalliopeSoundMotor::updateSampleOutput() +{ + + //stop playback if end of sample buffer reached + if (sample_pos >= sample_len) { + stopSamplePlayback(); + return; + } + + //update pwm period counter + --sample_period_tick; + + if (!sample_period_tick) { + //update pwm period counter and buffer position + sample_period_tick = sample_buffer[sample_pos]; + ++sample_pos; + + //skip toggling output if current sample = 0 + if (sample_period_tick) + { + //manuall toggle motor pins (faster than nrf_gpio_pin_toggle(), and can use correct toggling order) + if (sample_pos & 1) + { + nrf_gpio_pin_clear(CALLIOPE_PIN_MOTOR_IN1); + nrf_gpio_pin_set(CALLIOPE_PIN_MOTOR_IN2); + } + else + { + //toggle motor pins in reverse order to reduce transition noise + nrf_gpio_pin_clear(CALLIOPE_PIN_MOTOR_IN2); + nrf_gpio_pin_set(CALLIOPE_PIN_MOTOR_IN1); + } + } + } +} + + +void CalliopeSoundMotor::stopSamplePlayback() +{ + //refuse to run if not in sample playback mode + if (mode != 4) return; + + //disable interrupt service + if (sample_playing) { + sample_ticker.detach(); + sample_playing = false; + } + + //clear pins + nrf_gpio_pin_clear(CALLIOPE_PIN_MOTOR_IN1); + nrf_gpio_pin_clear(CALLIOPE_PIN_MOTOR_IN2); + + //deactivate controller & set mode to off + nrf_gpio_pin_clear(CALLIOPE_PIN_MOTOR_SLEEP); + mode = 0; +} + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //CHECK AND GETTER FUNCTIONS /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////