Generate callgraph
This commit is contained in:
@@ -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.
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 ();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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++);
|
||||
}
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user