ChibiOS 2.6.8, until I can figure out where to get it from git.

This commit is contained in:
Jared Boone
2015-07-08 08:40:23 -07:00
parent dc6fee8370
commit e1eea8e08a
1929 changed files with 575326 additions and 0 deletions

View File

@@ -0,0 +1,387 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 file has been contributed by:
Andrew Hannam aka inmarket.
*/
/**
* @file AT91SAM7/adc_lld.c
* @brief AT91SAM7 ADC subsystem low level driver source.
*
* @addtogroup ADC
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_ADC || defined(__DOXYGEN__)
/**
* @brief ADC1 Prescaler
* @detail Prescale = RoundUp(MCK / 2 / ADCClock - 1)
*/
#if ((((MCK/2)+(AT91_ADC1_CLOCK-1))/AT91_ADC1_CLOCK)-1) > 255
#define AT91_ADC1_PRESCALE 255
#else
#define AT91_ADC1_PRESCALE ((((MCK/2)+(AT91_ADC1_CLOCK-1))/AT91_ADC1_CLOCK)-1)
#endif
/**
* @brief ADC1 Startup Time
* @details Startup = RoundUp(ADCClock / 400,000 - 1)
* @note Corresponds to a startup delay > 20uS (as required from the datasheet)
*/
#if (((AT91_ADC1_CLOCK+399999)/400000)-1) > 127
#define AT91_ADC1_STARTUP 127
#else
#define AT91_ADC1_STARTUP (((AT91_ADC1_CLOCK+399999)/400000)-1)
#endif
#if AT91_ADC1_RESOLUTION == 8
#define AT91_ADC1_MAINMODE (((AT91_ADC1_SHTM & 0x0F) << 24) | ((AT91_ADC1_STARTUP & 0x7F) << 16) | ((AT91_ADC1_PRESCALE & 0xFF) << 8) | AT91C_ADC_LOWRES_8_BIT)
#else
#define AT91_ADC1_MAINMODE (((AT91_ADC1_SHTM & 0x0F) << 24) | ((AT91_ADC1_STARTUP & 0x7F) << 16) | ((AT91_ADC1_PRESCALE & 0xFF) << 8) | AT91C_ADC_LOWRES_10_BIT)
#endif
#if AT91_ADC1_TIMER < 0 || AT91_ADC1_TIMER > 2
#error "Unknown Timer specified for ADC1"
#endif
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
#if !ADC_USE_ADC1
#error "You must specify ADC_USE_ADC1 if you have specified HAL_USE_ADC"
#endif
/** @brief ADC1 driver identifier.*/
ADCDriver ADCD1;
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
#define ADCReg1 ((AT91S_ADC *)AT91C_ADC_CR)
#if AT91_ADC1_MAINMODE == 2
#define ADCTimer1 ((AT91S_TC *)AT91C_TC2_CCR)
#define AT91_ADC1_TIMERMODE AT91C_ADC_TRGSEL_TIOA2
#define AT91_ADC1_TIMERID AT91C_ID_TC2
#elif AT91_ADC1_MAINMODE == 1
#define ADCTimer1 ((AT91S_TC *)AT91C_TC1_CCR)
#define AT91_ADC1_TIMERMODE AT91C_ADC_TRGSEL_TIOA1
#define AT91_ADC1_TIMERID AT91C_ID_TC1
#else
#define ADCTimer1 ((AT91S_TC *)AT91C_TC0_CCR)
#define AT91_ADC1_TIMERMODE AT91C_ADC_TRGSEL_TIOA0
#define AT91_ADC1_TIMERID AT91C_ID_TC0
#endif
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
#define adc_sleep() ADCReg1->ADC_MR = (AT91_ADC1_MAINMODE | AT91C_ADC_SLEEP_MODE | AT91C_ADC_TRGEN_DIS)
#define adc_wake() ADCReg1->ADC_MR = (AT91_ADC1_MAINMODE | AT91C_ADC_SLEEP_NORMAL_MODE | AT91C_ADC_TRGEN_DIS)
#define adc_disable() { \
ADCReg1->ADC_IDR = 0xFFFFFFFF; \
ADCReg1->ADC_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS; \
adc_wake(); \
ADCReg1->ADC_CHDR = 0xFF; \
}
#define adc_clrint() { \
uint32_t isr, dummy; \
\
isr = ADCReg1->ADC_SR; \
if ((isr & AT91C_ADC_DRDY)) dummy = ADCReg1->ADC_LCDR; \
if ((isr & AT91C_ADC_EOC0)) dummy = ADCReg1->ADC_CDR0; \
if ((isr & AT91C_ADC_EOC1)) dummy = ADCReg1->ADC_CDR1; \
if ((isr & AT91C_ADC_EOC2)) dummy = ADCReg1->ADC_CDR2; \
if ((isr & AT91C_ADC_EOC3)) dummy = ADCReg1->ADC_CDR3; \
if ((isr & AT91C_ADC_EOC4)) dummy = ADCReg1->ADC_CDR4; \
if ((isr & AT91C_ADC_EOC5)) dummy = ADCReg1->ADC_CDR5; \
if ((isr & AT91C_ADC_EOC6)) dummy = ADCReg1->ADC_CDR6; \
if ((isr & AT91C_ADC_EOC7)) dummy = ADCReg1->ADC_CDR7; \
(void) dummy; \
}
#define adc_stop() { \
adc_disable(); \
adc_clrint(); \
}
/**
* We must keep stack usage to a minimum - the default AT91SAM7 isr stack size is very small.
* We sacrifice some speed and code size in order to achieve this by accessing the structure
* and registers directly rather than through the passed in pointers. This works because the
* AT91SAM7 supports only a single ADC device (although with 8 channels).
*/
static void handleint(void) {
uint32_t isr;
isr = ADCReg1->ADC_SR;
if (ADCD1.grpp) {
/* ADC overflow condition, this could happen only if the DMA is unable to read data fast enough.*/
if ((isr & AT91C_ADC_GOVRE)) {
_adc_isr_error_code(&ADCD1, ADC_ERR_OVERFLOW);
/* Transfer complete processing.*/
} else if ((isr & AT91C_ADC_RXBUFF)) {
if (ADCD1.grpp->circular) {
/* setup the DMA again */
ADCReg1->ADC_RPR = (uint32_t)ADCD1.samples;
if (ADCD1.depth <= 1) {
ADCReg1->ADC_RCR = ADCD1.grpp->num_channels;
ADCReg1->ADC_RNPR = 0;
ADCReg1->ADC_RNCR = 0;
} else {
ADCReg1->ADC_RCR = ADCD1.depth/2 * ADCD1.grpp->num_channels;
ADCReg1->ADC_RNPR = (uint32_t)(ADCD1.samples + (ADCD1.depth/2 * ADCD1.grpp->num_channels));
ADCReg1->ADC_RNCR = (ADCD1.depth - ADCD1.depth/2) * ADCD1.grpp->num_channels;
}
ADCReg1->ADC_PTCR = AT91C_PDC_RXTEN; // DMA enabled
}
_adc_isr_full_code(&ADCD1);
/* Half transfer processing.*/
} else if ((isr & AT91C_ADC_ENDRX)) {
// Make sure we get a full complete next time.
ADCReg1->ADC_RNPR = 0;
ADCReg1->ADC_RNCR = 0;
_adc_isr_half_code(&ADCD1);
}
} else {
/* Spurious interrupt - Make sure it doesn't happen again */
adc_disable();
}
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/**
* @brief ADC interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(ADC_IRQHandler) {
CH_IRQ_PROLOGUE();
handleint();
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level ADC driver initialization.
*
* @notapi
*/
void adc_lld_init(void) {
/* Turn on ADC in the power management controller */
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_ADC);
/* Driver object initialization.*/
adcObjectInit(&ADCD1);
ADCReg1->ADC_CR = 0; // 0 or AT91C_ADC_SWRST if you want to do a ADC reset
adc_stop();
adc_sleep();
/* Setup interrupt handler */
AIC_ConfigureIT(AT91C_ID_ADC,
AT91C_AIC_SRCTYPE_HIGH_LEVEL | AT91_ADC_IRQ_PRIORITY,
ADC_IRQHandler);
AIC_EnableIT(AT91C_ID_ADC);
}
/**
* @brief Configures and activates the ADC peripheral.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @notapi
*/
void adc_lld_start(ADCDriver *adcp) {
/* If in stopped state then wake up the ADC */
if (adcp->state == ADC_STOP) {
/* Take it out of sleep mode */
/* We could stay in sleep mode provided total conversion rate < 44kHz but we can't guarantee that here */
adc_wake();
/* TODO: We really should perform a conversion here just to ensure that we are out of sleep mode */
}
}
/**
* @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) {
adc_stop();
adc_sleep();
}
}
/**
* @brief Starts an ADC conversion.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @notapi
*/
void adc_lld_start_conversion(ADCDriver *adcp) {
uint32_t i;
(void) adcp;
/* Make sure everything is stopped first */
adc_stop();
/* Safety check the trigger value */
switch(ADCD1.grpp->trigger & ~ADC_TRIGGER_SOFTWARE) {
case ADC_TRIGGER_TIMER:
case ADC_TRIGGER_EXTERNAL:
break;
default:
((ADCConversionGroup *)ADCD1.grpp)->trigger = ADC_TRIGGER_SOFTWARE;
ADCD1.depth = 1;
((ADCConversionGroup *)ADCD1.grpp)->circular = 0;
break;
}
/* Count the real number of activated channels in case the user got it wrong */
((ADCConversionGroup *)ADCD1.grpp)->num_channels = 0;
for(i=1; i < 0x100; i <<= 1) {
if ((ADCD1.grpp->channelselects & i))
((ADCConversionGroup *)ADCD1.grpp)->num_channels++;
}
/* Set the channels */
ADCReg1->ADC_CHER = ADCD1.grpp->channelselects;
/* Set up the DMA */
ADCReg1->ADC_RPR = (uint32_t)ADCD1.samples;
if (ADCD1.depth <= 1 || !ADCD1.grpp->circular) {
ADCReg1->ADC_RCR = ADCD1.depth * ADCD1.grpp->num_channels;
ADCReg1->ADC_RNPR = 0;
ADCReg1->ADC_RNCR = 0;
} else {
ADCReg1->ADC_RCR = ADCD1.depth/2 * ADCD1.grpp->num_channels;
ADCReg1->ADC_RNPR = (uint32_t)(ADCD1.samples + (ADCD1.depth/2 * ADCD1.grpp->num_channels));
ADCReg1->ADC_RNCR = (ADCD1.depth - ADCD1.depth/2) * ADCD1.grpp->num_channels;
}
ADCReg1->ADC_PTCR = AT91C_PDC_RXTEN;
/* Set up interrupts */
ADCReg1->ADC_IER = AT91C_ADC_GOVRE | AT91C_ADC_ENDRX | AT91C_ADC_RXBUFF;
/* Set the trigger */
switch(ADCD1.grpp->trigger & ~ADC_TRIGGER_SOFTWARE) {
case ADC_TRIGGER_TIMER:
// Set up the timer if ADCD1.grpp->frequency != 0
if (ADCD1.grpp->frequency) {
/* Turn on Timer in the power management controller */
AT91C_BASE_PMC->PMC_PCER = (1 << AT91_ADC1_TIMERID);
/* Disable the clock and the interrupts */
ADCTimer1->TC_CCR = AT91C_TC_CLKDIS;
ADCTimer1->TC_IDR = 0xFFFFFFFF;
/* Set the Mode of the Timer Counter and calculate the period */
i = (MCK/2)/ADCD1.grpp->frequency;
if (i < (0x10000<<0)) {
ADCTimer1->TC_CMR = (AT91C_TC_ASWTRG_CLEAR | AT91C_TC_ACPC_CLEAR | AT91C_TC_ACPA_SET | AT91C_TC_LDRA_RISING |
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_CLKS_TIMER_DIV1_CLOCK);
} else if (i < (0x10000<<2)) {
i >>= 2;
ADCTimer1->TC_CMR = (AT91C_TC_ASWTRG_CLEAR | AT91C_TC_ACPC_CLEAR | AT91C_TC_ACPA_SET | AT91C_TC_LDRA_RISING |
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_CLKS_TIMER_DIV2_CLOCK);
} else if (i < (0x10000<<4)) {
i >>= 4;
ADCTimer1->TC_CMR = (AT91C_TC_ASWTRG_CLEAR | AT91C_TC_ACPC_CLEAR | AT91C_TC_ACPA_SET | AT91C_TC_LDRA_RISING |
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_CLKS_TIMER_DIV3_CLOCK);
} else if (i < (0x10000<<6)) {
i >>= 6;
ADCTimer1->TC_CMR = (AT91C_TC_ASWTRG_CLEAR | AT91C_TC_ACPC_CLEAR | AT91C_TC_ACPA_SET | AT91C_TC_LDRA_RISING |
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_CLKS_TIMER_DIV4_CLOCK);
} else {
i >>= 9;
ADCTimer1->TC_CMR = (AT91C_TC_ASWTRG_CLEAR | AT91C_TC_ACPC_CLEAR | AT91C_TC_ACPA_SET | AT91C_TC_LDRA_RISING |
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO | AT91C_TC_CLKS_TIMER_DIV5_CLOCK);
}
/* RC is the period, RC-RA is the pulse width (in this case = 1) */
ADCTimer1->TC_RC = i;
ADCTimer1->TC_RA = i - 1;
/* Start the timer counter */
ADCTimer1->TC_CCR = (AT91C_TC_CLKEN |AT91C_TC_SWTRG);
}
ADCReg1->ADC_MR = AT91_ADC1_MAINMODE | AT91C_ADC_SLEEP_NORMAL_MODE | AT91C_ADC_TRGEN_EN | AT91_ADC1_TIMERMODE;
break;
case ADC_TRIGGER_EXTERNAL:
/* Make sure the ADTRG pin is set as an input - assume pull-ups etc have already been set */
#if (SAM7_PLATFORM == SAM7S64) || (SAM7_PLATFORM == SAM7S128) || (SAM7_PLATFORM == SAM7S256) || (SAM7_PLATFORM == SAM7S512)
AT91C_BASE_PIOA->PIO_ODR = AT91C_PA8_ADTRG;
#elif (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || (SAM7_PLATFORM == SAM7X512)
AT91C_BASE_PIOB->PIO_ODR = AT91C_PB18_ADTRG;
#endif
ADCReg1->ADC_MR = AT91_ADC1_MAINMODE | AT91C_ADC_SLEEP_NORMAL_MODE | AT91C_ADC_TRGEN_EN | AT91C_ADC_TRGSEL_EXT;
break;
default:
ADCReg1->ADC_MR = AT91_ADC1_MAINMODE | AT91C_ADC_SLEEP_NORMAL_MODE | AT91C_ADC_TRGEN_DIS;
break;
}
/* Manually start a conversion if we need to */
if (ADCD1.grpp->trigger & ADC_TRIGGER_SOFTWARE)
ADCReg1->ADC_CR = AT91C_ADC_START;
}
/**
* @brief Stops an ongoing conversion.
*
* @param[in] adcp pointer to the @p ADCDriver object
*
* @notapi
*/
void adc_lld_stop_conversion(ADCDriver *adcp) {
(void) adcp;
adc_stop();
}
#endif /* HAL_USE_ADC */
/** @} */

View File

@@ -0,0 +1,303 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 file has been contributed by:
Andrew Hannam aka inmarket.
*/
/**
* @file AT91SAM7/adc_lld.h
* @brief AT91SAM7 ADC subsystem low level driver header.
*
* @addtogroup ADC
* @{
*/
#ifndef _ADC_LLD_H_
#define _ADC_LLD_H_
#if HAL_USE_ADC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @name Trigger Sources
* @{
*/
#define ADC_TRIGGER_SOFTWARE 0x8000 /**< @brief Software Triggering - Can be combined with another value */
#define ADC_TRIGGER_TIMER 0x0001 /**< @brief TIO Timer Counter Channel */
#define ADC_TRIGGER_EXTERNAL 0x0002 /**< @brief External Trigger */
/** @} */
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief ADC1 driver enable switch.
* @details If set to @p TRUE the support for ADC1 is included.
* @note The default is @p TRUE.
*/
#if !defined(ADC_USE_ADC1) || defined(__DOXYGEN__)
#define ADC_USE_ADC1 TRUE
#endif
/**
* @brief ADC1 Timer to use when a periodic conversion is requested.
* @details Should be set to 0..2
* @note The default is 0
*/
#if !defined(AT91_ADC1_TIMER) || defined(__DOXYGEN__)
#define AT91_ADC1_TIMER 0
#endif
/**
* @brief ADC1 Resolution.
* @details Either 8 or 10 bits
* @note The default is 10 bits.
*/
#if !defined(AT91_ADC1_RESOLUTION) || defined(__DOXYGEN__)
#define AT91_ADC1_RESOLUTION 10
#endif
/**
* @brief ADC1 Clock
* @details Maximum is 5MHz for 10bit or 8MHz for 8bit
* @note The default is calculated from AT91_ADC1_RESOLUTION to give the fastest possible ADCClock
*/
#if !defined(AT91_ADC1_CLOCK) || defined(__DOXYGEN__)
#if AT91_ADC1_RESOLUTION == 8
#define AT91_ADC1_CLOCK 8000000
#else
#define AT91_ADC1_CLOCK 5000000
#endif
#endif
/**
* @brief ADC1 Sample and Hold Time
* @details SHTM = RoundUp(ADCClock * SampleHoldTime). Range = RoundUp(ADCClock / 1,666,666) to 15
* @note Default corresponds to the minimum sample and hold time (600nS from the datasheet)
* @note Increasing the Sample Hold Time increases the ADC input impedance
*/
#if !defined(AT91_ADC1_SHTM) || defined(__DOXYGEN__)
#define AT91_ADC1_SHTM 0
#endif
#if AT91_ADC1_SHTM < ((AT91_ADC1_CLOCK+1666665)/1666666)
#undef AT91_ADC1_SHTM
#define AT91_ADC1_SHTM ((AT91_ADC1_CLOCK+1666665)/1666666)
#endif
#if AT91_ADC1_SHTM > 15
#undef AT91_ADC1_SHTM
#define AT91_ADC1_SHTM 15
#endif
/**
* @brief ADC interrupt priority level setting.
*/
#if !defined(AT91_ADC_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define AT91_ADC_IRQ_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if !defined(AT91_DMA_REQUIRED)
#define AT91_DMA_REQUIRED
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief ADC sample data type.
*/
#if AT91_ADC1_RESOLUTION == AT91C_ADC_LOWRES_8_BIT
typedef uint8_t adcsample_t;
#else
typedef uint16_t adcsample_t;
#endif
/**
* @brief Channels number in a conversion group.
*/
typedef uint16_t adc_channels_num_t;
/**
* @brief Possible ADC failure causes.
* @note Error codes are architecture dependent and should not relied
* upon.
*/
typedef enum {
ADC_ERR_OVERFLOW = 0, /**< ADC overflow condition. Something is not working fast enough. */
} adcerror_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 ADC error callback type.
*
* @param[in] adcp pointer to the @p ADCDriver object triggering the
* callback
* @param[in] err ADC error code
*/
typedef void (*adcerrorcallback_t)(ADCDriver *adcp, adcerror_t err);
/**
* @brief Conversion group configuration structure.
* @details This implementation-dependent structure describes a conversion
* operation.
* @note The use of this configuration structure requires knowledge of
* STM32 ADC cell registers interface, please refer to the STM32
* reference manual for details.
*/
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;
/**
* @brief Error callback or @p NULL.
*/
adcerrorcallback_t error_cb;
/* End of the mandatory fields.*/
/**
* @brief Select the ADC Channels to read.
* @details The number of bits at logic level one in this register must
* be equal to the number in the @p num_channels field.
*/
uint16_t channelselects;
/**
* @brief Select how to trigger the conversion.
*/
uint16_t trigger;
/**
* @brief When in ADC_TRIGGER_TIMER trigger mode - what frequency?
*/
uint32_t frequency;
} ADCConversionGroup;
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
} ADCConfig;
/**
* @brief Structure representing an ADC driver.
*/
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
#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.*/
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if 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_ */
/** @} */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,84 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support - ROUSSET -
* ----------------------------------------------------------------------------
* Copyright (c) 2006, Atmel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaiimer below.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the disclaimer below in the documentation and/or
* other materials provided with the distribution.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include "aic.h"
#include <board.h>
//------------------------------------------------------------------------------
// Exported functions
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
/// Configures the interrupt associated with the given source, using the
/// specified mode and interrupt handler.
/// \param source Interrupt source to configure.
/// \param mode Triggering mode of the interrupt.
/// \param handler Interrupt handler function.
//------------------------------------------------------------------------------
void AIC_ConfigureIT(unsigned int source,
unsigned int mode,
void (*handler)( void ))
{
// Disable the interrupt first
AT91C_BASE_AIC->AIC_IDCR = 1 << source;
// Configure mode and handler
AT91C_BASE_AIC->AIC_SMR[source] = mode;
AT91C_BASE_AIC->AIC_SVR[source] = (unsigned int) handler;
// Clear interrupt
AT91C_BASE_AIC->AIC_ICCR = 1 << source;
}
//------------------------------------------------------------------------------
/// Enables interrupts coming from the given (unique) source.
/// \param source Interrupt source to enable.
//------------------------------------------------------------------------------
void AIC_EnableIT(unsigned int source)
{
AT91C_BASE_AIC->AIC_IECR = 1 << source;
}
//------------------------------------------------------------------------------
/// Disables interrupts coming from the given (unique) source.
/// \param source Interrupt source to enable.
//------------------------------------------------------------------------------
void AIC_DisableIT(unsigned int source)
{
AT91C_BASE_AIC->AIC_IDCR = 1 << source;
}

