I2C-based H-bridge controller with PWM

During development of my Sumo robot, I ran into some problems around controlling the H-bridges:

I decided to use a slave microcontroller to solve these problems. I used a PIC16F84A, mostly out of convenience. It has enough I/O pins to run all four inputs of two H-bridges, plus a few left over for configuration and control. It generates phase-coherent PWM drive signals that are suitable to directly control an H-bridge. It's controlled by an I2C bus; a simple serial bus that is commonly implemented on microcontrollers. The AVR has hardware support for it (although it calls it TWI), making programming simple.

Device pinout

Device pinoutDevice pinout

The address bits let you have multiple motor controllers on the same bus. Each I2C device on a bus has a unique 7-bit address. This device has that split into a five-bit prefix ('10000') and a two-bit suffix, set by these pins. They should be tied high or low depending on the address that you want the chip to have.

The I2C pins are used to control the chip. They're covered in more detail below.

The Status LED is lit while an I2C transfer is taking place. It's mostly useful for debugging.

The H-bridge outputs are on PORTB. If you don't need braking, you can just connect the A and B outputs from the chip to A and B on your H-bridge. Most H-bridges will just melt if you try to brake them, so check before you connect!

You can control a motor directly from the A output, too (with a buffer or FET or power transistor!)

Control

The device has a fairly limited vocabulary as far as I2C transactions goes: it only responds to writes, addressed directly to it (no global calls), using the normal (fast) START condition (no slow STARTs) and 7-bit addresses. The default address is b'10000xx', where 'xx' is the value of the pin straps on PORTA. The device will accept an eight-bit data transfer in the format '0cdbpppp', where:

The device has a 10MHz clock by default, which gives a maximum I2C frequency of 8kHz. It can go faster, but will become less reliable. The most common symptom of a too-fast I2C bus is that transfers won't be ACKed. This is a recoverable condition; the device won't ACK unless it received all data bits intact. It won't (shouldn't!) receive the wrong data and destroy your H-bridge. Of course, if you want to run the bus faster, you can give it a faster clock - the PIC16F84A is specced up to 20MHz.

The default PWM output frequency is about 300Hz with a 10MHz clock. This appears to be a good number for small DC motors. The firmware can be tweaked to give higher or lower rates - or again, you can twiddle the clock rate.

A bit more on I2C

The I2C bus (also referred to as TWI, or Two Wire Interface) uses, unsurprisingly, two wires. It's pretty neat; there's a data (DATA) and clock (CLK) line that is shared amongst all of the devices on the bus. A few simple rules govern data transfer; the original spec from Philips is short and well-written. The clock rate (for standard-rate devices) is up to 400kHz, which is more than enough. Since the PIC uses a bit-banging I2C implementation, the highest clock rate is about 8kHz - still plenty for robotics applications.

The gist of how I2C works is:

There, wasn't that easy? :-)

Controller hardware

The controller is based around a PIC16F84A. It has 13 I/O's, a timer, and not a lot else. To program it I'm using JDM programmer hardware, ICProg software, and the excellent Microchip MPLAB suite to compile and simulate the software.

H-bridge controller schematicH-bridge controller schematic

The controller board, attached to the main control board: Note the I2C cable running between them, which provides the main control board with power.The controller board, attached to the main control board: Note the I2C cable running between them, which provides the main control board with power.

There's not much to the hardware. The address select pins need to be tied high or low depending on the I2C address that you would like the chip to use. The LED can be left out if you like. The crystal is 10MHz by default, but it can be adjusted to pretty much whatever you like given the caveats described elsewhere.

I use a 4-pin header to provide +5V, I2C data, I2C clock and ground. The plan is that each module on the robot will have two of these headers. They connect to the same pins so that modules can be daisy-chained together on the same bus. Each module can take its power from the bus, which simplifies everything greatly. Only one board needs to provide power to the bus. I'm doing it on the motor control board because it already has a (relatively) high voltage supply to run the motors.


Controller software

For those that just want the firmware download, we have:

Theory

There are two main tasks to be performed by the software: talking to the I2C bus, and setting the H-bridge outputs. The outputs need to be updated on a set schedule, so the PIC's timer is used to regulate it. The I2C bus is polled, and the PIC has no direct hardware support. This is why the maximum frequency is so low compared with I2C-native devices.

I2C implementation

The I2C implementation is fairly uninspiring; read the Philips I2C spec if you want to understand what's going on. It requires open-drain outputs for the bus lines. The PIC only has one (and I'd already attached it to a LED on the prototype). To simulate open-drain outputs, the PIC I/O's are tristated for a '1', and set as outputs (with an output value of '0') for a '0'.

Once the I2C code has received an entire transfer, it does some legwork for the H-bridge code. When a channel's PWM signal is high or low, a certain value is written to the output port. This is the mapping between command and H-bridge lines. Each channel has a pair of registers that set these high or low values. Depending on the command (forward, reverse, brake, coast), the I2C code will set up these register in advance, so all that the PWM code has to do is stick them on the output pins.

PWM implementation

The PWM uses a 'ratio' register for each channel to determine what fraction of the total time the PWM signal is 'high' for. On each iteration, a 'value' register is incremented. Its maximum value is the maximum PWM ratio. On each cycle, if 'value' is greater than 'ratio' for a given channel, the signal is low. Otherwise, it's high. Simple, in theory!

This algorithm has a few implications. First, with the H-bridge output algorithm, the four output PWM signals (per channel) are phase-coherent. This is necesary for H-bridges to prevent heating or possible H-bridge destruction.

The code runs once per possible output value. Hence the low PWM resolution (four bits). This isn't a problem for most motor control applications, although I'm sure someone will email me and tell me that they need more! More precision means more interrupts, which will limit the maximum I2C frequency.

Since the outputs are updated on a timer, the PWM frequency can be adjusted fairly trivially by adjusting the timer period. The only limitation on this is that the PWM interrupt takes cycles away from the I2C code. I assume that at worst, the I2C code will be interrupted once between each bus sample, which gives a maximum PWM frequency of about 2kHz. If you're running near this limit and in doubt, reduce the I2C speed, or stress-test the I2C bus, or add some error checking to your I2C transmitter. The I2C code won't ACK if it doesn't receive a transfer properly, so there's no (theoretical) risk of getting corrupt data.

H-bridge outputs

If the PWM signal for a given channel is high, we stick the stored 'high' value for that channel on the outputs. If not, we put 'low' on the outputs. That's pretty much it! All of the real work has been done by the I2C code.

There is a slight possibility of H-bridge heating here if the PIC outputs don't switch quickly enough, switch at slightly different times, or if the H-bridge itself can't switch fast enough. This would only come up during a forward/reverse or braking transition. It's not an issue in my application, but a possible improvement would be to have some 'coast' time in the middle of the transition during which the H-bridge stops conducting. This will restrict the maximum output power slightly, but it's not likely to be noticeable given that we are controlling electromechanical devices which can't change direction that quickly anyway.

It's entirely dependent on your situation - the H-bridge you're using, the purpose of the motors you're driving, the drive voltage, and so on. The easiest thing to do, if it's an issue, is to insert the 'coast' period in your control software.

The future

Stuff I'd like to do for Version 2: