© Hubert Henry Ward 2020
H. H. WardIntermediate C Programming for the PIC Microcontrollerhttps://doi.org/10.1007/978-1-4842-6068-5_4

4. Creating a Square Wave

Hubert Henry Ward1 
(1)
Leigh, UK
 

In this chapter, you are going to look at creating a square wave on one of the CCP outputs.

After reading this chapter, you should understand what the CCP module is. You should also appreciate why you would want to create a square wave output and how to use the PWM aspect of the CCP module to create a square wave.

In Chapter 6, you will look at an alternative approach to creating a square wave. The approach will involve using the compare aspect of the CCP module and using interrupts.

Why Create a Square Wave?

To answer this question, you are going to look at the two uses of a square wave. The first is in producing musical notes and the second is in setting the speed of a DC motor.

Musical Notes

To appreciate what this has to do with creating a square wave, you have to understand how we humans distinguish musical notes from each other. Indeed, it is really how we distinguish all of the different sounds around us. Each note, or sound, has its own particular frequency or combination of frequencies. Table 4-1 lists the frequency of some of the more important musical notes. A more complete list of the frequency of the musical notes is given in Appendix 8.
Table 4-1

The Frequency of the More Common Musical Notes

Note

Frequency (Hz)

Wavelength (cm)

G3

196.00

176.02

G#3/Ab3

207.65

166.14

A3

220.00

156.82

A#3/Bb3

233.08

148.02

B3

246.94

139.71

C4

261.63

131.87

C#4/Db4

277.18

124.47

D4

293.66

117.48

D#4/Eb4

311.13

110.89

E4

329.63

104.66

F4

349.23

98.79

F#4/Gb4

369.99

93.24

G4

392.00

88.01

C4 is the note that is more commonly known as middle C.

Note that the velocity at which sound travels through the air is 343m/s and the wavelength (λ) is related to frequency and velocity using the standard expression shown in Equation 4-1:
$$ lambda =frac{v}{f} $$
(Equation 4-1)

The term wavelength relates to the distance the sound travels to complete one full cycle of the signal or sound.

Knowing that the note G3 has a frequency of 196Hz, (i.e. 196 cycles in one second), then the wavelength of note G3 is shown in Equation 4-2:
$$ lambda =frac{343}{196}=1.75m mathrm{or} 175mathrm{cm} $$
(Equation 4-2)
Exercise 4.1

The answers to the exercises are at the end of the chapter. In this exercise, you should calculate the wavelength of the following frequencies, knowing that the velocity at which they travel is the velocity of sound:

Frequency = 48.999Hz

Frequency = 1046.502Hz

Frequency = 2,200Hz

If you could produce a signal at any of the frequencies in Table 4-1, then you could produce the respective musical note. The signal does not have to be a perfect sinusoidal waveform, but it would be better if it was.

In this way then, if you could get the PIC to produce a square wave at the correct frequencies, you could produce a series of musical notes. This is what you will do in the next chapter: you will produce a simple musical keyboard with eight notes.

The Speed of the Simple DC Motor

With respect to the control of a DC motor we are talking about setting the speed of the motor. This does not mean you will be controlling the speed because the load varies. You will simply be setting the speed of the motor. Controlling the speed (i.e. ensuring the speed stays the same irrespective of the load applied to the motor) is a whole different ballgame. Speed control is a book by itself.

The speed of the motor depends upon the voltage that is applied to that motor. Therefore, if you can vary the voltage applied to the motor, you can vary the speed of the motor.

Pulse Width Modulation

Pulse width modulation (PWM) is a very useful method of varying a DC voltage supply. To appreciate what PWM is, and what pulse width you are modulating, it’s useful to look at a typical DC square wave. This is shown in Figure 4-1.
../images/497005_1_En_4_Chapter/497005_1_En_4_Fig1_HTML.jpg
Figure 4-1

A typical square wave

The wave form, shown in Figure 4-1, is DC as the voltage never changes polarity. In this case, it is always positive. The wave form has what is termed a 50/50 duty cycle since the up time, or on time, named “mark” time, is the same as the down time, or off time, named “space” time. The mark time (M), or up time, is the pulse that is referred to in PWM. The width is the length of time that the mark, or pulse, extends for. The modulation in PWM refers to the fact that you vary the length of time that the mark extends for (i.e. PWM). Note that since the total time (i.e. the mark time plus the space time, known as the periodic time, T) does not vary, then as you increase the mark time you must decrease the space time and visa-versa.

If the waveform was that of a voltage being applied to a DC motor, then the motor would respond to the average of the voltage. For any DC square wave, the average voltage can be calculated using the expression in Equation 4-3:
$$ Vavge=frac{VmM}{T} or=frac{VmM}{M+S} $$
(Equation 4-3)

Note that Vavge is the average voltage, Vm is the voltage maximum, M is the mark time, T is the periodic time for the wave form (i.e. the time to complete one full cycle), and S is the space time.

Note that $$ frac{M}{M+S} $$ is termed the duty cycle. When the M time is equal to the space time, then the duty cycle is termed 50/50 and the average voltage is $$ frac{Vm}{2} $$.

I hope the above paragraphs explain the importance of creating a square.

Creating a Square Wave with the PWM Mode

One way you can create a square wave is to use the PWM mode of the CCP module of the PIC. This is the Capture Compare and Pulse Width Modulation module of the PIC. This module is a mixture of hardware, in that the actual circuitry is inside the PIC, and firmware, preprogrammed operations that are in the PIC. In this chapter, you will look at using the CCP in PWM mode to create the square wave.

What the programmer has to do is create the timing for the period T and the mark time, M. This is done using the timer2 inside the PIC, which just counts clock pulses. The procedure for doing this is as follows.

First, you must set the CCP module to PWM mode. This is done by writing to the control register CCPXCON. However, the 18F4525 has two CCP modules, which then means it has two CCPX outputs, hence the X, which should be replaced with 1 or 2 depending upon which CCP module and output you are using. Table 4-2 shows the use of the bits in the CCPXCON control register. Note that there will be two such control registers and the X is replaced with 1 and 2.
Table 4-2

The Bits of the CCPXCON Register

Bit7

Bit6

Bit5

Bit4

Bit3

Bit2

Bit1

Bit0

-

-

DCxB1

DCxB0

CCPxM3

CCPxM2

CCPxM1

CCPxM0

Bits 7 and 6

These are not used and should be read as 0.

Bits 5 and 4

These are used to store the two LSBs of the 10-bit number for the duty cycle.

Bits 3, 2, 1, and 0

These are used to set the use of the CCP module according to the setting shown in Table 4-3.

Table 4-3 shows how Bits 3, 2, 1, and 0 set up the CCP into its respective mode of operation.
Table 4-3

The Four Bits That Configure the CCP Modules

Bit3

Bit2

Bit1

Bit0

Setting

0

0

0

0

Capture/Compare/PWM disabled

0

0

0

1

Reserved

0

0

1

0

Compare mode, toggle output on match

0

0

1

1

Reserved

0

1

0

0

Capture mode every falling edge

0

1

0

1

Capture mode every rising edge

0

1

1

0

Capture mode every fourth rising edge

0

1

1

1

Capture mode every sixteenth rising edge

1

0

0

0

Compare mode, initialize CCPx pin low

1

0

0

1

Compare mode, initialize CCPx pin high

1

0

1

0

Compare mode, generate software interrupt

1

0

1

1

Compare mode, trigger special event

1

1

0

0

PWM mode P1A, P1C active high P1B P1D active high

1

1

0

1

PWM mode P1A, P1C active high P1B P1D active low

1

1

1

0

PWM mode P1A, P1C active low P1B P1D active high

1

1

1

1

PWM mode P1A, P1C active low P1B P1D active low

It should be noted that the last four settings in Table 4-3 show the mode for the PWM when the PIC is used in the extended mode. In this mode, the programmer has the ability to produce a H drive output. However, if all you want is a simple square wave output, then these four bits can be set to 1100, b3 and b2 can be a logic 1, and b1 and b0 can be set to a logic 0.

If you want to produce two square wave outputs, you must write to both CCP1CON and CCP2CON registers as well as load both CCPR1L and CCPR2L with the correct value. This will produce two square waves of the same frequency set by the value in the PR2 register. A program that produces two square waves is shown in Listing 4-2.

The following is an explanation of the bits in the CCPXCON register.
  • Bits 7 and 6 of the CCP1CON register are not used, so leave them at logic 0.

  • Bits 5 and 4 are where you store the two least significant bits of the binary number that is used to control the width of the mark pulse. You will look at this later in the chapter.

  • It is the least significant four bits (b3, b2, b1, and b0 of both CCPXCON registers) that control what mode the CCP module is in. Since you are trying to create a square wave with PWM, you want to set this module to PWM mode. This is done by setting these four bits as follows:
    • b3 = 1

    • b2 = 1

    • b1 = x

    • b0 = x
      • The x means it does not matter what logic level they are.

The data sheet does indicate that b1 and b0 can be set to any logic level; see section 15 in the datasheet. However, this is true only if you are dealing with just one CCP and creating one square wave output. The PIC18F4525 has two CCP modules, which means you can create two square wave outputs. When you do this, the phase relationship between the two square waves can be affected. This concept will be discussed later in this chapter.

One last thing before you start creating your square wave. You need to know on what pin the two square waves will be outputted by the PIC. The two outputs are CCP1 and CCP2. CCP1 is fixed on PORTC Bit2 (i.e. PORTCbits.RC2). However, CCP2 has two possible output locations. It can be sent out on PORTBbits.RB3 or PORTCbits.RC1. To decide which of these two bits the CCP2 is on, you must use one of the CONFIG words when you first set the CONFIG words up. The default setting is PORTCbits.RC1. I normally leave it at this so that I can use my configuration header file in all my projects.

The process of creating a square wave is based around letting timer2 count up until a time equal to the periodic time T of the desired square wave has been reached. Timer2, like all timers, simply counts clock pulses and so a count of one will equal a specific time according to the clock frequency used by timer2. Therefore, you need to know the number of pulses the timer has to count to reach the periodic time of the frequency of the square wave. This number is loaded into a special function register called PR2. When the value in timer2 matches the value in this PR2 register, then the square wave starts a new cycle.

To determine the specific value to load into PR2, Microchip provides an equation shown as equation 15.1 in section 15.4 of the datasheet. However, it is not the easiest equation to use, so I have rearranged it to produce an equation for PR2, which is what you want to work out. You simply want to know what value you need to load into the PR2 to set the frequency of the square wave. See Equation 4-4:
$$ PR2=frac{mathrm{OscFreq}}{mathrm{Frequency}	imes 4	imes TMR2mathrm{Preset}}-1 $$
(Equation 4-4)

Creating a 500Hz Square Wave

The best way to explain the process is to go through a simple example. In the following example, you will create a 500Hz square wave with an 8Mhz internal oscillator and a TMR2Preset value of 16. I will explain what the timer preset value does later.

Putting all the values into equation 4-4 you get Equation 4-5:
$$ PR2=frac{8{E}^6}{500	imes 4	imes 16}-1=249 $$
(Equation 4-5)

This means that to get a square wave with a frequency of 500Hz using an 8MHz oscillator, you simply have to load the PR2 with the value of 249, which is 0b11111001 or 0XF9, and then set the mark-to-space ratio.

I feel I should point out that timer2 counts clock pulses and the clock runs at a quarter of the frequency of the oscillator; note that is why there is a number 4 in Equation 4-4. This means that, with an oscillator frequency of 8Mhz, the clock runs at a 2Mhz. However, you have the ability to slow timer2 even more by dividing this 2Mhz frequency further. To control how much more you divide this frequency by, and so slow it down, use the timer2 preset value. There are three possible values this preset can be set to: 1, 4, and 16. You are using the value 16 for this timer preset, hence the 16 in Equation 4-5. This means that timer2 is counting at a rate of 2Mhz divided by 16, so at a frequency of 125kHz. The periodic time for this frequency is $$ T=frac{1}{f}=frac{1}{125{E}^3}=8{E}^{-6} $$ therefore = 8μs. This means it takes 8μs for timer2 to count one tick. This means that it will take 250x8μs for timer2 to count from 0 to 249. Therefore, it will take 2ms for the timer to count from 0 to 249, which is the periodic time for the 500Hz square wave you are trying to produce.

I hope this explains how Equation 4-4 works and how the PIC uses timer2 to create a square wave. As I stated earlier, you want to know the number the timer2 must count up to create a time equal to the periodic time of the frequency of the square wave you are creating.

