summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2014-07-15 18:36:40 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2014-07-15 18:36:40 +0200
commite1f7dada40559a47f4496381426f71c88cd9605b (patch)
tree87ec15ba1931f5b4ab3b232e9516d1a855306250
parent79b6705bfc60ed17ddbf6c36ead99e9f5c3c7404 (diff)
Almost complete wm8523 api. Initial experiments with I2S.
-rw-r--r--firmware/drivers/wm8523.c403
-rw-r--r--firmware/drivers/wm8523.h349
-rw-r--r--firmware/src/p2m.c93
-rw-r--r--firmware/src/sample.h4
4 files changed, 787 insertions, 62 deletions
diff --git a/firmware/drivers/wm8523.c b/firmware/drivers/wm8523.c
index f8cad99..96d5cae 100644
--- a/firmware/drivers/wm8523.c
+++ b/firmware/drivers/wm8523.c
@@ -26,74 +26,381 @@
*/
#include "wm8523.h"
-#if 1
+#include <ssp.h>
+#include <GPIO.h>
+#include <i2s.h>
-#include <string.h>
-#include <cli.h>
+#define READ_BIT 0b1
+#define WRITE_BIT 0b0
-#include "spi.h"
+// Registers:
+#define R0 0
+#define R1 1
+#define R2 2
+#define R3 3
+#define R4 4
+#define R5 5
+#define R6 6
+#define R7 7
+#define R8 8
-#define WM8523_WRITE 0
-#define WM8523_READ 1
+typedef struct __attribute__ ((packed)) {
+ uint8_t reg:7;
+ uint8_t rw:1;
+ uint8_t msbyte;
+ uint8_t lsbyte;
+} _reg_t;
-typedef union {
- struct __attribute__ ((packed)) {
- uint8_t rw:1; ///< See WM8523_WRITE and WM8523_READ
- uint8_t reg:7; ///< See page 35 in the WM8523 manual.
- uint16_t data;
- } val;
- uint8_t data[3];
-} WM8523_transfer_t;
+static void reg_read(uint8_t portnum, char reg, uint8_t *buf, int size)
+{
+ _reg_t data;
+ data.rw = READ_BIT;
+ data.reg = reg;
+
+ GPIOSetValue(0, 16, 0);
+ SSPSend(portnum, (uint8_t *)&data, 1); // write register number and read bit
+ SSPReceive(portnum, (uint8_t *)&data.msbyte, 2); // read 16bit register value
+ GPIOSetValue(0, 16, 1);
-void WM8523_init()
-{
- //cli_write("sizeof: %d", sizeof(WM8523_transfer_t));
- spi_init();
+ if(size == 1) {
+ buf[0] = data.lsbyte;
+ } else if(size == 2) {
+ buf[0] = data.lsbyte;
+ buf[1] = data.msbyte;
+ } else {
+ // Error
+ return;
+ }
}
-void WM8523_deinit()
+static void reg_write(uint8_t portnum, char reg, const uint8_t *buf, int size)
{
- spi_deinit();
+ _reg_t data;
+ data.rw = WRITE_BIT;
+ data.reg = reg;
+ switch(size) {
+ case 0:
+ data.lsbyte = 0;
+ data.msbyte = 0;
+ break;
+ case 1:
+ data.lsbyte = buf[0];
+ data.msbyte = 0;
+ break;
+ case 2:
+ data.lsbyte = buf[0];
+ data.msbyte = buf[1];
+ break;
+ default:
+ // Error...
+ return;
+ }
+
+ GPIOSetValue(0, 16, 0);
+ SSPSend(portnum, (uint8_t *)&data, sizeof(data));
+ GPIOSetValue(0, 16, 1);
}
-void WM8523_write(uint8_t reg, uint16_t data)
+/**
+ * Prepare I2S, SSP and GPIO.
+ */
+void wm8523_init(uint8_t portnum, wm8523_samplerate_t fs)
{
- WM8523_transfer_t t;
- t.val.rw = WM8523_WRITE;
- t.val.reg = reg;
- t.val.data = data;
+ // Init SPI
+ GPIOSetDir(0, 16, 1);
+ GPIOSetValue(0, 16, 1);
+
+ if(portnum == 0) SSP0Init();
+ else SSP1Init();
+
+ I2SInit();
+
+ // pg. 482
+ // I2STXMODE 0x400A 8030
+ // bit 3 Enable for the TX_MCLK output. When 0, output of TX_MCLK is not enabled. When 1, output of TX_MCLK is enabled.
+ // set to 1, default is 0
+ uint32_t *i2stxmode = (uint32_t*)0x400a8030;
+ *i2stxmode |=
+ (0b1 << 3) // Enable TX_MCLK output.
+ ;
+
+ // pg. 477
+ // I2SDAO 0x400A 8000
+ // bit 5 When 0, the interface is in master mode. When 1, the interface is in slave mode.
+ // set to 0, default is 1
+ uint32_t *i2sdao = (uint32_t*)0x400a8030;
+ *i2sdao &=
+ ~(0b1 << 5) // Set I2S in master mode.
+ ;
+
+
+ // pg. 481
+ // I2STXBITRATE 0x400A 8028
+ // bit 0-5 I2S transmit bit rate. This value plus one is used to divide TX_MCLK to produce the transmit bit clock.
+ // set to 31 (divide by 32): stereo (2) * 16bit
+ uint32_t *i2stxbirate = (uint32_t *)0x400a8028;
+ *i2stxbirate = 7;
- WM8523_transfer_t r;
- r.val.data = 0xffff;
+ // bitclock = mclock / (divider+1)
+ // samplerate = mclock / bitclock
- spi_read_write(t.data, r.data, 3);
+
+ (void)fs;
+ /*
+ WM8523_FS_8K,
+ WM8523_FS_32K,
+ WM8523_FS_44K1,
+ WM8523_FS_48K,
+ WM8523_FS_88K2,
+ WM8523_FS_96K,
+ WM8523_FS_176K4,
+ WM8523_FS_192K,
+ */
+ uint32_t *pclksel1 = (uint32_t *)0x400fc1ac;
+ *pclksel1 |=
+ (0b10 << 22) // Select I2S clock: PCLK/2 see pg. 57 table 41/42
+ ;
+
+ uint32_t *pinsel9 = (uint32_t *)0x4002c024;
+ *pinsel9 |=
+ (0b01 << 26) // Select p4.29 as TX_MCLK
+ ;
+
+ /*
+ uint32_t *pinsel3 = (uint32_t *)0x4002c00c;
+ *pinsel3 |=
+ (0b01 << 22) // config p1.27 as CLKOUT
+ ;
+
+ // Set up master clock see pg. 66
+ uint32_t *clkcfg = (uint32_t *)0x400fc1c8;
+ *clkcfg =
+ (0b0000 << 0) | // Select RTC as clock source
+ (0b1111 << 4) | // Divide by 16
+ (0b1 << 8) // Enable
+ ;
+ */
}
-uint16_t WM8523_read(uint8_t reg)
+unsigned short wm8523_get_chip_id(uint8_t portnum)
{
- WM8523_transfer_t t;
- t.val.rw = WM8523_READ;
- t.val.reg = reg;
- t.val.data = 0xffff;
-
- WM8523_transfer_t r;
+ unsigned short id;
+ reg_read(portnum, R0, (uint8_t*)&id, sizeof(id));
+ return id;
+}
- spi_read_write(t.data, r.data, 3);
-
- return r.data[0] | (r.data[1] << 8);//r.val.data;
+void wm8523_reset_registers(uint8_t portnum)
+{
+ reg_write(portnum, R0, 0, 0);
}
-/**
-Volume update registers R06h and R07h are unavailable in SPI control mode.
-To use volume update in software control mode, I2C mode must be used.
-*/
-void WM8523_configure()
+uint8_t wm8523_get_hardware_revision(uint8_t portnum)
+{
+ uint8_t rev;
+ reg_read(portnum, R1, &rev, sizeof(rev));
+ return rev;
+}
+
+wm8523_power_mode_t wm8523_get_power_mode(uint8_t portnum)
+{
+ wm8523_power_mode_t mode;
+ reg_read(portnum, R2, (uint8_t*)&mode, sizeof(mode));
+ return mode;
+}
+
+void wm8523_set_power_mode(uint8_t portnum, wm8523_power_mode_t mode)
+{
+ reg_write(portnum, R2, (uint8_t*)&mode, sizeof(mode));
+}
+
+void wm8523_set_aif_ctrl1(uint8_t portnum, wm8523_aif_ctrl1_t aif_ctrl1)
+{
+ aif_ctrl1.blank = aif_ctrl1.reserved = 0;
+ reg_write(portnum, R3, (uint8_t*)&aif_ctrl1, sizeof(aif_ctrl1));
+}
+
+wm8523_aif_ctrl1_t wm8523_get_aif_ctrl1(uint8_t portnum)
+{
+ wm8523_aif_ctrl1_t aif_ctrl1;
+
+ reg_read(portnum, R3, (uint8_t*)&aif_ctrl1, sizeof(aif_ctrl1));
+
+ return aif_ctrl1;
+}
+
+void wm8523_set_aif_ctrl2(uint8_t portnum, wm8523_aif_ctrl2_t aif_ctrl2)
+{
+ reg_write(portnum, R4, (uint8_t*)&aif_ctrl2, sizeof(aif_ctrl2));
+}
+
+wm8523_aif_ctrl2_t wm8523_get_aif_ctrl2(uint8_t portnum)
+{
+ wm8523_aif_ctrl2_t aif_ctrl2;
+
+ reg_read(portnum, R4, (uint8_t*)&aif_ctrl2, sizeof(aif_ctrl2));
+
+ return aif_ctrl2;
+}
+
+void wm8523_set_dac_ctrl3(uint8_t portnum, wm8523_dac_ctrl3_t dac_ctrl3)
+{
+ reg_write(portnum, R5, (uint8_t*)&dac_ctrl3, sizeof(dac_ctrl3));
+}
+
+wm8523_dac_ctrl3_t wm8523_get_dac_ctrl3(uint8_t portnum)
+{
+ wm8523_dac_ctrl3_t dac_ctrl3;
+
+ reg_read(portnum, R5, (uint8_t*)&dac_ctrl3, sizeof(dac_ctrl3));
+
+ return dac_ctrl3;
+}
+
+#include "dma.h"
+#include <LPC17xx.h>
+
+extern volatile uint8_t *I2STXBuffer, *I2SRXBuffer;
+extern volatile uint32_t I2SReadLength;
+extern volatile uint32_t I2SWriteLength;
+extern volatile uint32_t I2SRXDone, I2STXDone;
+extern volatile uint32_t I2SDMA0Done, I2SDMA1Done;
+
+short signal(int x)
{
- // spi_init();
+ static short v = 0;
+ if((x / 1000) % 2) v++;
+ else v--;
+ return x % 0xffff;
+}
+
+#if 0
+//
+// Sine approximation using taylor series
+// http://en.wikipedia.org/wiki/Taylor_series
+//
+#define M_PI 3.14159265359
+float expand(float x, int e)
+{
+
+ int fak = 1;
+ while(e) {
+ x *= x;
+ fak *= e;
+ e--;
+ }
- uint16_t id = WM8523_read(0); // Read chip id from reg0.
- (void)id;
- // cli_write("=%d=", id); // should be 34595 (0x8523)
+ return x / (float)fak;
}
-#endif/*0*/
+float _sin(float x)
+{
+ return x - expand(x, 3) + expand(x, 5) - expand(x, 7);
+}
+#endif
+
+#include "../src/sample.h"
+
+void wm8523_tone()
+{
+ // I2SSTATE 0x400A 8010
+ // bit 16-19 Reflects the current level of the Transmit FIFO.
+ uint32_t *i2sstate = (uint32_t *)0x400a8010;
+ (void)i2sstate;
+
+ // I2STXFIFO 0x400A 8008
+ // 8 × 32-bit transmit FIFO.
+ uint32_t *i2stxfifo = (uint32_t*)0x400a8008;
+
+ // Not DMA mode, enable I2S interrupts.
+ NVIC_EnableIRQ(I2S_IRQn);
+
+ // RX FIFO depth is 1, TX FIFO depth is 8.
+ I2SStart();
+ LPC_I2S->I2SIRQ = (8 << 16) | (1 << 8) | (0x01 << 0);
+
+ //uint32_t val = 0;
+ uint32_t cnt = 0;
+ while(1) {
+ while((*i2sstate & (0b1111 << 16)) != 0) { }
+
+ *i2stxfifo = samples[(cnt ++) % sizeof(samples)];
+ }
+
+
+#if 0
+ int i;
+
+ short *pcm = (short*)I2STXBuffer;
+
+ /// Configure temp register before reading
+ int pcm_size = BUFSIZE / sizeof(short) / 2; // 2 channels
+ for ( i = 0; i < pcm_size; i++ ) { // Clear buffer
+ //float s = _sin((float)i / 44100 * 2 * M_PI * 440);
+ short s = signal(i);
+ pcm[2 * i] = s * 1234567890;//s * 32000;//samples[i];
+ pcm[2 * i + 1] = s * 1234567890;//s * 32000;//samples[i];
+ //I2SRXBuffer[i] = 0;
+ }
+
+ // I2SInit(); // Initialize I2S
+
+ #if I2S_DMA_ENABLED
+ DMA_Init();
+
+ // Select secondary function(I2S) in DMA channels
+ LPC_SC->DMAREQSEL = (0x1<<DMA_I2S_REQ0)|(0x1<<DMA_I2S_REQ1);
+
+ // On DMA channel 0, Source is memory, destination is I2S TX FIFO,
+ // On DMA channel 1, source is I2S RX FIFO, Destination is memory
+
+ // Enable channel and IE bit
+ DMAChannel_Init( 0, M2P );
+ LPC_GPDMACH0->DMACCConfig |= (0x18001|(0x00<<1)|(DMA_I2S_REQ0<<6)|(0x01<<11));
+
+ //DMAChannel_Init( 1, P2M );
+ // LPC_GPDMACH1->CConfig |= (0x08001|(DMA_I2S_REQ1<<1)|(0x00<<6)|(0x02<<11));
+
+ NVIC_EnableIRQ(DMA_IRQn);
+
+ I2SStart();
+
+ // Channel 2 is for RX, enable RX first.
+ //LPC_I2S->I2SDMA2 = (0x01<<0) | (0x08<<8);
+
+ // Channel 1 is for TX.
+ LPC_I2S->I2SDMA1 = (0x01<<1) | (0x01<<16);
+
+ // Wait for both DMA0 and DMA1 to finish before verifying.
+ while(!I2SDMA0Done/* || !I2SDMA1Done*/);
+#else
+
+ // this does not compile
+
+ // Not DMA mode, enable I2S interrupts.
+ NVIC_EnableIRQ(I2S_IRQn);
+
+ // RX FIFO depth is 1, TX FIFO depth is 8.
+ I2SStart();
+ LPC_I2S->I2SIRQ = (8 << 16) | (1 << 8) | (0x01 << 0);
+
+ while(I2SWriteLength < BUFSIZE) {
+ while (((LPC_I2S->I2SSTATE >> 16) & 0xFF) == TXFIFO_FULL);
+ LPC_I2S->I2STXFIFO = I2STXBuffer[I2SWriteLength++];
+ }
+
+ I2STXDone = 1;
+
+ // Wait for RX and TX complete before comparison
+ while(!I2SRXDone || !I2STXDone);
+#endif
+ /*
+ // Validate TX and RX buffer
+ for(i=1; i<BUFSIZE; i++) {
+ if(I2SRXBuffer[i] != I2STXBuffer[i-1]) {
+ while(1); // Validation error
+ }
+ }
+ while(1); // Don't exit from main when finishing.
+ */
+#endif
+}
diff --git a/firmware/drivers/wm8523.h b/firmware/drivers/wm8523.h
index e1bb8bc..aeffe7b 100644
--- a/firmware/drivers/wm8523.h
+++ b/firmware/drivers/wm8523.h
@@ -29,9 +29,350 @@
#include <stdint.h>
-void WM8523_init();
-void WM8523_configure();
-uint16_t WM8523_read(uint8_t reg);
-void WM8523_write(uint8_t reg, uint16_t data);
+
+typedef enum {
+ WM8523_FS_8K,
+ WM8523_FS_32K,
+ WM8523_FS_44K1,
+ WM8523_FS_48K,
+ WM8523_FS_88K2,
+ WM8523_FS_96K,
+ WM8523_FS_176K4,
+ WM8523_FS_192K,
+} wm8523_samplerate_t;
+/**
+ * Prepare I2S, SSP and GPIO.
+ */
+void wm8523_init(uint8_t portnum, wm8523_samplerate_t fs);
+
+/**
+ * Read chip ID.
+ * Is always 0x8523
+ * Register: R0 [0:15] (read)
+ * Default: 1000 0101 0010 0011
+ */
+#define WM8532_CHIP_ID 0b1000010100100011
+unsigned short wm8523_get_chip_id(uint8_t portnum);
+
+/**
+ * Reset all register values to their defaults.
+ * Register: R0 [0:15] (write)
+ */
+void wm8523_reset_registers(uint8_t portnum);
+
+/**
+ * Read hardware revision counter.
+ * Register: R1 [0:2] (read)
+ */
+uint8_t wm8523_get_hardware_revision(uint8_t portnum);
+
+typedef enum __attribute__ ((packed)) {
+ WM8523_PWR_OFF = 0b00,
+ WM8523_PWR_POWER_DOWN = 0b01,
+ WM8523_PWR_POWER_UP_TO_MUTE = 0b10,
+ WM8523_PWR_POWER_UP_TO_UNMUTE = 0b11,
+} wm8523_power_mode_t;
+
+/**
+ * Get current power mode.
+ * Register: R2 [0:1] (read)
+ */
+wm8523_power_mode_t wm8523_get_power_mode(uint8_t portnum);
+
+/**
+ * Set power mode.
+ * Register: R2 [0:1] (write)
+ */
+void wm8523_set_power_mode(uint8_t portnum, wm8523_power_mode_t mode);
+
+/*
+R3 [0:1]
+Audio Data Interface Format
+00 = Right justified
+01 = Left justified
+10 = I2S format
+11 = DSP mode
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_FMT_RIGHT = 0b00, // Right justified
+ WM8523_FMT_LEFT = 0b01, // Left justified
+ WM8523_FMT_I2S = 0b10, // I2S format
+ WM8523_FMT_DSP = 0b11, // DSP mode
+} wm8523_audio_data_interface_format_t;
+
+/*
+R3 [3:4]
+Audio Data Word Length
+00 = 16 bits
+01 = 20 bits
+10 = 24 bits
+11 = 32 bits
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_WLEN_16 = 0b00, // 16 bit word length
+ WM8523_WLEN_20 = 0b01, // 20 bit word length
+ WM8523_WLEN_24 = 0b10, // 24 bit word length
+ WM8523_WLEN_32 = 0b11, // 32 bit word length
+} wm8523_audio_data_word_length_t;
+
+/*
+R3 [5]
+BCLK Inversion Control
+Slave mode:
+0 = Use rising edge
+1 = Use falling edge
+Master mode:
+0 = BCLK normal
+1 = BCLK inverted
+*/
+typedef enum __attribute__ ((packed)) {
+ // Slave mode:
+ WM8523_INVCTL_SLAVE_RISING = 0b0, // Use rising edge
+ WM8523_INVCTL_SLAVE_FALLING = 0b1, // Use falling edge
+
+ // Master mode:
+ WM8523_INVCTL_MASTER_NORMAL = 0b0, // BCLK normal
+ WM8523_INVCTL_MASTER_INVERTED = 0b1, // BCLK inverted
+} wm8523_bclk_inversion_control_t;
+
+/*
+R3 [6]
+LRCLK Inversion Control
+0 = Normal polarity
+1 = Inverted polarity
+When AIF_FMT[2:0]=011 (DSP Mode):
+0 = Mode A (2nd clock)
+1 = Mode B (1st clock)
+*/
+typedef enum __attribute__ ((packed)) {
+ // Slave mode:
+ WM8523_LRCLKINVCTL_SLAVE_NORMAL = 0b0, // Normal polarity
+ WM8523_LRCLKINVCTL_SLAVE_INVERTED = 0b1, // Inverted polarity
+
+ // Master mode:
+ WM8523_LRCLKINVCTL_MASTER_MODE_A = 0b0, // Mode A (2nd clock)
+ WM8523_LRCLKINVCTL_MASTER_MODE_B = 0b1, // Mode B (1st clock)
+} wm8523_lrclk_inversion_control_t;
+
+/*
+R3 [7]
+Master/Slave Select
+0 = Slave mode
+1 = Master mode
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_MODESEL_SLAVE = 0b0, // Slave mode
+ WM8523_MODESEL_MASTER = 0b1, // Master mode
+} wm8523_slave_master_mode_sel_t;
+
+/*
+R3 [8]
+DAC De-emphasis Control
+0 = No de-emphasis
+1 = De-emphasis enabled
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_DEEMPH_DISABLED = 0b0, // No de-emphasis
+ WM8523_DEEMPH_ENABLED = 0b1, // De-emphasis enabled
+} wm8523_dac_deemphasis_control_t;
+
+typedef struct __attribute__ ((packed)) {
+ wm8523_audio_data_interface_format_t fmt:2;
+ int reserved:1;
+ wm8523_audio_data_word_length_t wlen:2;
+ wm8523_bclk_inversion_control_t invctl:1;
+ wm8523_lrclk_inversion_control_t lrclkinvctl:1;
+ wm8523_slave_master_mode_sel_t modesel:1;
+ wm8523_dac_deemphasis_control_t deemp:1;
+ uint16_t blank:7;
+} wm8523_aif_ctrl1_t;
+
+void wm8523_set_aif_ctrl1(uint8_t portnum, wm8523_aif_ctrl1_t aif_ctrl1);
+wm8523_aif_ctrl1_t wm8523_get_aif_ctrl1(uint8_t portnum);
+
+/*
+R4 [0:2]
+MCLK:LRCLK Ratio
+000 = Auto detect
+001 = 128fs
+010 = 192fs
+011 = 256fs
+100 = 384fs
+101 = 512fs
+110 = 768fs
+111 = 1152fs
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_CLKRATIO_AUTO = 0b000, // Auto detect
+ WM8523_CLKRATIO_128_FS = 0b001, // 128fs
+ WM8523_CLKRATIO_192_FS = 0b010, // 192fs
+ WM8523_CLKRATIO_256_FS = 0b011, // 256fs
+ WM8523_CLKRATIO_384_FS = 0b100, // 384fs
+ WM8523_CLKRATIO_512_FS = 0b101, // 512fs
+ WM8523_CLKRATIO_768_FS = 0b110, // 768fs
+ WM8523_CLKRATIO_1152_FS = 0b111, // 1152fs
+} wm8523_mclk_lrclk_ratio_t;
+
+/*
+R4 [3:5]
+BCLK Divider Control (Master Mode)
+000 = MCLK/4
+001 = MCLK/8
+010 = 32fs
+011 = 64fs
+100 = 128fs
+101 - 111 reserved
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_CLKDIV_MCLK_4 = 0b000, // MCLK/4
+ WM8523_CLKDIV_MCLK_8 = 0b001, // MCLK/8
+ WM8523_CLKDIV_32_FS = 0b010, // 32fs
+ WM8523_CLKDIV_64_FS = 0b011, // 64fs
+ WM8523_CLKDIV_128_FS = 0b100, // 128fs
+} wm8523_bclk_divider_control_t;
+
+/*
+R4 [6:7]
+Digital Monomix Control
+00 = Stereo (normal operation)
+01 = Mono (Left data to DACR)
+10 = Mono (Right data to DACL)
+11 = Digital monomix, (L+R)/2
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_MIX_STEREO = 0b00, // Stereo (normal operation)
+ WM8523_MIX_MONO_LEFT = 0b01, // Mono (Left data to DACR)
+ WM8523_MIX_MONO_RIGHT = 0b10, // Mono (Right data to DACL)
+ WM8523_MIX_MONO_MIXED = 0b11, // Digital monomix, (L+R)/2
+} wm8523_digital_monomix_control_t;
+
+typedef struct __attribute__ ((packed)) {
+ wm8523_mclk_lrclk_ratio_t clkratio:3;
+ wm8523_bclk_divider_control_t clkdiv:3;
+ wm8523_digital_monomix_control_t mix:2;
+} wm8523_aif_ctrl2_t;
+
+void wm8523_set_aif_ctrl2(uint8_t portnum, wm8523_aif_ctrl2_t aif_ctrl2);
+wm8523_aif_ctrl2_t wm8523_get_aif_ctrl2(uint8_t portnum);
+
+/*
+R5 [0]
+DAC Digital Volume Decrease Control
+0 = Apply volume decreases instantly (step)
+1 = Ramp volume decreases
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_VOL_DOWN_RAMP = 0b00, // Apply volume decreases instantly (step)
+ WM8523_VOL_DOWN_INSTANT = 0b01, // Ramp volume decreases
+} wm8523_dac_vol_down_ramp_t;
+
+/*
+R5 [1]
+DAC Digital Volume Increase Control
+0 = Apply volume increases instantly (step)
+1 = Ramp volume increases
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_VOL_UP_RAMP = 0b00, // Apply volume increases instantly (step)
+ WM8523_VOL_UP_INSTANT = 0b01, // Ramp volume increases
+} wm8523_dac_vol_up_ramp_t;
+
+/*
+R5 [2]
+Left DAC Mute
+0 = Normal operation
+1 = Mute
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_DACL_UNMUTE = 0b00, // Normal operation
+ WM8523_DACL_MUTE = 0b01, // Mute
+} wm8523_dacl_mute_t;
+
+/*
+R5 [3]
+Right DAC Mute
+0 = Normal operation
+1 = Mute
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_DACR_UNMUTE = 0b00, // Normal operation
+ WM8523_DACR_MUTE = 0b01, // Mute
+} wm8523_dacr_mute_t;
+
+/*
+R5 [4]
+Zero Cross Enable
+0 = Do not use zero cross
+1 = Use zero cross
+*/
+typedef enum __attribute__ ((packed)) {
+ WM8523_ZERO_CROSSING_DISABLED = 0b00, // Do not use zero cross
+ WM8523_ZERO_CROSSING_ENABLED = 0b01, // Use zero cross
+} wm8523_dac_zc_t;
+
+typedef struct __attribute__ ((packed)) {
+ wm8523_dac_vol_down_ramp_t downramp:1;
+ wm8523_dac_vol_up_ramp_t upramp:1;
+ wm8523_dacl_mute_t lmute:1;
+ wm8523_dacr_mute_t rmute:1;
+ wm8523_dac_zc_t dac_zc:1;
+} wm8523_dac_ctrl3_t;
+
+void wm8523_set_dac_ctrl3(uint8_t portnum, wm8523_dac_ctrl3_t dac_ctrl3);
+wm8523_dac_ctrl3_t wm8523_get_dac_ctrl3(uint8_t portnum);
+
+/*
+R6 [0:8]
+Left DAC Digital Volume Control
+0 0000 0000 = -100dB
+0 0000 0001 = -99.75dB
+0 0000 0010 = -99.5dB
+...0.25dB steps
+1 1001 0000 = 0dB
+...0.25dB steps
+1 1011 1110 = +11.75dB
+1 11XX XXXX = +12dB
+*/
+
+/*
+R6 [9]
+Left DAC Digital Volume Update
+0 = Latch Left DAC volume setting into register map but do not update volume
+1 = Latch Left DAC volume setting into register map and update left and right channels simultaneously
+*/
+
+/*
+R7 [0:8]
+Right DAC Digital Volume Control
+0 0000 0000 = -100dB
+0 0000 0001 = -99.75dB
+0 0000 0010 = -99.5dB
+...0.25dB steps
+1 1001 0000 = 0dB
+...0.25dB steps
+1 1011 1110 = +11.75dB
+1 11XX XXXX = +12dB
+*/
+
+/*
+R7 [9]
+Right DAC Digital Volume Update
+0 = Latch Right DAC volume setting into register map but do not update volume
+1 = Latch Right DAC volume setting into register map and update left and right channels simultaneously
+*/
+
+/*
+R8 [0]
+Zero Detect Count Control
+0 = 1024
+1 = 2048
+*/
+
+
+void wm8523_tone();
+
+//void wm8523_configure();
+//uint16_t wm8523_read(uint8_t reg);
+//void wm8523_write(uint8_t reg, uint16_t data);
#endif/*__PEDAL2METAL_WM8523_H__*/
diff --git a/firmware/src/p2m.c b/firmware/src/p2m.c
index d44a01c..aa5250d 100644
--- a/firmware/src/p2m.c
+++ b/firmware/src/p2m.c
@@ -9,8 +9,8 @@
//#define IRQ_BLINKY
//#define BLINKY
//#define BLINKY
-//#define WM8523
-#define SPI
+#define WM8523
+//#define SPI
#ifdef DMA
@@ -136,7 +136,8 @@ int main (void)
#ifdef WM8523
-#include <cli.h>
+#include <ssp.h>
+#include <GPIO.h>
#include <wm8523.h>
#include <led.h>
@@ -147,20 +148,92 @@ int main (void)
LED_Init();
- _delay(1 << 22);
+ uint8_t portnum = 0;
+
+ wm8523_init(portnum, WM8523_FS_44K1);
+
+ if(wm8523_get_chip_id(portnum) != WM8532_CHIP_ID) goto fail;
- WM8523_init();
+ wm8523_reset_registers(portnum);
- _delay(1 << 22);
+ wm8523_power_mode_t pwr = WM8523_PWR_POWER_UP_TO_UNMUTE;
+ wm8523_set_power_mode(portnum, pwr);
- WM8523_configure();
+ wm8523_aif_ctrl1_t ctl1;
+ ctl1.fmt = WM8523_FMT_I2S;
+ ctl1.wlen = WM8523_WLEN_32;
+ ctl1.invctl = WM8523_INVCTL_SLAVE_RISING;
+ ctl1.lrclkinvctl = WM8523_LRCLKINVCTL_SLAVE_NORMAL;
+ ctl1.modesel = WM8523_MODESEL_SLAVE;
+ ctl1.deemp = WM8523_DEEMPH_ENABLED;
+ wm8523_set_aif_ctrl1(portnum, ctl1);
+
+ wm8523_aif_ctrl2_t ctl2;
+ ctl2.clkratio = WM8523_CLKRATIO_AUTO;
+ ctl2.clkdiv = WM8523_CLKDIV_MCLK_4;
+ ctl2.mix = WM8523_MIX_STEREO;
+ wm8523_set_aif_ctrl2(portnum, ctl2);
+
+ wm8523_dac_ctrl3_t ctl3;
+ ctl3.downramp = WM8523_VOL_DOWN_INSTANT;
+ ctl3.upramp = WM8523_VOL_UP_INSTANT;
+ ctl3.lmute = WM8523_DACL_UNMUTE;
+ ctl3.rmute = WM8523_DACR_UNMUTE;
+ ctl3.dac_zc = WM8523_ZERO_CROSSING_DISABLED;
+ wm8523_set_dac_ctrl3(portnum, ctl3);
+
+ /*
+ // Init SPI
+ GPIOSetDir(0, 16, 1);
+ GPIOSetValue(0, 16, 1);
+
+ SSP0Init();
+
+ uint8_t src_addr[16]; //16 byte Write buffer
+ uint8_t portnum = 0;
+
+ // Set bitwidth to 16
+ // Set interface format to I2S
+ GPIOSetValue(0, 16, 0);
+ src_addr[0] = 0x0 << 7 | 0x3; // write bit (0) | register 3
+ src_addr[1] =
+ 0x2 << 0 | // Set I2S mode
+ 0x0 << 2 | // Reserved
+ 0x0 << 3; // Set 16 bit
+ // The rest are 0 as default which is correct
+
+ SSPSend(portnum, (uint8_t *)src_addr, 2);
+ GPIOSetValue(0, 16, 1);
+
+ _delay(1 << 5);
+
+ // Power up and unmute
+ GPIOSetValue(0, 16, 0);
+ src_addr[0] = 0x0 << 7 | 0x2; // write bit (0) | register 2 (power register)
+ src_addr[1] = 0x2; // Set power mode: "power up and unmute"
+ SSPSend(portnum, (uint8_t *)src_addr, 2);
+ GPIOSetValue(0, 16, 1);
+ */
+
+
+ //wm8523_init();
+
+ // WM8523_configure();
+
+ //_delay(1 << 5);
+
+ wm8523_tone();
// Indicate that we didn't crash before the end...
- int i = 0;
- while(1) {
+ while(1) { // slow blink
LED_toggle();
_delay(1 << 21);
- i = 1 - i;
+ }
+
+fail:
+ while(1) { // fast blink
+ LED_toggle();
+ _delay(1 << 17);
}
}
diff --git a/firmware/src/sample.h b/firmware/src/sample.h
index 8ac9f46..5d96ba7 100644
--- a/firmware/src/sample.h
+++ b/firmware/src/sample.h
@@ -27,6 +27,8 @@ short samples[] = {
444,
367,
346,
+};
+#if 0
408,
394,
330,
@@ -1106,3 +1108,5 @@ short samples[] = {
486,
485
};
+
+#endif