STM32F407ZET6 y nordic nrf24l01 + comunicación con SPI accionado por interrupción

0

Actualmente estoy trabajando en STM32F407ZET6 MCU. Estoy tratando de generar una comunicación SPI basada en interrupciones con el conjunto de chips nrf24l01 + nordic. La MCU está en modo maestro. Puedo establecer una comunicación SPI pero parece que está usando un método de sondeo.

Uso la api uint8_t SPI1_send(uint8_t data) para enviar y recibir datos con SPI.

Necesita ayuda con la generación de código para SPI impulsado por interrupción. Si uso el SPI controlado por interrupciones, ¿todavía necesito usar el SPI1_send() API que mencioné?

void init_SPI1(void){

    GPIO_InitTypeDef GPIO_InitStruct;
    SPI_InitTypeDef SPI_InitStruct;

    // enable clock for used IO pins
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    /* configure pins used by SPI1
     * PA5 = SCK
     * PA6 = MISO
     * PA7 = MOSI
     */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_6 | GPIO_Pin_5;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // connect SPI1 pins to SPI alternate function
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);

    // enable clock for used IO pins
    //RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE); COMMENT THIS LINE AS PORTE IS NOT NEEDED NOW

    /* Configure the chip select pin
       in this case we will use PE7 , NOW PA4 */
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIOA->BSRRL |= GPIO_Pin_4; // set PE7 high; NOW PA4

    // enable peripheral clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // 84 Mhz

    /* configure SPI1 in Mode 0 
     * CPOL = 0 --> clock is low when idle
     * CPHA = 0 --> data is sampled at the first edge
     */
    SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // set to full duplex mode, seperate MOSI and MISO lines
    SPI_InitStruct.SPI_Mode = SPI_Mode_Master;     // transmit in master mode, NSS pin has to be always high
    SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // one packet of data is 8 bits wide
    SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;        // clock is low when idle
    SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;      // data sampled at first edge
    SPI_InitStruct.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; // set the NSS management to internal and pull internal NSS high
    SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // SPI frequency is APB2 frequency / 4 = 84Mhz/4 = 21Mhz 
    SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;// data is transmitted MSB first
    SPI_Init(SPI1, &SPI_InitStruct); 

    SPI_Cmd(SPI1, ENABLE); // enable SPI1     
}

/* This funtion is used to transmit and receive data 
* with SPI1
*           data --> data to be transmitted
*           returns received value
*/
uint8_t SPI1_send(uint8_t data){

    SPI1->DR = data; // write data to be transmitted to the SPI data register
    while( !(SPI1->SR & SPI_I2S_FLAG_TXE) ); // wait until transmit complete
    while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); // wait until receive complete
    while( SPI1->SR & SPI_I2S_FLAG_BSY ); // wait until SPI is not busy anymore
    return SPI1->DR; // return received data from SPI data register  
}

El código anterior se explica por sí mismo, tiene la función init SPI y la función Enviar / Recibir. Puedo agregar cómo llamo a estas funciones en main() también. Cuando necesito enviar o recibir datos a través de SPI a un chip nórdico, uso la función SPI1_send() . Como dije, si uso el modo de interrupción, todavía necesito usar la función SPI1_send() , ya que ISR se encargará de enviar / recibir, supongo.

    
pregunta Pradeep Ch

2 respuestas

1

La HAL proporciona esta funcionalidad.

Llamaría a HAL_SPI_Transmit_IT con un puntero a la estructura SPI, un puntero al búfer de datos y el tamaño del búfer de datos. Esto enviará todo su búfer, estilo de interrupción.

Obviamente, esto requiere que incluyas / cambies a la HAL, y la configures e inicialices correctamente.

Consulte Manual del usuario de ST UM1725 para una descripción de la biblioteca HAL SPI.

    
respondido por el Daniel
0

He visto el ejemplo y no hay necesidad de llamar a su propia función SPI1_Send() ya que todo se maneja en el ISR. aTxbuffer y aRxbuffer se usan para retener datos hasta que estén listos para ser enviados (TX) o hasta que el STM esté listo para leer los datos recibidos (RX). El tamaño del búfer se elige según la cantidad de bytes que transmita o reciba en cualquier ráfaga y también la velocidad con la que lea los datos que haya recibido. Funciona de esta manera:

El STM realiza un seguimiento de su posición actual dentro de ambos búferes ( ubRxIndex y ubTxIndex ); una vez que el registro de datos SPI está vacío ( TXE ), significa que el byte anterior se ha transmitido y el STM salta al ISR y escribe el siguiente byte desde el aTxbuffer usando SPI_I2S_SendData() , después de lo cual se incrementa ubTxIndex . Una vez que el registro de datos SPI NO está vacío ( RXNE ), el STM salta al ISR y lee el byte del registro SPI en aRxbuffer , después de lo cual se incrementa ubRxIndex . Esto continúa hasta que todos los bytes en el búfer de TX se hayan transmitido y el búfer de RX se llene con los bytes recibidos.

No creo que sea posible eliminar el sondeo por completo; incluso utilizando estos buffers, cada vez que envíe un byte, deberá verificar si algo es available() antes de leer. Además, si desea enviar, debe asegurarse de que el byte anterior ya haya desaparecido del búfer de TX. La única diferencia es que ahora las variables de sondeo que creó, que le indicarán CIERTAS (ya que se basan en interrupciones y no en indicadores molestos) cuando sea el momento de leer o escribir. Si aún desea continuar con esto, simplemente puede modificar la longitud de los buffers TX y RX a 1 byte cada uno. Cada vez que desea enviar a NRF, reinicia a ubTxindex a cero y simplemente escribe en el búfer de TX de 1 byte. Tan pronto como el registro de datos esté vacío, los datos serán enviados; Sin embargo, debe esperar esto; de lo contrario, puede intentar enviar otro byte sin leer la respuesta del primer byte. Lo mismo vale para recibir; tienes que esperar hasta que ubRxIndex se haya incrementado en el ISR antes de restablecer el ubRxIndex a cero y luego leer un byte del búfer.

Para hacer esto, en main.h , cambie BUFFERSIZE a 1. Luego configure SPI y habilite las interrupciones como en SPI_config() y main() en main.c . Luego defina estas variables y funciones en main.c :

uint8_t ubTxIndex = 0;
uint8_t ubRxIndex = 0;

uint8_t SPI_Send(uint8_t data){
    ubTxIndex = 0    // set TX buffer to the start
    ubRxIndex = 0  // reset the RX index to 0
    aTxBuffer[ubTxIndex] = data;  // write the byte into the buffer
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_TXE, ENABLE);  //enable tx int
    SPI_I2S_ITConfig(SPI1, SPI_I2S_IT_RXNE, ENABLE);  //enable rx int in case it was disabled
    while (ubRxIndex < BUFFERSIZE);  // wait for a byte to be received into the RX buffer
    return aRxBuffer[ubRxIndex - BUFFERSIZE];
}

Puede usar esto para enviar y recibir bytes del módulo NRF, sin verificar los indicadores SPI habituales que no son tan confiables de todos modos.

    
respondido por el TisteAndii

Lea otras preguntas en las etiquetas