« DIY - Dry Erase Board
» Object Oriented Javascript with Mootools


DIY - Electronic Speed Control

Chris Spurlock | 11.03.07 | DIY Projects, Hardware
diy-electronic-speed-control

As my first post I thought I should cover something easy, but I may as well start on something a little deeper. So for my first post here at Tehdevs I would like to cover the topic of Electronic Speed Control for brushed DC motors.

I will be building the ESC from the ground up, including the software. Hence, I assume some general knowledge of electronics and programming on your behalf. Specifically in the area of reading schematics and understanding assembly language.

This is a very basic speed controller, and is still in its infancy. It lacks sophistication, is very easily manipulated by EMFs, has a few quarks, but it proves the theory and gets the job done. You have to start somewhere, or at least that’s what I’ve been told. Did I mention it only takes roughly under $15 dollars in parts to make a forward only speed controller? Yes, you heard correctly. Why pay between $50 and $200 dollars for that store bought ESC when you can make your own and have a sense of pride of your creation? I’ll admit the store bought versions do have breaks and reverse, but for an additional $5 dollars in parts and a little more brain power and programming you can pull it off. Also, you will need a way to program a microprocessor. But it is a good investment as there are many different projects you can do with microprocessors.

Now, down to business…

First, I suppose to make the speed controller one should understand what its job is. It controls the speed the motor(s) turn, what direction(s) the motor(s) turn, and supplies a constant voltage to the rest of the vehicle’s/object’s components (usually). Just how does it know how fast and what direction to tell the motor to turn? Well, it gets this information from the receiver, which is in turn sent to the receiver from the transmitter. But it’s not in a readily understandable format to the motor. This is where the ESC’s job comes in. This signal coming in from the receiver must then be processed and then sent out to the motor.

Processing this signal from the receiver is a little tricky. The signal is constantly alternating between a HI and a LO state. The period of this signal is always 20ms, but it is only a HI for a period of 1.0ms to 2.0ms (roughly). This is called pulse width modulation (PWM). This is very important, and will play a crucial role later on in the project. Why such a short duty cycle you may ask? Well, the processors used in these types of projects are usually small and slow, and could use the extra long time during the other 18ms, or 90% or the pulse, to do the number crunching. The following diagram shows the relationship of the PWM signal. The way you interpret the signal is really up to you, and we will cover that in the next paragraph.

receiver signal.jpg

Okay, so what is the importance of this signal? Have you ever wondered how it knew you were not pulling the trigger, or putting it in reverse, or forward? Well, here’s how. When the trigger is at idle, the signal going from the receiver to the ESC will be a constant PWM of 1.5ms (again, roughly). If you pull the trigger slightly and watch the signal on an oscilloscope you will see it start shifting towards either the left or to the right. Eventually it will approach its limits and be near the 1.0ms or 2.0ms, depending on which way it is approaching. This depends on your transmitter. There is a switch on the transmitter that reverses the direction of the trigger, so it’s not very important to know exactly which side forward or reverse is, since you can easily just switch it on the controller. Just be aware that one direction is at 1.0ms and the other is at 2.0ms. The following diagram is an example of what a forward signal from a receiver could look like if you interpret the 1.0ms as forward.

forward signal.jpg

Now that you have a little bit of understanding of what goes into the ESC from the receiver, you need to know what gets sent out of the ESC to the motor. It reminds me a bit of a class I took in school; namely linear systems. You have a one-to-one relationship where the outcome is predictable, so all you need to do is find a transfer function. So what is the output from the ESC? If you measure the voltage across the motor when you are slightly pulling you will see a voltage that varies between 0V and the maximum voltage output from the ESC. But is this the real signal? Plainly put, no. The voltage going across the motor is really alternating. It is either a digital HI or LO, an on or a off. Does this sound familiar? The PWM from before comes back to lend a helping hand.

Now how does this help us? The output from the ESC to the motor is sent at a rate of around 1 KHz on average. This varies by company, usually 1 to 2 is the industry standard but I have seen 4 KHz touted by some companies. I played around with 4 KHz but the motor’s “whine” got on my nerves. Anyways, back to this signal. The motor sees this pulse a thousand times every second. It is not possible for that large piece of hardware to stop and start a thousand times a second.

So what happens? Well, we get a calculus problem out of this deal. The duty cycle is how long the pulse is high compared to being low, as a percentage between 0.00 and 1.00. And the period is the life time of the pulse before the next pulse begins. For simplicity’s sake I’ll take you to the answer. Since the motor can’t respond fast enough it is going to average the pulses. So what does the motor see? If you send the motor a signal of 7.2V a thousand times a second with a duty cycle of 0.5 it will think it’s getting a continuous supply of 3.6V. So the motor sees the voltage as being VM = D * VS. Pretty nifty huh? VS is the voltage source, i.e. the battery, D is the duty cycle, and VM, is the voltage the motor will “see”.

