cancel
Showing results for 
Search instead for 
Did you mean: 

Implementing a DMX512 Protocol Receiver with STM32G0C1CE and Zephyr OS v4.1.0

Prafu_N
Associate

 

Hello, So I'm tackling a DMX512 project using an STM32G0C1CE with Zephyr OS v4.1.0 and I'm running into a bit of a snag with detecting the BREAK and Mark After Break (MAB) signals to start receiving the DMX slot data via USART.

I've already tried a couple of things:

  1. Using the framing error detection, but that didn't seem to work out.
  2. Since I only have the USART3_RX pin available at PB11, I tried configuring it as a GPIO input to catch the BREAK and MAB using a GPIO interrupt. After detecting them, I switched it back to USART_RX mode. This allowed me to see the BREAK and MAB, but I couldn't reliably grab the subsequent DMX slot data within the USART interrupt.

Code:

#include <stdio.h>
#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/printk.h>
#include <zephyr/shell/shell.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys_clock.h>
#include <gpios_common/gpios_common.h>

#define LOG_MODULE_NAME USART3_MODULE
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG);

// DMX Constants
#define DMX_MAX_SLOTS 513 // 1 start code + 512 channel values

#define USART3_NODE DT_ALIAS(usart3)
#define USART3_RX_PORT DT_NODELABEL(gpiob)
#define USART3_RX_PIN 11

static const struct gpio_dt_spec rx_gpio = {
    .port = DEVICE_DT_GET(USART3_RX_PORT),
    .pin = USART3_RX_PIN,
    .dt_flags = GPIO_INPUT
};

static struct gpio_callback rx_gpio_cb;

static int64_t break_start_cycles = 0;
static bool break_detected = false;
static bool mab_detected = false;

static const struct device *usart3_dev;
static uint8_t dmx_data[DMX_MAX_SLOTS];
static size_t dmx_index = 0;
static bool receiving_dmx = false;

// --- BREAK + MAB Detection using GPIO ISR ---
static void rx_gpio_isr(const struct device *dev, struct gpio_callback *cb, gpio_port_pins_t pins)
{
    int level = gpio_pin_get_dt(&rx_gpio);
    uint64_t now = k_cycle_get_64();

    if (level == 0)
    {
        // FALLING edge = BREAK start
        break_start_cycles = now;
    }
    else
    {
        // RISING edge = BREAK end
        uint64_t delta_cycles = now - break_start_cycles;
        uint64_t delta_us = (delta_cycles * 1000000) / sys_clock_hw_cycles_per_sec();

        if (delta_us >= 88)
        {
            printk("BREAK detected (duration: %llu us)\n", delta_us);
            break_detected = true;

            // Measure MAB
            uint64_t mab_start_cycles = k_cycle_get_64();
            while (gpio_pin_get_dt(&rx_gpio) == 1)
            {
                // Wait for MAB to end
            }
            uint64_t mab_end_cycles = k_cycle_get_64();
            uint64_t mab_cycles = mab_end_cycles - mab_start_cycles;
            uint64_t mab_us = (mab_cycles * 1000000) / sys_clock_hw_cycles_per_sec();

            if (mab_us >= 8)
            {
                printk("MAB detected (duration: %llu us)\n", mab_us);
                mab_detected = true;
                receiving_dmx = false; // Reset DMX reception on new BREAK
                dmx_index = 0;

                // Stop GPIO interrupts and hand over to USART
                gpio_pin_interrupt_configure_dt(&rx_gpio, GPIO_INT_DISABLE);
                gpio_remove_callback(rx_gpio.port, &rx_gpio_cb);
            }
            else
            {
                // Invalid MAB, ignore
                break_detected = false;
            }
        }
        else
        {
            // Glitch or short pulse
            return;
        }
    }
}

