Working With The ADC (PART I)

The ADC is one of the most important blocks inside any of the PAC5xxx devices. Whereas there are plenty of sensor conveying real world information throughout the means of digital signals (e.g. a sonar sensor), a good portion of the sensory mechanisms out there rely on analog data. As a result, the only way in which we can take advantage of said analog information is by using an Analog to Digital Converter (ADC).

Here are some examples of sensors you may want to interface to which will most likely provide data in the analog domain:

1. Accelerometers.

2. Temperature Sensors

3. Humidity Sensors

4. Pressure Sensors

5. PH Sensors

6. Current Sensors

7. Voltage Monitoring

8. Light Sensors

9. Sound Sensors

10. Many more!

As you can see, there are plenty of sensory blocks which give us information a GPIO or digital input would not be able to do much with. For example, if you have a temperature sensor which gives you a voltage of 0.01V for a 10C temperature, goes up with each 1C degrees 0.01V until reaching 1.25V for 125C, then what kind of information could you be able to obtain with a GPIO? Pretty much zero! And I mean, LITERALLY!

Since a GPIO can only read 1’s and 0’s, and a 1 is read whenever the input voltage is higher than the threshold voltage (about 2.8V for TTL logic), then our temperature sensor will always read as 0, regardless of the ambient temperature.

An ADC input, however, would give us a representation in binary format of what the sensor voltage is, and hence the ambient temperature could be obtained.

Let’s take for example the PAC52xx ADC unit, which is a 10 bit ADC with a 2.5V reference voltage. What this tells us is that the ADC can read voltages as high as 2.5V (anything higher than the reference voltage would saturate the ADC and read as maximum value).

Since the ADC is 10 bits, this tells us we can get a total of 1024 possible readings ( 2^10 = 1024 counts). If we divide the maximum voltage of 2.5V by the maximum number of counts, we obtain the ADC resolution. In this case, 2.5V / 1024 counts = 2.44 mV / count.

What does this mean? Well, pretty much our ADC can only sense differences in voltages that are 2.44 mV apart. If you have two voltages which are 1.00244V and 1.00254V, the ADC will think of both numbers to be the same.

Luckily, our temperature sensor has a scale of 10mV/C (ten milli volts per degree Celsius), which means our ADC has plenty of resolution to discern different temperatures.

Here is an example of how different temperatures would look

Temperature (Degrees C) Sensor Output (V) ADC Representation (decimal) ADC Representation (Binary)
10 0.1 40 101000
11 0.11 45 101101
12 0.12 49 110001
13 0.13 53 110101
14 0.14 57 111001
15 0.15 61 111101
16 0.16 65 1000001
17 0.17 69 1000101
18 0.18 73 1001001
19 0.19 77 1001101
20 0.2 81 1010001
21 0.21 85 1010101
22 0.22 90 1011010
23 0.23 94 1011110
24 0.24 98 1100010
25 0.25 102 1100110

 

The equation used to transform the Temperature Sensor voltage to ADC reading was:

ADC reading = (Sensor Voltage / 2.5V) * 1023

This sensor only works until reaching an output equal to 1.25V (for 125 degrees C), so the ADC has enough head room to represent all possible temperatures (from 10C to 125C).

On the next blog posting I will introduce the PAC52xx ADC block and we will then study how to initialize it and use it to gather data for us.

Downloads:

Download Windows Application GUI for this project: Hydra-X_ADC_UART_GUI

Download Coocox Firmware for this project: HYDRA-X_ADC_SampleProject_10-30-2014

 

 

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

HYDRA-X Aids In The ALS Water Challenge Automation

This summer has been cooler than most previous ones, thanks to a wave of challenges accepted by people all across the globe with the intention of raising awareness for the Amyotrophic Lateral Sclerosis (ALS) or Lou Gehrig’s disease and increasing donations toward research for this noble cause.  Avayan documents how he decided to fully automate his ALS challenge by using an HYDRA-X10 board, tasked with controlling the actuation of the ubiquitous water bucket.

On his Instructable, Avayan tells us how he designed his rig, but also how the entire system operates. Letter “a” on his GUI would send a serial command to the HYDRA-X board through the computer’s COM Port, which would then send respective control signals to a stepper driver.

This project is a clear example of how to take advantage of the HYDRA-X10 integrated serial port, GPIO’s and timer functions, while taking advantage of its compact form factor and versatility.

We are glad HYDRA-X could be employed to help in raising awareness for such a noble cause!

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

Wheel Chair Robotics Using HYDRA-X, On Instructables

Avayan details at Instructables how he used two HYDRA-X20 Body boards with the BLDCM1 Head attachment to control the two large brushed DC motors found on most common electric wheel chairs or mobility scooters. The project revolves around the RC2PWm algorithm detailed on previous posts.

The system receives RC commands from an RC Radio/Receiver combo. The HYDRA-X board decodes the RC PWM signal and generates the PWM the motor needs to move in the right direction and at the commanded speed. A camera mounted on top of the robot gives us views from different angles.

This is a fun project which can be expanded to many different platforms, but could only be accomplished with a powerful power stage which is easy to obtain by using the integrated power stage within the PAC5220 at the HYDRA-X20 board, and the external FETs as provided by the BLDCM1 Head.

The instructable can be found here.

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

PWM Outputs for Motor Control – Finally, Controlling it!

Everything we have done so far has to do with initializing the motor driver, but we really haven’t done much driving at all. You will be amazed, but there is actually not a lot to this. In fact, there are only three functions taking care of the actual PWM actuation.

On this project there are three things I want to achieve.

1. I want to be able to enable and disable the motor.

2. I want to be able to control motor speed (i.e. control PWM duty cycle).

3. I want to be able to control motor rotation direction (i.e. motor current direction flow).

Believe it or not, here is all the code we need! And for the record, this could have been achieved on a single function. I made it into three functions to make it easier to follow and because I wasn’t feeling too lazy.

void motor_pwm_update(uint16_t duty_cycle)
{
//Motion Control PWM Duty Cycle should only be updated if
//the variable contains a value smaller than the TIMER A Period
if (duty_cycle < TIMERA_PERIOD)
{
PAC5XXX_TIMERA->CTR4 = duty_cycle; //TIMER A PWMA4 duty cycle update
PAC5XXX_TIMERA->CTR5 = duty_cycle; //TIMER A PWMA5 duty cycle update
}
}

void motor_pwm_disable()
{
PAC5XXX_GPIOA->OUT.b &= (~MOTOR_PWM_PIN_PORT_A); // PA[0,1] UL, VL PA[3,4] UH, VH
PAC5XXX_GPIOA->PSEL.s &= ~PSEL_MASK_A;

PAC5XXX_TIMERA->CTR4 = 0;
PAC5XXX_TIMERA->CTR5 = 0;
}

void h_bridge_config(uint8_t dir)
{
if (dir) //Motor moves Forward. Current flows from U to V
{
PAC5XXX_GPIOA->OUT.b = 0x02; //V output set to LO
PAC5XXX_GPIOA->PSEL.s = 0x0081; //U output set to PWM. V output set to GPIO
}
else //Motor moves Reverse. Current flows from V to U
{
PAC5XXX_GPIOA->OUT.b = 0x01; //U output set to LO
PAC5XXX_GPIOA->PSEL.s = 0x0104; //V output set to PWM. U output set to GPIO.
}
}

The function motor_pwm_update() is only used to update the PWM duty cycle. Notice I am updating the duty cycle on both half H Bridges although only one half H Bridge is PWM at any given time. The other half H Bridge is configured to enable the low side FET at all times during the PWM cycle (remember the Slow Decay methodology from the previous post?)

Updating the duty cycle of a PWM which is not being used is not a problem. The reason why I did it like this is because I do not want the function to worry about direction information. It updates the duty cycle and we are done!

The second function we need to understand here is the motor_pwm_disable(). This function disables the PWM’s by configuring the FET control outputs to GPIO, making the respective GPIO pins logic LO and then changing the duty cycle to 0.

I could have simply reconfigured the resources to be all GPIO and forget about the duty cycle. But the function that enables the H Bridge would fire up with a higher than 0% duty cycle so it is best to make sure that doesn’t happen.

The last function in this game is h_bridge_config(). This is the function in charge of configuring the half H Bridges so they are either Low Side FET ON or PWM side. The “dir” argument specifies whether I want current to flow from U to V or from V to U.

Something I must point out is that it is the h_bridge_config() function the one that actually takes care of enabling the H Bridge. This may seem odd, but the truth is there are many ways to skin the cat (although I truly have nothing against cats either).

When we configure the PASEL and PAOUT registers (which is only done inside of h_bridge_config()) , the H Bridge becomes alive. This is why I want the motor_pwm_disable() function to ensure the current duty cycle is zero. We do not want that motor to start up abruptly!

These three functions are called within the main control code which I will detail on the last post of this series. Basically the RC2PWM code will continually scan the RC PWM, determine the motor command (more on this later) and then adjust PWM and motor direction accordingly.

And there you have it! This is how we can control a brushed DC motor.  You can use this information to deal with many other forms of brushed DC motor control. For example, whether you are using a computer to specify motor speed or direction through a serial port, or whether you are using a potentiometer voltage read through an ADC input to specify the same parameters, the code we have specified up to this point would apply.

In a future project I will detail how to control different motor topologies such as 3 phase BLDC motors. You will see that although the control mechanism may be different, the concepts of PWM and H Bridge control translate perfectly well!

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

PWM Outputs For Motor Control – H Bridge Theory

It may seem a little counterintuitive to talk about PWM control for motors and not talk about the H Bridge. So before I confuse you any further, please allow me to confuse you with something else.

The H Bridge is something which has been discussed plenty out there. So I will not detail every possible angle about it, although for the subsequent code to make sense, I do need to detail my own nomenclature. In doing so, we will see most of what an H Bridge is and can do.

There is no better way to start talking about the H Bridge than by showing you the actual thing:

HBridgeBlock

It is called an H Bridge because it looks like the letter “H”. And it is a Bridge because it connects two sides so that current can flow in one of two possible directions. This is the perfect topology for powering up our brushed DC motor.

On a deeper level, we can see it is composed of 4 n Channel FETs. The two left side FETs make what we will call half H Bridge U, and the two right side FETs we will call half H Bridge V. Notice they could have been called half H Bridges A or B (or L and R), but since I am parting from a 3 phase motor inverter board (whose pin names are often called U,V,W) and I will use the U and V outputs, I will go with the U and V nomenclature.

If we have sides, we also have levels. The high side FETs are called high side FETs and the low side FETs are called low side FETs. REALLY??? Who would have guessed?

We can then give these FETs proper names such as UH (U Side, High Side), UL, VH and VL. Just so that we can keep track of them, because here is where we will start confusing things… NAH! This will be a breeze for you!

The importance about an H Bridge is that it gives us the ability to control both motor shaft direction of rotation (by selecting which FETs are ON and OFF) and  motor speed (by applying an ON/OFF rate to the FETs).

For example, we can control direction of current flow, which in turns specifies motor rotor direction of rotation, by enabling UH and VL or by enabling VH and UL as shown below:

HBridgeDirection

The two conditions above are considered legal conditions. Do note there are two other combinations we must avoid at all costs! They are better known as shoot through and consist of enabling both UH and UL FETs, or both VH and VL FETs, at the same time. The reason is that doing so entails a very low resistance path, which will most likely increase current to disproportionate levels, not to mention cook the power switches. Hopefully my depiction below has big enough prohibition crosses…

shootthrough

Needless to say, trying to enable both same side FETs in code is not a good idea. There are still cases, however, when both same side FETs could end up being turned ON at the same time. This mostly happens during switchover and can be easily remedied if a “Break Before Make” (BBM) connection topology is employed.

On conventional controllers, this BBM mechanism has to be implemented in code. On the other hand, our PAC5220 has internal circuitry, referred to as Dead Time Generators, which eliminate the need to code this! This block imposes a programmable dead band period of time which ensures enough time has passed between one FET turning OFF and the other one turning ON.

If you have taken the time to read the previous INITIALIZATION post  you will see two lines of code which initialize said block:

// Configuring Death Time Generators (DTG)
pac5xxx_dtg_config(DTGA0, MOTOR_LED_TICKS, MOTOR_TED_TICKS, 0, 0, 0, 0);
pac5xxx_dtg_config(DTGA1, MOTOR_LED_TICKS, MOTOR_TED_TICKS, 0, 0, 0, 0);

I will talk more about Dead Time Generators, and how to program them, on a different post.

Now, I told you previously we can control both direction and motor speed by specifying which FETs are ON and OFF. So far we have seen that selecting alternate FETs grant us the ability to control current flow direction, so how do we control speed then?

If you will recall from the INTRODUCTION post, to control motor speed what we need to do is specify how long the FETs are ON and OFF. The rate of ON and OFF we called the duty cycle. So all we have to do is choose one of the two possible legal conditions for current flow (the two that are not shoot through!) and turn their FETs ON and OFF with a controlled rate.

Not so fast! Turning FETs ON and OFF may seem as simple as turning FETs ON and OFF, but the laws of physics will be quick to get us at our own game if we are not cautious. The most nefarious aspect we have to keep in mind here, is that we are driving an inductive load and by the laws of physics, an inductive load will not allow abrupt changes in current.