The Mark Time or Duty Cycle

So far, you have loaded the PR2 register with the correct number to create a 500Hz square wave but there must be a mark time and a space time or a duty cycle. You can’t leave this calculation out because the registers involved will have data in them already but it will most likely be an unusable value. You need to make sure the value in the register used to control the mark-to-space ratio is the one you want.

You will start by creating a 50/50 duty cycle, which means the mark and space time are both equal to $$ frac{T}{2}=frac{2 ms}{2}=1 ms=1{E}^{-3} $$.

Again Microchip give us an equation to help with this. However, you really want to know the number that you can store in the appropriate register, which the PIC will use to control the duty cycle of the square wave. I have rearranged the expression from Microchip to give an expression for that number; it is shown in Equation 4-6.
$$ Number=frac{MarkTime	imes OscFreq}{TMR2mathrm{Preset}} $$
(Equation 4-6)

This is taken from equation 15.2 in section 15.4 of the datasheet for the PIC18F4525.

Knowing that for a 50/50 duty cycle, the mark time must be 1E-3 for your 500Hz square wave so you can calculate the number as shown in Equation 4-7:
$$ {displaystyle egin{array}{l} Number=frac{MarkTime	imes OscFreq}{TMR2mathrm{Preset}}\ {}	herefore Number=frac{1{E}^{-3}	imes 8{E}^6}{16}\ {}	herefore Number=500end{array}} $$
(Equation 4-7)

So you have calculated two numbers that, when used properly, will give you a 500Hz square wave with a 50/50 duty cycle. There are two issues to consider when it comes to storing these numbers.

First, you set the TMR2 Preset value using the T2CON register. There are three possible values for the TMR2 preset: 1, 4, and 16. Why did I choose 16? Well, if I had chosen 4, the PR2 number would have worked out at 996, and if I had chosen 1, the PR2 number would be 3984. So what is wrong with that? The answer is that this PIC is an 8-bit PIC and unless you can change them, as you can with some SFRs, all registers are 8 bits and an 8-bit register can only hold a value up to 255. So that is why a preset value of 16 was chosen: to reduce the value to below 255. You have the other possible values for the TIMER 2 Preset because you may want different frequencies for the square wave, and you could use different oscillator settings.

Now, as the registers are only 8 bits long, how can you store the number for the duty cycle since the value of 500 uses nine binary numbers? Microchip have given us a solution for this problem similar to the way you store the 10-bit results of the ADC conversion**. You can store the most significant 8 bits in a special register called CCPRXL. Note that there are two, one for each CCP module, so the X will be either 1 or 2.

The two least significant bits go into b5 and b4 of the CCPXCON registers; again, X is 1 or 2. The number you calculated using Equation 4-7 was 500 or 0b111110100. This means b5 and b4 of the CCPXCON registers are set to logic 0 and the 0b01111101 (note that the extra 0 at Bit7 is there to complete the 8-bit number) is loaded into the appropriate CCPRXL registers. You will use CCPR1L since you will be using the CCP1 output and thus the CCP1CON register to create the square wave.

** Please refer to my first book, C Programming for PIC Microcontrollers, for a complete explanation of the ADC.

This will then make a 500Hz square wave appear on the CCP1 of the PIC with a 50/50 duty cycle, assuming you have set the CCP1 pin to output.

The program is shown in Listing 4-1.
 1. #include <xc.h>
 2. #include <conFigInternalOscNoWDTNoLVP.h>
 3. #include <PICSetup.h>
 4. void main ()
 5. {
 6. initialise ();
 7. T2CON = 0x06;
 8. PR2 = 249;
 9. CCP1CON = 0b00001100;
10. CCPR1L = 0X7D;
11. while (1);
12. }
Listing 4-1

The Program for the 500Hz Square Wave

Analysis of Listing 4-1

The new instructions for the program are analyzed here.
Line 7 T2CON = 0X06;

This instruction simply loads the control register T2CON with the following 8-bit number: 0b00000110.

To understand what this instruction is doing, it’s useful to look at the usage of the T2CON register. This is shown in Table 4-4.
Table 4-4

The T2CON Register for Controlling Timer2

Bit7

Bit6

Bit5

Bit4

Bit3

Bit2

Bit1

Bit0

-

T2OUTPS3

T2OUTPS2

T2OUTPS1

T2OUPS0

TMR2ON

T2CKPS1

T2CKPS0

Bit7

Not used

Bit6

These are used to enable Post scales for timer2

0000 = 1:1

0001 = 1:2

"

"

"

1111 = 1:16

Bit5

Bit4

Bit3

Bit2

TIMR2ON

Logic 0 means off, Logic 1 means on

Bits 1 - 0

TMR2 Preset

00 = 1

01 = 4

1x = 16

The following is an explanation of what loading the T2CON register with 0b00000110 does.

This sets Bit2 to a logic 1, which simply turns the timer2 on.

This instruction also sets Bit1 to a logic 1 and Bit0 to a logic 0. This sets the timer2 preset to a value of 16.

Note also that Bits 6, 5, 4, and 3 are set to logic 0, which sets the Post Scalar to 0000. The use of the Post Scalar will be explained in Chapter 6.

Note that Bit7 is not used, so it is set to a logic 0.

In essence, then, this instruction turns Timer2 on and sets the preset value to 16.
Line 8 PR2 = 249;
This loads the PR2 register with the number 249, which is the value you calculated using Equation 4-5 to achieve a frequency of 500Hz. Note that you are using the default radix, which is the decimal number system.
Line 9 CCP1CON = 0b00001100;

This simply loads the CCP1CON, the control register for the CCP1 module with the value 0b00001100. A description of the usage of this control register is given in Tables 4-2 and 4-3. The particular use of the bits is given here.

Bit 7 and 6 of the CCP1CON register are not used, so leave them at logic 0.

Bits 5 and 4 are set to a logic 0 as this is what the two least significant bits of the binary conversion of 500 are as calculated using Equation 4-7.

The remaining fours bits (3, 2, 1, and 0) are set to 1100, respectively, to put the CCP module into the PWM mode of operation.

In essence, loading the CCP1CON register with 0b00001100 sets the CCP module to PWM mode.
Line 10 CCPR1L = 0X7D;

This loads the CCPR1L 8-bit register with the MSB bits of the 500 value created with Equation 4-7 (i.e. it loads 0b01111101 or 0X7D as shown).