Duty cycle of around 0.10:

pwm10.jpg

Duty cycle of around 0.90:

pwm90.jpg

There was no need to modulate the voltage physically using some form of amplification, and this leaves the full potential of the battery to flow nearly unrestricted. The only other component you need in series with the motor and battery is the switch controlled electronically to make the pulse. This switch is a MOSFET that can be switched on and off by the processor.

So just what needs to be done with this device, specifically? First, the device needs to measure the incoming signal from the receiver. The next step is the device must need to determine whether it’s left shifted or right shifted and also count/measure the pulse of the signal. This will determine whether it is a forward or reverse, and also the magnitude of the signal. The model of ESC in this topic is forward only, so the direction of shift plays no role and are treated the same, only magnitude is counted. Once you have a value counted for the length of the pulse stored, the device needs to map it between 0% and 90% duty cycle. The pulse will then be sent to the switch controlling the motor. Why only 90%? Well this will be discussed in the hardware section. If you were to make a reversible ESC, you would need to use software control, use four switches in a bridge layout, and determine which switches would be receiving the signal at any given time. Since the voltage across the motor will always be the same as the source, the only method you have of altering the direction is flow control. Perhaps it is a topic for another day.

Perhaps a simple flow chart is appropriate here. So, here we go:

flowchartsimple.jpg

Now that you have a little more understanding what the software needs to do, let’s cover some more hardware. We will return to the software in even more detail in a bit. I want to build upon each slowly and not reveal either of them all at once.

So far it is understood you will need switches and a microcontroller. So what else will you need? For the switches we will be using power MOSFETs so you will need a FET driver. You technically don’t need a LED but I wanted one to know when the device was on. You will need some capacitors to help control EMFs, though they didn’t help me much; more on that topic later. Since you are powering your microcontroller, LED, and not to mention all your expensive components on your RC vehicle or other device from a high current supply you will need a voltage regulator. Also count on needing some resistors. The most expensive items are the MOSFETs, voltage regulators, and the MCU (microcontroller unit). Buying in bulk will help cut costs on each component.

Here is a tabulated list of what you need and the item number from digi-key.com:

ITEM NUMBER NAME COST NEED
IRL1404Z-ND HEX/MOS N-CHAN 40V 200A TO-220AB $2.34 2
PIC16F684-I/P-ND IC PIC MCU FLASH 2KX14 14DIP $2.68 1
497-1168-1-ND IC LDO 1A REGULATOR DPAK $2.10 1
TC4468CPD-ND IC MOSFET DVR QUAD AND 14DIP $2.43 1
1.0KH-ND CFR-50JB-1K0 (1KOhm Resistors) $0.02 2
511-1242-ND LED 3.2MM 650NM RED DIFFUSED $0.36 1

The prototype board used in this device is from Radio Shack and is model 276-148 and has the same catalog number. It costs $1.99 and is actually perforated to break into two boards that are nearly identical. So the board for the project is really $1.00 and you have a spare board. Do not buy your resistors from Radio Shack. They charge a dollar for five resistors. I normally wouldn’t recommend buying them from digi-key either unless you are ordering other items. But since you are already buying other items the shipping is covered and thus you get them nearly for pennies. You can get 200 1KOhm resistors from digi-key for $3.76 while you can only get 5, yes 5, of the same exact resistors from Radio Shack for $1.99. And 1KOhm resistors are used everywhere, and I’m sure you want to burn a few for enjoyment, so spend a dollar-something more and stock up for later projects. I made the mistake of getting surface mounts the first time though and didn’t get a microscope to be able to see them to solder them on… so I had to order another batch of the regular ones.

Here are the few parts you’ll need to put this device together:

Dscn0713B.jpg

How will the hardware look? Here is a schematic for you:

ESC BB V0.1B.gif

Now let’s talk about it in detail. There is a MOSFET underneath the motor near to the ground of the battery. When the MOSFET is turned on the current is allowed to flow through the motor to the ground. A MOSFET is a switch essentially with an input and an output. The input is the drain, which is the input signal you wish to pass through to the output when the switch is on. The output is the source, which the input will show up on only if the gate, the switch, has a high enough voltage applied to it.

