ATmega328p ADC to PWM

Go To Last Post
84 posts / 0 new

Pages

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, the lower value for 0.1V is 20.48 and ofcource I understand that it can't be represented.

I have used the calculation in the source but does not provide any improvement for the maximum duty cycle.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, the value for 0.1V is 20.48 and ofcourse I understand that the figures behind the dot can't be represented.

I used the calculations in the source but it didn't provide a larger dutycycle towards 99%

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Cliff has explained it perfectly well.  And both Jim and I have even pointed out where you can find the source code which will completely solve your problem.  Jim even actually posted it here!  Why are you still working on this?

 

IF you expect the ADC to return a value no lower than 20, and

IF you expect the ADC to return a value no higher than 410, and

IF you want a minimum OCR1A value of 6 (2.73%), and

IF you want a maximum OCR1A value of 250 (98.05%), and

WERE you to use the Arduino map() function, then it would look like this:

OCR1A = map(ADC, 20, 410, 6, 250);

Since you're not using the Arduino environment, you can simply implement the same function in your own code.  Have you even >>looked<< at the source?

 

You will need to handle cases where the ADC value is outside the range you expect so that your device doesn't explode.  What happens if the sensor fails?  What happens if there is a short somewhere?

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I did make a recalculation and used it in the source but it gave no improvement for the maximum dutycycle e.g. 99.9%

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I will not open a random .docx file.  Post the text of it here instead.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Have you even >>looked<< at the source?

 

I searched a lot for the Arduino map() source but could not find it.

Could you please tell me where finding it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I will post it in txt format.

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I searched a lot for the Arduino map() source but could not find it.

Could you please tell me where finding it?

You have got to be kidding me.

 

It's in >>this<< thread!

 

Scroll to the top of the page and find post #11.

 

He first mentioned it in #8.

 

Also find it where I pointed you to a google search result it in post # 47, and mentioned it again in #54.

 

You are clearly not paying attention, or making much of an effort.

 

I'm out.  Good luck with your project.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Be_logic wrote:
but it gave no improvement for the maximum dutycycle e.g. 99.9%

???  Does everyone else know what that means?  Do you mean that you want a duty cycle greater than 99.9% ?  What duty cycle >>do<< you want?  For which input value?  What duty cycle are you getting now?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yesterday I made reply"s but can't see this here so I send it again.

 

The ADC value for 0.1V is 20.48, see the recalculations and ofcource I can use the figures after the dot.

At which part in the source I must place ORC1A = map(ADC, 20, 410, 6, 250);

Because I managed using the map function in Atmel Studio 7.

 

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dutycycle must be variable between 1 and 99.9%, now it's ~67.5 max

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

There must be some kind of a language barrier (or stubbornness, or laziness) at work here.  I don't care which.  I am tiring of it all, and just want it to end!  So here's a crack at it:

#include <avr/io.h>

#define ADC_MIN 21
#define ADC_MAX 409
#define PWM_MIN 6
#define PWM_MAX 250