In essence, it is the value in the CCPR1L register that sets the width of the up or mark time of the square wave.
Line 11 while (1);

This is using the while (test) type instruction. If the result of the test is true, the program must do what the instruction tells it to do. With the test being the simple (1), then the test result is always true since a logic 1 means true. This means that the micro must always do what the instruction tells it to do.

As there is nothing between the (1) and the semicolon, which signifies the end of the instruction, this while (1) test means the micro will do nothing forever. In this way, you are halting the micro at this point in the program.

This is all you need to do, so there will now be a 500Hz square wave outputted by the PIC on the CCP1 output. This is on bit2 of PORTC.

To test the program, it was simulated using the ECAD software PROTEUS. The circuit and the simulation are shown in Figure 4-2.
../images/497005_1_En_4_Chapter/497005_1_En_4_Fig2_HTML.jpg
Figure 4-2

The simulation of the 500Hz square wave

The oscilloscope shows a square wave with mark and space time equal to each other. Also, as the time for each square division of the screen is 0.2ms and there are 10 squares in one complete cycle, the periodic time T for the waveform is 10 x 0.2m = 2ms. This is the correct periodic time for the 500Hz square wave.

Creating Two Square Wave Outputs

This section of the text will just look at creating two square waves. It will also look at controlling the phase relationship between the two square waves.

The first difference is that you must load the CCP1CON and the CCPR1L registers but also the CCP2CON and the CCPR2L registers. To keep the two square waves at the same frequency and the same duty cycle, you must load the registers with the same values. The only variance is that the CCP1CON register is used to control the phase relationship of the two square waves. This is achieved by changing the logic in Bit1 and Bit0 of the CCP1CON register; the CCP2CON register does not affect the phase relationship between the outputs. The program is shown in Listing 4-2.
 1. #include <xc.h>
 2. #include <conFigInternalOscNoWDTNoLVP.h>
 3. #include <PICSetup.h>
 4. void main ()
 5. {
 6. initialise ();
 7. T2CON = 0x06;
 8. PR2 = 249;
 9. CCP1CON = 0b00001100;
10. CCPR1L = 0X7D;
11. CCP2CON = 0b00001100;
12. CCPR2L = 0X7D;
13. while (1);
14. }
Listing 4-2

The Program for Two Square Wave Outputs

The circuit for the simulation is shown in Figure 4-3 and the oscilloscope screen showing the two square waves is shown in Figure 4-3.
../images/497005_1_En_4_Chapter/497005_1_En_4_Fig3_HTML.jpg
Figure 4-3

The circuit simulation

Channel A, the yellow waveform, displays the CCP1 output and channel B, the blue waveform, displays the CCP2 output. It’s quite clear they are both 50/50 duty cycle and they are at 500Hz.

One thing that I should point out is that they are in phase with each other. This is because I have set Bit1 and Bit0 of the CCP1CON register to logic 0.

If you now set Bit1 and Bit0 of the CCP1CON register to logic 1, which is still allowed, the two square waves will be out of phase by 180O. Try it and see what happens.

Note that if you set Bit0 of the CCP1CON register to a logic 0 while keeping bit 1 at a logic 1, the two waveforms will still be 180O out of phase. The oscilloscope display for b1 is a logic 1 and b0 is a logic 0, as shown in Figure 4-4.
../images/497005_1_En_4_Chapter/497005_1_En_4_Fig4_HTML.jpg
Figure 4-4

Here b1 is set to logic 1 and b0 is set to logic 0.

The two wave forms are out of phase by 180O because when the channel A trace is positive, the channel B trace is at 0V. When the channel B trace is positive, the channel A trace is at 0V. This means the two wave forms are said to be in “anti-phase” with each other.

Setting the Speed of a DC Motor

Now that you have learned how to create one or two square wave outputs, let’s make use of them. Setting the speed of a DC motor is just one of the many useful applications of PIC programming and yet it is probably one of the easiest. Indeed, all you have to do is vary the duty cycle of the square wave since the DC motor will respond to the average voltage. For example, when the duty cycle is 50/50, then you should have half-speed. If it is 75/25, you have 3/4 speed, and when it is 25/75 you have 1/4 speed. However, you should bear in mind that this is with an ideal motor. All real motors have friction and inertia to deal with but the speed can, in essence, be set in this way.

Therefore, to vary the speed you need only change the number stored in the CCPRXL and the other two bits. Note that I am saying setting, not controlling, because if you want to control the speed of the motor you need to measure the actual speed and use it in some sort of closed loop control. This can be done using a PIC micro but this is more than I intend to do in this book.

To help explain setting the speed of the motor, let’s change the duty cycle of the CCP1 output in Listing 4-1 to 75/25 and see what happens to the voltage output on the oscilloscope.

First, calculate the mark time, which is done in Equation 4-8:
$$ MarkTime=T	imes frac{M}{M+S}=2{E}^{-3}	imes frac{75}{75+25}=1.5{E}^{-3}=1.5 mS $$
(Equation 4-8)
Now use this mark time to calculate the number for CCPR1L as shown in Equation 4-9:
$$ {displaystyle egin{array}{l} Number=frac{MarkTime	imes OscFreq}{TMR2mathrm{Preset}}\ {}	herefore Number=frac{1.5{E}^{-3}	imes 8{E}^6}{16}\ {}	herefore Number=750end{array}} $$
(Equation 4-9)

This number converts to 0b1011101110, which means b5 in CCP1CON is set to logic 1 and b4 is set to logic 0. Then 0b10111011 or 0XBB is loaded into the CCPR1L.

The changes to create the 75/25 square wave output are shown in Listing 4-3.
 1. #include <xc.h>
 2. #include <conFigInternalOscNoWDTNoLVP.h>
 3. #include <PICSetup.h>
 4. void main ()
 5. {
 6. initialise ();
 7. T2CON = 0x06;
 8. PR2 = 249;
 9. CCP1CON = 0b00101100;
10. CCPR1L = 0XBB;
11. while (1);
12. }
Listing 4-3

The Changes to Produce a 75/25 Square Wave Output

The simulation to show that the mark is 75% and the space is 25% is shown in Figure 4-5.
../images/497005_1_En_4_Chapter/497005_1_En_4_Fig5_HTML.jpg
Figure 4-5

The square wave output with 75% mark and 25% space

Exercise 4-2