What this means is that when we enable the FETs, the current will increase to some value. It should then be obvious that when we disable the FETs, the current will not be zero. Whatever it’s value is, if we try to change it abruptly, it won’t! Instead, the voltage will increase to some unwanted value. This is when stuff (as in FETs!) may break, so needless to say, we truly  do not want this to happen. What can we do?

We need to let the current continue to flow such that the law is preserved. We are going to kill a couple of birds with a single stone here (not that I have anything against birds…). Basically, the way in which we are going to apply our motor control PWM will allow for our FETs to be energized while maintaining current flow.

To do so, I will always have one low side FET turned ON and apply the PWM signal on the opposing side. It will look something like this:

H Bridge PWM

On this direction polarity, my VL FET will always be ON and the VH FET will be high impedance (OFF). The U Side will have the PWM which means that during TIME ON the high side FET UH will be energized (ON) and the low side will be OFF. Once the duty cycle elapses, and we move to the TIME OFF region of the PWM cycle, the UH FET is switched OFF and the UL FET will is switched ON.

Notice this switchover allows for the current to continue flowing from side U to side V as it was doing before. Of course current will be charging across the inductance during TIME ON and discharging during TIME OFF, but no abrupt change will take place, hence preserving the law.

The mechanism which allows for current to continue flowing across an inductive load in a safely manner is better known as Current Decay or Current Regulation. In this particular case we will use what is known as Slow Current Decay, because the current decays at a slow rate through both low side FETs. The topic of current regulation topologies is material for a different post.

With this PWM structure we have not only allowed current to decay safely we have in fact added a speed control mechanism as well. Motor speed will be directly proportional to the applied duty cycle, so all that we need to do is update this parameter and VOILA! Motor speed control at your disposition!

Do note that all this talk about turning this FET ON and turning this FET OFF may give you the impression there is a lot of code we need to take care of. In the following post you will see how little code we need. If you remember from previous posts, using PWM’s requires very little code because the hardware takes care of most of the required actuation.

There are only two things we need to do in code, and this only happens sporadically:

1. To configure each GPIO resource as a GPIO or a PWM output.

2. To enable the FET which happens to be configured as a GPIO on its respective Output Register.

In order to understand this better, we need to take a look at some registers. As it turns out, each H Bridge FET is connected to a PORT A resource. All of this stuff is detailed on the PAC5220 Users Guide’s chapter 25, so feel free to refer to it for further information. For the time being, please accept this mapping:

PA0 maps to DRL0 (low Side 1 which we will use as UL)

PA1 maps to DRL1 (low side 2 which we will use as VL)

PA2 maps to DRL2 (low side 3 which is WL and we will not use on this project)

PA3 maps to DRH3 (high side 1 which we will use as UH)

PA4 maps to DRH4 (high side 2 which we will use as VH)

PA5 maps to DRH5 (high side 3 which is WH and we will not use on this project)

The first step above (to configure each PAx pin as a GPIO or PWM output) is taken care of by writing to the PAPSEL register.

PAPSEL

Notice each two bits take care of one PAx resource. All of them become GPIO resources if we write a 00b to their respective location. The other combinations are specified below. For example, when we want PA3 to behave like a PWM, we can either do 01b, 10b or 11b. I have underlined which resources we are supposed to use. In the case of PA3 we want to use PWMA4, so we will use the combination 10b.

As explained earlier, we have two legal combinations:

1. U side  is PWM and V side is LO

2. V side is PWM and U side is LO

When a side is meant to be a PWM we will configure it with the PWM variant (e.g. 01b or 10b depending on the resource), and when a side is meant to be LO, we will configure it as a GPIO (e.g. 00b).

Simply configuring a resource as a GPIO, however, does not enable the FET. When we enable the resource as a GPIO we are simply stating that PAx pin will be an input or an output. We still need to configure the resource as an output and ensure the output is set to HI (which turns the low side FET ON).

To enable low side FETs we will need to look at the PAOUT register.

PAOUT

I will show you on the next posting the code where we modify PAOUT in order to enable and disable the low side FETs. All you need to understand at this time is that we will either make PA0 HI (UL FET is turned ON) or PA1 HI (VL FET is turned ON).

And so a question must arise: But where did we configure PA0 and PA1 as outputs? We actually did this during our initialization post on this line of code:

pac5xxx_gpio_out_enable_a(MOTOR_PWM_PIN_PORT_A);

We will not need to change whether PA0 and PA1 are inputs or outputs on a continuous basis, so we can do this once during the init. We will, on the other hand, need to change continuously whether the low side FET will need to be enabled/disabled. But as you will see later on, this is actually quite easy and takes very little code.

 Hopefully this is not too messy and you have managed to follow the discussion.  Next step will be to take a look at the code which actually configures the H Bridge for direction and modifies the PWM’s duty cycles.

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

PWM Outputs For Motor Control – Protection

One of the biggest problems we encounter while driving a motor is the fact that a power stage driving an inductive load is at risk of destruction. There are many things that can go wrong, but one of the most dreaded scenarios is for the power stage to be subjected to a condition in which current grows too large, too quickly, for the power switches to be able to take it.

I will not go into the details of all the possible scenarios which could bring sad tales of destruction and smoke release as I think we can all agree it is on our best interest to ensure they never take place. But for the most part, what we want to avoid here is a case in which the H Bridge supplies current to a very low resistive load, namely a short from output to output.

Typical H Bridge drivers will incorporate some kind of a mechanism to disable the power stage whenever current increases above a programmable level. Blocks in charge of such a task are better known as Over Current Protection or OCP.

The PAC52xx family of devices contain enough analog circuitry to grant us a very robust OCP functionality, protecting our power block from self annihilation. It comes in the form of a differential amplifier connected to a comparator along with a programmable 10 bit DAC. In other words, we program the 10 bit DAC to give us a voltage from 0V to 2.5V; say 1V. If the output of our differential amplifier ever goes above the DAC output, then the comparator will fire into a logic block which will immediate disable the H Bridge.

OCP_Block

The figure above portrays a crude block diagram of the aforementioned system. For a better diagram (with all pertinent internal components) I suggest you take a look at the PAC5220’s User Guide (chapter 24.3.1). Still, we can see that motor current will flow through a resistor we call the SENSE resistor. Of course this current will dissipate a voltage. This is the voltage we will sample and use to determine if a dangerous current is haunting our application.

For example, imagine I have a 10A current flowing through my motor (and hence my SENSE resistor). On my BLDC motor Hydra Head, I have a SENSE resistor equal to 3 milli ohms. Yes, that small! As a result, 10A will dissipate a 30 mV voltage across the resistor.

30 mV are kind of tough to read, so it would be nice if we could somehow amplify this signal. Luckily, the PAC5220 has all the goodies we need! In this case, the differential amplifier has a series of programmable gains we can benefit from. On this project I have chosen to use an 8X gain, which would take my 30 mV input to 240 mV.

