From 47db6a7e9ce9351f13213926883e4d6561115c13 Mon Sep 17 00:00:00 2001 From: lottaviano Date: Fri, 3 Jul 2009 13:41:06 +0000 Subject: [PATCH] Complete SSC (i2s) driver import for at91sam7 cpu. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@2733 38d2e660-2303-0410-9eaa-f027e97ec537 --- bertos/cfg/cfg_i2s.h | 73 ++++++++++ bertos/cpu/arm/io/at91_ssc.h | 265 +++++++++++++++++++++++++++++++++++ bertos/cpu/arm/io/at91sam7.h | 48 ++++++- bertos/drv/i2s.c | 246 +++++++++++++++----------------- bertos/drv/i2s.h | 109 +++++++++++++- 5 files changed, 599 insertions(+), 142 deletions(-) create mode 100644 bertos/cfg/cfg_i2s.h create mode 100644 bertos/cpu/arm/io/at91_ssc.h diff --git a/bertos/cfg/cfg_i2s.h b/bertos/cfg/cfg_i2s.h new file mode 100644 index 00000000..2188e88c --- /dev/null +++ b/bertos/cfg/cfg_i2s.h @@ -0,0 +1,73 @@ +/** + * \file + * + * + * \brief Configuration file for I2S module. + * + * \version $Id$ + * + * \author Luca Ottaviano + */ + +#ifndef CFG_I2S_H +#define CFG_I2S_H + +/** + * Length of each play buffer. + * + * $WIZ$ type = "int" + */ +#define CONFIG_PLAY_BUF_LEN 8192 + +/** + * Sampling frequency of the audio file. + * + * $WIZ$ type = "int" + */ +#define CONFIG_SAMPLE_FREQ 44100 + +/** + * Module logging level. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_level" + */ +#define I2S_LOG_LEVEL LOG_LVL_INFO + +/** + * Module logging format. + * + * $WIZ$ type = "enum" + * $WIZ$ value_list = "log_format" + */ +#define I2S_LOG_FORMAT LOG_FMT_TERSE + +#endif /* CFG_I2S_H */ diff --git a/bertos/cpu/arm/io/at91_ssc.h b/bertos/cpu/arm/io/at91_ssc.h new file mode 100644 index 00000000..51a34bc5 --- /dev/null +++ b/bertos/cpu/arm/io/at91_ssc.h @@ -0,0 +1,265 @@ +/** + * \file + * + * + * \version $Id$ + * + * \author Luca Ottaviano + * + * AT91SAM7 SSC register definitions. + * This file is based on NUT/OS implementation. See license below. + */ + +/* + * Copyright (C) 2006-2007 by egnite Software GmbH. All rights reserved. + * + * 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. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY EGNITE SOFTWARE GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL EGNITE + * SOFTWARE GMBH OR CONTRIBUTORS 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. + * + * For additional information see http://www.ethernut.de/ + */ + +#ifndef AT91_SSC_H +#define AT91_SSC_H + +#include + +/** + * SSC Control Register + */ +/*\{*/ +#define SSC_CR_OFF 0x00000000 ///< Control register offset. + +#define SSC_RXEN 0 ///< Receive enable. +#define SSC_RXDIS 1 ///< Receive disable. +#define SSC_TXEN 8 ///< Transmit enable. +#define SSC_TXDIS 9 ///< Transmit disable. +#define SSC_SWRST 15 ///< Software reset. +/*\}*/ + +/** + * SSC Clock Mode Register + */ +/*\{*/ +#define SSC_CMR_OFF 0x00000004 ///< Clock mode register offset. + +#define SSC_DIV_MASK 0x00000FFF ///< Clock divider. +/*\}*/ + +/** + * SSC Receive/Transmit Clock Mode Register + */ +/*\{*/ +#define SSC_RCMR_OFF 0x00000010 ///< Receive clock mode register offset. +#define SSC_TCMR_OFF 0x00000018 ///< Transmit clock mode register offset. + +#define SSC_CKS_MASK 0x00000003 ///< Receive clock selection. +#define SSC_CKS_DIV 0x00000000 ///< Divided clock. +#define SSC_CKS_CLK 0x00000001 ///< RK/TK clock signal. +#define SSC_CKS_PIN 0x00000002 ///< TK/RK pin. +#define SSC_CKO_MASK 0x0000001C ///< Receive clock output mode selection. +#define SSC_CKO_NONE 0x00000000 ///< None. +#define SSC_CKO_CONT 0x00000004 ///< Continous receive clock. +#define SSC_CKO_TRAN 0x00000008 ///< Receive clock only during data transfers. +#define SSC_CKI 5 ///< Receive clock inversion. +#define SSC_CKG_MASK 0x000000C0 ///< Receive clock gating selection. +#define SSC_CKG_NONE 0x00000000 ///< None, continous clock. +#define SSC_CKG_FL 0x00000040 ///< Continous receive clock. +#define SSC_CKG_FH 0x00000080 ///< Receive clock only during data transfers. +#define SSC_START_MASK 0x00000F00 ///< Receive start selection. +#define SSC_START_CONT 0x00000000 ///< Receive start as soon as enabled. +#define SSC_START_TX 0x00000100 ///< Receive start on transmit start. +#define SSC_START_RX 0x00000100 ///< Receive start on receive start. +#define SSC_START_LOW_F 0x00000200 ///< Receive start on low level RF. +#define SSC_START_HIGH_F 0x00000300 ///< Receive start on high level RF. +#define SSC_START_FALL_F 0x00000400 ///< Receive start on falling edge RF. +#define SSC_START_RISE_F 0x00000500 ///< Receive start on rising edge RF. +#define SSC_START_LEVEL_F 0x00000600 ///< Receive start on any RF level change. +#define SSC_START_EDGE_F 0x00000700 ///< Receive start on any RF edge. +#define SSC_START_COMP0 0x00000800 ///< Receive on compare 0. +#define SSC_STOP 12 ///< Receive stop selection. +#define SSC_STTDLY_MASK 0x00FF0000 ///< Receive start delay. +#define SSC_STTDLY_SHIFT 16 ///< Least significant bit of receive start delay. +#define SSC_PERIOD_MASK 0xFF000000 ///< Receive period divider selection. +#define SSC_PERIOD_SHIFT 24 ///< Least significant bit of receive period divider selection. +/*\}*/ + +/** + * SSC Receive/Transmit Frame Mode Registers + */ +/*\{*/ +#define SSC_RFMR_OFF 0x00000014 ///< Receive frame mode register offset. +#define SSC_TFMR_OFF 0x0000001C ///< Transmit frame mode register offset. + +#define SSC_DATLEN_MASK 0x0000001F ///< Data length. +#define SSC_LOOP 5 ///< Receiver loop mode. +#define SSC_DATDEF 5 ///< Transmit default value. + +#define SSC_MSBF 7 ///< Most significant bit first. +#define SSC_DATNB_MASK 0x00000F00 ///< Data number per frame. +#define SSC_DATNB_SHIFT 8 ///< Least significant bit of data number per frame. +#define SSC_FSLEN_MASK 0x000F0000 ///< Receive frame sync. length. +#define SSC_FSLEN_SHIFT 16 ///< Least significant bit of receive frame sync. length. +#define SSC_FSOS 0x00700000 ///< Receive frame sync. output selection. +#define SSC_FSOS_NONE 0x00000000 ///< No frame sync. Line set to input. +#define SSC_FSOS_NEGATIVE 0x00100000 ///< Negative pulse. +#define SSC_FSOS_POSITIVE 0x00200000 ///< Positive pulse. +#define SSC_FSOS_LOW 0x00300000 ///< Low during transfer. +#define SSC_FSOS_HIGH 0x00400000 ///< High during transfer. +#define SSC_FSOS_TOGGLE 0x00500000 ///< Toggling at each start. +#define SSC_FSDEN 23 ///< Frame sync. data enable. +#define SSC_FSEDGE 24 ///< Frame sync. edge detection. +/*\}*/ + +/** + * SSC Receive Holding Register + */ +/*\{*/ +#define SSC_RHR_OFF 0x00000020 ///< Receive holding register offset. +/*\}*/ + +/** + * SSC Transmit Holding Register + */ +/*\{*/ +#define SSC_THR_OFF 0x00000024 ///< Transmit holding register offset. +/*\}*/ + +/** + * SSC Receive Sync. Holding Register + */ +/*\{*/ +#define SSC_RSHR_OFF 0x00000030 ///< Receive sync. holding register offset. +/*\}*/ + +/** + * SSC Transmit Sync. Holding Register + */ +/*\{*/ +#define SSC_TSHR_OFF 0x00000034 ///< Transmit sync. holding register offset. +/*\}*/ + +/** + * SSC Receive Compare 0 Register + */ +/*\{*/ +#define SSC_RC0R_OFF 0x00000038 ///< Receive compare 0 register offset. +/*\}*/ + +/** + * SSC Receive Compare 1 Register + */ +/*\{*/ +#define SSC_RC1R_OFF 0x0000003C ///< Receive compare 1 register offset. +/*\}*/ + +/** + * SSC Status and Interrupt Register + */ +/*\{*/ +#define SSC_SR_OFF 0x00000040 ///< Status register offset. +#define SSC_IER_OFF 0x00000044 ///< Interrupt enable register offset. +#define SSC_IDR_OFF 0x00000048 ///< Interrupt disable register offset. +#define SSC_IMR_OFF 0x0000004C ///< Interrupt mask register offset. + +#define SSC_TXRDY 0 ///< Transmit ready. +#define SSC_TXEMPTY 1 ///< Transmit empty. +#define SSC_ENDTX 2 ///< End of transmission. +#define SSC_TXBUFE 3 ///< Transmit buffer empty. +#define SSC_RXRDY 4 ///< Receive ready. +#define SSC_OVRUN 5 ///< Receive overrun. +#define SSC_ENDRX 6 ///< End of receiption. +#define SSC_RXBUFF 7 ///< Receive buffer full. +#define SSC_CP0 8 ///< Compare 0. +#define SSC_CP1 9 ///< Compare 1. +#define SSC_TXSYN 10 ///< Transmit sync. +#define SSC_RXSYN 11 ///< Receive sync. +#define SSC_TXENA 16 ///< Transmit enable. +#define SSC_RXENA 17 ///< Receive enable. + + +#if defined(SSC_BASE) + #define SSC_CR (*((reg32_t *)(SSC_BASE + SSC_CR_OFF))) ///< Control register address. + #define SSC_CMR (*((reg32_t *)(SSC_BASE + SSC_CMR_OFF))) ///< Clock mode register address. + #define SSC_RCMR (*((reg32_t *)(SSC_BASE + SSC_RCMR_OFF))) ///< Receive clock mode register address. + #define SSC_TCMR (*((reg32_t *)(SSC_BASE + SSC_TCMR_OFF))) ///< Transmit clock mode register address. + #define SSC_RFMR (*((reg32_t *)(SSC_BASE + SSC_RFMR_OFF))) ///< Receive frame mode register address. + #define SSC_TFMR (*((reg32_t *)(SSC_BASE + SSC_TFMR_OFF))) ///< Transmit frame mode register address. + #define SSC_RHR (*((reg32_t *)(SSC_BASE + SSC_RHR_OFF))) ///< Receive holding register address. + #define SSC_THR (*((reg32_t *)(SSC_BASE + SSC_THR_OFF))) ///< Transmit holding register address. + #define SSC_RSHR (*((reg32_t *)(SSC_BASE + SSC_RSHR_OFF))) ///< Receive sync. holding register address. + #define SSC_TSHR (*((reg32_t *)(SSC_BASE + SSC_TSHR_OFF))) ///< Transmit sync. holding register address. + #define SSC_RC0R (*((reg32_t *)(SSC_BASE + SSC_RC0R_OFF))) ///< Receive compare 0 register address. + #define SSC_RC1R (*((reg32_t *)(SSC_BASE + SSC_RC1R_OFF))) ///< Receive compare 1 register address. + #define SSC_SR (*((reg32_t *)(SSC_BASE + SSC_SR_OFF))) ///< Status register address. + #define SSC_IER (*((reg32_t *)(SSC_BASE + SSC_IER_OFF))) ///< Interrupt enable register address. + #define SSC_IDR (*((reg32_t *)(SSC_BASE + SSC_IDR_OFF))) ///< Interrupt disable register address. + #define SSC_IMR (*((reg32_t *)(SSC_BASE + SSC_IMR_OFF))) ///< Interrupt mask register address. + #if defined(SSC_HAS_PDC) + #define SSC_RPR (*((reg32_t *)(SSC_BASE + PERIPH_RPR_OFF))) ///< PDC receive pointer register address. + #define SSC_RCR (*((reg32_t *)(SSC_BASE + PERIPH_RCR_OFF))) ///< PDC receive counter register address. + #define SSC_TPR (*((reg32_t *)(SSC_BASE + PERIPH_TPR_OFF))) ///< PDC transmit pointer register address. + #define SSC_TCR (*((reg32_t *)(SSC_BASE + PERIPH_TCR_OFF))) ///< PDC transmit counter register address. + #define SSC_RNPR (*((reg32_t *)(SSC_BASE + PERIPH_RNPR_OFF))) ///< PDC receive next pointer register address. + #define SSC_RNCR (*((reg32_t *)(SSC_BASE + PERIPH_RNCR_OFF))) ///< PDC receive next counter register address. + #define SSC_TNPR (*((reg32_t *)(SSC_BASE + PERIPH_TNPR_OFF))) ///< PDC transmit next pointer register address. + #define SSC_TNCR (*((reg32_t *)(SSC_BASE + PERIPH_TNCR_OFF))) ///< PDC transmit next counter register address. + #define SSC_PTCR (*((reg32_t *)(SSC_BASE + PERIPH_PTCR_OFF))) ///< PDC transfer control register address. + #define SSC_PTSR (*((reg32_t *)(SSC_BASE + PERIPH_PTSR_OFF))) ///< PDC transfer status register address. + #endif /* SSC_HAS_PDC */ + +#endif /* SSC_BASE */ + + +#endif /* AT91_SSC_H */ diff --git a/bertos/cpu/arm/io/at91sam7.h b/bertos/cpu/arm/io/at91sam7.h index dbc6f449..c983f50b 100644 --- a/bertos/cpu/arm/io/at91sam7.h +++ b/bertos/cpu/arm/io/at91sam7.h @@ -122,8 +122,25 @@ #define SSC_HAS_PDC 1 #define USART_HAS_PDC 1 + /* PDC registers */ + #define PERIPH_RPR_OFF 0x100 ///< Receive Pointer Register. + #define PERIPH_RCR_OFF 0x104 ///< Receive Counter Register. + #define PERIPH_TPR_OFF 0x108 ///< Transmit Pointer Register. + #define PERIPH_TCR_OFF 0x10C ///< Transmit Counter Register. + #define PERIPH_RNPR_OFF 0x110 ///< Receive Next Pointer Register. + #define PERIPH_RNCR_OFF 0x114 ///< Receive Next Counter Register. + #define PERIPH_TNPR_OFF 0x118 ///< Transmit Next Pointer Register. + #define PERIPH_TNCR_OFF 0x11C ///< Transmit Next Counter Register. + #define PERIPH_PTCR_OFF 0x120 ///< PDC Transfer Control Register. + #define PERIPH_PTSR_OFF 0x124 ///< PDC Transfer Status Register. + + #define PDC_RXTEN 0 + #define PDC_RXTDIS 1 + #define PDC_TXTEN 8 + #define PDC_TXTDIS 9 + #else - #error No base addrese register definition for selected ARM CPU + #error No base address register definition for selected ARM CPU #endif @@ -141,6 +158,7 @@ #include "at91_pwm.h" #include "at91_spi.h" #include "at91_twi.h" +#include "at91_ssc.h" //TODO: add other peripherals /** @@ -245,6 +263,34 @@ #endif /*\}*/ +/** + * SSC pins name + *\{ + */ +#if CPU_ARM_SAM7S_LARGE + + #define SSC_TF 15 // PA15 + #define SSC_TK 16 // PA16 + #define SSC_TD 17 // PA17 + #define SSC_RD 18 // PA18 + #define SSC_RK 19 // PA19 + #define SSC_RF 20 // PA20 + +#elif CPU_ARM_SAM7X + + #define SSC_TF 21 // PA21 + #define SSC_TK 22 // PA22 + #define SSC_TD 23 // PA23 + #define SSC_RD 24 // PA24 + #define SSC_RK 25 // PA25 + #define SSC_RF 26 // PA26 + +#else + #error No SSC pins name definition for selected ARM CPU + +#endif +/*\}*/ + /** * Timer counter pins definition. *\{ diff --git a/bertos/drv/i2s.c b/bertos/drv/i2s.c index 328f9f00..bdcd9852 100644 --- a/bertos/drv/i2s.c +++ b/bertos/drv/i2s.c @@ -1,168 +1,160 @@ - +/** + * \file + * + * + * \brief I2S driver implementation. + * + * \version $Id$ + * \author Luca Ottaviano + */ #include "i2s.h" #include -#include #include #include +#define DATALEN (15 & SSC_DATLEN_MASK) +// FIXME: this is not correct for 16 <= DATALEN < 24 +#define PDC_DIV ((DATALEN / 8) + 1) +/* PDC_DIV must be 1, 2 or 4, which are the bytes that are transferred + * each time the PDC reads from memory. + */ +STATIC_ASSERT(PDC_DIV % 2 == 0); +#define PDC_COUNT (CONFIG_PLAY_BUF_LEN / PDC_DIV) + static uint8_t play_buf1[CONFIG_PLAY_BUF_LEN]; static uint8_t play_buf2[CONFIG_PLAY_BUF_LEN]; -/* |x|x|CONT|SECOND|FIRST|IS_PLAYING|CUR_BUF| */ -/* |7|6| 5 | 4 | 3 | 2 | 1 0 | */ -static uint8_t status; -#define CURRENT_BUF 0x03 -#define IS_PLAYING 2 -#define FIRST_BUF_FULL 3 -#define SECOND_BUF_FULL 4 -#define CONTINUE_PLAY 5 - -INLINE bool is_buffer_full(int bv) -{ - return status & BV(bv); -} +// the buffer in PDC next is play_buf2 +volatile bool is_second_buf_next; uint8_t *i2s_getBuffer(unsigned buf_num) { - kprintf("getBuffer start\n"); - if (status & BV(IS_PLAYING)) - return 0; + LOG_INFO("getBuffer start\n"); - if ((buf_num == I2S_FIRST_BUF) && !is_buffer_full(FIRST_BUF_FULL)) + if (i2s_isPlaying()) { - status |= BV(FIRST_BUF_FULL); - kprintf("status [0x%02X]\n", status); - return play_buf1; + ASSERT(0); + return 0; } - else if ((buf_num == I2S_SECOND_BUF) && !is_buffer_full(SECOND_BUF_FULL)) - { - status |= BV(SECOND_BUF_FULL); - kprintf("status [0x%02X]\n", status); + + if (buf_num == I2S_SECOND_BUF) return play_buf2; - } + else if (buf_num == I2S_FIRST_BUF) + return play_buf1; else return 0; } uint8_t *i2s_getFreeBuffer(void) { - if (!(status & BV(IS_PLAYING))) + if (!i2s_isPlaying()) + { + ASSERT(0); return 0; + } - // disable irq - // ... - // set continue flag - // ... - // set buf_full flag - // ... - // enable irq - // ... - // return the buffer - - if ((status & CURRENT_BUF) == I2S_FIRST_BUF && !is_buffer_full(SECOND_BUF_FULL)) - return play_buf2; - else if ((status & CURRENT_BUF) == I2S_SECOND_BUF && !is_buffer_full(FIRST_BUF_FULL)) - return play_buf1; - else + // wait PDC transmission end + if (!(SSC_SR & BV(SSC_ENDTX))) return 0; -} -INLINE void setCurrentBuffer(int buffer) -{ - status &= ~CURRENT_BUF; - status |= CURRENT_BUF & buffer; -} + uint8_t *ret_buf = 0; + // the last time we got called, the second buffer was in PDC next + if (is_second_buf_next) + { + is_second_buf_next = false; + ret_buf = play_buf1; + } + // the last time the first buffer was in PDC next + else + { + is_second_buf_next = true; + ret_buf = play_buf2; + } -// code irq callback -static void i2s_dma_tx_irq_handler(void) __attribute__ ((interrupt)); -static void i2s_dma_tx_irq_handler(void) -{ - /* - if (status & BV(CONTINUE_PLAY)) + if (ret_buf) { - kprintf("irq\n"); - if ((status & CURRENT_BUF) == I2S_FIRST_BUF) - { - SSC_PTCR = BV(PDC_TXTDIS); - SSC_TPR = (reg32_t)play_buf2; - SSC_TCR = CONFIG_PLAY_BUF_LEN; - SSC_PTCR = BV(PDC_TXTEN); - - setCurrentBuffer(I2S_SECOND_BUF); - status &= ~BV(FIRST_BUF_FULL); - status &= ~BV(CONTINUE_PLAY); - } - // TODO: refactor. - else - { - SSC_PTCR = BV(PDC_TXTDIS); - SSC_TPR = (reg32_t)play_buf1; - SSC_TCR = CONFIG_PLAY_BUF_LEN; - SSC_PTCR = BV(PDC_TXTEN); - - setCurrentBuffer(I2S_FIRST_BUF); - status &= ~BV(SECOND_BUF_FULL); - status &= ~BV(CONTINUE_PLAY); - } + SSC_TNPR = (reg32_t) ret_buf; + SSC_TNCR = PDC_COUNT; } - */ - AIC_EOICR = 0; + return ret_buf; } bool i2s_start(void) { + /* Some time must pass between disabling and enabling again the transmission + * on SSC. A good empirical value seems >15 us. We try to avoid putting an + * explicit delay, instead we disable the transmitter when a sound finishes + * and hope that the delay has passed before we enter here again. + */ SSC_CR = BV(SSC_TXDIS); - //kprintf("%08lX\n", SSC_TCMR); - timer_udelay(15); + timer_delay(10); + SSC_PTCR = BV(PDC_TXTDIS); SSC_TPR = (reg32_t)play_buf1; - SSC_TCR = CONFIG_PLAY_BUF_LEN / 2; + SSC_TCR = PDC_COUNT; + SSC_TNPR = (reg32_t)play_buf2; + SSC_TNCR = PDC_COUNT; + is_second_buf_next = true; + SSC_PTCR = BV(PDC_TXTEN); + /* enable output */ SSC_CR = BV(SSC_TXEN); -// ASSERT(SSC_PTSR & BV(PDC_TXTEN)); - /* - kprintf("i2s_start start\n"); - if (status & (BV(FIRST_BUF_FULL) | BV(SECOND_BUF_FULL))) - { - setCurrentBuffer(I2S_FIRST_BUF); - SSC_PTCR = BV(PDC_TXTDIS); - SSC_TPR = (reg32_t)play_buf1; - SSC_TCR = CONFIG_PLAY_BUF_LEN; - - status |= BV(IS_PLAYING); - status |= BV(CONTINUE_PLAY); - kprintf("start: status [0x%02X]\n", status); - SSC_PTCR = BV(PDC_TXTEN); - - return true; - } - else - { - kprintf("start: buffers are not full\n"); - return false; - } - */ return true; } -// TODO renderlo configurabile -#define MCK_DIV 16 -#define DELAY ((1 << SSC_STTDLY_SHIFT) & SSC_STTDLY_MASK) -#define PERIOD ((15 << (SSC_PERIOD_SHIFT)) & SSC_PERIOD_MASK) -#define DATALEN (15 & SSC_DATLEN_MASK) -#define DATNB ((1 << SSC_DATNB_SHIFT) & SSC_DATNB_MASK) -#define FSLEN ((15 << SSC_FSLEN_SHIFT) & SSC_FSLEN_MASK) +#define CONFIG_SAMPLE_FREQ 44100 +#define BITS_PER_CHANNEL 16 +#define N_OF_CHANNEL 2 +// TODO: check the computed value? +/* The last parameter (2) is due to the hadware on at91sam7s. */ +#define MCK_DIV (CPU_FREQ / CONFIG_SAMPLE_FREQ / BITS_PER_CHANNEL / N_OF_CHANNEL / 2) + +#define CONFIG_DELAY 1 +#define CONFIG_PERIOD 15 +#define CONFIG_DATNB 1 +#define CONFIG_FSLEN 15 + +#define DELAY ((CONFIG_DELAY << SSC_STTDLY_SHIFT) & SSC_STTDLY_MASK) +#define PERIOD ((CONFIG_PERIOD << (SSC_PERIOD_SHIFT)) & SSC_PERIOD_MASK) +#define DATNB ((CONFIG_DATNB << SSC_DATNB_SHIFT) & SSC_DATNB_MASK) +#define FSLEN ((CONFIG_FSLEN << SSC_FSLEN_SHIFT) & SSC_FSLEN_MASK) #define SSC_DMA_IRQ_PRIORITY 5 void i2s_init(void) { - //TODO sistemare i pin - PIOA_PDR = BV(SPI1_SPCK) | BV(SPI1_MOSI) | BV(SPI1_NPCS0); + PIOA_PDR = BV(SSC_TK) | BV(SSC_TF) | BV(SSC_TD); /* reset device */ SSC_CR = BV(SSC_SWRST); @@ -172,27 +164,13 @@ void i2s_init(void) /* Disable all irqs */ SSC_IDR = 0xFFFFFFFF; - /* Set the vector. */ - AIC_SVR(SSC_ID) = i2s_dma_tx_irq_handler; - /* Initialize to edge triggered with defined priority. */ - AIC_SMR(SPI0_ID) = AIC_SRCTYPE_INT_EDGE_TRIGGERED | SSC_DMA_IRQ_PRIORITY; + /* Enable the SSC IRQ */ - AIC_IDCR = BV(SSC_ID); - /* Enable interrupt on tx buffer empty */ - SSC_IER = BV(SSC_ENDTX); + AIC_IECR = BV(SSC_ID); /* enable i2s */ PMC_PCER = BV(SSC_ID); - /* set current buffer to 1 */ - status = 0x01; - for (int i = 0; i < CONFIG_PLAY_BUF_LEN; ++i) - { - //uint32_t tmp = 0x5555; - //uint32_t tmp2 = 0x9999; - play_buf1[i] = i; - //play_buf1[i+4] = tmp2; - } + /* Enable SSC */ + SSC_CR = BV(SSC_TXEN); } - - diff --git a/bertos/drv/i2s.h b/bertos/drv/i2s.h index c3cfef50..1729b927 100644 --- a/bertos/drv/i2s.h +++ b/bertos/drv/i2s.h @@ -1,22 +1,117 @@ +/** + * \file + * + * + * \brief I2S driver functions. + * + * This driver uses a double buffering technique to keep i2s bus busy. First fill in the two buffers + * using i2s_getBuffer(), then start audio playing with i2s_start(). Then call i2s_getFreeBuffer() + * until you have finished your samples. The reproduction will automatically stop if you don't + * call i2s_getFreeBuffer() frequently enough. + * + * Example: + * \code + * // fill in the buffers before start + * buf = i2s_getBuffer(I2S_FIRST_BUF); + * // ... + * buf = i2s_getBuffer(I2S_SECOND_BUF); + * // ... + * // here the driver will play only the first two buffers... + * i2s_start(); + * // ...call getFreeBuffer() to continue playing. + * while (!(buf = i2s_getFreeBuffer())) + * ; + * // now fill the buffer again + * \endcode + * + * \version $Id$ + * \author Luca Ottaviano + * + * $WIZ$ module_name = "i2s" + * $WIZ$ module_configuration = "bertos/cfg/cfg_i2s.h" + * $WIZ$ module_supports = "not atmega103 and not atmega168 and not at91" + */ + #ifndef I2S_H #define I2S_H +#include "cfg/cfg_i2s.h" + #include +#include +#include -#define CONFIG_PLAY_BUF_LEN 64 -#define I2S_FIRST_BUF 1 -#define I2S_SECOND_BUF 2 +/** + * First buffer. + */ +#define I2S_FIRST_BUF 0 +/** + * Second buffer. + */ +#define I2S_SECOND_BUF 1 +/** + * Initializes the module and sets current buffer to I2S_FIRST_BUF. + */ void i2s_init(void); -/* Low level call that returns one of the two buffers or NULL if none is available */ +/** + * Returns one of the two buffers or NULL if none is available. + * + * You can't call this function if you have already started the player. + * \param buf_num The number of the buffer, ie I2S_FIRST_BUF or I2S_SECOND_BUF. + * \return A pointer to the buffer if the buffer is available (not full), 0 on errors + */ uint8_t *i2s_getBuffer(unsigned buf_num); -/* Returns a buffer that will be played after the current one. Blocking call */ +/** + * Returns a buffer that will be played after the current one. + * + * You should fill it faster than your reproduction time. You can't call this function + * if the player is not running + * \return The next buffer to be played, 0 if both are busy. + */ uint8_t *i2s_getFreeBuffer(void); -/* Starts playing from I2S_FIRST_BUFFER. You must have filled both buffers before calling this - * function. Does nothing if already playing. */ +/** + * Starts playing from I2S_FIRST_BUFFER. + * + * You must have filled both buffers before calling this function. Does nothing if already playing. + * \return false on errors, true otherwise. + */ bool i2s_start(void); +INLINE bool i2s_isPlaying(void) +{ + return !(SSC_SR & BV(SSC_TXEMPTY)); +} + #endif /* I2S_H */ -- 2.25.1