Waveform synthesis on the R4

Further to my thread Hairy output on R4 DAC the discussion has moved on from the initial problem to a wider discussion so I requested this new thread to be created.

Just been adding a timer to my wave form generator, with the help of
Under the Hood: Arduino UNO R4 - Timers - Phil Schatzmann. Although he says you can't get CD speed sampling (44.1KHz) with the audio tool kit There was no problem with the timer. I suspect it was without @susan-parker magic direct access methods. The big problem, as I see it, with the analogWave library is that if you change the scaling of the samples (volume) it just reduces the peak value. What needs to happen is that the DC bias at the mid range point needs to be maintained, if you are going to avoid the thump you get when a new note starts.

So I have been playing about with frequency and volume controls variables in the loop function as the wave plays. You can do that almost instantly unlike the analogWave library, which needed a minimum of about 50mS delay fefore any changes will take place.

At the moment I have just been seeing what they do. Here is a video of some 'effects' you can get with my odd ball wave function.

4 Likes

Nice video.
I am currently running in loop() code for a sine and a cosine by direct calculation (NOT lookup tables) which is clocking in at 226kHz.
This includes floating-point amplitude adjustment (not just shifts).
Would be slightly faster without the pin set/clear calls at 83nS each.
Of course there isn't anything else happening... :slight_smile:
Next to put it in a timer-interrupt - maybe 96kHz to start with.

As the post thing prompts, maybe we should change the thread?

Yes so does my code.

Yes I agree, I will ask a moderator to do this.

I have split the posts to a dedicated topic as requested.

Regards,
pert

3 Likes

Thanks again @susan-parker for all the code. I have it up and running on my minima and I am starting to get my head around the direct register access.

I noticed that in your DAC setup code, you have the following

//  *DAC12_DAVREFCR = 0x06;        // D/A VREF Control Register - Select External Vref; set VREFH&L pins used for LED

A few, hopefully simple, questions.

  1. Can you use the voltage on the minima AREF pin as the external reference voltage for the DAC Or is the external voltage reference in the code comments from somewhere else?

  2. If it is from somewhere else do you know of a way to supply a reference voltage to the DAC?

Hi,
The DAC uses different reference source setup and pins to the ADC.
On the Minima the DAC references are on the RxLED and TxLED pins. If you unsolder the two LED resistors R14 and R15 you can provide external reference into the DAC.
Yes, they are teeny!
Hope this helps

2 Likes

Thanks for splitting off the topic.

Update:

My loop is now running at 328kHz :slight_smile:

Cool, thanks for the update. Don't think I'll bother unsoldering anything. It's not crucial for my application but would be nice...

1 Like

Looking at the RA4M1 datasheet I note that sadly the Serial Sound Interface Enhanced (SSIE) is only available on the 100-pin package, see pin-lists section 1.7 :frowning:
However the SSIBCLK is available on P112 i.e. D10, so that might be useful if one wants to generate an audio-specific clock for something... maybe?

It could be, if you could hang an ISR off it. It would save using one of the timers to generate this.

Unfortunately, I have discovered that it needs the GPT321 for an internal clock source, so not really worth doing until I get a 100-pin version of the RA4M1.

2 Likes

Have just got the SSIE generating I2S running on the EK-RA4M1 dev board and a Pico Audio Pack with a PCM5100A DAC.

Pic show pure sine-wave in purple, and waveform constructed from 1st to 9th harmonics. This is all calculated "on-the-fly" in c. 14uS.

#define AUDIO_AMP_1      (float)0.70
#define AUDIO_AMP_2      (float)0.35
#define AUDIO_AMP_3      (float)0.20
#define AUDIO_AMP_4      (float)-0.05
#define AUDIO_AMP_5      (float)0.05
#define AUDIO_AMP_6      (float)-0.05
#define AUDIO_AMP_7		 (float)0.01
#define AUDIO_AMP_8      (float)-0.05
#define AUDIO_AMP_9		 (float)0.01

2 Likes

Have added the frequency changes cause by inharmonicity for strings, e.g. piano, etc. where the 3rd and higher harmonics are actually slightly higher than expected because of the effect of string-stiffness affects the node-points.

This is nominally the same tone as per the previous post's image, which has locked harmonics such as with a flute.

I got the info from here:
https://physics.stackexchange.com/questions/268568/why-are-the-harmonics-of-a-piano-tone-not-multiples-of-the-base-frequency

I will put up the code for this when I have it tidied up.

I think that answer ignored the fact that a piano has multiple strings and so can not be expected to have all the harmonics fixed to one fundamental, as there is more than one. This is because all the strings can't be tuned without instruments beyond that a piano tuner uses. And even if they were they would not stay that way for long.

