Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Why this is not working properly??

Status
Not open for further replies.
Joined
Jul 25, 2012
Messages
1,192
Helped
171
Reputation
342
Reaction score
162
Trophy points
1,343
Activity points
0
Why this is not working properly. I am trying to generate pwm signals and vary the speed of motor. The speed doesn't vary and the motor rotates on its own.

mikroC Code

Code:
#define SW P2_7_bit
#define MOTOR P1_0_bit

void main() {
unsigned int speed = 0;

    while (1) {
    
       if(SW == 1) {
          Delay_ms(20);
          if(SW == 1) {
               speed = speed + 1;
          }
       }

       if(speed > 4) {
         speed = 0;
       }
       
       switch(speed) {
	case(0):{
		MOTOR = 0;
  		break;

        }
        case(1):{
		MOTOR = 1;
		Delay_ms(25);
		MOTOR = 0;
		Delay_ms(75);
		break;

        }
        case(2):{
		MOTOR = 1;
                Delay_ms(50);
		MOTOR = 0;
                Delay_ms(50);
		break;

        }
        case(3):{
                MOTOR = 1;
                Delay_ms(75);
		MOTOR = 0;
                Delay_ms(25);
		break;
        }
        case(4):{
                MOTOR = 1;
                break;
        }
        default:
                ;

    }
  }
}
 

Attachments

  • pwm.rar
    16.4 KB · Views: 75
  • ss22.jpg
    ss22.jpg
    158 KB · Views: 119

I think you need to have some another way to interpret this....this code is giving the same current control over the period of time ( in which you are trying to observe the speed of the motor.....rather than doing in this way.... try this ........
Code:
#define SW P2_7_bit
#define MOTOR P1_0_bit

void main() {
unsigned int speed = 0;
unsigned char count =0x00;
    while (1) {
      

     count =0x00;
       if(SW == 1) {
          Delay_ms(20);
          if(SW == 1) {
               speed = speed + 1;
          }
       }

       if(speed > 4) {
         speed = 0;
       }
       
       switch(speed) {
	case(0):{
		while(count<100) // put the value as you need for 100
                {
                 MOTOR = 0;
                Delay_ms(75);
                 count =count+1;
  		}
                  break;

        }
        case(1):{
                while(count<100) // put the value as you need for 100
                {
		MOTOR = 1;
		Delay_ms(25);
		MOTOR = 0;
		Delay_ms(75);
                count =count+1; 
		}
                break;

        }
        case(2):{
              while(count<100) // put the value as you need for 100
                {
		MOTOR = 1;
                Delay_ms(50);
		MOTOR = 0;
                Delay_ms(50);
                count =count+1;
		}
               break;

        }
        case(3):{
                while(count<100) // put the value as you need for 100
                {
                MOTOR = 1;
                Delay_ms(75);
		MOTOR = 0;
                Delay_ms(25);
                 count =count+1; 
		}
               break;
        }
        case(4):{
                while(count<100) // put the value as you need for 100
                {
                MOTOR = 1;
                Delay_ms(100);
                count =count+1;
                }
                 break;
        }
        default:
                ;

    }
  }
}

What I had tried is give some train of pulses with your code in a way that you will able to observe the speed change of the motor...
I hope with the logic above.... you will able to observe ( Visually able to see the waveform of) the change in the speed of motor.....

Good Luck
 

Hello!

Instead of doing a loop, I think you should try to use hardware timers (if any on that processor).
That's what they are made for.
milind.a.kulkarni's method should work better, but the drawback is that the speed is not changed
in real time, but every 10 seconds.

By the way, although it's not an error: you don't need parentheses for cases.
Example:
case 1: // Not case (1):


Dora.
 

What should be the exact delays for producing pwms of 25%, 50%, 75%?
 

Hello!

What should be the exact delays for producing pwms of 25%, 50%, 75%?

I don't understand what you need since you have implemented it. Use delays proportional to 25, 50 and 75
(or simply 1, 2, 3) should do it.

But the chip you use has probably timers with timer outputs. Why not trying to setup a PWM output
directly? The good thing is that you don't have to care about setting the output low, then high, the
PWM engine does it by itself. And you could tune your motor with finer resolution (and without effort).

Dora.
 

Is my method of generating PWM OK? or is it better to use timers. Can you give an example of pwm using timers?
 

Hello!

I cannot judge whether your method is OK, it depends on the specs, on what you want to
achieve. I would do it with timers and interrupts because it gives your program a kind of hardware
multithreading structure: program's main loop just waits for button interrupts and the PWM
runs by itself.
As for the source code, I cannot help you, I don't have an AT8951 development environment.
But I'm sure there is some source code published by the maker, and explaining how to use
timers in PWM.

Dora.
 

But why this is not working. The motor is running on its owm and sw is not working.

Code:
#define SW P2_7_bit
#define MOTOR P1_0_bit

void main() {
unsigned int speed = 0;
unsigned char count =0x00;
  P0 = 0x00;
  p2 = 0x00;
  MOTOR = 0;
  
    while (1) {


       if(SW == 1) {
          Delay_ms(10);
          if(SW == 1) {
               speed = speed + 1;
          }
       }

       if(speed > 4) {
         speed = 0;
       }

       switch(speed) {
        case 0:{
               MOTOR = 0;
               break;

        }
        case 1:{
                       MOTOR = 1;
                Delay_ms(2);
                MOTOR = 0;
                Delay_ms(6);
                break;

        }
        case 2:{
                      MOTOR = 1;
                Delay_ms(4);
                MOTOR = 0;
                Delay_ms(4);
                break;

        }
        case 3:{
                MOTOR = 1;
                Delay_ms(6);
                MOTOR = 0;
                Delay_ms(2);
                break;
        }
        case 4:{
                MOTOR = 1;
                break;
        }
        default:
                ;

    }
  }
}
 

Attachments

  • pwm.rar
    16.8 KB · Views: 90

Your code IS working.

However, whenever you switch on SW, then the code will continously cycle through 0, 1, 2, 3, 4 at the rate of ~ 55 cycles/ sec.
Every 10mS you are updating 'speed', and doing a single pulse of full voltage for 0, 2, 4, 6 mS. In Case 4 you are leaving it ON for 10mS

Your motor will simply average out this strange pulsing, and work at a single steady speed.
So essentially SW has NO control on the PWM or just random control at best, depending on when you release the SW.

However I also notice that your P2_7 is floating when switch is released. This will NOT work. Essentially your input pin will most probably ALWAYS be reading SW as 1.

What you should do is connect R to Vcc, and Cap (say 10nF) to GND. Connect SWitch in parallel to Cap. Take input to P2_7 from junction of R & C. And also change your SW to check for '0' instead of '1'.

Also you need to do this --

Check for SW change, and ONLY on change should you update your 'speed' variable.
Then the speed will change at each push of the button.

Try it out. You WILL get some better results.
 
Last edited:

I did as you said but the speed is not varying and motor runs without pressing sw.

Code:
#define SW P2_7_bit
#define MOTOR P1_0_bit

void main() {
unsigned int speed = 0;
unsigned char count =0x00;

  MOTOR = 0;
  
    while (1) {


       if(SW == 0) {
          Delay_ms(10);
          if(SW == 0) {
               speed = speed + 1;
          }
       }

       if(speed > 4) {
         speed = 0;
       }

       switch(speed) {
        case 0:{
               MOTOR = 0;
               break;

        }
        case 1:{
                MOTOR = 1;
                Delay_ms(2);
                MOTOR = 0;
                Delay_ms(6);
                break;

        }
        case 2:{
                      MOTOR = 1;
                Delay_ms(4);
                MOTOR = 0;
                Delay_ms(4);
                break;

        }
        case 3:{
                MOTOR = 1;
                Delay_ms(6);
                MOTOR = 0;
                Delay_ms(2);
                break;
        }
        case 4:{
                MOTOR = 1;
                break;
        }
        default:
                ;

    }
  }
}

Code:
if(SW == 0) {
          Delay_ms(10);
          if(SW == 0) {
               speed = speed + 1;
          }
       }

Am I checking the sw condition properly. should I increase the delay to 20ms?
 

Attachments

  • pwm1.rar
    16.8 KB · Views: 83

I will have to assume you have connected the R+C+switch correctly. Do look at 'debounce circuit' etc to see what I'm talking about.

Next - you are still not looking for a CHANGE in SW value. You should store SW in (say) Prev_SW.
Then compare SW with Prev_SW, and ONLY if (SW ==0) && (SW != Prev_SW) should you go ahead in the loop.

ALSO - do check that your BJT is still working (i.e. not blown). This might have happened since you have not put in a reverse diode for clamping the inductive kick.
I suggest you check/ change the bjt, AND put in a reverse diode across your motor. Common 1N400x work fine.

Which motor are you using btw ?
PWM frequencies can range from 55hz (your case) to above 30Khz. So in fact reducing the delay is the right way to go actually. In fact, ideally the PWM time-period should be much less than L/R of the motor.
 

Hello!

Am I checking the sw condition properly. should I increase the delay to 20ms?

No, in this case, changing 10 ms to 20 ms or even 250 will basically not change the
structure of your program.
You might be interested in having only 1 increment at a time. So in this case, keep
track of the previous button state. And if the previous button state is the same as
the current button state, then do nothing. That can be done like this:

Code:
    uint8 button_state, previous state;
    while(1) {
        if(sw == 0) {
            button_state = 1;
            if(previous_state == 0) {
                speed += 1;
                if(speed > 4) speed = 0;
            }
        }
        [switch(speed) goes here]
        previous_state = button_state;
    }

And as your processing already uses delays, you don't need any delay for button
processing.

Dora.
 

Hello!

Instead of doing a loop, I think you should try to use hardware timers (if any on that processor).
That's what they are made for.
milind.a.kulkarni's method should work better, but the drawback is that the speed is not changed
in real time, but every 10 seconds.

By the way, although it's not an error: you don't need parentheses for cases.
Example:
case 1: // Not case (1):


Dora.

Hi Dora,

I proposed the loop bcz in the wavefrom Internetuser2k12 shows dose not show variation in the speed....so to make him feel about the PWM i used loop concept....but as you said hardware timer is a way but in most of the cases as I know, for an example PWM used in inverters is a fixed table/tables approach to mach the duty cycle in the software....where better to use interrupt timers to reduce the ALU load .....

Good Luck
 

Hi Dora,

I proposed the loop bcz in the wavefrom Internetuser2k12 shows dose not show variation in the speed....so to make him feel about the PWM i used loop concept....but as you said hardware timer is a way but in most of the cases as I know, for an example PWM used in inverters is a fixed table/tables approach to mach the duty cycle in the software....where better to use interrupt timers to reduce the ALU load .....

Good Luck

what ARE you talking about Milind ? PWM in inverters is only a single case of PWM requirement.... not a "most case" scenario. And why would a h/w timer have to "match" the duty cycle in software ? The original askers problem of not showing variations in speed are because of a few errors in his design/ implementation, and not because of any other reason. You are just complicating the issue with vague concepts rather than solving the specific one outlined.
 

Hello!

I would like to suggest a little program illustrating what can be done with a timer configured as
PWM output. Note that I don't have an AT89C51 environment, so I'm not sure that the port can be
as easy as this one. It's made with MSP430 Launchpad (that you can buy for 4.30 USD and use
with a free version of Code Composer Studio compiler).