Determine the minimum value of the numbers to be stored in the PR2, CCP1CON, and CCPR1L registers if you want a 20/80 duty cycle with a 2kHz square wave, using the 8MHz internal oscillator.

Driving the Motor

You now have to think about how you can drive the DC motor. Unless you are using very small 5V DC motors, you cannot drive them directly from the PIC. Even with 5V motors, the current the motor could take would have to be fairly small, in the order of 100mA maximum. This is because most outputs of the PIC can only source, or sink, a small amount of current.

This being the case, you will most likely need to use a driver chip. There are many that are suitable, such as the ULN2004A and the ULN2803A. They are Darlington transistors arrays. The ULN2004A is a 16-pin device with seven Darlington transistors; the ULN2803A is an 18-pin device with eight Darlington transistors. Each of the Darlington transistors can sink up to 500mA. They can be connected in parallel if required to accommodate a device that sinks more current. They can be supplied by a VCC up to 30V.

If that current capability is not enough, you can use a single Darlington transistor, and the TIP122 is one such transistor. It can sink up to 5Amps of current.

The ULN Drivers and the TIP122 actually switch the ground onto the load, so they sink the current of the load through them. Since most loads are inductive, and the switching off of inductive loads can lead to high voltage spikes across the Darlington transistor, or other switching device, these drivers all have flywheel diodes to protect them. One final point to appreciate about these driver ICs is that they can be driven from TTL voltages.

All these parameters make them ideal for interfacing the PIC to real-word circuits.

Creating a Three-Speed DC Motor Program

This will extend the basic square wave program to firstly drive the motor at half speed and then, when one of three buttons is momentarily pressed, the speed of the motorc will be changed as follows:
  • If no button is pressed, the motor runs at half speed.

  • If the button connected to RA0 is pressed, the motor speed increases to 3/4 speed.

  • If the button connected to RA1 is pressed, the motor speed increases even further.

  • If the button connected to RA2 is pressed, the motor speed reduces to 1/4 speed.

  • If the button connected to RA3 is pressed, the motor speed returns to 1/2 speed.

The code is shown in Listing 4-4.
 1. #include <xc.h>
 2. #include <conFigInternalOscNoWDTNoLVP.h>
 3. #include <PICSetup.h>
 4. void main ()
 5. {
 6. initialise ();
 7. T2CON = 0x06;
 8. PR2 = 249;
 9. CCP1CON = 0b00001100;
10. CCPR1L = 0X7D;
11. while (1)
12. {
13. if(PORTAbits.RA0) CCPR1L = 0xBB;
14. if(PORTAbits.RA1) CCPR1L = 0xDB;
15. if(PORTAbits.RA2) CCPR1L = 0x3E;
16. if(PORTAbits.RA3) CCPR1L = 0x7D;
17. }
18. }
Listing 4-4

The Three-Speed Control of a DC Motor

The only new instructions are those on lines 13, 14, and 15. They are all of the type if (the test is true) then do what I say here.
Line 13 if(PORTAbits.RA0) CCPR1l = 0XBB;

The test asks if the logic on Bit0 of PORTA is a logic 1. This will only come about if someone has pressed the switch connected to Bit0 of PORTA. If someone has pressed the switch, then the test will be true and the PIC will carry out the instruction CCPR1L = 0XBB;, which simply loads the CCPR1L with the value 0XBB. This is the value needed to increase the speed to 75%, as calculated in Equation 4-6.

Lines 14 and 15 change the speed of the motor in a similar way.

This program is not using the debounce subroutine that was used in Chapter 3. This because you are not concerned with how many times the switch is pressed. However, in a practical situation, it would be useful to use the debounce subroutine if only to prevent any stray noise on the switches changing the speed of the motor unintentionally.

The Proteus schematic for the simulation is shown in Figure 4-6.
../images/497005_1_En_4_Chapter/497005_1_En_4_Fig6_HTML.png
Figure 4-6

The schematic for the three-speed motor

Note that I am using the TIP122 Darlington transistor to switch the motor to ground and so sink the current from the motor. R4 is needed to prevent the transistor pulling the output of the PIC down to around 1.4V. It also sets the current being fed into the base of the TIP122.

The resistors R1, R2, R3, and R5 are there to limit the current flowing through the switches to around 5mA, thus protecting the switches. I refer to this arrangement for the switching as a pull up, as pressing the switch pulls the voltage at the input up to VCC. Others may refer to the arrangement as pull down resistors since the resistors are in the path down to 0V.

Using a Variable Input Voltage to Change the Speed of a DC Motor

This will involve using an analog input; you will use PORTA Bit0 and the ADC to convert the analog voltage to a binary value. Then you’ll use this value to vary the mark time of the square wave feeding the transistor that turns the motors on. This concept is shown in the circuit in Figure 4-7.

The algorithm: The PIC will constantly read the input voltage at the analog input on Bit0 of PORTA. You use that result of the ADC to vary the mark time of the square wave feeding the Darlington transistor.

The program is shown in Listing 4-5.
 1. #include <xc.h>
 2. #include <conFigInternalOscNoWDTNoLVP.h>
 3. #include <PICSetup.h>
 4. #include <math.h>
 5. void changeSpeed ()
 6. {
 7. ADCON0bits.GODONE = 1;
 8. while (ADCON0bits.GODONE);
 9. CCPR1L = round(ADRESH * 0.95);
10. }
11. void main ()
12. {
13. initialise ();
14. TRISA = 0X0F;
15. ADCON0 = 0X01;
16. ADCON1 = 0X0E;
17. ADCON2 = 0b00010001;
18. T2CON = 0x05;
19. PR2 = 249;
20. CCP1CON = 0b00001100;
21. CCPR1L = 0X7D;
22. while (1)
23. {
24. changeSpeed ();
25. }
26. }
Listing 4-5

The Variable Speed DC Motors

../images/497005_1_En_4_Chapter/497005_1_En_4_Fig7_HTML.jpg
Figure 4-7

Setting the speed of a DC motor with a variable resistor

Analysis of Listing 4-5

