Commit 66b37287 authored by Erik Strand's avatar Erik Strand

Update code for embedded programming

parent e253655b
......@@ -5,14 +5,14 @@ menu = "main"
weight = 9
+++
Files: [blink.c](/designs/07_blink.c)
Files: [attiny44a_blink](https://gitlab.cba.mit.edu/erik/attiny44a_blink)
Time to lose the training wheels. This week we're writing our own microcontroller code.
### Turning on the LED
Two weeks ago I added an LED and a button to Neil's [hello world](http://academy.cba.mit.edu/classes/embedded_programming/index.html#echo) board, so I'll start by making the LED turn on. I attached my LED to port `PB2`, and my switch to port PA7. To turn on the LED, we only need to do two things: enable `PB2` as an output, and set it high. Here's code that does just that.
Two weeks ago I added an LED and a button to Neil's [echo board](http://academy.cba.mit.edu/classes/embedded_programming/index.html#echo), so I'll start by making the LED turn on. I attached my LED to port `PB2`, and my switch to port `PA7`. To turn on the LED, we only need to do two things: enable `PB2` as an output, and set it high. Here's code that does just that.
{{< highlight c >}}
#include <avr/io.h>
......@@ -49,25 +49,25 @@ The easiest approach is to illuminate the LED only for the duration of the butto
#include <avr/io.h>
#define led_pin (1 << PB2)
#define switch_pin (1 << PA7)
#define button_pin (1 << PA7)
int main(void) {
// Configure led_pin as an output.
DDRB |= led_pin;
// Configure switch_pin as an input.
DDRA |= switch_pin;
// Configure button_pin as an input.
DDRA &= ~button_pin;
// Activate switch_pin's pullup resistor.
PORTA |= switch_pin;
// Activate button_pin's pullup resistor.
PORTA |= button_pin;
// Nothing left to do, so just spin.
while (1) {
// Turn on the LED when the button is pressed.
if (PINA & switch_pin) {
if (PINA & button_pin) {
// Turn off the LED.
PORTB &= ~led_pin;
} else {
// Turn on the LED.
PORTB |= led_pin;
}
}
......@@ -85,7 +85,7 @@ Another way to use the button is to toggle the state of the LED. So each time we
Let's ignore the button for now, and use a timer to blink the LED. As the ATTINY44A datasheet describes in chapters 11 and 12, there are two timers we can use: one 8 bit, and one 16 bit. The primary difference is that the 8 bit timer can only count to 255, while the 16 bit timer can count up to 65535. To debounce the button input, I shouldn't need to time anything longer than 10ms, so I bet I can make the 8 bit timer work.
The only initialization step is to set the prescalar. Page 8 of the datasheet gives the options. My ATTINY is clocked by a 20MHz resonator, so each clock cycle is only 50 nanoseconds. So I'll use a prescaler of 1024 so that my timer ticks on the order of microseconds instead of nanoseconds. Reading the timer, or setting it to a particular value, is easy: just use the `TCNT0` register.
The only initialization steps are to set the clock and timer prescalers. Page 31 of the datasheet gives the options for the former. We'll use the full 20MHz provided by the resonator. Page 8 gives the options for the timer. I'll use a prescaler of 1024 so that my timer ticks on the order of microseconds instead of nanoseconds. Reading the timer, or setting it to a particular value, is easy: just use the `TCNT0` register.
There's only one issue left: even though we scale the clock down by a factor of 1024, the maximum amount of time the timer can record is 255 * 1024 / 20,000,000 = 0.013056 seconds. If we turn the timer off and on with this period, we might notice it's dimmer than before but we'll never be able to see it change from on to off or vice versa. To get around this I'll let the timer tell me when 195 * 1024 / 20,000,000 ~ 10ms have passed, and I'll only toggle the LED when this happened 250 times. So we should see the LED blink every 2.5 seconds. (The time to evaluate the additional instructions is negligible compared to the amount of time we're waiting for the timer.)
......@@ -95,6 +95,10 @@ There's only one issue left: even though we scale the clock down by a factor of
#define led_pin (1 << PB2)
int main(void) {
// Set the clock prescaler to 1.
CLKPR = (1 << CLKPCE);
CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
// Set the 8 bit timer's prescaler to 1/1024.
TCCR0B |= 0b00000101;
......@@ -102,7 +106,7 @@ int main(void) {
DDRB |= led_pin;
// Blink the LED with ~2.5s half-period.
// 250 * 200 * (1024 / 20M) ~ 2.5s
// 250 * 200 * 1024 / 20M ~ 2.5s
int count = 0;
while (1) {
// If it's been 10ms, reset the timer and increment count.
......@@ -110,7 +114,6 @@ int main(void) {
TCNT0 = 0;
// When count reaches 250, reset it and toggle the LED.
if (++count == 250) {
// Toggle the LED state.
PORTB ^= led_pin;
count = 0;
}
......@@ -130,35 +133,44 @@ Now that we can use the timer let's go back to the button. We've seen all the pi
#include <avr/io.h>
#define led_pin (1 << PB2)
#define button_pin (1 << PA7)
int main(void) {
// Set the clock prescaler to 1.
CLKPR = (1 << CLKPCE);
CLKPR = (0 << CLKPS3) | (0 << CLKPS2) | (0 << CLKPS1) | (0 << CLKPS0);
// Set the 8 bit timer's prescaler to 1/1024.
TCCR0B |= 0b00000101;
// Configure led_pin as an output.
DDRB |= led_pin;
// Configure button_pin as an output.
DDRA &= ~button_pin;
// Activate button_pin's pullup resistor.
PORTA |= button_pin;
int bouncy_switch_state = 0;
int debounced_switch_state = 0;
// Toggle the LED when the button is pressed.
// Nothing left to do, so just spin.
while (1) {
// Use the timer to count how long it's been
// since we've seen switch_pin change state.
if ((PINA & switch_pin) != bouncy_switch_state) {
bouncy_switch_state = PINA & switch_pin;
// since button_pin changed state.
if ((PINA & button_pin) != bouncy_switch_state) {
bouncy_switch_state = PINA & button_pin;
TCNT0 = 0;
}
// If it's been 10ms since PA7 changed state,
// If it's been 10ms or more since PA7 changed state,
// it's not bouncing.
if (TCNT0 >= 195) {
// If the button is in a new state,
// it's been pressed (or released).
// It's been 10ms since the switch changed.
if (bouncy_switch_state != debounced_switch_state) {
debounced_switch_state = bouncy_switch_state;
// If the button is newly pressed,
// toggle the LED.
// If the button is newly pressed, toggle the LED.
if (debounced_switch_state == 0) {
PORTB ^= led_pin;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment