Today you will learn how to output analog voltages using PWM signals. PWM stands for “Pulse-Width Modulation“, that is the waveform of a signal is changed by changing the width between each pulse. PWMs have many applications:
- Generating wave forms with different frequencies, to playback music
- Approximating an analog output: pulsing an LED (turning it on and off) at speeds that the human eye can’t see which creates the illusion of the light being dimmed.
- Interfacing devices that can decode PWM signals, servo motor’s that RC car’s use. The pulse width encodes the desired angle that you wish the motor to turn to.
- Configure the built-in PWM module
- Emit sound
- Control a servo
- Tie together the previous module, analog input, and control a servo/sound making device using some sensor
- Learn about importing “.h” and “.c” files into AVR studio
What is PWM?
PWM signals are special square waves that can be outputted by a micro controller. A square wave (for the ATtiny 85) is an output that looks like this:
and oscillates from 0V to 5V. PWM functionality allows you to adjust the duty cycle of the square wave allowing you to create signals that aren’t high for 50% of the time and then low for the other 50% allowing you to generate signals that look like the following:
Internally, the AtTiny accomplishes this using a hardware timer. Instead of counting a number by adding it up, like in a for-loop, the timer is using dedicated hardware and makes its value available in memory. The timer also allows triggering events such as exceeding a maximum value in hardware. The PWM module uses this mechanism to turn on/off the chips output ports to create the desired waveform. When configuring PWM signals on the ATtimy85 or other like micro controllers you need to keep in mind these settings:
- Duty Cycle or time signal is high vs the time it is low
- The signal’s frequency/period
- what output pin do you wish to use
- how do I enable the timer
If you wish to follow along in the datasheet, go to 11.7.3 of the Long Version. There is also more information in 11.9 where the register description lies. The important registers that are worth mentioning are:
- DDRBL allows us to configure the pin’s of the microcontroller (need to set PB0 as an output pin)
- TCCR0A: Timer/Counter Control Register A
- COM0A1 and COM0B1: in Fast PWM mode, setting these to 1 we “Clear OC0A/OC0B on Compare Match, set OC0A/OC0B at BOTTOM (non-inverting mode)”
- WGM00-01: Here we choose Fast PWM mode as our Waveform Generation Mode.
- TCCR0B: Timer/Counter Control Register B
- CS00-CS02: This allows us to set the period of the PWM signal
- WGM02: the last bit to set Fast PWM mode
- OCROA: allows us to set the duty cycle of the signal
Its Code time:
The PWM code is split up into separate files to make your main file easier to read/understand. If you are interested in learning how to do this the basics are:
- You need a .h and .c file with the same name
- In the .h file you declare your functions and you define the name of the file
- In the .c file you write your functions and need to import the header file
There are 5 files that you need to download:
- pwm.h //header file for pwm code
- pwm.c //code for dealing with PWM functionality
- analog.h //header file for analog code
- analog.c //code for dealing with analog functionality
- AVRGCC2.c //where int main function exists
Download a copy of the working directory of the project here.
Using the Code:
The first goal is to dim/brighten an LED based on the reading of any analog sensor.
Connect an analog sensor to PB4 of the chip and an LED/resistor pair to PB0. You now need to figure out what values your sensor will be outputting so that you have a full control over the LED’s brightness instead of just a small range. Calculate the Lowest voltage, highest voltage, and the range of the sensor. Then use the custom_getAIN(double low, double high) function to get your analog reading. Pass this reading into the set_led_special(double reading, double range, double offset) function where range = high-low. Your code should look like this:
reading = custom_getAIN(parameters....); set_led_special(reading, (calculate range), 0);
The second goal is to turn a servo motor. Connect the servo motor to your micro controller using PB0 as the control line. Instead of using set_led_special() use set_servo_special(). This function does not just generate a simple square wave, but a digital PWM signal with a 50 Hz frame rate. Within each 20 ms timeframe, an active-high digital pulse controls the position. The pulse nominally ranges from 1.0 ms to 2.0 ms with 1.5 ms always being center of range.Thus, the length of the pulse controls the actual position of the servo horn.