From 3456c9e10432dd70cd1ec2f82a6a3cc19f009a15 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 13 Sep 2014 15:43:46 +0200 Subject: Some I2S interface cleaning up. --- firmware/drivers/i2s.c | 108 +++++++------------------------ firmware/drivers/i2s.h | 172 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 86 deletions(-) (limited to 'firmware') diff --git a/firmware/drivers/i2s.c b/firmware/drivers/i2s.c index 7c87133..df32801 100644 --- a/firmware/drivers/i2s.c +++ b/firmware/drivers/i2s.c @@ -26,24 +26,16 @@ */ #include "i2s.h" -static void i2s_set_power(int power) +void i2s_set_power(int power) { // 1. Enable I²S in PCONP register. Set PCI2S to 1 (see table 46, pg. 64) // Remark: On reset, the I²S interface is disabled (PCI2S = 0). uint32_t *pconp = (uint32_t *)0x400FC0C4; *pconp &= ~(0b1 << 27); // Clear bit - *pconp |= ~( (power & 0b1) << 27); // Set bit + *pconp |= ~( ((power != 0) & 0b1) << 27); // Set bit } -typedef enum { - I2S_CCLK_4 = 0b00, // PCLK_peripheral = CCLK/4 - I2S_CCLK = 0b01, // PCLK_peripheral = CCLK - I2S_CCLK_2 = 0b10, // PCLK_peripheral = CCLK/2 - I2S_CCLK_8 = 0b11, // PCLK_peripheral = CCLK/8, except for CAN1, CAN2, and - // CAN filtering when 0b11 selects = CCLK/6. -} i2s_clksel_t; - -static void i2s_set_clksel(i2s_clksel_t sel) +void i2s_set_clksel(i2s_clksel_t sel) { // 2. Clock: In PCLKSEL1 select PCLK_I2S. (see table 41, pg. 57) uint32_t *pclksel1 = (uint32_t*)0x400FC1AC; @@ -51,7 +43,7 @@ static void i2s_set_clksel(i2s_clksel_t sel) *pclksel1 |= ( (sel & 0b11) << 22); // Set bits } -static void i2s_set_pinsel() +void i2s_set_pinsel() { //3. Pins: Select I²S pins and their modes in PINSEL0 to PINSEL4 and PINMODE0 // to PINMODE4 (see Section 8.5). @@ -62,11 +54,11 @@ static void i2s_set_pinsel() // Reset ports *pinsel0 &= ~( - /* // We only do TX + // TX: (0b11 << 8) | // P0.4 (0b11 << 10) | // P0.5 (0b11 << 12) | // P0.6 - */ + // RX: (0b11 << 14) | // P0.7 (0b11 << 16) | // P0.8 (0b11 << 18)) // P0.9 @@ -75,11 +67,11 @@ static void i2s_set_pinsel() // Set ports *pinsel0 |= ( - /* // We only do TX + // TX: (0b01 << 8) | // P0.4: I2SRX_CLK (0b01 << 10) | // P0.5: I2SRX_WS (0b01 << 12) | // P0.6: I2SRX_SDA - */ + // RX: (0b01 << 14) | // P0.7: I2STX_CLK (0b01 << 16) | // P0.8: I2STX_WS (0b01 << 18)) // P0.9: I2STX_SDA @@ -99,23 +91,7 @@ static void i2s_set_pinsel() ; } -typedef enum { - WW_8_BIT = 0b00, // 8-bit data - WW_16_BIT = 0b01, // 16-bit data - WW_RESERVED = 0b10, // Reserved, do not use this setting - WW_32_BIT = 0b11, // 32-bit data -} wordwidth_t; - -typedef enum { - CH_MONO = 0b1, // Data is mono format - CH_STEREO = 0b0, // Data is stereo format -} channels_t; - -/** - * Sets DAO register values and stops/resets the bus. - */ -static void i2s_set_dao_register(wordwidth_t ww, - channels_t ch) +void i2s_set_dao_register(wordwidth_t ww, channels_t ch) { // See table 405, pg. 476 uint32_t *i2sdao = (uint32_t*)0x400A8000; @@ -130,12 +106,7 @@ static void i2s_set_dao_register(wordwidth_t ww, ; } -/* -** - * Sets DAI register values and stops/resets the bus. - * -static void i2s_set_dai_register(wordwidth_t ww, - channels_t ch) +void i2s_set_dai_register(wordwidth_t ww, channels_t ch) { // See table 406, pg. 477 uint32_t *i2sdai = (uint32_t*)0x400A8004; @@ -149,53 +120,36 @@ static void i2s_set_dai_register(wordwidth_t ww, (0b0 << 15) // no mute ; } -*/ -/** - * I2STXMCLK = PCLK_I2S * (X/Y) /2 - */ -static void i2s_set_tx_rate(uint8_t y_div, uint8_t x_div) +void i2s_set_tx_rate(uint8_t y_div, uint8_t x_div) { // See table 413, pg. 480 uint32_t *i2stxrate = (uint32_t*)0x400A8020; *i2stxrate = y_div | (x_div << 8); } -/* -static void i2s_set_rx_rate(uint8_t y_div, uint8_t x_div) +void i2s_set_rx_rate(uint8_t y_div, uint8_t x_div) { // See table 414, pg. 481 uint32_t *i2srxrate = (uint32_t*)0x400A8024; *i2srxrate = y_div | (x_div << 8); } -*/ -/** - * I²S transmit bit rate. This value plus one is used to divide TX_MCLK to - * produce the transmit bit clock. - */ -static void i2s_set_tx_clock_bitrate(uint8_t bitrate) +void i2s_set_tx_clock_bitrate(uint8_t bitrate) { // See table 415, pg. 481 uint32_t *i2stxbitrate = (uint32_t*)0x400A8028; *i2stxbitrate = (bitrate & 0b111111); } -/* -static void i2s_set_rx_clock_bitrate(uint8_t bitrate) +void i2s_set_rx_clock_bitrate(uint8_t bitrate) { // See table 416, pg. 481 uint32_t *i2srxbitrate = (uint32_t*)0x400A802C; *i2srxbitrate = (bitrate & 0b111111); } -*/ - -typedef enum { - CLK_TX_SRC = 0b00, // Select the TX fractional rate divider clock output as the source - CLK_RX_MCLK = 0b10, // Select the RX_MCLK signal as the TX_MCLK clock source -} clksel_t; -static void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena) +void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena) { // See table 417, pg 482 uint32_t *i2stxmode = (uint32_t*)0x400A8030; @@ -205,19 +159,17 @@ static void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena) ; } -/* -static void i2s_set_rx_mode_control(clksel_t c, int _4pin, int mcena) +void i2s_set_rx_mode_control(clksel_t c, int _4pin, int mcena) { // See table 418, pg 483 uint32_t *i2srxmode = (uint32_t*)0x400A8034; - *i2stxmode = c | + *i2srxmode = c | ((_4pin & 0b1) << 2) | ((mcena & 0b1) << 3) ; } -*/ -static void i2s_tx_reset() +void i2s_tx_reset() { // See table 405, pg. 476 uint32_t *i2sdao = (uint32_t*)0x400A8000; @@ -245,8 +197,7 @@ void i2s_tx_start() ; } -/* -static void i2s_rx_reset() +void i2s_rx_reset() { // See table 406, pg. 477 uint32_t *i2sdai = (uint32_t*)0x400A8004; @@ -254,10 +205,8 @@ static void i2s_rx_reset() (0b1 << 4) // reset ; } -*/ -/* -static void i2s_rx_stop() +void i2s_rx_stop() { // See table 406, pg. 477 uint32_t *i2sdai = (uint32_t*)0x400A8004; @@ -265,9 +214,8 @@ static void i2s_rx_stop() (0b1 << 3) // stop bit ; } -*/ -/* -static void i2s_rx_start() + +void i2s_rx_start() { // See table 406, pg. 477 uint32_t *i2sdai = (uint32_t*)0x400A8004; @@ -275,18 +223,6 @@ static void i2s_rx_start() ~(0b1 << 3) // stop bit ; } - */ -/* - * A data sample in the FIFO consists of: - * - 1×32 bits in 8-bit or 16-bit stereo modes. - * - 1×32 bits in mono modes. - * - 2×32 bits, first left data, second right data, in 32-bit stereo modes. - * - * I2STXFIFO: 0x400A 8008 - 8 x 32 bit - * I2SRXFIFO: 0x400A 800C - 8 x 32 bit - * - * See figure 113, pg. 489, for examples. - */ /** * This bit reflects the presence of Receive Interrupt or Transmit Interrupt. diff --git a/firmware/drivers/i2s.h b/firmware/drivers/i2s.h index 6e3acf2..386476e 100644 --- a/firmware/drivers/i2s.h +++ b/firmware/drivers/i2s.h @@ -29,8 +29,180 @@ #include +/** + * Set power of I²S circuit. + * @param power Integer value 0 is off, nonzero values are on. + */ +void i2s_set_power(int power); + +/** + * Clock selection mode. See table 41, pg. 57. + */ +typedef enum { + I2S_CCLK_4 = 0b00, ///< PCLK_peripheral = CCLK/4 + I2S_CCLK = 0b01, ///< PCLK_peripheral = CCLK + I2S_CCLK_2 = 0b10, ///< PCLK_peripheral = CCLK/2 + I2S_CCLK_8 = 0b11, ///< PCLK_peripheral = CCLK/8 +} i2s_clksel_t; + +#define I2S_CCLK_6 ((i2s_clksel_t)0b11) ///< For CAN1, CAN2, and CAN filtering when 0b11 selects = CCLK/6. + +/** + * Set clock selection for I²S circuit. + * @param sel Clock selection mode. + */ +void i2s_set_clksel(i2s_clksel_t sel); + +/** + * Set pin selection for I²S circuit. + * RX: + * - P0.4: I2SRX_CLK + * - P0.5: I2SRX_WS + * - P0.6: I2SRX_SDA + * - P?.?: RX_MCLK (currently not set) + * TX: + * - P0.7: I2STX_CLK + * - P0.8: I2STX_WS + * - P0.9: I2STX_SDA + * - P4.28: TX_MCLK + */ +void i2s_set_pinsel(); + +typedef enum { + WW_8_BIT = 0b00, // 8-bit data + WW_16_BIT = 0b01, // 16-bit data + // WW_RESERVED = 0b10, // Reserved, do not use this setting + WW_32_BIT = 0b11, // 32-bit data +} wordwidth_t; + +typedef enum { + CH_MONO = 0b1, // Data is mono format + CH_STEREO = 0b0, // Data is stereo format +} channels_t; + +/** + * Sets DAO register values and stops/resets the bus. + * @param ww Word width of pcm values. + * @param ch Number of channels (mono/stereo) + */ +void i2s_set_dao_register(wordwidth_t ww, channels_t ch); + +/** + * Sets DAI register values and stops/resets the bus. + * @param ww Word width of pcm values. + * @param ch Number of channels (mono/stereo) + */ +void i2s_set_dai_register(wordwidth_t ww, channels_t ch); + +/** + * Set clock transmit rate as PCLK_I2S * (X/Y) / 2 + * @param y_div I²S transmit MCLK rate denominator. This value is used to + * divide PCLK to produce the transmit MCLK. Eight bits of fractional divide + * supports a wide range of possibilities. A value of 0 stops the clock. + * @param x_div I²S transmit MCLK rate numerator. This value is used to + * multiply PCLK by to produce the transmit MCLK. A value of 0 stops the clock. + * Eight bits of fractional divide supports a wide range of possibilities. + * Note: the resulting ratio X/Y is divided by 2. + */ +void i2s_set_tx_rate(uint8_t y_div, uint8_t x_div); + +/** + * Set clock receive rate as PCLK_I2S * (X/Y) / 2 + * @param y_div I²S receive MCLK rate denominator. This value is used to divide + * PCLK to produce the receive MCLK. Eight bits of fractional divide supports + * a wide range of possibilities. A value of 0 stops the clock. + * @param x_div I²S receive MCLK rate numerator. This value is used to multiply + * PCLK by to produce the receive MCLK. A value of 0 stops the clock. Eight + * bits of fractional divide supports a wide range of possibilities. + * Note: the resulting ratio X/Y is divided by 2. + */ +void i2s_set_rx_rate(uint8_t y_div, uint8_t x_div); + +/** + * I²S transmit bit rate. + * @param bitrate This value plus one is used to divide TX_MCLK to produce the + * transmit bit clock. + */ +void i2s_set_tx_clock_bitrate(uint8_t bitrate); + +/** + * I²S receive bit rate. + * @param bitrate This value plus one is used to divide RX_MCLK to produce the + * receive bit clock. + */ +void i2s_set_rx_clock_bitrate(uint8_t bitrate); + +typedef enum { + CLK_TX_SRC = 0b00, ///< Select the TX fractional rate divider clock output as the source + CLK_RX_MCLK = 0b10, ///< Select the RX_MCLK signal as the TX_MCLK clock source +} clksel_t; + +/** + * + */ +void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena); + +/** + * + */ +void i2s_set_rx_mode_control(clksel_t c, int _4pin, int mcena); + +void i2s_tx_reset(); +void i2s_tx_stop(); +void i2s_tx_start(); + +void i2s_rx_reset(); +void i2s_rx_stop(); +void i2s_rx_start(); + +/* + * A data sample in the FIFO consists of: + * - 1×32 bits in 8-bit or 16-bit stereo modes. + * - 1×32 bits in mono modes. + * - 2×32 bits, first left data, second right data, in 32-bit stereo modes. + * + * I2STXFIFO: 0x400A 8008 - 8 x 32 bit (see table 407, pg 477) + * I2SRXFIFO: 0x400A 800C - 8 x 32 bit (see table 408, pg 478) + * + * See figure 113, pg. 489, for examples. + */ + +/** + * Write to TXFIFO + * I2STXFIFO: 0x400A 8008 - 8 x 32 bit (see table 407, pg 477) + */ +#define TF(t, n) t *n = (t*)0x400A8008 +inline void i2s_write_pcm_8_mono(int8_t s) { TF(int8_t, t); t[0] = s; } +inline void i2s_write_pcm_16_mono(int16_t s) { TF(int16_t, t); t[0] = s; } +inline void i2s_write_pcm_32_mono(int32_t s) { TF(int32_t, t); t[0] = s; } +inline void i2s_write_pcm_8_stereo(int8_t l, int8_t r) +{ TF(int8_t, t); t[0] = l; t[1] = r; } +inline void i2s_write_pcm_16_stereo(int16_t l, int16_t r) +{ TF(int16_t, t); t[0] = l; t[1] = r; } +inline void i2s_write_pcm_32_stereo(int32_t l, int32_t r) +{ TF(int32_t, t); t[0] = l; t[1] = r; } + +/** + * Read from RXFIFO + * I2SRXFIFO: 0x400A 800C - 8 x 32 bit (see table 408, pg 478) + */ +#define RF(t, n) t *n = (t*)0x400A800C +inline void i2s_read_pcm_8_mono(int8_t *s) { RF(int8_t, t); *s = t[0]; } +inline void i2s_read_pcm_16_mono(int16_t *s) { RF(int16_t, t); *s = t[0]; } +inline void i2s_read_pcm_32_mono(int32_t *s) { RF(int32_t, t); *s = t[0]; } +inline void i2s_read_pcm_8_stereo(int8_t *l, int8_t *r) +{ RF(int8_t, t); *l = t[0]; *r = t[1]; } +inline void i2s_read_pcm_16_stereo(int16_t *l, int16_t *r) +{ RF(int16_t, t); *l = t[0]; *r = t[1]; } +inline void i2s_read_pcm_32_stereo(int32_t *l, int32_t *r) +{ RF(int32_t, t); *l = t[0]; *r = t[1]; } + +/** + * Convenience function for power, pin, clock and register configuration. + */ void i2s_init(); + /** * Enables accesses on FIFOs, places the transmit channel in unmute mode. */ -- cgit v1.2.3