You may be asking why there is another MOSFET on the schematic if only one is needed to control the flow of current to the motor for a single direction ESC. That is a good question. When the MOSFET goes off, the motor doesn’t quit spinning instantly. In this state the motor acts as a generator. So where does this created energy go? Well the switching MOSFET underneath the motor doesn’t like this behavior and usually reacts with intense heat and a puff of smoke. I’ve seen it before, and unfortunately felt it with my fingers also. It will leave square imprints. The MOSFET under the motor is in the off state and has a zener diode in reverse bias. The generated energy by the motor is trying to push through it. My solution was to apply a “fly-back” MOSFET. I tied the on switch (gate) of the other MOSFET to the drain on the bottom MOSFET (denoted by a D on the schematic). This essentially turns on this “fly-back” MOSFET whenever the bottom MOSFET is off. This lets the current free spin in the loop back through the motor while the bottom MOSFET is off. This way both MOSFETs are alternating “on-states” and neither one are staying on continuously. This method allows the MOSFETs to stay cooler. I have yet needed to use heat sinks. Be carefule using heat sinks however. The tab on the MOSFETs also act as the drains.

Here are what some heat sinks look like if you desire to use some:

Dscn0711B.jpg

The capacitor going across the “fly-back” MOSFET should probably be in parallel with both of the MOSFETs to be effective. This would help keep voltage spikes down. Where it’s at right now I don’t think it’s actually doing anything to be honest.

Nearly half of the schematic is covered for this forward only electronic speed controller. Can you believe there are only about six parts in this device? So what to talk about next? Right, the MOSFET that is switching on and off control the flow of the current in the motor needs discussing. What is controlling that MOSFET?

You have a pretty sophisticated piece of hardware called a micro controller. Inside that micro controller you have a CPU, an ALU, a bunch of registers, memory, and plenty of other goodies. The MCU picked out for this project could easily be used in many other projects. You could use it in hardware for decoding caller ID signals and displaying them on LCD screens, creating simple video games and displaying them using NTSC output on a TV such as pong, frogger, etc, or you could make an Ethernet to serial module, create a way to make your SNES controller work with your USB port on your PC, etc.

For this project the MCU has the task of recording a signal, measuring the signal, determining a duty cycle, converting that duty cycle to another signal, and then sending that signal out to another device. That is all this device is doing, repeatedly. It could be more sophisticated. I would like it to incorporate a button on the device that you hold down while you pull the throttle to full throttle. As of right now I am simply guessing what full throttle should be, but the button would be a nice feature to add later.

Lucky for us this MCU, the PIC16F684, has a PWM module where there is a register you set from 00000000 and 11111111 for the duty cycle. This was the reason I chose this MCU. It was ready to go from the get go. The only problem was this MCU was too fast. It runs at a speed of 8MHz and the timer module would count every clock pulse. So what happens if the timer module reached 11111111 and it hasn’t reached the end of the pulse from the receiver? Well it wraps back and starts back at 00000000 and resumes counting. So you could end up with the device thinking it was shorter than it was really supposed to be because it started counting over. The resultant of this is when you pull the trigger slowly you end up reaching maximum throttle, then suddenly you bottom out to nothing and start all over slowly speeding up again even though you are still mid way through trigger pulling. That’s not acceptable in a race and you round the curve to see the finish line. In the beginning of the software initialization you must slow the device down to not wrap this number. This still does not completely solve the “trigger-wrap” issue though, as I will cover more on it in software later.

The input port on the PIC chip used to capture the signal from the receiver will be the RA5 port, or pin 2. The pin used for output to the FET driver will be P1D, or pin 8. Those are the only inputs and outputs you will be using for this project. If you were to make it forward/reverse or add breaks, you would need to use more outputs from the PWM module to control more switches. The positive power goes to the VDD pin, or pin 1, and comes from the output of the voltage regulator since it needs to be regulated 5V. Do not use voltage directly from the battery. The ground is okay to connect directly to the battery’s ground.

The regulator is pretty simple. Connect the battery’s positive to the regulator’s input, connect the regulator’s ground to the battery’s ground, and connect the output of the regulator to your devices that need regulated voltage.

The FET driver ensures the MOSFET gates receive a strong enough signal to cause it to switch properly. Since pins 8 and 9 are your input for your driver, and that’s just for a single channel, you have to tie the inputs together. The reason is these inputs go to a single output and are really an AND gate. Both inputs have to be HI in order for the output to be HI. The reason you want this FET driver is because when the input isn’t strong enough the output is driven to a LO. This ensures the gate of the MOSFET sees a reasonable signal and nothing in between. The output pin from the FET driver is pin 10 and this is the line that you wire up to the gate on your swtiching MOSFET that controls your motor. The positive line, pin 14, you connect directly to the power supply, i.e. battery. The ground is pin 7.

