Skip to content

Commit

Permalink
Merge pull request #190 from sensorium/devel/UnoR4
Browse files Browse the repository at this point in the history
Creating a devel branch for Arduino R4
  • Loading branch information
tomcombriat authored Jul 31, 2023
2 parents 68361b4 + eeaeef6 commit 1787763
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 4 deletions.
25 changes: 25 additions & 0 deletions AudioConfigRenesas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef AUDIOCONFIGRENESAS_H
#define AUDIOCONFIGRENESAS_H

#if not IS_RENESAS()
#error This header should be included for Arduino FSB board (Uno R4/Renesa) family, only
#endif




#define AUDIO_CHANNEL_1_PIN A0
#if (AUDIO_CHANNELS > 1)
#define AUDIO_CHANNEL_2_PIN 10
#endif

#define AUDIO_BITS 12 // outputting resolution of the on-bard DAC. Other values are *NOT* expected to work.

#define AUDIO_BITS_PER_CHANNEL AUDIO_BITS

#define AUDIO_BIAS ((uint16_t) 1<<(AUDIO_BITS-1))

#define BYPASS_MOZZI_OUTPUT_BUFFER true // Mozzi initial buffer are not of the good type, so we bypass it and create our own

#endif // #ifndef AUDIOCONFIGRENESAS_H

10 changes: 9 additions & 1 deletion CircularBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ On 18 April 2014, the simplified version on the Wikipedia page for power of 2 si
doesn't work - cbIsEmpty() returns true whether the buffer is full or empty.
*/

#define MOZZI_BUFFER_SIZE 256 // do not expect to change and it to work.
// just here for forward compatibility if one day
// the buffer size might be editable

/** Circular buffer object. Has a fixed number of cells, set to 256.
@tparam ITEM_TYPE the kind of data to store, eg. int, int8_t etc.
*/
Expand Down Expand Up @@ -47,9 +51,13 @@ class CircularBuffer
unsigned long count() {
return (num_buffers_read << 8) + start;
}
inline
ITEM_TYPE * address() {
return items;
}

private:
ITEM_TYPE items[256];
ITEM_TYPE items[MOZZI_BUFFER_SIZE];
uint8_t start; /* index of oldest itement */
uint8_t end; /* index at which to write new itement */
uint8_t s_msb;
Expand Down
2 changes: 2 additions & 0 deletions MozziGuts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ static void startSecondADCReadOnCurrentChannel();
# include "MozziGuts_impl_RP2040.hpp"
#elif (IS_MBED())
# include "MozziGuts_impl_MBED.hpp"
#elif (IS_RENESAS())
# include "MozziGuts_impl_RENESAS.hpp"
#else
# error "Platform not (yet) supported. Check MozziGuts_impl_template.hpp and existing implementations for a blueprint for adding your favorite MCU."
#endif
Expand Down
2 changes: 2 additions & 0 deletions MozziGuts.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,8 @@ HIFI is not available/not required on Teensy 3.* or ARM.
#include "AudioConfigStandardPlus.h"
#elif IS_AVR() && (AUDIO_MODE == HIFI)
#include "AudioConfigHiSpeed14bitPwm.h"
#elif IS_RENESAS()
#include "AudioConfigRenesas.h"
#endif
#else // EXTERNAL_AUDIO_OUTPUT==true
#if !defined(EXTERNAL_AUDIO_BITS)
Expand Down
161 changes: 161 additions & 0 deletions MozziGuts_impl_RENESAS.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
/*
* MozziGuts.cpp
*
* Copyright 2012 Tim Barrass.
*
* This file is part of Mozzi.
*
* Mozzi by Tim Barrass is licensed under a Creative Commons
* Attribution-NonCommercial-ShareAlike 4.0 International License.
*
*/

#if !(IS_RENESAS())
# error "Wrong implementation included for this platform"
#endif

#include <FspTimer.h>


////// BEGIN analog input code ////////

//#define MOZZI_FAST_ANALOG_IMPLEMENTED

#define getADCReading() 0


#define channelNumToIndex(channel) channel
uint8_t adcPinToChannelNum(uint8_t pin) {
return pin;
}

