From: lottaviano Date: Mon, 6 Jul 2009 16:40:37 +0000 (+0000) Subject: Move I2S at91 specific code to cpu/arm dir. X-Git-Tag: 2.2.0~224 X-Git-Url: https://codewiz.org/gitweb?a=commitdiff_plain;h=647e8b59350b817014d29f4b204a0a201824bf1b;p=bertos.git Move I2S at91 specific code to cpu/arm dir. git-svn-id: https://src.develer.com/svnoss/bertos/trunk@2744 38d2e660-2303-0410-9eaa-f027e97ec537 --- diff --git a/bertos/cpu/arm/drv/i2s_at91.c b/bertos/cpu/arm/drv/i2s_at91.c new file mode 100644 index 00000000..bdcd9852 --- /dev/null +++ b/bertos/cpu/arm/drv/i2s_at91.c @@ -0,0 +1,176 @@ +/** + * \file + * + * + * \brief I2S driver implementation. + * + * \version $Id$ + * \author Luca Ottaviano + */ + +#include "i2s.h" + +#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]; + +// the buffer in PDC next is play_buf2 +volatile bool is_second_buf_next; + +uint8_t *i2s_getBuffer(unsigned buf_num) +{ + LOG_INFO("getBuffer start\n"); + + if (i2s_isPlaying()) + { + ASSERT(0); + return 0; + } + + 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 (!i2s_isPlaying()) + { + ASSERT(0); + return 0; + } + + // wait PDC transmission end + if (!(SSC_SR & BV(SSC_ENDTX))) + return 0; + + 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; + } + + if (ret_buf) + { + SSC_TNPR = (reg32_t) ret_buf; + SSC_TNCR = PDC_COUNT; + } + 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); + timer_delay(10); + + SSC_PTCR = BV(PDC_TXTDIS); + SSC_TPR = (reg32_t)play_buf1; + 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); + + return true; +} + +#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) +{ + PIOA_PDR = BV(SSC_TK) | BV(SSC_TF) | BV(SSC_TD); + /* reset device */ + SSC_CR = BV(SSC_SWRST); + + SSC_CMR = MCK_DIV & SSC_DIV_MASK; + SSC_TCMR = SSC_CKS_DIV | SSC_CKO_CONT | SSC_CKG_NONE | DELAY | PERIOD | SSC_START_FALL_F; + SSC_TFMR = DATALEN | DATNB | FSLEN | BV(SSC_MSBF) | SSC_FSOS_NEGATIVE; + + /* Disable all irqs */ + SSC_IDR = 0xFFFFFFFF; + + /* Enable the SSC IRQ */ + AIC_IECR = BV(SSC_ID); + + /* enable i2s */ + PMC_PCER = BV(SSC_ID); + + /* Enable SSC */ + SSC_CR = BV(SSC_TXEN); +} diff --git a/bertos/cpu/arm/drv/i2s_at91.h b/bertos/cpu/arm/drv/i2s_at91.h new file mode 100644 index 00000000..2623702c --- /dev/null +++ b/bertos/cpu/arm/drv/i2s_at91.h @@ -0,0 +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 atmega8" + */ + +#ifndef I2S_H +#define I2S_H + +#include "cfg/cfg_i2s.h" + +#include +#include +#include + +/** + * 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); + +/** + * 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. + * + * 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. + * \return false on errors, true otherwise. + */ +bool i2s_start(void); + +INLINE bool i2s_isPlaying(void) +{ + return !(SSC_SR & BV(SSC_TXEMPTY)); +} + +#endif /* I2S_H */ diff --git a/bertos/drv/i2s.c b/bertos/drv/i2s.c deleted file mode 100644 index bdcd9852..00000000 --- a/bertos/drv/i2s.c +++ /dev/null @@ -1,176 +0,0 @@ -/** - * \file - * - * - * \brief I2S driver implementation. - * - * \version $Id$ - * \author Luca Ottaviano - */ - -#include "i2s.h" - -#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]; - -// the buffer in PDC next is play_buf2 -volatile bool is_second_buf_next; - -uint8_t *i2s_getBuffer(unsigned buf_num) -{ - LOG_INFO("getBuffer start\n"); - - if (i2s_isPlaying()) - { - ASSERT(0); - return 0; - } - - 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 (!i2s_isPlaying()) - { - ASSERT(0); - return 0; - } - - // wait PDC transmission end - if (!(SSC_SR & BV(SSC_ENDTX))) - return 0; - - 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; - } - - if (ret_buf) - { - SSC_TNPR = (reg32_t) ret_buf; - SSC_TNCR = PDC_COUNT; - } - 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); - timer_delay(10); - - SSC_PTCR = BV(PDC_TXTDIS); - SSC_TPR = (reg32_t)play_buf1; - 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); - - return true; -} - -#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) -{ - PIOA_PDR = BV(SSC_TK) | BV(SSC_TF) | BV(SSC_TD); - /* reset device */ - SSC_CR = BV(SSC_SWRST); - - SSC_CMR = MCK_DIV & SSC_DIV_MASK; - SSC_TCMR = SSC_CKS_DIV | SSC_CKO_CONT | SSC_CKG_NONE | DELAY | PERIOD | SSC_START_FALL_F; - SSC_TFMR = DATALEN | DATNB | FSLEN | BV(SSC_MSBF) | SSC_FSOS_NEGATIVE; - - /* Disable all irqs */ - SSC_IDR = 0xFFFFFFFF; - - /* Enable the SSC IRQ */ - AIC_IECR = BV(SSC_ID); - - /* enable i2s */ - PMC_PCER = BV(SSC_ID); - - /* Enable SSC */ - SSC_CR = BV(SSC_TXEN); -} diff --git a/bertos/drv/i2s.h b/bertos/drv/i2s.h deleted file mode 100644 index 2623702c..00000000 --- a/bertos/drv/i2s.h +++ /dev/null @@ -1,117 +0,0 @@ -/** - * \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 atmega8" - */ - -#ifndef I2S_H -#define I2S_H - -#include "cfg/cfg_i2s.h" - -#include -#include -#include - -/** - * 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); - -/** - * 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. - * - * 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. - * \return false on errors, true otherwise. - */ -bool i2s_start(void); - -INLINE bool i2s_isPlaying(void) -{ - return !(SSC_SR & BV(SSC_TXEMPTY)); -} - -#endif /* I2S_H */