static uint32_t map(const uint32_t x,
                    const uint32_t in_min, const uint32_t in_max,
                    const uint32_t out_min, const uint32_t out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

int main(void) {

  uint16_t adc;

  // Disable input buffer on ADC5.
  DIDR0 = (1 << ADC5D);
  // AVcc, ADC5.
  ADMUX = (1 << REFS0) | (5 << MUX0);
  // div128, free running, start.
  ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (7 << ADPS0);

  // OC1A output for PWM.
  DDRB = (1 << PORTB1);
  // Mode 5 (8-bit Fast PWM), /1, non-inverted output on OC1A.
  TCCR1A = (2 << COM1A0) | (1 << WGM10);
  TCCR1B = (1 << WGM12) | (1 << CS10);

  // Forever.
  while(1) {
    adc = ADC;
    // Constrain.
    if (adc < ADC_MIN) {
      adc = ADC_MIN;
    }
    else if (adc > ADC_MAX) {
      adc = ADC_MAX;
    }
    // Scale to PWM output.
    OCR1A = map(adc, ADC_MIN, ADC_MAX, PWM_MIN, PWM_MAX);
  }

}

Ask if you don't understand something.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Thu. Mar 15, 2018 - 03:26 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After a time I started AD to PWM conversion for an application again and I want to ask a question about the above source if that is allowed.

I want the lowest PWM value 0 to be set at the lowest adc value and I thought that was possible by means of scaling with the map() function.

Doesn't it work because I use Atmel Studio 7?

Best regards.

Bert

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

#define ADC_MIN 0 and #define PWM 0. I don't understand the relevance of Atmel Studio 7 - how does this affect the code apart from compiling it??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Be_logic wrote:
Doesn't it work because...

Define "doesn't work".  Start over and show schematic/connections.  Tell voltages >>right on the AVR pins<<.  Show a complete test program.  Tell language, toolchain, and build settings including optimization level.  Tell how you are testing.  Tell what you expect to happen; tell what >>is<< happening.

 

There is a reason for knowing each piece of information requested, to answer your query.

 

What answer is map() giving you?  For what input?

 

All that said, perhaps depending on your timer setup you are getting the "single pulse" PWM at 0.  You can't get full-on and full-off at the same time with Fast PWM on an AVR8.  If you want full-off then use inverted PWM at the expense of not being able to get full-on.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Be_logic wrote:
I want the lowest PWM value 0 to be set at the lowest adc value
Be_logic wrote:
Dutycycle must be variable between 1 and 99.9%,

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Source see #63

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Be_logic wrote:
Source see #63

Surely that answers few if any of my queries.  Did you tell how you are invoking the function etc?

theusch wrote:
Define "doesn't work". Start over and show schematic/connections. Tell voltages >>right on the AVR pins<<. Show a complete test program. Tell language, toolchain, and build settings including optimization level. Tell how you are testing. Tell what you expect to happen; tell what >>is<< happening. There is a reason for knowing each piece of information requested, to answer your query. What answer is map() giving you? For what input?

Then I went on to outline a possible cause.  Which is the >>same<< as in your other thread response!

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Be_logic,

 

You do understand what's happening when Joey does this?...

#define ADC_MIN 21
#define ADC_MAX 409
#define PWM_MIN 6
#define PWM_MAX 250
OCR1A = map(adc, ADC_MIN, ADC_MAX, PWM_MIN, PWM_MAX);

so in other words it's just:

OCR1A = map(adc, 21, 409, 6, 250);

So that's saying "here is an ADC reading in the "adc" variable, it will be somewhere between 21 and 409, now map the value to the range 6 to 250". So this can only ever set OCR1A to values between 6 and 250. It will set it to 6 when "adc" is 21 and set it to 250 when "adc" is 409. It will linearly scale all readings between 21..409 to be PWM values between 6 and 250.

 

If these limits for ADC readings or for PWM range do not match your actual implementation then change:

#define ADC_MIN 21
#define ADC_MAX 409
#define PWM_MIN 6
#define PWM_MAX 250

If you want an ADC reading of 0 to map to a PWM setting of 0 then perhaps:

#define ADC_MIN 0
#define ADC_MAX 409
#define PWM_MIN 0
#define PWM_MAX 250

All this is a long version of what Kartman said in #65

 

More generally you need to determine:

 

What is the reading of ADC at the minimum point  - set this as ADC_MIN

What is the reading of ADC at the maximum point - set this as ADC_MAX

What is the minimum PWM/OCR value you want at the "bottom end" - set this as PWM_MIN

What is the maximum PWM/OCR value you want at the "top end" - set this as PWM_MAX

 

Job done.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In a situation like this, it helps to do three things:  

 

 1) Clearly define what you wish to do:

     Read potentiometer and change (linearily) a PWM's duty cycle output according to the pot value.

 

2)  Write a simple, easy, clear Arduino program to do it:

     

    uint16_t  myADCvalue;

    unsigned long   myInterval=100;  

    uint8_t   myScaledADC;

 

    void setup() {

       myADCvalue= analogRead(0);   // ADC MUX 0

       analogWrite(myPWMchannel, myADCvalue/4);

    }

 

   void loop() {   // update PWM duty cycle every half-second

      if (millis() > myInterval) {

          myInterval = millis() + 500;

          myADCvalue= analogRead(0);   // ADC MUX 0

          analogWrite(myPWMchannel, myADCvalue/4);

      }

    }

 

3)   decide if you're going on to the next stage of the project, or, are going to spend hours trying to understand how it works in AVR Studio.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
If you want an ADC reading of 0 to map to a PWM setting of 0 then perhaps:
clawson wrote:
Job done.

Ummmm...not necessarily:https://www.avrfreaks.net/commen...

[you answered in that thread as well]

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
[you answered in that thread as well]
Seven years ago.

 

It's not clear to me that @Be_logic even understands the issue in that thread, namely that non-inverted fast pwm can't do 0% duty cycle.

 

In either case (for @Be_logic or that thread's OP) I'd suggest phase correct PWM.  It can go to 0% and 100% duty.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

joeymorin wrote:
It's not clear to me that @Be_logic even understands the issue in that thread, namely that non-inverted fast pwm can't do 0% duty cycle.

Apparently, the message got through after many repeated attempts.

 

the frustrating part is that he dug up the old pertinent thread, which had the same symptoms and outlined the cause and solution.  Yet the discussion was ignored, and something to the effect of "what could be the problem and how can it be cured" was posted.  Similarly in this thread.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I used/checked the information #70, #73 so now it's in phase correct mode.

 

    TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM10);  //WGM10 = mode 1 phase correct mode 8 bit

    // Set OC1A/OC1B on Compare Match when up-counting. Clear
    // OC1A/OC1B on Compare Match when down-counting.

    TCCR1B = (1 << CS10); // No pre scaling

    

    // Forever
    while(1)
    {
        adc = ADC;
        
        // constrain

        if (adc < ADC_MIN)
        {
            adc = 0;
        
        }
        else if (adc > ADC_MAX)
        {
            adc = ADC_MAX;
        }
        // Scale to PWM output
        OCR1A = map(adc, ADC_MIN, ADC_MAX, PWM_MIN, PWM_MAX);
    }

 

