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

feature request: UART with parity bit #26

Open
antonmeyer opened this issue May 27, 2023 · 2 comments
Open

feature request: UART with parity bit #26

antonmeyer opened this issue May 27, 2023 · 2 comments

Comments

@antonmeyer
Copy link

could you extend the UART with parity bit?

@boarchuz
Copy link
Owner

Hi @antonmeyer ,

Below I've modified the UART example to transmit the parity bit, and check the parity bit when receiving. I've tested at 9600 and 115200.

Would you mind testing it on your side, too? And let me know if there's anything in particular you need supported.

I'll clean it up then so it's much easier to include and customise. It will probably have the same usage as the existing ones, eg.
M_INCLUDE_UART_RX_PARITY(...) and M_INCLUDE_UART_TX_PARITY(...).

#include <sys/param.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"
#include "soc/rtc.h"
#include "esp_log.h"
#include "esp_attr.h"

#include "hulp.h"
#include "hulp_uart.h"

#include "sdkconfig.h"

#define PIN_ULP_TX GPIO_NUM_25
#define PIN_ULP_RX GPIO_NUM_26

#define BAUD_RATE 9600

#define ULP_STRING_GREETING     "Hello, what's your name?\n"
#define ULP_STRING_REPLY_START  "Nice to meet you, "
#define ULP_STRING_REPLY_END    "! I'm a ULP Coprocessor.\n"
#define ULP_RX_MAX_LEN          32

RTC_SLOW_ATTR ulp_var_t ulp_greeting HULP_UART_STRING_RESERVE(ULP_STRING_GREETING);
RTC_SLOW_ATTR ulp_var_t ulp_reply_start HULP_UART_STRING_RESERVE(ULP_STRING_REPLY_START);
RTC_SLOW_ATTR ulp_var_t ulp_reply_end HULP_UART_STRING_RESERVE(ULP_STRING_REPLY_END);

#define ULP_STRING_DEBUG    "PARITY ERROR\n"
RTC_SLOW_ATTR ulp_var_t ulp_debug_str HULP_UART_STRING_RESERVE(ULP_STRING_DEBUG);

RTC_SLOW_ATTR ulp_var_t ulp_rx_buffer HULP_UART_STRING_BUFFER(ULP_RX_MAX_LEN);

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
#endif

static RTC_SLOW_ATTR ulp_var_t ulp_subroutine_return_pc;