View File

@@ -0,0 +1,78 @@
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support - ROUSSET -
* ----------------------------------------------------------------------------
* Copyright (c) 2006, Atmel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaiimer below.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the disclaimer below in the documentation and/or
* other materials provided with the distribution.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ----------------------------------------------------------------------------
*/
//------------------------------------------------------------------------------
/// \dir
/// !Purpose
///
/// Methods and definitions for configuring interrupts using the Advanced
/// Interrupt Controller (AIC).
///
/// !Usage
/// -# Configure an interrupt source using AIC_ConfigureIT
/// -# Enable or disable interrupt generation of a particular source with
/// AIC_EnableIT and AIC_DisableIT.
//------------------------------------------------------------------------------
#ifndef AIC_H
#define AIC_H
//------------------------------------------------------------------------------
// Headers
//------------------------------------------------------------------------------
#include <board.h>
//------------------------------------------------------------------------------
// Definitions
//------------------------------------------------------------------------------
#ifndef AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL
/// Redefinition of missing constant.
#define AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE
#endif
//------------------------------------------------------------------------------
// Global functions
//------------------------------------------------------------------------------
extern void AIC_ConfigureIT(unsigned int source,
unsigned int mode,
void (*handler)( void ));
extern void AIC_EnableIT(unsigned int source);
extern void AIC_DisableIT(unsigned int source);
#endif //#ifndef AIC_H

View File

@@ -0,0 +1,56 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 _AT91SAM7_H_
#define _AT91SAM7_H_
/*
* Supported platforms.
*/
#define SAM7S64 0
#define SAM7S128 1
#define SAM7S256 2
#define SAM7S512 3
#define SAM7X128 4
#define SAM7X256 5
#define SAM7X512 6
#define SAM7A3 7
#ifndef SAM7_PLATFORM
#error "SAM7 platform not defined"
#endif
#if SAM7_PLATFORM == SAM7S64
#include "at91lib/AT91SAM7S64.h"
#elif SAM7_PLATFORM == SAM7S128
#include "at91lib/AT91SAM7S128.h"
#elif SAM7_PLATFORM == SAM7S256
#include "at91lib/AT91SAM7S256.h"
#elif SAM7_PLATFORM == SAM7S512
#include "at91lib/AT91SAM7S512.h"
#elif SAM7_PLATFORM == SAM7X128
#include "at91lib/AT91SAM7X128.h"
#elif SAM7_PLATFORM == SAM7X256
#include "at91lib/AT91SAM7X256.h"
#elif SAM7_PLATFORM == SAM7X512
#include "at91lib/AT91SAM7X512.h"
#elif SAM7_PLATFORM == SAM7A3
#include "at91lib/AT91SAM7A3.h"
#else
#error "SAM7 platform not supported"
#endif
#endif /* _AT91SAM7_H_ */

View File

@@ -0,0 +1,144 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/at91sam7_mii.c
* @brief AT91SAM7 low level MII driver code.
*
* @addtogroup AT91SAM7_MII
* @{
*/
#include "ch.h"
#include "hal.h"
#include "at91sam7_mii.h"
#if HAL_USE_MAC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level MII driver initialization.
*
* @notapi
*/
void miiInit(void) {
}
/**
* @brief Resets a PHY device.
*
* @param[in] macp pointer to the @p MACDriver object
*
* @notapi
*/
void miiReset(MACDriver *macp) {
(void)macp;
/*
* Disables the pullups on all the pins that are latched on reset by the PHY.
*/
AT91C_BASE_PIOB->PIO_PPUDR = PHY_LATCHED_PINS;
#ifdef PIOB_PHY_PD_MASK
/*
* PHY power control.
*/
AT91C_BASE_PIOB->PIO_OER = PIOB_PHY_PD_MASK; /* Becomes an output. */
AT91C_BASE_PIOB->PIO_PPUDR = PIOB_PHY_PD_MASK;/* Default pullup disabled. */
#if (PHY_HARDWARE == PHY_DAVICOM_9161)
AT91C_BASE_PIOB->PIO_CODR = PIOB_PHY_PD_MASK; /* Output to low level. */
#else
AT91C_BASE_PIOB->PIO_SODR = PIOB_PHY_PD_MASK; /* Output to high level. */
#endif
#endif
/*
* PHY reset by pulsing the NRST pin.
*/
AT91C_BASE_RSTC->RSTC_RMR = 0xA5000100;
AT91C_BASE_RSTC->RSTC_RCR = 0xA5000000 | AT91C_RSTC_EXTRST;
while (!(AT91C_BASE_RSTC->RSTC_RSR & AT91C_RSTC_NRSTL))
;
}
/**
* @brief Reads a PHY register through the MII interface.
*
* @param[in] macp pointer to the @p MACDriver object
* @param[in] addr the register address
* @return The register value.
*
* @notapi
*/
phyreg_t miiGet(MACDriver *macp, phyaddr_t addr) {
(void)macp;
AT91C_BASE_EMAC->EMAC_MAN = (0b01 << 30) | /* SOF */
(0b10 << 28) | /* RW */
(PHY_ADDRESS << 23) | /* PHYA */
(addr << 18) | /* REGA */
(0b10 << 16); /* CODE */
while (!( AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE))
;
return (phyreg_t)(AT91C_BASE_EMAC->EMAC_MAN & 0xFFFF);
}
/**
* @brief Writes a PHY register through the MII interface.
*
* @param[in] macp pointer to the @p MACDriver object
* @param[in] addr the register address
* @param[in] value the new register value
*
* @notapi
*/
void miiPut(MACDriver *macp, phyaddr_t addr, phyreg_t value) {
(void)macp;
AT91C_BASE_EMAC->EMAC_MAN = (0b01 << 30) | /* SOF */
(0b01 << 28) | /* RW */
(PHY_ADDRESS << 23) | /* PHYA */
(addr << 18) | /* REGA */
(0b10 << 16) | /* CODE */
value;
while (!( AT91C_BASE_EMAC->EMAC_NSR & AT91C_EMAC_IDLE))
;
}
#endif /* HAL_USE_MAC */
/** @} */

View File

@@ -0,0 +1,111 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/at91sam7_mii.h
* @brief AT91SAM7 low level MII driver header.
*
* @addtogroup AT91SAM7_MII
* @{
*/
#ifndef _AT91SAM7_MII_H_
#define _AT91SAM7_MII_H_
#if HAL_USE_MAC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
#define PHY_MICREL_KS8721 0
#define PHY_DAVICOM_9161 1
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief PHY manufacturer and model.
*/
#if !defined(PHY_HARDWARE) || defined(__DOXYGEN__)
#define PHY_HARDWARE PHY_MICREL_KS8721
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/**
* @brief Pins latched by the PHY at reset.
*/
#if PHY_HARDWARE == PHY_MICREL_KS8721
#define PHY_ADDRESS 1
#define PHY_ID MII_KS8721_ID
#define PHY_LATCHED_PINS (AT91C_PB4_ECRS | AT91C_PB5_ERX0 | \
AT91C_PB6_ERX1 | AT91C_PB7_ERXER | \
AT91C_PB13_ERX2 | AT91C_PB14_ERX3 | \
AT91C_PB15_ERXDV_ECRSDV | AT91C_PB16_ECOL | \
AT91C_PIO_PB26)
#elif PHY_HARDWARE == PHY_DAVICOM_9161
#define PHY_ADDRESS 0
#define PHY_ID MII_DM9161_ID
#define PHY_LATCHED_PINS (AT91C_PB0_ETXCK_EREFCK | AT91C_PB4_ECRS | \
AT91C_PB5_ERX0 | AT91C_PB6_ERX1 | \
AT91C_PB7_ERXER | AT91C_PB13_ERX2 | \
AT91C_PB14_ERX3 | AT91C_PB15_ERXDV_ECRSDV | \
AT91C_PB16_ECOL | AT91C_PB17_ERXCK)
#endif /* PHY_HARDWARE */
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Type of a PHY register value.
*/
typedef uint16_t phyreg_t;
/**
* @brief Type of a PHY register address.
*/
typedef uint8_t phyaddr_t;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#ifdef __cplusplus
extern "C" {
#endif
void miiInit(void);
void miiReset(MACDriver *macp);
phyreg_t miiGet(MACDriver *macp, phyaddr_t addr);
void miiPut(MACDriver *macp, phyaddr_t addr, phyreg_t value);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_MAC */
#endif /* _AT91SAM7_MII_H_ */
/** @} */

View File

@@ -0,0 +1,235 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/ext_lld.c
* @brief AT91SAM7 EXT subsystem low level driver source.
*
* @addtogroup EXT
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_EXT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief EXTDA driver identifier.
*/
EXTDriver EXTDA;
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3)
/**
* @brief EXTDB driver identifier.
*/
EXTDriver EXTDB;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Handles external interrupts.
*
* @param[in] extp pointer to the driver that received the interrupt
*/
static void ext_lld_serveInterrupt(EXTDriver *extp) {
uint32_t irqFlags;
uint32_t ch;
chSysLockFromIsr();
/* Read flags of pending PIO interrupts.*/
irqFlags = extp->pio->PIO_ISR;
/* Call callback function for any pending interrupt.*/
for(ch = 0; ch < EXT_MAX_CHANNELS; ch++) {
/* Check if the channel is activated and if its IRQ flag is set.*/
if((extp->config->channels[ch].mode &
EXT_CH_MODE_ENABLED & EXT_CH_MODE_EDGES_MASK)
&& ((1 << ch) & irqFlags)) {
(extp->config->channels[ch].cb)(extp, ch);
}
}
chSysUnlockFromIsr();
AT91C_BASE_AIC->AIC_EOICR = 0;
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/**
* @brief EXTI[0] interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(EXTIA_IRQHandler) {
CH_IRQ_PROLOGUE();
ext_lld_serveInterrupt(&EXTDA);
CH_IRQ_EPILOGUE();
}
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3)
/**
* @brief EXTI[1] interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(EXTIB_IRQHandler) {
CH_IRQ_PROLOGUE();
ext_lld_serveInterrupt(&EXTDB);
CH_IRQ_EPILOGUE();
}
#endif
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level EXT driver initialization.
*
* @notapi
*/
void ext_lld_init(void) {
/* Driver initialization.*/
extObjectInit(&EXTDA);
/* Set PIO base addresses.*/
EXTDA.pio = AT91C_BASE_PIOA;
/* Set peripheral IDs.*/
EXTDA.pid = AT91C_ID_PIOA;
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3)
/* Same for PIOB.*/
extObjectInit(&EXTDB);
EXTDB.pio = AT91C_BASE_PIOB;
EXTDB.pid = AT91C_ID_PIOB;
#endif
}
/**
* @brief Configures and activates the EXT peripheral.
*
* @param[in] extp pointer to the @p EXTDriver object
*
* @notapi
*/
void ext_lld_start(EXTDriver *extp) {
uint16_t ch;
uint32_t ier = 0;
const EXTConfig *config = extp->config;
switch(extp->pid) {
case AT91C_ID_PIOA:
AIC_ConfigureIT(AT91C_ID_PIOA, SAM7_computeSMR(config->mode,
config->priority),
EXTIA_IRQHandler);
break;
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3)
case AT91C_ID_PIOB:
AIC_ConfigureIT(AT91C_ID_PIOB, SAM7_computeSMR(config->mode,
config->priority),
EXTIB_IRQHandler);
break;
#endif
}
/* Enable and Disable channels with respect to config.*/
for(ch = 0; ch < EXT_MAX_CHANNELS; ch++) {
ier |= (config->channels[ch].mode & EXT_CH_MODE_EDGES_MASK & EXT_CH_MODE_ENABLED ? 1 : 0) << ch;
}
extp->pio->PIO_IER = ier;
extp->pio->PIO_IDR = ~ier;
/* Enable interrupt on corresponding PIO port in AIC.*/
AIC_EnableIT(extp->pid);
}
/**
* @brief Deactivates the EXT peripheral.
*
* @param[in] extp pointer to the @p EXTDriver object
*
* @notapi
*/
void ext_lld_stop(EXTDriver *extp) {
/* Disable interrupt on corresponding PIO port in AIC.*/
AIC_DisableIT(extp->pid);
}
/**
* @brief Enables an EXT channel.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] channel channel to be enabled
*
* @notapi
*/
void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel) {
chDbgCheck((extp->config->channels[channel].cb != NULL),
"Call back pointer can not be NULL");
extp->pio->PIO_IER = (1 << channel);
}
/**
* @brief Disables an EXT channel.
*
* @param[in] extp pointer to the @p EXTDriver object
* @param[in] channel channel to be disabled
*
* @notapi
*/
void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel) {
extp->pio->PIO_IDR = (1 << channel);
}
#endif /* HAL_USE_EXT */
/** @} */

View File

@@ -0,0 +1,239 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/ext_lld.h
* @brief AT91SAM7 EXT subsystem low level driver header.
*
* @addtogroup EXT
* @{
*/
#ifndef _EXT_LLD_H_
#define _EXT_LLD_H_
#if HAL_USE_EXT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @brief Pointer to the SAM7 AIC register block.
*/
#define SAM7_EXT_AIC ((AT91PS_AIC *)AT91C_BASE_AIC)
/**
* @brief Number of channels within one ext driver.
*/
#define EXT_MAX_CHANNELS 32
/**
* @brief Mask of priority bits in interrupt mode register.
*/
#define SAM7_EXT_PRIORITY_MASK 0x00000007
/**
* @brief Shifter for priority bits in interrupt mode register.
*/
#define SAM7_EXT_PRIORITY_SHIFTER 0
/**
* @brief Shifter for mode bits in interrupt mode register.
*/
#define SAM7_EXT_MODE_SHIFTER 5
/*
* On the SAM7 architecture, a single channel can only be enables or disabled
* Hence, undefine the other channel mode constants
*/
#ifdef EXT_CH_MODE_RISING_EDGE
#undef EXT_CH_MODE_RISING_EDGE
#endif
#ifdef EXT_CH_MODE_FALLING_EDGE
#undef EXT_CH_MODE_FALLING_EDGE
#endif
#ifdef EXT_CH_MODE_BOTH_EDGES
#undef EXT_CH_MODE_BOTH_EDGES
#endif
/**
* @name EXT channels mode
* @{
*/
#define EXT_CH_MODE_ENABLED 1 /**< @brief Channel is enabled. */
/** @} */
/**
* @name EXT drivers mode
* @{
*/
/**
* @brief Mask for modes.
*/
#define SAM7_EXT_MODE_MASK AT91C_AIC_SRCTYPE
/**
* @brief Falling edge callback.
*/
#define SAM7_EXT_MODE_FALLING_EDGE AT91C_AIC_SRCTYPE_EXT_NEGATIVE_EDGE
/**
* @brief Rising edge callback.
*/
#define SAM7_EXT_MODE_RISING_EDGE AT91C_AIC_SRCTYPE_POSITIVE_EDGE
/**
* @brief High-level callback.
*/
#define SAM7_EXT_MODE_HIGH_LEVEL AT91C_AIC_SRCTYPE_HIGH_LEVEL
/**
* @brief Low-level callback.
*/
#define SAM7_EXT_MODE_LOW_LEVEL AT91C_AIC_SRCTYPE_EXT_LOW_LEVEL
/** @} */
/**
* @name EXT drivers priorities
* @{
*/
#define SAM7_EXT_PRIOR_HIGHEST AT91C_AIC_PRIOR_HIGHEST
#define SAM7_EXT_PRIOR_LOWEST AT91C_AIC_PRIOR_LOWEST
/** @} */
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief EXT channel identifier.
*/
typedef uint32_t expchannel_t;
/**
* @brief Type of an EXT generic notification callback.
*
* @param[in] extp pointer to the @p EXPDriver object triggering the
* callback
*/
typedef void (*extcallback_t)(EXTDriver *extp, expchannel_t channel);
/**
* @brief Channel configuration structure.
*/
typedef struct {
/**
* @brief Channel mode.
*/
uint32_t mode;
/**
* @brief Channel callback.
*/
extcallback_t cb;
} EXTChannelConfig;
/**
* @brief Driver configuration structure.
* @note It could be empty on some architectures.
*/
typedef struct {
/**
* @brief Channel configurations.
*/
EXTChannelConfig channels[EXT_MAX_CHANNELS];
/* End of the mandatory fields.*/
/**
* @brief interrupt mode.
*/
uint32_t mode;
/**
* @brief interrupt priority.
*/
uint32_t priority;
} EXTConfig;
/**
* @brief Structure representing an EXT driver.
*/
struct EXTDriver {
/**
* @brief Driver state.
*/
extstate_t state;
/**
* @brief Current configuration data.
*/
const EXTConfig *config;
/* End of the mandatory fields.*/
/**
* @brief Pointer to the corresponding PIO registers block.
*/
AT91PS_PIO pio;
/**
* @brief peripheral ID of the corresponding PIO block.
*/
uint32_t pid;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/**
* @brief Computes the content for the interrupt source mode register.
*/
#define SAM7_computeSMR(mode, prio) ( \
((mode & SAM7_EXT_MODE_MASK) << SAM7_EXT_MODE_SHIFTER) | \
((prio & SAM7_EXT_PRIORITY_MASK) << SAM7_EXT_PRIORITY_SHIFTER) \
)
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if !defined(__DOXYGEN__)
extern EXTDriver EXTDA;
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3)
extern EXTDriver EXTDB;
#endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
void ext_lld_init(void);
void ext_lld_start(EXTDriver *extp);
void ext_lld_stop(EXTDriver *extp);
void ext_lld_channel_enable(EXTDriver *extp, expchannel_t channel);
void ext_lld_channel_disable(EXTDriver *extp, expchannel_t channel);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_EXT */
#endif /* _EXT_LLD_H_ */
/** @} */

View File

@@ -0,0 +1,451 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/gpt_lld.c
* @brief AT91SAM7 GPT subsystem low level driver source.
*
* @addtogroup GPT
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_GPT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief GPTD1 driver identifier.
* @note The driver GPTD1 allocates the complex timer TC0 when enabled.
*/
#if AT91_GPT_USE_TC0 || defined(__DOXYGEN__)
GPTDriver GPTD1;
#endif
/**
* @brief GPTD2 driver identifier.
* @note The driver GPTD2 allocates the timer TC1 when enabled.
*/
#if AT91_GPT_USE_TC1 || defined(__DOXYGEN__)
GPTDriver GPTD2;
#endif
/**
* @brief GPTD3 driver identifier.
* @note The driver GPTD3 allocates the timer TC2 when enabled.
*/
#if AT91_GPT_USE_TC2 || defined(__DOXYGEN__)
GPTDriver GPTD3;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Shared IRQ handler.
*
* @param[in] gptp pointer to a @p GPTDriver object
*/
static void gpt_lld_serve_interrupt(GPTDriver *gptp) {
// Read the status to clear the interrupts
{ uint32_t isr = gptp->tc->TC_SR; (void) isr; }
// Do the callback
gptp->config->callback(gptp);
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if AT91_GPT_USE_TC0
/**
* @brief TC1 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(TC0_IRQHandler) {
CH_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD1);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif /* AT91_GPT_USE_TC0 */
#if AT91_GPT_USE_TC1
/**
* @brief TC1 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(TC1_IRQHandler) {
CH_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD2);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif /* AT91_GPT_USE_TC1 */
#if AT91_GPT_USE_TC2
/**
* @brief TC1 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(TC2_IRQHandler) {
CH_IRQ_PROLOGUE();
gpt_lld_serve_interrupt(&GPTD2);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
}
#endif /* AT91_GPT_USE_TC2 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level GPT driver initialization.
*
* @notapi
*/
void gpt_lld_init(void) {
#if AT91_GPT_USE_TC0
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC0); // Turn on the power
GPTD1.tc = AT91C_BASE_TC0;
gptObjectInit(&GPTD1);
gpt_lld_stop(&GPTD1); // Make sure it is disabled
AIC_ConfigureIT(AT91C_ID_TC0, AT91C_AIC_SRCTYPE_HIGH_LEVEL | AT91_GPT_TC0_IRQ_PRIORITY, TC0_IRQHandler);
AIC_EnableIT(AT91C_ID_TC0);
#endif
#if AT91_GPT_USE_TC1
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC1); // Turn on the power
GPTD2.tc = AT91C_BASE_TC1;
gptObjectInit(&GPTD2);
gpt_lld_stop(&GPTD2); // Make sure it is disabled
AIC_ConfigureIT(AT91C_ID_TC1, AT91C_AIC_SRCTYPE_HIGH_LEVEL | AT91_GPT_TC1_IRQ_PRIORITY, TC1_IRQHandler);
AIC_EnableIT(AT91C_ID_TC1);
#endif
#if AT91_GPT_USE_TC2
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TC2); // Turn on the power
GPTD3.tc = AT91C_BASE_TC2;
gptObjectInit(&GPTD3);
gpt_lld_stop(&GPTD3); // Make sure it is disabled
AIC_ConfigureIT(AT91C_ID_TC2, AT91C_AIC_SRCTYPE_HIGH_LEVEL | AT91_GPT_TC2_IRQ_PRIORITY, TC2_IRQHandler);
AIC_EnableIT(AT91C_ID_TC2);
#endif
}
/**
* @brief Configures and activates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_start(GPTDriver *gptp) {
uint32_t cmr, bmr;
bmr = *AT91C_TCB_BMR;
cmr = (AT91C_TC_ASWTRG_CLEAR | AT91C_TC_ACPC_CLEAR | AT91C_TC_ACPA_SET |
AT91C_TC_WAVE | AT91C_TC_WAVESEL_UP_AUTO);
// Calculate clock
switch(gptp->config->clocksource) {
case GPT_CLOCK_MCLK:
switch(gptp->config->frequency) {
case MCK/2: cmr |= AT91C_TC_CLKS_TIMER_DIV1_CLOCK; break;
case MCK/8: cmr |= AT91C_TC_CLKS_TIMER_DIV2_CLOCK; break;
case MCK/32: cmr |= AT91C_TC_CLKS_TIMER_DIV3_CLOCK; break;
case MCK/128: cmr |= AT91C_TC_CLKS_TIMER_DIV4_CLOCK; break;
case MCK/1024: cmr |= AT91C_TC_CLKS_TIMER_DIV5_CLOCK; break;
default:
chDbgAssert(TRUE, "gpt_lld_start(), #1", "invalid frequency");
cmr |= AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
break;
}
break;
case GPT_CLOCK_FREQUENCY:
/* The mode and period will be calculated when the timer is started */
cmr |= AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
break;
case GPT_CLOCK_RE_TCLK0:
case GPT_CLOCK_FE_TCLK0:
if ((gptp->config->clocksource & 1)) cmr |= AT91C_TC_CLKI;
cmr |= AT91C_TC_CLKS_XC0;
#if AT91_GPT_USE_TC0
if (gptp == &GPTD1) bmr = (bmr & ~AT91C_TCB_TC0XC0S) | AT91C_TCB_TC0XC0S_TCLK0;
#endif
break;
case GPT_CLOCK_RE_TCLK1:
case GPT_CLOCK_FE_TCLK1:
if ((gptp->config->clocksource & 1)) cmr |= AT91C_TC_CLKI;
cmr |= AT91C_TC_CLKS_XC1;
#if AT91_GPT_USE_TC1
if (gptp == &GPTD2) bmr = (bmr & ~AT91C_TCB_TC1XC1S) | AT91C_TCB_TC1XC1S_TCLK1;
#endif
break;
case GPT_CLOCK_RE_TCLK2:
case GPT_CLOCK_FE_TCLK2:
if ((gptp->config->clocksource & 1)) cmr |= AT91C_TC_CLKI;
cmr |= AT91C_TC_CLKS_XC2;
#if AT91_GPT_USE_TC2
if (gptp == &GPTD3) bmr = (bmr & ~AT91C_TCB_TC2XC2S) | AT91C_TCB_TC2XC2S_TCLK2;
#endif
break;
case GPT_CLOCK_RE_TC0:
case GPT_CLOCK_FE_TC0:
if ((gptp->config->clocksource & 1)) cmr |= AT91C_TC_CLKI;
#if AT91_GPT_USE_TC0
if (gptp == &GPTD1) {
chDbgAssert(TRUE, "gpt_lld_start(), #2", "invalid clock");
cmr |= AT91C_TC_CLKS_XC0;
bmr = (bmr & ~AT91C_TCB_TC0XC0S) | AT91C_TCB_TC0XC0S_NONE;
break;
}
#endif
#if AT91_GPT_USE_TC1
if (gptp == &GPTD2) {
cmr |= AT91C_TC_CLKS_XC1;
bmr = (bmr & ~AT91C_TCB_TC1XC1S) | AT91C_TCB_TC1XC1S_TIOA0;
break;
}
#endif
#if AT91_GPT_USE_TC2
if (gptp == &GPTD3) {
cmr |= AT91C_TC_CLKS_XC2;
bmr = (bmr & ~AT91C_TCB_TC2XC2S) | AT91C_TCB_TC2XC2S_TIOA0;
break;
}
#endif
chDbgAssert(TRUE, "gpt_lld_start(), #3", "invalid GPT device");
cmr |= AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
break;
case GPT_CLOCK_RE_TC1:
case GPT_CLOCK_FE_TC1:
if ((gptp->config->clocksource & 1)) cmr |= AT91C_TC_CLKI;
#if AT91_GPT_USE_TC0
if (gptp == &GPTD1) {
cmr |= AT91C_TC_CLKS_XC0;
bmr = (bmr & ~AT91C_TCB_TC0XC0S) | AT91C_TCB_TC0XC0S_TIOA1;
break;
}
#endif
#if AT91_GPT_USE_TC1
if (gptp == &GPTD2) {
chDbgAssert(TRUE, "gpt_lld_start(), #4", "invalid clock");
cmr |= AT91C_TC_CLKS_XC1;
bmr = (bmr & ~AT91C_TCB_TC1XC1S) | AT91C_TCB_TC1XC1S_NONE;
break;
}
#endif
#if AT91_GPT_USE_TC2
if (gptp == &GPTD3) {
cmr |= AT91C_TC_CLKS_XC2;
bmr = (bmr & ~AT91C_TCB_TC2XC2S) | AT91C_TCB_TC2XC2S_TIOA1;
break;
}
#endif
chDbgAssert(TRUE, "gpt_lld_start(), #5", "invalid GPT device");
cmr |= AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
break;
case GPT_CLOCK_RE_TC2:
case GPT_CLOCK_FE_TC2:
if ((gptp->config->clocksource & 1)) cmr |= AT91C_TC_CLKI;
#if AT91_GPT_USE_TC0
if (gptp == &GPTD1) {
cmr |= AT91C_TC_CLKS_XC0;
bmr = (bmr & ~AT91C_TCB_TC0XC0S) | AT91C_TCB_TC0XC0S_TIOA2;
break;
}
#endif
#if AT91_GPT_USE_TC1
if (gptp == &GPTD2) {
cmr |= AT91C_TC_CLKS_XC1;
bmr = (bmr & ~AT91C_TCB_TC1XC1S) | AT91C_TCB_TC1XC1S_TIOA2;
break;
}
#endif
#if AT91_GPT_USE_TC2
if (gptp == &GPTD3) {
chDbgAssert(TRUE, "gpt_lld_start(), #6", "invalid clock");
cmr |= AT91C_TC_CLKS_XC2;
bmr = (bmr & ~AT91C_TCB_TC2XC2S) | AT91C_TCB_TC2XC2S_NONE;
break;
}
#endif
chDbgAssert(TRUE, "gpt_lld_start(), #7", "invalid GPT device");
cmr |= AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
break;
default:
chDbgAssert(TRUE, "gpt_lld_start(), #8", "invalid clock");
cmr |= AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
break;
}
// Calculate clock gating
chDbgAssert(gptp->config->clockgate == GPT_GATE_NONE || gptp->config->clockgate == GPT_GATE_TCLK0
|| gptp->config->clockgate == GPT_GATE_TCLK1 || gptp->config->clockgate == GPT_GATE_TCLK2
, "gpt_lld_start(), #9", "invalid clockgate");
cmr |= ((uint32_t)(gptp->config->clockgate & 0x03)) << 4; // special magic numbers here
// Calculate triggers
chDbgAssert(gptp->config->trigger == GPT_TRIGGER_NONE
|| gptp->config->trigger == GPT_TRIGGER_RE_TIOB || gptp->config->trigger == GPT_TRIGGER_FE_TIOB || gptp->config->trigger == GPT_TRIGGER_BE_TIOB
|| gptp->config->trigger == GPT_TRIGGER_RE_TCLK0 || gptp->config->trigger == GPT_TRIGGER_FE_TCLK0 || gptp->config->trigger == GPT_TRIGGER_BE_TCLK0
|| gptp->config->trigger == GPT_TRIGGER_RE_TCLK1 || gptp->config->trigger == GPT_TRIGGER_FE_TCLK1 || gptp->config->trigger == GPT_TRIGGER_BE_TCLK1
|| gptp->config->trigger == GPT_TRIGGER_RE_TCLK2 || gptp->config->trigger == GPT_TRIGGER_FE_TCLK2 || gptp->config->trigger == GPT_TRIGGER_BE_TCLK2
, "gpt_lld_start(), #10", "invalid trigger");
cmr |= ((uint32_t)(gptp->config->trigger & 0x03)) << 10; // special magic numbers here
cmr |= ((uint32_t)(gptp->config->trigger & 0x30)) << (8-4); // special magic numbers here
/* Set everything up but disabled */
gptp->tc->TC_CCR = AT91C_TC_CLKDIS;
gptp->tc->TC_IDR = 0xFFFFFFFF;
gptp->tc->TC_CMR = cmr;
gptp->tc->TC_RC = 65535;
gptp->tc->TC_RA = 32768;
*AT91C_TCB_BMR = bmr;
cmr = gptp->tc->TC_SR; // Clear any pending interrupts
}
/**
* @brief Deactivates the GPT peripheral.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_stop(GPTDriver *gptp) {
gptp->tc->TC_CCR = AT91C_TC_CLKDIS;
gptp->tc->TC_IDR = 0xFFFFFFFF;
{ uint32_t isr = gptp->tc->TC_SR; (void)isr; }
}
/**
* @brief Starts the timer in continuous mode.
*
* @param[in] gptp pointer to the @p GPTDriver object
* @param[in] interval period in ticks
*
* @notapi
*/
void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval) {
gpt_lld_change_interval(gptp, interval);
if (gptp->state == GPT_ONESHOT)
gptp->tc->TC_CMR |= AT91C_TC_CPCDIS;
else
gptp->tc->TC_CMR &= ~AT91C_TC_CPCDIS;
gptp->tc->TC_CCR = AT91C_TC_CLKEN|AT91C_TC_SWTRG;
if (gptp->config->callback)
gptp->tc->TC_IER = AT91C_TC_CPCS|AT91C_TC_COVFS;
}
/**
* @brief Stops the timer.
*
* @param[in] gptp pointer to the @p GPTDriver object
*
* @notapi
*/
void gpt_lld_stop_timer(GPTDriver *gptp) {
gptp->tc->TC_CCR = AT91C_TC_CLKDIS;
gptp->tc->TC_IDR = 0xFFFFFFFF;
{ uint32_t isr = gptp->tc->TC_SR; (void)isr; }
}
/**
* @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
*/
void gpt_lld_change_interval(GPTDriver *gptp, gptcnt_t interval) {
if (gptp->config->clocksource == GPT_CLOCK_FREQUENCY) {
uint32_t rc, cmr;
// Reset the timer to the (possibly) new frequency value
rc = (MCK/2)/gptp->config->frequency;
if (rc < (0x10000<<0)) {
cmr = AT91C_TC_CLKS_TIMER_DIV1_CLOCK;
} else if (rc < (0x10000<<2)) {
rc >>= 2;
cmr = AT91C_TC_CLKS_TIMER_DIV2_CLOCK;
} else if (rc < (0x10000<<4)) {
rc >>= 4;
cmr = AT91C_TC_CLKS_TIMER_DIV3_CLOCK;
} else if (rc < (0x10000<<6)) {
rc >>= 6;
cmr = AT91C_TC_CLKS_TIMER_DIV4_CLOCK;
} else {
rc >>= 9;
cmr = AT91C_TC_CLKS_TIMER_DIV5_CLOCK;
}
gptp->tc->TC_CMR = (gptp->tc->TC_CMR & ~AT91C_TC_CLKS) | cmr;
gptp->tc->TC_RC = rc;
gptp->tc->TC_RA = rc/2;
} else {
gptp->tc->TC_RC = interval;
gptp->tc->TC_RA = interval/2;
}
}
/**
* @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) {
gpt_lld_change_interval(gptp, interval);
gptp->tc->TC_CMR |= AT91C_TC_CPCDIS;
gptp->tc->TC_CCR = AT91C_TC_CLKEN|AT91C_TC_SWTRG;
while (!(gptp->tc->TC_SR & (AT91C_TC_CPCS|AT91C_TC_COVFS)));
}
#endif /* HAL_USE_GPT */
/** @} */

View File

@@ -0,0 +1,230 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/gpt_lld.h
* @brief AT91SAM7 GPT subsystem low level driver header.
*
* @addtogroup GPT
* @{
*/
#ifndef _GPT_LLD_H_
#define _GPT_LLD_H_
#if HAL_USE_GPT || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief GPTD1 driver enable switch.
* @details If set to @p TRUE the support for GPTD1 is included.
* @note The default is @p TRUE.
*/
#if !defined(AT91_GPT_USE_TC0) || defined(__DOXYGEN__)
#define AT91_GPT_USE_TC0 FALSE
#endif
/**
* @brief GPTD2 driver enable switch.
* @details If set to @p TRUE the support for GPTD2 is included.
* @note The default is @p TRUE.
*/
#if !defined(AT91_GPT_USE_TC1) || defined(__DOXYGEN__)
#define AT91_GPT_USE_TC1 FALSE
#endif
/**
* @brief GPTD3 driver enable switch.
* @details If set to @p TRUE the support for GPTD3 is included.
* @note The default is @p TRUE.
*/
#if !defined(AT91_GPT_USE_TC2) || defined(__DOXYGEN__)
#define AT91_GPT_USE_TC3 FALSE
#endif
/**
* @brief GPTD1 interrupt priority level setting.
*/
#if !defined(AT91_GPT_TC0_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define AT91_GPT_TC0_IRQ_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
/**
* @brief GPTD2 interrupt priority level setting.
*/
#if !defined(AT91_GPT_TC1_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define AT91_GPT_TC1_IRQ_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
/**
* @brief GPTD3 interrupt priority level setting.
*/
#if !defined(AT91_GPT_TC2_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define AT91_GPT_TC2_IRQ_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if !AT91_GPT_USE_TC0 && !AT91_GPT_USE_TC1 && !AT91_GPT_USE_TC2
#error "GPT driver activated but no TC peripheral assigned"
#endif
/*===========================================================================*/
/* 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.*/
/**
* @brief Timer Clock Source.
*/
uint8_t clocksource;
#define GPT_CLOCK_MCLK 0 // @< Internal clock. frequency must = MCLK/2, MCLK/8, MCLK/32, MCLK/128 or MCLK/1024
#define GPT_CLOCK_FREQUENCY 1 // @< Internal clock. interval is ignored. frequency determines rate
#define GPT_CLOCK_RE_TCLK0 2 // @< External TCLK0. Rising Edge
#define GPT_CLOCK_FE_TCLK0 3 // @< External TCLK0. Falling Edge
#define GPT_CLOCK_RE_TCLK1 4 // @< External TCLK1. Rising Edge
#define GPT_CLOCK_FE_TCLK1 5 // @< External TCLK1. Falling Edge
#define GPT_CLOCK_RE_TCLK2 6 // @< External TCLK2. Rising Edge
#define GPT_CLOCK_FE_TCLK2 7 // @< External TCLK2. Falling Edge
#define GPT_CLOCK_RE_TC0 8 // @< TC0 output. Rising Edge. Do not use on TC0
#define GPT_CLOCK_FE_TC0 9 // @< TC0 output. Falling Edge. Do not use on TC0
#define GPT_CLOCK_RE_TC1 10 // @< TC1 output. Rising Edge. Do not use on TC1
#define GPT_CLOCK_FE_TC1 11 // @< TC1 output. Falling Edge. Do not use on TC1
#define GPT_CLOCK_RE_TC2 12 // @< TC2 output. Rising Edge. Do not use on TC2
#define GPT_CLOCK_FE_TC2 13 // @< TC2 output. Falling Edge. Do not use on TC2
uint8_t clockgate;
#define GPT_GATE_NONE 0 // @< Clock gating off
#define GPT_GATE_TCLK0 1 // @< Clock on TCLK0 active high signal. If using this on TC0 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_GATE_TCLK1 2 // @< Clock on TCLK1 active high signal. If using this on TC1 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_GATE_TCLK2 3 // @< Clock on TCLK2 active high signal. If using this on TC2 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
uint8_t trigger;
#define GPT_TRIGGER_NONE 0x00 // @< Start immediately
#define GPT_TRIGGER_RE_TIOB 0x10 // @< Start on TIOB signal. Rising Edge.
#define GPT_TRIGGER_FE_TIOB 0x20 // @< Start on TIOB signal. Falling Edge.
#define GPT_TRIGGER_BE_TIOB 0x30 // @< Start on TIOB signal. Both Edges.
#define GPT_TRIGGER_RE_TCLK0 0x11 // @< Start on TCLK0 signal. Rising Edge. If using this on TC0 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_FE_TCLK0 0x21 // @< Start on TCLK0 signal. Falling Edge. If using this on TC0 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_BE_TCLK0 0x31 // @< Start on TCLK0 signal. Both Edges. If using this on TC0 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_RE_TCLK1 0x12 // @< Start on TCLK1 signal. Rising Edge. If using this on TC1 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_FE_TCLK1 0x22 // @< Start on TCLK1 signal. Falling Edge. If using this on TC1 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_BE_TCLK1 0x32 // @< Start on TCLK1 signal. Both Edges. If using this on TC1 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_RE_TCLK2 0x13 // @< Start on TCLK2 signal. Rising Edge. If using this on TC2 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_FE_TCLK2 0x23 // @< Start on TCLK2 signal. Falling Edge. If using this on TC2 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
#define GPT_TRIGGER_BE_TCLK2 0x33 // @< Start on TCLK2 signal. Both Edges. If using this on TC2 with GPT_CLOCK_xx_TIMx, the TIMx output will be used instead.
} GPTConfig;
/**
* @brief Structure representing a GPT driver.
*/
struct GPTDriver {
/**
* @brief Driver state.
*/
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 Pointer to the TCx registers block.
*/
AT91S_TC *tc;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AT91_GPT_USE_TC0 && !defined(__DOXYGEN__)
extern GPTDriver GPTD1;
#endif
#if AT91_GPT_USE_TC1 && !defined(__DOXYGEN__)
extern GPTDriver GPTD2;
#endif
#if AT91_GPT_USE_TC2 && !defined(__DOXYGEN__)
extern GPTDriver GPTD3;
#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 period);
void gpt_lld_stop_timer(GPTDriver *gptp);
void gpt_lld_change_interval(GPTDriver *gptp, gptcnt_t interval);
void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_GPT */
#endif /* _GPT_LLD_H_ */
/** @} */

View File

@@ -0,0 +1,129 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/hal_lld.c
* @brief AT91SAM7 HAL subsystem low level driver source.
*
* @addtogroup HAL
* @{
*/
#include "ch.h"
#include "hal.h"
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
static CH_IRQ_HANDLER(spurious_handler) {
CH_IRQ_PROLOGUE();
AT91SAM7_SPURIOUS_HANDLER_HOOK();
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level HAL driver initialization.
*
* @notapi
*/
void hal_lld_init(void) {
unsigned i;
/* FIQ Handler weak symbol defined in vectors.s.*/
void FiqHandler(void);
/* Default AIC setup, the device drivers will modify it as needed.*/
AT91C_BASE_AIC->AIC_ICCR = 0xFFFFFFFF;
AT91C_BASE_AIC->AIC_IDCR = 0xFFFFFFFF;
AT91C_BASE_AIC->AIC_SVR[0] = (AT91_REG)FiqHandler;
for (i = 1; i < 31; i++) {
AT91C_BASE_AIC->AIC_SVR[i] = (AT91_REG)NULL;
AT91C_BASE_AIC->AIC_EOICR = (AT91_REG)i;
}
AT91C_BASE_AIC->AIC_SPU = (AT91_REG)spurious_handler;
}
/**
* @brief AT91SAM7 clocks and PLL initialization.
* @note All the involved constants come from the file @p board.h.
* @note This function must be invoked only after the system reset.
*
* @special
*/
void at91sam7_clock_init(void) {
/* wait for reset */
while((AT91C_BASE_RSTC->RSTC_RSR & (AT91C_RSTC_SRCMP | AT91C_RSTC_NRSTL)) != AT91C_RSTC_NRSTL)
;
/* enable reset */
AT91C_BASE_RSTC->RSTC_RMR = ((0xA5 << 24) | AT91C_RSTC_URSTEN);
/* Flash Memory: 1 wait state, about 50 cycles in a microsecond.*/
#if SAM7_PLATFORM == SAM7X512
AT91C_BASE_MC->MC0_FMR = (AT91C_MC_FMCN & (50 << 16)) | AT91C_MC_FWS_1FWS;
AT91C_BASE_MC->MC1_FMR = (AT91C_MC_FMCN & (50 << 16)) | AT91C_MC_FWS_1FWS;
#else
AT91C_BASE_MC->MC_FMR = (AT91C_MC_FMCN & (50 << 16)) | AT91C_MC_FWS_1FWS;
#endif
/* Enables the main oscillator and waits 56 slow cycles as startup time.*/
AT91C_BASE_PMC->PMC_MOR = (AT91C_CKGR_OSCOUNT & (7 << 8)) | AT91C_CKGR_MOSCEN;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS))
;
/* PLL setup: DIV = 14, MUL = 72, PLLCOUNT = 10
PLLfreq = 96109714 Hz (rounded).*/
AT91C_BASE_PMC->PMC_PLLR = (AT91C_CKGR_DIV & 14) |
(AT91C_CKGR_PLLCOUNT & (10 << 8)) |
(AT91SAM7_USBDIV) |
(AT91C_CKGR_MUL & (72 << 16));
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK))
;
/* Master clock = PLLfreq / 2 = 48054858 Hz (rounded).*/
AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY))
;
AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK;
while (!(AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY))
;
}
/** @} */

View File