With this setting, phase correct, the frequency is indeed half as you can read in the datasheet.
But still the PWM is 14.5% at the lowest potmeter value and 45% at the highest value.

 

Regards,

Bert

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Be_logic wrote:
I used/checked the information #70
So show your new definitions of ADC_MIN, ADC_MAX, PWM_MIN and PWM_MAX.

Also note that this looks wrong:

        // constrain

        if (adc < ADC_MIN)
        {
            adc = 0;

        }
        else if (adc > ADC_MAX)
        {
            adc = ADC_MAX;
        }

0 is not in the range ADC_MIN to ADC_MAX (unless you changed the values?) so you cannot set this as the lower bound or the map() function will not work. You should have:

        // constrain

        if (adc < ADC_MIN)
        {
            adc = ADC_MIN;

        }
        else if (adc > ADC_MAX)
        {
            adc = ADC_MAX;
        }

It seems to me that you are just throwing code at your C editor without actually thinking about what you are doing. Software authoring should be a case of careful design followed by easy implementation. You seem to be skipping the design steps. If this is because you don't understand something then ask questions don't just guess at things randomly.

 

EDIT: oh this is ludicrous - I just read back through the thread as I can't believe it's taken 75 posts to get here and I found in #63 that Joey already gave you the correct code and you have actually CHANGED IT?!?! If you don't know what you are doing then don't just throw random ideas at the thing.

 

I already told you in #70 how to map to PWM_MIN=0 and that is by adjusting the four #define's - which bit of that sentence is it that you can't wrap your brain around??

Last Edited: Fri. Oct 12, 2018 - 11:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I had already modified the #define's and I missed the error adc = 0 and I also modified it.

Nevertheless, I keep the same% PWM values as before. However, I have found that the frequency is halved at phase correct PWM.

 

TCCR1A = (1 << COM1A1) | (1 << COM1A0) | (1 << WGM10);  //WGM10 = mode 1 phase correct mode 8 bit

    // Set OC1A/OC1B on Compare Match when up-counting. Clear
    // OC1A/OC1B on Compare Match when down-counting.

    TCCR1B = (1 << CS10); // No pre scaling

    

    // Forever
    while(1)
    {
        adc = ADC;
        
        // constrain

        if (adc < ADC_MIN)
        {
            adc = ADC_MIN;
        
        }
        else if (adc > ADC_MAX)
        {
            adc = ADC_MAX;
        }
        // Scale to PWM output
        OCR1A = map(adc, ADC_MIN, ADC_MAX, PWM_MIN, PWM_MAX);
    }

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is like pulling teeth. No one here is psychic (AFAIK) so we can't possibly know what you have changed the MIN/MAX values to if you don't show us?!?!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sorry, here it is

 

#define ADC_MIN 0    
#define ADC_MAX 438   
#define PWM_MIN 0           
#define PWM_MAX 115

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And have you confirmed that if you simply set OCR1A to 0 or 115 it changes the duty "full scale"? Have you also confirmed that your ADC really does provide readings of 0 and 438 at either end of the Pot travel?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How can I test the ADC if the ADC reads the values between 0 and 438? However, with my Salea Logic analyzer I can conclude that the pot with the lowest resistance value gives a PWM value of 14.5% and with the highest resistance value it is 45.1%. I hoped that with the map () function I would be able to reduce the 14.5% to almost 0%. The pot has a lowest resistance of 60 Ohm, so not 0.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just as an aside, likely not related to your issue... One subtle change needed in the code I posted in #63:

    // Constrain.
    if (adc <= ADC_MIN) {
      adc = ADC_MIN;
    }
    else if (adc > ADC_MAX) {
      adc = ADC_MAX;
    }

This will avoid a warning when ADC_MIN is defined as 0.

 

Unless you're using -Werror (which, you should, by the way), the code in #63 will still build.  If you >>are<< using -Werror, >>and<< you failed to notice a failed build, it might explain why you're not seeing the results you're expecting i.e. the new code isn't actually making it onto the target.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Have you also confirmed that your ADC really does provide readings of 0 and 438 at either end of the Pot travel?

Good catch.

 

Real-world control signals are rarely perfect.  As the AVR's 10-bit ADC has 1000 and a few more counts, I'll generally say e.g. anything below 11 is zero, and anything about 1009 is 100%.  Then map the intervening 1000 counts to 0.0% to 100.0%.

 

OP can test that assumption by feeding a hard 0 into the map call.  Usually I'll have such a sanity-check during dev anyway, to test output functionality.  Drive to e.g. 50%; wait a few seconds to read the result; drive to other values in succession delaying between.  Note that you don't start with 0% and 100% or you cannot tell whether stuck low/high.

 

As mentioned, we don;t really know the actual current code and test conditions.

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Fri. Oct 12, 2018 - 07:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for your response and today I have another time to get AD to PWM working, at least I hope so.

As a result of your tip, I manually set to 0 and 115 in the () function PWM_MIN and PWM_MAX, respectively.

But that doesn't matter.
I doubt that the folder function is updated with new values.

It should not be that difficult to get the minimum PWM value between 0 and ~ 3%?

Hopefully you want and can help me with that.

Pages