Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

I2D driver doesn't work (ESP8266-01) (GIT8266O-769) #1192

Open
papadeltasierra opened this issue Aug 7, 2022 · 12 comments · May be fixed by #1232
Open

I2D driver doesn't work (ESP8266-01) (GIT8266O-769) #1192

papadeltasierra opened this issue Aug 7, 2022 · 12 comments · May be fixed by #1232

Comments

@papadeltasierra
Copy link

Ref: #981

The issue above seems to be same issue but has been closed, claiming (if I read it correctly) some fault on the board being used. However I know my board works and yet this example still does not work.

Environment

  • Development Kit:
  • IDF version: a192988
  • Development Env: Make
  • Operating System: Ubuntu
  • Power Supply: external 5V

Problem Description

I have compiled and installed the examples/peripherals/i2s example but it does not work. I added a log to trace the number of bytes written by i2s_write() and this is always zero.

From reading the code and adding logging in various places, it would appear that the interrupt handler has to signal an underun, or at least some sort of TX event, before the curr_ptr is given a buffer to write to. As far as I can see the interrupt NEVER triggers.

This is NOT a hardware issue as my very old "hand rolled DMA code" from about 5 years ago correctly runs DMAso tehre is some sort of bug in the current SDK.

On thing that does look odd is that when the TX interrupt is enabled, we tweak the rx_link (sic) and when the RX interrupt is en/disabled, we tweak the tx_link! However this is not the cause of the issue as I tried switching these and this had not affect.

Expected Behavior

i2s_write() writes the expected number of bytes and the DMA causes the appropriate lines to switch appropriately.

Actual Behavior

i2s_write() always returns "0 bytes written" and there apears to be no "action" on the SLC lines.

Steps to repropduce

  1. Add a trace log after the is2_write() in i2s_example_main.c to trace the number of bytes written.
  2. Build and install the application
  3. Watch the debug output.

Code to reproduce this issue

just the is2_example_main.c application.```
Debug log goes here.
Please copy the plain text here for us to search the error log. Or attach the complete logs but leave the main part here if the log is too long.

@github-actions github-actions bot changed the title I2D driver doesn't work (ESP8266-01) I2D driver doesn't work (ESP8266-01) (GIT8266O-769) Aug 7, 2022
@AMKrol
Copy link

AMKrol commented Aug 20, 2022

Hi,

Try #1169. This change enable transmit data via I2S.
I was tried it on ESP-WROOM-02 board. Now the I2S interface work but it sent wrong data, backtrace for error reffers to line

memcpy(data_ptr, src_byte, bytes_can_write);

Probably is problem with definition of data_ptr in this place.

@papadeltasierra
Copy link
Author

Thanks @AMKrol . I've just got DMA working (sending out) and will try and sort out a PR in the next few days with my changes. Whoever decided to mix RX/TX and TX/RX in the same module was foolish; should have been consistent and just had a comment indicating that "write/send uses RX" and "read/incoming uses TX" to avoid lots of confusion!

@papadeltasierra
Copy link
Author

@AMKrol, I have a Git branch (based on master) with the fixes I believe you require for DMA to work. What is the process for making this available to you? I would suggest it gets some testing as I do not have a general purpose signal analyser but I have made the same changes to some code I have written (customising i2s.c for a specific use case of driving a 433MHz On-Off Keying transmitter) and this code now works whereas it did not with the code as it currently is in the master branch`.

In case this is the simplest, I have copied the diff below.

diff --git a/components/esp8266/driver/i2s.c b/components/esp8266/driver/i2s.c
index 15be3d6a..d9364031 100644
--- a/components/esp8266/driver/i2s.c
+++ b/components/esp8266/driver/i2s.c
@@ -129,8 +129,8 @@ esp_err_t i2s_enable_rx_intr(i2s_port_t i2s_num)
     I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
     I2S_CHECK(p_i2s_obj[i2s_num], "i2s not installed yet", ESP_FAIL);
     I2S_ENTER_CRITICAL();
