Code Snippets Submitted by SFriederichs
10-bit A/D Data Sampling and Transmission
/**@file endianness.c
@brief Code to transmit 16-bit ADC samples in big or little-endian order
@author Stephen Friederichs
@date 5/12/13
ADC Channels:
0 - Accelerometer X axis (Vertical)
1 - Accelerometer Y axis (Horizontal)
2 - Accelerometer Z axis (Lateral)
3 - Accelerometer 0G detect (Freefall detect)
The heartbeat LED is on Port D, pin 7
*/
/**@def F_CPU
@brief Clock frequency = 8MHZ - this is set by fuses and registers, not by this define
@note Always define this before including delay.h!
*/
#define F_CPU 8000000
/**@include io.h
@brief Include for AVR I/O register definitions
*/
#include <avr/io.h>
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include delay.h
@brief Include for delay functions such as _delay_ms() and _delay_us()
*/
#include <util/delay.h>
/* Basic bit manipulation macros - everyone should use these. Please, steal these! Don't not use them and
don't rewrite them yourself!
*/
#define SET(x,y) x |= (1 << y)
#define CLEAR(x,y) x &= ~(1<< y)
#define READ(x,y) ((0x00 == ((x & (1<<y))>> y))?0x00:0x01)
#define TOGGLE(x,y) (x ^= (1 << y))
int main(void)
{
//Variable to count the number of times the timer interrupt has fired
uint16_t ticks = 0;
uint16_t accel_data = 0;
uint8_t transmit_enable = 0x00;
uint8_t * uart_data_pointer = &accel_data;
/*Initialization Code*/
/* ATMega328 Datasheet Table 14-1 Pg 78
Configure PD7 for use as Heartbeat LED
Set as Output Low (initially)
*/
SET(DDRD,7); //Direction: output
CLEAR(PORTD,7); //State: Lo
/* TCCR1A - ATMega328 Datasheet Section 16.11.2 pg 134 - TCCR1A
No input capture used - bits 7:6 are 0
No waveform generation used - bits 4:3 are 0
Clock source select is bits 2:0 but are not yet set - wait until the
main loop is ready to start
*/
TCCR1A = 0x00;
/* TCCR1C - ATMega328 Datasheet Section 16.11.3 pg 135
This register is only used for output compare.
There's no output compare in this application so this can be all 0's
*/
TCCR1C = 0x00;
/* TCCR1B
Note: I've disabled the CKDIV8 fuse so that the clock source is 8MHz
As per ATMega328 Datasheet Section 16.9.1 page 123, setting the timer
to Normal mode causes the counter to count up until it reaches 0xFFFF
at which point it will overrun and start back at 0. To configure this
timer/counter to produce a period of 1ms we need to start counting
at a value that causes it to reach 65535 in 1ms.
What is that value?
With a clock prescaler of 32 each count of the timer is roughly
(1/8MHz)*32 = 1uS
1ms / 1us /tick = 1000 ticks /ms
The counter counts up to 65536, so to determine what value we have to
start at we subtract 1000 from 65536:
65536-1000 = 64536
*/
#define TIMER1_PERIOD 64536
TCNT1 = TIMER1_PERIOD;
//Configure ADC to read accelerometer data
//ATMega328 - Section 24.9.1 Pg 254 - ADMUX Register
/*ADC result - left-adjusted (Bit 5). The ADC result is 10-bits wide.
In practice, the least-significant 2 bits are often too noisy to be
of any use, so they are discarded. To support this, the ATMega328P is
capable of storing the upper eight bits of the ADC result in the
ADCH register alone. In this case, I want all 10 bits of the data
so I can show how to handle endianness in serial transmissions. As
a result, the most significant two bits are stored in ADCH and the least
significant 8 are stored in ADCL.
*/
/*ADC Channel - I only care about one - the Y axis on the accelerometer
which is channel 1.*/
ADMUX = (0x01 << 6) /*Reference - AVCC - 5V. */
|(0x00 << 5) /* Right-adjust ADC result - refer to
Section 24.9.3.2 pg 256*/
|(0x01 << 0); /*Channel set to X-Axis output on
accelerometer*/
/* ATMega328 Datasheet - Section 24.9.2 - ADCSRA - ADC Status
and Control Register
ADCEN - Bit 7 - Enable ADC - Obviously set this to 1
ADCSC - Bit 6 - Start Converstion - Not yet: 0
ADATE - Bit 5 - Auto-trigger ADC - I'll be manually triggering
the ADC, so 0
ADCIF - Bit 4 - ADC Interrupt Flag - Set when conversion
completes. Ignore.
ADCIE - Bit 3 - ADC Interrupt Enable - Everything will be polled
for this, so 0
ADPS - Bits 2:0 - ADC Prescaler
*/
/*ATMega328 Section 24.4 Pg245 discusses what the prescaler should be set to:
By default, the successive approximation circuitry requires an input clock
frequency between 50kHz and 200kHz to get maximum resolution.
The ClkIO is 8MHz and the prescaler options are 2,4,8,16,32,64 and 128.
1MHz/8 = ~125KHz, so that seems good. That value is 3
*/
ADCSRA = (0x01 << 7) //Enable ADC
|(0x03); //Set prescaler to 1/8 ClkIO - 125KHz
/* ATMega328 Datasheet Section 24.9.5 Pg 257 - DIDR0
This register allows digital input buffers on ADC pins to be
disabled. This saves power, so I'll do it
*/
DIDR0 = 0x01; //Turn off digital filtering on ADC channel 0
//Configure UART for 38400 8N1 Tx Communication
//Step 1 - Baud rate
/* ATMega328 Datasheet Section 20.10 - Table 20-6 pg 192
Baud rate settings for fosc of 8MHZ
Choosing baud rate of 38.4K for minimum error
U2Xn = 0 - Use standard (not double) data rate
UBRRn = 12
*/
UBRR0 = 12;
/* UCSR0A - UART 0 Control and Status Register A
ATMega328 Datasheet Section 20.11.2 pg 194
Bits 7:2 - Status bits
Bit 1 - Double UART transmission speed - No: 0
Bit 0 - Multi-Processor Communication Mode - No:0
*/
UCSR0A = 0x00;
/* UCSR0B - UART 0 Control and Status Register B
ATMega328 Datasheet Section 20.11.3 pg
Bit 7 - Rx Complete Interrupt Enable - 0
Bit 6 - Tx Complete Interrupt Enable - 0
Bit 5 - USART Data Register Empty interrupt enable - 0
Bit 4 - Receiver Enable - Set to 1
Bit 3 - Transmitter Enable - Set to 1
Bit 2 - Character Size Bit 2 - Set to 0 for 8 bits
Bit 1 - 9th receive bit - Ignore
Bit 0 - 9th transmit bit - Ignore
*/
UCSR0B = 0x00 | (1 << 3)
| (1 << 4);
/* UCSR0C - UART 0 Control and Status Register C
ATMega328 Datasheet Section 20.11.4 - Pg 196
Bits 7:6 - Set to asynchronous (clockless) mode: 00
Bits 5:4 - Parity setting - None : 00
Bit 3 - Stop select - 1 : 0
Bit 2:1 - Character size - 8 : 11
Bit 0 - Clock polarity: Don't care : 0
*/
UCSR0C = 0x03 << 1;
//Send a known pattern upon startup to verify the UART works
UDR0 = 0xA5;
//Wait until transmit is complete
while(0x00 == READ(UCSR0A,6));
UDR0 = 0x5A;
while(0x00 == READ(UCSR0A,6));
UDR0 = 0xA5;
//Wait until transmit is complete
while(0x00 == READ(UCSR0A,6));
/* Flash the LED for a second to show that initialization has successfully
occurred
*/
SET(PORTD,7);
_delay_ms(1000);
CLEAR(PORTD,7);
/* Start the timer/counter
ATMega328 Datasheet Section 16.11.2 Pg 135 - TCCR1B
No Waveform generation: bits 4:3 = 0
No input capture: bits 7:6 = 0
Clock select: ClkIO/8 - bits 2:0 = 010b = 0x02
*/
TCCR1B = 0x02; //This starts the counter/timer
while(1)
{
/* Timer overflow - Reading the accelerometer at a 1KHz rate
and flash the heartbeat LED at a reasonable period as well
*/
if(READ(TIFR1,0))
{
/* ATMega328 Datasheet Section 16.11.9 pg137
Setting TIFR1 bit 1 clears the overflow flag
*/
SET(TIFR1,0);
/* Reload the timer/counter count value to the
previous value so that the period remains the same
*/
TCNT1 = TIMER1_PERIOD;
//Read accelerometer data via ADC
SET(ADCSRA,6); //Start ADC conversion
/* Wait until conversion finishes - this should never
be more than 25*(8000000/8)^-1 seconds, which is
about 25us. Typical measured time is ~14.5us
*/
while(0x00 == READ(ADCSRA,4));
SET(ADCSRA,4); //Clear the interrupt flag by setting it to 1
//Clear acceleration data variable before loading new value
accel_data = 0;
/* When reading the full 10-bits from the ADC the
lower register must be read first
*/
accel_data |= (uint16_t)ADCL;
//Then the upper 2 bits
accel_data |= (uint16_t)(ADCH << 8);
/* Transmission of data is toggled by transmitting a
'0' (0x30) byte over serial
*/
if(0x01 == (READ(UCSR0A,7)))
{
if(0x30 == UDR0)
{
transmit_enable =
(0x00 == transmit_enable?0xFF:0x00);
}
}
if(0xFF == transmit_enable)
{
#ifdef BIG_ENDIAN
//Send high byte...
UDR0 = uart_data_pointer[1];
while(0x00 == READ(UCSR0A,6));
//...then low byte
UDR0 = uart_data_pointer[0];
while(0x00 == READ(UCSR0A,6));
#else
//Send low byte...
UDR0 = uart_data_pointer[0];
while(0x00 == READ(UCSR0A,6));
//...then high byte
UDR0 = uart_data_pointer[1];
while(0x00 == READ(UCSR0A,6));
#endif
}
//Blink Heartbeat LED
/*
The timer period is 1ms. To keep everything simple the LED will toggle
every 512 ticks - roughly every .5s.
*/
ticks++;
//If true, the current ticks is a multiple of 512
//So blink the heartbeat LED
if(0x8000 == (ticks << 7))
{
TOGGLE(PORTD,7);
}
}
//Main Loops
}
}
Delay Loop LED Blinker
/**@file led_blink.c
@brief The most basic approach to blinking an LED on AVR microcontrollers - specifically the ATMega328P
@author Stephen Friederichs
@date 3/28/13
@note This code assumes that the LED is active high (pin sources current)
*/
/**@def F_CPU
@brief Clock frequency = 8MHZ - this is set by fuses and registers, not by this define
@note Always define this before including delay.h!
*/
#define F_CPU 8000000
/**@include io.h
@brief Include for AVR I/O register definitions
*/
#include <avr/io.h>
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include delay.h
@brief Include for delay functions such as _delay_ms() and _delay_us()
*/
#include <util/delay.h>
/* Basic bit manipulation macros - everyone should use these. Please, steal these! Don't not use them and
don't rewrite them yourself!
*/
#define SET(x,y) x |= (1 << y)
#define CLEAR(x,y) x &= ~(1<< y)
#define READ(x,y) ((FALSE == ((x & (1<<y))>> y))?FALSE:TRUE)
#define TOGGLE(x,y) (x ^= (1<<y))
int main(void)
{
/*Initialization Code*/
/* ATMega328 Datasheet Table 14-1 Pg 78
Configure PD7 for use as Heartbeat LED
Set as Output Low (initially)
*/
SET(DDRD,7); //Direction: output
CLEAR(PORTD,7); //State: Lo
/* Flash the LED for a second to show that initialization has successfully
occurred
*/
SET(PORTD,7);
_delay_ms(1000);
CLEAR(PORTD,7);
while(1)
{
/*Set PD7 low for 500ms*/
CLEAR(PORTD,7);
_delay_ms(500);
/*Then set it high for 500ms*/
SET(PORTD,7);
_delay_ms(500);
/*Before repeating the process forever...*/
}
}
Stack Implementation with data hiding
/**@file stack.h
@brief This header file contains the public types, variables and methods associated with the stack implementation in stack.c. None of the internal implementation details are exposed. This allows the implementation to vary while the public interface remains the same.
@author Stephen Friederichs
@date 4/28/13
@note This compiles in Cygwin using GCC 4.5.3
*/
#ifndef __STACK_H__
#define __STACK_H__
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include stdlib.h
@brief Include stdlib for malloc and free definition
*/
#include <stdlib.h>
/**@include stddef.h
@brief Include stddef.h for definition of size_t
*/
#include <stddef.h>
/**@typedef stack
@brief Define the type for a stack handle
There are two fundamental aspects of data hiding in C used here.
The first is that you can define a type as a pointer to a struct WITHOUT
having defined the struct. The struct is defined in the source file alone
and no implementation details are exposed in the header file.
The second aspect of this typedef is that stack_t is defined as a
pointer to a const st_stack struct. Const correctness is tricky in C but
for this usage the stack_t type points to a constant struct - changes to the
struct are NOT ALLOWED when the stack_t type is used.
Is this tough security? No. This only ensures the compiler complains if
someone tries to dereference a stack_t type and mess with the data inside
(of course, they don't know what any of the data inside the struct IS due
to the fact it's hidden in the source file). An unscrupulous person could
cast to a void pointer and do whatever they want with it. Or edit the
header file to remove the const. And of course, if they have the source they
know exactly what's inside the struct and can do whatever they want.
Definitely read the Wikipedia article on const correctness to get this all
straight in your head: http://en.wikipedia.org/wiki/Const-correctness
*/
typedef const struct st_stack * stack_t;
/**@typedef stack_element_t
@brief Define the type of the stack elements - bytes in this case
*/
typedef uint8_t stack_element_t;
/**@fn stack_init
@brief Initializes the stack and returns a pointer to the stack struct
@param[in] size The number of elements that can be stored on the stack
@return A pointer to the stack or NULL if the initialization failed.
*/
stack_t stack_init(size_t size);
/**@fn stack_push
@brief Push an element on to the stack
@param[in] stack A pointer to the stack to which we are pushing data
@param[in] element The data to push to the stack
@return Status of the call
@retval -1 The supplied pointer doesn't point to a stack
@retval -2 The stack is full
@retval 0 The call succeeded
*/
int stack_push(stack_t stack, stack_element_t element);
/**@fn stack_pop
@brief Remove an element from the stack
@param[in] element Pointer to an element variable to hold the received data
@note The element argument is a const pointer to an element. This means that the function will not change the address of the pointer, but the value of the element can change (this is the entire point of the function call).
@return Status of the call
@retval -1 Call failed - not a valid stack
@retval -2 Call failed - stack empty
@retval 0 Call succeeded
*/
int stack_pop(stack_element_t const * element);
/**@fn stack_destroy
@brief This stack no longer pleases me and I wish it gone. Or the program is exiting. Either way, free the memory associated with the stack.
@param[in] stack The stack which should no longer exist.
@return Status of the call
@retval -1 Call failed - not a valid stack
@retval 0 Call succeeded
*/
int stack_destroy(stack_t stack);
#endif
/*---------------------------------------------------------------------------*/
#ifdef STACK_IMPLEMENTATION_1
/**@file stack.c
@brief This file implements a basic stack in C but uses C's scope system and typing to hide the internal implementation of the stack and only allow to publicly-advertised functions and variables. This stack implementation uses an array to hold the data and grows up.
@note Implementation 1
@author Stephen Friederichs
@date 4/20/13
*/
/**@include stack.h
@brief stack.h contains all of the types and includeds that allow this stack implementation uses.
*/
/* This file doesn't actually exist - it's all of the above definitions
To avoid errors, comment it out
*/
//#include <stack.h>
/**@def STACK_CANARY_VALUE
@brief Value that the canary in the stack struct must be set to for the stack to be considered a value stack object
*/
#define STACK_CANARY_VALUE 0x31
/**@struct st_stack
@brief Struct containing the internal variables for the stack implementation
*/
struct st_stack
{
uint8_t canary; /**< A value that will be initialized to a specific value to show signify that the pointer points to a stack and that the stack is a valid stack object. This can't protect against any malicious intent but should at least serve as an indication that someone might have tried to modify the internals of the stack object itself*/
stack_element_t * array;/**< Pointer to the array where the stack data is stored*/
size_t head; /**< Index of the most recently added element in the stack*/
size_t size; /**< The maximum size of the stack*/
};
/**@fn _stack_valid
@brief Returns 1 if the stack object is valid
@param[in] stack Pointer to the stack
@return Validity of the object
@retval 1 Valid object
@retval 0 Invalid object
@note This function can only be called from within this file.
*/
static int _stack_valid( stack_t stack)
{
return (STACK_CANARY_VALUE == stack->canary)?1:0;
}
/**@fn stack_init
See above
*/
stack_t stack_init(size_t size)
{
struct st_stack * new_stack = malloc(sizeof(st_stack));
if(NULL == new_stack)
{
return NULL;
}
new_stack->array = malloc(sizeof(st_element)*size));
if(NULL == new_stack->array)
{
/* Allocation of the array failed, so free the memory associated with the stack
object before returning
*/
free(new_stack);
return NULL;
}
new_stack->head = 0; /* This stack grows up so it starts at element 0*/
new_stack->size = size
new_stack->canary = STACK_CANARY_VALUE; /* Initialize the stack's canary
to the appropriate value*/
/* Return a pointer to the new stack object - appropriately cast
to the const type to avoid warnings
*/
return (stack_t)new_stack;
}
/**@fn stack_push
See above
*/
int stack_push(stack_t stack, stack_element_t element)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Object is not a stack*/
}
if(stack->head == (stack->size-1))
{
return -2; /* Stack is full*/
}
/* All checks passed, add element*/
stack_pointer->array[++head] = element;
return 0;
}
/**@fn stack_pop
See above
*/
int stack_pop(stack_t stack, stack_element const * element)
{
stack_element popped_element;
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack * stack_pointer = (st_stack*)stack;
if(!_stack_valid(stack))
{
return -1; /* Pointer doesn't point to a stack*/
}
/* Check to see if the stack is empty*/
if(0 == stack->head)
{
return -2; /* Stack is empty, cannot pop*/
}
*popped_element = stack->array[stack_pointer->head--];
return 0;
}
/**@fn stack_destroy
See above
*/
int stack_destroy(stack_t stack)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack stack_pointer = (st_stack*)stack;
if(!_stack_valid(stack))
{
return -1; /* Signal failure - not a stack object*/
}
/* Clear the canary - if the pointer to this struct is reused after the
stack is destroyed, the canary will be invalid and the call wil fail
*/
stack_pointer->canary = 0x00;
free(stack->array);
free(stack);
return 0;
}
/* Don't allow the use of the STACK_CANARY_VALUE outside of this vile*/
#undef STACK_CANARY_VALUE
#else //STACK_IMPLEMENTATION_2
/**@file stack.c
@brief This file implements a basic stack in C but uses C's scope system and typing to hide the internal implementation of the stack and only allow to publicly-advertised functions and variables. This stack implementation uses an array to hold the data and grows down.
@note Implementation 2
@author Stephen Friederichs
@date 4/20/13
*/
/**@include stack.h
@brief stack.h contains all of the types and includes that allow this stack implementation uses.
*/
/* This file doesn't actually exist - it would if this weren't one huge file
So comment this out to ensure no compilation errors
*/
//#include <stack.h>
/**@def STACK_CANARY_VALUE
@brief Value that the canary in the stack struct must be set to for the stack to be considered a value stack object
*/
#define STACK_CANARY_VALUE 0x32
/**@struct st_stack
@brief Struct containing the internal variables for the stack implementation
*/
struct st_stack
{
uint8_t canary; /**< A value that will be initialized to a specific value to show signify that the pointer points to a stack and that the stack is a valid stack object. This won't protect against any truly malicious intent but might indicate that someone tried to modify the internals of the object themselves.*/
stack_element_t * array; /**< Pointer to the array where the stack data is stored*/
size_t head; /**< Index of the most recently added element in the stack*/
size_t size; /**< The maximum size of the stack*/
};
/**@fn _stack_valid
@brief Returns 1 if the stack object is valid
@param[in] stack Pointer to the stack
@return Validity of the object
@retval 1 Valid object
@retval 0 Invalid object
@note This function can only be called from within this file.
*/
static int _stack_valid( stack_t stack)
{
/* Ensure we don't try to dereference a NULL pointer
Obviously if the pointer is NULL it's not a valid stack
*/
if(NULL == stack)
{
return 0;
}
return (STACK_CANARY_VALUE == stack->canary)?1:0;
}
/**@fn stack_init
See above
*/
stack_t stack_init(size_t size)
{
struct st_stack * new_stack = malloc(sizeof(st_stack));
if(NULL == new_stack)
{
return NULL;
}
new_stack->array = malloc(sizeof(st_element)*size));
if(NULL == new_stack->array)
{
/* Allocation failed, so free the memory associated with the stack
object before returning
*/
free(new_stack);
return NULL;
}
new_stack->head = size; /* This stack grows down so it starts at the
highest element*/
new_stack->size = size
new_stack->canary = STACK_CANARY_VALUE;
return (stack_t)new_stack;
}
/**@fn stack_push
See above
*/
int stack_push(stack_t stack, stack_element_t element)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Object is not a stack*/
}
if(0 == stack->head)
{
return -2; /* Stack is full*/
}
/* All checks passed, add element*/
stack_pointer->array[--head] = element;
/* Return success*/
return 0;
}
/**@fn stack_pop
See above
*/
int stack_pop(stack_t stack, stack_element const * element)
{
stack_element popped_element;
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer so we can modify
the head variable.
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Pointer doesn't point to a stack*/
}
/* Check to see if the stack is empty*/
if(stack->size == stack->head)
{
return -2; /* Stack is empty, cannot pop*/
}
*popped_element = stack->array[stack_pointer->head--];
/* Signal success*/
return 0;
}
/**@fn stack_destroy
See above
*/
int stack_destroy(stack_t stack)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer so the canary can
be cleared later
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Signal failure - not a stack object*/
}
/* Clear the canary - if the pointer to this struct is reused after the
stack is destroyed, the canary will be invalid and the call wil fail
*/
stack_pointer->canary = 0x00;
free(stack->array);
free(stack);
/* Return success*/
return 0;
}
/* Don't allow the use of the STACK_CANARY_VALUE outside of this vile*/
#undef STACK_CANARY_VALUE
#endif
Simple Bit Manipulation Macros
/* Basic bit manipulation macros
No one should ever have to rewrite these
*/
//Set bit y (0-indexed) of x to '1' by generating a a mask with a '1' in the proper bit location and ORing x with the mask.
#define SET(x,y) x |= (1 << y)
//Set bit y (0-indexed) of x to '0' by generating a mask with a '0' in the y position and 1's elsewhere then ANDing the mask with x.
#define CLEAR(x,y) x &= ~(1<< y)
//Return '1' if the bit value at position y within x is '1' and '0' if it's 0 by ANDing x with a bit mask where the bit in y's position is '1' and '0' elsewhere and comparing it to all 0's. Returns '1' in least significant bit position if the value of the bit is '1', '0' if it was '0'.
#define READ(x,y) ((0u == (x & (1<<y)))?0u:1u)
//Toggle bit y (0-index) of x to the inverse: '0' becomes '1', '1' becomes '0' by XORing x with a bitmask where the bit in position y is '1' and all others are '0'.
#define TOGGLE(x,y) (x ^= (1<<y))
LED Blinker Using a Timer/Counter
/**@file timer_blinker.c
@brief A more advanced LED blinker using a timer
@author Stephen Friederichs
@date 3/28/13
@note This code assumes that the LED is active high (pin sources current)
*/
/**@def F_CPU
@brief Clock frequency = 8MHZ - this is set by fuses and registers, not by this define
@note Always define this before including delay.h!
*/
#define F_CPU 8000000
/**@include io.h
@brief Include for AVR I/O register definitions
*/
#include <avr/io.h>
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include delay.h
@brief Include for delay functions such as _delay_ms() and _delay_us()
*/
#include <util/delay.h>
/* Basic bit manipulation macros - everyone should use these. Please, steal these! Don't not use them and
don't rewrite them yourself!
*/
#define SET(x,y) x |= (1 << y)
#define CLEAR(x,y) x &= ~(1<< y)
#define READ(x,y) ((0x00 == ((x & (1<<y))>> y))?0x00:0x01)
#define TOGGLE(x,y) (x ^= (1<<y))
int main(void)
{
/*Initialization Code*/
/* ATMega328 Datasheet Table 14-1 Pg 78
Configure PD7 for use as Heartbeat LED
Set as Output Low (initially)
*/
SET(DDRD,7); //Direction: output
CLEAR(PORTD,7); //State: Lo
/* TCCR1A - ATMega328 Datasheet Section 16.11.1 pg 132
No waveform generation is required on this timer, so set all
ports to normal operation
*/
TCCR1A = 0x00;
/* TCCR1C - ATMega328 Datasheet Section 16.11.3 pg 135
This register is only used for output compare.
There's no output compare in this application so this can be all 0's
*/
TCCR1C = 0x00;
/* TCCR1B
Note: I've disabled the CKDIV8 fuse so that the clock source is 8MHz
ATMega328 Datasheet Section 16.11.2 pg 134 - TCCR1A
No input capture used - bits 7:6 are 0
No waveform generation used - bits 4:3 are 0
Clock source select is bits 2:0 but are not yet set - wait until the
main loop is ready to start
As per ATMega328 Datasheet Section 16.9.1 page 123, setting the timer
to Normal mode causes the counter to count up until it reaches 0xFFFF
at which point it will overrun and start back at 0. To configure this
timer/counter to produce a period of 500ms we need to start counting
at a value that causes it to reach 65535 in 500ms.
What is that value?
With a clock prescaler of 256 each count of the timer is roughly
(1/8MHz)*256 = 32uS
500ms / 32us /tick = 15625 ticks /500ms
The counter counts up to 65535, so to determine what value we have to
start at we subtract 15635 from 65536:
65536-15625 = 49910
*/
#define TIMER1_PERIOD 49910
TCNT1 = TIMER1_PERIOD;
/* Flash the LED for a second to show that initialization has successfully
occurred
*/
SET(PORTD,7);
_delay_ms(1000);
CLEAR(PORTD,7);
/* Start the timer/counter
ATMega328 Datasheet Section 16.11.2 Pg 135 - TCCR1B
No Waveform generation: bits 4:3 = 0
No input capture: bits 7:6 = 0
Clock select: ClkIO/256 - bits 2:0 = 100b = 0x04
*/
TCCR1B = 0x04; //This starts the counter/timer
while(1)
{
/* Handle the Heartbeat LED
When the timer/counter reaches 65535 the 500ms period will have
elapsed and TIFR1 bit 1 will be '1'
*/
if(READ(TIFR1,0))
{
/* ATMega328 Datasheet Section 16.11.9 pg137
Setting TIFR1 bit 1 clears the overflow flag
*/
SET(TIFR1,0);
/* Toggle the LED to flash it at 1Hz*/
TOGGLE(PORTD,7);
/* Reload the timer/counter count value to the previous value
so that the period remains the same
*/
TCNT1 = TIMER1_PERIOD;
}
/* Now you can do useful work here - no delay loops!*/
}
}
Plotting 16-bit binary data
%Script to load and plot 16-bit accelerometer data
printf "Running acceleration data analysis script\r\n"
clear * %Clear all variables
ts = (1/1000); %1KHz sampling rate
%Path to TXT file with accelerometer samples
accel_data_path = "accel_data.txt";
%Open the acceleration data file as read-only, binary mode
file_accel_data = fopen(accel_data_path,"rb");
%Read unit16 samples from TXT file into an array
%count is # of samples, val is array of values
[val,count] = fread(file_accel_data,Inf,"uint16");
fclose(file_accel_data);
%Generate a time vector from t=0 to the end determined by count and sampling time
tmax = (count-1)*ts;
t=0:ts:tmax;
%Open figure 1
figure(1)
%Plot accelerometer samples
plot(t,val','1')
%Make the plot look pretty
title("Raw Sampled Accelerometer Data")
xlabel("Time (s)")
ylabel("Accelerometer Data")
%Save the plot to disk
print("plots/raw_accel_data.png")
Plotting 16-bit binary data
%Script to load and plot 16-bit accelerometer data
printf "Running acceleration data analysis script\r\n"
clear * %Clear all variables
ts = (1/1000); %1KHz sampling rate
%Path to TXT file with accelerometer samples
accel_data_path = "accel_data.txt";
%Open the acceleration data file as read-only, binary mode
file_accel_data = fopen(accel_data_path,"rb");
%Read unit16 samples from TXT file into an array
%count is # of samples, val is array of values
[val,count] = fread(file_accel_data,Inf,"uint16");
fclose(file_accel_data);
%Generate a time vector from t=0 to the end determined by count and sampling time
tmax = (count-1)*ts;
t=0:ts:tmax;
%Open figure 1
figure(1)
%Plot accelerometer samples
plot(t,val','1')
%Make the plot look pretty
title("Raw Sampled Accelerometer Data")
xlabel("Time (s)")
ylabel("Accelerometer Data")
%Save the plot to disk
print("plots/raw_accel_data.png")
10-bit A/D Data Sampling and Transmission
/**@file endianness.c
@brief Code to transmit 16-bit ADC samples in big or little-endian order
@author Stephen Friederichs
@date 5/12/13
ADC Channels:
0 - Accelerometer X axis (Vertical)
1 - Accelerometer Y axis (Horizontal)
2 - Accelerometer Z axis (Lateral)
3 - Accelerometer 0G detect (Freefall detect)
The heartbeat LED is on Port D, pin 7
*/
/**@def F_CPU
@brief Clock frequency = 8MHZ - this is set by fuses and registers, not by this define
@note Always define this before including delay.h!
*/
#define F_CPU 8000000
/**@include io.h
@brief Include for AVR I/O register definitions
*/
#include <avr/io.h>
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include delay.h
@brief Include for delay functions such as _delay_ms() and _delay_us()
*/
#include <util/delay.h>
/* Basic bit manipulation macros - everyone should use these. Please, steal these! Don't not use them and
don't rewrite them yourself!
*/
#define SET(x,y) x |= (1 << y)
#define CLEAR(x,y) x &= ~(1<< y)
#define READ(x,y) ((0x00 == ((x & (1<<y))>> y))?0x00:0x01)
#define TOGGLE(x,y) (x ^= (1 << y))
int main(void)
{
//Variable to count the number of times the timer interrupt has fired
uint16_t ticks = 0;
uint16_t accel_data = 0;
uint8_t transmit_enable = 0x00;
uint8_t * uart_data_pointer = &accel_data;
/*Initialization Code*/
/* ATMega328 Datasheet Table 14-1 Pg 78
Configure PD7 for use as Heartbeat LED
Set as Output Low (initially)
*/
SET(DDRD,7); //Direction: output
CLEAR(PORTD,7); //State: Lo
/* TCCR1A - ATMega328 Datasheet Section 16.11.2 pg 134 - TCCR1A
No input capture used - bits 7:6 are 0
No waveform generation used - bits 4:3 are 0
Clock source select is bits 2:0 but are not yet set - wait until the
main loop is ready to start
*/
TCCR1A = 0x00;
/* TCCR1C - ATMega328 Datasheet Section 16.11.3 pg 135
This register is only used for output compare.
There's no output compare in this application so this can be all 0's
*/
TCCR1C = 0x00;
/* TCCR1B
Note: I've disabled the CKDIV8 fuse so that the clock source is 8MHz
As per ATMega328 Datasheet Section 16.9.1 page 123, setting the timer
to Normal mode causes the counter to count up until it reaches 0xFFFF
at which point it will overrun and start back at 0. To configure this
timer/counter to produce a period of 1ms we need to start counting
at a value that causes it to reach 65535 in 1ms.
What is that value?
With a clock prescaler of 32 each count of the timer is roughly
(1/8MHz)*32 = 1uS
1ms / 1us /tick = 1000 ticks /ms
The counter counts up to 65536, so to determine what value we have to
start at we subtract 1000 from 65536:
65536-1000 = 64536
*/
#define TIMER1_PERIOD 64536
TCNT1 = TIMER1_PERIOD;
//Configure ADC to read accelerometer data
//ATMega328 - Section 24.9.1 Pg 254 - ADMUX Register
/*ADC result - left-adjusted (Bit 5). The ADC result is 10-bits wide.
In practice, the least-significant 2 bits are often too noisy to be
of any use, so they are discarded. To support this, the ATMega328P is
capable of storing the upper eight bits of the ADC result in the
ADCH register alone. In this case, I want all 10 bits of the data
so I can show how to handle endianness in serial transmissions. As
a result, the most significant two bits are stored in ADCH and the least
significant 8 are stored in ADCL.
*/
/*ADC Channel - I only care about one - the Y axis on the accelerometer
which is channel 1.*/
ADMUX = (0x01 << 6) /*Reference - AVCC - 5V. */
|(0x00 << 5) /* Right-adjust ADC result - refer to
Section 24.9.3.2 pg 256*/
|(0x01 << 0); /*Channel set to X-Axis output on
accelerometer*/
/* ATMega328 Datasheet - Section 24.9.2 - ADCSRA - ADC Status
and Control Register
ADCEN - Bit 7 - Enable ADC - Obviously set this to 1
ADCSC - Bit 6 - Start Converstion - Not yet: 0
ADATE - Bit 5 - Auto-trigger ADC - I'll be manually triggering
the ADC, so 0
ADCIF - Bit 4 - ADC Interrupt Flag - Set when conversion
completes. Ignore.
ADCIE - Bit 3 - ADC Interrupt Enable - Everything will be polled
for this, so 0
ADPS - Bits 2:0 - ADC Prescaler
*/
/*ATMega328 Section 24.4 Pg245 discusses what the prescaler should be set to:
By default, the successive approximation circuitry requires an input clock
frequency between 50kHz and 200kHz to get maximum resolution.
The ClkIO is 8MHz and the prescaler options are 2,4,8,16,32,64 and 128.
1MHz/8 = ~125KHz, so that seems good. That value is 3
*/
ADCSRA = (0x01 << 7) //Enable ADC
|(0x03); //Set prescaler to 1/8 ClkIO - 125KHz
/* ATMega328 Datasheet Section 24.9.5 Pg 257 - DIDR0
This register allows digital input buffers on ADC pins to be
disabled. This saves power, so I'll do it
*/
DIDR0 = 0x01; //Turn off digital filtering on ADC channel 0
//Configure UART for 38400 8N1 Tx Communication
//Step 1 - Baud rate
/* ATMega328 Datasheet Section 20.10 - Table 20-6 pg 192
Baud rate settings for fosc of 8MHZ
Choosing baud rate of 38.4K for minimum error
U2Xn = 0 - Use standard (not double) data rate
UBRRn = 12
*/
UBRR0 = 12;
/* UCSR0A - UART 0 Control and Status Register A
ATMega328 Datasheet Section 20.11.2 pg 194
Bits 7:2 - Status bits
Bit 1 - Double UART transmission speed - No: 0
Bit 0 - Multi-Processor Communication Mode - No:0
*/
UCSR0A = 0x00;
/* UCSR0B - UART 0 Control and Status Register B
ATMega328 Datasheet Section 20.11.3 pg
Bit 7 - Rx Complete Interrupt Enable - 0
Bit 6 - Tx Complete Interrupt Enable - 0
Bit 5 - USART Data Register Empty interrupt enable - 0
Bit 4 - Receiver Enable - Set to 1
Bit 3 - Transmitter Enable - Set to 1
Bit 2 - Character Size Bit 2 - Set to 0 for 8 bits
Bit 1 - 9th receive bit - Ignore
Bit 0 - 9th transmit bit - Ignore
*/
UCSR0B = 0x00 | (1 << 3)
| (1 << 4);
/* UCSR0C - UART 0 Control and Status Register C
ATMega328 Datasheet Section 20.11.4 - Pg 196
Bits 7:6 - Set to asynchronous (clockless) mode: 00
Bits 5:4 - Parity setting - None : 00
Bit 3 - Stop select - 1 : 0
Bit 2:1 - Character size - 8 : 11
Bit 0 - Clock polarity: Don't care : 0
*/
UCSR0C = 0x03 << 1;
//Send a known pattern upon startup to verify the UART works
UDR0 = 0xA5;
//Wait until transmit is complete
while(0x00 == READ(UCSR0A,6));
UDR0 = 0x5A;
while(0x00 == READ(UCSR0A,6));
UDR0 = 0xA5;
//Wait until transmit is complete
while(0x00 == READ(UCSR0A,6));
/* Flash the LED for a second to show that initialization has successfully
occurred
*/
SET(PORTD,7);
_delay_ms(1000);
CLEAR(PORTD,7);
/* Start the timer/counter
ATMega328 Datasheet Section 16.11.2 Pg 135 - TCCR1B
No Waveform generation: bits 4:3 = 0
No input capture: bits 7:6 = 0
Clock select: ClkIO/8 - bits 2:0 = 010b = 0x02
*/
TCCR1B = 0x02; //This starts the counter/timer
while(1)
{
/* Timer overflow - Reading the accelerometer at a 1KHz rate
and flash the heartbeat LED at a reasonable period as well
*/
if(READ(TIFR1,0))
{
/* ATMega328 Datasheet Section 16.11.9 pg137
Setting TIFR1 bit 1 clears the overflow flag
*/
SET(TIFR1,0);
/* Reload the timer/counter count value to the
previous value so that the period remains the same
*/
TCNT1 = TIMER1_PERIOD;
//Read accelerometer data via ADC
SET(ADCSRA,6); //Start ADC conversion
/* Wait until conversion finishes - this should never
be more than 25*(8000000/8)^-1 seconds, which is
about 25us. Typical measured time is ~14.5us
*/
while(0x00 == READ(ADCSRA,4));
SET(ADCSRA,4); //Clear the interrupt flag by setting it to 1
//Clear acceleration data variable before loading new value
accel_data = 0;
/* When reading the full 10-bits from the ADC the
lower register must be read first
*/
accel_data |= (uint16_t)ADCL;
//Then the upper 2 bits
accel_data |= (uint16_t)(ADCH << 8);
/* Transmission of data is toggled by transmitting a
'0' (0x30) byte over serial
*/
if(0x01 == (READ(UCSR0A,7)))
{
if(0x30 == UDR0)
{
transmit_enable =
(0x00 == transmit_enable?0xFF:0x00);
}
}
if(0xFF == transmit_enable)
{
#ifdef BIG_ENDIAN
//Send high byte...
UDR0 = uart_data_pointer[1];
while(0x00 == READ(UCSR0A,6));
//...then low byte
UDR0 = uart_data_pointer[0];
while(0x00 == READ(UCSR0A,6));
#else
//Send low byte...
UDR0 = uart_data_pointer[0];
while(0x00 == READ(UCSR0A,6));
//...then high byte
UDR0 = uart_data_pointer[1];
while(0x00 == READ(UCSR0A,6));
#endif
}
//Blink Heartbeat LED
/*
The timer period is 1ms. To keep everything simple the LED will toggle
every 512 ticks - roughly every .5s.
*/
ticks++;
//If true, the current ticks is a multiple of 512
//So blink the heartbeat LED
if(0x8000 == (ticks << 7))
{
TOGGLE(PORTD,7);
}
}
//Main Loops
}
}
Stack Implementation with data hiding
/**@file stack.h
@brief This header file contains the public types, variables and methods associated with the stack implementation in stack.c. None of the internal implementation details are exposed. This allows the implementation to vary while the public interface remains the same.
@author Stephen Friederichs
@date 4/28/13
@note This compiles in Cygwin using GCC 4.5.3
*/
#ifndef __STACK_H__
#define __STACK_H__
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include stdlib.h
@brief Include stdlib for malloc and free definition
*/
#include <stdlib.h>
/**@include stddef.h
@brief Include stddef.h for definition of size_t
*/
#include <stddef.h>
/**@typedef stack
@brief Define the type for a stack handle
There are two fundamental aspects of data hiding in C used here.
The first is that you can define a type as a pointer to a struct WITHOUT
having defined the struct. The struct is defined in the source file alone
and no implementation details are exposed in the header file.
The second aspect of this typedef is that stack_t is defined as a
pointer to a const st_stack struct. Const correctness is tricky in C but
for this usage the stack_t type points to a constant struct - changes to the
struct are NOT ALLOWED when the stack_t type is used.
Is this tough security? No. This only ensures the compiler complains if
someone tries to dereference a stack_t type and mess with the data inside
(of course, they don't know what any of the data inside the struct IS due
to the fact it's hidden in the source file). An unscrupulous person could
cast to a void pointer and do whatever they want with it. Or edit the
header file to remove the const. And of course, if they have the source they
know exactly what's inside the struct and can do whatever they want.
Definitely read the Wikipedia article on const correctness to get this all
straight in your head: http://en.wikipedia.org/wiki/Const-correctness
*/
typedef const struct st_stack * stack_t;
/**@typedef stack_element_t
@brief Define the type of the stack elements - bytes in this case
*/
typedef uint8_t stack_element_t;
/**@fn stack_init
@brief Initializes the stack and returns a pointer to the stack struct
@param[in] size The number of elements that can be stored on the stack
@return A pointer to the stack or NULL if the initialization failed.
*/
stack_t stack_init(size_t size);
/**@fn stack_push
@brief Push an element on to the stack
@param[in] stack A pointer to the stack to which we are pushing data
@param[in] element The data to push to the stack
@return Status of the call
@retval -1 The supplied pointer doesn't point to a stack
@retval -2 The stack is full
@retval 0 The call succeeded
*/
int stack_push(stack_t stack, stack_element_t element);
/**@fn stack_pop
@brief Remove an element from the stack
@param[in] element Pointer to an element variable to hold the received data
@note The element argument is a const pointer to an element. This means that the function will not change the address of the pointer, but the value of the element can change (this is the entire point of the function call).
@return Status of the call
@retval -1 Call failed - not a valid stack
@retval -2 Call failed - stack empty
@retval 0 Call succeeded
*/
int stack_pop(stack_element_t const * element);
/**@fn stack_destroy
@brief This stack no longer pleases me and I wish it gone. Or the program is exiting. Either way, free the memory associated with the stack.
@param[in] stack The stack which should no longer exist.
@return Status of the call
@retval -1 Call failed - not a valid stack
@retval 0 Call succeeded
*/
int stack_destroy(stack_t stack);
#endif
/*---------------------------------------------------------------------------*/
#ifdef STACK_IMPLEMENTATION_1
/**@file stack.c
@brief This file implements a basic stack in C but uses C's scope system and typing to hide the internal implementation of the stack and only allow to publicly-advertised functions and variables. This stack implementation uses an array to hold the data and grows up.
@note Implementation 1
@author Stephen Friederichs
@date 4/20/13
*/
/**@include stack.h
@brief stack.h contains all of the types and includeds that allow this stack implementation uses.
*/
/* This file doesn't actually exist - it's all of the above definitions
To avoid errors, comment it out
*/
//#include <stack.h>
/**@def STACK_CANARY_VALUE
@brief Value that the canary in the stack struct must be set to for the stack to be considered a value stack object
*/
#define STACK_CANARY_VALUE 0x31
/**@struct st_stack
@brief Struct containing the internal variables for the stack implementation
*/
struct st_stack
{
uint8_t canary; /**< A value that will be initialized to a specific value to show signify that the pointer points to a stack and that the stack is a valid stack object. This can't protect against any malicious intent but should at least serve as an indication that someone might have tried to modify the internals of the stack object itself*/
stack_element_t * array;/**< Pointer to the array where the stack data is stored*/
size_t head; /**< Index of the most recently added element in the stack*/
size_t size; /**< The maximum size of the stack*/
};
/**@fn _stack_valid
@brief Returns 1 if the stack object is valid
@param[in] stack Pointer to the stack
@return Validity of the object
@retval 1 Valid object
@retval 0 Invalid object
@note This function can only be called from within this file.
*/
static int _stack_valid( stack_t stack)
{
return (STACK_CANARY_VALUE == stack->canary)?1:0;
}
/**@fn stack_init
See above
*/
stack_t stack_init(size_t size)
{
struct st_stack * new_stack = malloc(sizeof(st_stack));
if(NULL == new_stack)
{
return NULL;
}
new_stack->array = malloc(sizeof(st_element)*size));
if(NULL == new_stack->array)
{
/* Allocation of the array failed, so free the memory associated with the stack
object before returning
*/
free(new_stack);
return NULL;
}
new_stack->head = 0; /* This stack grows up so it starts at element 0*/
new_stack->size = size
new_stack->canary = STACK_CANARY_VALUE; /* Initialize the stack's canary
to the appropriate value*/
/* Return a pointer to the new stack object - appropriately cast
to the const type to avoid warnings
*/
return (stack_t)new_stack;
}
/**@fn stack_push
See above
*/
int stack_push(stack_t stack, stack_element_t element)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Object is not a stack*/
}
if(stack->head == (stack->size-1))
{
return -2; /* Stack is full*/
}
/* All checks passed, add element*/
stack_pointer->array[++head] = element;
return 0;
}
/**@fn stack_pop
See above
*/
int stack_pop(stack_t stack, stack_element const * element)
{
stack_element popped_element;
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack * stack_pointer = (st_stack*)stack;
if(!_stack_valid(stack))
{
return -1; /* Pointer doesn't point to a stack*/
}
/* Check to see if the stack is empty*/
if(0 == stack->head)
{
return -2; /* Stack is empty, cannot pop*/
}
*popped_element = stack->array[stack_pointer->head--];
return 0;
}
/**@fn stack_destroy
See above
*/
int stack_destroy(stack_t stack)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack stack_pointer = (st_stack*)stack;
if(!_stack_valid(stack))
{
return -1; /* Signal failure - not a stack object*/
}
/* Clear the canary - if the pointer to this struct is reused after the
stack is destroyed, the canary will be invalid and the call wil fail
*/
stack_pointer->canary = 0x00;
free(stack->array);
free(stack);
return 0;
}
/* Don't allow the use of the STACK_CANARY_VALUE outside of this vile*/
#undef STACK_CANARY_VALUE
#else //STACK_IMPLEMENTATION_2
/**@file stack.c
@brief This file implements a basic stack in C but uses C's scope system and typing to hide the internal implementation of the stack and only allow to publicly-advertised functions and variables. This stack implementation uses an array to hold the data and grows down.
@note Implementation 2
@author Stephen Friederichs
@date 4/20/13
*/
/**@include stack.h
@brief stack.h contains all of the types and includes that allow this stack implementation uses.
*/
/* This file doesn't actually exist - it would if this weren't one huge file
So comment this out to ensure no compilation errors
*/
//#include <stack.h>
/**@def STACK_CANARY_VALUE
@brief Value that the canary in the stack struct must be set to for the stack to be considered a value stack object
*/
#define STACK_CANARY_VALUE 0x32
/**@struct st_stack
@brief Struct containing the internal variables for the stack implementation
*/
struct st_stack
{
uint8_t canary; /**< A value that will be initialized to a specific value to show signify that the pointer points to a stack and that the stack is a valid stack object. This won't protect against any truly malicious intent but might indicate that someone tried to modify the internals of the object themselves.*/
stack_element_t * array; /**< Pointer to the array where the stack data is stored*/
size_t head; /**< Index of the most recently added element in the stack*/
size_t size; /**< The maximum size of the stack*/
};
/**@fn _stack_valid
@brief Returns 1 if the stack object is valid
@param[in] stack Pointer to the stack
@return Validity of the object
@retval 1 Valid object
@retval 0 Invalid object
@note This function can only be called from within this file.
*/
static int _stack_valid( stack_t stack)
{
/* Ensure we don't try to dereference a NULL pointer
Obviously if the pointer is NULL it's not a valid stack
*/
if(NULL == stack)
{
return 0;
}
return (STACK_CANARY_VALUE == stack->canary)?1:0;
}
/**@fn stack_init
See above
*/
stack_t stack_init(size_t size)
{
struct st_stack * new_stack = malloc(sizeof(st_stack));
if(NULL == new_stack)
{
return NULL;
}
new_stack->array = malloc(sizeof(st_element)*size));
if(NULL == new_stack->array)
{
/* Allocation failed, so free the memory associated with the stack
object before returning
*/
free(new_stack);
return NULL;
}
new_stack->head = size; /* This stack grows down so it starts at the
highest element*/
new_stack->size = size
new_stack->canary = STACK_CANARY_VALUE;
return (stack_t)new_stack;
}
/**@fn stack_push
See above
*/
int stack_push(stack_t stack, stack_element_t element)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Object is not a stack*/
}
if(0 == stack->head)
{
return -2; /* Stack is full*/
}
/* All checks passed, add element*/
stack_pointer->array[--head] = element;
/* Return success*/
return 0;
}
/**@fn stack_pop
See above
*/
int stack_pop(stack_t stack, stack_element const * element)
{
stack_element popped_element;
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer so we can modify
the head variable.
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Pointer doesn't point to a stack*/
}
/* Check to see if the stack is empty*/
if(stack->size == stack->head)
{
return -2; /* Stack is empty, cannot pop*/
}
*popped_element = stack->array[stack_pointer->head--];
/* Signal success*/
return 0;
}
/**@fn stack_destroy
See above
*/
int stack_destroy(stack_t stack)
{
/* The passed pointer is a pointer to a const stack,
so generate a non-const pointer so the canary can
be cleared later
*/
st_stack * stack_pointer = (st_stack *)stack;
if(!_stack_valid(stack))
{
return -1; /* Signal failure - not a stack object*/
}
/* Clear the canary - if the pointer to this struct is reused after the
stack is destroyed, the canary will be invalid and the call wil fail
*/
stack_pointer->canary = 0x00;
free(stack->array);
free(stack);
/* Return success*/
return 0;
}
/* Don't allow the use of the STACK_CANARY_VALUE outside of this vile*/
#undef STACK_CANARY_VALUE
#endif
LED Blinker Using a Timer/Counter
/**@file timer_blinker.c
@brief A more advanced LED blinker using a timer
@author Stephen Friederichs
@date 3/28/13
@note This code assumes that the LED is active high (pin sources current)
*/
/**@def F_CPU
@brief Clock frequency = 8MHZ - this is set by fuses and registers, not by this define
@note Always define this before including delay.h!
*/
#define F_CPU 8000000
/**@include io.h
@brief Include for AVR I/O register definitions
*/
#include <avr/io.h>
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include delay.h
@brief Include for delay functions such as _delay_ms() and _delay_us()
*/
#include <util/delay.h>
/* Basic bit manipulation macros - everyone should use these. Please, steal these! Don't not use them and
don't rewrite them yourself!
*/
#define SET(x,y) x |= (1 << y)
#define CLEAR(x,y) x &= ~(1<< y)
#define READ(x,y) ((0x00 == ((x & (1<<y))>> y))?0x00:0x01)
#define TOGGLE(x,y) (x ^= (1<<y))
int main(void)
{
/*Initialization Code*/
/* ATMega328 Datasheet Table 14-1 Pg 78
Configure PD7 for use as Heartbeat LED
Set as Output Low (initially)
*/
SET(DDRD,7); //Direction: output
CLEAR(PORTD,7); //State: Lo
/* TCCR1A - ATMega328 Datasheet Section 16.11.1 pg 132
No waveform generation is required on this timer, so set all
ports to normal operation
*/
TCCR1A = 0x00;
/* TCCR1C - ATMega328 Datasheet Section 16.11.3 pg 135
This register is only used for output compare.
There's no output compare in this application so this can be all 0's
*/
TCCR1C = 0x00;
/* TCCR1B
Note: I've disabled the CKDIV8 fuse so that the clock source is 8MHz
ATMega328 Datasheet Section 16.11.2 pg 134 - TCCR1A
No input capture used - bits 7:6 are 0
No waveform generation used - bits 4:3 are 0
Clock source select is bits 2:0 but are not yet set - wait until the
main loop is ready to start
As per ATMega328 Datasheet Section 16.9.1 page 123, setting the timer
to Normal mode causes the counter to count up until it reaches 0xFFFF
at which point it will overrun and start back at 0. To configure this
timer/counter to produce a period of 500ms we need to start counting
at a value that causes it to reach 65535 in 500ms.
What is that value?
With a clock prescaler of 256 each count of the timer is roughly
(1/8MHz)*256 = 32uS
500ms / 32us /tick = 15625 ticks /500ms
The counter counts up to 65535, so to determine what value we have to
start at we subtract 15635 from 65536:
65536-15625 = 49910
*/
#define TIMER1_PERIOD 49910
TCNT1 = TIMER1_PERIOD;
/* Flash the LED for a second to show that initialization has successfully
occurred
*/
SET(PORTD,7);
_delay_ms(1000);
CLEAR(PORTD,7);
/* Start the timer/counter
ATMega328 Datasheet Section 16.11.2 Pg 135 - TCCR1B
No Waveform generation: bits 4:3 = 0
No input capture: bits 7:6 = 0
Clock select: ClkIO/256 - bits 2:0 = 100b = 0x04
*/
TCCR1B = 0x04; //This starts the counter/timer
while(1)
{
/* Handle the Heartbeat LED
When the timer/counter reaches 65535 the 500ms period will have
elapsed and TIFR1 bit 1 will be '1'
*/
if(READ(TIFR1,0))
{
/* ATMega328 Datasheet Section 16.11.9 pg137
Setting TIFR1 bit 1 clears the overflow flag
*/
SET(TIFR1,0);
/* Toggle the LED to flash it at 1Hz*/
TOGGLE(PORTD,7);
/* Reload the timer/counter count value to the previous value
so that the period remains the same
*/
TCNT1 = TIMER1_PERIOD;
}
/* Now you can do useful work here - no delay loops!*/
}
}
Delay Loop LED Blinker
/**@file led_blink.c
@brief The most basic approach to blinking an LED on AVR microcontrollers - specifically the ATMega328P
@author Stephen Friederichs
@date 3/28/13
@note This code assumes that the LED is active high (pin sources current)
*/
/**@def F_CPU
@brief Clock frequency = 8MHZ - this is set by fuses and registers, not by this define
@note Always define this before including delay.h!
*/
#define F_CPU 8000000
/**@include io.h
@brief Include for AVR I/O register definitions
*/
#include <avr/io.h>
/**@include stdint.h
@brief Include for standard integer definitions (ie, uint8_t, int32_t, etc)
*/
#include <stdint.h>
/**@include delay.h
@brief Include for delay functions such as _delay_ms() and _delay_us()
*/
#include <util/delay.h>
/* Basic bit manipulation macros - everyone should use these. Please, steal these! Don't not use them and
don't rewrite them yourself!
*/
#define SET(x,y) x |= (1 << y)
#define CLEAR(x,y) x &= ~(1<< y)
#define READ(x,y) ((FALSE == ((x & (1<<y))>> y))?FALSE:TRUE)
#define TOGGLE(x,y) (x ^= (1<<y))
int main(void)
{
/*Initialization Code*/
/* ATMega328 Datasheet Table 14-1 Pg 78
Configure PD7 for use as Heartbeat LED
Set as Output Low (initially)
*/
SET(DDRD,7); //Direction: output
CLEAR(PORTD,7); //State: Lo
/* Flash the LED for a second to show that initialization has successfully
occurred
*/
SET(PORTD,7);
_delay_ms(1000);
CLEAR(PORTD,7);
while(1)
{
/*Set PD7 low for 500ms*/
CLEAR(PORTD,7);
_delay_ms(500);
/*Then set it high for 500ms*/
SET(PORTD,7);
_delay_ms(500);
/*Before repeating the process forever...*/
}
}
Simple Bit Manipulation Macros
/* Basic bit manipulation macros
No one should ever have to rewrite these
*/
//Set bit y (0-indexed) of x to '1' by generating a a mask with a '1' in the proper bit location and ORing x with the mask.
#define SET(x,y) x |= (1 << y)
//Set bit y (0-indexed) of x to '0' by generating a mask with a '0' in the y position and 1's elsewhere then ANDing the mask with x.
#define CLEAR(x,y) x &= ~(1<< y)
//Return '1' if the bit value at position y within x is '1' and '0' if it's 0 by ANDing x with a bit mask where the bit in y's position is '1' and '0' elsewhere and comparing it to all 0's. Returns '1' in least significant bit position if the value of the bit is '1', '0' if it was '0'.
#define READ(x,y) ((0u == (x & (1<<y)))?0u:1u)
//Toggle bit y (0-index) of x to the inverse: '0' becomes '1', '1' becomes '0' by XORing x with a bitmask where the bit in position y is '1' and all others are '0'.
#define TOGGLE(x,y) (x ^= (1<<y))