ATTiny85; order of magnitude loss of speed when enabling map AnalogRead function

Good day all.
Have attached the sketch in question for all to review. It is a W.I.P. so it will require a few more refinements.

I have attached two 0-scope shots to simplify explaining the waveform construction.
Basically the program should provide a variable length and variable height voltage ramp with of course variable deadtime between successive ramps.

The first issue: When I uncomment the map AnalogRead functions (which are for the variable length, height and deadtime) the Minimum Ramp Marktime (on time) becomes an Order of Magnitude greater.

I require a minimum ramp marktime of about 4ms. This is accomplished by the code until I uncomment the aforementioned functions which then minimum ramp marktime becomes 40ms.

The second issue is that I have substituted variables for the pwmMax function which controls the ramp height and apparently it doesn’t.

The main issue is that the program must use a fast pwm clock to make proper use of the low pass filter on the output of the ATTiny85 which is used to refine the pwm output and actually change it into a voltage ramp. At slower (normal) clock speeds the ATTiny85 does not have the resolution necessary to produce a smooth voltage ramp.

Any suggestion as how to put the map AnalogReadfunctions in an ISR would be appreciated.

Thanks
lost_bro

/*
    This Sketch will generate a variable voltage ramp signal 
    simultaneous to a syncronized *boxed* square wave.
    //----------------------------------------------------//
    Output: PP 7; P2 of ATTiny85 to a 'Low Pass Filter'
    Values of cap & resistor to be calculated according to 
    roll-off frequency appropriate for working PWM freq. 

    ARDUINO_ATTiny ---/\/\/\--- | ---- INPUT COMPARATOR
                              |
                             ---
                             ---
                              |
                              |
                            GROUND 
                           
   This will produce a *nice* voltage ramp/saw tooth waveform.
   Can be made adjustable by substituting appropriate potentio-
   meter for resistor.   
*/
//////////////////////////////////
#define F_CPU 8000000       // This is used by delay.h library
#include <stdlib.h>
#include <util/delay.h>     // Adds delay_ms and delay_us functions  
#include <avr/io.h>         // Adds useful constants


/// Define Variables ////

int pwmMin=0;               // minimum pwm voltage (0-255)
int pwmMax=255;             // maximum pwm voltage (0-255)
float ramplength=1;         // us
float INTERVAL=3000;        // the repeat delay interval; us.


/// Define pins for ATTiny85  ///

volatile int outPin = 0;             // PhysicalPin 5; P0
int ledPin = 1;                      // PP 6; P1   
int pwmMaxPin = 1;                   // PP 7; P2           Sets Max Ramp hieght.
int ramplengthPin = 2;               // PP 3; P4           Sets Ramp length / Mark Time.
int intervalPin = 3;                 // PP 2; P3           Sets DeadTime inbetween Ramp signals.



///////////////////////////////////////////////////
void setup()
{
    
    /*
    Control Register A for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0A is 8 bits: [COM0A1:COM0A0:COM0B1:COM0B0:unused:unused:WGM01:WGM00]
    2<<COM0A0: sets bits COM0A0 and COM0A1, which (in Fast PWM mode) clears OC0A on compare-match, and sets OC0A at BOTTOM
    2<<COM0B0: sets bits COM0B0 and COM0B1, which (in Fast PWM mode) clears OC0B on compare-match, and sets OC0B at BOTTOM
    3<<WGM00: sets bits WGM00 and WGM01, which (when combined with WGM02 from TCCR0B below) enables Fast PWM mode
    */
    TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
   
    /*
    Control Register B for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0B is 8 bits: [FOC0A:FOC0B:unused:unused:WGM02:CS02:CS01:CS00]
    0<<WGM02: bit WGM02 remains clear, which (when combined with WGM00 and WGM01 from TCCR0A above) enables Fast PWM mode
    1<<CS00: sets bits CS01 (leaving CS01 and CS02 clear), which tells Timer/Counter-0 to not use a prescalar
    */
    TCCR0B = 0<<WGM02 | 1<<CS00;
   
    /*
    Control Register for Timer/Counter-1 (Timer/Counter-1 is configured with just one register: this one)
    TCCR1 is 8 bits: [CTC1:PWM1A:COM1A1:COM1A0:CS13:CS12:CS11:CS10]
    0<<PWM1A: bit PWM1A remains clear, which prevents Timer/Counter-1 from using pin OC1A (which is shared with OC0B)
    0<<COM1A0: bits COM1A0 and COM1A1 remain clear, which also prevents Timer/Counter-1 from using pin OC1A (see PWM1A above)
    1<<CS10: sets bit CS11 which tells Timer/Counter-1  to not use a prescalar
    */
    TCCR1 = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;
   
    /*
    General Control Register for Timer/Counter-1 (this is for Timer/Counter-1 and is a poorly named register)
    GTCCR is 8 bits: [TSM:PWM1B:COM1B1:COM1B0:FOC1B:FOC1A:PSR1:PSR0]
    1<<PWM1B: sets bit PWM1B which enables the use of OC1B (since we disabled using OC1A in TCCR1)
    2<<COM1B0: sets bit COM1B1 and leaves COM1B0 clear, which (when in PWM mode) clears OC1B on compare-match, and sets at BOTTOM
    */
    GTCCR = 1<<PWM1B | 2<<COM1B0;
    
    pinMode(outPin, OUTPUT);
    pinMode(ledPin, OUTPUT); 
    
  
}


//////////////////////////////////////////////////////////
void loop()
{
static uint32_t StartUs=micros();
static uint32_t StartMs=millis();
static uint8_t Pwm=0;
float  Dir=2.7;
  

  ////////// pwmMin, pwmMax and interval take an analogRead in for 0 to 1023 as output with 1023 for 5V and 0 for 0V./////
  
  
  //INTERVAL = map(analogRead(intervalPin),0,1023,0,30023);                  // adjust all maps according to
  // pwmMax = map(analogRead(pwmMaxPin),0,1023,115,1023);                    // circuit layout & potentiomenters
  //ramplength = map(analogRead(ramplengthPin),0,1023,20000,200000);         // adjust map for packetcount




  
  ///// Increment/decrement PWM on ledPin with a period of 'x RampLength' in us. /////
  
  if((micros()-StartUs) >= ramplength)  //12=20ms; 8=14ms; 6=11ms; 5=ms; 1=3ms; OnTime of Ramp;
  
    {
   
    
    ////We arrived here every 'x ramplength' in microseconds////
    StartUs=micros();

        
        Pwm+=Dir;                                 //increment or decrement PWM depending of sign of Dir 
        
        analogWrite(ledPin, Pwm);                 //Update built-in LED
        
        if(Pwm==pwmMax) Dir=-1;                   //if PWM reaches the maximum: change direction 
        {
        digitalWrite(outPin, HIGH);
        }
        if(Pwm==pwmMin)                           //if PWM reaches the minimum://30=640mv float; 40=840mv float;
        {                                         // delay_ms(40)=36ms; (20)=20ms;
        digitalWrite(outPin, LOW);
        delay(INTERVAL), Dir=+1;                //delay *INTERVAL* & change direction 
        }
  }                                               // delay_ms(40)=36ms; (20)=20ms;
  
  
}

Hello All

OK, I have changed some of the code and placed the map AnalogRead functions outside of the principle function.

Still no luck :frowning:

Any clever suggestions?
I know I am missing something fundamental regarding this issue.
Thanks
lost_bro

[code]

/*
    This Sketch will generate a variable volatage ramp signal 
    simultaneous to a syncronized *boxed* square wave.
    //----------------------------------------------------//
    Output: PP 7; P2 of ATTiny85 to a 'Low Pass Filter'
    Values of cap & resistor to be calculated according to 
    roll-off frequency appropriate for working PWM freq. 

    ARDUINO_ATTiny ---/\/\/\--- | ---- INPUT COMPARATOR
                              |
                             ---
                             ---
                              |
                              |
                            GROUND 
                           
   This will produce a *nice* voltage ramp/saw tooth waveform.
   Can be made *variable* by substituting appropriate potentio-
   meter for resistor.   
*/
//////////////////////////////////
#define F_CPU 8000000       // This is used by delay.h library
#include <stdlib.h>
#include <util/delay.h>     // Adds delay_ms and delay_us functions  
#include <avr/io.h>         // Adds useful constants


/// Define Variables ////

int pwmMin=0;               // minimum pwm voltage (0-255)
int pwmMax=255;             // maximum pwm voltage (0-255)
float ramplength;         // us
float INTERVAL=3000;        // the repeat delay interval; ms.
static uint32_t StartUs=micros();
static uint32_t StartMs=millis();
static uint8_t Pwm=0;

/// Define pins for ATTiny85  ///

volatile int outPin = 0;             // PhysicalPin 5; P0
int ledPin = 1;                      // PP 6; P1   
int pwmMaxPin = 1;                   // PP 7; P2           Sets Max Ramp hieght.
int ramplengthPin = 2;               // PP 3; P4           Sets Ramp length / Mark Time.
int intervalPin = 3;                 // PP 2; P3           Sets DeadTime inbetween Ramp signals.



///////////////////////////////////////////////////
void setup()
{
    
    /*
    Control Register A for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0A is 8 bits: [COM0A1:COM0A0:COM0B1:COM0B0:unused:unused:WGM01:WGM00]
    2<<COM0A0: sets bits COM0A0 and COM0A1, which (in Fast PWM mode) clears OC0A on compare-match, and sets OC0A at BOTTOM
    2<<COM0B0: sets bits COM0B0 and COM0B1, which (in Fast PWM mode) clears OC0B on compare-match, and sets OC0B at BOTTOM
    3<<WGM00: sets bits WGM00 and WGM01, which (when combined with WGM02 from TCCR0B below) enables Fast PWM mode
    */
    TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
   
    /*
    Control Register B for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0B is 8 bits: [FOC0A:FOC0B:unused:unused:WGM02:CS02:CS01:CS00]
    0<<WGM02: bit WGM02 remains clear, which (when combined with WGM00 and WGM01 from TCCR0A above) enables Fast PWM mode
    1<<CS00: sets bits CS01 (leaving CS01 and CS02 clear), which tells Timer/Counter-0 to not use a prescalar
    */
    TCCR0B = 0<<WGM02 | 1<<CS00;
   
    /*
    Control Register for Timer/Counter-1 (Timer/Counter-1 is configured with just one register: this one)
    TCCR1 is 8 bits: [CTC1:PWM1A:COM1A1:COM1A0:CS13:CS12:CS11:CS10]
    0<<PWM1A: bit PWM1A remains clear, which prevents Timer/Counter-1 from using pin OC1A (which is shared with OC0B)
    0<<COM1A0: bits COM1A0 and COM1A1 remain clear, which also prevents Timer/Counter-1 from using pin OC1A (see PWM1A above)
    1<<CS10: sets bit CS11 which tells Timer/Counter-1  to not use a prescalar
    */
    TCCR1 = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;
   
    /*
    General Control Register for Timer/Counter-1 (this is for Timer/Counter-1 and is a poorly named register)
    GTCCR is 8 bits: [TSM:PWM1B:COM1B1:COM1B0:FOC1B:FOC1A:PSR1:PSR0]
    1<<PWM1B: sets bit PWM1B which enables the use of OC1B (since we disabled using OC1A in TCCR1)
    2<<COM1B0: sets bit COM1B1 and leaves COM1B0 clear, which (when in PWM mode) clears OC1B on compare-match, and sets at BOTTOM
    */
    GTCCR = 1<<PWM1B | 2<<COM1B0;
    
    pinMode(outPin, OUTPUT);
    pinMode(ledPin, OUTPUT); 
    
  
}