This is also for single (metal) strings per note e.g. guitar as well - not because there are multiple strings for the same note, which is another complication.

What I am testing is how far I can push the RA4M1 processor, which is of course quite slow compared to many other 32bit offerings, with doing some sort of real-world waveform-synthesis application at CD speed samples at 44.1kHz

Also I am currently generating 10 sines at 25bit accuracy per loop through direct calculation - not using any form of lookup table - including scaling their respective amplitudes - before adding them all together.

I can also generate a bit-clock close enough to get a 44.1kHz frame rate with the standard 12.0 MHz clock to get a reliable PLL lock in the Audio DAC :slight_smile:

Hi Susan, I'm following you on Github and love the fast acces code to the Arduino R4 cores! Thanks for all the work, it functions wonderfully! I really like what you see on the scope (piano harmonics) but I cannot find the example code, here and/or on Github. Do yo perhaps have a link? Thanks in advance!!

1 Like

If you look at the first post in the now rearranged thread. That video contains the code to do it. I also changed it so that you can control exactly what harmonics and what amplitudes of these harmonics you get in the waveform.

Hi @bastenhoor , thanks for the interest.

I wrote the code in Renesas e2studio, hacking one of the examples - it's not tidy.

You can move the code sine-synthesis bit across to the Arduino IDE, but you will need the RA4M1 EK dev board to run the I2S channel - or use an SPI DAC.

Note the inner sine code is inline-assembler, which is what makes this possible, with Paul Stoffregen's 11th order Taylor Series Approximation

https://github.com/PaulStoffregen/Audio/blob/master/utility/dspinst.h

Note there is a bug in the original inline assembler code, which I found and fixed.

/***********************************************************************************************************************
 * File Name    : hal_entry.c
 * Description  : Contains data structures and functions used in hal_entry.c.
 **********************************************************************************************************************/
/***********************************************************************************************************************
 * DISCLAIMER
 * This software is supplied by Renesas Electronics Corporation and is only intended for use with Renesas products. No
 * other uses are authorized. This software is owned by Renesas Electronics Corporation and is protected under all
 * applicable laws, including copyright laws.
 * THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
 * THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED. TO THE MAXIMUM
 * EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES
 * SHALL BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR ANY REASON RELATED TO THIS
 * SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 * Renesas reserves the right, without notice, to make changes to this software and to discontinue the availability of
 * this software. By using this software, you agree to the additional terms and conditions found by accessing the
 * following link:
 * http://www.renesas.com/disclaimer
 *
 * Copyright (C) 2020 Renesas Electronics Corporation. All rights reserved.
 ***********************************************************************************************************************/
#include "common_utils.h"
#include "stdio.h"

#include "I:\Arduino Projects\6 - Uno R4 Boards\Code\Includes\susan_ra4m1_minima_register_defines.h"

// Why are the harmonics of a piano tone not multiples of the base frequency?
//  https://physics.stackexchange.com/questions/268568/why-are-the-harmonics-of-a-piano-tone-not-multiples-of-the-base-frequency

// Using Inline Assembly in C/C++
//  https://www.codeproject.com/Articles/15971/Using-Inline-Assembly-in-C-C
// Note: use the  __asm__ ("assembly code"); form.


#define BUFF_SIZE   0x1000U     //Sample audio buffer size

/* Destination buffer to receive the sample audio data */
uint8_t g_dest_buff[BUFF_SIZE] = {RESET_VALUE};

/* Source buffer to transmit the sample audio data */
const uint8_t g_src_buff[BUFF_SIZE] = {RESET_VALUE};


#define MAX_TIME    0xffff      //Max time value for time out
#define LED_PIN BSP_IO_PORT_01_PIN_06

#define SSIE_BUFF_SIZE 0x10
#define SSIE_MAX_TIME 0x01FF

#define COMPLEX_SINE
#define INHARMONICITY  // Live harmonic/tone frequency offsets
#define SINE_24_BITS   // Full 25 bit resolution - not needed for 16 bit CD quality output

/*
#define PORTBASE 0x40040000 // Port Base

#define PMISC_PWPR ((volatile unsigned char  *)(PORTBASE + 0x0D03))            // Write-Protect Register - 19.2.6
#define PWPR_PFSWE  6  // PmnPFS Register Write Enable; 0: Writing to PmnPFS register disabled, 1: Writing enabled.
#define PWPR_B0WI   7  // PFSWE Bit Write Disable; 0: Writing to the PFSWE bit enabled, 1: Writing disabled

#define P200PFS 0x0880  // Port 2 Pin Function Select Register
#define PFS_P205PFS ((volatile unsigned int *)(PORTBASE + P200PFS + (5 * 4))) // N/C - IRQ1 - CLKOUT
#define PFS_P106PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 6 * 4))) // D6
*/
void R_BSP_WarmStart(bsp_warm_start_event_t event);