-    p_i2s_obj[i2s_num]->dma->int_ena.tx_suc_eof = 1;
-    p_i2s_obj[i2s_num]->dma->int_ena.tx_dscr_err = 1;
+    p_i2s_obj[i2s_num]->dma->int_ena.rx_eof = 1;
+    p_i2s_obj[i2s_num]->dma->int_ena.rx_dscr_err = 1;
     I2S_EXIT_CRITICAL();
     return ESP_OK;
 }
@@ -140,8 +140,8 @@ esp_err_t i2s_disable_rx_intr(i2s_port_t i2s_num)
     I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
     I2S_CHECK(p_i2s_obj[i2s_num], "i2s not installed yet", ESP_FAIL);
     I2S_ENTER_CRITICAL();
-    p_i2s_obj[i2s_num]->dma->int_ena.tx_suc_eof = 0;
-    p_i2s_obj[i2s_num]->dma->int_ena.tx_dscr_err = 0;
+    p_i2s_obj[i2s_num]->dma->int_ena.rx_eof = 0;
+    p_i2s_obj[i2s_num]->dma->int_ena.rx_dscr_err = 0;
     I2S_EXIT_CRITICAL();
     return ESP_OK;
 }
@@ -151,8 +151,8 @@ esp_err_t i2s_disable_tx_intr(i2s_port_t i2s_num)
     I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
     I2S_CHECK(p_i2s_obj[i2s_num], "i2s not installed yet", ESP_FAIL);
     I2S_ENTER_CRITICAL();
-    p_i2s_obj[i2s_num]->dma->int_ena.rx_eof = 0;
-    p_i2s_obj[i2s_num]->dma->int_ena.rx_dscr_err = 0;
+    p_i2s_obj[i2s_num]->dma->int_ena.tx_suc_eof = 0;
+    p_i2s_obj[i2s_num]->dma->int_ena.tx_dscr_err = 0;
     I2S_EXIT_CRITICAL();
     return ESP_OK;
 }
@@ -162,8 +162,8 @@ esp_err_t i2s_enable_tx_intr(i2s_port_t i2s_num)
     I2S_CHECK((i2s_num < I2S_NUM_MAX), "i2s_num error", ESP_ERR_INVALID_ARG);
     I2S_CHECK(p_i2s_obj[i2s_num], "i2s not installed yet", ESP_FAIL);
     I2S_ENTER_CRITICAL();
-    p_i2s_obj[i2s_num]->dma->int_ena.rx_eof = 1;
-    p_i2s_obj[i2s_num]->dma->int_ena.rx_dscr_err = 1;
+    p_i2s_obj[i2s_num]->dma->int_ena.tx_suc_eof = 1;
+    p_i2s_obj[i2s_num]->dma->int_ena.tx_dscr_err = 1;
     I2S_EXIT_CRITICAL();
     return ESP_OK;
 }
@@ -380,12 +380,12 @@ esp_err_t i2s_start(i2s_port_t i2s_num)

     if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) {
         i2s_enable_tx_intr(i2s_num);
-        p_i2s_obj[i2s_num]->dma->rx_link.start = 1;
+        p_i2s_obj[i2s_num]->dma->tx_link.start = 1;
     }

     if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) {
         i2s_enable_rx_intr(i2s_num);
-        p_i2s_obj[i2s_num]->dma->tx_link.start = 1;
+        p_i2s_obj[i2s_num]->dma->rx_link.start = 1;
     }

     // Both TX and RX are started to ensure clock generation
@@ -406,12 +406,12 @@ esp_err_t i2s_stop(i2s_port_t i2s_num)
     dma_intr_disable();

     if (p_i2s_obj[i2s_num]->mode & I2S_MODE_TX) {
-        p_i2s_obj[i2s_num]->dma->rx_link.stop = 1;
+        p_i2s_obj[i2s_num]->dma->tx_link.stop = 1;
         i2s_disable_tx_intr(i2s_num);
     }

     if (p_i2s_obj[i2s_num]->mode & I2S_MODE_RX) {
-        p_i2s_obj[i2s_num]->dma->tx_link.stop = 1;
+        p_i2s_obj[i2s_num]->dma->rx_link.stop = 1;
         i2s_disable_rx_intr(i2s_num);
     }

@MishaSyd
Copy link

The inconsistency in the description of the registers can be corrected using the slc_struct.h file. You need to swap struct txlink and struct r xlink. Swap struct int_st, int_ena, int_clr

