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
|
||||
@@ -0,0 +1,123 @@
|
||||
/** @file button.c
|
||||
@author M.P. Hayes
|
||||
@date 28 Aug 2008
|
||||
@brief Button polling (no debouncing).
|
||||
@note This polls pushbutton switches but does not do debouncing.
|
||||
*/
|
||||
|
||||
#include "system.h"
|
||||
#include "button.h"
|
||||
#include "pio.h"
|
||||
|
||||
|
||||
/* Button configuration structure. */
|
||||
typedef struct
|
||||
{
|
||||
pio_t pio;
|
||||
/* True for active high, false for active low. */
|
||||
bool active_high;
|
||||
} button_cfg_t;
|
||||
|
||||
|
||||
/** Button state structure. */
|
||||
typedef struct
|
||||
{
|
||||
bool current;
|
||||
bool previous;
|
||||
} button_state_t;
|
||||
|
||||
|
||||
/* Define buttons. */
|
||||
static const button_cfg_t buttons_cfg[] =
|
||||
{
|
||||
{.pio = BUTTON1_PIO, .active_high = 1},
|
||||
/* Add config data for additional buttons here. */
|
||||
};
|
||||
|
||||
#define BUTTONS_NUM ARRAY_SIZE (buttons_cfg)
|
||||
|
||||
static button_state_t buttons_state[BUTTONS_NUM];
|
||||
|
||||
|
||||
/** Return true if button state changed from up to down since
|
||||
last call to button_update
|
||||
@param button index of button to select
|
||||
@return 1 if button changed from up to down otherwise 0 */
|
||||
bool
|
||||
button_push_event_p (uint8_t button)
|
||||
{
|
||||
if (button >= BUTTONS_NUM)
|
||||
return 0;
|
||||
return buttons_state[button].current && !buttons_state[button].previous;
|
||||
}
|
||||
|
||||
|
||||
/** Return true if button state changed from down to up since
|
||||
last call to button_update
|
||||
@param button index of button to select
|
||||
@return 1 if button changed from down to up otherwise 0 */
|
||||
bool
|
||||
button_release_event_p (uint8_t button)
|
||||
{
|
||||
if (button >= BUTTONS_NUM)
|
||||
return 0;
|
||||
return !buttons_state[button].current && buttons_state[button].previous;
|
||||
}
|
||||
|
||||
|
||||
/** Return true if button down (pushed).
|
||||
@param button index of button to select
|
||||
@return 1 if button down otherwise 0 */
|
||||
bool
|
||||
button_down_p (uint8_t button)
|
||||
{
|
||||
if (button >= BUTTONS_NUM)
|
||||
return 0;
|
||||
return buttons_state[button].current;
|
||||
}
|
||||
|
||||
|
||||
/** Return true if button up (released).
|
||||
@param button index of button to select
|
||||
@return 1 if button down otherwise 0 */
|
||||
bool
|
||||
button_up_p (uint8_t button)
|
||||
{
|
||||
if (button >= BUTTONS_NUM)
|
||||
return 0;
|
||||
return !buttons_state[button].current;
|
||||
}
|
||||
|
||||
|
||||
/** Poll all the buttons and update their state. */
|
||||
void
|
||||
button_update (void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < BUTTONS_NUM; i++)
|
||||
{
|
||||
buttons_state[i].previous = buttons_state[i].current;
|
||||
|
||||
buttons_state[i].current = ! (pio_input_get (buttons_cfg[i].pio)
|
||||
^ buttons_cfg[i].active_high);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Initialise button driver. */
|
||||
void button_init (void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < BUTTONS_NUM; i++)
|
||||
{
|
||||
/* Configure pio for input and enable internal pullup resistor
|
||||
if active low. If button is active high it is assumed
|
||||
there is an external pulldon resistor. */
|
||||
pio_config_set (buttons_cfg[i].pio,
|
||||
buttons_cfg[i].active_high ? PIO_INPUT : PIO_PULLUP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/** @file button.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 15 Feb 2003
|
||||
@brief Button polling (no debouncing).
|
||||
|
||||
@defgroup button Pushbutton driver
|
||||
|
||||
This module implements a simple pushbutton driver. Note, no
|
||||
debouncing is performed.
|
||||
|
||||
Here's an example application where the buttons are polled at 50 Hz:
|
||||
|
||||
@code
|
||||
#include "pacer.h"
|
||||
#include "button.h"
|
||||
|
||||
void main (void)
|
||||
{
|
||||
system_init ();
|
||||
button_init ();
|
||||
pacer_init (50);
|
||||
|
||||
while (1)
|
||||
{
|
||||
pacer_wait ();
|
||||
button_update ();
|
||||
|
||||
if (button_push_event_p (0))
|
||||
// Do something
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
|
||||
|
||||
#ifndef BUTTON_H
|
||||
#define BUTTON_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
|
||||
/** Poll all the buttons and update their state. */
|
||||
void button_update (void);
|
||||
|
||||
|
||||
/** Return true if button state changed from up to down since
|
||||
last call to button_update
|
||||
@param button index of button to select
|
||||
@return 1 if button changed from up to down otherwise 0 */
|
||||
bool button_push_event_p (uint8_t button);
|
||||
|
||||
|
||||
/** Return true if button state changed from down to up since
|
||||
last call to button_update
|
||||
@param button index of button to select
|
||||
@return 1 if button changed from down to up otherwise 0 */
|
||||
bool button_release_event_p (uint8_t button);
|
||||
|
||||
|
||||
/** Return true if button down (pushed).
|
||||
@param button index of button to select
|
||||
@return 1 if button down otherwise 0 */
|
||||
bool button_down_p (uint8_t button);
|
||||
|
||||
|
||||
/** Return true if button up (released).
|
||||
@param button index of button to select
|
||||
@return 1 if button down otherwise 0 */
|
||||
bool button_up_p (uint8_t button);
|
||||
|
||||
|
||||
/** Initialise button driver. */
|
||||
void button_init (void);
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
/** @file display.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief Bit-mapped display driver.
|
||||
*/
|
||||
#include "system.h"
|
||||
#include "ledmat.h"
|
||||
#include "display.h"
|
||||
|
||||
/** The state of the display (frame buffer). */
|
||||
static uint8_t display[DISPLAY_WIDTH];
|
||||
|
||||
|
||||
/** Set state of a display pixel.
|
||||
@param col pixel column
|
||||
@param row pixel row
|
||||
@param val pixel state. */
|
||||
void display_pixel_set (uint8_t col, uint8_t row, bool val)
|
||||
{
|
||||
uint8_t bitmask;
|
||||
uint8_t pattern;
|
||||
|
||||
if (col >= DISPLAY_WIDTH || row >= DISPLAY_HEIGHT)
|
||||
return;
|
||||
|
||||
bitmask = BIT (row);
|
||||
pattern = display[col] & ~bitmask;
|
||||
|
||||
if (val)
|
||||
pattern |= bitmask;
|
||||
|
||||
display[col] = pattern;
|
||||
}
|
||||
|
||||
|
||||
/** Get state of a display pixel.
|
||||
@param col pixel column
|
||||
@param row pixel row
|
||||
@return pixel state or zero if outside display. */
|
||||
bool display_pixel_get (uint8_t col, uint8_t row)
|
||||
{
|
||||
uint8_t bitmask;
|
||||
|
||||
if (col >= DISPLAY_WIDTH || row >= DISPLAY_HEIGHT)
|
||||
return 0;
|
||||
|
||||
bitmask = BIT (row);
|
||||
|
||||
return (display[col] & bitmask) != 0;
|
||||
}
|
||||
|
||||
|
||||
/** Scroll display contents one column to left. */
|
||||
void display_scroll_left (void)
|
||||
{
|
||||
int col;
|
||||
|
||||
for (col = 0; col < DISPLAY_WIDTH - 1; col++)
|
||||
display[col] = display[col + 1];
|
||||
}
|
||||
|
||||
|
||||
/** Scroll display contents one row down. */
|
||||
void display_scroll_down (void)
|
||||
{
|
||||
int col;
|
||||
|
||||
for (col = 0; col < DISPLAY_WIDTH; col++)
|
||||
display[col] <<= 1;
|
||||
}
|
||||
|
||||
|
||||
/** Update display (perform refreshing). */
|
||||
void display_update (void)
|
||||
{
|
||||
static uint8_t col = 0;
|
||||
|
||||
ledmat_display_column (display[col], col);
|
||||
|
||||
col++;
|
||||
if (col >= DISPLAY_WIDTH)
|
||||
col = 0;
|
||||
}
|
||||
|
||||
|
||||
/** Clear display. */
|
||||
void display_clear (void)
|
||||
{
|
||||
int col;
|
||||
|
||||
for (col = 0; col < DISPLAY_WIDTH; col++)
|
||||
display[col] = 0;
|
||||
}
|
||||
|
||||
|
||||
/** Initialise display. */
|
||||
void display_init (void)
|
||||
{
|
||||
ledmat_init ();
|
||||
display_clear ();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/** @file display.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief Bit-mapped display driver.
|
||||
|
||||
@defgroup display Bit-mapped display driver
|
||||
|
||||
This module implements a simple display frame-buffer. It abstracts the
|
||||
multiplexing of a LED matrix display.
|
||||
*/
|
||||
#ifndef DISPLAY_H
|
||||
#define DISPLAY_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#define DISPLAY_WIDTH LEDMAT_COLS_NUM
|
||||
#define DISPLAY_HEIGHT LEDMAT_ROWS_NUM
|
||||
|
||||
/** Set state of a display pixel.
|
||||
@param col pixel column (0 left)
|
||||
@param row pixel row (0 top)
|
||||
@param val pixel state. */
|
||||
void display_pixel_set (uint8_t col, uint8_t row, bool val);
|
||||
|
||||
|
||||
/** Get state of a display pixel.
|
||||
@param col pixel column (0 left)
|
||||
@param row pixel row (0 top)
|
||||
@return pixel state or zero if outside display. */
|
||||
bool display_pixel_get (uint8_t col, uint8_t row);
|
||||
|
||||
|
||||
/** Scroll display contents one column to left. */
|
||||
void display_scroll_left (void);
|
||||
|
||||
|
||||
/** Scroll display contents one row down. */
|
||||
void display_scroll_down (void);
|
||||
|
||||
|
||||
/** Update display (perform refreshing). */
|
||||
void display_update (void);
|
||||
|
||||
|
||||
/** Clear display. */
|
||||
void display_clear (void);
|
||||
|
||||
|
||||
/** Initialise display. */
|
||||
void display_init (void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,95 @@
|
||||
/** @file ir.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief Infrared driver.
|
||||
@note This should be compiled with optimisation enabled to make the
|
||||
modulation frequency more accurate.
|
||||
*/
|
||||
#include "system.h"
|
||||
#include "delay.h"
|
||||
#include "ir.h"
|
||||
#include "pio.h"
|
||||
|
||||
#define IR_MODULATION_PERIOD_US (1e6 / IR_MODULATION_FREQ)
|
||||
|
||||
#define IR_SET_TWEAK_US 0.2
|
||||
#define LOOP_TWEAK_US 0.8
|
||||
|
||||
|
||||
/** Turn IR transmitter LED on. */
|
||||
static inline void ir_tx_on (void)
|
||||
{
|
||||
pio_output_high (IR_TX_HIGH_PIO);
|
||||
}
|
||||
|
||||
|
||||
/** Turn IR transmitter LED off. */
|
||||
static inline void ir_tx_off (void)
|
||||
{
|
||||
pio_output_low (IR_TX_HIGH_PIO);
|
||||
}
|
||||
|
||||
|
||||
/** Modulate the IR transmitter LED at IR_MODULATION_FREQ.
|
||||
@param state is 1 to enable modulation, 0 to disable modulation
|
||||
@param count is the number of modulation periods
|
||||
@note This returns after @c count modulation periods. */
|
||||
void ir_tx_set (uint8_t state, uint16_t count)
|
||||
{
|
||||
uint16_t i = 0;
|
||||
|
||||
/* This modulates the IR LED by bit bashing the IR LED PIO.
|
||||
Alternatively, we could use the timer0 peripheral for a more
|
||||
accurate modulation frequency.
|
||||
|
||||
The timing could be made more accurate by tweaking the delays
|
||||
to compensate for the other instructions. */
|
||||
|
||||
if (state)
|
||||
{
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
ir_tx_on ();
|
||||
DELAY_US (IR_MODULATION_PERIOD_US / 2 - IR_SET_TWEAK_US);
|
||||
ir_tx_off ();
|
||||
DELAY_US (IR_MODULATION_PERIOD_US / 2 - IR_SET_TWEAK_US - LOOP_TWEAK_US);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
DELAY_US (IR_MODULATION_PERIOD_US - LOOP_TWEAK_US);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Return output state of IR receiver.
|
||||
@return IR receiver state (1 = IR modulation detected). */
|
||||
uint8_t ir_rx_get (void)
|
||||
{
|
||||
/* The output of the IR receiver (TSOP6136TR) is inverted. It is
|
||||
normally high but goes low when it detects modulated IR light. */
|
||||
|
||||
return !pio_input_get (IR_RX_PIO);
|
||||
}
|
||||
|
||||
|
||||
/** Initialise PIOs for IR transmitter LED and receiver. */
|
||||
void ir_init (void)
|
||||
{
|
||||
/* Configure transmitter PIOs as outputs and ensure IR LED off.
|
||||
Note that the IR LED is driven by two PIOs; to turn on the LED
|
||||
IR_TX_HIGH_PIO must be high and IR_TX_LOW_PIO must be low.
|
||||
IR_TX_HIGH_PIO is also connected to the timer 0 output so
|
||||
we can modulate the LED by the timer. IR_TX_LOW_PIO is also
|
||||
connected to the UART TXD output so we can drive it from the UART. */
|
||||
|
||||
pio_config_set (IR_TX_HIGH_PIO, PIO_OUTPUT_LOW);
|
||||
pio_config_set (IR_TX_LOW_PIO, PIO_OUTPUT_LOW);
|
||||
|
||||
/* Configure receiver PIO as input. IR_RX_PIO is also connected to the
|
||||
UART RXD input. */
|
||||
pio_config_set (IR_RX_PIO, PIO_INPUT);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/** @file ir.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief Infrared driver.
|
||||
|
||||
@defgroup IR Infrared (IR) driver
|
||||
|
||||
This module implements a simple hardware abstraction of an IR LED and
|
||||
IR receiver.
|
||||
*/
|
||||
|
||||
#ifndef IR_H
|
||||
#define IR_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
#ifndef IR_MODULATION_FREQ
|
||||
/** The nominal modulation frequency. This can be defined in system.h. */
|
||||
#define IR_MODULATION_FREQ 36e3
|
||||
#endif
|
||||
|
||||
|
||||
/** Modulate the IR transmitter LED at IR_MODULATION_FREQ.
|
||||
@param state is 1 to enable modulation, 0 to disable modulation
|
||||
@param count is the number of modulation periods
|
||||
@note This returns after @c count modulation periods. */
|
||||
void ir_tx_set (uint8_t state, uint16_t count);
|
||||
|
||||
|
||||
/** Return output state of IR receiver.
|
||||
@return IR receiver state (1 = IR modulation detected). */
|
||||
uint8_t ir_rx_get (void);
|
||||
|
||||
|
||||
/* Initialise PIOs for IR transmitter LED and receiver. */
|
||||
void ir_init (void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
/** @file ir_serial.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief Infrared serial driver.
|
||||
*/
|
||||
|
||||
/* This should probably be compiled with optimisation enabled
|
||||
otherwise some of the timing may be dodgy.
|
||||
For documentation of the protocol see ir_serial.h */
|
||||
|
||||
|
||||
#include "ir_serial.h"
|
||||
#include "delay.h"
|
||||
|
||||
#define IR_SERIAL_DIT_PERIOD 0.6e-3
|
||||
#define IR_SERIAL_MODULATIONS_PER_DIT ((int)(IR_SERIAL_DIT_PERIOD * IR_MODULATION_FREQ))
|
||||
#define IR_SERIAL_MODULATIONS_PER_START (IR_SERIAL_MODULATIONS_PER_DIT * 4)
|
||||
#define IR_SERIAL_MODULATIONS_PER_ONE (IR_SERIAL_MODULATIONS_PER_DIT * 2)
|
||||
#define IR_SERIAL_MODULATIONS_PER_ZERO (IR_SERIAL_MODULATIONS_PER_DIT)
|
||||
#define IR_SERIAL_MODULATIONS_PER_BREAK (IR_SERIAL_MODULATIONS_PER_DIT)
|
||||
|
||||
#define IR_SERIAL_DELAY_US 10
|
||||
#define IR_SERIAL_DIT_COUNT (1e6 * IR_SERIAL_DIT_PERIOD / IR_SERIAL_DELAY_US)
|
||||
#define IR_SERIAL_START_COUNT_MAX ((int)(4.5 * IR_SERIAL_DIT_COUNT))
|
||||
#define IR_SERIAL_ONE_COUNT_MAX ((int)(2.5 * IR_SERIAL_DIT_COUNT))
|
||||
#define IR_SERIAL_ZERO_COUNT_MAX ((int)(1.25 * IR_SERIAL_DIT_COUNT))
|
||||
#define IR_SERIAL_BREAK_COUNT_MAX ((int)(1.5 * IR_SERIAL_DIT_COUNT))
|
||||
|
||||
|
||||
/** Transmit a start code. */
|
||||
static inline void ir_serial_tx_start (void)
|
||||
{
|
||||
ir_tx_set (1, IR_SERIAL_MODULATIONS_PER_START);
|
||||
ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK);
|
||||
}
|
||||
|
||||
|
||||
/** Transmit a data bit. */
|
||||
static inline void ir_serial_tx_bit (uint8_t state)
|
||||
{
|
||||
ir_tx_set (1, state ? IR_SERIAL_MODULATIONS_PER_ONE
|
||||
: IR_SERIAL_MODULATIONS_PER_ZERO);
|
||||
ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK);
|
||||
}
|
||||
|
||||
|
||||
/** Transmit a stop code. */
|
||||
static inline void ir_serial_tx_stop (void)
|
||||
{
|
||||
ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK);
|
||||
}
|
||||
|
||||
|
||||
/** Transmit 8 bits of data over IR serial link.
|
||||
@param data byte to transmit
|
||||
@note No error checking is performed. This function blocks until
|
||||
the frame is transmitted. */
|
||||
void ir_serial_transmit (uint8_t data)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Send start code. */
|
||||
ir_serial_tx_start ();
|
||||
|
||||
/* Eight data bits LSB first. */
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
ir_serial_tx_bit (data & 1);
|
||||
data >>= 1;
|
||||
}
|
||||
|
||||
/* Send stop code. */
|
||||
ir_serial_tx_stop ();
|
||||
}
|
||||
|
||||
|
||||
/** Receive 8 bits of data over IR serial link.
|
||||
@param pdata pointer to byte to store received data
|
||||
@return status code
|
||||
@note No error checking is performed. If there is no activity on the
|
||||
IR serial link, this function returns immediately. Otherwise, this
|
||||
function blocks until the entire frame is received. */
|
||||
ir_serial_ret_t ir_serial_receive (uint8_t *pdata)
|
||||
{
|
||||
int i;
|
||||
int count;
|
||||
uint8_t data;
|
||||
bool data_err;
|
||||
|
||||
/* Check for start code; if not present return. */
|
||||
if (!ir_rx_get ())
|
||||
return IR_SERIAL_NONE;
|
||||
|
||||
/* Wait for end of start code or timeout. */
|
||||
for (count = 0; ir_rx_get (); count++)
|
||||
{
|
||||
if (count >= IR_SERIAL_START_COUNT_MAX)
|
||||
return IR_SERIAL_START_ERR;
|
||||
|
||||
DELAY_US (IR_SERIAL_DELAY_US);
|
||||
}
|
||||
|
||||
/* We may have received a rogue short pulse but we may have been
|
||||
called closed to the falling transition. */
|
||||
|
||||
data_err = 0;
|
||||
data = 0;
|
||||
|
||||
/* Eight data bits LSB first. */
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
data >>= 1;
|
||||
|
||||
/* Wait for IR modulation to start or timeout (indicating
|
||||
detection of a false start code). */
|
||||
for (count = 0; !ir_rx_get (); count++)
|
||||
{
|
||||
if (count >= IR_SERIAL_BREAK_COUNT_MAX)
|
||||
return IR_SERIAL_BREAK_ERR;
|
||||
|
||||
DELAY_US (IR_SERIAL_DELAY_US);
|
||||
}
|
||||
|
||||
/* If there is another transmission in this slot we will get
|
||||
too small a value for dit_count. This is likely to trigger
|
||||
a bit error. */
|
||||
for (count = 0; count < IR_SERIAL_ONE_COUNT_MAX
|
||||
&& ir_rx_get (); count++)
|
||||
{
|
||||
DELAY_US (IR_SERIAL_DELAY_US);
|
||||
}
|
||||
|
||||
if (count >= IR_SERIAL_ONE_COUNT_MAX)
|
||||
data_err = 1;
|
||||
|
||||
if (count >= IR_SERIAL_ZERO_COUNT_MAX)
|
||||
data |= 0x80;
|
||||
}
|
||||
|
||||
/* Perhaps should check for stop code. If not found, there is
|
||||
likely to have been interference from another transmitter. */
|
||||
|
||||
*pdata = data;
|
||||
return data_err ? IR_SERIAL_DATA_ERR : IR_SERIAL_OK;
|
||||
}
|
||||
|
||||
|
||||
/** Initialise IR serial driver. */
|
||||
void ir_serial_init (void)
|
||||
{
|
||||
ir_init ();
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/** @file ir_serial.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief Infrared serial driver.
|
||||
|
||||
@defgroup IR_serial Infrared serial driver
|
||||
|
||||
This module implements a simple serial protocol over an IR link.
|
||||
It uses a pulse width modulation technique similar to the Sony
|
||||
SIRC protocol. A message consists of a start code, 8 data bits
|
||||
(transmitted LSB first), and a stop code. Each code is comprised
|
||||
of 0.6 ms intervals called a dit:
|
||||
|
||||
Start bit: four dits on, one dit off
|
||||
|
||||
One bit: two dits on, one dit off
|
||||
|
||||
Zero bit: one dit on, one dit off
|
||||
|
||||
Stop bit: one dit off
|
||||
|
||||
Note, the duration of a message depends on the number of non-zero
|
||||
bits in the data byte. The shortest message duration is 22 dits
|
||||
(13.2 ms) and the longest message is 30 dits (18 ms). No error
|
||||
detection or correction is performed apart from checking of
|
||||
invalid codes.
|
||||
|
||||
Here's a simple application that transmits a data byte of value 7.
|
||||
|
||||
@code
|
||||
#include "ir_serial.h"
|
||||
|
||||
void main (void)
|
||||
{
|
||||
system_init ();
|
||||
ir_serial_init ();
|
||||
|
||||
ir_serial_transmit (7);
|
||||
|
||||
while (1)
|
||||
{
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
|
||||
Here's another simple application that receives a data byte.
|
||||
|
||||
@code
|
||||
#include "ir_serial.h"
|
||||
#include "pacer.h"
|
||||
|
||||
void main (void)
|
||||
{
|
||||
system_init ();
|
||||
ir_serial_init ();
|
||||
|
||||
pacer_init (1000);
|
||||
|
||||
while (1)
|
||||
{
|
||||
ir_serial_ret_t ret;
|
||||
|
||||
pacer_wait ();
|
||||
|
||||
ret = ir_serial_receive (&data);
|
||||
if (ret == IR_SERIAL_OK)
|
||||
{
|
||||
// Process the received byte.
|
||||
}
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
|
||||
#ifndef IR_SERIAL_H
|
||||
#define IR_SERIAL_H
|
||||
|
||||
#include "ir.h"
|
||||
|
||||
/** Status return codes. */
|
||||
typedef enum ir_serial_ret
|
||||
{
|
||||
/** A valid frame has been received. */
|
||||
IR_SERIAL_OK = 1,
|
||||
/** No data to read. */
|
||||
IR_SERIAL_NONE = 0,
|
||||
/** Invalid start code detected. */
|
||||
IR_SERIAL_START_ERR = -1,
|
||||
/** Invalid data code detected. */
|
||||
IR_SERIAL_DATA_ERR = -2,
|
||||
/** Invalid break code detected. */
|
||||
IR_SERIAL_BREAK_ERR = -3,
|
||||
/** Invalid stop code detected. */
|
||||
IR_SERIAL_STOP_ERR = -4
|
||||
} ir_serial_ret_t;
|
||||
|
||||
|
||||
/** Transmit 8 bits of data over IR serial link.
|
||||
@param data byte to transmit
|
||||
@note No error checking is performed. This function blocks until
|
||||
the frame is transmitted. */
|
||||
void ir_serial_transmit (uint8_t data);
|
||||
|
||||
|
||||
/** Receive 8 bits of data over IR serial link.
|
||||
@param pdata pointer to byte to store received data
|
||||
@return status code
|
||||
@note No error checking is performed. If there is no activity on the
|
||||
IR serial link, this function returns immediately. Otherwise, this
|
||||
function blocks until the entire frame is received. */
|
||||
ir_serial_ret_t ir_serial_receive (uint8_t *pdata);
|
||||
|
||||
|
||||
/** Initialise IR serial driver. */
|
||||
void ir_serial_init (void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/** @file led.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 15 Feb 2003
|
||||
@brief LED driver.
|
||||
*/
|
||||
|
||||
#include "system.h"
|
||||
#include "pio.h"
|
||||
#include "led.h"
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pio_t pio;
|
||||
/* True for active high, false for active low. */
|
||||
bool active_high;
|
||||
} led_cfg_t;
|
||||
|
||||
|
||||
/* Define LEDs. */
|
||||
static const led_cfg_t leds_cfg[] =
|
||||
{
|
||||
{.pio = LED1_PIO, .active_high = 1},
|
||||
/* Add config data for additional LEDs here. */
|
||||
};
|
||||
|
||||
#define LEDS_NUM ARRAY_SIZE (leds_cfg)
|
||||
|
||||
|
||||
void led_set (uint8_t led, bool state)
|
||||
{
|
||||
if (led >= LEDS_NUM)
|
||||
return;
|
||||
pio_output_set (leds_cfg[led].pio,
|
||||
leds_cfg[led].active_high ? state : !state);
|
||||
}
|
||||
|
||||
|
||||
/** Initialise LED driver. */
|
||||
void led_init (void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
for (i = 0; i < LEDS_NUM; i++)
|
||||
{
|
||||
pio_config_set (leds_cfg[i].pio,
|
||||
leds_cfg[i].active_high ? PIO_OUTPUT_HIGH
|
||||
: PIO_OUTPUT_LOW);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/** @file led.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 15 Feb 2003
|
||||
@brief LED driver.
|
||||
|
||||
@defgroup LED LED driver
|
||||
|
||||
This module implements a simple LED driver.
|
||||
|
||||
Here's an example application that turns on a LED:
|
||||
|
||||
@code
|
||||
#include "led.h"
|
||||
|
||||
void main (void)
|
||||
{
|
||||
system_init ();
|
||||
led_init ();
|
||||
|
||||
led_set (LED1, 1);
|
||||
|
||||
while (1)
|
||||
{
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
|
||||
#ifndef LED_H
|
||||
#define LED_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
|
||||
/** Set LED to desired state.
|
||||
@param led LED to select
|
||||
@param state desired state (non-zero for on). */
|
||||
void led_set (uint8_t led, bool state);
|
||||
|
||||
|
||||
/** Initialise LED driver. */
|
||||
void led_init (void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,78 @@
|
||||
/** @file ledmat.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief LED matrix driver.
|
||||
*/
|
||||
#include "system.h"
|
||||
#include "pio.h"
|
||||
#include "ledmat.h"
|
||||
|
||||
|
||||
/** Define PIO pins driving LED matrix rows. */
|
||||
static const pio_t ledmat_rows[] =
|
||||
{
|
||||
LEDMAT_ROW1_PIO, LEDMAT_ROW2_PIO, LEDMAT_ROW3_PIO,
|
||||
LEDMAT_ROW4_PIO, LEDMAT_ROW5_PIO, LEDMAT_ROW6_PIO,
|
||||
LEDMAT_ROW7_PIO
|
||||
};
|
||||
|
||||
|
||||
/** Define PIO pins driving LED matrix columns. */
|
||||
static const pio_t ledmat_cols[] =
|
||||
{
|
||||
LEDMAT_COL1_PIO, LEDMAT_COL2_PIO, LEDMAT_COL3_PIO,
|
||||
LEDMAT_COL4_PIO, LEDMAT_COL5_PIO
|
||||
};
|
||||
|
||||
|
||||
/** Initialise PIO pins to drive LED matrix. */
|
||||
void ledmat_init (void)
|
||||
{
|
||||
uint8_t row;
|
||||
uint8_t col;
|
||||
|
||||
for (row = 0; row < LEDMAT_ROWS_NUM; row++)
|
||||
{
|
||||
/* The rows are active low so configure PIO as an initially
|
||||
high output. */
|
||||
pio_config_set (ledmat_rows[row], PIO_OUTPUT_HIGH);
|
||||
}
|
||||
|
||||
for (col = 0; col < LEDMAT_COLS_NUM; col++)
|
||||
{
|
||||
/* The columns are active low so configure PIO as an initially
|
||||
high output. */
|
||||
pio_config_set (ledmat_cols[col], PIO_OUTPUT_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Display pattern on specified column.
|
||||
@param pattern bit pattern to display for selected column
|
||||
@param col selected column. */
|
||||
void ledmat_display_column (uint8_t pattern, uint8_t col)
|
||||
{
|
||||
static uint8_t col_prev = 0;
|
||||
uint8_t row;
|
||||
|
||||
/* Disable previous column to prevent ghosting while rows modified. */
|
||||
pio_output_high (ledmat_cols[col_prev]);
|
||||
|
||||
/* Activate desired rows based on desired pattern. */
|
||||
for (row = 0; row < LEDMAT_ROWS_NUM; row++)
|
||||
{
|
||||
/* The rows are active low. */
|
||||
if (pattern & 1)
|
||||
pio_output_low (ledmat_rows[row]);
|
||||
else
|
||||
pio_output_high (ledmat_rows[row]);
|
||||
|
||||
pattern >>= 1;
|
||||
}
|
||||
|
||||
/* Enable new column. */
|
||||
pio_output_low (ledmat_cols[col]);
|
||||
col_prev = col;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/** @file ledmat.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 23 August 2010
|
||||
@brief LED matrix driver.
|
||||
|
||||
@defgroup ledmat LED matrix driver
|
||||
|
||||
This module implements a simple hardware abstraction of a LED matrix.
|
||||
*/
|
||||
#ifndef LEDMAT_H
|
||||
#define LEDMAT_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
/** Initialise PIO pins to drive LED matrix. */
|
||||
void ledmat_init (void);
|
||||
|
||||
|
||||
/** Display pattern on specified column.
|
||||
@param pattern bit pattern to display for selected column
|
||||
@param col selected column. */
|
||||
void ledmat_display_column (uint8_t pattern, uint8_t col);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
/** @file navswitch.c
|
||||
@author M.P. Hayes
|
||||
@date 28 Aug 2008
|
||||
@brief Navswitch driver.
|
||||
@note This polls the 5-way navigation switch
|
||||
but does not do debouncing.
|
||||
*/
|
||||
|
||||
#include "system.h"
|
||||
#include "navswitch.h"
|
||||
#include "delay.h"
|
||||
#include "pio.h"
|
||||
|
||||
|
||||
/* Navswitch configuration structure. */
|
||||
typedef struct
|
||||
{
|
||||
pio_t pio;
|
||||
} navswitch_cfg_t;
|
||||
|
||||
|
||||
/** Navswitch state structure. */
|
||||
typedef struct
|
||||
{
|
||||
bool current;
|
||||
bool previous;
|
||||
} navswitch_state_t;
|
||||
|
||||
|
||||
/* Define navswitch PIO connections. */
|
||||
static const navswitch_cfg_t navswitch_cfg[] =
|
||||
{
|
||||
{NAVSWITCH_NORTH_PIO},
|
||||
{NAVSWITCH_EAST_PIO},
|
||||
{NAVSWITCH_SOUTH_PIO},
|
||||
{NAVSWITCH_WEST_PIO},
|
||||
{NAVSWITCH_PUSH_PIO}
|
||||
};
|
||||
|
||||
#define NAVSWITCH_NUM ARRAY_SIZE (navswitch_cfg)
|
||||
|
||||
static navswitch_state_t navswitch_state[NAVSWITCH_NUM];
|
||||
|
||||
|
||||
/** Return true if navswitch state changed from up to down since
|
||||
last call to navswitch_update
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch changed from up to down otherwise 0 */
|
||||
bool
|
||||
navswitch_push_event_p (uint8_t navswitch)
|
||||
{
|
||||
return navswitch_state[navswitch].current
|
||||
&& !navswitch_state[navswitch].previous;
|
||||
}
|
||||
|
||||
|
||||
/** Return true if navswitch state changed from down to up since
|
||||
last call to navswitch_update
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch changed from down to up otherwise 0 */
|
||||
bool
|
||||
navswitch_release_event_p (uint8_t navswitch)
|
||||
{
|
||||
return !navswitch_state[navswitch].current
|
||||
&& navswitch_state[navswitch].previous;
|
||||
}
|
||||
|
||||
|
||||
/** Return true if navswitch down (pushed).
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch down otherwise 0 */
|
||||
bool
|
||||
navswitch_down_p (uint8_t navswitch)
|
||||
{
|
||||
return navswitch_state[navswitch].current;
|
||||
}
|
||||
|
||||
|
||||
/** Return true if navswitch up (released).
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch down otherwise 0 */
|
||||
bool
|
||||
navswitch_up_p (uint8_t navswitch)
|
||||
{
|
||||
return !navswitch_state[navswitch].current;
|
||||
}
|
||||
|
||||
|
||||
/** Poll all the navswitches and update their state. */
|
||||
void
|
||||
navswitch_update (void)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* The switch is a 5-way navigation switch with push. The common
|
||||
connection is connected to ground. The 5 switch outputs share
|
||||
the LED matrix display column drive pins so we need to save the
|
||||
state of the column outputs, switch them to inputs, read the
|
||||
switch states, switch them back to outputs, and restore the
|
||||
output state.
|
||||
|
||||
When a switch is pushed it will pull its associated PIO pin low
|
||||
via a 2.2 kohm resistor. When the PIO pin is configured as an
|
||||
input with pullup, pushing the switch will also turn on the
|
||||
column MOSFET driver. This will cause some ghosting of the
|
||||
display. This can be avoided by driving all the LED matrix
|
||||
rows low while we read the switches. */
|
||||
|
||||
for (i = 0; i < NAVSWITCH_NUM; i++)
|
||||
{
|
||||
pio_config_t config;
|
||||
|
||||
navswitch_state[i].previous = navswitch_state[i].current;
|
||||
|
||||
config = pio_config_get (navswitch_cfg[i].pio);
|
||||
|
||||
/* Momentarily force PIO pin high to charge gate capacitance of
|
||||
MOSFET otherwise will always read logic low and think the
|
||||
switch is pushed. Alternatively, we need to wait 10 us or
|
||||
more so that the gate capacitance of the MOSFET charges via
|
||||
the pullup resistor. */
|
||||
pio_config_set (navswitch_cfg[i].pio, PIO_OUTPUT_HIGH);
|
||||
|
||||
/* Switch PIO to an input to read switch. */
|
||||
pio_config_set (navswitch_cfg[i].pio, PIO_PULLUP);
|
||||
navswitch_state[i].current = pio_input_get (navswitch_cfg[i].pio) == 0;
|
||||
|
||||
/* Restore PIO state. */
|
||||
pio_config_set (navswitch_cfg[i].pio, config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Initialise navswitch driver. */
|
||||
void navswitch_init (void)
|
||||
{
|
||||
/* Nothing to do since we configure PIOs when polling the switch. */
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/** @file navswitch.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 15 Feb 2003
|
||||
@brief Navswitch polling (no debouncing).
|
||||
|
||||
@defgroup navswitch Navswitch driver
|
||||
|
||||
This module implements a simple navswitch driver. Note, no
|
||||
debouncing is performed.
|
||||
|
||||
Here's an example application where the navswitches are polled at 50 Hz:
|
||||
|
||||
@code
|
||||
#include "pacer.h"
|
||||
#include "navswitch.h"
|
||||
|
||||
void main (void)
|
||||
{
|
||||
system_init ();
|
||||
navswitch_init ();
|
||||
pacer_init (50);
|
||||
|
||||
while (1)
|
||||
{
|
||||
pacer_wait ();
|
||||
navswitch_update ();
|
||||
|
||||
if (navswitch_push_event_p (NAVSWITCH_EAST))
|
||||
// Do something
|
||||
|
||||
if (navswitch_push_event_p (NAVSWITCH_WEST))
|
||||
// Do something
|
||||
}
|
||||
}
|
||||
@endcode
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NAVSWITCH_H
|
||||
#define NAVSWITCH_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
|
||||
enum {NAVSWITCH_NORTH, NAVSWITCH_EAST, NAVSWITCH_SOUTH, NAVSWITCH_WEST,
|
||||
NAVSWITCH_PUSH};
|
||||
|
||||
|
||||
/** Poll all the navswitches and update their state. */
|
||||
void navswitch_update (void);
|
||||
|
||||
|
||||
/** Return true if navswitch state changed from up to down since
|
||||
last call to navswitch_update
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch changed from up to down otherwise 0 */
|
||||
bool navswitch_push_event_p (uint8_t navswitch);
|
||||
|
||||
|
||||
/** Return true if navswitch state changed from down to up since
|
||||
last call to navswitch_update
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch changed from down to up otherwise 0 */
|
||||
bool navswitch_release_event_p (uint8_t navswitch);
|
||||
|
||||
|
||||
/** Return true if navswitch down (pushed).
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch down otherwise 0 */
|
||||
bool navswitch_down_p (uint8_t navswitch);
|
||||
|
||||
|
||||
/** Return true if navswitch up (released).
|
||||
@param navswitch index of navswitch to select
|
||||
@return 1 if navswitch down otherwise 0 */
|
||||
bool navswitch_up_p (uint8_t navswitch);
|
||||
|
||||
|
||||
/** Initialise navswitch driver. */
|
||||
void navswitch_init (void);
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
#ifndef AVRTEST_H
|
||||
#define AVRTEST_H
|
||||
|
||||
#include "system.h"
|
||||
|
||||
typedef struct SFR
|
||||
{
|
||||
uint8_t PORTB;
|
||||
uint8_t DDRB;
|
||||
uint8_t PINB;
|
||||
uint8_t PORTC;
|
||||
uint8_t DDRC;
|
||||
uint8_t PINC;
|
||||
uint8_t PORTD;
|
||||
uint8_t DDRD;
|
||||
uint8_t PIND;
|
||||
} SFR_t;
|
||||
|
||||
|
||||
#define PORTB SFR.PORTB
|
||||
#define PORTC SFR.PORTC
|
||||
#define PORTD SFR.PORTD
|
||||
#define DDRB SFR.DDRB
|
||||
#define DDRC SFR.DDRC
|
||||
#define DDRD SFR.DDRD
|
||||
#define PINB SFR.PINB
|
||||
#define PINC SFR.PINC
|
||||
#define PIND SFR.PINC
|
||||
|
||||
extern SFR_t SFR;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1 @@
|
||||
#define DELAY_US(US)
|
||||
@@ -0,0 +1,85 @@
|
||||
/** @file eeprom.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 15 May 2007
|
||||
@brief
|
||||
*/
|
||||
#include "system.h"
|
||||
#include "eeprom.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define EEPROM_SIZE 512
|
||||
#define EEPROM_FILENAME "ucfk_eeprom.dat"
|
||||
|
||||
|
||||
static void
|
||||
eeprom_file_write (void *buffer, eeprom_size_t size)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen (EEPROM_FILENAME, "w");
|
||||
if (fp)
|
||||
{
|
||||
fwrite (buffer, size, 1, fp);
|
||||
fclose (fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
eeprom_file_read (void *buffer, eeprom_size_t size)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen (EEPROM_FILENAME, "r");
|
||||
if (!fp)
|
||||
{
|
||||
/* If file not present assume EEPROM is erased and thus
|
||||
contains the values 0xff. */
|
||||
memset (buffer, 0xff, size);
|
||||
eeprom_file_write (buffer, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
fread (buffer, size, 1, fp);
|
||||
fclose (fp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* 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;
|
||||
uint8_t eeprom[EEPROM_SIZE];
|
||||
|
||||
eeprom_file_read (eeprom, EEPROM_SIZE);
|
||||
|
||||
for (i = 0; i < size && addr < EEPROM_SIZE; i++)
|
||||
data[i] = eeprom[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;
|
||||
uint8_t eeprom[EEPROM_SIZE];
|
||||
|
||||
eeprom_file_read (eeprom, EEPROM_SIZE);
|
||||
|
||||
for (i = 0; i < size && addr < EEPROM_SIZE; i++)
|
||||
eeprom[addr++] = data[i];
|
||||
|
||||
eeprom_file_write (eeprom, EEPROM_SIZE);
|
||||
|
||||
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,64 @@
|
||||
/** @file ir_uart.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 1 Aug 2011
|
||||
@brief This is just a stub that does nothing.
|
||||
*/
|
||||
#include "system.h"
|
||||
#include "ir_uart.h"
|
||||
|
||||
|
||||
/* Return non-zero if there is a character ready to be read. */
|
||||
bool
|
||||
ir_uart_read_ready_p (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Read character from IR_UART. This blocks if nothing
|
||||
is available to read. */
|
||||
int8_t
|
||||
ir_uart_getc (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return non-zero if a character can be written without blocking. */
|
||||
bool
|
||||
ir_uart_write_ready_p (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Return non-zero if transmitter finished. */
|
||||
bool
|
||||
ir_uart_write_finished_p (void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Write character to IR_UART. This returns zero if
|
||||
the character could not be written. */
|
||||
int8_t
|
||||
ir_uart_putc (__unused__ char ch)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Write string to IR_UART. */
|
||||
void
|
||||
ir_uart_puts (__unused__ const char *str)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/* Initialise ir_uart and set baud rate. */
|
||||
uint8_t
|
||||
ir_uart_init (void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/** @file ir_uart.h
|
||||
@author Michael Hayes
|
||||
@date 10 December 2004
|
||||
*/
|
||||
|
||||
#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,94 @@
|
||||
/** @file mgetkey.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 17 July 2011
|
||||
@brief Non-blocking raw keyboard reading.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "mgetkey.h"
|
||||
|
||||
static struct termios tty_attr_save;
|
||||
static int tty_init = 0;
|
||||
|
||||
|
||||
/* Get the terminal attributes. */
|
||||
static int mgetkey_attr_get (struct termios *ptty_attr)
|
||||
{
|
||||
if (tcgetattr (STDIN_FILENO, ptty_attr) == -1)
|
||||
{
|
||||
perror ("mgetkey: Cannot get tty attributes");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Set terminal attributes ensuring that the original attributes are
|
||||
saved. */
|
||||
static int mgetkey_attr_set (struct termios *ptty_attr)
|
||||
{
|
||||
if (tcsetattr (STDIN_FILENO, TCSANOW, ptty_attr) == -1)
|
||||
{
|
||||
perror ("mgetkey: Cannot set tty attributes");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int mgetkey_init (void)
|
||||
{
|
||||
struct termios tty_attr;
|
||||
|
||||
if (!tty_init)
|
||||
{
|
||||
if (!mgetkey_attr_get (&tty_attr_save))
|
||||
return 0;
|
||||
|
||||
/* Restore terminal attributes on exit. */
|
||||
atexit (mgetkey_reset);
|
||||
tty_init = 1;
|
||||
}
|
||||
|
||||
/* Get terminal attributes. */
|
||||
if (!mgetkey_attr_get (&tty_attr))
|
||||
return 0;
|
||||
|
||||
tty_attr.c_lflag &= ~ECHO; /* No echo. */
|
||||
tty_attr.c_lflag &= ~ICANON; /* No canonical processing. */
|
||||
tty_attr.c_cc[VMIN] = 0; /* Don't block. */
|
||||
tty_attr.c_cc[VTIME] = 0; /* Don't block. */
|
||||
|
||||
/* Set terminal attributes. */
|
||||
return mgetkey_attr_set (&tty_attr);
|
||||
}
|
||||
|
||||
|
||||
/* Restore the terminal attributes. */
|
||||
void mgetkey_reset (void)
|
||||
{
|
||||
mgetkey_attr_set (&tty_attr_save);
|
||||
}
|
||||
|
||||
|
||||
/* Read a single key stroke from the keyboard. This blocks until
|
||||
a key is typed. Note that the key is not echoed. */
|
||||
int mgetkey (void)
|
||||
{
|
||||
int key;
|
||||
|
||||
if (!tty_init)
|
||||
mgetkey_init ();
|
||||
|
||||
key = getchar ();
|
||||
if (key == -1)
|
||||
key = 0;
|
||||
return key;
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/** @file mgetkey.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 17 July 2011
|
||||
@brief Non-blocking raw keyboard reading.
|
||||
*/
|
||||
|
||||
#ifndef MGETKEY_H
|
||||
#define MGETKEY_H
|
||||
|
||||
/* Read a single key stroke from the keyboard. If no key has been
|
||||
pressed then zero is returned. Note that the key is not
|
||||
echoed. */
|
||||
int mgetkey (void);
|
||||
|
||||
|
||||
/* Restore terminal settings. */
|
||||
void mgetkey_reset (void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
/** @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.
|
||||
*/
|
||||
@@ -0,0 +1,265 @@
|
||||
/** @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"
|
||||
#include "avrtest.h"
|
||||
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
|
||||
/** Set input state for pio (this is only for use by the test scaffold).
|
||||
@param pio
|
||||
@param state value for pio input */
|
||||
static inline
|
||||
void pio_input_set (pio_t pio, bool state)
|
||||
{
|
||||
if (state)
|
||||
PIO_PIN_ (pio) |= PIO_BITMASK_ (pio);
|
||||
else
|
||||
PIO_PIN_ (pio) &= ~PIO_BITMASK_ (pio);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,402 @@
|
||||
/** @file system.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 15 May 2007
|
||||
@brief Test scaffold for UCFK4.
|
||||
*/
|
||||
|
||||
#include "system.h"
|
||||
#include "avrtest.h"
|
||||
#include "pio.h"
|
||||
#include "mgetkey.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
/** This needs to be at least 50 * 5 for a refresh rate of 50 Hz. */
|
||||
#define SYSTEM_UPDATE_RATE 400
|
||||
|
||||
#define SYSTEM_KEYBOARD_RELEASE_PERIOD 0.2
|
||||
|
||||
#define SYSTEM_DISPLAY_PERSISTENCE_PERIOD 0.01
|
||||
|
||||
|
||||
SFR_t SFR = {0, 0, ~0, 0, 0, ~0, 0, 0, ~0};
|
||||
|
||||
|
||||
/* Define PIO pins driving LED matrix rows and columns. */
|
||||
static const pio_t ledmat_rows[] =
|
||||
{
|
||||
LEDMAT_ROW1_PIO,
|
||||
LEDMAT_ROW2_PIO,
|
||||
LEDMAT_ROW3_PIO,
|
||||
LEDMAT_ROW4_PIO,
|
||||
LEDMAT_ROW5_PIO,
|
||||
LEDMAT_ROW6_PIO,
|
||||
LEDMAT_ROW7_PIO
|
||||
};
|
||||
|
||||
static const pio_t ledmat_cols[] =
|
||||
{
|
||||
LEDMAT_COL1_PIO,
|
||||
LEDMAT_COL2_PIO,
|
||||
LEDMAT_COL3_PIO,
|
||||
LEDMAT_COL4_PIO,
|
||||
LEDMAT_COL5_PIO
|
||||
};
|
||||
|
||||
|
||||
typedef enum {NAVSWITCH_NONE, NAVSWITCH_EAST, NAVSWITCH_SOUTH,
|
||||
NAVSWITCH_WEST, NAVSWITCH_NORTH, NAVSWITCH_PUSH} system_navswitch_t;
|
||||
|
||||
typedef enum {BUTTON_NONE, BUTTON_1} system_button_t;
|
||||
|
||||
|
||||
static void system_display_update (void)
|
||||
{
|
||||
int row;
|
||||
int col;
|
||||
/* Persistence time (s). */
|
||||
double tau = SYSTEM_DISPLAY_PERSISTENCE_PERIOD;
|
||||
/* Sample period (s). */
|
||||
double delta_t;
|
||||
/* Exponential filter parameter. */
|
||||
double alpha;
|
||||
static uint8_t tdisplay[LEDMAT_ROWS_NUM][LEDMAT_COLS_NUM];
|
||||
|
||||
delta_t = 1.0 / SYSTEM_UPDATE_RATE;
|
||||
alpha = tau / (tau + delta_t);
|
||||
|
||||
for (row = 0; row < LEDMAT_ROWS_NUM; row++)
|
||||
{
|
||||
for (col = 0; col < LEDMAT_COLS_NUM; col++)
|
||||
{
|
||||
uint8_t on;
|
||||
|
||||
/* Determine if pixel is on. */
|
||||
on = !pio_output_get (ledmat_rows[row])
|
||||
&& !pio_output_get (ledmat_cols[col]);
|
||||
|
||||
/* Apply exponential filter to provide some persistence.
|
||||
This has an impulse response of exp(-t / tau) u(t). */
|
||||
tdisplay[row][col] = tdisplay[row][col] * alpha + (1 - alpha) * 100 * on;
|
||||
|
||||
printf ("%c", tdisplay[row][col] > 1 ? '@' : '.');
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
printf ("\n");
|
||||
/* Move cursor up. */
|
||||
printf ("\e[%dA", LEDMAT_ROWS_NUM + 1);
|
||||
}
|
||||
|
||||
|
||||
static void system_navswitch_set (system_navswitch_t navswitch, bool state)
|
||||
{
|
||||
switch (navswitch)
|
||||
{
|
||||
case NAVSWITCH_NONE:
|
||||
break;
|
||||
|
||||
case NAVSWITCH_NORTH:
|
||||
pio_input_set (NAVSWITCH_NORTH_PIO, !state);
|
||||
break;
|
||||
|
||||
case NAVSWITCH_EAST:
|
||||
pio_input_set (NAVSWITCH_EAST_PIO, !state);
|
||||
break;
|
||||
|
||||
case NAVSWITCH_SOUTH:
|
||||
pio_input_set (NAVSWITCH_SOUTH_PIO, !state);
|
||||
break;
|
||||
|
||||
case NAVSWITCH_WEST:
|
||||
pio_input_set (NAVSWITCH_WEST_PIO, !state);
|
||||
break;
|
||||
|
||||
case NAVSWITCH_PUSH:
|
||||
pio_input_set (NAVSWITCH_PUSH_PIO, !state);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void system_navswitch_press (system_navswitch_t navswitch)
|
||||
{
|
||||
system_navswitch_set (navswitch, 1);
|
||||
}
|
||||
|
||||
|
||||
static void system_navswitch_release (system_navswitch_t navswitch)
|
||||
{
|
||||
system_navswitch_set (navswitch, 0);
|
||||
}
|
||||
|
||||
|
||||
static void system_navswitch_select (system_navswitch_t navswitch)
|
||||
{
|
||||
static system_navswitch_t last = NAVSWITCH_NONE;
|
||||
static int counter;
|
||||
|
||||
if (navswitch == NAVSWITCH_NONE)
|
||||
{
|
||||
if (counter)
|
||||
{
|
||||
counter--;
|
||||
if (!counter)
|
||||
{
|
||||
system_navswitch_release (last);
|
||||
last = NAVSWITCH_NONE;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
counter = SYSTEM_UPDATE_RATE * SYSTEM_KEYBOARD_RELEASE_PERIOD;
|
||||
|
||||
if (last == navswitch)
|
||||
return;
|
||||
|
||||
system_navswitch_release (last);
|
||||
|
||||
last = navswitch;
|
||||
|
||||
system_navswitch_press (navswitch);
|
||||
}
|
||||
|
||||
|
||||
static void system_button_set (system_button_t button, bool state)
|
||||
{
|
||||
switch (button)
|
||||
{
|
||||
case BUTTON_NONE:
|
||||
break;
|
||||
|
||||
case BUTTON_1:
|
||||
/* Active high. */
|
||||
pio_input_set (BUTTON1_PIO, state);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void system_button_press (system_button_t button)
|
||||
{
|
||||
system_button_set (button, 1);
|
||||
}
|
||||
|
||||
|
||||
static void system_button_release (system_button_t button)
|
||||
{
|
||||
system_button_set (button, 0);
|
||||
}
|
||||
|
||||
|
||||
static void system_button_select (system_button_t button)
|
||||
{
|
||||
static system_button_t last = BUTTON_NONE;
|
||||
static int counter;
|
||||
|
||||
if (button == BUTTON_NONE)
|
||||
{
|
||||
if (counter)
|
||||
{
|
||||
counter--;
|
||||
if (!counter)
|
||||
{
|
||||
system_button_release (last);
|
||||
last = BUTTON_NONE;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
counter = SYSTEM_UPDATE_RATE * SYSTEM_KEYBOARD_RELEASE_PERIOD;
|
||||
|
||||
if (last == button)
|
||||
return;
|
||||
|
||||
system_button_release (last);
|
||||
|
||||
last = button;
|
||||
|
||||
system_button_press (button);
|
||||
}
|
||||
|
||||
|
||||
static void system_keyboard_init (void)
|
||||
{
|
||||
system_navswitch_release (NAVSWITCH_PUSH);
|
||||
system_navswitch_release (NAVSWITCH_NORTH);
|
||||
system_navswitch_release (NAVSWITCH_EAST);
|
||||
system_navswitch_release (NAVSWITCH_SOUTH);
|
||||
system_navswitch_release (NAVSWITCH_WEST);
|
||||
|
||||
system_button_release (BUTTON_1);
|
||||
}
|
||||
|
||||
|
||||
static void system_keyboard_update (void)
|
||||
{
|
||||
static int state = 0;
|
||||
int key;
|
||||
|
||||
system_navswitch_select (NAVSWITCH_NONE);
|
||||
system_button_select (BUTTON_NONE);
|
||||
|
||||
key = mgetkey ();
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case 0:
|
||||
switch (key)
|
||||
{
|
||||
case '\e':
|
||||
/* Have escape character. */
|
||||
state = 1;
|
||||
break;
|
||||
|
||||
case ' ':
|
||||
system_navswitch_select (NAVSWITCH_PUSH);
|
||||
break;
|
||||
|
||||
case '.':
|
||||
system_button_select (BUTTON_1);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (key == '[')
|
||||
state = 2;
|
||||
else
|
||||
state = 0;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
state = 0;
|
||||
switch (key)
|
||||
{
|
||||
case 'A':
|
||||
system_navswitch_select (NAVSWITCH_NORTH);
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
system_navswitch_select (NAVSWITCH_SOUTH);
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
system_navswitch_select (NAVSWITCH_EAST);
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
system_navswitch_select (NAVSWITCH_WEST);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void system_update (void)
|
||||
{
|
||||
system_display_update ();
|
||||
system_keyboard_update ();
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
system_handler (__unused__ int sig, __unused__ siginfo_t *si,
|
||||
__unused__ void *uc)
|
||||
{
|
||||
system_update ();
|
||||
}
|
||||
|
||||
|
||||
typedef void (*sighandler_t)(int, siginfo_t *, void *);
|
||||
|
||||
static int system_interrupt_init (sighandler_t handler, int rate)
|
||||
{
|
||||
timer_t timerid;
|
||||
struct sigevent sev;
|
||||
struct itimerspec its;
|
||||
long long period_ns;
|
||||
sigset_t mask;
|
||||
struct sigaction sa;
|
||||
static int signum = 0;
|
||||
|
||||
/* When debugging with gdb use "handle SIG34 noprint". */
|
||||
|
||||
if (!signum)
|
||||
signum = SIGRTMIN;
|
||||
|
||||
/* Establish handler for timer signal. */
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
sa.sa_sigaction = handler;
|
||||
sigemptyset (&sa.sa_mask);
|
||||
if (sigaction (signum, &sa, NULL) == -1)
|
||||
{
|
||||
perror ("sigaction");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Block timer signal temporarily. */
|
||||
sigemptyset (&mask);
|
||||
sigaddset (&mask, signum);
|
||||
if (sigprocmask (SIG_SETMASK, &mask, NULL) == -1)
|
||||
{
|
||||
perror ("sigprocmask");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Create the timer. */
|
||||
sev.sigev_notify = SIGEV_SIGNAL;
|
||||
sev.sigev_signo = signum;
|
||||
sev.sigev_value.sival_ptr = &timerid;
|
||||
if (timer_create (CLOCK_REALTIME, &sev, &timerid) == -1)
|
||||
{
|
||||
perror ("timer_create");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Start the timer */
|
||||
period_ns = 1000000000 / rate;
|
||||
its.it_value.tv_sec = period_ns / 1000000000;
|
||||
its.it_value.tv_nsec = period_ns % 1000000000;
|
||||
its.it_interval.tv_sec = its.it_value.tv_sec;
|
||||
its.it_interval.tv_nsec = its.it_value.tv_nsec;
|
||||
if (timer_settime (timerid, 0, &its, NULL) == -1)
|
||||
{
|
||||
perror ("timer_settime");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Unlock the timer signal, so that timer notification
|
||||
can be delivered. */
|
||||
if (sigprocmask (SIG_UNBLOCK, &mask, NULL) == -1)
|
||||
{
|
||||
perror ("sigprocmask");
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
signum++;
|
||||
return signum;
|
||||
}
|
||||
|
||||
|
||||
void system_init (void)
|
||||
{
|
||||
system_keyboard_init ();
|
||||
|
||||
system_interrupt_init (system_handler, SYSTEM_UPDATE_RATE);
|
||||
}
|
||||
@@ -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,57 @@
|
||||
/** @file system.h
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 15 May 2007
|
||||
@brief Target specific definitions
|
||||
*/
|
||||
#ifndef TARGET_H
|
||||
#define TARGET_H
|
||||
|
||||
#define F_CPU 8000000
|
||||
|
||||
|
||||
/* LED matrix columns. */
|
||||
#define LEDMAT_COL1_PIO PIO_DEFINE(PORT_C, 5)
|
||||
#define LEDMAT_COL2_PIO PIO_DEFINE(PORT_C, 7)
|
||||
#define LEDMAT_COL3_PIO PIO_DEFINE(PORT_C, 4)
|
||||
#define LEDMAT_COL4_PIO PIO_DEFINE(PORT_B, 7)
|
||||
#define LEDMAT_COL5_PIO PIO_DEFINE(PORT_C, 6)
|
||||
#define LEDMAT_COLS_NUM 5
|
||||
|
||||
|
||||
/* LED matrix rows. */
|
||||
#define LEDMAT_ROW1_PIO PIO_DEFINE(PORT_B, 0)
|
||||
#define LEDMAT_ROW2_PIO PIO_DEFINE(PORT_B, 1)
|
||||
#define LEDMAT_ROW3_PIO PIO_DEFINE(PORT_B, 2)
|
||||
#define LEDMAT_ROW4_PIO PIO_DEFINE(PORT_B, 3)
|
||||
#define LEDMAT_ROW5_PIO PIO_DEFINE(PORT_B, 4)
|
||||
#define LEDMAT_ROW6_PIO PIO_DEFINE(PORT_B, 5)
|
||||
#define LEDMAT_ROW7_PIO PIO_DEFINE(PORT_B, 6)
|
||||
#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_HIGH_PIO PIO_DEFINE(PORT_D, 3)
|
||||
#define IR_TX_LOW_PIO PIO_DEFINE(PORT_D, 0)
|
||||
#define IR_RX_PIO PIO_DEFINE(PORT_D, 2)
|
||||
|
||||
#define IR_MODULATION_FREQ 36e3
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
/** @file timer.c
|
||||
@author M. P. Hayes, UCECE
|
||||
@date 21 August 2007
|
||||
Description: Support for timer.
|
||||
*/
|
||||
#include "timer.h"
|
||||
#include "unistd.h"
|
||||
#include "time.h"
|
||||
#include "system.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static timer_tick_t offset;
|
||||
|
||||
|
||||
/* Initialise timer. */
|
||||
void timer_init (void)
|
||||
{
|
||||
/* Save offset so timer starts around 0. */
|
||||
offset = timer_get ();
|
||||
}
|
||||
|
||||
|
||||
/** Get current time:
|
||||
@return current time in ticks. */
|
||||
timer_tick_t timer_get (void)
|
||||
{
|
||||
struct timespec time;
|
||||
uint64_t time_us;
|
||||
uint64_t ticks;
|
||||
|
||||
clock_gettime (CLOCK_MONOTONIC, &time);
|
||||
|
||||
time_us = time.tv_sec * 1000000 + time.tv_nsec / 1000;
|
||||
ticks = time_us * TIMER_RATE / 1000000;
|
||||
|
||||
return (timer_tick_t) ticks - offset;
|
||||
}
|
||||
|
||||
|
||||
/** 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;
|
||||
|
||||
/* Allow for 1000 tick overrun. */
|
||||
if (diff < TIMER_OVERRUN_MAX)
|
||||
return now;
|
||||
|
||||
diff = when - now;
|
||||
|
||||
usleep (diff * 1e6 / TIMER_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** 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 */
|
||||
Reference in New Issue
Block a user