1. Using a a timer.
A timer is basically a counter with comparison functionality. That's the only thing it can do but it
does it very well. It has usually myriads of configurations, counting up, counting down, counting
up to a certain value and then counting down to another value, etc. This time, I will use a count up
method, and I will use 2 values. One sets the timer upper limit, and the other one sets up the
comparison. And I set the comparison result to output.
Since the purpose here is to set a duty ratio, I will set up the upper limit at 100, and the comparison
limit at 25, which will correspond to the duty ratio in %. The source code looks like this:
(I just modified TI's source code, nothing fancy here)


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
#include "MSP430G2452.h"
 
void main(void) {
    WDTCTL = WDTPW + WDTHOLD;       // Stop WDT
    P1DIR |= 0x04;                  // P1.2 output
    P1SEL |= 0x04;                  // PWM output selected
    CCR0 = 100;                     // PWM Period/2
    CCR1 = 25;                      // CCR1 PWM duty cycle
    CCTL1 = OUTMOD_6;               // CCR1 toggle/set
    TACTL = TASSEL_2 + MC_1;        // SMCLK, up mode
    _BIS_SR(LPM0_bits);             // Enter LPM0
}



What you can notice is that there is no while loop. This is because the timer can run by itself,
and once configured it does not need the CPU anymore. Put the CPU to sleep and that's it.

2. Some cosmetics
Now it works, but if you read this code after 6 months, you may not understand anymore what
it does. Let's do some cosmetics for a more readable version:


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "MSP430G2452.h"
 
#define PWM_PERIOD      100
#define DEFAULT_RATIO   25
 
typedef unsigned char   uint8;
 
//  Function prototypes
void setup_pwm(void);
void set_duty(uint8 duty);
//  Global variables
uint8 duty;
//------------------------------------------------------------------------------
//  Main program. Does basically nothing except setting the start condition
void main(void) {
    WDTCTL = WDTPW + WDTHOLD;       // Stop WDT
    duty = DEFAULT_RATIO;
    setup_pwm();
    set_duty(DEFAULT_RATIO);
    _BIS_SR(LPM0_bits + GIE);               // Enter low power mode
}
 
//------------------------------------------------------------------------------
//  Functions implementation
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//  Configures timer A for direct pwm output to port 1.2. The reference frequency
//  is SMCLK (about 1 MHz);
void setup_pwm(void) {
    P1DIR |= 0x04;                  // P1.2 output
    P1SEL |= 0x04;                  // PWM output selected
    CCTL1 = OUTMOD_6;               // CCR1 toggle/set
    TACTL = TASSEL_2 + MC_1;        // SMCLK, up mode
    CCR0 = 100;                     // PWM Period
}
//------------------------------------------------------------------------------
//  As the total period has already been set in timer configuration, this
//  function only sets the "up" part of the signal.
void set_duty(uint8 duty) {
    CCR1 = duty;                    // CCR1 PWM duty cycle
}



This time, there are a few more definitions and if you read this program later or give it to
someone, the main() is short and therefore easy to understand with self-explanatory function
names.

3. Using a button
Next step. We need a button input. The launchpad board has only 1 usable button (the other
is reset) on P1.3. We will therefore add one function to setup the button interrupts:


Code C - [expand]
1
2
3
4
5
6
7
8
#define BUTTON 0x08
 
void setup_button(void) {
//  P1DIR &= ~BUTTON;               //  Make sure that port 1 is in input
    P1IE |= BUTTON;                 //  Port 1 interrupt enable at bit 3
    P1IES |= BUTTON;                //  Trigger on falling edge
    P1IFG &= ~BUTTON;               //  Reset port vector in case it was high.
}



Port1 bit 2 is used for PWM output, bit 3 will be used for the button input (no choice here).
In the above code, P1IE (IE for interrupt enable) is set to the button value. P1IES sets
the edge. As pressing the button makes the level go to 0V, we have to setup the interrupt
edge to 1. And the last instruction is in the case that the interrupt flag is already high.

When detecting an interrupt, the following function is called:


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
#pragma vector=PORT1_VECTOR
__interrupt void Port1ISR(void) {
    _DINT();
    duty += 25;
    if(duty > 100) duty = 0;
    set_duty(duty); 
    P1IFG &= ~BUTTON;               //  Reset interrupt flag
    _EINT();
    
}



In the "specs" of this program, the original poster wanted to have 4 setup levels,
and that's why I add 25 to the duty value. The values sequence will therefore be:
25 -> 50 -> 75 -> 100 -> 0 -> 25, etc...

Note that the adjustment can be arbitrarily small without code modification.
And if you need even finer steps, you can set PWM_PERIOD to 1000 and add
1 to duty everytime... But careful: you will have to press 1000 times to come
back to 0. And on top of that, if the timer total count is high, then the PWM
period will be long, and the motor might become "jumpy".
Note that I wrote _DINT() (disable interrupts) and _EINT(). This is not really
required here, but it's a good programming practice in order to avoid nested
interrupts.
Important: P1IFG (the interrupt flag) has to be reset after each interrupt. Button
interrupts are not automatically reset.

4. Program summary
Here is the full code:

Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include "MSP430G2452.h"
 
#define PWM_PERIOD      100
#define DEFAULT_RATIO   25
#define BUTTON          0x08        //  For P1.3
 
typedef unsigned char   uint8;
 
//  Function prototypes
void setup_pwm(void);
void setup_button(void);
void set_duty(uint8 duty);
//  Global variables
uint8 duty;
//------------------------------------------------------------------------------
//  Main program. Does basically nothing except setting the start condition
void main(void) {
    WDTCTL = WDTPW + WDTHOLD;       // Stop WDT
    duty = DEFAULT_RATIO;
    setup_pwm();
    set_duty(DEFAULT_RATIO);
    setup_button();
    _BIS_SR(LPM0_bits + GIE);               // Enter low power mode
}
 
//------------------------------------------------------------------------------
//  Functions implementation
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//  Configures timer A for direct pwm output to port 1.2. The reference frequency
//  is SMCLK (about 1 MHz);
void setup_pwm(void) {
    P1DIR |= 0x04;                  // P1.2 output
    P1SEL |= 0x04;                  // PWM output selected
    CCTL1 = OUTMOD_6;               // CCR1 toggle/set
    TACTL = TASSEL_2 + MC_1;        // SMCLK, up mode
    CCR0 = 100;                     // PWM Period
}
//------------------------------------------------------------------------------
//  As the total period has already been set in timer configuration, this
//  function only sets the "up" part of the signal.
void set_duty(uint8 duty) {
    CCR1 = duty;                    // CCR1 PWM duty cycle
}
//------------------------------------------------------------------------------
//  Setup launchpad's button (which is on port 1.3)
void setup_button(void) {
//  P1DIR &= ~BUTTON;               //  Make sure that port 1 is in input
    P1IE |= BUTTON;                 //  Port 1 interrupt enable at bit 3
    P1IES |= BUTTON;                //  Trigger on falling edge
    P1IFG &= ~BUTTON;               //  Reset port vector in case it was high.
}
 
//------------------------------------------------------------------------------
// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR
__interrupt void Port1ISR(void) {
    _DINT();
    duty += 25;
    if(duty > 100) duty = 0;
    set_duty(duty); 
    P1IFG &= ~BUTTON;               //  Reset interrupt flag
    _EINT();
    
}



What can be noticed (same as previously) is that there is no while loop.
- When there is no interrupt, the timer runs by itself in PWM mode. The CPU sleeps.
- When there is an interrupt, the CPU wakes up, performs the interrupt tasks
as defined in the interrupt service routine and goes back to sleep.

Last step, here are a few scope captures of the PWM output.

5. Comments, conclusion
There is apparently a small problem in PWM100: when reaching the value 100,
the output spends one clock low. That's because I forgot to set the maximal count
at 100 -1. If I correct it to 99 and set the duty at 100, the output would be always
low. Anyway, it works and this bug is a really minor issue.

You can notice from the scope screen copies that the PWM frequency is around 10 kHz
(not a surprise, the default chip frequency is 1 MHz and I used a period of 100 clocks).
10 kHz should be more than enough to drive a motor accurately. For even more
accuracy, the chip frequency could be set at to 16 MHz, which would yield a PWM frequency
of 160 kHz. Or 1 frequency of 16 kHz with a PWM resolution of 1000 instead of 100.
So there are many ways of tuning this program for your needs.

A simple PWM program has been built, that uses minimal CPU resources.
Flash footprint:
The needed flash size is 480 bytes, but could certainly become smaller with some optimization.
Responsivity:
It's quite responsive. By toggling a port in a while loop, the PWM frequency would be at least
10 times lower. In the present case, it's not an issue, but responsivity could become an issue
in a servo loop.

That's it. Let's hope somebody can use that.

Dora.
 

Attachments

  • PWM0.png
    PWM0.png
    8.3 KB · Views: 108
  • PWM25.png
    PWM25.png
    10.4 KB · Views: 106
  • PWM50.png
    PWM50.png
    10 KB · Views: 99
  • PWM75.png
    PWM75.png
    10.2 KB · Views: 113
  • PWM100.png
    PWM100.png
    9.2 KB · Views: 106

Hello!

.....For even more
accuracy, the chip frequency could be set at to 16 MHz, which would yield a PWM frequency
of 160 kHz. Or 1 frequency of 16 kHz with a PWM resolution of 1000 instead of 100.
So there are many ways of tuning this program for your needs.

.....Dora.

Hey there Dora.... could you explain in more detail how the PWM resolution is related to the frequency ? And especially how you went from 100 to 1000 ?
 

Hello!

Hey there Dora.... could you explain in more detail how the PWM resolution is related to the
frequency ? And especially how you went from 100 to 1000 ?

As mentioned earlier, I use a timer with comparison to some value. In the above case, It counts
repeatedly up to PWM_PERIOD (and from here, when the comparator is set to duty, if the counter
is less than duty, the comparator outputs 0, and if it's more, it outputs 1).

Therefore the duty ratio resolution will be exactly 100 in this case, and we can modify the ratio
by steps of 1%, and the PWM frequency will be 10 kHz.

Now if I want a greater resolution, I can set the period to 1000 steps instead of 100, but it
will take 10 times longer to count, and the PWM frequency will therefore be 1 kHz.

Now if you want to try this program with a setup of 1000:

1. Add a typedef for uint16 instead of uint8 (uint8 cannot be used to count up to 1000) :
typedef unsigned short uint16;
2. Change the prototype of the duty setup function so that it accepts uint16,
and modify the implementation accordingly.
3. Change #define PWM_PERIOD from 100 to 1000. Or maybe 999. See my remark at the end
of my last post.

Beside this, everything should work fine (but I have not tested it). But as mentioned earlier, it
will take 1000 button actions to setup a duty of 100%. And if you press one time too much,
you're back to 0...

Now an interesting modification of this software would consist in using a potentiometer, read
its value and tune the duty ratio accordingly. I guess this can be done in about 5 ~ 10 lines.

Other possible modifications: The launchpad has other interruptible pins that you can use for more
buttons. For example up fast that would increase by 10 or more, up slow that would increase by 1.
And similarly down fast and down slow.

Have fun!

Dora.

- - - Updated - - -

Hello (again):

but as you said hardware timer is a way but in most of the cases as I know, for an
example PWM used in inverters is a fixed table/tables approach to mach the duty
cycle in the software....where better to use interrupt timers to reduce the ALU load .....

Yes, you're right.
And there is a problem to be known for the timer method:
For instance, if I want to set up a ratio of 25%, the timer method is quite brutal:
In the above example:
- It counts up to 25, switches the output
- It counts from 75 to 100 (or 99), and switches the output back.
So if you want a smooth output, you need a very fast clock.

A method that I prefer by far is the VHDL solution. Basically use an adder
and set the output high everytime it overflows. Example with 25 and 100.
Set the adder to 0. -> No overflow
Add 25 -> 25 -> No overflow
Add 25 -> 50 -> No overflow
Add 25 -> 75 -> No overflow
Add 25 -> 100 -> Overflow & reset.

For a setup of 25, exactly once in every 4 clocks the result is high. If you set to
26, for example, it will also once every 4 clocks, and sometimes after 3 clocks
only, which gives a statistical output of 26% high.

Of course, it can be easily implemented in C, but one loop takes quite a few
clocks...

Dora.
 

Status
Not open for further replies.

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top