@@ -0,0 +1,90 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/hal_lld.h
* @brief AT91SAM7 HAL subsystem low level driver header.
*
* @addtogroup HAL
* @{
*/
#ifndef _HAL_LLD_H_
#define _HAL_LLD_H_
#include "at91sam7.h"
#include "at91lib/aic.h"
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @brief Defines the support for realtime counters in the HAL.
*/
#define HAL_IMPLEMENTS_COUNTERS FALSE
/**
* @brief Platform name.
*/
#define PLATFORM_NAME "AT91SAM7x"
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief Default action for the spurious handler, nothing.
*/
#if !defined(AT91SAM7_SPURIOUS_HANDLER_HOOK) || defined(__DOXYGEN__)
#define AT91SAM7_SPURIOUS_HANDLER_HOOK()
#endif
/**
* @brief Default divider for the USB clock - half the PLL clock.
*/
#if !defined(AT91SAM7_USBDIV) || defined(__DOXYGEN__)
#define AT91SAM7_USBDIV AT91C_CKGR_USBDIV_1
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#ifdef __cplusplus
extern "C" {
#endif
void hal_lld_init(void);
void at91sam7_clock_init(void);
#ifdef __cplusplus
}
#endif
#endif /* _HAL_LLD_H_ */
/** @} */

View File

@@ -0,0 +1,464 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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.
*/
/*
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
aka barthess.
*/
/**
* @file AT91SAM7/i2c_lld.c
* @brief AT91SAM7 I2C subsystem low level driver source.
* @note I2C peripheral interrupts on AT91SAM7 platform must have highest
* priority in system.
*
* @addtogroup I2C
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_I2C || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/** @brief I2C1 driver identifier.*/
#if SAM7_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) { \
chSysLockFromIsr(); \
if ((i2cp)->thread != NULL) { \
Thread *tp = (i2cp)->thread; \
(i2cp)->thread = NULL; \
tp->p_u.rdymsg = (msg); \
chSchReadyI(tp); \
} \
chSysUnlockFromIsr(); \
}
/**
* @brief Helper function.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
static void _i2c_lld_serve_rx_interrupt(I2CDriver *i2cp){
if (i2cp->rxbytes == 1)
AT91C_BASE_TWI->TWI_CR |= AT91C_TWI_STOP;
*(i2cp->rxbuf) = AT91C_BASE_TWI->TWI_RHR;
i2cp->rxbuf++;
i2cp->rxbytes--;
if (i2cp->rxbytes == 0){
AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_RXRDY;
AT91C_BASE_TWI->TWI_IER = AT91C_TWI_TXCOMP;
}
}
/**
* @brief Helper function.
* @note During write operation you do not need to set STOP manually.
* It sets automatically when THR and shift registers becomes empty.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
static void _i2c_lld_serve_tx_interrupt(I2CDriver *i2cp){
if (i2cp->txbytes == 0){
AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_TXRDY;
AT91C_BASE_TWI->TWI_IER = AT91C_TWI_TXCOMP;
}
else{
AT91C_BASE_TWI->TWI_THR = *(i2cp->txbuf);
i2cp->txbuf++;
i2cp->txbytes--;
}
}
/**
* @brief I2C shared ISR code.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
static void i2c_lld_serve_interrupt(I2CDriver *i2cp) {
uint32_t sr;
sr = AT91C_BASE_TWI->TWI_SR;
/* this masking doing in official Atmel driver. Is it needed ??? */
sr &= AT91C_BASE_TWI->TWI_IMR;
if (sr & AT91C_TWI_NACK){
i2cp->errors |= I2CD_ACK_FAILURE;
wakeup_isr(i2cp, RDY_RESET);
return;
}
if (sr & AT91C_TWI_RXRDY){
_i2c_lld_serve_rx_interrupt(i2cp);
}
else if (sr & AT91C_TWI_TXRDY){
_i2c_lld_serve_tx_interrupt(i2cp);
}
else if (sr & AT91C_TWI_TXCOMP){
AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_TXCOMP;
wakeup_isr(i2cp, RDY_OK);
}
else
chDbgPanic("Invalid value");
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if SAM7_I2C_USE_I2C1 || defined(__DOXYGEN__)
/**
* @brief I2C1 event interrupt handler.
*
* @notapi
*/
CH_IRQ_HANDLER(TWI_IRQHandler) {
CH_IRQ_PROLOGUE();
i2c_lld_serve_interrupt(&I2CD1);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif /* STM32_I2C_USE_I2C1 */
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level I2C driver initialization.
*
* @notapi
*/
void i2c_lld_init(void) {
#if SAM7_I2C_USE_I2C1
i2cObjectInit(&I2CD1);
I2CD1.thread = NULL;
I2CD1.txbuf = NULL;
I2CD1.rxbuf = NULL;
I2CD1.txbytes = 0;
I2CD1.rxbytes = 0;
#if SAM7_PLATFORM == SAM7S512 || SAM7_PLATFORM == SAM7S256 || SAM7_PLATFORM == SAM7S128 || SAM7_PLATFORM == SAM7S64
AT91C_BASE_PIOA->PIO_PDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
AT91C_BASE_PIOA->PIO_ASR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
AT91C_BASE_PIOA->PIO_MDER = AT91C_PA3_TWD | AT91C_PA4_TWCK;
AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
#elif SAM7_PLATFORM == SAM7X512 || SAM7_PLATFORM == SAM7X256 || SAM7_PLATFORM == SAM7X128
AT91C_BASE_PIOA->PIO_PDR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
AT91C_BASE_PIOA->PIO_ASR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
AT91C_BASE_PIOA->PIO_MDER = AT91C_PA10_TWD | AT91C_PA11_TWCK;
AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA10_TWD | AT91C_PA11_TWCK;
#elif SAM7_PLATFORM == SAM7A3
AT91C_BASE_PIOA->PIO_PDR = AT91C_PA0_TWD | AT91C_PA1_TWCK;
AT91C_BASE_PIOA->PIO_ASR = AT91C_PA0_TWD | AT91C_PA1_TWCK;
AT91C_BASE_PIOA->PIO_MDER = AT91C_PA0_TWD | AT91C_PA1_TWCK;
AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA0_TWD | AT91C_PA1_TWCK;
#else
#error "SAM7 platform not supported"
#endif
AIC_ConfigureIT(AT91C_ID_TWI,
AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | SAM7_I2C_I2C1_IRQ_PRIORITY,
TWI_IRQHandler);
#endif /* STM32_I2C_USE_I2C1 */
}
/**
* @brief Configures and activates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
void i2c_lld_start(I2CDriver *i2cp) {
volatile uint32_t fake;
/* If in stopped state then enables the I2C clocks.*/
if (i2cp->state == I2C_STOP) {
#if SAM7_I2C_USE_I2C1
if (&I2CD1 == i2cp) {
/* enable peripheral clock */
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_TWI);
/* Enables associated interrupt vector.*/
AIC_EnableIT(AT91C_ID_TWI);
/* Reset */
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_SWRST;
fake = AT91C_BASE_TWI->TWI_RHR;
/* Set master mode */
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_MSDIS;
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_MSEN;
/* Setup I2C parameters. */
AT91C_BASE_TWI->TWI_CWGR = i2cp->config->cwgr;
}
#endif /* STM32_I2C_USE_I2C1 */
}
(void)fake;
}
/**
* @brief Deactivates the I2C peripheral.
*
* @param[in] i2cp pointer to the @p I2CDriver object
*
* @notapi
*/
void i2c_lld_stop(I2CDriver *i2cp) {
/* If not in stopped state then disables the I2C clock.*/
if (i2cp->state != I2C_STOP) {
#if SAM7_I2C_USE_I2C1
if (&I2CD1 == i2cp) {
AT91C_BASE_TWI->TWI_IDR = AT91C_TWI_TXCOMP | AT91C_TWI_RXRDY |
AT91C_TWI_TXRDY | AT91C_TWI_NACK;
AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_TWI);
AIC_DisableIT(AT91C_ID_TWI);
}
#endif
}
}
/**
* @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 this value is ignored on SAM7 platform.
*
* @return The operation status.
* @retval RDY_OK if the function succeeded.
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
*
* @notapi
*/
msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout) {
(void)timeout;
/* delete trash from RHR*/
volatile uint32_t fake;
fake = AT91C_BASE_TWI->TWI_RHR;
(void)fake;
/* Initializes driver fields.*/
i2cp->rxbuf = rxbuf;
i2cp->rxbytes = rxbytes;
i2cp->txbuf = NULL;
i2cp->txbytes = 0;
/* tune master mode register */
AT91C_BASE_TWI->TWI_MMR = 0;
AT91C_BASE_TWI->TWI_MMR |= (addr << 16) | AT91C_TWI_MREAD;
/* enable just needed interrupts */
AT91C_BASE_TWI->TWI_IER = AT91C_TWI_RXRDY | AT91C_TWI_NACK;
/* In single data byte master read or write, the START and STOP must both be set. */
if (rxbytes == 1)
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP | AT91C_TWI_START;
else
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START;
/* Waits for the operation completion.*/
i2cp->thread = chThdSelf();
chSchGoSleepS(THD_STATE_SUSPENDED);
return chThdSelf()->p_u.rdymsg;
}
/**
* @brief Read data via the I2C bus as master using internal slave addressing.
* @details Address bytes must be written in special purpose SAM7 registers.
*
* @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 this value is ignored on SAM7 platform.
*
* @return The operation status.
* @retval RDY_OK if the function succeeded.
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
*
* @notapi
*/
msg_t i2c_lld_transceive_timeout(I2CDriver *i2cp, i2caddr_t addr,
const uint8_t *txbuf, size_t txbytes,
uint8_t *rxbuf, size_t rxbytes,
systime_t timeout) {
(void)timeout;
/* delete trash from RHR*/
volatile uint32_t fake;
fake = AT91C_BASE_TWI->TWI_RHR;
(void)fake;
/* Initializes driver fields.*/
i2cp->rxbuf = rxbuf;
i2cp->rxbytes = rxbytes;
/* tune master mode register */
AT91C_BASE_TWI->TWI_MMR = 0;
AT91C_BASE_TWI->TWI_MMR |= (addr << 16) | (txbytes << 8) | AT91C_TWI_MREAD;
/* store internal slave address in TWI_IADR registers */
AT91C_BASE_TWI->TWI_IADR = 0;
while (txbytes > 0){
AT91C_BASE_TWI->TWI_IADR = (AT91C_BASE_TWI->TWI_IADR << 8);
AT91C_BASE_TWI->TWI_IADR |= *(txbuf++);
txbytes--;
}
/* enable just needed interrupts */
AT91C_BASE_TWI->TWI_IER = AT91C_TWI_RXRDY | AT91C_TWI_NACK;
/* Internal address of I2C slave was set in special Atmel registers.
* Now we must call read function. The I2C cell automatically sends
* bytes from IADR register to bus and issues repeated start. */
if (rxbytes == 1)
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP | AT91C_TWI_START;
else
AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START;
/* Waits for the operation completion.*/
i2cp->thread = chThdSelf();
chSchGoSleepS(THD_STATE_SUSPENDED);
return chThdSelf()->p_u.rdymsg;
}
/**
* @brief Transmits data via the I2C bus as master.
* @details When performing reading through write you can not write more than
* 3 bytes of data to I2C slave. This is SAM7 platform limitation.
*
* @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 this value is ignored on SAM7 platform.
*
* @return The operation status.
* @retval RDY_OK if the function succeeded.
* @retval RDY_RESET if one or more I2C errors occurred, the errors can
* be retrieved using @p i2cGetErrors().
*
* @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) {
(void)timeout;
/* SAM7 specific check */
chDbgCheck(((rxbytes == 0) ||
((txbytes > 0) && (txbytes < 4) && (rxbuf != NULL))),
"i2c_lld_master_transmit_timeout");
/* prepare to read through write operation */
if (rxbytes > 0){
return i2c_lld_transceive_timeout(i2cp, addr, txbuf, txbytes, rxbuf,
rxbytes, timeout);
}
else{
if (txbytes == 1){
/* In single data byte master read or write, the START and STOP
* must both be set. */
AT91C_BASE_TWI->TWI_CR |= AT91C_TWI_STOP;
}
AT91C_BASE_TWI->TWI_MMR = 0;
AT91C_BASE_TWI->TWI_MMR |= addr << 16;
/* enable just needed interrupts */
AT91C_BASE_TWI->TWI_IER = AT91C_TWI_TXRDY | AT91C_TWI_NACK;
/* correct size and pointer because first byte will be written
* for issue start condition */
i2cp->txbuf = txbuf + 1;
i2cp->txbytes = txbytes - 1;
/* According to datasheet there is no need to set START manually
* we just need to write first byte in THR */
AT91C_BASE_TWI->TWI_THR = txbuf[0];
/* Waits for the operation completion.*/
i2cp->thread = chThdSelf();
chSchGoSleepS(THD_STATE_SUSPENDED);
return chThdSelf()->p_u.rdymsg;
}
}
#endif /* HAL_USE_I2C */
/** @} */

View File

@@ -0,0 +1,204 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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.
*/
/*
Concepts and parts of this file have been contributed by Uladzimir Pylinsky
aka barthess.
*/
/**
* @file AT91SAM7/i2c_lld.h
* @brief AT91SAM7 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 Peripheral clock frequency.
*/
#define I2C_CLK_FREQ ((STM32_PCLK1) / 1000000)
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @name Configuration options
* @{
*/
/**
* @brief I2C1 driver enable switch.
* @details If set to @p TRUE the support for I2C1 is included.
* @note The default is @p FALSE.
*/
#if !defined(SAM7_I2C_USE_I2C1) || defined(__DOXYGEN__)
#define SAM7_I2C_USE_I2C1 FALSE
#endif
/**
* @brief I2C1 interrupt priority level setting.
*/
#if !defined(SAM7_I2C_I2C1_IRQ_PRIORITY) || defined(__DOXYGEN__)
#define SAM7_I2C_I2C1_IRQ_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
/** @} */
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/** @brief error checks */
#if !SAM7_I2C_USE_I2C1
#error "I2C driver activated but no I2C peripheral assigned"
#endif
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Type representing I2C address.
*/
typedef uint16_t i2caddr_t;
/**
* @brief I2C Driver condition flags type.
*/
typedef uint32_t i2cflags_t;
/**
* @brief Driver configuration structure.
*/
typedef struct {
/**
* @brief CWGR regitster content.
*/
uint32_t cwgr;
} I2CConfig;
/**
* @brief Type of a structure representing an I2C driver.
*/
typedef struct I2CDriver I2CDriver;
/**
* @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;
/**
* @brief Pointer to receive buffer.
*/
uint8_t *rxbuf;
/**
* @brief Pointer to transmit buffer.
*/
const uint8_t *txbuf;
/**
* @brief Bytes count to be received.
*/
size_t rxbytes;
/**
* @brief Bytes count to be transmitted.
*/
size_t txbytes;
#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;
};
/*===========================================================================*/
/* 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 SAM7_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_ */
/** @} */

View File