/* Private functions */
static void deinit_gpt(void);
static void deinit_ssi(void);

/*******************************************************************************************************************//**
 * @addtogroup SSI_EP
 * @{
 **********************************************************************************************************************/

/* Global variables */
volatile i2s_event_t g_i2s_event = I2S_EVENT_TX_EMPTY;  //an actual event updates in callback

#define SSIE_FRAME_RATE     44117            // Adjust to get accurate match
#define AUDIO_MAX_AMPLITUDE 0x7FFFFFFF
#define AUDIO_MAX_GLOBAL    0x1FFFFFFF

#define SINE_GEN_SLOTS     10

#define AUDIO_NOTE_HZ    (float)440.00
#define AUDIO_NOTE_PHASE (float)0.00

#define AUDIO_AMP_0      (float)1.00
#define AUDIO_AMP_1      (float)0.70
#define AUDIO_AMP_2      (float)0.35
#define AUDIO_AMP_3      (float)0.20
#define AUDIO_AMP_4      (float)-0.05
#define AUDIO_AMP_5      (float)0.05
#define AUDIO_AMP_6      (float)-0.05
#define AUDIO_AMP_7		 (float)0.01
#define AUDIO_AMP_8      (float)-0.05
#define AUDIO_AMP_9		 (float)0.01
#define AUDIO_AMP_10     (float)-0.05
#define AUDIO_AMP_11	 (float)0.01
#define AUDIO_AMP_12     (float)-0.05

#define AUDIO_ZERO     0
#define AUDIO_FIRST    1
#define AUDIO_SECOND   2
#define AUDIO_THIRD    3
#define AUDIO_FORTH    4
#define AUDIO_FIFTH    5
#define AUDIO_SIXTH    6
#define AUDIO_SEVENTH  7
#define AUDIO_EIGHTH   8
#define AUDIO_NINTH    9
#define AUDIO_TENTH    10
#define AUDIO_ELEVENTH 11
#define AUDIO_TWELFTH  12

// Multiplier for inharmonicity of harmonics
#define AUDIO_INHAM_1    (float)1.00   // 440/440	Note Funderment and Second harmonics match their tones
#define AUDIO_INHAM_2    (float)1.00   // 880/880
#define AUDIO_INHAM_3    (float)1.008  // 1330/1320
#define AUDIO_INHAM_4    (float)1.011  // 1780/1760
#define AUDIO_INHAM_5    (float)1.014  // 2230/2200
#define AUDIO_INHAM_6    (float)1.015  // 2680/2640
#define AUDIO_INHAM_7    (float)1.019  // 3140/3080
#define AUDIO_INHAM_8    (float)1.026  // 3610/3520
#define AUDIO_INHAM_9    (float)1.033  // 4090/3960
#define AUDIO_INHAM_10	 (float)1.039  // 4570/4400
#define AUDIO_INHAM_11	 (float)1.045  // 5060/4840
#define AUDIO_INHAM_12	 (float)1.055  // 5570/5280


static uint32_t sine_freq_table[SINE_GEN_SLOTS];

/*
static float sine_harmonic_table[SINE_GEN_SLOTS] =
  {
  AUDIO_FIRST,  AUDIO_FIRST,
  AUDIO_SECOND, AUDIO_THIRD,
  AUDIO_FORTH,  AUDIO_FIFTH,
  AUDIO_SIXTH,  AUDIO_SEVENTH,
  AUDIO_EIGHTH, AUDIO_NINTH
  };
*/

static float sine_amp_table[SINE_GEN_SLOTS] =
  {
  AUDIO_AMP_0, AUDIO_AMP_1,
  AUDIO_AMP_2, AUDIO_AMP_3,
  AUDIO_AMP_4, AUDIO_AMP_5,
  AUDIO_AMP_6, AUDIO_AMP_7,
  AUDIO_AMP_8, AUDIO_AMP_9
  };

/*
static float sine_phase_table[SINE_GEN_SLOTS] =
  {
  AUDIO_NOTE_PHASE, AUDIO_NOTE_PHASE + 90.00,
  AUDIO_NOTE_PHASE, AUDIO_NOTE_PHASE + 90.00,
  AUDIO_NOTE_PHASE, AUDIO_NOTE_PHASE + 90.00,
  AUDIO_NOTE_PHASE, AUDIO_NOTE_PHASE + 90.00
  };
*/

