From 075faa4e2521b0d90df564516dfc618506cca8e9 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 13 Sep 2014 22:11:12 +0200 Subject: I2S test and a number of bugfixes. --- firmware/drivers/i2s.c | 61 ++++++++++++++++++++++++++++++-------- firmware/drivers/i2s.h | 71 ++++++++++++++++++++++++++++----------------- firmware/test/i2s/Makefile | 8 +++-- firmware/test/i2s/i2stest.c | 25 +++++++++++++++- firmware/test/test.c | 59 +++++++++++++++++++++++++++++++++++++ firmware/test/test.h | 33 +++++++++++++++++++++ 6 files changed, 215 insertions(+), 42 deletions(-) create mode 100644 firmware/test/test.c create mode 100644 firmware/test/test.h diff --git a/firmware/drivers/i2s.c b/firmware/drivers/i2s.c index df32801..2d900be 100644 --- a/firmware/drivers/i2s.c +++ b/firmware/drivers/i2s.c @@ -32,7 +32,7 @@ void i2s_set_power(int power) // Remark: On reset, the I²S interface is disabled (PCI2S = 0). uint32_t *pconp = (uint32_t *)0x400FC0C4; *pconp &= ~(0b1 << 27); // Clear bit - *pconp |= ~( ((power != 0) & 0b1) << 27); // Set bit + *pconp |= ((power != 0) & 0b1) << 27; // Set bit } void i2s_set_clksel(i2s_clksel_t sel) @@ -91,7 +91,7 @@ void i2s_set_pinsel() ; } -void i2s_set_dao_register(wordwidth_t ww, channels_t ch) +void i2s_set_dao_register(i2s_wordwidth_t ww, i2s_channels_t ch) { // See table 405, pg. 476 uint32_t *i2sdao = (uint32_t*)0x400A8000; @@ -106,7 +106,7 @@ void i2s_set_dao_register(wordwidth_t ww, channels_t ch) ; } -void i2s_set_dai_register(wordwidth_t ww, channels_t ch) +void i2s_set_dai_register(i2s_wordwidth_t ww, i2s_channels_t ch) { // See table 406, pg. 477 uint32_t *i2sdai = (uint32_t*)0x400A8004; @@ -149,7 +149,7 @@ void i2s_set_rx_clock_bitrate(uint8_t bitrate) *i2srxbitrate = (bitrate & 0b111111); } -void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena) +void i2s_set_tx_mode_control(i2s_tx_clksel_t c, int _4pin, int mcena) { // See table 417, pg 482 uint32_t *i2stxmode = (uint32_t*)0x400A8030; @@ -159,7 +159,7 @@ 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_set_rx_mode_control(i2s_rx_clksel_t c, int _4pin, int mcena) { // See table 418, pg 483 uint32_t *i2srxmode = (uint32_t*)0x400A8034; @@ -280,19 +280,54 @@ int i2s_get_state_tx_level() return (*i2sstate >> 16) & 0b1111; // bit 16-19 } -void i2s_init() +int i2s_init(int pclkdiv, int bitrate, int x, int y, + int bitwidth, int channels) { i2s_set_power(1); - i2s_set_clksel(I2S_CCLK_2); + + i2s_clksel_t clk = I2S_CCLK; + switch(pclkdiv) { + case 1: clk = I2S_CCLK; break; + case 2: clk = I2S_CCLK_2; break; + case 4: clk = I2S_CCLK_4; break; + case 8: clk = I2S_CCLK_8; break; + default: + return 1; + } + i2s_set_clksel(clk); + i2s_set_pinsel(); - i2s_set_dao_register(WW_16_BIT, CH_STEREO); - i2s_set_tx_rate(1, 1); - i2s_set_tx_clock_bitrate(7); + + i2s_wordwidth_t ww = I2S_WW_16_BIT; + switch(bitwidth) { + case 8: ww = I2S_WW_8_BIT; break; + case 16: ww = I2S_WW_16_BIT; break; + case 32: ww = I2S_WW_32_BIT; break; + default: + return 1; + } + + i2s_channels_t ch = I2S_CH_MONO; + switch(channels) { + case 1: ch = I2S_CH_MONO; break; + case 2: ch = I2S_CH_STEREO; break; + default: + return 1; + } + + i2s_set_dao_register(ww, ch); + + if((x & 0xff) != x || (y & 0xff) != y) return 1; + i2s_set_tx_rate(x, y); + + if((bitrate & 0b111111) != bitrate) return 1; + i2s_set_tx_clock_bitrate(bitrate); + i2s_set_tx_mode_control(CLK_TX_SRC, 1, 1); - i2s_tx_reset(); + //i2s_tx_reset(); i2s_tx_stop(); - + /* int i; i = i2s_get_state_irq(); i = i2s_get_state_dmareq1(); @@ -300,6 +335,8 @@ void i2s_init() i = i2s_get_state_rx_level(); i = i2s_get_state_tx_level(); (void)i; + */ + return 0; } #if 0 diff --git a/firmware/drivers/i2s.h b/firmware/drivers/i2s.h index 8107e30..6a8d446 100644 --- a/firmware/drivers/i2s.h +++ b/firmware/drivers/i2s.h @@ -70,30 +70,30 @@ void i2s_set_clksel(i2s_clksel_t sel); 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; + I2S_WW_8_BIT = 0b00, // 8-bit data + I2S_WW_16_BIT = 0b01, // 16-bit data + //I2S_WW_RESERVED = 0b10, // Reserved, do not use this setting + I2S_WW_32_BIT = 0b11, // 32-bit data +} i2s_wordwidth_t; typedef enum { - CH_MONO = 0b1, // Data is mono format - CH_STEREO = 0b0, // Data is stereo format -} channels_t; + I2S_CH_MONO = 0b1, // Data is mono format + I2S_CH_STEREO = 0b0, // Data is stereo format +} i2s_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); +void i2s_set_dao_register(i2s_wordwidth_t ww, i2s_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); +void i2s_set_dai_register(i2s_wordwidth_t ww, i2s_channels_t ch); /** * Set clock transmit rate as PCLK_I2S * (X/Y) / 2 @@ -136,17 +136,22 @@ 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; +} i2s_tx_clksel_t; /** * */ -void i2s_set_tx_mode_control(clksel_t c, int _4pin, int mcena); +void i2s_set_tx_mode_control(i2s_tx_clksel_t c, int _4pin, int mcena); + +typedef enum { + CLK_RX_SRC = 0b00, ///< Select the RX fractional rate divider clock output as the source + CLK_TX_MCLK = 0b10, ///< Select the TX_MCLK signal as the RX_MCLK clock source +} i2s_rx_clksel_t; /** * */ -void i2s_set_rx_mode_control(clksel_t c, int _4pin, int mcena); +void i2s_set_rx_mode_control(i2s_rx_clksel_t c, int _4pin, int mcena); void i2s_tx_reset(); void i2s_tx_stop(); @@ -173,14 +178,17 @@ void i2s_rx_start(); * 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) +static inline void i2s_write_pcm_8_mono(int8_t s) +{ TF(int8_t, t); t[0] = s; } +static inline void i2s_write_pcm_16_mono(int16_t s) +{ TF(int16_t, t); t[0] = s; } +static inline void i2s_write_pcm_32_mono(int32_t s) +{ TF(int32_t, t); t[0] = s; } +static 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) +static 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) +static inline void i2s_write_pcm_32_stereo(int32_t l, int32_t r) { TF(int32_t, t); t[0] = l; t[1] = r; } /** @@ -188,20 +196,31 @@ inline void i2s_write_pcm_32_stereo(int32_t l, int32_t r) * 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) +static inline void i2s_read_pcm_8_mono(int8_t *s) +{ RF(int8_t, t); *s = t[0]; } +static inline void i2s_read_pcm_16_mono(int16_t *s) +{ RF(int16_t, t); *s = t[0]; } +static inline void i2s_read_pcm_32_mono(int32_t *s) +{ RF(int32_t, t); *s = t[0]; } +static 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) +static 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) +static 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. + * @param pclkdiv Value of pclkdiv as found by the clkcalc tool. + * @param bitrate Value of bitrate as found by the clkcalc tool. + * @param x Value of x as found by the clkcalc tool. + * @param y Value of y as found by the clkcalc tool. + * @param bitwidth Width of each sample in bits (8, 16 or 32). + * @param channels Number of channels (1 or 2). + * @return 0 on success, 1 on error. */ -void i2s_init(); +int i2s_init(int pclkdiv, int bitrate, int x, int y, + int bitwidth, int channels); /** diff --git a/firmware/test/i2s/Makefile b/firmware/test/i2s/Makefile index c88f443..ed7860c 100644 --- a/firmware/test/i2s/Makefile +++ b/firmware/test/i2s/Makefile @@ -42,7 +42,9 @@ LINKER_SCRIPT = ${LPC}/LPC17xx.ld CSRCS = \ ${LPC}/system_LPC17xx.c \ ${LPC}/startup_LPC17xx.c \ - ${DRV}/i2s.c + ${DRV}/led.c \ + ${DRV}/i2s.c \ + ../test.c CSRCS += ${PROJ}.c ASRCS = @@ -91,8 +93,8 @@ nuke: clean # openocd -f openocd.cfg -c 'flash write_image erase $(PROJ).hex' -c 'verify_image $(PROJ).hex' -c 'reset run' flash: $(EXECNAME) $(CP) -O binary $(EXECNAME) $(PROJ).bin - ./fix-lpcchecksum $(PROJ).bin - openocd -f openocd.cfg \ + ../../fix-lpcchecksum $(PROJ).bin + openocd -f ../../openocd.cfg \ -c 'flash write_image erase $(PROJ).bin' \ -c 'verify_image $(PROJ).bin' \ -c 'reset run' diff --git a/firmware/test/i2s/i2stest.c b/firmware/test/i2s/i2stest.c index 16ea291..31f5c20 100644 --- a/firmware/test/i2s/i2stest.c +++ b/firmware/test/i2s/i2stest.c @@ -25,13 +25,36 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include +#include + +#include "../test.h" int main (void) { - i2s_init(); + led_init(); + + // From: tools/clkcalc 48000 16 2 + int pclkdiv = 1; + int bitrate = 24; + int x = 96; + int y = 125; + + int bitwidth = 16; + int channels = 2; + + int res = i2s_init(pclkdiv, bitrate, x, y, bitwidth, channels); + + if(res) error(); i2s_tx_start(); + // success(); + + int16_t s = 0; while(1) { + //while(i2s_get_state_tx_level() < 3) {} + i2s_write_pcm_16_stereo(s, s); + if((s % 2) == 0) led_toggle(); + s++; } } diff --git a/firmware/test/test.c b/firmware/test/test.c new file mode 100644 index 0000000..8671c5e --- /dev/null +++ b/firmware/test/test.c @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * test.c + * + * Sat Sep 13 21:10:33 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pedal2Metal. + * + * Pedal2Metal is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pedal2Metal is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pedal2Metal; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#include "test.h" + +#include + +volatile int temp; + +static void delay(int del) +{ + int i; + for(i = 0; i < del; i++) { + temp = i; + } +} + +// 21: slow, success +// 17: fast, error +static void blink(int t) +{ + while(1) { + led_toggle(); + delay(1 << t); + } +} + +void error() +{ + blink(17); +} + +void success() +{ + blink(22); +} diff --git a/firmware/test/test.h b/firmware/test/test.h new file mode 100644 index 0000000..9ab8484 --- /dev/null +++ b/firmware/test/test.h @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/*************************************************************************** + * test.h + * + * Sat Sep 13 21:10:33 CEST 2014 + * Copyright 2014 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Pedal2Metal. + * + * Pedal2Metal is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Pedal2Metal is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Pedal2Metal; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#ifndef __PEDAL2METAL_TEST_H__ +#define __PEDAL2METAL_TEST_H__ + +void error(); +void success(); + +#endif/*__PEDAL2METAL_TEST_H__*/ -- cgit v1.2.3