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.
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)
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.
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.
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.
/* 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.