// static float   sine_inharm_table[SINE_GEN_SLOTS]; // Table to hold inharmonicity ratios for harmonics

static uint32_t sine_hdelta_table[SINE_GEN_SLOTS]; // Table to hold harmonic delta adding

static int32_t sine_output_table[SINE_GEN_SLOTS];



/*******************************************************************************************************************//**
 * The RA Configuration tool generates main() and uses it to generate threads if an RTOS is used.  This function is
 * called by main() when no RTOS is used.
 **********************************************************************************************************************/
void hal_entry(void)
  {
static  int32_t out_sine, out_cosine;
static  int32_t acc_sine, acc_cosine;
    int32_t result_taylor_sine;
    int32_t result_taylor_cosine;

static float sine_freq_local = AUDIO_NOTE_HZ;

static  int32_t sine_amp_global = AUDIO_MAX_GLOBAL;
static  int32_t sine_amp_local = 0x7FFFFFFF;
       uint32_t freq_time_cosine;
static uint32_t freq_time_accumulate = 0;
static uint32_t freq_time_1st_acc    = 0;
static uint32_t freq_time_2nd_acc    = 0;
static uint32_t freq_time_3rd_acc    = 0;
static uint32_t freq_time_4th_acc    = 0;
static uint32_t freq_time_5th_acc    = 0;
static uint32_t freq_time_6th_acc    = 0;
static uint32_t freq_time_7th_acc    = 0;
static uint32_t freq_time_8th_acc    = 0;
static uint32_t freq_time_9th_acc    = 0;
static uint32_t freq_gen_sine = 0;
static uint32_t freq_time_delta = 0x1 << 24;  // This value gives 837Hz in fast loop() - was c. 300Hz
// static uint32_t freq_time_delta_1hz = (4294967295 / SSIE_FRAME_RATE);  // 97392 at 44.100kHz = 440.18Hz
static uint32_t freq_time_delta_1hz = (4294967295 / SSIE_FRAME_RATE);  // 97392 for 44.1kHz


//static uint32_t test_flags = 0;

	/* Error status */
  fsp_err_t err = FSP_SUCCESS;
  fsp_pack_version_t version = {RESET_VALUE};
  volatile uint32_t time_out = MAX_TIME;       // time_out value which is used to break the infinite loop

  /* Unlock Port Pin Write-Protection */
  *PMISC_PWPR = 0x00;  // Clear BOWI
  *PMISC_PWPR = 0x40;  // Set PFSWE

  *PFS_P205PFS   = (0b01001 << 24);  // Select PSEL[4:0] for CLKOUT - See Table 19.8
  *PFS_P205PFS  |= (0x1 << 16);      // Port Mode Control - Used as an I/O port for peripheral function
  *PFS_P205PFS  |= (0x1 << 10);      // Port Drive Capability - Set to Middle - Reduces Rise/Fall time from 6 to 4 nS

    /* Version get API for FLEX pack information */
  R_FSP_VersionGet(&version);

    /* Example Project information printed on the Console */
  APP_PRINT(BANNER_INFO, EP_VERSION, version.version_id_b.major, version.version_id_b.minor, version.version_id_b.patch);
  APP_PRINT("\r\nThe project demonstrates SSI module by transmitting and receiving \r\n"
            "the sample audio data in loop back connection and prints the status  \r\n"
            "by comparing the transmitted and received data buffers \r\n");

    /* Open SSI module */
  err = R_SSI_Open(&g_i2s_ctrl, &g_i2s_cfg);
    /* Handle error */
  if(FSP_SUCCESS != err)
    {
    APP_ERR_PRINT("\r\n SSI open failed \r\n");
        /* Trap here */
    APP_ERR_TRAP(err);
    }

    /* Open GPT in periodic mode as internal clock for SSI bit clock */
  err = R_GPT_Open(&g_timer_ctrl, &g_timer_cfg);
    /* Handle error */
  if(FSP_SUCCESS != err)
    {
    APP_ERR_PRINT("\r\n GPT open failed, Closing SSI \r\n");
    deinit_ssi();
        /* Trap here */
    APP_ERR_TRAP(err);
    }

    /* Start GPT in periodic mode */
  err = R_GPT_Start(&g_timer_ctrl);
    /* Handle error */
  if(FSP_SUCCESS != err)
    {
    APP_ERR_PRINT("\r\n GPT start failed, Closing SSI and GPT \r\n");
    deinit_ssi();
    deinit_gpt();
        /* Trap here */
    APP_ERR_TRAP(err);
    }

  // Change SSIE AudioClock source GPT Timer settings
  *GPT321_GTSTP  = 0x00000010; // Stop Timer 1
  *GPT321_GTCCRA = 0x0008;     //
  *GPT321_GTCCRB = 0x0008;     //
  *GPT321_GTCCRC = 0x0008;     //
  *GPT321_GTCCRD = 0x0008;     //
  *GPT321_GTPR   = 0x0010;     // 0x0E = 50kHz; 0x0F = 46.9kHz; 0x10 = 44.1kHz with 32bit L/R clock lengths (64bits per cycle)
  *GPT321_GTPBR  = 0x0010;     //   when Audio Clock Rate = MCK (without division)
  *GPT321_GTCR  |= 0x1;        // Start Timer 1 with a PreScale of zero

    /* Transmit and receive the sample audio data buffer using WriteRead API from source buffer to destination buffer
     * through loop back connection from SSITXD to SSIRXD */
//  err = R_SSI_WriteRead(&g_i2s_ctrl, g_src_buff, g_dest_buff, BUFF_SIZE);
    err = R_SSI_Write(&g_i2s_ctrl, g_src_buff, SSIE_BUFF_SIZE);

  /* Handle error */
  if(FSP_SUCCESS != err)
    {
    APP_ERR_PRINT("\r\n WriteRead API failed, Closing SSI and GPT \r\n");
    deinit_ssi();
    deinit_gpt();
        // Trap here
    APP_ERR_TRAP(err);
    }

    /*Wait for completion of WriteRead operation using I2S_EVENT_IDLE event and time_out. Using these both ensures
     *that the DTC transfer will be over by the time transmit underflow occurs during R_SSI_WriteRead processing.
     *This is important so the receive buffer can be flushed in the transmit underflow error processing.
     *Without this, the last frame (two samples) could be lost during R_SSI_WriteRead. */
//  while ((I2S_EVENT_IDLE != g_i2s_event) || ((time_out--) > 0));

    /* Compare the transmission of sample audio data from source buffer to destination buffer with WriteRead API */
//  int cmp_result = memcmp(g_src_buff, g_dest_buff, sizeof(g_src_buff));
/*
  if (RESET_VALUE == cmp_result)
    {
    APP_PRINT("\r\nCompared the transmitted sample audio data of SSI with received data is successful");
    }
  else
    {
    APP_PRINT("\r\nCompared the transmitted sample audio data of SSI with received data is failed");
    }
*/

//  *SSIE0_SSISCR |= (0x111 << SSISCR_TDFS_2_0); // 111: SSIFTDR has eight stages or more free space
  *SSIE0_SSISCR = 0x00000000; // 000: SSIFTDR has one stage or more free space
  *SSIE0_SSICR  |= (0x1 << SSICR_TUIEN);  // Enable transmit underflow interrupt output
//  *SSIE0_SSIFCR |= (0x1 <<  SSIFCR_TIE);  // Enable transmit data empty interrupts

  uint8_t fifo_load_count = 0;
  for(fifo_load_count = 0; fifo_load_count <= 5; fifo_load_count++)
    {
    *SSIE0_SSIFTDR_HA = 0x000A;
    *SSIE0_SSIFTDR_HA = 0x0005;
    }

  *SSIE0_SSICR  |= (0x1   << SSICR_TEN);  // Enable transmit operation

// Calculate increment for frequency.
  freq_time_delta = (uint32_t)((float)freq_time_delta_1hz * sine_freq_local);

  sine_hdelta_table[0] = (uint32_t)((float)freq_time_delta_1hz * sine_freq_local);
  sine_hdelta_table[1] = (uint32_t)((float)freq_time_delta_1hz * sine_freq_local);
  sine_hdelta_table[2] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 2) * AUDIO_INHAM_2);
  sine_hdelta_table[3] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 3) * AUDIO_INHAM_3);
  sine_hdelta_table[4] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 4) * AUDIO_INHAM_4);
  sine_hdelta_table[5] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 5) * AUDIO_INHAM_5);
  sine_hdelta_table[6] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 6) * AUDIO_INHAM_6);
  sine_hdelta_table[7] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 7) * AUDIO_INHAM_7);
  sine_hdelta_table[8] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 8) * AUDIO_INHAM_8);
  sine_hdelta_table[9] = (uint32_t)((( (float)freq_time_delta_1hz * sine_freq_local) * 9) * AUDIO_INHAM_9);


  while(true)   // While loop operates inside of write-pin timing for paired *PFS_P106PFS_BY operations
    {
//  R_IOPORT_PinWrite(&g_ioport_ctrl, LED_PIN, BSP_IO_LEVEL_HIGH);  // 590nS to set high
//	*PFS_P106PFS_BY = 0x05;  // 83nS to set high

	time_out = SSIE_MAX_TIME;
//	err = R_SSI_WriteRead(&g_i2s_ctrl, g_src_buff, g_dest_buff, SSIE_BUFF_SIZE);
//    err = R_SSI_Write(&g_i2s_ctrl, g_src_buff, SSIE_BUFF_SIZE);

//	while ((I2S_EVENT_IDLE != g_i2s_event) || ((time_out--) > 0));

	if((*SSIE0_SSIFSR & ~((uint32_t)0x1 << SSIFSR_TDE)) == (uint32_t)0x0)
	  {
	  *PFS_P106PFS_BY = 0x05;  // 83nS to set high

#ifdef COMPLEX_SINE
//	  *SSIE0_SSIFTDR = sine_output_table[0];      // Load previous loops calculated values
//	  *SSIE0_SSIFTDR = sine_output_table[1];
	  acc_sine = sine_output_table[1] + sine_output_table[2] + sine_output_table[3] + sine_output_table[4] + sine_output_table[5] + sine_output_table[6] + sine_output_table[7] + sine_output_table[8] + sine_output_table[9];
	  acc_cosine = sine_output_table[0];

	  __asm__ ("smmulr %0, %1, %2" : "=r" (out_sine) : "r" (acc_sine), "r" (sine_amp_global));
	  *SSIE0_SSIFTDR = out_sine;      //
	  __asm__ ("smmulr %0, %1, %2" : "=r" (out_cosine) : "r" (acc_cosine), "r" (sine_amp_global));
	  *SSIE0_SSIFTDR = out_cosine;

#else
	  *SSIE0_SSIFTDR = out_sine;      // Load previous loops calculated values
	  *SSIE0_SSIFTDR = out_cosine;
#endif

#ifdef INHARMONICITY
	  sine_freq_table[0] = freq_time_accumulate += freq_time_delta;
	  sine_freq_table[1] = freq_time_1st_acc += sine_hdelta_table[1];
	  sine_freq_table[2] = freq_time_2nd_acc += sine_hdelta_table[2];
	  sine_freq_table[3] = freq_time_3rd_acc += sine_hdelta_table[3];
	  sine_freq_table[4] = freq_time_4th_acc += sine_hdelta_table[4];
	  sine_freq_table[5] = freq_time_5th_acc += sine_hdelta_table[5];
	  sine_freq_table[6] = freq_time_6th_acc += sine_hdelta_table[6];
	  sine_freq_table[7] = freq_time_7th_acc += sine_hdelta_table[7];
	  sine_freq_table[8] = freq_time_8th_acc += sine_hdelta_table[8];
	  sine_freq_table[9] = freq_time_9th_acc += sine_hdelta_table[9];
#else
	  sine_freq_table[0] = freq_time_accumulate += freq_time_delta;
	  sine_freq_table[1] = freq_time_1st_acc +=  freq_time_delta;
	  sine_freq_table[2] = freq_time_2nd_acc += (freq_time_delta * 2);
	  sine_freq_table[3] = freq_time_3rd_acc += (freq_time_delta * 3);
	  sine_freq_table[4] = freq_time_4th_acc += (freq_time_delta * 4);
	  sine_freq_table[5] = freq_time_5th_acc += (freq_time_delta * 5);
	  sine_freq_table[6] = freq_time_6th_acc += (freq_time_delta * 6);
	  sine_freq_table[7] = freq_time_7th_acc += (freq_time_delta * 7);
	  sine_freq_table[8] = freq_time_8th_acc += (freq_time_delta * 8);
	  sine_freq_table[9] = freq_time_9th_acc += (freq_time_delta * 9);
#endif
	  int32_t angle, sum, p1, p2, p3, p5, p7, p9, p11, term;

#ifdef COMPLEX_SINE

	  uint8_t sine_gen_count;

	  for(sine_gen_count = 0; sine_gen_count < SINE_GEN_SLOTS; sine_gen_count++)
	    {
		freq_gen_sine = sine_freq_table[sine_gen_count];

	  if (freq_gen_sine >= 0xC0000000u || freq_gen_sine < 0x40000000u)
		{
		angle = (int32_t)freq_gen_sine; // valid from -90 to +90 degrees
		}
	  else
		{
		angle = (int32_t)(0x7FFFFFFFu - freq_gen_sine);
		}
		term = angle << 1;
		__asm__ ("smmulr %0, %1, %2" : "=r" (p1) : "r" (term), "r" (1686629713));
		__asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p1), "r" (p1));
		p2 = term << 3;
		__asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p2), "r" (p1));
		p3 = term << 3;
		term = p1 << 1;
		__asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (term), "r" (p3), "r" (1431655765));
		__asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p3), "r" (p2));
		p5 = term << 1;
		__asm__ ("smmlar %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p5), "r" (286331153));
		__asm__ ("smmulr %0, %1, %2" : "=r" (p7) : "r" (p5), "r" (p2));
		__asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p7), "r" (54539267));