@@ -0,0 +1,551 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/mac_lld.c
* @brief AT91SAM7 low level MAC driver code.
*
* @addtogroup MAC
* @{
*/
#include <string.h>
#include "ch.h"
#include "hal.h"
#include "mii.h"
#include "at91sam7_mii.h"
#if HAL_USE_MAC || defined(__DOXYGEN__)
#define EMAC_PIN_MASK (AT91C_PB0_ETXCK_EREFCK | AT91C_PB1_ETXEN | \
AT91C_PB2_ETX0 | AT91C_PB3_ETX1 | \
AT91C_PB4_ECRS | AT91C_PB5_ERX0 | \
AT91C_PB6_ERX1 | AT91C_PB7_ERXER | \
AT91C_PB8_EMDC | AT91C_PB9_EMDIO | \
AT91C_PB10_ETX2 | AT91C_PB11_ETX3 | \
AT91C_PB12_ETXER | AT91C_PB13_ERX2 | \
AT91C_PB14_ERX3 | AT91C_PB15_ERXDV_ECRSDV | \
AT91C_PB16_ECOL | AT91C_PB17_ERXCK)
#define RSR_BITS (AT91C_EMAC_BNA | AT91C_EMAC_REC | AT91C_EMAC_OVR)
#define TSR_BITS (AT91C_EMAC_UBR | AT91C_EMAC_COL | AT91C_EMAC_RLES | \
AT91C_EMAC_BEX | AT91C_EMAC_COMP | AT91C_EMAC_UND)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief Ethernet driver 1.
*/
MACDriver ETHD1;
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
#ifndef __DOXYGEN__
static uint8_t default_mac[] = {0xAA, 0x55, 0x13, 0x37, 0x01, 0x10};
static EMACDescriptor *rxptr;
static EMACDescriptor *txptr;
static EMACDescriptor rd[EMAC_RECEIVE_DESCRIPTORS]
__attribute__((aligned(8)));
static EMACDescriptor td[EMAC_TRANSMIT_DESCRIPTORS]
__attribute__((aligned(8)));
static uint8_t rb[EMAC_RECEIVE_DESCRIPTORS * EMAC_RECEIVE_BUFFERS_SIZE]
__attribute__((aligned(8)));
static uint8_t tb[EMAC_TRANSMIT_DESCRIPTORS * EMAC_TRANSMIT_BUFFERS_SIZE]
__attribute__((aligned(8)));
#endif
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief IRQ handler.
*/
/** @cond never*/
__attribute__((noinline))
/** @endcond*/
static void serve_interrupt(void) {
uint32_t isr, rsr, tsr;
/* Fix for the EMAC errata */
isr = AT91C_BASE_EMAC->EMAC_ISR;
rsr = AT91C_BASE_EMAC->EMAC_RSR;
tsr = AT91C_BASE_EMAC->EMAC_TSR;
if ((isr & AT91C_EMAC_RCOMP) || (rsr & RSR_BITS)) {
if (rsr & AT91C_EMAC_REC) {
chSysLockFromIsr();
chSemResetI(&ETHD1.rdsem, 0);
#if MAC_USE_EVENTS
chEvtBroadcastI(&ETHD1.rdevent);
#endif
chSysUnlockFromIsr();
}
AT91C_BASE_EMAC->EMAC_RSR = RSR_BITS;
}
if ((isr & AT91C_EMAC_TCOMP) || (tsr & TSR_BITS)) {
if (tsr & AT91C_EMAC_COMP) {
chSysLockFromIsr();
chSemResetI(&ETHD1.tdsem, 0);
chSysUnlockFromIsr();
}
AT91C_BASE_EMAC->EMAC_TSR = TSR_BITS;
}
AT91C_BASE_AIC->AIC_EOICR = 0;
}
/**
* @brief Cleans an incomplete frame.
*
* @param[in] from the start position of the incomplete frame
*/
static void cleanup(EMACDescriptor *from) {
while (from != rxptr) {
from->w1 &= ~W1_R_OWNERSHIP;
if (++from >= &rd[EMAC_RECEIVE_DESCRIPTORS])
from = rd;
}
}
/**
* @brief MAC address setup.
*
* @param[in] p pointer to a six bytes buffer containing the MAC
* address
*/
static void set_address(const uint8_t *p) {
AT91C_BASE_EMAC->EMAC_SA1L = (AT91_REG)((p[3] << 24) | (p[2] << 16) |
(p[1] << 8) | p[0]);
AT91C_BASE_EMAC->EMAC_SA1H = (AT91_REG)((p[5] << 8) | p[4]);
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
/**
* @brief EMAC IRQ handler.
*
* @isr
*/
CH_IRQ_HANDLER(irq_handler) {
CH_IRQ_PROLOGUE();
serve_interrupt();
CH_IRQ_EPILOGUE();
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level MAC initialization.
*
* @notapi
*/
void mac_lld_init(void) {
miiInit();
macObjectInit(&ETHD1);
/*
* Associated PHY initialization.
*/
miiReset(&ETHD1);
/*
* EMAC pins setup. Note, PB18 is not included because it is
* used as #PD control and not as EF100.
*/
AT91C_BASE_PIOB->PIO_ASR = EMAC_PIN_MASK;
AT91C_BASE_PIOB->PIO_PDR = EMAC_PIN_MASK;
AT91C_BASE_PIOB->PIO_PPUDR = EMAC_PIN_MASK;
}
/**
* @brief Configures and activates the MAC peripheral.
*
* @param[in] macp pointer to the @p MACDriver object
*
* @notapi
*/
void mac_lld_start(MACDriver *macp) {
unsigned i;
/*
* Buffers initialization.
*/
for (i = 0; i < EMAC_RECEIVE_DESCRIPTORS; i++) {
rd[i].w1 = (uint32_t)&rb[i * EMAC_RECEIVE_BUFFERS_SIZE];
rd[i].w2 = 0;
}
rd[EMAC_RECEIVE_DESCRIPTORS - 1].w1 |= W1_R_WRAP;
rxptr = rd;
for (i = 0; i < EMAC_TRANSMIT_DESCRIPTORS; i++) {
td[i].w1 = (uint32_t)&tb[i * EMAC_TRANSMIT_BUFFERS_SIZE];
td[i].w2 = EMAC_TRANSMIT_BUFFERS_SIZE | W2_T_LAST_BUFFER | W2_T_USED;
}
td[EMAC_TRANSMIT_DESCRIPTORS - 1].w2 |= W2_T_WRAP;
txptr = td;
/*
* EMAC clock enable.
*/
AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_EMAC;
/*
* EMAC Initial setup.
*/
AT91C_BASE_EMAC->EMAC_NCR = 0; /* Stopped but MCE active.*/
AT91C_BASE_EMAC->EMAC_NCFGR = 2 << 10; /* MDC-CLK = MCK / 32 */
AT91C_BASE_EMAC->EMAC_USRIO = AT91C_EMAC_CLKEN;/* Enable EMAC in MII mode.*/
AT91C_BASE_EMAC->EMAC_RBQP = (AT91_REG)rd; /* RX descriptors list.*/
AT91C_BASE_EMAC->EMAC_TBQP = (AT91_REG)td; /* TX descriptors list.*/
AT91C_BASE_EMAC->EMAC_RSR = AT91C_EMAC_OVR |
AT91C_EMAC_REC |
AT91C_EMAC_BNA; /* Clears RSR.*/
AT91C_BASE_EMAC->EMAC_NCFGR |= AT91C_EMAC_DRFCS;/* Initial NCFGR settings.*/
AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TE |
AT91C_EMAC_RE |
AT91C_EMAC_CLRSTAT;/* Initial NCR settings.*/
if (macp->config->mac_address == NULL)
set_address(default_mac);
else
set_address(macp->config->mac_address);
/*
* PHY device identification.
*/
AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE;
if ((miiGet(&ETHD1, MII_PHYSID1) != (PHY_ID >> 16)) ||
((miiGet(&ETHD1, MII_PHYSID2) & 0xFFF0) != (PHY_ID & 0xFFF0)))
chSysHalt();
AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE;
/*
* Interrupt configuration.
*/
AT91C_BASE_EMAC->EMAC_IER = AT91C_EMAC_RCOMP | AT91C_EMAC_TCOMP;
AIC_ConfigureIT(AT91C_ID_EMAC,
AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL | EMAC_INTERRUPT_PRIORITY,
irq_handler);
AIC_EnableIT(AT91C_ID_EMAC);
}
/**
* @brief Deactivates the MAC peripheral.
*
* @param[in] macp pointer to the @p MACDriver object
*
* @notapi
*/
void mac_lld_stop(MACDriver *macp) {
(void)macp;
}
/**
* @brief Returns a transmission descriptor.
* @details One of the available transmission descriptors is locked and
* returned.
*
* @param[in] macp pointer to the @p MACDriver object
* @param[out] tdp pointer to a @p MACTransmitDescriptor structure
* @return The operation status.
* @retval RDY_OK the descriptor has been obtained.
* @retval RDY_TIMEOUT descriptor not available.
*
* @notapi
*/
msg_t mac_lld_get_transmit_descriptor(MACDriver *macp,
MACTransmitDescriptor *tdp) {
EMACDescriptor *edp;
(void)macp;
if (!macp->link_up)
return RDY_TIMEOUT;
chSysLock();
edp = txptr;
if (!(edp->w2 & W2_T_USED) || (edp->w2 & W2_T_LOCKED)) {
chSysUnlock();
return RDY_TIMEOUT;
}
/*
* Set the buffer size and configuration, the buffer is also marked
* as locked.
*/
if (++txptr >= &td[EMAC_TRANSMIT_DESCRIPTORS]) {
edp->w2 = W2_T_LOCKED | W2_T_USED | W2_T_LAST_BUFFER | W2_T_WRAP;
txptr = td;
}
else
edp->w2 = W2_T_LOCKED | W2_T_USED | W2_T_LAST_BUFFER;
chSysUnlock();
tdp->offset = 0;
tdp->size = EMAC_TRANSMIT_BUFFERS_SIZE;
tdp->physdesc = edp;
return RDY_OK;
}
/**
* @brief Writes to a transmit descriptor's stream.
*
* @param[in] tdp pointer to a @p MACTransmitDescriptor structure
* @param[in] buf pointer to the buffer containing the data to be
* written
* @param[in] size number of bytes to be written
* @return The number of bytes written into the descriptor's
* stream, this value can be less than the amount
* specified in the parameter @p size if the maximum
* frame size is reached.
*
* @notapi
*/
size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp,
uint8_t *buf,
size_t size) {
if (size > tdp->size - tdp->offset)
size = tdp->size - tdp->offset;
if (size > 0) {
memcpy((uint8_t *)(tdp->physdesc->w1 & W1_T_ADDRESS_MASK) +
tdp->offset,
buf, size);
tdp->offset += size;
}
return size;
}
/**
* @brief Releases a transmit descriptor and starts the transmission of the
* enqueued data as a single frame.
*
* @param[in] tdp the pointer to the @p MACTransmitDescriptor structure
*
* @notapi
*/
void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp) {
chSysLock();
tdp->physdesc->w2 = (tdp->physdesc->w2 &
~(W2_T_LOCKED | W2_T_USED | W2_T_LENGTH_MASK)) |
tdp->offset;
AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_TSTART;
chSysUnlock();
}
/**
* @brief Returns a receive descriptor.
*
* @param[in] macp pointer to the @p MACDriver object
* @param[out] rdp pointer to a @p MACReceiveDescriptor structure
* @return The operation status.
* @retval RDY_OK the descriptor has been obtained.
* @retval RDY_TIMEOUT descriptor not available.
*
* @notapi
*/
msg_t mac_lld_get_receive_descriptor(MACDriver *macp,
MACReceiveDescriptor *rdp) {
unsigned n;
EMACDescriptor *edp;
(void)macp;
n = EMAC_RECEIVE_DESCRIPTORS;
/*
* Skips unused buffers, if any.
*/
skip:
while ((n > 0) && !(rxptr->w1 & W1_R_OWNERSHIP)) {
if (++rxptr >= &rd[EMAC_RECEIVE_DESCRIPTORS])
rxptr = rd;
n--;
}
/*
* Skips fragments, if any, cleaning them up.
*/
while ((n > 0) && (rxptr->w1 & W1_R_OWNERSHIP) &&
!(rxptr->w2 & W2_R_FRAME_START)) {
rxptr->w1 &= ~W1_R_OWNERSHIP;
if (++rxptr >= &rd[EMAC_RECEIVE_DESCRIPTORS])
rxptr = rd;
n--;
}
/*
* Now compute the total frame size skipping eventual incomplete frames
* or holes...
*/
restart:
edp = rxptr;
while (n > 0) {
if (!(rxptr->w1 & W1_R_OWNERSHIP)) {
/* Empty buffer for some reason... cleaning up the incomplete frame.*/
cleanup(edp);
goto skip;
}
/*
* End Of Frame found.
*/
if (rxptr->w2 & W2_R_FRAME_END) {
rdp->offset = 0;
rdp->size = rxptr->w2 & W2_T_LENGTH_MASK;
rdp->physdesc = edp;
return RDY_OK;
}
if ((edp != rxptr) && (rxptr->w2 & W2_R_FRAME_START)) {
/* Found another start... cleaning up the incomplete frame.*/
cleanup(edp);
goto restart; /* Another start buffer for some reason... */
}
if (++rxptr >= &rd[EMAC_RECEIVE_DESCRIPTORS])
rxptr = rd;
n--;
}
return RDY_TIMEOUT;
}
/**
* @brief Reads from a receive descriptor's stream.
*
* @param[in] rdp pointer to a @p MACReceiveDescriptor structure
* @param[in] buf pointer to the buffer that will receive the read data
* @param[in] size number of bytes to be read
* @return The number of bytes read from the descriptor's
* stream, this value can be less than the amount
* specified in the parameter @p size if there are
* no more bytes to read.
*
* @notapi
*/
size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp,
uint8_t *buf,
size_t size) {
if (size > rdp->size - rdp->offset)
size = rdp->size - rdp->offset;
if (size > 0) {
uint8_t *src = (uint8_t *)(rdp->physdesc->w1 & W1_R_ADDRESS_MASK) +
rdp->offset;
uint8_t *limit = &rb[EMAC_RECEIVE_DESCRIPTORS * EMAC_RECEIVE_BUFFERS_SIZE];
if (src >= limit)
src -= EMAC_RECEIVE_DESCRIPTORS * EMAC_RECEIVE_BUFFERS_SIZE;
if (src + size > limit ) {
memcpy(buf, src, (size_t)(limit - src));
memcpy(buf + (size_t)(limit - src), rb, size - (size_t)(limit - src));
}
else
memcpy(buf, src, size);
rdp->offset += size;
}
return size;
}
/**
* @brief Releases a receive descriptor.
* @details The descriptor and its buffer are made available for more incoming
* frames.
*
* @param[in] rdp the pointer to the @p MACReceiveDescriptor structure
*
* @notapi
*/
void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp) {
bool_t done;
EMACDescriptor *edp = rdp->physdesc;
unsigned n = EMAC_RECEIVE_DESCRIPTORS;
do {
done = ((edp->w2 & W2_R_FRAME_END) != 0);
chDbgAssert(edp->w1 & W1_R_OWNERSHIP,
"mac_lld_release_receive_descriptor(), #1",
"found not owned descriptor");
edp->w1 &= ~(W1_R_OWNERSHIP | W2_R_FRAME_START | W2_R_FRAME_END);
if (++edp >= &rd[EMAC_RECEIVE_DESCRIPTORS])
edp = rd;
n--;
}
while ((n > 0) && !done);
/*
* Make rxptr point to the descriptor where the next frame will most
* likely appear.
*/
rxptr = edp;
}
/**
* @brief Updates and returns the link status.
*
* @param[in] macp pointer to the @p MACDriver object
* @return The link status.
* @retval TRUE if the link is active.
* @retval FALSE if the link is down.
*
* @notapi
*/
bool_t mac_lld_poll_link_status(MACDriver *macp) {
uint32_t ncfgr, bmsr, bmcr, lpa;
AT91C_BASE_EMAC->EMAC_NCR |= AT91C_EMAC_MPE;
(void)miiGet(macp, MII_BMSR);
bmsr = miiGet(macp, MII_BMSR);
if (!(bmsr & BMSR_LSTATUS)) {
AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE;
return macp->link_up = FALSE;
}
ncfgr = AT91C_BASE_EMAC->EMAC_NCFGR & ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
bmcr = miiGet(macp, MII_BMCR);
if (bmcr & BMCR_ANENABLE) {
lpa = miiGet(macp, MII_LPA);
if (lpa & (LPA_100HALF | LPA_100FULL | LPA_100BASE4))
ncfgr |= AT91C_EMAC_SPD;
if (lpa & (LPA_10FULL | LPA_100FULL))
ncfgr |= AT91C_EMAC_FD;
}
else {
if (bmcr & BMCR_SPEED100)
ncfgr |= AT91C_EMAC_SPD;
if (bmcr & BMCR_FULLDPLX)
ncfgr |= AT91C_EMAC_FD;
}
AT91C_BASE_EMAC->EMAC_NCFGR = ncfgr;
AT91C_BASE_EMAC->EMAC_NCR &= ~AT91C_EMAC_MPE;
return macp->link_up = TRUE;
}
#endif /* HAL_USE_MAC */
/** @} */

View File