If you desire you can wire up a LED in series with a 1KOhm resistor between the voltage regulators output and the ground. When the device is on the light will let you know. Just make sure you have the LED facing the correct direction or current will not flow.

Throw you a toggle switch in between the battery on the positive side and that nearly covers it for the hardware aspect of the device. Hopefully you will end up with something better looking than the following:

Whhhaaaaahhhhhhhhh…. extreeeeeeme cloooooseuuuuup!!!!!!!!!!

Dscn0691B.jpg

We’re ready to switch gears and head back over to software. Sure, it looks cool, but we need to make this hunk of hardware do something. Remember the simple flow chart? We now need to elaborate on each of those parts and put them into code.

The first thing is to initialize the micro controller. We are getting ready to look at our first bit of actual code for this project. Our development environment will be Microchip’s MPLab IDE and writing in PIC assembly. Alright, let’s have a look at the initialization code:

The first line is a compiler directive and is not really assembly code at all. What it does is make it a little easier on you. This line of code does about eight things total. One of those things being it makes sure it uses the internal clock of the micro controller. ORG H’0000′ simply says to start storing the program code at memory location originating at the address 0000. 0000 is a hexadecimal number. The next instruction MOVLW will be at that address, and then MOVWF will be at 0001, and so on. This project isn’t to teach assembly, but I will walk you through it as much as possible. MOVLW is moving the literal value of 00000001 to the working register. Think of this as a work bench where you do your work. You will put your values here then use your tools, or operations, on the values, or move them elsewhere. The value 00000001 is a binary value. The next line MOVWF is moving the value in the working register to a location you specify. In this case it is the OSCCON register. This is the oscillator configuration register. Starting with the right bit being the 0 bit, that is the bit you use to select whether you use an internal clock. A 1 means you want to use the internal clock and a 0 means you want to use an external clock. We still want to use our internal clock so let’s leave it 1. But we want to change it from the default 4MHz to a whopping 31KHz. Yep, 31KHz is all we need for this project. Bits 6 through 4 control the speed. You have 8 speed settings ranging from 31KHz to 8MHz to choose from depending on if you put 000 up to 111 in the register. So we are going to put 000 in bits 4 through 6. So we are only concerned about x000xxx1. But since on startup the x’s are all 0’s we can safely assume we can set them to 0’s as well, or else we’d have to set the individual bits to preserve the values of those bits. Now that we have our device running slow enough to do our bidding, we can continue to the next line.

We have three functions referenced here. When you do a CALL operation you look at the operand, look up the address, and jump to it. When you are done with the function you will return back to the next line following the CALL operation. The first CALL we have is to set up the pulse width modulation ports used to control the motor. The second CALL is to set up the general ports used to capture the signal coming in from the receiver. The last CALL is to set up the timer module that the general ports use to measure the signal from the receiver. We will look at each of these functions in detail later.

Let’s move on to the MAINLOOP where the action happens.

You may of heard while you were in school to never use a GOTO; that is was bad programming practice. Well, that is not always the case. Sometimes you need to be stuck in an infinite loop. What do you think Windows is? Admittedly, higher level languages you can accomplish this without the use of a GOTO command. That is not usually the case in lower level languages. In assembly the command of choice is usually a good old goto if you do not need to remember what address you were just at. If you do need to remember what address you were just at use some type of function call. Just remember to return or else that address value will be stuck on the stack and will mess up whatever you were doing with the stack, again this depends on the hardware setup.

The first line you see is the label of the MAINLOOP. This label will be equal to the address value of the following operation, which is a CALL operation. This CALL operation is jumping to the address of the first operation designated by DO_CAPTURE, doing all of the operations it comes to in order, and then returning when it sees RETURN to the value it stored on the stack when the CALL operation was first called. When it comes back from this CALL operation it goes to the next line which is CALL CALC_PULSE, and it is very similar. When the processor sees CALL CALC_PULSE the current address is put onto the stack, and then the processor jumps to the address designated by CALC_PULSE and does all of the operations it comes to until it sees a RETURN. Once it sees the RETURN where ever it was at it will take that address off of the stack and return to it, and go to the next line, which happens to be GOTO MAINLOOP. If you recall the address stored in MAINLOOP is the address for CALL DO_CAPTURE. So what is happening here? All we are doing is CALL DO_CAPTURE, CALL CALC_PULSE, CALL DO_CAPTURE, CALL CALC_PULSE, etc. This is done from the moment you hit the MAINLOOP label and never stops until you turn the device off, or it blows up. And that is a possibility in the RC world, trust me.

Now let’s start taking a look at all these methods. We only have five to cover, but each of them are crucial to the performance of this device.

