STM32F103RB DMA issue.
Hi all,
I'm using SPI to communicate with an Adafruit 2.8" TFT LCD, (ILI9341 based). This is mostly working fine, I can plot pixels, draw lines, circles fonts etc. So the communication with the display is working. The STM end of things is a Nucleo 64 board with a F103RB.
However as part of my text plotting routines I want to be able to scroll the display when it becomes full, this obviously requires reading a line of pixel data and then writing it again several lines up the screen. I have this working completely reliably using a pair of for next loops along with a function to read or write a block of data. However this is slow.
So I have re-implemented this using DMA to do the reading and writing, as this will be faster (and it is about 2x the speed). After much fiddling I got this working.
However I then added code un-related to my DMA code and the DMA seemed to randomly stop working, I have no idea why? One thing to note is that I only use the DMA for transfering the large blocks of data during scrolling (320 pixels * 3 bytes / pixel), not for the normal commands to the display. I don't however change anything about the SPI channel (apart from switching on and off the DMA flags), once initialised.
My SPI Init code :
LL_SPI_InitTypeDef SPI_InitStruct;
LL_GPIO_InitTypeDef GPIO_InitStruct;
LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_4);
LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_5);
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_SPI2);
/**SPI2 GPIO Configuration
PB13 ------> SPI2_SCK
PB14 ------> SPI2_MISO
PB15 ------> SPI2_MOSI
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_13|LL_GPIO_PIN_15;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_14;
GPIO_InitStruct.Mode = LL_GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
SPI_InitStruct.TransferDirection = LL_SPI_FULL_DUPLEX;
SPI_InitStruct.Mode = LL_SPI_MODE_MASTER;
SPI_InitStruct.DataWidth = LL_SPI_DATAWIDTH_8BIT;
SPI_InitStruct.ClockPolarity = LL_SPI_POLARITY_HIGH;
SPI_InitStruct.ClockPhase = LL_SPI_PHASE_2EDGE;
SPI_InitStruct.NSS = LL_SPI_NSS_SOFT;
SPI_InitStruct.BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2;
SPI_InitStruct.BitOrder = LL_SPI_MSB_FIRST;
SPI_InitStruct.CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.CRCPoly = 10;
LL_SPI_Init(SPI2, &SPI_InitStruct);
/* Setup flag pins, CS, C/D, reset */
GPIO_InitStruct.Pin = nCS_IL9341_Pin | CD_IL9341_Pin | RES_IL9341_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
ILIDeSelect();
ILIClearReset();
LL_SPI_Enable(SPI2);My DMA code :
void ILI9341_ConfigDMA(void)
{
LL_DMA_InitTypeDef DMA_InitStructure;
/* DMA1 used for SPI2 Transmission
* DMA1 used for SPI2 Reception
*/
/* (1) Enable the clock of DMA1 and DMA1 */
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_4);
LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_5);
DMA_InitStructure.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetRegAddr(SPI2);
DMA_InitStructure.MemoryOrM2MDstAddress = 0;
DMA_InitStructure.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
DMA_InitStructure.NbData = (LCD_WIDTH*3);
DMA_InitStructure.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
DMA_InitStructure.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
DMA_InitStructure.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
DMA_InitStructure.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
DMA_InitStructure.Mode = LL_DMA_MODE_NORMAL;
DMA_InitStructure.Priority = LL_DMA_PRIORITY_LOW;
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_4, &DMA_InitStructure);
LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_5);
DMA_InitStructure.PeriphOrM2MSrcAddress = LL_SPI_DMA_GetRegAddr(SPI2);;
DMA_InitStructure.MemoryOrM2MDstAddress = 0;
DMA_InitStructure.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
DMA_InitStructure.NbData = (LCD_WIDTH*3);
DMA_InitStructure.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
DMA_InitStructure.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
DMA_InitStructure.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE;
DMA_InitStructure.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
DMA_InitStructure.Mode = LL_DMA_MODE_NORMAL;
DMA_InitStructure.Priority = LL_DMA_PRIORITY_LOW;
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_5, &DMA_InitStructure);
}
void ILI9341_DMA_SendReceive(uint8_t *Buffer,
uint32_t Count,
uint8_t DummySend)
{
uint8_t Dummy = LCD_NOP;
_delay_us(1);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_4,Count);
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_4,(uint32_t)Buffer);
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_5,Count);
if(DummySend)
{
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_5,(uint32_t) &Dummy);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_5, LL_DMA_MEMORY_NOINCREMENT);
}
else
{
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_5,(uint32_t) Buffer);
LL_DMA_SetMemoryIncMode(DMA1, LL_DMA_CHANNEL_5, LL_DMA_MEMORY_INCREMENT);
}
LL_SPI_EnableDMAReq_RX(SPI2);
LL_SPI_EnableDMAReq_TX(SPI2);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_4);
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_5);
while (!LL_DMA_IsActiveFlag_TC5(DMA1) && !LL_DMA_IsActiveFlag_TC4(DMA1))
{}
while (LL_SPI_IsActiveFlag_BSY(SPI2) && !LL_SPI_IsActiveFlag_RXNE(SPI2))
{}
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_4);
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_5);
LL_SPI_DisableDMAReq_RX(SPI2);
LL_SPI_DisableDMAReq_TX(SPI2);
}
#define DMA_CALL_READ 1
#define DMA_CALL_WRITE 1
void ILI9341_Scroll_ScreenDMA(uint16_t Lines)
{
uint16_t LineNo;
// uint8_t Dummy = LCD_NOP;
uint16_t NoBytes;
static uint8_t __attribute__((aligned(4))) LineBuff[(320*3)]; // write buffer
ILI9341_ConfigDMA();
ILISelect();
ILI9341_Write_Command(LCD_PIXEL_FORMAT);
ILI9341_Write_Data(PX_18_IF);
NoBytes=LCD_WIDTH*3;
for(LineNo=Lines;LineNo < LCD_HEIGHT ;LineNo++)
{
ILI9341_Set_AddressRW(0,LineNo,LCD_WIDTH,LineNo+1,0);
ILI9341_Read_Data(); // skip dummy byte
ILI9341_DMA_SendReceive(LineBuff,NoBytes+1,1);
ILIDeSelect();
ILISelect();
ILI9341_Write_Command(LCD_NOP);
ILI9341_Set_AddressRW(0,(LineNo-Lines),LCD_WIDTH,(LineNo-Lines)+1,1);
ILISetData();
ILI9341_DMA_SendReceive(LineBuff,NoBytes,0);
ILIDeSelect();
ILISelect();
ILI9341_Write_Command(LCD_NOP);
}
ILI9341_Write_Command(LCD_PIXEL_FORMAT);
ILI9341_Write_Data(PX_DEFAULT);
ILIDeSelect();
}Anyone have any idea why this is the case? That adding un-related code can cause this to work / not work seemingly at random?
Cheers.
Phill.