@@ -0,0 +1,252 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/mac_lld.h
* @brief AT91SAM7 low level MAC driver header.
*
* @addtogroup MAC
* @{
*/
#ifndef _MAC_LLD_H_
#define _MAC_LLD_H_
#if HAL_USE_MAC || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @brief This implementation does not support the zero-copy mode API.
*/
#define MAC_SUPPORTS_ZERO_COPY FALSE
#define EMAC_RECEIVE_BUFFERS_SIZE 128 /* Do not modify */
#define EMAC_TRANSMIT_BUFFERS_SIZE MAC_BUFFERS_SIZE
#define EMAC_RECEIVE_DESCRIPTORS \
(((((MAC_BUFFERS_SIZE - 1) | (EMAC_RECEIVE_BUFFERS_SIZE - 1)) + 1) \
/ EMAC_RECEIVE_BUFFERS_SIZE) * MAC_RECEIVE_BUFFERS)
#define EMAC_TRANSMIT_DESCRIPTORS MAC_TRANSMIT_BUFFERS
#define W1_R_OWNERSHIP 0x00000001
#define W1_R_WRAP 0x00000002
#define W1_R_ADDRESS_MASK 0xFFFFFFFC
#define W2_R_LENGTH_MASK 0x00000FFF
#define W2_R_FRAME_START 0x00004000
#define W2_R_FRAME_END 0x00008000
#define W2_R_CFI 0x00010000
#define W2_R_VLAN_PRIO_MASK 0x000E0000
#define W2_R_PRIO_TAG_DETECTED 0x00100000
#define W2_R_VLAN_TAG_DETECTED 0x00200000
#define W2_R_TYPE_ID_MATCH 0x00400000
#define W2_R_ADDR4_MATCH 0x00800000
#define W2_R_ADDR3_MATCH 0x01000000
#define W2_R_ADDR2_MATCH 0x02000000
#define W2_R_ADDR1_MATCH 0x04000000
#define W2_R_RFU1 0x08000000
#define W2_R_ADDR_EXT_MATCH 0x10000000
#define W2_R_UNICAST_MATCH 0x20000000
#define W2_R_MULTICAST_MATCH 0x40000000
#define W2_R_BROADCAST_DETECTED 0x80000000
#define W1_T_ADDRESS_MASK 0xFFFFFFFF
#define W2_T_LENGTH_MASK 0x000007FF
#define W2_T_LOCKED 0x00000800 /* Not an EMAC flag. */
#define W2_T_RFU1 0x00003000
#define W2_T_LAST_BUFFER 0x00008000
#define W2_T_NO_CRC 0x00010000
#define W2_T_RFU2 0x07FE0000
#define W2_T_BUFFERS_EXHAUSTED 0x08000000
#define W2_T_TRANSMIT_UNDERRUN 0x10000000
#define W2_T_RETRY_LIMIT_EXC 0x20000000
#define W2_T_WRAP 0x40000000
#define W2_T_USED 0x80000000
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief Number of available transmit buffers.
*/
#if !defined(MAC_TRANSMIT_BUFFERS) || defined(__DOXYGEN__)
#define MAC_TRANSMIT_BUFFERS 2
#endif
/**
* @brief Number of available receive buffers.
*/
#if !defined(MAC_RECEIVE_BUFFERS) || defined(__DOXYGEN__)
#define MAC_RECEIVE_BUFFERS 2
#endif
/**
* @brief Maximum supported frame size.
*/
#if !defined(MAC_BUFFERS_SIZE) || defined(__DOXYGEN__)
#define MAC_BUFFERS_SIZE 1518
#endif
/**
* @brief Interrupt priority level for the EMAC device.
*/
#if !defined(EMAC_INTERRUPT_PRIORITY) || defined(__DOXYGEN__)
#define EMAC_INTERRUPT_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 3)
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief Structure representing a buffer physical descriptor.
* @note It represents both descriptor types.
*/
typedef struct {
uint32_t w1;
uint32_t w2;
} EMACDescriptor;
/**
* @brief Driver configuration structure.
*/
typedef struct {
/**
* @brief MAC address.
*/
const uint8_t *mac_address;
/* End of the mandatory fields.*/
} MACConfig;
/**
* @brief Structure representing a MAC driver.
*/
struct MACDriver {
/**
* @brief Driver state.
*/
macstate_t state;
/**
* @brief Current configuration data.
*/
const MACConfig *config;
/**
* @brief Transmit semaphore.
*/
Semaphore tdsem;
/**
* @brief Receive semaphore.
*/
Semaphore rdsem;
#if MAC_USE_EVENTS || defined(__DOXYGEN__)
/**
* @brief Receive event.
*/
EventSource rdevent;
#endif
/* End of the mandatory fields.*/
/**
* @brief Link status flag.
*/
bool_t link_up;
};
/**
* @brief Structure representing a transmit descriptor.
*/
typedef struct {
/**
* @brief Current write offset.
*/
size_t offset;
/**
* @brief Available space size.
*/
size_t size;
/* End of the mandatory fields.*/
/**
* @brief Pointer to the physical descriptor.
*/
EMACDescriptor *physdesc;
} MACTransmitDescriptor;
/**
* @brief Structure representing a receive descriptor.
*/
typedef struct {
/**
* @brief Current read offset.
*/
size_t offset;
/**
* @brief Available data size.
*/
size_t size;
/* End of the mandatory fields.*/
/**
* @brief Pointer to the first descriptor of the buffers chain.
*/
EMACDescriptor *physdesc;
} MACReceiveDescriptor;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if !defined(__DOXYGEN__)
extern MACDriver ETHD1;
#endif
#ifdef __cplusplus
extern "C" {
#endif
void mac_lld_init(void);
void mac_lld_start(MACDriver *macp);
void mac_lld_stop(MACDriver *macp);
msg_t mac_lld_get_transmit_descriptor(MACDriver *macp,
MACTransmitDescriptor *tdp);
size_t mac_lld_write_transmit_descriptor(MACTransmitDescriptor *tdp,
uint8_t *buf,
size_t size);
void mac_lld_release_transmit_descriptor(MACTransmitDescriptor *tdp);
msg_t mac_lld_get_receive_descriptor(MACDriver *macp,
MACReceiveDescriptor *rdp);
size_t mac_lld_read_receive_descriptor(MACReceiveDescriptor *rdp,
uint8_t *buf,
size_t size);
void mac_lld_release_receive_descriptor(MACReceiveDescriptor *rdp);
bool_t mac_lld_poll_link_status(MACDriver *macp);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_MAC */
#endif /* _MAC_LLD_H_ */
/** @} */

View File

@@ -0,0 +1,159 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/pal_lld.c
* @brief AT91SAM7 PIO low level driver code.
*
* @addtogroup PAL
* @{
*/
#include "ch.h"
#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 AT91SAM7 I/O ports configuration.
* @details PIO registers initialization.
*
* @param[in] config the AT91SAM7 ports configuration
*
* @notapi
*/
void _pal_lld_init(const PALConfig *config) {
uint32_t ports = (1 << AT91C_ID_PIOA);
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3)
ports |= (1 << AT91C_ID_PIOB);
#endif
AT91C_BASE_PMC->PMC_PCER = ports;
/*
* PIOA setup.
*/
AT91C_BASE_PIOA->PIO_PPUER = config->P0Data.pusr; /* Pull-up as spec.*/
AT91C_BASE_PIOA->PIO_PPUDR = ~config->P0Data.pusr;
AT91C_BASE_PIOA->PIO_PER = 0xFFFFFFFF; /* PIO enabled.*/
AT91C_BASE_PIOA->PIO_ODSR = config->P0Data.odsr; /* Data as specified.*/
AT91C_BASE_PIOA->PIO_OER = config->P0Data.osr; /* Dir. as specified.*/
AT91C_BASE_PIOA->PIO_ODR = ~config->P0Data.osr;
AT91C_BASE_PIOA->PIO_IFDR = 0xFFFFFFFF; /* Filter disabled.*/
AT91C_BASE_PIOA->PIO_IDR = 0xFFFFFFFF; /* Int. disabled.*/
AT91C_BASE_PIOA->PIO_MDDR = 0xFFFFFFFF; /* Push Pull drive.*/
AT91C_BASE_PIOA->PIO_ASR = 0xFFFFFFFF; /* Peripheral A.*/
AT91C_BASE_PIOA->PIO_OWER = 0xFFFFFFFF; /* Write enabled.*/
/*
* PIOB setup.
*/
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3)
AT91C_BASE_PIOB->PIO_PPUER = config->P1Data.pusr; /* Pull-up as spec.*/
AT91C_BASE_PIOB->PIO_PPUDR = ~config->P1Data.pusr;
AT91C_BASE_PIOB->PIO_PER = 0xFFFFFFFF; /* PIO enabled.*/
AT91C_BASE_PIOB->PIO_ODSR = config->P1Data.odsr; /* Data as specified.*/
AT91C_BASE_PIOB->PIO_OER = config->P1Data.osr; /* Dir. as specified.*/
AT91C_BASE_PIOB->PIO_ODR = ~config->P1Data.osr;
AT91C_BASE_PIOB->PIO_IFDR = 0xFFFFFFFF; /* Filter disabled.*/
AT91C_BASE_PIOB->PIO_IDR = 0xFFFFFFFF; /* Int. disabled.*/
AT91C_BASE_PIOB->PIO_MDDR = 0xFFFFFFFF; /* Push Pull drive.*/
AT91C_BASE_PIOB->PIO_ASR = 0xFFFFFFFF; /* Peripheral A.*/
AT91C_BASE_PIOB->PIO_OWER = 0xFFFFFFFF; /* Write enabled.*/
#endif
}
/**
* @brief Pads mode setup.
* @details This function programs a pads group belonging to the same port
* with the specified mode.
* @note This function is not meant to be invoked directly from the
* application code.
* @note @p PAL_MODE_RESET is implemented as input with pull-up.
* @note @p PAL_MODE_UNCONNECTED is implemented as push pull output with
* high state.
* @note @p PAL_MODE_OUTPUT_OPENDRAIN also enables the pull-up resistor.
*
* @param[in] port the port identifier
* @param[in] mask the group mask
* @param[in] mode the mode
*
* @notapi
*/
void _pal_lld_setgroupmode(ioportid_t port,
ioportmask_t mask,
iomode_t mode) {
if (mode & AT91_PAL_OPENDRAIN)
port->PIO_MDER = mask;
else
port->PIO_MDDR = mask;
if (mode & AT91_PAL_PULLUP)
port->PIO_PPUER = mask;
else
port->PIO_PPUDR = mask;
if (mode & AT91_PAL_OUT_SET)
port->PIO_SODR = mask;
if (mode & AT91_PAL_OUT_CLEAR)
port->PIO_CODR = mask;
switch (mode & AT91_PAL_DIR_MASK) {
case AT91_PAL_DIR_INPUT:
port->PIO_ODR = mask;
port->PIO_PER = mask;
break;
case AT91_PAL_DIR_OUTPUT:
port->PIO_OER = mask;
port->PIO_PER = mask;
break;
case AT91_PAL_DIR_PERIPH_A:
port->PIO_OER = mask;
port->PIO_PDR = mask;
port->PIO_ASR = mask;
break;
case AT91_PAL_DIR_PERIPH_B:
port->PIO_OER = mask;
port->PIO_PDR = mask;
port->PIO_BSR = mask;
break;
}
}
#endif /* HAL_USE_PAL */
/** @} */

View File

@@ -0,0 +1,292 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/pal_lld.h
* @brief AT91SAM7 PIO low level driver header.
*
* @addtogroup PAL
* @{
*/
#ifndef _PAL_LLD_H_
#define _PAL_LLD_H_
#if HAL_USE_PAL || defined(__DOXYGEN__)
/*===========================================================================*/
/* Unsupported modes and specific modes */
/*===========================================================================*/
// AT91: Port direction
#define AT91_PAL_DIR_MASK 0x03
#define AT91_PAL_DIR_INPUT 0x00
#define AT91_PAL_DIR_OUTPUT 0x01
#define AT91_PAL_DIR_PERIPH_A 0x02
#define AT91_PAL_DIR_PERIPH_B 0x03
// AT91: Additional flags
#define AT91_PAL_OUT_SET 0x04
#define AT91_PAL_OUT_CLEAR 0x08
#define AT91_PAL_PULLUP 0x10
#define AT91_PAL_OPENDRAIN 0x20
// Unsupported
#undef PAL_MODE_INPUT_PULLDOWN
// Redefine the standard io modes
#undef PAL_MODE_RESET
#undef PAL_MODE_UNCONNECTED
#undef PAL_MODE_INPUT
#undef PAL_MODE_INPUT_PULLUP
#undef PAL_MODE_INPUT_ANALOG
#undef PAL_MODE_OUTPUT_PUSHPULL
#undef PAL_MODE_OUTPUT_OPENDRAIN
#define PAL_MODE_RESET (AT91_PAL_DIR_INPUT|AT91_PAL_PULLUP)
#define PAL_MODE_UNCONNECTED (AT91_PAL_DIR_OUTPUT|AT91_PAL_OUT_SET)
#define PAL_MODE_INPUT (AT91_PAL_DIR_INPUT)
#define PAL_MODE_INPUT_PULLUP (AT91_PAL_DIR_INPUT|AT91_PAL_PULLUP)
#define PAL_MODE_INPUT_ANALOG (AT91_PAL_DIR_INPUT)
#define PAL_MODE_OUTPUT_PUSHPULL (AT91_PAL_DIR_OUTPUT)
#define PAL_MODE_OUTPUT_OPENDRAIN (AT91_PAL_DIR_OUTPUT|AT91_PAL_PULLUP|AT91_PAL_OPENDRAIN)
// Add the AT91 specific ones
#define PAL_MODE_PERIPH_A_PUSHPULL (AT91_PAL_DIR_PERIPH_A)
#define PAL_MODE_PERIPH_A_OPENDRAIN (AT91_PAL_DIR_PERIPH_A|AT91_PAL_PULLUP|AT91_PAL_OPENDRAIN)
#define PAL_MODE_PERIPH_B_PUSHPULL (AT91_PAL_DIR_PERIPH_A)
#define PAL_MODE_PERIPH_B_OPENDRAIN (AT91_PAL_DIR_PERIPH_A|AT91_PAL_PULLUP|AT91_PAL_OPENDRAIN)
/*===========================================================================*/
/* I/O Ports Types and constants. */
/*===========================================================================*/
/**
* @brief PIO port setup info.
*/
typedef struct {
/** Initial value for ODSR register (data).*/
uint32_t odsr;
/** Initial value for OSR register (direction).*/
uint32_t osr;
/** Initial value for PUSR register (Pull-ups).*/
uint32_t pusr;
} at91sam7_pio_setup_t;
/**
* @brief AT91SAM7 PIO static initializer.
* @details An instance of this structure must be passed to @p palInit() at
* system startup time in order to initialize the digital I/O
* subsystem. This represents only the initial setup, specific pads
* or whole ports can be reprogrammed at later time.
*/
typedef struct {
/** @brief Port 0 setup data.*/
at91sam7_pio_setup_t P0Data;
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3) || \
defined(__DOXYGEN__)
/** @brief Port 1 setup data.*/
at91sam7_pio_setup_t P1Data;
#endif
} PALConfig;
/**
* @brief Width, in bits, of an I/O port.
*/
#define PAL_IOPORTS_WIDTH 32
/**
* @brief Whole port mask.
* @details This macro specifies all the valid bits into a port.
*/
#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFFFFFF)
/**
* @brief Digital I/O port sized unsigned type.
*/
typedef uint32_t ioportmask_t;
/**
* @brief Digital I/O modes.
*/
typedef uint32_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 AT91PS_PIO ioportid_t;
/*===========================================================================*/
/* I/O Ports Identifiers. */
/*===========================================================================*/
/**
* @brief PIO port A identifier.
*/
#define IOPORT1 AT91C_BASE_PIOA
/**
* @brief PIO port B identifier.
*/
#if (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512) || (SAM7_PLATFORM == SAM7A3) || \
defined(__DOXYGEN__)
#define IOPORT2 AT91C_BASE_PIOB
#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 architecture-dependent ports configuration
*
* @notapi
*/
#define pal_lld_init(config) _pal_lld_init(config)
/**
* @brief Reads the physical I/O port states.
* @details This function is implemented by reading the PIO_PDSR register, the
* implementation has no side effects.
*
* @param[in] port port identifier
* @return The port bits.
*
* @notapi
*/
#define pal_lld_readport(port) ((port)->PIO_PDSR)
/**
* @brief Reads the output latch.
* @details This function is implemented by reading the PIO_ODSR register, the
* implementation has no side effects.
*
* @param[in] port port identifier
* @return The latched logical states.
*
* @notapi
*/
#define pal_lld_readlatch(port) ((port)->PIO_ODSR)
/**
* @brief Writes a bits mask on a I/O port.
* @details This function is implemented by writing the PIO_ODSR register, the
* implementation has no side effects.
*
* @param[in] port the port identifier
* @param[in] bits the bits to be written on the specified port
*
* @notapi
*/
#define pal_lld_writeport(port, bits) ((port)->PIO_ODSR = (bits))
/**
* @brief Sets a bits mask on a I/O port.
* @details This function is implemented by writing the PIO_SODR register, the
* implementation has no side effects.
*
* @param[in] port port identifier
* @param[in] bits bits to be ORed on the specified port
*
* @notapi
*/
#define pal_lld_setport(port, bits) ((port)->PIO_SODR = (bits))
/**
* @brief Clears a bits mask on a I/O port.
* @details This function is implemented by writing the PIO_CODR register, the
* implementation has no side effects.
*
* @param[in] port port identifier
* @param[in] bits bits to be cleared on the specified port
*
* @notapi
*/
#define pal_lld_clearport(port, bits) ((port)->PIO_CODR = (bits))
/**
* @brief Writes a group of bits.
* @details This function is implemented by writing the PIO_OWER, PIO_ODSR and
* PIO_OWDR registers, the implementation is not atomic because the
* multiple accesses.
*
* @param[in] port port identifier
* @param[in] mask group mask
* @param[in] offset the group bit offset within the port
* @param[in] bits bits to be written. Values exceeding the group
* width are masked.
*
* @notapi
*/
#define pal_lld_writegroup(port, mask, offset, bits) \
((port)->PIO_OWER = (mask) << (offset), \
(port)->PIO_ODSR = (bits) << (offset), \
(port)->PIO_OWDR = (mask) << (offset))
/**
* @brief Pads group mode setup.
* @details This function programs a pads group belonging to the same port
* with the specified mode.
* @note @p PAL_MODE_UNCONNECTED is implemented as push pull output with
* high state.
*
* @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 Writes a logical state on an output pad.
*
* @param[in] port port identifier
* @param[in] pad pad number within the port
* @param[in] bit logical value, the value must be @p PAL_LOW or
* @p PAL_HIGH
*
* @notapi
*/
#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit)
extern const 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,136 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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.
*/
/**
* @defgroup AT91SAM7 AT91SAM7 Drivers
* @details This section describes all the supported drivers on the AT91SAM7
* platform and the implementation details of the single drivers.
*
* @ingroup platforms
*/
/**
* @defgroup AT91SAM7_HAL AT91SAM7 Initialization Support
* @details The AT91SAM7 HAL support is responsible for system initialization.
*
* @section at91sam7_hal_1 Supported HW resources
* - MC.
* - PMC.
* .
* @section at91sam7_hal_2 AT91SAM7 HAL driver implementation features
* - PLLs startup and stabilization.
* - Clock source selection.
* - Flash wait states.
* .
* @ingroup AT91SAM7
*/
/**
* @defgroup AT91SAM7_MAC AT91SAM7 MAC Support
* @details The AT91SAM7 MAC driver supports the EMAC peripheral.
*
* @section at91sam7_mac_1 Supported HW resources
* - EMAC.
* .
* @ingroup AT91SAM7
*/
/**
* @defgroup AT91SAM7_MII AT91SAM7 MII Support
* @details This driver supports the AT91SAM7 EMAC peripheral communicating
* with an external PHY transceiver. The driver currently supports
* the Micrel KS8721 PHY and the Davicom DV9161 modules. This driver
* is used internally by the MAC driver.
*
* @ingroup AT91SAM7
*/
/**
* @defgroup AT91SAM7_PAL AT91SAM7 PAL Support
* @details The AT91SAM7 PAL driver supports the PIO peripherals.
*
* @section at91sam7_pal_1 Supported HW resources
* - PIOA.
* - PIOB.
* .
* @section at91sam7_pal_2 AT91SAM7 PAL driver implementation features
* The PAL driver implementation fully supports the following hardware
* capabilities:
* - 32 bits wide ports.
* - Atomic set/reset functions.
* - Output latched regardless of the pad setting.
* - Direct read of input pads regardless of the pad setting.
* .
* @section at91sam7_pal_3 Supported PAL setup modes
* The AT91SAM7 PAL driver supports the following I/O modes:
* - @p PAL_MODE_RESET.
* - @p PAL_MODE_UNCONNECTED.
* - @p PAL_MODE_INPUT.
* - @p PAL_MODE_INPUT_ANALOG (same as @p PAL_MODE_INPUT).
* - @p PAL_MODE_INPUT_PULLUP.
* - @p PAL_MODE_OUTPUT_PUSHPULL.
* - @p PAL_MODE_OUTPUT_OPENDRAIN.
* .
* Any attempt to setup an invalid mode is ignored.
*
* @section at91sam7_pal_4 Suboptimal behavior
* The AT91SAM7 PIO is less than optimal in several areas, the limitations
* should be taken in account while using the PAL driver:
* - Pad/port toggling operations are not atomic.
* - Pad/group mode setup is not atomic.
* .
* @ingroup AT91SAM7
*/
/**
* @defgroup AT91SAM7_SERIAL AT91SAM7 Serial Support
* @details The AT91SAM7 Serial driver uses the USART/UART peripherals in a
* buffered, interrupt driven, implementation.
*
* @section at91sam7_serial_1 Supported HW resources
* The serial driver can support any of the following hardware resources:
* - USART1.
* - USART2.
* - DBGU.
* .
* @section at91sam7_serial_2 AT91SAM7 Serial driver implementation features
* - Clock stop for reduced power usage when the driver is in stop state.
* - Each USART can be independently enabled and programmed. Unused
* peripherals are left in low power mode.
* - Fully interrupt driven.
* - Programmable priority levels for each USART.
* .
* @ingroup AT91SAM7
*/
/**
* @defgroup AT91SAM7_SPI AT91SAM7 SPI Support
* @details The SPI driver supports the AT91SAM7 SPI peripherals using DMA
* channels for maximum performance.
*
* @section at91sam7_spi_1 Supported HW resources
* - SPI1.
* - SPI2.
* .
* @section at91sam7_spi_2 AT91SAM7 SPI driver implementation features
* - Clock stop for reduced power usage when the driver is in stop state.
* - Each SPI can be independently enabled and programmed. Unused
* peripherals are left in low power mode.
* - Programmable interrupt priority levels for each SPI.
* - DMA is used for receiving and transmitting.
* .
* @ingroup AT91SAM7
*/

View File

@@ -0,0 +1,16 @@
# List of all the AT91SAM7 platform files.
PLATFORMSRC = ${CHIBIOS}/os/hal/platforms/AT91SAM7/hal_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/pal_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/i2c_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/adc_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/ext_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/serial_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/spi_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/mac_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/pwm_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/gpt_lld.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/at91sam7_mii.c \
${CHIBIOS}/os/hal/platforms/AT91SAM7/at91lib/aic.c
# Required include directories
PLATFORMINC = ${CHIBIOS}/os/hal/platforms/AT91SAM7

View File

@@ -0,0 +1,467 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 file has been contributed by:
Andrew Hannam aka inmarket.
*/
/**
* @file AT91SAM7/pwm_lld.c
* @brief AT91SAM7 PWM Driver subsystem low level driver source.
*
* @addtogroup PWM
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_PWM || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
#ifdef UNUSED
#elif defined(__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
#elif defined(__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif
#define PWMC_M ((AT91S_PWMC *)AT91C_PWMC_MR)
#define PWM_MCK_MASK 0x0F00
#define PWM_MCK_SHIFT 8
typedef struct pindef {
uint32_t portpin; /* Set to 0 if this pin combination is invalid */
AT91S_PIO *pio;
AT91_REG *perab;
} pindef_t;
typedef struct pwmpindefs {
pindef_t pin[3];
} pwmpindefs_t;
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
#if PWM_USE_PWM1 && !defined(__DOXYGEN__)
PWMDriver PWMD1;
#endif
#if PWM_USE_PWM2 && !defined(__DOXYGEN__)
PWMDriver PWMD2;
#endif
#if PWM_USE_PWM3 && !defined(__DOXYGEN__)
PWMDriver PWMD3;
#endif
#if PWM_USE_PWM4 && !defined(__DOXYGEN__)
PWMDriver PWMD4;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
#if (SAM7_PLATFORM == SAM7S64) || (SAM7_PLATFORM == SAM7S128) || \
(SAM7_PLATFORM == SAM7S256) || (SAM7_PLATFORM == SAM7S512)
#if PWM_USE_PWM1 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP1 = {{
{ AT91C_PA0_PWM0 , AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_ASR },
{ AT91C_PA11_PWM0, AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
{ AT91C_PA23_PWM0, AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
}};
#endif
#if PWM_USE_PWM2 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP2 = {{
{ AT91C_PA1_PWM1 , AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_ASR },
{ AT91C_PA12_PWM1, AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
{ AT91C_PA24_PWM1, AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
}};
#endif
#if PWM_USE_PWM3 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP3 = {{
{ AT91C_PA2_PWM2 , AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_ASR },
{ AT91C_PA13_PWM2, AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
{ AT91C_PA25_PWM2, AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
}};
#endif
#if PWM_USE_PWM4 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP4 = {{
{ AT91C_PA7_PWM3 , AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
{ AT91C_PA14_PWM3, AT91C_BASE_PIOA, &AT91C_BASE_PIOA->PIO_BSR },
{ 0, 0, 0 },
}};
#endif
#elif (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512)
#if PWM_USE_PWM1 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP1 = {{
{ AT91C_PB19_PWM0, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_ASR },
{ AT91C_PB27_PWM0, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_BSR },
{ 0, 0, 0 },
}};
#endif
#if PWM_USE_PWM2 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP2 = {{
{ AT91C_PB20_PWM1, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_ASR },
{ AT91C_PB28_PWM1, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_BSR },
{ 0, 0, 0 },
}};
#endif
#if PWM_USE_PWM3 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP3 = {{
{ AT91C_PB21_PWM2, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_ASR },
{ AT91C_PB29_PWM2, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_BSR },
{ 0, 0, 0 },
}};
#endif
#if PWM_USE_PWM4 && !defined(__DOXYGEN__)
static const pwmpindefs_t PWMP4 = {{
{ AT91C_PB22_PWM3, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_ASR },
{ AT91C_PB30_PWM3, AT91C_BASE_PIOB, &AT91C_BASE_PIOB->PIO_BSR },
{ 0, 0, 0 },
}};
#endif
#else
#error "PWM pins not defined for this SAM7 version"
#endif
#if PWM_USE_PWM2 && !defined(__DOXYGEN__)
PWMDriver PWMD2;
#endif
#if PWM_USE_PWM3 && !defined(__DOXYGEN__)
PWMDriver PWMD3;
#endif
#if PWM_USE_PWM4 && !defined(__DOXYGEN__)
PWMDriver PWMD4;
#endif
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if defined(__GNUC__)
__attribute__((noinline))
#endif
/**
* @brief Common IRQ handler.
*/
static void pwm_lld_serve_interrupt(void) {
uint32_t isr;
isr = PWMC_M->PWMC_ISR;
#if PWM_USE_PWM1
if ((isr & 1) && PWMD1.config->channels[0].callback)
PWMD1.config->channels[0].callback(&PWMD1);
#endif
#if PWM_USE_PWM2
if ((isr & 2) && PWMD2.config->channels[0].callback)
PWMD2.config->channels[0].callback(&PWMD2);
#endif
#if PWM_USE_PWM3
if ((isr & 4) && PWMD3.config->channels[0].callback)
PWMD3.config->channels[0].callback(&PWMD3);
#endif
#if PWM_USE_PWM4
if ((isr & 8) && PWMD4.config->channels[0].callback)
PWMD4.config->channels[0].callback(&PWMD4);
#endif
}
CH_IRQ_HANDLER(PWMIrqHandler) {
CH_IRQ_PROLOGUE();
pwm_lld_serve_interrupt();
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level PWM driver initialization.
*
* @notapi
*/
void pwm_lld_init(void) {
/* Driver initialization.*/
#if PWM_USE_PWM1 && !defined(__DOXYGEN__)
pwmObjectInit(&PWMD1);
PWMD1.chbit = 1;
PWMD1.reg = AT91C_BASE_PWMC_CH0;
PWMD1.pins = &PWMP1;
#endif
#if PWM_USE_PWM2 && !defined(__DOXYGEN__)
pwmObjectInit(&PWMD2);
PWMD2.chbit = 2;
PWMD2.reg = AT91C_BASE_PWMC_CH1;
PWMD2.pins = &PWMP2;
#endif
#if PWM_USE_PWM3 && !defined(__DOXYGEN__)
pwmObjectInit(&PWMD3);
PWMD3.chbit = 4;
PWMD3.reg = AT91C_BASE_PWMC_CH2;
PWMD3.pins = &PWMP3;
#endif
#if PWM_USE_PWM4 && !defined(__DOXYGEN__)
pwmObjectInit(&PWMD4);
PWMD4.chbit = 8;
PWMD4.reg = AT91C_BASE_PWMC_CH3;
PWMD4.pins = &PWMP4;
#endif
/* Turn on PWM in the power management controller */
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_PWMC);
/* Setup interrupt handler */
PWMC_M->PWMC_IDR = 0xFFFFFFFF;
AIC_ConfigureIT(AT91C_ID_PWMC,
AT91C_AIC_SRCTYPE_HIGH_LEVEL | AT91SAM7_PWM_PRIORITY,
PWMIrqHandler);
AIC_EnableIT(AT91C_ID_PWMC);
}
/**
* @brief Configures and activates the PWM peripheral.
*
* @param[in] pwmp pointer to the @p PWMDriver object
*
* @notapi
*/
void pwm_lld_start(PWMDriver *pwmp) {
uint32_t mode, mr, div, pre;
/* Steps:
1. Turn the IO pin to a PWM output
2. Configuration of Clock if DIVA or DIVB used
3. Selection of the clock for each channel (CPRE field in the PWM_CMRx register)
4. Configuration of the waveform alignment for each channel (CALG field in the PWM_CMRx register)
5. Configuration of the output waveform polarity for each channel (CPOL in the PWM_CMRx register)
6. Configuration of the period for each channel (CPRD in the PWM_CPRDx register). Writing in
PWM_CPRDx Register is possible while the channel is disabled. After validation of the
channel, the user must use PWM_CUPDx Register to update PWM_CPRDx
7. Enable Interrupts (Writing CHIDx in the PWM_IER register)
*/
/* Make sure it is off first */
pwm_lld_disable_channel(pwmp, 0);
/* Configuration.*/
mode = pwmp->config->channels[0].mode;
/* Step 1 */
if (mode & PWM_OUTPUT_PIN1) {
pwmp->pins->pin[0].perab[0] = pwmp->pins->pin[0].portpin; /* Select A or B peripheral */
pwmp->pins->pin[0].pio->PIO_PDR = pwmp->pins->pin[0].portpin; /* Turn PIO into PWM output */
pwmp->pins->pin[0].pio->PIO_MDDR = pwmp->pins->pin[0].portpin; /* Turn off PIO multi-drive */
if (mode & PWM_DISABLEPULLUP_PIN1)
pwmp->pins->pin[0].pio->PIO_PPUDR = pwmp->pins->pin[0].portpin; /* Turn off PIO pullup */
else
pwmp->pins->pin[0].pio->PIO_PPUER = pwmp->pins->pin[0].portpin; /* Turn on PIO pullup */
}
if (mode & PWM_OUTPUT_PIN2) {
pwmp->pins->pin[1].perab[0] = pwmp->pins->pin[1].portpin;
pwmp->pins->pin[1].pio->PIO_PDR = pwmp->pins->pin[1].portpin;
pwmp->pins->pin[1].pio->PIO_MDDR = pwmp->pins->pin[1].portpin;
if (mode & PWM_DISABLEPULLUP_PIN2)
pwmp->pins->pin[1].pio->PIO_PPUDR = pwmp->pins->pin[1].portpin;
else
pwmp->pins->pin[1].pio->PIO_PPUER = pwmp->pins->pin[1].portpin;
}
if ((mode & PWM_OUTPUT_PIN3) && pwmp->pins->pin[2].portpin) {
pwmp->pins->pin[2].perab[0] = pwmp->pins->pin[2].portpin;
pwmp->pins->pin[2].pio->PIO_PDR = pwmp->pins->pin[2].portpin;
pwmp->pins->pin[2].pio->PIO_MDDR = pwmp->pins->pin[2].portpin;
if (mode & PWM_DISABLEPULLUP_PIN3)
pwmp->pins->pin[2].pio->PIO_PPUDR = pwmp->pins->pin[2].portpin;
else
pwmp->pins->pin[2].pio->PIO_PPUER = pwmp->pins->pin[2].portpin;
}
/* Step 2 */
if ((mode & PWM_MCK_MASK) == PWM_MCK_DIV_CLKA) {
if (!pwmp->config->frequency) {
/* As slow as we go */
PWMC_M->PWMC_MR = (PWMC_M->PWMC_MR & 0xFFFF0000) | (10 << 8) | (255 << 0);
} else if (pwmp->config->frequency > MCK) {
/* Just use MCLK */
mode &= ~PWM_MCK_MASK;
} else {
div = MCK / pwmp->config->frequency;
if (mode & PWM_OUTPUT_CENTER) div >>= 1;
for(pre = 0; div > 255 && pre < 10; pre++) div >>= 1;
if (div > 255) div = 255;
PWMC_M->PWMC_MR = (PWMC_M->PWMC_MR & 0xFFFF0000) | (pre << 8) | (div << 0);
}
} else if ((mode & PWM_MCK_MASK) == PWM_MCK_DIV_CLKB) {
if (!pwmp->config->frequency) {
/* As slow as we go */
PWMC_M->PWMC_MR = (PWMC_M->PWMC_MR & 0x0000FFFF) | (10 << 24) | (255 << 16);
} else if (pwmp->config->frequency > MCK) {
/* Just use MCLK */
mode &= ~PWM_MCK_MASK;
} else {
div = MCK / pwmp->config->frequency;
if (mode & PWM_OUTPUT_CENTER) div >>= 1;
for(pre = 0; div > 255 && pre < 10; pre++) div >>= 1;
if (div > 255) div = 255;
PWMC_M->PWMC_MR = (PWMC_M->PWMC_MR & 0x0000FFFF) | (pre << 24) | (div << 16);
}
}
/* Step 3 -> 5 */
mr = (mode & PWM_MCK_MASK) >> PWM_MCK_SHIFT;
if (mode & PWM_OUTPUT_CENTER) mr |= AT91C_PWMC_CALG;
if (mode & PWM_OUTPUT_ACTIVE_HIGH) mr |= AT91C_PWMC_CPOL;
pwmp->reg->PWMC_CMR = mr;
/* Step 6 */
pwmp->reg->PWMC_CPRDR = pwmp->period;
/* Step 7 */
if (pwmp->config->channels[0].callback)
PWMC_M->PWMC_IER = pwmp->chbit;
}
/**
* @brief Deactivates the PWM peripheral.
*
* @param[in] pwmp pointer to the @p PWMDriver object
*
* @notapi
*/
void pwm_lld_stop(PWMDriver *pwmp) {
/* Make sure it is off */
pwm_lld_disable_channel(pwmp, 0);
/* Turn the pin back to a PIO pin - we have forgotten pull-up and multi-drive state for the pin though */
if (pwmp->config->channels[0].mode & PWM_OUTPUT_PIN1)
pwmp->pins->pin[0].pio->PIO_PER = pwmp->pins->pin[0].portpin;
if (pwmp->config->channels[0].mode & PWM_OUTPUT_PIN2)
pwmp->pins->pin[1].pio->PIO_PER = pwmp->pins->pin[1].portpin;
if ((pwmp->config->channels[0].mode & PWM_OUTPUT_PIN3) && pwmp->pins->pin[2].portpin)
pwmp->pins->pin[2].pio->PIO_PER = pwmp->pins->pin[2].portpin;
}
/**
* @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) {
pwmp->period = period;
if (PWMC_M->PWMC_SR & pwmp->chbit) {
pwmp->reg->PWMC_CMR |= AT91C_PWMC_CPD;
pwmp->reg->PWMC_CUPDR = period;
} else {
pwmp->reg->PWMC_CPRDR = 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 UNUSED(channel),
pwmcnt_t width) {
/*
6. Configuration of the duty cycle for each channel (CDTY in the PWM_CDTYx register).
Writing in PWM_CDTYx Register is possible while the channel is disabled. After validation of
the channel, the user must use PWM_CUPDx Register to update PWM_CDTYx.
7. Enable the PWM channel (Writing CHIDx in the PWM_ENA register)
*/
/* Step 6 */
if (PWMC_M->PWMC_SR & pwmp->chbit) {
pwmp->reg->PWMC_CMR &= ~AT91C_PWMC_CPD;
pwmp->reg->PWMC_CUPDR = width;
} else {
pwmp->reg->PWMC_CDTYR = width;
PWMC_M->PWMC_ENA = pwmp->chbit;
}
/* Step 7 */
PWMC_M->PWMC_ENA = pwmp->chbit;
}
/**
* @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 UNUSED(channel)) {
PWMC_M->PWMC_IDR = pwmp->chbit;
PWMC_M->PWMC_DIS = pwmp->chbit;
}
#endif /* HAL_USE_PWM */
/** @} */