The new instructions are as follows:
Line 4 #include <math.h>
This tells the compiler to include the header file math.h . This is an open source header file and it includes a whole range of math functions. You need it since you are using the “round” function, which is used to round up a decimal number to an integer.
Line 5 void changeSpeed ()
This is where you create a subroutine to change the speed of the motor. It reads the voltage at the analog input and then coverts it to a binary value that can be loaded into the CCPR1L register to control the PWM of the square wave feeding the DC motor.
Line 6 {
This is the standard opening curly bracket for the subroutine.
Line 7 ADCON0bits.GODONE = 1;
This tells the PIC to start the ADC (analog-to-digital conversion) process.**
Line 8 while (ADCON0bits.GODONE);
This tells the PIC to wait until the ADC has finished. Note the use of while (the test is true ) do nothing in this case.
Line 9 CCPR1L = round(ADRESH * 0.95);

This loads the CCPR1L with the 8-bit number stored in the ADRESH. Note that the ADRESH is where the PIC stores the high byte of the result of the ADC conversion. However, the 8-bit number is multiplied by 0.95 first before it is loaded into the CCPR1L register. However, multiplying an integer by 0.95 could result in a decimal value as the result. A decimal number cannot be loaded into the CCPR1L, so the value must be rounded up first before it is stored in the CCPR1L. This is why you need to use the round function that is listed in the maths.h header file included in line 4.

Another problem is that the value stored in the CCPR1L register cannot be allowed to produce a mark time that is equal to or greater than the period of the square wave. If this was allowed to happen, the square wave would collapse. Since the period time is set by the value in the PR2 register, which in this case is 249, the value for the mark time cannot be allowed to be the same or greater than the value in the PR2 register (i.e. it cannot be the same or greater than 249 in this case). Therefore, if you set the maximum value that can be loaded into the CCPR1L register at 242, a value that is less than 249, then you can determine the multiplying factor that you need to use here.

To do so, you must be aware that the 8-bit value in the ADRESH can go up to a maximum of 255, as it is an 8-bit register. Then using Equation 4-10, you have
$$ Factor=frac{max Value}{255}=frac{242}{255}=0.95 $$
(Equation 4-10)
This is how the value of 0.95 is calculated.
Line 10 }
This is simply the closing curly bracket for the subroutine.
Line 11 void main ()
This is setting up the main loop as before.
Line 12 {
This is the opening bracket of the main loop.
Line 13  initialise ();
This is the call to go through all the instructions of the initialise subroutine. Note that this subroutine is written in the PICSetUp.h header file.
Line 14 TRISA = 0X0F;
This is required because in the PICSetup.h header file you made all the bits on PORTA inputs by writing 0XFF to TRISA. However, as an example of what you have to do if you want something different from what is set in that header file, you are changing the use of the bits on PORTA. Bits 7, 6, 5, and 4 are all set to outputs and Bits 3, 2, 1, and 0 are set to inputs. So you insert an instruction here that overwrites the data in TRISA that you wrote in within the header file, and replaces it with data that you specify here.
Line 15  ADCON1 = 0X01;

This line and the next two lines all write to registers that control the ADC module inside the PIC. For a thorough analysis of how this module works and how these registers allow you as a programmer, to control it, you should read my first book, C Programming for PIC Microcontrollers. However, I will briefly explain what these three instructions do.

This one, on line 15, simply turns the ADC module on and connects it to channel 0, which is on Bit 0 of PORTA.
Line 16  ADCON1 = 0X0E;
This sets all 13 channels, except channel 0, to digital. Channel 0 has to be set to analog as this is the input, Bit0 of PORTA, to which the variable voltage, which will be used to control the speed of the DC motor, will be connected to. That is why it must be set as an analog input and this instruction does that.
Line 17 ADCON2 = 0b00010001;

This controls how long the PIC waits for the whole acquisition and conversion of the analog input to complete. If it does not wait long enough, the result may be inaccurate. If the PIC waits too long, it could be wasting time. Therefore, you must get this setting correct.

In this way, these three instructions control how the ADC conversion operates.
Line 18 T2CON = 0X05;

Table 4-2 describes what each of the 8 bits in this control register does. Loading 0X05 into the register actually loads 0b00000101 in binary into the register. From that, you can see that Bit2 is a logic 1, which simply turns timer2 on.

Also, Bit1 is a logic 0 and Bit0 is a logic 1. This sets the timer2 preset value to 4.

To appreciate what this does, you need to consider the next instruction in the listing.
Line 19 PR2 = 249;
This loads the PR2 register with the value of 249 in decimal. If you now look at Equation 4-11, which can be used to calculate the periodic time of the square wave you can generate, you get the following result:
$$ PERIOD=frac{left( PR2+1
ight)left[4	imes mathrm{Timer}2mathrm{PreSet}
ight]}{OSCFequency} $$
(Equation 4-11)
Putting your values in gets you Equation 4-12:
$$ {displaystyle egin{array}{l} PERIOD=frac{left(249+1
ight)left[4	imes 4
ight]}{8{E}^6}\ {}	herefore PERIOD=frac{400}{8{E}^6}=500{E}^{-6}=500mu send{array}} $$
(Equation 4-12)
Knowing that the PERIOD is given the symbol T and that the frequency can be calculated using Equation 4-13:
$$ {displaystyle egin{array}{l}f=frac{1}{T}\ {}	herefore f=frac{1}{500{E}^{-6}}=2 kHzend{array}} $$
(Equation 4-13)
This shows that it is the combination of the value loaded into the PR2 register and the Timer2 Preset value that sets the frequency of the square wave created, in this program, to 2kHz.
Line  20 CCP1CON = 0b00001100;
This simply sets the CCP1 module into PWM mode so that you can create a square wave on the CCP1 output, which is on Bit2 of PORTC.
Line 21 CCPR1L =  0X7D;
This sets the square wave up with a 50/50 duty cycle. This number, 0X7D, was calculated using Equation 4-2.
Line 22 while (1)
This sets up a forever loop that makes the micro carry out the instructions listed between the following set of curly brackets forever.
Line 23 {
The opening curly bracket for the forever loop.
Line 24  changeSpeed ();

This is the only instruction inside the curly brackets. All it does is call the subroutine changeSpeed. In this subroutine, the micro reads the input on bit0 of PORTA. It then converts the value from analog to digital in the ADC module. It then loads the CCPR1L register with the modified result. Note that it is the CCPR1L register that alters the mark time of the square wave. Therefore, all the program does forever is alter the pulse width of the voltage applied to the motor and thus it alters the speed of the motor.

Note that since there is only one instruction inside the curly brackets, this instruction could have been written as

while (1) changeSpeed ();

This would have saved two lines in the program (i.e. lines 23 and 25).
Line 25 }
This is the closing bracket of the forever loop.
Line 26 }

