Skip to content

Instantly share code, notes, and snippets.

@theapi
Last active January 3, 2016 22:49
Show Gist options
  • Save theapi/8531211 to your computer and use it in GitHub Desktop.
Save theapi/8531211 to your computer and use it in GitHub Desktop.
Port manipulation using a timer interrupt instead of delay
/*
* 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