mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-04 15:24:12 +01:00
836 lines
21 KiB
C
836 lines
21 KiB
C
|
/**
|
||
|
* \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
|