AVR HAL ported from RT-2 (serial/pal/hal tested)

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@6999 35acf78f-673a-0410-8e92-d51de3d6d3f4
This commit is contained in:
utzig 2014-06-29 23:06:17 +00:00
parent 5eb1ae8cc3
commit 766a2388d4
23 changed files with 4995 additions and 0 deletions

190
os/hal/ports/AVR/adc_lld.c Normal file
View File

@ -0,0 +1,190 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/adc_lld.c
* @brief ADC Driver subsystem low level driver source.
*
* @addtogroup ADC
* @{
*/
#include "hal.h"
#if HAL_USE_ADC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/** @brief ADC1 driver identifier.*/
#if AVR_ADC_USE_ADC1 || defined(__DOXYGEN__)
ADCDriver ADCD1;
#endif
/*===========================================================================*/
/* Driver local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static size_t getAdcChannelNumberFromMask(uint8_t mask, uint8_t currentChannel) {
for (uint8_t i = 0; mask > 0; i++) {
if (mask & 0x01) {
if (!currentChannel)
return i;
currentChannel--;
}
mask >>= 1;
}
/* error, should never reach this line */
}
static void setAdcChannel(uint8_t channelNum) {
ADMUX = (ADMUX & 0xf8) | (channelNum & 0x07);
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#include <util/delay.h>
OSAL_IRQ_HANDLER(ADC_vect) {
OSAL_IRQ_PROLOGUE();
uint8_t low = ADCL;
uint8_t high = ADCH;
uint16_t result = (high << 8) | low;
ADCD1.samples[ADCD1.currentBufferPosition] = result;
ADCD1.currentBufferPosition++;
size_t bufferSize = ADCD1.depth * ADCD1.grpp->num_channels;
size_t currentChannel = ADCD1.currentBufferPosition % ADCD1.grpp->num_channels;
size_t currentIteration = ADCD1.currentBufferPosition / ADCD1.grpp->num_channels;
if (ADCD1.grpp->circular && currentChannel == 0 && currentIteration == ADCD1.depth/2) {
_adc_isr_half_code(&ADCD1);
}
if (ADCD1.currentBufferPosition == bufferSize) {
_adc_isr_full_code(&ADCD1);
} else {
setAdcChannel(getAdcChannelNumberFromMask(ADCD1.grpp->channelsMask,currentChannel));
ADCSRA |= 1 << ADSC;
}
OSAL_IRQ_EPILOGUE();
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level ADC driver initialization.
*
* @notapi
*/
void adc_lld_init(void) {
adcObjectInit(&ADCD1);
//prescaler 128, only value possible at 20Mhz, interrupt
ADCSRA = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADIE);
ADCSRB = 0; //single shot
//uso aref, only valid for arduino. arduino ha aref collegato
ADMUX = (0 << REFS1) | (0 << REFS0);
}
/**
* @brief Configures and activates the ADC peripheral.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @notapi
*/
void adc_lld_start(ADCDriver *adcp) {
if (adcp->state == ADC_STOP) {
/* Clock activation.*/
ADCSRA |= (1 << ADEN);
}
if (adcp->config != NULL) {
ADMUX = (adcp->config->analog_reference << REFS0);
}
}
/**
* @brief Deactivates the ADC peripheral.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @notapi
*/
void adc_lld_stop(ADCDriver *adcp) {
if (adcp->state == ADC_READY) {
/* Clock de-activation.*/
ADCSRA &= ~(1 << ADEN);
}
}
/**
* @brief Starts an ADC conversion.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @notapi
*/
void adc_lld_start_conversion(ADCDriver *adcp) {
adcp->currentBufferPosition=0;
setAdcChannel(getAdcChannelNumberFromMask(adcp->grpp->channelsMask,0));
ADCSRA |= 1 << ADSC;
}
/**
* @brief Stops an ongoing conversion.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @notapi
*/
void adc_lld_stop_conversion(ADCDriver *adcp) {
ADCSRA &= ~(1 << ADSC);
}
#endif /* HAL_USE_ADC */
/** @} */

198
os/hal/ports/AVR/adc_lld.h Normal file
View File

@ -0,0 +1,198 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/adc_lld.h
* @brief ADC Driver subsystem low level driver source.
*
* @addtogroup ADC
* @{
*/
#ifndef _ADC_LLD_H_
#define _ADC_LLD_H_
#if HAL_USE_ADC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
#define ANALOG_REFERENCE_AREF 0
#define ANALOG_REFERENCE_AVCC 1
#define ANALOG_REFERENCE_1V1 2
#define ANALOG_REFERENCE_2V56 3
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if !CH_USE_SEMAPHORES
#error "the ADC driver requires CH_USE_SEMAPHORES"
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief ADC sample data type.
*/
typedef uint16_t adcsample_t;
/**
* @brief Channels number in a conversion group.
*/
typedef uint16_t adc_channels_num_t;
/**
* @brief Type of a structure representing an ADC driver.
*/
typedef struct ADCDriver ADCDriver;
/**
* @brief ADC notification callback type.
*
* @param[in] adcp pointer to the @p ADCDriver object triggering the
* callback
* @param[in] buffer pointer to the most recent samples data
* @param[in] n number of buffer rows available starting from @p buffer
*/
typedef void (*adccallback_t)(ADCDriver *adcp, adcsample_t *buffer, size_t n);
/**
* @brief Conversion group configuration structure.
* @details This implementation-dependent structure describes a conversion
* operation.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
typedef struct {
/**
* @brief Enables the circular buffer mode for the group.
*/
bool_t circular;
/**
* @brief Number of the analog channels belonging to the conversion group.
*/
adc_channels_num_t num_channels;
/**
* @brief Callback function associated to the group or @p NULL.
*/
adccallback_t end_cb;
/* End of the mandatory fields.*/
uint8_t channelsMask;
} ADCConversionGroup;
/**
* @brief Driver configuration structure.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
* @note It could be empty on some architectures.
*/
typedef struct {
uint8_t analog_reference;
} ADCConfig;
/**
* @brief Structure representing an ADC driver.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
struct ADCDriver {
/**
* @brief Driver state.
*/
adcstate_t state;
/**
* @brief Current configuration data.
*/
const ADCConfig *config;
/**
* @brief Current samples buffer pointer or @p NULL.
*/
adcsample_t *samples;
/**
* @brief Current samples buffer depth or @p 0.
*/
size_t depth;
/**
* @brief Current conversion group pointer or @p NULL.
*/
const ADCConversionGroup *grpp;
#if ADC_USE_WAIT || defined(__DOXYGEN__)
/**
* @brief Waiting thread.
*/
Thread *thread;
#endif /* ADC_USE_WAIT */
#if ADC_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
/**
* @brief Mutex protecting the peripheral.
*/
Mutex mutex;
#elif CH_USE_SEMAPHORES
Semaphore semaphore;
#endif
#endif /* ADC_USE_MUTUAL_EXCLUSION */
#if defined(ADC_DRIVER_EXT_FIELDS)
ADC_DRIVER_EXT_FIELDS
#endif
/* End of the mandatory fields.*/
/**
* @brief Current position in the buffer.
*/
size_t currentBufferPosition;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AVR_ADC_USE_ADC1 && !defined(__DOXYGEN__)
extern ADCDriver ADCD1;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void adc_lld_init(void);
void adc_lld_start(ADCDriver *adcp);
void adc_lld_stop(ADCDriver *adcp);
void adc_lld_start_conversion(ADCDriver *adcp);
void adc_lld_stop_conversion(ADCDriver *adcp);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_ADC */
#endif /* _ADC_LLD_H_ */
/** @} */

View File

@ -0,0 +1,94 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef _AVR_PINS_H_
#define _AVR_PINS_H_
#include <avr/io.h>
#if AVR_SPI_USE_SPI1
#if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__)
#define PIN_SPI1 PINB
#define PORT_SPI1 PORTB
#define DDR_SPI1 DDRB
#define SPI1_SS 4
#define SPI1_SCK 7
#define SPI1_MOSI 5
#define SPI1_MISO 6
#elif defined(__AVR_ATmega328P__)
#define PIN_SPI1 PINB
#define PORT_SPI1 PORTB
#define DDR_SPI1 DDRB
#define SPI1_SS 2
#define SPI1_SCK 5
#define SPI1_MOSI 3
#define SPI1_MISO 4
#elif defined(__AVR_ATmega2560__) || \
defined(__AVR_ATmega1280__) || \
defined(__AVR_ATmega128__)
#define PIN_SPI1 PINB
#define PORT_SPI1 PORTB
#define DDR_SPI1 DDRB
#define SPI1_SS 0
#define SPI1_SCK 1
#define SPI1_MOSI 2
#define SPI1_MISO 3
#elif defined(__AVR_AT90CAN128__) || \
defined(__AVR_AT90CAN64__) || \
defined(__AVR_AT90CAN32__)
#define PIN_SPI1 PINB
#define PORT_SPI1 PORTB
#define DDR_SPI1 DDRB
#define SPI1_SS 0
#define SPI1_SCK 1
#define SPI1_MOSI 2
#define SPI1_MISO 3
#else
#warning "Device not supported by SPI driver"
#endif
#endif /* AVR_SPI_USE_SPI1 */
#if AVR_ADC_USE_ADC1
#if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__)
#define PINADC PINA
#define PORTADC PORTA
#define DDRADC DDRA
#elif defined(__AVR_ATmega328P__)
#define PINADC PINC
#define PORTADC PORTC
#define DDRADC DDRC
#elif defined(__AVR_ATmega2560__) || \
defined(__AVR_ATmega1280__) || \
defined(__AVR_ATmega128__)
#define PINADC PINF
#define PORTADC PORTF
#define DDRADC DDRF
#elif defined(__AVR_AT90CAN128__) || \
defined(__AVR_AT90CAN64__) || \
defined(__AVR_AT90CAN32__)
#define PINADC PINF
#define PORTADC PORTF
#define DDRADC DDRF
#else
#warning "Device not supported by ADC driver"
#endif
#endif /* AVR_ADC_USE_ADC1 */
#endif /* _AVR_PINS_H_ */

View File

@ -0,0 +1,48 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef _AVR_TIMERS_H_
#define _AVR_TIMERS_H_
#if ((AVR_GPT_USE_TIM1 && AVR_PWM_USE_TIM1) || \
(AVR_GPT_USE_TIM1 && AVR_ICU_USE_TIM1) || \
(AVR_PWM_USE_TIM1 && AVR_ICU_USE_TIM1))
#error "Timer 1 cannot simultaneously be used by multiple drivers."
#endif
#if ((AVR_GPT_USE_TIM2 && AVR_PWM_USE_TIM2))
#error "Timer 2 cannot simultaneously be used by multiple drivers."
#endif
#if ((AVR_GPT_USE_TIM3 && AVR_PWM_USE_TIM3) || \
(AVR_GPT_USE_TIM3 && AVR_ICU_USE_TIM3) || \
(AVR_PWM_USE_TIM3 && AVR_ICU_USE_TIM3))
#error "Timer 3 cannot simultaneously be used by multiple drivers."
#endif
#if ((AVR_GPT_USE_TIM4 && AVR_PWM_USE_TIM4) || \
(AVR_GPT_USE_TIM4 && AVR_ICU_USE_TIM4) || \
(AVR_PWM_USE_TIM4 && AVR_ICU_USE_TIM4))
#error "Timer 4 cannot simultaneously be used by multiple drivers."
#endif
#if ((AVR_GPT_USE_TIM5 && AVR_PWM_USE_TIM5) || \
(AVR_GPT_USE_TIM5 && AVR_ICU_USE_TIM5) || \
(AVR_PWM_USE_TIM5 && AVR_ICU_USE_TIM5))
#error "Timer 5 cannot simultaneously be used by multiple drivers."
#endif
#endif /* _AVR_TIMERS_H_ */

350
os/hal/ports/AVR/gpt_lld.c Normal file
View File

@ -0,0 +1,350 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This driver is based on the work done by Matteo Serva available at
http://github.com/matteoserva/ChibiOS-AVR
*/
/**
* @file AVR/gpt_lld.c
* @brief AVR GPT driver subsystem low level driver.
*
* @addtogroup GPT
* @{
*/
#include "hal.h"
#if HAL_USE_GPT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
#define PRESCALER_SIZE_BASE 5
#define PRESCALER_SIZE_EXTENDED 7
// FIXME: could use better names here!
typedef struct {
volatile uint8_t *tccra;
volatile uint8_t *tccrb;
volatile uint8_t *ocr1;
volatile uint8_t *ocr2;
volatile uint8_t *tcnt1;
volatile uint8_t *tcnt2;
volatile uint8_t *tifr;
volatile uint8_t *timsk;
} timer_registers_t;
const timer_registers_t regs_table[] = {
#if AVR_GPT_USE_TIM1 || defined(__DOXYGEN__)
{ &TCCR1A, &TCCR1B, &OCR1AH, &OCR1AL, &TCNT1H, &TCNT1L, &TIFR1, &TIMSK1 },
#endif
#if AVR_GPT_USE_TIM2 || defined(__DOXYGEN__)
{ &TCCR2A, &TCCR2B, &OCR2A, &OCR2A, &TCNT2, &TCNT2, &TIFR2, &TIMSK2 },
#endif
#if AVR_GPT_USE_TIM3 || defined(__DOXYGEN__)
{ &TCCR3A, &TCCR3B, &OCR3AH, &OCR3AL, &TCNT3H, &TCNT3L, &TIFR3, &TIMSK3 },
#endif
#if AVR_GPT_USE_TIM4 || defined(__DOXYGEN__)
{ &TCCR4A, &TCCR4B, &OCR4AH, &OCR4AL, &TCNT4H, &TCNT4L, &TIFR4, &TIMSK4 },
#endif
#if AVR_GPT_USE_TIM5 || defined(__DOXYGEN__)
{ &TCCR5A, &TCCR5B, &OCR5AH, &OCR5AL, &TCNT5H, &TCNT5L, &TIFR5, &TIMSK5 },
#endif
};
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
#if AVR_GPT_USE_TIM1 || defined(__DOXYGEN__)
GPTDriver GPTD1;
#endif
#if AVR_GPT_USE_TIM2 || defined(__DOXYGEN__)
GPTDriver GPTD2;
#endif
#if AVR_GPT_USE_TIM3 || defined(__DOXYGEN__)
GPTDriver GPTD3;
#endif
#if AVR_GPT_USE_TIM4 || defined(__DOXYGEN__)
GPTDriver GPTD4;
#endif
#if AVR_GPT_USE_TIM5 || defined(__DOXYGEN__)
GPTDriver GPTD5;
#endif
/*===========================================================================*/
/* Driver local variables. */
/*===========================================================================*/
static uint16_t ratio_base[] = { 1024, 256, 64, 8, 1 };
static uint8_t clock_source_base[]= { 5, 4, 3, 2, 1 };
static uint16_t ratio_extended[] = { 1024, 256, 128, 64, 32, 8, 1 };
static uint8_t clock_source_extended[] = { 7, 6, 5, 4, 3, 2, 1 };
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static uint8_t prescaler(uint16_t freq, uint16_t *ratio, uint8_t n)
{
uint8_t i;
for (i = 0; i < n; ++i) {
uint32_t result = F_CPU / ratio[i] / freq;
if (result > 256UL)
return i - 1;
if ((result * ratio[i] * freq) == F_CPU)
return i;
}
}
static void gpt_lld_serve_interrupt(GPTDriver *gptp)
{
gptp->counter++;
if (gptp->counter == gptp->period) {
gptp->counter = 0;
if (gptp->state == GPT_ONESHOT) {
gptp->state = GPT_READY; /* Back in GPT_READY state. */
gpt_lld_stop_timer(gptp); /* Timer automatically stopped. */
}
gptp->callback(gptp);
}
}
static void gpt_lld_dummy_callback(GPTDriver *gptp)
{
}
static uint8_t getTimerIndex(GPTDriver *gptp)
{
uint8_t index = 0;
#if AVR_GPT_USE_TIM1 || defined(__DOXYGEN__)
if (gptp == &GPTD1) return index;
else index++;
#endif
#if AVR_GPT_USE_TIM2 || defined(__DOXYGEN__)
if (gptp == &GPTD2) return index;
else index++;
#endif
#if AVR_GPT_USE_TIM3 || defined(__DOXYGEN__)
if (gptp == &GPTD3) return index;
else index++;
#endif
#if AVR_GPT_USE_TIM4 || defined(__DOXYGEN__)
if (gptp == &GPTD4) return index;
else index++;
#endif
#if AVR_GPT_USE_TIM5 || defined(__DOXYGEN__)
if (gptp == &GPTD5) return index;
else index++;
#endif
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if AVR_GPT_USE_TIM1 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER1_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD1);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_GPT_USE_TIM2 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER2_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD2);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_GPT_USE_TIM3 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER3_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD3);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_GPT_USE_TIM4 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER4_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD4);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_GPT_USE_TIM5 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER5_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD5);
OSAL_IRQ_EPILOGUE();
}
#endif
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level GPT driver initialization.
*
* @notapi
*/
void gpt_lld_init(void)
{
#if AVR_GPT_USE_TIM1 || defined(__DOXYGEN__)
gptObjectInit(&GPTD1);
#endif
#if AVR_GPT_USE_TIM2 || defined(__DOXYGEN__)
gptObjectInit(&GPTD2);
#endif
#if AVR_GPT_USE_TIM3 || defined(__DOXYGEN__)
gptObjectInit(&GPTD3);
#endif
#if AVR_GPT_USE_TIM4 || defined(__DOXYGEN__)
gptObjectInit(&GPTD4);
#endif
#if AVR_GPT_USE_TIM5 || defined(__DOXYGEN__)
gptObjectInit(&GPTD5);
#endif
}
/**
* @brief Configures and activates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_start(GPTDriver *gptp)
{
uint8_t psc;
if (gptp->state == GPT_STOP) {
/* Clock activation.*/
}
/* Configuration.*/
#if AVR_GPT_USE_TIM2 || defined(__DOXYGEN__)
if (gptp == &GPTD2) {
psc = prescaler(gptp->config->frequency, ratio_extended, PRESCALER_SIZE_EXTENDED);
gptp->clock_source = clock_source_extended[psc] & 0x07;
TCCR2A = (1 << WGM21) | (0 << WGM20);
TCCR2B = (0 << WGM22);
OCR2A = F_CPU / ratio_extended[psc] /gptp->config->frequency - 1;
return;
}
#endif
uint8_t i = getTimerIndex(gptp);
psc = prescaler(gptp->config->frequency, ratio_base, PRESCALER_SIZE_BASE);
gptp->clock_source = clock_source_base[psc] & 0x07;
*regs_table[i].tccra = (0 << WGM11) |
(0 << WGM10) |
(0 << COM1A1) |
(0 << COM1A0) |
(0 << COM1B1) |
(0 << COM1B0);
*regs_table[i].tccrb = (1 << WGM12);
*regs_table[i].ocr1 = 0;
*regs_table[i].ocr2 = F_CPU / ratio_base[psc] / gptp->config->frequency - 1;
}
/**
* @brief Deactivates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_stop(GPTDriver *gptp)
{
/* nothing to be done */
if (gptp->state == GPT_READY) {
/* Clock de-activation.*/
}
gpt_lld_stop_timer(gptp);
}
/**
* @brief Starts the timer in continuous mode.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] period period in ticks
*
* @notapi
*/
void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t period)
{
gptp->callback = gptp->config->callback;
gptp->period = period;
gptp->counter = 0;
uint8_t i = getTimerIndex(gptp);
*regs_table[i].tcnt1 = 0;
*regs_table[i].tcnt2 = 0;
*regs_table[i].tifr = (1 << OCF1A);
*regs_table[i].timsk = (1 << OCIE1A);
*regs_table[i].tccrb |= (gptp->clock_source << CS10);
}
/**
* @brief Stops the timer.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_stop_timer(GPTDriver *gptp)
{
uint8_t i = getTimerIndex(gptp);
*regs_table[i].tccrb &= ~((7 << CS10) | (1 << OCIE1A));
*regs_table[i].tifr = (1 << OCF1A);
}
/**
* @brief Starts the timer in one shot mode and waits for completion.
* @details This function specifically polls the timer waiting for completion
* in order to not have extra delays caused by interrupt servicing,
* this function is only recommended for short delays.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval time interval in ticks
*
* @notapi
*/
void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval)
{
gptp->callback = gpt_lld_dummy_callback;
gpt_lld_start_timer(gptp, interval);
//FIX
while (gptp->state != GPT_READY) {}
}
#endif /* HAL_USE_GPT */
/** @} */

221
os/hal/ports/AVR/gpt_lld.h Normal file
View File

@ -0,0 +1,221 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This driver is based on the work done by Matteo Serva available at
http://github.com/matteoserva/ChibiOS-AVR
*/
/**
* @file AVR/gpt_lld.h
* @brief AVR GPT driver subsystem low level driver.
*
* @addtogroup GPT
* @{
*/
#ifndef _GPT_LLD_H_
#define _GPT_LLD_H_
#if HAL_USE_GPT || defined(__DOXYGEN__)
#include "avr_timers.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief GPT1 driver enable switch.
* @details If set to @p TRUE the support for GPT1 is included.
* @note The default is @p FALSE.
*/
#if !defined(AVR_GPT_USE_TIM1)
#define AVR_GPT_USE_TIM1 FALSE
#endif
/**
* @brief GPT2 driver enable switch.
* @details If set to @p TRUE the support for GPT2 is included.
* @note The default is @p FALSE.
*/
#if !defined(AVR_GPT_USE_TIM2)
#define AVR_GPT_USE_TIM2 FALSE
#endif
/**
* @brief GPT3 driver enable switch.
* @details If set to @p TRUE the support for GPT3 is included.
* @note The default is @p FALSE.
*/
#if !defined(AVR_GPT_USE_TIM3)
#define AVR_GPT_USE_TIM3 FALSE
#endif
/**
* @brief GPT4 driver enable switch.
* @details If set to @p TRUE the support for GPT4 is included.
* @note The default is @p FALSE.
*/
#if !defined(AVR_GPT_USE_TIM4)
#define AVR_GPT_USE_TIM4 FALSE
#endif
/**
* @brief GPT5 driver enable switch.
* @details If set to @p TRUE the support for GPT5 is included.
* @note The default is @p FALSE.
*/
#if !defined(AVR_GPT_USE_TIM5)
#define AVR_GPT_USE_TIM5 FALSE
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief GPT frequency type.
*/
typedef uint32_t gptfreq_t;
/**
* @brief GPT counter type.
*/
typedef uint16_t gptcnt_t;
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
/**
* @brief Timer clock in Hz.
* @note The low level can use assertions in order to catch invalid
* frequency specifications.
*/
gptfreq_t frequency;
/**
* @brief Timer callback pointer.
* @note This callback is invoked on GPT counter events.
*/
gptcallback_t callback;
/* End of the mandatory fields.*/
} GPTConfig;
/**
* @brief Structure representing a GPT driver.
*/
struct GPTDriver {
/**
* @brief Driver state.
*/
volatile gptstate_t state;
/**
* @brief Current configuration data.
*/
const GPTConfig *config;
#if defined(GPT_DRIVER_EXT_FIELDS)
GPT_DRIVER_EXT_FIELDS
#endif
/* End of the mandatory fields.*/
/**
* @brief input clock from prescaler
*/
uint8_t clock_source;
/**
* @brief Lenght of the period in clock ticks
*/
gptcnt_t period;
/**
* @brief Current clock tick.
*/
gptcnt_t counter;
/**
* @brief Function called from the interrupt service routine
*/
gptcallback_t callback;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @brief Changes the interval of GPT peripheral.
* @details This function changes the interval of a running GPT unit.
* @pre The GPT unit must have been activated using @p gptStart().
* @pre The GPT unit must have been running in continuous mode using
* @p gptStartContinuous().
* @post The GPT unit interval is changed to the new value.
* @note The function has effect at the next cycle start.
*
* @param[in] gptp pointer to a @p GPTDriver object
* @param[in] interval new cycle time in timer ticks
* @notapi
*/
// FIXME: placeholder to enable compile, should be implemented!
#define gpt_lld_change_interval(gptp, interval)
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AVR_GPT_USE_TIM1 || defined(__DOXYGEN__)
extern GPTDriver GPTD1;
#endif
#if AVR_GPT_USE_TIM2 || defined(__DOXYGEN__)
extern GPTDriver GPTD2;
#endif
#if AVR_GPT_USE_TIM3 || defined(__DOXYGEN__)
extern GPTDriver GPTD3;
#endif
#if AVR_GPT_USE_TIM4 || defined(__DOXYGEN__)
extern GPTDriver GPTD4;
#endif
#if AVR_GPT_USE_TIM5 || defined(__DOXYGEN__)
extern GPTDriver GPTD5;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void gpt_lld_init(void);
void gpt_lld_start(GPTDriver *gptp);
void gpt_lld_stop(GPTDriver *gptp);
void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval);
void gpt_lld_stop_timer(GPTDriver *gptp);
void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_GPT */
#endif /* _GPT_LLD_H_ */
/** @} */

103
os/hal/ports/AVR/hal_lld.c Normal file
View File

@ -0,0 +1,103 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/hal_lld.c
* @brief AVR HAL subsystem low level driver code.
*
* @addtogroup HAL
* @{
*/
#include "hal.h"
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/**
* @brief Timer0 interrupt handler.
*/
OSAL_IRQ_HANDLER(AVR_TIMER_VECT) {
OSAL_IRQ_PROLOGUE();
osalSysLockFromISR();
osalOsTimerHandlerI();
osalSysUnlockFromISR();
OSAL_IRQ_EPILOGUE();
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level HAL driver initialization.
*
* @notapi
*/
void hal_lld_init(void) {
/*
* Timer 0 setup.
*/
#if defined(TCCR0B) /* Timer has multiple output comparators */
TCCR0A = (1 << WGM01) | (0 << WGM00) | /* CTC mode. */
(0 << COM0A1) | (0 << COM0A0) | /* OC0A disabled. */
(0 << COM0B1) | (0 << COM0B0); /* OC0B disabled. */
TCCR0B = (0 << WGM02) | AVR_TIMER_PRESCALER_BITS; /* CTC mode. */
OCR0A = AVR_TIMER_COUNTER - 1;
TCNT0 = 0; /* Reset counter. */
TIFR0 = (1 << OCF0A); /* Reset pending. */
TIMSK0 = (1 << OCIE0A); /* IRQ on compare. */
#elif defined(TCCR0A) /* AT90CAN doesn't have TCCR0B and slightly different TCCR0A */
TCCR0A = (1 << WGM01) | (0 << WGM00) | /* CTC mode. */
(0 << COM0A1) | (0 << COM0A0); /* OC0A disabled. */
OCR0A = AVR_TIMER_COUNTER - 1;
TCNT0 = 0; /* Reset counter. */
TIFR0 = (1 << OCF0A); /* Reset pending. */
TIMSK0 = (1 << OCIE0A); /* IRQ on compare. */
#elif defined(TCCR0) /* Timer has single output comparator */
TCCR0 = (1 << WGM01) | (0 << WGM00) | /* CTC mode. */
(0 << COM01) | (0 << COM00) | /* OC0A disabled. */
AVR_TIMER_PRESCALER_BITS;
OCR0 = AVR_TIMER_COUNTER - 1;
TCNT0 = 0; /* Reset counter. */
TIFR = (1 << OCF0); /* Reset pending. */
TIMSK = (1 << OCIE0); /* IRQ on compare. */
#else
#error "Neither TCCR0A nor TCCR0 registers are defined"
#endif
}
/** @} */

117
os/hal/ports/AVR/hal_lld.h Normal file
View File

@ -0,0 +1,117 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/hal_lld.h
* @brief AVR HAL subsystem low level driver header.
*
* @addtogroup HAL
* @{
*/
#ifndef _HAL_LLD_H_
#define _HAL_LLD_H_
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @brief Defines the support for realtime counters in the HAL.
*/
#define HAL_IMPLEMENTS_COUNTERS FALSE
/**
* @brief Platform name.
*/
#define PLATFORM_NAME "AVR"
/**
* @brief Timer maximum value
*/
#define AVR_TIMER_COUNTER_MAX 255
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/* Work out what the timer interrupt is called on this MCU */
#ifdef TIMER0_COMPA_vect
#define AVR_TIMER_VECT TIMER0_COMPA_vect
#elif defined(TIMER_COMPA_vect)
#define AVR_TIMER_VECT TIMER_COMPA_vect
#elif defined(TIMER0_COMP_vect)
#define AVR_TIMER_VECT TIMER0_COMP_vect
#else
#error "Cannot find interrupt vector name for timer"
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/* Find the most suitable prescaler setting for the desired CH_CFG_ST_FREQUENCY */
#if ((F_CPU / CH_CFG_ST_FREQUENCY) <= AVR_TIMER_COUNTER_MAX)
#define AVR_TIMER_PRESCALER 1
#define AVR_TIMER_PRESCALER_BITS (0 << CS02) | (0 << CS01) | (1 << CS00); /* CLK */
#elif ((F_CPU / CH_CFG_ST_FREQUENCY / 8) <= AVR_TIMER_COUNTER_MAX)
#define AVR_TIMER_PRESCALER 8
#define AVR_TIMER_PRESCALER_BITS (0 << CS02) | (1 << CS01) | (0 << CS00); /* CLK/8 */
#elif ((F_CPU / CH_CFG_ST_FREQUENCY / 64) <= AVR_TIMER_COUNTER_MAX)
#define AVR_TIMER_PRESCALER 64
#define AVR_TIMER_PRESCALER_BITS (0 << CS02) | (1 << CS01) | (1 << CS00); /* CLK/64 */
#elif ((F_CPU / CH_CFG_ST_FREQUENCY / 256) <= AVR_TIMER_COUNTER_MAX)
#define AVR_TIMER_PRESCALER 256
#define AVR_TIMER_PRESCALER_BITS (1 << CS02) | (0 << CS01) | (0 << CS00); /* CLK/256 */
#elif ((F_CPU / CH_CFG_ST_FREQUENCY / 1024) <= AVR_TIMER_COUNTER_MAX)
#define AVR_TIMER_PRESCALER 1024
#define AVR_TIMER_PRESCALER_BITS (1 << CS02) | (0 << CS01) | (1 << CS00); /* CLK/1024 */
#else
#error "Frequency too low for timer, please set CH_CFG_ST_FREQUENCY to a higher value"
#endif
#define AVR_TIMER_COUNTER (F_CPU / CH_CFG_ST_FREQUENCY / AVR_TIMER_PRESCALER)
/* Test if CH_CFG_ST_FREQUENCY can be matched exactly using this timer */
#define F_CPU_ (AVR_TIMER_COUNTER * AVR_TIMER_PRESCALER * CH_CFG_ST_FREQUENCY)
#if (F_CPU_ != F_CPU)
#warning "CH_CFG_ST_FREQUENCY cannot be generated exactly using timer"
#endif
#undef F_CPU_
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#ifdef __cplusplus
extern "C" {
#endif
void hal_lld_init(void);
#ifdef __cplusplus
}
#endif
#endif /* _HAL_LLD_H_ */
/** @} */

288
os/hal/ports/AVR/i2c_lld.c Normal file
View File

@ -0,0 +1,288 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/i2c_lld.c
* @brief AVR I2C subsystem low level driver source.
*
* @addtogroup I2C
* @{
*/
#include "hal.h"
#if HAL_USE_I2C || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/** @brief I2C driver identifier.*/
#if AVR_I2C_USE_I2C1 || defined(__DOXYGEN__)
I2CDriver I2CD1;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Wakes up the waiting thread.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] msg wakeup message
*
* @notapi
*/
#define wakeup_isr(i2cp, msg) { \
osalSysLockFromISR(); \
if ((i2cp)->thread != NULL) { \
thread_t *tp = (i2cp)->thread; \
(i2cp)->thread = NULL; \
tp->p_u.rdymsg = (msg); \
chSchReadyI(tp); \
} \
osalSysUnlockFromISR(); \
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if AVR_I2C_USE_I2C1 || defined(__DOXYGEN__)
/**
* @brief I2C event interrupt handler.
*
* @notapi
*/
OSAL_IRQ_HANDLER(TWI_vect) {
OSAL_IRQ_PROLOGUE();
I2CDriver *i2cp = &I2CD1;
switch (TWSR & 0xF8) {
case TWI_START:
case TWI_REPEAT_START:
TWDR = (i2cp->addr << 1);
if ((i2cp->txbuf == NULL) || (i2cp->txbytes == 0) || (i2cp->txidx == i2cp->txbytes)) {
TWDR |= 0x01;
}
TWCR = ((1 << TWINT) | (1 << TWEN) | (1 << TWIE));
break;
case TWI_MASTER_TX_ADDR_ACK:
case TWI_MASTER_TX_DATA_ACK:
if (i2cp->txidx < i2cp->txbytes) {
TWDR = i2cp->txbuf[i2cp->txidx++];
TWCR = ((1 << TWINT) | (1 << TWEN) | (1 << TWIE));
} else {
if (i2cp->rxbuf && i2cp->rxbytes) {
TWCR = ((1 << TWSTA) | (1 << TWINT) | (1 << TWEN) | (1 << TWIE));
} else {
TWCR = ((1 << TWSTO) | (1 << TWINT) | (1 << TWEN));
wakeup_isr(i2cp, MSG_OK);
}
}
break;
case TWI_MASTER_RX_ADDR_ACK:
if (i2cp->rxidx == (i2cp->rxbytes - 1)) {
TWCR = ((1 << TWINT) | (1 << TWEN) | (1 << TWIE));
} else {
TWCR = ((1 << TWEA) | (1 << TWINT) | (1 << TWEN) | (1 << TWIE));
}
break;
case TWI_MASTER_RX_DATA_ACK:
i2cp->rxbuf[i2cp->rxidx++] = TWDR;
if (i2cp->rxidx == (i2cp->rxbytes - 1)) {
TWCR = ((1 << TWINT) | (1 << TWEN) | (1 << TWIE));
} else {
TWCR = ((1 << TWEA) | (1 << TWINT) | (1 << TWEN) | (1 << TWIE));
}
break;
case TWI_MASTER_RX_DATA_NACK:
i2cp->rxbuf[i2cp->rxidx] = TWDR;
TWCR = ((1 << TWSTO) | (1 << TWINT) | (1 << TWEN));
wakeup_isr(i2cp, MSG_OK);
case TWI_MASTER_TX_ADDR_NACK:
case TWI_MASTER_TX_DATA_NACK:
case TWI_MASTER_RX_ADDR_NACK:
i2cp->errors |= I2CD_ACK_FAILURE;
break;
case TWI_ARBITRATION_LOST:
i2cp->errors |= I2CD_ARBITRATION_LOST;
break;
case TWI_BUS_ERROR:
i2cp->errors |= I2CD_BUS_ERROR;
break;
default:
/* FIXME: only gets here if there are other MASTERs in the bus */
TWCR = ((1 << TWSTO) | (1 << TWINT) | (1 << TWEN));
wakeup_isr(i2cp, MSG_RESET);
}
if (i2cp->errors != I2CD_NO_ERROR) {
TWCR = ((1 << TWSTO) | (1 << TWINT) | (1 << TWEN));
wakeup_isr(i2cp, MSG_RESET);
}
OSAL_IRQ_EPILOGUE();
}
#endif /* AVR_I2C_USE_I2C1 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level I2C driver initialization.
*
* @notapi
*/
void i2c_lld_init(void) {
i2cObjectInit(&I2CD1);
}
/**
* @brief Configures and activates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
void i2c_lld_start(I2CDriver *i2cp) {
/* TODO: Test TWI without external pull-ups (use internal) */
/* Configure prescaler to 1 */
TWSR &= 0xF8;
/* Configure baudrate */
TWBR = ((F_CPU / i2cp->config->clock_speed) - 16) / 2;
}
/**
* @brief Deactivates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
void i2c_lld_stop(I2CDriver *i2cp) {
if (i2cp->state != I2C_STOP) {
/* Disable TWI subsystem and stop all operations */
TWCR &= ~(1 << TWEN);
}
}
/**
* @brief Receives data via the I2C bus as master.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] addr slave device address
* @param[out] rxbuf pointer to the receive buffer
* @param[in] rxbytes number of bytes to be received
* @param[in] timeout the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_INFINITE no timeout.
* .
* @return The operation status.
* @retval MSG_OK if the function succeeded.
* @retval MSG_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
* @retval MSG_TIMEOUT if a timeout occurred before operation end. <b>After a
* timeout the driver must be stopped and restarted
* because the bus is in an uncertain state</b>.
*
* @notapi
*/
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout) {
i2cp->addr = addr;
i2cp->txbuf = NULL;
i2cp->txbytes = 0;
i2cp->txidx = 0;
i2cp->rxbuf = rxbuf;
i2cp->rxbytes = rxbytes;
i2cp->rxidx = 0;
/* Send START */
TWCR = ((1 << TWSTA) | (1 << TWINT) | (1 << TWEN) | (1 << TWIE));
osalSysLock();
i2cp->thread = chThdGetSelfX();
chSchGoSleepS(THD_STATE_SUSPENDED);
chSysUnlock();
return chThdGetSelfX()->p_u.rdymsg;
}
/**
* @brief Transmits data via the I2C bus as master.
*
* @param[in] i2cp pointer to the @p I2CDriver object
* @param[in] addr slave device address
* @param[in] txbuf pointer to the transmit buffer
* @param[in] txbytes number of bytes to be transmitted
* @param[out] rxbuf pointer to the receive buffer
* @param[in] rxbytes number of bytes to be received
* @param[in] timeout the number of ticks before the operation timeouts,
* the following special values are allowed:
* - @a TIME_INFINITE no timeout.
* .
* @return The operation status.
* @retval MSG_OK if the function succeeded.
* @retval MSG_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
* @retval MSG_TIMEOUT if a timeout occurred before operation end. <b>After a
* timeout the driver must be stopped and restarted
* because the bus is in an uncertain state</b>.
*
* @notapi
*/
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
const uint8_t *txbuf, size_t txbytes,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout) {
i2cp->addr = addr;
i2cp->txbuf = txbuf;
i2cp->txbytes = txbytes;
i2cp->txidx = 0;
i2cp->rxbuf = rxbuf;
i2cp->rxbytes = rxbytes;
i2cp->rxidx = 0;
TWCR = ((1 << TWSTA) | (1 << TWINT) | (1 << TWEN) | (1 << TWIE));
chSysLock();
i2cp->thread = chThdGetSelfX();
chSchGoSleepS(THD_STATE_SUSPENDED);
chSysUnlock();
return chThdGetSelfX()->p_u.rdymsg;
}
#endif /* HAL_USE_I2C */
/** @} */

224
os/hal/ports/AVR/i2c_lld.h Normal file
View File

@ -0,0 +1,224 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/i2c_lld.h
* @brief AVR I2C subsystem low level driver header.
*
* @addtogroup I2C
* @{
*/
#ifndef _I2C_LLD_H_
#define _I2C_LLD_H_
#if HAL_USE_I2C || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/** @brief START transmitted.*/
#define TWI_START 0x08
/** @brief Repeated START transmitted.*/
#define TWI_REPEAT_START 0x10
/** @brief Arbitration Lost.*/
#define TWI_ARBITRATION_LOST 0x38
/** @brief Bus errors.*/
#define TWI_BUS_ERROR 0x00
/** @brief SLA+W transmitted with ACK response.*/
#define TWI_MASTER_TX_ADDR_ACK 0x18
/** @brief SLA+W transmitted with NACK response.*/
#define TWI_MASTER_TX_ADDR_NACK 0x20
/** @brief DATA transmitted with ACK response.*/
#define TWI_MASTER_TX_DATA_ACK 0x28
/** @brief DATA transmitted with NACK response.*/
#define TWI_MASTER_TX_DATA_NACK 0x30
/** @brief SLA+R transmitted with ACK response.*/
#define TWI_MASTER_RX_ADDR_ACK 0x40
/** @brief SLA+R transmitted with NACK response.*/
#define TWI_MASTER_RX_ADDR_NACK 0x48
/** @brief DATA received with ACK response.*/
#define TWI_MASTER_RX_DATA_ACK 0x50
/** @brief DATA received with NACK response.*/
#define TWI_MASTER_RX_DATA_NACK 0x58
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief I2C driver enable switch.
* @details If set to @p TRUE the support for I2C is included.
* @note The default is @p FALSE.
*/
#if !defined(AVR_I2C_USE_I2C1) || defined(__DOXYGEN__)
#define AVR_I2C_USE_I2C1 FALSE
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Type representing I2C address.
*/
typedef uint8_t i2caddr_t;
/**
* @brief I2C Driver condition flags type.
*/
typedef uint8_t i2cflags_t;
/**
* @brief Driver configuration structure.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
typedef struct {
/**
* @brief Specifies the I2C clock frequency.
*/
uint32_t clock_speed;
} I2CConfig;
/**
* @brief Structure representing an I2C driver.
*/
struct I2CDriver {
/**
* @brief Driver state.
*/
i2cstate_t state;
/**
* @brief Current configuration data.
*/
const I2CConfig *config;
/**
* @brief Error flags.
*/
i2cflags_t errors;
#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
/**
* @brief Mutex protecting the bus.
*/
Mutex mutex;
#elif CH_USE_SEMAPHORES
Semaphore semaphore;
#endif
#endif /* I2C_USE_MUTUAL_EXCLUSION */
#if defined(I2C_DRIVER_EXT_FIELDS)
I2C_DRIVER_EXT_FIELDS
#endif
/* End of the mandatory fields.*/
/**
* @brief Thread waiting for I/O completion.
*/
Thread *thread;
/**
* @brief Address of slave device.
*/
i2caddr_t addr;
/**
* @brief Pointer to the buffer with data to send.
*/
const uint8_t *txbuf;
/**
* @brief Number of bytes of data to send.
*/
size_t txbytes;
/**
* @brief Current index in buffer when sending data.
*/
size_t txidx;
/**
* @brief Pointer to the buffer to put received data.
*/
uint8_t *rxbuf;
/**
* @brief Number of bytes of data to receive.
*/
size_t rxbytes;
/**
* @brief Current index in buffer when receiving data.
*/
size_t rxidx;
};
/**
* @brief Type of a structure representing an I2C driver.
*/
typedef struct I2CDriver I2CDriver;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @brief Get errors from I2C driver.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
#define i2c_lld_get_errors(i2cp) ((i2cp)->errors)
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if !defined(__DOXYGEN__)
#if AVR_I2C_USE_I2C1
extern I2CDriver I2CD1;
#endif
#endif /* !defined(__DOXYGEN__) */
#ifdef __cplusplus
extern "C" {
#endif
void i2c_lld_init(void);
void i2c_lld_start(I2CDriver *i2cp);
void i2c_lld_stop(I2CDriver *i2cp);
msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
const uint8_t *txbuf, size_t txbytes,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout);
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_I2C */
#endif /* _I2C_LLD_H_ */
/** @} */