/** NOTE: Code needed to trigger a conversion on a new channel */
void adcStartConversion(uint8_t channel) {
#warning Fast analog read not implemented on this platform
}

/** NOTE: Code needed to trigger a subsequent conversion on the latest channel. If your platform has no special code for it, you should store the channel from
* adcStartConversion(), and simply call adcStartConversion(previous_channel), here. */
void startSecondADCReadOnCurrentChannel() {
#warning Fast analog read not implemented on this platform
}

/** NOTE: Code needed to set up faster than usual analog reads, e.g. specifying the number of CPU cycles that the ADC waits for the result to stabilize.
* This particular function is not super important, so may be ok to leave empty, at least, if the ADC is fast enough by default. */
void setupFastAnalogRead(int8_t speed) {
#warning Fast analog read not implemented on this platform
}

/** NOTE: Code needed to initialize the ADC for asynchronous reads. Typically involves setting up an interrupt handler for when conversion is done, and
* possibly calibration. */
void setupMozziADC(int8_t speed) {
#warning Fast analog read not implemented on this platform
}

/* NOTE: Most platforms call a specific function/ISR when conversion is complete. Provide this function, here.
* From inside its body, simply call advanceADCStep(). E.g.:
void stm32_adc_eoc_handler() {
advanceADCStep();
}
*/
////// END analog input code ////////



//// BEGIN AUDIO OUTPUT code ///////

/*
The strategy to output sound on this platform differs somehow from what is usually done in Mozzi.
Usually, Mozzi's circular buffer are read from the outputting device (PWM, DAC...) or committed as
a whole (for the MBED platform) and thus emptied. Once a free spot is present, Mozzi fills it with updateAudio().
Here, the DAC works straight from a buffer, outputting samples from it at a fixed rate.
This is sweet as we can branch it straight to Mozzi's buffer and it will read from it without
further action from us.
The big difference is that it *does not* empty the buffer, nor notify that something has been
read, which is okay for outputting a periodic signal where just a full period is present
in the buffer.
As a consequence we need to artificially empty the buffer at the same rate that the DAC is reading
it.
*/

FspTimer timer;

#if (EXTERNAL_AUDIO_OUTPUT != true) // otherwise, the last stage - audioOutput() - will be provided by
CircularBuffer<uint16_t> output_buffer;
#include "MozziGuts_impl_RENESAS_analog.hpp"
#endif


//////////////// TIMER ////////////////

#if EXTERNAL_AUDIO_OUTPUT == true
void timer_callback_dummy(timer_callback_args_t __attribute__((unused)) *args){defaultAudioOutput();};
#else
//void timer_callback_dummy(timer_callback_args_t __attribute__((unused)) *args){
void timer_callback_dummy(timer_callback_args_t __attribute__((unused)) *args){output_buffer.read();}; // to empty the buffer (the dac does not take care of it), a bit a waste of timer...
#endif

void timer_init() {
uint8_t type;
int8_t tindex = FspTimer::get_available_timer(type);

if (tindex < 0) {
tindex = FspTimer::get_available_timer(type, true);
}

if (tindex >= 0) {
timer.begin(TIMER_MODE_PERIODIC, type, tindex, AUDIO_RATE, 50.0,timer_callback_dummy);
timer.setup_overflow_irq();
}

#if EXTERNAL_AUDIO_OUTPUT != true // we need to set up another timer for dac caring
// note: it is running at the same speed than the other one, but could not manage
// to get the other one updating the dac and removing the samples from the buffer…
tindex = FspTimer::get_available_timer(type);

if (tindex < 0) {
tindex = FspTimer::get_available_timer(type, true);
}

if (tindex >= 0) {
FspTimer::force_use_of_pwm_reserved_timer();
timer_dac.begin(TIMER_MODE_PERIODIC, type, tindex, AUDIO_RATE, 50.0);
timer_dac.setup_overflow_irq();
dtc_cfg_extend.activation_source = timer_dac.get_cfg()->cycle_end_irq;
timer_dac.open();
#endif
timer.open();
}
}


inline void audioOutput(const AudioOutput f) {
output_buffer.write(f+AUDIO_BIAS);
}
#define canBufferAudioOutput() (!output_buffer.isFull())