Let’s start at the top. The first method we come to is the SETUP_PWM method. This function sets up Port C on the PIC16F684 for use with the built in PWM module. This makes our job a little more simple since they added this module, but we need to take care in setting it up or these ports will behave as regular input/output ports. This is also one of the longest methods of the software so I will go over it in pieces. So let’s take a look, shall we?

The first line is just a label so we know what address the first operator is located at. An important thing about the PIC16F684 that we need to cover is memory banks. The PIC MCU has different memory banks for different special memory registers. It’s a hard concept to grasp at first. For instance, two overlaying addresses in the two banks could really be the same register or they could be two different registers. An example is the STATUS register. In bank 0 it is the 0×03 address and in bank 1 it is the 0×83 address, but they point to the exact same array of bits. On the other hand, in bank 0 the 0×05 address points to the PORTA register and in bank 1 the 0×85 address points for the TRISCA register. If you are not in bank 1 you will not effectively be able to work with the TRISCA register. Think of these register banks as overlaying and the registers are sometimes one in the same and sometimes they are not.

Now, we want to work with the PORTC register, which is in bank 0 so we need to toggle the STATUS register. This is the reason the STATUS register is in both bank 0 and 1. No matter which bank you are currently in you can reach it. A few of the registers are set up this way. There is a bit in the STATUS register that sets which bank we are working with. If it is cleared than we will be in bank 0 and if it is set than we will be in bank 1. There is an operator to set a single bit in a register called BCF. This operator takes two operands. The first operand is the address of the register you want to work on. In this case we have a name defined for us thanks to the file we included in the first line of code in the program. The second operand is the number of the bit to clear, which also has a name thanks to the same include file. We are going to clear the RP0 bit in the STATUS register to switch to bank 0.

The next line is CLRF which just writes all 0’s to a specified address, which is PORTC. So we are just making sure it is not going to put out all HI values out on the lines upon turning on. This could be disastrous in certain hardware setups, such as H-bridges, where you could ground out a battery’s positive to it’s negative. I did this in the shop and as soon as I hit the “send hex code to device” I watched my proto-board send smoke signals up. Honestly, I enjoy that stuff though.

The next two lines look familiar. We are moving a value to the working register and then moving it to another register. But what that other register is and what the meaning of the value we are moving is what we need to talk about. When the device starts up we have comparators turned on. Well, the bad part is they are turned on in the place of the PORTC, which is bad because we want to use another module on that port. So we need to disable it. To turn the comparators off we need to write 1’s to the <2:0> bits in the CMCON0 register in bank 0. We are already in bank 0 so we just MOVLW 07h. That moves 00000111 to the working register. In case you were wondering you can write the operands in many ways such as B’00000111′ or 07h, whichever way you are comfortable. Anyways, writing 1’s to those three bits will turn off the comparators and let us use PORTC as the input/output ports we were needing it for.

Alright, now we need to go back to bank 1 so we need to do a BSF which sets a bit in a register. So we do BSF STATUS, RP0 to go to bank 1. The next line is CLRF ANSEL which is writing all 0’s to the ANSEL register. This is a special register in bank 1 that determines whether the input/output from PORTC will be analog or digital. We want it to be digital since we have a FET driver to do the work for us. So we are going to set them to 0. This is easy. Just use a CLRF on the ANSEL register. The TRISC register determines whether the pins on PORTC are inputs or ouputs. There are 6 inputs/ouputs on C port. Four of them are used in the PWM module. The ones used are as follows: xxDCBA21. Pins 1 and 2 are general inputs/outputs and the A, B, C, D became our PWM module pins. We set the A, B, C, and D as inputs while we configure the rest of the PWM so we set them to 1s. This gives us a value of 00111100. So, after moving this binary number to the working register and then to the TRISC register to make the ports we want inputs we switch back to bank 0. We do this just because we always put things back the way they were. Granted, we always have access to the STATUS register, but what our mother always told us about putting things back is a good rule here.

Finally, we can move on to another segment of code. In the next section we are needing to set up the period of the PWM and configure the PWM mode. I haven’t talked much about the PWM modes available to chose from. Thus far I have only mentioned what a single PWM signal looks like, so how can there be modes, hold on to that thought and let’s take a look at the next couple of lines of code.

