Generate callgraph

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