Forum: Mikrocontroller und Digitale Elektronik STM32L151CC SPI DMA ILI9341 will nicht laufen.


von Martin P. (mar_tin)


Lesenswert?

Moin :-)

Ich steuere erfolgreich mit einem STM32L151CC ein TFT Display mit 
ILI9341 per SPI an. Da das viel zu langsam läuft versuche ich es jetzt 
mit SPI über DMA und bekomme bisher nicht mal eine Reaktion vom Display 
:-(

Die Funktionen "Fill" und "DrawPixel" senden (wie bei SPI) je nur ein 
byte an das Display. Das macht per DMA natürlich keinen Sinn, aber es 
ist auch nur zum Testen. Hauptsache da tut sich überhaupt mal was...

Beim Debug springe ich in die Funktion "DMA1_Channel4_IRQHandler".
Daher nehme ich an, dass die Kommunikation grundlegend funktioniert(?).

Könnte da mal bitte jemand drüber schauen? Verstehe ich da noch etwas 
grundlegend falsch?

Gruß,

Martin


#include "stm32l1xx_rcc.h"
#include "stm32l1xx_gpio.h"
#include "stm32l1xx_dma.h"
#include "stm32l1xx_spi.h"
#include "misc.h"
#include "ili9341.h"

void InitLCD(void);
void Fill(uint32_t color);
void DrawPixel(uint16_t x, uint16_t y, uint32_t color);

uint16_t SPIBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA};

int main(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  SPI_InitTypeDef SPI_InitStructure;
  DMA_InitTypeDef DMA_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;

  SystemInit();

  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

  /*
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
  GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);
  //GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_SPI2);
  */

  // PB11 RST
  // PB12  CS
  // PB15  MOSI
  // PB14 DC/MISO
  // PB13 SCK

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  GPIO_Init(GPIOB, &GPIO_InitStructure);

  GPIO_WriteBit(GPIOB, GPIO_Pin_12, SET);

  SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
  SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
  SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
  SPI_InitStructure.SPI_CRCPolynomial = 0;
  SPI_InitStructure.SPI_DataSize = SPI_DataSize_16b;
  SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
  SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
  SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
  SPI_Init(SPI2, &SPI_InitStructure);

  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
  SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Rx, ENABLE);

  SPI_Cmd(SPI2, ENABLE);

  // DMA Channel 4 - SPI RX
  DMA_InitStructure.DMA_BufferSize = 0;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPIBuffer;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
  DMA_InitStructure.DMA_PeripheralDataSize = 
DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_Init(DMA1_Channel4, &DMA_InitStructure);

  DMA_ITConfig(DMA1_Channel4, DMA_IT_TC, ENABLE);

  // DMA Channel 5 - SPI TX
  DMA_InitStructure.DMA_BufferSize = 0;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPIBuffer;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI2->DR;
  DMA_InitStructure.DMA_PeripheralDataSize = 
DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_Init(DMA1_Channel5, &DMA_InitStructure);

  NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);

  InitLCD();
  //Fill(0xFFFF);
  DrawPixel(50,50,0xFFFF);
  while(1)
  {

  }

}

void SendCommand(uint16_t data) {

  DMA_Cmd(DMA1_Channel4, DISABLE);
  DMA_Cmd(DMA1_Channel5, DISABLE);
  DMA_SetCurrDataCounter(DMA1_Channel4, 1);
  DMA_SetCurrDataCounter(DMA1_Channel5, 1);
  SPIBuffer[0] = data;
  //SPIBuffer[1] = 0x5678;

  // Chip Select Low
  ILI9341_WRX_RESET;
  //ILI9341_CS_RESET;
  GPIO_WriteBit(GPIOB, GPIO_Pin_12, RESET);

  DMA_Cmd(DMA1_Channel4, ENABLE);
  DMA_Cmd(DMA1_Channel5, ENABLE);

}

void SendData(uint16_t data) {

  DMA_Cmd(DMA1_Channel4, DISABLE);
  DMA_Cmd(DMA1_Channel5, DISABLE);
  DMA_SetCurrDataCounter(DMA1_Channel4, 1);
  DMA_SetCurrDataCounter(DMA1_Channel5, 1);
  SPIBuffer[0] = data;

  // Chip Select Low
  ILI9341_WRX_SET;
  //ILI9341_CS_RESET;
  GPIO_WriteBit(GPIOB, GPIO_Pin_12, RESET);

  DMA_Cmd(DMA1_Channel4, ENABLE);
  DMA_Cmd(DMA1_Channel5, ENABLE);
}