//////////////////////////////////////////////////////////
void AnalogRead ()
{
  ////////// pwmMin, pwmMax and interval take an analogRead in for 0 to 1023 as output with 1023 for 5V and 0 for 0V./////
  INTERVAL = map(analogRead(intervalPin),0,1023,0,30023);                  // adjust all maps according to
  pwmMax = map(analogRead(pwmMaxPin),0,1023,115,1023);                    // circuit layout & potentiomenters
  ramplength = map(analogRead(ramplengthPin),0,1023,20000,200000);         // adjust map for packetcount
}

//////////////////////////////////////////////////////////
void Signal()
{

   float   Dir=2.7;
  
  if((micros()-StartUs) >= ramplength)             ///// Increment/decrement PWM on ledPin with a period of 'x RampLength' in us. /////
                                                        //12=20ms; 8=14ms; 6=11ms; 5=ms; 1=3ms; OnTime of Ramp;
   {
     Pwm += Dir;                                   //increment or decrement PWM depending of sign of Dir 
        
        analogWrite(ledPin, Pwm);                 //Update built-in LED
        
        if(Pwm==pwmMax) Dir=-1;                   //if PWM reaches the maximum: change direction 
        {
        digitalWrite(outPin, HIGH);
        }
        if(Pwm==pwmMin)                           //if PWM reaches the minimum://30=640mv float; 40=840mv float;
        {                                         // delay_ms(40)=36ms; (20)=20ms;
        digitalWrite(outPin, LOW);
        delay(INTERVAL), Dir=+1;                //delay *INTERVAL* & change direction 
        }
        
        StartUs = micros();                     // and save the time when we made the change
  }                                               // delay_ms(40)=36ms; (20)=20ms;
     
  
  
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////

void loop ()
  {
    
     Signal ();

   
     AnalogRead ();
     
    
  }
  
//////////////////////////////////////////////////////////////////////////////////////////////////////////

[/code]

Not sure what you intend, but there are some strange features to your code:

(delay(INTERVAL), Dir=+1);                //delay *INTERVAL* & change direction

Is your use of the comma operator above intentional? Why the parentheses?

     AnalogRead ();

Does nothing. Normally, you would set a variable equal to the result.

  unsigned long int  Dir=2.7;

An integer cannot have a decimal fraction.

Hello jremington

jremington:
Not sure what you intend, but there are some strange features to your code:

(delay(INTERVAL), Dir=+1);                //delay *INTERVAL* & change direction

Is your use of the comma operator above intentional? Why the parentheses?

Sorry got ahead of myself with the paste & copy, I have rewritten this code probably 50 times now!
Have removed the parentheses.......

     AnalogRead ();

Does nothing. Normally, you would set a variable equal to the result.

AnalogRead () in this part of the code is a loop function: just above in the same sketch, you will see the
void AnalogRead () call.

  unsigned long int  Dir=2.7;

An integer cannot have a decimal fraction.

Yes, not correct, have changed it to float.

I will rerun the code again tomorrow when I have time and report back.

Thanks
lost_bro

INTERVAL and ramplength are declared as floats. The map function that you are getting their values from, and the delay and micros() comparisons that you are sending them to all use integral values. This requires useless converting the values to and from floats every time through loop. I don't know if this would be responsible for a 10x performance hit, but it's were I would start. Change those variables to normal integral types, but don't need floats for them.

The use of the curlies below might indicate a bug (not related to your issue).

    if (Pwm == pwmMax) Dir = -1;              //if PWM reaches the maximum: change direction

{
      digitalWrite(outPin, HIGH);
    }

The digitaWrite will always be executed by the code.

map() is a slow function and you have 3 of them. Perhaps you could replace it with some left or right shifts (division or multiplication) and some addition.

What is the point of having pwmMax greater than 255?

Calling your function AnalogRead() is just confusing - give it a sensible and non-confusing name.

…R

lost_bro:
Yes, not correct, have changed it to float.

Why did you need it to be a float when you are adding it's value to an integral type?

Good day All
I really appreciate all the comments and assistance that you are providing.
OK, I have corrected (to the best of my ability) taking into consideration all above posted comments and have reposted below the now updated version of same sketch:

Still no joy :confused:

I am beginning to believe that the only way to implement this type of architecture is to implement an ISR function.

Please note that in the below posted o-scope screen shots, I have only enabled one map AnalogRead function, NOT the three that are in the program.

The code still experiences the same result: without mapAnalogRead = 4ms; with mapAnalogRead = 40ms.

I would appreciate any and all other comments/options to try with this code.
Thanks to all for their time and patience with this.
lost_bro

[code]


/*
    This Sketch will generate a variable volatage ramp signal 
    simultaneous to a syncronized *boxed* square wave.
    //----------------------------------------------------//
    Output: PP 7; P2 of ATTiny85 to a 'Low Pass Filter'
    Values of cap & resistor to be calculated according to 
    roll-off frequency appropriate for working PWM freq. 

    ARDUINO_ATTiny ---/\/\/\--- | ---- INPUT COMPARATOR
                              |
                             ---
                             ---
                              |
                              |
                            GROUND 
                           
   This will produce a *nice* voltage ramp/saw tooth waveform.
   Can be made *variable* by substituting appropriate potentio-
   meter for resistor.   
*/
//////////////////////////////////
#define F_CPU 8000000       // This is used by delay.h library
#include <stdlib.h>
#include <util/delay.h>     // Adds delay_ms and delay_us functions  
#include <avr/io.h>         // Adds useful constants


/// Define Variables ////

int pwmMin=0;               // minimum pwm voltage (0-255)
int pwmMax=255;             // maximum pwm voltage (0-255)
int ramplength=4;         // us
int INTERVAL=3000;        // the repeat delay interval; ms.
static uint32_t StartUs=micros();
static uint32_t StartMs=millis();
static uint8_t Pwm=0;

/// Define pins for ATTiny85  ///

volatile int outPin = 0;             // PhysicalPin 5; P0
int ledPin = 1;                      // PP 6; P1   
int pwmMaxPin = 1;                   // PP 7; P2           Sets Max Ramp hieght.
int ramplengthPin = 2;               // PP 3; P4           Sets Ramp length / Mark Time.
int intervalPin = 3;                 // PP 2; P3           Sets DeadTime inbetween Ramp signals.



///////////////////////////////////////////////////
void setup()
{
    
    /*
    Control Register A for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0A is 8 bits: [COM0A1:COM0A0:COM0B1:COM0B0:unused:unused:WGM01:WGM00]
    2<<COM0A0: sets bits COM0A0 and COM0A1, which (in Fast PWM mode) clears OC0A on compare-match, and sets OC0A at BOTTOM
    2<<COM0B0: sets bits COM0B0 and COM0B1, which (in Fast PWM mode) clears OC0B on compare-match, and sets OC0B at BOTTOM
    3<<WGM00: sets bits WGM00 and WGM01, which (when combined with WGM02 from TCCR0B below) enables Fast PWM mode
    */
    TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
   
    /*
    Control Register B for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0B is 8 bits: [FOC0A:FOC0B:unused:unused:WGM02:CS02:CS01:CS00]
    0<<WGM02: bit WGM02 remains clear, which (when combined with WGM00 and WGM01 from TCCR0A above) enables Fast PWM mode
    1<<CS00: sets bits CS01 (leaving CS01 and CS02 clear), which tells Timer/Counter-0 to not use a prescalar
    */
    TCCR0B = 0<<WGM02 | 1<<CS00;
   
    /*
    Control Register for Timer/Counter-1 (Timer/Counter-1 is configured with just one register: this one)
    TCCR1 is 8 bits: [CTC1:PWM1A:COM1A1:COM1A0:CS13:CS12:CS11:CS10]
    0<<PWM1A: bit PWM1A remains clear, which prevents Timer/Counter-1 from using pin OC1A (which is shared with OC0B)
    0<<COM1A0: bits COM1A0 and COM1A1 remain clear, which also prevents Timer/Counter-1 from using pin OC1A (see PWM1A above)
    1<<CS10: sets bit CS11 which tells Timer/Counter-1  to not use a prescalar
    */
    TCCR1 = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;
   
    /*
    General Control Register for Timer/Counter-1 (this is for Timer/Counter-1 and is a poorly named register)
    GTCCR is 8 bits: [TSM:PWM1B:COM1B1:COM1B0:FOC1B:FOC1A:PSR1:PSR0]
    1<<PWM1B: sets bit PWM1B which enables the use of OC1B (since we disabled using OC1A in TCCR1)
    2<<COM1B0: sets bit COM1B1 and leaves COM1B0 clear, which (when in PWM mode) clears OC1B on compare-match, and sets at BOTTOM
    */
    GTCCR = 1<<PWM1B | 2<<COM1B0;
    
    pinMode(outPin, OUTPUT);
    pinMode(ledPin, OUTPUT); 
    
  
}


//////////////////////////////////////////////////////////
void loop()
{

int  Dir=2.;
  

  ////////// pwmMin, pwmMax and interval take an analogRead in for 0 to 1023 as output with 1023 for 5V and 0 for 0V./////
  
  
  //INTERVAL = map(analogRead(intervalPin),0,1023,0,30023);                  // adjust all maps according to
  // pwmMax = map(analogRead(pwmMaxPin),0,1023,1,255);                    // circuit layout & potentiomenters
 // ramplength = map(analogRead(ramplengthPin),0,1023,7000,100000);         // adjust map for packetcount




  
  ///// Increment/decrement PWM on ledPin with a period of 'x RampLength' in us. /////
  
  if((micros()-StartUs) >= ramplength)  //12=20ms; 8=14ms; 6=11ms; 5=ms; 1=3ms; OnTime of Ramp;
  
    {
   
    
    ////We arrived here every 'x ramplength' in microseconds////
    StartUs=micros();

        
        Pwm+=Dir;                                 //increment or decrement PWM depending of sign of Dir 
        
        analogWrite(ledPin, Pwm);                 //Update built-in LED
        
        if(Pwm==pwmMax) Dir=-1;                   //if PWM reaches the maximum: change direction 
        
        digitalWrite(outPin, HIGH);
        
        if(Pwm==pwmMin)                           //if PWM reaches the minimum://30=640mv float; 40=840mv float;
        {                                        // delay_ms(40)=36ms; (20)=20ms;
        digitalWrite(outPin, LOW);
        delay(INTERVAL);
        Dir=+1;                //delay *INTERVAL* & change direction 
        }
  }                                               // delay_ms(40)=36ms; (20)=20ms;
  
  
}

[/code]

You have rampLength defined as 1 µs. The shortest interval used by micros() is 4 µs.

Can you provide an english (not code) outline of what the program is intended to do?

...R

Robin2:
You have rampLength defined as 1 µs. The shortest interval used by micros() is 4 µs.

Can you provide an english (not code) outline of what the program is intended to do?

...R

Good evening Robin2
Yes, I will post a outline of the process in English.
Did you see the prelog to the sketch up top........ it describes the basis of the code.
Should I outline it as comments step/step on the Sketch?
I have to step out, will be back later tonite.
Thanks
lost_bro

Hello All:

To help define the process:

Overall the process should take input from three distinct potentiometers:

1- control ramp height. PP 7; P2

2- control ramp length (marktime). PP 3; P4

3- control signal deadtime (time between ramp's marktime, ie; ramp frequency). PP 2; P3

The aTTiny85 processor is set to fast pwm by code contained in void setup ().

The pwm output will be on PP 6; P1, (ledPin= onboardled).

The pwm is run directly into a low pass filter to give the required 'VOLTAGE RAMP'.
This ramp can be fine tuned according to the final necessities by varying the values of the low pass filter (ie: roll-off).

Basically the three potentiometers will provide control over the three aforementioned characteristics of the final filtered voltage ramp. The ramp height, ramp length and deadtime between ramps,(ie ramp frequency) will be controlled by the ATTiny's three potentiometers.

Simultaneously to the ramp signal generation, a concurrent boxed square wave will be outputted on PhysicalPin 5; P0 (outPin in code). This square wave will be in-sync with the generated ramp wave (marktime).

This can be seen from scope shots of the actual filtered circuit which I have up and running.

I have used the same map AnalogRead functions with the ATTiny85 prior to this code with success.

I did NOT experience the order of magnitude speed loss I am seeing now with this code.

I did use an ISR function (pin change interrupt) to call the void loops().

The problem I see with the ISR in this case is that I am using fast pwm to generate a pwm which as you know has many leading & falling edges for each filtered ramp generated by the low pass filter.

So an ISR using pin change interrupt protocol will trigger multiple times during the span of just one filtered ramp period. And I need it to trigger just once each ramp period.

I have thought about using the 'INTO' option which renders an external interrupt which could be trigger by a feedback signal taken off of the filtered ramp output on the lowpass filter pcb. But..... the ATTiny85 only has this option on one distinct pin. If I remember correctly, it is one of the Analog input pins which are ALL used for the three pots.

If you would like to see the old code I used for the ATTiny85 ISR pin change interrupt protocol (which seems to work as intended..) I can post it. This code does not suffer from the order of magnitude speed loss that this new code does.

Also, I am not sure what speed this Attiny85 chip is running at, this could be another relevant issue, especially since it is NOT the same Chinese digispark copy that I used in the last project.

If anyone has the link to a working tutorial on how to flash/burn the attiny85 and get the 16.5Mhz processor speed (this is what the digispark uses, I think :slight_smile: ) I would appreciate it.

edit: I see there is an option in the IDE to burn bootloader but the Attiny85 I have came running the blink/flash led program, so I don't really know what the deal is with this chip.

Regarding the 1us on the " int ramplength=1; // us " .

I found that by running the program and checking the timing w/ the scope, the values do NOT coincide precisely.......(1000us = 1ms) any value less than the 4ms minimum as shown on the scope only returns the same 4ms signal timing until I uncomment any one of the map AnalogRead functions which then jumps to 40ms minimum marktime.

I have only written a few programs for Arduino, so I not an expert dealing with the coding......

I appreciate any and all help with this.
Thanks
lost_bro

EDIT: forgot to mention that it would be nice to have a final variable ramp length of 1-30 milliseconds and a frequency of .1 to 50Hz. So as you can see, the actual ramp characteristics are comparatively slow, only in millis.

But............ the fast pwm must be maintained in order to generate the necessary pwm speed required for rendering of the final voltage ramp via filtering of the pwm.

I'm not sure I understand all that - but probably because I am not familiar with waveform generation and no because your description is inadequate.

If I understand from the last part, you want a wave that goes from 0 to max in a time between 1 millisec and 30 millisecs (and declines the same way?) AND you want this wave to be repeatable at a rate between 1 every 10 seconds and 50 per second. How will the time when the wave is NOT ramping up or down be allocated - especially for the very low frequencies?

Perhaps you can post a diagram showing some examples of the output you want.

I'm just guessing now ...
I imagine you want to create a PWM signal and by steps change its duty cycle from very low to very high to give effect to the ramp up (and vice versa for down).

And you want to use 3 potentiometers to allow the user to change the ramp interval, the repeat frequency and something else?

If that is a reasonable description mybe you can explain your problem by reference to it (i.e. terms that I understand).

It would also help to describe the range of numbers that need to be used to produce the range of duty cycles - i.e. the numbers that must be given the the appropriate Timer register.

And am I correct to think that the timing of the longer periods is done using millis() or micros()?

...R

OK, I get what’s going on now. The scope trace helped.

Details of your requirements become important here when you start needing to do tradeoffs. Since the pots are part of your user interface, there’s no need at all to check them at every pass through loop. UI elements will usually be way at the bottom in terms of priority. The timescales that humans and microcontrollers work at are so different that “100 millisecond delay” is perceived as instantaneous for a human, but an eternity for a microcontroller.

Since you doubt your clock speed, upload a simple sketch that does:

void setup() {pinMode(X, OUTPUT);}
void loop()
{
digitalWrite(X,HIGH);
delay(1000);
digitalWrite(X,LOW);
delay(1000);
}

and make sure the toggling is happening at the expected 1 second intervals. If it is, you’re good.

As for the actual operation of your code, right now you have this completely backwards. You have a dead-time in between ramps where you do nothing, and then you try to do everything at the same time you are generating the ramp pulse. You should be ignoring the pots during the ramp generation since it is time-critical, and using the unimportant dead time between pulses to read them.

Replace your delay(INTERVAL); statement with this:

auto delayStartTime = millis();
do
{
  INTERVAL = map(analogRead(intervalPin),0,1023,0,30023);                  // adjust all maps according to
  pwmMax = map(analogRead(pwmMaxPin),0,1023,1,255);                    // circuit layout & potentiomenters
 ramplength = map(analogRead(ramplengthPin),0,1023,7000,100000);         // adjust map for packetcount
} while(millis()-delayStartTime<INTERVAL);

The do-while construction guarantees that the pots will be read once before the conditional is evaluated, so they will be read even if the INTERVAL is turned all the way down to 0.

Good day All:
Thanks again for taking the time to review this code:

OK, I followed the advice of Jiggy-Ninja and rewrote according to his suggestions.

I now post the re-worked code …

The code now produces a potentiometer controlled variable ramp marktime of 4-24ms :slight_smile: :slight_smile: :slight_smile:

and a potentiometer controlled variable DeadTime (time off between ramps) :slight_smile: :slight_smile: :slight_smile:

But… now the third potentiometer (ramp height control) is NOT working :confused: :frowning:

So, now that the map Analog function is NO longer causing havoc with the timing, I now cannot control the ramp height.

The code is using ‘pwm == pwmMax’ and ‘pwm==pwmMin’;

‘pwmMax’ value is taken from a map AnalogRead function.

‘pwmMin’ value is a ‘int’ constant listed with the variables.

Funny thing is that the ‘pwmMin’ value DOES work, giving the ramp deadtime (0ff-time) a minimum voltage float value as intended. I can control the deadtime float voltage by changing the ‘int’ value for ‘pwmMin’.

The ‘pwmMax’ value, no matter if it is in form of an ‘int’ value or taken from the potentiometer does NOT work.

So, now I need to figure out what happened to this statement as well.

As per request to help with the confusion, I have attached two more o-scope screenshots of the waveforms in question.

Also annotated ‘pwmMax’ & ‘pwmMin’ for reference.

Thanks again for all the suggestions.
Please let me know if anyone has any suggestion concerning the non working pwm Max & Min function.

lost_bro

EDIT: I did run the one second led flash script, and it scopes out to about 980-990ms both Marktime & Deadtime. So I guess we are good with the timing…

Also, I did change the code to reflect Micros instead of Millis… when I use Millis, I do NOT get a response on the scope equal to the correct/calculated in code timing values.

I will have to spend more time on this tomorrow after work again.

/*
    This Sketch will generate a variable volatage ramp signal 
    simultaneous to a syncronized *boxed* square wave.
    //----------------------------------------------------//
    Output: PP 7; P2 of ATTiny85 to a 'Low Pass Filter'
    Values of cap & resistor to be calculated according to 
    roll-off frequency appropriate for working PWM freq. 

    ARDUINO_ATTiny ---/\/\/\--- | ---- INPUT COMPARATOR
                              |
                             ---
                             ---
                              |
                              |
                            GROUND 
                           
   This will produce a *nice* voltage ramp/saw tooth waveform.
   Can be made *variable* by substituting appropriate potentio-
   meter for resistor.   
*/
//////////////////////////////////
//#define F_CPU 8000000       // This is used by delay.h library
#include <stdlib.h>
//#include <util/delay.h>       // Adds delay_ms and delay_us functions  
#include <avr/io.h>               // Adds useful constants


/// Define Variables ////

int pwmMin=30;               // minimum pwm voltage (0-255)
int pwmMax=55;               // maximum pwm voltage (0-255)
int ramplength=4;              // us
int INTERVAL=30000;          // the repeat delay interval; ms.
int delayStartTime;
static uint32_t StartUs=micros();
static uint32_t StartMs=millis();
static uint8_t Pwm=0;

/// Define pins for ATTiny85  ///

volatile int outPin = 0;               // PhysicalPin 5; P0
int ledPin = 1;                          // PP 6; P1   
int pwmMaxPin = 1;                   // PP 7; P2           Sets Max Ramp hieght.
int ramplengthPin = 2;               // PP 3; P4           Sets Ramp length / Mark Time.
int intervalPin = 3;                    // PP 2; P3           Sets DeadTime inbetween Ramp signals.



///////////////////////////////////////////////////
void setup()
{
    
    /*
    Control Register A for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0A is 8 bits: [COM0A1:COM0A0:COM0B1:COM0B0:unused:unused:WGM01:WGM00]
    2<<COM0A0: sets bits COM0A0 and COM0A1, which (in Fast PWM mode) clears OC0A on compare-match, and sets OC0A at BOTTOM
    2<<COM0B0: sets bits COM0B0 and COM0B1, which (in Fast PWM mode) clears OC0B on compare-match, and sets OC0B at BOTTOM
    3<<WGM00: sets bits WGM00 and WGM01, which (when combined with WGM02 from TCCR0B below) enables Fast PWM mode
    */
    TCCR0A = 2<<COM0A0 | 2<<COM0B0 | 3<<WGM00;
   
    /*
    Control Register B for Timer/Counter-0 (Timer/Counter-0 is configured using two registers: A and B)
    TCCR0B is 8 bits: [FOC0A:FOC0B:unused:unused:WGM02:CS02:CS01:CS00]
    0<<WGM02: bit WGM02 remains clear, which (when combined with WGM00 and WGM01 from TCCR0A above) enables Fast PWM mode
    1<<CS00: sets bits CS01 (leaving CS01 and CS02 clear), which tells Timer/Counter-0 to not use a prescalar
    */
    TCCR0B = 0<<WGM02 | 1<<CS00;
   
    /*
    Control Register for Timer/Counter-1 (Timer/Counter-1 is configured with just one register: this one)
    TCCR1 is 8 bits: [CTC1:PWM1A:COM1A1:COM1A0:CS13:CS12:CS11:CS10]
    0<<PWM1A: bit PWM1A remains clear, which prevents Timer/Counter-1 from using pin OC1A (which is shared with OC0B)
    0<<COM1A0: bits COM1A0 and COM1A1 remain clear, which also prevents Timer/Counter-1 from using pin OC1A (see PWM1A above)
    1<<CS10: sets bit CS11 which tells Timer/Counter-1  to not use a prescalar
    */
    TCCR1 = 0<<PWM1A | 0<<COM1A0 | 1<<CS10;
   
    /*
    General Control Register for Timer/Counter-1 (this is for Timer/Counter-1 and is a poorly named register)
    GTCCR is 8 bits: [TSM:PWM1B:COM1B1:COM1B0:FOC1B:FOC1A:PSR1:PSR0]
    1<<PWM1B: sets bit PWM1B which enables the use of OC1B (since we disabled using OC1A in TCCR1)
    2<<COM1B0: sets bit COM1B1 and leaves COM1B0 clear, which (when in PWM mode) clears OC1B on compare-match, and sets at BOTTOM
    */
    GTCCR = 1<<PWM1B | 2<<COM1B0;
    
    pinMode(outPin, OUTPUT);
    pinMode(ledPin, OUTPUT); 
    
  
}


//////////////////////////////////////////////////////////
void loop()
{

int  Dir=2;
  
////// Increment/decrement PWM on ledPin with a period of 'x RampLength' in us. /////
  
  if((micros()-StartUs) >= ramplength)                             // ramp marktime;
  
    {
   
    
    ////We arrived here every 'x ramplength' in microseconds////
    

        
        Pwm+=Dir;                            //increment or decrement PWM depending of sign of Dir 
        
        analogWrite(ledPin, Pwm);         //Update built-in LED
        
        if(Pwm==pwmMax) Dir=-1;      //if PWM reaches the maximum: change direction 
        
        digitalWrite(outPin, HIGH);
        
        if(Pwm==pwmMin) 
                                                     //if PWM reaches the minimum:
        {                                             
          digitalWrite(outPin, LOW);
          delay(INTERVAL);
          Dir=+1;                                 //delay *INTERVAL* & change direction 
          delayStartTime = micros();
              do
              {
                INTERVAL   = map(analogRead(intervalPin),0,1023,800,50000);  
                pwmMax     = map(analogRead(pwmMaxPin),0,1023,55,255);    
                ramplength = map(analogRead(ramplengthPin),0,1023,2,11800); 
              }
              while(micros()-delayStartTime<=INTERVAL);
                                                
        }
        StartUs=micros();
  }                                              
  
}

Good day Robin2

I hope the following will explain a little better about what I'm trying to achieve.

Robin2:
I'm not sure I understand all that - but probably because I am not familiar with waveform generation and no because your description is inadequate.

---------- I have attached some o-scope screenshots to help with the explanation.---------

If I understand from the last part, you want a wave that goes from 0 to max in a time between 1 millisec and 30 millisecs (and declines the same way?)

------What I am trying to obtain is a ramp with a gradual build-up to pwmMax and a rapid fall to pwmMin.----

------Actually I tried to use: 'if(Pwm==pwmMax) Dir=-1; ' and ' Dir=+1; ' to control the pitch of the ramp. It did work under some code configurations, and now it doesn't. -------

Perhaps you can post a diagram showing some examples of the output you want.

-------- Yes, have posted , please check scope shots.-----------

And you want to use 3 potentiometers to allow the user to change the ramp interval, the repeat frequency and something else?


1- ramp Marktime (ontime)
2- ramp Deadtime (off time)
3- ramp height (ie; pwmMax & pwmMin functions)

Thanks for the assistance Robin2
take care, peace
lost_bro

lost_bro:
The code now produces a potentiometer controlled variable ramp marktime of 4-24ms :slight_smile: :slight_smile: :slight_smile:

Woot!

and a potentiometer controlled variable DeadTime (time off between ramps) :slight_smile: :slight_smile: :slight_smile:

Hollah!

But… now the third potentiometer (ramp height control) is NOT working :confused: :frowning:

What?

Were you able to control the pulse height before? Because looking over you code it looks like it was malfunctioning from the start. It appears that your intention is to ramp up to pwmmax, reverse direction, then ramp down to pwmmin, then pause for the dead-time. But the scope traces you are posting show the ramp going all the way to Vcc then straight back down to 0. I think the problem’s here:

//////////////////////////////////////////////////////////
void loop()
{

int  Dir=2;
 
////// Increment/decrement PWM on ledPin with a period of 'x RampLength' in us. /////

Why do you set Dir on every pass through loop() here on line 99? Why do you set it to 2 here when every other place uses 1? How about you just do enum ramp_direction {FALLING=-1, PAUSED=0, RISING=1}; and use those values? You probably also intended Dir to be [b]static[/b].

        StartUs=micros();

Minor point here on line 136, this is not the recommended way of advancing the interval. Use StartUs += rampLength; instead.

EDIT: I did run the one second led flash script, and it scopes out to about 980-990ms both Marktime & Deadtime. So I guess we are good with the timing…

Also, I did change the code to reflect Micros instead of Millis… when I use Millis, I do NOT get a response on the scope equal to the correct/calculated in code timing values.

I will have to spend more time on this tomorrow after work again.

That is because I told you to replace the delay statement with the do-while loop, not supplement it.

if(Pwm==pwmMin)
                                                     //if PWM reaches the minimum:
        {                                             
          digitalWrite(outPin, LOW);
          delay(INTERVAL);   <-- PITCH IT. Throw it out the door.
          Dir=+1;                                 //delay *INTERVAL* & change direction
          delayStartTime = micros();
              do
              {
                INTERVAL   = map(analogRead(intervalPin),0,1023,800,50000); 
                pwmMax     = map(analogRead(pwmMaxPin),0,1023,55,255);   
                ramplength = map(analogRead(ramplengthPin),0,1023,2,11800);
              }
              while(micros()-delayStartTime<=INTERVAL); 
                                               
        }

Actually, based on your response to Robin, it looks like the sawtooth is what you are intending, not a half triangle wave. Am I reading that right?


Triangle, or sawtooth?

Jiggy-Ninja:
Actually, based on your response to Robin, it looks like the sawtooth is what you are intending, not a half triangle wave. Am I reading that right?


Triangle, or sawtooth?

Good Evening Jiggy-Ninja

Yes!

Please see attached screenshot of actual waveform produced by this code.

take care, peace
lost_bro

Jiggy-Ninja:
Woot!
Hollah!
What?

Were you able to control the pulse height before? Because looking over you code it looks like it was malfunctioning from the start. It appears that your intention is to ramp up to pwmmax, reverse direction, then ramp down to pwmmin, then pause for the dead-time. But the scope traces you are posting show the ramp going all the way to Vcc then straight back down to 0. I think the problem’s here:

//////////////////////////////////////////////////////////

void loop()
{

int  Dir=2;

////// Increment/decrement PWM on ledPin with a period of ‘x RampLength’ in us. /////



Why do you set Dir on every pass through loop() here on line 99? Why do you set it to 2 here when every other place uses 1? How about you just do `enum ramp_direction {FALLING=-1, PAUSED=0, RISING=1};` and use those values? You probably also intended Dir to be `[b]static[/b]`.


StartUs=micros();



Minor point here on line 136, this is not the recommended way of advancing the interval. Use `StartUs += rampLength;` instead.
That is because I told you to **replace** the delay statement with the do-while loop, not supplement it.


if(Pwm==pwmMin)
                                                    //if PWM reaches the minimum:
        {                                           
          digitalWrite(outPin, LOW);
          delay(INTERVAL);  ← PITCH IT. Throw it out the door.
          Dir=+1;                                //delay INTERVAL & change direction
          delayStartTime = micros();
              do
              {
                INTERVAL  = map(analogRead(intervalPin),0,1023,800,50000);
                pwmMax    = map(analogRead(pwmMaxPin),0,1023,55,255); 
                ramplength = map(analogRead(ramplengthPin),0,1023,2,11800);
              }
              while(micros()-delayStartTime<=INTERVAL);
                                             
        }

Hello Again Jiggy-Ninja

Thanks for taking the time to review this again.

Yes, I did run the code without the ‘delay(INTERVAL)’ and the simultaneous boxed squarewave never goes low, ie the output on ‘outPin’ —blue trace on scopeshots stays HIGH. So the squarewave never happens.

This is because I am using this as control for the square wave and not related to the pwm ramp wave.
(there are two waveforms being generated at the same time. please see my scopeshots).

Yes, I will change or delete the ‘Dir=2’ tomorrow and run it again after work.
When I originally put ‘Dir=2’ in the loop it made the rampwave duty cycle shorter, go figure…
Will recheck tomorrow.

I will also change the code to: StartUs += rampLength, did have that on one of my versions of this code, don’t remember why I changed it back???

How about you just do enum ramp_direction {FALLING=-1, PAUSED=0, RISING=1}; and use those values?
Can this be implemented with the map AnalogRead input?
The ‘PAUSED=0’ is the ramp deadtime? correct?

I will rewrite this tomorrow and run it and then post back.

Thanks again.
take care, peace
lost_bro