It is this 240 mV the voltage we will compare with the internal 10 bit DAC output. So say you don’t want a current higher than 10A on your system. If so, then we need to program the DAC to output a 240 mV output. If the system current happens to be larger than 10A, then the comparator output will become asserted and our protection block will be engaged, disabling the H Bridge.

Since our DAC is a 10 bit block, and its reference voltage is 2.5V, each bit represents 2.5V/1024 = 2.44 mV. To program the DAC to output 240 mV, we would need 0.240V / 0.00244V = 98.3 counts. We could select 98 counts , which would give us 9.97A shutdown point (98 * ((2.5V/1024) / 8) / 0.003 Ohms = 9.969A) or 99 counts , which would give us a 10.07A shutdown point (99 * ((2.5V/1024) / 8) / 0.003 Ohms = 10.07A). Either would be fine, in my opinion.

The tricky part when dealing with OCP is selecting the shutdown point. If you select the shutdown current too high, you risk FET localized heating which results in reduced life (and could be as bad as no FET life at all!). On the other hand, if you select the shutdown current too low, the system may trigger an OCP event during normal run time. No need to start a discussion of which one is worst, as they are both highly undesirable. So how do we do it?

Unfortunately this is one of those things where experience will have the last word. On my current project experiments I have chosen a 50A maximum current because that allows me to run the system while under load, but also protects the FETs if I do something foolish on my firmware. When I increased the OCP value to 75V, death ensued.

Each system will have its own set of requirements. Larger motors will require larger currents and vice versa. Which one will you need only you know. But here is the code that I am using at the moment so you can modify it according to your own system’s needs. I have documented it enough so you can make your own changes in the event you need a smaller current.


// Configure AIO10 for Over Current protection

// CFGAIO0: 0x63 = 01100010 (01xxxxxx = DiffAmpMode; xx100xxx = DiffAmp Gain is 8X;

// xxxxxx11 = LP10 Comparator Enabled with 4 us Blanking Time)

pac5xxx_tile_register_write(ADDR_CFGAIO0, 0x63);

// CFGAIO1: 0x20 = 00100000 (0010xxxx = LPROT10 PR1 enable; xxxx0xxx = DiffAmp Offset Disabled;

// xxxxx0xx = DiffAmp Calibration Disabled; xxxxxx00 HP10 Comparator Disabled

pac5xxx_tile_register_write(ADDR_CFGAIO1, 0x20);

// 50A

// Set HPROT and LPROT protection threshold

// Voltage at SENSE R = 50A * 0.003 Ohms = 150 mV (assuming maximum current of 50A)

// Voltage after Diff Amp = 150 mV * 8 = 1.2V

// DAC Voltage must be equal or larger than 1.2V. Since LPDAC is 10 bits and VREF is 2.5V

// DAC# = 1.2V/2.5V * 1023 = 491d. 491d = 0x1EB = 0111101011b

// LPDAC0 contains 8 MSB's (bits 9:2) LPDAC0 = 01111010b = 0x7A

// LPDAC1 contains 2 LSB's (bits 1:0) LPDAC1 = 00000011b = 0x03

pac5xxx_tile_register_write(ADDR_LPDAC0, 0x7A); // LPDAC: LPROT DAC (bits 9:2) 0x7A

pac5xxx_tile_register_write(ADDR_LPDAC1, 0x03); // LPDAC: LPROT DAC (bits 1:0) 0x03

// Enable HPROT, LPROT hysteresis

pac5xxx_tile_register_write(ADDR_SIGSET, 0x04); // Enable comparator hysteresis on LPROT comparators

// Enable protection interrupt mask for LPROT10

pac5xxx_tile_register_write(ADDR_PROTINTM, 0x01); // PROTINTEN: LP10INTEN

// High Side and Low Side PR1 Protection Enable

pac5xxx_tile_register_write(ADDR_CFGDRV1, 0xA0); // CFGDRV1: HPROT will disable HS and LS drivers

// Enable system nINTM

//Interrupts will be made available on the nIRQ pin at PORTB

pac5xxx_tile_register_write(ADDR_SYSSTAT, 1); // Enable nINTM interrupts from CAFE to MCU (SSEN = 1)

Do note I do not recommend using larger than 50A currents with the EH-BLDCM1-1 Head board. Not for long, at least. The reason is the SENSE resistor is rated at 7W and at 50A, it will be seeing 7.5W (50A * 50A * 0.003 Ohms = 7.5W). This problem can be solved by using a different SENSE resistor.

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

PWM Outputs For Motor Control – Initialization

We used PWM outputs a little bit during one of our LED blinking code examples. If you read through those tutorials, chances are you will recognize some of the SDK instructions.

On this blog post, I will not detail every single instruction. I have added enough comments on the code to ensure the most important aspects of the SDK function calls can be understood.

	//CLKIN Source is Reference Clock.
	//On this code example, we will use the PLL. It will be running at 50 MHz
	//ACLK will be running at 50 MHz and and HCLK will be running at 25 MHz
	pac5xxx_sys_ccs_config(CCSCTL_CLKIN_CLKREF, CCSCTL_ACLKDIV_DIV1, CCSCTL_HCLKDIV_DIV2);
	pac5xxx_sys_pll_config(50);
	pac5xxx_sys_ccs_pll_select();
	pac5xxx_memctl_wait_state(FLASH_WSTATE_HCLK_LTE_25MHZ);

	pac5xxx_gpio_out_enable_e(0x39);		//JIQ DEBUG

	uart_init();

	// Configure Timer D clock input for HCLK source and /16 divider
	// Timer D Frequency is 1,562,500. Each clock cycle is 640 ns.
	// Full Timer D period (640 ns * 65535) is 41.94 ms. Enough to measure an RC PWM pulse
	pac5xxx_timer_clock_config(TimerD, TxCTL_CS_HCLK, TxCTL_PS_DIV16);

	// Configure Timer D to COUNT UP Mode and count up to max value (65535d = 0xFFFF)
	pac5xxx_timer_base_config(TimerD, 0xFFFF, 0, TxCTL_MODE_UP, 0);

	// PD4 is configured as PWMD1 - RC PWM signal input
	pac5xxx_timer_io_select_pwmd1_pd4();

	// Timer D CCTR0 configure to CAPTURE and INTERRUPT on both RISE and FALL edges
	pac5xxx_timer_cctrl_capture_config(TimerD, CAPTURE_MODE, RC_PWM_CCTR, INT_INTEN,TxCCTL_CED_BOTH);

	// Timer D Base interrupt is used as a Watchdog Timer to ensure RC transmitter is in place
	pac5xxx_timer_base_int_enable(TimerD, 1);

	// Timer D Interrupt priority is configured to 1
	NVIC_SetPriority(TimerD_IRQn, 1);
	NVIC_EnableIRQ(TimerD_IRQn);

	// Configure nIRQ1/PB0 [CAFE fault interrupt] as digital input (active low)
	// The PB0 interrupt tells us whether there has been an Over Current Event
	pac5xxx_gpio_configure_interrupt_b(NIRQ1_PIN_MASK, 0);

	// Configure Motor PWM channels (Timer A)
	// Configuring Death Time Generators (DTG)
	pac5xxx_dtg_config(DTGA0, MOTOR_LED_TICKS, MOTOR_TED_TICKS, 0, 0, 0, 0);
	pac5xxx_dtg_config(DTGA1, MOTOR_LED_TICKS, MOTOR_TED_TICKS, 0, 0, 0, 0);

	// Configure Timer A clock input for HCLK source and /1 divider
	// Timer A Frequency is 25 MHz. Each clock cycle is 40 ns.
	// Full Timer A period will be set to 1024 (0x3FF) ticks.
	// 40 ns * 1024 ticks is 40.96 us.
	// PWM Switching frequency will be 24.414 KHz.
	// This is higher than audible noise.
	pac5xxx_timer_clock_config(TimerA, TxCTL_CS_HCLK, TxCTL_PS_DIV1);
	pac5xxx_timer_base_config(TimerA, TIMERA_PERIOD, 0, TxCTL_MODE_UP, 0);

	pac5xxx_timer_io_select_pwma4_pa3();	// UH
	pac5xxx_timer_io_select_pwma5_pa4();	// VH
	pac5xxx_timer_io_select_pwma0_pa0();	// UL
	pac5xxx_timer_io_select_pwma1_pa1();	// VL

	pac5xxx_gpio_out_enable_a(MOTOR_PWM_PIN_PORT_A);

	// Configure Timer B clock input for HCLK source and /8 divider
	// Timer B Frequency is 3,125,000. Each clock cycle is 320 ns.
	// With 3,125 Timer B ticks, we can measure 1 ms.
	// This will be our main control time base (for PWM Update)
	pac5xxx_timer_clock_config(TimerB, TxCTL_CS_HCLK, TxCTL_PS_DIV8);

	// Configure Timer B to COUNT UP Mode and count up to max value (65535d = 0xFFFF)
	pac5xxx_timer_base_config(TimerB, 3125, 0, TxCTL_MODE_UP, 0);

	// Timer B Base interrupt is used as our 1 ms Time Base
	pac5xxx_timer_base_int_enable(TimerB, 1);

	// Timer B Interrupt priority is configured to 2
	NVIC_SetPriority(TimerB_IRQn, 2);
	NVIC_EnableIRQ(TimerB_IRQn);

	// Set initial duty cycles;
	new_command.DIR = 0;
	current_command.DIR = 0;
	h_bridge_config(current_command.DIR);

	new_command.DCP = 0;
	current_command.DCP = 0;
	motor_pwm_update(current_command.DCP);

The massive code above is my peripheral_init() function. It is in charge of initializing the resources we will be using for our RC2PWM application. Namely, these are:

1. System Clock: Our clock will run at 50 MHz.

2. Timer D: We will use Timer D to measure pulses. In this case, the pulses are the PWM pulses originating from the RC receiver and are the ones carrying command information. We will use Capture Module D0 to trigger on both rise and falling edges (e.g. generate an interrupt). We will also generate an interrupt on Timer D overflows as we can use this information to verify RC PWM pulse integrity.

3. Timer B: We will use Timer B as a 1 ms tick which will run the state machine in charge of ramping up and down the motor’s PWM command.

4. GPIOB Interrupt: We will use one of the GPIOB resources to generate an interrupt if an overcurrent is generated. This is very important because we do not want our system to enable the FETs when deadly shorts are present.

5. Timer A: We will use Timer A as our PWM generator. We will also use the associated Dead Time Generators (DTG) to ensure the FETs are not subjected to shoot through.

6. PWM GPIO: We will use two out of the three half H Bridges. The PAC5220 Users Guide offers more information on which PORTA resource is connected to the H Bridge pre drive stages.

The entire PAC5220_RC2PWM project will be posted on the Downloads Page. Feel free to download a copy and study this exciting project further.

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

PWM Outputs for Motor Control – Introduction

On our current project, we have learned how to sample the pulse radiating from a Remote Control radio and sample its timing information. We could use this information for plenty of different purposes, but what I would like to accomplish in this project is controlling a brushed DC motor remotely. It is very important to understand the PWM from an RC source is an informational form of PWM and as such it cannot be directly employed to control a motor.

A motor requires a completely different form of PWM to operate. It requires what I like to call an Energy PWM. What do I mean by this?

Not certain if this is apparent to you, but allow me to assure you the speed of any motor is directly proportional to the voltage we apply to it. Yes, I know… Some people like to say it is the current which drives the motor speed. Not wanting to start a war here, so I will let this topic slide for the moment. Just concede me a little favor and for the time being please accept that voltage is what drives the motor speed.

If so, what happens when I connect my motor to a 12V battery? Well, it will move at the speed it will move when a 12V voltage is applied at its terminals, of course. This is all fine and simple to follow, but what if I wanted the motor to move at a slower rate? I would either need a different battery or I would somehow need to scale the voltage down.

Somebody may suggest using a voltage divider and although this could work, I would prefer to advise against such a solution. You would lose too much energy in the form of heat across those resistors. Oh no! What we want to do is to decrease the voltage the motor sees by applying a PWM across its terminals.

When you apply a PWM to the motor, you are in essence connecting and disconnecting the motor very rapidly (i.e. thousands of times per second). What happens then is that the motor inductance gets charged and discharged at the rate of the PWM frequency. But because the motor inductance can only charge when we are applying the voltage and discharge when we remove the voltage, what the motor ends up seeing is a voltage directly proportional to the PWM duty cycle.

But what is the PWM Duty Cycle?

Duty cycle refers to the ratio of TIME ON to TOTAL TIME. In other words, how long the motor is enabled with regards to how long it could have been enabled, if maxed out. The higher the TIME ON, the more energy is being transferred into the motor. We can use the following equation to derive this duty cycle parameter:

DutyCycle

It may make sense to offer a quick refresher on PWM technologies. Remember that Total Time is in essence the same as the Period, which is basically the inverse of the PWM frequency. Also remember that Total Time must equal TIME ON + TIME OFF.

Duty Cycle will in essence give you a number from 0 to 1. We can normalize this to whatever we want; I like to use percentages (e.g. multiply the 0 to 1 number by 100). We will see, however, at the end it is all the same. What matters is the ratio!

PWM_DutyCycle

