├── HiFi.cpp ├── HiFi.h ├── README.md ├── examples ├── Passthrough │ └── Passthrough.ino └── SineWaveOut │ ├── SineWaveOut.ino │ └── sine.c ├── keywords.txt ├── ssc.c └── ssc.h /HiFi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | HiFi.cpp 3 | 4 | This library implements a driver for the SSC peripheral on the Arduino 5 | DUE board (ARM Cortex-M3 based). This can be used to interface with external 6 | high quality ADCs, DACs, or CODECs instead of the limited bit-depth 7 | converters that are on the SAM3X. 8 | 9 | Currently, only I2S mode is supported, although the SSC peripheral can 10 | support left-justified, right justified and TDM (>2 channels) modes. 11 | These modes could be added, but were left out for simplicity. Most codecs 12 | support I2S mode and it doesn't really have many options which reduces 13 | overall complexity. This seems like a good compromise for the Arduino 14 | platform. 15 | 16 | You can configure the library to transmit, receive, or both, but only in slave 17 | mode. The SAM3X doesn't appear to have the capability of driving an MCLK 18 | signal that is synchronous with the TK/RK and TF/RF signals, which is needed 19 | by many of the audio converters. Additionally, it doesn't seem to be able to 20 | generate clocks that are multiples of standard audio sampling rates (44.1k, 21 | 48k, etc.). It's simpler to use an external clock master and just set the 22 | SSC to slave to that. That allows the playback/recording of wave files that 23 | can be generated/rendered on other systems easily. 24 | 25 | For transmit and receive, there are 2 clock signals needed - frame clock and 26 | bit clock. As mentioned above, these are inputs to the SSC. If both 27 | transmit and receive are desired, one side can be synchronized to the other 28 | to reduce the overall pin count (i.e. don't need separate receive and 29 | transmit clocks). Although the SSC does support independent transmit and 30 | receive rates if needed (e.g. separate input and output sample rates). 31 | 32 | To use this library, place the files in a folder called 'HiFi' under the 33 | libraries directory in your sketches folder. 34 | 35 | This library is free software; you can redistribute it and/or 36 | modify it under the terms of the GNU Lesser General Public 37 | License as published by the Free Software Foundation; either 38 | version 2.1 of the License, or (at your option) any later version. 39 | 40 | This library is distributed in the hope that it will be useful, 41 | but WITHOUT ANY WARRANTY; without even the implied warranty of 42 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 43 | See the GNU Lesser General Public License for more details. 44 | 45 | You should have received a copy of the GNU Lesser General Public 46 | License along with this library; if not, write to the Free Software 47 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 48 | */ 49 | 50 | #include "HiFi.h" 51 | 52 | // Make sure that data is first pin in the list. 53 | const PinDescription SSCTXPins[]= 54 | { 55 | { PIOA, PIO_PA16B_TD, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT, PIN_ATTR_DIGITAL, NO_ADC, NO_ADC, NOT_ON_PWM, NOT_ON_TIMER }, // A0 56 | { PIOA, PIO_PA15B_TF, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT, PIN_ATTR_DIGITAL, NO_ADC, NO_ADC, NOT_ON_PWM, NOT_ON_TIMER }, // PIN 24 57 | { PIOA, PIO_PA14B_TK, ID_PIOA, PIO_PERIPH_B, PIO_DEFAULT, PIN_ATTR_DIGITAL, NO_ADC, NO_ADC, NOT_ON_PWM, NOT_ON_TIMER }, // PIN 23 58 | }; 59 | 60 | // Make sure that data is first pin in the list. 61 | const PinDescription SSCRXPins[]= 62 | { 63 | { PIOB, PIO_PB18A_RD, ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT, PIN_ATTR_DIGITAL, NO_ADC, NO_ADC, NOT_ON_PWM, NOT_ON_TIMER }, // A9 64 | { PIOB, PIO_PB17A_RF, ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT, PIN_ATTR_DIGITAL, NO_ADC, NO_ADC, NOT_ON_PWM, NOT_ON_TIMER }, // A8 65 | { PIOB, PIO_PB19A_RK, ID_PIOB, PIO_PERIPH_A, PIO_DEFAULT, PIN_ATTR_DIGITAL, NO_ADC, NO_ADC, NOT_ON_PWM, NOT_ON_TIMER }, // A10 66 | }; 67 | 68 | void HiFiClass::begin(void) 69 | { 70 | // Enable module 71 | pmc_enable_periph_clk(ID_SSC); 72 | ssc_reset(SSC); 73 | 74 | _dataOutAddr = (uint32_t *)ssc_get_tx_access(SSC); 75 | _dataInAddr = (uint32_t *)ssc_get_rx_access(SSC); 76 | 77 | // Enable SSC interrupt line from the core 78 | NVIC_DisableIRQ(SSC_IRQn); 79 | NVIC_ClearPendingIRQ(SSC_IRQn); 80 | NVIC_SetPriority(SSC_IRQn, 0); // most arduino interrupts are set to priority 0. 81 | NVIC_EnableIRQ(SSC_IRQn); 82 | } 83 | 84 | void HiFiClass::configureTx( HiFiAudioMode_t audioMode, 85 | HiFiClockMode_t clkMode, 86 | uint8_t bitsPerChannel ) 87 | { 88 | clock_opt_t tx_clk_option; 89 | data_frame_opt_t tx_data_frame_option; 90 | 91 | memset((uint8_t *)&tx_clk_option, 0, sizeof(clock_opt_t)); 92 | memset((uint8_t *)&tx_data_frame_option, 0, sizeof(data_frame_opt_t)); 93 | 94 | /////////////////////////////////////////////////////////////////////////// 95 | /// Transmitter IO Pin configuration 96 | /////////////////////////////////////////////////////////////////////////// 97 | uint8_t endpin = sizeof(SSCTXPins)/sizeof(SSCTXPins[0]); 98 | if (clkMode == HIFI_CLK_MODE_USE_TK_RK_CLK) 99 | { 100 | endpin = 1; 101 | } 102 | 103 | for (int i=0; i < endpin; i++) 104 | { 105 | PIO_Configure(SSCTXPins[i].pPort, 106 | SSCTXPins[i].ulPinType, 107 | SSCTXPins[i].ulPin, 108 | SSCTXPins[i].ulPinConfiguration); 109 | } 110 | 111 | // Note: there is a function in the Atmel ssc driver for configuration of 112 | // the peripheral in I2S mode, but it is incomplete and buggy. This library 113 | // will configure the SSC directly which will also shed some light on the 114 | // various configuration parameters should a user need something slightly 115 | // different. 116 | 117 | /////////////////////////////////////////////////////////////////////////// 118 | /// Transmitter clock mode configuration 119 | /////////////////////////////////////////////////////////////////////////// 120 | switch (clkMode) 121 | { 122 | case HIFI_CLK_MODE_USE_EXT_CLKS: 123 | // Use clocks on the TK/TF pins 124 | // Despite what this looks like, it actually means use the TK clock 125 | // pin. There is both an error in the documentation and the macro 126 | // definition supplied by Atmel in the ssc.h file. The document author 127 | // just cut and paste and the Atmel engineer decided to keep the 128 | // macro consistent with the data sheet (or didn't pay attention...). 129 | // I imagine that this will get fixed at some point, so this may need 130 | // to be revisited. 131 | tx_clk_option.ul_cks = SSC_TCMR_CKS_RK; 132 | 133 | if (audioMode == HIFI_AUDIO_MODE_MONO_RIGHT) 134 | { 135 | // high level on the frame clock is right channel in 136 | // I2S. If we're only using the right channel, then 137 | // we can set start condition for right-only 138 | tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_RISING; 139 | } 140 | else 141 | { 142 | // stereo or mono-left will start in the left channel slot 143 | tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_FALLING; 144 | } 145 | break; 146 | 147 | case HIFI_CLK_MODE_USE_TK_RK_CLK: 148 | // Despite what this looks like, it actually means use the RK clock 149 | // pin. There is both an error in the documentation and the macro 150 | // definition supplied by Atmel in the ssc.h file. The document author 151 | // just cut and paste and the Atmel engineer decided to keep the 152 | // macro consistent with the data sheet (or didn't pay attention...). 153 | // I imagine that this will get fixed at some point, so this may need 154 | // to be revisited. See the external clock case above. 155 | tx_clk_option.ul_cks = SSC_TCMR_CKS_TK; 156 | 157 | // Use the receiver's configuration as the start trigger for 158 | // transmit. The receiver must be configured and running in 159 | // order for this to work. 160 | tx_clk_option.ul_start_sel = SSC_TCMR_START_RECEIVE; 161 | break; 162 | } 163 | 164 | // No output clocks 165 | tx_clk_option.ul_ckg = SSC_TCMR_CKG_NONE; 166 | tx_clk_option.ul_period = 0; // we're not master -- set to 0 167 | tx_clk_option.ul_cko = SSC_TCMR_CKO_NONE; 168 | tx_clk_option.ul_cki = 0; 169 | // I2S has a one bit delay on the data. 170 | tx_clk_option.ul_sttdly = 1; 171 | 172 | /////////////////////////////////////////////////////////////////////////// 173 | /// Transmitter frame mode configuration. 174 | /////////////////////////////////////////////////////////////////////////// 175 | tx_data_frame_option.ul_datlen = bitsPerChannel - 1; 176 | tx_data_frame_option.ul_msbf = SSC_TFMR_MSBF; 177 | 178 | // number of channels 179 | if (audioMode == HIFI_AUDIO_MODE_STEREO) 180 | { 181 | tx_data_frame_option.ul_datnb = 1; 182 | } 183 | else 184 | { 185 | tx_data_frame_option.ul_datnb = 0; 186 | } 187 | 188 | // No frame clock output 189 | tx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NONE; 190 | 191 | /////////////////////////////////////////////////////////////////////////// 192 | /// Load configuration and enable TX interrupt 193 | /////////////////////////////////////////////////////////////////////////// 194 | ssc_set_transmitter(SSC, &tx_clk_option, &tx_data_frame_option); 195 | ssc_enable_interrupt(SSC, SSC_IER_TXRDY); 196 | } 197 | 198 | void HiFiClass::enableTx(bool enable) 199 | { 200 | if (enable) 201 | { 202 | ssc_enable_tx(SSC); 203 | } 204 | else 205 | { 206 | ssc_disable_tx(SSC); 207 | } 208 | } 209 | 210 | void HiFiClass::onTxReady(void(*function)(HiFiChannelID_t)) { 211 | onTxReadyCallback = function; 212 | } 213 | 214 | void HiFiClass::configureRx( HiFiAudioMode_t audioMode, 215 | HiFiClockMode_t clkMode, 216 | uint8_t bitsPerChannel ) 217 | { 218 | clock_opt_t rx_clk_option; 219 | data_frame_opt_t rx_data_frame_option; 220 | 221 | memset((uint8_t *)&rx_clk_option, 0, sizeof(clock_opt_t)); 222 | memset((uint8_t *)&rx_data_frame_option, 0, sizeof(data_frame_opt_t)); 223 | 224 | /////////////////////////////////////////////////////////////////////////// 225 | /// Receiver IO Pin configuration 226 | /////////////////////////////////////////////////////////////////////////// 227 | uint8_t endpin = sizeof(SSCRXPins)/sizeof(SSCRXPins[0]); 228 | if (clkMode == HIFI_CLK_MODE_USE_TK_RK_CLK) 229 | { 230 | endpin = 1; 231 | } 232 | 233 | for (int i=0; i < (sizeof(SSCRXPins)/sizeof(SSCRXPins[0])); i++) 234 | { 235 | PIO_Configure(SSCRXPins[i].pPort, 236 | SSCRXPins[i].ulPinType, 237 | SSCRXPins[i].ulPin, 238 | SSCRXPins[i].ulPinConfiguration); 239 | } 240 | 241 | // Note: there is a function in the Atmel ssc driver for configuration of 242 | // the peripheral in I2S mode, but it is incomplete and buggy. This library 243 | // will configure the SSC directly which will also shed some light on the 244 | // various configuration parameters should a user need something slightly 245 | // different. 246 | 247 | /////////////////////////////////////////////////////////////////////////// 248 | /// Receiver clock mode configuration 249 | /////////////////////////////////////////////////////////////////////////// 250 | switch (clkMode) 251 | { 252 | case HIFI_CLK_MODE_USE_EXT_CLKS: 253 | // Use clocks on the RK/RF pins 254 | rx_clk_option.ul_cks = SSC_RCMR_CKS_RK; 255 | 256 | if (audioMode == HIFI_AUDIO_MODE_MONO_RIGHT) 257 | { 258 | // high level on the frame clock is right channel in 259 | // I2S. If we're only using the right channel, then 260 | // we can set start condition for right-only 261 | rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_RISING; 262 | } 263 | else 264 | { 265 | // stereo or mono-left will start in the left channel slot 266 | rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_FALLING; 267 | } 268 | break; 269 | 270 | case HIFI_CLK_MODE_USE_TK_RK_CLK: 271 | // Use the clock selected by the transmitter config 272 | // (i.e. sync receiver to transmitter). 273 | rx_clk_option.ul_cks = SSC_RCMR_CKS_TK; 274 | 275 | // Use the transmitter's configuration as the start trigger 276 | // The transmitter must be configured and running in 277 | // order for this to work. 278 | rx_clk_option.ul_start_sel = SSC_RCMR_START_TRANSMIT; 279 | break; 280 | } 281 | 282 | // No output clocks 283 | rx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE; 284 | rx_clk_option.ul_period = 0; // we're not master -- set to 0 285 | rx_clk_option.ul_cko = SSC_RCMR_CKO_NONE; 286 | // I2S latches data on the rising edge of the clock. 287 | rx_clk_option.ul_cki = SSC_RCMR_CKI; 288 | // I2S has a one bit delay on the data. 289 | rx_clk_option.ul_sttdly = 1; 290 | 291 | /////////////////////////////////////////////////////////////////////////// 292 | /// Receiver frame mode configuration. 293 | /////////////////////////////////////////////////////////////////////////// 294 | rx_data_frame_option.ul_datlen = bitsPerChannel - 1; 295 | rx_data_frame_option.ul_msbf = SSC_RFMR_MSBF; 296 | 297 | // number of channels 298 | if (audioMode == HIFI_AUDIO_MODE_STEREO) 299 | { 300 | rx_data_frame_option.ul_datnb = 1; 301 | } 302 | else 303 | { 304 | rx_data_frame_option.ul_datnb = 0; 305 | } 306 | 307 | // No frame clock output 308 | rx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NONE; 309 | 310 | /////////////////////////////////////////////////////////////////////////// 311 | /// Load configuration and enable RX interrupt 312 | /////////////////////////////////////////////////////////////////////////// 313 | ssc_set_receiver(SSC, &rx_clk_option, &rx_data_frame_option); 314 | ssc_enable_interrupt(SSC, SSC_IER_RXRDY); 315 | } 316 | 317 | void HiFiClass::enableRx(bool enable) 318 | { 319 | if (enable) 320 | { 321 | ssc_enable_rx(SSC); 322 | } 323 | else 324 | { 325 | ssc_disable_rx(SSC); 326 | } 327 | } 328 | 329 | void HiFiClass::onRxReady(void(*function)(HiFiChannelID_t)) { 330 | onRxReadyCallback = function; 331 | } 332 | 333 | void HiFiClass::onService(void) 334 | { 335 | HiFiChannelID_t channel; 336 | 337 | // read and save status -- some bits are cleared on a read 338 | uint32_t status = ssc_get_status(SSC); 339 | 340 | if (ssc_is_tx_ready(SSC) == SSC_RC_YES) 341 | { 342 | if (HiFi.onTxReadyCallback) 343 | { 344 | // The TXSYN event is triggered based on what the start 345 | // condition was set to during configuration. This 346 | // is usually the left channel, except in the case of the 347 | // mono right setup, in which case it's the right. This 348 | // may need to change if support for other formats 349 | // (e.g. TDM) is added. 350 | if (status & SSC_IER_TXSYN) 351 | { 352 | channel = HIFI_CHANNEL_ID_1; 353 | } 354 | else 355 | { 356 | channel = HIFI_CHANNEL_ID_2; 357 | } 358 | HiFi.onTxReadyCallback(channel); 359 | } 360 | } 361 | 362 | if (ssc_is_rx_ready(SSC) == SSC_RC_YES) 363 | { 364 | if (HiFi.onRxReadyCallback) 365 | { 366 | // The RXSYN event is triggered based on what the start 367 | // condition was set to during configuration. This 368 | // is usually the left channel, except in the case of the 369 | // mono right setup, in which case it's the right. This 370 | // may need to change if support for other formats 371 | // (e.g. TDM) is added. 372 | if (status & SSC_IER_RXSYN) 373 | { 374 | channel = HIFI_CHANNEL_ID_1; 375 | } 376 | else 377 | { 378 | channel = HIFI_CHANNEL_ID_2; 379 | } 380 | HiFi.onRxReadyCallback(channel); 381 | } 382 | } 383 | } 384 | 385 | /** 386 | * \brief Synchronous Serial Controller Handler. 387 | * 388 | */ 389 | void SSC_Handler(void) 390 | { 391 | HiFi.onService(); 392 | } 393 | 394 | // Create our object 395 | HiFiClass HiFi = HiFiClass(); 396 | 397 | -------------------------------------------------------------------------------- /HiFi.h: -------------------------------------------------------------------------------- 1 | /* 2 | Parallel.h 3 | 4 | This library implements a limited functionality parallel port on the Arduino 5 | DUE board (ARM Cortex-M3 based). 6 | 7 | The DUE board pins out the data bus on the extended digital headers along 8 | with the control signals NC1 and NWR. Some of the address signals are 9 | connected to the PWM pins (A0-A5), but a full address bus is unavailable. 10 | There is also conflict between the SS1 pin for SPI and the NRD signal used 11 | for the parallel bus. In short the hardware wasn't designed for use with 12 | external parallel memories. 13 | 14 | The library does allow connection to some of the lower resolution LCD 15 | controllers that used index addressing and can speed up read/write times 16 | considerably. 17 | 18 | To use this library, place the files in a folder called 'Parallel' under the 19 | libraries directory in your sketches folder. 20 | 21 | This library is free software; you can redistribute it and/or 22 | modify it under the terms of the GNU Lesser General Public 23 | License as published by the Free Software Foundation; either 24 | version 2.1 of the License, or (at your option) any later version. 25 | 26 | This library is distributed in the hope that it will be useful, 27 | but WITHOUT ANY WARRANTY; without even the implied warranty of 28 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 29 | See the GNU Lesser General Public License for more details. 30 | 31 | You should have received a copy of the GNU Lesser General Public 32 | License along with this library; if not, write to the Free Software 33 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 34 | */ 35 | 36 | #ifndef HIFI_H 37 | #define HIFI_H 38 | 39 | #include "Arduino.h" 40 | #include "ssc.h" 41 | 42 | typedef enum 43 | { 44 | HIFI_AUDIO_MODE_MONO_LEFT, 45 | HIFI_AUDIO_MODE_MONO_RIGHT, 46 | HIFI_AUDIO_MODE_STEREO 47 | } HiFiAudioMode_t; 48 | 49 | typedef enum 50 | { 51 | HIFI_CLK_MODE_USE_EXT_CLKS, 52 | HIFI_CLK_MODE_USE_TK_RK_CLK 53 | } HiFiClockMode_t; 54 | 55 | typedef enum 56 | { 57 | // in MONO modes, channel 1 will be the only one used. 58 | // In STEREO modes, channel 1 is left, channel 2 is right 59 | HIFI_CHANNEL_ID_1, 60 | HIFI_CHANNEL_ID_2 61 | } HiFiChannelID_t; 62 | 63 | class HiFiClass { 64 | public: 65 | HiFiClass() { }; 66 | void begin(); 67 | 68 | void configureTx(HiFiAudioMode_t busMode, 69 | HiFiClockMode_t clkMode, 70 | uint8_t bitsPerChannel ); 71 | void enableTx(bool enable); 72 | void onTxReady(void(*)(HiFiChannelID_t)); 73 | 74 | void configureRx(HiFiAudioMode_t busMode, 75 | HiFiClockMode_t clkMode, 76 | uint8_t bitsPerChannel ); 77 | void enableRx(bool enable); 78 | void onRxReady(void(*)(HiFiChannelID_t)); 79 | 80 | void write(uint32_t value) 81 | { 82 | *(_dataOutAddr) = value; 83 | } 84 | 85 | uint32_t read() 86 | { 87 | return *(_dataInAddr); 88 | } 89 | 90 | // Interrupt handler function 91 | void onService(void); 92 | 93 | private: 94 | uint32_t *_dataOutAddr; 95 | uint32_t *_dataInAddr; 96 | 97 | // Callback user functions 98 | void (*onTxReadyCallback)(HiFiChannelID_t channel); 99 | void (*onRxReadyCallback)(HiFiChannelID_t channel); 100 | }; 101 | 102 | extern HiFiClass HiFi; 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ArduinoDueHiFi 2 | ============== 3 | 4 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/235294e9ee4c42ce863f7b10c74c79c8)](https://app.codacy.com/app/delsauce/ArduinoDueHiFi?utm_source=github.com&utm_medium=referral&utm_content=delsauce/ArduinoDueHiFi&utm_campaign=Badge_Grade_Settings) 5 | 6 | An I2S audio codec driver library for the Arduino Due board. 7 | 8 | This library will configure the SSC on the ARM to transmit and/or 9 | receive in I2S mode. This will allow the Arduino to interface with 10 | a large number of audio codecs and enable higher quality audio I/O 11 | than what is available with the standard on-chip ADC and DAC. The 12 | library can be used to enable CD quality audio signal processing, 13 | waveform synthesis, audio file (e.g. wav) recording and playback, etc. 14 | 15 | The driver currently supports slave mode only as the ARM doesn't 16 | appear to be able to generate the appropriate MCLK signal to drive 17 | external converters. It may be desirable to record and playback 18 | files and transfer them to another device (e.g. PC) for further use 19 | so the clocks can be supplied by the converter to get to a standard 20 | sampling frequency (e.g. 32kHz, 44.1kHz, 48kHz, etc.). 21 | 22 | Although the SSC peripheral suppors many modes (Left-justified, I2S, 23 | TDM) only I2S is supported out-of-the box to keep the driver simple 24 | and easier to understand. Most audio converters support this protocol. 25 | 26 | A couple of simple examples are provided that demonstrate usage of the 27 | library. 28 | -------------------------------------------------------------------------------- /examples/Passthrough/Passthrough.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example uses the HiFi library to connect with a Cirrus CS4271 audio 3 | codec. The codec has been configured to generate all the clocks and the 4 | Arduino will sync to them. The codec is using I2S mode to communicate 5 | with the SSC peripheral. 6 | 7 | The sketch takes any data is receives from the codec and sends it right 8 | back out. 9 | 10 | This sketch is free software; you can redistribute it and/or 11 | modify it under the terms of the GNU Lesser General Public 12 | License as published by the Free Software Foundation; either 13 | version 2.1 of the License, or (at your option) any later version. 14 | 15 | This library is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 18 | See the GNU Lesser General Public License for more details. 19 | 20 | You should have received a copy of the GNU Lesser General Public 21 | License along with this library; if not, write to the Free Software 22 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 23 | */ 24 | 25 | #include 26 | 27 | void codecTxReadyInterrupt(HiFiChannelID_t); 28 | void codecRxReadyInterrupt(HiFiChannelID_t); 29 | 30 | static uint32_t ldat = 0; 31 | static uint32_t rdat = 0; 32 | 33 | void setup() { 34 | 35 | // set codec into reset 36 | pinMode(7, OUTPUT); 37 | digitalWrite(7, LOW); 38 | 39 | HiFi.begin(); 40 | 41 | // Configure transmitter for 2 channels, external TK/TF clocks, 32 bit per 42 | // channel (data less than 32-bit is left justified in the 32 bit word, but 43 | // codec config needs 32 clocks per channel). 44 | HiFi.configureTx(HIFI_AUDIO_MODE_STEREO, HIFI_CLK_MODE_USE_EXT_CLKS, 32); 45 | 46 | // Same config as above, except sync the receiver to transmitter (RK/RF 47 | // clock signals not needed) 48 | HiFi.configureRx(HIFI_AUDIO_MODE_STEREO, HIFI_CLK_MODE_USE_TK_RK_CLK, 32); 49 | 50 | // Since we've decided to sync the receiver to the transmitter, we could 51 | // handle both reading and writing in a single interrupt (receive or 52 | // transmit). This example uses both just for demonstration purposes. 53 | HiFi.onTxReady(codecTxReadyInterrupt); 54 | HiFi.onRxReady(codecRxReadyInterrupt); 55 | 56 | // release codec from reset 57 | digitalWrite(7, HIGH); 58 | 59 | // Enable both receiver and transmitter. 60 | HiFi.enableRx(true); 61 | HiFi.enableTx(true); 62 | } 63 | 64 | void loop() { 65 | } 66 | 67 | void codecTxReadyInterrupt(HiFiChannelID_t channel) 68 | { 69 | if (channel == HIFI_CHANNEL_ID_1) 70 | { 71 | // Left channel 72 | HiFi.write(ldat); 73 | } 74 | else 75 | { 76 | // Right channel 77 | HiFi.write(rdat); 78 | } 79 | } 80 | 81 | void codecRxReadyInterrupt(HiFiChannelID_t channel) 82 | { 83 | if (channel == HIFI_CHANNEL_ID_1) 84 | { 85 | // Left channel 86 | ldat = HiFi.read(); 87 | } 88 | else 89 | { 90 | // Right channel 91 | rdat = HiFi.read(); 92 | } 93 | } 94 | 95 | -------------------------------------------------------------------------------- /examples/SineWaveOut/SineWaveOut.ino: -------------------------------------------------------------------------------- 1 | /* 2 | This example implements a very simple sine wave generator using 3 | the HiFi library to output audio to a codec or DAC. The HiFi 4 | library uses the SSC peripheral in I2S mode to communicated with 5 | external converters. 6 | 7 | This sketch is free software; you can redistribute it and/or 8 | modify it under the terms of the GNU Lesser General Public 9 | License as published by the Free Software Foundation; either 10 | version 2.1 of the License, or (at your option) any later version. 11 | 12 | This library is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | See the GNU Lesser General Public License for more details. 16 | 17 | You should have received a copy of the GNU Lesser General Public 18 | License along with this library; if not, write to the Free Software 19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 | */ 21 | 22 | #include 23 | 24 | extern const int16_t sine[512]; 25 | 26 | uint16_t sineTblPtr; 27 | int16_t sign; 28 | 29 | void setup() { 30 | 31 | sineTblPtr = 0; 32 | sign = 1; 33 | 34 | // set codec into reset 35 | pinMode(7, OUTPUT); 36 | digitalWrite(7, LOW); 37 | 38 | HiFi.begin(); 39 | 40 | // Configure transmitter for 2 channels, external TK/TF clocks, 32 bit per 41 | // channel (data less than 32-bit is left justified in the 32 bit word, but 42 | // codec config needs 32 clocks per channel). 43 | HiFi.configureTx(HIFI_AUDIO_MODE_STEREO, HIFI_CLK_MODE_USE_EXT_CLKS, 32); 44 | 45 | // Register to be notified when the transmit register is ready for more data 46 | HiFi.onTxReady(codecTxReadyInterrupt); 47 | 48 | // release codec from reset 49 | digitalWrite(7, HIGH); 50 | 51 | // Enable both receiver and transmitter. 52 | HiFi.enableTx(true); 53 | } 54 | 55 | void loop() { 56 | } 57 | 58 | void codecTxReadyInterrupt(HiFiChannelID_t channel) 59 | { 60 | HiFi.write((sign * sine[sineTblPtr])<<16); 61 | 62 | if (channel == HIFI_CHANNEL_ID_2) 63 | { 64 | if (sineTblPtr == ((sizeof(sine)/sizeof(sine[0])) - 1)) 65 | { 66 | sign *= -1; 67 | sineTblPtr=0; 68 | } 69 | else 70 | { 71 | sineTblPtr++; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /examples/SineWaveOut/sine.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sine.c 3 | * 4 | * Created: 3/26/2013 1:06:37 PM 5 | * Author: mike 6 | */ 7 | 8 | #include 9 | 10 | const int16_t sine[512] = 11 | { 12 | 0,201,402,603,804,1005,1206,1407, 13 | 1608,1809,2009,2210,2410,2611,2811,3012, 14 | 3212,3412,3612,3811,4011,4210,4410,4609, 15 | 4808,5007,5205,5404,5602,5800,5998,6195, 16 | 6393,6590,6786,6983,7179,7375,7571,7767, 17 | 7962,8157,8351,8545,8739,8933,9126,9319, 18 | 9512,9704,9896,10087,10278,10469,10659,10849, 19 | 11039,11228,11417,11605,11793,11980,12167,12353, 20 | 12539,12725,12910,13094,13279,13462,13645,13828, 21 | 14010,14191,14372,14553,14732,14912,15090,15269, 22 | 15446,15623,15800,15976,16151,16325,16499,16673, 23 | 16846,17018,17189,17360,17530,17700,17869,18037, 24 | 18204,18371,18537,18703,18868,19032,19195,19357, 25 | 19519,19680,19841,20000,20159,20317,20475,20631, 26 | 20787,20942,21096,21250,21403,21554,21705,21856, 27 | 22005,22154,22301,22448,22594,22739,22884,23027, 28 | 23170,23311,23452,23592,23731,23870,24007,24143, 29 | 24279,24413,24547,24680,24811,24942,25072,25201, 30 | 25329,25456,25582,25708,25832,25955,26077,26198, 31 | 26319,26438,26556,26674,26790,26905,27019,27133, 32 | 27245,27356,27466,27575,27683,27790,27896,28001, 33 | 28105,28208,28310,28411,28510,28609,28706,28803, 34 | 28898,28992,29085,29177,29268,29358,29447,29534, 35 | 29621,29706,29791,29874,29956,30037,30117,30195, 36 | 30273,30349,30424,30498,30571,30643,30714,30783, 37 | 30852,30919,30985,31050,31113,31176,31237,31297, 38 | 31356,31414,31470,31526,31580,31633,31685,31736, 39 | 31785,31833,31880,31926,31971,32014,32057,32098, 40 | 32137,32176,32213,32250,32285,32318,32351,32382, 41 | 32412,32441,32469,32495,32521,32545,32567,32589, 42 | 32609,32628,32646,32663,32678,32692,32705,32717, 43 | 32728,32737,32745,32752,32757,32761,32765,32766, 44 | 45 | 32767,32766,32765,32761,32757,32752,32745,32737, 46 | 32728,32717,32705,32692,32678,32663,32646,32628, 47 | 32609,32589,32567,32545,32521,32495,32469,32441, 48 | 32412,32382,32351,32318,32285,32250,32213,32176, 49 | 32137,32098,32057,32014,31971,31926,31880,31833, 50 | 31785,31736,31685,31633,31580,31526,31470,31414, 51 | 31356,31297,31237,31176,31113,31050,30985,30919, 52 | 30852,30783,30714,30643,30571,30498,30424,30349, 53 | 30273,30195,30117,30037,29956,29874,29791,29706, 54 | 29621,29534,29447,29358,29268,29177,29085,28992, 55 | 28898,28803,28706,28609,28510,28411,28310,28208, 56 | 28105,28001,27896,27790,27683,27575,27466,27356, 57 | 27245,27133,27019,26905,26790,26674,26556,26438, 58 | 26319,26198,26077,25955,25832,25708,25582,25456, 59 | 25329,25201,25072,24942,24811,24680,24547,24413, 60 | 24279,24143,24007,23870,23731,23592,23452,23311, 61 | 23170,23027,22884,22739,22594,22448,22301,22154, 62 | 22005,21856,21705,21554,21403,21250,21096,20942, 63 | 20787,20631,20475,20317,20159,20000,19841,19680, 64 | 19519,19357,19195,19032,18868,18703,18537,18371, 65 | 18204,18037,17869,17700,17530,17360,17189,17018, 66 | 16846,16673,16499,16325,16151,15976,15800,15623, 67 | 15446,15269,15090,14912,14732,14553,14372,14191, 68 | 14010,13828,13645,13462,13279,13094,12910,12725, 69 | 12539,12353,12167,11980,11793,11605,11417,11228, 70 | 11039,10849,10659,10469,10278,10087,9896,9704, 71 | 9512,9319,9126,8933,8739,8545,8351,8157, 72 | 7962,7767,7571,7375,7179,6983,6786,6590, 73 | 6393,6195,5998,5800,5602,5404,5205,5007, 74 | 4808,4609,4410,4210,4011,3811,3612,3412, 75 | 3212,3012,2811,2611,2410,2210,2009,1809, 76 | 1608,1407,1206,1005,804,603,402,201 77 | }; 78 | 79 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map HiFi 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | HiFi KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | begin KEYWORD2 14 | configureTx KEYWORD2 15 | enableTx KEYWORD2 16 | onTxReady KEYWORD2 17 | configureRx KEYWORD2 18 | enableRx KEYWORD2 19 | onRxReady KEYWORD2 20 | write KEYWORD2 21 | read KEYWORD2 22 | 23 | 24 | ####################################### 25 | # Constants (LITERAL1) 26 | ####################################### 27 | HIFI_AUDIO_MODE_MONO_LEFT LITERAL1 28 | HIFI_AUDIO_MODE_MONO_RIGHT LITERAL1 29 | HIFI_AUDIO_MODE_STEREO LITERAL1 30 | 31 | HIFI_CLK_MODE_USE_EXT_CLKS LITERAL1 32 | HIFI_CLK_MODE_USE_TK_RK_CLK LITERAL1 33 | 34 | -------------------------------------------------------------------------------- /ssc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * 4 | * \brief Synchronous Serial Controller (SSC) driver for SAM. 5 | * 6 | * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved. 7 | * 8 | * \asf_license_start 9 | * 10 | * \page License 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. The name of Atmel may not be used to endorse or promote products derived 23 | * from this software without specific prior written permission. 24 | * 25 | * 4. This software may only be redistributed and used in connection with an 26 | * Atmel microcontroller product. 27 | * 28 | * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED 29 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 30 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE 31 | * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR 32 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 36 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 37 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | * POSSIBILITY OF SUCH DAMAGE. 39 | * 40 | * \asf_license_stop 41 | * 42 | */ 43 | 44 | #include 45 | #include "ssc.h" 46 | 47 | /// @cond 0 48 | /**INDENT-OFF**/ 49 | #ifdef __cplusplus 50 | extern "C" { 51 | #endif 52 | /**INDENT-ON**/ 53 | /// @endcond 54 | 55 | /** 56 | * \defgroup sam_drivers_ssc_group Synchronous Serial Controller (SSC) 57 | * 58 | * The Synchronous Serial Controller (SSC) provides a synchronous communication 59 | * link with external devices. It supports many serial synchronous communication 60 | * protocols generally used in audio and telecom applications such as I2S, 61 | * Short Frame Sync, Long Frame Sync, etc. 62 | * This is a driver for configuration and use of the SSC peripheral. 63 | * 64 | * @{ 65 | */ 66 | 67 | #define SSC_WPKEY SSC_WPMR_WPKEY(0x535343) 68 | 69 | /** 70 | * \brief Set up clock. 71 | * 72 | * \param p_ssc Pointer to an SSC instance. 73 | * \param ul_bitrate Desired bit clock. 74 | * \param ul_mck MCK clock. 75 | * 76 | * \retval SSC_RC_YES Success. 77 | * \retval SSC_RC_NO Invalid input value. 78 | */ 79 | uint32_t ssc_set_clock_divider(Ssc *p_ssc, uint32_t ul_bitrate, 80 | uint32_t ul_mck) 81 | { 82 | if (ul_mck && ul_bitrate) { 83 | p_ssc->SSC_CMR = SSC_CMR_DIV(((ul_mck + ul_bitrate) / ul_bitrate) >> 1); 84 | return SSC_RC_YES; 85 | } else { 86 | return SSC_RC_NO; 87 | } 88 | } 89 | 90 | /** 91 | * \brief Setup for I2S transmitter. 92 | * 93 | * \note If working in master mode, the divided clock needs to be configured before 94 | * calling this function according to the sample rate and ul_datlen field. 95 | * 96 | * \param p_ssc Pointer to an SSC instance. 97 | * \param ul_mode Working mode, SSC_I2S_MASTER_OUT or SSC_I2S_SLAVE_OUT. 98 | * \param ul_cks Source clock selection while working in SSC_I2S_SLAVE_OUT mode. 99 | * \param ul_ch_mode Channel mode, stereo or mono. 100 | * \param ul_datlen Data length for one channel. 101 | */ 102 | void ssc_i2s_set_transmitter(Ssc *p_ssc, uint32_t ul_mode, 103 | uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen) 104 | { 105 | clock_opt_t tx_clk_option; 106 | data_frame_opt_t tx_data_frame_option; 107 | 108 | /* Initialize the local variable. */ 109 | memset((uint8_t *)&tx_clk_option, 0, sizeof(clock_opt_t)); 110 | memset((uint8_t *)&tx_data_frame_option, 0, sizeof(data_frame_opt_t)); 111 | 112 | /* Data start: MonoLeft-Falling, MonoRight-Rising, Stero-Edge. */ 113 | switch (ul_ch_mode) { 114 | case SSC_AUDIO_MONO_RIGHT: 115 | tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_RISING; 116 | break; 117 | case SSC_AUDIO_MONO_LEFT: 118 | tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_FALLING; 119 | break; 120 | case SSC_AUDIO_STERO: 121 | tx_clk_option.ul_start_sel = SSC_TCMR_START_RF_EDGE; 122 | break; 123 | } 124 | if (ul_mode & SSC_I2S_MASTER_OUT) { 125 | /* Stereo has 2 data words, and mono has only one data word. */ 126 | if (SSC_AUDIO_STERO == ul_ch_mode) { 127 | tx_data_frame_option.ul_datnb = 1; 128 | } else { 129 | tx_data_frame_option.ul_datnb = 0; 130 | } 131 | 132 | /* Configure TCMR Settings. */ 133 | tx_clk_option.ul_cks = SSC_TCMR_CKS_MCK; 134 | tx_clk_option.ul_cko = SSC_TCMR_CKO_CONTINUOUS; 135 | tx_clk_option.ul_cki = 0; 136 | tx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE; 137 | /* The delay is defined by I2S protocol. */ 138 | tx_clk_option.ul_sttdly = 1; 139 | tx_clk_option.ul_period = ul_datlen - 1; 140 | 141 | /* Configure TFMR Settings. */ 142 | tx_data_frame_option.ul_datlen = ul_datlen - 1; 143 | tx_data_frame_option.ul_msbf = SSC_TFMR_MSBF; 144 | tx_data_frame_option.ul_fslen = ul_datlen - 1; 145 | tx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NEGATIVE; 146 | tx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE; 147 | } else if (ul_mode & SSC_I2S_SLAVE_OUT) { 148 | /* Configure TCMR Settings. */ 149 | tx_clk_option.ul_cks = ul_cks; 150 | tx_clk_option.ul_cko = SSC_TCMR_CKO_NONE; 151 | tx_clk_option.ul_cki = 0; 152 | tx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE; 153 | tx_clk_option.ul_sttdly = 1; 154 | tx_clk_option.ul_period = 0; 155 | 156 | /* Configure TFMR Settings. */ 157 | tx_data_frame_option.ul_datlen = ul_datlen - 1; 158 | tx_data_frame_option.ul_msbf = SSC_TFMR_MSBF; 159 | tx_data_frame_option.ul_fslen = 0; 160 | tx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NONE; 161 | tx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE; 162 | } 163 | /* Configure the default level on TD pin. */ 164 | ssc_set_td_default_level(p_ssc, 0); 165 | 166 | /* Configure the SSC transmitter. */ 167 | ssc_set_transmitter(p_ssc, &tx_clk_option, &tx_data_frame_option); 168 | } 169 | 170 | /** 171 | * \brief Setup for I2S receiver. 172 | * 173 | * \note If working in master mode, the divided clock needs to be configured before 174 | * calling this function according to the sample rate and ul_datlen field. 175 | * 176 | * \param p_ssc Pointer to an SSC instance. 177 | * \param ul_mode Working mode, SSC_I2S_MASTER_IN or SSC_I2S_SLAVE_IN. 178 | * \param ul_cks Source clock selection while working in SSC_I2S_SLAVE_IN mode. 179 | * \param ul_ch_mode Channel mode, stereo or mono. 180 | * \param ul_datlen Data length for one channel. 181 | */ 182 | void ssc_i2s_set_receiver(Ssc *p_ssc, uint32_t ul_mode, 183 | uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen) 184 | { 185 | clock_opt_t rx_clk_option; 186 | data_frame_opt_t rx_data_frame_option; 187 | 188 | /* Initialize the local variable. */ 189 | memset((uint8_t *)&rx_clk_option, 0, sizeof(clock_opt_t)); 190 | memset((uint8_t *)&rx_data_frame_option, 0, sizeof(data_frame_opt_t)); 191 | 192 | /* Data start: MonoLeft-Falling, MonoRight-Rising, Stero-Edge. */ 193 | switch (ul_ch_mode) { 194 | case SSC_AUDIO_MONO_RIGHT: 195 | rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_RISING; 196 | break; 197 | case SSC_AUDIO_MONO_LEFT: 198 | rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_FALLING; 199 | break; 200 | case SSC_AUDIO_STERO: 201 | rx_clk_option.ul_start_sel = SSC_RCMR_START_RF_EDGE; 202 | break; 203 | } 204 | if (ul_mode & SSC_I2S_MASTER_IN) { 205 | /* Stereo has 2 data words, and mono has only one data word. */ 206 | if (SSC_AUDIO_STERO == ul_ch_mode) { 207 | rx_data_frame_option.ul_datnb = 1; 208 | } else { 209 | rx_data_frame_option.ul_datnb = 0; 210 | } 211 | 212 | /* Configure RCMR Settings. */ 213 | rx_clk_option.ul_cks = SSC_TCMR_CKS_MCK; 214 | rx_clk_option.ul_cko = SSC_TCMR_CKO_CONTINUOUS; 215 | rx_clk_option.ul_cki = 0; 216 | rx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE; 217 | rx_clk_option.ul_sttdly = 1; 218 | rx_clk_option.ul_period = ul_datlen - 1; 219 | 220 | /* Configure RFMR Settings. */ 221 | rx_data_frame_option.ul_datlen = ul_datlen - 1; 222 | rx_data_frame_option.ul_msbf = SSC_TFMR_MSBF; 223 | rx_data_frame_option.ul_fslen = ul_datlen - 1; 224 | rx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NEGATIVE; 225 | rx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE; 226 | } else if (ul_mode & SSC_I2S_SLAVE_IN) { 227 | /* Configure TCMR Settings. */ 228 | rx_clk_option.ul_cks = ul_cks; 229 | rx_clk_option.ul_cko = SSC_TCMR_CKO_NONE; 230 | rx_clk_option.ul_cki = 0; 231 | rx_clk_option.ul_ckg = SSC_RCMR_CKG_NONE; 232 | rx_clk_option.ul_sttdly = 1; 233 | rx_clk_option.ul_period = 0; 234 | 235 | /* Configure TFMR Settings. */ 236 | rx_data_frame_option.ul_datlen = ul_datlen - 1; 237 | rx_data_frame_option.ul_msbf = SSC_TFMR_MSBF; 238 | rx_data_frame_option.ul_fslen = 0; 239 | rx_data_frame_option.ul_fsos = SSC_TFMR_FSOS_NONE; 240 | rx_data_frame_option.ul_fsedge = SSC_TFMR_FSEDGE_POSITIVE; 241 | } 242 | 243 | /* Configure the SSC receiver. */ 244 | ssc_set_receiver(p_ssc, &rx_clk_option, &rx_data_frame_option); 245 | } 246 | 247 | /** 248 | * \brief Reset SSC module. 249 | * 250 | * \param p_ssc Pointer to an SSC instance. 251 | */ 252 | void ssc_reset(Ssc *p_ssc) 253 | { 254 | p_ssc->SSC_CR = SSC_CR_SWRST; 255 | p_ssc->SSC_CMR = 0; 256 | p_ssc->SSC_RCMR = 0; 257 | p_ssc->SSC_RFMR = 0; 258 | p_ssc->SSC_TCMR = 0; 259 | p_ssc->SSC_TFMR = 0; 260 | } 261 | 262 | /** 263 | * \brief Enable SSC receiver. 264 | * 265 | * \param p_ssc Pointer to an SSC instance. 266 | */ 267 | void ssc_enable_rx(Ssc *p_ssc) 268 | { 269 | p_ssc->SSC_CR = SSC_CR_RXEN; 270 | } 271 | 272 | /** 273 | * \brief Disable SSC receiver. 274 | * 275 | * \param p_ssc Pointer to an SSC instance. 276 | */ 277 | void ssc_disable_rx(Ssc *p_ssc) 278 | { 279 | p_ssc->SSC_CR = SSC_CR_RXDIS; 280 | } 281 | 282 | /** 283 | * \brief Enable SSC Transmitter. 284 | * 285 | * \param p_ssc Pointer to an SSC instance. 286 | */ 287 | void ssc_enable_tx(Ssc *p_ssc) 288 | { 289 | p_ssc->SSC_CR = SSC_CR_TXEN; 290 | } 291 | 292 | /** 293 | * \brief Disable SSC Transmitter. 294 | * 295 | * \param p_ssc Pointer to an SSC instance. 296 | */ 297 | void ssc_disable_tx(Ssc *p_ssc) 298 | { 299 | p_ssc->SSC_CR = SSC_CR_TXDIS; 300 | } 301 | 302 | /** 303 | * \brief Configure SSC to work in normal mode. 304 | * 305 | * \param p_ssc Pointer to an SSC instance. 306 | */ 307 | void ssc_set_normal_mode(Ssc *p_ssc) 308 | { 309 | p_ssc->SSC_RFMR &= ~SSC_RFMR_LOOP; 310 | } 311 | 312 | /** 313 | * \brief Configure SSC to work in loop mode. 314 | * 315 | * \param p_ssc Pointer to an SSC instance. 316 | */ 317 | void ssc_set_loop_mode(Ssc *p_ssc) 318 | { 319 | p_ssc->SSC_RFMR |= SSC_RFMR_LOOP; 320 | } 321 | 322 | /** 323 | * \brief Configure SSC receive stop selection. 324 | * 325 | * \param p_ssc Pointer to an SSC instance. 326 | * \param ul_sel Compare 0 used or Compare both 0 & 1 used. 327 | */ 328 | void ssc_set_rx_stop_selection(Ssc *p_ssc, uint32_t ul_sel) 329 | { 330 | if (SSC_RX_STOP_COMPARE_0_1 == ul_sel) { 331 | p_ssc->SSC_RCMR |= SSC_RCMR_STOP; 332 | } else if (SSC_RX_STOP_COMPARE_0 == ul_sel) { 333 | p_ssc->SSC_RCMR &= ~SSC_RCMR_STOP; 334 | } 335 | } 336 | 337 | /** 338 | * \brief Configure SSC default level driven on the TD pin while 339 | * out of transmission. 340 | * 341 | * \param p_ssc Pointer to an SSC instance. 342 | * \param ul_level The default driven level of TD pin. 343 | */ 344 | void ssc_set_td_default_level(Ssc *p_ssc, uint32_t ul_level) 345 | { 346 | if (ul_level) { 347 | p_ssc->SSC_TFMR |= SSC_TFMR_DATDEF; 348 | } else { 349 | p_ssc->SSC_TFMR &= ~SSC_TFMR_DATDEF; 350 | } 351 | } 352 | 353 | /** 354 | * \brief The TD line is driven with the SSC_TSHR register value 355 | * during the transmission of the Transmit Frame Sync Signal. 356 | * 357 | * \param p_ssc Pointer to an SSC instance. 358 | */ 359 | void ssc_enable_tx_frame_sync_data(Ssc *p_ssc) 360 | { 361 | p_ssc->SSC_TFMR |= SSC_TFMR_FSDEN; 362 | } 363 | 364 | /** 365 | * \brief The TD line is driven with the default value during the Transmit 366 | * Frame Sync signal. 367 | * 368 | * \param p_ssc Pointer to an SSC instance. 369 | */ 370 | void ssc_disable_tx_frame_sync_data(Ssc *p_ssc) 371 | { 372 | p_ssc->SSC_TFMR &= ~SSC_TFMR_FSDEN; 373 | } 374 | 375 | /** 376 | * \brief Configure SSC receiver clock mode and date frame configuration. 377 | * 378 | * \param p_ssc Pointer to an SSC instance. 379 | * \param p_rx_clk_opt Pointer to the receiver clock configuration structure. 380 | * \param p_rx_data_frame Pointer to the receiver data frame configuration structure. 381 | */ 382 | void ssc_set_receiver(Ssc *p_ssc, clock_opt_t *p_rx_clk_opt, 383 | data_frame_opt_t *p_rx_data_frame) 384 | { 385 | if (p_rx_clk_opt == NULL) { 386 | p_ssc->SSC_RCMR = 0; 387 | } else { 388 | p_ssc->SSC_RCMR |= p_rx_clk_opt->ul_cks | 389 | p_rx_clk_opt->ul_cko | p_rx_clk_opt->ul_cki | 390 | p_rx_clk_opt->ul_ckg | 391 | p_rx_clk_opt->ul_start_sel | 392 | SSC_RCMR_PERIOD(p_rx_clk_opt->ul_period) | 393 | SSC_RCMR_STTDLY(p_rx_clk_opt->ul_sttdly); 394 | } 395 | 396 | if (p_rx_data_frame == NULL) { 397 | p_ssc->SSC_RFMR = 0; 398 | } else { 399 | p_ssc->SSC_RFMR |= SSC_RFMR_DATLEN(p_rx_data_frame->ul_datlen) | 400 | p_rx_data_frame->ul_msbf | 401 | SSC_RFMR_DATNB(p_rx_data_frame->ul_datnb) | 402 | SSC_RFMR_FSLEN(p_rx_data_frame->ul_fslen) | 403 | SSC_RFMR_FSLEN_EXT(p_rx_data_frame->ul_fslen_ext) | 404 | p_rx_data_frame->ul_fsos | 405 | p_rx_data_frame->ul_fsedge; 406 | } 407 | } 408 | 409 | /** 410 | * \brief Configure SSC transmitter clock mode and date frame configuration. 411 | * 412 | * \param p_ssc Pointer to an SSC instance. 413 | * \param p_tx_clk_opt Pointer to the transmitter clock configuration structure. 414 | * \param p_tx_data_frame Pointer to the transmitter data frame configuration structure. 415 | */ 416 | void ssc_set_transmitter(Ssc *p_ssc, clock_opt_t *p_tx_clk_opt, 417 | data_frame_opt_t *p_tx_data_frame) 418 | { 419 | if (p_tx_clk_opt == NULL) { 420 | p_ssc->SSC_TCMR = 0; 421 | } else { 422 | p_ssc->SSC_TCMR |= p_tx_clk_opt->ul_cks | 423 | p_tx_clk_opt->ul_cko | p_tx_clk_opt->ul_cki | 424 | p_tx_clk_opt->ul_ckg | 425 | p_tx_clk_opt->ul_start_sel | 426 | SSC_RCMR_PERIOD(p_tx_clk_opt->ul_period) | 427 | SSC_RCMR_STTDLY(p_tx_clk_opt->ul_sttdly); 428 | } 429 | 430 | if (p_tx_data_frame == NULL) { 431 | p_ssc->SSC_TFMR = 0; 432 | } else { 433 | p_ssc->SSC_TFMR |= SSC_RFMR_DATLEN(p_tx_data_frame->ul_datlen) | 434 | p_tx_data_frame->ul_msbf | 435 | SSC_RFMR_DATNB(p_tx_data_frame->ul_datnb) | 436 | SSC_RFMR_FSLEN(p_tx_data_frame->ul_fslen) | 437 | SSC_RFMR_FSLEN_EXT(p_tx_data_frame->ul_fslen_ext) | 438 | p_tx_data_frame->ul_fsos | 439 | p_tx_data_frame->ul_fsedge; 440 | } 441 | } 442 | 443 | /** 444 | * \brief Configure SSC Receive Compare Register. 445 | * 446 | * \param p_ssc Pointer to an SSC instance. 447 | * \param ul_id Compare register ID. 448 | * \param ul_value Value to configure. 449 | */ 450 | void ssc_set_rx_compare(Ssc *p_ssc, uint32_t ul_id, uint32_t ul_value) 451 | { 452 | switch (ul_id) { 453 | case COMPARE_ID0: 454 | p_ssc->SSC_RC0R = ul_value; 455 | break; 456 | case COMPARE_ID1: 457 | p_ssc->SSC_RC1R = ul_value; 458 | break; 459 | } 460 | } 461 | 462 | /** 463 | * \brief Get SSC Receive Compare Register. 464 | * 465 | * \param p_ssc Pointer to an SSC instance. 466 | * \param ul_id Compare register ID. 467 | * 468 | * \return Receive Compare Register value for the specified ul_id, otherwise SSC_RC_INVALID. 469 | */ 470 | uint32_t ssc_get_rx_compare(Ssc *p_ssc, uint32_t ul_id) 471 | { 472 | switch (ul_id) { 473 | case COMPARE_ID0: 474 | return p_ssc->SSC_RC0R; 475 | case COMPARE_ID1: 476 | return p_ssc->SSC_RC1R; 477 | default: 478 | return SSC_RC_INVALID; 479 | } 480 | } 481 | 482 | /** 483 | * \brief Enable SSC interrupts. 484 | * 485 | * \param p_ssc Pointer to an SSC instance. 486 | * \param ul_sources Interrupts to be enabled. 487 | */ 488 | void ssc_enable_interrupt(Ssc *p_ssc, uint32_t ul_sources) 489 | { 490 | p_ssc->SSC_IER = ul_sources; 491 | } 492 | 493 | /** 494 | * \brief Disable SSC interrupts. 495 | * 496 | * \param p_ssc Pointer to an SSC instance. 497 | * \param ul_sources Interrupts to be enabled. 498 | */ 499 | void ssc_disable_interrupt(Ssc *p_ssc, uint32_t ul_sources) 500 | { 501 | p_ssc->SSC_IDR = ul_sources; 502 | } 503 | 504 | /** 505 | * \brief Read SSC interrupt mask. 506 | * 507 | * \param p_ssc Pointer to an SSC instance. 508 | * 509 | * \return The interrupt mask value. 510 | */ 511 | uint32_t ssc_get_interrupt_mask(Ssc *p_ssc) 512 | { 513 | return p_ssc->SSC_IMR; 514 | } 515 | 516 | /** 517 | * \brief Read SSC status. 518 | * 519 | * \param p_ssc Pointer to an SSC instance. 520 | * 521 | * \return The SSC status value. 522 | */ 523 | uint32_t ssc_get_status(Ssc *p_ssc) 524 | { 525 | return p_ssc->SSC_SR; 526 | } 527 | 528 | /** 529 | * \brief Check if data has been loaded in SSC_THR and is waiting to be loaded 530 | * in the Transmit Shift Register (TSR). 531 | * 532 | * \param p_ssc Pointer to an SSC instance. 533 | * 534 | * \retval SSC_RC_YES There is no data in the SSC_THR. 535 | * \retval SSC_RC_NO There is one data in the SSC_THR. 536 | */ 537 | uint32_t ssc_is_tx_ready(Ssc *p_ssc) 538 | { 539 | if (p_ssc->SSC_SR & SSC_SR_TXRDY) { 540 | return SSC_RC_YES; 541 | } 542 | return SSC_RC_NO; 543 | } 544 | 545 | /** 546 | * \brief Check if the last data written in SSC_THR has been loaded in TSR 547 | * and the last data loaded in TSR has been transmitted. 548 | * 549 | * \param p_ssc Pointer to an SSC instance. 550 | * 551 | * \retval SSC_RC_YES Both of the two registers are empty. 552 | * \retval SSC_RC_NO At least one of the two registers is not empty. 553 | */ 554 | uint32_t ssc_is_tx_empty(Ssc *p_ssc) 555 | { 556 | if (p_ssc->SSC_SR & SSC_SR_TXEMPTY) { 557 | return SSC_RC_YES; 558 | } 559 | return SSC_RC_NO; 560 | } 561 | 562 | /** 563 | * \brief Check if data has been received and loaded in SSC_RHR. 564 | * 565 | * \param p_ssc Pointer to an SSC instance. 566 | * 567 | * \retval SSC_RC_YES There is one data in the SSC_RHR. 568 | * \retval SSC_RC_NO There is no data in the SSC_RHR. 569 | */ 570 | uint32_t ssc_is_rx_ready(Ssc *p_ssc) 571 | { 572 | if (p_ssc->SSC_SR & SSC_SR_RXRDY) { 573 | return SSC_RC_YES; 574 | } 575 | return SSC_RC_NO; 576 | } 577 | 578 | /** 579 | * \brief Check if transmitter is enabled. 580 | * 581 | * \param p_ssc Pointer to an SSC instance. 582 | * 583 | * \retval SSC_RC_YES The transmitter is enabled. 584 | * \retval SSC_RC_NO The transmitter is disabled. 585 | */ 586 | uint32_t ssc_is_tx_enabled(Ssc *p_ssc) 587 | { 588 | if (p_ssc->SSC_SR & SSC_SR_TXEN) { 589 | return SSC_RC_YES; 590 | } 591 | return SSC_RC_NO; 592 | } 593 | 594 | /** 595 | * \brief Check if receiver is enabled. 596 | * 597 | * \param p_ssc Pointer to an SSC instance. 598 | * 599 | * \retval SSC_RC_YES The receiver is enabled. 600 | * \retval SSC_RC_NO The receiver is disabled. 601 | */ 602 | uint32_t ssc_is_rx_enabled(Ssc *p_ssc) 603 | { 604 | if (p_ssc->SSC_SR & SSC_SR_RXEN) { 605 | return SSC_RC_YES; 606 | } 607 | return SSC_RC_NO; 608 | } 609 | 610 | //#if SAM3S || SAM4S 611 | #if SAM3S_SERIES || SAM4S_SERIES 612 | /** 613 | * \brief Check if one receive buffer is filled. 614 | * 615 | * \param p_ssc Pointer to an SSC instance. 616 | * 617 | * \retval SSC_RC_YES Receive Counter has reached zero. 618 | * \retval SSC_RC_NO Data is written on the Receive Counter Register or 619 | * Receive Next Counter Register. 620 | */ 621 | uint32_t ssc_is_rx_buf_end(Ssc *p_ssc) 622 | { 623 | if (p_ssc->SSC_SR & SSC_SR_ENDRX) { 624 | return SSC_RC_YES; 625 | } 626 | return SSC_RC_NO; 627 | } 628 | 629 | /** 630 | * \brief Check if the register SSC_TCR has reached 0. 631 | * 632 | * \param p_ssc Pointer to an SSC instance. 633 | * 634 | * \retval SSC_RC_YES The register SSC_TCR has reached 0. 635 | * \retval SSC_RC_NO The register SSC_TCR hasn't reached 0. 636 | */ 637 | uint32_t ssc_is_tx_buf_end(Ssc *p_ssc) 638 | { 639 | if (p_ssc->SSC_SR & SSC_SR_ENDTX) { 640 | return SSC_RC_YES; 641 | } 642 | return SSC_RC_NO; 643 | } 644 | 645 | /** 646 | * \brief Check if both receive buffers are full. 647 | * 648 | * \param p_ssc Pointer to an SSC instance. 649 | * 650 | * \retval SSC_RC_YES Both of the two receive buffers have reached 0. 651 | * \retval SSC_RC_NO One of the two receive buffers hasn't reached 0. 652 | */ 653 | uint32_t ssc_is_rx_buf_full(Ssc *p_ssc) 654 | { 655 | if (p_ssc->SSC_SR & SSC_SR_RXBUFF) { 656 | return SSC_RC_YES; 657 | } 658 | return SSC_RC_NO; 659 | } 660 | 661 | /** 662 | * \brief Check if both transmit buffers are empty. 663 | * 664 | * \param p_ssc Pointer to an SSC instance. 665 | * 666 | * \retval SSC_RC_YES Both of the two transmit buffers have reached 0. 667 | * \retval SSC_RC_NO One of the two transmit buffers hasn't reached 0. 668 | */ 669 | uint32_t ssc_is_tx_buf_empty(Ssc *p_ssc) 670 | { 671 | if (p_ssc->SSC_SR & SSC_SR_TXBUFE) { 672 | return SSC_RC_YES; 673 | } 674 | return SSC_RC_NO; 675 | } 676 | 677 | /** 678 | * \brief Get SSC PDC registers base address. 679 | * 680 | * \param p_ssc Pointer to SSC registers set instance. 681 | * 682 | * \return SSC PDC registers base address for PDC driver to access. 683 | */ 684 | Pdc *ssc_get_pdc_base(Ssc *p_ssc) 685 | { 686 | return (Pdc *)&(p_ssc->SSC_RPR); 687 | } 688 | #endif 689 | 690 | /** 691 | * \brief Write to SSC Transmit Holding Register. 692 | * Send data through SSC Data frame. 693 | * 694 | * \param p_ssc Pointer to an SSC instance. 695 | * \param ul_frame Frame data to be transmitted. 696 | * 697 | * \retval SSC_RC_ERROR Time-out. 698 | * \retval SSC_RC_OK Success. 699 | * 700 | */ 701 | uint32_t ssc_write(Ssc *p_ssc, uint32_t ul_frame) 702 | { 703 | uint32_t ul_timeout = SSC_DEFAULT_TIMEOUT; 704 | 705 | while (!(p_ssc->SSC_SR & SSC_SR_TXEMPTY)) { 706 | if (!ul_timeout--) { 707 | return SSC_RC_ERROR; 708 | } 709 | } 710 | 711 | p_ssc->SSC_THR = ul_frame; 712 | return SSC_RC_OK; 713 | } 714 | 715 | /** 716 | * \brief Read from SSC Receive Holding Register. 717 | * Read data that is received in SSC Data frame. 718 | * 719 | * \param p_ssc Pointer to an SSC instance. 720 | * \param ul_data Pointer to the location where to store the received data. 721 | * 722 | * \retval SSC_RC_ERROR Time-out. 723 | * \retval SSC_RC_OK Success. 724 | */ 725 | uint32_t ssc_read(Ssc *p_ssc, uint32_t *ul_data) 726 | { 727 | uint32_t ul_timeout = SSC_DEFAULT_TIMEOUT; 728 | 729 | while (!(p_ssc->SSC_SR & SSC_SR_RXRDY)) { 730 | if (!ul_timeout--) { 731 | return SSC_RC_ERROR; 732 | } 733 | } 734 | 735 | *ul_data = p_ssc->SSC_RHR; 736 | return SSC_RC_OK; 737 | } 738 | 739 | /** 740 | * \brief Write to SSC Transmit Synchronization Holding Register. 741 | * Send data through SSC Synchronization frame. If there is sync data that needs to be 742 | * transmitted, call this function first to send out the sync data, and then call the 743 | * ssc_write() function to send out application data. 744 | * 745 | * \param p_ssc Pointer to an SSC instance. 746 | * \param ul_frame Frame Synchronization data. 747 | */ 748 | void ssc_write_sync_data(Ssc *p_ssc, uint32_t ul_frame) 749 | { 750 | p_ssc->SSC_TSHR = ul_frame; 751 | } 752 | 753 | /** 754 | * \brief Read from SSC Receive Synchronization Holding Register. 755 | * Read data that is received in SSC Synchronization frame. When the sync data is actually 756 | * used, after successfully reading the application data by calling ssc_read(), call 757 | * this function, and the return sync data is useful. 758 | * 759 | * \param p_ssc Pointer to an SSC instance. 760 | * 761 | * \return Current RSHR value. 762 | */ 763 | uint32_t ssc_read_sync_data(Ssc *p_ssc) 764 | { 765 | return p_ssc->SSC_RSHR; 766 | } 767 | 768 | //#if (SAM3XA || SAM3U) 769 | #if (SAM3XA_SERIES || SAM3U_SERIES) 770 | /** 771 | * \brief Get Transmit address for DMA operation. 772 | * 773 | * \param p_ssc Pointer to an SSC instance. 774 | * 775 | * \return Transmitting address for DMA access. 776 | */ 777 | void *ssc_get_tx_access(Ssc *p_ssc) 778 | { 779 | return (void *)&(p_ssc->SSC_THR); 780 | } 781 | 782 | /** 783 | * \brief Get Receive address for DMA operation. 784 | * 785 | * \param p_ssc Pointer to an SSC instance. 786 | * 787 | * \return Transmitting address for DMA access. 788 | */ 789 | void *ssc_get_rx_access(Ssc *p_ssc) 790 | { 791 | return (void *)&(p_ssc->SSC_RHR); 792 | } 793 | #endif 794 | 795 | /** 796 | * \brief Enable or disable write protection of SSC registers. 797 | * 798 | * \param p_ssc Pointer to an SSC instance. 799 | * \param ul_enable 1 to enable, 0 to disable. 800 | */ 801 | void ssc_set_writeprotect(Ssc *p_ssc, uint32_t ul_enable) 802 | { 803 | if (ul_enable) { 804 | p_ssc->SSC_WPMR = SSC_WPKEY | SSC_WPMR_WPEN; 805 | } else { 806 | p_ssc->SSC_WPMR = SSC_WPKEY; 807 | } 808 | } 809 | 810 | /** 811 | * \brief Indicate write protect status. 812 | * 813 | * \param p_ssc Pointer to an SSC instance. 814 | * 815 | * \return 0 if the peripheral is not protected. Write Protect Violation Status otherwise. 816 | */ 817 | uint32_t ssc_get_writeprotect_status(Ssc *p_ssc) 818 | { 819 | uint32_t ul_reg_val; 820 | 821 | ul_reg_val = p_ssc->SSC_WPMR; 822 | if (ul_reg_val & SSC_WPMR_WPEN) { 823 | return (ul_reg_val & SSC_WPSR_WPVSRC_Msk) >> SSC_WPSR_WPVSRC_Pos; 824 | } else { 825 | return 0; 826 | } 827 | } 828 | 829 | //@} 830 | 831 | /// @cond 0 832 | /**INDENT-OFF**/ 833 | #ifdef __cplusplus 834 | } 835 | #endif 836 | /**INDENT-ON**/ 837 | /// @endcond 838 | -------------------------------------------------------------------------------- /ssc.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file 3 | * 4 | * \brief Synchronous Serial Controller (SSC) driver for SAM. 5 | * 6 | * Copyright (c) 2011-2012 Atmel Corporation. All rights reserved. 7 | * 8 | * \asf_license_start 9 | * 10 | * \page License 11 | * 12 | * Redistribution and use in source and binary forms, with or without 13 | * modification, are permitted provided that the following conditions are met: 14 | * 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 22 | * 3. The name of Atmel may not be used to endorse or promote products derived 23 | * from this software without specific prior written permission. 24 | * 25 | * 4. This software may only be redistributed and used in connection with an 26 | * Atmel microcontroller product. 27 | * 28 | * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED 29 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 30 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE 31 | * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR 32 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 33 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 34 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 36 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 37 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 38 | * POSSIBILITY OF SUCH DAMAGE. 39 | * 40 | * \asf_license_stop 41 | * 42 | */ 43 | 44 | #ifndef SSC_H_INCLUDED 45 | #define SSC_H_INCLUDED 46 | 47 | //#include "compiler.h" 48 | #include "sam.h" 49 | 50 | /// @cond 0 51 | /**INDENT-OFF**/ 52 | #ifdef __cplusplus 53 | extern "C" { 54 | #endif 55 | /**INDENT-ON**/ 56 | /// @endcond 57 | 58 | //! Receive stop selection. 59 | #define SSC_RX_STOP_COMPARE_0 0 60 | #define SSC_RX_STOP_COMPARE_0_1 1 61 | 62 | //! Compare register ID. 63 | #define COMPARE_ID0 0 64 | #define COMPARE_ID1 1 65 | 66 | //! SSC module default timeout. */ 67 | #define SSC_DEFAULT_TIMEOUT 10000 68 | 69 | //! \brief SSC driver return codes. 70 | enum ssc_return_code { 71 | SSC_RC_OK = 0, //!< OK 72 | SSC_RC_YES = 0, //!< Yes 73 | SSC_RC_NO = 1, //!< No 74 | SSC_RC_ERROR = 1, //!< General error 75 | SSC_RC_INVALID = 0xFFFFFFFF //!< Parameter invalid 76 | }; 77 | 78 | //! Data frame structure. 79 | typedef struct { 80 | //! Data bits length per transfer, should be 0 to 31. 81 | uint32_t ul_datlen; 82 | //! Bit sequence LSBF or MSBF. 83 | //! For receiver configuration, SSC_RFMR_MSBF or 0. 84 | //! For transmitter configuration, SSC_TFMR_MSBF or 0. 85 | uint32_t ul_msbf; 86 | //! Data number per frame, should be 0 to 15. 87 | uint32_t ul_datnb; 88 | //! Frame Sync. length should be 0 to 15. 89 | uint32_t ul_fslen; 90 | //! Frame Sync. length extension field, should be 0 to 15. 91 | uint32_t ul_fslen_ext; 92 | //! Frame Sync. output selection. 93 | //! For receiver configuration, one of SSC_RFMR_FSOS_NONE, SSC_RFMR_FSOS_NEGATIVE, SSC_RFMR_FSOS_POSITIVE, 94 | //! SSC_RFMR_FSOS_LOW, SSC_RFMR_FSOS_HIGH or SSC_RFMR_FSOS_TOGGLING. 95 | //! For transmitter configuration, one of SSC_TFMR_FSOS_NONE, SSC_TFMR_FSOS_NEGATIVE, SSC_TFMR_FSOS_POSITIVE 96 | //! SSC_TFMR_FSOS_LOW, SSC_TFMR_FSOS_HIGH, SSC_TFMR_FSOS_TOGGLING, 97 | uint32_t ul_fsos; 98 | //! Frame Sync. edge detection. 99 | //! For receiver configuration, SSC_RFMR_FSEDGE_POSITIVE or SSC_RFMR_FSEDGE_NEGATIVE. 100 | //! For transmitter configuration, SSC_TFMR_FSEDGE_POSITIVE or SSC_TFMR_FSEDGE_NEGATIVE. 101 | uint32_t ul_fsedge; 102 | } data_frame_opt_t; 103 | 104 | //! Clock mode structure. 105 | typedef struct { 106 | //! Communication clock selection. 107 | //! For receiver configuration, one of SSC_RCMR_CKS_MCK, SSC_RCMR_CKS_TK or SSC_RCMR_CKS_RK. 108 | //! For transmitter configuration, one of SSC_TCMR_CKS_MCK, SSC_TCMR_CKS_TK or SSC_TCMR_CKS_RK. 109 | uint32_t ul_cks; 110 | //! Communication clock output mode selection. 111 | //! For receiver configuration, one of SSC_RCMR_CKO_NONE, SSC_RCMR_CKO_CONTINUOUS or SSC_RCMR_CKO_TRANSFER. 112 | //! For transmitter configuration, one of SSC_TCMR_CKO_NONE, SSC_TCMR_CKO_CONTINUOUS or SSC_TCMR_CKO_TRANSFER. 113 | uint32_t ul_cko; 114 | //! Communication clock inversion. 115 | //! For receiver configuration, SSC_RCMR_CKI or 0. 116 | //! For transmitter configuration, SSC_TCMR_CKI or 0. 117 | uint32_t ul_cki; 118 | //! Communication clock gating selection. 119 | //! For receiver configuration, one of SSC_RCMR_CKG_NONE, SSC_RCMR_CKG_CONTINUOUS and SSC_RCMR_CKG_TRANSFER. 120 | //! For transmitter configuration, one of SSC_TCMR_CKG_NONE, SSC_TCMR_CKG_CONTINUOUS and SSC_TCMR_CKG_TRANSFER. 121 | uint32_t ul_ckg; 122 | //! Period divider selection, should be 0 to 255. 123 | uint32_t ul_period; 124 | //! Communication start delay, should be 0 to 255. 125 | uint32_t ul_sttdly; 126 | //! Communication start selection. 127 | //! For receiver configuration, one of SSC_RCMR_START_CONTINUOUS, SSC_RCMR_START_TRANSMIT, SSC_RCMR_START_RF_LOW, 128 | //! SSC_RCMR_START_RF_HIGH, SSC_RCMR_START_RF_FALLING, SSC_RCMR_START_RF_RISING, SSC_RCMR_START_RF_LEVEL, 129 | //! SSC_RCMR_START_RF_EDGE or SSC_RCMR_START_CMP_0. 130 | //! For transmitter configuration, one of SSC_TCMR_START_CONTINUOUS, SSC_TCMR_START_TRANSMIT, SSC_TCMR_START_RF_LOW, 131 | //! SSC_TCMR_START_RF_HIGH, SSC_TCMR_START_RF_FALLING, SSC_TCMR_START_RF_RISING, SSC_TCMR_START_RF_LEVEL, 132 | //! SSC_TCMR_START_RF_EDGE or SSC_TCMR_START_CMP_0. 133 | uint32_t ul_start_sel; 134 | } clock_opt_t; 135 | 136 | //! SSC working role in I2S mode. 137 | #define SSC_I2S_MASTER_OUT (1 << 0) //! Working mode for transmitter as master. 138 | #define SSC_I2S_MASTER_IN (1 << 1) //! Working mode for receiver as master. 139 | #define SSC_I2S_SLAVE_OUT (1 << 2) //! Working mode for transmitter as slave. 140 | #define SSC_I2S_SLAVE_IN (1 << 3) //! Working mode for receiver as slave. 141 | 142 | //! Bit for SSC Audio channel left. 143 | #define SSC_AUDIO_CH_LEFT (1 << 0) 144 | //! Bit for SSC Audio channel right. 145 | #define SSC_AUDIO_CH_RIGHT (1 << 1) 146 | //! SSC Audio Channel modes. 147 | enum { 148 | //! Mono, left channel enabled. 149 | SSC_AUDIO_MONO_LEFT = (SSC_AUDIO_CH_LEFT), 150 | //! Mono, right channel enabled. 151 | SSC_AUDIO_MONO_RIGHT = (SSC_AUDIO_CH_RIGHT), 152 | //! Stereo, two channels. 153 | SSC_AUDIO_STERO = (SSC_AUDIO_CH_LEFT | SSC_AUDIO_CH_RIGHT) 154 | }; 155 | 156 | uint32_t ssc_set_clock_divider(Ssc *p_ssc, uint32_t ul_bitclock, uint32_t ul_mck); 157 | void ssc_i2s_set_transmitter(Ssc *p_ssc, uint32_t ul_mode, 158 | uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen); 159 | void ssc_i2s_set_receiver(Ssc *p_ssc, uint32_t ul_mode, 160 | uint32_t ul_cks, uint32_t ul_ch_mode, uint32_t ul_datlen); 161 | void ssc_reset(Ssc *p_ssc); 162 | void ssc_enable_rx(Ssc *p_ssc); 163 | void ssc_disable_rx(Ssc *p_ssc); 164 | void ssc_enable_tx(Ssc *p_ssc); 165 | void ssc_disable_tx(Ssc *p_ssc); 166 | void ssc_set_normal_mode(Ssc *p_ssc); 167 | void ssc_set_loop_mode(Ssc *p_ssc); 168 | void ssc_set_rx_stop_selection(Ssc *p_ssc, uint32_t ul_sel); 169 | void ssc_set_td_default_level(Ssc *p_ssc, uint32_t ul_level); 170 | void ssc_enable_tx_frame_sync_data(Ssc *p_ssc); 171 | void ssc_disable_tx_frame_sync_data(Ssc *p_ssc); 172 | void ssc_set_receiver(Ssc *p_ssc, clock_opt_t *p_rx_clk_opt, data_frame_opt_t *p_rx_data_frame); 173 | void ssc_set_transmitter(Ssc *p_ssc, clock_opt_t *p_tx_clk_opt, data_frame_opt_t *p_tx_data_frame); 174 | void ssc_set_rx_compare(Ssc *p_ssc, uint32_t ul_id, uint32_t ul_value); 175 | uint32_t ssc_get_rx_compare(Ssc *p_ssc, uint32_t ul_id); 176 | void ssc_enable_interrupt(Ssc *p_ssc, uint32_t ul_sources); 177 | void ssc_disable_interrupt(Ssc *p_ssc, uint32_t ul_sources); 178 | uint32_t ssc_get_interrupt_mask(Ssc *p_ssc); 179 | uint32_t ssc_get_status(Ssc *p_ssc); 180 | uint32_t ssc_is_tx_ready(Ssc *p_ssc); 181 | uint32_t ssc_is_tx_empty(Ssc *p_ssc); 182 | uint32_t ssc_is_rx_ready(Ssc *p_ssc); 183 | uint32_t ssc_is_tx_enabled(Ssc *p_ssc); 184 | uint32_t ssc_is_rx_enabled(Ssc *p_ssc); 185 | //#if SAM3S || SAM4S 186 | #if SAM3S_SERIES || SAM4S_SERIES 187 | uint32_t ssc_is_rx_buf_end(Ssc *p_ssc); 188 | uint32_t ssc_is_tx_buf_end(Ssc *p_ssc); 189 | uint32_t ssc_is_rx_buf_full(Ssc *p_ssc); 190 | uint32_t ssc_is_tx_buf_empty(Ssc *p_ssc); 191 | Pdc *ssc_get_pdc_base(Ssc *p_ssc); 192 | #endif 193 | uint32_t ssc_write(Ssc *p_ssc, uint32_t ul_frame); 194 | uint32_t ssc_read(Ssc *p_ssc, uint32_t *ul_data); 195 | void ssc_write_sync_data(Ssc *p_ssc, uint32_t ul_frame); 196 | uint32_t ssc_read_sync_data(Ssc *p_ssc); 197 | //#if (SAM3XA || SAM3U) 198 | #if (SAM3XA_SERIES || SAM3U_SERIES) 199 | void *ssc_get_tx_access(Ssc *p_ssc); 200 | void *ssc_get_rx_access(Ssc *p_ssc); 201 | #endif 202 | void ssc_set_writeprotect(Ssc *p_ssc, uint32_t ul_enable); 203 | uint32_t ssc_get_writeprotect_status(Ssc *p_ssc); 204 | 205 | /// @cond 0 206 | /**INDENT-OFF**/ 207 | #ifdef __cplusplus 208 | } 209 | #endif 210 | /**INDENT-ON**/ 211 | /// @endcond 212 | 213 | #endif /* SSC_H_INCLUDED */ 214 | --------------------------------------------------------------------------------