uint32_t tx_done: 1;
uint32_t tx_suc_eof: 1;

uint32_t rx_done: 1;
uint32_t rx_eof: 1;
Swap uint32_t rx_eof_des_addr; uint32_t tx_eof_des_addr;
Along with your vaccinations, you need to add the following to the i2s.c file:
extern void rom_i2c_writeReg_Mask(int, int, int, int, int, int);
#ifndef i2c_bppll
#define i2c_bbpll 0x67
#define i2c_bbpll_en_audio_clock_out 4
#define i2c_bbpll_en_audio_clock_out_msb 7
#define i2c_bbpll_en_audio_clock_out_lsb 7
#define i2c_bbpll_hostid 4
#define i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata) rom_i2c_writeReg_Mask(block, host_id, reg_add, Msb, Lsb, indata)

Insert the line into the static esp_err_t i2s_param_config(i2s_port_t i2s_num, const i2s_config_t *i2s_config) function
i2c_writeReg_Mask_def(i2c_bbpll, i2c_bbpll_en_audio_clock_out, 1);

@papadeltasierra
Copy link
Author

@MishaSyd Am I correct that you are making the code back like the old versions of the libraries were? My issue arises because the meaning of "TX" and "RX" is switched between the two sides of the DMA code some sometimes RX means "from hardware towards memory via DMA" and sometimes it means "from memory towards hardware via DMA" - at least the old versions of the DMA code were consistent in that "sending" out "from memory towards hardware via DMA" was always RX (and vice versa).

@MishaSyd
Copy link

MishaSyd commented Dec 27, 2022 via email

@MishaSyd
Copy link

MishaSyd commented Dec 27, 2022 via email

@ncvicchi
Copy link

Thanks @AMKrol . I've just got DMA working (sending out) and will try and sort out a PR in the next few days with my changes. Whoever decided to mix RX/TX and TX/RX in the same module was foolish; should have been consistent and just had a comment indicating that "write/send uses RX" and "read/incoming uses TX" to avoid lots of confusion!

@papadeltasierra did you succeded on making I2S example to work? I applied the changes from your diff but still no luck.

@papadeltasierra
Copy link
Author

I am unable to test the samples because I don't have a scope to test output but I do have a heavily hacked example that is using DMA to drive a 433MHz transmitter and this would not work until I applied my changes. It runs 24/7 on my desk and uses DMA to generate a stable OOF keyed signal (using multiple by 32 coding i.e. an output logic "1" is represented by 0xFFFFFFF) to allow for the fact that I can't run the DMA slow enough to represent the required OOF intervals signal as a simple 1:1 mapping.
This is code that used to work using a far older version of the APIs (similarly hacked) but when I had to rework the code (the server I used as an HTTP relay was closed and I switched to using Azure IoTHub), I updated to the new API and discovered that the DMA would not work as written.

@papadeltasierra
Copy link
Author

Can you see this project? https://github.com/papadeltasierra/display433 There should be no secrets in here (please tell me if there are ;-) ) but the "i2s_ook" code is my heavily hacked code that uses the DMA.

@ncvicchi
Copy link

I tried to use that code with no success either :( (migrating it to the 3.4 sdk).
I can't believe this has gone so long unfixed

@papadeltasierra
Copy link
Author

papadeltasierra commented Jan 25, 2023

Sorry - that's a much as I can help. I tinker a little but no more. One of those nice cheap oscilloscopes is one my "nice to have" list but it's never quote reached the top. I don't know how good your understanding of DMA and the software is but I found these bugs by printing out the code and spending an afternoon reading it carefully. The key thing is that in the code to make the pins go up and down is "receive" and to be able to read data from an external source you "send".
My understanding is that it looks at it from the perspective of something between the DMA and the pins so the "something" "receives" from the DMA and toggles the pins, which I think of as "sending"! In the newer version of the SDK, someone tried to alter this meaning on the DMA side so now we have "receive" and "send" having different meanings in different bits of the code causing confusion and bugs. The very old SDK was correct but very differently structures but you can just about map between the two.
Best of luck!

@szekelyisz szekelyisz linked a pull request Apr 4, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants