Atmel AVR XMEGA-A USART MPCM (modo de comunicación multiprocesador) configuración con ASF 3.33

0

Estoy usando dos XMEGA256-A3BU en las placas Xplata XMEGA-A3BU. Mi objetivo es hacer posible MPCM (modo de comunicación multiprocesador) entre los dos usando su USART en modo síncrono.
Se conectan físicamente para que las PC1 (SCL), PC2 (RXD) y PC3 (TXD) del maestro se conecten a la PC1 (SCL), PC3 (TXD) y PC2 (RXD) del esclavo, respectivamente. Utilizo el "servicio serie USART" de ASF 3.33 para hacer que la comunicación funcione.

Código de inicialización del maestro:

static usart_serial_options_t usart_options = {
    .baudrate = 1200;
    .charlength = USART_CHSIZE_8BIT_gc,
    .paritytype = USART_PMODE_ODD_gc,
    .stopbits = false
};

sysclk_enable_module(SYSCLK_PORT_C, PR_USART0_bm);
usart_serial_init(&USARTC0, &usart_options);

usart_set_mode(&USARTC0, USART_CMODE_SYNCHRONOUS_gc);

// Pin to set as output for clock signal.
ioport_configure_pin(IOPORT_CREATE_PIN(PORTC, 1), IOPORT_DIR_OUTPUT);
// Port to output clock signal on.
PORTCFG.CLKEVOUT = PORTCFG_CLKOUT_PC1_gc;

usart_set_rx_interrupt_level(&USARTC0, USART_RXCINTLVL_OFF_gc);
usart_set_tx_interrupt_level(&USARTC0, USART_TXCINTLVL_OFF_gc);

Código de inicialización del esclavo:

static usart_serial_options_t usart_options = {
    .baudrate = 1200;
    .charlength = USART_CHSIZE_8BIT_gc,
    .paritytype = USART_PMODE_ODD_gc,
    .stopbits = false
};

sysclk_enable_module(SYSCLK_PORT_C, PR_USART0_bm);
usart_serial_init(&USARTC0, &usart_options);

usart_set_mode(&USARTC0, USART_CMODE_SYNCHRONOUS_gc);

// Pin to set as input for clock signal.
ioport_configure_pin(IOPORT_CREATE_PIN(PORTC, 1), IOPORT_DIR_INPUT);
// Do not output the clock signal.
PORTCFG.CLKEVOUT = PORTCFG_CLKOUT_OFF_gc;

usart_set_rx_interrupt_level(&USARTC0, USART_RXCINTLVL_OFF_gc);
usart_set_tx_interrupt_level(&USARTC0, USART_TXCINTLVL_OFF_gc);

Utilizo usart_serial_putchar(&USARTC0, buffer[i]) y usart_serial_getchar(&USARTC0, &buffer[i]) para enviar y recibir datos para las pruebas, el diseño final usará DMA.

Para hacer funcionar el MPCM (y hacer posible agregar más esclavos al bus), sé que necesito cambiar al 1 bit de inicio + 9 bits de datos + paridad + 1 formato de bit de parada, y también sé que de alguna manera debería usar estas constantes que encontré en el archivo iox256a3bu.h :

#define USART_MPCM_bm  0x02  /* Multi-processor Communication Mode bit mask. */
#define USART_MPCM_bp  1  /* Multi-processor Communication Mode bit position. */

¿Podría alguien ayudarme con algún código de ejemplo ASF? ¿Qué debo agregar para configurar la propia dirección del esclavo y dónde debo definir en el maestro a quién tengo intención de enviar el mensaje?

    
pregunta Andras

1 respuesta

1

He pasado algunas horas en el problema, y afortunadamente ahora funciona con un amo y dos esclavos. No usa DMA porque creo que no es práctico en este caso, así que decidí usar interrupciones en su lugar.
Primero cambié el formato de USART a 9 bits, por lo que el método de inicialización comienza así:

static usart_serial_options_t usart_options = {
    .baudrate = 1200;
    .charlength = USART_CHSIZE_9BIT_gc,
    .paritytype = USART_PMODE_ODD_gc,
    .stopbits = false
};

Y cambié el nivel de interrupción al final:

usart_set_rx_interrupt_level(USART_SERIAL, USART_RXCINTLVL_OFF_gc);
usart_set_tx_interrupt_level(USART_SERIAL, USART_TXCINTLVL_MED_gc);

Luego definí dos métodos para reemplazar los métodos usart_serial_putchar(&USARTC0, buffer[i]) y usart_serial_getchar(&USARTC0, &buffer[i]) :

static void usart_send_mpcm_data(usart_if usart, uint8_t data, bool is_address)
{
    /* Wait until we are ready to send the next 9 bits */
    while (usart_data_register_is_empty(usart) == false) {}

    /* First we need to set the 9. bit of the frame, which is the indicator for the address frame */
    if (is_address)
    {
        (usart)->CTRLB |= USART_TXB8_bm;
    } else
    {
        (usart)->CTRLB &= ~USART_TXB8_bm;
    }

    /* Second, let's load the 8-bit data into the DATA register of the USART */
    (usart)->DATA = data;
}


static bool usart_receive_mpcm_data(usart_if usart, uint8_t* data, uint8_t my_address)
{
    bool result = false;

    /* Are we in MPCM mode? */
    bool MPCM_mode = usart->CTRLB & USART_MPCM_bm;
    /* Is this an address frame? */
    bool address_frame = usart->STATUS & USART_RXB8_bm;

    /* Read char, clearing RXCIF */
    uint8_t read_data = usart->DATA;

    if ((!MPCM_mode) && (!address_frame))
    {
        /* This is a data frame, and we are interested in it */
        *data = read_data;
        result = true;
    }

    if (address_frame)
    {
        /* This frame is an address frame, so let's check if it addresses us (my_address) or broadcasts (0) */
        MPCM_mode = !((read_data == my_address) || (read_data == 0));
    }

    /* Set the MPCM bit in the USART's control register */
    if (MPCM_mode)
    {
        usart->CTRLB |= USART_MPCM_bm;
    } else
    {
        usart->CTRLB &= ~USART_MPCM_bm;
    }

    /* Return true if we read some useful data */
    return result;
}

Después de tener estos, pude configurar el método de interrupción en los esclavos:

// USART-C Reception Complete Interrupt
ISR(USARTC0_RXC_vect)
{
    if (usart_receive_mpcm_data(&USARTC0, &usart_destination[usart_interrupt_counter], settings.device_index))
    {
        usart_interrupt_counter = (usart_interrupt_counter+1) % DMA_BUFFER_SIZE;
        rx_counter += 1;    
    }
}

En el maestro puedo usar el método usart_send_mpcm_data(&USARTC0, usart_source[i], false); para enviar datos, o usart_send_mpcm_data(&USARTC0, slave_address, true); para cambiar la dirección del esclavo de destino. Si la dirección es 0, entonces ese será un mensaje de difusión, por lo que todos los esclavos recibirán los datos.

    
respondido por el Andras

Lea otras preguntas en las etiquetas