This is another post about the AZ3166 MXChip DevKit – this post builds on part 4 of my getting started series, and describes another way to implement pulse-width-modulation (PWM) in code.
You might wonder why I’m spending so much time with servos and this device – my personal use-case is that I’d like to use the sensors on the device alter something in the physical world – to use a classic example, when the temperature falls below a certain value, I’d like to use a servo to change the position of a thermostat.
I normally use PWM for controlling servos where I’ve not been able to use a pre-packaged library (like the one that ships with the Arduino IDE). That package doesn’t work with the AZ3166 hardware, so in a previous post I described how to use PWM and the Arduino function analogWrite to control a servo position.
The code below describes how to control a servo by sending a PWM signal to the PWM_OUT pin, and allowing 1s for the wiper to reach its final position.
#define analogIn A2
int inputValue = 0;
for (int i = 5; i < 31; i++)
inputValue = analogRead(analogIn);
My servo is a little bit special in that it has an extra pin that allows me to measure an analog value corresponding to the wiper position between 0 and 180, so I’m able to graph the position against the value that I pass to analogWrite.
The graph below shows the position of the servo against the value passed to analogWrite.
I found a couple of things which were a bit odd:
- The documentation for the device says I can control PWM through three pins corresponding to RGB_R, RGB_G, RGB_B – but I could only issue a PWM signal using analogWrite through physical pin 7 (corresponding to the software pin PWM_OUT). I could physically observe this PWM through the servo, and also through the green LED, but I couldn’t replicate this with the red or blue LEDs or their pins.
- I can control the position of the servo by changing what value I pass to the analogWrite function – however, through a process of trial and error, I found that even though I can pass integers between 0 and 255 to analogWrite, the only values which allow me to control the servo position are between 5 and 31. Given there’s 180 degrees in a full servo sweep, that means I don’t have very much control over the servo’s angular position using analogWrite and the AZ3166. Also, guessing/trial and error isn’t a great way to achieve repeatable control.
Since writing that post, I’ve found a different way of controlling servos. The AZ3166 uses the MBED microcontroller library, mbed.h, which refers to another useful library, PwmOut.h.
I found the code for mbed.h on my machine at:
Also the code for PwmOut.h is on my machine at:
How to use the PwmOut.h functions to create and control a PWM signal
I found the comment by Arthur Ma on Github very helpful when writing this post.
So let’s say I wanted to instantiate one of the three pins available for PWM use – let’s choose PB4, which is shared with the red onboard LED, RGB_R – I could declare it in my arduino code in this way.
which is equivalent to:
This just says “make pin PB_4 available to send a PWM signal”.
The next bit is really useful – I can control the frequency and period of the PWM signal using the functions available in the PwmOut.h library. So if I wanted my pin’s PWM signal to have a frequency of 50Hz, i.e. a period of 20ms (because 1/50 = 0.02), I could send the instruction:
The servo’s position is determined by the width of the pulse sent each cycle, and typically a pulse width of 1.5ms will turn the servo to its central (90 degree) position. So if I wanted to do this, I could send the instruction
1500 microseconds is the same as 1.5ms. The function pulsewidth_us allows me to set the width in microseconds, and pulsewidth_ms allows me to set it in milliseconds.
I wrote the program below and uploaded it to my AZ3166 to see how the analog signal varied with pulse width, and found I can control the position of the wiper much more accurately now, and I can control it on any of Pins 5, 7, and 10 (corresponding to RGB_R, RGB_G, and RGB_B).
#define analogIn A2
for (int i = 1; i < 3000; i++)
I’ve included the graph of results below.
There are some limitations of the cheap hobby servo which are now obvious.
- You can see that the servo only reliably sweeps between pulse-width values of about 690 and 2270.
- The servo jumps from 0 degrees to about a 40 degree position at a pulse width of 253microseconds, after which there is a very linear relationship.
- The servo output reports a higher than expected value quite often, as shown by the data points above the main trend line – with this knowledge, I can anticipate this behaviour and code for it.
I’ve discovered a better way of controlling pulse-width-modulation signals on the AZ3166 device using PwmOut – previously I used analogWrite with a single pin, and now I can use three pins, and control the period and pulse width to much higher degree of accuracy.