mirror of
https://github.com/digistump/DigistumpArduino.git
synced 2025-11-02 20:44:50 -08:00
Initial import of support files for all Digistump boards - Digispark, Pro, DigiX - including libraries, examples, tools, and other support files for the Arduino IDE
This commit is contained in:
962
hardware/digistump/sam/system/libsam/source/adc.c
Normal file
962
hardware/digistump/sam/system/libsam/source/adc.c
Normal file
@@ -0,0 +1,962 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* \defgroup sam_drivers_adc_group Analog-to-digital Converter (ADC)
|
||||
*
|
||||
* Driver for the Analog-to-digital Converter. This driver provides access to the main
|
||||
* features of the ADC controller.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES
|
||||
/**
|
||||
* \brief Initialize the given ADC with the specified ADC clock and startup time.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_mck Main clock of the device (value in Hz).
|
||||
* \param ul_adc_clock Analog-to-Digital conversion clock (value in Hz).
|
||||
* \param uc_startup ADC start up time. Please refer to the product datasheet
|
||||
* for details.
|
||||
*
|
||||
* \return 0 on success.
|
||||
*/
|
||||
uint32_t adc_init(Adc *p_adc, const uint32_t ul_mck,
|
||||
const uint32_t ul_adc_clock, const uint8_t uc_startup)
|
||||
{
|
||||
uint32_t ul_prescal;
|
||||
|
||||
/* Reset the controller. */
|
||||
p_adc->ADC_CR = ADC_CR_SWRST;
|
||||
|
||||
/* Reset Mode Register. */
|
||||
p_adc->ADC_MR = 0;
|
||||
|
||||
/* Reset PDC transfer. */
|
||||
p_adc->ADC_PTCR = (ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS);
|
||||
p_adc->ADC_RCR = 0;
|
||||
p_adc->ADC_RNCR = 0;
|
||||
|
||||
ul_prescal = ul_mck / (2 * ul_adc_clock) - 1;
|
||||
p_adc->ADC_MR |= ADC_MR_PRESCAL(ul_prescal) |
|
||||
((uc_startup << ADC_MR_STARTUP_Pos) &
|
||||
ADC_MR_STARTUP_Msk);
|
||||
return 0;
|
||||
}
|
||||
#elif SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Initialize the given ADC with the specified ADC clock and startup time.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_mck Main clock of the device (value in Hz).
|
||||
* \param ul_adc_clock Analog-to-Digital conversion clock (value in Hz).
|
||||
* \param uc_startup ADC start up time. Please refer to the product datasheet
|
||||
* for details.
|
||||
*
|
||||
* \return 0 on success.
|
||||
*/
|
||||
uint32_t adc_init(Adc *p_adc, const uint32_t ul_mck,
|
||||
const uint32_t ul_adc_clock, const uint8_t uc_startuptime)
|
||||
{
|
||||
uint32_t startup_table[] = { 0, 8, 16, 24, 64, 80, 96, 112, 512, 576, 640, 704, 768, 832, 896, 960 };
|
||||
uint32_t ul_prescal, ul_startup, ul_mr_startup, ul_real_adc_clock;
|
||||
p_adc->ADC_CR = ADC_CR_SWRST;
|
||||
|
||||
/* Reset Mode Register. */
|
||||
p_adc->ADC_MR = 0;
|
||||
|
||||
/* Reset PDC transfer. */
|
||||
p_adc->ADC_PTCR = (ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS);
|
||||
p_adc->ADC_RCR = 0;
|
||||
p_adc->ADC_RNCR = 0;
|
||||
if (ul_mck % (2 * ul_adc_clock)) {
|
||||
// Division with reminder
|
||||
ul_prescal = ul_mck / (2 * ul_adc_clock);
|
||||
} else {
|
||||
// Whole division
|
||||
ul_prescal = ul_mck / (2 * ul_adc_clock) - 1;
|
||||
}
|
||||
ul_real_adc_clock = ul_mck / (2 * (ul_prescal + 1));
|
||||
|
||||
// ADC clocks needed to get ul_startuptime uS
|
||||
ul_startup = (ul_real_adc_clock / 1000000) * uc_startuptime;
|
||||
|
||||
// Find correct MR_STARTUP value from conversion table
|
||||
for (ul_mr_startup=0; ul_mr_startup<16; ul_mr_startup++) {
|
||||
if (startup_table[ul_mr_startup] >= ul_startup)
|
||||
break;
|
||||
}
|
||||
if (ul_mr_startup==16)
|
||||
return -1;
|
||||
p_adc->ADC_MR |= ADC_MR_PRESCAL(ul_prescal) |
|
||||
((ul_mr_startup << ADC_MR_STARTUP_Pos) & ADC_MR_STARTUP_Msk);
|
||||
return 0;
|
||||
}
|
||||
#elif SAM3U_SERIES
|
||||
/**
|
||||
* \brief Initialize the given ADC with the specified ADC clock and startup time.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_mck Main clock of the device (value in Hz).
|
||||
* \param ul_adc_clock Analog-to-Digital conversion clock (in Hz).
|
||||
* \param ul_startuptime ADC startup time value (value in us).
|
||||
* Please refer to the product datasheet for details.
|
||||
* \param ul_offmode_startuptime ADC off mode startup time value (in us).
|
||||
* Please refer to the product datasheet for details.
|
||||
*
|
||||
* \return 0 on success.
|
||||
*/
|
||||
uint32_t adc_init(Adc *p_adc, const uint32_t ul_mck, const uint32_t ul_adc_clock,
|
||||
const uint32_t ul_startuptime)
|
||||
{
|
||||
uint32_t ul_prescal, ul_startup;
|
||||
p_adc->ADC_CR = ADC_CR_SWRST;
|
||||
|
||||
/* Reset Mode Register. */
|
||||
p_adc->ADC_MR = 0;
|
||||
|
||||
/* Reset PDC transfer. */
|
||||
p_adc->ADC_PTCR = (ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS);
|
||||
p_adc->ADC_RCR = 0;
|
||||
p_adc->ADC_RNCR = 0;
|
||||
ul_prescal = ul_mck / (2 * ul_adc_clock) - 1;
|
||||
ul_startup = ((ul_adc_clock / 1000000) * ul_startuptime / 8) - 1;
|
||||
p_adc->ADC_MR |= ADC_MR_PRESCAL(ul_prescal) |
|
||||
((ul_startup << ADC_MR_STARTUP_Pos) &
|
||||
ADC_MR_STARTUP_Msk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Configure the conversion resolution.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param resolution ADC resolution.
|
||||
*
|
||||
*/
|
||||
void adc_set_resolution(Adc *p_adc,const enum adc_resolution_t resolution)
|
||||
{
|
||||
p_adc->ADC_MR |= (resolution << 4) & ADC_MR_LOWRES;
|
||||
}
|
||||
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configure conversion trigger and free run mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param trigger Conversion trigger.
|
||||
* \param uc_freerun ADC_MR_FREERUN_ON enables freerun mode,
|
||||
* ADC_MR_FREERUN_OFF disables freerun mode.
|
||||
*
|
||||
*/
|
||||
void adc_configure_trigger(Adc *p_adc, const enum adc_trigger_t trigger,
|
||||
uint8_t uc_freerun)
|
||||
{
|
||||
p_adc->ADC_MR |= trigger | ((uc_freerun << 7) & ADC_MR_FREERUN);
|
||||
}
|
||||
#elif SAM3U_SERIES
|
||||
/**
|
||||
* \brief Configure conversion trigger and free run mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param trigger Conversion trigger.
|
||||
*/
|
||||
void adc_configure_trigger(Adc *p_adc, const enum adc_trigger_t trigger)
|
||||
{
|
||||
p_adc->ADC_MR |= trigger;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configures ADC power saving mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param uc_sleep ADC_MR_SLEEP_NORMAL keeps the ADC Core and reference voltage
|
||||
* circuitry ON between conversions.
|
||||
* ADC_MR_SLEEP_SLEEP keeps the ADC Core and reference voltage circuitry OFF
|
||||
* between conversions.
|
||||
* \param uc_fwup ADC_MR_FWUP_OFF configures sleep mode as uc_sleep setting,
|
||||
* ADC_MR_FWUP_ON keeps voltage reference ON and ADC Core OFF between conversions.
|
||||
*/
|
||||
void adc_configure_power_save(Adc *p_adc, const uint8_t uc_sleep, const uint8_t uc_fwup)
|
||||
{
|
||||
p_adc->ADC_MR |= (((uc_sleep << 5) & ADC_MR_SLEEP) |
|
||||
((uc_fwup << 6) & ADC_MR_FWUP));
|
||||
}
|
||||
#elif SAM3U_SERIES
|
||||
/**
|
||||
* \brief Configure ADC power saving mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param uc_sleep ADC_MR_SLEEP_NORMAL keeps the ADC Core and reference
|
||||
* voltage circuitry ON between conversions.
|
||||
* ADC_MR_SLEEP_SLEEP keeps the ADC Core and reference voltage circuitry
|
||||
* OFF between conversions.
|
||||
* \param uc_offmode 0 for Standby Mode (if Sleep Bit = 1), 1 for Off Mode.
|
||||
*/
|
||||
void adc_configure_power_save(Adc *p_adc, const uint8_t uc_sleep)
|
||||
{
|
||||
p_adc->ADC_MR |= ((uc_sleep << 5) & ADC_MR_SLEEP);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configure conversion sequence.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ch_list Channel sequence list.
|
||||
* \param number Number of channels in the list.
|
||||
*/
|
||||
void adc_configure_sequence(Adc *p_adc, const enum adc_channel_num_t ch_list[],
|
||||
uint8_t uc_num)
|
||||
{
|
||||
uint8_t uc_counter;
|
||||
if (uc_num < 8) {
|
||||
for (uc_counter = 0; uc_counter < uc_num; uc_counter++) {
|
||||
p_adc->ADC_SEQR1 |=
|
||||
ch_list[uc_counter] << (4 * uc_counter);
|
||||
}
|
||||
} else {
|
||||
for (uc_counter = 0; uc_counter < 8; uc_counter++) {
|
||||
p_adc->ADC_SEQR1 |=
|
||||
ch_list[uc_counter] << (4 * uc_counter);
|
||||
}
|
||||
for (uc_counter = 0; uc_counter < uc_num - 8; uc_counter++) {
|
||||
p_adc->ADC_SEQR2 |=
|
||||
ch_list[uc_counter] << (4 * uc_counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configure ADC timing.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param uc_tracking ADC tracking time = uc_tracking / ADC clock.
|
||||
* \param uc_settling Analog settling time = (uc_settling + 1) / ADC clock.
|
||||
* \param uc_transfer Data transfer time = (uc_transfer * 2 + 3) / ADC clock.
|
||||
*/
|
||||
void adc_configure_timing(Adc *p_adc, const uint8_t uc_tracking,
|
||||
const enum adc_settling_time_t settling,const uint8_t uc_transfer)
|
||||
{
|
||||
p_adc->ADC_MR |= ADC_MR_TRANSFER(uc_transfer)
|
||||
| settling | ADC_MR_TRACKTIM(uc_tracking);
|
||||
}
|
||||
#elif SAM3N_SERIES
|
||||
/**
|
||||
* \brief Configure ADC timing.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param uc_tracking ADC tracking time = uc_tracking / ADC clock.
|
||||
*/
|
||||
void adc_configure_timing(Adc *p_adc, const uint8_t uc_tracking)
|
||||
{
|
||||
p_adc->ADC_MR |= ADC_MR_TRACKTIM(uc_tracking);
|
||||
}
|
||||
#elif SAM3U_SERIES
|
||||
/**
|
||||
* \brief Configure ADC timing.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_sh ADC sample and hold time = uc_sh / ADC clock.
|
||||
*/
|
||||
void adc_configure_timing(Adc *p_adc, const uint32_t ul_sh)
|
||||
{
|
||||
p_adc->ADC_MR |= ADC_MR_SHTIM(ul_sh);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Enable analog change.
|
||||
*
|
||||
* \note It allows different analog settings for each channel.
|
||||
*
|
||||
* \param p_Adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_enable_anch(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_MR |= ADC_MR_ANACH;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Disable analog change.
|
||||
*
|
||||
* \note DIFF0, GAIN0 and OFF0 are used for all channels.
|
||||
*
|
||||
* \param p_Adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_disable_anch(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_MR &= ~ADC_MR_ANACH;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Start analog-to-digital conversion.
|
||||
*
|
||||
* \note If one of the hardware event is selected as ADC trigger,
|
||||
* this function can NOT start analog to digital conversion.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
|
||||
void adc_start(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_CR = ADC_CR_START;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stop analog-to-digital conversion.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_stop(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_CR = ADC_CR_SWRST;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable the specified ADC channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*/
|
||||
void adc_enable_channel(Adc *p_adc, const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
p_adc->ADC_CHER = 1 << adc_ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable all ADC channels.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_enable_all_channel(Adc *p_adc)
|
||||
{
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
p_adc->ADC_CHER = 0xFFFF;
|
||||
#elif SAM3U_SERIES
|
||||
p_adc->ADC_CHER = 0xFF;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable the specified ADC channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*/
|
||||
void adc_disable_channel(Adc *p_adc, const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
p_adc->ADC_CHDR = 1 << adc_ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable all ADC channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_disable_all_channel(Adc *p_adc)
|
||||
{
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
p_adc->ADC_CHDR = 0xFFFF;
|
||||
#elif SAM3U_SERIES
|
||||
p_adc->ADC_CHDR = 0xFF;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the ADC channel status.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*
|
||||
* \retval 1 if channel is enabled.
|
||||
* \retval 0 if channel is disabled.
|
||||
*/
|
||||
uint32_t adc_get_channel_status(const Adc *p_adc, const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
return p_adc->ADC_CHSR & (1 << adc_ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the ADC result data of the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*
|
||||
* \return ADC value of the specified channel.
|
||||
*/
|
||||
uint32_t adc_get_channel_value(const Adc *p_adc, const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
uint32_t ul_data = 0;
|
||||
|
||||
if (15 >= adc_ch) {
|
||||
ul_data = *(p_adc->ADC_CDR + adc_ch);
|
||||
}
|
||||
|
||||
return ul_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the last ADC result data.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return ADC latest value.
|
||||
*/
|
||||
uint32_t adc_get_latest_value(const Adc *p_adc)
|
||||
{
|
||||
return p_adc->ADC_LCDR;
|
||||
}
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Enable TAG option so that the number of the last converted channel
|
||||
* can be indicated.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_enable_tag(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_EMR |= ADC_EMR_TAG;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Disable TAG option.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_disable_tag(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_EMR &= ~ADC_EMR_TAG;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Indicate the last converted channel.
|
||||
*
|
||||
* \note If TAG option is NOT enabled before, an incorrect channel
|
||||
* number is returned.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return The last converted channel number.
|
||||
*/
|
||||
enum adc_channel_num_t adc_get_tag(const Adc *p_adc)
|
||||
{
|
||||
return (p_adc->ADC_LCDR & ADC_LCDR_CHNB_Msk) >> ADC_LCDR_CHNB_Pos;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Enable conversion sequencer.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_start_sequencer(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_MR |= ADC_MR_USEQ;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Disable conversion sequencer.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_stop_sequencer(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_MR &= ~ADC_MR_USEQ;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configure comparison mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param uc_mode ADC comparison mode.
|
||||
*/
|
||||
void adc_set_comparison_mode(Adc *p_adc, const uint8_t uc_mode)
|
||||
{
|
||||
p_adc->ADC_EMR &= (uint32_t) ~ (ADC_EMR_CMPMODE_Msk);
|
||||
p_adc->ADC_EMR |= (uc_mode & ADC_EMR_CMPMODE_Msk);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Get comparison mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \retval Compare mode value.
|
||||
*/
|
||||
uint32_t adc_get_comparison_mode(const Adc *p_adc)
|
||||
{
|
||||
return p_adc->ADC_EMR & ADC_EMR_CMPMODE_Msk;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configure ADC compare window.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param w_low_threshold Low threshold of compare window.
|
||||
* \param w_high_threshold High threshold of compare window.
|
||||
*/
|
||||
void adc_set_comparison_window(Adc *p_adc, const uint16_t us_low_threshold,
|
||||
const uint16_t us_high_threshold)
|
||||
{
|
||||
p_adc->ADC_CWR = ADC_CWR_LOWTHRES(us_low_threshold) |
|
||||
ADC_CWR_HIGHTHRES(us_high_threshold);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configure comparison selected channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param channel ADC channel number.
|
||||
*/
|
||||
void adc_set_comparison_channel(Adc *p_adc, const enum adc_channel_num_t channel)
|
||||
{
|
||||
if (channel < 16) {
|
||||
p_adc->ADC_EMR &= (uint32_t) ~ (ADC_EMR_CMPALL);
|
||||
p_adc->ADC_EMR &= (uint32_t) ~ (ADC_EMR_CMPSEL_Msk);
|
||||
p_adc->ADC_EMR |= (channel << ADC_EMR_CMPSEL_Pos);
|
||||
} else {
|
||||
p_adc->ADC_EMR |= ADC_EMR_CMPALL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Enable differential input for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param channel ADC channel number.
|
||||
*/
|
||||
void adc_enable_channel_differential_input(Adc *p_adc, const enum adc_channel_num_t channel)
|
||||
{
|
||||
p_adc->ADC_COR |= 0x01u << (16 + channel);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Disable differential input for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param channel ADC channel number.
|
||||
*/
|
||||
void adc_disable_channel_differential_input(Adc *p_adc, const enum adc_channel_num_t channel)
|
||||
{
|
||||
uint32_t ul_temp;
|
||||
ul_temp = p_adc->ADC_COR;
|
||||
p_adc->ADC_COR &= 0xfffeffffu << channel;
|
||||
p_adc->ADC_COR |= ul_temp;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Enable analog signal offset for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param channel ADC channel number.
|
||||
*/
|
||||
void adc_enable_channel_input_offset(Adc *p_adc, const enum adc_channel_num_t channel)
|
||||
{
|
||||
p_adc->ADC_COR |= 0x01u << channel;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Disable analog signal offset for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param channel ADC channel number.
|
||||
*/
|
||||
void adc_disable_channel_input_offset(Adc *p_adc, const enum adc_channel_num_t channel)
|
||||
{
|
||||
uint32_t ul_temp;
|
||||
ul_temp = p_adc->ADC_COR;
|
||||
p_adc->ADC_COR &= (0xfffffffeu << channel);
|
||||
p_adc->ADC_COR |= ul_temp;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Configure input gain for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param channel ADC channel number.
|
||||
* \param gain Gain value for the input.
|
||||
*/
|
||||
void adc_set_channel_input_gain(Adc *p_adc, const enum adc_channel_num_t channel,
|
||||
const enum adc_gainvalue_t gain)
|
||||
{
|
||||
p_adc->ADC_CGR |= (0x03u << (2 * channel)) & (gain << (2 * channel));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3SD8_SERIES || SAM4S_SERIES
|
||||
/**
|
||||
* \brief Set ADC auto calibration mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_set_calibmode(Adc * p_adc)
|
||||
{
|
||||
p_adc->ADC_CR |= ADC_CR_AUTOCAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Return the actual ADC clock.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_mck Main clock of the device (in Hz).
|
||||
*
|
||||
* \return The actual ADC clock (in Hz).
|
||||
*/
|
||||
uint32_t adc_get_actual_adc_clock(const Adc *p_adc, const uint32_t ul_mck)
|
||||
{
|
||||
uint32_t ul_adcfreq;
|
||||
uint32_t ul_prescal;
|
||||
|
||||
/* ADCClock = MCK / ( (PRESCAL+1) * 2 ) */
|
||||
ul_prescal = ((p_adc->ADC_MR & ADC_MR_PRESCAL_Msk) >> ADC_MR_PRESCAL_Pos);
|
||||
ul_adcfreq = ul_mck / ((ul_prescal + 1) * 2);
|
||||
return ul_adcfreq;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable ADC interrupts.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_source Interrupts to be enabled.
|
||||
*/
|
||||
void adc_enable_interrupt(Adc *p_adc, const uint32_t ul_source)
|
||||
{
|
||||
p_adc->ADC_IER = ul_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable ADC interrupts.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_source Interrupts to be disabled.
|
||||
*/
|
||||
void adc_disable_interrupt(Adc *p_adc, const uint32_t ul_source)
|
||||
{
|
||||
p_adc->ADC_IDR = ul_source;
|
||||
}
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Get ADC interrupt and overrun error status.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return ADC status structure.
|
||||
*/
|
||||
uint32_t adc_get_status(const Adc *p_adc)
|
||||
{
|
||||
return p_adc->ADC_ISR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get ADC interrupt and overrun error status.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return ADC status structure.
|
||||
*/
|
||||
uint32_t adc_get_overrun_status(const Adc *p_adc)
|
||||
{
|
||||
return p_adc->ADC_OVER;
|
||||
}
|
||||
#elif SAM3U_SERIES
|
||||
/**
|
||||
* \brief Read ADC interrupt and overrun error status.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \retval ADC status structure.
|
||||
*/
|
||||
uint32_t adc_get_status(const Adc *p_adc)
|
||||
{
|
||||
return p_adc->ADC_SR;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Read ADC interrupt mask.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return The interrupt mask value.
|
||||
*/
|
||||
uint32_t adc_get_interrupt_mask(const Adc *p_adc)
|
||||
{
|
||||
return p_adc->ADC_IMR;
|
||||
}
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Adapt performance versus power consumption.
|
||||
*
|
||||
* \note Please refer to ADC Characteristics in the product datasheet
|
||||
* for more details.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ibctl ADC Bias current control.
|
||||
*/
|
||||
void adc_set_bias_current(Adc *p_adc, const uint8_t uc_ibctl)
|
||||
{
|
||||
p_adc->ADC_ACR |= ADC_ACR_IBCTL(uc_ibctl);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Turn on temperature sensor.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_enable_ts(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_ACR |= ADC_ACR_TSON;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Turn off temperature sensor.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc_disable_ts(Adc *p_adc)
|
||||
{
|
||||
p_adc->ADC_ACR &= ~ADC_ACR_TSON;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Enable or disable write protection of ADC registers.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_enable 1 to enable, 0 to disable.
|
||||
*/
|
||||
void adc_set_writeprotect(Adc *p_adc, const uint32_t ul_enable)
|
||||
{
|
||||
p_adc->ADC_WPMR |= ADC_WPMR_WPKEY(ul_enable);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3XA_SERIES
|
||||
/**
|
||||
* \brief Indicate write protect status.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return 0 if the peripheral is not protected, or 16-bit write protect
|
||||
* violation Status.
|
||||
*/
|
||||
uint32_t adc_get_writeprotect_status(const Adc *p_adc)
|
||||
{
|
||||
return p_adc->ADC_WPSR & ADC_WPSR_WPVS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* \brief calcul_startup
|
||||
*/
|
||||
static uint32_t calcul_startup(const uint32_t ul_startup)
|
||||
{
|
||||
uint32_t ul_startup_value = 0;
|
||||
|
||||
if (ul_startup == 0)
|
||||
ul_startup_value = 0;
|
||||
else if (ul_startup == 1)
|
||||
ul_startup_value = 8;
|
||||
else if (ul_startup == 2)
|
||||
ul_startup_value = 16;
|
||||
else if (ul_startup == 3)
|
||||
ul_startup_value = 24;
|
||||
else if (ul_startup == 4)
|
||||
ul_startup_value = 64;
|
||||
else if (ul_startup == 5)
|
||||
ul_startup_value = 80;
|
||||
else if (ul_startup == 6)
|
||||
ul_startup_value = 96;
|
||||
else if (ul_startup == 7)
|
||||
ul_startup_value = 112;
|
||||
else if (ul_startup == 8)
|
||||
ul_startup_value = 512;
|
||||
else if (ul_startup == 9)
|
||||
ul_startup_value = 576;
|
||||
else if (ul_startup == 10)
|
||||
ul_startup_value = 640;
|
||||
else if (ul_startup == 11)
|
||||
ul_startup_value = 704;
|
||||
else if (ul_startup == 12)
|
||||
ul_startup_value = 768;
|
||||
else if (ul_startup == 13)
|
||||
ul_startup_value = 832;
|
||||
else if (ul_startup == 14)
|
||||
ul_startup_value = 896;
|
||||
else if (ul_startup == 15)
|
||||
ul_startup_value = 960;
|
||||
|
||||
return ul_startup_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check ADC configurations.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_mck Main clock of the device (in Hz).
|
||||
*/
|
||||
void adc_check(Adc *p_adc, const uint32_t ul_mck)
|
||||
{
|
||||
uint32_t ul_adcfreq;
|
||||
uint32_t ul_prescal;
|
||||
uint32_t ul_startup;
|
||||
|
||||
/* ADCClock = MCK / ( (PRESCAL+1) * 2 ) */
|
||||
ul_prescal = ((p_adc->ADC_MR & ADC_MR_PRESCAL_Msk) >>
|
||||
ADC_MR_PRESCAL_Pos);
|
||||
ul_adcfreq = ul_mck / ((ul_prescal + 1) * 2);
|
||||
printf("ADC clock frequency = %d Hz\r\n", (int)ul_adcfreq);
|
||||
|
||||
if (ul_adcfreq < ADC_FREQ_MIN) {
|
||||
printf("adc frequency too low (out of specification: %d Hz)\r\n",
|
||||
(int)ADC_FREQ_MIN);
|
||||
}
|
||||
if (ul_adcfreq > ADC_FREQ_MAX) {
|
||||
printf("adc frequency too high (out of specification: %d Hz)\r\n",
|
||||
(int)ADC_FREQ_MAX);
|
||||
}
|
||||
|
||||
ul_startup = ((p_adc->ADC_MR & ADC_MR_STARTUP_Msk) >>
|
||||
ADC_MR_STARTUP_Pos);
|
||||
if (!(p_adc->ADC_MR & ADC_MR_SLEEP_SLEEP)) {
|
||||
/* 40ms */
|
||||
if (ADC_STARTUP_NORM * ul_adcfreq / 1000000 >
|
||||
calcul_startup(ul_startup)) {
|
||||
printf("Startup time too small: %d, programmed: %d\r\n",
|
||||
(int)(ADC_STARTUP_NORM * ul_adcfreq /
|
||||
1000000),
|
||||
(int)calcul_startup(ul_startup));
|
||||
}
|
||||
} else {
|
||||
if (p_adc->ADC_MR & ADC_MR_FREERUN_ON) {
|
||||
puts("FreeRun forbidden in sleep mode\r");
|
||||
}
|
||||
if (!(p_adc->ADC_MR & ADC_MR_FWUP_ON)) {
|
||||
/* Sleep 40ms */
|
||||
if (ADC_STARTUP_NORM * ul_adcfreq / 1000000 >
|
||||
calcul_startup(ul_startup)) {
|
||||
printf("Startup time too small: %d, programmed: %d\r\n",
|
||||
(int)(ADC_STARTUP_NORM * ul_adcfreq / 1000000),
|
||||
(int)(calcul_startup(ul_startup)));
|
||||
}
|
||||
} else {
|
||||
if (p_adc->ADC_MR & ADC_MR_FWUP_ON) {
|
||||
/* Fast Wake Up Sleep Mode: 12ms */
|
||||
if (ADC_STARTUP_FAST * ul_adcfreq / 1000000 >
|
||||
calcul_startup(ul_startup)) {
|
||||
printf("Startup time too small: %d, programmed: %d\r\n",
|
||||
(int)(ADC_STARTUP_NORM * ul_adcfreq / 1000000),
|
||||
(int)(calcul_startup(ul_startup)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Get PDC registers base address.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return ADC PDC register base address.
|
||||
*/
|
||||
Pdc *adc_get_pdc_base(const Adc *p_adc)
|
||||
{
|
||||
return PDC_ADC;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
399
hardware/digistump/sam/system/libsam/source/adc12_sam3u.c
Normal file
399
hardware/digistump/sam/system/libsam/source/adc12_sam3u.c
Normal file
@@ -0,0 +1,399 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* \defgroup sam_drivers_adc_group Analog-to-digital Converter (ADC)
|
||||
*
|
||||
* Driver for the Analog-to-digital Converter. This driver provides access to the main
|
||||
* features of the ADC controller.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#if SAM3U_SERIES
|
||||
|
||||
/**
|
||||
* \brief Initialize the given ADC with the specified ADC clock and startup time.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_mck Main clock of the device (in Hz).
|
||||
* \param ul_adc_clock Analog-to-Digital conversion clock (in Hz).
|
||||
* \param ul_startuptime ADC startup time value (value in us).
|
||||
* Please refer to the product datasheet for more details.
|
||||
* \param ul_offmode_startuptime ADC off mode startup time value (value in us).
|
||||
* Please refer to the product datasheet for more details.
|
||||
*
|
||||
* \return 0 on success.
|
||||
*/
|
||||
uint32_t adc12b_init(Adc12b *p_adc, const uint32_t ul_mck, const uint32_t ul_adc_clock,
|
||||
const uint32_t ul_startuptime, const uint32_t ul_offmode_startuptime)
|
||||
{
|
||||
uint32_t ul_prescal, ul_startup, ul_offmode;
|
||||
p_adc->ADC12B_CR = ADC12B_CR_SWRST;
|
||||
|
||||
/* Reset Mode Register */
|
||||
p_adc->ADC12B_MR = 0;
|
||||
|
||||
/* Reset PDC transfer */
|
||||
p_adc->ADC12B_PTCR = (ADC12B_PTCR_RXTDIS | ADC12B_PTCR_TXTDIS);
|
||||
p_adc->ADC12B_RCR = 0;
|
||||
p_adc->ADC12B_RNCR = 0;
|
||||
ul_prescal = ul_mck / (2 * ul_adc_clock) - 1;
|
||||
ul_startup = ((ul_adc_clock / 1000000) * ul_startuptime / 8) - 1;
|
||||
ul_offmode = ((ul_adc_clock / 1000000) * ul_offmode_startuptime / 8) -
|
||||
1;
|
||||
p_adc->ADC12B_MR |=
|
||||
ADC12B_MR_PRESCAL(ul_prescal) | ((ul_startup <<
|
||||
ADC12B_MR_STARTUP_Pos) &
|
||||
ADC12B_MR_STARTUP_Msk);
|
||||
p_adc->ADC12B_EMR |= (ul_offmode << 16) & (0xffu << 16);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure conversion resolution.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param resolution ADC resolution.
|
||||
*/
|
||||
void adc12b_set_resolution(Adc12b *p_adc, const enum adc_resolution_t resolution)
|
||||
{
|
||||
p_adc->ADC12B_MR |= (resolution << 4) & ADC12B_MR_LOWRES;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure conversion trigger and free run mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param trigger Conversion trigger.
|
||||
*/
|
||||
void adc12b_configure_trigger(Adc12b *p_adc, const enum adc_trigger_t trigger)
|
||||
{
|
||||
p_adc->ADC12B_MR |= trigger;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure ADC power saving mode.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param uc_sleep ADC_MR_SLEEP_NORMAL keeps the ADC Core and reference
|
||||
* voltage circuitry ON between conversions.
|
||||
* ADC_MR_SLEEP_SLEEP keeps the ADC Core and reference voltage circuitry
|
||||
* OFF between conversions.
|
||||
* \param uc_offmode 0 Standby Mode (if Sleep Bit = 1), 1 Off Mode.
|
||||
*/
|
||||
void adc12b_configure_power_save(Adc12b *p_adc, const uint8_t uc_sleep,
|
||||
uint8_t uc_offmode)
|
||||
{
|
||||
p_adc->ADC12B_MR |= ((uc_sleep << 5) & ADC12B_MR_SLEEP);
|
||||
p_adc->ADC12B_EMR |= uc_offmode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure ADC timing.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_sh ADC sample and hold time = uc_sh / ADC clock.
|
||||
*/
|
||||
void adc12b_configure_timing(Adc12b *p_adc, const uint32_t ul_sh)
|
||||
{
|
||||
p_adc->ADC12B_MR |= ADC12B_MR_SHTIM(ul_sh);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Start ADC conversion.
|
||||
*
|
||||
* \note If one of the hardware event is selected as ADC trigger,
|
||||
* this function can NOT start ADC conversion.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_start(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_CR = ADC12B_CR_START;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stop ADC conversion.
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_stop(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_CR = ADC12B_CR_SWRST;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable the specified ADC channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*/
|
||||
void adc12b_enable_channel(Adc12b *p_adc, const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
p_adc->ADC12B_CHER = 1 << adc_ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable all ADC channels.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_enable_all_channel(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_CHER = 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable the specified ADC channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*/
|
||||
void adc12b_disable_channel(Adc12b *p_adc, const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
p_adc->ADC12B_CHDR = 1 << adc_ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable all ADC channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_disable_all_channel(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_CHDR = 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the ADC channel status.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*
|
||||
* \retval 1 if channel is enabled.
|
||||
* \retval 0 if channel is disabled.
|
||||
*/
|
||||
uint32_t adc12b_get_channel_status(const Adc12b *p_adc, const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
return p_adc->ADC12B_CHSR & (1 << adc_ch);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the ADC result data of the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param adc_ch ADC channel number.
|
||||
*
|
||||
* \return ADC value of the specified channel.
|
||||
*/
|
||||
uint32_t adc12b_get_channel_value(const Adc12b *p_adc,const enum adc_channel_num_t adc_ch)
|
||||
{
|
||||
uint32_t dwData = 0;
|
||||
|
||||
if (15 >= adc_ch) {
|
||||
dwData = *(p_adc->ADC12B_CDR + adc_ch);
|
||||
}
|
||||
|
||||
return dwData;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the last ADC result data.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return ADC latest value.
|
||||
*/
|
||||
uint32_t adc12b_get_latest_value(const Adc12b *p_adc)
|
||||
{
|
||||
return p_adc->ADC12B_LCDR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable differential input for all channels.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_enable_differential_input(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_ACR |= (0x01u << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable differential input for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_disable_differential_input(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_ACR &= (0x01u << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable analog signal offset for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_enable_input_offset(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_ACR |= (0x01u << 17);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable analog signal offset for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*/
|
||||
void adc12b_disable_input_offset(Adc12b *p_adc)
|
||||
{
|
||||
p_adc->ADC12B_ACR &= (0x01u << 17);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure input gain for the specified channel.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param gain Gain value for the input.
|
||||
*/
|
||||
void adc12b_set_input_gain(Adc12b *p_adc, const enum adc_gainvalue_t gain)
|
||||
{
|
||||
p_adc->ADC12B_ACR |= (0x03u & gain);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return the actual ADC clock.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_mck Main clock of the device (in Hz).
|
||||
*
|
||||
* \retval 0 The actual ADC clock (in Hz).
|
||||
*/
|
||||
uint32_t adc12b_get_actual_adc_clock(const Adc12b *p_adc, const uint32_t ul_mck)
|
||||
{
|
||||
uint32_t ul_adcfreq;
|
||||
uint32_t ul_prescal;
|
||||
|
||||
/* ADCClock = MCK / ( (PRESCAL+1) * 2 ) */
|
||||
ul_prescal = ((p_adc->ADC12B_MR & ADC12B_MR_PRESCAL_Msk) >>
|
||||
ADC12B_MR_PRESCAL_Pos);
|
||||
ul_adcfreq = ul_mck / ((ul_prescal + 1) * 2);
|
||||
return ul_adcfreq;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable ADC interrupts.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_source Interrupts to be enabled.
|
||||
*/
|
||||
void adc12b_enable_interrupt(Adc12b *p_adc, const uint32_t ul_source)
|
||||
{
|
||||
p_adc->ADC12B_IER = ul_source;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable ADC interrupts.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param ul_source Interrupts to be disabled.
|
||||
*/
|
||||
void adc12b_disable_interrupt(Adc12b *p_adc, const uint32_t ul_source)
|
||||
{
|
||||
p_adc->ADC12B_IDR = ul_source;
|
||||
}
|
||||
|
||||
/** \brief Read ADC interrupt mask.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return The interrupt mask value.
|
||||
*/
|
||||
uint32_t adc12b_get_interrupt_mask(const Adc12b *p_adc)
|
||||
{
|
||||
return p_adc->ADC12B_IMR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read ADC interrupt status.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \retval ADC interrupt status.
|
||||
*/
|
||||
uint32_t adc12b_get_status(const Adc12b *p_adc)
|
||||
{
|
||||
return p_adc->ADC12B_SR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Adapt performance versus power consumption.
|
||||
*
|
||||
* \note Please refer to ADC Characteristics in the product datasheet
|
||||
* for more details.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
* \param uc_ibctl ADC Bias current control.
|
||||
*/
|
||||
void adc12b_set_bias_current(Adc12b *p_adc, const uint8_t uc_ibctl)
|
||||
{
|
||||
p_adc->ADC12B_ACR |= ADC12B_ACR_IBCTL(uc_ibctl);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get PDC registers base address.
|
||||
*
|
||||
* \param p_adc Pointer to an ADC instance.
|
||||
*
|
||||
* \return ADC PDC register base address.
|
||||
*/
|
||||
Pdc *adc12b_get_pdc_base(const Adc12b *p_adc)
|
||||
{
|
||||
return PDC_ADC12B;
|
||||
}
|
||||
#endif // SAM3U_SERIES
|
||||
|
||||
//@}
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
804
hardware/digistump/sam/system/libsam/source/can.c
Normal file
804
hardware/digistump/sam/system/libsam/source/can.c
Normal file
@@ -0,0 +1,804 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Controller Area Network (CAN) driver module for SAM.
|
||||
*
|
||||
* Copyright (c) 2011 - 2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* 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
|
||||
* EXPRESSLY AND SPECIFICALLY 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.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
#include <string.h>
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
#if SAM3XA_SERIES
|
||||
|
||||
|
||||
/** Define the timemark mask. */
|
||||
#define TIMEMARK_MASK 0x0000ffff
|
||||
|
||||
/* CAN timeout for synchronization. */
|
||||
#define CAN_TIMEOUT 100000
|
||||
|
||||
/** The max value for CAN baudrate prescale. */
|
||||
#define CAN_BAUDRATE_MAX_DIV 128
|
||||
|
||||
/** Define the scope for TQ. */
|
||||
#define CAN_MIN_TQ_NUM 8
|
||||
#define CAN_MAX_TQ_NUM 25
|
||||
|
||||
/** Define the fixed bit time value. */
|
||||
#define CAN_BIT_SYNC 1
|
||||
#define CAN_BIT_IPT 2
|
||||
|
||||
typedef struct {
|
||||
uint8_t uc_tq; //! CAN_BIT_SYNC + uc_prog + uc_phase1 + uc_phase2 = uc_tq, 8 <= uc_tq <= 25.
|
||||
uint8_t uc_prog; //! Propagation segment, (3-bits + 1), 1~8;
|
||||
uint8_t uc_phase1; //! Phase segment 1, (3-bits + 1), 1~8;
|
||||
uint8_t uc_phase2; //! Phase segment 2, (3-bits + 1), 1~8, CAN_BIT_IPT <= uc_phase2;
|
||||
uint8_t uc_sjw; //! Resynchronization jump width, (2-bits + 1), min(uc_phase1, 4);
|
||||
uint8_t uc_sp; //! Sample point value, 0~100 in percent.
|
||||
} can_bit_timing_t;
|
||||
|
||||
/** Values of bit time register for different baudrates, Sample point = ((1 + uc_prog + uc_phase1) / uc_tq) * 100%. */
|
||||
const can_bit_timing_t can_bit_time[] = {
|
||||
{8, (2 + 1), (1 + 1), (1 + 1), (2 + 1), 75},
|
||||
{9, (1 + 1), (2 + 1), (2 + 1), (1 + 1), 67},
|
||||
{10, (2 + 1), (2 + 1), (2 + 1), (2 + 1), 70},
|
||||
{11, (3 + 1), (2 + 1), (2 + 1), (3 + 1), 72},
|
||||
{12, (2 + 1), (3 + 1), (3 + 1), (3 + 1), 67},
|
||||
{13, (3 + 1), (3 + 1), (3 + 1), (3 + 1), 77},
|
||||
{14, (3 + 1), (3 + 1), (4 + 1), (3 + 1), 64},
|
||||
{15, (3 + 1), (4 + 1), (4 + 1), (3 + 1), 67},
|
||||
{16, (4 + 1), (4 + 1), (4 + 1), (3 + 1), 69},
|
||||
{17, (5 + 1), (4 + 1), (4 + 1), (3 + 1), 71},
|
||||
{18, (4 + 1), (5 + 1), (5 + 1), (3 + 1), 67},
|
||||
{19, (5 + 1), (5 + 1), (5 + 1), (3 + 1), 68},
|
||||
{20, (6 + 1), (5 + 1), (5 + 1), (3 + 1), 70},
|
||||
{21, (7 + 1), (5 + 1), (5 + 1), (3 + 1), 71},
|
||||
{22, (6 + 1), (6 + 1), (6 + 1), (3 + 1), 68},
|
||||
{23, (7 + 1), (7 + 1), (6 + 1), (3 + 1), 70},
|
||||
{24, (6 + 1), (7 + 1), (7 + 1), (3 + 1), 67},
|
||||
{25, (7 + 1), (7 + 1), (7 + 1), (3 + 1), 68}
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief Configure CAN baudrate.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param ul_mck The input main clock for the CAN module.
|
||||
* \param ul_baudrate Baudrate value (kB/s), allowed values:
|
||||
* 1000, 800, 500, 250, 125, 50, 25, 10, 5.
|
||||
*
|
||||
* \retval Set the baudrate successfully or not.
|
||||
*/
|
||||
static uint32_t can_set_baudrate(Can *p_can, uint32_t ul_mck, uint32_t ul_baudrate)
|
||||
{
|
||||
uint8_t uc_tq;
|
||||
uint8_t uc_prescale;
|
||||
uint32_t ul_mod;
|
||||
uint32_t ul_cur_mod;
|
||||
can_bit_timing_t *p_bit_time;
|
||||
|
||||
/* Check whether the baudrate prescale will be greater than the max divide value. */
|
||||
if (((ul_mck + (ul_baudrate * CAN_MAX_TQ_NUM * 1000 - 1)) /
|
||||
(ul_baudrate * CAN_MAX_TQ_NUM * 1000)) > CAN_BAUDRATE_MAX_DIV) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether the input MCK is too small. */
|
||||
if (ul_mck < ul_baudrate * CAN_MIN_TQ_NUM * 1000) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize it as the minimum Time Quantum. */
|
||||
uc_tq = CAN_MIN_TQ_NUM;
|
||||
|
||||
/* Initialize the remainder as the max value. When the remainder is 0, get the right TQ number. */
|
||||
ul_mod = 0xffffffff;
|
||||
/* Find out the approximate Time Quantum according to the baudrate. */
|
||||
for (uint8_t i = CAN_MIN_TQ_NUM; i <= CAN_MAX_TQ_NUM; i++) {
|
||||
if ((ul_mck / (ul_baudrate * i * 1000)) <= CAN_BAUDRATE_MAX_DIV) {
|
||||
ul_cur_mod = ul_mck % (ul_baudrate * i * 1000);
|
||||
if (ul_cur_mod < ul_mod){
|
||||
ul_mod = ul_cur_mod;
|
||||
uc_tq = i;
|
||||
if (!ul_mod) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate the baudrate prescale value. */
|
||||
uc_prescale = ul_mck / (ul_baudrate * uc_tq * 1000);
|
||||
|
||||
/* Get the right CAN BIT Timing group. */
|
||||
p_bit_time = (can_bit_timing_t *)&can_bit_time[uc_tq - CAN_MIN_TQ_NUM];
|
||||
|
||||
/* Before modifying the CANBR register, disable the CAN controller. */
|
||||
can_disable(p_can);
|
||||
|
||||
/* Write into the CAN baudrate register. */
|
||||
p_can->CAN_BR = CAN_BR_PHASE2(p_bit_time->uc_phase2 - 1) |
|
||||
CAN_BR_PHASE1(p_bit_time->uc_phase1 - 1) |
|
||||
CAN_BR_PROPAG(p_bit_time->uc_prog - 1) |
|
||||
CAN_BR_SJW(p_bit_time->uc_sjw - 1) |
|
||||
CAN_BR_BRP(uc_prescale - 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initialize CAN controller.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param ul_mck CAN module input clock.
|
||||
* \param ul_baudrate CAN communication baudrate in kbs.
|
||||
*
|
||||
* \retval 0 If failed to initialize the CAN module; otherwise successful.
|
||||
*
|
||||
* \note PMC clock for CAN peripheral should be enabled before calling this function.
|
||||
*/
|
||||
uint32_t can_init(Can *p_can, uint32_t ul_mck, uint32_t ul_baudrate)
|
||||
{
|
||||
uint32_t ul_flag;
|
||||
uint32_t ul_tick;
|
||||
|
||||
/* Initialize the baudrate for CAN module. */
|
||||
ul_flag = can_set_baudrate(p_can, ul_mck, ul_baudrate);
|
||||
if (ul_flag == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reset the CAN eight message mailbox. */
|
||||
can_reset_all_mailbox(p_can);
|
||||
|
||||
/* Enable the CAN controller. */
|
||||
can_enable(p_can);
|
||||
|
||||
/* Wait until the CAN is synchronized with the bus activity. */
|
||||
ul_flag = 0;
|
||||
ul_tick = 0;
|
||||
while (!(ul_flag & CAN_SR_WAKEUP) && (ul_tick < CAN_TIMEOUT)) {
|
||||
ul_flag = can_get_status(p_can);
|
||||
ul_tick++;
|
||||
}
|
||||
|
||||
/* Timeout or the CAN module has been synchronized with the bus. */
|
||||
if (CAN_TIMEOUT == ul_tick) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable CAN Controller.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_enable(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR |= CAN_MR_CANEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable CAN Controller.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_disable(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR &= ~CAN_MR_CANEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable CAN Controller low power mode.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_disable_low_power_mode(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR &= ~CAN_MR_LPM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable CAN Controller low power mode.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_enable_low_power_mode(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR |= CAN_MR_LPM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable CAN Controller autobaud/listen mode.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_disable_autobaud_listen_mode(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR &= ~CAN_MR_ABM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable CAN Controller autobaud/listen mode.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_enable_autobaud_listen_mode(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR |= CAN_MR_ABM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief CAN Controller won't generate overload frame.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_disable_overload_frame(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR &= ~CAN_MR_OVL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief CAN Controller will generate an overload frame after each successful
|
||||
* reception for mailboxes configured in Receive mode, Producer and Consumer.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_enable_overload_frame(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR |= CAN_MR_OVL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure the timestamp capture point, at the start or the end of frame.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param ul_flag 0: Timestamp is captured at each start of frame;
|
||||
* 1: Timestamp is captured at each end of frame.
|
||||
*/
|
||||
void can_set_timestamp_capture_point(Can *p_can, uint32_t ul_flag)
|
||||
{
|
||||
if (ul_flag) {
|
||||
p_can->CAN_MR |= CAN_MR_TEOF;
|
||||
} else {
|
||||
p_can->CAN_MR &= ~CAN_MR_TEOF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable CAN Controller time triggered mode.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_disable_time_triggered_mode(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR &= ~CAN_MR_TTM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable CAN Controller time triggered mode.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_enable_time_triggered_mode(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR |= CAN_MR_TTM;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable CAN Controller timer freeze.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_disable_timer_freeze(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR &= ~CAN_MR_TIMFRZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable CAN Controller timer freeze.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_enable_timer_freeze(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR |= CAN_MR_TIMFRZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable CAN Controller transmit repeat function.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_disable_tx_repeat(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR |= CAN_MR_DRPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable CAN Controller transmit repeat function.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_enable_tx_repeat(Can *p_can)
|
||||
{
|
||||
p_can->CAN_MR &= ~CAN_MR_DRPT;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure CAN Controller reception synchronization stage.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param ul_stage The reception stage to be configured.
|
||||
*
|
||||
* \note This is just for debug purpose only.
|
||||
*/
|
||||
void can_set_rx_sync_stage(Can *p_can, uint32_t ul_stage)
|
||||
{
|
||||
p_can->CAN_MR = (p_can->CAN_MR & ~CAN_MR_RXSYNC_Msk) | ul_stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable CAN interrupt.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param dw_mask Interrupt to be enabled.
|
||||
*/
|
||||
void can_enable_interrupt(Can *p_can, uint32_t dw_mask)
|
||||
{
|
||||
p_can->CAN_IER = dw_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable CAN interrupt.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param dw_mask Interrupt to be disabled.
|
||||
*/
|
||||
void can_disable_interrupt(Can *p_can, uint32_t dw_mask)
|
||||
{
|
||||
p_can->CAN_IDR = dw_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get CAN Interrupt Mask.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*
|
||||
* \retval CAN interrupt mask.
|
||||
*/
|
||||
uint32_t can_get_interrupt_mask(Can *p_can)
|
||||
{
|
||||
return (p_can->CAN_IMR);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get CAN status.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*
|
||||
* \retval CAN status.
|
||||
*/
|
||||
uint32_t can_get_status(Can *p_can)
|
||||
{
|
||||
return (p_can->CAN_SR);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the 16-bit free-running internal timer count.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*
|
||||
* \retval The internal CAN free-running timer counter.
|
||||
*/
|
||||
uint32_t can_get_internal_timer_value(Can *p_can)
|
||||
{
|
||||
return (p_can->CAN_TIM);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get CAN timestamp register value.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*
|
||||
* \retval The timestamp value.
|
||||
*/
|
||||
uint32_t can_get_timestamp_value(Can *p_can)
|
||||
{
|
||||
return (p_can->CAN_TIMESTP);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get CAN transmit error counter.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*
|
||||
* \retval Transmit error counter.
|
||||
*/
|
||||
uint8_t can_get_tx_error_cnt(Can *p_can)
|
||||
{
|
||||
return (uint8_t) (p_can->CAN_ECR >> CAN_ECR_TEC_Pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get CAN receive error counter.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*
|
||||
* \retval Receive error counter.
|
||||
*/
|
||||
uint8_t can_get_rx_error_cnt(Can *p_can)
|
||||
{
|
||||
return (uint8_t) (p_can->CAN_ECR >> CAN_ECR_REC_Pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reset the internal free-running 16-bit timer.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*
|
||||
* \note If the internal timer counter is frozen, this function automatically
|
||||
* re-enables it.
|
||||
*/
|
||||
void can_reset_internal_timer(Can *p_can)
|
||||
{
|
||||
p_can->CAN_TCR |= CAN_TCR_TIMRST;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send global transfer request.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param uc_mask Mask for mailboxes that are requested to transfer.
|
||||
*/
|
||||
void can_global_send_transfer_cmd(Can *p_can, uint8_t uc_mask)
|
||||
{
|
||||
uint32_t ul_reg;
|
||||
|
||||
ul_reg = p_can->CAN_TCR & ((uint32_t)~GLOBAL_MAILBOX_MASK);
|
||||
p_can->CAN_TCR = ul_reg | uc_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send global abort request.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param uc_mask Mask for mailboxes that are requested to abort.
|
||||
*/
|
||||
void can_global_send_abort_cmd(Can *p_can, uint8_t uc_mask)
|
||||
{
|
||||
uint32_t ul_reg;
|
||||
|
||||
ul_reg = p_can->CAN_ACR & ((uint32_t)~GLOBAL_MAILBOX_MASK);
|
||||
p_can->CAN_ACR = ul_reg | uc_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure the timemark for the mailbox.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param uc_index Indicate which mailbox is to be configured.
|
||||
* \param us_cnt The timemark to be set.
|
||||
*
|
||||
* \note The timemark is active in Time Triggered mode only.
|
||||
*/
|
||||
void can_mailbox_set_timemark(Can *p_can, uint8_t uc_index, uint16_t us_cnt)
|
||||
{
|
||||
uint32_t ul_reg;
|
||||
|
||||
ul_reg = p_can->CAN_MB[uc_index].CAN_MMR & ((uint32_t)~TIMEMARK_MASK);
|
||||
p_can->CAN_MB[uc_index].CAN_MMR = ul_reg | us_cnt;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get status of the mailbox.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param uc_index Indicate which mailbox is to be read.
|
||||
*
|
||||
* \retval The mailbox status.
|
||||
*/
|
||||
uint32_t can_mailbox_get_status(Can *p_can, uint8_t uc_index)
|
||||
{
|
||||
return (p_can->CAN_MB[uc_index].CAN_MSR);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send single mailbox transfer request.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param uc_index Indicate which mailbox is to be configured.
|
||||
*/
|
||||
void can_mailbox_send_transfer_cmd(Can *p_can, uint8_t uc_index)
|
||||
{
|
||||
p_can->CAN_MB[uc_index].CAN_MCR |= CAN_MCR_MTCR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send single mailbox abort request.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param uc_index Indicate which mailbox is to be configured.
|
||||
*/
|
||||
void can_mailbox_send_abort_cmd(Can *p_can, uint8_t uc_index)
|
||||
{
|
||||
p_can->CAN_MB[uc_index].CAN_MCR |= CAN_MCR_MACR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initialize the mailbox in different mode and set up related configuration.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param p_mailbox Pointer to a CAN mailbox instance.
|
||||
*/
|
||||
void can_mailbox_init(Can *p_can, can_mb_conf_t *p_mailbox)
|
||||
{
|
||||
uint8_t uc_index;
|
||||
|
||||
uc_index = (uint8_t)p_mailbox->ul_mb_idx;
|
||||
/* Check the object type of the mailbox. If it's used to disable the mailbox, reset the whole mailbox. */
|
||||
if (!p_mailbox->uc_obj_type) {
|
||||
p_can->CAN_MB[uc_index].CAN_MMR = 0;
|
||||
p_can->CAN_MB[uc_index].CAN_MAM = 0;
|
||||
p_can->CAN_MB[uc_index].CAN_MID = 0;
|
||||
p_can->CAN_MB[uc_index].CAN_MDL = 0;
|
||||
p_can->CAN_MB[uc_index].CAN_MDH = 0;
|
||||
p_can->CAN_MB[uc_index].CAN_MCR = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set the priority in Transmit mode. */
|
||||
p_can->CAN_MB[uc_index].CAN_MMR = (p_can->CAN_MB[uc_index].CAN_MMR &
|
||||
~CAN_MMR_PRIOR_Msk) | (p_mailbox-> uc_tx_prio << CAN_MMR_PRIOR_Pos);
|
||||
|
||||
/* Set the message ID and message acceptance mask for the mailbox in other modes. */
|
||||
if (p_mailbox->uc_id_ver) {
|
||||
p_can->CAN_MB[uc_index].CAN_MAM = p_mailbox->ul_id_msk | CAN_MAM_MIDE;
|
||||
p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id | CAN_MAM_MIDE;
|
||||
} else {
|
||||
p_can->CAN_MB[uc_index].CAN_MAM = p_mailbox->ul_id_msk;
|
||||
p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id;
|
||||
}
|
||||
|
||||
/* Set up mailbox in one of the five different modes. */
|
||||
p_can->CAN_MB[uc_index].CAN_MMR = (p_can->CAN_MB[uc_index].CAN_MMR &
|
||||
~CAN_MMR_MOT_Msk) | (p_mailbox-> uc_obj_type << CAN_MMR_MOT_Pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read receive information for the mailbox.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param p_mailbox Pointer to a CAN mailbox instance.
|
||||
*
|
||||
* \retval Different CAN mailbox transfer status.
|
||||
*
|
||||
* \note Read the mailbox status before calling this function.
|
||||
*/
|
||||
uint32_t can_mailbox_read(Can *p_can, can_mb_conf_t *p_mailbox)
|
||||
{
|
||||
uint32_t ul_status;
|
||||
uint8_t uc_index;
|
||||
uint32_t ul_retval;
|
||||
|
||||
ul_retval = 0;
|
||||
uc_index = (uint8_t)p_mailbox->ul_mb_idx;
|
||||
ul_status = p_mailbox->ul_status;
|
||||
|
||||
/* Check whether there is overwriting happening in Receive with Overwrite mode,
|
||||
or there're messages lost in Receive mode. */
|
||||
if ((ul_status & CAN_MSR_MRDY) && (ul_status & CAN_MSR_MMI)) {
|
||||
ul_retval = CAN_MAILBOX_RX_OVER;
|
||||
}
|
||||
|
||||
/* Read the message family ID. */
|
||||
p_mailbox->ul_fid = p_can->CAN_MB[uc_index].CAN_MFID & CAN_MFID_MFID_Msk;
|
||||
|
||||
/* Read received data length. */
|
||||
p_mailbox->uc_length = (ul_status & CAN_MSR_MDLC_Msk) >> CAN_MSR_MDLC_Pos;
|
||||
|
||||
/* Read received data. */
|
||||
p_mailbox->ul_datal = p_can->CAN_MB[uc_index].CAN_MDL;
|
||||
if (p_mailbox->uc_length > 4) {
|
||||
p_mailbox->ul_datah = p_can->CAN_MB[uc_index].CAN_MDH;
|
||||
}
|
||||
|
||||
/* Read the mailbox status again to check whether the software needs to re-read mailbox data register. */
|
||||
p_mailbox->ul_status = p_can->CAN_MB[uc_index].CAN_MSR;
|
||||
ul_status = p_mailbox->ul_status;
|
||||
if (ul_status & CAN_MSR_MMI) {
|
||||
ul_retval |= CAN_MAILBOX_RX_NEED_RD_AGAIN;
|
||||
} else {
|
||||
ul_retval |= CAN_MAILBOX_TRANSFER_OK;
|
||||
}
|
||||
|
||||
/* Enable next receive process. */
|
||||
can_mailbox_send_transfer_cmd(p_can, uc_index);
|
||||
|
||||
return ul_retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Prepare transmit information and write them into the mailbox.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param p_mailbox Pointer to a CAN mailbox instance.
|
||||
*
|
||||
* \retval CAN_MAILBOX_NOT_READY: Failed because mailbox isn't ready.
|
||||
* CAN_MAILBOX_TRANSFER_OK: Successfully write message into mailbox.
|
||||
*
|
||||
* \note After calling this function, the mailbox message won't be sent out until
|
||||
* can_mailbox_send_transfer_cmd() is called.
|
||||
*/
|
||||
uint32_t can_mailbox_write(Can *p_can, can_mb_conf_t *p_mailbox)
|
||||
{
|
||||
uint32_t ul_status;
|
||||
uint8_t uc_index;
|
||||
|
||||
uc_index = (uint8_t)p_mailbox->ul_mb_idx;
|
||||
/* Read the mailbox status firstly to check whether the mailbox is ready or not. */
|
||||
p_mailbox->ul_status = can_mailbox_get_status(p_can, uc_index);
|
||||
ul_status = p_mailbox->ul_status;
|
||||
if (!(ul_status & CAN_MSR_MRDY)) {
|
||||
return CAN_MAILBOX_NOT_READY;
|
||||
}
|
||||
|
||||
/* Write transmit identifier. */
|
||||
if (p_mailbox->uc_id_ver) {
|
||||
p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id | CAN_MAM_MIDE;
|
||||
} else {
|
||||
p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id;
|
||||
}
|
||||
|
||||
/* Write transmit data into mailbox data register. */
|
||||
p_can->CAN_MB[uc_index].CAN_MDL = p_mailbox->ul_datal;
|
||||
if (p_mailbox->uc_length > 4) {
|
||||
p_can->CAN_MB[uc_index].CAN_MDH = p_mailbox->ul_datah;
|
||||
}
|
||||
|
||||
/* Write transmit data length into mailbox control register. */
|
||||
p_can->CAN_MB[uc_index].CAN_MCR = (p_can->CAN_MB[uc_index].CAN_MCR &
|
||||
~CAN_MCR_MDLC_Msk) | CAN_MCR_MDLC(p_mailbox->uc_length);
|
||||
|
||||
return CAN_MAILBOX_TRANSFER_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Require to send out a remote frame.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
* \param p_mailbox Pointer to a CAN mailbox instance.
|
||||
*
|
||||
* \retval CAN_MAILBOX_NOT_READY: Failed because mailbox isn't ready for transmitting message.
|
||||
* CAN_MAILBOX_TRANSFER_OK: Successfully send out a remote frame.
|
||||
*/
|
||||
uint32_t can_mailbox_tx_remote_frame(Can *p_can, can_mb_conf_t *p_mailbox)
|
||||
{
|
||||
uint32_t ul_status;
|
||||
uint8_t uc_index;
|
||||
|
||||
uc_index = (uint8_t)p_mailbox->ul_mb_idx;
|
||||
/* Read the mailbox status firstly to check whether the mailbox is ready or not. */
|
||||
p_mailbox->ul_status = p_can->CAN_MB[uc_index].CAN_MSR;
|
||||
ul_status = p_mailbox->ul_status;
|
||||
if (!(ul_status & CAN_MSR_MRDY)) {
|
||||
return CAN_MAILBOX_NOT_READY;
|
||||
}
|
||||
|
||||
/* Write transmit identifier. */
|
||||
if (p_mailbox->uc_id_ver) {
|
||||
p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id | CAN_MAM_MIDE;
|
||||
} else {
|
||||
p_can->CAN_MB[uc_index].CAN_MID = p_mailbox->ul_id;
|
||||
}
|
||||
|
||||
/* Set the RTR bit in the sent frame. */
|
||||
p_can->CAN_MB[uc_index].CAN_MCR |= CAN_MCR_MRTR;
|
||||
|
||||
/* Set the MBx bit in the Transfer Command Register to send out the remote frame. */
|
||||
can_global_send_transfer_cmd(p_can, (1 << uc_index));
|
||||
|
||||
return CAN_MAILBOX_TRANSFER_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reset the eight mailboxes.
|
||||
*
|
||||
* \param p_can Pointer to a CAN peripheral instance.
|
||||
*/
|
||||
void can_reset_all_mailbox(Can *p_can)
|
||||
{
|
||||
can_mb_conf_t mb_config_t;
|
||||
|
||||
/* Set the mailbox object type parameter to disable the mailbox. */
|
||||
mb_config_t.uc_obj_type = CAN_MB_DISABLE_MODE;
|
||||
|
||||
for (uint8_t i = 0; i < CANMB_NUMBER; i++) {
|
||||
mb_config_t.ul_mb_idx = i;
|
||||
can_mailbox_init(p_can, &mb_config_t);
|
||||
}
|
||||
}
|
||||
|
||||
// from wilfredo
|
||||
uint32_t can_reset_mailbox_data(can_mb_conf_t *p_mailbox)
|
||||
{
|
||||
if ( p_mailbox == NULL )
|
||||
{
|
||||
return 1U ;
|
||||
}
|
||||
|
||||
#if 0
|
||||
p_mailbox->ul_mb_idx = 0;
|
||||
p_mailbox->uc_obj_type = 0;
|
||||
p_mailbox->uc_id_ver = 0;
|
||||
p_mailbox->uc_length = 0;
|
||||
p_mailbox->uc_tx_prio = 0;
|
||||
p_mailbox->ul_status = 0;
|
||||
p_mailbox->ul_id_msk = 0;
|
||||
p_mailbox->ul_id = 0;
|
||||
p_mailbox->ul_fid = 0;
|
||||
p_mailbox->ul_datal = 0;
|
||||
p_mailbox->ul_datah = 0;
|
||||
#else
|
||||
memset( p_mailbox, 0, sizeof( can_mb_conf_t ) ) ;
|
||||
#endif
|
||||
|
||||
return 0U ;
|
||||
}
|
||||
|
||||
#endif // SAM3XA_SERIES
|
||||
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
486
hardware/digistump/sam/system/libsam/source/dacc.c
Normal file
486
hardware/digistump/sam/system/libsam/source/dacc.c
Normal file
@@ -0,0 +1,486 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
#if (SAM3XA_SERIES) || (SAM3N_SERIES) || (SAM3S_SERIES)
|
||||
|
||||
/**
|
||||
* \defgroup sam_drivers_dacc_group Digital-to-Analog Converter Controller (DACC)
|
||||
*
|
||||
* \par Purpose
|
||||
*
|
||||
* Driver for the Digital-to-Analog Converter Controller. It provides access to the main
|
||||
* features of the DAC controller.
|
||||
*
|
||||
* \par Usage
|
||||
*
|
||||
* -# DACC clock should be enabled before using it.
|
||||
* - \ref pmc_enable_periph_clk() can be used to enable the clock.
|
||||
* -# Reset DACC with \ref dacc_reset().
|
||||
* -# If DACC can be enabled/disabled, uses \ref dacc_enable() and
|
||||
* \ref dacc_disable().
|
||||
* -# Initialize DACC timing with \ref dacc_set_timing() (different DAC
|
||||
* peripheral may require different parameters).
|
||||
* -# Write conversion data with \ref dacc_write_conversion_data().
|
||||
* -# Configure trigger with \ref dacc_set_trigger()
|
||||
* and \ref dacc_disable_trigger().
|
||||
* -# Configure FIFO transfer mode with \ref dacc_set_transfer_mode().
|
||||
* -# Control interrupts with \ref dacc_enable_interrupt(),
|
||||
* \ref dacc_disable_interrupt(), \ref dacc_get_interrupt_mask() and
|
||||
* \ref dacc_get_interrupt_status().
|
||||
* -# DACC registers support write protect with \ref dacc_set_writeprotect()
|
||||
* and \ref dacc_get_writeprotect_status().
|
||||
* -# If the DACC can work with PDC, use \ref dacc_get_pdc_base() to get
|
||||
* PDC register base for the DAC controller.
|
||||
* -# If the DACC has several channels to process, the following functions can
|
||||
* be used:
|
||||
* - Enable/Disable TAG and select output channel selection by
|
||||
* \ref dacc_set_channel_selection(),
|
||||
* \ref dacc_enable_flexible_channel_selection().
|
||||
* - Enable/disable channel by \ref dacc_enable_channel() /
|
||||
* \ref dacc_disable_channel(), get channel status by
|
||||
* \ref dacc_get_channel_status().
|
||||
*
|
||||
* \section dependencies Dependencies
|
||||
* This driver does not depend on other modules.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
//! Max channel number
|
||||
#if (SAM3N_SERIES)
|
||||
# define MAX_CH_NB 0
|
||||
#else
|
||||
# define MAX_CH_NB 1
|
||||
#endif
|
||||
|
||||
//! DACC Write Protect Key "DAC" in ASCII
|
||||
#define DACC_WP_KEY (0x444143)
|
||||
|
||||
/**
|
||||
* \brief Reset DACC.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*/
|
||||
void dacc_reset(Dacc *p_dacc)
|
||||
{
|
||||
p_dacc->DACC_CR = DACC_CR_SWRST;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable trigger and set the trigger source.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_trigger Trigger source number.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
uint32_t dacc_set_trigger(Dacc *p_dacc, uint32_t ul_trigger)
|
||||
{
|
||||
uint32_t mr = p_dacc->DACC_MR & (~(DACC_MR_TRGSEL_Msk));
|
||||
#if (SAM3N_SERIES)
|
||||
p_dacc->DACC_MR = mr
|
||||
| DACC_MR_TRGEN
|
||||
| ((ul_trigger << DACC_MR_TRGSEL_Pos) & DACC_MR_TRGSEL_Msk);
|
||||
#else
|
||||
p_dacc->DACC_MR = mr | DACC_MR_TRGEN_EN | DACC_MR_TRGSEL(ul_trigger);
|
||||
#endif
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable trigger (free run mode).
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*/
|
||||
void dacc_disable_trigger(Dacc *p_dacc)
|
||||
{
|
||||
p_dacc->DACC_MR &= ~DACC_MR_TRGEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the transfer mode.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_mode Transfer mode configuration.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
uint32_t dacc_set_transfer_mode(Dacc *p_dacc, uint32_t ul_mode)
|
||||
{
|
||||
if (ul_mode) {
|
||||
#if (SAM3N_SERIES)
|
||||
p_dacc->DACC_MR |= DACC_MR_WORD;
|
||||
#else
|
||||
p_dacc->DACC_MR |= DACC_MR_WORD_WORD;
|
||||
#endif
|
||||
} else {
|
||||
#if (SAM3N_SERIES)
|
||||
p_dacc->DACC_MR &= (~DACC_MR_WORD);
|
||||
#else
|
||||
p_dacc->DACC_MR &= (~DACC_MR_WORD_WORD);
|
||||
#endif
|
||||
}
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable DACC interrupts.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_interrupt_mask The interrupt mask.
|
||||
*/
|
||||
void dacc_enable_interrupt(Dacc *p_dacc, uint32_t ul_interrupt_mask)
|
||||
{
|
||||
p_dacc->DACC_IER = ul_interrupt_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable DACC interrupts.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_interrupt_mask The interrupt mask.
|
||||
*/
|
||||
void dacc_disable_interrupt(Dacc *p_dacc, uint32_t ul_interrupt_mask)
|
||||
{
|
||||
p_dacc->DACC_IDR = ul_interrupt_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the interrupt mask.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*
|
||||
* \return The interrupt mask.
|
||||
*/
|
||||
uint32_t dacc_get_interrupt_mask(Dacc *p_dacc)
|
||||
{
|
||||
return p_dacc->DACC_IMR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the interrupt status.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*
|
||||
* \return The interrupt status.
|
||||
*/
|
||||
uint32_t dacc_get_interrupt_status(Dacc *p_dacc)
|
||||
{
|
||||
return p_dacc->DACC_ISR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write data to conversion register.
|
||||
*
|
||||
* \note The \a ul_data could be output data or data with channel TAG when
|
||||
* flexible mode is used.
|
||||
*
|
||||
* In flexible mode the 2 bits, DACC_CDR[13:12] which are otherwise unused,
|
||||
* are employed to select the channel in the same way as with the USER_SEL
|
||||
* field. Finally, if the WORD field is set, the 2 bits, DACC_CDR[13:12] are
|
||||
* used for channel selection of the first data and the 2 bits,
|
||||
* DACC_CDR[29:28] for channel selection of the second data.
|
||||
*
|
||||
* \see dacc_enable_flexible_selection()
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_data The data to be transferred to analog value.
|
||||
*/
|
||||
void dacc_write_conversion_data(Dacc *p_dacc, uint32_t ul_data)
|
||||
{
|
||||
p_dacc->DACC_CDR = ul_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable or disable write protect of DACC registers.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_enable 1 to enable, 0 to disable.
|
||||
*/
|
||||
void dacc_set_writeprotect(Dacc *p_dacc, uint32_t ul_enable)
|
||||
{
|
||||
if (ul_enable) {
|
||||
p_dacc->DACC_WPMR = DACC_WPMR_WPKEY(DACC_WP_KEY)
|
||||
| DACC_WPMR_WPEN;
|
||||
} else {
|
||||
p_dacc->DACC_WPMR = DACC_WPMR_WPKEY(DACC_WP_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the write protect status.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*
|
||||
* \return Write protect status.
|
||||
*/
|
||||
uint32_t dacc_get_writeprotect_status(Dacc *p_dacc)
|
||||
{
|
||||
return p_dacc->DACC_WPSR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get PDC registers base address.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*
|
||||
* \return DACC PDC register base address.
|
||||
*/
|
||||
Pdc *dacc_get_pdc_base(Dacc *p_dacc)
|
||||
{
|
||||
p_dacc = p_dacc;
|
||||
return PDC_DACC;
|
||||
}
|
||||
|
||||
#if (SAM3N_SERIES) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* \brief Enable DACC.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*/
|
||||
void dacc_enable(Dacc *p_dacc)
|
||||
{
|
||||
p_dacc->DACC_MR |= DACC_MR_DACEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable DACC.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
void dacc_disable(Dacc *p_dacc)
|
||||
{
|
||||
p_dacc->DACC_MR &= (~DACC_MR_DACEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the DACC timing.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_startup Startup time selection.
|
||||
* \param ul_clock_divider Clock divider for internal trigger.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
uint32_t dacc_set_timing(Dacc *p_dacc, uint32_t ul_startup,
|
||||
uint32_t ul_clock_divider)
|
||||
{
|
||||
uint32_t mr = p_dacc->DACC_MR
|
||||
& ~(DACC_MR_STARTUP_Msk | DACC_MR_CLKDIV_Msk);
|
||||
p_dacc->DACC_MR = mr | DACC_MR_STARTUP(ul_startup)
|
||||
| DACC_MR_CLKDIV(ul_clock_divider);
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
#endif /* #if (SAM3N_SERIES) */
|
||||
|
||||
#if (SAM3S_SERIES) || (SAM3XA_SERIES) || (SAM4S_SERIES) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* \brief Disable flexible (TAG) mode and select a channel for DAC outputs.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_channel Channel to select.
|
||||
*
|
||||
* \return \ref DACC_RC_OK if successful.
|
||||
*/
|
||||
uint32_t dacc_set_channel_selection(Dacc *p_dacc, uint32_t ul_channel)
|
||||
{
|
||||
uint32_t mr = p_dacc->DACC_MR & (~DACC_MR_USER_SEL_Msk);
|
||||
if (ul_channel > MAX_CH_NB) {
|
||||
return DACC_RC_INVALID_PARAM;
|
||||
}
|
||||
mr &= ~(DACC_MR_TAG);
|
||||
mr |= ul_channel << DACC_MR_USER_SEL_Pos;
|
||||
p_dacc->DACC_MR = mr;
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable the flexible channel selection mode (TAG).
|
||||
*
|
||||
* In this mode the 2 bits, DACC_CDR[13:12] which are otherwise unused, are
|
||||
* employed to select the channel in the same way as with the USER_SEL field.
|
||||
* Finally, if the WORD field is set, the 2 bits, DACC_CDR[13:12] are used
|
||||
* for channel selection of the first data and the 2 bits, DACC_CDR[29:28]
|
||||
* for channel selection of the second data.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*/
|
||||
void dacc_enable_flexible_selection(Dacc *p_dacc)
|
||||
{
|
||||
p_dacc->DACC_MR |= DACC_MR_TAG;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the power save mode.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_sleep_mode Sleep mode configuration.
|
||||
* \param ul_fast_wakeup_mode Fast wakeup mode configuration.
|
||||
*
|
||||
* \return \ref DACC_RC_OK if successful.
|
||||
*/
|
||||
uint32_t dacc_set_power_save(Dacc *p_dacc,
|
||||
uint32_t ul_sleep_mode, uint32_t ul_fast_wakeup_mode)
|
||||
{
|
||||
if (ul_sleep_mode) {
|
||||
p_dacc->DACC_MR |= DACC_MR_SLEEP;
|
||||
} else {
|
||||
p_dacc->DACC_MR &= (~DACC_MR_SLEEP);
|
||||
}
|
||||
if (ul_fast_wakeup_mode) {
|
||||
p_dacc->DACC_MR |= DACC_MR_FASTWKUP;
|
||||
} else {
|
||||
p_dacc->DACC_MR &= (~DACC_MR_FASTWKUP);
|
||||
}
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set DACC timings.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_refresh Refresh period setting value.
|
||||
* \param ul_maxs Max speed mode configuration.
|
||||
* \param ul_startup Startup time selection.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
uint32_t dacc_set_timing(Dacc *p_dacc,
|
||||
uint32_t ul_refresh, uint32_t ul_maxs, uint32_t ul_startup)
|
||||
{
|
||||
uint32_t mr = p_dacc->DACC_MR
|
||||
& (~(DACC_MR_REFRESH_Msk | DACC_MR_STARTUP_Msk));
|
||||
mr |= DACC_MR_REFRESH(ul_refresh);
|
||||
if (ul_maxs) {
|
||||
mr |= DACC_MR_MAXS;
|
||||
} else {
|
||||
mr &= ~DACC_MR_MAXS;
|
||||
}
|
||||
mr |= (DACC_MR_STARTUP_Msk & ((ul_startup) << DACC_MR_STARTUP_Pos));
|
||||
p_dacc->DACC_MR = mr;
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable DACC channel.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_channel The output channel to enable.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
uint32_t dacc_enable_channel(Dacc *p_dacc, uint32_t ul_channel)
|
||||
{
|
||||
if (ul_channel > MAX_CH_NB)
|
||||
return DACC_RC_INVALID_PARAM;
|
||||
|
||||
p_dacc->DACC_CHER = DACC_CHER_CH0 << ul_channel;
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable DACC channel.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_channel The output channel to disable.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
uint32_t dacc_disable_channel(Dacc *p_dacc, uint32_t ul_channel)
|
||||
{
|
||||
if (ul_channel > MAX_CH_NB) {
|
||||
return DACC_RC_INVALID_PARAM;
|
||||
}
|
||||
p_dacc->DACC_CHDR = DACC_CHDR_CH0 << ul_channel;
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the channel status.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*
|
||||
* \return DACC channel status.
|
||||
*/
|
||||
uint32_t dacc_get_channel_status(Dacc *p_dacc)
|
||||
{
|
||||
return p_dacc->DACC_CHSR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the analog control value.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
* \param ul_analog_control Analog control configuration.
|
||||
*
|
||||
* \return \ref DACC_RC_OK for OK.
|
||||
*/
|
||||
uint32_t dacc_set_analog_control(Dacc *p_dacc, uint32_t ul_analog_control)
|
||||
{
|
||||
p_dacc->DACC_ACR = ul_analog_control;
|
||||
return DACC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the analog control value.
|
||||
*
|
||||
* \param p_dacc Pointer to a DACC instance.
|
||||
*
|
||||
* \return Current setting of analog control.
|
||||
*/
|
||||
uint32_t dacc_get_analog_control(Dacc *p_dacc)
|
||||
{
|
||||
return p_dacc->DACC_ACR;
|
||||
}
|
||||
#endif /* (SAM3S_SERIES) || (SAM3XA_SERIES) */
|
||||
|
||||
//@}
|
||||
|
||||
#endif // SAM3XA_SERIES
|
||||
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
402
hardware/digistump/sam/system/libsam/source/efc.c
Normal file
402
hardware/digistump/sam/system/libsam/source/efc.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Enhanced Embedded Flash Controller (EEFC) driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* 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
|
||||
* EXPRESSLY AND SPECIFICALLY 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.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
#include <string.h>
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* \defgroup sam_drivers_efc_group Enhanced Embedded Flash Controller (EEFC)
|
||||
*
|
||||
* The Enhanced Embedded Flash Controller ensures the interface of the Flash block with
|
||||
* the 32-bit internal bus.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/* Address definition for read operation */
|
||||
#if (SAM3XA_SERIES || SAM3U_SERIES /*|| SAM4SD16 || SAM4SD32*/)
|
||||
# define READ_BUFF_ADDR0 IFLASH0_ADDR
|
||||
# define READ_BUFF_ADDR1 IFLASH1_ADDR
|
||||
#elif (SAM3S_SERIES || SAM3N_SERIES)
|
||||
# define READ_BUFF_ADDR IFLASH_ADDR
|
||||
#elif (SAM3U_SERIES || SAM4S_SERIES)
|
||||
# define READ_BUFF_ADDR IFLASH0_ADDR
|
||||
#else
|
||||
# warning Only reading unique id for sam3 is implemented.
|
||||
#endif
|
||||
|
||||
/* Flash Writing Protection Key */
|
||||
#define FWP_KEY 0x5Au
|
||||
|
||||
#if SAM4S_SERIES
|
||||
#define EEFC_FCR_FCMD(value) \
|
||||
((EEFC_FCR_FCMD_Msk & ((value) << EEFC_FCR_FCMD_Pos)))
|
||||
#define EEFC_ERROR_FLAGS (EEFC_FSR_FLOCKE | EEFC_FSR_FCMDE | EEFC_FSR_FLERR)
|
||||
#else
|
||||
#define EEFC_ERROR_FLAGS (EEFC_FSR_FLOCKE | EEFC_FSR_FCMDE)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Local function declaration.
|
||||
* Because they are RAM functions, they need 'extern' declaration.
|
||||
*/
|
||||
extern void efc_write_fmr(Efc *p_efc, uint32_t ul_fmr);
|
||||
extern uint32_t efc_perform_fcr(Efc *p_efc, uint32_t ul_fcr);
|
||||
|
||||
/**
|
||||
* \brief Initialize the EFC controller.
|
||||
*
|
||||
* \param ul_access_mode 0 for 128-bit, EEFC_FMR_FAM for 64-bit.
|
||||
* \param ul_fws The number of wait states in cycle (no shift).
|
||||
*
|
||||
* \return 0 if successful.
|
||||
*/
|
||||
uint32_t efc_init(Efc *p_efc, uint32_t ul_access_mode, uint32_t ul_fws)
|
||||
{
|
||||
efc_write_fmr(p_efc, ul_access_mode | EEFC_FMR_FWS(ul_fws));
|
||||
return EFC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable the flash ready interrupt.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
*/
|
||||
void efc_enable_frdy_interrupt(Efc *p_efc)
|
||||
{
|
||||
uint32_t ul_fmr = p_efc->EEFC_FMR;
|
||||
|
||||
efc_write_fmr(p_efc, ul_fmr | EEFC_FMR_FRDY);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable the flash ready interrupt.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
*/
|
||||
void efc_disable_frdy_interrupt(Efc *p_efc)
|
||||
{
|
||||
uint32_t ul_fmr = p_efc->EEFC_FMR;
|
||||
|
||||
efc_write_fmr(p_efc, ul_fmr & (~EEFC_FMR_FRDY));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set flash access mode.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
* \param ul_mode 0 for 128-bit, EEFC_FMR_FAM for 64-bit.
|
||||
*/
|
||||
void efc_set_flash_access_mode(Efc *p_efc, uint32_t ul_mode)
|
||||
{
|
||||
uint32_t ul_fmr = p_efc->EEFC_FMR & (~EEFC_FMR_FAM);
|
||||
|
||||
efc_write_fmr(p_efc, ul_fmr | ul_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get flash access mode.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
*
|
||||
* \return 0 for 128-bit or EEFC_FMR_FAM for 64-bit.
|
||||
*/
|
||||
uint32_t efc_get_flash_access_mode(Efc *p_efc)
|
||||
{
|
||||
return (p_efc->EEFC_FMR & EEFC_FMR_FAM);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set flash wait state.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
* \param ul_fws The number of wait states in cycle (no shift).
|
||||
*/
|
||||
void efc_set_wait_state(Efc *p_efc, uint32_t ul_fws)
|
||||
{
|
||||
uint32_t ul_fmr = p_efc->EEFC_FMR & (~EEFC_FMR_FWS_Msk);
|
||||
|
||||
efc_write_fmr(p_efc, ul_fmr | EEFC_FMR_FWS(ul_fws));
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get flash wait state.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
*
|
||||
* \return The number of wait states in cycle (no shift).
|
||||
*/
|
||||
uint32_t efc_get_wait_state(Efc *p_efc)
|
||||
{
|
||||
return ((p_efc->EEFC_FMR & EEFC_FMR_FWS_Msk) >> EEFC_FMR_FWS_Pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Perform the given command and wait until its completion (or an error).
|
||||
*
|
||||
* \note Unique ID commands are not supported, use efc_read_unique_id.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
* \param ul_command Command to perform.
|
||||
* \param ul_argument Optional command argument.
|
||||
*
|
||||
* \note This function will automatically choose to use IAP function.
|
||||
*
|
||||
* \return 0 if successful, otherwise returns an error code.
|
||||
*/
|
||||
uint32_t efc_perform_command(Efc *p_efc, uint32_t ul_command,
|
||||
uint32_t ul_argument)
|
||||
{
|
||||
// Unique ID commands are not supported.
|
||||
if (ul_command == EFC_FCMD_STUI || ul_command == EFC_FCMD_SPUI) {
|
||||
return EFC_RC_NOT_SUPPORT;
|
||||
}
|
||||
|
||||
#if (SAM3XA_SERIES || SAM3U4)
|
||||
// Use IAP function with 2 parameters in ROM.
|
||||
static uint32_t(*iap_perform_command) (uint32_t, uint32_t);
|
||||
uint32_t ul_efc_nb = (p_efc == EFC0) ? 0 : 1;
|
||||
|
||||
iap_perform_command =
|
||||
(uint32_t(*)(uint32_t, uint32_t))
|
||||
*((uint32_t *) CHIP_FLASH_IAP_ADDRESS);
|
||||
iap_perform_command(ul_efc_nb,
|
||||
EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(ul_argument) |
|
||||
EEFC_FCR_FCMD(ul_command));
|
||||
return (p_efc->EEFC_FSR & EEFC_ERROR_FLAGS);
|
||||
#elif (SAM3N_SERIES || SAM3S_SERIES || SAM4S_SERIES || SAM3U_SERIES)
|
||||
// Use IAP function with 2 parameter in ROM.
|
||||
static uint32_t(*iap_perform_command) (uint32_t, uint32_t);
|
||||
|
||||
iap_perform_command =
|
||||
(uint32_t(*)(uint32_t, uint32_t))
|
||||
*((uint32_t *) CHIP_FLASH_IAP_ADDRESS);
|
||||
#if SAM4S_SERIES
|
||||
uint32_t ul_efc_nb = (p_efc == EFC0) ? 0 : 1;
|
||||
iap_perform_command(ul_efc_nb,
|
||||
EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FARG(ul_argument) |
|
||||
EEFC_FCR_FCMD(ul_command));
|
||||
#else
|
||||
iap_perform_command(0,
|
||||
EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(ul_argument) |
|
||||
EEFC_FCR_FCMD(ul_command));
|
||||
#endif
|
||||
return (p_efc->EEFC_FSR & EEFC_ERROR_FLAGS);
|
||||
#else
|
||||
// Use RAM Function.
|
||||
return efc_perform_fcr(p_efc,
|
||||
EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(ul_argument) |
|
||||
EEFC_FCR_FCMD(ul_command));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the current status of the EEFC.
|
||||
*
|
||||
* \note This function clears the value of some status bits (FLOCKE, FCMDE).
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
*
|
||||
* \return The current status.
|
||||
*/
|
||||
uint32_t efc_get_status(Efc *p_efc)
|
||||
{
|
||||
return p_efc->EEFC_FSR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the result of the last executed command.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
*
|
||||
* \return The result of the last executed command.
|
||||
*/
|
||||
uint32_t efc_get_result(Efc *p_efc)
|
||||
{
|
||||
return p_efc->EEFC_FRR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Perform read sequence. Supported sequences are read Unique ID and
|
||||
* read User Signature
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
* \param ul_cmd_st Start command to perform.
|
||||
* \param ul_cmd_sp Stop command to perform.
|
||||
* \param p_ul_buf Pointer to an data buffer.
|
||||
* \param ul_size Buffer size.
|
||||
*
|
||||
* \return 0 if successful, otherwise returns an error code.
|
||||
*/
|
||||
#ifdef __ICCARM__
|
||||
__ramfunc
|
||||
#else
|
||||
__attribute__ ((section(".ramfunc")))
|
||||
#endif
|
||||
uint32_t efc_perform_read_sequence(Efc *p_efc,
|
||||
uint32_t ul_cmd_st, uint32_t ul_cmd_sp,
|
||||
uint32_t *p_ul_buf, uint32_t ul_size)
|
||||
{
|
||||
volatile uint32_t ul_status;
|
||||
uint32_t ul_cnt;
|
||||
|
||||
#if (SAM3U4 || SAM3XA_SERIES /*|| SAM4SD16 || SAM4SD32*/)
|
||||
uint32_t *p_ul_data =
|
||||
(uint32_t *) ((p_efc == EFC0) ?
|
||||
READ_BUFF_ADDR0 : READ_BUFF_ADDR1);
|
||||
#elif (SAM3S_SERIES || SAM4S_SERIES || SAM3N_SERIES || SAM3U_SERIES)
|
||||
uint32_t *p_ul_data = (uint32_t *) READ_BUFF_ADDR;
|
||||
#else
|
||||
return EFC_RC_NOT_SUPPORT;
|
||||
#endif
|
||||
|
||||
if (p_ul_buf == NULL) {
|
||||
return EFC_RC_INVALID;
|
||||
}
|
||||
|
||||
p_efc->EEFC_FMR |= (0x1u << 16);
|
||||
|
||||
/* Send the Start Read command */
|
||||
#if SAM4S_SERIES
|
||||
p_efc->EEFC_FCR = EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FARG(0)
|
||||
| EEFC_FCR_FCMD(ul_cmd_st);
|
||||
#else
|
||||
p_efc->EEFC_FCR = EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(0)
|
||||
| EEFC_FCR_FCMD(ul_cmd_st);
|
||||
#endif
|
||||
/* Wait for the FRDY bit in the Flash Programming Status Register
|
||||
* (EEFC_FSR) falls.
|
||||
*/
|
||||
do {
|
||||
ul_status = p_efc->EEFC_FSR;
|
||||
} while ((ul_status & EEFC_FSR_FRDY) == EEFC_FSR_FRDY);
|
||||
|
||||
/* The data is located in the first address of the Flash
|
||||
* memory mapping.
|
||||
*/
|
||||
for (ul_cnt = 0; ul_cnt < ul_size; ul_cnt++) {
|
||||
p_ul_buf[ul_cnt] = p_ul_data[ul_cnt];
|
||||
}
|
||||
|
||||
/* To stop the read mode */
|
||||
p_efc->EEFC_FCR =
|
||||
#if SAM4S_SERIES
|
||||
EEFC_FCR_FKEY_PASSWD | EEFC_FCR_FARG(0) |
|
||||
EEFC_FCR_FCMD(ul_cmd_sp);
|
||||
#else
|
||||
EEFC_FCR_FKEY(FWP_KEY) | EEFC_FCR_FARG(0) |
|
||||
EEFC_FCR_FCMD(ul_cmd_sp);
|
||||
#endif
|
||||
/* Wait for the FRDY bit in the Flash Programming Status Register (EEFC_FSR)
|
||||
* rises.
|
||||
*/
|
||||
do {
|
||||
ul_status = p_efc->EEFC_FSR;
|
||||
} while ((ul_status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY);
|
||||
|
||||
p_efc->EEFC_FMR &= ~(0x1u << 16);
|
||||
|
||||
return EFC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set mode register.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
* \param ul_fmr Value of mode register
|
||||
*/
|
||||
#ifdef __ICCARM__
|
||||
__ramfunc
|
||||
#else
|
||||
__attribute__ ((section(".ramfunc")))
|
||||
#endif
|
||||
void efc_write_fmr(Efc *p_efc, uint32_t ul_fmr)
|
||||
{
|
||||
p_efc->EEFC_FMR = ul_fmr;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Perform command.
|
||||
*
|
||||
* \param p_efc Pointer to an EFC instance.
|
||||
* \param ul_fcr Flash command.
|
||||
*
|
||||
* \return The current status.
|
||||
*/
|
||||
#ifdef __ICCARM__
|
||||
__ramfunc
|
||||
#else
|
||||
__attribute__ ((section(".ramfunc")))
|
||||
#endif
|
||||
uint32_t efc_perform_fcr(Efc *p_efc, uint32_t ul_fcr)
|
||||
{
|
||||
volatile uint32_t ul_status;
|
||||
|
||||
p_efc->EEFC_FCR = ul_fcr;
|
||||
do {
|
||||
ul_status = p_efc->EEFC_FSR;
|
||||
} while ((ul_status & EEFC_FSR_FRDY) != EEFC_FSR_FRDY);
|
||||
|
||||
return (ul_status & EEFC_ERROR_FLAGS);
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
802
hardware/digistump/sam/system/libsam/source/emac.c
Normal file
802
hardware/digistump/sam/system/libsam/source/emac.c
Normal file
@@ -0,0 +1,802 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief EMAC (Ethernet MAC) driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* 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
|
||||
* EXPRESSLY AND SPECIFICALLY 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.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
#include <string.h>
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
#if SAM3XA_SERIES
|
||||
|
||||
/**
|
||||
* \defgroup emac_group Ethernet Media Access Controller
|
||||
*
|
||||
* See \ref emac_quickstart.
|
||||
*
|
||||
* Driver for the EMAC (Ethernet Media Access Controller).
|
||||
* This file contains basic functions for the EMAC, with support for all modes, settings
|
||||
* and clock speeds.
|
||||
*
|
||||
* \section dependencies Dependencies
|
||||
* This driver does not depend on other modules.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define EMAC_RX_BUFFERS 16
|
||||
#define EMAC_TX_BUFFERS 8
|
||||
#define MAC_PHY_RETRY_MAX 1000000
|
||||
|
||||
|
||||
/** TX descriptor lists */
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
static emac_tx_descriptor_t gs_tx_desc[EMAC_TX_BUFFERS];
|
||||
/** TX callback lists */
|
||||
static emac_dev_tx_cb_t gs_tx_callback[EMAC_TX_BUFFERS];
|
||||
/** RX descriptors lists */
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
static emac_rx_descriptor_t gs_rx_desc[EMAC_RX_BUFFERS];
|
||||
/** Send Buffer. Section 3.6 of AMBA 2.0 spec states that burst should not cross the
|
||||
* 1K Boundaries. Receive buffer manager write operations are burst of 2 words => 3 lsb bits
|
||||
* of the address shall be set to 0.
|
||||
*/
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
static uint8_t gs_uc_tx_buffer[EMAC_TX_BUFFERS * EMAC_TX_UNITSIZE]
|
||||
__attribute__ ((aligned(8)));
|
||||
|
||||
#ifdef __ICCARM__ /* IAR */
|
||||
#pragma data_alignment=8
|
||||
#endif
|
||||
/** Receive Buffer */
|
||||
static uint8_t gs_uc_rx_buffer[EMAC_RX_BUFFERS * EMAC_RX_UNITSIZE]
|
||||
__attribute__ ((aligned(8)));
|
||||
|
||||
/**
|
||||
* EMAC device memory management struct.
|
||||
*/
|
||||
typedef struct emac_dev_mem {
|
||||
/* Pointer to allocated buffer for RX. The address should be 8-byte aligned
|
||||
and the size should be EMAC_RX_UNITSIZE * wRxSize. */
|
||||
uint8_t *p_rx_buffer;
|
||||
/* Pointer to allocated RX descriptor list. */
|
||||
emac_rx_descriptor_t *p_rx_dscr;
|
||||
/* RX size, in number of registered units (RX descriptors). */
|
||||
uint16_t us_rx_size;
|
||||
/* Pointer to allocated buffer for TX. The address should be 8-byte aligned
|
||||
and the size should be EMAC_TX_UNITSIZE * wTxSize. */
|
||||
uint8_t *p_tx_buffer;
|
||||
/* Pointer to allocated TX descriptor list. */
|
||||
emac_tx_descriptor_t *p_tx_dscr;
|
||||
/* TX size, in number of registered units (TX descriptors). */
|
||||
uint16_t us_tx_size;
|
||||
} emac_dev_mem_t;
|
||||
|
||||
/** Return count in buffer */
|
||||
#define CIRC_CNT(head,tail,size) (((head) - (tail)) % (size))
|
||||
|
||||
/*
|
||||
* Return space available, from 0 to size-1.
|
||||
* Always leave one free char as a completely full buffer that has (head == tail),
|
||||
* which is the same as empty.
|
||||
*/
|
||||
#define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size))
|
||||
|
||||
/** Circular buffer is empty ? */
|
||||
#define CIRC_EMPTY(head, tail) (head == tail)
|
||||
/** Clear circular buffer */
|
||||
#define CIRC_CLEAR(head, tail) (head = tail = 0)
|
||||
|
||||
/** Increment head or tail */
|
||||
static void circ_inc(uint16_t *headortail, uint32_t size)
|
||||
{
|
||||
(*headortail)++;
|
||||
if((*headortail) >= size) {
|
||||
(*headortail) = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Wait PHY operation to be completed.
|
||||
*
|
||||
* \param p_emac HW controller address.
|
||||
* \param ul_retry The retry times, 0 to wait forever until completeness.
|
||||
*
|
||||
* Return EMAC_OK if the operation is completed successfully.
|
||||
*/
|
||||
static uint8_t emac_wait_phy(Emac* p_emac, const uint32_t ul_retry)
|
||||
{
|
||||
volatile uint32_t ul_retry_count = 0;
|
||||
|
||||
while (!emac_is_phy_idle(p_emac)) {
|
||||
if (ul_retry == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ul_retry_count++;
|
||||
|
||||
if (ul_retry_count >= ul_retry) {
|
||||
return EMAC_TIMEOUT;
|
||||
}
|
||||
}
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable transfer, reset registers and descriptor lists.
|
||||
*
|
||||
* \param p_dev Pointer to EMAC driver instance.
|
||||
*
|
||||
*/
|
||||
static void emac_reset_tx_mem(emac_device_t* p_dev)
|
||||
{
|
||||
Emac *p_hw = p_dev->p_hw;
|
||||
uint8_t *p_tx_buff = p_dev->p_tx_buffer;
|
||||
emac_tx_descriptor_t *p_td = p_dev->p_tx_dscr;
|
||||
|
||||
uint32_t ul_index;
|
||||
uint32_t ul_address;
|
||||
|
||||
/* Disable TX */
|
||||
emac_enable_transmit(p_hw, 0);
|
||||
|
||||
/* Set up the TX descriptors */
|
||||
CIRC_CLEAR(p_dev->us_tx_head, p_dev->us_tx_tail);
|
||||
for (ul_index = 0; ul_index < p_dev->us_tx_list_size; ul_index++) {
|
||||
ul_address = (uint32_t) (&(p_tx_buff[ul_index * EMAC_TX_UNITSIZE]));
|
||||
p_td[ul_index].addr = ul_address;
|
||||
p_td[ul_index].status.val = EMAC_TXD_USED;
|
||||
}
|
||||
p_td[p_dev->us_tx_list_size - 1].status.val =
|
||||
EMAC_TXD_USED | EMAC_TXD_WRAP;
|
||||
|
||||
/* Set transmit buffer queue */
|
||||
emac_set_tx_queue(p_hw, (uint32_t) p_td);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable receiver, reset registers and descriptor list.
|
||||
*
|
||||
* \param p_drv Pointer to EMAC Driver instance.
|
||||
*/
|
||||
static void emac_reset_rx_mem(emac_device_t* p_dev)
|
||||
{
|
||||
Emac *p_hw = p_dev->p_hw;
|
||||
uint8_t *p_rx_buff = p_dev->p_rx_buffer;
|
||||
emac_rx_descriptor_t *pRd = p_dev->p_rx_dscr;
|
||||
|
||||
uint32_t ul_index;
|
||||
uint32_t ul_address;
|
||||
|
||||
/* Disable RX */
|
||||
emac_enable_receive(p_hw, 0);
|
||||
|
||||
/* Set up the RX descriptors */
|
||||
p_dev->us_rx_idx = 0;
|
||||
for (ul_index = 0; ul_index < p_dev->us_rx_list_size; ul_index++) {
|
||||
ul_address = (uint32_t) (&(p_rx_buff[ul_index * EMAC_RX_UNITSIZE]));
|
||||
pRd[ul_index].addr.val = ul_address & EMAC_RXD_ADDR_MASK;
|
||||
pRd[ul_index].status.val = 0;
|
||||
}
|
||||
pRd[p_dev->us_rx_list_size - 1].addr.val |= EMAC_RXD_WRAP;
|
||||
|
||||
/* Set receive buffer queue */
|
||||
emac_set_rx_queue(p_hw, (uint32_t) pRd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Initialize the allocated buffer lists for EMAC driver to transfer data.
|
||||
* Must be invoked after emac_dev_init() but before RX/TX starts.
|
||||
*
|
||||
* \note If input address is not 8-byte aligned, the address is automatically
|
||||
* adjusted and the list size is reduced by one.
|
||||
*
|
||||
* \param p_emac Pointer to EMAC instance.
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
* \param p_dev_mm Pointer to the EMAC memory management control block.
|
||||
* \param p_tx_cb Pointer to allocated TX callback list.
|
||||
*
|
||||
* \return EMAC_OK or EMAC_PARAM.
|
||||
*/
|
||||
static uint8_t emac_init_mem(Emac* p_emac, emac_device_t* p_emac_dev,
|
||||
emac_dev_mem_t* p_dev_mm,
|
||||
emac_dev_tx_cb_t* p_tx_cb)
|
||||
{
|
||||
if (p_dev_mm->us_rx_size <= 1 || p_dev_mm->us_tx_size <= 1 || p_tx_cb == NULL) {
|
||||
return EMAC_PARAM;
|
||||
}
|
||||
|
||||
/* Assign RX buffers */
|
||||
if (((uint32_t) p_dev_mm->p_rx_buffer & 0x7)
|
||||
|| ((uint32_t) p_dev_mm->p_rx_dscr & 0x7)) {
|
||||
p_dev_mm->us_rx_size--;
|
||||
}
|
||||
p_emac_dev->p_rx_buffer =
|
||||
(uint8_t *) ((uint32_t) p_dev_mm->p_rx_buffer & 0xFFFFFFF8);
|
||||
p_emac_dev->p_rx_dscr =
|
||||
(emac_rx_descriptor_t *) ((uint32_t) p_dev_mm->p_rx_dscr
|
||||
& 0xFFFFFFF8);
|
||||
p_emac_dev->us_rx_list_size = p_dev_mm->us_rx_size;
|
||||
|
||||
/* Assign TX buffers */
|
||||
if (((uint32_t) p_dev_mm->p_tx_buffer & 0x7)
|
||||
|| ((uint32_t) p_dev_mm->p_tx_dscr & 0x7)) {
|
||||
p_dev_mm->us_tx_size--;
|
||||
}
|
||||
p_emac_dev->p_tx_buffer =
|
||||
(uint8_t *) ((uint32_t) p_dev_mm->p_tx_buffer & 0xFFFFFFF8);
|
||||
p_emac_dev->p_tx_dscr =
|
||||
(emac_tx_descriptor_t *) ((uint32_t) p_dev_mm->p_tx_dscr
|
||||
& 0xFFFFFFF8);
|
||||
p_emac_dev->us_tx_list_size = p_dev_mm->us_tx_size;
|
||||
p_emac_dev->func_tx_cb_list = p_tx_cb;
|
||||
|
||||
/* Reset TX & RX */
|
||||
emac_reset_rx_mem(p_emac_dev);
|
||||
emac_reset_tx_mem(p_emac_dev);
|
||||
|
||||
/* Enable Rx and Tx, plus the statistics register */
|
||||
emac_enable_transmit(p_emac, 1);
|
||||
emac_enable_receive(p_emac, 1);
|
||||
emac_enable_statistics_write(p_emac, 1);
|
||||
|
||||
/* Set up the interrupts for transmission and errors */
|
||||
emac_enable_interrupt(p_emac,
|
||||
EMAC_IER_RXUBR | /* Enable receive used bit read interrupt. */
|
||||
EMAC_IER_TUND | /* Enable transmit underrun interrupt. */
|
||||
EMAC_IER_RLE | /* Enable retry limit exceeded interrupt. */
|
||||
EMAC_IER_TXERR | /* Enable transmit buffers exhausted in mid-frame interrupt. */
|
||||
EMAC_IER_TCOMP | /* Enable transmit complete interrupt. */
|
||||
EMAC_IER_ROVR | /* Enable receive overrun interrupt. */
|
||||
EMAC_IER_HRESP | /* Enable Hresp not OK interrupt. */
|
||||
EMAC_IER_PFR | /* Enable pause frame received interrupt. */
|
||||
EMAC_IER_PTZ); /* Enable pause time zero interrupt. */
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read the PHY register.
|
||||
*
|
||||
* \param p_emac Pointer to the EMAC instance.
|
||||
* \param uc_phy_address PHY address.
|
||||
* \param uc_address Register address.
|
||||
* \param p_value Pointer to a 32-bit location to store read data.
|
||||
*
|
||||
* \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout.
|
||||
*/
|
||||
uint8_t emac_phy_read(Emac* p_emac, uint8_t uc_phy_address, uint8_t uc_address,
|
||||
uint32_t* p_value)
|
||||
{
|
||||
emac_maintain_phy(p_emac, uc_phy_address, uc_address, 1, 0);
|
||||
|
||||
if (emac_wait_phy(p_emac, MAC_PHY_RETRY_MAX) == EMAC_TIMEOUT) {
|
||||
return EMAC_TIMEOUT;
|
||||
}
|
||||
*p_value = emac_get_phy_data(p_emac);
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write the PHY register.
|
||||
*
|
||||
* \param p_emac Pointer to the EMAC instance.
|
||||
* \param uc_phy_address PHY Address.
|
||||
* \param uc_address Register Address.
|
||||
* \param ul_value Data to write, actually 16-bit data.
|
||||
*
|
||||
* \Return EMAC_OK if successfully, EMAC_TIMEOUT if timeout.
|
||||
*/
|
||||
uint8_t emac_phy_write(Emac* p_emac, uint8_t uc_phy_address,
|
||||
uint8_t uc_address, uint32_t ul_value)
|
||||
{
|
||||
emac_maintain_phy(p_emac, uc_phy_address, uc_address, 0, ul_value);
|
||||
|
||||
if (emac_wait_phy(p_emac, MAC_PHY_RETRY_MAX) == EMAC_TIMEOUT) {
|
||||
return EMAC_TIMEOUT;
|
||||
}
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initialize the EMAC driver.
|
||||
*
|
||||
* \param p_emac Pointer to the EMAC instance.
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param p_opt EMAC configure options.
|
||||
*/
|
||||
void emac_dev_init(Emac* p_emac, emac_device_t* p_emac_dev,
|
||||
emac_options_t* p_opt)
|
||||
{
|
||||
emac_dev_mem_t emac_dev_mm;
|
||||
|
||||
/* Disable TX & RX and more */
|
||||
emac_network_control(p_emac, 0);
|
||||
emac_disable_interrupt(p_emac, ~0u);
|
||||
|
||||
emac_clear_statistics(p_emac);
|
||||
|
||||
/* Clear all status bits in the receive status register. */
|
||||
emac_clear_rx_status(p_emac, EMAC_RSR_OVR | EMAC_RSR_REC | EMAC_RSR_BNA);
|
||||
|
||||
/* Clear all status bits in the transmit status register */
|
||||
emac_clear_tx_status(p_emac, EMAC_TSR_UBR | EMAC_TSR_COL | EMAC_TSR_RLES
|
||||
| EMAC_TSR_BEX | EMAC_TSR_COMP | EMAC_TSR_UND);
|
||||
|
||||
/* Clear interrupts */
|
||||
emac_get_interrupt_status(p_emac);
|
||||
|
||||
/* Enable the copy of data into the buffers
|
||||
ignore broadcasts, and not copy FCS. */
|
||||
emac_set_configure(p_emac,
|
||||
emac_get_configure(p_emac) | EMAC_NCFGR_DRFCS | EMAC_NCFGR_PAE);
|
||||
|
||||
emac_enable_copy_all(p_emac, p_opt->uc_copy_all_frame);
|
||||
emac_disable_broadcast(p_emac, p_opt->uc_no_boardcast);
|
||||
|
||||
/* Fill in EMAC device memory management */
|
||||
emac_dev_mm.p_rx_buffer = gs_uc_rx_buffer;
|
||||
emac_dev_mm.p_rx_dscr = gs_rx_desc;
|
||||
emac_dev_mm.us_rx_size = EMAC_RX_BUFFERS;
|
||||
|
||||
emac_dev_mm.p_tx_buffer = gs_uc_tx_buffer;
|
||||
emac_dev_mm.p_tx_dscr = gs_tx_desc;
|
||||
emac_dev_mm.us_tx_size = EMAC_TX_BUFFERS;
|
||||
|
||||
emac_init_mem(p_emac, p_emac_dev, &emac_dev_mm, gs_tx_callback);
|
||||
|
||||
emac_set_address(p_emac, 0, p_opt->uc_mac_addr);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Frames can be read from the EMAC in multiple sections.
|
||||
* Read ul_frame_size bytes from the EMAC receive buffers to pcTo.
|
||||
* p_rcv_size is the size of the entire frame. Generally emac_read
|
||||
* will be repeatedly called until the sum of all the ul_frame_size equals
|
||||
* the value of p_rcv_size.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param p_frame Address of the frame buffer.
|
||||
* \param ul_frame_size Length of the frame.
|
||||
* \param p_rcv_size Received frame size.
|
||||
*
|
||||
* \return EMAC_OK if receiving frame successfully, otherwise failed.
|
||||
*/
|
||||
uint32_t emac_dev_read(emac_device_t* p_emac_dev, uint8_t* p_frame,
|
||||
uint32_t ul_frame_size, uint32_t* p_rcv_size)
|
||||
{
|
||||
uint16_t us_buffer_length;
|
||||
uint32_t tmp_ul_frame_size = 0;
|
||||
uint8_t *p_tmp_frame = 0;
|
||||
uint16_t us_tmp_idx = p_emac_dev->us_rx_idx;
|
||||
emac_rx_descriptor_t *p_rx_td =
|
||||
&p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
int8_t c_is_frame = 0;
|
||||
|
||||
if (p_frame == NULL)
|
||||
return EMAC_PARAM;
|
||||
|
||||
/* Set the default return value */
|
||||
*p_rcv_size = 0;
|
||||
|
||||
/* Process received RX descriptor */
|
||||
while ((p_rx_td->addr.val & EMAC_RXD_OWNERSHIP) == EMAC_RXD_OWNERSHIP) {
|
||||
/* A start of frame has been received, discard previous fragments */
|
||||
if ((p_rx_td->status.val & EMAC_RXD_SOF) == EMAC_RXD_SOF) {
|
||||
/* Skip previous fragment */
|
||||
while (us_tmp_idx != p_emac_dev->us_rx_idx) {
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
|
||||
circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
|
||||
}
|
||||
/* Reset the temporary frame pointer */
|
||||
p_tmp_frame = p_frame;
|
||||
tmp_ul_frame_size = 0;
|
||||
/* Start to gather buffers in a frame */
|
||||
c_is_frame = 1;
|
||||
}
|
||||
|
||||
/* Increment the pointer */
|
||||
circ_inc(&us_tmp_idx, p_emac_dev->us_rx_list_size);
|
||||
|
||||
/* Copy data in the frame buffer */
|
||||
if (c_is_frame) {
|
||||
if (us_tmp_idx == p_emac_dev->us_rx_idx) {
|
||||
do {
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
|
||||
|
||||
} while (us_tmp_idx != p_emac_dev->us_rx_idx);
|
||||
|
||||
return EMAC_RX_NULL;
|
||||
}
|
||||
/* Copy the buffer into the application frame */
|
||||
us_buffer_length = EMAC_RX_UNITSIZE;
|
||||
if ((tmp_ul_frame_size + us_buffer_length) > ul_frame_size) {
|
||||
us_buffer_length = ul_frame_size - tmp_ul_frame_size;
|
||||
}
|
||||
|
||||
memcpy(p_tmp_frame,
|
||||
(void *)(p_rx_td->addr.val & EMAC_RXD_ADDR_MASK),
|
||||
us_buffer_length);
|
||||
p_tmp_frame += us_buffer_length;
|
||||
tmp_ul_frame_size += us_buffer_length;
|
||||
|
||||
/* An end of frame has been received, return the data */
|
||||
if ((p_rx_td->status.val & EMAC_RXD_EOF) == EMAC_RXD_EOF) {
|
||||
/* Frame size from the EMAC */
|
||||
*p_rcv_size = (p_rx_td->status.val & EMAC_RXD_LEN_MASK);
|
||||
|
||||
/* All data have been copied in the application frame buffer => release TD */
|
||||
while (p_emac_dev->us_rx_idx != us_tmp_idx) {
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[p_emac_dev->us_rx_idx];
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
circ_inc(&p_emac_dev->us_rx_idx, p_emac_dev->us_rx_list_size);
|
||||
}
|
||||
|
||||
/* Application frame buffer is too small so that all data have not been copied */
|
||||
if (tmp_ul_frame_size < *p_rcv_size) {
|
||||
return EMAC_SIZE_TOO_SMALL;
|
||||
}
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
}
|
||||
/* SOF has not been detected, skip the fragment */
|
||||
else {
|
||||
p_rx_td->addr.val &= ~(EMAC_RXD_OWNERSHIP);
|
||||
p_emac_dev->us_rx_idx = us_tmp_idx;
|
||||
}
|
||||
|
||||
/* Process the next buffer */
|
||||
p_rx_td = &p_emac_dev->p_rx_dscr[us_tmp_idx];
|
||||
}
|
||||
|
||||
return EMAC_RX_NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send ulLength bytes from pcFrom. This copies the buffer to one of the
|
||||
* EMAC Tx buffers, and then indicates to the EMAC that the buffer is ready.
|
||||
* If lEndOfFrame is true then the data being copied is the end of the frame
|
||||
* and the frame can be transmitted.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param p_buffer Pointer to the data buffer.
|
||||
* \param ul_size Length of the frame.
|
||||
* \param func_tx_cb Transmit callback function.
|
||||
*
|
||||
* \return Length sent.
|
||||
*/
|
||||
uint32_t emac_dev_write(emac_device_t* p_emac_dev, void *p_buffer,
|
||||
uint32_t ul_size, emac_dev_tx_cb_t func_tx_cb)
|
||||
{
|
||||
|
||||
volatile emac_tx_descriptor_t *p_tx_td;
|
||||
volatile emac_dev_tx_cb_t *p_func_tx_cb;
|
||||
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
|
||||
/* Check parameter */
|
||||
if (ul_size > EMAC_TX_UNITSIZE) {
|
||||
return EMAC_PARAM;
|
||||
}
|
||||
|
||||
/* Pointers to the current transmit descriptor */
|
||||
p_tx_td = &p_emac_dev->p_tx_dscr[p_emac_dev->us_tx_head];
|
||||
|
||||
/* If no free TxTd, buffer can't be sent, schedule the wakeup callback */
|
||||
if (CIRC_SPACE(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
|
||||
p_emac_dev->us_tx_list_size) == 0) {
|
||||
return EMAC_TX_BUSY;
|
||||
}
|
||||
|
||||
/* Pointers to the current Tx callback */
|
||||
p_func_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_head];
|
||||
|
||||
/* Set up/copy data to transmission buffer */
|
||||
if (p_buffer && ul_size) {
|
||||
/* Driver manages the ring buffer */
|
||||
memcpy((void *)p_tx_td->addr, p_buffer, ul_size);
|
||||
}
|
||||
|
||||
/* Tx callback */
|
||||
*p_func_tx_cb = func_tx_cb;
|
||||
|
||||
/* Update transmit descriptor status */
|
||||
|
||||
/* The buffer size defined is the length of ethernet frame,
|
||||
so it's always the last buffer of the frame. */
|
||||
if (p_emac_dev->us_tx_head == p_emac_dev->us_tx_list_size - 1) {
|
||||
p_tx_td->status.val =
|
||||
(ul_size & EMAC_TXD_LEN_MASK) | EMAC_TXD_LAST
|
||||
| EMAC_TXD_WRAP;
|
||||
} else {
|
||||
p_tx_td->status.val =
|
||||
(ul_size & EMAC_TXD_LEN_MASK) | EMAC_TXD_LAST;
|
||||
}
|
||||
|
||||
circ_inc(&p_emac_dev->us_tx_head, p_emac_dev->us_tx_list_size);
|
||||
|
||||
/* Now start to transmit if it is still not done */
|
||||
emac_start_transmission(p_hw);
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get current load of transmit.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
*
|
||||
* \return Current load of transmit.
|
||||
*/
|
||||
uint32_t emac_dev_get_tx_load(emac_device_t* p_emac_dev)
|
||||
{
|
||||
uint16_t us_head = p_emac_dev->us_tx_head;
|
||||
uint16_t us_tail = p_emac_dev->us_tx_tail;
|
||||
return CIRC_CNT(us_head, us_tail, p_emac_dev->us_tx_list_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register/Clear RX callback. Callback will be invoked after the next received
|
||||
* frame.
|
||||
*
|
||||
* When emac_dev_read() returns EMAC_RX_NULL, the application task calls
|
||||
* emac_dev_set_rx_callback() to register func_rx_cb() callback and enters suspend state.
|
||||
* The callback is in charge to resume the task once a new frame has been
|
||||
* received. The next time emac_dev_read() is called, it will be successful.
|
||||
*
|
||||
* This function is usually invoked from the RX callback itself with NULL
|
||||
* callback, to unregister. Once the callback has resumed the application task,
|
||||
* there is no need to invoke the callback again.
|
||||
*
|
||||
* \param p_emac_dev Pointer to the EMAC device instance.
|
||||
* \param func_tx_cb Receive callback function.
|
||||
*/
|
||||
void emac_dev_set_rx_callback(emac_device_t* p_emac_dev,
|
||||
emac_dev_tx_cb_t func_rx_cb)
|
||||
{
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
if (func_rx_cb == NULL) {
|
||||
emac_disable_interrupt(p_hw, EMAC_IDR_RCOMP);
|
||||
p_emac_dev->func_rx_cb = NULL;
|
||||
} else {
|
||||
p_emac_dev->func_rx_cb = func_rx_cb;
|
||||
emac_enable_interrupt(p_hw, EMAC_IER_RCOMP);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register/Clear TX wakeup callback.
|
||||
*
|
||||
* When emac_dev_write() returns EMAC_TX_BUSY (all transmit descriptor busy), the application
|
||||
* task calls emac_dev_set_tx_wakeup_callback() to register func_wakeup() callback and
|
||||
* enters suspend state. The callback is in charge to resume the task once
|
||||
* several transmit descriptors have been released. The next time emac_dev_write() will be called,
|
||||
* it shall be successful.
|
||||
*
|
||||
* This function is usually invoked with NULL callback from the TX wakeup
|
||||
* callback itself, to unregister. Once the callback has resumed the
|
||||
* application task, there is no need to invoke the callback again.
|
||||
*
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
* \param func_wakeup Pointer to wakeup callback function.
|
||||
* \param uc_threshold Number of free transmit descriptor before wakeup callback invoked.
|
||||
*
|
||||
* \return EMAC_OK, EMAC_PARAM on parameter error.
|
||||
*/
|
||||
uint8_t emac_dev_set_tx_wakeup_callback(emac_device_t* p_emac_dev,
|
||||
emac_dev_wakeup_cb_t func_wakeup_cb, uint8_t uc_threshold)
|
||||
{
|
||||
if (func_wakeup_cb == NULL) {
|
||||
p_emac_dev->func_wakeup_cb = NULL;
|
||||
} else {
|
||||
if (uc_threshold <= p_emac_dev->us_tx_list_size) {
|
||||
p_emac_dev->func_wakeup_cb = func_wakeup_cb;
|
||||
p_emac_dev->uc_wakeup_threshold = uc_threshold;
|
||||
} else {
|
||||
return EMAC_PARAM;
|
||||
}
|
||||
}
|
||||
|
||||
return EMAC_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Reset TX & RX queue & statistics.
|
||||
*
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
*/
|
||||
void emac_dev_reset(emac_device_t* p_emac_dev)
|
||||
{
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
emac_reset_rx_mem(p_emac_dev);
|
||||
emac_reset_tx_mem(p_emac_dev);
|
||||
emac_network_control(p_hw, EMAC_NCR_TE | EMAC_NCR_RE
|
||||
| EMAC_NCR_WESTAT | EMAC_NCR_CLRSTAT);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief EMAC Interrupt handler.
|
||||
*
|
||||
* \param p_emac_dev Pointer to EMAC device instance.
|
||||
*/
|
||||
void emac_handler(emac_device_t* p_emac_dev)
|
||||
{
|
||||
Emac *p_hw = p_emac_dev->p_hw;
|
||||
|
||||
emac_tx_descriptor_t *p_tx_td;
|
||||
emac_dev_tx_cb_t *p_tx_cb;
|
||||
volatile uint32_t ul_isr;
|
||||
volatile uint32_t ul_rsr;
|
||||
volatile uint32_t ul_tsr;
|
||||
uint32_t ul_rx_status_flag;
|
||||
uint32_t ul_tx_status_flag;
|
||||
|
||||
ul_isr = emac_get_interrupt_status(p_hw);
|
||||
ul_rsr = emac_get_rx_status(p_hw);
|
||||
ul_tsr = emac_get_tx_status(p_hw);
|
||||
|
||||
ul_isr &= ~(emac_get_interrupt_mask(p_hw) | 0xFFC300);
|
||||
|
||||
/* RX packet */
|
||||
if ((ul_isr & EMAC_ISR_RCOMP) || (ul_rsr & EMAC_RSR_REC)) {
|
||||
ul_rx_status_flag = EMAC_RSR_REC;
|
||||
|
||||
/* Check OVR */
|
||||
if (ul_rsr & EMAC_RSR_OVR) {
|
||||
ul_rx_status_flag |= EMAC_RSR_OVR;
|
||||
}
|
||||
/* Check BNA */
|
||||
if (ul_rsr & EMAC_RSR_BNA) {
|
||||
ul_rx_status_flag |= EMAC_RSR_BNA;
|
||||
}
|
||||
/* Clear status */
|
||||
emac_clear_rx_status(p_hw, ul_rx_status_flag);
|
||||
|
||||
/* Invoke callbacks */
|
||||
if (p_emac_dev->func_rx_cb) {
|
||||
p_emac_dev->func_rx_cb(ul_rx_status_flag);
|
||||
}
|
||||
}
|
||||
|
||||
/* TX packet */
|
||||
if ((ul_isr & EMAC_ISR_TCOMP) || (ul_tsr & EMAC_TSR_COMP)) {
|
||||
|
||||
ul_tx_status_flag = EMAC_TSR_COMP;
|
||||
|
||||
/* A frame transmitted */
|
||||
|
||||
/* Check RLE */
|
||||
if (ul_tsr & EMAC_TSR_RLES) {
|
||||
/* Status RLE & Number of discarded buffers */
|
||||
ul_tx_status_flag = EMAC_TSR_RLES | CIRC_CNT(p_emac_dev->us_tx_head,
|
||||
p_emac_dev->us_tx_tail, p_emac_dev->us_tx_list_size);
|
||||
p_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_tail];
|
||||
emac_reset_tx_mem(p_emac_dev);
|
||||
emac_enable_transmit(p_hw, 1);
|
||||
}
|
||||
/* Check COL */
|
||||
if (ul_tsr & EMAC_TSR_COL) {
|
||||
ul_tx_status_flag |= EMAC_TSR_COL;
|
||||
}
|
||||
/* Check BEX */
|
||||
if (ul_tsr & EMAC_TSR_BEX) {
|
||||
ul_tx_status_flag |= EMAC_TSR_BEX;
|
||||
}
|
||||
/* Check UND */
|
||||
if (ul_tsr & EMAC_TSR_UND) {
|
||||
ul_tx_status_flag |= EMAC_TSR_UND;
|
||||
}
|
||||
/* Clear status */
|
||||
emac_clear_tx_status(p_hw, ul_tx_status_flag);
|
||||
|
||||
if (!CIRC_EMPTY(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail)) {
|
||||
/* Check the buffers */
|
||||
do {
|
||||
p_tx_td = &p_emac_dev->p_tx_dscr[p_emac_dev->us_tx_tail];
|
||||
p_tx_cb = &p_emac_dev->func_tx_cb_list[p_emac_dev->us_tx_tail];
|
||||
/* Any error? Exit if buffer has not been sent yet */
|
||||
if ((p_tx_td->status.val & EMAC_TXD_USED) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Notify upper layer that a packet has been sent */
|
||||
if (*p_tx_cb) {
|
||||
(*p_tx_cb) (ul_tx_status_flag);
|
||||
}
|
||||
|
||||
circ_inc(&p_emac_dev->us_tx_tail, p_emac_dev->us_tx_list_size);
|
||||
} while (CIRC_CNT(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
|
||||
p_emac_dev->us_tx_list_size));
|
||||
}
|
||||
|
||||
if (ul_tsr & EMAC_TSR_RLES) {
|
||||
/* Notify upper layer RLE */
|
||||
if (*p_tx_cb) {
|
||||
(*p_tx_cb) (ul_tx_status_flag);
|
||||
}
|
||||
}
|
||||
|
||||
/* If a wakeup has been scheduled, notify upper layer that it can
|
||||
send other packets, and the sending will be successful. */
|
||||
if ((CIRC_SPACE(p_emac_dev->us_tx_head, p_emac_dev->us_tx_tail,
|
||||
p_emac_dev->us_tx_list_size) >= p_emac_dev->uc_wakeup_threshold)
|
||||
&& p_emac_dev->func_wakeup_cb) {
|
||||
p_emac_dev->func_wakeup_cb();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
#endif // SAM3XA_SERIES
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
94
hardware/digistump/sam/system/libsam/source/gpbr.c
Normal file
94
hardware/digistump/sam/system/libsam/source/gpbr.c
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief General Purpose Backup Registers (GPBR) driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* 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
|
||||
* EXPRESSLY AND SPECIFICALLY 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.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "gpbr.h"
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* \defgroup sam_drivers_gpbr_group General Purpose Backup Registers (GPBR)
|
||||
*
|
||||
* Driver for the General Purpose Backup Registers. This driver provides access
|
||||
* to the main features of the GPBR controller.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* \brief Read the specified backup register.
|
||||
*
|
||||
* \param ul_reg_num General purpose backup register number.
|
||||
*
|
||||
* \return Value of the specified backup register.
|
||||
*/
|
||||
uint32_t gpbr_read(gpbr_num_t ul_reg_num)
|
||||
{
|
||||
return GPBR->SYS_GPBR[ul_reg_num];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write a value to the specified backup register.
|
||||
*
|
||||
* \param ul_reg_num General purpose backup register number.
|
||||
* \param ul_value Value to be written.
|
||||
*/
|
||||
void gpbr_write(gpbr_num_t ul_reg_num, uint32_t ul_value)
|
||||
{
|
||||
GPBR->SYS_GPBR[ul_reg_num] = ul_value;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
@@ -0,0 +1,33 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "interrupt_sam_nvic.h"
|
||||
|
||||
//! Global NVIC interrupt enable status (by default it's enabled)
|
||||
int g_interrupt_enabled = 1;
|
||||
386
hardware/digistump/sam/system/libsam/source/pio.c
Normal file
386
hardware/digistump/sam/system/libsam/source/pio.c
Normal file
@@ -0,0 +1,386 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** \file */
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
#include "chip.h"
|
||||
|
||||
/**
|
||||
* \brief Configures Pio pin internal pull-up.
|
||||
*
|
||||
* \param pPio Pointer to a PIO controller.
|
||||
* \param dwMask Bitmask of one or more pin(s) to configure.
|
||||
* \param dwPullUpEnable Indicates if the pin(s) internal pull-up shall be configured.
|
||||
*/
|
||||
extern void PIO_DisableInterrupt( Pio *pPio, const uint32_t dwMask )
|
||||
{
|
||||
/* Disable interrupts on the pin */
|
||||
pPio->PIO_IDR = dwMask ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures Pio pin internal pull-up.
|
||||
*
|
||||
* \param pPio Pointer to a PIO controller.
|
||||
* \param dwMask Bitmask of one or more pin(s) to configure.
|
||||
* \param dwPullUpEnable Indicates if the pin(s) internal pull-up shall be configured.
|
||||
*/
|
||||
extern void PIO_PullUp( Pio *pPio, const uint32_t dwMask, const uint32_t dwPullUpEnable )
|
||||
{
|
||||
/* Enable the pull-up(s) if necessary */
|
||||
if ( dwPullUpEnable )
|
||||
{
|
||||
pPio->PIO_PUER = dwMask ;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPio->PIO_PUDR = dwMask ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures Glitch or Debouncing filter for input.
|
||||
*
|
||||
* \param pin Pointer to a Pin instance describing one or more pins.
|
||||
* \param cuttoff Cutt off frequency for debounce filter.
|
||||
*/
|
||||
extern void PIO_SetDebounceFilter( Pio* pPio, const uint32_t dwMask, const uint32_t dwCuttOff )
|
||||
{
|
||||
#if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
|
||||
pPio->PIO_IFSCER = dwMask ; /* set Debouncing, 0 bit field no effect */
|
||||
#elif (defined _SAM3XA_) || (defined _SAM3U_)
|
||||
pPio->PIO_DIFSR = dwMask ; /* set Debouncing, 0 bit field no effect */
|
||||
#else
|
||||
#error "The specified chip is not supported."
|
||||
#endif
|
||||
pPio->PIO_SCDR = ((32678/(2*(dwCuttOff))) - 1) & 0x3FFF; /* the lowest 14 bits work */
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets a high output level on all the PIOs defined in the given Pin instance.
|
||||
* This has no immediate effects on PIOs that are not output, but the PIO
|
||||
* controller will memorize the value they are changed to outputs.
|
||||
*
|
||||
* \param pin Pointer to a Pin instance describing one or more pins.
|
||||
*/
|
||||
extern void PIO_Set( Pio* pPio, const uint32_t dwMask )
|
||||
{
|
||||
pPio->PIO_SODR = dwMask ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns 1 if one or more PIO of the given Pin instance currently have
|
||||
* a high level; otherwise returns 0. This method returns the actual value that
|
||||
* is being read on the pin. To return the supposed output value of a pin, use
|
||||
* PIO_GetOutputDataStatus() instead.
|
||||
*
|
||||
* \param pin Pointer to a Pin instance describing one or more pins.
|
||||
*
|
||||
* \return 1 if the Pin instance contains at least one PIO that currently has
|
||||
* a high level; otherwise 0.
|
||||
*/
|
||||
extern uint32_t PIO_Get( Pio* pPio, const EPioType dwType, const uint32_t dwMask )
|
||||
{
|
||||
uint32_t dwReg ;
|
||||
|
||||
if ( (dwType == PIO_OUTPUT_0) || (dwType == PIO_OUTPUT_1) )
|
||||
{
|
||||
dwReg = pPio->PIO_ODSR ;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwReg = pPio->PIO_PDSR ;
|
||||
}
|
||||
|
||||
if ( (dwReg & dwMask) == 0 )
|
||||
{
|
||||
return 0 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1 ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets a low output level on all the PIOs defined in the given Pin instance.
|
||||
* This has no immediate effects on PIOs that are not output, but the PIO
|
||||
* controller will memorize the value they are changed to outputs.
|
||||
*
|
||||
* \param pin Pointer to a Pin instance describing one or more pins.
|
||||
*/
|
||||
extern void PIO_Clear( Pio* pPio, const uint32_t dwMask )
|
||||
{
|
||||
pPio->PIO_CODR = dwMask ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures one pin of a PIO controller as being controlled by specific peripheral.
|
||||
*
|
||||
* \param pPio Pointer to a PIO controller.
|
||||
* \param dwType PIO type.
|
||||
* \param dwMask Bitmask of one or more pin(s) to configure.
|
||||
*/
|
||||
extern void PIO_SetPeripheral( Pio* pPio, EPioType dwType, uint32_t dwMask )
|
||||
{
|
||||
uint32_t dwSR ;
|
||||
|
||||
/* Disable interrupts on the pin(s) */
|
||||
pPio->PIO_IDR = dwMask ;
|
||||
|
||||
switch ( dwType )
|
||||
{
|
||||
case PIO_PERIPH_A :
|
||||
#if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
|
||||
dwSR = pPio->PIO_ABCDSR[0] ;
|
||||
pPio->PIO_ABCDSR[0] &= (~dwMask & dwSR) ;
|
||||
|
||||
dwSR = pPio->PIO_ABCDSR[1];
|
||||
pPio->PIO_ABCDSR[1] &= (~dwMask & dwSR) ;
|
||||
#endif /* (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_) */
|
||||
|
||||
#if (defined _SAM3U_) || (defined _SAM3XA_)
|
||||
dwSR = pPio->PIO_ABSR ;
|
||||
pPio->PIO_ABSR &= (~dwMask & dwSR) ;
|
||||
#endif /* (defined _SAM3U_) || (defined _SAM3XA_) */
|
||||
break ;
|
||||
|
||||
case PIO_PERIPH_B :
|
||||
#if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
|
||||
dwSR = pPio->PIO_ABCDSR[0] ;
|
||||
pPio->PIO_ABCDSR[0] = (dwMask | dwSR) ;
|
||||
|
||||
dwSR = pPio->PIO_ABCDSR[1] ;
|
||||
pPio->PIO_ABCDSR[1] &= (~dwMask & dwSR) ;
|
||||
#endif /* (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_) */
|
||||
|
||||
#if (defined _SAM3U_) || (defined _SAM3XA_)
|
||||
dwSR = pPio->PIO_ABSR ;
|
||||
pPio->PIO_ABSR = (dwMask | dwSR) ;
|
||||
#endif /* (defined _SAM3U_) || (defined _SAM3XA_) */
|
||||
break ;
|
||||
|
||||
#if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
|
||||
case PIO_PERIPH_C :
|
||||
dwSR = pPio->PIO_ABCDSR[0] ;
|
||||
pPio->PIO_ABCDSR[0] &= (~dwMask & dwSR) ;
|
||||
|
||||
dwSR = pPio->PIO_ABCDSR[1] ;
|
||||
pPio->PIO_ABCDSR[1] = (dwMask | dwSR) ;
|
||||
break ;
|
||||
|
||||
case PIO_PERIPH_D :
|
||||
dwSR = pPio->PIO_ABCDSR[0] ;
|
||||
pPio->PIO_ABCDSR[0] = (dwMask | dwSR) ;
|
||||
|
||||
dwSR = pPio->PIO_ABCDSR[1] ;
|
||||
pPio->PIO_ABCDSR[1] = (dwMask | dwSR) ;
|
||||
break ;
|
||||
#endif /* (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_) */
|
||||
|
||||
// other types are invalid in this function
|
||||
case PIO_INPUT :
|
||||
case PIO_OUTPUT_0 :
|
||||
case PIO_OUTPUT_1 :
|
||||
case PIO_NOT_A_PIN :
|
||||
return ;
|
||||
}
|
||||
|
||||
// Remove the pins from under the control of PIO
|
||||
pPio->PIO_PDR = dwMask ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures one or more pin(s) or a PIO controller as inputs. Optionally,
|
||||
* the corresponding internal pull-up(s) and glitch filter(s) can be enabled.
|
||||
*
|
||||
* \param pPio Pointer to a PIO controller.
|
||||
* \param dwMask Bitmask indicating which pin(s) to configure as input(s).
|
||||
* \param dwAttribute .
|
||||
*/
|
||||
extern void PIO_SetInput( Pio* pPio, uint32_t dwMask, uint32_t dwAttribute )
|
||||
{
|
||||
PIO_DisableInterrupt( pPio, dwMask ) ;
|
||||
PIO_PullUp( pPio, dwMask, dwAttribute & PIO_PULLUP ) ;
|
||||
|
||||
/* Enable Input Filter if necessary */
|
||||
if ( dwAttribute & (PIO_DEGLITCH | PIO_DEBOUNCE) )
|
||||
{
|
||||
pPio->PIO_IFER = dwMask ;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPio->PIO_IFDR = dwMask ;
|
||||
}
|
||||
|
||||
/* Enable de-glitch or de-bounce if necessary */
|
||||
#if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
|
||||
if ( dwAttribute & PIO_DEGLITCH )
|
||||
{
|
||||
pPio->PIO_IFSCDR = dwMask ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( dwAttribute & PIO_DEBOUNCE )
|
||||
{
|
||||
pPio->PIO_IFSCER = dwMask ;
|
||||
}
|
||||
}
|
||||
#elif (defined _SAM3U_) || (defined _SAM3XA_)
|
||||
if ( dwAttribute & PIO_DEGLITCH )
|
||||
{
|
||||
pPio->PIO_SCIFSR = dwMask ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( dwAttribute & PIO_DEBOUNCE )
|
||||
{
|
||||
pPio->PIO_SCIFSR = dwMask ;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error "The specified chip is not supported."
|
||||
#endif
|
||||
|
||||
/* Configure pin as input */
|
||||
pPio->PIO_ODR = dwMask ;
|
||||
pPio->PIO_PER = dwMask ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures one or more pin(s) of a PIO controller as outputs, with the
|
||||
* given default value. Optionally, the multi-drive feature can be enabled
|
||||
* on the pin(s).
|
||||
*
|
||||
* \param pPio Pointer to a PIO controller.
|
||||
* \param dwMask Bitmask indicating which pin(s) to configure.
|
||||
* \param defaultValue Default level on the pin(s).
|
||||
* \param enableMultiDrive Indicates if the pin(s) shall be configured as open-drain.
|
||||
* \param enablePullUp Indicates if the pin shall have its pull-up activated.
|
||||
*/
|
||||
extern void PIO_SetOutput( Pio* pPio, uint32_t dwMask, uint32_t dwDefaultValue,
|
||||
uint32_t dwMultiDriveEnable, uint32_t dwPullUpEnable )
|
||||
{
|
||||
PIO_DisableInterrupt( pPio, dwMask ) ;
|
||||
PIO_PullUp( pPio, dwMask, dwPullUpEnable ) ;
|
||||
|
||||
/* Enable multi-drive if necessary */
|
||||
if ( dwMultiDriveEnable )
|
||||
{
|
||||
pPio->PIO_MDER = dwMask ;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPio->PIO_MDDR = dwMask ;
|
||||
}
|
||||
|
||||
/* Set default value */
|
||||
if ( dwDefaultValue )
|
||||
{
|
||||
pPio->PIO_SODR = dwMask ;
|
||||
}
|
||||
else
|
||||
{
|
||||
pPio->PIO_CODR = dwMask ;
|
||||
}
|
||||
|
||||
/* Configure pin(s) as output(s) */
|
||||
pPio->PIO_OER = dwMask ;
|
||||
pPio->PIO_PER = dwMask ;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* \return 1 if the pins have been configured properly; otherwise 0.
|
||||
*/
|
||||
extern uint32_t PIO_Configure( Pio* pPio, const EPioType dwType, const uint32_t dwMask, const uint32_t dwAttribute )
|
||||
{
|
||||
/* Configure pins */
|
||||
switch ( dwType )
|
||||
{
|
||||
case PIO_PERIPH_A :
|
||||
case PIO_PERIPH_B :
|
||||
#if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
|
||||
case PIO_PERIPH_C :
|
||||
case PIO_PERIPH_D :
|
||||
#endif /* (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_) */
|
||||
/* Put the pin under control of peripheral */
|
||||
PIO_SetPeripheral( pPio, dwType, dwMask ) ;
|
||||
/* Disable interrupts on the pin(s) */
|
||||
PIO_DisableInterrupt( pPio, dwMask ) ;
|
||||
/* Enable Pullup */
|
||||
PIO_PullUp( pPio, dwMask, (dwAttribute & PIO_PULLUP) ) ;
|
||||
break;
|
||||
|
||||
case PIO_INPUT :
|
||||
PIO_SetInput( pPio, dwMask, dwAttribute ) ;
|
||||
break;
|
||||
|
||||
case PIO_OUTPUT_0 :
|
||||
case PIO_OUTPUT_1 :
|
||||
PIO_SetOutput( pPio, dwMask, (dwType == PIO_OUTPUT_1),
|
||||
(dwAttribute & PIO_OPENDRAIN) ? 1 : 0,
|
||||
(dwAttribute & PIO_PULLUP) ? 1 : 0);
|
||||
break ;
|
||||
|
||||
default :
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns 1 if one or more PIO of the given Pin are configured to output a
|
||||
* high level (even if they are not output).
|
||||
* To get the actual value of the pin, use PIO_Get() instead.
|
||||
*
|
||||
* \param pPio Pointer to a Pin instance describing one or more pins.
|
||||
*
|
||||
* \return 1 if the Pin instance contains at least one PIO that is configured
|
||||
* to output a high level; otherwise 0.
|
||||
*/
|
||||
extern uint32_t PIO_GetOutputDataStatus( const Pio* pPio, const uint32_t dwMask )
|
||||
{
|
||||
/* Test if pin is under control of PIO */
|
||||
if ( (pPio->PIO_PSR & dwMask) != 0 )
|
||||
{
|
||||
/* Test if pin is configured as output */
|
||||
if ( (pPio->PIO_OSR & dwMask) != 0 )
|
||||
{
|
||||
return 1 ;
|
||||
}
|
||||
}
|
||||
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
1046
hardware/digistump/sam/system/libsam/source/pmc.c
Normal file
1046
hardware/digistump/sam/system/libsam/source/pmc.c
Normal file
File diff suppressed because it is too large
Load Diff
624
hardware/digistump/sam/system/libsam/source/pwmc.c
Normal file
624
hardware/digistump/sam/system/libsam/source/pwmc.c
Normal file
@@ -0,0 +1,624 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** \addtogroup pwm_module Working with PWM
|
||||
* The PWM driver provides the interface to configure and use the PWM
|
||||
* peripheral.
|
||||
*
|
||||
* The PWM macrocell controls square output waveforms of 4 channels.
|
||||
* Characteristics of output waveforms such as period, duty-cycle,
|
||||
* dead-time can be configured.\n
|
||||
* Some of PWM channels can be linked together as synchronous ul_channel and
|
||||
* duty-cycle of synchronous channels can be updated by PDC automaticly.
|
||||
*
|
||||
* Before enabling the channels, they must have been configured first.
|
||||
* The main settings include:
|
||||
* <ul>
|
||||
* <li>Configuration of the clock generator.</li>
|
||||
* <li>Selection of the clock for each ul_channel.</li>
|
||||
* <li>Configuration of output waveform characteristics, such as period, duty-cycle etc.</li>
|
||||
* <li>Configuration for synchronous channels if needed.</li>
|
||||
* - Selection of the synchronous channels.
|
||||
* - Selection of the moment when the WRDY flag and the corresponding PDC
|
||||
* transfer request are set (PTRM and PTRCS in the PWM_SCM register).
|
||||
* - Configuration of the update mode (UPDM in the PWM_SCM register).
|
||||
* - Configuration of the update period (UPR in the PWM_SCUP register).
|
||||
* </ul>
|
||||
*
|
||||
* After the channels is enabled, the user must use respective update registers
|
||||
* to change the wave characteristics to prevent unexpected output waveform.
|
||||
* i.e. PWM_CDTYUPDx register should be used if user want to change duty-cycle
|
||||
* when the ul_channel is enabled.
|
||||
*
|
||||
* For more accurate information, please look at the PWM section of the
|
||||
* Datasheet.
|
||||
*
|
||||
* Related files :\n
|
||||
* \ref pwmc.c\n
|
||||
* \ref pwmc.h.\n
|
||||
*/
|
||||
/*@{*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of the Pulse Width Modulation Controller (PWM) peripheral.
|
||||
*
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Local functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Finds a prescaler/divisor couple to generate the desired frequency
|
||||
* from MCK.
|
||||
*
|
||||
* Returns the value to enter in PWM_CLK or 0 if the configuration cannot be
|
||||
* met.
|
||||
*
|
||||
* \param frequency Desired frequency in Hz.
|
||||
* \param mck Master clock frequency in Hz.
|
||||
*/
|
||||
static uint16_t FindClockConfiguration(
|
||||
uint32_t frequency,
|
||||
uint32_t mck)
|
||||
{
|
||||
uint32_t divisors[11] = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024};
|
||||
uint8_t divisor = 0;
|
||||
uint32_t prescaler;
|
||||
|
||||
assert(frequency < mck);
|
||||
|
||||
/* Find prescaler and divisor values */
|
||||
prescaler = (mck / divisors[divisor]) / frequency;
|
||||
while ((prescaler > 255) && (divisor < 11)) {
|
||||
|
||||
divisor++;
|
||||
prescaler = (mck / divisors[divisor]) / frequency;
|
||||
}
|
||||
|
||||
/* Return result */
|
||||
if ( divisor < 11 )
|
||||
{
|
||||
// TRACE_DEBUG( "Found divisor=%u and prescaler=%u for freq=%uHz\n\r", divisors[divisor], prescaler, frequency ) ;
|
||||
|
||||
return prescaler | (divisor << 8) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0 ;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Exported functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Configures PWM a ul_channel with the given parameters, basic configure function.
|
||||
*
|
||||
* The PWM controller must have been clocked in the PMC prior to calling this
|
||||
* function.
|
||||
* Beware: this function disables the ul_channel. It waits until disable is effective.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
* \param prescaler Channel prescaler.
|
||||
* \param alignment Channel alignment.
|
||||
* \param polarity Channel polarity.
|
||||
*/
|
||||
void PWMC_ConfigureChannel( Pwm* pPwm, uint32_t ul_channel, uint32_t prescaler, uint32_t alignment, uint32_t polarity )
|
||||
{
|
||||
pPwm->PWM_CH_NUM[0].PWM_CMR = 1;
|
||||
|
||||
// assert(prescaler < PWM_CMR0_CPRE_MCKB);
|
||||
assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
|
||||
assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
|
||||
|
||||
/* Disable ul_channel (effective at the end of the current period) */
|
||||
if ((pPwm->PWM_SR & (1 << ul_channel)) != 0) {
|
||||
pPwm->PWM_DIS = 1 << ul_channel;
|
||||
while ((pPwm->PWM_SR & (1 << ul_channel)) != 0);
|
||||
}
|
||||
|
||||
/* Configure ul_channel */
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_CMR = prescaler | alignment | polarity;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures PWM a ul_channel with the given parameters, extend configure function.
|
||||
*
|
||||
* The PWM controller must have been clocked in the PMC prior to calling this
|
||||
* function.
|
||||
* Beware: this function disables the ul_channel. It waits until disable is effective.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
* \param prescaler Channel prescaler.
|
||||
* \param alignment Channel alignment.
|
||||
* \param polarity Channel polarity.
|
||||
* \param countEventSelect Channel counter event selection.
|
||||
* \param DTEnable Channel dead time generator enable.
|
||||
* \param DTHInverte Channel Dead-Time PWMHx output Inverted.
|
||||
* \param DTLInverte Channel Dead-Time PWMHx output Inverted.
|
||||
*/
|
||||
void PWMC_ConfigureChannelExt( Pwm* pPwm, uint32_t ul_channel, uint32_t prescaler, uint32_t alignment, uint32_t polarity,
|
||||
uint32_t countEventSelect, uint32_t DTEnable, uint32_t DTHInverte, uint32_t DTLInverte )
|
||||
{
|
||||
// assert(prescaler < PWM_CMR0_CPRE_MCKB);
|
||||
assert((alignment & (uint32_t)~PWM_CMR_CALG) == 0);
|
||||
assert((polarity & (uint32_t)~PWM_CMR_CPOL) == 0);
|
||||
assert((countEventSelect & (uint32_t)~PWM_CMR_CES) == 0);
|
||||
assert((DTEnable & (uint32_t)~PWM_CMR_DTE) == 0);
|
||||
assert((DTHInverte & (uint32_t)~PWM_CMR_DTHI) == 0);
|
||||
assert((DTLInverte & (uint32_t)~PWM_CMR_DTLI) == 0);
|
||||
|
||||
/* Disable ul_channel (effective at the end of the current period) */
|
||||
if ((pPwm->PWM_SR & (1 << ul_channel)) != 0) {
|
||||
pPwm->PWM_DIS = 1 << ul_channel;
|
||||
while ((pPwm->PWM_SR & (1 << ul_channel)) != 0);
|
||||
}
|
||||
|
||||
/* Configure ul_channel */
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_CMR = prescaler | alignment | polarity |
|
||||
countEventSelect | DTEnable | DTHInverte | DTLInverte;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures PWM clocks A & B to run at the given frequencies.
|
||||
*
|
||||
* This function finds the best MCK divisor and prescaler values automatically.
|
||||
*
|
||||
* \param clka Desired clock A frequency (0 if not used).
|
||||
* \param clkb Desired clock B frequency (0 if not used).
|
||||
* \param mck Master clock frequency.
|
||||
*/
|
||||
void PWMC_ConfigureClocks(uint32_t clka, uint32_t clkb, uint32_t mck)
|
||||
{
|
||||
uint32_t mode = 0;
|
||||
uint32_t result;
|
||||
|
||||
/* Clock A */
|
||||
if (clka != 0) {
|
||||
|
||||
result = FindClockConfiguration(clka, mck);
|
||||
assert( result != 0 ) ;
|
||||
mode |= result;
|
||||
}
|
||||
|
||||
/* Clock B */
|
||||
if (clkb != 0) {
|
||||
|
||||
result = FindClockConfiguration(clkb, mck);
|
||||
assert( result != 0 ) ;
|
||||
mode |= (result << 16);
|
||||
}
|
||||
|
||||
/* Configure clocks */
|
||||
// TRACE_DEBUG( "Setting PWM_CLK = 0x%08X\n\r", mode ) ;
|
||||
PWM->PWM_CLK = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the period value used by a PWM ul_channel.
|
||||
*
|
||||
* This function writes directly to the CPRD register if the ul_channel is disabled;
|
||||
* otherwise, it uses the update register CPRDUPD.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
* \param period Period value.
|
||||
*/
|
||||
void PWMC_SetPeriod( Pwm* pPwm, uint32_t ul_channel, uint16_t period)
|
||||
{
|
||||
/* If ul_channel is disabled, write to CPRD */
|
||||
if ((pPwm->PWM_SR & (1 << ul_channel)) == 0) {
|
||||
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_CPRD = period;
|
||||
}
|
||||
/* Otherwise use update register */
|
||||
else {
|
||||
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_CPRDUPD = period;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the duty cycle used by a PWM ul_channel.
|
||||
* This function writes directly to the CDTY register if the ul_channel is disabled;
|
||||
* otherwise it uses the update register CDTYUPD.
|
||||
* Note that the duty cycle must always be inferior or equal to the ul_channel
|
||||
* period.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
* \param duty Duty cycle value.
|
||||
*/
|
||||
void PWMC_SetDutyCycle( Pwm* pPwm, uint32_t ul_channel, uint16_t duty)
|
||||
{
|
||||
assert(duty <= pPwm->PWM_CH_NUM[ul_channel].PWM_CPRD);
|
||||
|
||||
/* If ul_channel is disabled, write to CDTY */
|
||||
if ((pPwm->PWM_SR & (1 << ul_channel)) == 0) {
|
||||
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_CDTY = duty;
|
||||
}
|
||||
/* Otherwise use update register */
|
||||
else {
|
||||
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_CDTYUPD = duty;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the dead time used by a PWM ul_channel.
|
||||
* This function writes directly to the DT register if the ul_channel is disabled;
|
||||
* otherwise it uses the update register DTUPD.
|
||||
* Note that the dead time must always be inferior or equal to the ul_channel
|
||||
* period.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
* \param timeH Dead time value for PWMHx output.
|
||||
* \param timeL Dead time value for PWMLx output.
|
||||
*/
|
||||
void PWMC_SetDeadTime( Pwm* pPwm, uint32_t ul_channel, uint16_t timeH, uint16_t timeL)
|
||||
{
|
||||
assert(timeH <= pPwm->PWM_CH_NUM[ul_channel].PWM_CPRD);
|
||||
assert(timeL <= pPwm->PWM_CH_NUM[ul_channel].PWM_CPRD);
|
||||
|
||||
/* If ul_channel is disabled, write to DT */
|
||||
if ((pPwm->PWM_SR & (1 << ul_channel)) == 0) {
|
||||
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_DT = timeH | (timeL << 16);
|
||||
}
|
||||
/* Otherwise use update register */
|
||||
else {
|
||||
pPwm->PWM_CH_NUM[ul_channel].PWM_DTUPD = timeH | (timeL << 16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures Syncronous ul_channel with the given parameters.
|
||||
* Beware: At this time, the channels should be disabled.
|
||||
*
|
||||
* \param channels Bitwise OR of Syncronous channels.
|
||||
* \param updateMode Syncronous ul_channel update mode.
|
||||
* \param requestMode PDC transfer request mode.
|
||||
* \param requestComparisonSelect PDC transfer request comparison selection.
|
||||
*/
|
||||
void PWMC_ConfigureSyncChannel( Pwm* pPwm, uint32_t ul_channels, uint32_t updateMode, uint32_t requestMode, uint32_t requestComparisonSelect )
|
||||
{
|
||||
pPwm->PWM_SCM = ul_channels | updateMode | requestMode | requestComparisonSelect;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the update period of the synchronous channels.
|
||||
* This function writes directly to the SCUP register if the ul_channel #0 is disabled;
|
||||
* otherwise it uses the update register SCUPUPD.
|
||||
*
|
||||
* \param period update period.
|
||||
*/
|
||||
void PWMC_SetSyncChannelUpdatePeriod( Pwm* pPwm, uint8_t period )
|
||||
{
|
||||
/* If ul_channel is disabled, write to SCUP */
|
||||
if ((pPwm->PWM_SR & (1 << 0)) == 0) {
|
||||
|
||||
pPwm->PWM_SCUP = period;
|
||||
}
|
||||
/* Otherwise use update register */
|
||||
else {
|
||||
|
||||
pPwm->PWM_SCUPUPD = period;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets synchronous channels update unlock.
|
||||
*
|
||||
* Note: If the UPDM field is set to 0, writing the UPDULOCK bit to 1
|
||||
* triggers the update of the period value, the duty-cycle and
|
||||
* the dead-time values of synchronous channels at the beginning
|
||||
* of the next PWM period. If the field UPDM is set to 1 or 2,
|
||||
* writing the UPDULOCK bit to 1 triggers only the update of
|
||||
* the period value and of the dead-time values of synchronous channels.
|
||||
* This bit is automatically reset when the update is done.
|
||||
*/
|
||||
void PWMC_SetSyncChannelUpdateUnlock( Pwm* pPwm )
|
||||
{
|
||||
pPwm->PWM_SCUC = PWM_SCUC_UPDULOCK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables the given PWM ul_channel.
|
||||
*
|
||||
* This does NOT enable the corresponding pin;this must be done in the user code.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
*/
|
||||
void PWMC_EnableChannel( Pwm* pPwm, uint32_t ul_channel)
|
||||
{
|
||||
pPwm->PWM_ENA = 1 << ul_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disables the given PWM ul_channel.
|
||||
*
|
||||
* Beware, ul_channel will be effectively disabled at the end of the current period.
|
||||
* Application can check ul_channel is disabled using the following wait loop:
|
||||
* while ((PWM->PWM_SR & (1 << ul_channel)) != 0);
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
*/
|
||||
void PWMC_DisableChannel( Pwm* pPwm, uint32_t ul_channel)
|
||||
{
|
||||
pPwm->PWM_DIS = 1 << ul_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables the period interrupt for the given PWM ul_channel.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
*/
|
||||
void PWMC_EnableChannelIt( Pwm* pPwm, uint32_t ul_channel)
|
||||
{
|
||||
pPwm->PWM_IER1 = 1 << ul_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disables the period interrupt for the given PWM ul_channel.
|
||||
*
|
||||
* \param ul_channel Channel number.
|
||||
*/
|
||||
void PWMC_DisableChannelIt( Pwm* pPwm, uint32_t ul_channel)
|
||||
{
|
||||
pPwm->PWM_IDR1 = 1 << ul_channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables the selected interrupts sources on a PWMC peripheral.
|
||||
*
|
||||
* \param sources1 Bitwise OR of selected interrupt sources of PWM_IER1.
|
||||
* \param sources2 Bitwise OR of selected interrupt sources of PWM_IER2.
|
||||
*/
|
||||
void PWMC_EnableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
|
||||
{
|
||||
pPwm->PWM_IER1 = sources1;
|
||||
pPwm->PWM_IER2 = sources2;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disables the selected interrupts sources on a PWMC peripheral.
|
||||
*
|
||||
* \param sources1 Bitwise OR of selected interrupt sources of PWM_IDR1.
|
||||
* \param sources2 Bitwise OR of selected interrupt sources of PWM_IDR2.
|
||||
*/
|
||||
void PWMC_DisableIt( Pwm* pPwm, uint32_t sources1, uint32_t sources2)
|
||||
{
|
||||
pPwm->PWM_IDR1 = sources1;
|
||||
pPwm->PWM_IDR2 = sources2;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends the contents of buffer through a PWMC peripheral, using the PDC to
|
||||
* take care of the transfer.
|
||||
*
|
||||
* Note: Duty cycle of syncronous channels can update by PDC
|
||||
* when the field UPDM (Update Mode) in the PWM_SCM register is set to 2.
|
||||
*
|
||||
* \param pwmc Pointer to an Pwm instance.
|
||||
* \param pvBuffer Data buffer to send.
|
||||
* \param length Length of the data buffer.
|
||||
*/
|
||||
uint8_t PWMC_WriteBuffer( Pwm *pwmc, void* pvBuffer, uint32_t length)
|
||||
{
|
||||
/* Check if first bank is free */
|
||||
if (pwmc->PWM_TCR == 0) {
|
||||
|
||||
pwmc->PWM_TPR = (uint32_t) pvBuffer;
|
||||
pwmc->PWM_TCR = length;
|
||||
pwmc->PWM_PTCR = PERIPH_PTCR_TXTEN;
|
||||
return 1;
|
||||
}
|
||||
/* Check if second bank is free */
|
||||
else if (pwmc->PWM_TNCR == 0) {
|
||||
|
||||
pwmc->PWM_TNPR = (uint32_t) pvBuffer;
|
||||
pwmc->PWM_TNCR = length;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* No free banks */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set PWM output override value.
|
||||
*
|
||||
* \param value Bitwise OR of output override value.
|
||||
*/
|
||||
void PWMC_SetOverrideValue( Pwm* pPwm, uint32_t value)
|
||||
{
|
||||
pPwm->PWM_OOV = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enalbe override output.
|
||||
*
|
||||
* \param value Bitwise OR of output selection.
|
||||
* \param sync 0: enable the output asyncronously, 1: enable it syncronously
|
||||
*/
|
||||
void PWMC_EnableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
|
||||
{
|
||||
if (sync) {
|
||||
|
||||
pPwm->PWM_OSSUPD = value;
|
||||
} else {
|
||||
|
||||
pPwm->PWM_OSS = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disalbe override output.
|
||||
*
|
||||
* \param value Bitwise OR of output selection.
|
||||
* \param sync 0: enable the output asyncronously, 1: enable it syncronously
|
||||
*/
|
||||
void PWMC_DisableOverrideOutput( Pwm* pPwm, uint32_t value, uint32_t sync)
|
||||
{
|
||||
if (sync) {
|
||||
|
||||
pPwm->PWM_OSCUPD = value;
|
||||
} else {
|
||||
|
||||
pPwm->PWM_OSC = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set PWM fault mode.
|
||||
*
|
||||
* \param mode Bitwise OR of fault mode.
|
||||
*/
|
||||
void PWMC_SetFaultMode( Pwm* pPwm, uint32_t mode)
|
||||
{
|
||||
pPwm->PWM_FMR = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief PWM fault clear.
|
||||
*
|
||||
* \param fault Bitwise OR of fault to clear.
|
||||
*/
|
||||
void PWMC_FaultClear( Pwm* pPwm, uint32_t fault)
|
||||
{
|
||||
pPwm->PWM_FCR = fault;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set PWM fault protection value.
|
||||
*
|
||||
* \param value Bitwise OR of fault protection value.
|
||||
*/
|
||||
void PWMC_SetFaultProtectionValue( Pwm* pPwm, uint32_t value)
|
||||
{
|
||||
pPwm->PWM_FPV = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable PWM fault protection.
|
||||
*
|
||||
* \param value Bitwise OR of FPEx[y].
|
||||
*/
|
||||
void PWMC_EnableFaultProtection( Pwm* pPwm, uint32_t ul_channel, uint32_t ul_value)
|
||||
{
|
||||
/* Fault Protection Enable Register */
|
||||
uint32_t ul_fault_enable_reg = 0;
|
||||
|
||||
#if (SAM3XA_SERIES)
|
||||
if (ul_channel < 4)
|
||||
{
|
||||
ul_channel *= 8;
|
||||
ul_fault_enable_reg = pPwm->PWM_FPE1;
|
||||
ul_fault_enable_reg &= ~(0xFF << ul_channel);
|
||||
ul_fault_enable_reg |= (ul_value << ul_channel);
|
||||
pPwm->PWM_FPE1 = ul_fault_enable_reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
ul_channel -= 4;
|
||||
ul_channel *= 8;
|
||||
ul_fault_enable_reg = pPwm->PWM_FPE2;
|
||||
ul_fault_enable_reg &= ~(0xFF << ul_channel);
|
||||
ul_fault_enable_reg |= (ul_value << ul_channel);
|
||||
pPwm->PWM_FPE2 = ul_fault_enable_reg;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (SAM3U_SERIES || SAM3S_SERIES || SAM3SD8_SERIES || SAM4S_SERIES)
|
||||
ul_channel *= 8;
|
||||
ul_fault_enable_reg = pPwm->PWM_FPE;
|
||||
ul_fault_enable_reg &= ~(0xFF << ul_channel);
|
||||
ul_fault_enable_reg |= (ul_value << ul_channel);
|
||||
pPwm->PWM_FPE = ul_fault_enable_reg;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure comparison unit.
|
||||
*
|
||||
* \param x comparison x index
|
||||
* \param value comparison x value.
|
||||
* \param mode comparison x mode
|
||||
*/
|
||||
void PWMC_ConfigureComparisonUnit( Pwm* pPwm, uint32_t x, uint32_t value, uint32_t mode)
|
||||
{
|
||||
assert(x < 8);
|
||||
|
||||
/* If ul_channel is disabled, write to CMPxM & CMPxV */
|
||||
if ((pPwm->PWM_SR & (1 << 0)) == 0) {
|
||||
pPwm->PWM_CMP[x].PWM_CMPM = mode;
|
||||
pPwm->PWM_CMP[x].PWM_CMPV = value;
|
||||
}
|
||||
/* Otherwise use update register */
|
||||
else {
|
||||
pPwm->PWM_CMP[x].PWM_CMPMUPD = mode;
|
||||
pPwm->PWM_CMP[x].PWM_CMPVUPD = value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure event line mode.
|
||||
*
|
||||
* \param x Line x
|
||||
* \param mode Bitwise OR of line mode selection
|
||||
*/
|
||||
void PWMC_ConfigureEventLineMode( Pwm* pPwm, uint32_t x, uint32_t mode)
|
||||
{
|
||||
assert(x < 2);
|
||||
|
||||
if (x == 0)
|
||||
{
|
||||
pPwm->PWM_ELMR[0] = mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (x == 1)
|
||||
{
|
||||
pPwm->PWM_ELMR[1] = mode;
|
||||
}
|
||||
}
|
||||
}
|
||||
193
hardware/digistump/sam/system/libsam/source/rstc.c
Normal file
193
hardware/digistump/sam/system/libsam/source/rstc.c
Normal file
@@ -0,0 +1,193 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Reset Controller (RSTC) driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* 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
|
||||
* EXPRESSLY AND SPECIFICALLY 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.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rstc.h"
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* \defgroup sam_drivers_rstc_group Reset Controller (RSTC)
|
||||
*
|
||||
* Driver for the RSTC (Reset Controller). This driver provides access to the main
|
||||
* features of the Reset controller.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define RSTC_KEY 0xA5000000
|
||||
|
||||
/**
|
||||
* \brief Set external reset length.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
* \param ul_length The length of external reset.
|
||||
*/
|
||||
void rstc_set_external_reset(Rstc *p_rstc, const uint32_t ul_length)
|
||||
{
|
||||
uint32_t mode = p_rstc->RSTC_MR;
|
||||
|
||||
mode &= ~(RSTC_MR_ERSTL_Msk | RSTC_MR_KEY_Msk);
|
||||
mode |= (RSTC_MR_ERSTL(ul_length) | RSTC_KEY);
|
||||
|
||||
p_rstc->RSTC_MR = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable user reset.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*/
|
||||
void rstc_enable_user_reset(Rstc *p_rstc)
|
||||
{
|
||||
uint32_t mode = p_rstc->RSTC_MR;
|
||||
|
||||
mode &= ~RSTC_MR_KEY_Msk;
|
||||
mode |= (RSTC_MR_URSTEN | RSTC_KEY);
|
||||
|
||||
p_rstc->RSTC_MR = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable user reset.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*/
|
||||
void rstc_disable_user_reset(Rstc *p_rstc)
|
||||
{
|
||||
uint32_t mode = p_rstc->RSTC_MR;
|
||||
|
||||
mode &= ~(RSTC_MR_URSTEN | RSTC_MR_KEY_Msk);
|
||||
mode |= RSTC_KEY;
|
||||
|
||||
p_rstc->RSTC_MR = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable user reset interrupt.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*/
|
||||
void rstc_enable_user_reset_interrupt(Rstc *p_rstc)
|
||||
{
|
||||
uint32_t mode = p_rstc->RSTC_MR;
|
||||
|
||||
mode &= ~RSTC_MR_KEY_Msk;
|
||||
mode |= (RSTC_MR_URSTIEN | RSTC_KEY);
|
||||
|
||||
p_rstc->RSTC_MR = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable user reset interrupt.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*/
|
||||
void rstc_disable_user_reset_interrupt(Rstc *p_rstc)
|
||||
{
|
||||
uint32_t mode = p_rstc->RSTC_MR;
|
||||
|
||||
mode &= ~(RSTC_MR_URSTIEN | RSTC_MR_KEY_Msk);
|
||||
mode |= RSTC_KEY;
|
||||
|
||||
p_rstc->RSTC_MR = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Perform software reset.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*/
|
||||
void rstc_start_software_reset(Rstc *p_rstc)
|
||||
{
|
||||
p_rstc->RSTC_CR = RSTC_KEY | RSTC_CR_PROCRST | RSTC_CR_PERRST;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Asserts the NRST pin for external resets.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*/
|
||||
void rstc_reset_extern(Rstc *p_rstc)
|
||||
{
|
||||
p_rstc->RSTC_CR = RSTC_KEY | RSTC_CR_EXTRST;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get RSTC status.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*
|
||||
* \return RSTC status.
|
||||
*/
|
||||
uint32_t rstc_get_status(Rstc *p_rstc)
|
||||
{
|
||||
return p_rstc->RSTC_SR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get reset cause.
|
||||
*
|
||||
* \param p_rstc Pointer to an RSTC instance.
|
||||
*
|
||||
* \return The last reset cause.
|
||||
*/
|
||||
uint32_t rstc_get_reset_cause(Rstc *p_rstc)
|
||||
{
|
||||
return (p_rstc->RSTC_SR & RSTC_SR_RSTTYP_Msk);
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
450
hardware/digistump/sam/system/libsam/source/rtc.c
Normal file
450
hardware/digistump/sam/system/libsam/source/rtc.c
Normal file
@@ -0,0 +1,450 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** \addtogroup rtc_module Working with RTC
|
||||
* The RTC driver provides the interface to configure and use the RTC
|
||||
* peripheral.
|
||||
*
|
||||
* It manages date, time, and alarms.\n
|
||||
* This timer is clocked by the 32kHz system clock, and is not impacted by
|
||||
* power management settings (PMC). To be accurate, it is better to use an
|
||||
* external 32kHz crystal instead of the internal 32kHz RC.\n
|
||||
*
|
||||
* It uses BCD format, and time can be set in AM/PM or 24h mode through a
|
||||
* configuration bit in the mode register.\n
|
||||
*
|
||||
* To update date or time, the user has to follow these few steps :
|
||||
* <ul>
|
||||
* <li>Set UPDTIM and/or UPDCAL bit(s) in RTC_CR,</li>
|
||||
* <li>Polling or IRQ on the ACKUPD bit of RTC_CR,</li>
|
||||
* <li>Clear ACKUPD bit in RTC_SCCR,</li>
|
||||
* <li>Update Time and/or Calendar values in RTC_TIMR/RTC_CALR (BCD format),</li>
|
||||
* <li>Clear UPDTIM and/or UPDCAL bit in RTC_CR.</li>
|
||||
* </ul>
|
||||
* An alarm can be set to happen on month, date, hours, minutes or seconds,
|
||||
* by setting the proper "Enable" bit of each of these fields in the Time and
|
||||
* Calendar registers.
|
||||
* This allows a large number of configurations to be available for the user.
|
||||
* Alarm occurence can be detected even by polling or interrupt.
|
||||
*
|
||||
* A check of the validity of the date and time format and values written by the user is automatically done.
|
||||
* Errors are reported through the Valid Entry Register.
|
||||
*
|
||||
* For more accurate information, please look at the RTC section of the
|
||||
* Datasheet.
|
||||
*
|
||||
* Related files :\n
|
||||
* \ref rtc.c\n
|
||||
* \ref rtc.h.\n
|
||||
*/
|
||||
/*@{*/
|
||||
/*@}*/
|
||||
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of Real Time Clock (RTC) controller.
|
||||
*
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Exported functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Sets the RTC in either 12 or 24 hour mode.
|
||||
*
|
||||
* \param mode Hour mode.
|
||||
*/
|
||||
extern void RTC_SetHourMode( Rtc* pRtc, uint32_t dwMode )
|
||||
{
|
||||
assert((dwMode & 0xFFFFFFFE) == 0);
|
||||
|
||||
pRtc->RTC_MR = dwMode ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Gets the RTC mode.
|
||||
*
|
||||
* \return Hour mode.
|
||||
*/
|
||||
extern uint32_t RTC_GetHourMode( Rtc* pRtc )
|
||||
{
|
||||
uint32_t dwMode ;
|
||||
|
||||
dwMode = pRtc->RTC_MR;
|
||||
dwMode &= 0xFFFFFFFE;
|
||||
|
||||
return dwMode ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables the selected interrupt sources of the RTC.
|
||||
*
|
||||
* \param sources Interrupt sources to enable.
|
||||
*/
|
||||
extern void RTC_EnableIt( Rtc* pRtc, uint32_t dwSources )
|
||||
{
|
||||
assert((dwSources & (uint32_t)(~0x1F)) == 0);
|
||||
|
||||
pRtc->RTC_IER = dwSources ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disables the selected interrupt sources of the RTC.
|
||||
*
|
||||
* \param sources Interrupt sources to disable.
|
||||
*/
|
||||
extern void RTC_DisableIt( Rtc* pRtc, uint32_t dwSources )
|
||||
{
|
||||
assert((dwSources & (uint32_t)(~0x1F)) == 0);
|
||||
|
||||
pRtc->RTC_IDR = dwSources ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the current time in the RTC.
|
||||
*
|
||||
* \note In successive update operations, the user must wait at least one second
|
||||
* after resetting the UPDTIM/UPDCAL bit in the RTC_CR before setting these
|
||||
* bits again. Please look at the RTC section of the datasheet for detail.
|
||||
*
|
||||
* \param ucHour Current hour in 12 or 24 hour mode.
|
||||
* \param ucMinute Current minute.
|
||||
* \param ucSecond Current second.
|
||||
*
|
||||
* \return 0 sucess, 1 fail to set
|
||||
*/
|
||||
extern int RTC_SetTime( Rtc* pRtc, uint8_t ucHour, uint8_t ucMinute, uint8_t ucSecond )
|
||||
{
|
||||
uint32_t dwTime=0 ;
|
||||
uint8_t ucHour_bcd ;
|
||||
uint8_t ucMin_bcd ;
|
||||
uint8_t ucSec_bcd ;
|
||||
|
||||
/* if 12-hour mode, set AMPM bit */
|
||||
if ( (pRtc->RTC_MR & RTC_MR_HRMOD) == RTC_MR_HRMOD )
|
||||
{
|
||||
if ( ucHour > 12 )
|
||||
{
|
||||
ucHour -= 12 ;
|
||||
dwTime |= RTC_TIMR_AMPM ;
|
||||
}
|
||||
}
|
||||
ucHour_bcd = (ucHour%10) | ((ucHour/10)<<4) ;
|
||||
ucMin_bcd = (ucMinute%10) | ((ucMinute/10)<<4) ;
|
||||
ucSec_bcd = (ucSecond%10) | ((ucSecond/10)<<4) ;
|
||||
|
||||
/* value overflow */
|
||||
if ( (ucHour_bcd & (uint8_t)(~RTC_HOUR_BIT_LEN_MASK)) |
|
||||
(ucMin_bcd & (uint8_t)(~RTC_MIN_BIT_LEN_MASK)) |
|
||||
(ucSec_bcd & (uint8_t)(~RTC_SEC_BIT_LEN_MASK)))
|
||||
{
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
dwTime = ucSec_bcd | (ucMin_bcd << 8) | (ucHour_bcd<<16) ;
|
||||
|
||||
pRtc->RTC_CR |= RTC_CR_UPDTIM ;
|
||||
while ((pRtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD) ;
|
||||
pRtc->RTC_SCCR = RTC_SCCR_ACKCLR ;
|
||||
pRtc->RTC_TIMR = dwTime ;
|
||||
pRtc->RTC_CR &= (uint32_t)(~RTC_CR_UPDTIM) ;
|
||||
pRtc->RTC_SCCR |= RTC_SCCR_SECCLR ;
|
||||
|
||||
return (int)(pRtc->RTC_VER & RTC_VER_NVTIM) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Retrieves the current time as stored in the RTC in several variables.
|
||||
*
|
||||
* \param pucHour If not null, current hour is stored in this variable.
|
||||
* \param pucMinute If not null, current minute is stored in this variable.
|
||||
* \param pucSecond If not null, current second is stored in this variable.
|
||||
*/
|
||||
extern void RTC_GetTime( Rtc* pRtc, uint8_t *pucHour, uint8_t *pucMinute, uint8_t *pucSecond )
|
||||
{
|
||||
uint32_t dwTime ;
|
||||
|
||||
/* Get current RTC time */
|
||||
dwTime = pRtc->RTC_TIMR ;
|
||||
while ( dwTime != pRtc->RTC_TIMR )
|
||||
{
|
||||
dwTime = pRtc->RTC_TIMR ;
|
||||
}
|
||||
|
||||
/* Hour */
|
||||
if ( pucHour )
|
||||
{
|
||||
*pucHour = ((dwTime & 0x00300000) >> 20) * 10
|
||||
+ ((dwTime & 0x000F0000) >> 16);
|
||||
|
||||
if ( (dwTime & RTC_TIMR_AMPM) == RTC_TIMR_AMPM )
|
||||
{
|
||||
*pucHour += 12 ;
|
||||
}
|
||||
}
|
||||
|
||||
/* Minute */
|
||||
if ( pucMinute )
|
||||
{
|
||||
*pucMinute = ((dwTime & 0x00007000) >> 12) * 10
|
||||
+ ((dwTime & 0x00000F00) >> 8);
|
||||
}
|
||||
|
||||
/* Second */
|
||||
if ( pucSecond )
|
||||
{
|
||||
*pucSecond = ((dwTime & 0x00000070) >> 4) * 10
|
||||
+ (dwTime & 0x0000000F);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets a time alarm on the RTC.
|
||||
* The match is performed only on the provided variables;
|
||||
* Setting all pointers to 0 disables the time alarm.
|
||||
*
|
||||
* \note In AM/PM mode, the hour value must have bit #7 set for PM, cleared for
|
||||
* AM (as expected in the time registers).
|
||||
*
|
||||
* \param pucHour If not null, the time alarm will hour-match this value.
|
||||
* \param pucMinute If not null, the time alarm will minute-match this value.
|
||||
* \param pucSecond If not null, the time alarm will second-match this value.
|
||||
*
|
||||
* \return 0 success, 1 fail to set
|
||||
*/
|
||||
extern int RTC_SetTimeAlarm( Rtc* pRtc, uint8_t *pucHour, uint8_t *pucMinute, uint8_t *pucSecond )
|
||||
{
|
||||
uint32_t dwAlarm=0 ;
|
||||
|
||||
/* Hour */
|
||||
if ( pucHour )
|
||||
{
|
||||
dwAlarm |= RTC_TIMALR_HOUREN | ((*pucHour / 10) << 20) | ((*pucHour % 10) << 16);
|
||||
}
|
||||
|
||||
/* Minute */
|
||||
if ( pucMinute )
|
||||
{
|
||||
dwAlarm |= RTC_TIMALR_MINEN | ((*pucMinute / 10) << 12) | ((*pucMinute % 10) << 8);
|
||||
}
|
||||
|
||||
/* Second */
|
||||
if ( pucSecond )
|
||||
{
|
||||
dwAlarm |= RTC_TIMALR_SECEN | ((*pucSecond / 10) << 4) | (*pucSecond % 10);
|
||||
}
|
||||
|
||||
pRtc->RTC_TIMALR = dwAlarm ;
|
||||
|
||||
return (int)(pRtc->RTC_VER & RTC_VER_NVTIMALR) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Retrieves the current year, month and day from the RTC.
|
||||
* Month, day and week values are numbered starting at 1.
|
||||
*
|
||||
* \param pYwear Current year (optional).
|
||||
* \param pucMonth Current month (optional).
|
||||
* \param pucDay Current day (optional).
|
||||
* \param pucWeek Current day in current week (optional).
|
||||
*/
|
||||
extern void RTC_GetDate( Rtc* pRtc, uint16_t *pwYear, uint8_t *pucMonth, uint8_t *pucDay, uint8_t *pucWeek )
|
||||
{
|
||||
uint32_t dwDate ;
|
||||
|
||||
/* Get current date (multiple reads are necessary to insure a stable value) */
|
||||
do
|
||||
{
|
||||
dwDate = pRtc->RTC_CALR ;
|
||||
}
|
||||
while ( dwDate != pRtc->RTC_CALR ) ;
|
||||
|
||||
/* Retrieve year */
|
||||
if ( pwYear )
|
||||
{
|
||||
*pwYear = (((dwDate >> 4) & 0x7) * 1000)
|
||||
+ ((dwDate & 0xF) * 100)
|
||||
+ (((dwDate >> 12) & 0xF) * 10)
|
||||
+ ((dwDate >> 8) & 0xF);
|
||||
}
|
||||
|
||||
/* Retrieve month */
|
||||
if ( pucMonth )
|
||||
{
|
||||
*pucMonth = (((dwDate >> 20) & 1) * 10) + ((dwDate >> 16) & 0xF);
|
||||
}
|
||||
|
||||
/* Retrieve day */
|
||||
if ( pucDay )
|
||||
{
|
||||
*pucDay = (((dwDate >> 28) & 0x3) * 10) + ((dwDate >> 24) & 0xF);
|
||||
}
|
||||
|
||||
/* Retrieve week */
|
||||
if ( pucWeek )
|
||||
{
|
||||
*pucWeek = ((dwDate >> 21) & 0x7);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the current year, month and day in the RTC.
|
||||
* Month, day and week values must be numbered starting from 1.
|
||||
*
|
||||
* \note In successive update operations, the user must wait at least one second
|
||||
* after resetting the UPDTIM/UPDCAL bit in the RTC_CR before setting these
|
||||
* bits again. Please look at the RTC section of the datasheet for detail.
|
||||
*
|
||||
* \param wYear Current year.
|
||||
* \param ucMonth Current month.
|
||||
* \param ucDay Current day.
|
||||
* \param ucWeek Day number in current week.
|
||||
*
|
||||
* \return 0 success, 1 fail to set
|
||||
*/
|
||||
extern int RTC_SetDate( Rtc* pRtc, uint16_t wYear, uint8_t ucMonth, uint8_t ucDay, uint8_t ucWeek )
|
||||
{
|
||||
uint32_t wDate ;
|
||||
uint8_t ucCent_bcd ;
|
||||
uint8_t ucYear_bcd ;
|
||||
uint8_t ucMonth_bcd ;
|
||||
uint8_t ucDay_bcd ;
|
||||
uint8_t ucWeek_bcd ;
|
||||
|
||||
ucCent_bcd = ((wYear/100)%10) | ((wYear/1000)<<4);
|
||||
ucYear_bcd = (wYear%10) | (((wYear/10)%10)<<4);
|
||||
ucMonth_bcd = ((ucMonth%10) | (ucMonth/10)<<4);
|
||||
ucDay_bcd = ((ucDay%10) | (ucDay/10)<<4);
|
||||
ucWeek_bcd = ((ucWeek%10) | (ucWeek/10)<<4);
|
||||
|
||||
/* value over flow */
|
||||
if ( (ucCent_bcd & (uint8_t)(~RTC_CENT_BIT_LEN_MASK)) |
|
||||
(ucYear_bcd & (uint8_t)(~RTC_YEAR_BIT_LEN_MASK)) |
|
||||
(ucMonth_bcd & (uint8_t)(~RTC_MONTH_BIT_LEN_MASK)) |
|
||||
(ucWeek_bcd & (uint8_t)(~RTC_WEEK_BIT_LEN_MASK)) |
|
||||
(ucDay_bcd & (uint8_t)(~RTC_DATE_BIT_LEN_MASK))
|
||||
)
|
||||
{
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
|
||||
/* Convert values to date register value */
|
||||
wDate = ucCent_bcd |
|
||||
(ucYear_bcd << 8) |
|
||||
(ucMonth_bcd << 16) |
|
||||
(ucWeek_bcd << 21) |
|
||||
(ucDay_bcd << 24);
|
||||
|
||||
/* Update calendar register */
|
||||
pRtc->RTC_CR |= RTC_CR_UPDCAL ;
|
||||
while ((pRtc->RTC_SR & RTC_SR_ACKUPD) != RTC_SR_ACKUPD) ;
|
||||
|
||||
pRtc->RTC_SCCR = RTC_SCCR_ACKCLR;
|
||||
pRtc->RTC_CALR = wDate ;
|
||||
pRtc->RTC_CR &= (uint32_t)(~RTC_CR_UPDCAL) ;
|
||||
pRtc->RTC_SCCR |= RTC_SCCR_SECCLR; /* clear SECENV in SCCR */
|
||||
|
||||
return (int)(pRtc->RTC_VER & RTC_VER_NVCAL) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets a date alarm in the RTC.
|
||||
* The alarm will match only the provided values;
|
||||
* Passing a null-pointer disables the corresponding field match.
|
||||
*
|
||||
* \param pucMonth If not null, the RTC alarm will month-match this value.
|
||||
* \param pucDay If not null, the RTC alarm will day-match this value.
|
||||
*
|
||||
* \return 0 success, 1 fail to set
|
||||
*/
|
||||
extern int RTC_SetDateAlarm( Rtc* pRtc, uint8_t *pucMonth, uint8_t *pucDay )
|
||||
{
|
||||
uint32_t dwAlarm ;
|
||||
|
||||
dwAlarm = ((pucMonth) || (pucDay)) ? (0) : (0x01010000);
|
||||
|
||||
/* Compute alarm field value */
|
||||
if ( pucMonth )
|
||||
{
|
||||
dwAlarm |= RTC_CALALR_MTHEN | ((*pucMonth / 10) << 20) | ((*pucMonth % 10) << 16);
|
||||
}
|
||||
|
||||
if ( pucDay )
|
||||
{
|
||||
dwAlarm |= RTC_CALALR_DATEEN | ((*pucDay / 10) << 28) | ((*pucDay % 10) << 24);
|
||||
}
|
||||
|
||||
/* Set alarm */
|
||||
pRtc->RTC_CALALR = dwAlarm ;
|
||||
|
||||
return (int)(pRtc->RTC_VER & RTC_VER_NVCALALR) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Clear flag bits of status clear command register in the RTC.
|
||||
*
|
||||
* \param mask Bits mask of cleared events
|
||||
*/
|
||||
extern void RTC_ClearSCCR( Rtc* pRtc, uint32_t dwMask )
|
||||
{
|
||||
/* Clear all flag bits in status clear command register */
|
||||
dwMask &= RTC_SCCR_ACKCLR | RTC_SCCR_ALRCLR | RTC_SCCR_SECCLR | RTC_SCCR_TIMCLR | RTC_SCCR_CALCLR ;
|
||||
|
||||
pRtc->RTC_SCCR = dwMask ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get flag bits of status register in the RTC.
|
||||
*
|
||||
* \param mask Bits mask of Status Register
|
||||
*
|
||||
* \return Status register & mask
|
||||
*/
|
||||
extern uint32_t RTC_GetSR( Rtc* pRtc, uint32_t dwMask )
|
||||
{
|
||||
uint32_t dwEvent ;
|
||||
|
||||
dwEvent = pRtc->RTC_SR ;
|
||||
|
||||
return (dwEvent & dwMask) ;
|
||||
}
|
||||
|
||||
132
hardware/digistump/sam/system/libsam/source/rtt.c
Normal file
132
hardware/digistump/sam/system/libsam/source/rtt.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** \addtogroup rtt_module Working with RTT
|
||||
* The RTT driver provides the interface to configure and use the RTT
|
||||
* peripheral.
|
||||
*
|
||||
* The Real-time Timer is used to count elapsed seconds.\n
|
||||
* This timer is clocked by the 32kHz system clock divided by a programmable
|
||||
* 16-bit balue. To be accurate, it is better to use an
|
||||
* external 32kHz crystal instead of the internal 32kHz RC.\n
|
||||
*
|
||||
* To count elapsed seconds, the user could follow these few steps:
|
||||
* <ul>
|
||||
* <li>Programming PTPRES in RTT_MR to feeding the timer with a 1Hz signal.</li>
|
||||
* <li>Writing the bit RTTRST in RTT_MR to restart the timer with new settings.</li>
|
||||
* </ul>
|
||||
*
|
||||
* An alarm can be set to happen on second by setting alarm value in RTT_AR.
|
||||
* Alarm occurence can be detected by polling or interrupt.
|
||||
*
|
||||
* For more accurate information, please look at the RTT section of the
|
||||
* Datasheet.
|
||||
*
|
||||
* Related files :\n
|
||||
* \ref rtt.c\n
|
||||
* \ref rtt.h.\n
|
||||
*/
|
||||
/*@{*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of Real Time Timer (RTT) controller.
|
||||
*
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
#include "chip.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Exported functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Changes the prescaler value of the given RTT and restarts it.
|
||||
*
|
||||
* \note This function disables RTT interrupt sources.
|
||||
*
|
||||
* \param rtt Pointer to a Rtt instance.
|
||||
* \param prescaler Prescaler value for the RTT.
|
||||
*/
|
||||
void RTT_SetPrescaler(Rtt *rtt, uint16_t prescaler)
|
||||
{
|
||||
rtt->RTT_MR = (prescaler | RTT_MR_RTTRST);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the current value of the RTT timer value.
|
||||
*
|
||||
* \param rtt Pointer to a Rtt instance.
|
||||
*/
|
||||
uint32_t RTT_GetTime(Rtt *rtt)
|
||||
{
|
||||
return rtt->RTT_VR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables the specified RTT interrupt sources.
|
||||
*
|
||||
* \param rtt Pointer to a Rtt instance.
|
||||
* \param sources Bitmask of interrupts to enable.
|
||||
*/
|
||||
void RTT_EnableIT(Rtt *rtt, uint32_t sources)
|
||||
{
|
||||
assert( (sources & 0x0004FFFF) == 0 ) ;
|
||||
rtt->RTT_MR |= sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the status register value of the given RTT.
|
||||
*
|
||||
* \param rtt Pointer to an Rtt instance.
|
||||
*/
|
||||
uint32_t RTT_GetStatus(Rtt *rtt)
|
||||
{
|
||||
return rtt->RTT_SR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures the RTT to generate an alarm at the given time.
|
||||
*
|
||||
* \param pRtt Pointer to an Rtt instance.
|
||||
* \param time Alarm time.
|
||||
*/
|
||||
void RTT_SetAlarm(Rtt *pRtt, uint32_t time)
|
||||
{
|
||||
assert(time > 0);
|
||||
|
||||
pRtt->RTT_AR = time - 1;
|
||||
}
|
||||
352
hardware/digistump/sam/system/libsam/source/spi.c
Normal file
352
hardware/digistump/sam/system/libsam/source/spi.c
Normal file
@@ -0,0 +1,352 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** \addtogroup spi_module Working with SPI
|
||||
* The SPI driver provides the interface to configure and use the SPI
|
||||
* peripheral.
|
||||
*
|
||||
* The Serial Peripheral Interface (SPI) circuit is a synchronous serial
|
||||
* data link that provides communication with external devices in Master
|
||||
* or Slave Mode.
|
||||
*
|
||||
* To use the SPI, the user has to follow these few steps:
|
||||
* -# Enable the SPI pins required by the application (see pio.h).
|
||||
* -# Configure the SPI using the \ref SPI_Configure(). This enables the
|
||||
* peripheral clock. The mode register is loaded with the given value.
|
||||
* -# Configure all the necessary chip selects with \ref SPI_ConfigureNPCS().
|
||||
* -# Enable the SPI by calling \ref SPI_Enable().
|
||||
* -# Send/receive data using \ref SPI_Write() and \ref SPI_Read(). Note that \ref SPI_Read()
|
||||
* must be called after \ref SPI_Write() to retrieve the last value read.
|
||||
* -# Send/receive data using the PDC with the \ref SPI_WriteBuffer() and
|
||||
* \ref SPI_ReadBuffer() functions.
|
||||
* -# Disable the SPI by calling \ref SPI_Disable().
|
||||
*
|
||||
* For more accurate information, please look at the SPI section of the
|
||||
* Datasheet.
|
||||
*
|
||||
* Related files :\n
|
||||
* \ref spi.c\n
|
||||
* \ref spi.h.\n
|
||||
*/
|
||||
/*@{*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of Serial Peripheral Interface (SPI) controller.
|
||||
*
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Exported functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Enables a SPI peripheral.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*/
|
||||
extern void SPI_Enable( Spi* spi )
|
||||
{
|
||||
spi->SPI_CR = SPI_CR_SPIEN ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disables a SPI peripheral.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*/
|
||||
extern void SPI_Disable( Spi* spi )
|
||||
{
|
||||
spi->SPI_CR = SPI_CR_SPIDIS ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables one or more interrupt sources of a SPI peripheral.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param sources Bitwise OR of selected interrupt sources.
|
||||
*/
|
||||
extern void SPI_EnableIt( Spi* spi, uint32_t dwSources )
|
||||
{
|
||||
spi->SPI_IER = dwSources ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disables one or more interrupt sources of a SPI peripheral.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param sources Bitwise OR of selected interrupt sources.
|
||||
*/
|
||||
extern void SPI_DisableIt( Spi* spi, uint32_t dwSources )
|
||||
{
|
||||
spi->SPI_IDR = dwSources ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures a SPI peripheral as specified. The configuration can be computed
|
||||
* using several macros (see \ref spi_configuration_macros).
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param id Peripheral ID of the SPI.
|
||||
* \param configuration Value of the SPI configuration register.
|
||||
*/
|
||||
extern void SPI_Configure( Spi* spi, uint32_t dwId, uint32_t dwConfiguration )
|
||||
{
|
||||
pmc_enable_periph_clk( dwId ) ;
|
||||
spi->SPI_CR = SPI_CR_SPIDIS ;
|
||||
|
||||
/* Execute a software reset of the SPI twice */
|
||||
spi->SPI_CR = SPI_CR_SWRST ;
|
||||
spi->SPI_CR = SPI_CR_SWRST ;
|
||||
spi->SPI_MR = dwConfiguration ;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Configures a chip select of a SPI peripheral. The chip select configuration
|
||||
* is computed using several macros (see \ref spi_configuration_macros).
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param npcs Chip select to configure (0, 1, 2 or 3).
|
||||
* \param configuration Desired chip select configuration.
|
||||
*/
|
||||
void SPI_ConfigureNPCS( Spi* spi, uint32_t dwNpcs, uint32_t dwConfiguration )
|
||||
{
|
||||
spi->SPI_CSR[dwNpcs] = dwConfiguration ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the current status register of the given SPI peripheral.
|
||||
* \note This resets the internal value of the status register, so further
|
||||
* read may yield different values.
|
||||
* \param spi Pointer to a Spi instance.
|
||||
* \return SPI status register.
|
||||
*/
|
||||
extern uint32_t SPI_GetStatus( Spi* spi )
|
||||
{
|
||||
return spi->SPI_SR ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads and returns the last word of data received by a SPI peripheral. This
|
||||
* method must be called after a successful SPI_Write call.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*
|
||||
* \return readed data.
|
||||
*/
|
||||
extern uint32_t SPI_Read( Spi* spi )
|
||||
{
|
||||
while ( (spi->SPI_SR & SPI_SR_RDRF) == 0 ) ;
|
||||
|
||||
return spi->SPI_RDR & 0xFFFF ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends data through a SPI peripheral. If the SPI is configured to use a fixed
|
||||
* peripheral select, the npcs value is meaningless. Otherwise, it identifies
|
||||
* the component which shall be addressed.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param npcs Chip select of the component to address (0, 1, 2 or 3).
|
||||
* \param data Word of data to send.
|
||||
*/
|
||||
extern void SPI_Write( Spi* spi, uint32_t dwNpcs, uint16_t wData )
|
||||
{
|
||||
/* Send data */
|
||||
while ( (spi->SPI_SR & SPI_SR_TXEMPTY) == 0 ) ;
|
||||
spi->SPI_TDR = wData | SPI_PCS( dwNpcs ) ;
|
||||
while ( (spi->SPI_SR & SPI_SR_TDRE) == 0 ) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if SPI transfer finish.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*
|
||||
* \return Returns 1 if there is no pending write operation on the SPI; otherwise
|
||||
* returns 0.
|
||||
*/
|
||||
extern uint32_t SPI_IsFinished( Spi* spi )
|
||||
{
|
||||
return ((spi->SPI_SR & SPI_SR_TXEMPTY) != 0) ;
|
||||
}
|
||||
|
||||
#if (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_)
|
||||
/**
|
||||
* \brief Enable Spi PDC transmit
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*/
|
||||
extern void SPI_PdcEnableTx( Spi* spi )
|
||||
{
|
||||
spi->SPI_PTCR = SPI_PTCR_TXTEN ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable Spi PDC transmit
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*/
|
||||
extern void SPI_PdcDisableTx( Spi* spi )
|
||||
{
|
||||
spi->SPI_PTCR = SPI_PTCR_TXTDIS ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable Spi PDC receive
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*/
|
||||
extern void SPI_PdcEnableRx( Spi* spi )
|
||||
{
|
||||
spi->SPI_PTCR = SPI_PTCR_RXTEN ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable Spi PDC receive
|
||||
* \param spi Pointer to an Spi instance.
|
||||
*/
|
||||
extern void SPI_PdcDisableRx( Spi* spi )
|
||||
{
|
||||
spi->SPI_PTCR = SPI_PTCR_RXTDIS ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set PDC transmit and next transmit buffer address and size.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param txBuf PDC transmit buffer address.
|
||||
* \param txCount Length in bytes of the transmit buffer.
|
||||
* \param txNextBuf PDC next transmit buffer address.
|
||||
* \param txNextCount Length in bytes of the next transmit buffer.
|
||||
*/
|
||||
extern void SPI_PdcSetTx( Spi* spi, void* pvTxBuf, uint32_t dwTxCount, void* pvTxNextBuf, uint32_t dwTxNextCount )
|
||||
{
|
||||
spi->SPI_TPR = (uint32_t)pvTxBuf ;
|
||||
spi->SPI_TCR = dwTxCount ;
|
||||
spi->SPI_TNPR = (uint32_t)pvTxNextBuf ;
|
||||
spi->SPI_TNCR = dwTxNextCount ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set PDC receive and next receive buffer address and size.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param rxBuf PDC receive buffer address.
|
||||
* \param rxCount Length in bytes of the receive buffer.
|
||||
* \param rxNextBuf PDC next receive buffer address.
|
||||
* \param rxNextCount Length in bytes of the next receive buffer.
|
||||
*/
|
||||
extern void SPI_PdcSetRx( Spi* spi, void* pvRxBuf, uint32_t dwRxCount, void* pvRxNextBuf, uint32_t dwRxNextCount )
|
||||
{
|
||||
spi->SPI_RPR = (uint32_t)pvRxBuf ;
|
||||
spi->SPI_RCR = dwRxCount ;
|
||||
spi->SPI_RNPR = (uint32_t)pvRxNextBuf ;
|
||||
spi->SPI_RNCR = dwRxNextCount ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends the contents of buffer through a SPI peripheral, using the PDC to
|
||||
* take care of the transfer.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param buffer Data buffer to send.
|
||||
* \param length Length of the data buffer.
|
||||
*/
|
||||
extern uint32_t SPI_WriteBuffer( Spi* spi, void* pvBuffer, uint32_t dwLength )
|
||||
{
|
||||
/* Check if first bank is free */
|
||||
if ( spi->SPI_TCR == 0 )
|
||||
{
|
||||
spi->SPI_TPR = (uint32_t)pvBuffer ;
|
||||
spi->SPI_TCR = dwLength ;
|
||||
spi->SPI_PTCR = PERIPH_PTCR_TXTEN ;
|
||||
|
||||
return 1 ;
|
||||
}
|
||||
/* Check if second bank is free */
|
||||
else
|
||||
{
|
||||
if ( spi->SPI_TNCR == 0 )
|
||||
{
|
||||
spi->SPI_TNPR = (uint32_t)pvBuffer ;
|
||||
spi->SPI_TNCR = dwLength ;
|
||||
|
||||
return 1 ;
|
||||
}
|
||||
}
|
||||
|
||||
/* No free banks */
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads data from a SPI peripheral until the provided buffer is filled. This
|
||||
* method does NOT need to be called after SPI_Write or SPI_WriteBuffer.
|
||||
*
|
||||
* \param spi Pointer to an Spi instance.
|
||||
* \param buffer Data buffer to store incoming bytes.
|
||||
* \param length Length in bytes of the data buffer.
|
||||
*/
|
||||
extern uint32_t SPI_ReadBuffer( Spi* spi, void *pvBuffer, uint32_t dwLength )
|
||||
{
|
||||
/* Check if the first bank is free */
|
||||
if ( spi->SPI_RCR == 0 )
|
||||
{
|
||||
spi->SPI_RPR = (uint32_t)pvBuffer ;
|
||||
spi->SPI_RCR = dwLength ;
|
||||
spi->SPI_PTCR = PERIPH_PTCR_RXTEN ;
|
||||
|
||||
return 1 ;
|
||||
}
|
||||
/* Check if second bank is free */
|
||||
else
|
||||
{
|
||||
if ( spi->SPI_RNCR == 0 )
|
||||
{
|
||||
spi->SPI_RNPR = (uint32_t)pvBuffer ;
|
||||
spi->SPI_RNCR = dwLength ;
|
||||
return 1 ;
|
||||
}
|
||||
}
|
||||
|
||||
/* No free bank */
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
#endif /* (defined _SAM3S_) || (defined _SAM3S8_) || (defined _SAM3N_) */
|
||||
|
||||
835
hardware/digistump/sam/system/libsam/source/ssc.c
Normal file
835
hardware/digistump/sam/system/libsam/source/ssc.c
Normal file
@@ -0,0 +1,835 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief Synchronous Serial Controller (SSC) driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* 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
|
||||
* EXPRESSLY AND SPECIFICALLY 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.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "ssc.h"
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* \defgroup sam_drivers_ssc_group Synchronous Serial Controller (SSC)
|
||||
*
|
||||
* The Synchronous Serial Controller (SSC) provides a synchronous communication
|
||||
* link with external devices. It supports many serial synchronous communication
|
||||
* protocols generally used in audio and telecom applications such as I2S,
|
||||
* Short Frame Sync, Long Frame Sync, etc.
|
||||
* This is a driver for configuration and use of the SSC peripheral.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define SSC_WPKEY SSC_WPMR_WPKEY(0x535343)
|
||||
|
||||
/**
|
||||
* \brief Set up clock.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_bitrate Desired bit clock.
|
||||
* \param ul_mck MCK clock.
|
||||
*
|
||||
* \retval SSC_RC_YES Success.
|
||||
* \retval SSC_RC_NO Invalid input value.
|
||||
*/
|
||||
uint32_t ssc_set_clock_divider(Ssc *p_ssc, uint32_t ul_bitrate,
|
||||
uint32_t ul_mck)
|
||||
{
|
||||
if (ul_mck && ul_bitrate) {
|
||||
p_ssc->SSC_CMR = SSC_CMR_DIV(((ul_mck + ul_bitrate) / ul_bitrate) >> 1);
|
||||
return SSC_RC_YES;
|
||||
} else {
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Setup for I2S transmitter.
|
||||
*
|
||||
* \note If working in master mode, the divided clock needs to be configured before
|
||||
* calling this function according to the sample rate and ul_datlen field.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_mode Working mode, SSC_I2S_MASTER_OUT or SSC_I2S_SLAVE_OUT.
|
||||
* \param ul_cks Source clock selection while working in SSC_I2S_SLAVE_OUT mode.
|
||||
* \param ul_ch_mode Channel mode, stereo or mono.
|
||||
* \param ul_datlen Data length for one channel.
|
||||
*/
|
||||
void ssc_i2s_set_transmitter(Ssc *p_ssc, uint32_t ul_mode,
|
||||
uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen)
|
||||
{
|
||||
clock_opt_t tx_clk_option;
|
||||
data_frame_opt_t tx_data_frame_option;
|
||||
|
||||
/* Initialize the local variable. */
|
||||
memset((uint8_t *)&tx_clk_option, 0, sizeof(clock_opt_t));
|
||||
memset((uint8_t *)&tx_data_frame_option, 0, sizeof(data_frame_opt_t));
|
||||
|
||||
/* Data start: MonoLeft-Falling, MonoRight-Rising, Stero-Edge. */
|
||||
switch (ul_ch_mode) {
|
||||
case SSC_AUDIO_MONO_RIGHT:
|
||||
tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_RISING;
|
||||
break;
|
||||
case SSC_AUDIO_MONO_LEFT:
|
||||
tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_FALLING;
|
||||
break;
|
||||
case SSC_AUDIO_STERO:
|
||||
tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_EDGE;
|
||||
break;
|
||||
}
|
||||
if (ul_mode & SSC_I2S_MASTER_OUT) {
|
||||
/* Stereo has 2 data words, and mono has only one data word. */
|
||||
if (SSC_AUDIO_STERO == ul_ch_mode) {
|
||||
tx_data_frame_option.ul_datnb = 1;
|
||||
} else {
|
||||
tx_data_frame_option.ul_datnb = 0;
|
||||
}
|
||||
|
||||
/* Configure TCMR Settings. */
|
||||
tx_clk_option.ul_cks = SSC_TCMR_CKS_MCK;
|
||||
tx_clk_option.ul_cko = SSC_TCMR_CKO_CONTINUOUS;
|
||||
tx_clk_option.ul_cki = 0;
|
||||
tx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE;
|
||||
/* The delay is defined by I2S protocol. */
|
||||
tx_clk_option.ul_sttdly = 1;
|
||||
tx_clk_option.ul_period = ul_datlen - 1;
|
||||
|
||||
/* Configure TFMR Settings. */
|
||||
tx_data_frame_option.ul_datlen = ul_datlen - 1;
|
||||
tx_data_frame_option.ul_msbf = SSC_TFMR_MSBF;
|
||||
tx_data_frame_option.ul_fslen = ul_datlen - 1;
|
||||
tx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NEGATIVE;
|
||||
tx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE;
|
||||
} else if (ul_mode & SSC_I2S_SLAVE_OUT) {
|
||||
/* Configure TCMR Settings. */
|
||||
tx_clk_option.ul_cks = ul_cks;
|
||||
tx_clk_option.ul_cko = SSC_TCMR_CKO_NONE;
|
||||
tx_clk_option.ul_cki = 0;
|
||||
tx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE;
|
||||
tx_clk_option.ul_sttdly = 1;
|
||||
tx_clk_option.ul_period = 0;
|
||||
|
||||
/* Configure TFMR Settings. */
|
||||
tx_data_frame_option.ul_datlen = ul_datlen - 1;
|
||||
tx_data_frame_option.ul_msbf = SSC_TFMR_MSBF;
|
||||
tx_data_frame_option.ul_fslen = 0;
|
||||
tx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NONE;
|
||||
tx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE;
|
||||
}
|
||||
/* Configure the default level on TD pin. */
|
||||
ssc_set_td_default_level(p_ssc, 0);
|
||||
|
||||
/* Configure the SSC transmitter. */
|
||||
ssc_set_transmitter(p_ssc, &tx_clk_option, &tx_data_frame_option);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Setup for I2S receiver.
|
||||
*
|
||||
* \note If working in master mode, the divided clock needs to be configured before
|
||||
* calling this function according to the sample rate and ul_datlen field.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_mode Working mode, SSC_I2S_MASTER_IN or SSC_I2S_SLAVE_IN.
|
||||
* \param ul_cks Source clock selection while working in SSC_I2S_SLAVE_IN mode.
|
||||
* \param ul_ch_mode Channel mode, stereo or mono.
|
||||
* \param ul_datlen Data length for one channel.
|
||||
*/
|
||||
void ssc_i2s_set_receiver(Ssc *p_ssc, uint32_t ul_mode,
|
||||
uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen)
|
||||
{
|
||||
clock_opt_t rx_clk_option;
|
||||
data_frame_opt_t rx_data_frame_option;
|
||||
|
||||
/* Initialize the local variable. */
|
||||
memset((uint8_t *)&rx_clk_option, 0, sizeof(clock_opt_t));
|
||||
memset((uint8_t *)&rx_data_frame_option, 0, sizeof(data_frame_opt_t));
|
||||
|
||||
/* Data start: MonoLeft-Falling, MonoRight-Rising, Stero-Edge. */
|
||||
switch (ul_ch_mode) {
|
||||
case SSC_AUDIO_MONO_RIGHT:
|
||||
rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_RISING;
|
||||
break;
|
||||
case SSC_AUDIO_MONO_LEFT:
|
||||
rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_FALLING;
|
||||
break;
|
||||
case SSC_AUDIO_STERO:
|
||||
rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_EDGE;
|
||||
break;
|
||||
}
|
||||
if (ul_mode & SSC_I2S_MASTER_IN) {
|
||||
/* Stereo has 2 data words, and mono has only one data word. */
|
||||
if (SSC_AUDIO_STERO == ul_ch_mode) {
|
||||
rx_data_frame_option.ul_datnb = 1;
|
||||
} else {
|
||||
rx_data_frame_option.ul_datnb = 0;
|
||||
}
|
||||
|
||||
/* Configure RCMR Settings. */
|
||||
rx_clk_option.ul_cks = SSC_TCMR_CKS_MCK;
|
||||
rx_clk_option.ul_cko = SSC_TCMR_CKO_CONTINUOUS;
|
||||
rx_clk_option.ul_cki = 0;
|
||||
rx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE;
|
||||
rx_clk_option.ul_sttdly = 1;
|
||||
rx_clk_option.ul_period = ul_datlen - 1;
|
||||
|
||||
/* Configure RFMR Settings. */
|
||||
rx_data_frame_option.ul_datlen = ul_datlen - 1;
|
||||
rx_data_frame_option.ul_msbf = SSC_TFMR_MSBF;
|
||||
rx_data_frame_option.ul_fslen = ul_datlen - 1;
|
||||
rx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NEGATIVE;
|
||||
rx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE;
|
||||
} else if (ul_mode & SSC_I2S_SLAVE_IN) {
|
||||
/* Configure TCMR Settings. */
|
||||
rx_clk_option.ul_cks = ul_cks;
|
||||
rx_clk_option.ul_cko = SSC_TCMR_CKO_NONE;
|
||||
rx_clk_option.ul_cki = 0;
|
||||
rx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE;
|
||||
rx_clk_option.ul_sttdly = 1;
|
||||
rx_clk_option.ul_period = 0;
|
||||
|
||||
/* Configure TFMR Settings. */
|
||||
rx_data_frame_option.ul_datlen = ul_datlen - 1;
|
||||
rx_data_frame_option.ul_msbf = SSC_TFMR_MSBF;
|
||||
rx_data_frame_option.ul_fslen = 0;
|
||||
rx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NONE;
|
||||
rx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE;
|
||||
}
|
||||
|
||||
/* Configure the SSC receiver. */
|
||||
ssc_set_receiver(p_ssc, &rx_clk_option, &rx_data_frame_option);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reset SSC module.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_reset(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_CR = SSC_CR_SWRST;
|
||||
p_ssc->SSC_CMR = 0;
|
||||
p_ssc->SSC_RCMR = 0;
|
||||
p_ssc->SSC_RFMR = 0;
|
||||
p_ssc->SSC_TCMR = 0;
|
||||
p_ssc->SSC_TFMR = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable SSC receiver.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_enable_rx(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_CR = SSC_CR_RXEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable SSC receiver.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_disable_rx(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_CR = SSC_CR_RXDIS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable SSC Transmitter.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_enable_tx(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_CR = SSC_CR_TXEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable SSC Transmitter.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_disable_tx(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_CR = SSC_CR_TXDIS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure SSC to work in normal mode.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_set_normal_mode(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_RFMR &= ~SSC_RFMR_LOOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure SSC to work in loop mode.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_set_loop_mode(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_RFMR |= SSC_RFMR_LOOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure SSC receive stop selection.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_sel Compare 0 used or Compare both 0 & 1 used.
|
||||
*/
|
||||
void ssc_set_rx_stop_selection(Ssc *p_ssc, uint32_t ul_sel)
|
||||
{
|
||||
if (SSC_RX_STOP_COMPARE_0_1 == ul_sel) {
|
||||
p_ssc->SSC_RCMR |= SSC_RCMR_STOP;
|
||||
} else if (SSC_RX_STOP_COMPARE_0 == ul_sel) {
|
||||
p_ssc->SSC_RCMR &= ~SSC_RCMR_STOP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure SSC default level driven on the TD pin while
|
||||
* out of transmission.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_level The default driven level of TD pin.
|
||||
*/
|
||||
void ssc_set_td_default_level(Ssc *p_ssc, uint32_t ul_level)
|
||||
{
|
||||
if (ul_level) {
|
||||
p_ssc->SSC_TFMR |= SSC_TFMR_DATDEF;
|
||||
} else {
|
||||
p_ssc->SSC_TFMR &= ~SSC_TFMR_DATDEF;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief The TD line is driven with the SSC_TSHR register value
|
||||
* during the transmission of the Transmit Frame Sync Signal.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_enable_tx_frame_sync_data(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_TFMR |= SSC_TFMR_FSDEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief The TD line is driven with the default value during the Transmit
|
||||
* Frame Sync signal.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*/
|
||||
void ssc_disable_tx_frame_sync_data(Ssc *p_ssc)
|
||||
{
|
||||
p_ssc->SSC_TFMR &= ~SSC_TFMR_FSDEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure SSC receiver clock mode and date frame configuration.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param p_rx_clk_opt Pointer to the receiver clock configuration structure.
|
||||
* \param p_rx_data_frame Pointer to the receiver data frame configuration structure.
|
||||
*/
|
||||
void ssc_set_receiver(Ssc *p_ssc, clock_opt_t *p_rx_clk_opt,
|
||||
data_frame_opt_t *p_rx_data_frame)
|
||||
{
|
||||
if (p_rx_clk_opt == NULL) {
|
||||
p_ssc->SSC_RCMR = 0;
|
||||
} else {
|
||||
p_ssc->SSC_RCMR |= p_rx_clk_opt->ul_cks |
|
||||
p_rx_clk_opt->ul_cko | p_rx_clk_opt->ul_cki |
|
||||
p_rx_clk_opt->ul_ckg |
|
||||
p_rx_clk_opt->ul_start_sel |
|
||||
SSC_RCMR_PERIOD(p_rx_clk_opt->ul_period) |
|
||||
SSC_RCMR_STTDLY(p_rx_clk_opt->ul_sttdly);
|
||||
}
|
||||
|
||||
if (p_rx_data_frame == NULL) {
|
||||
p_ssc->SSC_RFMR = 0;
|
||||
} else {
|
||||
p_ssc->SSC_RFMR |= SSC_RFMR_DATLEN(p_rx_data_frame->ul_datlen) |
|
||||
p_rx_data_frame->ul_msbf |
|
||||
SSC_RFMR_DATNB(p_rx_data_frame->ul_datnb) |
|
||||
SSC_RFMR_FSLEN(p_rx_data_frame->ul_fslen) |
|
||||
SSC_RFMR_FSLEN_EXT(p_rx_data_frame->ul_fslen_ext) |
|
||||
p_rx_data_frame->ul_fsos |
|
||||
p_rx_data_frame->ul_fsedge;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure SSC transmitter clock mode and date frame configuration.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param p_tx_clk_opt Pointer to the transmitter clock configuration structure.
|
||||
* \param p_tx_data_frame Pointer to the transmitter data frame configuration structure.
|
||||
*/
|
||||
void ssc_set_transmitter(Ssc *p_ssc, clock_opt_t *p_tx_clk_opt,
|
||||
data_frame_opt_t *p_tx_data_frame)
|
||||
{
|
||||
if (p_tx_clk_opt == NULL) {
|
||||
p_ssc->SSC_TCMR = 0;
|
||||
} else {
|
||||
p_ssc->SSC_TCMR |= p_tx_clk_opt->ul_cks |
|
||||
p_tx_clk_opt->ul_cko | p_tx_clk_opt->ul_cki |
|
||||
p_tx_clk_opt->ul_ckg |
|
||||
p_tx_clk_opt->ul_start_sel |
|
||||
SSC_RCMR_PERIOD(p_tx_clk_opt->ul_period) |
|
||||
SSC_RCMR_STTDLY(p_tx_clk_opt->ul_sttdly);
|
||||
}
|
||||
|
||||
if (p_tx_data_frame == NULL) {
|
||||
p_ssc->SSC_TFMR = 0;
|
||||
} else {
|
||||
p_ssc->SSC_TFMR |= SSC_RFMR_DATLEN(p_tx_data_frame->ul_datlen) |
|
||||
p_tx_data_frame->ul_msbf |
|
||||
SSC_RFMR_DATNB(p_tx_data_frame->ul_datnb) |
|
||||
SSC_RFMR_FSLEN(p_tx_data_frame->ul_fslen) |
|
||||
SSC_RFMR_FSLEN_EXT(p_tx_data_frame->ul_fslen_ext) |
|
||||
p_tx_data_frame->ul_fsos |
|
||||
p_tx_data_frame->ul_fsedge;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configure SSC Receive Compare Register.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_id Compare register ID.
|
||||
* \param ul_value Value to configure.
|
||||
*/
|
||||
void ssc_set_rx_compare(Ssc *p_ssc, uint32_t ul_id, uint32_t ul_value)
|
||||
{
|
||||
switch (ul_id) {
|
||||
case COMPARE_ID0:
|
||||
p_ssc->SSC_RC0R = ul_value;
|
||||
break;
|
||||
case COMPARE_ID1:
|
||||
p_ssc->SSC_RC1R = ul_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get SSC Receive Compare Register.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_id Compare register ID.
|
||||
*
|
||||
* \return Receive Compare Register value for the specified ul_id, otherwise SSC_RC_INVALID.
|
||||
*/
|
||||
uint32_t ssc_get_rx_compare(Ssc *p_ssc, uint32_t ul_id)
|
||||
{
|
||||
switch (ul_id) {
|
||||
case COMPARE_ID0:
|
||||
return p_ssc->SSC_RC0R;
|
||||
case COMPARE_ID1:
|
||||
return p_ssc->SSC_RC1R;
|
||||
default:
|
||||
return SSC_RC_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable SSC interrupts.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_sources Interrupts to be enabled.
|
||||
*/
|
||||
void ssc_enable_interrupt(Ssc *p_ssc, uint32_t ul_sources)
|
||||
{
|
||||
p_ssc->SSC_IER = ul_sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable SSC interrupts.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_sources Interrupts to be enabled.
|
||||
*/
|
||||
void ssc_disable_interrupt(Ssc *p_ssc, uint32_t ul_sources)
|
||||
{
|
||||
p_ssc->SSC_IDR = ul_sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read SSC interrupt mask.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \return The interrupt mask value.
|
||||
*/
|
||||
uint32_t ssc_get_interrupt_mask(Ssc *p_ssc)
|
||||
{
|
||||
return p_ssc->SSC_IMR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read SSC status.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \return The SSC status value.
|
||||
*/
|
||||
uint32_t ssc_get_status(Ssc *p_ssc)
|
||||
{
|
||||
return p_ssc->SSC_SR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if data has been loaded in SSC_THR and is waiting to be loaded
|
||||
* in the Transmit Shift Register (TSR).
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES There is no data in the SSC_THR.
|
||||
* \retval SSC_RC_NO There is one data in the SSC_THR.
|
||||
*/
|
||||
uint32_t ssc_is_tx_ready(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_TXRDY) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if the last data written in SSC_THR has been loaded in TSR
|
||||
* and the last data loaded in TSR has been transmitted.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES Both of the two registers are empty.
|
||||
* \retval SSC_RC_NO At least one of the two registers is not empty.
|
||||
*/
|
||||
uint32_t ssc_is_tx_empty(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_TXEMPTY) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if data has been received and loaded in SSC_RHR.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES There is one data in the SSC_RHR.
|
||||
* \retval SSC_RC_NO There is no data in the SSC_RHR.
|
||||
*/
|
||||
uint32_t ssc_is_rx_ready(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_RXRDY) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if transmitter is enabled.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES The transmitter is enabled.
|
||||
* \retval SSC_RC_NO The transmitter is disabled.
|
||||
*/
|
||||
uint32_t ssc_is_tx_enabled(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_TXEN) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if receiver is enabled.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES The receiver is enabled.
|
||||
* \retval SSC_RC_NO The receiver is disabled.
|
||||
*/
|
||||
uint32_t ssc_is_rx_enabled(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_RXEN) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
#if (SAM3S_SERIES) || (SAM4S_SERIES)
|
||||
/**
|
||||
* \brief Check if one receive buffer is filled.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES Receive Counter has reached zero.
|
||||
* \retval SSC_RC_NO Data is written on the Receive Counter Register or
|
||||
* Receive Next Counter Register.
|
||||
*/
|
||||
uint32_t ssc_is_rx_buf_end(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_ENDRX) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if the register SSC_TCR has reached 0.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES The register SSC_TCR has reached 0.
|
||||
* \retval SSC_RC_NO The register SSC_TCR hasn't reached 0.
|
||||
*/
|
||||
uint32_t ssc_is_tx_buf_end(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_ENDTX) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if both receive buffers are full.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES Both of the two receive buffers have reached 0.
|
||||
* \retval SSC_RC_NO One of the two receive buffers hasn't reached 0.
|
||||
*/
|
||||
uint32_t ssc_is_rx_buf_full(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_RXBUFF) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if both transmit buffers are empty.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \retval SSC_RC_YES Both of the two transmit buffers have reached 0.
|
||||
* \retval SSC_RC_NO One of the two transmit buffers hasn't reached 0.
|
||||
*/
|
||||
uint32_t ssc_is_tx_buf_empty(Ssc *p_ssc)
|
||||
{
|
||||
if (p_ssc->SSC_SR & SSC_SR_TXBUFE) {
|
||||
return SSC_RC_YES;
|
||||
}
|
||||
return SSC_RC_NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get SSC PDC registers base address.
|
||||
*
|
||||
* \param p_ssc Pointer to SSC registers set instance.
|
||||
*
|
||||
* \return SSC PDC registers base address for PDC driver to access.
|
||||
*/
|
||||
Pdc *ssc_get_pdc_base(Ssc *p_ssc)
|
||||
{
|
||||
return (Pdc *)&(p_ssc->SSC_RPR);
|
||||
}
|
||||
#endif // (SAM3S_SERIES) || (SAM4S_SERIES)
|
||||
|
||||
/**
|
||||
* \brief Write to SSC Transmit Holding Register.
|
||||
* Send data through SSC Data frame.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_frame Frame data to be transmitted.
|
||||
*
|
||||
* \retval SSC_RC_ERROR Time-out.
|
||||
* \retval SSC_RC_OK Success.
|
||||
*
|
||||
*/
|
||||
uint32_t ssc_write(Ssc *p_ssc, uint32_t ul_frame)
|
||||
{
|
||||
uint32_t ul_timeout = SSC_DEFAULT_TIMEOUT;
|
||||
|
||||
while (!(p_ssc->SSC_SR & SSC_SR_TXEMPTY)) {
|
||||
if (!ul_timeout--) {
|
||||
return SSC_RC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
p_ssc->SSC_THR = ul_frame;
|
||||
return SSC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read from SSC Receive Holding Register.
|
||||
* Read data that is received in SSC Data frame.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_data Pointer to the location where to store the received data.
|
||||
*
|
||||
* \retval SSC_RC_ERROR Time-out.
|
||||
* \retval SSC_RC_OK Success.
|
||||
*/
|
||||
uint32_t ssc_read(Ssc *p_ssc, uint32_t *ul_data)
|
||||
{
|
||||
uint32_t ul_timeout = SSC_DEFAULT_TIMEOUT;
|
||||
|
||||
while (!(p_ssc->SSC_SR & SSC_SR_RXRDY)) {
|
||||
if (!ul_timeout--) {
|
||||
return SSC_RC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
*ul_data = p_ssc->SSC_RHR;
|
||||
return SSC_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write to SSC Transmit Synchronization Holding Register.
|
||||
* Send data through SSC Synchronization frame. If there is sync data that needs to be
|
||||
* transmitted, call this function first to send out the sync data, and then call the
|
||||
* ssc_write() function to send out application data.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_frame Frame Synchronization data.
|
||||
*/
|
||||
void ssc_write_sync_data(Ssc *p_ssc, uint32_t ul_frame)
|
||||
{
|
||||
p_ssc->SSC_TSHR = ul_frame;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read from SSC Receive Synchronization Holding Register.
|
||||
* Read data that is received in SSC Synchronization frame. When the sync data is actually
|
||||
* used, after successfully reading the application data by calling ssc_read(), call
|
||||
* this function, and the return sync data is useful.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \return Current RSHR value.
|
||||
*/
|
||||
uint32_t ssc_read_sync_data(Ssc *p_ssc)
|
||||
{
|
||||
return p_ssc->SSC_RSHR;
|
||||
}
|
||||
|
||||
#if (SAM3XA_SERIES || SAM3U_SERIES)
|
||||
/**
|
||||
* \brief Get Transmit address for DMA operation.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \return Transmitting address for DMA access.
|
||||
*/
|
||||
void *ssc_get_tx_access(Ssc *p_ssc)
|
||||
{
|
||||
return (void *)&(p_ssc->SSC_THR);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get Receive address for DMA operation.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \return Transmitting address for DMA access.
|
||||
*/
|
||||
void *ssc_get_rx_access(Ssc *p_ssc)
|
||||
{
|
||||
return (void *)&(p_ssc->SSC_RHR);
|
||||
}
|
||||
#endif // (SAM3XA_SERIES || SAM3U_SERIES)
|
||||
|
||||
/**
|
||||
* \brief Enable or disable write protection of SSC registers.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
* \param ul_enable 1 to enable, 0 to disable.
|
||||
*/
|
||||
void ssc_set_writeprotect(Ssc *p_ssc, uint32_t ul_enable)
|
||||
{
|
||||
if (ul_enable) {
|
||||
p_ssc->SSC_WPMR = SSC_WPKEY | SSC_WPMR_WPEN;
|
||||
} else {
|
||||
p_ssc->SSC_WPMR = SSC_WPKEY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Indicate write protect status.
|
||||
*
|
||||
* \param p_ssc Pointer to an SSC instance.
|
||||
*
|
||||
* \return 0 if the peripheral is not protected. Write Protect Violation Status otherwise.
|
||||
*/
|
||||
uint32_t ssc_get_writeprotect_status(Ssc *p_ssc)
|
||||
{
|
||||
uint32_t ul_reg_val;
|
||||
|
||||
ul_reg_val = p_ssc->SSC_WPMR;
|
||||
if (ul_reg_val & SSC_WPMR_WPEN) {
|
||||
return (ul_reg_val & SSC_WPSR_WPVSRC_Msk) >> SSC_WPSR_WPVSRC_Pos;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
236
hardware/digistump/sam/system/libsam/source/tc.c
Normal file
236
hardware/digistump/sam/system/libsam/source/tc.c
Normal file
@@ -0,0 +1,236 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of Timer Counter (TC).
|
||||
*
|
||||
*/
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Headers
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Global functions
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Configures a Timer Counter Channel
|
||||
*
|
||||
* Configures a Timer Counter to operate in the given mode. Timer is stopped
|
||||
* after configuration and must be restarted with TC_Start(). All the
|
||||
* interrupts of the timer are also disabled.
|
||||
*
|
||||
* \param pTc Pointer to a Tc instance.
|
||||
* \param channel Channel number.
|
||||
* \param mode Operating mode (TC_CMR value).
|
||||
*/
|
||||
extern void TC_Configure( Tc *pTc, uint32_t dwChannel, uint32_t dwMode )
|
||||
{
|
||||
TcChannel* pTcCh ;
|
||||
|
||||
assert( dwChannel < (sizeof( pTc->TC_CHANNEL )/sizeof( pTc->TC_CHANNEL[0] )) ) ;
|
||||
pTcCh = pTc->TC_CHANNEL+dwChannel ;
|
||||
|
||||
/* Disable TC clock */
|
||||
pTcCh->TC_CCR = TC_CCR_CLKDIS ;
|
||||
|
||||
/* Disable interrupts */
|
||||
pTcCh->TC_IDR = 0xFFFFFFFF ;
|
||||
|
||||
/* Clear status register */
|
||||
pTcCh->TC_SR ;
|
||||
|
||||
/* Set mode */
|
||||
pTcCh->TC_CMR = dwMode ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reset and Start the TC Channel
|
||||
*
|
||||
* Enables the timer clock and performs a software reset to start the counting.
|
||||
*
|
||||
* \param pTc Pointer to a Tc instance.
|
||||
* \param dwChannel Channel number.
|
||||
*/
|
||||
extern void TC_Start( Tc *pTc, uint32_t dwChannel )
|
||||
{
|
||||
TcChannel* pTcCh ;
|
||||
|
||||
assert( dwChannel < (sizeof( pTc->TC_CHANNEL )/sizeof( pTc->TC_CHANNEL[0] )) ) ;
|
||||
|
||||
pTcCh = pTc->TC_CHANNEL+dwChannel ;
|
||||
pTcCh->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Stop TC Channel
|
||||
*
|
||||
* Disables the timer clock, stopping the counting.
|
||||
*
|
||||
* \param pTc Pointer to a Tc instance.
|
||||
* \param dwChannel Channel number.
|
||||
*/
|
||||
extern void TC_Stop(Tc *pTc, uint32_t dwChannel )
|
||||
{
|
||||
TcChannel* pTcCh ;
|
||||
|
||||
assert( dwChannel < (sizeof( pTc->TC_CHANNEL )/sizeof( pTc->TC_CHANNEL[0] )) ) ;
|
||||
|
||||
pTcCh = pTc->TC_CHANNEL+dwChannel ;
|
||||
pTcCh->TC_CCR = TC_CCR_CLKDIS ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Find best MCK divisor
|
||||
*
|
||||
* Finds the best MCK divisor given the timer frequency and MCK. The result
|
||||
* is guaranteed to satisfy the following equation:
|
||||
* \code
|
||||
* (MCK / (DIV * 65536)) <= freq <= (MCK / DIV)
|
||||
* \endcode
|
||||
* with DIV being the highest possible value.
|
||||
*
|
||||
* \param dwFreq Desired timer frequency.
|
||||
* \param dwMCk Master clock frequency.
|
||||
* \param dwDiv Divisor value.
|
||||
* \param dwTcClks TCCLKS field value for divisor.
|
||||
* \param dwBoardMCK Board clock frequency.
|
||||
*
|
||||
* \return 1 if a proper divisor has been found, otherwise 0.
|
||||
*/
|
||||
extern uint32_t TC_FindMckDivisor( uint32_t dwFreq, uint32_t dwMCk, uint32_t *dwDiv, uint32_t *dwTcClks, uint32_t dwBoardMCK )
|
||||
{
|
||||
const uint32_t adwDivisors[5] = { 2, 8, 32, 128, dwBoardMCK / 32768 } ;
|
||||
|
||||
uint32_t dwIndex = 0 ;
|
||||
|
||||
/* Satisfy lower bound */
|
||||
while ( dwFreq < ((dwMCk / adwDivisors[dwIndex]) / 65536) )
|
||||
{
|
||||
dwIndex++ ;
|
||||
|
||||
/* If no divisor can be found, return 0 */
|
||||
if ( dwIndex == (sizeof( adwDivisors )/sizeof( adwDivisors[0] )) )
|
||||
{
|
||||
return 0 ;
|
||||
}
|
||||
}
|
||||
|
||||
/* Try to maximize DIV while satisfying upper bound */
|
||||
while ( dwIndex < 4 )
|
||||
{
|
||||
|
||||
if ( dwFreq > (dwMCk / adwDivisors[dwIndex + 1]) )
|
||||
{
|
||||
break ;
|
||||
}
|
||||
dwIndex++ ;
|
||||
}
|
||||
|
||||
/* Store results */
|
||||
if ( dwDiv )
|
||||
{
|
||||
*dwDiv = adwDivisors[dwIndex] ;
|
||||
}
|
||||
if ( dwTcClks )
|
||||
{
|
||||
*dwTcClks = dwIndex ;
|
||||
}
|
||||
|
||||
return 1 ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read Timer Counter Counter Value on the selected TC & channel.
|
||||
*
|
||||
* \param p_tc Pointer to a TC instance.
|
||||
* \param ul_channel Channel to configure.
|
||||
*
|
||||
* \return RC value.
|
||||
*/
|
||||
uint32_t TC_ReadCV(Tc *p_tc, uint32_t ul_channel){
|
||||
return p_tc->TC_CHANNEL[ul_channel].TC_CV;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get current status on the selected channel.
|
||||
*
|
||||
* \param p_tc Pointer to a TC instance.
|
||||
* \param ul_channel Channel to configure.
|
||||
*
|
||||
* \return The current TC status.
|
||||
*/
|
||||
uint32_t TC_GetStatus(Tc *p_tc, uint32_t ul_channel)
|
||||
{
|
||||
TcChannel *tc_channel;
|
||||
|
||||
tc_channel = p_tc->TC_CHANNEL + ul_channel;
|
||||
return tc_channel->TC_SR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set RA on the selected channel.
|
||||
*
|
||||
* \param tc Pointer to a TC instance.
|
||||
* \param chan Channel to configure.
|
||||
* \param v New value for RA.
|
||||
*/
|
||||
void TC_SetRA(Tc *tc, uint32_t chan, uint32_t v) {
|
||||
tc->TC_CHANNEL[chan].TC_RA = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set RB on the selected channel.
|
||||
*
|
||||
* \param tc Pointer to a TC instance.
|
||||
* \param chan Channel to configure.
|
||||
* \param v New value for RB.
|
||||
*/
|
||||
void TC_SetRB(Tc *tc, uint32_t chan, uint32_t v) {
|
||||
tc->TC_CHANNEL[chan].TC_RB = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set RC on the selected channel.
|
||||
*
|
||||
* \param tc Pointer to a TC instance.
|
||||
* \param chan Channel to configure.
|
||||
* \param v New value for RC.
|
||||
*/
|
||||
void TC_SetRC(Tc *tc, uint32_t chan, uint32_t v) {
|
||||
tc->TC_CHANNEL[chan].TC_RC = v;
|
||||
}
|
||||
|
||||
118
hardware/digistump/sam/system/libsam/source/timetick.c
Normal file
118
hardware/digistump/sam/system/libsam/source/timetick.c
Normal file
@@ -0,0 +1,118 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
* Implement simple system tick usage.
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Local variables
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/** Tick Counter united by ms */
|
||||
static volatile uint32_t _dwTickCount=0 ;
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Exported Functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Handler for Sytem Tick interrupt.
|
||||
*/
|
||||
extern void TimeTick_Increment( void )
|
||||
{
|
||||
_dwTickCount++ ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures the SAM3 SysTick & reset tickCount.
|
||||
* Systick interrupt handler will generates 1ms interrupt and increase a
|
||||
* tickCount.
|
||||
* \param dwNew_MCK Current master clock.
|
||||
*/
|
||||
extern uint32_t TimeTick_Configure( uint32_t dwNew_MCK )
|
||||
{
|
||||
_dwTickCount = 0 ;
|
||||
|
||||
return SysTick_Config( dwNew_MCK/1000 ) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get current Tick Count, in ms.
|
||||
*/
|
||||
extern uint32_t GetTickCount( void )
|
||||
{
|
||||
return _dwTickCount ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sync Wait for several ms
|
||||
*/
|
||||
extern void Wait( volatile uint32_t dwMs )
|
||||
{
|
||||
uint32_t dwStart ;
|
||||
uint32_t dwCurrent ;
|
||||
|
||||
dwStart = _dwTickCount ;
|
||||
do
|
||||
{
|
||||
dwCurrent = _dwTickCount ;
|
||||
} while ( dwCurrent - dwStart < dwMs ) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sync Sleep for several ms
|
||||
*/
|
||||
extern void Sleep( volatile uint32_t dwMs )
|
||||
{
|
||||
uint32_t dwStart ;
|
||||
uint32_t dwCurrent ;
|
||||
|
||||
dwStart = _dwTickCount ;
|
||||
|
||||
do
|
||||
{
|
||||
dwCurrent = _dwTickCount ;
|
||||
|
||||
if ( dwCurrent - dwStart > dwMs )
|
||||
{
|
||||
break ;
|
||||
}
|
||||
|
||||
__WFI() ;
|
||||
} while( 1 ) ;
|
||||
}
|
||||
|
||||
160
hardware/digistump/sam/system/libsam/source/trng.c
Normal file
160
hardware/digistump/sam/system/libsam/source/trng.c
Normal file
@@ -0,0 +1,160 @@
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \brief TRNG driver for SAM.
|
||||
*
|
||||
* Copyright (c) 2011-2012 Atmel Corporation. All rights reserved.
|
||||
*
|
||||
* \asf_license_start
|
||||
*
|
||||
* \page License
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* 3. The name of Atmel may not be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* 4. This software may only be redistributed and used in connection with an
|
||||
* Atmel microcontroller product.
|
||||
*
|
||||
* 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
|
||||
* EXPRESSLY AND SPECIFICALLY 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.
|
||||
*
|
||||
* \asf_license_stop
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup group_sam_drivers_trng TRNG - True Random Number Generator
|
||||
*
|
||||
* Driver for the TRNG (True Random Number Generator). This driver provides access
|
||||
* to the main features of the TRNG controller.
|
||||
*
|
||||
* \{
|
||||
*/
|
||||
|
||||
#include "../chip.h"
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
#if SAM3XA_SERIES
|
||||
|
||||
/* TRNG Security Key Value */
|
||||
#define TRNG_KEY 0x524E47
|
||||
|
||||
/**
|
||||
* \brief Enable TRNG.
|
||||
*
|
||||
* \param p_trng Pointer to a TRNG instance.
|
||||
*
|
||||
*/
|
||||
void trng_enable(Trng *p_trng)
|
||||
{
|
||||
p_trng->TRNG_CR = TRNG_CR_ENABLE | TRNG_CR_KEY(TRNG_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable TRNG.
|
||||
*
|
||||
* \param p_trng Pointer to a TRNG instance.
|
||||
*
|
||||
*/
|
||||
void trng_disable(Trng *p_trng)
|
||||
{
|
||||
p_trng->TRNG_CR = TRNG_CR_KEY(TRNG_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enable TRNG interrupt.
|
||||
*
|
||||
* \param p_trng Pointer to a TRNG instance.
|
||||
*
|
||||
*/
|
||||
void trng_enable_interrupt(Trng *p_trng)
|
||||
{
|
||||
p_trng->TRNG_IER = TRNG_IER_DATRDY;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable TRNG interrupt.
|
||||
*
|
||||
* \param p_trng Pointer to a TRNG instance.
|
||||
*
|
||||
*/
|
||||
void trng_disable_interrupt(Trng *p_trng)
|
||||
{
|
||||
p_trng->TRNG_IDR = TRNG_IER_DATRDY;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get TRNG interrupt mask.
|
||||
*
|
||||
* \param p_trng Pointer to a TRNG instance.
|
||||
*
|
||||
* \retval The interrupt mask value.
|
||||
*/
|
||||
uint32_t trng_get_interrupt_mask(Trng *p_trng)
|
||||
{
|
||||
return p_trng->TRNG_IMR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get TRNG interrupt status.
|
||||
*
|
||||
* \param p_trng Pointer to a TRNG instance.
|
||||
*
|
||||
* \retval The interrupt status value.
|
||||
*/
|
||||
uint32_t trng_get_interrupt_status(Trng *p_trng)
|
||||
{
|
||||
return p_trng->TRNG_ISR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read TRNG output data.
|
||||
*
|
||||
* \param p_trng Pointer to a TRNG instance.
|
||||
*
|
||||
* \retval The output data value.
|
||||
*/
|
||||
uint32_t trng_read_output_data(Trng *p_trng)
|
||||
{
|
||||
return p_trng->TRNG_ODATA;
|
||||
}
|
||||
|
||||
#endif // SAM3XA_SERIES
|
||||
|
||||
/// @cond 0
|
||||
/**INDENT-OFF**/
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
/**INDENT-ON**/
|
||||
/// @endcond
|
||||
|
||||
/**
|
||||
* \}
|
||||
*/
|
||||
380
hardware/digistump/sam/system/libsam/source/twi.c
Normal file
380
hardware/digistump/sam/system/libsam/source/twi.c
Normal file
@@ -0,0 +1,380 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** \addtogroup twi_module Working with TWI
|
||||
* The TWI driver provides the interface to configure and use the TWI
|
||||
* peripheral.
|
||||
*
|
||||
* \section Usage
|
||||
* <ul>
|
||||
* <li> Configures a TWI peripheral to operate in master mode, at the given
|
||||
* frequency (in Hz) using TWI_Configure(). </li>
|
||||
* <li> Sends a STOP condition on the TWI using TWI_Stop().</li>
|
||||
* <li> Starts a read operation on the TWI bus with the specified slave using
|
||||
* TWI_StartRead(). Data must then be read using TWI_ReadByte() whenever
|
||||
* a byte is available (poll using TWI_ByteReceived()).</li>
|
||||
* <li> Starts a write operation on the TWI to access the selected slave using
|
||||
* TWI_StartWrite(). A byte of data must be provided to start the write;
|
||||
* other bytes are written next.</li>
|
||||
* <li> Sends a byte of data to one of the TWI slaves on the bus using TWI_WriteByte().
|
||||
* This function must be called once before TWI_StartWrite() with the first byte of data
|
||||
* to send, then it shall be called repeatedly after that to send the remaining bytes.</li>
|
||||
* <li> Check if a byte has been received and can be read on the given TWI
|
||||
* peripheral using TWI_ByteReceived().<
|
||||
* Check if a byte has been sent using TWI_ByteSent().</li>
|
||||
* <li> Check if the current transmission is complete (the STOP has been sent)
|
||||
* using TWI_TransferComplete().</li>
|
||||
* <li> Enables & disable the selected interrupts sources on a TWI peripheral
|
||||
* using TWI_EnableIt() and TWI_DisableIt().</li>
|
||||
* <li> Get current status register of the given TWI peripheral using
|
||||
* TWI_GetStatus(). Get current status register of the given TWI peripheral, but
|
||||
* masking interrupt sources which are not currently enabled using
|
||||
* TWI_GetMaskedStatus().</li>
|
||||
* </ul>
|
||||
* For more accurate information, please look at the TWI section of the
|
||||
* Datasheet.
|
||||
*
|
||||
* Related files :\n
|
||||
* \ref twi.c\n
|
||||
* \ref twi.h.\n
|
||||
*/
|
||||
/*@{*/
|
||||
/*@}*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of Two Wire Interface (TWI).
|
||||
*
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Exported functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Configures a TWI peripheral to operate in master mode, at the given
|
||||
* frequency (in Hz). The duty cycle of the TWI clock is set to 50%.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \param twck Desired TWI clock frequency.
|
||||
* \param mck Master clock frequency.
|
||||
*/
|
||||
void TWI_ConfigureMaster( Twi* pTwi, uint32_t dwTwCk, uint32_t dwMCk )
|
||||
{
|
||||
uint32_t dwCkDiv = 0 ;
|
||||
uint32_t dwClDiv ;
|
||||
uint32_t dwOk = 0 ;
|
||||
|
||||
assert( pTwi ) ;
|
||||
|
||||
/* SVEN: TWI Slave Mode Enabled */
|
||||
pTwi->TWI_CR = TWI_CR_SVEN ;
|
||||
/* Reset the TWI */
|
||||
pTwi->TWI_CR = TWI_CR_SWRST ;
|
||||
pTwi->TWI_RHR ;
|
||||
|
||||
/* TWI Slave Mode Disabled, TWI Master Mode Disabled. */
|
||||
pTwi->TWI_CR = TWI_CR_SVDIS ;
|
||||
pTwi->TWI_CR = TWI_CR_MSDIS ;
|
||||
|
||||
/* Set master mode */
|
||||
pTwi->TWI_CR = TWI_CR_MSEN ;
|
||||
|
||||
/* Configure clock */
|
||||
while ( !dwOk )
|
||||
{
|
||||
dwClDiv = ((dwMCk / (2 * dwTwCk)) - 4) / (1<<dwCkDiv) ;
|
||||
|
||||
if ( dwClDiv <= 255 )
|
||||
{
|
||||
dwOk = 1 ;
|
||||
}
|
||||
else
|
||||
{
|
||||
dwCkDiv++ ;
|
||||
}
|
||||
}
|
||||
|
||||
assert( dwCkDiv < 8 ) ;
|
||||
// TRACE_DEBUG( "Using CKDIV = %u and CLDIV/CHDIV = %u\n\r", dwCkDiv, dwClDiv ) ;
|
||||
|
||||
pTwi->TWI_CWGR = 0 ;
|
||||
pTwi->TWI_CWGR = (dwCkDiv << 16) | (dwClDiv << 8) | dwClDiv ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Configures a TWI peripheral to operate in slave mode.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \param slaveAddress Slave address.
|
||||
*/
|
||||
void TWI_ConfigureSlave(Twi *pTwi, uint8_t slaveAddress)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
/* TWI software reset */
|
||||
pTwi->TWI_CR = TWI_CR_SWRST;
|
||||
pTwi->TWI_RHR;
|
||||
|
||||
/* Wait at least 10 ms */
|
||||
for (i=0; i < 1000000; i++);
|
||||
|
||||
/* TWI Slave Mode Disabled, TWI Master Mode Disabled*/
|
||||
pTwi->TWI_CR = TWI_CR_SVDIS | TWI_CR_MSDIS;
|
||||
|
||||
/* Configure slave address. */
|
||||
pTwi->TWI_SMR = 0;
|
||||
pTwi->TWI_SMR = TWI_SMR_SADR(slaveAddress);
|
||||
|
||||
/* SVEN: TWI Slave Mode Enabled */
|
||||
pTwi->TWI_CR = TWI_CR_SVEN;
|
||||
|
||||
/* Wait at least 10 ms */
|
||||
for (i=0; i < 1000000; i++);
|
||||
assert( (pTwi->TWI_CR & TWI_CR_SVDIS)!= TWI_CR_SVDIS ) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends a STOP condition on the TWI.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
*/
|
||||
void TWI_Stop( Twi *pTwi )
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
|
||||
pTwi->TWI_CR = TWI_CR_STOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Starts a read operation on the TWI bus with the specified slave, it returns
|
||||
* immediately. Data must then be read using TWI_ReadByte() whenever a byte is
|
||||
* available (poll using TWI_ByteReceived()).
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \param address Slave address on the bus.
|
||||
* \param iaddress Optional internal address bytes.
|
||||
* \param isize Number of internal address bytes.
|
||||
*/
|
||||
void TWI_StartRead(
|
||||
Twi *pTwi,
|
||||
uint8_t address,
|
||||
uint32_t iaddress,
|
||||
uint8_t isize)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
assert( (address & 0x80) == 0 ) ;
|
||||
assert( (iaddress & 0xFF000000) == 0 ) ;
|
||||
assert( isize < 4 ) ;
|
||||
|
||||
/* Set slave address and number of internal address bytes. */
|
||||
pTwi->TWI_MMR = 0;
|
||||
pTwi->TWI_MMR = (isize << 8) | TWI_MMR_MREAD | (address << 16);
|
||||
|
||||
/* Set internal address bytes */
|
||||
pTwi->TWI_IADR = 0;
|
||||
pTwi->TWI_IADR = iaddress;
|
||||
|
||||
/* Send START condition */
|
||||
pTwi->TWI_CR = TWI_CR_START;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads a byte from the TWI bus. The read operation must have been started
|
||||
* using TWI_StartRead() and a byte must be available (check with TWI_ByteReceived()).
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \return byte read.
|
||||
*/
|
||||
uint8_t TWI_ReadByte(Twi *pTwi)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
|
||||
return pTwi->TWI_RHR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends a byte of data to one of the TWI slaves on the bus.
|
||||
* \note This function must be called once before TWI_StartWrite() with
|
||||
* the first byte of data to send, then it shall be called repeatedly
|
||||
* after that to send the remaining bytes.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \param byte Byte to send.
|
||||
*/
|
||||
void TWI_WriteByte(Twi *pTwi, uint8_t byte)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
|
||||
pTwi->TWI_THR = byte;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Starts a write operation on the TWI to access the selected slave, then
|
||||
* returns immediately. A byte of data must be provided to start the write;
|
||||
* other bytes are written next.
|
||||
* after that to send the remaining bytes.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \param address Address of slave to acccess on the bus.
|
||||
* \param iaddress Optional slave internal address.
|
||||
* \param isize Number of internal address bytes.
|
||||
* \param byte First byte to send.
|
||||
*/
|
||||
void TWI_StartWrite(
|
||||
Twi *pTwi,
|
||||
uint8_t address,
|
||||
uint32_t iaddress,
|
||||
uint8_t isize,
|
||||
uint8_t byte)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
assert( (address & 0x80) == 0 ) ;
|
||||
assert( (iaddress & 0xFF000000) == 0 ) ;
|
||||
assert( isize < 4 ) ;
|
||||
|
||||
/* Set slave address and number of internal address bytes. */
|
||||
pTwi->TWI_MMR = 0;
|
||||
pTwi->TWI_MMR = (isize << 8) | (address << 16);
|
||||
|
||||
/* Set internal address bytes. */
|
||||
pTwi->TWI_IADR = 0;
|
||||
pTwi->TWI_IADR = iaddress;
|
||||
|
||||
/* Write first byte to send.*/
|
||||
TWI_WriteByte(pTwi, byte);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if a byte have been receiced from TWI.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \return 1 if a byte has been received and can be read on the given TWI
|
||||
* peripheral; otherwise, returns 0. This function resets the status register.
|
||||
*/
|
||||
uint8_t TWI_ByteReceived(Twi *pTwi)
|
||||
{
|
||||
return ((pTwi->TWI_SR & TWI_SR_RXRDY) == TWI_SR_RXRDY);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if a byte have been sent to TWI.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \return 1 if a byte has been sent so another one can be stored for
|
||||
* transmission; otherwise returns 0. This function clears the status register.
|
||||
*/
|
||||
uint8_t TWI_ByteSent(Twi *pTwi)
|
||||
{
|
||||
return ((pTwi->TWI_SR & TWI_SR_TXRDY) == TWI_SR_TXRDY);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check if current transmission is complet.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \return 1 if the current transmission is complete (the STOP has been sent);
|
||||
* otherwise returns 0.
|
||||
*/
|
||||
uint8_t TWI_TransferComplete(Twi *pTwi)
|
||||
{
|
||||
return ((pTwi->TWI_SR & TWI_SR_TXCOMP) == TWI_SR_TXCOMP);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables the selected interrupts sources on a TWI peripheral.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \param sources Bitwise OR of selected interrupt sources.
|
||||
*/
|
||||
void TWI_EnableIt(Twi *pTwi, uint32_t sources)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
assert( (sources & 0xFFFFF088) == 0 ) ;
|
||||
|
||||
pTwi->TWI_IER = sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disables the selected interrupts sources on a TWI peripheral.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \param sources Bitwise OR of selected interrupt sources.
|
||||
*/
|
||||
void TWI_DisableIt(Twi *pTwi, uint32_t sources)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
assert( (sources & 0xFFFFF088) == 0 ) ;
|
||||
|
||||
pTwi->TWI_IDR = sources;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get the current status register of the given TWI peripheral.
|
||||
* \note This resets the internal value of the status register, so further
|
||||
* read may yield different values.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
* \return TWI status register.
|
||||
*/
|
||||
uint32_t TWI_GetStatus(Twi *pTwi)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
|
||||
return pTwi->TWI_SR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns the current status register of the given TWI peripheral, but
|
||||
* masking interrupt sources which are not currently enabled.
|
||||
* \note This resets the internal value of the status register, so further
|
||||
* read may yield different values.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
*/
|
||||
uint32_t TWI_GetMaskedStatus(Twi *pTwi)
|
||||
{
|
||||
uint32_t status;
|
||||
|
||||
assert( pTwi != NULL ) ;
|
||||
|
||||
status = pTwi->TWI_SR;
|
||||
status &= pTwi->TWI_IMR;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends a STOP condition. STOP Condition is sent just after completing
|
||||
* the current byte transmission in master read mode.
|
||||
* \param pTwi Pointer to an Twi instance.
|
||||
*/
|
||||
void TWI_SendSTOPCondition(Twi *pTwi)
|
||||
{
|
||||
assert( pTwi != NULL ) ;
|
||||
|
||||
pTwi->TWI_CR |= TWI_CR_STOP;
|
||||
}
|
||||
|
||||
38
hardware/digistump/sam/system/libsam/source/udp.c
Normal file
38
hardware/digistump/sam/system/libsam/source/udp.c
Normal file
@@ -0,0 +1,38 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#if SAM3S_SERIES || SAM4S_SERIES
|
||||
|
||||
#include "USB_device.h"
|
||||
#include "udp.h"
|
||||
|
||||
|
||||
#endif /* SAM3S_SERIES || SAM4S_SERIES */
|
||||
471
hardware/digistump/sam/system/libsam/source/udphs.c
Normal file
471
hardware/digistump/sam/system/libsam/source/udphs.c
Normal file
@@ -0,0 +1,471 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#if 0 //SAM3U_SERIES
|
||||
|
||||
#include "USB_device.h"
|
||||
#include "udphs.h"
|
||||
|
||||
/// Max size of the FMA FIFO
|
||||
#define EPT_VIRTUAL_SIZE (16384u)
|
||||
#define SHIFT_INTERUPT (8u)
|
||||
|
||||
int _cmark;
|
||||
int _cend;
|
||||
|
||||
// Global variable for endpoint number
|
||||
unsigned int NumEndpoint=0;
|
||||
|
||||
|
||||
void USBD_WaitIN(void)
|
||||
{
|
||||
// while (!(UEINTX & (1<<TXINI)));
|
||||
while (!(UDPHS->UDPHS_EPT[0].UDPHS_EPTSTA & UDPHS_EPTSTA_TX_PK_RDY));
|
||||
}
|
||||
|
||||
void USBD_WaitOUT(void)
|
||||
{
|
||||
// while (!(UEINTX & (1<<RXOUTI)))
|
||||
// ;
|
||||
// Waiting for Status stage
|
||||
while (UDPHS_EPTSTA_RX_BK_RDY != (UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSTA & UDPHS_EPTSTA_RX_BK_RDY));
|
||||
}
|
||||
|
||||
void USBD_ClearIN(void)
|
||||
{
|
||||
// UEINTX = ~(1<<TXINI);
|
||||
UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTCLRSTA = UDPHS_EPTCLRSTA_TX_COMPLT;
|
||||
}
|
||||
|
||||
void USBD_ClearOUT(void)
|
||||
{
|
||||
// UEINTX = ~(1<<RXOUTI);
|
||||
UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTCLRSTA = UDPHS_EPTCLRSTA_RX_BK_RDY;
|
||||
}
|
||||
|
||||
uint8_t USBD_WaitForINOrOUT(void)
|
||||
{
|
||||
// while (!(UEINTX & ((1<<TXINI)|(1<<RXOUTI))))
|
||||
// ;
|
||||
// return (UEINTX & (1<<RXOUTI)) == 0;
|
||||
while (!(UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSTA & (UDPHS_EPTSTA_RX_BK_RDY | UDPHS_EPTSTA_TX_PK_RDY)));
|
||||
return (UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSTA & UDPHS_EPTSTA_RX_BK_RDY) == 0;
|
||||
}
|
||||
|
||||
void USBD_ClearRxFlag(unsigned char bEndpoint)
|
||||
{
|
||||
UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTCLRSTA = UDPHS_EPTCLRSTA_RX_BK_RDY;
|
||||
}
|
||||
|
||||
void USBD_Stall(void)
|
||||
{
|
||||
// UECONX = (1<<STALLRQ) | (1<<EPEN);
|
||||
UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSETSTA = UDPHS_EPTSETSTA_FRCESTALL;
|
||||
}
|
||||
|
||||
uint8_t USBD_Stalled(void)
|
||||
{
|
||||
// return UEINTX & (1<<STALLEDI);
|
||||
// Check if the data has been STALLed
|
||||
return ( UDPHS_EPTSTA_FRCESTALL == (UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSTA & UDPHS_EPTSTA_FRCESTALL));
|
||||
}
|
||||
|
||||
uint8_t USBD_ReceivedSetupInt(void)
|
||||
{
|
||||
// return UEINTX & (1<<RXSTPI);
|
||||
return ( UDPHS_EPTSTA_RX_SETUP == (UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSTA & UDPHS_EPTSTA_RX_SETUP) );
|
||||
}
|
||||
|
||||
void USBD_ClearSetupInt(void)
|
||||
{
|
||||
// UEINTX = ~((1<<RXSTPI) | (1<<RXOUTI) | (1<<TXINI));
|
||||
UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTCLRSTA = UDPHS_EPTSTA_RX_SETUP | UDPHS_EPTCLRSTA_RX_BK_RDY | UDPHS_EPTCLRSTA_TX_COMPLT;
|
||||
}
|
||||
|
||||
uint8_t USBD_ReadWriteAllowed(void)
|
||||
{
|
||||
//return UEINTX & (1<<RWAL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void USBD_SetEP(uint8_t ep)
|
||||
{
|
||||
// UENUM = ep;
|
||||
NumEndpoint = ep & 7;
|
||||
}
|
||||
|
||||
uint16_t USBD_FifoByteCount(void)
|
||||
{
|
||||
// return UEBCLX;
|
||||
// SAM3X
|
||||
//return ((UOTGHS->UOTGHS_DEVEPTISR[ep] & UOTGHS_DEVEPTISR_BYCT_Msk) >> UOTGHS_DEVEPTISR_BYCT_Pos);
|
||||
// SAM3U //AT91C_UDPHS_BYTE_COUNT (0x7FF << 20)
|
||||
return ((UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSTA & UDPHS_EPTSTA_BYTE_COUNT_Msk) >> UDPHS_EPTSTA_BYTE_COUNT_Pos);
|
||||
}
|
||||
|
||||
uint8_t USBD_FifoFree(void)
|
||||
{
|
||||
// return UEINTX & (1<<FIFOCON);
|
||||
return( 0 != (UDPHS->UDPHS_EPT[NumEndpoint].UDPHS_EPTSTA & UDPHS_EPTSTA_TX_PK_RDY ));
|
||||
}
|
||||
|
||||
void USBD_ReleaseRX(void)
|
||||
{
|
||||
UEINTX = 0x6B; // FIFOCON=0 NAKINI=1 RWAL=1 NAKOUTI=0 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=1
|
||||
}
|
||||
|
||||
void USBD_ReleaseTX()
|
||||
{
|
||||
UEINTX = 0x3A; // FIFOCON=0 NAKINI=0 RWAL=1 NAKOUTI=1 RXSTPI=1 RXOUTI=0 STALLEDI=1 TXINI=0
|
||||
}
|
||||
|
||||
uint8_t USBD_FrameNumber(void)
|
||||
{
|
||||
return UDFNUML;
|
||||
}
|
||||
|
||||
uint8_t USBD_GetConfiguration(void)
|
||||
{
|
||||
return _usbConfiguration;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void USBD_Recv(volatile uint8_t* data, uint8_t count)
|
||||
{
|
||||
uint8_t *pFifo;
|
||||
|
||||
pFifo = (uint8_t*)((uint32_t *)UDPHS_RAM_ADDR + (EPT_VIRTUAL_SIZE * NumEndpoint));
|
||||
|
||||
while (count--)
|
||||
{
|
||||
*data++ = pFifo[0];
|
||||
}
|
||||
|
||||
// RXLED1; // light the RX LED
|
||||
// RxLEDPulse = TX_RX_LED_PULSE_MS;
|
||||
}
|
||||
|
||||
uint8_t USBD_Recv8(void)
|
||||
{
|
||||
uint8_t *pFifo;
|
||||
|
||||
// RXLED1; // light the RX LED
|
||||
// RxLEDPulse = TX_RX_LED_PULSE_MS;
|
||||
|
||||
pFifo = (uint8_t*)((uint32_t *)UDPHS_RAM_ADDR + (EPT_VIRTUAL_SIZE * NumEndpoint));
|
||||
|
||||
// return UEDATX;
|
||||
return (pFifo[0]);
|
||||
}
|
||||
|
||||
void USBD_Send8(uint8_t d)
|
||||
{
|
||||
uint8_t *pFifo;
|
||||
pFifo = (uint8_t*)((uint32_t *)UDPHS_RAM_ADDR + (EPT_VIRTUAL_SIZE * NumEndpoint));
|
||||
// UEDATX = d;
|
||||
pFifo[0] =d;
|
||||
}
|
||||
|
||||
// Blocking Send of data to an endpoint
|
||||
int USBD_Send(uint8_t ep, const void* d, int len)
|
||||
{
|
||||
if (!_usbConfiguration)
|
||||
return -1;
|
||||
|
||||
int r = len;
|
||||
const uint8_t* data = (const uint8_t*)d;
|
||||
uint8_t zero = ep & TRANSFER_ZERO;
|
||||
uint8_t timeout = 250; // 250ms timeout on send? TODO
|
||||
while (len)
|
||||
{
|
||||
uint8_t n = USB_SendSpace(ep);
|
||||
if (n == 0)
|
||||
{
|
||||
if (!(--timeout))
|
||||
return -1;
|
||||
delay(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n > len)
|
||||
n = len;
|
||||
len -= n;
|
||||
{
|
||||
SetEP(ep);
|
||||
if (ep & TRANSFER_ZERO)
|
||||
{
|
||||
while (n--)
|
||||
Send8(0);
|
||||
}
|
||||
else if (ep & TRANSFER_PGM)
|
||||
{
|
||||
while (n--)
|
||||
Send8(*data++);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (n--)
|
||||
Send8(*data++);
|
||||
}
|
||||
// if (!ReadWriteAllowed() || ((len == 0) && (ep & TRANSFER_RELEASE))) // Release full buffer
|
||||
// ReleaseTX();
|
||||
}
|
||||
}
|
||||
TXLED1; // light the TX LED
|
||||
TxLEDPulse = TX_RX_LED_PULSE_MS;
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// Space in send EP
|
||||
uint8_t USBD_SendSpace(uint8_t ep)
|
||||
{
|
||||
SetEP(ep);
|
||||
if (!ReadWriteAllowed())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 64 - FifoByteCount();
|
||||
}
|
||||
|
||||
// Number of bytes, assumes a rx endpoint
|
||||
uint8_t USBD_Available(uint8_t ep)
|
||||
{
|
||||
SetEP(ep);
|
||||
|
||||
return FifoByteCount();
|
||||
}
|
||||
|
||||
void USBD_InitEP(uint8_t index, uint8_t type, uint8_t size)
|
||||
{
|
||||
UENUM = index;
|
||||
UECONX = 1;
|
||||
UECFG0X = type;
|
||||
UECFG1X = size;
|
||||
}
|
||||
|
||||
|
||||
void USBD_InitEndpoints(void)
|
||||
{
|
||||
for (uint8_t i = 1; i < sizeof(_initEndpoints); i++)
|
||||
{
|
||||
// Reset Endpoint Fifos
|
||||
UDPHS->UDPHS_EPT[i].UDPHS_EPTCLRSTA = UDPHS_EPTCLRSTA_TOGGLESQ | UDPHS_EPTCLRSTA_FRCESTALL;
|
||||
UDPHS->UDPHS_EPTRST = 1<<i;
|
||||
|
||||
//UECONX = 1;
|
||||
//UECFG0X = pgm_read_byte(_initEndpoints+i);
|
||||
UDPHS->UDPHS_EPT[i].UDPHS_EPTCFG = _initEndpoints[i];
|
||||
|
||||
while( (signed int)UDPHS_EPTCFG_EPT_MAPD != (signed int)((UDPHS->UDPHS_EPT[i].UDPHS_EPTCFG) & (unsigned int)UDPHS_EPTCFG_EPT_MAPD) )
|
||||
;
|
||||
UDPHS->UDPHS_EPT[i].UDPHS_EPTCTLENB = UDPHS_EPTCTLENB_EPT_ENABL;
|
||||
|
||||
// UECFG1X = EP_DOUBLE_64;
|
||||
}
|
||||
///\// UERST = 0x7E; // And reset them
|
||||
///\// UERST = 0;
|
||||
}
|
||||
|
||||
void USBD_InitControl(int end)
|
||||
{
|
||||
SetEP(0);
|
||||
UDPHS->UDPHS_EPT[0].UDPHS_EPTCFG = _initEndpoints[0];
|
||||
|
||||
while( (signed int)UDPHS_EPTCFG_EPT_MAPD != (signed int)((UDPHS->UDPHS_EPT[0].UDPHS_EPTCFG) & (unsigned int)UDPHS_EPTCFG_EPT_MAPD) )
|
||||
;
|
||||
|
||||
UDPHS->UDPHS_EPT[0].UDPHS_EPTCTLENB = UDPHS_EPTCTLENB_RX_BK_RDY
|
||||
| UDPHS_EPTCTLENB_RX_SETUP
|
||||
| UDPHS_EPTCTLENB_EPT_ENABL;
|
||||
|
||||
_cmark = 0;
|
||||
_cend = end;
|
||||
}
|
||||
|
||||
void UDPHS_Handler( void )
|
||||
{
|
||||
unsigned int status;
|
||||
unsigned char numIT;
|
||||
|
||||
// Get interrupts status
|
||||
status = UDPHS->UDPHS_INTSTA & UDPHS->UDPHS_IEN;
|
||||
|
||||
// Handle all UDPHS interrupts
|
||||
while (status != 0) {
|
||||
|
||||
// Start of Frame - happens every millisecond so we use it for TX and RX LED one-shot timing, too
|
||||
if ((status & UDPHS_IEN_INT_SOF) != 0) {
|
||||
|
||||
#ifdef CDC_ENABLED
|
||||
USB_Flush(CDC_TX); // Send a tx frame if found
|
||||
#endif
|
||||
|
||||
// check whether the one-shot period has elapsed. if so, turn off the LED
|
||||
if (TxLEDPulse && !(--TxLEDPulse))
|
||||
TXLED0;
|
||||
if (RxLEDPulse && !(--RxLEDPulse))
|
||||
RXLED0;
|
||||
|
||||
// Acknowledge interrupt
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_INT_SOF;
|
||||
status &= ~UDPHS_IEN_INT_SOF;
|
||||
}
|
||||
// Suspend
|
||||
// This interrupt is always treated last (hence the '==')
|
||||
else if (status == UDPHS_IEN_DET_SUSPD) {
|
||||
|
||||
//UDPHS_DisableBIAS();
|
||||
|
||||
// Enable wakeup
|
||||
UDPHS->UDPHS_IEN |= UDPHS_IEN_WAKE_UP | UDPHS_IEN_ENDOFRSM;
|
||||
UDPHS->UDPHS_IEN &= ~UDPHS_IEN_DET_SUSPD;
|
||||
|
||||
// Acknowledge interrupt
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_DET_SUSPD | UDPHS_CLRINT_WAKE_UP;
|
||||
|
||||
//UDPHS_DisableUsbClock();
|
||||
|
||||
}
|
||||
// Resume
|
||||
else if( ((status & UDPHS_IEN_WAKE_UP) != 0) // line activity
|
||||
|| ((status & UDPHS_IEN_ENDOFRSM) != 0)) { // pc wakeup
|
||||
{
|
||||
|
||||
//UDPHS_EnableUsbClock();
|
||||
//UDPHS_EnableBIAS();
|
||||
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_WAKE_UP | UDPHS_CLRINT_ENDOFRSM
|
||||
| UDPHS_CLRINT_DET_SUSPD;
|
||||
|
||||
UDPHS->UDPHS_IEN |= UDPHS_IEN_ENDOFRSM | UDPHS_IEN_DET_SUSPD;
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_WAKE_UP | UDPHS_CLRINT_ENDOFRSM;
|
||||
UDPHS->UDPHS_IEN &= ~UDPHS_IEN_WAKE_UP;
|
||||
}
|
||||
}
|
||||
// End of Reset
|
||||
else if ((status & UDPHS_IEN_ENDRESET) == UDPHS_IEN_ENDRESET) {
|
||||
|
||||
InitControl(0); // init ep0
|
||||
_usbConfiguration = 0; // not configured yet
|
||||
//UEIENX = 1 << RXSTPE; // Enable interrupts for ep0
|
||||
|
||||
//UDPHS_ResetEndpoints();
|
||||
//UDPHS_DisableEndpoints();
|
||||
//USBD_ConfigureEndpoint(0);
|
||||
UDPHS->UDPHS_IEN |= (1<<SHIFT_INTERUPT<<0);
|
||||
|
||||
// Flush and enable the Suspend interrupt
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_WAKE_UP | UDPHS_CLRINT_DET_SUSPD;
|
||||
|
||||
//// Enable the Start Of Frame (SOF) interrupt if needed
|
||||
UDPHS->UDPHS_IEN |= UDPHS_IEN_INT_SOF;
|
||||
|
||||
// Acknowledge end of bus reset interrupt
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_ENDRESET;
|
||||
|
||||
UDPHS->UDPHS_IEN |= UDPHS_IEN_DET_SUSPD;
|
||||
}
|
||||
// Handle upstream resume interrupt
|
||||
else if (status & UDPHS_IEN_UPSTR_RES) {
|
||||
|
||||
// - Acknowledge the IT
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_UPSTR_RES;
|
||||
}
|
||||
// Endpoint interrupts
|
||||
else {
|
||||
// Handle endpoint interrupts
|
||||
for (numIT = 0; numIT < NUM_IT_MAX; numIT++) {
|
||||
|
||||
if ((status & (1 << SHIFT_INTERUPT << numIT)) != 0) {
|
||||
USB_ISR();
|
||||
//EndpointHandler(numIT); // TODO: interrupt for bulk
|
||||
}
|
||||
}
|
||||
}
|
||||
// Retrieve new interrupt status
|
||||
status = UDPHS->UDPHS_INTSTA & UDPHS->UDPHS_IEN;
|
||||
}
|
||||
}
|
||||
|
||||
void USBD_Attach( void )
|
||||
{/*
|
||||
_usbConfiguration = 0;
|
||||
|
||||
//UHWCON = 0x01; // power internal reg
|
||||
//USBCON = (1<<USBE)|(1<<FRZCLK); // clock frozen, usb enabled
|
||||
//PLLCSR = 0x12; // Need 16 MHz xtal
|
||||
//while (!(PLLCSR & (1<<PLOCK))) // wait for lock pll
|
||||
// ;
|
||||
PMC->PMC_PCER = (1 << ID_UDPHS);
|
||||
// Enable 480MHZ
|
||||
//AT91C_BASE_CKGR->CKGR_UCKR |= (AT91C_CKGR_PLLCOUNT & (3 << 20)) | AT91C_CKGR_UPLLEN;
|
||||
CKGR->CKGR_UCKR |= ((0xf << 20) & (3 << 20)) | AT91C_CKGR_UPLLEN;
|
||||
// Wait until UTMI PLL is locked
|
||||
while ((PMC->PMC_SR & PMC_LOCKU) == 0);
|
||||
|
||||
// Reset and enable IP UDPHS
|
||||
UDPHS->UDPHS_CTRL &= ~UDPHS_CTRL_EN_UDPHS;
|
||||
UDPHS->UDPHS_CTRL |= UDPHS_CTRL_EN_UDPHS;
|
||||
|
||||
//USBCON = ((1<<USBE)|(1<<OTGPADE)); // start USB clock
|
||||
UDPHS->UDPHS_IEN = 0;
|
||||
UDPHS->UDPHS_CLRINT = UDPHS_CLRINT_UPSTR_RES
|
||||
| UDPHS_CLRINT_ENDOFRSM
|
||||
| UDPHS_CLRINT_WAKE_UP
|
||||
| UDPHS_CLRINT_ENDRESET
|
||||
| UDPHS_CLRINT_INT_SOF
|
||||
| UDPHS_CLRINT_MICRO_SOF
|
||||
| UDPHS_CLRINT_DET_SUSPD;
|
||||
|
||||
// Enable interrupts for EOR (End of Reset), wake up and SOF (start of frame)
|
||||
//UDIEN = (1<<EORSTE)|(1<<SOFE);
|
||||
UDPHS->UDPHS_IEN = UDPHS_IEN_ENDOFRSM
|
||||
| UDPHS_IEN_WAKE_UP
|
||||
| UDPHS_IEN_DET_SUSPD;
|
||||
|
||||
// enable attach resistor
|
||||
//UDCON = 0;
|
||||
UDPHS->UDPHS_CTRL &= ~UDPHS_CTRL_DETACH; // Pull Up on DP
|
||||
UDPHS->UDPHS_CTRL |= UDPHS_CTRL_PULLD_DIS; // Disable Pull Down
|
||||
|
||||
TX_RX_LED_INIT;
|
||||
*/}
|
||||
|
||||
void USBD_Detach( void )
|
||||
{
|
||||
UDPHS->UDPHS_CTRL |= UDPHS_CTRL_DETACH; // detach
|
||||
UDPHS->UDPHS_CTRL &= ~UDPHS_CTRL_PULLD_DIS; // Enable Pull Down
|
||||
}
|
||||
|
||||
#endif /* SAM3U_SERIES */
|
||||
42
hardware/digistump/sam/system/libsam/source/uotghs.c
Normal file
42
hardware/digistump/sam/system/libsam/source/uotghs.c
Normal file
@@ -0,0 +1,42 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#if SAM3XA_SERIES
|
||||
|
||||
void (*gpf_isr)(void) = (0UL);
|
||||
|
||||
void UOTGHS_Handler( void )
|
||||
{
|
||||
if (gpf_isr)
|
||||
gpf_isr();
|
||||
}
|
||||
|
||||
#endif /* SAM3XA_SERIES */
|
||||
334
hardware/digistump/sam/system/libsam/source/uotghs_device.c
Normal file
334
hardware/digistump/sam/system/libsam/source/uotghs_device.c
Normal file
@@ -0,0 +1,334 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "chip.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if SAM3XA_SERIES
|
||||
|
||||
//#define TRACE_UOTGHS_DEVICE(x) x
|
||||
#define TRACE_UOTGHS_DEVICE(x)
|
||||
|
||||
extern void (*gpf_isr)(void);
|
||||
|
||||
static volatile uint32_t ul_send_fifo_ptr[MAX_ENDPOINTS];
|
||||
static volatile uint32_t ul_recv_fifo_ptr[MAX_ENDPOINTS];
|
||||
|
||||
void UDD_SetStack(void (*pf_isr)(void))
|
||||
{
|
||||
gpf_isr = pf_isr;
|
||||
}
|
||||
|
||||
uint32_t UDD_Init(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < MAX_ENDPOINTS; ++i)
|
||||
{
|
||||
ul_send_fifo_ptr[i] = 0;
|
||||
ul_recv_fifo_ptr[i] = 0;
|
||||
}
|
||||
|
||||
// Enables the USB Clock
|
||||
pmc_enable_periph_clk(ID_UOTGHS);
|
||||
pmc_enable_upll_clock();
|
||||
pmc_switch_udpck_to_upllck(0); // div=0+1
|
||||
pmc_enable_udpck();
|
||||
|
||||
// Configure interrupts
|
||||
NVIC_SetPriority((IRQn_Type) ID_UOTGHS, 0UL);
|
||||
NVIC_EnableIRQ((IRQn_Type) ID_UOTGHS);
|
||||
|
||||
// Always authorize asynchrone USB interrupts to exit from sleep mode
|
||||
// for SAM3 USB wake up device except BACKUP mode
|
||||
//pmc_set_fast_startup_input(PMC_FSMR_USBAL);
|
||||
|
||||
// ID pin not used then force device mode
|
||||
otg_disable_id_pin();
|
||||
otg_force_device_mode();
|
||||
|
||||
// Enable USB hardware
|
||||
otg_disable_pad();
|
||||
otg_enable_pad();
|
||||
otg_enable();
|
||||
otg_unfreeze_clock();
|
||||
|
||||
// Check USB clock
|
||||
//while (!Is_otg_clock_usable())
|
||||
// ;
|
||||
|
||||
// Enable High Speed
|
||||
udd_low_speed_disable();
|
||||
udd_high_speed_enable();
|
||||
|
||||
//otg_ack_vbus_transition();
|
||||
// Force Vbus interrupt in case of Vbus always with a high level
|
||||
// This is possible with a short timing between a Host mode stop/start.
|
||||
/*if (Is_otg_vbus_high()) {
|
||||
otg_raise_vbus_transition();
|
||||
}
|
||||
otg_enable_vbus_interrupt();*/
|
||||
otg_freeze_clock();
|
||||
|
||||
return 0UL ;
|
||||
}
|
||||
|
||||
void UDD_Attach(void)
|
||||
{
|
||||
irqflags_t flags = cpu_irq_save();
|
||||
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_Attach\r\n");)
|
||||
|
||||
otg_unfreeze_clock();
|
||||
|
||||
// Check USB clock because the source can be a PLL
|
||||
while (!Is_otg_clock_usable());
|
||||
|
||||
// Authorize attach if Vbus is present
|
||||
udd_attach_device();
|
||||
|
||||
// Enable USB line events
|
||||
udd_enable_reset_interrupt();
|
||||
// udd_enable_sof_interrupt();
|
||||
|
||||
cpu_irq_restore(flags);
|
||||
}
|
||||
|
||||
void UDD_Detach(void)
|
||||
{
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_Detach\r\n");)
|
||||
UOTGHS->UOTGHS_DEVCTRL |= UOTGHS_DEVCTRL_DETACH;
|
||||
}
|
||||
|
||||
void UDD_InitEP( uint32_t ul_ep_nb, uint32_t ul_ep_cfg )
|
||||
{
|
||||
ul_ep_nb = ul_ep_nb & 0xF; // EP range is 0..9, hence mask is 0xF.
|
||||
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_InitEP : init EP %lu\r\n", ul_ep_nb);)
|
||||
|
||||
// Configure EP
|
||||
UOTGHS->UOTGHS_DEVEPTCFG[ul_ep_nb] = ul_ep_cfg;
|
||||
// Enable EP
|
||||
udd_enable_endpoint(ul_ep_nb);
|
||||
|
||||
if (!Is_udd_endpoint_configured(ul_ep_nb)) {
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_InitEP : ERROR FAILED TO INIT EP %lu\r\n", ul_ep_nb);)
|
||||
while(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UDD_InitEndpoints(const uint32_t* eps_table, const uint32_t ul_eps_table_size)
|
||||
{
|
||||
uint32_t ul_ep_nb ;
|
||||
|
||||
for (ul_ep_nb = 1; ul_ep_nb < ul_eps_table_size; ul_ep_nb++)
|
||||
{
|
||||
// Configure EP
|
||||
UOTGHS->UOTGHS_DEVEPTCFG[ul_ep_nb] = eps_table[ul_ep_nb];
|
||||
// Enable EP
|
||||
udd_enable_endpoint(ul_ep_nb);
|
||||
|
||||
if (!Is_udd_endpoint_configured(ul_ep_nb)) {
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_InitEP : ERROR FAILED TO INIT EP %lu\r\n", ul_ep_nb);)
|
||||
while(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until ready to accept IN packet.
|
||||
void UDD_WaitIN(void)
|
||||
{
|
||||
while (!(UOTGHS->UOTGHS_DEVEPTISR[EP0] & UOTGHS_DEVEPTISR_TXINI))
|
||||
;
|
||||
}
|
||||
|
||||
void UDD_WaitOUT(void)
|
||||
{
|
||||
while (!(UOTGHS->UOTGHS_DEVEPTISR[EP0] & UOTGHS_DEVEPTISR_RXOUTI))
|
||||
;
|
||||
}
|
||||
|
||||
// Send packet.
|
||||
void UDD_ClearIN(void)
|
||||
{
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_ClearIN: sent %lu bytes\r\n", ul_send_fifo_ptr[EP0]);)
|
||||
|
||||
UOTGHS->UOTGHS_DEVEPTICR[EP0] = UOTGHS_DEVEPTICR_TXINIC;
|
||||
ul_send_fifo_ptr[EP0] = 0;
|
||||
}
|
||||
|
||||
void UDD_ClearOUT(void)
|
||||
{
|
||||
UOTGHS->UOTGHS_DEVEPTICR[EP0] = UOTGHS_DEVEPTICR_RXOUTIC;
|
||||
ul_recv_fifo_ptr[EP0] = 0;
|
||||
}
|
||||
|
||||
// Wait for IN FIFO to be ready to accept data or OUT FIFO to receive data.
|
||||
// Return true if new IN FIFO buffer available.
|
||||
uint32_t UDD_WaitForINOrOUT(void)
|
||||
{
|
||||
while (!(UOTGHS->UOTGHS_DEVEPTISR[EP0] & (UOTGHS_DEVEPTISR_TXINI | UOTGHS_DEVEPTISR_RXOUTI)))
|
||||
;
|
||||
return ((UOTGHS->UOTGHS_DEVEPTISR[EP0] & UOTGHS_DEVEPTISR_RXOUTI) == 0);
|
||||
}
|
||||
|
||||
uint32_t UDD_ReceivedSetupInt(void)
|
||||
{
|
||||
return UOTGHS->UOTGHS_DEVEPTISR[EP0] & UOTGHS_DEVEPTISR_RXSTPI;
|
||||
}
|
||||
|
||||
void UDD_ClearSetupInt(void)
|
||||
{
|
||||
UOTGHS->UOTGHS_DEVEPTICR[EP0] = (UOTGHS_DEVEPTICR_RXSTPIC);
|
||||
}
|
||||
|
||||
uint32_t UDD_Send(uint32_t ep, const void* data, uint32_t len)
|
||||
{
|
||||
const uint8_t *ptr_src = data;
|
||||
uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(ep);
|
||||
uint32_t i;
|
||||
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_Send (1): ep=%lu ul_send_fifo_ptr=%lu len=%lu\r\n", ep, ul_send_fifo_ptr[ep], len);)
|
||||
|
||||
while( UOTGHS_DEVEPTISR_TXINI != (UOTGHS->UOTGHS_DEVEPTISR[ep] & UOTGHS_DEVEPTISR_TXINI )) {}
|
||||
|
||||
if (ep == EP0)
|
||||
{
|
||||
if (ul_send_fifo_ptr[ep] + len > EP0_SIZE)
|
||||
len = EP0_SIZE - ul_send_fifo_ptr[ep];
|
||||
}
|
||||
else
|
||||
{
|
||||
ul_send_fifo_ptr[ep] = 0;
|
||||
}
|
||||
for (i = 0, ptr_dest += ul_send_fifo_ptr[ep]; i < len; ++i)
|
||||
*ptr_dest++ = *ptr_src++;
|
||||
|
||||
ul_send_fifo_ptr[ep] += i;
|
||||
|
||||
if (ep == EP0)
|
||||
{
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_Send (2): ep=%lu ptr_dest=%lu maxlen=%d\r\n", ep, ul_send_fifo_ptr[ep], EP0_SIZE);)
|
||||
if (ul_send_fifo_ptr[ep] == EP0_SIZE)
|
||||
{
|
||||
UDD_ClearIN(); // Fifo is full, release this packet // UOTGHS->UOTGHS_DEVEPTICR[EP0] = UOTGHS_DEVEPTICR_TXINIC;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UOTGHS->UOTGHS_DEVEPTICR[ep] = UOTGHS_DEVEPTICR_TXINIC;
|
||||
UOTGHS->UOTGHS_DEVEPTIDR[ep] = UOTGHS_DEVEPTIDR_FIFOCONC;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void UDD_Send8(uint32_t ep, uint8_t data )
|
||||
{
|
||||
uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(ep);
|
||||
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_Send8 : ul_send_fifo_ptr=%lu data=0x%x\r\n", ul_send_fifo_ptr[ep], data);)
|
||||
|
||||
ptr_dest[ul_send_fifo_ptr[ep]] = data;
|
||||
ul_send_fifo_ptr[ep] += 1;
|
||||
}
|
||||
|
||||
uint8_t UDD_Recv8(uint32_t ep)
|
||||
{
|
||||
uint8_t *ptr_dest = (uint8_t *) &udd_get_endpoint_fifo_access8(ep);
|
||||
uint8_t data = ptr_dest[ul_recv_fifo_ptr[ep]];
|
||||
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_Recv8 : ul_recv_fifo_ptr=%lu\r\n", ul_recv_fifo_ptr[ep]);)
|
||||
|
||||
ul_recv_fifo_ptr[ep] += 1;
|
||||
return data;
|
||||
}
|
||||
|
||||
void UDD_Recv(uint32_t ep, uint8_t* data, uint32_t len)
|
||||
{
|
||||
uint8_t *ptr_src = (uint8_t *) &udd_get_endpoint_fifo_access8(ep);
|
||||
uint8_t *ptr_dest = data;
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0, ptr_src += ul_recv_fifo_ptr[ep]; i < len; ++i)
|
||||
*ptr_dest++ = *ptr_src++;
|
||||
|
||||
ul_recv_fifo_ptr[ep] += i;
|
||||
}
|
||||
|
||||
void UDD_Stall(void)
|
||||
{
|
||||
UOTGHS->UOTGHS_DEVEPT = (UOTGHS_DEVEPT_EPEN0 << EP0);
|
||||
UOTGHS->UOTGHS_DEVEPTIER[EP0] = UOTGHS_DEVEPTIER_STALLRQS;
|
||||
}
|
||||
|
||||
|
||||
uint32_t UDD_FifoByteCount(uint32_t ep)
|
||||
{
|
||||
return ((UOTGHS->UOTGHS_DEVEPTISR[ep] & UOTGHS_DEVEPTISR_BYCT_Msk) >> UOTGHS_DEVEPTISR_BYCT_Pos);
|
||||
}
|
||||
|
||||
void UDD_ReleaseRX(uint32_t ep)
|
||||
{
|
||||
TRACE_UOTGHS_DEVICE(puts("=> UDD_ReleaseRX\r\n");)
|
||||
// UOTGHS->UOTGHS_DEVEPTICR[ep] = (UOTGHS_DEVEPTICR_NAKOUTIC | UOTGHS_DEVEPTICR_RXOUTIC);
|
||||
UOTGHS->UOTGHS_DEVEPTICR[ep] = UOTGHS_DEVEPTICR_RXOUTIC;
|
||||
UOTGHS->UOTGHS_DEVEPTIDR[ep] = UOTGHS_DEVEPTIDR_FIFOCONC;
|
||||
ul_recv_fifo_ptr[ep] = 0;
|
||||
}
|
||||
|
||||
void UDD_ReleaseTX(uint32_t ep)
|
||||
{
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_ReleaseTX ep=%lu\r\n", ep);)
|
||||
// UOTGHS->UOTGHS_DEVEPTICR[ep] = (UOTGHS_DEVEPTICR_NAKINIC | UOTGHS_DEVEPTICR_RXOUTIC | UOTGHS_DEVEPTICR_TXINIC);
|
||||
UOTGHS->UOTGHS_DEVEPTICR[ep] = UOTGHS_DEVEPTICR_TXINIC;
|
||||
UOTGHS->UOTGHS_DEVEPTIDR[ep] = UOTGHS_DEVEPTIDR_FIFOCONC;
|
||||
ul_send_fifo_ptr[ep] = 0;
|
||||
}
|
||||
|
||||
// Return true if the current bank is not full.
|
||||
uint32_t UDD_ReadWriteAllowed(uint32_t ep)
|
||||
{
|
||||
return (UOTGHS->UOTGHS_DEVEPTISR[ep] & UOTGHS_DEVEPTISR_RWALL);
|
||||
}
|
||||
|
||||
void UDD_SetAddress(uint32_t addr)
|
||||
{
|
||||
TRACE_UOTGHS_DEVICE(printf("=> UDD_SetAddress : setting address to %lu\r\n", addr);)
|
||||
|
||||
udd_configure_address(addr);
|
||||
udd_enable_address();
|
||||
}
|
||||
|
||||
uint32_t UDD_GetFrameNumber(void)
|
||||
{
|
||||
return udd_frame_number();
|
||||
}
|
||||
|
||||
#endif /* SAM3XA_SERIES */
|
||||
490
hardware/digistump/sam/system/libsam/source/uotghs_host.c
Normal file
490
hardware/digistump/sam/system/libsam/source/uotghs_host.c
Normal file
@@ -0,0 +1,490 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include "chip.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if SAM3XA_SERIES
|
||||
|
||||
//#define TRACE_UOTGHS_HOST(x) x
|
||||
#define TRACE_UOTGHS_HOST(x)
|
||||
|
||||
extern void (*gpf_isr)(void);
|
||||
|
||||
// Handle UOTGHS Host driver state
|
||||
static uhd_vbus_state_t uhd_state = UHD_STATE_NO_VBUS;
|
||||
|
||||
/**
|
||||
* \brief Interrupt sub routine for USB Host state machine management.
|
||||
*/
|
||||
static void UHD_ISR(void)
|
||||
{
|
||||
// Manage dis/connection event
|
||||
if (Is_uhd_disconnection() && Is_uhd_disconnection_int_enabled()) {
|
||||
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : Disconnection INT\r\n");)
|
||||
uhd_ack_disconnection();
|
||||
uhd_disable_disconnection_int();
|
||||
// Stop reset signal, in case of disconnection during reset
|
||||
uhd_stop_reset();
|
||||
// Disable wakeup/resumes interrupts,
|
||||
// in case of disconnection during suspend mode
|
||||
//UOTGHS->UOTGHS_HSTIDR = UOTGHS_HSTIDR_HWUPIEC
|
||||
// | UOTGHS_HSTIDR_RSMEDIEC
|
||||
// | UOTGHS_HSTIDR_RXRSMIEC;
|
||||
uhd_ack_connection();
|
||||
uhd_enable_connection_int();
|
||||
uhd_state = UHD_STATE_DISCONNECTED;
|
||||
return;
|
||||
}
|
||||
if (Is_uhd_connection() && Is_uhd_connection_int_enabled()) {
|
||||
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : Connection INT\r\n");)
|
||||
uhd_ack_connection();
|
||||
uhd_disable_connection_int();
|
||||
uhd_ack_disconnection();
|
||||
uhd_enable_disconnection_int();
|
||||
//uhd_enable_sof();
|
||||
uhd_state = UHD_STATE_CONNECTED;
|
||||
return;
|
||||
}
|
||||
|
||||
// Manage Vbus error
|
||||
if (Is_uhd_vbus_error_interrupt())
|
||||
{
|
||||
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS error INT\r\n");)
|
||||
uhd_ack_vbus_error_interrupt();
|
||||
uhd_state = UHD_STATE_DISCONNECTED; //UHD_STATE_ERROR;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check USB clock ready after asynchronous interrupt
|
||||
while (!Is_otg_clock_usable())
|
||||
;
|
||||
otg_unfreeze_clock();
|
||||
|
||||
// Manage Vbus state change
|
||||
if (Is_otg_vbus_transition())
|
||||
{
|
||||
otg_ack_vbus_transition();
|
||||
if (Is_otg_vbus_high())
|
||||
{
|
||||
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS transition INT : UHD_STATE_DISCONNECT\r\n");)
|
||||
uhd_state = UHD_STATE_DISCONNECTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS transition INT : UHD_STATE_NO_VBUS\r\n");)
|
||||
otg_freeze_clock();
|
||||
uhd_state = UHD_STATE_NO_VBUS;
|
||||
}
|
||||
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : VBUS transition INT : done.\r\n");)
|
||||
return;
|
||||
}
|
||||
|
||||
// Other errors
|
||||
if (Is_uhd_errors_interrupt())
|
||||
{
|
||||
TRACE_UOTGHS_HOST(printf(">>> UHD_ISR : Other error INT\r\n");)
|
||||
uhd_ack_errors_interrupt();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Set the interrupt sub routines callback for USB interrupts.
|
||||
*
|
||||
* \param pf_isr the ISR address.
|
||||
*/
|
||||
void UHD_SetStack(void (*pf_isr)(void))
|
||||
{
|
||||
gpf_isr = pf_isr;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initialize the UOTGHS host driver.
|
||||
*/
|
||||
void UHD_Init(void)
|
||||
{
|
||||
irqflags_t flags;
|
||||
|
||||
// To avoid USB interrupt before end of initialization
|
||||
flags = cpu_irq_save();
|
||||
|
||||
// Setup USB Host interrupt callback
|
||||
UHD_SetStack(&UHD_ISR);
|
||||
|
||||
// Enables the USB Clock
|
||||
pmc_enable_upll_clock();
|
||||
pmc_switch_udpck_to_upllck(0); // div=0+1
|
||||
pmc_enable_udpck();
|
||||
pmc_enable_periph_clk(ID_UOTGHS);
|
||||
|
||||
// Always authorize asynchronous USB interrupts to exit of sleep mode
|
||||
// For SAM3 USB wake up device except BACKUP mode
|
||||
NVIC_SetPriority((IRQn_Type) ID_UOTGHS, 0);
|
||||
NVIC_EnableIRQ((IRQn_Type) ID_UOTGHS);
|
||||
|
||||
// ID pin not used then force host mode
|
||||
otg_disable_id_pin();
|
||||
otg_force_host_mode();
|
||||
|
||||
// Signal is active low (because all SAM3X Pins are high after startup)
|
||||
// Hence VBOF must be low after connection request to power up the remote device
|
||||
// uhd_set_vbof_active_low();
|
||||
|
||||
// According to the Arduino Due circuit the VBOF must be active high to power up the remote device
|
||||
uhd_set_vbof_active_high();
|
||||
|
||||
otg_enable_pad();
|
||||
otg_enable();
|
||||
|
||||
otg_unfreeze_clock();
|
||||
|
||||
// Check USB clock
|
||||
while (!Is_otg_clock_usable())
|
||||
;
|
||||
|
||||
// Clear all interrupts that may have been set by a previous host mode
|
||||
UOTGHS->UOTGHS_HSTICR = UOTGHS_HSTICR_DCONNIC | UOTGHS_HSTICR_DDISCIC
|
||||
| UOTGHS_HSTICR_HSOFIC | UOTGHS_HSTICR_HWUPIC
|
||||
| UOTGHS_HSTICR_RSMEDIC | UOTGHS_HSTICR_RSTIC
|
||||
| UOTGHS_HSTICR_RXRSMIC;
|
||||
|
||||
otg_ack_vbus_transition();
|
||||
|
||||
// Enable Vbus change and error interrupts
|
||||
// Disable automatic Vbus control after Vbus error
|
||||
Set_bits(UOTGHS->UOTGHS_CTRL,
|
||||
UOTGHS_CTRL_VBUSHWC | UOTGHS_CTRL_VBUSTE | UOTGHS_CTRL_VBERRE);
|
||||
|
||||
uhd_enable_vbus();
|
||||
|
||||
// Force Vbus interrupt when Vbus is always high
|
||||
// This is possible due to a short timing between a Host mode stop/start.
|
||||
if (Is_otg_vbus_high())
|
||||
{
|
||||
otg_raise_vbus_transition();
|
||||
}
|
||||
|
||||
// Enable main control interrupt
|
||||
// Connection, SOF and reset
|
||||
UOTGHS->UOTGHS_HSTIER = UOTGHS_HSTICR_DCONNIC;
|
||||
|
||||
otg_freeze_clock();
|
||||
|
||||
uhd_state = UHD_STATE_NO_VBUS;
|
||||
|
||||
cpu_irq_restore(flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Trigger a USB bus reset.
|
||||
*/
|
||||
void UHD_BusReset(void)
|
||||
{
|
||||
uhd_start_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get VBUS state.
|
||||
*
|
||||
* \return VBUS status.
|
||||
*/
|
||||
uhd_vbus_state_t UHD_GetVBUSState(void)
|
||||
{
|
||||
return uhd_state;
|
||||
}
|
||||
|
||||
/*uhd_speed_t uhd_get_speed(void)
|
||||
{
|
||||
switch (uhd_get_speed_mode())
|
||||
{
|
||||
case UOTGHS_SR_SPEED_HIGH_SPEED:
|
||||
return UHD_SPEED_HIGH;
|
||||
|
||||
case UOTGHS_SR_SPEED_FULL_SPEED:
|
||||
return UHD_SPEED_FULL;
|
||||
|
||||
case UOTGHS_SR_SPEED_LOW_SPEED:
|
||||
return UHD_SPEED_LOW;
|
||||
|
||||
default:
|
||||
return UHD_SPEED_LOW;
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* \brief Allocate FIFO for pipe 0.
|
||||
*
|
||||
* \param ul_add Address of remote device for pipe 0.
|
||||
* \param ul_ep_size Actual size of the FIFO in bytes.
|
||||
*
|
||||
* \retval 0 success.
|
||||
* \retval 1 error.
|
||||
*/
|
||||
uint32_t UHD_Pipe0_Alloc(uint32_t ul_add, uint32_t ul_ep_size)
|
||||
{
|
||||
if (ul_ep_size < 8)
|
||||
{
|
||||
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP0_Alloc : incorrect pipe size!\r\n");)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Is_uhd_pipe_enabled(0))
|
||||
{
|
||||
// Pipe is already allocated
|
||||
return 0;
|
||||
}
|
||||
|
||||
uhd_enable_pipe(0);
|
||||
uhd_configure_pipe(0, // Pipe 0
|
||||
0, // No frequency
|
||||
0, // Enpoint 0
|
||||
UOTGHS_HSTPIPCFG_PTYPE_CTRL,
|
||||
UOTGHS_HSTPIPCFG_PTOKEN_SETUP,
|
||||
ul_ep_size,
|
||||
UOTGHS_HSTPIPCFG_PBK_1_BANK, 0);
|
||||
|
||||
uhd_allocate_memory(0);
|
||||
|
||||
if (!Is_uhd_pipe_configured(0))
|
||||
{
|
||||
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP0_Alloc : incorrect pipe settings!\r\n");)
|
||||
uhd_disable_pipe(0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uhd_configure_address(0, ul_add);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Allocate a new pipe.
|
||||
*
|
||||
* \note UOTGHS maximum pipe number is limited to 10, meaning that only a limited
|
||||
* amount of devices can be connected. Unfortunately, using only one pipe shared accross
|
||||
* various endpoints and devices is not possible because the UOTGHS IP does not allow to
|
||||
* change the data toggle value through register interface.
|
||||
*
|
||||
* \param ul_dev_addr Address of remote device.
|
||||
* \param ul_dev_ep Targeted endpoint of remote device.
|
||||
* \param ul_type Pipe type.
|
||||
* \param ul_dir Pipe direction.
|
||||
* \param ul_maxsize Pipe size.
|
||||
* \param ul_interval Polling interval (if applicable to pipe type).
|
||||
* \param ul_nb_bank Number of banks associated with this pipe.
|
||||
*
|
||||
* \return the newly allocated pipe number on success, 0 otherwise.
|
||||
*/
|
||||
uint32_t UHD_Pipe_Alloc(uint32_t ul_dev_addr, uint32_t ul_dev_ep, uint32_t ul_type, uint32_t ul_dir, uint32_t ul_maxsize, uint32_t ul_interval, uint32_t ul_nb_bank)
|
||||
{
|
||||
uint32_t ul_pipe = 1;
|
||||
|
||||
for (ul_pipe = 1; ul_pipe < UOTGHS_EPT_NUM; ++ul_pipe)
|
||||
{
|
||||
if (Is_uhd_pipe_enabled(ul_pipe))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
uhd_enable_pipe(ul_pipe);
|
||||
|
||||
uhd_configure_pipe(ul_pipe, ul_interval, ul_dev_ep, ul_type, ul_dir,
|
||||
ul_maxsize, ul_nb_bank, UOTGHS_HSTPIPCFG_AUTOSW);
|
||||
|
||||
uhd_allocate_memory(ul_pipe);
|
||||
|
||||
if (!Is_uhd_pipe_configured(ul_pipe))
|
||||
{
|
||||
uhd_disable_pipe(ul_pipe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uhd_configure_address(ul_pipe, ul_dev_addr);
|
||||
|
||||
// Pipe is configured and allocated successfully
|
||||
return ul_pipe;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free a pipe.
|
||||
*
|
||||
* \param ul_pipe Pipe number to free.
|
||||
*/
|
||||
void UHD_Pipe_Free(uint32_t ul_pipe)
|
||||
{
|
||||
// Unalloc pipe
|
||||
uhd_disable_pipe(ul_pipe);
|
||||
uhd_unallocate_memory(ul_pipe);
|
||||
uhd_reset_pipe(ul_pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Read from a pipe.
|
||||
*
|
||||
* \param ul_pipe Pipe number.
|
||||
* \param ul_size Maximum number of data to read.
|
||||
* \param data Buffer to store the data.
|
||||
*
|
||||
* \return number of data read.
|
||||
*/
|
||||
uint32_t UHD_Pipe_Read(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data)
|
||||
{
|
||||
uint8_t *ptr_ep_data = 0;
|
||||
uint8_t nb_byte_received = 0;
|
||||
uint32_t ul_nb_trans = 0;
|
||||
|
||||
// Get information to read data
|
||||
nb_byte_received = uhd_byte_count(ul_pipe);
|
||||
|
||||
ptr_ep_data = (uint8_t *) & uhd_get_pipe_fifo_access(ul_pipe, 8);
|
||||
|
||||
// Copy data from pipe to payload buffer
|
||||
while (ul_size && nb_byte_received) {
|
||||
*data++ = *ptr_ep_data++;
|
||||
ul_nb_trans++;
|
||||
ul_size--;
|
||||
nb_byte_received--;
|
||||
}
|
||||
|
||||
return ul_nb_trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Write into a pipe.
|
||||
*
|
||||
* \param ul_pipe Pipe number.
|
||||
* \param ul_size Maximum number of data to read.
|
||||
* \param data Buffer containing data to write.
|
||||
*/
|
||||
void UHD_Pipe_Write(uint32_t ul_pipe, uint32_t ul_size, uint8_t* data)
|
||||
{
|
||||
volatile uint8_t *ptr_ep_data = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
// Check pipe
|
||||
if (!Is_uhd_pipe_enabled(ul_pipe))
|
||||
{
|
||||
// Endpoint not valid
|
||||
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe is not enabled!\r\n");)
|
||||
return;
|
||||
}
|
||||
|
||||
ptr_ep_data = (volatile uint8_t *)&uhd_get_pipe_fifo_access(ul_pipe, 8);
|
||||
for (i = 0; i < ul_size; ++i)
|
||||
*ptr_ep_data++ = *data++;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Send a pipe content.
|
||||
*
|
||||
* \param ul_pipe Pipe number.
|
||||
* \param ul_token_type Token type.
|
||||
*/
|
||||
void UHD_Pipe_Send(uint32_t ul_pipe, uint32_t ul_token_type)
|
||||
{
|
||||
// Check pipe
|
||||
if (!Is_uhd_pipe_enabled(ul_pipe))
|
||||
{
|
||||
// Endpoint not valid
|
||||
TRACE_UOTGHS_HOST(printf("/!\\ UHD_EP_Send : pipe %lu is not enabled!\r\n", ul_pipe);)
|
||||
return;
|
||||
}
|
||||
|
||||
// Set token type for zero length packet
|
||||
// When actually using the FIFO, pipe token MUST be configured first
|
||||
uhd_configure_pipe_token(ul_pipe, ul_token_type);
|
||||
|
||||
// Clear interrupt flags
|
||||
uhd_ack_setup_ready(ul_pipe);
|
||||
uhd_ack_in_received(ul_pipe);
|
||||
uhd_ack_out_ready(ul_pipe);
|
||||
uhd_ack_short_packet(ul_pipe);
|
||||
uhd_ack_nak_received(ul_pipe);
|
||||
|
||||
// Send actual packet
|
||||
uhd_ack_fifocon(ul_pipe);
|
||||
uhd_unfreeze_pipe(ul_pipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Check for pipe transfer completion.
|
||||
*
|
||||
* \param ul_pipe Pipe number.
|
||||
* \param ul_token_type Token type.
|
||||
*
|
||||
* \retval 0 transfer is not complete.
|
||||
* \retval 1 transfer is complete.
|
||||
*/
|
||||
uint32_t UHD_Pipe_Is_Transfer_Complete(uint32_t ul_pipe, uint32_t ul_token_type)
|
||||
{
|
||||
// Check for transfer completion depending on token type
|
||||
switch (ul_token_type)
|
||||
{
|
||||
case UOTGHS_HSTPIPCFG_PTOKEN_SETUP:
|
||||
if (Is_uhd_setup_ready(ul_pipe))
|
||||
{
|
||||
uhd_freeze_pipe(ul_pipe);
|
||||
uhd_ack_setup_ready(ul_pipe);
|
||||
return 1;
|
||||
}
|
||||
|
||||
case UOTGHS_HSTPIPCFG_PTOKEN_IN:
|
||||
if (Is_uhd_in_received(ul_pipe))
|
||||
{
|
||||
// In case of low USB speed and with a high CPU frequency,
|
||||
// a ACK from host can be always running on USB line
|
||||
// then wait end of ACK on IN pipe.
|
||||
while(!Is_uhd_pipe_frozen(ul_pipe))
|
||||
;
|
||||
|
||||
// IN packet received
|
||||
uhd_ack_in_received(ul_pipe);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
case UOTGHS_HSTPIPCFG_PTOKEN_OUT:
|
||||
if (Is_uhd_out_ready(ul_pipe))
|
||||
{
|
||||
// OUT packet sent
|
||||
uhd_freeze_pipe(ul_pipe);
|
||||
uhd_ack_out_ready(ul_pipe);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* SAM3XA_SERIES */
|
||||
409
hardware/digistump/sam/system/libsam/source/usart.c
Normal file
409
hardware/digistump/sam/system/libsam/source/usart.c
Normal file
@@ -0,0 +1,409 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/** \addtogroup usart_module Working with USART
|
||||
* The USART driver provides the interface to configure and use the USART peripheral.\n
|
||||
*
|
||||
* The USART supports several kinds of comminication modes such as full-duplex asynchronous/
|
||||
* synchronous serial commnunication,RS485 with driver control signal,ISO7816,SPI and Test modes.
|
||||
*
|
||||
* To start a USART transfer with \ref AT91SAM3S_PDC "PDC" support, the user could follow these steps:
|
||||
* <ul>
|
||||
* <li> Configure USART with expected mode and baudrate(see \ref USART_Configure), which could be done by:
|
||||
* -# Resetting and disabling transmitter and receiver by setting US_CR(Control Register). </li>
|
||||
* -# Conifguring the USART in a specific mode by setting USART_MODE bits in US_MR(Mode Register) </li>
|
||||
* -# Setting baudrate which is different from mode to mode.
|
||||
</li>
|
||||
* <li> Enable transmitter or receiver respectively by set US_CR_TXEN or US_CR_RXEN in US_CR.</li>
|
||||
* <li> Read from or write to the peripheral with \ref USART_ReadBuffer or \ref USART_WriteBuffer.
|
||||
These operations could be done by polling or interruption. </li>
|
||||
* <li> For polling, check the status bit US_CSR_ENDRX/US_CSR_RXBUFF (READ) or US_CSR_ENDTX/
|
||||
US_CSR_TXBUFE (WRITE). </li>
|
||||
* <li> For interruption,"enable" the status bit through US_IER and
|
||||
realize the hanler with USARTx_IrqHandler according to IRQ vector
|
||||
table which is defined in board_cstartup_<toolchain>.c
|
||||
To enable the interruption of USART,it should be configured with priority and enabled first through
|
||||
NVIC .</li>
|
||||
* </ul>
|
||||
*
|
||||
* For more accurate information, please look at the USART section of the
|
||||
* Datasheet.
|
||||
*
|
||||
* Related files :\n
|
||||
* \ref usart.c\n
|
||||
* \ref usart.h\n
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of USART (Universal Synchronous Asynchronous Receiver Transmitter)
|
||||
* controller.
|
||||
*
|
||||
*/
|
||||
/*------------------------------------------------------------------------------
|
||||
* Headers
|
||||
*------------------------------------------------------------------------------*/
|
||||
#include "chip.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Local definitions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* Exported functions
|
||||
*------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Configures an USART peripheral with the specified parameters.
|
||||
*
|
||||
*
|
||||
* \param usart Pointer to the USART peripheral to configure.
|
||||
* \param mode Desired value for the USART mode register (see the datasheet).
|
||||
* \param baudrate Baudrate at which the USART should operate (in Hz).
|
||||
* \param masterClock Frequency of the system master clock (in Hz).
|
||||
*/
|
||||
void USART_Configure(Usart *usart,
|
||||
uint32_t mode,
|
||||
uint32_t baudrate,
|
||||
uint32_t masterClock)
|
||||
{
|
||||
/* Reset and disable receiver & transmitter*/
|
||||
usart->US_CR = US_CR_RSTRX | US_CR_RSTTX
|
||||
| US_CR_RXDIS | US_CR_TXDIS;
|
||||
|
||||
/* Configure mode*/
|
||||
usart->US_MR = mode;
|
||||
|
||||
/* Configure baudrate*/
|
||||
/* Asynchronous, no oversampling*/
|
||||
if ( ((mode & US_MR_SYNC) == 0) && ((mode & US_MR_OVER) == 0) )
|
||||
{
|
||||
usart->US_BRGR = (masterClock / baudrate) / 16;
|
||||
}
|
||||
|
||||
if( ((mode & US_MR_USART_MODE_SPI_MASTER) == US_MR_USART_MODE_SPI_MASTER)
|
||||
|| ((mode & US_MR_SYNC) == US_MR_SYNC))
|
||||
{
|
||||
if( (mode & US_MR_USCLKS_Msk) == US_MR_USCLKS_MCK)
|
||||
{
|
||||
usart->US_BRGR = masterClock / baudrate;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( (mode & US_MR_USCLKS_DIV) == US_MR_USCLKS_DIV)
|
||||
{
|
||||
usart->US_BRGR = masterClock / baudrate / 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* TODO other modes*/
|
||||
}
|
||||
/**
|
||||
* \brief Enables or disables the transmitter of an USART peripheral.
|
||||
*
|
||||
*
|
||||
* \param usart Pointer to an USART peripheral
|
||||
* \param enabled If true, the transmitter is enabled; otherwise it is
|
||||
* disabled.
|
||||
*/
|
||||
void USART_SetTransmitterEnabled(Usart *usart, uint8_t enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
|
||||
usart->US_CR = US_CR_TXEN;
|
||||
}
|
||||
else {
|
||||
|
||||
usart->US_CR = US_CR_TXDIS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Enables or disables the receiver of an USART peripheral
|
||||
*
|
||||
*
|
||||
* \param usart Pointer to an USART peripheral
|
||||
* \param enabled If true, the receiver is enabled; otherwise it is disabled.
|
||||
*/
|
||||
void USART_SetReceiverEnabled(Usart *usart,
|
||||
uint8_t enabled)
|
||||
{
|
||||
if (enabled) {
|
||||
|
||||
usart->US_CR = US_CR_RXEN;
|
||||
}
|
||||
else {
|
||||
|
||||
usart->US_CR = US_CR_RXDIS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends one packet of data through the specified USART peripheral. This
|
||||
* function operates synchronously, so it only returns when the data has been
|
||||
* actually sent.
|
||||
*
|
||||
*
|
||||
* \param usart Pointer to an USART peripheral.
|
||||
* \param data Data to send including 9nth bit and sync field if necessary (in
|
||||
* the same format as the US_THR register in the datasheet).
|
||||
* \param timeOut Time out value (0 = no timeout).
|
||||
*/
|
||||
void USART_Write(
|
||||
Usart *usart,
|
||||
uint16_t data,
|
||||
volatile uint32_t timeOut)
|
||||
{
|
||||
if (timeOut == 0) {
|
||||
|
||||
while ((usart->US_CSR & US_CSR_TXEMPTY) == 0);
|
||||
}
|
||||
else {
|
||||
|
||||
while ((usart->US_CSR & US_CSR_TXEMPTY) == 0) {
|
||||
|
||||
if (timeOut == 0) {
|
||||
|
||||
// TRACE_ERROR("USART_Write: Timed out.\n\r");
|
||||
return;
|
||||
}
|
||||
timeOut--;
|
||||
}
|
||||
}
|
||||
|
||||
usart->US_THR = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends the contents of a data buffer through the specified USART peripheral.
|
||||
* This function returns immediately (1 if the buffer has been queued, 0
|
||||
* otherwise); poll the ENDTX and TXBUFE bits of the USART status register
|
||||
* to check for the transfer completion.
|
||||
*
|
||||
* \param usart Pointer to an USART peripheral.
|
||||
* \param buffer Pointer to the data buffer to send.
|
||||
* \param size Size of the data buffer (in bytes).
|
||||
*/
|
||||
uint8_t USART_WriteBuffer(
|
||||
Usart *usart,
|
||||
void *buffer,
|
||||
uint32_t size)
|
||||
{
|
||||
/* Check if the first PDC bank is free*/
|
||||
if ((usart->US_TCR == 0) && (usart->US_TNCR == 0)) {
|
||||
|
||||
usart->US_TPR = (uint32_t) buffer;
|
||||
usart->US_TCR = size;
|
||||
usart->US_PTCR = US_PTCR_TXTEN;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* Check if the second PDC bank is free*/
|
||||
else if (usart->US_TNCR == 0) {
|
||||
|
||||
usart->US_TNPR = (uint32_t) buffer;
|
||||
usart->US_TNCR = size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Reads and return a packet of data on the specified USART peripheral. This
|
||||
* function operates asynchronously, so it waits until some data has been
|
||||
* received.
|
||||
*
|
||||
* \param usart Pointer to an USART peripheral.
|
||||
* \param timeOut Time out value (0 -> no timeout).
|
||||
*/
|
||||
uint16_t USART_Read(
|
||||
Usart *usart,
|
||||
volatile uint32_t timeOut)
|
||||
{
|
||||
if (timeOut == 0) {
|
||||
|
||||
while ((usart->US_CSR & US_CSR_RXRDY) == 0);
|
||||
}
|
||||
else {
|
||||
|
||||
while ((usart->US_CSR & US_CSR_RXRDY) == 0) {
|
||||
|
||||
if (timeOut == 0) {
|
||||
|
||||
// TRACE_ERROR( "USART_Read: Timed out.\n\r" ) ;
|
||||
return 0;
|
||||
}
|
||||
timeOut--;
|
||||
}
|
||||
}
|
||||
|
||||
return usart->US_RHR;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Reads data from an USART peripheral, filling the provided buffer until it
|
||||
* becomes full. This function returns immediately with 1 if the buffer has
|
||||
* been queued for transmission; otherwise 0.
|
||||
*
|
||||
* \param usart Pointer to an USART peripheral.
|
||||
* \param buffer Pointer to the buffer where the received data will be stored.
|
||||
* \param size Size of the data buffer (in bytes).
|
||||
*/
|
||||
uint8_t USART_ReadBuffer(Usart *usart,
|
||||
void *buffer,
|
||||
uint32_t size)
|
||||
{
|
||||
/* Check if the first PDC bank is free*/
|
||||
if ((usart->US_RCR == 0) && (usart->US_RNCR == 0)) {
|
||||
|
||||
usart->US_RPR = (uint32_t) buffer;
|
||||
usart->US_RCR = size;
|
||||
usart->US_PTCR = US_PTCR_RXTEN;
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* Check if the second PDC bank is free*/
|
||||
else if (usart->US_RNCR == 0) {
|
||||
|
||||
usart->US_RNPR = (uint32_t) buffer;
|
||||
usart->US_RNCR = size;
|
||||
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Returns 1 if some data has been received and can be read from an USART;
|
||||
* otherwise returns 0.
|
||||
*
|
||||
* \param usart Pointer to an Usart instance.
|
||||
*/
|
||||
uint8_t USART_IsDataAvailable(Usart *usart)
|
||||
{
|
||||
if ((usart->US_CSR & US_CSR_RXRDY) != 0) {
|
||||
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sets the filter value for the IRDA demodulator.
|
||||
*
|
||||
* \param pUsart Pointer to an Usart instance.
|
||||
* \param filter Filter value.
|
||||
*/
|
||||
void USART_SetIrdaFilter(Usart *pUsart, uint8_t filter)
|
||||
{
|
||||
assert( pUsart != NULL ) ;
|
||||
|
||||
pUsart->US_IF = filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Sends one packet of data through the specified USART peripheral. This
|
||||
* function operates synchronously, so it only returns when the data has been
|
||||
* actually sent.
|
||||
*
|
||||
* \param usart Pointer to an USART peripheral.
|
||||
* \param c Character to send
|
||||
*/
|
||||
void USART_PutChar(
|
||||
Usart *usart,
|
||||
uint8_t c)
|
||||
{
|
||||
/* Wait for the transmitter to be ready*/
|
||||
while ((usart->US_CSR & US_CSR_TXEMPTY) == 0);
|
||||
|
||||
/* Send character*/
|
||||
usart->US_THR = c;
|
||||
|
||||
/* Wait for the transfer to complete*/
|
||||
while ((usart->US_CSR & US_CSR_TXEMPTY) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Return 1 if a character can be read in USART
|
||||
*/
|
||||
uint32_t USART_IsRxReady(Usart *usart)
|
||||
{
|
||||
return (usart->US_CSR & US_CSR_RXRDY);
|
||||
}
|
||||
/**
|
||||
* \brief Get present status
|
||||
*/
|
||||
uint32_t USART_GetStatus(Usart *usart)
|
||||
{
|
||||
return usart->US_CSR;
|
||||
}
|
||||
/**
|
||||
* \brief Enable interrupt
|
||||
*/
|
||||
void USART_EnableIt(Usart *usart,uint32_t mode)
|
||||
{
|
||||
usart->US_IER = mode;
|
||||
}
|
||||
/**
|
||||
* \brief Disable interrupt
|
||||
*/
|
||||
void USART_DisableIt(Usart *usart,uint32_t mode)
|
||||
{
|
||||
usart->US_IDR = mode;
|
||||
}
|
||||
/**
|
||||
* \brief Reads and returns a character from the USART.
|
||||
*
|
||||
* \note This function is synchronous (i.e. uses polling).
|
||||
* \param usart Pointer to an USART peripheral.
|
||||
* \return Character received.
|
||||
*/
|
||||
uint8_t USART_GetChar(Usart *usart)
|
||||
{
|
||||
while ((usart->US_CSR & US_CSR_RXRDY) == 0);
|
||||
return usart->US_RHR;
|
||||
}
|
||||
132
hardware/digistump/sam/system/libsam/source/wdt.c
Normal file
132
hardware/digistump/sam/system/libsam/source/wdt.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/* ----------------------------------------------------------------------------
|
||||
* SAM Software Package License
|
||||
* ----------------------------------------------------------------------------
|
||||
* Copyright (c) 2011-2012, Atmel Corporation
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following condition is met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the disclaimer below.
|
||||
*
|
||||
* 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.
|
||||
* ----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Implementation of Watchdog Timer (WDT) controller.
|
||||
*
|
||||
*/
|
||||
|
||||
/** \addtogroup wdt_module Working with WDT
|
||||
* The WDT driver provides the interface to configure and use the WDT
|
||||
* peripheral.
|
||||
*
|
||||
* The WDT can be used to prevent system lock-up if the software becomes
|
||||
* trapped in a deadlock. It can generate a general reset or a processor
|
||||
* reset only. It is clocked by slow clock divided by 128.
|
||||
*
|
||||
* The WDT is running at reset with 16 seconds watchdog period (slow clock at 32.768 kHz)
|
||||
* and external reset generation enabled. The user must either disable it or
|
||||
* reprogram it to meet the application requires.
|
||||
*
|
||||
* To use the WDT, the user could follow these few steps:
|
||||
* <ul>
|
||||
* <li>Enable watchdog with given mode using \ref WDT_Enable().
|
||||
* <li>Restart the watchdog using \ref WDT_Restart() within the watchdog period.
|
||||
* </ul>
|
||||
*
|
||||
* For more accurate information, please look at the WDT section of the
|
||||
* Datasheet.
|
||||
*
|
||||
* \note
|
||||
* The Watchdog Mode Register (WDT_MR) can be written only once.\n
|
||||
*
|
||||
* Related files :\n
|
||||
* \ref wdt.c\n
|
||||
* \ref wdt.h.\n
|
||||
*/
|
||||
/*@{*/
|
||||
/*@}*/
|
||||
|
||||
/*---------------------------------------------------------------------------
|
||||
* Headers
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "chip.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Exported functions
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* \brief Enable watchdog with given mode.
|
||||
*
|
||||
* \note The Watchdog Mode Register (WDT_MR) can be written only once.
|
||||
* Only a processor reset resets it.
|
||||
*
|
||||
* \param dwMode WDT mode to be set
|
||||
*/
|
||||
extern void WDT_Enable( Wdt* pWDT, uint32_t dwMode )
|
||||
{
|
||||
pWDT->WDT_MR = dwMode ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Disable watchdog.
|
||||
*
|
||||
* \note The Watchdog Mode Register (WDT_MR) can be written only once.
|
||||
* Only a processor reset resets it.
|
||||
*/
|
||||
extern void WDT_Disable( Wdt* pWDT )
|
||||
{
|
||||
pWDT->WDT_MR = WDT_MR_WDDIS;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Watchdog restart.
|
||||
*/
|
||||
extern void WDT_Restart( Wdt* pWDT )
|
||||
{
|
||||
pWDT->WDT_CR = 0xA5000001;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Watchdog get status.
|
||||
*/
|
||||
extern uint32_t WDT_GetStatus( Wdt* pWDT )
|
||||
{
|
||||
return (pWDT->WDT_SR & 0x3) ;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Watchdog get period.
|
||||
*
|
||||
* \param dwMs desired watchdog period in millisecond.
|
||||
*/
|
||||
extern uint32_t WDT_GetPeriod( uint32_t dwMs )
|
||||
{
|
||||
if ( (dwMs < 4) || (dwMs > 16000) )
|
||||
{
|
||||
return 0 ;
|
||||
}
|
||||
return ((dwMs << 8) / 1000) ;
|
||||
}
|
||||
Reference in New Issue
Block a user