#ifdef SINE_24_BITS
		__asm__ ("smmulr %0, %1, %2" : "=r" (p9) : "r" (p7), "r" (p2));
		__asm__ ("smmlar %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p9), "r" (6059919));
		__asm__ ("smmulr %0, %1, %2" : "=r" (p11) : "r" (p9), "r" (p2));
		__asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p11), "r" (440721));
#endif
		result_taylor_sine = sum << 1;

		sine_amp_local = (int)((float)AUDIO_MAX_AMPLITUDE * sine_amp_table[sine_gen_count]);
		__asm__ ("smmulr %0, %1, %2" : "=r" (out_sine) : "r" (result_taylor_sine), "r" (sine_amp_local));
		sine_output_table[sine_gen_count] = out_sine;
	    }

#else

	  if (freq_time_accumulate >= 0xC0000000u || freq_time_accumulate < 0x40000000u)
	    {
	    angle = (int32_t)freq_time_accumulate; // valid from -90 to +90 degrees
	    }
	  else
	    {
	    angle = (int32_t)(0x7FFFFFFFu - freq_time_accumulate);
	    }
	  term = angle << 1;
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p1) : "r" (term), "r" (1686629713));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p1), "r" (p1));
	  p2 = term << 3;
	  __asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p2), "r" (p1));
	  p3 = term << 3;
	  term = p1 << 1;
	  __asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (term), "r" (p3), "r" (1431655765));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p3), "r" (p2));
	  p5 = term << 1;
	  __asm__ ("smmlar %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p5), "r" (286331153));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p7) : "r" (p5), "r" (p2));
	  __asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p7), "r" (54539267));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p9) : "r" (p7), "r" (p2));
	  __asm__ ("smmlar %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p9), "r" (6059919));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p11) : "r" (p9), "r" (p2));
	  __asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p11), "r" (440721));
	  result_taylor_sine = sum << 1;
	  __asm__ ("smmulr %0, %1, %2" : "=r" (out_sine) : "r" (result_taylor_sine), "r" (sine_amp_local));

//	  *SSIE0_SSIFTDR = out_sine;  // Output value at start of loop

	  freq_time_cosine = freq_time_accumulate + 0x40000000;
	  if (freq_time_cosine >= 0xC0000000u || freq_time_cosine < 0x40000000u)
	    {
	    angle = (int32_t)freq_time_cosine; // valid from -90 to +90 degrees
	    }
	  else
	    {
	    angle = (int32_t)(0x7FFFFFFFu - freq_time_cosine);
	    }
	  term = angle << 1;
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p1) : "r" (term), "r" (1686629713));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p1), "r" (p1));
	  p2 = term << 3;
	  __asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p2), "r" (p1));
	  p3 = term << 3;
	  term = p1 << 1;
	  __asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (term), "r" (p3), "r" (1431655765));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (term) : "r" (p3), "r" (p2));
	  p5 = term << 1;
	  __asm__ ("smmlar %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p5), "r" (286331153));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p7) : "r" (p5), "r" (p2));
	  __asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p7), "r" (54539267));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p9) : "r" (p7), "r" (p2));
	  __asm__ ("smmlar %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p9), "r" (6059919));
	  __asm__ ("smmulr %0, %1, %2" : "=r" (p11) : "r" (p9), "r" (p2));
	  __asm__ ("smmlsr %0, %2, %3, %1" : "=r" (sum) : "r" (sum), "r" (p11), "r" (440721));
	  result_taylor_cosine = sum << 1;
	  __asm__ ("smmulr %0, %1, %2" : "=r" (out_cosine) : "r" (result_taylor_cosine), "r" (sine_amp_local));

//	  *SSIE0_SSIFTDR = out_cosine;  // Output value at start of loop
#endif

	  }
	else
	  {
	  *PFS_P106PFS_BY = 0x04;  // 83nS to clear low
	  }