The first two lines of code are the typical moving a value to our working register than moving it to our desired destination, which this time is the PR2 register. This register is used to determine the period of the PWM signal. The PWM period can be calculated by the following equation: PWM Period = [(PR2) + 1] * 4 * TOSC * (TMR2 Prescale Value). The TMR2 Prescale value will be either 1, 4, or 16. This will be determined by the T2CON register later, but for now I will let you in on the secret that I chose the value of 1 so we can solve our equation. The PR2 value is the decimal value of FF, or 11111111, which is 255, and TOSC is the time in seconds it takes for the clock to oscillate one time. 1/31KHz will be the TOSC since 31KHz is the FOSC we have selected. This gives us something interesting: PWM Period = [255 + 1] * 4 * 1/31000 * 1 = 30.27Hz. We have a signal going to the motor at a relatively slower speed than we expected as compared to ESCs manufactured by companies. It still works however, but it is something that should be looked into changing. Changing the speed of the FOSC wouldn’t work or we would have to change the way we time the incoming pulse from the receiver, because then we would rollover our timer again. And you can’t scale the value either or the trigger doesn’t map and you have the trigger wrapping issue I mentioned earlier. This is the situation where you start at neutral and slowly build speed by pulling the trigger and suddenly the motor starts back at zero and begins slowly building speed back up. Essentially you made a saw wave. A function would need to be made to map the throttle appropriately, and I will show you how simply I made the mapping for my ESC. I didn’t. But more on that later. On to the next part.

The CCP1CON register controls what the PWM module and what mode it is in. The first 4 bits will determine what mode it is in. To have A, B, C, and D set all as active HI we needed to have 1100 for bits <3:0>. Bits <5:4> are actually the duty cycles least significants bits. This is an awkward place to put them, since they are separated from the other 8 bits of the duty cycle, so it is a good thing they are the least significant. We can just assume 0’s are fine for now, especially since we are initializing the register and since we will be updating the duty cycle constantly later. The last two bits <7:6> cover whether it needs to be full-bridge, single output, half-bridge, or full-bridge reverse. This is something I haven’t mentioned before. I do not plan on going into detail here, just know that full-bridge uses 4 outputs and you can change the direction of the motor using 4 switches and flow control. A single output allows you to control the speed of a single motor using just one switch. I went ahead and used full-bridge mode since I had planned on doing a forward/reverse speed controller (I just don’t have the hardware set up to take advantage of the software) before I simplified the design, I just don’t change the direction bit anywhere in the program. The full-bridge value is a 01. Put all that together and we have 01001100. Throw that into the CCP1CON, and we’re still no where near done setting up the PWM. So let’s keep on setting up this module. Trust me it makes outputting a PWM signal easy in the end.

Let’s take a look at what we have to do next. Here we have to initialize the duty cycle of the PWM signal. This register is the register we will continuously update in the other subroutine that runs in the main loop, but for now we just need to initialize it. This isn’t extremely crucial since it changes continuously. However, you do not want it full throttle when the device starts up so I recommend a nice set of zeros. We also need to configure the timer used in the PWM module. Let’s take a look at some more wonderful code:

The first variable we are going to be setting is the CCPR1L register. While in PWM mode this register is actually the 8 most significant bits of the duty cycle, and the CCPR1H register is not even used. Odd nomenclature I know, but that’s how Microchip set it up to be used. The other 2 bits of the 10 bit duty cycle are in the CCP1CON register and are bits <5:4> if you must now. However they are the least significant bits, and you will not feel much of a difference by leaving them 0s the entire time. This just means you would be incrementing the duty cycle by 4’s instead of by 1’s. The counter is so sensitive I couldn’t tell the difference. These registers are continuously compared with the TMR2 module and when a match occurs it takes the necessary action, such as toggling a certain line, etc. However, while setting up the PWM module we do not want to have any output so we set the duty cycle to all 0’s for now, just so our car, or example, doesn’t take off down the street upon turning it on.

In the next line of code, BCF PIR1, TMR2IF, we are going to clear the bit for the timer2 module interrupt flag. With it reset we will have it ready for the next time it is “raised” so to speak. The next register we are sending data to is the T2CON register. We need to setup the prescale value which are bits <0:1>. For a prescale value of 1 we need to use the value of 00. Bit 2 is whether the timer is on. I want to leave it off for now. Bits <3:6> are the postscale value. I selected a value of 1:16 to try to get a scaled counter. This was a value of 1111. Bit 7 is unused so we have 01111000 to go in the register. The line after this, BSF T2CON,TMR2ON, is turning on the timer since it’s setting bit 2 in the T2CON register to a 1. After this we need to wait on the timer flag to be thrown before we can finish setting up the PWM module. Let’s take a look at some more code to see how to wait until that happens. This will take our first look at a branch condition.