336
os/hal/ports/AVR/icu_lld.c Normal file
View File

@ -0,0 +1,336 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/icu_lld.c
* @brief AVR ICU driver subsystem low level driver source.
*
* @addtogroup ICU
* @{
*/
#include "hal.h"
#if HAL_USE_ICU || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
typedef struct {
volatile uint8_t *tccra;
volatile uint8_t *tccrb;
volatile uint16_t *tcnt;
volatile uint8_t *timsk;
} icu_registers_t;
static icu_registers_t regs_table[]=
{
#if AVR_ICU_USE_TIM1 || defined(__DOXYGEN__)
{&TCCR1A, &TCCR1B, &TCNT1, &TIMSK1},
#endif
#if AVR_ICU_USE_TIM3 || defined(__DOXYGEN__)
{&TCCR3A, &TCCR3B, &TCNT3, &TIMSK3},
#endif
#if AVR_ICU_USE_TIM4 || defined(__DOXYGEN__)
{&TCCR4A, &TCCR4B, &TCNT4, &TIMSK4},
#endif
#if AVR_ICU_USE_TIM5 || defined(__DOXYGEN__)
{&TCCR5A, &TCCR5B, &TCNT5, &TIMSK5},
#endif
};
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief ICU1 driver identifier.
*/
#if AVR_ICU_USE_TIM1 || defined(__DOXYGEN__)
ICUDriver ICUD1;
#endif
/**
* @brief ICU3 driver identifier.
*/
#if AVR_ICU_USE_TIM3 || defined(__DOXYGEN__)
ICUDriver ICUD3;
#endif
/**
* @brief ICU4 driver identifier.
*/
#if AVR_ICU_USE_TIM4 || defined(__DOXYGEN__)
ICUDriver ICUD4;
#endif
/**
* @brief ICU5 driver identifier.
*/
#if AVR_ICU_USE_TIM5 || defined(__DOXYGEN__)
ICUDriver ICUD5;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static inline void handle_capture_isr(ICUDriver *icup,
volatile uint16_t *icr,
volatile uint8_t *tccrb,
volatile uint16_t *tcnt)
{
uint16_t value = *icr;
uint8_t rising = (*tccrb & (1 << ICES1)) ? 1 : 0;
*tccrb ^= (1 << ICES1);
if ((icup->config->mode == ICU_INPUT_ACTIVE_HIGH && rising) ||
(icup->config->mode == ICU_INPUT_ACTIVE_LOW && !rising)) {
icup->width = value;
if (icup->config->width_cb != NULL)
icup->config->width_cb(icup);
} else {
icup->period = value;
if (icup->config->period_cb != NULL)
icup->config->period_cb(icup);
/* Reset counter at the end of every cycle */
*tcnt = 0;
}
}
static uint8_t index(ICUDriver *icup)
{
uint8_t index = 0;
#if AVR_ICU_USE_TIM1 || defined(__DOXYGEN__)
if (icup == &ICUD1) return index;
else index++;
#endif
#if AVR_ICU_USE_TIM3 || defined(__DOXYGEN__)
if (icup == &ICUD3) return index;
else index++;
#endif
#if AVR_ICU_USE_TIM4 || defined(__DOXYGEN__)
if (icup == &ICUD4) return index;
else index++;
#endif
#if AVR_ICU_USE_TIM5 || defined(__DOXYGEN__)
if (icup == &ICUD5) return index;
else index++;
#endif
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if AVR_ICU_USE_TIM1 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER1_CAPT_vect)
{
OSAL_IRQ_PROLOGUE();
handle_capture_isr(&ICUD1, &ICR1, &TCCR1B, &TCNT1);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER1_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
ICUD1.config->overflow_cb(&ICUD1);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_ICU_USE_TIM3 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER3_CAPT_vect)
{
OSAL_IRQ_PROLOGUE();
handle_capture_isr(&ICUD3, &ICR3, &TCCR3B, &TCNT3);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER3_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
ICUD3.config->overflow_cb(&ICUD3);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_ICU_USE_TIM4 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER4_CAPT_vect)
{
OSAL_IRQ_PROLOGUE();
handle_capture_isr(&ICUD4, &ICR4, &TCCR4B, &TCNT4);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER4_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
ICUD4.config->overflow_cb(&ICUD4);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_ICU_USE_TIM5 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER5_CAPT_vect)
{
OSAL_IRQ_PROLOGUE();
handle_capture_isr(&ICUD5, &ICR5, &TCCR5B, &TCNT5);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER5_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
ICUD5.config->overflow_cb(&ICUD5);
OSAL_IRQ_EPILOGUE();
}
#endif
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level ICU driver initialization.
*
* @notapi
*/
void icu_lld_init(void) {
#if AVR_ICU_USE_TIM1
icuObjectInit(&ICUD1);
#endif
#if AVR_ICU_USE_TIM3
icuObjectInit(&ICUD3);
#endif
#if AVR_ICU_USE_TIM4
icuObjectInit(&ICUD4);
#endif
#if AVR_ICU_USE_TIM5
icuObjectInit(&ICUD5);
#endif
}
/**
* @brief Configures and activates the ICU peripheral.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @notapi
*/
void icu_lld_start(ICUDriver *icup) {
if (icup->state == ICU_STOP) {
uint8_t i = index(icup);
/* Normal waveform generation (counts from 0 to 0xFFFF) */
*regs_table[i].tccra &= ~((1 << WGM11) | (1 << WGM10));
*regs_table[i].tccrb &= ~((1 << WGM13) | (1 << WGM12));
/* Enable noise canceler, set prescale to CLK/1024 */
*regs_table[i].tccrb |= (1 << ICNC1) | (1 << CS12) | (1 << CS10);
if (icup->config->mode == ICU_INPUT_ACTIVE_HIGH)
*regs_table[i].tccrb |= (1 << ICES1);
else
*regs_table[i].tccrb &= ~(1 << ICES1);
}
}
/**
* @brief Deactivates the ICU peripheral.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @notapi
*/
void icu_lld_stop(ICUDriver *icup) {
if (icup->state == ICU_READY) {
/* Resets the peripheral.*/
/* Disables the peripheral.*/
#if AVR_ICU_USE_TIM1
if (&ICUD1 == icup) {
}
#endif /* AVR_ICU_USE_TIM1 */
}
}
/**
* @brief Enables the input capture.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @notapi
*/
void icu_lld_enable(ICUDriver *icup) {
uint8_t i = index(icup);
icup->width = icup->period = 0;
*regs_table[i].tcnt = 0;
*regs_table[i].timsk |= (1 << ICIE1);
if (icup->config->overflow_cb != NULL)
*regs_table[i].timsk |= (1 << TOIE1);
}
/**
* @brief Disables the input capture.
*
* @param[in] icup pointer to the @p ICUDriver object
*
* @notapi
*/
void icu_lld_disable(ICUDriver *icup) {
uint8_t i = index(icup);
*regs_table[i].timsk &= ~((1 << ICIE1) | (1 << TOIE1));
}
/**
* @brief Returns the width of the latest pulse.
* @details The pulse width is defined as number of ticks between the start
* edge and the stop edge.
*
* @param[in] icup pointer to the @p ICUDriver object
* @return The number of ticks.
*
* @notapi
*/
icucnt_t icu_lld_get_width(ICUDriver *icup) {
return icup->width;
}
/**
* @brief Returns the width of the latest cycle.
* @details The cycle width is defined as number of ticks between a start
* edge and the next start edge.
*
* @param[in] icup pointer to the @p ICUDriver object
* @return The number of ticks.
*
* @notapi
*/
icucnt_t icu_lld_get_period(ICUDriver *icup) {
return icup->period;
}
#endif /* HAL_USE_ICU */
/** @} */

195
os/hal/ports/AVR/icu_lld.h Normal file
View File

@ -0,0 +1,195 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/icu_lld.h
* @brief AVR ICU driver subsystem low level driver header.
*
* @addtogroup ICU
* @{
*/
#ifndef _ICU_LLD_H_
#define _ICU_LLD_H_
#if HAL_USE_ICU || defined(__DOXYGEN__)
#include "avr_timers.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief ICU driver enable switch.
* @details If set to @p TRUE the support for ICU1 is included.
*/
#if !defined(AVR_ICU_USE_TIM1) || defined(__DOXYGEN__)
#define AVR_ICU_USE_TIM1 FALSE
#endif
/**
* @brief ICU driver enable switch.
* @details If set to @p TRUE the support for ICU3 is included.
*/
#if !defined(AVR_ICU_USE_TIM3) || defined(__DOXYGEN__)
#define AVR_ICU_USE_TIM3 FALSE
#endif
/**
* @brief ICU driver enable switch.
* @details If set to @p TRUE the support for ICU4 is included.
*/
#if !defined(AVR_ICU_USE_TIM4) || defined(__DOXYGEN__)
#define AVR_ICU_USE_TIM4 FALSE
#endif
/**
* @brief ICU driver enable switch.
* @details If set to @p TRUE the support for ICU5 is included.
*/
#if !defined(AVR_ICU_USE_TIM5) || defined(__DOXYGEN__)
#define AVR_ICU_USE_TIM5 FALSE
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief ICU driver mode.
*/
typedef enum {
ICU_INPUT_ACTIVE_HIGH = 0, /**< Trigger on rising edge. */
ICU_INPUT_ACTIVE_LOW = 1, /**< Trigger on falling edge. */
} icumode_t;
/**
* @brief ICU frequency type.
*/
typedef uint16_t icufreq_t;
/**
* @brief ICU counter type.
*/
typedef uint16_t icucnt_t;
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
/**
* @brief Driver mode.
*/
icumode_t mode;
/**
* @brief Timer clock in Hz.
* @note The low level can use assertions in order to catch invalid
* frequency specifications.
*/
icufreq_t frequency;
/**
* @brief Callback for pulse width measurement.
*/
icucallback_t width_cb;
/**
* @brief Callback for cycle period measurement.
*/
icucallback_t period_cb;
/**
* @brief Callback for timer overflow.
*/
icucallback_t overflow_cb;
/* End of the mandatory fields.*/
} ICUConfig;
/**
* @brief Structure representing an ICU driver.
*/
struct ICUDriver {
/**
* @brief Driver state.
*/
icustate_t state;
/**
* @brief Current configuration data.
*/
const ICUConfig *config;
#if defined(ICU_DRIVER_EXT_FIELDS)
ICU_DRIVER_EXT_FIELDS
#endif
/* End of the mandatory fields.*/
/**
* @brief Width value read by ISR.
*/
icucnt_t width;
/**
* @brief Period value read by ISR.
*/
icucnt_t period;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AVR_ICU_USE_TIM1 && !defined(__DOXYGEN__)
extern ICUDriver ICUD1;
#endif
#if AVR_ICU_USE_TIM3 && !defined(__DOXYGEN__)
extern ICUDriver ICUD3;
#endif
#if AVR_ICU_USE_TIM4 && !defined(__DOXYGEN__)
extern ICUDriver ICUD4;
#endif
#if AVR_ICU_USE_TIM5 && !defined(__DOXYGEN__)
extern ICUDriver ICUD5;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void icu_lld_init(void);
void icu_lld_start(ICUDriver *icup);
void icu_lld_stop(ICUDriver *icup);
void icu_lld_enable(ICUDriver *icup);
void icu_lld_disable(ICUDriver *icup);
icucnt_t icu_lld_get_width(ICUDriver *icup);
icucnt_t icu_lld_get_period(ICUDriver *icup);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_ICU */
#endif /* _ICU_LLD_H_ */
/** @} */

156
os/hal/ports/AVR/pal_lld.c Normal file
View File

@ -0,0 +1,156 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/pal_lld.c
* @brief AVR GPIO low level driver code.
*
* @addtogroup PAL
* @{
*/
#include "hal.h"
#if HAL_USE_PAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief AVR GPIO ports configuration.
* @details GPIO registers initialization.
*
* @param[in] config the AVR ports configuration
*
* @notapi
*/
void _pal_lld_init(const PALConfig *config) {
#if defined(PORTA) || defined(__DOXYGEN__)
PORTA = config->porta.out;
DDRA = config->porta.dir;
#endif
#if defined(PORTB) || defined(__DOXYGEN__)
PORTB = config->portb.out;
DDRB = config->portb.dir;
#endif
#if defined(PORTC) || defined(__DOXYGEN__)
PORTC = config->portc.out;
DDRC = config->portc.dir;
#endif
#if defined(PORTD) || defined(__DOXYGEN__)
PORTD = config->portd.out;
DDRD = config->portd.dir;
#endif
#if defined(PORTE) || defined(__DOXYGEN__)
PORTE = config->porte.out;
DDRE = config->porte.dir;
#endif
#if defined(PORTF) || defined(__DOXYGEN__)
PORTF = config->portf.out;
DDRF = config->portf.dir;
#endif
#if defined(PORTG) || defined(__DOXYGEN__)
PORTG = config->portg.out;
DDRG = config->portg.dir;
#endif
#if defined(PORTH) || defined(__DOXYGEN__)
PORTH = config->porth.out;
DDRH = config->porth.dir;
#endif
#if defined(PORTJ) || defined(__DOXYGEN__)
PORTJ = config->portj.out;
DDRJ = config->portj.dir;
#endif
#if defined(PORTK) || defined(__DOXYGEN__)
PORTK = config->portk.out;
DDRK = config->portk.dir;
#endif
#if defined(PORTL) || defined(__DOXYGEN__)
PORTL = config->portl.out;
DDRL = config->portl.dir;
#endif
}
/**
* @brief Pads mode setup.
* @details This function programs a pads group belonging to the same port
* with the specified mode.
*
* @param[in] port the port identifier
* @param[in] mask the group mask
* @param[in] mode the mode
*
* @note This function is not meant to be invoked directly by the application
* code.
* @note @p PAL_MODE_UNCONNECTED is implemented as output as recommended by
* the AVR Family User's Guide. Unconnected pads are set to input
* with pull-up by default.
*
* @notapi
*/
void _pal_lld_setgroupmode(ioportid_t port,
ioportmask_t mask,
iomode_t mode) {
switch (mode) {
case PAL_MODE_RESET:
case PAL_MODE_INPUT:
case PAL_MODE_INPUT_ANALOG:
port->dir &= ~mask;
port->out &= ~mask;
break;
case PAL_MODE_UNCONNECTED:
case PAL_MODE_INPUT_PULLUP:
port->dir &= ~mask;
port->out |= mask;
break;
case PAL_MODE_OUTPUT_PUSHPULL:
port->dir |= mask;
break;
}
}
#endif /* HAL_USE_PAL */
/** @} */

346
os/hal/ports/AVR/pal_lld.h Normal file
View File

@ -0,0 +1,346 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/pal_lld.h
* @brief AVR GPIO low level driver header.
*
* @addtogroup PAL
* @{
*/
#ifndef _PAL_LLD_H_
#define _PAL_LLD_H_
#include "avr_pins.h"
#if HAL_USE_PAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Unsupported modes and specific modes */
/*===========================================================================*/
#undef PAL_MODE_INPUT_PULLDOWN
#undef PAL_MODE_OUTPUT_OPENDRAIN
/*===========================================================================*/
/* I/O Ports Types and constants. */
/*===========================================================================*/
/**
* @brief Width, in bits, of an I/O port.
*/
#define PAL_IOPORTS_WIDTH 8
/**
* @brief Whole port mask.
* @details This macro specifies all the valid bits into a port.
*/
#define PAL_WHOLE_PORT ((ioportmask_t)0xFF)
/**
* @brief AVR setup registers.
*/
typedef struct {
uint8_t out;
uint8_t dir;
} avr_gpio_setup_t;
/**
* @brief AVR registers block.
* @note On some devices registers do not follow this layout on some
* ports, the ports with abnormal layout cannot be used through
* PAL driver. Example: PORT F on Mega128.
*/
typedef struct {
volatile uint8_t in;
volatile uint8_t dir;
volatile uint8_t out;
} avr_gpio_registers_t;
/**
* @brief Generic I/O ports static initializer.
* @details An instance of this structure must be passed to @p palInit() at
* system startup time in order to initialized the digital I/O
* subsystem. This represents only the initial setup, specific pads
* or whole ports can be reprogrammed at later time.
*/
typedef struct {
#if defined(PORTA) || defined(__DOXYGEN__)
avr_gpio_setup_t porta;
#endif
#if defined(PORTB) || defined(__DOXYGEN__)
avr_gpio_setup_t portb;
#endif
#if defined(PORTC) || defined(__DOXYGEN__)
avr_gpio_setup_t portc;
#endif
#if defined(PORTD) || defined(__DOXYGEN__)
avr_gpio_setup_t portd;
#endif
#if defined(PORTE) || defined(__DOXYGEN__)
avr_gpio_setup_t porte;
#endif
#if defined(PORTF) || defined(__DOXYGEN__)
avr_gpio_setup_t portf;
#endif
#if defined(PORTG) || defined(__DOXYGEN__)
avr_gpio_setup_t portg;
#endif
#if defined(PORTH) || defined(__DOXYGEN__)
avr_gpio_setup_t porth;
#endif
#if defined(PORTJ) || defined(__DOXYGEN__)
avr_gpio_setup_t portj;
#endif
#if defined(PORTK) || defined(__DOXYGEN__)
avr_gpio_setup_t portk;
#endif
#if defined(PORTL) || defined(__DOXYGEN__)
avr_gpio_setup_t portl;
#endif
} PALConfig;
/**
* @brief Digital I/O port sized unsigned type.
*/
typedef uint8_t ioportmask_t;
/**
* @brief Digital I/O modes.
*/
typedef uint8_t iomode_t;
/**
* @brief Port Identifier.
* @details This type can be a scalar or some kind of pointer, do not make
* any assumption about it, use the provided macros when populating
* variables of this type.
*/
typedef avr_gpio_registers_t *ioportid_t;
/*===========================================================================*/
/* I/O Ports Identifiers. */
/*===========================================================================*/
#if defined(PORTA) || defined(__DOXYGEN__)
/**
* @brief GPIO port A identifier.
*/
#define IOPORT1 ((volatile avr_gpio_registers_t *)&PINA)
#endif
#if defined(PORTB) || defined(__DOXYGEN__)
/**
* @brief GPIO port B identifier.
*/
#define IOPORT2 ((volatile avr_gpio_registers_t *)&PINB)
#endif
#if defined(PORTC) || defined(__DOXYGEN__)
/**
* @brief GPIO port C identifier.
*/
#define IOPORT3 ((volatile avr_gpio_registers_t *)&PINC)
#endif
#if defined(PORTD) || defined(__DOXYGEN__)
/**
* @brief GPIO port D identifier.
*/
#define IOPORT4 ((volatile avr_gpio_registers_t *)&PIND)
#endif
#if defined(PORTE) || defined(__DOXYGEN__)
/**
* @brief GPIO port E identifier.
*/
#define IOPORT5 ((volatile avr_gpio_registers_t *)&PINE)
#endif
#if defined(PORTF) || defined(__DOXYGEN__)
/**
* @brief GPIO port F identifier.
*/
#define IOPORT6 ((volatile avr_gpio_registers_t *)&PINF)
#endif
#if defined(PORTG) || defined(__DOXYGEN__)
/**
* @brief GPIO port G identifier.
*/
#define IOPORT7 ((volatile avr_gpio_registers_t *)&PING)
#endif
#if defined(PORTH) || defined(__DOXYGEN__)
/**
* @brief GPIO port H identifier.
*/
#define IOPORT8 ((volatile avr_gpio_registers_t *)&PINH)
#endif
#if defined(PORTJ) || defined(__DOXYGEN__)
/**
* @brief GPIO port J identifier.
*/
#define IOPORT9 ((volatile avr_gpio_registers_t *)&PINJ)
#endif
#if defined(PORTK) || defined(__DOXYGEN__)
/**
* @brief GPIO port K identifier.
*/
#define IOPORT10 ((volatile avr_gpio_registers_t *)&PINK)
#endif
#if defined(PORTL) || defined(__DOXYGEN__)
/**
* @brief GPIO port L identifier.
*/
#define IOPORT11 ((volatile avr_gpio_registers_t *)&PINL)
#endif
#if defined(PORTADC) || defined(__DOXYGEN__)
/**
* @brief GPIO port ADC identifier.
*/
#define IOPORTADC ((volatile avr_gpio_registers_t *)&PINADC)
#endif
#if defined(PORT_SPI1) || defined(__DOXYGEN__)
/**
* @brief GPIO port SPI1 identifier.
*/
#define IOPORTSPI1 ((volatile avr_gpio_registers_t *)&PIN_SPI1)
#endif
/*===========================================================================*/
/* Implementation, some of the following macros could be implemented as */
/* functions, if so please put them in pal_lld.c. */
/*===========================================================================*/
/**
* @brief Low level PAL subsystem initialization.
*
* @param[in] config the architecture-dependent ports configuration
*
* @notapi
*/
#define pal_lld_init(config) _pal_lld_init(config)
/**
* @brief Reads the physical I/O port states.
*
* @param[in] port port identifier
* @return The port bits.
*
* @notapi
*/
#define pal_lld_readport(port) ((port)->in)
/**
* @brief Reads the output latch.
* @details The purpose of this function is to read back the latched output
* value.
*
* @param[in] port port identifier
* @return The latched logical states.
*
* @notapi
*/
#define pal_lld_readlatch(port) ((port)->out)
/**
* @brief Writes a bits mask on a I/O port.
*
* @param[in] port port identifier
* @param[in] bits bits to be written on the specified port
*
* @notapi
*/
#define pal_lld_writeport(port, bits) ((port)->out = bits)
/**
* @brief Pads group mode setup.
* @details This function programs a pads group belonging to the same port
* with the specified mode.
* @note Programming an unknown or unsupported mode is silently ignored.
*
* @param[in] port port identifier
* @param[in] mask group mask
* @param[in] offset group bit offset within the port
* @param[in] mode group mode
*
* @notapi
*/
#define pal_lld_setgroupmode(port, mask, offset, mode) \
_pal_lld_setgroupmode(port, mask << offset, mode)
/**
* @brief Sets a pad logical state to @p PAL_HIGH.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
*
* @notapi
*/
#define pal_lld_setpad(port, pad) \
__asm__ __volatile__ \
( \
"sbi %0,%1\n\t" \
: \
: "I" (_SFR_IO_ADDR(port->out)), \
"I" (pad) \
\
)
/**
* @brief Clears a pad logical state to @p PAL_LOW.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
*
* @notapi
*/
#define pal_lld_clearpad(port, pad) \
__asm__ __volatile__ \
( \
"cbi %0,%1\n\t" \
: \
: "I" (_SFR_IO_ADDR(port->out)), \
"I" (pad) \
\
)
extern ROMCONST PALConfig pal_default_config;
#ifdef __cplusplus
extern "C" {
#endif
void _pal_lld_init(const PALConfig *config);
void _pal_lld_setgroupmode(ioportid_t port,
ioportmask_t mask,
iomode_t mode);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_PAL */
#endif /* _PAL_LLD_H_ */
/** @} */

View File

@ -0,0 +1,14 @@
# List of all the AVR platform files.
PLATFORMSRC = ${CHIBIOS}/os/hal/ports/AVR/hal_lld.c \
${CHIBIOS}/os/hal/ports/AVR/pal_lld.c \
${CHIBIOS}/os/hal/ports/AVR/serial_lld.c \
${CHIBIOS}/os/hal/ports/AVR/adc_lld.c \
${CHIBIOS}/os/hal/ports/AVR/i2c_lld.c \
${CHIBIOS}/os/hal/ports/AVR/spi_lld.c \
${CHIBIOS}/os/hal/ports/AVR/gpt_lld.c \
${CHIBIOS}/os/hal/ports/AVR/pwm_lld.c \
${CHIBIOS}/os/hal/ports/AVR/icu_lld.c \
${CHIBIOS}/os/hal/ports/AVR/st_lld.c
# Required include directories
PLATFORMINC = ${CHIBIOS}/os/hal/ports/AVR

491
os/hal/ports/AVR/pwm_lld.c Normal file
View File

@ -0,0 +1,491 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This driver is based on the work done by Matteo Serva available at
http://github.com/matteoserva/ChibiOS-AVR
*/
/**
* @file AVR/pwm_lld.c
* @brief AVR PWM driver subsystem low level driver.
*
* @addtogroup PWM
* @{
*/
#include "hal.h"
#if HAL_USE_PWM || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
typedef struct {
volatile uint8_t *tccra;
volatile uint8_t *tccrb;
volatile uint8_t *ocrah;
volatile uint8_t *ocral;
volatile uint8_t *ocrbh;
volatile uint8_t *ocrbl;
volatile uint8_t *ocrch;
volatile uint8_t *ocrcl;
volatile uint8_t *tifr;
volatile uint8_t *timsk;
} timer_registers_t;
static timer_registers_t regs_table[]=
{
#if AVR_PWM_USE_TIM1 || defined(__DOXYGEN__)
#if defined(OCR1C)
{&TCCR1A, &TCCR1B, &OCR1AH, &OCR1AL, &OCR1BH, &OCR1BL, &OCR1CH, &OCR1CL, &TIFR1, &TIMSK1},
#else
{&TCCR1A, &TCCR1B, &OCR1AH, &OCR1AL, &OCR1BH, &OCR1BL, NULL, NULL, &TIFR1, &TIMSK1},
#endif
#endif
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
{&TCCR2A, &TCCR2B, &OCR2A, &OCR2A, &OCR2B, &OCR2B, NULL, NULL, &TIFR2, &TIMSK2},
#endif
#if AVR_PWM_USE_TIM3 || defined(__DOXYGEN__)
{&TCCR3A, &TCCR3B, &OCR3AH, &OCR3AL, &OCR3BH, &OCR3BL, &OCR3CH, &OCR3CL, &TIFR3, &TIMSK3},
#endif
#if AVR_PWM_USE_TIM4 || defined(__DOXYGEN__)
{&TCCR4A, &TCCR4B, &OCR4AH, &OCR4AL, &OCR4CH, &OCR4CL, &OCR4CH, &OCR4CL, &TIFR4, &TIMSK4},
#endif
#if AVR_PWM_USE_TIM5 || defined(__DOXYGEN__)
{&TCCR5A, &TCCR5B, &OCR5AH, &OCR5AL, &OCR5BH, &OCR5BL, &OCR5CH, &OCR5CL, &TIFR5, &TIMSK5},
#endif
};
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/** @brief PWM driver identifiers.*/
#if AVR_PWM_USE_TIM1 || defined(__DOXYGEN__)
PWMDriver PWMD1;
#endif
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
PWMDriver PWMD2;
#endif
#if AVR_PWM_USE_TIM3 || defined(__DOXYGEN__)
PWMDriver PWMD3;
#endif
#if AVR_PWM_USE_TIM4 || defined(__DOXYGEN__)
PWMDriver PWMD4;
#endif
#if AVR_PWM_USE_TIM5 || defined(__DOXYGEN__)
PWMDriver PWMD5;
#endif
/*===========================================================================*/
/* Driver local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static void config_channel(volatile uint8_t *tccra,
uint8_t com1,
uint8_t com0,
pwmmode_t mode)
{
*tccra &= ~((1 << com1) | (1 << com0));
if (mode == PWM_OUTPUT_ACTIVE_HIGH)
*tccra |= ((1 << com1) | (0 << com0)); /* non inverting mode */
else if (mode == PWM_OUTPUT_ACTIVE_LOW)
*tccra |= (1 << com1) | (1 << com0); /* inverting mode */
}
static uint8_t timer_index(PWMDriver *pwmp)
{
uint8_t index = 0;
#if AVR_PWM_USE_TIM1 || defined(__DOXYGEN__)
if (pwmp == &PWMD1) return index;
else index++;
#endif
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
if (pwmp == &PWMD2) return index;
else index++;
#endif
#if AVR_PWM_USE_TIM3 || defined(__DOXYGEN__)
if (pwmp == &PWMD3) return index;
else index++;
#endif
#if AVR_PWM_USE_TIM4 || defined(__DOXYGEN__)
if (pwmp == &PWMD4) return index;
else index++;
#endif
#if AVR_PWM_USE_TIM5 || defined(__DOXYGEN__)
if (pwmp == &PWMD5) return index;
else index++;
#endif
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*
* interrupt for compare1&2 and clock overflow. pwmd1 & pwmd2
*/
#if AVR_PWM_USE_TIM1 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER1_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD1.config->callback(&PWMD1);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER1_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD1.config->channels[0].callback(&PWMD1);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER1_COMPB_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD1.config->channels[1].callback(&PWMD1);
OSAL_IRQ_EPILOGUE();
}
#if PWM_CHANNELS > 2
OSAL_IRQ_HANDLER(TIMER1_COMPC_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD1.config->channels[2].callback(&PWMD1);
OSAL_IRQ_EPILOGUE();
}
#endif
#endif
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER2_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD2.config->callback(&PWMD2);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER2_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD2.config->channels[0].callback(&PWMD2);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER2_COMPB_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD2.config->channels[1].callback(&PWMD2);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_PWM_USE_TIM3 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER3_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD3.config->callback(&PWMD3);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER3_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD3.config->channels[0].callback(&PWMD3);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER3_COMPB_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD3.config->channels[1].callback(&PWMD3);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER3_COMPC_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD3.config->channels[2].callback(&PWMD3);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_PWM_USE_TIM4 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER4_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD4.config->callback(&PWMD4);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER4_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD4.config->channels[0].callback(&PWMD4);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER4_COMPB_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD4.config->channels[1].callback(&PWMD4);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER4_COMPC_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD4.config->channels[2].callback(&PWMD4);
OSAL_IRQ_EPILOGUE();
}
#endif
#if AVR_PWM_USE_TIM5 || defined(__DOXYGEN__)
OSAL_IRQ_HANDLER(TIMER5_OVF_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD5.config->callback(&PWMD5);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER5_COMPA_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD5.config->channels[0].callback(&PWMD5);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER5_COMPB_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD5.config->channels[1].callback(&PWMD5);
OSAL_IRQ_EPILOGUE();
}
OSAL_IRQ_HANDLER(TIMER5_COMPC_vect)
{
OSAL_IRQ_PROLOGUE();
PWMD5.config->channels[2].callback(&PWMD5);
OSAL_IRQ_EPILOGUE();
}
#endif
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level PWM driver initialization.
*
* @notapi
*/
void pwm_lld_init(void)
{
#if AVR_PWM_USE_TIM1 || defined(__DOXYGEN__)
pwmObjectInit(&PWMD1);
TCCR1A = (1 << WGM11) | (1 << WGM10);
TCCR1B = (0 << WGM13) | (1 << WGM12);
#endif
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
pwmObjectInit(&PWMD2);
TCCR2A = (1 << WGM21) | (1 << WGM20);
TCCR2B = (0 << WGM22);
#endif
#if AVR_PWM_USE_TIM3 || defined(__DOXYGEN__)
pwmObjectInit(&PWMD3);
TCCR3A = (1 << WGM31) | (1 << WGM30);
TCCR3B = (0 << WGM33) | (1 << WGM32);
#endif
#if AVR_PWM_USE_TIM4 || defined(__DOXYGEN__)
pwmObjectInit(&PWMD4);
TCCR4A = (1 << WGM41) | (1 << WGM40);
TCCR4B = (0 << WGM43) | (1 << WGM42);
#endif
#if AVR_PWM_USE_TIM5 || defined(__DOXYGEN__)
pwmObjectInit(&PWMD5);
TCCR5A = (1 << WGM51) | (1 << WGM50);
TCCR5B = (0 << WGM53) | (1 << WGM52);
#endif
}
/**
* @brief Configures and activates the PWM peripheral.
*
* @param[in] pwmp pointer to the @p PWMDriver object
*
* @notapi
*/
void pwm_lld_start(PWMDriver *pwmp)
{
if (pwmp->state == PWM_STOP) {
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
if (pwmp == &PWMD2) {
TCCR2B &= ~((1 << CS22) | (1 << CS21));
TCCR2B |= (1 << CS20);
if (pwmp->config->callback != NULL)
TIMSK2 |= (1 << TOIE2);
return;
}
#endif
/* TODO: support other prescaler options */
uint8_t i = timer_index(pwmp);
*regs_table[i].tccrb &= ~(1 << CS11);
*regs_table[i].tccrb |= (1 << CS12) | (1 << CS10);
*regs_table[i].timsk = (1 << TOIE1);
}
}
/**
* @brief Deactivates the PWM peripheral.
*
* @param[in] pwmp pointer to the @p PWMDriver object
*
* @notapi
*/
void pwm_lld_stop(PWMDriver *pwmp)
{
uint8_t i = timer_index(pwmp);
*regs_table[i].tccrb &= ~((1 << CS12) | (1 << CS11) | (1 << CS10));
*regs_table[i].timsk = 0;
}
/**
* @brief Changes the period the PWM peripheral.
* @details This function changes the period of a PWM unit that has already
* been activated using @p pwmStart().
* @pre The PWM unit must have been activated using @p pwmStart().
* @post The PWM unit period is changed to the new value.
* @note The function has effect at the next cycle start.
* @note If a period is specified that is shorter than the pulse width
* programmed in one of the channels then the behavior is not
* guaranteed.
*
* @param[in] pwmp pointer to a @p PWMDriver object
* @param[in] period new cycle time in ticks
*
* @notapi
*/
void pwm_lld_change_period(PWMDriver *pwmp, pwmcnt_t period)
{
}
/**
* @brief Enables a PWM channel.
* @pre The PWM unit must have been activated using @p pwmStart().
* @post The channel is active using the specified configuration.
* @note Depending on the hardware implementation this function has
* effect starting on the next cycle (recommended implementation)
* or immediately (fallback implementation).
*
* @param[in] pwmp pointer to a @p PWMDriver object
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
* @param[in] width PWM pulse width as clock pulses number
*
* @notapi
*/
void pwm_lld_enable_channel(PWMDriver *pwmp,
pwmchannel_t channel,
pwmcnt_t width)
{
uint16_t val = width;
if (val > MAX_PWM_VALUE)
val = MAX_PWM_VALUE;
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
if (pwmp == &PWMD2) {
config_channel(&TCCR2A,
7 - 2*channel,
6 - 2*channel,
pwmp->config->channels[channel].mode);
TIMSK2 |= (1 << (channel + 1));
/* Timer 2 is 8 bit */
if (val > 0xFF)
val = 0xFF;
if (pwmp->config->channels[channel].callback) {
switch (channel) {
case 0: OCR2A = val; break;
case 1: OCR2B = val; break;
}
}
return;
}
#endif
uint8_t i = timer_index(pwmp);
config_channel(regs_table[i].tccra,
7 - 2*channel,
6 - 2*channel,
pwmp->config->channels[channel].mode);
volatile uint8_t *ocrh, *ocrl;
switch (channel) {
case 1:
ocrh = regs_table[i].ocrbh;
ocrl = regs_table[i].ocrbl;
break;
case 2:
ocrh = regs_table[i].ocrch;
ocrl = regs_table[i].ocrcl;
break;
default:
ocrh = regs_table[i].ocrah;
ocrl = regs_table[i].ocral;
}
*ocrh = val >> 8;
*ocrl = val & 0xFF;
*regs_table[i].tifr |= (1 << (channel + 1));
if (pwmp->config->channels[channel].callback)
*regs_table[i].timsk |= (1 << (channel + 1));
}
/**
* @brief Disables a PWM channel.
* @pre The PWM unit must have been activated using @p pwmStart().
* @post The channel is disabled and its output line returned to the
* idle state.
* @note Depending on the hardware implementation this function has
* effect starting on the next cycle (recommended implementation)
* or immediately (fallback implementation).
*
* @param[in] pwmp pointer to a @p PWMDriver object
* @param[in] channel PWM channel identifier (0...PWM_CHANNELS-1)
*
* @notapi
*/
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel)
{
uint8_t i = timer_index(pwmp);
config_channel(regs_table[i].tccra,
7 - 2*channel,
6 - 2*channel,
PWM_OUTPUT_DISABLED);
*regs_table[i].timsk &= ~(1 << (channel + 1));
}
#endif /* HAL_USE_PWM */
/** @} */

214
os/hal/ports/AVR/pwm_lld.h Normal file
View File

@ -0,0 +1,214 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
This driver is based on the work done by Matteo Serva available at
http://github.com/matteoserva/ChibiOS-AVR
*/
/**
* @file AVR/pwm_lld.h
* @brief AVR PWM driver subsystem low level driver.
*
* @addtogroup PWM
* @{
*/
#ifndef _PWM_LLD_H_
#define _PWM_LLD_H_
#if HAL_USE_PWM || defined(__DOXYGEN__)
#include "avr_timers.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
#if !defined(AVR_PWM_USE_TIM1)
#define AVR_PWM_USE_TIM1 FALSE
#endif
#if !defined(AVR_PWM_USE_TIM2)
#define AVR_PWM_USE_TIM2 FALSE
#endif
#if !defined(AVR_PWM_USE_TIM3)
#define AVR_PWM_USE_TIM3 FALSE
#endif
#if !defined(AVR_PWM_USE_TIM4)
#define AVR_PWM_USE_TIM4 FALSE
#endif
#if !defined(AVR_PWM_USE_TIM5)
#define AVR_PWM_USE_TIM5 FALSE
#endif
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief Number of PWM channels per PWM driver.
*/
#if !defined(PWM_CHANNELS) || defined(__DOXYGEN__)
#if defined(TIMER1_COMPC_vect)
#define PWM_CHANNELS 3
#else
#define PWM_CHANNELS 2
#endif
#endif
#define MAX_PWM_VALUE 0x3FF
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief PWM mode type.
*/
typedef uint8_t pwmmode_t;
/**
* @brief PWM channel type.
*/
typedef uint8_t pwmchannel_t;
/**
* @brief PWM counter type.
*/
typedef uint16_t pwmcnt_t;
/**
* @brief PWM driver channel configuration structure.
* @note Some architectures may not be able to support the channel mode
* or the callback, in this case the fields are ignored.
*/
typedef struct {
/**
* @brief Channel active logic level.
*/
pwmmode_t mode;
/**
* @brief Channel callback pointer.
* @note This callback is invoked on the channel compare event. If set to
* @p NULL then the callback is disabled.
*/
pwmcallback_t callback;
/* End of the mandatory fields.*/
} PWMChannelConfig;
/**
* @brief Driver configuration structure.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
typedef struct {
/**
* @brief Timer clock in Hz.
* @note The low level can use assertions in order to catch invalid
* frequency specifications.
*/
uint16_t frequency;
/**
* @brief PWM period in ticks.
* @note The low level can use assertions in order to catch invalid
* period specifications.
*/
pwmcnt_t period;
/**
* @brief Periodic callback pointer.
* @note This callback is invoked on PWM counter reset. If set to
* @p NULL then the callback is disabled.
*/
pwmcallback_t callback;
/**
* @brief Channels configurations.
*/
PWMChannelConfig channels[PWM_CHANNELS];
/* End of the mandatory fields.*/
} PWMConfig;
/**
* @brief Structure representing an PWM driver.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
struct PWMDriver {
/**
* @brief Driver state.
*/
pwmstate_t state;
/**
* @brief Current configuration data.
*/
const PWMConfig *config;
/**
* @brief Current PWM period in ticks.
*/
pwmcnt_t period;
#if defined(PWM_DRIVER_EXT_FIELDS)
PWM_DRIVER_EXT_FIELDS
#endif
/* End of the mandatory fields.*/
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AVR_PWM_USE_TIM1 || defined(__DOXYGEN__)
extern PWMDriver PWMD1;
#endif
#if AVR_PWM_USE_TIM2 || defined(__DOXYGEN__)
extern PWMDriver PWMD2;
#endif
#if AVR_PWM_USE_TIM3 || defined(__DOXYGEN__)
extern PWMDriver PWMD3;
#endif
#if AVR_PWM_USE_TIM4 || defined(__DOXYGEN__)
extern PWMDriver PWMD4;
#endif
#if AVR_PWM_USE_TIM5 || defined(__DOXYGEN__)
extern PWMDriver PWMD5;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void pwm_lld_init(void);
void pwm_lld_start(PWMDriver *pwmp);
void pwm_lld_stop(PWMDriver *pwmp);
void pwm_lld_change_period(PWMDriver *pwmp, pwmcnt_t period);
void pwm_lld_enable_channel(PWMDriver *pwmp,
pwmchannel_t channel,
pwmcnt_t width);
void pwm_lld_disable_channel(PWMDriver *pwmp, pwmchannel_t channel);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_PWM */
#endif /* _PWM_LLD_H_ */
/** @} */

View File

@ -0,0 +1,378 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/serial_lld.c
* @brief AVR low level serial driver code.
*
* @addtogroup SERIAL
* @{
*/
#include "hal.h"
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief USART0 serial driver identifier.
* @note The name does not follow the convention used in the other ports
* (COMn) because a name conflict with the AVR headers.
*/
#if AVR_SERIAL_USE_USART0 || defined(__DOXYGEN__)
SerialDriver SD1;
/* USARTs are not consistently named across the AVR range */
#ifdef USART0_RX_vect
#define AVR_SD1_RX_VECT USART0_RX_vect
#define AVR_SD1_TX_VECT USART0_UDRE_vect
#elif defined(USART_RX_vect)
#define AVR_SD1_RX_VECT USART_RX_vect
#define AVR_SD1_TX_VECT USART_UDRE_vect
#else
#error "Cannot find USART to use for SD1"
#endif
#endif /* AVR_SERIAL_USE_USART0 */
/**
* @brief USART1 serial driver identifier.
* @note The name does not follow the convention used in the other ports
* (COMn) because a name conflict with the AVR headers.
*/
#if AVR_SERIAL_USE_USART1 || defined(__DOXYGEN__)
SerialDriver SD2;
/* Check if USART1 exists for this MCU */
#ifdef USART1_RX_vect
#define AVR_SD2_RX_VECT USART1_RX_vect
#define AVR_SD2_TX_VECT USART1_UDRE_vect
#else
#error "Cannot find USART to use for SD2"
#endif
#endif /* AVR_SERIAL_USE_USART1 */
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/**
* @brief Driver default configuration.
*/
static const SerialConfig default_config = {
UBRR(SERIAL_DEFAULT_BITRATE),
USART_CHAR_SIZE_8
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static void set_error(uint8_t sra, SerialDriver *sdp) {
eventflags_t sts = 0;
uint8_t dor = 0;
uint8_t upe = 0;
uint8_t fe = 0;
#if AVR_SERIAL_USE_USART0
if (&SD1 == sdp) {
dor = (1 << DOR0);
upe = (1 << UPE0);
fe = (1 << FE0);
}
#endif
#if AVR_SERIAL_USE_USART1
if (&SD2 == sdp) {
dor = (1 << DOR1);
upe = (1 << UPE1);
fe = (1 << FE1);
}
#endif
if (sra & dor)
sts |= SD_OVERRUN_ERROR;
if (sra & upe)
sts |= SD_PARITY_ERROR;
if (sra & fe)
sts |= SD_FRAMING_ERROR;
osalSysLockFromISR();
chnAddFlagsI(sdp, sts);
osalSysUnlockFromISR();
}
#if AVR_SERIAL_USE_USART0 || defined(__DOXYGEN__)
static void notify1(GenericQueue *qp) {
(void)qp;
UCSR0B |= (1 << UDRIE0);
}
/**
* @brief USART0 initialization.
*
* @param[in] config the architecture-dependent serial driver configuration
*/
static void usart0_init(const SerialConfig *config) {
UBRR0L = config->sc_brr;
UBRR0H = config->sc_brr >> 8;
UCSR0A = 0;
UCSR0B = (1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0);
switch (config->sc_bits_per_char) {
case USART_CHAR_SIZE_5:
UCSR0C = 0;
break;
case USART_CHAR_SIZE_6:
UCSR0C = (1 << UCSZ00);
break;
case USART_CHAR_SIZE_7:
UCSR0C = (1 << UCSZ01);
break;
case USART_CHAR_SIZE_9:
UCSR0B |= (1 << UCSZ02);
UCSR0C = (1 << UCSZ00) | (1 << UCSZ01);
break;
case USART_CHAR_SIZE_8:
default:
UCSR0C = (1 << UCSZ00) | (1 << UCSZ01);
}
}
/**
* @brief USART0 de-initialization.
*/
static void usart0_deinit(void) {
UCSR0A = 0;
UCSR0B = 0;
UCSR0C = 0;
}
#endif
#if AVR_SERIAL_USE_USART1 || defined(__DOXYGEN__)
static void notify2(GenericQueue *qp) {
(void)qp;
UCSR1B |= (1 << UDRIE1);
}
/**
* @brief USART1 initialization.
*
* @param[in] config the architecture-dependent serial driver configuration
*/
static void usart1_init(const SerialConfig *config) {
UBRR1L = config->sc_brr;
UBRR1H = config->sc_brr >> 8;
UCSR1A = 0;
UCSR1B = (1 << RXEN1) | (1 << TXEN1) | (1 << RXCIE1);
switch (config->sc_bits_per_char) {
case USART_CHAR_SIZE_5:
UCSR1C = 0;
break;
case USART_CHAR_SIZE_6:
UCSR1C = (1 << UCSZ10);
break;
case USART_CHAR_SIZE_7:
UCSR1C = (1 << UCSZ11);
break;
case USART_CHAR_SIZE_9:
UCSR1B |= (1 << UCSZ12);
UCSR1C = (1 << UCSZ10) | (1 << UCSZ11);
break;
case USART_CHAR_SIZE_8:
default:
UCSR1C = (1 << UCSZ10) | (1 << UCSZ11);
}
}
/**
* @brief USART1 de-initialization.
*/
static void usart1_deinit(void) {
UCSR1A = 0;
UCSR1B = 0;
UCSR1C = 0;
}
#endif
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if AVR_SERIAL_USE_USART0 || defined(__DOXYGEN__)
/**
* @brief USART0 RX interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(AVR_SD1_RX_VECT) {
uint8_t sra;
OSAL_IRQ_PROLOGUE();
sra = UCSR0A;
if (sra & ((1 << DOR0) | (1 << UPE0) | (1 << FE0)))
set_error(sra, &SD1);
osalSysLockFromISR();
sdIncomingDataI(&SD1, UDR0);
osalSysUnlockFromISR();
OSAL_IRQ_EPILOGUE();
}
/**
* @brief USART0 TX interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(AVR_SD1_TX_VECT) {
msg_t b;
OSAL_IRQ_PROLOGUE();
osalSysLockFromISR();
b = sdRequestDataI(&SD1);
osalSysUnlockFromISR();
if (b < Q_OK)
UCSR0B &= ~(1 << UDRIE0);
else
UDR0 = b;
OSAL_IRQ_EPILOGUE();
}
#endif /* AVR_SERIAL_USE_USART0 */
#if AVR_SERIAL_USE_USART1 || defined(__DOXYGEN__)
/**
* @brief USART1 RX interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(AVR_SD2_RX_VECT) {
uint8_t sra;
OSAL_IRQ_PROLOGUE();
sra = UCSR1A;
if (sra & ((1 << DOR1) | (1 << UPE1) | (1 << FE1)))
set_error(sra, &SD2);
osalSysLockFromISR();
sdIncomingDataI(&SD2, UDR1);
osalSysUnlockFromISR();
OSAL_IRQ_EPILOGUE();
}
/**
* @brief USART1 TX interrupt handler.
*
* @isr
*/
OSAL_IRQ_HANDLER(AVR_SD2_TX_VECT) {
msg_t b;
OSAL_IRQ_PROLOGUE();
osalSysLockFromISR();
b = sdRequestDataI(&SD2);
osalSysUnlockFromISR();
if (b < Q_OK)
UCSR1B &= ~(1 << UDRIE1);
else
UDR1 = b;
OSAL_IRQ_EPILOGUE();
}
#endif /* AVR_SERIAL_USE_USART1 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level serial driver initialization.
*
* @notapi
*/
void sd_lld_init(void) {
#if AVR_SERIAL_USE_USART0
sdObjectInit(&SD1, NULL, notify1);
#endif
#if AVR_SERIAL_USE_USART1
sdObjectInit(&SD2, NULL, notify2);
#endif
}
/**
* @brief Low level serial driver configuration and (re)start.
*
* @param[in] sdp pointer to a @p SerialDriver object
* @param[in] config the architecture-dependent serial driver configuration.
* If this parameter is set to @p NULL then a default
* configuration is used.
*
* @notapi
*/
void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) {
if (config == NULL)
config = &default_config;
#if AVR_SERIAL_USE_USART0
if (&SD1 == sdp) {
usart0_init(config);
return;
}
#endif
#if AVR_SERIAL_USE_USART1
if (&SD2 == sdp) {
usart1_init(config);
return;
}
#endif
}
/**
* @brief Low level serial driver stop.
* @details De-initializes the USART, stops the associated clock, resets the
* interrupt vector.
*
* @param[in] sdp pointer to a @p SerialDriver object
*
* @notapi
*/
void sd_lld_stop(SerialDriver *sdp) {
#if AVR_SERIAL_USE_USART0
if (&SD1 == sdp)
usart0_deinit();
#endif
#if AVR_SERIAL_USE_USART1
if (&SD2 == sdp)
usart1_deinit();
#endif
}
#endif /* HAL_USE_SERIAL */
/** @} */

View File

@ -0,0 +1,158 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/serial_lld.h
* @brief AVR low level serial driver header.
*
* @addtogroup SERIAL
* @{
*/
#ifndef _SERIAL_LLD_H_
#define _SERIAL_LLD_H_
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief USART0 driver enable switch.
* @details If set to @p TRUE the support for USART0 is included.
* @note The default is @p FALSE.
*/
#if !defined(AVR_SERIAL_USE_USART0) || defined(__DOXYGEN__)
#define AVR_SERIAL_USE_USART0 TRUE
#endif
/**
* @brief USART1 driver enable switch.
* @details If set to @p TRUE the support for USART1 is included.
* @note The default is @p TRUE.
*/
#if !defined(AVR_SERIAL_USE_USART1) || defined(__DOXYGEN__)
#define AVR_SERIAL_USE_USART1 TRUE
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief AVR Serial Driver configuration structure.
* @details An instance of this structure must be passed to @p sdStart()
* in order to configure and start a serial driver operations.
*/
typedef struct {
/**
* @brief Initialization value for the BRR register.
*/
uint16_t sc_brr;
/**
* @brief Number of bits per character (USART_CHAR_SIZE_5 to USART_CHAR_SIZE_9).
*/
uint8_t sc_bits_per_char;
} SerialConfig;
/**
* @brief @p SerialDriver specific data.
*/
#define _serial_driver_data \
_base_asynchronous_channel_data \
/* Driver state.*/ \
sdstate_t state; \
/* Input queue.*/ \
InputQueue iqueue; \
/* Output queue.*/ \
OutputQueue oqueue; \
/* Input circular buffer.*/ \
uint8_t ib[SERIAL_BUFFERS_SIZE]; \
/* Output circular buffer.*/ \
uint8_t ob[SERIAL_BUFFERS_SIZE]; \
/* End of the mandatory fields.*/
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @brief Macro for baud rate computation.
* @note Make sure the final baud rate is within tolerance.
*/
#define UBRR(b) (((F_CPU / b) >> 4) - 1)
/**
* @brief Macro for baud rate computation when U2Xn == 1.
* @note Make sure the final baud rate is within tolerance.
*/
#define UBRR2x(b) (((F_CPU / b) >> 3) - 1)
/**
* @brief Macro for baud rate computation.
* @note Make sure the final baud rate is within tolerance.
* @note This version uses floating point math for greater accuracy.
*/
#define UBRR_F(b) ((((double) F_CPU / (double) b) / 16.0) - 0.5)
/**
* @brief Macro for baud rate computation when U2Xn == 1.
* @note Make sure the final baud rate is within tolerance.
* @note This version uses floating point math for greater accuracy.
*/
#define UBRR2x_F(b) ((((double) F_CPU / (double) b) / 8.0) - 0.5)
#define USART_CHAR_SIZE_5 0
#define USART_CHAR_SIZE_6 1
#define USART_CHAR_SIZE_7 2
#define USART_CHAR_SIZE_8 3
#define USART_CHAR_SIZE_9 4
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AVR_SERIAL_USE_USART0 && !defined(__DOXYGEN__)
extern SerialDriver SD1;
#endif
#if AVR_SERIAL_USE_USART1 && !defined(__DOXYGEN__)
extern SerialDriver SD2;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void sd_lld_init(void);
void sd_lld_start(SerialDriver *sdp, const SerialConfig *config);
void sd_lld_stop(SerialDriver *sdp);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_SERIAL */
#endif /* _SERIAL_LLD_H_ */
/** @} */

429
os/hal/ports/AVR/spi_lld.c Normal file
View File

@ -0,0 +1,429 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/spi_lld.c
* @brief AVR SPI subsystem low level driver source.
*
* @addtogroup SPI
* @{
*/
#include "hal.h"
#if HAL_USE_SPI || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief SPI1 driver identifier.
*/
#if AVR_SPI_USE_SPI1 || defined(__DOXYGEN__)
SPIDriver SPID1;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if AVR_SPI_USE_SPI1 || defined(__DOXYGEN__)
/**
* @brief SPI event interrupt handler.
*
* @notapi
*/
OSAL_IRQ_HANDLER(SPI_STC_vect) {
OSAL_IRQ_PROLOGUE();
SPIDriver *spip = &SPID1;
/* spi_lld_exchange or spi_lld_receive */
if (spip->rxbuf && spip->rxidx < spip->rxbytes) {
spip->rxbuf[spip->rxidx++] = SPDR; // receive
}
/* spi_lld_exchange, spi_lld_send or spi_lld_ignore */
if (spip->txidx < spip->txbytes) {
if (spip->txbuf) {
SPDR = spip->txbuf[spip->txidx++]; // send
} else {
SPDR = 0; spip->txidx++; // dummy send
}
}
/* spi_lld_send */
else if (spip->rxidx < spip->rxbytes) { /* rx not done */
SPDR = 0; // dummy send to keep the clock going
}
/* rx done and tx done */
if (spip->rxidx >= spip->rxbytes && spip->txidx >= spip->txbytes) {
_spi_isr_code(spip);
}
OSAL_IRQ_EPILOGUE();
}
#endif /* AVR_SPI_USE_SPI1 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level SPI driver initialization.
*
* @notapi
*/
void spi_lld_init(void) {
#if AVR_SPI_USE_SPI1
/* Driver initialization.*/
spiObjectInit(&SPID1);
#endif /* AVR_SPI_USE_SPI1 */
}
/**
* @brief Configures and activates the SPI peripheral.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @notapi
*/
void spi_lld_start(SPIDriver *spip) {
uint8_t dummy;
/* Configures the peripheral.*/
if (spip->state == SPI_STOP) {
/* Enables the peripheral.*/
#if AVR_SPI_USE_SPI1
if (&SPID1 == spip) {
/* Enable SPI clock using Power Reduction Register */
#if defined(PRR0)
PRR0 &= ~(1 << PRSPI);
#elif defined(PRR)
PRR &= ~(1 << PRSPI);
#endif
/* SPI enable, SPI interrupt enable */
SPCR |= ((1 << SPE) | (1 << SPIE));
switch (spip->config->role) {
case SPI_ROLE_SLAVE:
SPCR &= ~(1 << MSTR); /* master mode */
DDR_SPI1 |= (1 << SPI1_MISO); /* output */
DDR_SPI1 &= ~((1 << SPI1_MOSI) | (1 << SPI1_SCK) | (1 << SPI1_SS)); /* input */
break;
case SPI_ROLE_MASTER: /* fallthrough */
default:
SPCR |= (1 << MSTR); /* slave mode */
DDR_SPI1 |= ((1 << SPI1_MOSI) | (1 << SPI1_SCK)); /* output */
DDR_SPI1 &= ~(1 << SPI1_MISO); /* input */
spip->config->ssport->dir |= (1 << spip->config->sspad);
}
switch (spip->config->bitorder) {
case SPI_LSB_FIRST:
SPCR |= (1 << DORD);
break;
case SPI_MSB_FIRST: /* fallthrough */
default:
SPCR &= ~(1 << DORD);
break;
}
SPCR &= ~((1 << CPOL) | (1 << CPHA));
switch (spip->config->mode) {
case SPI_MODE_1:
SPCR |= (1 << CPHA);
break;
case SPI_MODE_2:
SPCR |= (1 << CPOL);
break;
case SPI_MODE_3:
SPCR |= ((1 << CPOL) | (1 << CPHA));
break;
case SPI_MODE_0: /* fallthrough */
default: break;
}
SPCR &= ~((1 << SPR1) | (1 << SPR0));
SPSR &= ~(1 << SPI2X);
switch (spip->config->clockrate) {
case SPI_SCK_FOSC_2:
SPSR |= (1 << SPI2X);
break;
case SPI_SCK_FOSC_8:
SPSR |= (1 << SPI2X);
SPCR |= (1 << SPR0);
break;
case SPI_SCK_FOSC_16:
SPCR |= (1 << SPR0);
break;
case SPI_SCK_FOSC_32:
SPSR |= (1 << SPI2X);
SPCR |= (1 << SPR1);
break;
case SPI_SCK_FOSC_64:
SPCR |= (1 << SPR1);
break;
case SPI_SCK_FOSC_128:
SPCR |= ((1 << SPR1) | (1 << SPR0));
break;
case SPI_SCK_FOSC_4: /* fallthrough */
default: break;
}
/* dummy reads before enabling interrupt */
dummy = SPSR;
dummy = SPDR;
(void) dummy; /* suppress warning about unused variable */
SPCR |= (1 << SPIE);
}
#endif /* AVR_SPI_USE_SPI1 */
}
}
/**
* @brief Deactivates the SPI peripheral.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @notapi
*/
void spi_lld_stop(SPIDriver *spip) {
if (spip->state == SPI_READY) {
/* Resets the peripheral.*/
/* Disables the peripheral.*/
#if AVR_SPI_USE_SPI1
if (&SPID1 == spip) {
SPCR &= ((1 << SPIE) | (1 << SPE));
spip->config->ssport->dir &= ~(1 << spip->config->sspad);
}
/* Disable SPI clock using Power Reduction Register */
#if defined(PRR0)
PRR0 |= (1 << PRSPI);
#elif defined(PRR)
PRR |= (1 << PRSPI);
#endif
#endif /* AVR_SPI_USE_SPI1 */
}
}
/**
* @brief Asserts the slave select signal and prepares for transfers.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @notapi
*/
void spi_lld_select(SPIDriver *spip) {
/**
* NOTE: This should only be called in master mode
*/
spip->config->ssport->out &= ~(1 << spip->config->sspad);
}
/**
* @brief Deasserts the slave select signal.
* @details The previously selected peripheral is unselected.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @notapi
*/
void spi_lld_unselect(SPIDriver *spip) {
/**
* NOTE: This should only be called in master mode
*/
spip->config->ssport->out |= (1 << spip->config->sspad);
}
/**
* @brief Ignores data on the SPI bus.
* @details This asynchronous function starts the transmission of a series of
* idle words on the SPI bus and ignores the received data.
* @post At the end of the operation the configured callback is invoked.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to be ignored
*
* @notapi
*/
void spi_lld_ignore(SPIDriver *spip, size_t n) {
spip->rxbuf = spip->txbuf = NULL;
spip->txbytes = n;
spip->txidx = 0;
SPDR = 0;
}
/**
* @brief Exchanges data on the SPI bus.
* @details This asynchronous function starts a simultaneous transmit/receive
* operation.
* @post At the end of the operation the configured callback is invoked.
* @note The buffers are organized as uint8_t arrays for data sizes below or
* equal to 8 bits else it is organized as uint16_t arrays.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to be exchanged
* @param[in] txbuf the pointer to the transmit buffer
* @param[out] rxbuf the pointer to the receive buffer
*
* @notapi
*/
void spi_lld_exchange(SPIDriver *spip, size_t n,
const void *txbuf, void *rxbuf) {
spip->rxbuf = rxbuf;
spip->txbuf = txbuf;
spip->txbytes = spip->rxbytes = n;
spip->txidx = spip->rxidx = 0;
SPDR = spip->txbuf[spip->txidx++];
}
/**
* @brief Sends data over the SPI bus.
* @details This asynchronous function starts a transmit operation.
* @post At the end of the operation the configured callback is invoked.
* @note The buffers are organized as uint8_t arrays for data sizes below or
* equal to 8 bits else it is organized as uint16_t arrays.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to send
* @param[in] txbuf the pointer to the transmit buffer
*
* @notapi
*/
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
spip->rxbuf = NULL;
spip->txbuf = txbuf;
spip->txbytes = n;
spip->txidx = 0;
SPDR = spip->txbuf[spip->txidx++];
}
/**
* @brief Receives data from the SPI bus.
* @details This asynchronous function starts a receive operation.
* @post At the end of the operation the configured callback is invoked.
* @note The buffers are organized as uint8_t arrays for data sizes below or
* equal to 8 bits else it is organized as uint16_t arrays.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] n number of words to receive
* @param[out] rxbuf the pointer to the receive buffer
*
* @notapi
*/
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
spip->txbuf = NULL;
spip->rxbuf = rxbuf;
spip->rxbytes = n;
spip->rxidx = 0;
/* Write dummy byte to start communication */
SPDR = 0;
}
/**
* @brief Exchanges one frame using a polled wait.
* @details This synchronous function exchanges one frame using a polled
* synchronization method. This function is useful when exchanging
* small amount of data on high speed channels, usually in this
* situation is much more efficient just wait for completion using
* polling than suspending the thread waiting for an interrupt.
*
* @param[in] spip pointer to the @p SPIDriver object
* @param[in] frame the data frame to send over the SPI bus
* @return The received data frame from the SPI bus.
*/
#if AVR_SPI_USE_16BIT_POLLED_EXCHANGE
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
uint16_t spdr = 0;
uint8_t dummy;
/* disable interrupt */
SPCR &= ~(1 << SPIE);
SPDR = frame >> 8;
while (!(SPSR & (1 << SPIF))) ;
spdr = SPDR << 8;
SPDR = frame & 0xFF;
while (!(SPSR & (1 << SPIF))) ;
spdr |= SPDR;
dummy = SPSR;
dummy = SPDR;
(void) dummy; /* suppress warning about unused variable */
SPCR |= (1 << SPIE);
return spdr;
}
#else /* AVR_SPI_USE_16BIT_POLLED_EXCHANGE */
uint8_t spi_lld_polled_exchange(SPIDriver *spip, uint8_t frame) {
uint8_t spdr = 0;
uint8_t dummy;
/* disable interrupt */
SPCR &= ~(1 << SPIE);
SPDR = frame;
while (!(SPSR & (1 << SPIF))) ;
spdr = SPDR;
dummy = SPSR;
dummy = SPDR;
(void) dummy; /* suppress warning about unused variable */
SPCR |= (1 << SPIE);
return spdr;
}
#endif /* AVR_SPI_USE_16BIT_POLLED_EXCHANGE */
#endif /* HAL_USE_SPI */
/** @} */

237
os/hal/ports/AVR/spi_lld.h Normal file
View File

@ -0,0 +1,237 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/spi_lld.h
* @brief AVR SPI subsystem low level driver header.
*
* @addtogroup SPI
* @{
*/
#ifndef _SPI_LLD_H_
#define _SPI_LLD_H_
#if HAL_USE_SPI || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/** @brief SPI Mode (Polarity/Phase) */
#define SPI_ROLE_MASTER 0
#define SPI_ROLE_SLAVE 1
/** @brief SPI Mode (Polarity/Phase) */
#define SPI_CPOL0_CPHA0 0
#define SPI_CPOL0_CPHA1 1
#define SPI_CPOL1_CPHA0 2
#define SPI_CPOL1_CPHA1 3
#define SPI_MODE_0 SPI_CPOL0_CPHA0
#define SPI_MODE_1 SPI_CPOL0_CPHA1
#define SPI_MODE_2 SPI_CPOL1_CPHA0
#define SPI_MODE_3 SPI_CPOL1_CPHA1
/** @brief Bit order */
#define SPI_LSB_FIRST 0
#define SPI_MSB_FIRST 1
/** @brief SPI clock rate FOSC/x */
#define SPI_SCK_FOSC_2 0
#define SPI_SCK_FOSC_4 1
#define SPI_SCK_FOSC_8 2
#define SPI_SCK_FOSC_16 3
#define SPI_SCK_FOSC_32 4
#define SPI_SCK_FOSC_64 5
#define SPI_SCK_FOSC_128 6
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief SPI driver enable switch.
* @details If set to @p TRUE the support for SPI1 is included.
*/
#if !defined(AVR_SPI_USE_SPI1) || defined(__DOXYGEN__)
#define AVR_SPI_USE_SPI1 FALSE
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Type of a structure representing an SPI driver.
*/
typedef struct SPIDriver SPIDriver;
/**
* @brief SPI notification callback type.
*
* @param[in] spip pointer to the @p SPIDriver object triggering the
* callback
*/
typedef void (*spicallback_t)(SPIDriver *spip);
/**
* @brief Driver configuration structure.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
typedef struct {
/**
* @brief Role: Master or Slave
*/
uint8_t role;
/**
* @brief Port used of Slave Select
*/
ioportid_t ssport;
/**
* @brief Pad used of Slave Select
*/
uint8_t sspad;
/**
* @brief Polarity/Phase mode
*/
uint8_t mode;
/**
* @brief Use MSB/LSB first?
*/
uint8_t bitorder;
/**
* @brief Clock rate of the subsystem
*/
uint8_t clockrate;
/**
* @brief Operation complete callback.
*/
spicallback_t end_cb;
/* End of the mandatory fields.*/
} SPIConfig;
/**
* @brief Structure representing an SPI driver.
* @note Implementations may extend this structure to contain more,
* architecture dependent, fields.
*/
struct SPIDriver {
/**
* @brief Driver state.
*/
spistate_t state;
/**
* @brief Current configuration data.
*/
SPIConfig *config;
#if SPI_USE_WAIT || defined(__DOXYGEN__)
/**
* @brief Waiting thread.
*/
Thread *thread;
#endif /* SPI_USE_WAIT */
#if SPI_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
#if CH_USE_MUTEXES || defined(__DOXYGEN__)
/**
* @brief Mutex protecting the bus.
*/
Mutex mutex;
#elif CH_USE_SEMAPHORES
Semaphore semaphore;
#endif
#endif /* SPI_USE_MUTUAL_EXCLUSION */
#if defined(SPI_DRIVER_EXT_FIELDS)
SPI_DRIVER_EXT_FIELDS
#endif
/**
* @brief Pointer to the buffer with data to send.
*/
uint8_t *txbuf;
/**
* @brief Number of bytes of data to send.
*/
size_t txbytes;
/**
* @brief Current index in buffer when sending data.
*/
size_t txidx;
/**
* @brief Pointer to the buffer to put received data.
*/
uint8_t *rxbuf;
/**
* @brief Number of bytes of data to receive.
*/
size_t rxbytes;
/**
* @brief Current index in buffer when receiving data.
*/
size_t rxidx;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AVR_SPI_USE_SPI1 && !defined(__DOXYGEN__)
extern SPIDriver SPID1;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void spi_lld_init(void);
void spi_lld_start(SPIDriver *spip);
void spi_lld_stop(SPIDriver *spip);
void spi_lld_select(SPIDriver *spip);
void spi_lld_unselect(SPIDriver *spip);
void spi_lld_ignore(SPIDriver *spip, size_t n);
void spi_lld_exchange(SPIDriver *spip, size_t n,
const void *txbuf, void *rxbuf);
void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf);
void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf);
#if AVR_SPI_USE_16BIT_POLLED_EXCHANGE
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame);
#else
uint8_t spi_lld_polled_exchange(SPIDriver *spip, uint8_t frame);
#endif
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_SPI */
#endif /* _SPI_LLD_H_ */
/** @} */

67
os/hal/ports/AVR/st_lld.c Normal file
View File

@ -0,0 +1,67 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/st_lld.c
* @brief ST Driver subsystem low level driver code.
*
* @addtogroup ST
* @{
*/
#include "hal.h"
#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level ST driver initialization.
*
* @notapi
*/
void st_lld_init(void) {
}
#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */
/** @} */

141
os/hal/ports/AVR/st_lld.h Normal file
View File

@ -0,0 +1,141 @@
/*
ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file AVR/st_lld.h
* @brief ST Driver subsystem low level driver header.
* @details This header is designed to be include-able without having to
* include other files from the HAL.
*
* @addtogroup AVR
* @{
*/
#ifndef _ST_LLD_H_
#define _ST_LLD_H_
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#ifdef __cplusplus
extern "C" {
#endif
void st_lld_init(void);
#ifdef __cplusplus
}
#endif
/*===========================================================================*/
/* Driver inline functions. */
/*===========================================================================*/
/**
* @brief Returns the time counter value.
*
* @return The counter value.
*
* @notapi
*/
static inline systime_t st_lld_get_counter(void) {
return (systime_t)0;
}
/**
* @brief Starts the alarm.
* @note Makes sure that no spurious alarms are triggered after
* this call.
*
* @param[in] time the time to be set for the first alarm
*
* @notapi
*/
static inline void st_lld_start_alarm(systime_t time) {
(void)time;
}
/**
* @brief Stops the alarm interrupt.
*
* @notapi
*/
static inline void st_lld_stop_alarm(void) {
}
/**
* @brief Sets the alarm time.
*
* @param[in] time the time to be set for the next alarm
*
* @notapi
*/
static inline void st_lld_set_alarm(systime_t time) {
(void)time;
}
/**
* @brief Returns the current alarm time.
*
* @return The currently set alarm time.
*
* @notapi
*/
static inline systime_t st_lld_get_alarm(void) {
return (systime_t)0;
}
/**
* @brief Determines if the alarm is active.
*
* @return The alarm status.
* @retval false if the alarm is not active.
* @retval true is the alarm is active
*
* @notapi
*/
static inline bool st_lld_is_alarm_active(void) {
return false;
}
#endif /* _ST_LLD_H_ */
/** @} */