//  R_IOPORT_PinWrite(&g_ioport_ctrl, LED_PIN, BSP_IO_LEVEL_LOW);   // 670nS to set low - (includes the while() execution?)
//	*PFS_P106PFS_BY = 0x04;  // 83nS to clear low
    }
  }


// https://renesas.github.io/fsp/group___i2_s___a_p_i.html

/*******************************************************************************************************************//**
 * @brief       User defined SSI callback
 * @param[in]   p_args
 * @return      None
 **********************************************************************************************************************/
void i2s_callback(i2s_callback_args_t *p_args)
  {
//  if( NULL != p_args)
 //   {
	if(I2S_EVENT_TX_EMPTY == p_args->event)
	  {
	*PFS_P106PFS_BY = 0x05;  // 83nS to set high

//    g_i2s_event = p_args->event; // capture callback event for validating the i2s transfer event

    *SSIE0_SSIFTDR_HA = 0x000A;
    *SSIE0_SSIFTDR_HA = 0x0005;

    *PFS_P106PFS_BY = 0x04;  // 83nS to clear low
	  }
	else if(I2S_EVENT_IDLE == p_args->event)
	  {
      *PFS_P106PFS_BY = 0x05;  // 83nS to set high
	  }

//    }
  }

/*******************************************************************************************************************//**
 * @brief       Close SSI module
 * @param[in]   None
 * @return      None
 **********************************************************************************************************************/