static void startAudio() {
#if EXTERNAL_AUDIO_OUTPUT != true
dac_creation(AUDIO_CHANNEL_1_PIN);
#endif
timer_init(); // this need to be done between the DAC creation and initialization in the case where the on-board DAC is used, hence the ugly repetition here.
#if EXTERNAL_AUDIO_OUTPUT != true
dac_init();
R_DTC_Open(&dtc_ctrl, &dtc_cfg);
R_DTC_Enable(&dtc_ctrl);

// The following branches the DAC straight on Mozzi's circular buffer.
dtc_cfg.p_info->p_src = output_buffer.address();
dtc_cfg.p_info->length = MOZZI_BUFFER_SIZE;
R_DTC_Reconfigure(&dtc_ctrl, dtc_cfg.p_info);
timer_dac.start();
#endif
timer.start();

}

void stopMozzi() {
timer.stop();
}
//// END AUDIO OUTPUT code ///////
88 changes: 88 additions & 0 deletions MozziGuts_impl_RENESAS_analog.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
Most of this file is drawn, more or less adapted from the AnalogWave example
for Renesas board from Arduino.
It contains functions to create and start the on-board DAC (and associate timer).
*/

FspTimer timer_dac;
#include <dac.h>
#include <FspTimer.h>
#include <r_dtc.h>


volatile uint32_t pin;
uint8_t dac_bits;
dtc_instance_ctrl_t dtc_ctrl;
transfer_info_t dtc_info;
dtc_extended_cfg_t dtc_cfg_extend;
transfer_cfg_t dtc_cfg;

////////////// DAC CREATION AND FIRST INIT ///////////////////
// DAC creation, will take care of specifying the number of bits according to the
// capacity of the DAC (12 only for now) and set up the transfer mode straight from
// an external buffer.
void dac_creation(pin_size_t pinN) {
if (IS_DAC(pinN)) {
auto cfg_dac = getPinCfgs(pinN, PIN_CFG_REQ_DAC);
pin = cfg_dac[0];
if (IS_DAC_8BIT(pin)) {
dac_bits = 8;
dtc_info.transfer_settings_word_b.size = TRANSFER_SIZE_1_BYTE;
if (GET_CHANNEL(pin) == 0) {
#ifdef DAC_ADDRESS_8_CH0
dtc_info.p_dest = (void *)DAC_ADDRESS_8_CH0;
#endif
} else if (GET_CHANNEL(pin) == 1) {
#ifdef DAC_ADDRESS_8_CH1
dtc_info.p_dest = (void *)DAC_ADDRESS_8_CH1;
#endif
}
} else {
dac_bits = 12;
dtc_info.transfer_settings_word_b.size = TRANSFER_SIZE_2_BYTE;
if (GET_CHANNEL(pin) == 0) {
#ifdef DAC_ADDRESS_12_CH0
dtc_info.p_dest = (void *)DAC_ADDRESS_12_CH0;
#endif
} else if (GET_CHANNEL(pin) == 1) {
#ifdef DAC_ADDRESS_12_CH1
dtc_info.p_dest = (void *)DAC_ADDRESS_12_CH1;
#endif
}
}
}

dtc_info.transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED;
dtc_info.transfer_settings_word_b.repeat_area = TRANSFER_REPEAT_AREA_SOURCE;
dtc_info.transfer_settings_word_b.irq = TRANSFER_IRQ_END;
dtc_info.transfer_settings_word_b.chain_mode = TRANSFER_CHAIN_MODE_DISABLED;
dtc_info.transfer_settings_word_b.src_addr_mode =
TRANSFER_ADDR_MODE_INCREMENTED;
dtc_info.transfer_settings_word_b.mode = TRANSFER_MODE_REPEAT;
dtc_info.p_src = (void const *)NULL;

dtc_info.num_blocks = 0;
dtc_info.length = 0;

dtc_cfg.p_info = &dtc_info;
dtc_cfg.p_extend = &dtc_cfg_extend;

dtc_cfg_extend.activation_source = FSP_INVALID_VECTOR;
}


