antoinevg / daisy_bsp

Board support package for the Electro-Smith Daisy Seed.
MIT License
67 stars 13 forks source link

Audio examples don't seem to work with new codec #8

Open backtail opened 2 years ago

backtail commented 2 years ago

Hi!

Unfortunately, the sound examples in the daisy_bsp crate don't seem to work on my Seed. I'm not completey sure why yet, but I think I can narrow it down. All other examples work great without a problem. I got one of the latest boards, with the WM8731 codec. It might have something to do with swap due to the ongoing chip shortage.

What I am sure of:

I'm not sure if:

or I could have messed up completely and just overlooked something very obvious

antoinevg commented 2 years ago

Oooh, I hadn't heard we have a new codec!

I think you're right about the chip shortage, most AKM codecs have been unobtainium for a while now.

So the bad news is that the WM8731 needs to be configured via I2C before you can start it up.

The good news is that I wrote a WM8731 driver for the esp32 a while back and it won't be a lot of work to port the I2C code to the Seed.

If you'd like to tackl it you can find the esp32 implementation here:

https://github.com/antoinevg/stage_esp32/tree/master/api/src/driver/wm8731

Otherwise I'm happy to take a look if/when I can get my hands on one of the new Seed boards!

backtail commented 2 years ago

Many thanks for the fast reply!

I'm glad you already have a solution ready. I'll try add the driver myself and post about the result soon.

backtail commented 2 years ago

I got the I2C configuration going now, the codec receives the same instructions as the ESP32 driver and acknowlegdes all of them. But the same problem as before remains: The STM32H7 doesn't send the I2S data to the DAC input. I might have an idea though. Since rev 1.1 of the daisy board doesn't have a proper schematic for reference, I fear that this specific pin might have been moved to a different pin on the STM32H7 side. I will try the different SAI pin configurations for the I2S data lane in the coming week and report later!

antoinevg commented 2 years ago

Nice! Looking forward! :-)

backtail commented 2 years ago

A little question: Does it make a difference if the pac crate uses a similar (but probably different) chipset to access its peripherals?

image

antoinevg commented 2 years ago

It looks like rustdoc doesn't check what features are set in Cargo.toml when generating these and just grabs the first one that's defined in the origin crate.

We do pull in the stm32h750 version though:

https://github.com/antoinevg/daisy_bsp/blob/4d827f584c7ade0c951c460b65edabcfb52a89ec/Cargo.toml#L33

backtail commented 2 years ago

Okay, good to know!