void SetCursorPosition(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t 
y2) {
  SendCommand(0x2A);
  SendData(x1 >> 8);
  SendData(x1 & 0xFF);
  SendData(x2 >> 8);
  SendData(x2 & 0xFF);

  SendCommand(0x2B);
  SendData(y1 >> 8);
  SendData(y1 & 0xFF);
  SendData(y2 >> 8);
  SendData(y2 & 0xFF);
}

void Fill(uint32_t color) {
  unsigned int n, i, j;
  i = color >> 8;
  j = color & 0xFF;
  SetCursorPosition(0, 0, 239, 319);

  SendCommand(ILI9341_GRAM);

  for (n = 0; n < 76800; n++) {
    SendData(i);
    SendData(j);
  }
}

void DrawPixel(uint16_t x, uint16_t y, uint32_t color) {

  SetCursorPosition(x, y, x, y);

  SendCommand(ILI9341_GRAM);
  SendData(color >> 8);
  SendData(color & 0xFF);
}


void InitLCD(void){

  unsigned int i = 0;

  ILI9341_RST_SET;

  SendCommand(ILI9341_RESET);
  for(i=0;i<500000;i++);

  SendCommand(ILI9341_POWERA);
  SendData(0x39);
  SendData(0x2C);
  SendData(0x00);
  SendData(0x34);
  SendData(0x02);
  SendCommand(ILI9341_POWERB);
  SendData(0x00);
  SendData(0xC1);
  SendData(0x30);
  SendCommand(ILI9341_DTCA);
  SendData(0x85);
  SendData(0x00);
  SendData(0x78);
  SendCommand(ILI9341_DTCB);
  SendData(0x00);
  SendData(0x00);
  SendCommand(ILI9341_POWER_SEQ);
  SendData(0x64);
  SendData(0x03);
  SendData(0x12);
  SendData(0x81);
  SendCommand(ILI9341_PRC);
  SendData(0x20);
  SendCommand(ILI9341_POWER1);
  SendData(0x23);
  SendCommand(ILI9341_POWER2);
  SendData(0x10);
  SendCommand(ILI9341_VCOM1);
  SendData(0x3E);
  SendData(0x28);
  SendCommand(ILI9341_VCOM2);
  SendData(0x86);
  SendCommand(ILI9341_MAC);
  SendData(0x48);
  SendCommand(ILI9341_PIXEL_FORMAT);
  SendData(0x55);
  SendCommand(ILI9341_FRC);
  SendData(0x00);
  SendData(0x18);
  SendCommand(ILI9341_DFC);
  SendData(0x08);
  SendData(0x82);
  SendData(0x27);
  SendCommand(ILI9341_3GAMMA_EN);
  SendData(0x00);
  SendCommand(ILI9341_COLUMN_ADDR);
  SendData(0x00);
  SendData(0x00);
  SendData(0x00);
  SendData(0xEF);
  SendCommand(ILI9341_PAGE_ADDR);
  SendData(0x00);
  SendData(0x00);
  SendData(0x01);
  SendData(0x3F);
  SendCommand(ILI9341_GAMMA);
  SendData(0x01);
  SendCommand(ILI9341_PGAMMA);
  SendData(0x0F);
  SendData(0x31);
  SendData(0x2B);
  SendData(0x0C);
  SendData(0x0E);
  SendData(0x08);
  SendData(0x4E);
  SendData(0xF1);
  SendData(0x37);
  SendData(0x07);
  SendData(0x10);
  SendData(0x03);
  SendData(0x0E);
  SendData(0x09);
  SendData(0x00);
  SendCommand(ILI9341_NGAMMA);
  SendData(0x00);
  SendData(0x0E);
  SendData(0x14);
  SendData(0x03);
  SendData(0x11);
  SendData(0x07);
  SendData(0x31);
  SendData(0xC1);
  SendData(0x48);
  SendData(0x08);
  SendData(0x0F);
  SendData(0x0C);
  SendData(0x31);
  SendData(0x36);
  SendData(0x0F);
  SendCommand(ILI9341_SLEEP_OUT);

  for(i=0;i<250000;i++);

  SendCommand(ILI9341_DISPLAY_ON);
  SendCommand(ILI9341_GRAM);
}

void spi_handleDMA1Ch4Interrupt(void){
  // Chip Select High
  GPIO_WriteBit(GPIOB, GPIO_Pin_12, SET);
}

void DMA1_Channel4_IRQHandler(void){
  spi_handleDMA1Ch4Interrupt();
  DMA_ClearFlag(DMA1_FLAG_TC4);
}

von holger (Gast)


Lesenswert?

Der DMA Interrupt sagt nur das das letzte Byte in das
SPI Datenregister geschrieben wurde. Damit ist es aber
noch nicht gesendet! Du ziehst also CS zu früh hoch.

von Martin P. (mar_tin)


Lesenswert?

Stimmt..

DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);

while (DMA_GetFlagStatus(DMA1_FLAG_TC5) == RESET);

Wäre es so richtig?

Das führt in eine Endlos-Schleife... :-(

von Jim M. (turboj)


Lesenswert?

holger schrieb:
> Der DMA Interrupt sagt nur das das letzte Byte in das
> SPI Datenregister geschrieben wurde. Damit ist es aber
> noch nicht gesendet! Du ziehst also CS zu früh hoch.

Schwachsinn. Der DMA IRQ ist für RX Channel, und der kommt 
offensichtlich erst wenn der Transfer beendet wurde.

Das Problem des OP ist, dass er IMO nicht auf den Interrupt wartet bevor 
das nächste Wort gesendet wird.

von Martin P. (mar_tin)


Lesenswert?

So hatte ich es auch gelesen. Der IRQ vom Rx kommt sobald der Transfer 
beendet ist. Aber müsste "DMA1_FLAG_TC5" dann nicht gesetzt sein? Bzw. 
"DMA1_FLAG_TC4"? Werden beide nicht gesetzt...

von Martin P. (mar_tin)


Lesenswert?

Update:

Nachdem ich die Reihenfolge "Channel ENABLE" und "CS Low" geändert habe, 
kann ich das Setzen der u.g. Flags abwarten. Also schonmal keine 
Endlos-Schleife mehr. Hat sich leider trotzdem noch nix am Display 
getan...

DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel4, 1);
DMA_SetCurrDataCounter(DMA1_Channel5, 1);
SPIBuffer[0] = data;

ILI9341_WRX_RESET;

DMA_Cmd(DMA1_Channel4, ENABLE);
DMA_Cmd(DMA1_Channel5, ENABLE);

// Chip Select Low
GPIO_WriteBit(GPIOB, GPIO_Pin_12, RESET);

while (!DMA_GetFlagStatus(DMA1_FLAG_TC5));
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET);
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);

von Jim M. (turboj)


Lesenswert?

Martin Petri schrieb:
> DMA_Cmd(DMA1_Channel4, ENABLE);
> DMA_Cmd(DMA1_Channel5, ENABLE);
>
> // Chip Select Low
> GPIO_WriteBit(GPIOB, GPIO_Pin_12, RESET);

Das ist aber ein übles Race. CS kommt so praktisch immer etwas zu spät.
Das CS Low kann viel zu spät kommen, sobald andere Interrupts aktiv 
sind.

Dein Handler nimmt nach jedem empfangenen Wort CS wieder hoch, ist das 
überhaupt korrekt? Ich kenne das so, das CS für das ganze Kommando aktiv 
low sein muss.

von Martin P. (mar_tin)


Lesenswert?

Ja, das hab' ich mir gestern Abend auch schon überlegt. Ist wohl eher 
Zufall, dass DMA1_FLAG_TC5 anschließend passt.

Mir ist noch nicht klar, wie man die Initialisierung in einem Schwung an 
den ILI9341 übergibt. Beim "Command" muss RS (ILI9341_WRX_SET) ja 0 sein 
und beim Daten senden 1. Ich hab keine Idee wie ich das zwischendurch 
wechseln könnte, wenn ich die Initialisierungs-Bytes in den Buffer packe 
und abschicke.

Oder macht man die Initialisierung noch per SPI ohne DMA und wechselt 
dann in einen DMA Modus...(?)

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.