// DAC initialization
void dac_init() {
if (IS_DAC_8BIT(pin)) {
#if DAC8_HOWMANY > 0
if (GET_CHANNEL(pin) < DAC8_HOWMANY) {
_dac8[GET_CHANNEL(pin)].init();
}
#endif
} else {
if (GET_CHANNEL(pin) < DAC12_HOWMANY) {
_dac12[GET_CHANNEL(pin)].init();
}
}
}

12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Feedback about others is welcome.
Model | Pin | Tested
----- | --- | ------
Arduino Uno | 9 | yes
Arduino Uno R4 | A0 | yes
Arduino Duemilanove | 9 | yes
Arduino Nano | 9 | yes
Arduino Pro Mini | 9 | yes
Expand Down Expand Up @@ -355,6 +356,17 @@ Compiles and runs using Arduino's standard and Arduino_AdvancedAnalog libraries.
- PDM_VIA_SERIAL: returns a pulse-density modulated signal on one of the hardware UART of the board (Serial ports). Default is using the SERIAL2, on pin D18.
- This port should support other MBED based Arduino boards like the Arduino Portenta, in *theory*. It has only been tested on the giga but feedbacks are welcome!

### Arduino Uno R4
port by Thomas Combriat

Compiles and runs using Arduino's standard library (Renesas 0.8.7 at the time of this writing).

- A few particularities:
- Because this board has an on-board DAC (A0), but only one, STEREO is not implemented and Mozzi uses this pin. Usage of other pins using PWM for instance is not implemented yet.
- Two timers are claimed by Mozzi when using the on-board DAC, one when using `EXTERNAL_AUDIO_OUTPUT`.
- `mozziAnalogRead()` is not yet implemented and falls back on Arduino's `analogRead()`.


***

## Use and Remix
Expand Down
1 change: 1 addition & 0 deletions extras/NEWS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ NEWS
get the latest version from https://sensorium.github.io/Mozzi/

not yet released (latest github version)
- new partial port of the Arduino Uno R4
- new partial port of the Arduino Giga by Thomas Friedrichsmeier & Thomas Combriat
- new (partial) port to the STM32duino core by Thomas Friedrichsmeier
- compilation fixes to old (libmaple-based) STM32 core
Expand Down
11 changes: 9 additions & 2 deletions hardware_defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,14 @@
#define IS_GIGA() 0
#endif

#if (defined(__arm__) && !IS_STM32() && !IS_TEENSY3() && !IS_TEENSY4() && !IS_RP2040() && !IS_SAMD21() && !IS_MBED())
// Arduino Uno R4 (Renesas arch)
#if (defined(ARDUINO_FSP))
#define IS_RENESAS() 1
#else
#define IS_RENESAS() 0
#endif

#if (defined(__arm__) && !IS_STM32() && !IS_TEENSY3() && !IS_TEENSY4() && !IS_RP2040() && !IS_SAMD21() && !IS_MBED() && !IS_RENESAS())
#define IS_STM32DUINO() 1
#else
#define IS_STM32DUINO() 0
Expand All @@ -87,7 +94,7 @@
#define IS_ESP32() 0
#endif

#if !(IS_AVR() || IS_TEENSY3() || IS_TEENSY4() || IS_STM32() || IS_STM32DUINO() || IS_ESP8266() || IS_SAMD21() || IS_ESP32() || IS_RP2040() || IS_MBED())
#if !(IS_AVR() || IS_TEENSY3() || IS_TEENSY4() || IS_STM32() || IS_STM32DUINO() || IS_ESP8266() || IS_SAMD21() || IS_ESP32() || IS_RP2040() || IS_MBED() || IS_RENESAS())
#error Your hardware is not supported by Mozzi or not recognized. Edit hardware_defines.h to proceed.
#endif

Expand Down
2 changes: 1 addition & 1 deletion mozzi_pgmspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include "hardware_defines.h"

#if IS_ESP8266() || IS_ESP32() || IS_RP2040()
#if IS_ESP8266() || IS_ESP32() || IS_RP2040() || IS_RENESAS()
template<typename T> inline T FLASH_OR_RAM_READ(T* address) {
return (T) (*address);
}
Expand Down

0 comments on commit 1787763

Please sign in to comment.