Generate callgraph

This commit is contained in:
Michael Hayes
2011-08-27 01:46:08 +00:00
commit 513810e138
233 changed files with 22993 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
This is a collection of modules to provide a hardware abstraction
layer for the AVR architecture.
delay.h --- provides timing delay routines.
eeprom.h --- provides routines for reading/writing EEPROM memory for non-volatile storage.
pacer.h --- provides routines for constructing paced loops using timer 1.
pio.h --- provides routines for accessing the I/O ports. Note pio.h uses
advanced techniques; I suggest looking at pio-simple.h and pio-simple.c for a much simpler implementation.
system.h --- system dependent definitions, such as CPU speed and pin allocations.
config.h --- common configuration definitions.
+90
View File
@@ -0,0 +1,90 @@
/** @file adc.c
@author Michael Hayes
@date 3 Feb 2005
@brief Routines to use AVR onboard ADC in polling mode.
*/
#include "system.h"
#include "adc.h"
#include "bits.h"
/* ADC Clock Prescaler : 2, 2, 4, 8, 16, 32, 64, 128 =
2^ADCCLKPRE. Prescale ADC clock by 64 (13 cycles * (64
prescaler) / 12MHz = 70 us conversion time). */
#define ADCCLKPRE 6
void adc_reference_select (adc_ref_mode_t mode)
{
BITS_INSERT (ADMUX, mode, 6, 7);
}
/* Initalise the ADC. */
void adc_init (uint8_t channels)
{
adc_reference_select (ADC_REF_AVCC);
/* Right adjust result. */
ADMUX &= ~BIT (ADLAR);
/* Enable ADC, prescale ADC clock by 64. */
ADCSRA |= BIT (ADEN) | BIT (ADPS2) | BIT (ADPS1);
#ifdef ADATE
/* Disable ADC auto trigger, disable ADC interrupt. */
ADCSRA &= ~ (BIT (ADATE) | BIT (ADIE) | BIT (ADPS0));
#else
ADCSRA &= ~ (BIT (ADIE) | BIT (ADPS0));
#endif
/* Disable digital input buffers for selected ADC channels. */
#ifdef DIDR0
DIDR0 = channels;
#endif
}
/* Starts a conversion in the ADC on the specified channel. */
bool adc_start (adc_channel_t channel)
{
if (channel >= ADC_NUM)
return 0;
BITS_INSERT (ADMUX, channel, 0, 3);
if (!adc_ready_p ())
return 0;
/* Start conversion. */
ADCSRA |= BIT (ADSC);
return 1;
}
/* Returns true if a conversion is not in progress. */
bool adc_ready_p (void)
{
return ! (ADCSRA & BIT (ADSC));
}
/** Read the current ADC value.
@return the ADC value.
@note this blocks until the ADC has finished a conversion. */
adc_sample_t
adc_read (void)
{
while (!adc_ready_p ())
continue;
return ADC;
}
/** Disables the ADC. */
void adc_disable (void)
{
/* Disable ADC, Disable ADC Interrupt. */
ADCSRA &= ~ (BIT (ADEN) | BIT (ADIE));
}
+63
View File
@@ -0,0 +1,63 @@
/** @file adc.h
@author M. P. Hayes, UCECE
@date 3 Feb 2005
@brief Routines to use AVR onboard ADC in both a polling mode
and automatically updating mode.
*/
#ifndef ADC_H
#define ADC_H
#include "system.h"
/** ADC channels. */
typedef enum {ADC0 = 0, ADC1, ADC2, ADC3, ADC4, ADC5, ADC6, ADC7, ADC_NUM}
adc_channel_t;
/** ADC reference modes. */
typedef enum {ADC_REF_EXT = 0, ADC_REF_AVCC, ADC_REF_RESERVED, ADC_REF_2V56}
adc_ref_mode_t;
/** ADC bits per conversion. */
enum {ADC_BITS = 10};
/** ADC sample size. */
typedef uint16_t adc_sample_t;
/** Select ADC reference mode. */
extern void
adc_reference_select (adc_ref_mode_t mode);
/** Initalises the ADC registers for polling operation. */
extern void
adc_init (uint8_t channels);
/** Starts a conversion in the ADC on the specified channel. */
extern bool
adc_conversion_start (adc_channel_t channel);
/** Returns true if a conversion is not in progress. */
extern bool
adc_ready_p (void);
/** Returns 1 if valid sample read. */
extern int8_t
adc_read (adc_sample_t *value);
/** Halts any currently running conversion. */
extern void
adc_stop (void);
/** Disables the ADC from doing anything. Requires reinitalisation. */
extern void
adc_disable (void);
#endif
+22
View File
@@ -0,0 +1,22 @@
/** @file bits.h
@author M. P. Hayes, UCECE
@date 15 May 2007
@brief Macros for manipulating bit fields.
*/
#ifndef BITS_H
#define BITS_H
#define BITS_MASK(first, last) ((1 << ((last) + 1)) - (1 << (first)))
#define BITS_CLEAR(reg, first, last) ((reg) &= BITS_MASK (first, last))
#define BITS_SET(reg, first, last) ((reg) |= BITS_MASK (first, last))
#define BITS_EXTRACT(reg, first, last) \
(((reg) & BITS_MASK (first, last)) >> (first))
#define BITS_INSERT(reg, val, first, last) \
(reg) = ((reg) & ~BITS_MASK (first, last)) \
| (((val) & BITS_MASK (0, last - first)) << (first))
#endif
+93
View File
@@ -0,0 +1,93 @@
/** @file delay.h
@author M. P. Hayes, UCECE
@date 08 June 2002
@brief Delay routines for timing.
@note All files that use DELAY_US must be compiled with full optimisation.
@defgroup delay Delay routines
This module provides routines for short timing delays. These are
implemented as delay loops. DELAY_US must not be called with a variable
argument since the run-time floating point calculation will almost certainly
swamp the delay you require.
@note The macro F_CPU defines the CPU clock frequency (in Hertz).
*/
#ifndef DELAY_H
#define DELAY_H
#include "system.h"
#include <util/delay.h>
/* There are two timing loop routines provided in delay.h:
_delay_loop_1 uses an 8-bit counter and requires 3 cycles per
iteration but has a maximum of 256 iterations. With F_CPU = 8e6
then the range of delays is 0.375 us to 96.0 us in steps of 0.375 us.
_delay_loop_2 uses a 16-bit counter, requires 4 cycles per
iteration and has a maximum of 65536 iterations. With F_CPU = 8e6
then the range of delays is 0.5 us to 32.768 ms in steps of 0.5 us. */
#define DELAY1_LOOP_CYCLES 3
#define DELAY1_LOOPS_MAX 256
#define DELAY1_US_MAX (DELAY1_LOOPS_MAX * DELAY1_LOOP_CYCLES * 1e6 / (F_CPU))
#define DELAY2_LOOP_CYCLES 4
#define DELAY2_LOOPS_MAX 65536
#define DELAY2_US_MAX (DELAY2_LOOPS_MAX * DELAY2_LOOP_CYCLES * 1e6 / (F_CPU))
/** Delay a selected number of microseconds
@param us number of microseconds to delay. This must be a constant.
@note This should be only used with a constant argument otherwise
it will expand to a lot of code and require slow floating point operations to compute
the number of cycles to delay. */
#define DELAY_US(us) \
do \
{ \
if ((us) > DELAY1_US_MAX) \
{ \
double __tmp2 = ((double)F_CPU * (us)) / (DELAY2_LOOP_CYCLES * 1e6); \
uint16_t __ticks2; \
\
if ((us) > DELAY2_US_MAX) \
__ticks2 = 0; \
else \
__ticks2 = (uint16_t)__tmp2; \
_delay_loop_2 (__ticks2); \
} \
else \
{ \
double __tmp1 = ((double)F_CPU * (us)) / (DELAY1_LOOP_CYCLES * 1e6); \
uint8_t __ticks1; \
\
if (__tmp1 < 1.0) \
__ticks1 = 1; \
else \
__ticks1 = (uint8_t)__tmp1; \
_delay_loop_1 (__ticks1); \
} \
} \
while (0)
/** Delay a selected number of milliseconds
@param ms number of milliseconds to delay. This can be a variable. */
static __inline__ void
delay_ms (uint8_t ms)
{
/* FIXME: "i" should only need to be a uint8_t
Some bug has occured using a newer version of the avr-gcc that
causes this routine to only delay 1ms if both "ms" and "i"
are uint8_t. */
uint16_t i;
for (i = 0; i < ms; i++)
{
DELAY_US (1000);
}
}
#endif /* DELAY_H */
+71
View File
@@ -0,0 +1,71 @@
/** @file eeprom.c
@author M. P. Hayes, UCECE
@date 15 May 2007
@brief
*/
#include "system.h"
#include <avr/io.h>
#include "eeprom.h"
static void
eeprom_write_byte (eeprom_addr_t addr, uint8_t data)
{
/* Wait for completion of previous write. */
while (EECR & _BV (EEPE))
continue;
/* Set up address and data registers */
EEAR = addr;
EEDR = data;
EECR |= _BV (EEMPE);
/* Start eeprom write. */
EECR |= _BV (EEPE);
}
static uint8_t eeprom_read_byte (eeprom_addr_t addr)
{
/* Wait for completion of previous write. */
while (EECR & _BV (EEPE))
continue;
/* Set up address register */
EEAR = addr;
/* Start eeprom read. */
EECR |= _BV (EERE);
/* Return data from data register */
return EEDR;
}
/* Read SIZE bytes from ADDR into BUFFER. */
eeprom_size_t
eeprom_read (eeprom_addr_t addr, void *buffer, eeprom_size_t size)
{
eeprom_size_t i;
uint8_t *data = buffer;
for (i = 0; i < size; i++)
data[i] = eeprom_read_byte (addr++);
return i;
}
/* Write SIZE bytes to ADDR from BUFFER. */
eeprom_size_t
eeprom_write (eeprom_addr_t addr, const void *buffer, eeprom_size_t size)
{
eeprom_size_t i;
const uint8_t *data = buffer;
for (i = 0; i < size; i++)
eeprom_write_byte (addr++, data[i]);
return i;
}
+30
View File
@@ -0,0 +1,30 @@
/** @file eeprom.h
@author Michael Hayes
@date 13 Nov 2006
@brief Routines to read/write internal EEPROM.
@defgroup eeprom EEPROM driver
This module implements a driver to read/write the EEPROM of an
ATmega microcontroller. The EEPROM is useful for storing
persistent data when the power is removed.
*/
#ifndef EEPROM_H
#define EEPROM_H
#include "system.h"
typedef uint16_t eeprom_addr_t;
typedef uint16_t eeprom_size_t;
/* Read SIZE bytes from ADDR into BUFFER. */
extern eeprom_size_t
eeprom_read (eeprom_addr_t addr, void *buffer, eeprom_size_t size);
/* Write SIZE bytes to ADDR from BUFFER. */
extern eeprom_size_t
eeprom_write (eeprom_addr_t addr, const void *buffer, eeprom_size_t size);
#endif
+85
View File
@@ -0,0 +1,85 @@
/** @file ir_uart.c
@author Michael Hayes
@date 10 December 2004
*/
#include "system.h"
#include "ir_uart.h"
#include "usart1.h"
#include "timer0.h"
#include "pio.h"
/* Return non-zero if there is a character ready to be read. */
bool
ir_uart_read_ready_p (void)
{
return usart1_read_ready_p ();
}
/* Read character from IR_UART. This blocks if nothing
is available to read. */
int8_t
ir_uart_getc (void)
{
return usart1_getc ();
}
/* Return non-zero if a character can be written without blocking. */
bool
ir_uart_write_ready_p (void)
{
return usart1_write_ready_p ();
}
/* Return non-zero if transmitter finished. */
bool
ir_uart_write_finished_p (void)
{
return usart1_write_finished_p ();
}
/* Write character to IR_UART. This returns zero if
the character could not be written. */
int8_t
ir_uart_putc (char ch)
{
return usart1_putc (ch);
}
/* Write string to IR_UART. */
void
ir_uart_puts (const char *str)
{
usart1_puts (str);
}
/* Initialise ir_uart and set baud rate. */
uint8_t
ir_uart_init (void)
{
usart1_cfg_t usart1_cfg =
{
.baud_divisor = USART1_BAUD_DIVISOR (IR_UART_BAUD_RATE),
.bits = 8
};
/* Need to configure DDR so pin is an output.
The state is controlled by the timer. */
pio_config_set (IR_TX_HIGH_PIO, PIO_OUTPUT_HIGH);
timer0_init ();
timer0_period_set (F_CPU / (IR_MODULATION_FREQ * 2));
timer0_mode_set (TIMER0_MODE_CTC);
timer0_output_set (TIMER0_OUTPUT_B, TIMER0_OUTPUT_MODE_TOGGLE);
timer0_start ();
return usart1_init (&usart1_cfg);
}
+95
View File
@@ -0,0 +1,95 @@
/** @file ir_uart.h
@author Michael Hayes
@date 10 December 2004
@defgroup IR_uart Infrared uart driver
Here's a simple application that transmits the ASCII code '?' repeatedly.
@code
#include "ir_uart.h"
void main (void)
{
system_init ();
ir_uart_init ();
while (1)
{
ir_uart_putc ('?');
}
}
@endcode
Here's another simple application that receives data bytes.
@code
#include "ir_uart.h"
void main (void)
{
system_init ();
ir_uart_init ();
while (1)
{
if (ir_uart_read_ready_p ())
{
char ch;
ch = ir_uart_getc ();
// Process the received byte.
}
}
}
@endcode
*/
#ifndef IR_UART_H
#define IR_UART_H
#include "system.h"
#ifndef IR_UART_BAUD_RATE
#define IR_UART_BAUD_RATE 2400
#endif
/* Return non-zero if there is a character ready to be read. */
bool
ir_uart_read_ready_p (void);
/* Read character from IR_UART. This blocks if nothing
is available to read. */
int8_t
ir_uart_getc (void);
/* Return non-zero if a character can be written without blocking. */
bool
ir_uart_write_ready_p (void);
/* Return non-zero if transmitter finished. */
bool
ir_uart_write_finished_p (void);
/* Write character to IR_UART. This returns zero if
the character could not be written. */
int8_t
ir_uart_putc (char ch);
/* Write string to IR_UART. */
void
ir_uart_puts (const char *str);
/* Initialise ir_uart and set baud rate. */
uint8_t
ir_uart_init (void);
#endif
+306
View File
@@ -0,0 +1,306 @@
/** @file pio-simple.c
@author M. P. Hayes, UCECE
@date 13 Aug 2009
@brief PIO hardware abstraction for AVR microcontroller.
@note This implementation is written for clarity not efficiency.
*/
#include "pio-simple.h"
/** Configure pio
@param pio */
bool pio_config_set (pio_t pio, pio_config_t config)
{
uint8_t bitmask;
bitmask = BIT (pio.bit);
switch (pio.port)
{
case PORT_B:
switch (config)
{
case PIO_INPUT:
DDRB &= ~bitmask;
PORTB &= ~bitmask;
break;
case PIO_PULLUP:
DDRB &= ~bitmask;
PORTB |= bitmask;
break;
case PIO_OUTPUT_LOW:
PORTB &= ~bitmask;
DDRB |= bitmask;
break;
case PIO_OUTPUT_HIGH:
PORTB |= bitmask;
DDRB |= bitmask;
break;
default:
return 0;
}
break;
case PORT_C:
switch (config)
{
case PIO_INPUT:
DDRC &= ~bitmask;
PORTC &= ~bitmask;
break;
case PIO_PULLUP:
DDRC &= ~bitmask;
PORTC |= bitmask;
break;
case PIO_OUTPUT_LOW:
PORTC &= ~bitmask;
DDRC |= bitmask;
break;
case PIO_OUTPUT_HIGH:
PORTC |= bitmask;
DDRC |= bitmask;
break;
default:
return 0;
}
break;
case PORT_D:
switch (config)
{
case PIO_INPUT:
DDRD &= ~bitmask;
PORTD &= ~bitmask;
break;
case PIO_PULLUP:
DDRD &= ~bitmask;
PORTD |= bitmask;
break;
case PIO_OUTPUT_LOW:
PORTD &= ~bitmask;
DDRD |= bitmask;
break;
case PIO_OUTPUT_HIGH:
PORTD |= bitmask;
DDRD |= bitmask;
break;
default:
return 0;
}
break;
default:
return 0;
}
return 1;
}
/** Set pio high.
@param pio */
void pio_output_high (pio_t pio)
{
uint8_t bitmask;
bitmask = BIT (pio.bit);
switch (pio.port)
{
case PORT_B:
PORTB |= bitmask;
break;
case PORT_C:
PORTC |= bitmask;
break;
case PORT_D:
PORTD |= bitmask;
break;
default:
break;
}
}
/** Set pio low.
@param pio */
void pio_output_low (pio_t pio)
{
uint8_t bitmask;
bitmask = BIT (pio.bit);
switch (pio.port)
{
case PORT_B:
PORTB &= ~bitmask;
break;
case PORT_C:
PORTC &= ~bitmask;
break;
case PORT_D:
PORTD &= ~bitmask;
break;
default:
break;
}
}
/** Toggle pio.
@param pio */
void pio_output_toggle (pio_t pio)
{
uint8_t bitmask;
bitmask = BIT (pio.bit);
switch (pio.port)
{
case PORT_B:
PORTB ^= bitmask;
break;
case PORT_C:
PORTC ^= bitmask;
break;
case PORT_D:
PORTD ^= bitmask;
break;
default:
break;
}
}
/** Read input state from pio.
@param pio
@return input state of pio */
bool pio_input_get (pio_t pio)
{
uint8_t bitmask;
bitmask = BIT (pio.bit);
switch (pio.port)
{
case PORT_B:
return (PINB & bitmask) != 0;
case PORT_C:
return (PINC & bitmask) != 0;
case PORT_D:
return (PIND & bitmask) != 0;
default:
return 0;
}
}
/** Set pio to desired state
@param pio
@param state value to write pio */
void pio_output_set (pio_t pio, bool state)
{
if (state)
pio_output_high (pio);
else
pio_output_low (pio);
}
/** Return pio configuration
@param pio
@return config */
pio_config_t pio_config_get (pio_t pio)
{
bool ddr;
bool port;
uint8_t bitmask;
bitmask = BIT (pio.bit);
switch (pio.port)
{
case PORT_B:
ddr = (DDRB & bitmask) != 0;
port = (DDRB & bitmask) != 0;
break;
case PORT_C:
ddr = (DDRC & bitmask) != 0;
port = (DDRC & bitmask) != 0;
break;
case PORT_D:
ddr = (DDRD & bitmask) != 0;
port = (DDRD & bitmask) != 0;
break;
default:
return 0;
}
if (ddr)
{
if (port)
return PIO_OUTPUT_HIGH;
else
return PIO_OUTPUT_LOW;
}
if (port)
return PIO_PULLUP;
return PIO_INPUT;
}
/** Get output state of pio.
@param pio
@return output state of pio */
bool pio_output_get (pio_t pio)
{
uint8_t bitmask;
bitmask = BIT (pio.bit);
switch (pio.port)
{
case PORT_B:
return (PORTB & bitmask) != 0;
case PORT_C:
return (PORTC & bitmask) != 0;
case PORT_D:
return (PORTD & bitmask) != 0;
default:
return 0;
}
}
+77
View File
@@ -0,0 +1,77 @@
/** @file pio-simple.h
@author M. P. Hayes, UCECE
@date 11 Jan 2006
@brief PIO hardware abstraction.
*/
#ifndef PIO_H
#define PIO_H
#include "system.h"
typedef enum pio_port_enum
{
PORT_A, PORT_B, PORT_C, PORT_D, PORT_E
} pio_port_t;
typedef uint8_t pio_bit_t;
typedef struct pio_struct
{
pio_port_t port;
pio_bit_t bit;
} pio_t;
typedef enum pio_config_enum
{
PIO_INPUT = 1, PIO_OUTPUT_LOW, PIO_OUTPUT_HIGH, PIO_PULLUP
} pio_config_t;
/* Define a PIO as a structure in terms of a port and a bit. */
#define PIO_DEFINE(PORT, PORTBIT) (pio_t){PORT, PORTBIT}
/** Configure pio.
@param pio */
bool pio_config_set (pio_t pio, pio_config_t config);
/** Configure pio.
@param pio
@return config */
pio_config_t pio_config_get (pio_t pio);
/** Set pio high.
@param pio */
void pio_output_high (pio_t pio);
/** Set pio low.
@param pio */
void pio_output_low (pio_t pio);
/** Toggle pio.
@param pio */
void pio_output_toggle (pio_t pio);
/** Read input state from pio.
@param pio
@return input state of pio */
bool pio_input_get (pio_t pio);
/** Set pio to desired state
@param pio
@param state value to write pio */
void pio_output_set (pio_t pio, bool state);
/** Get output state of pio.
@param pio
@return output state of pio */
bool pio_output_get (pio_t pio);
#endif
+9
View File
@@ -0,0 +1,9 @@
/** @file pio.c
@author M. P. Hayes, UCECE
@date 11 Jan 2006
@brief PIO hardware abstraction for AVR microcontroller.
@note This implementation is empty; inline functions are used
in pio.h instead for performance.
*/
#include "system.h"
#include "pio.h"
+253
View File
@@ -0,0 +1,253 @@
/** @file pio.h
@author M. P. Hayes, UCECE
@date 11 Jan 2006
@brief PIO hardware abstraction for AVR microcontroller.
@note Macros and inline functions are used to avoid function
call overhead and to allow compile-time constant folding.
@defgroup pio PIO driver
This module implements a driver for general purpose I/O ports.
Its purpose is to provide a microcontroller independent
abstraction of a PIO pin. Here's an example:
@code
#include "pio.h"
#define LED_PIO PIO_DEFINE (PORT_B, 5)
void main (void)
{
system_init ();
pio_config_set (LED_PIO, PIO_OUTPUT_LOW);
pio_output_high (LED_PIO);
while (1);
}
@endcode
In this example, when port B5 is configured as an output it is set
low.
*/
#ifndef PIO_H
#define PIO_H
#include "system.h"
#ifdef AVR
#include <avr/io.h>
#endif
/** Define port names, note not all the ports are available on some AVRs. */
#ifdef PORTA
#define PORT_A &PORTA
#endif
#ifdef PORTB
#define PORT_B &PORTB
#endif
#ifdef PORTC
#define PORT_C &PORTC
#endif
#ifdef PORTD
#define PORT_D &PORTD
#endif
#ifdef PORTE
#define PORT_E &PORTE
#endif
#ifdef PORTF
#define PORT_F &PORTF
#endif
#ifdef PORTG
#define PORT_G &PORTG
#endif
typedef volatile uint8_t *pio_port_t;
typedef uint8_t pio_mask_t;
typedef struct
{
pio_port_t port;
pio_mask_t bitmask;
} pio_t;
/** Define PIO pin types. The two flavours of PIO_OUTPUT are to
ensure the port is configured without glitching due to the time
taken before calling pio_output_set. */
typedef enum pio_config_enum
{
PIO_INPUT = 1, /** Configure as input pin. */
PIO_PULLUP, /** Configure as input pin with pullup. */
PIO_OUTPUT_LOW, /** Configure as output, initially low. */
PIO_OUTPUT_HIGH, /** Configure as output, initially high. */
} pio_config_t;
/** DDR PORT Comment
0 0 High impedance input
0 1 Weak pullup input
1 0 Output low
1 1 Output high
*/
/** Define a PIO as a compound literal structure in terms of a port and
a bitmask. */
#define PIO_DEFINE(PORT, PORTBIT) (pio_t){.port = PORT, .bitmask = BIT (PORTBIT)}
/** Private macro to lookup bitmask. */
#define PIO_BITMASK_(pio) (pio.bitmask)
/** Private macro to lookup port register. */
#define PIO_PORT_(pio) (pio.port)
/** Private macro to map a pio to its corresponding data direction
register (DDR). NB, the DDR and PORT registers must be separated
by the same number of bytes in all cases. PORTB is used for the
pattern since PORTA is not always defined for some AVRs. */
#define PIO_DDR_(pio) (*(PIO_PORT_ (pio) + (&DDRB - &PORTB)))
/** Private macro to map a pio to its input register (PIN). NB, the
PIN and PORT registers must be separated by the same number of
bytes in all cases. PORTB is used for the pattern since PORTA is
not always defined for some AVRs. */
#define PIO_PIN_(pio) (*(PIO_PORT_ (pio) + (&PINB - &PORTB)))
/** Private macro to access a pio data register. */
#define PIO_DATA_(pio) (*PIO_PORT_ (pio))
/** Configure pio.
@param pio PIO to configure
@param config PIO configuration type
@return non-zero for success. */
static inline
bool pio_config_set (pio_t pio, pio_config_t config)
{
switch (config)
{
case PIO_OUTPUT_LOW:
PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio);
PIO_DDR_ (pio) |= PIO_BITMASK_ (pio);
return 1;
case PIO_OUTPUT_HIGH:
PIO_DATA_ (pio) |= PIO_BITMASK_ (pio);
PIO_DDR_ (pio) |= PIO_BITMASK_ (pio);
return 1;
case PIO_INPUT:
PIO_DDR_ (pio) &= ~PIO_BITMASK_ (pio);
PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio);
return 1;
case PIO_PULLUP:
PIO_DDR_ (pio) &= ~PIO_BITMASK_ (pio);
PIO_DATA_ (pio) |= PIO_BITMASK_ (pio);
return 1;
default:
return 0;
}
}
/** Return pio configuration
@param pio
@return config */
static inline
pio_config_t pio_config_get (pio_t pio)
{
bool ddr;
bool port;
ddr = PIO_DDR_ (pio) & PIO_BITMASK_ (pio);
port = PIO_DATA_ (pio) & PIO_BITMASK_ (pio);
if (ddr)
{
if (port)
return PIO_OUTPUT_HIGH;
else
return PIO_OUTPUT_LOW;
}
if (port)
return PIO_PULLUP;
return PIO_INPUT;
}
/** Set pio high.
@param pio */
static inline
void pio_output_high (pio_t pio)
{
PIO_DATA_ (pio) |= PIO_BITMASK_ (pio);
}
/** Set pio low.
@param pio */
static inline
void pio_output_low (pio_t pio)
{
PIO_DATA_ (pio) &= ~PIO_BITMASK_ (pio);
}
/** Toggle pio.
@param pio */
static inline
void pio_output_toggle (pio_t pio)
{
PIO_DATA_ (pio) ^= PIO_BITMASK_ (pio);
}
/** Read input state from pio.
@param pio
@return input state of pio */
static inline
bool pio_input_get (pio_t pio)
{
return (PIO_PIN_ (pio) & PIO_BITMASK_ (pio)) != 0;
}
/** Get output state of pio.
@param pio
@return output state of pio */
static inline
bool pio_output_get (pio_t pio)
{
return (PIO_DATA_ (pio) & PIO_BITMASK_ (pio)) != 0;
}
/** Set pio to desired state.
@param pio
@param state value to write pio */
static inline
void pio_output_set (pio_t pio, bool state)
{
state ? pio_output_high (pio) : pio_output_low (pio);
}
#endif
+35
View File
@@ -0,0 +1,35 @@
/** @file prescale.c
@author M. P. Hayes, UCECE
@date 3 Aug 2011
@brief Prescaler selection for 8-bit timer/counters.
*/
#include "system.h"
#include "prescale.h"
#include <stdlib.h>
uint8_t prescale_select (uint16_t period, const uint8_t *log_prescales,
uint8_t num_prescales, uint8_t *pcounter)
{
uint8_t i;
uint16_t counter = 0;
/* If the desired period is T we need to choose the counter value
c and the prescale value s to minimise |T - c x 2^p| with the
constraints that 0 < c < 256 and 2^p is one of the available
prescale values. */
/* Search through prescaler options looking for first one
where the counter value is < 256. */
for (i = 0; i < num_prescales; i++)
{
uint16_t divisor;
divisor = 1 << log_prescales[i];
counter = (period + (divisor + 1) / 2) >> log_prescales[i];
if (counter < 256)
break;
}
*pcounter = counter;
return i;
}
+15
View File
@@ -0,0 +1,15 @@
/** @file prescale.h
@author M. P. Hayes, UCECE
@date 3 Aug 2011
@brief Prescaler selection for timer/counters.
*/
#ifndef PRESCALE_H
#define PRESCALE_H
#include "system.h"
uint8_t prescale_select (uint16_t period, const uint8_t *log_prescales,
uint8_t num_prescales, uint8_t *pcounter);
#endif
+40
View File
@@ -0,0 +1,40 @@
/** @file system.c
@author M. P. Hayes, UCECE
@date 15 May 2007
@brief UCFK system initialisation
*/
#include "system.h"
#include <avr/wdt.h>
static void system_clock_init (void)
{
/* Switch 1 MHz CPU clock to 8 MHz. */
CLKPR = BIT (CLKPCE);
CLKPR = 0;
}
static void system_watchdog_timer_init (void)
{
wdt_reset ();
/* Clear WDRF in MCUSR. */
MCUSR &= ~BIT (WDRF);
/* Write logical one to WDCE and WDE and keep old prescaler
setting to prevent unintentional time-out. */
WDTCSR |= BIT (WDCE) | BIT (WDE);
/* Turn off watchdog timer. */
WDTCSR = 0x00;
}
void system_init (void)
{
system_clock_init ();
system_watchdog_timer_init ();
}
+74
View File
@@ -0,0 +1,74 @@
/** @file system.h
@author M. P. Hayes, UCECE
@date 15 May 2007
@brief System specific definitions
*/
#ifndef SYSTEM_H
#define SYSTEM_H
/* Data typedefs. */
#include <stdint.h>
typedef uint8_t bool;
/* Useful macros. */
#define BIT(X) (1 << (X))
#define ARRAY_SIZE(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0]))
#define __unused__ __attribute__ ((unused))
/* Clock frequency Hz. */
#define F_CPU 8000000
/* LED matrix columns. */
#define LEDMAT_COL1_PIO PIO_DEFINE(PORT_C, 6)
#define LEDMAT_COL2_PIO PIO_DEFINE(PORT_B, 7)
#define LEDMAT_COL3_PIO PIO_DEFINE(PORT_C, 4)
#define LEDMAT_COL4_PIO PIO_DEFINE(PORT_C, 7)
#define LEDMAT_COL5_PIO PIO_DEFINE(PORT_C, 5)
#define LEDMAT_COLS_NUM 5
/* LED matrix rows. */
#define LEDMAT_ROW1_PIO PIO_DEFINE(PORT_B, 6)
#define LEDMAT_ROW2_PIO PIO_DEFINE(PORT_B, 5)
#define LEDMAT_ROW3_PIO PIO_DEFINE(PORT_B, 4)
#define LEDMAT_ROW4_PIO PIO_DEFINE(PORT_B, 3)
#define LEDMAT_ROW5_PIO PIO_DEFINE(PORT_B, 2)
#define LEDMAT_ROW6_PIO PIO_DEFINE(PORT_B, 1)
#define LEDMAT_ROW7_PIO PIO_DEFINE(PORT_B, 0)
#define LEDMAT_ROWS_NUM 7
/* Button. */
#define BUTTON1 0
#define BUTTON1_PIO PIO_DEFINE(PORT_D, 7)
/* Navswitch. */
#define NAVSWITCH_PUSH_PIO LEDMAT_COL3_PIO
#define NAVSWITCH_EAST_PIO LEDMAT_COL1_PIO
#define NAVSWITCH_WEST_PIO LEDMAT_COL2_PIO
#define NAVSWITCH_NORTH_PIO LEDMAT_COL4_PIO
#define NAVSWITCH_SOUTH_PIO LEDMAT_COL5_PIO
/* LED (active high). */
#define LED1 0
#define LED1_PIO PIO_DEFINE(PORT_C, 2)
/* Infrared transmitter LED and receiver. */
#define IR_TX_LOW_PIO PIO_DEFINE(PORT_D, 3)
#define IR_TX_HIGH_PIO PIO_DEFINE(PORT_D, 0)
#define IR_RX_PIO PIO_DEFINE(PORT_D, 2)
#define IR_MODULATION_FREQ 36e3
void system_init (void);
#endif
+73
View File
@@ -0,0 +1,73 @@
/** @file system.h
@author M. P. Hayes, UCECE
@date 15 May 2007
@brief System specific definitions
*/
#ifndef SYSTEM_H
#define SYSTEM_H
/* Data typedefs. */
#include <stdint.h>
typedef uint8_t bool;
/* Useful macros. */
#define BIT(X) (1 << (X))
#define ARRAY_SIZE(ARRAY) (sizeof (ARRAY) / sizeof (ARRAY[0]))
#define __unused__ __attribute__ ((unused))
/* Clock frequency Hz. */
#define F_CPU 8000000
/* LED matrix columns. */
#define LEDMAT_COL1_PIO PIO_DEFINE(PORT_C, 6)
#define LEDMAT_COL2_PIO PIO_DEFINE(PORT_B, 7)
#define LEDMAT_COL3_PIO PIO_DEFINE(PORT_C, 4)
#define LEDMAT_COL4_PIO PIO_DEFINE(PORT_C, 7)
#define LEDMAT_COL5_PIO PIO_DEFINE(PORT_C, 5)
#define LEDMAT_COLS_NUM 5
/* LED matrix rows. */
#define LEDMAT_ROW1_PIO PIO_DEFINE(PORT_B, 6)
#define LEDMAT_ROW2_PIO PIO_DEFINE(PORT_B, 5)
#define LEDMAT_ROW3_PIO PIO_DEFINE(PORT_B, 4)
#define LEDMAT_ROW4_PIO PIO_DEFINE(PORT_B, 3)
#define LEDMAT_ROW5_PIO PIO_DEFINE(PORT_B, 2)
#define LEDMAT_ROW6_PIO PIO_DEFINE(PORT_B, 1)
#define LEDMAT_ROW7_PIO PIO_DEFINE(PORT_B, 0)
#define LEDMAT_ROWS_NUM 7
/* Button. */
#define BUTTON1 0
#define BUTTON1_PIO PIO_DEFINE(PORT_D, 7)
/* Navswitch. */
#define NAVSWITCH_PUSH_PIO LEDMAT_COL3_PIO
#define NAVSWITCH_EAST_PIO LEDMAT_COL1_PIO
#define NAVSWITCH_WEST_PIO LEDMAT_COL2_PIO
#define NAVSWITCH_NORTH_PIO LEDMAT_COL4_PIO
#define NAVSWITCH_SOUTH_PIO LEDMAT_COL5_PIO
/* LED (active high). */
#define LED1 0
#define LED1_PIO PIO_DEFINE(PORT_C, 2)
/* Infrared transmitter LED and receiver. */
#define IR_TX_LOW_PIO PIO_DEFINE(PORT_D, 3)
#define IR_TX_HIGH_PIO PIO_DEFINE(PORT_D, 0)
#define IR_RX_PIO PIO_DEFINE(PORT_D, 2)
#define IR_MODULATION_FREQ 36e3
#endif
+74
View File
@@ -0,0 +1,74 @@
/** @file timer.c
@author M. P. Hayes, UCECE
@date 23 August 2010
@brief Timer module
*/
#include <avr/io.h>
#include "timer.h"
#include "system.h"
/* Timer/counter1 is a 16-bit counter with prescale options 1, 8, 64,
256, and 1024. With F_CPU = 8e6 this corresponds to clock rates of
8 MHz, 1 MHz, 125 kHz, 31.25 kHz, 7.8125 kHz. The counter will
roll over every 8.192 ms, 65.536 ms, 524.288 ms, 2.097s, and 8.389 s. */
#if TIMER_CLOCK_DIVISOR == 1
#define TCCR1B_INIT 0x01
#elif TIMER_CLOCK_DIVISOR == 8
#define TCCR1B_INIT 0x02
#elif TIMER_CLOCK_DIVISOR == 64
#define TCCR1B_INIT 0x03
#elif TIMER_CLOCK_DIVISOR == 256
#define TCCR1B_INIT 0x04
#elif TIMER_CLOCK_DIVISOR == 1024
#define TCCR1B_INIT 0x05
#else
#error Invalid TIMER_CLOCK_DIVISOR
#endif
/** Initialise timer. */
void timer_init (void)
{
/* Start timer in normal mode so that it rolls over at 65535 to 0. */
TCCR1A = 0x00;
TCCR1B = TCCR1B_INIT;
TCNT1 = 0;
}
/** Get current time:
@return current time in ticks. */
timer_tick_t timer_get (void)
{
return TCNT1;
}
/** Wait until specified time:
@param when time to sleep until
@return current time. */
timer_tick_t timer_wait_until (timer_tick_t when)
{
while (1)
{
timer_tick_t diff;
timer_tick_t now;
now = timer_get ();
diff = now - when;
if (diff < TIMER_OVERRUN_MAX)
return now;
}
}
/** Wait for specified period:
@param period how long to wait
@return current time. */
timer_tick_t timer_wait (timer_tick_t period)
{
return timer_wait_until (timer_get () + period);
}
+97
View File
@@ -0,0 +1,97 @@
/** @file timer.h
@author M. P. Hayes, UCECE
@date 18 August 2011
@brief Timer support.
@defgroup timer Timer module
This module provides simple timer support by abstracting a
hardware timer.
The rate that the timer is incremented is specified by the
macro TIMER_CLOCK_DIVISOR in the header file system.h This
must be a value supported by the timer clock prescaler.
The rate that the timer is incremented can be found using the
macro TIMER_RATE.
The timer is free-running and will increment TIMER_RATE times
per second. When the timer reaches 65535 on the next increment
it rolls over to 0.
Here's a simple example for turning an LED on for 0.5 second
and then off for 0.75 second.
@code
#include "timer.h"
#include "led.h"
void main (void)
{
timer_tick_t now;
system_init ();
timer_init ();
led_init ();
now = timer_get ();
while (1)
{
led_set (LED1, 1);
now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.5));
led_set (LED1, 0);
now = timer_wait_until (now + (timer_tick_t)(TIMER_RATE * 0.75));
}
}
@endcode
*/
#ifndef TIMER_H
#define TIMER_H
#include "system.h"
#ifndef TIMER_CLOCK_DIVISOR
#define TIMER_CLOCK_DIVISOR 256
#endif
/** Rate in Hz that the timer is incremented. */
#define TIMER_RATE (F_CPU / TIMER_CLOCK_DIVISOR)
/** The maximum overrun (in ticks). */
#define TIMER_OVERRUN_MAX 1000
/** The maximum delay (in ticks). */
#define TIMER_DELAY_MAX (65536u - TIMER_OVERRUN_MAX)
/** Define timer ticks. */
typedef uint16_t timer_tick_t;
/** Get current time:
@return current time in ticks. */
timer_tick_t timer_get (void);
/** Wait until specified time:
@param when time to sleep until
@return current time. */
timer_tick_t timer_wait_until (timer_tick_t when);
/** Wait for specified period:
@param period how long to wait
@return current time. */
timer_tick_t timer_wait (timer_tick_t period);
/** Initialise timer. */
void timer_init (void);
#endif /* TIMER_H */
+182
View File
@@ -0,0 +1,182 @@
/** @file timer0.c
@author M. P. Hayes, UCECE
@date 15 May 2007
@brief
*/
#include "system.h"
#include "timer0.h"
#include "bits.h"
#include "prescale.h"
#include <avr/io.h>
/* Timer/Counter0 is a general purpose, single channel, 8-bit
Timer/Counter module.
It has two output compare registers and two corresponding outputs:
OC0A and OCOB. However, the counter can only be reset by the A
output compure register, OCRA.
TODO: rewrite driver to provide standard modes, such as:
* generate square wave on OCOA
* generate square wave on OCOB
* generate PWM on OCOA
* generate PWM on OCOB
* programmable timer (for delays)
*/
/* Prescale values:
1, 8, 64, 256, or 1024.
2^0, 2^3, 2^6, 2^8, 2^10. */
static const uint8_t log_prescales[] = {0, 3, 6, 8, 10};
static uint8_t prescale_bits = 1;
static inline void
timer0_prescaler_set (uint8_t value)
{
BITS_INSERT (TCCR0B, value, CS00, CS02);
}
static inline uint8_t
timer0_prescaler_get (void)
{
return BITS_EXTRACT (TCCR0B, CS00, CS02);
}
static inline bool
timer0_running_p (void)
{
return timer0_prescaler_get () != 0;
}
/* Set the desired period in terms of clocks. The closest
suitable period is returned. */
uint16_t timer0_period_set (uint16_t period)
{
uint8_t counter;
uint8_t index;
index = prescale_select (period, log_prescales,
ARRAY_SIZE (log_prescales), &counter);
prescale_bits = index + 1;
/* F = F_CPU / (2 * prescale * (1 + OCR0A))
Maximum frequency of F_CPU / 2 when OCR0A = 0. */
OCR0A = counter - 1;
OCR0B = 0;
/* A prescale value of 0 selects no clock source and is used to
stop the timer. The cheapskates haven't provided a start/stop
bit so we cannot set the prescaler if the timer is stopped. */
if (timer0_running_p ())
timer0_prescaler_set (prescale_bits);
/* Return the quantised period. */
return counter << log_prescales[index];
}
void
timer0_start (void)
{
/* Clear overflow flags. */
TIFR0 &= ~(BIT (OCF0A) | BIT (OCF0B));
/* Enable timer by supplying a clock from the prescaler. */
timer0_prescaler_set (prescale_bits);
}
void
timer0_stop (void)
{
/* Disable clock source. */
timer0_prescaler_set (0);
}
bool
timer0_compare_p (void)
{
return (TIFR0 & BIT (OCF0B)) != 0;
}
void timer0_output_set (timer0_output_t output, timer0_output_mode_t mode)
{
switch (output)
{
case TIMER0_OUTPUT_A:
/* Select output mode for 0C0A.
The port pin must also be configured as an output. */
BITS_INSERT (TCCR0A, mode, COM0A0, COM0A1);
break;
case TIMER0_OUTPUT_B:
/* Select output mode for 0C0B.
The port pin must also be configured as an output. */
BITS_INSERT (TCCR0A, mode, COM0B0, COM0B1);
break;
default:
break;
}
}
int8_t
timer0_mode_set (timer0_mode_t mode)
{
switch (mode)
{
case TIMER0_MODE_NORMAL:
/* Normal mode---counter not reset. It rolls over when it
gets to 0xFF and thus there is limited frequency control
for waveform generation. */
TCCR0A &= ~(BIT (WGM01) | BIT (WGM00));
/* Need WGM02 bit set to 0 (default) in TCCR0B. */
break;
case TIMER0_MODE_CTC:
/* Set CTC mode. Clear timer in compare mode---counter reset
on match with OCRA. */
TCCR0A &= ~BIT (WGM00);
TCCR0A |= BIT (WGM01);
/* Need WGM02 bit set to 0 (default) in TCCR0B. */
break;
default:
return 0;
}
return 1;
}
int8_t
timer0_init (void)
{
/* Set to normal mode, clock source disabled, output compare match
to not trigger OC pin. */
TCCR0A = 0;
TCCR0B = 0;
TCNT0 = 0;
OCR0B = 0;
timer0_mode_set (TIMER0_MODE_CTC);
return 1;
}
+48
View File
@@ -0,0 +1,48 @@
/** @file timer0.h
@author M. P. Hayes, UCECE
@date 15 May 2007
@brief
*/
#ifndef TIMER0_H
#define TIMER0_H
#include "system.h"
#define TIMER0_DIVISOR(FREQ) ((uint16_t) (F_CPU / (FREQ)))
typedef enum {TIMER0_MODE_NORMAL,
TIMER0_MODE_CTC,
TIMER0_MODE_PWM} timer0_mode_t;
typedef enum {TIMER0_OUTPUT_A,
TIMER0_OUTPUT_B} timer0_output_t;
typedef enum {TIMER0_OUTPUT_MODE_DISABLE,
TIMER0_OUTPUT_MODE_TOGGLE,
TIMER0_OUTPUT_MODE_CLEAR,
TIMER0_OUTPUT_MODE_SET} timer0_output_mode_t;
uint16_t timer0_period_set (uint16_t period);
void timer0_start (void);
void timer0_stop (void);
bool timer0_compare_p (void);
void timer0_output_set (timer0_output_t output, timer0_output_mode_t mode);
int8_t timer0_mode_set (timer0_mode_t mode);
int8_t timer0_init (void);
#endif
+98
View File
@@ -0,0 +1,98 @@
/** @file usart1.c
@author Michael Hayes
@date 10 March 2005
@brief Routines for interfacing with the usart1 on an Atmega32u2
*/
#include "system.h"
#include "usart1.h"
#include <avr/io.h>
void
usart1_baud_divisor_set (uint16_t divisor)
{
UBRR1H = (divisor - 1) >> 8;
UBRR1L = (divisor - 1) & 0xFF;
}
uint8_t
usart1_init (const usart1_cfg_t *cfg)
{
uint8_t bits;
usart1_baud_divisor_set (cfg->baud_divisor);
/* Default to 8 bits. */
bits = cfg->bits;
if (bits < 5 || bits > 8)
bits = 8;
/* Enable receiver and transmitter. */
UCSR1B = BIT (RXEN1) | BIT (TXEN1);
/* Set frame format: asynchronous USART, 1 stop bit, no parity. */
UCSR1C = ((bits - 5) << UCSZ10) | (0 << UPM10);
return 1;
}
/* Return non-zero if there is a character ready to be read. */
bool
usart1_read_ready_p (void)
{
return (UCSR1A & BIT (RXC1)) != 0;
}
/* Return non-zero if a character can be written without blocking. */
bool
usart1_write_ready_p (void)
{
return (UCSR1A & BIT (UDRE1)) != 0;
}
/* Return non-zero if transmitter finished. */
bool
usart1_write_finished_p (void)
{
return (UCSR1A & BIT (TXC1)) != 0;
}
/* Write character to USART1. */
int8_t
usart1_putc (char ch)
{
if (ch == '\n')
usart1_putc ('\r');
while (!usart1_write_ready_p ())
continue;
UDR1 = ch;
return 1;
}
/* Read character from USART1. This blocks until a character is read. */
int8_t
usart1_getc (void)
{
/* Wait for something in receive buffer. */
while (!usart1_read_ready_p ())
continue;
return UDR1;
}
/* Write string to USART1. This blocks until the string is written. */
void
usart1_puts (const char *str)
{
while (*str)
usart1_putc (*str++);
}
+60
View File
@@ -0,0 +1,60 @@
/** @file usart1.h
@author Michael Hayes
@date 10 December 2004
@brief Routines for interfacing with the usart1 on an Atmega32u2
*/
#ifndef USART1_H
#define USART1_H
#include "system.h"
typedef struct usart1_cfg_struct
{
uint16_t baud_divisor;
uint8_t bits;
} usart1_cfg_t;
#define USART1_BAUD_DIVISOR(BAUD_RATE) ((F_CPU / 16) / (BAUD_RATE))
/* Return non-zero if there is a character ready to be read. */
bool
usart1_read_ready_p (void);
/* Read character from USART1. This blocks if nothing is available to
read. */
int8_t
usart1_getc (void);
/* Return non-zero if a character can be written without blocking. */
bool
usart1_write_ready_p (void);
/* Return non-zero if transmitter finished. */
bool
usart1_write_finished_p (void);
/* Write character to USART1. This returns zero if the character
could not be written. */
int8_t
usart1_putc (char ch);
/* Write string to USART1. */
void
usart1_puts (const char *str);
/* Initialise usart1 and set baud rate. */
uint8_t
usart1_init (const usart1_cfg_t *cfg);
#endif