What this first line, BTFSC, is doing is testing a bit whether it is a 1 or a 0. Depending on the result it will execute the following instruction. You can use this to your advantage. By following it with a GOTO, or some type of jump or branch, and then another one in the next line you can get a while loop out of the deal. Here’s how it works. BTFSC stands for Bit Test f, Skip if Clear. We are going to test the TMR2 interrupt flag in the PIR1 register and if it is not set to a 1 yet, we are going to skip the following line and execute the GOTO WAITFORSET. This will take us right back to the BTFSC instruction. But as soon as the TMR2IF bit is set to a 1 in the PIR1 register and we test it we are going to execute the following line which is GOTO TMR2OVERFLOW. This label is placed after these three lines of code thus getting you out of this while loop. Remember the labels are for the address of the following opcodes so the TMR2OVERFLOW label we have named is for the first opcode in the next bit of code for which we have not yet seen. I placed the label in this bit of code because I was going to be talking about it in this discussion.

After the timer has went off we are able to finish setting up the PWM module. Well technically it is finished setting up and we just need to turn it on. To do this we need to enable the outputs on port c again and flip a certain bit, and we can move on to another topic. So, in order to work with the outputs we need to be in bank 1 so let’s take care of that. If you recall how to do that it is just setting the RP0 bit in the STATUS register. After that we need to enable the pins on port c as outputs. A 0 denotes a pin to be an output. I’m not to worried as to having too many outputs at this point. So, I’m just going to CLRF to the TRISC register that controls whether port c pins are inputs/outputs. After we do that we need to get back to bank 0. That involves clearing the RP0 bit of the STATUS register. The next line is the BCF ECCPAS,ECCPASE operation. the ECCPASE bit in the ECCPAS register signals whether the PWM is operating. If it is 0 than it is on. So that is why we are going to clear it now. As soon as we clear it we are flipping the on switch so to speak. And we are now ready to do so. PWM activate! Anyways, what we get out of this subroutine is a continuous PWM output of which we can manipulate at any given time. We can easily change the duty cycle and the mode. So as we get updated information from the receiver, all we need to do is update the duty cycle for the outgoing PWM, or even change direction if we had the hardware built for the other three outputs. How easy can it be, right? Yeah, we’ll get there. Well, not for the direction and other three outputs, but for the updating the speed we will.

Now let’s take a look at setting up Port A for use with capturing the incoming signal from the radio receiver.

If you didn’t notice, this subroutine looks very similar. For this reason I will not discuss it in great detail. It is nearly identical to setting up Port C with a few alterations. The major difference is it pertains to Port A. The second difference is only one port is being set up as an input. Technically all of the ports should have been set up as an input since it is the only pin being used. Why should all of the pins be set as inputs you ask? Well, if one of the output pins accidentally becomes shorted with another wire it could be disastrous, depending on whether the output pin is driven HI or LO. This can result in fires, which by all means are neat to watch in the right circumstances, just not in the hands of customers. So the end result of this subroutine is a pin on Port A we have to play around with as an input. In the next subroutine is where we will actually do something with the pin.

The first line we have here is a Bit Test f, Skip if Set. We are going to test bit 5 in Port A if it is set. If it is set to 1 then we are going to skip the following line and execute GOTO WAITFORLOW, which essentially puts us in a loop until we see a 0 in bit 5 of Port A. So what do we get out of this? We are waiting for a LO on bit 5 in Port A, then we GOTO GOHERE. We want to make sure we start the count at the very beginning of the pulse, so we want to make sure we are at a LO so we can wait for a HI. Does that make sense? Well, it did to me anyways, so that’s all that matters… heh. Onward.

Now that we have a confirmed LO on the line, we are waiting for it to go HI. The BTFSC part is nearly identical to the above part where we are waiting on an action. As soon as we get a 1 in bit 5 we move to STARTTIMING and move on our way. The three MOVs are initializing the counters. Since the pulse just went HI we want to set the TMR1 module at 00000000 00000000 and start from there. Now, since we are only using the low byte, we really only needed to set the TMR1L byte to 00000000. We are very cautious not to allow rollovers to occur since the PWM module doesn’t utilize the TMR1H register. This makes us need to use caution, since if we go to fast, and hit 11111111, then go back to 00000000, it leads to the question: “How long was our pulse?”. This is where the rollover question arises.

Here we are again, waiting on something to happen. We are sitting on a HI and waiting on it to go LO. As soon as it goes LO we move to STOPTIMING and throw what we have at the moment into storage. Now since the least significant part counts faster we want to capture it first. We quickly move TMR1L to the data storage we have labeled TIMER_COUNT_LOW, and the other register to TIMER_COUNT_HIGH. TMR1L will contain how many clock cycles occurred while the pulse was high, assuming no rollovers occurred. Since PWM doesn’t utilize the TMR1H we cannot count on accessing it in case of rollover events. If you know of a better method, inform me.