static void deinit_ssi(void)
  {
  fsp_err_t err = FSP_SUCCESS;
    /* Close SSI Module */
  err = R_SSI_Close(&g_i2s_ctrl);
    /* Handle error */
  if (FSP_SUCCESS != err)
    {
    APP_ERR_PRINT("\r\n R_SSI_Close API Failed \r\n");
    }
  }

/*******************************************************************************************************************//**
 * @brief       Close GPT module
 * @param[in]   None
 * @return      None
 **********************************************************************************************************************/
static void deinit_gpt(void)
  {
  fsp_err_t err = FSP_SUCCESS;
    /* Close GPT module */
  err = R_GPT_Close(&g_timer_ctrl);
    /* Handle error */
  if (FSP_SUCCESS != err)
    {
    APP_ERR_PRINT("\r\n R_SSI_Close API Failed \r\n");
    }
  }

/*******************************************************************************************************************//**
 * This function is called at various points during the startup process.  This implementation uses the event that is
 * called right before main() to set up the pins.
 *
 * @param[in]  event    Where at in the start up process the code is currently at
 **********************************************************************************************************************/
void R_BSP_WarmStart(bsp_warm_start_event_t event)
  {
  if (BSP_WARM_START_POST_C == event)
    {
		/* C runtime environment and system clocks are setup. */

		/* Configure pins. */
	R_IOPORT_Open(&g_ioport_ctrl, &g_bsp_pin_cfg);
	}
  }

/*
void usb_setup(void)
  {
  g_usb_on_usb.open(&g_basic0_ctrl, &g_basic0_cfg);
  }

void usb_print(const char* s)
  {
  g_usb_on_usb.write(&g_basic0_ctrl, (uint8_t*)s, (uint32_t)strlen(s), USB_CLASS_PCDC);
  usb_wait_write_complete();
  }

void usb_wait_write_complete(void)
  {
  usb_status_t usb_write_event = USB_STATUS_NONE;
  int32_t timeout_count = UINT16_MAX;
  fsp_err_t err = FSP_SUCCESS;
  usb_event_info_t event_info;

  do
    {
    g_usb_on_usb.eventGet(&event_info, &usb_write_event);
    --timeout_count;
    if(0 > timeout_count)
      {
      timeout_count = 0;
      break;
      }
    }
  while(USB_STATUS_WRITE_COMPLETE != usb_write_event);
  }
*/

/*******************************************************************************************************************//**
 * @} (end defgroup SSI_EP)
 **********************************************************************************************************************/

Note that the EK-RA4M1 devboard has a Segger debug chip on it as well, so it is easy to pop on the Arduino Minima bootloader and run code on the board from the Arduino IDE.

This then gives one the full debug capability of using a Segger device, the main difference is that under e2studio the registers are fully named, whereas running the Segger Jlink software separately the registers are generic.

https://www.renesas.com/en/products/microcontrollers-microprocessors/ra-cortex-m-mcus/ek-ra4m1-evaluation-kit-ra4m1-mcu-group

The main difference to the Minima is that the dev-board runs at 3.3V, not 5V, and also it has both crystals fitted.

The price is very good too, being a similar price to an Arduino DUE.

1 Like

Thanks! the EK-RA4M1 devboard is in my Mouser basket! Good to know about the 3.3v :wink: I also saw your R4 overclock topic and found boards that have a 16Mhz crystal already soldered on together with the capacitors. I'm going to study the code!

1 Like