// --- Configure GPIO Interrupt on RX pin ---
static void configure_rx_gpio_interrupt()
{
    gpio_pin_configure_dt(&rx_gpio, GPIO_INPUT);
    gpio_init_callback(&rx_gpio_cb, rx_gpio_isr, BIT(rx_gpio.pin));
    gpio_add_callback(rx_gpio.port, &rx_gpio_cb);
    gpio_pin_interrupt_configure_dt(&rx_gpio, GPIO_INT_EDGE_BOTH);

    printk("GPIO edge detection on PB11 enabled\n");
}

// --- USART3 RX ISR to receive DMX frame ---
static void uart_interrupt_callback(const struct device *dev, void *user_data)
{
    printk("%s\n", __func__);
    uart_irq_update(dev);

    if (uart_irq_rx_ready(dev))
    {
        uint8_t byte;
        while (uart_poll_in(dev, &byte) == 0)
        {
            // printk("Received byte: 0x%02X\n", byte);                         // Print received byte
            // printk("DMX index: %zu\n", dmx_index);                           // Print current DMX index
            // printk("Receiving DMX: %s\n", receiving_dmx ? "true" : "false"); // Print receiving status
            if (!receiving_dmx && byte == 0x00)
            {
                // Start code detected
                receiving_dmx = true;
                dmx_index = 0;
            }

            // printk("DMX index: %zu\n", dmx_index);                           // Print current DMX index
            // printk("Receiving DMX: %s\n", receiving_dmx ? "true" : "false"); // Print receiving status
            if (dmx_index < DMX_MAX_SLOTS)
            {
                printk("Received byte: 0x%02X\n", byte);                         // Print received byte
                dmx_data[dmx_index++] = byte;
            }
            // printk("DMX index: %zu\n", dmx_index);                           // Print current DMX index
            if (dmx_index == DMX_MAX_SLOTS)
            {
                receiving_dmx = false;

                // Full DMX frame received
                printk("DMX Frame received:\nStart Code: 0x%02X, Channel 1: 0x%02X\n",
                       dmx_data[0], dmx_data[1]);

                // Optional: print first 10 channels
                printk("First 10 slots: ");
                for (int i = 1; i <= 10 && i < DMX_MAX_SLOTS; i++)
                {
                    printk("%02X ", dmx_data[i]);
                }
                printk("\n");
            }
        }
    }
}

// --- Enable USART3 RX after BREAK detection ---
static void enable_usart3_rx()
{
    usart3_dev = DEVICE_DT_GET(USART3_NODE);
    if (!usart3_dev)
    {
        LOG_ERR("USART3: Device driver not found.\n");
        return;
    }

    if (!device_is_ready(usart3_dev))
    {
        LOG_ERR("Device %s is not ready.\n", usart3_dev->name);
        return;
    }
    printk("USART3 Driver Init Successful\n");

    uart_irq_callback_user_data_set(usart3_dev, uart_interrupt_callback, NULL);
    uart_irq_rx_enable(usart3_dev);
    printk("USART3 RX enabled (after BREAK + MAB)\n");
    k_busy_wait(100); // wait 100 microseconds
}

// --- Init Function ---
void usart3_init()
{
    printk("DMX Receiver: GPIO BREAK + MAB detection → USART3 RX\n");

    if (!device_is_ready(rx_gpio.port))
    {
        LOG_ERR("PB11 GPIO port not ready");
        return;
    }

    configure_rx_gpio_interrupt();

    // Wait for BREAK + MAB
    while (!break_detected || !mab_detected)
    {
        k_sleep(K_MSEC(10));
    }

    // Switch to USART3 RX
    enable_usart3_rx();
    rs485_set_receive_mode(); // Switch RS-485 to receive direction

    // Main thread can sleep forever — UART ISR handles reception
    while (1)
    {
        k_sleep(K_MSEC(10));
    }
}

Would you happen to know of any code examples or have any suggestions on how to properly detect the BREAK and MAB signals and then seamlessly transition to receiving the complete DMX slot data over USART in this setup? Any guidance would be a huge help! Thanks!

 

0 REPLIES 0