void init_ulp()
{
    enum
    {
        LBL_TX_GREET,
        LBL_TX_GREET_RETURN,

        LBL_RX_RESPONSE,
        LBL_RX_RESPONSE_RETURN,

        LBL_TX_REPLY_START,
        LBL_TX_REPLY_START_RETURN,

        LBL_TX_RESPONSE,
        LBL_TX_RESPONSE_RETURN,

        LBL_TX_REPLY_END,
        LBL_TX_REPLY_END_RETURN,

        LBL_SUBROUTINE_TX_ENTRY,
        LBL_SUBROUTINE_RX_ENTRY,

        LBL_UART_RX_NEXT_BYTE,
        LBL_UART_RX_DONE,
        LBL_UART_RX_NEXT_BIT,
        LBL_UART_RX_START_DATA_BITS,
        LBL_UART_RX_READ_FIRST_BIT,
        LBL_UART_RX_PARITY_ERROR,

        LBL_UART_TX_ENTRY,
        LBL_UART_TX_DONE,
        LBL_UART_TX_NEXT_BYTE,
        LBL_UART_TX_FIRST_BYTE,
        LBL_UART_TX_NEXT_BIT,
        LBL_UART_TX_BIT_IN_R0,
        LBL_UART_TX_NEXT_WORD,
        LBL_UART_TX_0_BIT,
        LBL_UART_TX_BIT_DONE,
        LBL_UART_TX_PARITY_BIT,

        LBL_UART_RETURN,
    };

    const ulp_insn_t program[] = {
        //Request name
        I_MOVI(R2, 0),
        M_MOVL(R3, LBL_TX_GREET_RETURN),
        I_PUT(R3, R2, ulp_subroutine_return_pc),
        I_MOVO(R1, ulp_greeting),
        M_BX(LBL_SUBROUTINE_TX_ENTRY),
        M_LABEL(LBL_TX_GREET_RETURN),

        //Read response (name) into buffer
        I_MOVI(R2, 0),
        M_MOVL(R3, LBL_RX_RESPONSE_RETURN),
        I_PUT(R3, R2, ulp_subroutine_return_pc),
        I_MOVO(R1, ulp_rx_buffer),
        M_BX(LBL_SUBROUTINE_RX_ENTRY),
        M_LABEL(LBL_RX_RESPONSE_RETURN),

        //Start intro
        I_MOVI(R2, 0),
        M_MOVL(R3, LBL_TX_REPLY_START_RETURN),
        I_PUT(R3, R2, ulp_subroutine_return_pc),
        I_MOVO(R1, ulp_reply_start),
        M_BX(LBL_SUBROUTINE_TX_ENTRY),
        M_LABEL(LBL_TX_REPLY_START_RETURN),


        //Echo the name that was just received
        I_MOVI(R2, 0),
        M_MOVL(R3, LBL_TX_RESPONSE_RETURN),
        I_PUT(R3, R2, ulp_subroutine_return_pc),
        I_MOVO(R1, ulp_rx_buffer),
        M_BX(LBL_SUBROUTINE_TX_ENTRY),
        M_LABEL(LBL_TX_RESPONSE_RETURN),

        //End intro
        I_MOVI(R2, 0),
        M_MOVL(R3, LBL_TX_REPLY_END_RETURN),
        I_PUT(R3, R2, ulp_subroutine_return_pc),
        I_MOVO(R1, ulp_reply_end),
        M_BX(LBL_SUBROUTINE_TX_ENTRY),
        M_LABEL(LBL_TX_REPLY_END_RETURN),

        //Sleep
        I_WAKE(),
        I_HALT(),

        //Dependencies for UART

        M_LABEL(LBL_SUBROUTINE_RX_ENTRY),
                    I_MOVI(R2, 0), // R2 == rx_len
                M_LABEL(LBL_UART_RX_NEXT_BYTE),
                    I_LD(R0, R1, 0), // Load metadata (note: buffer size in R0 upper 8 bits)
                    I_SUBR(R0, R0, R2), // Check if full (note: current received length in R2 upper 8 bits)
                    M_BL(LBL_UART_RX_DONE, 1 << 8),
                    I_GPIO_READ(PIN_ULP_RX),                           /*Wait here until pin goes low (start bit)*/
                        I_BGE(-1, 1),
                    I_STAGE_RST(),
                    // Delay 72 for 115200
                    I_DELAY(({
                        uint32_t fast_clk_freq = hulp_get_fast_clk_freq();
                        fast_clk_freq += (fast_clk_freq + 1) / 2;
                        fast_clk_freq += BAUD_RATE / 2;
                        fast_clk_freq /= BAUD_RATE;
                        fast_clk_freq -= 39;
                        (uint16_t)fast_clk_freq;
                    })),
                M_LABEL(LBL_UART_RX_NEXT_BIT),
                    // Delay until approx middle of bit ready (delay = I_DELAY(x) + loop execution overhead)
                        I_STAGE_INC(1),     // Increment bit count
                        // If this is first bit, go delay and read level
                        M_BSLT(LBL_UART_RX_READ_FIRST_BIT, 2),
                        I_ADDR(R1, R1, R0), // Toggle bit15 for odd/even bit count for partiy check
                        I_RSHI(R3, R3, 1),    // Rx bit -> reg_buf:15
                        I_ORR(R3, R3, R0),
                        // Delay 34 for 115200
                        I_DELAY((uint16_t)((hulp_get_fast_clk_freq() + (BAUD_RATE/2)) / (BAUD_RATE) - 40)),
                M_LABEL(LBL_UART_RX_READ_FIRST_BIT),
                        I_GPIO_READ(PIN_ULP_RX),
                        I_LSHI(R0, R0, 15),
                        M_BSLT(LBL_UART_RX_NEXT_BIT, 1 + 8), // If this is a start delay or data bit then loop
                        // R1[15] == odd number of 1 bits, R0[15] == parity_bit
                        I_ADDR(R0, R1, R0), // Add bit to lower bits of len for parity check
                        I_ANDI(R1, R1, (uint16_t)((1<<11)-1)), // Clear parity bit
                        M_BGE(LBL_UART_RX_PARITY_ERROR, (1 << 11)),

                    // Store the byte. ulpstring =one word metadata, then 2 chars in every word thereafter, so offset = 1+length/2
                    I_RSHI(R0, R2, 1 + 8),           /* length in upper 8 bits, shift 8+1 to halve it */
                    I_ADDR(R0, R1, R0),                   /*  add to string ptr */
                    I_ST(R3, R0, 1),                          /* then I_ST with 1 offset (as first word is metadata)) */
                    I_GPIO_READ(PIN_ULP_RX),                           /* Wait here until pin goes high to sync with stop bit*/
                        I_BL(-1, 1),
                    I_SUBI(R0, R3, ('\n')<<8),    /* Most recent byte is in upper 8 bits, so subtract (termination_char)<<8*/
                    M_BL(LBL_UART_RX_DONE, 1<<8),                                   /* If upper bits are 8b0 then byte matches termination_char so end */
                    I_ADDI(R2, R2, 1<<8),                      /*  else increment length and loop back to beginning of new byte */
                    M_BGE(LBL_UART_RX_NEXT_BYTE, 0),
                M_LABEL(LBL_UART_RX_PARITY_ERROR),
                    // Set length to 0 and fall through to save metadata
                    I_MOVI(R2, 0),

                #if 1 // Debugging
                    I_LD(R3, R1, 0),              /*Load the metadata (termination char / buffer full branches here)*/
                    I_ANDI(R3, R3, 0xFF<<8),          /*Update metadata with received length*/
                    I_RSHI(R2, R2, 8),
                    I_ORR(R3, R3, R2),
                    I_ST(R3, R1, 0),              /*  This I_ST also sets updated flag on metadata var */

                    I_MOVO(R1, ulp_debug_str),
                    M_BX(LBL_SUBROUTINE_TX_ENTRY),
                #endif


                M_LABEL(LBL_UART_RX_DONE),
                    I_LD(R3, R1, 0),              /*Load the metadata (termination char / buffer full branches here)*/
                    I_ANDI(R3, R3, 0xFF<<8),          /*Update metadata with received length*/
                    I_RSHI(R2, R2, 8),
                    I_ORR(R3, R3, R2),
                    I_ST(R3, R1, 0),              /*  This I_ST also sets updated flag on metadata var */

            M_BX(LBL_UART_RETURN),


        M_LABEL(LBL_SUBROUTINE_TX_ENTRY),
                #define ULP_UART_TX_STAGE_INC_VAL 28
                I_LD(R3, R1, 0),
                I_ANDI(R3, R3, 0xFF), // R3 = length
            M_LABEL(LBL_UART_TX_NEXT_WORD),
                I_ADDI(R1, R1, 1), // R1 = pointer + byte index
                I_LD(R2, R1, 0), // R2 = 2 data bytes
                I_STAGE_RST(),
            M_LABEL(LBL_UART_TX_NEXT_BYTE),
                I_SUBI(R3, R3, 1), // --len
                M_BXF(LBL_UART_TX_DONE),
                // Start
                I_HULP_UART_TX_LOW((PIN_ULP_TX)),
                I_DELAY((uint16_t)(hulp_get_fast_clk_freq() / (BAUD_RATE) - 19)),
            M_LABEL(LBL_UART_TX_NEXT_BIT),
                I_ANDI(R0, R2, 1),
                I_RSHI(R2, R2, 1),
            M_LABEL(LBL_UART_TX_BIT_IN_R0),
                M_BL(LBL_UART_TX_0_BIT, 1),
                I_HULP_UART_TX_HIGH((PIN_ULP_TX)),
                I_ADDI(R1, R1, 1 << 15),
                I_DELAY((uint16_t)((hulp_get_fast_clk_freq() / (BAUD_RATE)) - 42)),
            M_LABEL(LBL_UART_TX_BIT_DONE),
                I_STAGE_INC(ULP_UART_TX_STAGE_INC_VAL),
                 // if data bit, go to tx_next_bit
                M_BSLT(LBL_UART_TX_NEXT_BIT, MIN(((uint8_t)((8) * ULP_UART_TX_STAGE_INC_VAL)), ((uint8_t)((8+1+8) * ULP_UART_TX_STAGE_INC_VAL)))),
                // Prepare parity
                I_RSHI(R0, R1, 15),
                // if parity, tx parity bit
                M_BSLT(LBL_UART_TX_BIT_IN_R0, MIN(((uint8_t)((8+1) * ULP_UART_TX_STAGE_INC_VAL)), ((uint8_t)((8+1+8+1) * ULP_UART_TX_STAGE_INC_VAL)))),
                // else end byte and loop to next byte/word
                I_HULP_UART_TX_HIGH((PIN_ULP_TX)),
                I_DELAY((uint16_t)(hulp_get_fast_clk_freq() / (BAUD_RATE) - 0)),
                // Note: first byte stage = 252, second byte stage = 248
                M_BSGE(LBL_UART_TX_NEXT_BYTE, (uint8_t)((8+1) * ULP_UART_TX_STAGE_INC_VAL)),
                M_BX(LBL_UART_TX_NEXT_WORD),

            M_LABEL(LBL_UART_TX_0_BIT),
                I_HULP_UART_TX_LOW((PIN_ULP_TX)),
                I_DELAY((uint16_t)((hulp_get_fast_clk_freq() / (BAUD_RATE)) - 44)),
                M_BX(LBL_UART_TX_BIT_DONE),

            M_LABEL(LBL_UART_TX_DONE),

        M_LABEL(LBL_UART_RETURN),
            I_MOVI(R2, 0),
            I_GET(R3, R2, ulp_subroutine_return_pc),
            I_BXR(R3),

    };

    // Set the contents of string buffers
    if(
        hulp_uart_string_set(ulp_greeting, ARRAY_SIZE(ulp_greeting), (const char*)ULP_STRING_GREETING) < 0 ||
        hulp_uart_string_set(ulp_reply_start, ARRAY_SIZE(ulp_reply_start), ULP_STRING_REPLY_START) < 0 ||
        hulp_uart_string_set(ulp_reply_end, ARRAY_SIZE(ulp_reply_end), ULP_STRING_REPLY_END) < 0 ||
        hulp_uart_string_set(ulp_debug_str, ARRAY_SIZE(ulp_debug_str), ULP_STRING_DEBUG) < 0
    )
    {
        abort();
    }

#if CONFIG_HULP_UART_TX_OD
    ESP_ERROR_CHECK(hulp_configure_pin(PIN_ULP_TX, RTC_GPIO_MODE_DISABLED, GPIO_PULLUP_ONLY, 0));
#else
    ESP_ERROR_CHECK(hulp_configure_pin(PIN_ULP_TX, RTC_GPIO_MODE_OUTPUT_ONLY, GPIO_FLOATING, 1));
#endif
    ESP_ERROR_CHECK(hulp_configure_pin(PIN_ULP_RX, RTC_GPIO_MODE_INPUT_ONLY, GPIO_PULLUP_ONLY, 0));
    ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 2/* 10ULL */ * 1000 * 1000, 0));
    ESP_ERROR_CHECK(hulp_ulp_run(0));
}

void ulp_isr(void *task_handle_ptr)
{
    xTaskNotifyFromISR(*(TaskHandle_t*)task_handle_ptr, 0, eNoAction, NULL);
}

void app_main()
{
    // ULP will trigger an interrupt when a new UART string is received. Set up here.
    TaskHandle_t main_handle =  xTaskGetCurrentTaskHandle();
    hulp_ulp_isr_register(&ulp_isr, &main_handle);
    hulp_ulp_interrupt_en();

    // Load and start the program.
    init_ulp();

    for (;;)
    {
        // Block until notified by ULP+ISR
        xTaskNotifyWait(0, 0, NULL, portMAX_DELAY);

        // Get the contents of the buffer and display
        char name[ULP_RX_MAX_LEN + 1];
        int len = hulp_uart_string_get(ulp_rx_buffer, name, sizeof(name), false);
        if(len < 0)
        {
            abort();
        }
        printf("ULP RX String (%d): %s\n", len, name);
    }
}

@antonmeyer
Copy link
Author

antonmeyer commented May 30, 2023 via email

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