Last active
January 3, 2016 22:49
-
-
Save theapi/8531211 to your computer and use it in GitHub Desktop.
Port manipulation using a timer interrupt instead of delay
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* main.c | |
* ATmega328p port manipulation | |
*/ | |
/******************************************************************************** | |
Includes | |
********************************************************************************/ | |
#include <avr/io.h> | |
#include <avr/interrupt.h> | |
#include <util/atomic.h> | |
/******************************************************************************** | |
Macros and Defines | |
********************************************************************************/ | |
// 16MHz Clock | |
#define F_CPU 16000000UL | |
// Calculate the value needed for | |
// the CTC match value in OCR1A. | |
#define CTC_MATCH_OVERFLOW ((F_CPU / 1000) / 8) | |
/******************************************************************************** | |
Function Prototypes | |
********************************************************************************/ | |
void initTimer1(); | |
unsigned long millis(); | |
char waited(long delay); | |
void iterateLeds(); | |
void ledCountTime(); | |
void cylon(long delay); | |
void ledVuBar(uint8_t max); | |
void ledPatternShift(uint8_t pattern, long delay); | |
void allOn(); | |
/******************************************************************************** | |
Global Variables | |
********************************************************************************/ | |
volatile unsigned long timer1_millis; | |
unsigned long milliseconds_since; | |
char cylonDirection; // the current direction the cylon sweep is moving. | |
char vuBarDirection; | |
/******************************************************************************** | |
Interrupt Routines | |
********************************************************************************/ | |
ISR (TIMER1_COMPA_vect) | |
{ | |
timer1_millis++; | |
} | |
/******************************************************************************** | |
Main | |
********************************************************************************/ | |
int main(void) { | |
initTimer1(); | |
// Enable global interrupts | |
sei(); | |
DDRD = 0xFF; // set all to output | |
PORTD = 0; // all off | |
while(1) { | |
//allOn(); | |
cylon(100); | |
//iterateLeds(); | |
//ledCountTime(); | |
//ledVuBar(8); | |
//ledPatternShift(0b00000111, 150); | |
} | |
return 0; | |
} | |
/******************************************************************************** | |
Functions | |
********************************************************************************/ | |
void initTimer1() | |
{ | |
// CTC mode, Clock/8 | |
TCCR1B |= (1 << WGM12) | (1 << CS11); | |
// Load the high byte, then the low byte | |
// into the output compare | |
OCR1AH = (CTC_MATCH_OVERFLOW >> 8); | |
OCR1AL = (unsigned char) CTC_MATCH_OVERFLOW; | |
// Enable the compare match interrupt | |
TIMSK1 |= (1 << OCIE1A); | |
} | |
unsigned long millis() | |
{ | |
unsigned long millis_return; | |
// Ensure this cannot be disrupted | |
ATOMIC_BLOCK(ATOMIC_FORCEON) { | |
millis_return = timer1_millis; | |
} | |
return millis_return; | |
} | |
/** | |
* Waited for a time to pass, rather than using a delay. | |
* | |
* @returns 1 if waited long enough, 0 if not. | |
*/ | |
char waited(long delay) | |
{ | |
unsigned long milliseconds_current = millis(); | |
if (milliseconds_current - milliseconds_since > delay) { | |
milliseconds_since = milliseconds_current; | |
return 1; | |
} | |
return 0; | |
} | |
void allOn() | |
{ | |
PORTD = 0xff; | |
} | |
void iterateLeds() | |
{ | |
if (waited(250)) { | |
if (PORTD == 0) { | |
// Shifting zeros does nothing so turn on the first led/bit | |
PORTD = 1; | |
} else { | |
// Bit shift the port register to turn on each led individually. | |
PORTD = PORTD << 1; | |
} | |
} | |
} | |
void ledCountTime() | |
{ | |
if (waited(1000)) { | |
// Go back to the start | |
if (PORTD == 255) { | |
PORTD = 0; | |
return; | |
} | |
// Add one to the register | |
// The leds show the byte status of the register so the appropriate leds will light :) | |
PORTD = PORTD + 1; | |
} | |
} | |
void cylon(long delay) | |
{ | |
if (waited(delay)) { | |
if (PORTD == 0) { | |
// Create something to shift | |
PORTD = 1; | |
} else if (cylonDirection == 0) { | |
// Shift the on bit to the left | |
PORTD = PORTD << 1; | |
} else { | |
// Shift the on bit to the right | |
PORTD = PORTD >> 1; | |
} | |
// When the highest bit is lit, reverse the direction. | |
// highest bit is 10000000 / 128 / 0x80 | |
// which can be checked using the bitwiae AND operator | |
// (could do PORTD == 128, but learning bit manipulation here) | |
// Compare the current byte to a (byte) mask for the highest bit. | |
// eg; 11111111 & 10000000 == true | |
if (PORTD & (1 << 7)) { | |
// reverse the direction for next time | |
cylonDirection = 1; | |
} else if (PORTD == 0x01) { | |
// set the direction forward | |
cylonDirection = 0; | |
} | |
} | |
} | |
void ledVuBar(uint8_t level) | |
{ | |
if (waited(50)) { | |
// As 0 is a valid reading we can accept a level of 8. | |
if (level == 0) { | |
// No leds on | |
PORTD = 0; | |
return; | |
} | |
// When the highest bit is lit, reverse the direction. | |
// level has one subtracted because the input is goes up to 8 to include 0. | |
// Compare the current byte to a (byte) mask for the highest bit. | |
// eg; 11111111 & 10000000 == true | |
if (PORTD & (1 << --level)) { | |
// reverse the direction | |
vuBarDirection = 1; | |
} else if (PORTD == 0) { | |
// set the direction forward | |
vuBarDirection = 0; | |
} | |
if (PORTD == 0) { | |
// Create something to shift | |
PORTD = 1; | |
} else if (vuBarDirection == 0) { | |
// Turn on the next most significant bit (next on the left) | |
PORTD |= (PORTD << 1); | |
} else { | |
// Turn off the most significant bit (on the left) | |
PORTD &= (PORTD >> 1); | |
} | |
} | |
} | |
/** | |
* The pattern of leds lit, sweeping along the line, wrapping around. | |
* If the leds were in a circle, it would carry on round and round the circle. | |
*/ | |
void ledPatternShift(uint8_t pattern, long delay) | |
{ | |
if (waited(delay)) { | |
//uint8_t pattern = 0b00000111; | |
if (PORTD == 0) { | |
// Create the pattern to shift | |
PORTD = pattern; | |
return; | |
} | |
// At the end of the register add on bits to the start again. | |
if (PORTD & (1 << 7)) { | |
// Shift left | |
PORTD = (PORTD << 1); | |
// Add one to the start (wrap around) | |
PORTD |= (1 << 0); | |
} else { | |
// Shift left | |
PORTD = (PORTD << 1); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment