1
0
mirror of https://github.com/arduino/Arduino.git synced 2024-12-02 13:24:12 +01:00
Arduino/hardware/arduino/sam/system/libsam/source/ssc.c
2012-12-19 15:20:14 +01:00

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