You may wondering how does this function work with the rest of the program. If we are stuck in the program waiting on the pulse the entire time, when do we have time to do other operations? Well, this pulse from the radio is only HI for a maximum of 10% of the signal period, remember? And I do not believe that the companies that made the radios for the RC cars/trucks did this by coincidence either.

This subroutine sets up the TMR1 module for us to be able to measure the incoming pulse from the receiver. This will essentially be a continuous counter, of which we can restart whenever we wish, and also measure the value whenever we wish. Bits <5:4> are the timer prescaler. Setting it to 10 will be a 1:4 scale, which happens to work very well in the next subroutine I talk about. Bit 2 is whether to synchronize with an external clock. Setting this to a 1 will make it not synchronize. Bit 1 selects whether to use an internal or external clock. Setting it to a 0 will use the internal clock. Bit 0 turns the TMR1 module on so we set it to a 1. So we have a value of 00010101 to put in the register. That should be enough to configure it, and it works.

Now this is where everything gets screwy. This subroutine is supposed to take the duty cycle of the input signal from the receiver which is from 7.5% for neutral to 5% for forward for example and map it to 0% and 90% forward respectively. Now, that is harder than it sounds. I can do it mathematically using Linear Systems and Z-Transforms finding the transform functions, etc… but they never actually taught me how to apply it to hardware, so how exactly did it help me in the real world. Well, it didn’t. Anyways, enough ranting. I did find a way around it though. By playing around with prescalers, etc, I managed to get the TMR1 module while counting the pulse to come near to 90% on a full trigger pull, thus, getting by the need to do any calculations. Now, I just throw the value of the pulse we counted into the duty cycle directly. Now, there are a few bad results to this though. If you squeeze the trigger harder than you are supposed to you can still get it wrap since we didn’t map it like we were supposed to. But for just throwing a number in the register it’s pretty close.

I do plan on writing some code to map the incoming pulse we counted to a good 0% to 90% pulse however to eliminate this wrap problem regardless. Also, if I plan on make it have reverse someday making a mapping function will be essential. This is because you will need to map the incoming radio duty cycle of 5-7.5% to outgoing duty cycle of 0-90% in one direction. Then you will have to map the incoming radio duty cycle of 7.5-10% to outgoing duty cycle of 0-90% in the other direction. Also switching the direction bit is needed, obviously. This will be using an H-bridge setup which is easy. The hard part is the mapping function. For a forward/reverse ESC the only real main difference in software would be in this method.

This section above us is just the labels to the Data Bytes and Data Words we reference in the program. Note, that not all of these variables were used as they are from older versions of the ESC. In assembly you have to allocate memory to be sure you do not overwrite anything. Well, Windows is the same way, or else you end up with BSOD.

Now to get this code to the PIC chip you will need a programmer. There are many ways to do this. You can build a serial programmer with the many designs people have made on the net, or you can buy a commercial programmer such as the one in the two pictures below. The programmer in the pictures is called the PicKit 2. It is relatively cheap, $50, considering it’s wide range of uses and the fact you can program a chip and pull the chip off the board and use it in any hardware you wish.

Stuff you get with the PicKit 2 (You also get software, but you could easily download it for free from their site too):

Dscn0706B.jpg

PIC16F684 on the PicKit 2 programmer:

Dscn0707B.jpg

The ESC connected to an RC Car:

Dscn0687B.jpg

Closeup of the ESC connected to an RC Car:

Dscn0683B.jpg

Here is a video of me showing the ESC controlling a motor on a radio controlled car. This is my Mini-T with it’s back wheels taken off so it doesn’t take off when I give it throttle. This video is of decent quality, but the sound is lacking.

Video of the Electronic Speed Controller in Action

Here is a video of me running my Mini-T with the speed controller on the car. I however was driving and operating my cell phone camera at the same time so I could not steer. And since it didn’t have reverse, or breaks, and couldn’t steer since I was holding a camera, I went off of the road. Video is bad quality since it was before I got my camera. But it is still funny.

Video of my RC Car in Action

Here is the file if you want to download the assembly file to play around with. It should compile directly in MPLab if you select PIC16F684 as the device you are using. The newest version of MPLab recognizes the PicKit 2 so if you have the PicKit 2 drivers installed and you have a PicKit 2 you will be able to download my code to a PIC16F684 and build this hardware and test my ESC for yourself.

Assembly file for the ESC

If you try out my ESC let me know how it goes. If you make any improvements or mods let me know how they go. I would love to see videos of my ESC running on vehicles especially. Questions and comments are more than welcome.

6 Comments

:

:


« DIY - Dry Erase Board
» Object Oriented Javascript with Mootools