Skip to content

Commit

Permalink
Merge pull request #9 from ZIMO-Elektronik/rmt
Browse files Browse the repository at this point in the history
fix!: limit number of preamble bits of RMT encoder to 30
  • Loading branch information
higaski committed Mar 26, 2024
2 parents 240e3ce + a7ec252 commit 2da5ae8
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 66 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

##
- Limit number of preamble bits of RMT encoder to 30
- Rename dcc_encoder_config_t::cutoutbit_duration to bidibit_duration

## 0.32.0
- Individual timings for namespace dcc::rx and dcc::tx
- Move time2bit to namespace dcc::rx
Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,27 @@ Similar to the other encoders of the [ESP-IDF](https://github.com/espressif/esp-
#include <rmt_dcc_encoder.h>

dcc_encoder_config_t encoder_config{.num_preamble = 17u,
.cutoutbit_duration = 60u,
.bidibit_duration = 60u,
.bit1_duration = 58u,
.bit0_duration = 100u,
.endbit_duration = 58u - 24u,
.flags{.zimo0 = true}};
.flags{.invert = false, .zimo0 = true}};
rmt_encoder_handle_t* encoder;
ESP_ERROR_CHECK(rmt_new_dcc_encoder(&encoder_config, &encoder));
```
```
The following members of `dcc_encoder_config_t` may require some explanation.
#### BiDi bit duration
This duration may be set to values between 57-61 to enable the generation of BiDi cutout bits prior to the next preamble. These four cutout bits would be sent in the background if the cutout was not active. The following graphic from [RCN-217](https://normen.railcommunity.de/RCN-217.pdf) visualizes these bits with a dashed line.
![](https://raw.githubusercontent.com/ZIMO-Elektronik/DCC/rmt/data/images/bidibit_duration.png)
#### End bit duration
Mainly due to a workaround of [esp-idf #13003](https://github.com/espressif/esp-idf/issues/13003) the end bit duration can adjusted independently of the bit1 duration. This allows the RMT transmission complete callback to be executed at the right time.
#### Flags
- invert
Boolean value which corresponds to the level of the first half bit.
- zimo0
Transmit 0-bit prior to preamble.
3 changes: 2 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Consist address is never used, although it should be sent in channel 1 in case it's active
- Literal for converting CV number to index? The "- 1u" everything is fucking ugly
- dcc::tx::CrtpBase currently pops it's deque at a bad time. Although the design or inplace_deque guarantees that it's not UB it looks funky.
- dcc::tx::CrtpBase currently pops it's deque at a bad time. Although the design or inplace_deque guarantees that it's not UB it looks funky.
- Replace `RMT_MEM_ALLOC_CAPS` macro with [`rmt_alloc_encoder_mem`](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/rmt.html#_CPPv421rmt_alloc_encoder_mem6size_t)
Binary file added data/images/bidibit_duration.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions examples/rmt/main/app_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ extern "C" void app_main() {

// New DCC encoder
dcc_encoder_config_t encoder_config{.num_preamble = 17u,
.cutoutbit_duration = 60u,
.bidibit_duration = 60u,
.bit1_duration = 58u,
.bit0_duration = 100u,
.endbit_duration = 58u - 24u,
.flags{.zimo0 = true}};
.flags{.invert = false, .zimo0 = true}};
rmt_encoder_handle_t rmt_encoder{};
ESP_ERROR_CHECK(rmt_new_dcc_encoder(&encoder_config, &rmt_encoder));

Expand Down
2 changes: 1 addition & 1 deletion idf_component.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ maintainers:
- "Vincent Hamp <hamp@zimo.at>"

dependencies:
idf: ">=5.0.0"
idf: ">=5.0.3"
13 changes: 9 additions & 4 deletions include/rmt_dcc_encoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ extern "C" {

/// DCC encoder configuration
typedef struct {
/// Number of preamble bits [17, 255]
/// Number of preamble bits [17, 30]
uint8_t num_preamble;

/// Optional duration of cutout bit [0 | 57, 61]
uint16_t cutoutbit_duration;
/// Optional duration of BiDi cutout bit 0 || [57, 61]
uint8_t bidibit_duration;

/// Duration of 1 bit [56, 60]
uint8_t bit1_duration;
Expand All @@ -31,10 +31,15 @@ typedef struct {
uint8_t bit0_duration;

/// Duration of end bit [0, 60]
///
/// This is mostly to work around the limitations of
/// https://github.com/espressif/esp-idf/issues/13003
uint8_t endbit_duration;

struct {
/// Invert
/// Invert RMT symbol level
///
/// This boolean value corresponds to the level of the first half bit.
bool invert : 1;

/// ZIMO 0
Expand Down
114 changes: 59 additions & 55 deletions src/rmt_dcc_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,13 @@ typedef struct {
rmt_encoder_t base;
rmt_encoder_t* copy_encoder;
rmt_encoder_t* bytes_encoder;
rmt_symbol_word_t cutout_symbol;
rmt_symbol_word_t bidi_symbol;
rmt_symbol_word_t one_symbol;
rmt_symbol_word_t zero_symbol;
rmt_symbol_word_t end_symbol;
size_t num_preamble_symbols;
size_t num_symbols;
enum { Cutout, Zimo0, Preamble, Start, Data, End } state;
enum { BiDi, Zimo0, Preamble, Start, Data, End } state;
struct {
bool zimo0 : 1;
} flags;
Expand All @@ -58,33 +58,38 @@ static size_t IRAM_ATTR rmt_encode_dcc_bit(rmt_dcc_encoder_t* dcc_encoder,
rmt_encode_state_t* ret_state,
rmt_symbol_word_t const* symbol) {
size_t encoded_symbols = 0u;
rmt_encode_state_t state = 0;
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_encoder_handle_t copy_encoder = dcc_encoder->copy_encoder;
encoded_symbols += copy_encoder->encode(
copy_encoder, channel, symbol, sizeof(rmt_symbol_word_t), &state);
*ret_state = state;
return encoded_symbols;
}

/// Encode cutout
/// Encode BiDi cutout
///
/// \param dcc_encoder DCC encoder handle
/// \param channel RMT TX channel handle
/// \param ret_state Returned current encoder state
/// \return Number of RMT symbols that the primary data has been encoded into
static size_t IRAM_ATTR rmt_encode_dcc_cutout(rmt_dcc_encoder_t* dcc_encoder,
rmt_channel_handle_t channel,
rmt_encode_state_t* ret_state) {
static size_t IRAM_ATTR rmt_encode_dcc_bidi(rmt_dcc_encoder_t* dcc_encoder,
rmt_channel_handle_t channel,
rmt_encode_state_t* ret_state) {
size_t encoded_symbols = 0u;
rmt_encode_state_t state = 0;
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_encoder_handle_t copy_encoder = dcc_encoder->copy_encoder;

if (!dcc_encoder->cutout_symbol.duration0) state |= RMT_ENCODING_COMPLETE;
// Skip
if (!dcc_encoder->bidi_symbol.duration0) {
state |= RMT_ENCODING_COMPLETE;
dcc_encoder->state = Zimo0;
}
// Encode 4 BiDi cutout symbols
else
while (dcc_encoder->state == Cutout) {
while (dcc_encoder->state == BiDi) {
size_t const tmp = copy_encoder->encode(copy_encoder,
channel,
&dcc_encoder->cutout_symbol,
&dcc_encoder->bidi_symbol,
sizeof(rmt_symbol_word_t),
&state);
encoded_symbols += tmp;
Expand All @@ -94,13 +99,9 @@ static size_t IRAM_ATTR rmt_encode_dcc_cutout(rmt_dcc_encoder_t* dcc_encoder,
dcc_encoder->num_symbols = 0u;
dcc_encoder->state = Zimo0;
}
if (state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
}
if (state & RMT_ENCODING_MEM_FULL) break;
}

out:
*ret_state = state;
return encoded_symbols;
}
Expand All @@ -115,12 +116,20 @@ static size_t IRAM_ATTR rmt_encode_dcc_zimo0(rmt_dcc_encoder_t* dcc_encoder,
rmt_channel_handle_t channel,
rmt_encode_state_t* ret_state) {
size_t encoded_symbols = 0u;
rmt_encode_state_t state = 0;
if (!dcc_encoder->flags.zimo0) state |= RMT_ENCODING_COMPLETE;
else
rmt_encode_state_t state = RMT_ENCODING_RESET;

// Skip
if (!dcc_encoder->flags.zimo0) {
state |= RMT_ENCODING_COMPLETE;
dcc_encoder->state = Preamble;
}
// Encode ZIMO 0
else {
encoded_symbols += rmt_encode_dcc_bit(
dcc_encoder, channel, &state, &dcc_encoder->zero_symbol);
if (state & RMT_ENCODING_COMPLETE) dcc_encoder->state = Preamble;
if (state & RMT_ENCODING_COMPLETE) dcc_encoder->state = Preamble;
}

*ret_state = state;
return encoded_symbols;
}
Expand All @@ -135,7 +144,7 @@ static size_t IRAM_ATTR rmt_encode_dcc_preamble(rmt_dcc_encoder_t* dcc_encoder,
rmt_channel_handle_t channel,
rmt_encode_state_t* ret_state) {
size_t encoded_symbols = 0u;
rmt_encode_state_t state = 0;
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_encoder_handle_t copy_encoder = dcc_encoder->copy_encoder;

while (dcc_encoder->state == Preamble) {
Expand All @@ -151,13 +160,9 @@ static size_t IRAM_ATTR rmt_encode_dcc_preamble(rmt_dcc_encoder_t* dcc_encoder,
dcc_encoder->num_symbols = 0u;
dcc_encoder->state = Start;
}
if (state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
}
if (state & RMT_ENCODING_MEM_FULL) break;
}

out:
*ret_state = state;
return encoded_symbols;
}
Expand All @@ -172,7 +177,7 @@ static size_t IRAM_ATTR rmt_encode_dcc_start(rmt_dcc_encoder_t* dcc_encoder,
rmt_channel_handle_t channel,
rmt_encode_state_t* ret_state) {
size_t encoded_symbols = 0u;
rmt_encode_state_t state = 0;
rmt_encode_state_t state = RMT_ENCODING_RESET;
encoded_symbols +=
rmt_encode_dcc_bit(dcc_encoder, channel, &state, &dcc_encoder->zero_symbol);
if (state & RMT_ENCODING_COMPLETE) dcc_encoder->state = Data;
Expand All @@ -194,7 +199,7 @@ static size_t IRAM_ATTR rmt_encode_dcc_data(rmt_dcc_encoder_t* dcc_encoder,
size_t data_size,
rmt_encode_state_t* ret_state) {
size_t encoded_symbols = 0u;
rmt_encode_state_t state = 0;
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_encoder_handle_t bytes_encoder = dcc_encoder->bytes_encoder;
uint8_t const* data = (uint8_t const*)primary_data;

Expand All @@ -211,7 +216,6 @@ static size_t IRAM_ATTR rmt_encode_dcc_data(rmt_dcc_encoder_t* dcc_encoder,
dcc_encoder->num_symbols = 0u;
dcc_encoder->state = End;
}
if (state & RMT_ENCODING_MEM_FULL) state |= RMT_ENCODING_MEM_FULL;

*ret_state = state;
return encoded_symbols;
Expand All @@ -227,12 +231,12 @@ static size_t IRAM_ATTR rmt_encode_dcc_end(rmt_dcc_encoder_t* dcc_encoder,
rmt_channel_handle_t channel,
rmt_encode_state_t* ret_state) {
size_t encoded_symbols = 0u;
rmt_encode_state_t state = 0;
rmt_encode_state_t state = RMT_ENCODING_RESET;
encoded_symbols +=
rmt_encode_dcc_bit(dcc_encoder, channel, &state, &dcc_encoder->end_symbol);
if (state & RMT_ENCODING_COMPLETE) {
dcc_encoder->num_symbols = 0u;
dcc_encoder->state = Cutout;
dcc_encoder->state = BiDi;
}
*ret_state = state;
return encoded_symbols;
Expand All @@ -252,18 +256,18 @@ static size_t IRAM_ATTR rmt_encode_dcc(rmt_encoder_t* encoder,
size_t data_size,
rmt_encode_state_t* ret_state) {
size_t encoded_symbols = 0;
rmt_encode_state_t state = 0;
rmt_encode_state_t state = RMT_ENCODING_RESET;
rmt_encode_state_t session_state = 0;
rmt_dcc_encoder_t* dcc_encoder =
__containerof(encoder, rmt_dcc_encoder_t, base);

switch (dcc_encoder->state) {
case Cutout:
case BiDi:
encoded_symbols +=
rmt_encode_dcc_cutout(dcc_encoder, channel, &session_state);
rmt_encode_dcc_bidi(dcc_encoder, channel, &session_state);
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
break;
}
// fallthrough

Expand All @@ -272,7 +276,7 @@ static size_t IRAM_ATTR rmt_encode_dcc(rmt_encoder_t* encoder,
rmt_encode_dcc_zimo0(dcc_encoder, channel, &session_state);
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
break;
}
// fallthrough

Expand All @@ -281,7 +285,7 @@ static size_t IRAM_ATTR rmt_encode_dcc(rmt_encoder_t* encoder,
rmt_encode_dcc_preamble(dcc_encoder, channel, &session_state);
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
break;
}
// fallthrough

Expand All @@ -291,7 +295,7 @@ static size_t IRAM_ATTR rmt_encode_dcc(rmt_encoder_t* encoder,
rmt_encode_dcc_start(dcc_encoder, channel, &session_state);
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
break;
}
// fallthrough

Expand All @@ -300,7 +304,7 @@ static size_t IRAM_ATTR rmt_encode_dcc(rmt_encoder_t* encoder,
dcc_encoder, channel, primary_data, data_size, &session_state);
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
break;
}
if (dcc_encoder->state < End) goto start;
// fallthrough
Expand All @@ -311,12 +315,11 @@ static size_t IRAM_ATTR rmt_encode_dcc(rmt_encoder_t* encoder,
if (session_state & RMT_ENCODING_COMPLETE) state |= RMT_ENCODING_COMPLETE;
if (session_state & RMT_ENCODING_MEM_FULL) {
state |= RMT_ENCODING_MEM_FULL;
goto out;
break;
}
// fallthrough
}

out:
*ret_state = state;
return encoded_symbols;
}
Expand Down Expand Up @@ -371,16 +374,17 @@ esp_err_t rmt_new_dcc_encoder(dcc_encoder_config_t const* config,
rmt_encoder_handle_t* ret_encoder) {
esp_err_t ret = ESP_OK;
rmt_dcc_encoder_t* dcc_encoder = NULL;
ESP_GOTO_ON_FALSE(config && ret_encoder && //
config->num_preamble >= 17u && //
(!config->cutoutbit_duration || //
(config->cutoutbit_duration >= 57u && //
config->cutoutbit_duration <= 61u)) && //
config->bit1_duration >= 56u && //
config->bit1_duration <= 60u && //
config->bit0_duration >= 97u && //
config->bit0_duration <= 114u && //
config->endbit_duration <= 60u, //
ESP_GOTO_ON_FALSE(config && ret_encoder && //
config->num_preamble >= 17u && //
config->num_preamble <= 30u && //
(!config->bidibit_duration || //
(config->bidibit_duration >= 57u && //
config->bidibit_duration <= 61u)) && //
config->bit1_duration >= 56u && //
config->bit1_duration <= 60u && //
config->bit0_duration >= 97u && //
config->bit0_duration <= 114u && //
config->endbit_duration <= 60u, //
ESP_ERR_INVALID_ARG,
err,
TAG,
Expand All @@ -405,11 +409,11 @@ esp_err_t rmt_new_dcc_encoder(dcc_encoder_config_t const* config,
dcc_encoder->num_preamble_symbols = config->num_preamble;

// Setup RMT symbols
if (config->cutoutbit_duration)
dcc_encoder->cutout_symbol = (rmt_symbol_word_t){
.duration0 = config->cutoutbit_duration,
if (config->bidibit_duration)
dcc_encoder->bidi_symbol = (rmt_symbol_word_t){
.duration0 = config->bidibit_duration,
.level0 = 0u ^ config->flags.invert,
.duration1 = config->cutoutbit_duration,
.duration1 = config->bidibit_duration,
.level1 = 1u ^ config->flags.invert,
};
dcc_encoder->one_symbol = (rmt_symbol_word_t){
Expand Down

0 comments on commit 2da5ae8

Please sign in to comment.