AnsweredAssumed Answered

STM32F103 - DMA with SPI, Rx works Tx doesn't

Question asked by DeKoepi on May 3, 2014
Latest reply on May 14, 2014 by DeKoepi
Dear ST community,

I'm stuck in my project currently. I managed to master SPI access to my RAM bank made out of a 74HC138 and 5 23LC1024 RAM chips. I did run into trouble with 8bit transfers and had to copy the way used in the STM Peripheral Library. This works flawless now in polling mode. Next issue was I2C to a camera module with th SCCB dialect; had to bitbang the protocol in the end to make it work. I mastered USART1 output for debugging messages. I disabled JTAG so my PB3/4 pins for signaling frame start/row start can actually be accessed.

Now as I'm making progress, I need more speed to transfer the data sent by the camera module faster to the SPI ram. Thus my idea is to poll that data into a buffer of i.e. 256 bytes, when full transfer the buffer via DMA to SPI, while filling a second buffer using polling. Direct DMA doesn't seem to be an option as there is always a single byte ready on PB8-PB15 upon PCLK HIGH, with HREF HIGH and VSYNC LOW. I don't see how DMA could be used directly with these conditions, thus I try a buffer.

For a first test, I'm doing something incredibly simple: Fill a tx buffer, empty a rx buffer, setup DMA and SPI and give it the "go!". During the secnd run I find the data that I added the run before via polling from the camera module. Thus DMA rx works ok, while DMA tx doesn't seem to work.

This is my current code (stripped down to the essentials for the DMA test):

intmain()
{
     /* OV7670 connections
     * XCLK PA8 Min. 8 MHz for starting the cam;
     * VSYNC PB3 LOW marks start of frame; when HIGH then stop
     * HREF PB4 HIGH starts line; when LOW: line stop, don't read pixels
     * PCLK PB5 HIGH when pixel data valid
     * SCIO PB6 I2C_SCL
     * SDIO PB7 I2C_SDA
     * D0-D7 PB08-PB15 sample at rising edge of PCLK
     */
 
 
    /* SPI1 pins
      NSS = PA4 on stm32f051xx (has to be enabled/output/high for hardware SPI)
      SCK = PA5
      MISO = PA6  -> Input
      MOSI = PA7
     */
 
 
    /* Chip Select pins
     * A0,1,2 -> PA0,1,2 -> GPIO 0,1,2
     */
 
 
    /* 23LC1024 Instructions are sent this way: CS LOW, send command,
     * (Read/write: send address,) send data, CS HIGH
     */
 
    SystemInit();
 
    // Enable the needed peripheral clocks
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |
            RCC_APB2Periph_USART1 | RCC_APB2Periph_SPI1 |
            RCC_APB2Periph_AFIO, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 
 
    // Not using JTAG or SW debug. Disable them so that PA13-15, PB3-4 can be remapped.
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable , ENABLE);
    // Configure PB.03 (JTDO) and PB.04 (JTRST) as output push-pull (INPUT later)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
 
// ----------------------------------------------------------------------------
 
 
// SPI setup
    // SCK and MISO are Output, Alternate Function floating (PP) as Master
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 
    // MISO -> input (as Master, input floating!)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 
    // (N)SS needs to be Output; for hardware SPI to work set it HIGH and AF_PP
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 
    GPIOA->BSRR |= GPIO_Pin_4;  // GPIOA Pin 4 (NSS) is HIGH (for hardware SPI support)
 
 
    // Mux Chip Select - deselect all (all pins high)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2;  // bitmask 0x07
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
 
    chip_deselect();
 
 
    // SPI initial setup - 23LC1024 SPI mode 0
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;  // SPI_Mode 0 / High=1
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; // SPI_Mode 0 / 2EDGE=1
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft | SPI_NSSInternalSoft_Set; // set the NSS management to internal  | SPI_NSSInternalSoft_Set and pull internal NSS high
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_Init(SPI1, &SPI_InitStructure);
 
 
    // tell SPI1 to use DMA
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx | SPI_I2S_DMAReq_Tx, ENABLE);
 
 
    SPI_Cmd(SPI1, ENABLE);
 
 
    // for SPI transfers, get the function address for pointer arithmetics
    get_spixbase();
 
 
// ----------------------------------------------------------------------------
 
 
    // DMA RX SPI1 channel
    DMA_DeInit(DMA_Channel_SPI1_RX);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
//  DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_rx;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = buffer_size;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_Channel_SPI1_RX, &DMA_InitStructure);
 
 
    // DMA TX SPI1 channel
    DMA_DeInit(DMA_Channel_SPI1_TX);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
//  DMA_InitStructure.DMA_PeripheralBaseAddr = spixbase;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer1_tx;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = buffer_size;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA_Channel_SPI1_TX, &DMA_InitStructure);
 
 
    // Interrupt at DMA transfer complete
    DMA_ITConfig(DMA_Channel_SPI1_TX, DMA_IT_TC, ENABLE);
    DMA_ITConfig(DMA_Channel_SPI1_RX, DMA_IT_TC, ENABLE);
 
 
// ----------------------------------------------------------------------------
 
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
 
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
 
// ----------------------------------------------------------------------------
  
    uart_tx("Starting program.\n");
 
 
// Check the RAM first
 
    for (int bank=0; bank<5; bank++) {
        bool is_ok=true;
        chip_select(bank);
 
        // Send WRITE MODE register command to chip
        spi1_tx(WRMR);
        spi1_tx(SEQUENTIAL_MODE);
        chip_deselect();
        chip_select(bank);
 
        // Send WRITE command to chip
        spi1_tx(WRITE);
 
        // as default sequential mode, thus address has to be sent only at start
        address = 0;
        spi1_tx((address>>16));   //send even MoreMSByte address first
        spi1_tx((address>>8));   //send MSByte address first
        spi1_tx((address));      //send LSByte address
 
        for (int i=0; i<buffer_size; i++) {
            buffer1_tx[i]=i;
            buffer1_rx[i]=0;
        }
 
        for (int i=0; i < (ram_size/buffer_size); i++) {
            spi1_dma_tx();
        }
        chip_deselect();
         chip_select(bank); 
        // Send WRITE MODE register command to chip
        spi1_tx(WRMR);
        spi1_tx(SEQUENTIAL_MODE);
        chip_deselect();
        chip_select(bank);
 
        // Send READ command to chip
        spi1_tx(READ);
 
        // as default sequential mode, thus address has to be sent only at start
        address = 0;
        spi1_tx((address>>16));   //send even MoreMSByte address first
        spi1_tx((address>>8));   //send MSByte address first
        spi1_tx((address));      //send LSByte address
 
        for (int i=0; i<buffer_size; i++) {
            buffer1_tx[i]=0xFF;
            buffer1_rx[i]=0;
        }
 
        for (int i=0; i < (ram_size/buffer_size); i++) {
            // if( (spi1_tx(0xFF)) != (i%0xFF)) is_ok=false;
            spi1_dma_tx();
            for (int i=0; i<buffer_size; i++) {
                if (buffer1_rx[i] != i) is_ok=false;
            }
        }
        sprintf(text,"RAM chip %i is %s ->  ", bank, (is_ok? "OK." : "not OK!"));
        uart_tx(text);
        for (int i=0; i<10; i++) {
            sprintf(text, "[%i] %i  ", i, buffer1_rx[i]);
            uart_tx(text);
        }
        uart_tx("\n");
        chip_deselect();
    }
 
    SPI_Cmd(SPI1, DISABLE);

    /* Infinite loop */
    while (1) {
        // we had this one before: sit and wait.
    }
}
 
 
// ----------------------------------------------------------------------------
 
// SPI support functions
 
void chip_select(uint8_t RAM_bank) {
    GPIOA->BSRR |= (RAM_bank);
    GPIOA->BRR |= ( ~(RAM_bank) & 0x07) | GPIO_Pin_4;
    // GPIOA Pin 4 (NSS) is LOW  (for hardware SPI support)
// of chip_select
 
 
void chip_deselect(void) {
    GPIOA->BSRR |= 0x07 | GPIO_Pin_4;  // 0x07 => 0b00000111
    // GPIOA Pin 4 (NSS) is HIGH (for hardware SPI support)
// of chip_deselect
 
// SPI transfer Byte
inline uint8_t spi1_tx(uint8_t tx)
{
    // write data to be transmitted to SPI
    *(__IO uint8_t *) spixbase = tx;
    if ( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ) { while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); } // wait until receive complete
    return ( *(__IO uint8_t *) spixbase );
}
 
void spi1_dma_tx(void) {
 
    // Wait until DMA transfer complete
//  while (!((DMA1->ISR) & DMA_FLAG_SPI1_TC_RX )) ;
 
    DMA_Cmd(DMA_Channel_SPI1_TX, DISABLE);
    DMA_Cmd(DMA_Channel_SPI1_RX, DISABLE);
    DMA_SetCurrDataCounter(DMA_Channel_SPI1_TX, buffer_size);
    DMA_SetCurrDataCounter(DMA_Channel_SPI1_RX, buffer_size);
    DMA_Cmd(DMA_Channel_SPI1_RX, ENABLE);
    DMA_Cmd(DMA_Channel_SPI1_TX, ENABLE);
}
 
 
void DMA1_Channel2_IRQHandler(void){
    if(DMA_GetITStatus(DMA1_IT_TC2))
        DMA_ClearFlag(DMA1_FLAG_TC2);
    if ( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ) { while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); } // wait until receive complete
}
 
void DMA1_Channel3_IRQHandler(void){
    if(DMA_GetITStatus(DMA1_IT_TC3))
        DMA_ClearFlag(DMA1_FLAG_TC3);
    if ( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ) { while( !(SPI1->SR & SPI_I2S_FLAG_RXNE) ); } // wait until receive complete
}
 
// taken from ST library for 8bit SPI writes
void get_spixbase(void)
{
      spixbase = (uint32_t)SPI1;
      spixbase += 0x0C;
}


Yes, I read RM0008 reference manual, also the STM standard peripheral lib, the examples in the library, plenty of examples on the net ... still, I'm stuck. Any suggestions are welcome!

Thank you for your time,
Best regards,
Dirk

Edit: Found out how to properly mark up code. Further reduce the code snippet to be better readable.

Outcomes