This is the closing bracket for the main loop.

I hope this explains how the program uses the PWM to set the speed of a simple DC motor by using a variable input.

Creating a Musical Note

As stated earlier, all sounds have their own individual frequency, or combination of frequencies, that allows us humans to separate them from other sounds. Some sounds have a combination of frequencies but a pure note will have just one frequency. The note middle C has a frequency of 261.63Hz with a wavelength of 1.3187 m. See the table in Appendix 7. Ideally the signal should be a perfect sinusoidal waveform. However, a square at 261.63Hz will be good enough for most uses. If you want a more perfect wave, you can pass the square wave through a low-pass filter. However, a simple passive filter will attenuate the signal, therefore an active filter may be better. This is not covered in this book.

Creating the Middle C Note

Creating the middle C note will require creating a square wave at the frequency of 261.63Hz. Applying what you have learned thus far means you will need to determine what value needs to be inputted to the PR2 register and what number to store in the CCPR1L register. As this note is middle C, which would normally be the middle of the range of notes, the value in the PR2 register should be ideally 124 (i.e. the middle value that can be stored in the 8-bit register, the PR2). This is to allow you to increase or decrease the frequency produced equally either side of middle C. Therefore, you know that the PR2 value should be 124 and the period of the 261.63Hz square wave is 3.822ms, so see Equation 4-14.
$$ T=frac{1}{f}=frac{1}{261.63}=3.822 ms $$
(Equation 4-14)
Using the expression for the period shown in Equation 4-11, you can calculate a suitable oscillator frequency as follows in Equation 4-15:
$$ mathrm{PERIOD}=left[ PR2+1
ight]left(frac{4	imes TMR2mathrm{Preset}}{mathrm{OSCFreq}}
ight) $$
(Equation 4-15)
$$ 	herefore mathrm{OSCFreq}=left[ PR2+1
ight]left(frac{4	imes TMR2mathrm{Preset}}{mathrm{PERIOD}}
ight) $$
$$ mathrm{OSCFreq}=left[124+1
ight]left(frac{4	imes 16}{3.822{mathrm{E}}^{hbox{-} 3}}
ight)=2.093145 MHz $$
This is using a TMR2Prset value of 16. You could have used a value of 4 or a value of 1. This means there could be three possible oscillator frequencies you could have used to produce a PR2 value of 124 that would produce a square wave of 261.63 Hz. They are listed in Table 4-5.
Table 4-5

The Three Possible Oscillator Frequencies to Produce the Note Middle C

TMR2Preset Value

Oscillator Frequency

1

130.822kHz

4

523.286kHz

16

2.093145MHz

Since you are using the internal oscillator block, then using the data sheet you can see that any of the available oscillator frequencies would give the same percentage error of around 4.4%. Therefore, it is up to you which oscillator frequency you should use. I chose the 2Mhz oscillator frequency that uses a TMR2Preset value of 16.

So, using these values, you can now calculate the actual PR2 value you must use to produce a square wave with a frequency of 261.63Hz. This is done as follows shown in Equation 4-16:
$$ PR2=frac{mathrm{OCSFreq}}{mathrm{Frequency}	imes 4	imes TMR2mathrm{Preset}}-1 $$
(Equation 4-16)
$$ PR2=frac{2{E}^6}{261.63	imes 4	imes 16}-1=118.4375 $$

As the PR2 register can only store whole integers, let the PR2 value = 118.

The square wave should have a 50/50 duty cycle so the pulse width should equal half the period. Therefore, the pulse time should be 1.911ms. Using this value, the number that must be stored in the CCPR1L register can be calculated as follows in Equation 4-17:
$$ mathrm{NUMBER}=frac{mathrm{MarkTine}	imes mathrm{OscFreq}}{TMR2mathrm{Preset}} $$
(Equation 4-17)
$$ NUMBER=frac{1.911{E}^{-3}	imes 2{E}^6}{16}=238.875 $$
Again, this must be an integer, so let the number be 239. This converts to a 10-bit binary as 0011101111. The two LSB are 1, 1 and they must be stored in Bit5 and Bit4 of the CCP1CON register. The remaining 8 MSB are 00111011. This is the 8-bit number that must be loaded into the CCPR1L register. To confirm that the work so far is OK, you will write a program to create this middle C note. This is shown in Listing 4-6.
1. #include <xc.h>
2. #include <conFigInternalOscNoWDTNoLVP.h>
3. #include <PICSetup.h>
4. void main ()
5. {
           i. initialise ();
           ii. OSCCON = 0b01010100;
           iii. T2CON = 0x06;
           iv. PR2 = 118;
6. CCP1CON = 0b00111100;
7. CCPR1L = 0X3B;
8. while (1);
9. }
Listing 4-6

The Code to Create the Note Middle C

../images/497005_1_En_4_Chapter/497005_1_En_4_Fig8_HTML.jpg
Figure 4-8

The oscilloscope display of a simulation of the program

Figure 4-8 shows the display on the oscilloscope of a simulation of the program. You can see that the time to complete one cycle is 3.8ms, which is the correct periodic time for the note middle C.

It is not showing a perfect 50/50 duty cycle due to the rounding errors in the calculations. An alternative approach, which maybe a better approach, to producing this middle C note is covered in Chapter 7.

Creating a Musical Keyboard

The algorithm for this program is as follows:
  1. 1.

    You need to save the settings for the CCP1CON register and the CCPR1L register for all of the musical notes you are going to make available.

     
  2. 2.

    You also need to allocate an input to each of the eight notes you will be using.

     
The program is shown in Listing 4-7.
 1. /*
 2. * File:   musicalNotesProg.c
 3. Author: hubert.ward
 4. *
 5. Created on 03 January 2019, 14:25
 6. */
 7. //Some include files
 8. #include <conFigInternalOscNoWDTNoLVP.h>
 9. #include <xc.h>