The picture above shows a PWM signal where the duty cycle is 25%, 50% and 75%.  What we need to deduce from this is, that if my input voltage is 12V, then the motor will be seeing the equivalent to 3V, 6V and 9V. Similarly, if the motor speed at 12V were 1000 RPM, then at the respective duty cycles we would observe speeds very close to 250 RPM, 500 RPM and 750 RPM (if we disregard motor losses, which is a rather advanced topic for this tutorial). In essence, we have controlled the motor’s speed by scaling down the H bridge’s input voltage which in turn was achieved by controlling the PWM duty cycle.

Notice it would be impossible to achieve this if we use the RC radio PWM output. Why? Well, for starters, that PWM has a pulse width as low as 1 ms and as high as 2 ms. However, the PWM period is about 21 ms. This tells me, the relationship between TIME ON and TOTAL TIME can only be in between 4.76% (1 / 21 = 0.0476) and 9.52% (2/21 = 0.0952). This means that if you have a motor which needs 12V to operate, your input voltage would need to be 120V! Is it doable? Yes. Is it practical? Ehhh… NO!

The other problem we would observe while using the RC radio PWM to directly control the motor is the switching frequency. Since the PWM period is 21 ms, the PWM frequency would be something like 47.6 Hz. I am not certain if the audible component would be bad enough (but I am almost positive I wouldn’t like it one bit), but the fact that you are turning the motor ON and OFF 47 times per second would make the motor actuation quite lousy. You would observe the motor vibrating too much and as far as I know that is a very undesirable trait. This is why you will see at a later time we want PWM frequencies to be in the KHz range, with 20 KHz being one of the most employed when possible (i.e. the controller allows for it and the switching losses are not too detrimental to the application).

To recapitulate: Although both RC PWM and Motor PWM are signals based on modulating a pulse width, the RC PWM is meant to be used as a means to transmit information, whereas the Motor PWM is meant to be used as a means to scale down input power. Said differently, the RC PWM signal contains information we can use, but it can’t be used to drive the motor directly because it doesn’t work very well as a means to scale down energy levels.

No worries! Generating an astoundingly good PWM to drive our motor is piece of cake for the PAC52xx devices and the Hydra-X boards.

On this next step, I will code my Hydra-X20 board to generate the control structure to drive an H Bridge with a PWM frequency higher than 20 KHz and with enough resolution to obtain any duty cycle in between 0% and 100% we can possibly need.

Why Hydra-X20 and not Hydra-X10? Hydra-X20 is based on the PAC5220 device which incorporates pre drive stages we will use to drive our big FETs. At the end of this project, we will be able to move a few hundred pounds very easily. Did I tell you the Hydra-X is a very muscular microcontroller?

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

Having Fun With The Timer’s Input Capture Function – PART II

On Part I of these series we gave an introduction to the RC PWM control topology and why it is convenient to use a Timer Input Capture function to gather its timing information. This project’s goal is to measure the RC PWM’s pulse width originating from an RC radio and being offered by the RC receiver digital output. Here is a block diagram for the proposed project:

Block Diagram

Since we have taken care of most of the theory behind this set of goals, let’s take a look at the code right away. Do note that due to an increased level of complexity, we are now starting to deal with projects, instead of a single file holding all the code. What this means to this blog postings is that from now on, I will just place the snippets of code pertaining to the logic being explained, instead of the entire file. Hence, I recommend you take a look at the project which you can download from the DOWNLOAD section, if you want to get the big picture.

Also, I need to point out I am going to reuse the serial communication code from one of our previous tutorials. I will not explain much about this code as you can see it as part of the project. If you have questions, feel free to check the Simple Serial Communications study.

#define RC_PWM_CCTR 0 //CCTR0 on Timer B
#define CAPTURE_MODE 1
#define INT_INTEN 1

uint16_t rcpwm_time_rise, rcpwm_time_fall, rcpwm_width;

int main(void)
{
__disable_irq();

//CLKIN Source is Reference Clock.
//On this code example, we will use the PLL. It will be running at 50 MHz
//ACLK will be running at 50 MHz and and HCLK will be running at 25 MHz
pac5xxx_sys_ccs_config(CCSCTL_CLKIN_CLKREF, CCSCTL_ACLKDIV_DIV1, CCSCTL_HCLKDIV_DIV2);
pac5xxx_sys_pll_config(50);
pac5xxx_sys_ccs_pll_select();
pac5xxx_memctl_wait_state(FLASH_WSTATE_HCLK_LTE_25MHZ);

uart_init();

// Configure Timer B clock input for HCLK source and /16 divider
// Timer B Frequency is 1,562,500. Each clock cycle is 640 ns.
// Full Timer B period (640 ns * 65535) is 41.94 ms. Enough to measure an RC PWM pulse
pac5xxx_timer_clock_config(TimerB, TxCTL_CS_HCLK, TxCTL_PS_DIV16);

// Configure Timer B to COUNT UP Mode and count up to max value (65535d = 0xFFFF)
pac5xxx_timer_base_config(TimerB, 0xFFFF, 0, TxCTL_MODE_UP, 0);

// PD2 is configured as PWMB0 - RC PWM signal input
pac5xxx_timer_io_select_pwmb0_pd2();

// Timer B CCTR0 configure to CAPTURE and INTERRUPT on both RISE and FALL edges
pac5xxx_timer_cctrl_capture_config(TimerB, CAPTURE_MODE, RC_PWM_CCTR, INT_INTEN,TxCCTL_CED_BOTH);

// Timer B Interrupt priority is configured to 1
NVIC_SetPriority(TimerB_IRQn, 1);
NVIC_EnableIRQ(TimerB_IRQn);
__enable_irq();
while(1);
}

I want to start with the MAIN() as there are some meaningful changes going on. The first thing you will notice is I am using something called the PLL. What is this? PLL stands for Phased Locked Loop and it is basically a clock system block which allows us to run our different clocks (e.g. ACLK, HCLK, etc.) as high as 100 MHz. It is based on the 4 MHz Clock Reference, so we are still using that resource. However, instead of using the reference clock directly, we go through the PLL and obtain a higher frequency. We explained the Clock System on our first blog tutorial, but here is a refresher:

PLLClock

Before we had been running our sample code at 4 MHz. That was because we were in Kindergarten mode. We have since graduated from our Pre-School years and it is time we step it up a notch. 4 MHz is just too slow for any meaningful cool application to take place. In the same fashion people like speed when racing, we also like speed when microcontrolling. So why not use it? On this application I do not need 100 MHz, so I will set my PLL to operate at 50 MHz, and divide by two the HCLK so code is executed at 25 MHz. Way better than 16 MHz!

I need to take an important segway here. On the PAC52xx MCU, we can execute code as fast as 50 MHz (now, that’s way better than 16 MHz!!!), but only when executed from RAM. How to move code from ROM to RAM will be the topic of another tutorial. Needless to say, we do not need to run code at 50 MHz for this application as all we are doing is measuring pulses as short as 1 ms. Plenty of time there!

The next steps is to call the UART configuration function. This is basically the exact same code we used on our previous tutorial. Feel free to check it on the project. Do note, however, I needed to update the BAUD rate dividers as the UART is now switching at 25 MHz, instead of 4 MHz.

pac5xxx_timer_clock_config(TimerB, TxCTL_CS_HCLK, TxCTL_PS_DIV16);

The following step is to configure Timer B. Since we are measuring such a slow pulse (as Star Trek’s Data would say, “For an Android, 1 ms is an eternity!”), we need to step down Timer B’s frequency quite a bit. We will set its divider at 16 and this will give us a frequency of 25 MHz / 16 = 1.5625 MHz.

pac5xxx_timer_base_config(TimerB, 0xFFFF, 0, TxCTL_MODE_UP, 0);

We will configure the timer to COUNT UP MODE and set its period (maximum counting value) to 65535 (0xFFFF in hex). What this means is that it takes 41.9 ms for the Timer B counter to go from 0 to 0xFFFF (Period = 65535 cycles / 1.5625 MHz). 41.9 ms is enough to measure both the pulse and the amount of time between pulses. The zeroes on that function call are to disable SINEGLE SHOT operation and disable Timer B Slave Synchronization. Too advanced for this tutorial so feel free to accept it for the time being and study it more profoundly at a later time.

NOTE: On Coocox, if you hover your mouse on top of the SDK function call, it will show you the function declaration. This is a great way of learning what the function needs in the form of parameters, without needing to search it into its originating file.

Moving on…

pac5xxx_timer_io_select_pwmb0_pd2();

With the io_select SDK function call, we are configuring the Timer B Input Capture resource PWMB0 (CCTR0) to be present on PORT D2. What this means is that the pin PD2 is now an input connected to the Timer B PWMB0 resource. The SDK has a function similar to this one for every possible PWM to GPIO relationship. So configuring timer resources is quite painless.

pac5xxx_timer_cctrl_capture_config(TimerB, CAPTURE_MODE, RC_PWM_CCTR, INT_INTEN, TxCCTL_CED_BOTH);

In the same fashion, the SDK contains functions to configure the timer functions as either PWM output or Input Captures. All you need to do is pass a few simple parameters, and VOILA! In this case we have told the system that PWMB0 will be operating in CAPTURE MODE, will have its interrupt enabled (so we can trigger on input events), and we will trigger on both RISE and FALL events.

The last step is to enable the Timer B Interrupt and assign a priority. I assigned 1, but it might as well be 0. In reality it does not matter at this time as the only two interrupt sources are the Timer B and the UART. Since the UART has the least priority, any other priority on Timer B would have worked. To be honest, in this application we can even have both sources to have the same interrupt priority and it wouldn’t harm the real time operation. We have plenty of time to do things! As more and more real time aspects are added, though, you would need to get more careful with how interrupts are prioritized.

We need to look at one last piece of code and that is Timer B’s iSR. Here it is:

void TimerB_IRQHandler(void)
{
	if (pac5xxx_timer_b_cctrl_0_int())
		{
		//Add Timer B Capture/Compare 0 ISR code here
		if (pac5xxx_gpio_in_d() & 0x04)			//rising edge detected
			{
			rcpwm_time_rise = pac5xxx_timer_b_ccctr0_value();
			}
		else									//falling edge detected
			{
			rcpwm_time_fall = pac5xxx_timer_b_ccctr0_value();
			if (rcpwm_time_fall > rcpwm_time_fall)
				{
				rcpwm_width = rcpwm_time_fall - rcpwm_time_rise;
				}
			else
				{
				rcpwm_width = (0xFFFF - rcpwm_time_rise) + rcpwm_time_fall;
				}
			}

		pac5xxx_timer_b_cctrl_0_int_clear();
		}
}

Look at how ridiculously simple it is to measure our pulse width. I basically have three variables: rcpwm_rise, rcpwm_fall and rcpwm_width. When a Timer B ISR is generated, I have no idea who generated it so first thing I need to do is check whether the PWMB0 (CCTR0) flag is set. If it is, then I can start gathering my data.

At the same time, even if I know that a PWMB0 interrupt was generated, I have no idea whether it was caused by a rising edge or a falling edge. Hence I need to check for this. Although PD2 is configured as a timer resource, and an input capture in particular, I can still read PD2 as an input. If I see PD2 to be high, then clearly I must have witnessed the rising edge event. Otherwise, it was a falling edge.

If it is a rising edge what we are dealing with, then we get the capture timer value and store it on the rcpwm_rise variable. If it was a falling, then we store it on the rcpwm_fall variable. Regardless, we use the pac5xxx_timer_b_ccctr0_value() SDK function call to get this information. You could have also accessed the memory location directly by reading PAC5XXX_TIMERB->CTR0, but this is yet another step in advancement. Maybe when we go to High School level…

On a rise event there is nothing else to do so we are done. On a fall event, however, we now have all the information we need to compute the pulse width. Here is the trick, though. We have no idea whether there has been a Timer B overflow, so just subtracting the RISE time from the FALL time is not going to work. We could enable the Overflow Interrupt on Timer B and add some weird logic to tell us, but this is just too much work.

There is a simpler fix. If the FALL time is larger than the RISE time, that means we are still counting time without an overflow. The future is at the future and the past is at the past. We can then use the simple equation:

rcpwm_width = rcpwm_time_fall - rcpwm_time_rise;

If the FALL time, however, is smaller than the RISE time, then we either have a timer machine or an overflow. Considering physicist have practically set their mind on time travel being an impossibility (due to weird paradoxes arising), this case must imply an overflow. Relax! Your grandfather is safe from being killed by you traveling to the past!

In this case we can use a different equation which takes into consideration the overflow event.

rcpwm_width = (0xFFFF - rcpwm_time_rise) + rcpwm_time_fall;

In essence, this is how it looks in time:

Overflows

To validate my code, I changed the UART application to tell me the number of milliseconds I am getting as I articulate my remote control lever. In this case, instead of toggling an LED, when I press the button I obtain back a 16 bit number which corresponds to the number of ticks Timer B is measuring from rising edge to falling edge. If I multiply this number by 0.00064 (1000 / (25E6/16)) I will obtain the number of milliseconds my pulse has as a width. As expected, this is how it looks as I move my RC radio lever to left, center and right:

UART_Width

And this part of the tutorial is D-O-N-E, DONE! Next step is to actually use the information on the rcpwm_width variable to control the speed of a motor. But that looks like a completely different tutorial, so stay tuned for the RC2PWM tutorial segment.

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+

Having Fun With The Timer’s Input Capture Function – PART I

One of my favorite control topologies for my robotic projects is using an RC radio to control behaviors such as motor speed or angle of rotation. The first time I did this was to power up my 5 foot tall R2D2 replica and I used an HC11 microcontroller to code this control mechanism. Whereas the HC11 is now Smithsonian material, the need to control motors via a remote control link is far from obsolete!

This is a great project to showcase both the Timer Input Capture as well as the Timer Output Compare function found in vast quantities on any of our PAC52xx devices and henceforth, our Hydra-X boards. On this blog posting, however, I will focus on the Input Capture aspect of things. We can get busy with Output Compare functions, or PWM generation, at a later time.

The premise at hand is quite simple. Typical Remote Control (RC) radios (the same used to control cars, airplanes, helicopters and boats), rely on an analog signal I am going to call RC PWM. Whether that is its real name or not, it is beyond me, but for the past 20 years that is what I have called it. So please excuse me if it has a different name.

The RC PWM signal consists on a pulse which is transmitted about every 21 ms and has a width of anywhere in between 1 ms and 2 ms. The screen capture below shows two consecutive pulses and the close to 21 ms between them:

tek00000

Nominally, the pulse width sits at 1.5 ms. We call this the center position. However, the width of this pulse is made to vary through time and herein lies what I will call the command. In other words, the user operating the radio moves a lever (AKA a potentiometer) up and down or sideways, and the radio changes the pulse width to more or less than 1.5 ms, without being smaller than 1 ms or larger than 2 ms.

RCPulseWidth

What the pulse width means is highly arbitrary. On a plane throttle, the wider the pulse (i.e. the closer to 2 ms in width), the faster the motor spins. In this implementation, however, there is no need for a direction of rotation selection as you would not want a propeller to rotate, say, in reverse. So we only need to use pulse width information from 1.5 ms to 2 ms, where 2 ms is full speed and 1.5 ms implies motor disablement. The figure below shows motor speed on a per pulse width basis.

ThrottleRCPWM

If the signal is used to drive an RC servo, the pulse width is directly tied to servo axial position. For example, a 1 ms pulse would command the servo to rotate to its full left position, while a 2 ms pulse would command a full right. Anything in between 1 ms and 2 ms would stand for positions in between full left and full right. The figure below shows RC servo actuation on a per pulse width basis.

RCServoPosition

My control topology is not that of an airplane throttle or an RC servo. The truth is there is no need to discuss what it is that I am trying to achieve here, as all we will do on this tutorial is to sample the width of our PWM pulse in order to extract its information, but I am going to tell you anyway.

On my bi-wheel robots, I like to control motor speed separately. This is a highly subjective decision and am not trying to undermine any other implementation like coupled motor control, or others. I just like it that way! What this means is that I use two RC radio channels to control two motor H Bridges separately. This allows me to move my robot forward and back (both motors forward or reverse) and also steer on both directions (one motor forward and one motor reversed).

RobotTranslation

The two motor H Bridges will take care of powering the entire deal, but the command must come from the radio.

Here is where somebody could stop me and say “If the RC radio uses PWM to communicate and the H Bridges use PWM to control motor speed, why don’t we just feed the RC radio PWM into the H Bridge and be done with it?”

Great question! But as we will see later on, the RC PWM is by no means the same as the PWM we use to derive H Bridge speed control.

Regardless, the first step to get this control topology underway is to extract the timing information from the RC PWM pulse. I have decided (actually I decided this on my own somewhere during 1998, although it is quite possible it had been decided by the RC community long before I was born) that a 1.5 ms pulse will correspond to a disabled motor, a 1.0 ms will command the motor’s full speed in the reverse direction and a 2.0 ms pulse will command the motor’s full speed in the forward direction.

I will then adjust the H Bridges PWM from 0% to 100% duty cycle depending on the value of the pulse width as follows. If the pulse is below 1.5 ms, I will increase H Bridge PWM duty cycle as RC PWM decreases (inverse relationship). If the pulse is above 1.5 ms, I will increase H Bridge PWM duty cycle as RC PWM increases (direct relationship). This is the same as saying my H Bridge PWM duty cycle will increase the farther away from 1.5 ms, the RC PWM pulse width is.

The figure below shows the proposed control logistics:

MotorControlAlgorithm2

One last detail. In order for my motors not to go crazy executing PWM commands when the remote control is OFF, I will use the RC PWM frequency information as a safety mechanism. If you recall, I specified the RC PWM pulse to happen every 21 ms. At least that’s how it works on my radio. When I switch off the radio, though, I see odd pulses every now and then. If I don’t teach my controller to ignore all of these spurious pulses, it will by definition react on them. And that is nasty stuff!

The solution is to install a mechanism which disables the controller whenever we do not see periodic pulses as expected. More on this later.

I have been talking about PWM pulses here and PWM pulses there, but how does all of this ties to the PAC52xx input capture function? Well, as it turns out, the RC PWM is a pulse which means it starts with a rising edge and ends with a falling edge. Because we can program our timer functions to react to these logic level transition events, while at the same time capturing the current timer value, we can measure the pulse width very precisely.

Let me explain this a little bit more in detail.

I am going to program my timer to react to rising and falling edge events. This means that whenever either of them happen, the selected timer’s interrupt will be triggered and we can execute some code to extract the pulse width.

What we need to understand, however, is that the timer, being a free running counter, will be incrementing itself from 0 to a preprogrammed value (say 65535 as it is a 16 bit counter). When the rising or falling edge event occurs, immediately, the counter value will be stored on the timer’s function respective Input Capture register. This is crucial because if we want to measure the pulse width accurately, we want to get the actual time in which it rose and the actual time in which it fell. If we then subtract the rise time from the fall time, we will have acquired our pulse width.

The following picture details the algorithm we will follow:

CaptureEvents

In this example, we have programmed the timer to operate on COUNT UP MODE. We have also programmed the timer to count up to as much as 65535. Just for the sake of this example, lets assume the timer is configured to operate with a 1 MHz frequency (each timer tick is 1 us) and let’s  imagine a pulse is captured as shown.

If so, while the timer has been counting up from 0 to 65535, the rising edge event will latch the timer value of 1000. The ISR will be executed and we will save this value on memory. Some time later the falling edge will be detected and the counter value of 2000 will be latched immediately on its respective input capture register. We run the ISR again and save this value.

Since now we have both timer values, we can subtract 2000 – 1000 and obtain the pulse width as 1000 timer ticks. We know each timer tick is 1 us, hence the pulse width must be equal to 1 ms. With this information we can now update our control algorithm as required.

Should we look at the code implementation? Well, this blog posting is already fairly long so I will detail the code example on the second part of this series. Hope you don’t feel your brain to be too modulated!

Sharing is caring...     Email this to someoneShare on FacebookShare on LinkedInTweet about this on TwitterShare on Google+