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

Some questions about carrier frequency and duty cycle #4

Closed
Fonger opened this issue Jul 12, 2019 · 3 comments
Closed

Some questions about carrier frequency and duty cycle #4

Fonger opened this issue Jul 12, 2019 · 3 comments

Comments

@Fonger
Copy link
Contributor

Fonger commented Jul 12, 2019

I used to work with high level raspberry pi with LIRC. I find it sometimes unstable. Even if there's hardware 38KHz pwm support, however, the IR space delay using usleep() is too inaccurate under linux to guarantee a successful IR signal. (my AC fails to get my generated IR signal about 3~5% even if it's very close to the AC and with a transistor/5V support)

Q1: I'm very new to esp-8266 and RTOS. I think it's possible to make this much more accurate to use NodeMCU (ESP-8266) compared to raspberry pi, right? I just ordered NodeMCU v3 so I can't test right now. I'm just trying to build esp-homekit-demo and investigate the code.

Q2: How can I adjust carrier frequency and duty cycle when sending IR signal? For example, I want to emit 36khz / 30% duty cycle or 40khz (for Sony), is it possible?

I investigate other libraries, it seems most of them generate pulse manually by using
1 / 38KHz delay and gpio_wrtie high or low on any GPIO output. I think this is very inaccurate because the result are rounded to 26 us (from 26.315789474us). The actual generated carrier frequency is 38461Hz. It seems that your library use very different mechanism to generate the pulse. I can't figure out how to change carrier frequency and duty cycle.

I think this is the key part but I can't figure out why it's 62 and 2 here.

    // Set i2s clk freq 
    I2S.CONF = SET_FIELD(I2S.CONF, I2S_CONF_BCK_DIV, 62);
    I2S.CONF = SET_FIELD(I2S.CONF, I2S_CONF_CLKM_DIV, 2);
    I2S.CONF = SET_FIELD(I2S.CONF, I2S_CONF_BITS_MOD, 0);
@maximkulkin
Copy link
Owner

Yes, when I first started IR remote project, I looked for existing libraries and haven't found any decent one. IRRemoteESP8266 is direct port from Arduino and while having good library of different IR protocols, still assumes Arduino's single thread execution and thus relies on Arduino's usleep() precision. I wanted something that could reliably work on RTOS, so I created this one.

To answer your first question, yes it should be much more accurate as relies on hardware counters and interrupts (so it can run in background). My tests showed pretty decent precision with both clock generation and timing larger pulses.

Thanks for bringing up different frequencies. I haven't encountered different frequency protocols so this library at the moment works only with 38KHz. Yes, it relies on the same hardware pulses which occur every ~26us, so your resulting frequency might be different, although from what I have learned receivers usually have some tolerance towards frequency range and it should work fine.

The frequency indeed is configured by two dividers in I2S registers (lines that you have mentioned), 62 and 2 are dividers that give frequency closest to 38KHz. Here is an example from i2s_dma library to calculate dividers based on frequency (https://github.com/SuperHouse/esp-open-rtos/blob/a8c60e096093e9e9f4a60b885676adc2cf5b790a/extras/i2s_dma/i2s_dma.c#L133-L153):

#define BASE_FREQ (160000000L)

i2s_clock_div_t i2s_get_clock_div(int32_t freq)
{
    i2s_clock_div_t div = {0, 0};
    int32_t best_freq = 0;

    for (uint32_t bclk_div = 1; bclk_div < 64; bclk_div++) {
        for (uint32_t clkm_div = 1; clkm_div < 64; clkm_div++) {
            int32_t curr_freq = BASE_FREQ / (bclk_div * clkm_div);
            if (abs(freq - curr_freq) < abs(freq - best_freq)) {
                best_freq = curr_freq;
                div.clkm_div = clkm_div;
                div.bclk_div = bclk_div;
            }
        }
    }

    return div;
}

Can't find much info about I2S duty cycle, but Espressif ESP8266 Technical Reference mentions that their IR remote interface uses 1/3 (~30%) duty cycle.

@Fonger
Copy link
Contributor Author

Fonger commented Jul 17, 2019

@maximkulkin

This library rocks! Thanks! I finally get my ESP8266 and this library is working 100% perfect.
(same circuit, same power supply, same raw signals, but I only get 95-97% success rate from Raspberry pi LIRC library)

Q: I'm still confused by the i2s_dma library. I run the code with freq=38000 and
the result is 160000000 / 62(clkm_div) / 62(bclk_div) = 41623
It's not 62 and 2 and the output freq is about 41623 Hz

FWIW, in ESP8266 Technical Reference section 13.1.1, it mentions that GPIO14 can generate standard square ware with duty ratio of 50% exactly. It also says that system_timer_reinit() should be invoked to improve the timing precision of FRC2. I don't know exactly what these mean. Maybe this is is potential improvement?

13.1.1 Transmitting IR

Users can use the following methods to transmit carrier wave:
• BCK of I2S
• 38KHz carrier frequency generated by WS pin
• Carrier wave generated by any GPIO via sigma-delta function. However, the duty ratio
of carrier wave generated by sigma-delta is around 20%, thus MTMS pin (GPIO14) is
suggested, for this pin can generate standard square wave at a carrier frequency of
38KHz and a duty ratio of 50% exactly.

In the sample codes, data transmission queue is generated via the DSR TIMER interface of
system FRC2, while a state machine driving the transmission of infrared data is also
generated.
Considering that the timing precision of transmitting NEC infrared code should reach a level
of µs, when initiating IR TX, system_timer_reinit() should be invoked to improve the timing
precision of FRC2
. In user_config.h, enable the definition of USE_US_TIMER, then interface
function os_timer_arm_us can be invoked to implement precise timing at the level of µs

@maximkulkin
Copy link
Owner

You got me puzzled for a few days because I couldn't remember what is the rationale there.

So, indeed, the frequency formula for I2S data transmission is like that 160M / clkm / bclk. But the thing is that this gives you data transmission. But this library uses WS (word switch) pin of I2S. Word switch assumes that I2S transmission is two 16bit channels and WS alternates every 16 periods of main I2S frequency (to signal left and right 16 bit word). That gives you frequency that is 32 times slower. Thus the math for frequency in this library case is 160M / 32 / clkm / bclk. In my case it is 160M / 32 / 62 / 2 = 40322Hz with 50% duty cycle.

I do not remember why I picked this numbers (as there are better combinations like 10 & 13 or 3 & 44), I think I snitched it somewhere and it worked for me and I do not have enough equipment to experiment on. Also, logic analyzer shows timings pretty close to 38KHz.

Let me know if you'll find other variations working better for you.

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

No branches or pull requests

2 participants