View File

@@ -0,0 +1,284 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 file has been contributed by:
Andrew Hannam aka inmarket.
*/
/**
* @file AT91SAM7/pwm_lld.h
* @brief AT91SAM7 PWM Driver subsystem low level driver header.
*
* @addtogroup PWM
* @{
*/
#ifndef _PWM_LLD_H_
#define _PWM_LLD_H_
#if HAL_USE_PWM || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief Number of PWM channels per PWM driver.
*/
#define PWM_CHANNELS 1
/**
* @brief PWM device interrupt priority level setting.
*/
#if !defined(AT91SAM7_PWM_PRIORITY) || defined(__DOXYGEN__)
#define AT91SAM7_PWM_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 4)
#endif
/**
* @brief PWMD1 driver enable switch.
* @details If set to @p TRUE the support for PWMD1 is included.
* @note The default is @p TRUE.
*/
#if !defined(PWM_USE_PWM1) || defined(__DOXYGEN__)
#define PWM_USE_PWM1 TRUE
#endif
/**
* @brief PWMD2 driver enable switch.
* @details If set to @p TRUE the support for PWMD1 is included.
* @note The default is @p TRUE.
*/
#if !defined(PWM_USE_PWM2) || defined(__DOXYGEN__)
#define PWM_USE_PWM2 TRUE
#endif
/**
* @brief PWMD3 driver enable switch.
* @details If set to @p TRUE the support for PWMD1 is included.
* @note The default is @p TRUE.
*/
#if !defined(PWM_USE_PWM3) || defined(__DOXYGEN__)
#define PWM_USE_PWM3 TRUE
#endif
/**
* @brief PWMD4 driver enable switch.
* @details If set to @p TRUE the support for PWMD1 is included.
* @note The default is @p TRUE.
*/
#if !defined(PWM_USE_PWM4) || defined(__DOXYGEN__)
#define PWM_USE_PWM4 TRUE
#endif
/**
* @brief PWM left (count up) logic
*/
#define PWM_OUTPUT_LEFT 0x00000000
/**
* @brief PWM center (count up-down) logic. Gives symetric waveform
*/
#define PWM_OUTPUT_CENTER 0x00000010
/**
* @brief PWM Master Clock = MCK / 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, CLKA or CLKB. CLKA or CLKB uses the frequency field
*/
#define PWM_MCK_DIV_1 0x00000000
#define PWM_MCK_DIV_2 0x00000100
#define PWM_MCK_DIV_4 0x00000200
#define PWM_MCK_DIV_8 0x00000300
#define PWM_MCK_DIV_16 0x00000400
#define PWM_MCK_DIV_32 0x00000500
#define PWM_MCK_DIV_64 0x00000600
#define PWM_MCK_DIV_128 0x00000700
#define PWM_MCK_DIV_256 0x00000800
#define PWM_MCK_DIV_512 0x00000900
#define PWM_MCK_DIV_1024 0x00000A00
#define PWM_MCK_DIV_CLKA 0x00000B00
#define PWM_MCK_DIV_CLKB 0x00000C00
/**
* @brief Which PWM output pins to turn on. PIN1 is the lowest numbered pin, PIN2 next lowest, and then on some packages PIN3.
*/
#define PWM_OUTPUT_PIN1 0x00001000
#define PWM_OUTPUT_PIN2 0x00002000
#define PWM_OUTPUT_PIN3 0x00004000
/**
* @brief Which PWM output pins should have pullups disabled.
*/
#define PWM_DISABLEPULLUP_PIN1 0x00010000
#define PWM_DISABLEPULLUP_PIN2 0x00020000
#define PWM_DISABLEPULLUP_PIN3 0x00040000
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief PWM mode type.
*/
typedef uint32_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.
*/
uint32_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.*/
/**
* @brief The PWM internal channel number as a bit mask (1, 2, 4 or 8).
*/
uint32_t chbit;
/**
* @brief Pointer to the PWMCx registers block.
*/
AT91S_PWMC_CH *reg;
/**
* @brief Pointer to the output pins descriptor.
*/
const struct pwmpindefs *pins;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if PWM_USE_PWM1 && !defined(__DOXYGEN__)
extern PWMDriver PWMD1;
#endif
#if PWM_USE_PWM2 && !defined(__DOXYGEN__)
extern PWMDriver PWMD2;
#endif
#if PWM_USE_PWM3 && !defined(__DOXYGEN__)
extern PWMDriver PWMD3;
#endif
#if PWM_USE_PWM4 && !defined(__DOXYGEN__)
extern PWMDriver PWMD4;
#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,444 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/serial_lld.c
* @brief AT91SAM7 low level serial driver code.
*
* @addtogroup SERIAL
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_SERIAL || defined(__DOXYGEN__)
#if (SAM7_PLATFORM == SAM7S64) || (SAM7_PLATFORM == SAM7S128) || \
(SAM7_PLATFORM == SAM7S256) || (SAM7_PLATFORM == SAM7S512)
#define SAM7_USART0_RX AT91C_PA5_RXD0
#define SAM7_USART0_TX AT91C_PA6_TXD0
#define SAM7_USART1_RX AT91C_PA21_RXD1
#define SAM7_USART1_TX AT91C_PA22_TXD1
#define SAM7_DBGU_RX AT91C_PA9_DRXD
#define SAM7_DBGU_TX AT91C_PA10_DTXD
#elif (SAM7_PLATFORM == SAM7X128) || (SAM7_PLATFORM == SAM7X256) || \
(SAM7_PLATFORM == SAM7X512)
#define SAM7_USART0_RX AT91C_PA0_RXD0
#define SAM7_USART0_TX AT91C_PA1_TXD0
#define SAM7_USART1_RX AT91C_PA5_RXD1
#define SAM7_USART1_TX AT91C_PA6_TXD1
#define SAM7_DBGU_RX AT91C_PA27_DRXD
#define SAM7_DBGU_TX AT91C_PA28_DTXD
#elif (SAM7_PLATFORM == SAM7A3)
#define SAM7_USART0_RX AT91C_PA2_RXD0
#define SAM7_USART0_TX AT91C_PA3_TXD0
#define SAM7_USART1_RX AT91C_PA7_RXD1
#define SAM7_USART1_TX AT91C_PA8_TXD1
#define SAM7_USART2_RX AT91C_PA9_RXD2
#define SAM7_USART2_TX AT91C_PA10_TXD2
#define SAM7_DBGU_RX AT91C_PA30_DRXD
#define SAM7_DBGU_TX AT91C_PA31_DTXD
#else
#error "serial lines not defined for this SAM7 version"
#endif /* HAL_USE_SERIAL */
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
#if USE_SAM7_USART0 || defined(__DOXYGEN__)
/** @brief USART0 serial driver identifier.*/
SerialDriver SD1;
#endif
#if USE_SAM7_USART1 || defined(__DOXYGEN__)
/** @brief USART1 serial driver identifier.*/
SerialDriver SD2;
#endif
#if (SAM7_PLATFORM == SAM7A3)
#if USE_SAM7_USART2 || defined(__DOXYGEN__)
/** @brief USART2 serial driver identifier.*/
SerialDriver SD3;
#endif
#endif
#if USE_SAM7_DBGU_UART || defined(__DOXYGEN__)
/** @brief DBGU_UART serial driver identifier.*/
SerialDriver SDDBG;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/** @brief Driver default configuration.*/
static const SerialConfig default_config = {
SERIAL_DEFAULT_BITRATE,
AT91C_US_USMODE_NORMAL | AT91C_US_CLKS_CLOCK |
AT91C_US_CHRL_8_BITS | AT91C_US_PAR_NONE | AT91C_US_NBSTOP_1_BIT
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief USART initialization.
*
* @param[in] sdp communication channel associated to the USART
* @param[in] config the architecture-dependent serial driver configuration
*/
static void usart_init(SerialDriver *sdp, const SerialConfig *config) {
AT91PS_USART u = sdp->usart;
/* Disables IRQ sources and stop operations.*/
u->US_IDR = 0xFFFFFFFF;
u->US_CR = AT91C_US_RSTRX | AT91C_US_RSTTX | AT91C_US_RSTSTA;
/* New parameters setup.*/
if (config->sc_mr & AT91C_US_OVER)
u->US_BRGR = MCK / (config->sc_speed * 8);
else
u->US_BRGR = MCK / (config->sc_speed * 16);
u->US_MR = config->sc_mr;
u->US_RTOR = 0;
u->US_TTGR = 0;
/* Enables operations and IRQ sources.*/
u->US_CR = AT91C_US_RXEN | AT91C_US_TXEN | AT91C_US_DTREN | AT91C_US_RTSEN;
u->US_IER = AT91C_US_RXRDY | AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE |
AT91C_US_RXBRK;
}
/**
* @brief USART de-initialization.
*
* @param[in] u pointer to an USART I/O block
*/
static void usart_deinit(AT91PS_USART u) {
/* Disables IRQ sources and stop operations.*/
u->US_IDR = 0xFFFFFFFF;
u->US_CR = AT91C_US_RSTRX | AT91C_US_RSTTX | AT91C_US_RSTSTA;
u->US_MR = 0;
u->US_RTOR = 0;
u->US_TTGR = 0;
}
/**
* @brief Error handling routine.
*
* @param[in] err USART CSR register value
* @param[in] sdp communication channel associated to the USART
*/
static void set_error(SerialDriver *sdp, AT91_REG csr) {
flagsmask_t sts = 0;
if (csr & AT91C_US_OVRE)
sts |= SD_OVERRUN_ERROR;
if (csr & AT91C_US_PARE)
sts |= SD_PARITY_ERROR;
if (csr & AT91C_US_FRAME)
sts |= SD_FRAMING_ERROR;
if (csr & AT91C_US_RXBRK)
sts |= SD_BREAK_DETECTED;
chSysLockFromIsr();
chnAddFlagsI(sdp, sts);
chSysUnlockFromIsr();
}
#if defined(__GNUC__)
__attribute__((noinline))
#endif
#if !USE_SAM7_DBGU_UART
static
#endif
/**
* @brief Common IRQ handler.
*
* @param[in] sdp communication channel associated to the USART
*/
void sd_lld_serve_interrupt(SerialDriver *sdp) {
uint32_t csr;
AT91PS_USART u = sdp->usart;
csr = u->US_CSR;
if (csr & AT91C_US_RXRDY) {
chSysLockFromIsr();
sdIncomingDataI(sdp, u->US_RHR);
chSysUnlockFromIsr();
}
if ((u->US_IMR & AT91C_US_TXRDY) && (csr & AT91C_US_TXRDY)) {
msg_t b;
chSysLockFromIsr();
b = chOQGetI(&sdp->oqueue);
if (b < Q_OK) {
chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
u->US_IDR = AT91C_US_TXRDY;
}
else
u->US_THR = b;
chSysUnlockFromIsr();
}
csr &= (AT91C_US_OVRE | AT91C_US_FRAME | AT91C_US_PARE | AT91C_US_RXBRK);
if (csr != 0) {
set_error(sdp, csr);
u->US_CR = AT91C_US_RSTSTA;
}
AT91C_BASE_AIC->AIC_EOICR = 0;
}
#if USE_SAM7_USART0 || defined(__DOXYGEN__)
static void notify1(GenericQueue *qp) {
(void)qp;
AT91C_BASE_US0->US_IER = AT91C_US_TXRDY;
}
#endif
#if USE_SAM7_USART1 || defined(__DOXYGEN__)
static void notify2(GenericQueue *qp) {
(void)qp;
AT91C_BASE_US1->US_IER = AT91C_US_TXRDY;
}
#endif
#if (SAM7_PLATFORM == SAM7A3)
#if USE_SAM7_USART2 || defined(__DOXYGEN__)
static void notify3(GenericQueue *qp) {
(void)qp;
AT91C_BASE_US2->US_IER = AT91C_US_TXRDY;
}
#endif
#endif /* (SAM7_PLATFORM == SAM7A3) */
#if USE_SAM7_DBGU_UART || defined(__DOXYGEN__)
static void notify_dbg(GenericQueue *qp) {
(void)qp;
AT91C_BASE_DBGU->DBGU_IER = AT91C_US_TXRDY;
}
#endif
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if USE_SAM7_USART0 || defined(__DOXYGEN__)
/**
* @brief USART0 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(USART0IrqHandler) {
CH_IRQ_PROLOGUE();
sd_lld_serve_interrupt(&SD1);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif
#if USE_SAM7_USART1 || defined(__DOXYGEN__)
/**
* @brief USART1 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(USART1IrqHandler) {
CH_IRQ_PROLOGUE();
sd_lld_serve_interrupt(&SD2);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif
#if (SAM7_PLATFORM == SAM7A3)
#if USE_SAM7_USART2 || defined(__DOXYGEN__)
/**
* @brief USART2 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(USART2IrqHandler) {
CH_IRQ_PROLOGUE();
sd_lld_serve_interrupt(&SD3);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif
#endif /* (SAM7_PLATFORM == SAM7A3) */
/* note - DBGU_UART IRQ is the SysIrq in board.c
since it's not vectored separately by the AIC.*/
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level serial driver initialization.
*
* @notapi
*/
void sd_lld_init(void) {
#if USE_SAM7_USART0
sdObjectInit(&SD1, NULL, notify1);
SD1.usart = AT91C_BASE_US0;
AT91C_BASE_PIOA->PIO_PDR = SAM7_USART0_RX | SAM7_USART0_TX;
AT91C_BASE_PIOA->PIO_ASR = SAM7_USART0_RX | SAM7_USART0_TX;
AT91C_BASE_PIOA->PIO_PPUDR = SAM7_USART0_RX | SAM7_USART0_TX;
AIC_ConfigureIT(AT91C_ID_US0,
AT91C_AIC_SRCTYPE_HIGH_LEVEL | SAM7_USART0_PRIORITY,
USART0IrqHandler);
#endif
#if USE_SAM7_USART1
sdObjectInit(&SD2, NULL, notify2);
SD2.usart = AT91C_BASE_US1;
AT91C_BASE_PIOA->PIO_PDR = SAM7_USART1_RX | SAM7_USART1_TX;
AT91C_BASE_PIOA->PIO_ASR = SAM7_USART1_RX | SAM7_USART1_TX;
AT91C_BASE_PIOA->PIO_PPUDR = SAM7_USART1_RX | SAM7_USART1_TX;
AIC_ConfigureIT(AT91C_ID_US1,
AT91C_AIC_SRCTYPE_HIGH_LEVEL | SAM7_USART1_PRIORITY,
USART1IrqHandler);
#endif
#if (SAM7_PLATFORM == SAM7A3)
#if USE_SAM7_USART2
sdObjectInit(&SD3, NULL, notify3);
SD3.usart = AT91C_BASE_US2;
AT91C_BASE_PIOA->PIO_PDR = SAM7_USART2_RX | SAM7_USART2_TX;
AT91C_BASE_PIOA->PIO_ASR = SAM7_USART2_RX | SAM7_USART2_TX;
AT91C_BASE_PIOA->PIO_PPUDR = SAM7_USART2_RX | SAM7_USART2_TX;
AIC_ConfigureIT(AT91C_ID_US2,
AT91C_AIC_SRCTYPE_HIGH_LEVEL | SAM7_USART2_PRIORITY,
USART2IrqHandler);
#endif
#endif /* (SAM7_PLATFORM == SAM7A3) */
#if USE_SAM7_DBGU_UART
sdObjectInit(&SDDBG, NULL, notify_dbg);
/* this is a little cheap, but OK for now since there's enough overlap
between dbgu and usart register maps. it means we can reuse all the
same usart interrupt handling and config that already exists.*/
SDDBG.usart = (AT91PS_USART)AT91C_BASE_DBGU;
AT91C_BASE_PIOA->PIO_PDR = SAM7_DBGU_RX | SAM7_DBGU_TX;
AT91C_BASE_PIOA->PIO_ASR = SAM7_DBGU_RX | SAM7_DBGU_TX;
AT91C_BASE_PIOA->PIO_PPUDR = SAM7_DBGU_RX | SAM7_DBGU_TX;
#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 (sdp->state == SD_STOP) {
#if USE_SAM7_USART0
if (&SD1 == sdp) {
/* Starts the clock and clears possible sources of immediate interrupts.*/
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_US0);
/* Enables associated interrupt vector.*/
AIC_EnableIT(AT91C_ID_US0);
}
#endif
#if USE_SAM7_USART1
if (&SD2 == sdp) {
/* Starts the clock and clears possible sources of immediate interrupts.*/
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_US1);
/* Enables associated interrupt vector.*/
AIC_EnableIT(AT91C_ID_US1);
}
#endif
#if (SAM7_PLATFORM == SAM7A3)
#if USE_SAM7_USART2
if (&SD3 == sdp) {
/* Starts the clock and clears possible sources of immediate interrupts.*/
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_US2);
/* Enables associated interrupt vector.*/
AIC_EnableIT(AT91C_ID_US2);
}
#endif
#endif /* (SAM7_PLATFORM == SAM7A3) */
/* Note - no explicit start for SD3 (DBGU_UART) since it's not included
in the AIC or PMC.*/
}
usart_init(sdp, config);
}
/**
* @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 (sdp->state == SD_READY) {
usart_deinit(sdp->usart);
#if USE_SAM7_USART0
if (&SD1 == sdp) {
AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_US0);
AIC_DisableIT(AT91C_ID_US0);
return;
}
#endif
#if USE_SAM7_USART1
if (&SD2 == sdp) {
AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_US1);
AIC_DisableIT(AT91C_ID_US1);
return;
}
#endif
#if USE_SAM7_DBGU_UART
if (&SDDBG == sdp) {
AT91C_BASE_DBGU->DBGU_IDR = 0xFFFFFFFF;
return;
}
#endif
}
}
#endif /* HAL_USE_SERIAL */
/** @} */

