Hairy output on R4 DAC

I am just trying to look at writing to the ADC output on the R4 Minima board.
I have a simple for loop in the loop function successively outputting the contents of an array twice by using the analogWrite call with an 10 bit resolution.

However when I look at the resultant signal on a scope I find that the D/A outputs drops, or rises, between successive samples. There seems to be a 4.6uS gap between successive analogWrite calls.

Theses screen grabs illustrate this.

I know I could add a filter to stop the drop but I am curious to know why the analogWrite function acts like this.

Is there a better way than analogWrite to output a value. ( actually from looking at the analogWave library I know there is, but I haven't been able to untangle this so far)

1 Like

you read from ADC not write to my friend...
AnalogDigitalConversor: 1v -> 0b00001111 or 0b00010000 in case vref=2v and adc bit result is 8bitwide...

the oposite would be digital to analog conversor... meaning a summator device i guess... but more complex...

like 1111 -> 10mA+10mA+10mA+10mA =40mA*100ohm=4v;

but i really dont think a microcontroller either the ic itself or a bedboard or arduino or even a pc motherboard would have this feature inside i mean, the adder is a single ic by itself , i dont think would be a good idea to manipulate voltages inside a chip you dont really wanna burn & replace... so...

cheers.

PS: analogwrite ... yea sounds like "turn my int into volts beatch!" but nope my friend, it doesnt work that way =(

PS: PWM <- is what you see on the scope, i think. and the waveform its bc u are overloading its ouput capability i suppose.

Hi Mike; I dont have a UNO R4 Minima but the data sheet

says

7 Digital Analog Converter (DAC)
The UNO R4 Minima has a DAC with up to 12-bit resolution attached to the A0 analog pin.

You will have seen this
https://www.renesas.com/us/en/document/dst/renesas-ra4m1-group-datasheet

I cant see any explanation of how it works but I'd not expect PWM from a "proper" DAC.
Are you using A0?

1 Like

No my fiend you can write to the DAC on the R4, please read the title of the post.

I know about PWM I do not want to use that I want to use the built in DAC. Please look at the examples of using A0 to generate an output signal at:-

The line

analogWave wave(DAC);

Directs the pin A0 to output a waveform.
Did you read that this is a question about an R4?
We are not in Kansas any more.

Yes John.

No on the front sheet of that data sheet it says:-

Analog
14-bit A/D Converter (ADC14)
12-bit D/A Converter (DAC12)
8-bit D/A Converter (DAC8) ×2 (for ACMPLP) Low-Power Analog Comparator (ACMPLP) × 2 perational Amplifier (OPAMP) × 4
Temperature Sensor (TSN)

The second 8 bit DAC is on pin A1 as well as A0. My traces were taken using the 12 bit D/A converter.
Cheers
Mike

From your waveforms it is apparent that analogWrite(A0); does a DAC initialization each time it is called.

The

analogWave wave(DAC);

... is different code and will only do an initialization once.

See here for my direct register setup and use DAC ADC with interrupts example here:
https://github.com/TriodeGirl/Arduino-UNO-R4-code-DAC-ADC-Ints-Fast_Pins/

Here is the barebones code, with timed measurements:

/*  Arduino UNO R4 Minima code for DAC12 demonstration
 *
 *  Susan Parker - 19th August 2023.
 *
 * This code is "AS IS" without warranty or liability. 

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
*/

// ARM-developer - Accessing memory-mapped peripherals
// https://developer.arm.com/documentation/102618/0100

// Low Power Mode Control - See datasheet section 10
#define SYSTEM 0x40010000 // System Registers
#define SYSTEM_SBYCR   ((volatile unsigned short *)(SYSTEM + 0xE00C))      // Standby Control Register
#define SYSTEM_MSTPCRA ((volatile unsigned int   *)(SYSTEM + 0xE01C))      // Module Stop Control Register A

#define MSTP 0x40040000 // Module Registers
#define MSTP_MSTPCRB   ((volatile unsigned int   *)(MSTP   + 0x7000))      // Module Stop Control Register B
#define MSTPB2   2  // CAN0
#define MSTPB8   8  // IIC1
#define MSTPB9   9  // IIC0
#define MSTPB18 18  // SPI1
#define MSTPB19 19  // SPI0
#define MSTPB22 22  // SCI9
#define MSTPB29 29  // SCI2
#define MSTPB30 30  // SCI1
#define MSTPB31 31  // SCI0

#define MSTP_MSTPCRC   ((volatile unsigned int   *)(MSTP + 0x7004))        // Module Stop Control Register C
#define MSTP_MSTPCRD   ((volatile unsigned int   *)(MSTP + 0x7008))        // Module Stop Control Register D
#define MSTPD2   2  // AGT1   - Asynchronous General Purpose Timer 1 Module
#define MSTPD3   3  // AGT0   - Asynchronous General Purpose Timer 0 Module
#define MSTPD5   5  // GPT320 and GPT321 General 32 bit PWM Timer Module
#define MSTPD6   6  // GPT162 to GPT167 General 16 bit PWM Timer Module
#define MSTPD14 14  // POEG   - Port Output Enable for GPT Module Stop
#define MSTPD16 16  // ADC140 - 14-Bit A/D Converter Module
#define MSTPD19 19  // DAC8   -  8-Bit D/A Converter Module
#define MSTPD20 20  // DAC12  - 12-Bit D/A Converter Module
#define MSTPD29 29  // ACMPLP - Low-Power Analog Comparator Module
#define MSTPD31 31  // OPAMP  - Operational Amplifier Module

// The Mode Control bits are read as 1, the write value should be 1.
// Bit value 0: Cancel the module-stop state 
// Bit value 1: Enter the module-stop state.


// ====  Asynchronous General Purpose Timer (AGT) =====
#define AGTBASE 0x40084000 
#define AGT0_AGTCR    ((volatile unsigned char  *)(AGTBASE + 0x008))  // AGT Control Register

// 12-Bit D/A Converter
#define DACBASE 0x40050000          // DAC Base - DAC output on A0 (P014 AN09 DAC)
#define DAC12_DADR0    ((volatile unsigned short *)(DACBASE + 0xE000))      // D/A Data Register 0 
#define DAC12_DACR     ((volatile unsigned char  *)(DACBASE + 0xE004))      // D/A Control Register
#define DAC12_DADPR    ((volatile unsigned char  *)(DACBASE + 0xE005))      // DADR0 Format Select Register
#define DAC12_DAADSCR  ((volatile unsigned char  *)(DACBASE + 0xE006))      // D/A A/D Synchronous Start Control Register
#define DAC12_DAVREFCR ((volatile unsigned char  *)(DACBASE + 0xE007))      // D/A VREF Control Register

// =========== Ports ============
// 19.2.5 Port mn Pin Function Select Register (PmnPFS/PmnPFS_HA/PmnPFS_BY) (m = 0 to 9; n = 00 to 15)
#define PORTBASE 0x40040000 /* Port Base */

#define P000PFS 0x0800  // Port 0 Pin Function Select Register
#define PFS_P000PFS ((volatile unsigned int *)(PORTBASE + P000PFS))            // 
#define PFS_P001PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 1 * 4))) // 
#define PFS_P002PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 2 * 4))) // 
#define PFS_P003PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 3 * 4))) // 
#define PFS_P004PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 4 * 4))) // 
#define PFS_P005PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 5 * 4))) // 
#define PFS_P006PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 6 * 4))) // 
#define PFS_P007PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 7 * 4))) // 
#define PFS_P008PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 8 * 4))) // 
// #define PFS_P009PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 9 * 4))) // Does not exist
#define PFS_P010PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (10 * 4))) // 
#define PFS_P011PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (11 * 4))) // 
#define PFS_P012PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (12 * 4))) // 
#define PFS_P013PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (13 * 4))) // 
#define PFS_P014PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (14 * 4))) // A0 / DAC12
#define PFS_P015PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (15 * 4))) // 

#define PFS_P100PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843))   // 8 bits - A5
#define PFS_P101PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 1 * 4))) // A4
#define PFS_P102PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 2 * 4))) // D5
#define PFS_P103PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 3 * 4))) // D4
#define PFS_P104PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 4 * 4))) // D3
#define PFS_P105PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 5 * 4))) // D2
#define PFS_P106PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 6 * 4))) // D6
#define PFS_P107PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 7 * 4))) // D7
#define PFS_P108PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 8 * 4))) // SWDIO
#define PFS_P109PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 9 * 4))) // D11 / MOSI
#define PFS_P110PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + (10 * 4))) // D12 / MISO
#define PFS_P111PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + (11 * 4))) // D13 / SCLK
#define PFS_P112PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + (12 * 4))) // D10 / CS
#define PFS_P300PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3))            // SWCLK (P300)
#define PFS_P301PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (01 * 4))) // D0 / RxD (P301)
#define PFS_P302PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (02 * 4))) // D1 / TxD (P302) 
#define PFS_P303PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (03 * 4))) // D9
#define PFS_P304PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (04 * 4))) // D8


// Quick loop DAC output test

void setup()
  {                                                 
  *PFS_P107PFS_BY = 0x04;               // Set D7 output low - DAC time flag pin

  setup_dac();

  *AGT0_AGTCR = 0;                     // disable Millis counter, delay etc. don't want this Interrupt messing up output stability
  }

uint16_t loop_count = 0;               // One 0 to 4095 ramp takes 2.7mS

void loop()                            // Total loop()          - takes c. 667nS per loop; or c. 750nS per loop with if()
  {
  *PFS_P107PFS_BY = 0x05;              // Set D7 output high    - takes c.  83nS

  *DAC12_DADR0 = loop_count++;         // DAC update            - takes c. 210nS  - DAC ignores top 4 bits

  *PFS_P107PFS_BY = 0x04;              // Set D7 output low     - takes c.  83nS
/*
  if(loop_count >= 4096)               // loop() and loop_count - takes c. 500nS
    loop_count = 0;                    // ... when test is true, adds c. 83nS to loop time to reset counter
*/
  }                                    // bare loop()           - takes c. 210nS


void setup_dac(void)       // Note make sure ADC is stopped before setup DAC
  {
  *MSTP_MSTPCRD &= ~(0x01 << MSTPD20);  // Enable DAC12 module
  *DAC12_DADPR    = 0x00;               // DADR0 Format Select Register - Set right-justified format
//  *DAC12_DAADSCR  = 0x80;               // D/A A/D Synchronous Start Control Register - Enable
  *DAC12_DAADSCR  = 0x00;               // D/A A/D Synchronous Start Control Register - Default
// 36.3.2 Notes on Using the Internal Reference Voltage as the Reference Voltage
  *DAC12_DAVREFCR = 0x00;               // D/A VREF Control Register - Write 0x00 first - see 36.2.5
  *DAC12_DADR0    = 0x0000;             // D/A Data Register 0 
   delayMicroseconds(10);               // Needed delay - see datasheet
  *DAC12_DAVREFCR = 0x01;               // D/A VREF Control Register - Select AVCC0/AVSS0 for Vref
  *DAC12_DACR     = 0x5F;               // D/A Control Register - 
   delayMicroseconds(5);                // 
  *DAC12_DADR0    = 0x0800;             // D/A Data Register 0 
  *PFS_P014PFS   = 0x00000000;          // Port Mode Control - Make sure all bits cleared
  *PFS_P014PFS  |= (0x1 << 15);         // ... use as an analog pin
  }

3 Likes

Thanks, yes that is the conclusion I reached, especially as the time was so close to the overhead of the digitalWrite operation on a Uno R3.

The main problem I have with the analogWave library is that it simply goes about producing the waveform in a way that no one would / should ever dream of using for audio applications. This is because the quantitation noise band will change depending on the frequency of the note being produced. This makes the simple design of any restoration filter impossible, or at least very impracticable.

As you might know I consider myself more of a hardware guy than a systems software one, and I am having great difficult understanding how the analogWave library actually gets to write to the ADC.

I used to know all about the way Arm devices access peripherals in the early days of Arm, back when they made the Archimedes computer, but I haven't caught up with the latest system, past the first Raspberry PIs

I very much admire the work you are doing with this and I have seen the code in that link you posted. But many many thanks for that example, I will try it out.
However, I think we can consider this thread closed, and thanks again.

3 Likes

Wonder if this should be considered a bug? That is the analogWrite to the DAC
does:

void CDac::analogWrite(int value) {

  if (value > (1 << analogWriteResolution())) {
    value = (1 << analogWriteResolution());
  }

  if (!pin_init) {
    pin_init = true;
    R_IOPORT_PinCfg(NULL, g_pin_cfg[pin].pin,
                    (uint32_t)((uint32_t)IOPORT_CFG_ANALOG_ENABLE |
                               (uint32_t)IOPORT_CFG_PERIPHERAL_PIN |
                               (uint32_t)IOPORT_PERIPHERAL_CAC_AD));
  }

  if (IS_DAC_8BIT(dac)) {
#if DAC8_HOWMANY > 0
    value = map(value, 0, (1 << analogWriteResolution()), 0, (1 << 8));
    open(&ctrl_dac8, &cfg);
    write(&ctrl_dac8, value);
    start(&ctrl_dac8);
#endif
  } else {
    value = map(value, 0, (1 << analogWriteResolution()), 0, (1 << 12));
    open(&ctrl_dac, &cfg);
    write(&ctrl_dac, value);
    start(&ctrl_dac);
  }
}

Maybe in this case it should detect that it is already running and if so maybe just needs to do
the: write(&ctrl_dac, value);

4 Likes

Spoof account?

Well he/she only joined today and has only posted two things, so could be.

Possibly some sort of . . . whatever the polar opposite of AI is, designed to get negative responses.
The other posts are pretty ... out there.

Well I would say so. Seems easy enough to fix with a static flag, but I wonder if there is some issue of safety in case someone switches what they are doing with this pin in a non nonsensical way?
Thanks for looking up how it works.

@susan-parker
I played about with your code and added a 256 samples long wave table which is a cycle of sin and two cycles of saw. I used this as an example of a complex wave shape in my book and I think it is quite funky. Although it looks a lot nicer than it sounds.
This is a screenshot of the wave.

and this is the code that produced it.

/*  Arduino UNO R4 Minima code for DAC12 demonstration
 *
 *  Susan Parker - 19th August 2023.
 *
 * This code is "AS IS" without warranty or liability. 
 * With wave table added by Grumpy_Mike

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
*/

// ARM-developer - Accessing memory-mapped peripherals
// https://developer.arm.com/documentation/102618/0100

// Low Power Mode Control - See datasheet section 10
#define SYSTEM 0x40010000 // System Registers
#define SYSTEM_SBYCR   ((volatile unsigned short *)(SYSTEM + 0xE00C))      // Standby Control Register
#define SYSTEM_MSTPCRA ((volatile unsigned int   *)(SYSTEM + 0xE01C))      // Module Stop Control Register A

#define MSTP 0x40040000 // Module Registers
#define MSTP_MSTPCRB   ((volatile unsigned int   *)(MSTP   + 0x7000))      // Module Stop Control Register B
#define MSTPB2   2  // CAN0
#define MSTPB8   8  // IIC1
#define MSTPB9   9  // IIC0
#define MSTPB18 18  // SPI1
#define MSTPB19 19  // SPI0
#define MSTPB22 22  // SCI9
#define MSTPB29 29  // SCI2
#define MSTPB30 30  // SCI1
#define MSTPB31 31  // SCI0

#define MSTP_MSTPCRC   ((volatile unsigned int   *)(MSTP + 0x7004))        // Module Stop Control Register C
#define MSTP_MSTPCRD   ((volatile unsigned int   *)(MSTP + 0x7008))        // Module Stop Control Register D
#define MSTPD2   2  // AGT1   - Asynchronous General Purpose Timer 1 Module
#define MSTPD3   3  // AGT0   - Asynchronous General Purpose Timer 0 Module
#define MSTPD5   5  // GPT320 and GPT321 General 32 bit PWM Timer Module
#define MSTPD6   6  // GPT162 to GPT167 General 16 bit PWM Timer Module
#define MSTPD14 14  // POEG   - Port Output Enable for GPT Module Stop
#define MSTPD16 16  // ADC140 - 14-Bit A/D Converter Module
#define MSTPD19 19  // DAC8   -  8-Bit D/A Converter Module
#define MSTPD20 20  // DAC12  - 12-Bit D/A Converter Module
#define MSTPD29 29  // ACMPLP - Low-Power Analog Comparator Module
#define MSTPD31 31  // OPAMP  - Operational Amplifier Module

// The Mode Control bits are read as 1, the write value should be 1.
// Bit value 0: Cancel the module-stop state 
// Bit value 1: Enter the module-stop state.


// ====  Asynchronous General Purpose Timer (AGT) =====
#define AGTBASE 0x40084000 
#define AGT0_AGTCR    ((volatile unsigned char  *)(AGTBASE + 0x008))  // AGT Control Register

// 12-Bit D/A Converter
#define DACBASE 0x40050000          // DAC Base - DAC output on A0 (P014 AN09 DAC)
#define DAC12_DADR0    ((volatile unsigned short *)(DACBASE + 0xE000))      // D/A Data Register 0 
#define DAC12_DACR     ((volatile unsigned char  *)(DACBASE + 0xE004))      // D/A Control Register
#define DAC12_DADPR    ((volatile unsigned char  *)(DACBASE + 0xE005))      // DADR0 Format Select Register
#define DAC12_DAADSCR  ((volatile unsigned char  *)(DACBASE + 0xE006))      // D/A A/D Synchronous Start Control Register
#define DAC12_DAVREFCR ((volatile unsigned char  *)(DACBASE + 0xE007))      // D/A VREF Control Register

// =========== Ports ============
// 19.2.5 Port mn Pin Function Select Register (PmnPFS/PmnPFS_HA/PmnPFS_BY) (m = 0 to 9; n = 00 to 15)
#define PORTBASE 0x40040000 /* Port Base */

#define P000PFS 0x0800  // Port 0 Pin Function Select Register
#define PFS_P000PFS ((volatile unsigned int *)(PORTBASE + P000PFS))            // 
#define PFS_P001PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 1 * 4))) // 
#define PFS_P002PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 2 * 4))) // 
#define PFS_P003PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 3 * 4))) // 
#define PFS_P004PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 4 * 4))) // 
#define PFS_P005PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 5 * 4))) // 
#define PFS_P006PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 6 * 4))) // 
#define PFS_P007PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 7 * 4))) // 
#define PFS_P008PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 8 * 4))) // 
// #define PFS_P009PFS ((volatile unsigned int *)(PORTBASE + P000PFS + ( 9 * 4))) // Does not exist
#define PFS_P010PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (10 * 4))) // 
#define PFS_P011PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (11 * 4))) // 
#define PFS_P012PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (12 * 4))) // 
#define PFS_P013PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (13 * 4))) // 
#define PFS_P014PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (14 * 4))) // A0 / DAC12
#define PFS_P015PFS ((volatile unsigned int *)(PORTBASE + P000PFS + (15 * 4))) // 

#define PFS_P100PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843))   // 8 bits - A5
#define PFS_P101PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 1 * 4))) // A4
#define PFS_P102PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 2 * 4))) // D5
#define PFS_P103PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 3 * 4))) // D4
#define PFS_P104PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 4 * 4))) // D3
#define PFS_P105PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 5 * 4))) // D2
#define PFS_P106PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 6 * 4))) // D6
#define PFS_P107PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 7 * 4))) // D7
#define PFS_P108PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 8 * 4))) // SWDIO
#define PFS_P109PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 9 * 4))) // D11 / MOSI
#define PFS_P110PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + (10 * 4))) // D12 / MISO
#define PFS_P111PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + (11 * 4))) // D13 / SCLK
#define PFS_P112PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + (12 * 4))) // D10 / CS
#define PFS_P300PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3))            // SWCLK (P300)
#define PFS_P301PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (01 * 4))) // D0 / RxD (P301)
#define PFS_P302PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (02 * 4))) // D1 / TxD (P302) 
#define PFS_P303PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (03 * 4))) // D9
#define PFS_P304PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x08C3 + (04 * 4))) // D8


// Quick loop DAC output test

 int waveTable [256];
 uint16_t loop_count = 0;               // One 0 to 255 cycle takes 193.2uS
 
void setup()
  {                                                 
  *PFS_P107PFS_BY = 0x04;               // Set D7 output low - DAC time flag pin

  setup_dac();

  *AGT0_AGTCR = 0;                     // disable Millis counter, delay etc. don't want this Interrupt messing up output stability
  generateTable();
  }


void loop()                            // Total loop()          - takes c. 667nS per loop; or c. 750nS per loop with if()
  {
  *PFS_P107PFS_BY = 0x05;              // Set D7 output high    - takes c.  83nS

  *DAC12_DADR0 = waveTable[(++loop_count) & 0xFF];   // DAC update            - takes c. 210nS  - DAC ignores top 4 bits

  *PFS_P107PFS_BY = 0x04;              // Set D7 output low     - takes c.  83nS
/*
  if(loop_count >= 4096)               // loop() and loop_count - takes c. 500nS
    loop_count = 0;                    // ... when test is true, adds c. 83nS to loop time to reset counter
*/
  }                                    // bare loop()           - takes c. 210nS

void generateTable(){
  const float pi = 3.1415;
  float angle;
  for(int i =0;i<128;i++){ // first part of the wave table is a sin
    angle = ((2.0 * pi)) / (128.0 / (float)i);
    waveTable[i] = (int)(2047.0 + 2047.0 * sin(angle));
  }
  // next part of the wave table is two cycles of saw wave
  int sampleValue = 2048; // start of saw wave
  for(int i =128;i<256;i++){ // second part of the wave table is a saw
   waveTable[i] = sampleValue;
   sampleValue = (sampleValue - 64) & 0xFFF; // keep to 12 bits
  }
}

void setup_dac(void)       // Note make sure ADC is stopped before setup DAC
  {
  *MSTP_MSTPCRD &= ~(0x01 << MSTPD20);  // Enable DAC12 module
  *DAC12_DADPR    = 0x00;               // DADR0 Format Select Register - Set right-justified format
//  *DAC12_DAADSCR  = 0x80;               // D/A A/D Synchronous Start Control Register - Enable
  *DAC12_DAADSCR  = 0x00;               // D/A A/D Synchronous Start Control Register - Default
// 36.3.2 Notes on Using the Internal Reference Voltage as the Reference Voltage
  *DAC12_DAVREFCR = 0x00;               // D/A VREF Control Register - Write 0x00 first - see 36.2.5
  *DAC12_DADR0    = 0x0000;             // D/A Data Register 0 
   delayMicroseconds(10);               // Needed delay - see datasheet
  *DAC12_DAVREFCR = 0x01;               // D/A VREF Control Register - Select AVCC0/AVSS0 for Vref
  *DAC12_DACR     = 0x5F;               // D/A Control Register - 
   delayMicroseconds(5);                // 
  *DAC12_DADR0    = 0x0800;             // D/A Data Register 0 
  *PFS_P014PFS   = 0x00000000;          // Port Mode Control - Make sure all bits cleared
  *PFS_P014PFS  |= (0x1 << 15);         // ... use as an analog pin
  }

So a raw "outputting every sample" pass produces a frequency of 5.176 KHz or a time of 192.2uS then I am getting a sample rate of about 1.33 Msps which seems quite impressive for this new Arduino. Of course this is much faster than anyone would possibly need for audio purposes, but it shows how much in the tank you have to play with.
Thanks again.

1 Like

Nice :slight_smile:

Thanks to @KurtE for the pointers how to get code to use the RA4M1 FPU, I have fast sine and cosine running.

Note the sinf() and cosf() functions take a variable time to execute, as can bee seen with the asymmetric yellow #1 trace.

The purple #3 trace is immediately around the sinf() call, the green #4 trace is the start and end of the code inside loop()

2 Likes

3 posts were split to a new topic: Waveform synthesis on the R4