10. #include <PICSetUp.h>
11. #include <musicalNotes.h>
12. //some variables
13. unsigned char n;
14. //Some subroutines
15. void debounce ()
16. {
17. TMR0 = 0;
18. while (TMR0 < 100);
19. }
20. void main()
21. {
22. initialise ();
23. TRISB = 0XFF;
24. TRISC = 0;
25. T2CON = 0X06;
26. OSCCON = 0b01010100;
27. CCP1CON = 0b00001100;
28. while (1)
29. {
30. if (PORTAbits.RA0)
31. {
32. G3;
33. debounce ();
34. while (PORTAbits.RA0);
35. }
36. if (PORTAbits.RA1)
37. {
38. A3;
39. debounce ();
40. while (PORTAbits.RA1);
41. }
42. if (PORTAbits.RA2)
43. {
44. B3;
45. debounce ();
46. while (PORTAbits.RA2);
47. }
48. if (PORTAbits.RA3)
49. {
50. C4;
51. debounce ();
52. while (PORTAbits.RA3);
53. }
54. if (PORTAbits.RA4)
55. {
56. D4;
57. debounce ();
58. while (PORTAbits.RA4);
59. }
60. if (PORTAbits.RA5)
61. {
62. F4;
63. debounce ();
64. while (PORTAbits.RA5);
65. }
66. if (PORTAbits.RA6)
67. {
68. F4;
69. debounce ();
70. while (PORTAbits.RA6);
71. }
72. if (PORTAbits.RA7)
73. {
74. G5;
75. debounce ();
76. while (PORTAbits.RA7);
77. }
78. Nonote;
79. }
80. }
Listing 4-7

The Code for the Musical Keyboard

The Analysis Of Listing 4-7

I will restrict this to the new or more important instructions.
Line 11 #include <musicalNotes.h>

This includes a header file that defines phrases for the settings of the CCP1CON register and the CCPR1L register to create a range of different frequency square waves, one for each musical note you want to use. An example of one of the definitions is

#define C4 PR2 = 118, CCPR1L = 0X3B, CCP1CONbits.DC1B1 = 1, CCP1CONbits.DC1B0 = 1;

These are the values that have been calculated for the note C4, which is middle C. Note that I am also including setting the two bits in the CCP1CON register. They are

CCP1CONbits.DC1B1 = 1, CCP1CONbits.DC1B0 = 1;

Note that CCP1CONbits.DC1B1 is actually Bit5 of the CCP1CON register and CCP1CONbits.DC1B0 is Bit4.

There is a small difference between this type of definition and the previous definitions you have used:

#define startbutton PORTAbits.RA0

The above definition for the startbutton does not end with a semicolon. However, the #define for the note C4 above does end with a semicolon.

This is because the #define C4 is listing a series of instructions and each instruction is separated by a comma but the last one uses the semicolon because it is the end of the list of instructions. There are four instructions for each definition of the note.

The #define startbutton PORTAbits.RA0 is really explaining to the compiler what the label startbutton stands for.

The full listing for this musicalNotes.h header file is given in Appendix 7.

Lines 15 to 19 create the debounce subroutine . The purpose of this subroutine was explained in Chapter 3.
Line 23 TRISB = 0XFF;
This is to load the TRISB register with all logic 1s. This sets all the bits in PORTB as inputs. You need this as the PICSetUp.h header file sets all of PORTB to outputs so you must override this.
Line 26 OSCCON = 0b01010100;

This overrides the setting the PICSetUp.h header file as you want to reduce the oscillator frequency to 2Mhz. This is needed because at 8Mhz the value calculated for the PR2 would be 472. This would be too big to go into an 8-bit register. You cannot divide the frequency any more so you need to reduce the frequency from 8Mhz to 2Mhz.

Lines 30 to 35 set out by asking if the switch connected to bit0 of PORTA has been pressed. If it has, the micro carries out the instructions to produce the note G3. It then calls the debounce subroutine and makes the micro wait until the switch has been released.

In this way, the PIC will play the note G3 for as long as the user keeps the button on Bit0 of PORTA pressed.

As soon as the user releases the button, the micro will move on to line 150.
Line 150 Nonote;

This makes the micro carry out the following series of instructions:

PR2 = 70, CCPR1L = 0X4F, CCP1CONbits.DC1B1 = 0, CCP1CONbits.DC1B0 = 1;

This is because the phrase Nonote is defined as that in the musicalnote.h header file.

What this does is make the mark time bigger than the period as set by the PR2 value. This will make the PIC send out a flat line on CCP1 instead of a square wave. Therefore, the frequency will be zero and no sound will be sent out.

The lines 36 to 149 simply ask if a button connected to one of the inputs has been pressed. If a button has been pressed, it sends out the appropriate note.

I hope this brief analysis helps you to understand how this program works.

Summary

In this chapter, you learned how to use the Capture Compare PWM module of the PIC to create different square waves. You also learned how to use those square waves to set the speed of a DC motor and how to create different musical notes.

You also learned about using the #define statement to create labels and a series of instructions.

In the next chapter, you will look at controlling and using two common types of DC motors, stepper motors and servo motors.

Answers to the Exercises

Exercise 4-1:
  • Frequency = 48.999 Hz

  • Frequency = 1046.502Hz

  • Frequency = 2,200 Hz

Using
$$ lambda =frac{v}{f} $$
when f = 48.999
$$ lambda =frac{343}{48.999}=7m $$
when f = 1046.502
$$ lambda =frac{343}{1046.502}=32.7 cm $$
when f = 2,200
$$ lambda =frac{343}{2200}=15.59 cm $$

Exercise 4-2: Square wave frequency = 2kHz. Therefore, the periodic time = 500μs.

First, you calculate the PR2 value using
$$ PR2=frac{mathrm{OCSFreq}}{mathrm{Frequency}	imes 4	imes TMR2mathrm{Preset}}-1 $$
To get the minimum values, try the highest TMR2Preset value first. If this doesn’t produce a valid PR2 value, try the next TMR2Preset value. Therefore, start off with a TMR2Preset of 16.
$$ PR2=frac{8{mathrm{E}}^6}{2{E}^3	imes 4	imes 16}-1=63 $$

This PR2 value is OK.

The 20/80 would give a mark time of 125μs. Therefore, using Equation 15, you have:
$$ number=frac{125{E}^{-6}	imes 8{E}^6}{16}=63 $$

Converting 63 to a 10-bit binary number gets you 0000111111. The two least significant bits are 1,1 and they are stored in the CCP1CON register Bits 5 and 4. The remaining 8-bit number of 00001111 is what is stored in the CCPR1L register. You could use 0b00001111 or 0X0F or decimal 15.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.189.63