View File

@@ -0,0 +1,191 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/serial_lld.h
* @brief AT91SAM7 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 UART0 driver enable switch.
* @details If set to @p TRUE the support for USART1 is included.
* @note The default is @p TRUE.
*/
#if !defined(USE_SAM7_USART0) || defined(__DOXYGEN__)
#define USE_SAM7_USART0 TRUE
#endif
/**
* @brief UART1 driver enable switch.
* @details If set to @p TRUE the support for USART2 is included.
* @note The default is @p TRUE.
*/
#if !defined(USE_SAM7_USART1) || defined(__DOXYGEN__)
#define USE_SAM7_USART1 TRUE
#endif
#if (SAM7_PLATFORM == SAM7A3)
/**
* @brief UART2 driver enable switch.
* @details If set to @p TRUE the support for USART3 is included.
* @note The default is @p TRUE.
*/
#if !defined(USE_SAM7_USART2) || defined(__DOXYGEN__)
#define USE_SAM7_USART2 TRUE
#endif
#endif /* (SAM7_PLATFORM == SAM7A3) */
/**
* @brief DBGU UART driver enable switch.
* @details If set to @p TRUE the support for the DBGU UART is included.
* @note The default is @p TRUE.
*/
#if !defined(USE_SAM7_DBGU_UART) || defined(__DOXYGEN__)
#define USE_SAM7_DBGU_UART TRUE
#endif
/**
* @brief UART1 interrupt priority level setting.
*/
#if !defined(SAM7_USART0_PRIORITY) || defined(__DOXYGEN__)
#define SAM7_USART0_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
/**
* @brief UART2 interrupt priority level setting.
*/
#if !defined(SAM7_USART1_PRIORITY) || defined(__DOXYGEN__)
#define SAM7_USART1_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
#if (SAM7_PLATFORM == SAM7A3)
/**
* @brief UART2 interrupt priority level setting.
*/
#if !defined(SAM7_USART2_PRIORITY) || defined(__DOXYGEN__)
#define SAM7_USART2_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
#endif /* (SAM7_PLATFORM == SAM7A3) */
/**
* @brief DBGU_UART interrupt priority level setting.
*/
#if !defined(SAM7_DBGU_UART_PRIORITY) || defined(__DOXYGEN__)
#define SAM7_DBGU_UART_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 2)
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver data structures and types. */
/*===========================================================================*/
/**
* @brief AT91SAM7 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 Bit rate.
* @details This is written to the US_BRGR register of the appropriate AT91S_USART
*/
uint32_t sc_speed;
/**
* @brief Initialization value for the MR register.
* @details This is written to the US_MR register of the appropriate AT91S_USART
*/
uint32_t sc_mr;
} 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.*/ \
/* Pointer to the USART registers block.*/ \
AT91PS_USART usart;
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if USE_SAM7_USART0 && !defined(__DOXYGEN__)
extern SerialDriver SD1;
#endif
#if USE_SAM7_USART1 && !defined(__DOXYGEN__)
extern SerialDriver SD2;
#endif
#if (SAM7_PLATFORM == SAM7A3)
#if USE_SAM7_USART2 && !defined(__DOXYGEN__)
extern SerialDriver SD3;
#endif
#endif
#if USE_SAM7_DBGU_UART
extern SerialDriver SDDBG;
#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);
#if USE_SAM7_DBGU_UART
void sd_lld_serve_interrupt(SerialDriver *sdp);
#endif
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_SERIAL */
#endif /* _SERIAL_LLD_H_ */
/** @} */

View File

@@ -0,0 +1,436 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/spi_lld.c
* @brief AT91SAM7 low level SPI driver code.
*
* @addtogroup SPI
* @{
*/
#include "ch.h"
#include "hal.h"
#if HAL_USE_SPI || defined(__DOXYGEN__)
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
#if AT91SAM7_SPI_USE_SPI0 || defined(__DOXYGEN__)
/** @brief SPI1 driver identifier.*/
SPIDriver SPID1;
#endif
#if AT91SAM7_SPI_USE_SPI1 || defined(__DOXYGEN__)
/** @brief SPI2 driver identifier.*/
SPIDriver SPID2;
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/**
* @brief Idle line value.
* @details This thing's DMA apparently does not allow to *not* increment
* the memory pointer so a buffer filled with ones is required
* somewhere.
* @note This buffer size also limits the maximum transfer size, 512B,
* for @p spiReceive() and @p spiIgnore(). @p spiSend() and
* @p spiExchange are not affected.
*/
static const uint16_t idle_buf[] = {
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF,
0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF
};
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
/**
* @brief Initializes a SPI device.
*/
static void spi_init(AT91PS_SPI spi) {
/* Software reset must be written twice (errata for revision B parts).*/
spi->SPI_CR = AT91C_SPI_SWRST;
spi->SPI_CR = AT91C_SPI_SWRST;
spi->SPI_RCR = 0;
spi->SPI_RNCR = 0;
spi->SPI_TCR = 0;
spi->SPI_TNCR = 0;
spi->SPI_PTCR = AT91C_PDC_RXTDIS | AT91C_PDC_TXTDIS;
spi->SPI_MR = AT91C_SPI_MSTR | AT91C_SPI_MODFDIS;
}
#if defined(__GNUC__)
__attribute__((noinline))
#endif
/**
* @brief Shared interrupt handling code.
*
* @param[in] spip pointer to the @p SPIDriver object
*/
static void spi_lld_serve_interrupt(SPIDriver *spip) {
uint32_t sr = spip->spi->SPI_SR;
if ((sr & AT91C_SPI_ENDRX) != 0) {
(void)spip->spi->SPI_RDR; /* Clears eventual overflow.*/
spip->spi->SPI_PTCR = AT91C_PDC_RXTDIS |
AT91C_PDC_TXTDIS; /* PDC disabled. */
spip->spi->SPI_IDR = AT91C_SPI_ENDRX; /* Interrupt disabled. */
spip->spi->SPI_CR = AT91C_SPI_SPIDIS; /* SPI disabled. */
/* Portable SPI ISR code defined in the high level driver, note, it is
a macro.*/
_spi_isr_code(spip);
}
}
/*===========================================================================*/
/* Driver interrupt handlers. */
/*===========================================================================*/
#if AT91SAM7_SPI_USE_SPI0 || defined(__DOXYGEN__)
/**
* @brief SPI0 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(SPI0IrqHandler) {
CH_IRQ_PROLOGUE();
spi_lld_serve_interrupt(&SPID1);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif
#if AT91SAM7_SPI_USE_SPI1 || defined(__DOXYGEN__)
/**
* @brief SPI1 interrupt handler.
*
* @isr
*/
CH_IRQ_HANDLER(SPI1IrqHandler) {
CH_IRQ_PROLOGUE();
spi_lld_serve_interrupt(&SPID2);
AT91C_BASE_AIC->AIC_EOICR = 0;
CH_IRQ_EPILOGUE();
}
#endif
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
/**
* @brief Low level SPI driver initialization.
*
* @notapi
*/
void spi_lld_init(void) {
#if AT91SAM7_SPI_USE_SPI0
spiObjectInit(&SPID1);
SPID1.spi = AT91C_BASE_SPI0;
spi_init(AT91C_BASE_SPI0);
AT91C_BASE_PIOA->PIO_PDR = SPI0_MISO | SPI0_MOSI | SPI0_SCK;
AT91C_BASE_PIOA->PIO_ASR = SPI0_MISO | SPI0_MOSI | SPI0_SCK;
AT91C_BASE_PIOA->PIO_PPUDR = SPI0_MISO | SPI0_MOSI | SPI0_SCK;
AIC_ConfigureIT(AT91C_ID_SPI0,
AT91C_AIC_SRCTYPE_HIGH_LEVEL | AT91SAM7_SPI0_PRIORITY,
SPI0IrqHandler);
#endif
#if AT91SAM7_SPI_USE_SPI1
spiObjectInit(&SPID2);
SPID2.spi = AT91C_BASE_SPI1;
spi_init(AT91C_BASE_SPI1);
AT91C_BASE_PIOA->PIO_PDR = SPI1_MISO | SPI1_MOSI | SPI1_SCK;
AT91C_BASE_PIOA->PIO_BSR = SPI1_MISO | SPI1_MOSI | SPI1_SCK;
AT91C_BASE_PIOA->PIO_PPUDR = SPI1_MISO | SPI1_MOSI | SPI1_SCK;
AIC_ConfigureIT(AT91C_ID_SPI1,
AT91C_AIC_SRCTYPE_HIGH_LEVEL | AT91SAM7_SPI1_PRIORITY,
SPI1IrqHandler);
#endif
}
/**
* @brief Configures and activates the SPI peripheral.
*
* @param[in] spip pointer to the @p SPIDriver object
*
* @notapi
*/
void spi_lld_start(SPIDriver *spip) {
if (spip->state == SPI_STOP) {
#if AT91SAM7_SPI_USE_SPI0
if (&SPID1 == spip) {
/* Clock activation.*/
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SPI0);
/* Enables associated interrupt vector.*/
AIC_EnableIT(AT91C_ID_SPI0);
// Setup the chip select pin
// No bug here but control CS0 automatically anyway
if (spip->config->ssport == AT91C_BASE_PIOA && spip->config->sspad == SPI0_CS0) {
AT91C_BASE_PIOA->PIO_PDR = 1 << SPI0_CS0;
AT91C_BASE_PIOA->PIO_OER = 1 << SPI0_CS0;
AT91C_BASE_PIOA->PIO_ASR = 1 << SPI0_CS0;
} else if (spip->config->ssport) {
uint32_t mask;
mask = 1 << spip->config->sspad;
spip->config->ssport->PIO_SODR = mask;
spip->config->ssport->PIO_OER = mask;
spip->config->ssport->PIO_PER = mask;
spip->config->ssport->PIO_MDDR = mask;
spip->config->ssport->PIO_PPUDR = mask;
}
}
#endif
#if AT91SAM7_SPI_USE_SPI1
if (&SPID2 == spip) {
/* Clock activation.*/
AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_SPI1);
/* Enables associated interrupt vector.*/
AIC_EnableIT(AT91C_ID_SPI1);
// Setup the chip select pin
// A bug in SPI1 requires that the CS be automatically controlled if it matches the CS0 pin.
// If it is not automatically controlled SPI1 will turn off when data is written.
// If you are not using CS0 make absolutely sure you don't use it as an output pin and
// clear it otherwise you will get the bug in anyway.
if (spip->config->ssport == AT91C_BASE_PIOA && spip->config->sspad == SPI1_CS0) {
AT91C_BASE_PIOA->PIO_PDR = 1 << SPI1_CS0;
AT91C_BASE_PIOA->PIO_OER = 1 << SPI1_CS0;
AT91C_BASE_PIOA->PIO_BSR = 1 << SPI1_CS0;
} else if (spip->config->ssport) {
uint32_t mask;
mask = 1 << spip->config->sspad;
spip->config->ssport->PIO_SODR = mask;
spip->config->ssport->PIO_OER = mask;
spip->config->ssport->PIO_PER = mask;
spip->config->ssport->PIO_MDDR = mask;
spip->config->ssport->PIO_PPUDR = mask;
}
}
#endif
}
/* Configuration.*/
spip->spi->SPI_CSR[0] = spip->config->csr;
}
/**
* @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_STOP) {
#if AT91SAM7_SPI_USE_SPI0
if (&SPID1 == spip) {
AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_SPI0);
AIC_DisableIT(AT91C_ID_SPI0);
}
#endif
#if AT91SAM7_SPI_USE_SPI1
if (&SPID1 == spip) {
AT91C_BASE_PMC->PMC_PCDR = (1 << AT91C_ID_SPI1);
AIC_DisableIT(AT91C_ID_SPI0);
}
#endif
}
}
/**
* @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) {
// Not needed for CS0 but it doesn't hurt either
palClearPad(spip->config->ssport, 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) {
// Not needed for CS0 but it doesn't hurt either
palSetPad(spip->config->ssport, spip->config->sspad);
}
/**
* @brief Ignores data on the SPI bus.
* @details This function transmits a series of idle words on the SPI bus and
* ignores the received data. This function can be invoked even
* when a slave select signal has not been yet asserted.
*
* @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->spi->SPI_TCR = n;
spip->spi->SPI_RCR = n;
spip->spi->SPI_TPR = (AT91_REG)idle_buf;
spip->spi->SPI_RPR = (AT91_REG)idle_buf;
spip->spi->SPI_IER = AT91C_SPI_ENDRX;
spip->spi->SPI_CR = AT91C_SPI_SPIEN;
spip->spi->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
}
/**
* @brief Exchanges data on the SPI bus.
* @details This function performs a simultaneous transmit/receive operation.
* @note The buffers are organized as uint8_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->spi->SPI_TCR = n;
spip->spi->SPI_RCR = n;
spip->spi->SPI_TPR = (AT91_REG)txbuf;
spip->spi->SPI_RPR = (AT91_REG)rxbuf;
spip->spi->SPI_IER = AT91C_SPI_ENDRX;
spip->spi->SPI_CR = AT91C_SPI_SPIEN;
spip->spi->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
}
/**
* @brief Sends data over the SPI bus.
* @note The buffers are organized as uint8_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->spi->SPI_TCR = n;
spip->spi->SPI_RCR = n;
spip->spi->SPI_TPR = (AT91_REG)txbuf;
spip->spi->SPI_RPR = (AT91_REG)idle_buf;
spip->spi->SPI_IER = AT91C_SPI_ENDRX;
spip->spi->SPI_CR = AT91C_SPI_SPIEN;
spip->spi->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
}
/**
* @brief Receives data from the SPI bus.
* @note The buffers are organized as uint8_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->spi->SPI_TCR = n;
spip->spi->SPI_RCR = n;
spip->spi->SPI_TPR = (AT91_REG)idle_buf;
spip->spi->SPI_RPR = (AT91_REG)rxbuf;
spip->spi->SPI_IER = AT91C_SPI_ENDRX;
spip->spi->SPI_CR = AT91C_SPI_SPIEN;
spip->spi->SPI_PTCR = AT91C_PDC_RXTEN | AT91C_PDC_TXTEN;
}
/**
* @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.
*/
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
spip->spi->SPI_CR = AT91C_SPI_SPIEN;
spip->spi->SPI_TDR = frame;
while ((spip->spi->SPI_SR & AT91C_SPI_RDRF) == 0)
;
return spip->spi->SPI_RDR;
}
#endif /* HAL_USE_SPI */
/** @} */

View File

@@ -0,0 +1,234 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 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 AT91SAM7/spi_lld.h
* @brief AT91SAM7 low level SPI driver header.
*
* @addtogroup SPI
* @{
*/
#ifndef _SPI_LLD_H_
#define _SPI_LLD_H_
#if HAL_USE_SPI || defined(__DOXYGEN__)
/*===========================================================================*/
/* Device compatibility.. */
/*===========================================================================*/
#if SAM7_PLATFORM == SAM7S512 || SAM7_PLATFORM == SAM7S256 || SAM7_PLATFORM == SAM7S128 || SAM7_PLATFORM == SAM7S64
#define SPI0_MISO AT91C_PA12_MISO
#define SPI0_MOSI AT91C_PA13_MOSI
#define SPI0_SCK AT91C_PA14_SPCK
#define SPI0_CS0 11 // PA11
#elif SAM7_PLATFORM == SAM7X512 || SAM7_PLATFORM == SAM7X256 || SAM7_PLATFORM == SAM7X128
#define SPI0_MISO AT91C_PA16_SPI0_MISO
#define SPI0_MOSI AT91C_PA17_SPI0_MOSI
#define SPI0_SCK AT91C_PA18_SPI0_SPCK
#define SPI0_CS0 12 // PA12
#define SPI1_MISO AT91C_PA24_SPI1_MISO
#define SPI1_MOSI AT91C_PA23_SPI1_MOSI
#define SPI1_SCK AT91C_PA22_SPI1_SPCK
#define SPI1_CS0 21 // PA21
#elif SAM7_PLATFORM == SAM7A3
#define SPI0_MISO AT91C_PA15_SPI0_MISO
#define SPI0_MOSI AT91C_PA16_SPI0_MOSI
#define SPI0_SCK AT91C_PA17_SPI0_SPCK
#define SPI0_CS0 11 // PA11
#define SPI1_MISO AT91C_PA8_SPI1_MISO
#define SPI1_MOSI AT91C_PA9_SPI1_MOSI
#define SPI1_SCK AT91C_PA10_SPI1_SPCK
#define SPI1_CS0 4 // PA4
#else
#error "SAM7 platform not supported"
#endif
#if defined (AT91C_BASE_SPI)
#define AT91C_BASE_SPI0 AT91C_BASE_SPI
#define AT91C_ID_SPI0 AT91C_ID_SPI
#endif
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver pre-compile time settings. */
/*===========================================================================*/
/**
* @brief SPID1 enable switch (SPI0 device).
* @details If set to @p TRUE the support for SPI0 is included.
* @note The default is @p TRUE.
*/
#if !defined(AT91SAM7_SPI_USE_SPI0) || defined(__DOXYGEN__)
#define AT91SAM7_SPI_USE_SPI0 TRUE
#endif
/**
* @brief SPID2 enable switch (SPI1 device).
* @details If set to @p TRUE the support for SPI1 is included.
* @note The default is @p TRUE.
*/
#if !defined(AT91SAM7_SPI_USE_SPI1) || defined(__DOXYGEN__)
#define AT91SAM7_SPI_USE_SPI1 TRUE
#endif
/**
* @brief SPI0 device interrupt priority level setting.
*/
#if !defined(AT91SAM7_SPI0_PRIORITY) || defined(__DOXYGEN__)
#define AT91SAM7_SPI0_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 1)
#endif
/**
* @brief SPI1 device interrupt priority level setting.
*/
#if !defined(AT91SAM7_SPI1_PRIORITY) || defined(__DOXYGEN__)
#define AT91SAM7_SPI1_PRIORITY (AT91C_AIC_PRIOR_HIGHEST - 1)
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if defined (AT91C_BASE_SPI) && AT91SAM7_SPI_USE_SPI1
#error "SPI1 not present"
#endif
#if !AT91SAM7_SPI_USE_SPI0 && !AT91SAM7_SPI_USE_SPI1
#error "SPI driver activated but no SPI peripheral assigned"
#endif
/*===========================================================================*/
/* 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.
*/
typedef struct {
/**
* @brief Operation complete callback or @p NULL.
*/
spicallback_t end_cb;
/* End of the mandatory fields.*/
/**
* @brief The chip select line port.
*/
ioportid_t ssport;
/**
* @brief The chip select line pad number.
*/
uint16_t sspad;
/**
* @brief SPI Chip Select Register initialization data.
*/
uint32_t csr;
} SPIConfig;
/**
* @brief Structure representing a SPI driver.
*/
struct SPIDriver {
/**
* @brief Driver state.
*/
spistate_t state;
/**
* @brief Current configuration data.
*/
const 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
/* End of the mandatory fields.*/
/**
* @brief Pointer to the SPIx registers block.
*/
AT91PS_SPI spi;
};
/*===========================================================================*/
/* Driver macros. */
/*===========================================================================*/
/*===========================================================================*/
/* External declarations. */
/*===========================================================================*/
#if AT91SAM7_SPI_USE_SPI0 && !defined(__DOXYGEN__)
extern SPIDriver SPID1;
#endif
#if AT91SAM7_SPI_USE_SPI1 && !defined(__DOXYGEN__)
extern SPIDriver SPID2;
#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);
uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame);
#ifdef __cplusplus
}
#endif
#endif /* HAL_USE_SPI */
#endif /* _SPI_LLD_H_ */
/** @} */