I forked the bsp crate and committed all my changes (https://github.com/backtail/daisy_bsp/commit/61b8844f4d2dda9c4218e3976d2d01d9932b6e4b). The SAI pin config seems to be right, I double checked with working C++ library. Maybe you could quickly glance through my changes and spot a problem. The SDA_A pin on the Codec still doesn't receive any data from the MCU. My suspicion is that the DMA isn't working properly and I don't exactly know how to debug this problem. The audio data lies properly in the TX_BUFFER and the DMA1_STR1() intterrupt seems to work fine.

antoinevg commented 2 years ago

In theory the DMA configuration should remain unchanged as the interface should be independent of whatever Codec is attached to SAI.

Something else that springs to mind is that the SAI / I2S interface may not be configured correctly for the new codec.

What I was using for the AK4556:

https://github.com/antoinevg/daisy_bsp/blob/4d827f584c7ade0c951c460b65edabcfb52a89ec/src/audio.rs#L128-L135

Things to look at are the framing and also bit rate, if I recall correctly my esp-idf I2C code configured the WM8731 to use 16bit @ 48kHz rather than 24bit @ 48kHz.

For what it's worth, here's the corresponding SAI config code from my esp-idf implementation:

https://github.com/antoinevg/stage_esp32/blob/a85f56dda98fcda5f6f5af3603f2777236b2c55b/api/src/driver/wm8731/mod.rs#L202-L227

    pub unsafe fn init(port: i2s_port_t, pins: Pins, config: &audio::Config) -> Result<(), EspError> {
        // initialize i2s driver
        log!(TAG, "initialize i2s peripheral");
        let i2s_config = i2s_config_t {
            mode: i2s_mode_t::I2S_MODE_MASTER
                | i2s_mode_t::I2S_MODE_RX
                | i2s_mode_t::I2S_MODE_TX,
            sample_rate: config.fs as c_int,
            bits_per_sample: i2s_bits_per_sample_t::I2S_BITS_PER_SAMPLE_16BIT,
            channel_format: i2s_channel_fmt_t::I2S_CHANNEL_FMT_RIGHT_LEFT,
            communication_format: i2s_comm_format_t::I2S_COMM_FORMAT_I2S
                                | i2s_comm_format_t::I2S_COMM_FORMAT_I2S_MSB,
            intr_alloc_flags: ESP_INTR_FLAG_LEVEL1 as i32,
            dma_buf_count: 4,
            dma_buf_len: config.block_length as i32,
            use_apll: false,
            ..i2s_config_t::default()
        };
        i2s_driver_install(port, &i2s_config, 0, core::ptr::null_mut()).as_result()?;

        // configure pins for i2s peripheral
        log!(TAG, "configure pins for i2s peripheral: {:?}", pins);
        i2s_set_pin(port, &pins.into()).as_result()?;

        // zero dma buffer
        i2s_zero_dma_buffer(port).as_result()?;

Maybe also worth comparing against what Electrosmith have in their repo:

https://github.com/electro-smith/libDaisy/tree/master/src/dev https://github.com/electro-smith/libDaisy/tree/master/src/per

harshnoiise commented 2 years ago

Any progress on this? I finally was able to interface an SSD1306 to my Daisy Pod today :'). Wanted to start doing some audio examples now but I have a newer pod which means I have the newer codec so looks like I won't be able to until this get's figured out

backtail commented 2 years ago

Unfortuntely, I have not figured out how to setup the configuration for the WM8731 yet... I have used the same config setup as in the C++ libdaisy today. No audio output yet. Still, i am not sure, if data is being transferred correctly from buffer to peripheral and sent, since I can't meausure any data signal on the WM8731 DAC input pin. However, I have not tried fiddling with the I²S setup yet, so the problem might lie there and not with the DMA.

backtail commented 2 years ago

I think I finally found the culprit!

In the new revision the transmit and receive channels of the SAI are reversed. That explains why I couldn't read any data on the codec data input pin.

https://github.com/electro-smith/libDaisy/blob/26256aaa54e4117c904ca431c0165981a6205fdf/src/daisy_seed.cpp#L267-L303

image

And also good news: It's very easy to check which revision it is. That means we can write the code in a manner that it works with both versions.

https://github.com/electro-smith/libDaisy/blob/26256aaa54e4117c904ca431c0165981a6205fdf/src/daisy_seed.cpp#L329-L345

image

I will try setup channel B of the SAI on the weekend, since I should probably spend more time studying for my exams :D

backtail commented 2 years ago

Update:

Codec is now working fine! :partying_face:

Only issue now is that I haven't quite figured out how to config the DMA. I can send and receive audio via SAI1_CHX_DR, but the DMA is not shifting to or from peripheral. I probably got the directions wrong or something along those lines.

Once that little issue is fixed up, I will implement the board version check, and then I would suggest a pull request?

backtail commented 2 years ago

How exactly is the DMA configured? Is it possible to specifically assign stream 0 of DMA1 to SAI1 block B and stream 1 with block A? I haven't reversed the DMA directions since I thought that for example https://github.com/antoinevg/daisy_bsp/blob/209d938b58d2ffe0e05af96103c17aa5a0cc0391/src/audio.rs#L106-L112

the DMA stream grabs its counterpart. Meaning, here stream 0 is configured as as mem_to_per and so it acts as transmitter. In line 108, the "whole" SAI peripheral is handed over. However I'd like to configure this as SAI1<CH_A> or SAI1<CH_B> respectively.

I swapped the Rx and Tx everywhere else, so the only thing missing right now, is that the right DMA channel is associated to the right peripheral.

image

image

image

compare to ... https://github.com/antoinevg/daisy_bsp/blob/209d938b58d2ffe0e05af96103c17aa5a0cc0391/src/audio.rs#L128-L135 https://github.com/antoinevg/daisy_bsp/blob/209d938b58d2ffe0e05af96103c17aa5a0cc0391/src/audio.rs#L145-L151 https://github.com/antoinevg/daisy_bsp/blob/209d938b58d2ffe0e05af96103c17aa5a0cc0391/src/audio.rs#L205-L210

Am I maybe missing something obvious?

antoinevg commented 2 years ago

If I remember correctly, configuring the DMA direction with the appropriate dma::MemoryToPeripheral and dma::PeripheralToMemory types should do it:

https://github.com/antoinevg/daisy_bsp/blob/209d938b58d2ffe0e05af96103c17aa5a0cc0391/src/audio.rs#L47-L57

backtail commented 2 years ago

Okay, I did a deep dive into the rust HAL DMA configuration file and found this: https://github.com/stm32-rs/stm32h7xx-hal/blob/b874460e54c7b27e32495dab46984fa4fb82f51f/src/dma/dma.rs#L938-L941 image

Here, the SAI blocks can only be configured in one specific way.

But in the new revision of the Daisy, these directions are reversed: https://github.com/electro-smith/libDaisy/blob/26256aaa54e4117c904ca431c0165981a6205fdf/src/daisy_seed.cpp#L267-L303

That means we need to configure the DMA without HAL until this will be patched out.

harshnoiise commented 2 years ago

@backtail do you have a working branch of this configuration of the DMA without HAL for the meantime? I just wanna start working on some audio stuff in rust on my daisy seed 😭

backtail commented 2 years ago

I am working on it right now. Either this evening or tomorrow it will be up!

backtail commented 2 years ago

Little update: Codec now works with the DMA! :tada: I just need to figure out a weird number conversion mistake, but that shouldn't be too hard. In the end, I manually set the registers for the DMA directions via pac:

// manually configure Channel B as transmit stream
let dma1_reg = unsafe { pac::Peripherals::steal().DMA1 };
dma1_reg.st[0].cr.modify(|_ , w | w.dir().peripheral_to_memory());
// manually configure Channel A as receive stream
dma1_reg.st[1].cr.modify(|_ , w | w.dir().memory_to_peripheral());

I will also update the lib with a revision check for compatibility with the old daisy boards. Will be up on monday (unfortunately I don't have time this weekend).

Here's a picture of the current audio output.

image

backtail commented 2 years ago

Finally finished!

You can find the major changes I did in these 2 commits (b2b4f9584172b338c32d66098a8923213e9349bd and 61b8844f4d2dda9c4218e3976d2d01d9932b6e4b).

Please refer to the examples/audio_testsignal.rs on commit 61b8844f4d2dda9c4218e3976d2d01d9932b6e4b on how to differently setup for the new codec.

PS: the number conversion bug is also fixed

harshnoiise commented 2 years ago

@backtail Solid work!! I just compiled and flashed it onto my daisy pod (the audio_testsignal example) and indeed got audio output!

A few things:

Sounds like it might have something to do with configuring mono/stereo logic for audio output? not sure if that has been implemented for this crate yet.

Oh, and this could be unrelated, but also, whenever I flash the Pod with the audio_testsignal example, as soon as it's flashed, it just produces a very high pitched noise, until i reset it via the reset button, and then it runs the audio_testsignal example as expected. Any idea of why that may be?


EDIT: So i replaced the sin oscillator in audio_testsignal with another saw oscillator and just very slightly detuned them from each other, and observed the following:

backtail commented 2 years ago
  1. When the headphone jack is fully inserted, I only get audio in the left headphone speaker, and the right headphone speaker just produces a high pitched buzzing noise
  2. When the headphone jack is half way inserted, i can head audio in both headphone speakers (left and right), but can still faintly hear the hight pitched buzzing noise (though it's a lot quieter in this scenario than when the headphone jack is fully inserted).

I wasn't able to reproduce this behavior. For my Daisy Pod, the Line Out and Headphones jacks work as expected. I didn't try it with headphones, though. However, I think that's a hardware and not a software issue either way. Are you able to measure Pin 18 and Pin 19 of your Seed with an oscilloscope? If those signals are clean then it's very likely an issue with your Pod.

Oh, and this could be unrelated, but also, whenever I flash the Pod with the audio_testsignal example, as soon as it's flashed, it just produces a very high pitched noise, until i reset it via the reset button, and then it runs the audio_testsignal example as expected. Any idea of why that may be?

I think this may be related to the way you flash your Daisy. I have two ways of uploading the binary to the board:

  1. cargo run with a running OpenOCD connection → works seamlessly when flashing new code without hick up or harsh noiise :wink:
  2. using the gdb debugger in VSCode and attach it to the running OpenOCD connection → once the new code is flashed, the debugger is stuck at the point where the MCU last instruction happened until I reset the device. This produces some high-pitched noise, because I believe that the debugger doesn't automatically reset the device.

EDIT: So i replaced the sin oscillator in audio_testsignal with another saw oscillator and just very slightly detuned them from each other, and observed the following:

* When headphone jack is full inserted to line out, I hear both saw oscillators, in each headphone speaker, it almost sounds as if I'm hearing each oscillator by themself in each speaker, (ie osc 1 goes to left speaker and osc 2 goes to right speaker).

* When the headphone jack is halfway inserted, the result sounds as expected as you would expect with two saw oscillators slightly detuned each other (natural phasing going on).
  Not sure if this is all by design or not based off the `src/audio.rs` file, so I'm just mentioning my findings in case it's not!

I think that is by design, actually. The 3.5mm jacks on the Pod have switches on them (to probably detect if TS or TRS connectors are being used). If you don't fully insert the jacks, you might touch the "right channel" switch with your "left channel" tip and that might cause the mixing of both signals on the same speaker.

miselaytes-anton commented 2 years ago

Thanks a lot for your effort!

I have tried to pass through audio from line in to phones output on my Pod and the sound I get is quite distorted. Do you maybe know what could be causing it?

backtail commented 2 years ago

@miselaytes-anton I haven't had time to check that example yet. Is it only clipping? Then it might be the configuration of the input levels of the new codec (it's quite easy to adjust these). I will look at that tomorrow!

miselaytes-anton commented 2 years ago

hm, I have tried lowering those, it makes the sound quieter, but it still has distortion. Looking forward to you test results.

backtail commented 2 years ago

@miselaytes-anton I was able to reproduce the noise. It's there even when no audio is being passed through! I will have to look at that in detail when I'm able to enter the lab next week (currently in quarantine) and maybe try a few little ideas the coming days.

miselaytes-anton commented 2 years ago

@backtail cool thanks! I was struggling a long time myself to make the codec work. So I am really happy you made this progress, so that I can reuse your work a bit. Since my codec implementation is different from yours and I still ran into the same problems I feel it's maybe not codec related.

backtail commented 2 years ago

I commented on your code, I spotted a little error.

On little thing I did was to turn on the ADC high pass filter (Reg: 0b000101, Bit 0: 1). That got rid of the high pitch noise when no audio is being passed through. But it didn't reduce to clipping. Changing the input line levels acted a like gate threshold effect. I also believe it's not a problem with the codec, but rather with the code. It might be number conversion error.

miselaytes-anton commented 2 years ago

I was able to get perfectly clean sound after I have removed .set_clock_strobe config options, so the following lines: https://github.com/backtail/daisy_bsp/blob/b7b80f78dafc837b90e97a265d2a3378094b84f7/src/audio.rs#L147 and https://github.com/backtail/daisy_bsp/blob/b7b80f78dafc837b90e97a265d2a3378094b84f7/src/audio.rs#L152

sainteckes commented 1 year ago

Hey I have a daisy board lying around, and I can not use the audio example as well with 0.5.1. Are there any updates on this or is it working with your repo backtail?

backtail commented 1 year ago

It should be working with my repo AFAIK. Prerequisite is a Daisy Seed with the WM8731 codec, it doesn't work with the older Daisy revisions.

sainteckes commented 1 year ago

It is the newest version Rev5. I still didn't manage to make the passthrough example work with rust. I will integrate my lib as a clib for now.

chaosprint commented 1 year ago

Perhaps try this? https://github.com/chaosprint/daisy-rust-playground

It's based on work from this repo and @backtail 's work

sainteckes commented 1 year ago

cool thanks, i will give it a try :) and https://github.com/mtthw-meyer/libdaisy-rust as well