PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : EA DOMG-163 an SPI - Ich krieg die Krise



White_Fox
24.10.2016, 16:13
Hallo

Ich will oben genanntes LCD mit einem STM32 über SPI ansteuern, allerdings will das Ganze nicht wie ich. :(
Datenblatt:
http://cdn-reichelt.de/documents/datenblatt/A500/dog-me.pdf

Das LCD wird mit 3,3V versorgt, die Beschaltung erfolgte nach Datenblatt. Die Initialisierung ebenso. Die SPI wird mit 2MHz getaktet, das macht mit dem verwendeten Taktteiler irgendwas knapp unter 8kHz.

Init:

void SPI2_IntHandler(DMA_HandleTypeDef *hdma_spi2_tx){
HAL_Delay(1);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
}

void testLCD(){
unsigned short pl; //pl - Pufferlänge
unsigned long to; //to - Time Out
char t=0;

//Chip-Select-Pin
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);

//RS_Pin
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
//Init-Befehle für LCD
char lcdpuffer[] = {0b00111001, 0b00010101, 0b01010101, 0b01101110, 0b01110010, 0b00001111, 0b00000001, 0b00000110};
pl = 8;
to = 50;
char s[]= "Test OK";

//Init senden
HAL_SPI_Transmit(&hspi2, lcdpuffer, pl, to);
HAL_Delay(100);
printf("LCD initialisiert\n");

//RS_Pin
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
//String in s an LCD senden
pl = 6;
HAL_SPI_Transmit(&hspi2, s, pl, to);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);



SPI-Init:

/* SPI2 init function */
static void MX_SPI2_Init(void)
{

hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}

}

Hat irgendjemand eine Ahnung, warum das Scheißteil nicht kooperieren will? Die SPI jedenfalls scheint auszugeben was sie soll...sagt zumindest das Oszilloskop von einem Freund.

PS: Langsam glaube ich an Voodoo...
Während ich das hier schreibe und zwischendurch mal rumprobiere, habe ich die Spannung versehentlich kurz kurzgeschlossen. Ist Mist (das Netzteil liefert kontinuierlich 40A, wenn es muß), aber das LCD hat endlich mal das angezeigt was es anzeigen soll.
Auch heute Nachmittag waren einmal kurz etwas Blödsinn drauf...

Hat irgendjemand ne Ahnung was da los sein könnte?

Ach...und nach Neustart funktioniert es wieder nicht. :(

BMS
24.10.2016, 17:43
Hallo White_Fox,
habe schon mal ein ähnliches Display vom gleichen Hersteller verwendet (DOGM132), erfahrungsgemäß sind die Datenblätter eher knapp.

Was z.B. nicht genannt wird, ob/wie lange eine Startup-Zeit eingehalten werden muss. Am einfachsten könnte man vor der Initialisierung eine Pause programmieren.
Auch nicht spezifiziert wird, wie beim SPI die CPOL / CPHA einzustellen ist, eventuell muss man hier probieren.
Ich kenne die HAL-Funktionen jetzt nicht näher, aber vermutlich wird zwischen dem Senden der einzelnen Intiilisierungs-Bytes keine Pause eingelegt?
Eventuell bringt eine Pause zwischen den Befehlen etwas. Zumindest der Clear Display Befehl braucht ~1ms.
Die Initialisierungs-Sequenz habe ich jetzt nicht überprüft. Das Datenblatt zeigt nur 8bit Initialisierungen an. Das könnte man sicherheitshalber auch nochmal mit dem Datenblatt vergleichen.

Habe selber auch schon an einigen Displays rumprobiert, die Pausen zu programmieren hilft meist.
Sind alle Pins richtig angeschlossen und an jedem Ausgang auch mal nachgemessen oder eine LED blinken lassen?
Liegt der PSB-Anschluss auf GND?
Sind Reset, VIN, VDD an 3,3V?
Ist die Chip-Select-Leitung in Ordnung? Hat das Display einen Abblockkondensator bekommen? Ist die Betriebsspannung im Rahmen?

Viele Grüße,
Bernhard

White_Fox
25.10.2016, 10:45
Hallo BMS

Nun, das LCD hat 2s bevor die SPI Daten rausschiebt. 10ms zwischen den Befehlen hab ich schon ausprobiert. Initialisiert wird auch mit acht Bit. Taktpolarität und -phase sind im Datenblatt vom Controller...die passen soweit.

Das LCD hat auch seinen eigenen Abblockkondensator...das sollte hinhauen. Die Betriebsspannung liegt bei stabilen 3,3V.

und wie gesagt...aus irgendeinem mir unerfindlichen Grund hat es einmal kurz funktioniert. :(

White_Fox
25.10.2016, 15:40
Jetzt gehts rund...

Mit folgendem Code hab ich meinen STM32 gefüttert:


void testLCD(){
unsigned short pl; //pl - Pufferlänge
unsigned long to; //to - Time Out
char t=0;

//Chip-Select-Pin
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET);

//RS_Pin
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
//Init-Befehle für LCD
char lcdpuffer[] = {0b00001111, 0b00111001, 0b00010101, 0b01010101, 0b01101110, 0b01110010, 0b00111000, 0b00001111, 0b00000001, 0b00000110};
pl = 9;
to = 50;
char s[]= "Test OK";

//Init senden
HAL_SPI_Transmit(&hspi2, lcdpuffer, pl, to);
HAL_Delay(100);
//printf("LCD initialisiert\n");

HAL_Delay(10);
//RS_Pin
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);

//Text senden
HAL_SPI_Transmit(&hspi2, s, 7, 10);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);

Am Ende der Text-Übertragung habe ich mal den RS-Pin getoggelt. Und es hat funktioniert!

Einmal Ein- und Ausschalten, und es funktioniert nicht mehr. Und ich krieg es auch nicht mehr hin daß es wieder funktioniert. Was ist das bloß für ein Mist...

BMS
25.10.2016, 16:03
Wenn ich das richtig sehe, wird hier der RS-Pin getoggelt.

void SPI2_IntHandler(DMA_HandleTypeDef *hdma_spi2_tx){
HAL_Delay(1);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_2);
}
Frage: Wozu?

Ist diese Interrupt-Routine während der Initialisierung aktiv? Besser nicht...

Grüße, Bernhard

White_Fox
29.10.2016, 14:37
Hallo BMS

Errstmal: leider ist dieser Interrupt nicht aktiv. ich hab da mal im Debug-Modus einen Haltepunkt reingesetzt, jedoch kam der NIE zum Auslösen. Anscheinend stelle ich mich beim Thema Interrupt noch zu doof an.

Den RS-Pin zu toggeln war ein Hinweis, den ich auf mikrocontroller.net gefunden habe. Da fragte auch wer, der sein LCD (gleicher Typ9 zwar initialisieren konnte, aber keine Zeichen dargestellt bekam. Wie gesagt, weil die aber nie angesprungen wird hab ich mich darum nicht mehr gekümmert die wieder rauszunehmen. Werd ich trotzdem gleich mal machen...

White_Fox
30.10.2016, 19:30
So...es reicht. Ich werde jetzt erstmal von dem HAL-Kram wegkommen und das Ganze selber per Registereintrag machen.

Ich meld mich wieder, wenns klappt...oder nicht...

White_Fox
06.11.2016, 09:50
Also...ich hab in der letzten Woche das Ganze mal ohne den HAL-Kram nochmal gemacht. Um keine Verwirrung aufkommen zu lassen, der Code enthält neben der SPI-LCD-Sache noch Inits für ein paar LEDs und Taster und zukünftige Sachen...die sind aber nicht weiter wichtig bzw. funktionieren.

Das LCD sagt wieder nichts...ich hab aber gestern mal wieder (zufällig) ein paar wirre Zeichen auf dem LCD gehabt. War nicht reproduzierbar. Langsam glaube ich aber eine Gemeinsamkeit zwischen diesem "zufälligen" Funktionieren zu erkennen. Ich hatte wohl immer ein Oszilloskop an MOSI, oder an Clk oder an beiden Pins. Ich kann allerdings nicht vorstellen, daß das Oszi vllt einen dringend notwendigen Pulldown-Widerstand geliefert hat. Immerhin hab ich die Pins des STM32 als Push-Pull-Stufen konfiguriert.

Drei Fragen:
1.: Kann irgendjemand aus dem Code herauslesen, ob ich etwas falsch gemacht habe?
Datenblatt LCD: http://www.reichelt.de/index.html?ACTION=7&LA=3&OPEN=0&INDEX=0&FILENAME=A500%252Fdog-me.pdf
Datenblatt LCD-Controller: http://www.lcd-module.de/eng/pdf/zubehoer/st7036.pd
Die SPI-Konfiguration ist auf Seite 47 dargestellt.

Die Registerbeschreibung für den STM32 sind hier:
http://www.st.com/content/ccc/resource/technical/document/reference_manual/4d/ed/bc/89/b5/70/40/dc/DM00135183.pdf/files/DM00135183.pdf/jcr:content/translations/en.DM00135183.pdf
Die Beschreibung der SPI-Schnittstelle fängt ab S. 848 an, die Controllregister werden auf S. 888 beschrieben.

Ich habe, um die Timings zu verlängern, mal vor jeder Datenübertragung (SndBfhlLCD() und SndDtnLCD()) einen Haltepunkt gesetzt und das Programm im Debug-Modus laufen lassen. >Kein Erfolg.

2.: Sieht die Taktkonfiguration OK aus? Ich benutze keinen Quarz, mit wieviel die CPU befeuert wird ist erstmal nicht so wichtig. Daher hab ich die PLL in Ruhe gelassen. Den Bus, an dem die SPI2 hängt, will ich niedriger takten damit ich sicherheitshalber eine niedrige SPI-Frequenz generieren kann. Bei meiner aktuellen Einstellung sollte der SPI-Clock bei <10kHz liegen. Ich kann mir schwer vorstellen daß das für das LCD zu rasch ist.

3.: Irgendwas mache ich bei der Interupt-Konfiguration noch falsch. Gibt es irgendwo sowas wie "Interrupts global aktivieren" (sei-Befehl bei den AVRs), den ich übersehen habe? Der SPI-Interrupt wird nie angesprungen.


Ich hoffe, irgendwer hat da eine Lösung für...
Ach ja, anbei noch der Code:

/************************************************** *******************
* SEGGER MICROCONTROLLER GmbH & Co. KG *
* Solutions for real time microcontroller applications *
************************************************** ********************
* *
* (c) 2014 - 2016 SEGGER Microcontroller GmbH & Co. KG *
* *
* www.segger.com Support: support@segger.com *
* *
************************************************** ********************

-------------------------- END-OF-HEADER -----------------------------

File : main.c
Purpose : Generic application start
*/

#include <stdio.h>
#include <stdlib.h>
#include <stm32f446xx.h>

//Takteinstellungen konfigurieren
void TaktInit(){
//APB1: Tim6, SPI2, USART3
//AHB1: PortE, PortB, PortC
//APB2: Tim1

//SysClk 16MHz (interner Oszillator) - Takt für APB1 auf 2MHz setzen

RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB-Prescaler /1 => 16NHz
RCC->CFGR |= RCC_CFGR_PPRE1_DIV8; //Takt für APB1 = AHB/8
}

//Nested Vector Interrupt Controller konfigurieren
void NVICInit(){
NVIC_EnableIRQ(SPI2_IRQn);
}

//Pins, die als einfache Standard-EAs (Taster/LEDs) verwendet werden,konfigurieren
void EAInit(){
//Pins für LEDs initialisieren
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; //Takt für PortE aktivieren
//PE0
GPIOE->MODER |= GPIO_MODER_MODER0_0;
GPIOE->MODER &= ~GPIO_MODER_MODER0_1; //PE0 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_0; //PE0 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR0; //PE0 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR0_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR0_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_0; //LED abschalten
//PE1
GPIOE->MODER |= GPIO_MODER_MODER1_0;
GPIOE->MODER &= ~GPIO_MODER_MODER1_1; //PE1 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_1; //PE1 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR1; //PE1 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR1_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR1_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_1; //LED abschalten
//PE2
GPIOE->MODER |= GPIO_MODER_MODER2_0;
GPIOE->MODER &= ~GPIO_MODER_MODER2_1; //PE2 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_2; //PE2 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2; //PE2 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR2_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR2_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_2; //LED abschalten
//PE3
GPIOE->MODER |= GPIO_MODER_MODER3_0;
GPIOE->MODER &= ~GPIO_MODER_MODER3_1; //PE3 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_3; //PE3 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3; //PE3 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR3_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR3_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_3; //LED abschalten
//PE4
GPIOE->MODER |= GPIO_MODER_MODER4_0;
GPIOE->MODER &= ~GPIO_MODER_MODER4_1; //PE4 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_4; //PE4 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4; //PE4 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR4_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR4_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_4; //LED abschalten
//PE5
GPIOE->MODER |= GPIO_MODER_MODER5_0;
GPIOE->MODER &= ~GPIO_MODER_MODER5_1; //PE5 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_5; //PE5 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; //PE5 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR5_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR5_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_5; //LED abschalten
//PE6
GPIOE->MODER |= GPIO_MODER_MODER6_0;
GPIOE->MODER &= ~GPIO_MODER_MODER6_1; //PE6 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_6; //PE6 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6; //PE6 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR6_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR6_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_6; //LED abschalten

//Pins für Taster initialisieren
//PB5
GPIOB->MODER &= ~GPIO_MODER_MODER5_0;
GPIOB->MODER &= ~GPIO_MODER_MODER5_1; //PB5 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_5;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR5_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR5_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR5_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR5_1; //Pull-up-Widerstände einschalten PUPDR ->0
//PB6
GPIOB->MODER &= ~GPIO_MODER_MODER6_0;
GPIOB->MODER &= ~GPIO_MODER_MODER6_1; //PB6 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_6;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR6_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR6_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR6_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR6_1; //Pull-up-Widerstände einschalten PUPDR ->01
//PB7
GPIOB->MODER &= ~GPIO_MODER_MODER7_0;
GPIOB->MODER &= ~GPIO_MODER_MODER7_1; //PB7 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_7;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR7_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR7_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR7_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR7_1; //Pull-up-Widerstände einschalten PUPDR ->01
//PB12
GPIOB->MODER &= ~GPIO_MODER_MODER12_0;
GPIOB->MODER &= ~GPIO_MODER_MODER12_1; //PB12 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_12;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR12_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR12_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR12_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR12_1; //Pull-up-Widerstände einschalten PUPDR ->01
//PB13
GPIOB->MODER &= ~GPIO_MODER_MODER13_0;
GPIOB->MODER &= ~GPIO_MODER_MODER13_1; //PB13 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_13;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR13_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR13_1; //Pull-up-Widerstände einschalten PUPDR ->01
}

//SPI2 konfigurieren, es wird nur ein LCD damit bedient.
void SPIInit(){
//Taktversorgung aktivieren
RCC->AHB1ENR |= RCC_APB1ENR_SPI2EN; //Takt für SPI2 aktivieren
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; //Takt für PortB aktivieren
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Takt für PortC aktivieren

//Pins konfigurieren
//PB10 -> SPI2-Clock
GPIOB->MODER &= ~GPIO_MODER_MODER10_0;
GPIOB->MODER |= GPIO_MODER_MODER10_1; //PB10 -> Alternate Function MODER ->10
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_10; //PB10 als Push-Pull-Stufe
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR10_0;
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR10_1; //PB10 High-Speed
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR10_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR10_1; //Pull-up/-down-Widerstände abschalten
GPIOB->AFR[1] |= (0b0101<<8); //SPI2 AF5

//PB15 -> SPI2-MoSi
GPIOB->MODER &= ~GPIO_MODER_MODER15_0;
GPIOB->MODER |= GPIO_MODER_MODER15_1; //PB15 -> Alternate Function MODER ->10
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_15; //PB15 als Push-Pull-Stufe
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15_0;
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15_1; //PB15 High-Speed
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR15_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR15_1; //Pull-up/-down-Widerstände abschalten
GPIOB->AFR[1] |= (0b0101<<28); //SPI2 AF5

//PC3 -> SPI2-Select LCD
GPIOC->MODER |= GPIO_MODER_MODER3_0;
GPIOC->MODER &= ~GPIO_MODER_MODER3_1; //PC3 als Ausgang MODER ->01
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_3; //PC3 als Push-Pull-Stufe
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3_0;
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3_1; //PC3 High-Speed
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR3_0;
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR3_1; //Pull-up/-down-Widerstände abschalten

//PC2 -> SPI2-R/S
GPIOC->MODER |= GPIO_MODER_MODER2_0;
GPIOC->MODER |= GPIO_MODER_MODER2_1; //PC2 als Ausgang MODER ->01
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_3; //PC2 als Push-Pull-Stufe
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2_0;
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2_1; //PC2 als High-Speed
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR2_0;
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR2_1; //Pull-up/-down-Widerstände abschalten

//SPI konfigurieren
SPI2->CR1 |= SPI_CR1_BIDIMODE; //Bidirektionaler Modus
SPI2->CR1 |= SPI_CR1_BIDIOE; //Nur senden
SPI2->CR1 &= SPI_CR1_CRCEN; //CRC-Berechnung abschalten
SPI2->CR1 &= ~SPI_CR1_CRCNEXT; //Keine CRC-Phase
SPI2->CR1 &= ~SPI_CR1_DFF; //8-Bit Frameformat
SPI2->CR1 &= ~SPI_CR1_RXONLY; //Receive-Only-Modus abschalten
SPI2->CR1 &= ~SPI_CR1_SSM; //Software Slave Management abschalten
//SPI2->CR1 &= ~SPI_CR1_SSI; //NSS-Pin
SPI2->CR1 &= ~SPI_CR1_LSBFIRST; //MSB zuerst übertragen
SPI2->CR1 |= SPI_CR1_SPE; //SPI aktivieren
SPI2->CR1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0; //Taktteiler 256 -> 2MHz/256 -> 7,8kHz
SPI2->CR1 |= SPI_CR1_MSTR; //Master configuration
SPI2->CR1 |= SPI_CR1_CPOL; //Takt=1 im Leerlauf
SPI2->CR1 &= ~SPI_CR1_CPHA; //Clockphase, first clock transistion is first data capture edge

SPI2->CR2 |= SPI_CR2_TXEIE; //Tx-buffer empty interupt aktivieren
SPI2->CR2 &= ~SPI_CR2_RXNEIE; //Rx-buffer not empty interrupt deaktivieren
SPI2->CR2 &= ~SPI_CR2_ERRIE; //Error Interrupt deaktivieren
SPI2->CR2 &= ~SPI_CR2_FRF; //Motorola-Modus (Sandard)
SPI2->CR2 &= ~SPI_CR2_SSOE; //SS Output deaktivieren
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; //TXE DMA-Request deaktivieren
SPI2->CR2 &= ~SPI_CR2_RXDMAEN; //RXNE DMA Request deaktivieren
}

void LCDInit(){
SndBfhlLCD(0b00111001);
SndBfhlLCD(0b00010101);
SndBfhlLCD(0b01010101);
SndBfhlLCD(0b01101110);

SndBfhlLCD(0b01110010);
SndBfhlLCD(0b00111000);
SndBfhlLCD(0b00001111);
SndBfhlLCD(0b00000001);
SndBfhlLCD(0b00000110);
printf("Init-Befehle an LCD gesendet\n");
}

//Sendet ein Byte ans LCD
void SndLCD(char Byte){
while (SPI2->SR & SPI_SR_TXE){ //Warten bis Transmitpuffer frei ist
}
SPI2->DR = Byte; //Byte ins Transmitregister schreiben
}

//Sendet ein Befehlbyte ans LCD
void SndBfhlLCD(char byte){
while (SPI2->SR & SPI_SR_TXE){ //Warten bis Transmitpuffer frei ist
}
GPIOC->BSRR |= GPIO_BSRR_BR_2; //RS-Pin zurücksetzen
SPI2->DR = byte; //Byte senden
while(SPI2->SR & SPI_SR_TXE){ //Warten bis Übertragung abgeschlossen ist
}
//RS NICHT toggeln
}

//Sendet ein Datenbyte ans LCD
void SndDtnlLCD(char byte){
while (SPI2->SR & SPI_SR_TXE){ //Warten bis Transmitpuffer frei ist
}
GPIOC->BSRR |= GPIO_BSRR_BS_2; //RS-Pin setzen
SPI2->DR = byte; //Byte senden
while(SPI2->SR & SPI_SR_TXE){ //Warten bis Übertragung abgeschlossen ist
}
GPIOC->BSRR |= GPIO_BSRR_BS_2; //RS toggeln
GPIOC->BSRR |= GPIO_BSRR_BR_2;
}

//SPI2 Interrupt
void SPI2_IRQHandler (void){
GPIOE->BSRR |= GPIO_BSRR_BR_4; //LED an E4 einschalten
printf("SPI2 Interrupt Event\n");
}

void main(void){
TaktInit();
NVICInit();
EAInit();

GPIOE->BSRR |= GPIO_BSRR_BR_5; //LED an E5 einschalten

SPIInit();
LCDInit();

SndDtnlLCD('T');
SndDtnlLCD('e');
SndDtnlLCD('s');
SndDtnlLCD('t');
SndDtnlLCD(' ');
SndDtnlLCD('O');
SndDtnlLCD('K');

GPIOE->BSRR |= GPIO_BSRR_BR_1; //LED an E1 einschalten
}

White_Fox
07.11.2016, 16:21
Hat niemand eine Idee? Schade. :(

Ich hab den Code heute um folgende While-Schleife in der main ergänzt:

while(1){
printf("Klack\n");
LCDInit();
SndDtnlLCD('T');
SndDtnlLCD('e');
SndDtnlLCD('s');
SndDtnlLCD('t');
SndDtnlLCD(' ');
SndDtnlLCD('O');
SndDtnlLCD('K');
}
Ziel war, mit einem Oszilloskop zu prüfen ob die SPI wenigstens überhaupt muckt. Aber da kommt nichts...absolut nichts. Jetzt frag ich mich doch, warum? Ich hab mal den Code durchgesehen den mir die Cube generiert hat. Das Einzige, was ich nicht gemacht habe, war, den Power-Controller zu konfigurieren (PWR). Die Cube schreibt noch irgendwas in die VOS-Bits. Aber das ist für die SPI doch auch gar nicht notwendig, wenn ich das Datenblatt richtig verstehe...und GPIOs funktionieren ja auch.

botty
08.11.2016, 18:18
Hallo,

mir fallen einige Dinge bei der ganzen Sache auf. Leider bin ich schon eine Weile aus der STM32 Programmierung heraus, so dass das was ich hier schreibe eher als Hinweise denn als Lösungen zu verstehen sind.

Zuerst der SPI-Takt, damit liegst du meiner Meinung nach ziemlich daneben. Im Datenblatt des Controlers vom LCD ist hinten ein Diagramm und eine Tabelle zu finden, in der steht, dass die CLK-Zyklen mindesten 200ns lang sein müssen. Wenn ich mich nicht verrechne, dann sind das 5MHz und da würde ich auch den SPI-Bus-Takt so nahe wie möglich heranbringen. Da du eh mit dem internen Clock-Mechanismus des STM arbeitest und der 16MHz abgibt, würde ich den APB1-Bustakt auf 16MHz stehen lassen und im SPI2->CR1 Register mit dem Vorteiler 4MHz einstellen.

Im Verhältnis zu dem, was der STM an Datenmenge ans SPI anlegen kann und wie schnell der LCD-Controller das verarbeiten kann, besteht 'ne gewisse Diskrepanz. Wenn du dir mal die Initialisierungssequenz vom LCD-Controler ansiehst, dann sind da mehrere Wartezeiten angegben. Von ca. 27uS nach Commando-Bytes (entspricht ca. 13 Bytes die du bei 4MHz losschicken könntest), über 40ms nach dem Reset, bis hin zu einem besonderem Befehl, der 200ms zur Verarbeitung braucht, bevor der Controler überhaupt das nächste Commando oder Datenbyte verabeiten könnte.
Diese Zeiten müssen eingehalten werden und so schließt es sich aus, dass du Interruptgetrieben ein Byte nach dem anderen übertragen könntest.

Daher würde ich das SPI2->CR2 Register mit den Interrupts einfach mal in Ruhe lassen (auskommentieren), dann ein Byte nach dem anderem übertragen und dazwischen SPI abschalten und die Wartezeiten einhalten.

Ein klarer Fehler liegt bei dir in der Verwendung des SPE-Bits im CR1 Register vor.
Du schaltest das Ding an und konfigurierst noch weiter z.B. MSTR, CPOL, CPHA und das gesamte CR2 Register. Zumindestens bei den Bits steht im Prog-Manual drin, dass man diese Bits nicht bei laufender Peripherie setzen darf. Auch beim Teiler für den SPI-Takt steht das dabei und ziemlich sicher ist das der Grund, dass du nichts am Oszi sehen konntest.
Also alles konfigurieren ohne das SPE Bit. Das wird erst in der Übertragungfunktion gesetzt, die für ein Commando Byte im Pseudo-Code so lautet:


RS Leitung setzen
CS Leitung setzen
SPE Bit setzen (einschalten)
TXE Bit pollen bis DR frei ist
DR beschreiben
TXE Bit pollen bis DR frei ist
BSY Bit pollen bis es 0 ist (Übertragung vollständig)
SPE zurück setzen (ausschalten)

Wichtig ist vor dem Ausschalten das Busy-Bit zu kontrollieren, da das Datum im DR Register in ein internes Shift-Register kopiert wird. Das setzt das TXE Bit, bedeutet aber noch nicht das die Übertragung komplett ist. Die ist erst vollständig, wenn das Busy-Bit zu 0 wird!

Gruss botty

White_Fox
09.11.2016, 19:28
Hallo botty

Mensch, vielen Dank für deine Hilfe. Das werd ich so bald wie möglich mal ausprobieren.

Ja...daß das LCD die Daten nicht schneller verarbeitet nur weil es diese über SPI reinbekommt ist mir schon klar. Dummerweise bietet das LCD über SPI keinen Rückkanal um festzustellen, ob es fertig ist oder nicht. Daher auch die lahme SPI.

White_Fox
11.11.2016, 14:50
@botty:
Ich habe deine Vorschläge ausprobiert...das Oszi zeigt immer noch keinen Mucks. Ich hab es zwischendurch mal mit dem Programm getestet, daß ich mit der HAL-Bibliothek gemacht habe. Das funktioniert.

Mir ist aber was aufgefallen: Ich habe mal die Funktionen angeschaut, wo die Cube die Initialisierung der GPIOs und der SPI gemacht hat. Die Pins sind dieselben. Allerdings wird kein Pin als "Alternate Function" initialisiert.

Cube-Code:

/*Configure GPIO pins : PC2 PC3 */
GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);


/* SPI2 init function */
static void MX_SPI2_Init(void)
{

hspi2.Instance = SPI2;
hspi2.Init.Mode = SPI_MODE_MASTER;
hspi2.Init.Direction = SPI_DIRECTION_2LINES;
hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
hspi2.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi2.Init.NSS = SPI_NSS_SOFT;
hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi2.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi2) != HAL_OK)
{
Error_Handler();
}

}

Auch unter den Suchwort "AFR" oder "function" findet meine IDE im cubegenerierten Projekt nichts relevantes. Hat jemand eine Ahnung, was die HAL/Cube da eigentlich macht? Immerhin läuft die SPI da.

Noch eine andere Auffälligkeit: Im Datenblatt ist das Alternate-Function-Register in zwei 16-Bit-Register je Port aufgeteilt. Meine IDE kennt aber nur ein AFR (mit AFR[0] oder AFR[1] kann der Compiler aber was anfangen), ich hätte aber eher sowas wie AFRL und AFRH erwartet. Das AFR als Array hab ich aus irgendeinem Inernet-Beispiel, aber kann das überhaupt stimmen?

- - - Aktualisiert - - -

Ich hab meinen Code mal im Debugger durchlaufen lassen. Beim Warten auf das Tx-Empty--Flag kommt er nicht mehr aus der zweiten While-Schleife raus...anscheinend läuft das SPI-Modul überhaupt nicht. Nur...warum? Takt ist drin.


//Sendet ein Befehlbyte ans LCD
void SndBfhlLCD(char byte){
GPIOC->BSRR |= GPIO_BSRR_BR_2; //RS-Pin zurücksetzen
SPI2->CR1 |= SPI_CR1_SPE; //SPI aktivieren
while (SPI2->SR & SPI_SR_TXE){ //Warten bis Transmit-Register frei ist
}
SPI2->DR = byte; //Byte senden
while (SPI2->SR & SPI_SR_TXE){ //Warten bis Transmit-Register frei ist
}
while (!(SPI2->SR & SPI_SR_BSY)){ //Warten bis laufende Übertragung abgeschlossen ist
}
//SPI2->CR1 &= ~SPI_CR1_SPE; //SPI deaktivieren
//RS NICHT toggeln
}

botty
11.11.2016, 15:50
Hi,
also zur HAL kann ich nicht viel sagen. Wenn ich hier einen stm32f446er mit SPI2 in CubeMX auswähle und den Transfer-Master-Mode mit Soft Nss eingebe, dann werden MOSI und CLK mit Alternate Function generiert?!? -> Keine Ahnung warum das bei mir anders ist als bei dir?!?

Zu deinem Code:
TXE und BSY verhalten sich genau umgekehrt wie du es benutzt und die ziehst die CS Leitung nicht runter bevor du schreibst:


void SndBfhlLCD(char byte){
SPI2->CR1 |= SPI_CR1_SPE; //SPI aktivieren
GPIOC->BSRR |= GPIO_BSRR_BR_2; //RS-Pin zurücksetzen
GPIOC->BSRR |= GPIO_BSRR_BR_3; // CS runterziehen

while ( ! (SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
SPI2->DR = byte; //Byte senden
while ( ! (SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
while (SPI2->SR & SPI_SR_BSY){ //Warten bis laufende Übertragung abgeschlossen ist
}

GPIOC->BSRR |= GPIO_BSRR_BS_3; // CS high
GPIOC->BSRR |= GPIO_BSRR_BS_2; //RS-Pin high
SPI2->CR1 &= ~SPI_CR1_SPE; //SPI deaktivieren
}

Hoffe das das jetzt klappt.
Hab heute auch zwei Stunden nach genau einem Bit gefahndet und kann deinen Ärger verstehen.

Gruss botty

White_Fox
11.11.2016, 20:01
Autsch...da werd ich TXE und BSY mal noch fix drehen. Und CS natürlich einbauen. Das Oszi hab ich erst am Mo wieder zur Verfügung, falls das LCD die Arbeit noch verweigert (wovon ich sicherheitshalber mal ausgehe).

- - - Aktualisiert - - -

Nachtrag:
Der STM32 kommt im Debug-Mode immer noch nicht aus der zweiten While-Schleife. Wenn ich einen Haltepunkt am Ende der Funktion setze, wird dieser nie erreicht -> Endlosschleife. :(

Was muß denn gemacht werden, um ein Peripherie-Element wie die SPI zu nutzen? Meines Wissens ist dies:
-Takt für Pins aktivieren
-Takt für Peripherie aktivieren
-Pins konfigurieren
-Peripherie konfigurieren
-> Ferig

Hab ich was übersehen in dieser Liste?

Noch was...was macht eigentlich die SystemInit() ? (Hab die grad mal am Anfang des Programms aufgerufen, ändert aber auch nichts.)


void SndBfhlLCD(char byte){
SPI2->CR1 |= SPI_CR1_SPE; //SPI aktivieren
GPIOC->BSRR |= GPIO_BSRR_BR_2; //RS-Pin zurücksetzen
GPIOC->BSRR |= GPIO_BSRR_BR_3; //CS runterziehen

while (!(SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
SPI2->DR = byte; //Byte senden
while (!(SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
while (SPI2->SR & SPI_SR_BSY){ //Warten bis laufende Übertragung abgeschlossen ist
}
GPIOC->BSRR |= GPIO_BSRR_BS_3; //CS high
GPIOC->BSRR |= GPIO_BSRR_BS_2; //RS-Pin high
SPI2->CR1 &= ~SPI_CR1_SPE; //SPI deaktivieren
//RS NICHT toggeln
}


Ich wünsche an dieser Stelle mal ein schönes Wochenende :)

Siro
11.11.2016, 21:28
Hallo,
ich habe das Display auch schon in Gang gebracht, aber im 4 Bit Modus.
Das lief auch SEHR schwer anfangs....
Hast Du den Kontrast richtig eingestellt und die Kondensatoren dran.
Bei der 3,3 Volt Versorgung gibt es wohl eine Follower, der auch eingestellt werden muss.
Hab auch schonmal ewig gesucht und dann stellte sich heraus, dass ich nur nix gesehen hatte, weil der Kontrast falsch eingestellt war.
Der CSB und PSB Pin auch richtig ? und nicht vergessen die Datenleitungen, obwohl nicht benutzt festzulegen..

Ich weiss nicht wie schnell deine Pins toggeln, aber mein Prozessor war zu schnell und ich musste delays einbauen.
2-3 Mikrosekunden.

Ganz wichtig war die Initialisierung, da hat jedes "ähnliche" Display seinen Eigenheiten.
Wobei die DOGM Serie sich doch von anderen Displays unterscheidet
Zudem gibt es ja auch 2 Instruction Tables, die richtig hin und hergeschaltet werden müssen.

Am Anfang darf man ja auch nicht das Busy Flag abfrage, weil es noch nicht funktioniert.
Wie das genau beim SPI Modus läuft, hab ich aber nicht ausprobiert.

Schau Dir mal die Zeiten an, beim Initialisieren von mir:


/* initialize LCD 4 Bit mode FOSC 380 KHz */
/* Power Up need ca. 40ms internal Power on reset ST7036 */
Wait_us(50000);

LCD_DataOut8(0x30); /* Function set */
Wait_us(2000); /* wait > 1,6ms */

LCD_DataOut8(0x30); /* Function set */
Wait_us(50); /* wait > 26,3 us */

LCD_DataOut8(0x30); /* Function set */
Wait_us(50); /* wait > 26,3 us */


LCD_DataOut8(0x20); /* Function Set 4 BIT DL=0 */
Wait_us(50); /* wait > 26,3 us */

/* !!!! erst ab jetzt ist der 4 Bit Modus aktiv */

LCD_WaitBusy();

/* DL = High= 8 Bit DL=Low = 4 Bit */
/* N=1=2 Zeilen N=0=1Zeile */
LCD_DataOut4(0x29); /* Function Set 4 Bit N=1 DH=0 IS2=0 IS1=1 DH=1=Double Height*/
LCD_WaitBusy();

LCD_DataOut4(0x14); /* BIAS Intern OSC frequency BS= 1 0 FX=1 */
LCD_WaitBusy();

LCD_DataOut4(0x78); /* Contrast set C3 C2 C1 C0 */
LCD_WaitBusy();

LCD_DataOut4(0x55); /* 0x5E Power Ion=1 Bon=1 C5=1 C4=1 */
LCD_WaitBusy();

LCD_DataOut4(0x6D); /* Follower control Fon Rab2 Rab1 Rab0 */
LCD_WaitBusy();

/* LCD_DataOut4(0x28); */ /* switch back to instruction table 0 */
/* LCD_WaitBusy(); */

LCD_DataOut4(0x0F); /* Display On/off D= C= B= */
LCD_WaitBusy();

LCD_DataOut4(0x01); /* Clear Display, Cursor home */
LCD_WaitBusy();

LCD_DataOut4(0x06); /* Cursor auto increment Entry Mode set ID= S= cursor move dir */
LCD_WaitBusy();

White_Fox
12.11.2016, 12:39
Hallo Siro

Erstmal danke...aber momentan läuft die SPI noch nichtmal. Ich komm dann aber gern auf deinen Code zurück (in V2 werd ich definitiv die Ansteuerung im 4- oder 8-Bitmodus nutzen, nie wieder LCD über SPI, grmpf).

Wsk8
12.11.2016, 14:00
SPI1 mit SPL für einen 411RE, sollte aber Pin-Kompatibel mit dem 446 sein. Für CS muss noch ein Pin konfiguriert werden.


#include "SPI.h"


void Spi_Init(SPI_TypeDef *SPIx)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

// Periph clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1);

// Configure SPI pins: SCK, MOSI
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// Configure pins: MISO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOA, &GPIO_InitStructure);


// Reset SPI Interface
SPI_I2S_DeInit(SPIx);

// SPI configuration
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPIx, &SPI_InitStructure);

SPI_CalculateCRC(SPIx, DISABLE);

// Enable the SPI
SPI_Cmd(SPIx, ENABLE);
}

uint8_t Spi_ReadByte(SPI_TypeDef *SPIx)
{
return Spi_WriteByte(SPIx, 0xFF);
}

uint8_t Spi_WriteByte(SPI_TypeDef *SPIx, uint8_t _data)
{
// Loop while DR register is not empty
while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_TXE) == RESET);

// Send byte through the SPIx peripheral
SPI_I2S_SendData(SPIx, _data);

while(SPI_I2S_GetFlagStatus(SPIx, SPI_I2S_FLAG_RXNE) == RESET);

// Return the byte read from the SPI bus
return (uint8_t)SPI_I2S_ReceiveData(SPIx); }

mfg

botty
13.11.2016, 10:48
Hi again,

die Zeile in der Initialisierung stimmt nicht


RCC->AHB1ENR |= RCC_APB1ENR_SPI2EN; //Takt für SPI2 aktivieren

sondern muss wohl so lauten


RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; //Takt für SPI2 aktivieren


Gruss botty

White_Fox
13.11.2016, 17:24
Hätt ich schreiben sollen..die hab ich am Freitag schon gefunden (und behoben). Ich hatte gehofft das wäre es bereits gewesen, aber irgend so ein kleiner verfluchter Fehlerteufel muß da noch drin sein. Ich hab den Rechner mit dem Projekt grad nicht an, ich stell morgen nochmal die letzte Codeversion rein.

Klebwax
14.11.2016, 00:34
Du hast zwei Probleme und die solltest du getrennt lösen. Das erste ist dein SPI Controler, das zweite das Display. Ich hab, wie ich gerade rausgefunden hab, auch mal was mit diesem Display gemacht. Bei für mich neuen Bausteinen mit simplem synchronen seriellen Protokoll, fange ich mit Bitbanging an. Ich programmiere einfach direkt das Timingdiagramm nach. Wenn der Baustein dann zuckt, kann man ja immer noch die HW im µC einsetzen. Meist bleibt es aber bei dieser Lösung, so auch bei diesem Display.

Ich kann meine Sachen im Augenblick nicht nachvollziehen, hab keine HW lauffähig. Ich pack aber trotzdem hier mal den wesentlichen Teil des Codes rein. Passt natürlich nicht für deinen Prozessor, ist aber plain C. Mein Prozessor ist ein PIC24 mit 16MHz Befehlstakt,

//uint8_t LCD_InitTab[] = {0x39,0x14,0x55,0x6d,0x78,0x38,0x0f,0x06,0x01};
const uint8_t LCD_InitTab[] = {0x29,0x14,0x56,0x6d,0x70,0x38,0x0f};

#define _____ 0x00
#define ____X 0x01
#define ___X_ 0x02
#define ___XX 0x03
#define __X__ 0x04
#define __X_X 0x05
#define __XX_ 0x06
#define __XXX 0x07
#define _X___ 0x08
#define _X__X 0x09
#define _X_X_ 0x0A
#define _X_XX 0x0B
#define _XX__ 0x0C
#define _XX_X 0x0D
#define _XXX_ 0x0E
#define _XXXX 0x0F
#define X____ 0x10
#define X___X 0x11
#define X__X_ 0x12
#define X__XX 0x13
#define X_X__ 0x14
#define X_X_X 0x15
#define X_XX_ 0x16
#define X_XXX 0x17
#define XX___ 0x18
#define XX__X 0x19
#define XX_X_ 0x1A
#define XX_XX 0x1B
#define XXX__ 0x1C
#define XXX_X 0x1D
#define XXXX_ 0x1E
#define XXXXX 0x1F

const uint8_t Chargen[] = {
#ifdef ARROWS
// left arrow
_____,
__X__,
_X___,
XXXXX,
_X___,
__X__,
_____,
_____,
// top left arrow
XXX__,
XX___,
X_X__,
___X_,
____X,
_____,
_____,
_____,
// up arrow
__X__,
_XXX_,
X_X_X,
__X__,
__X__,
__X__,
_____,
_____,
// top right arrow
__XXX,
___XX,
__X_X,
_X___,
X____,
_____,
_____,
_____,
// right arrow
_____,
__X__,
___X_,
XXXXX,
___X_,
__X__,
_____,
_____,
// bottom right arrow
_____,
_____,
_____,
X____,
_X___,
__X_X,
___XX,
__XXX,
// down arrow
_____,
_____,
__X__,
__X__,
__X__,
X_X_X,
_XXX_,
__X__,
// bottom left arrow
_____,
_____,
_____,
____X,
___X_,
X_X__,
XX___,
XXX__,
#else
// bottom row
_____,
_____,
_____,
_____,
_____,
_____,
_____,
XXXXX,
// two rows
_____,
_____,
_____,
_____,
_____,
_____,
XXXXX,
XXXXX,
// three rows
_____,
_____,
_____,
_____,
_____,
XXXXX,
XXXXX,
XXXXX,
// four rows
_____,
_____,
_____,
_____,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
// five rows
_____,
_____,
_____,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
// six rows
_____,
_____,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
// seven rows
_____,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
// full block
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
XXXXX,
#endif
};

void SPI_WriteByte(uint8_t data) {
uint8_t mask = 0x80;
while (mask) {
SCKout = 0;
if (data & mask) {
SDOout = 1;
} else {
SDOout = 0;
}
SCKout = 1;
mask >>= 1;
}
}

void LCD_SendCMD(uint8_t command){
RSout = 0;
CSBout = 0;
SPI_WriteByte(command);
CSBout = 1;
__delay_us(28);
}

void LCD_SendData(uint8_t data){
RSout = 1;
CSBout = 0;
SPI_WriteByte(data);
CSBout = 1;
__delay_us(28);
}

void LCD_SendCMDBlock(const uint8_t* commands, int length) {
while (length) {
LCD_SendCMD(*commands++);
length--;
}
}

void LCD_SendDataBlock(const uint8_t* data, int length) {
RSout = 1;
CSBout = 0;
while (length) {
LCD_SendData(*data++);
length--;
}
CSBout = 1;
}

void LCD_ClearDisplay(void){
LCD_SendCMD(0x01);
__delay_us(1500);
}

void LCD_SetCursor(int mode){
LCD_SendCMD(0b00001100 | (mode & 0b00000011));
}

void LCD_SetDisplayAddress(int address){
LCD_SendCMD(0b10000000 | (address & 0b01111111));
}

void LCD_SetCharGenAddress(int address){
LCD_SendCMD(0b01000000 | (address & 0b00111111));
}

void LCD_Init(void){
LCD_SendCMDBlock(LCD_InitTab, sizeof(LCD_InitTab));
LCD_ClearDisplay();
}

int LCD_SetExtraChars(const uint8_t* BitPattern, int length) {
if(*BitPattern == 0xFF){ // unprogrammed EEPROM data
return -1;
}
if(length > 64){
length = 64;
}
LCD_SetCharGenAddress(0);
LCD_SendDataBlock((uint8_t*) BitPattern, length);
return 0;
}

void LCD_SetBacklight(int intensity) {
int tmp;
if (intensity > 15) {
intensity = 15;
}
tmp = 0x01 << intensity;
OC2R = tmp;
}


Ist schon eine Weile her, daß ich das geschrieben hab, daher weiß ich nicht mehr, was der Unterschied der beiden Initialisierungssquenzen am Anfang ist. Da sind auch noch zusätzliche Zeichen drin, die auf die freien Plätze programmiert werden können und ein paar #defines, die das besser "lesbar" machen. Das Backlight hatte ich wohl an einem PWM Ausgang.

Achso noch was:

RSout = 0;
CSBout = 0;

hier werden Portbits PIC-typisch (auf 0) gesetzt, da gibts irgendwo an anderer Stelle die #defines dazu, es gibt noch SCKout und SDOout, also 4 Ports.

Ich hoffe, es hilft dir etwas

MfG Klebwax

White_Fox
14.11.2016, 16:03
Danke für den Codeschnipsel...werd ich mir genauer ansehen wenn die SPI läuft.

Der Code, wie er momentan ist. Momentan versuche ich mich mal an der USART in der Hoffnung, den Fehler eingrenzen zu können. Wenn ich die UART zum Laufen bringe ist die SPI-Init Mist. Wenn die USART auch nicht will hab ich wohl ein grundsätzlicheres Konfiproblem.


#include <stdio.h>
#include <stdlib.h>
#include <stm32f446xx.h>

//Takteinstellungen konfigurieren
void TaktInit(){
//APB1: Tim6, SPI2, USART3
//AHB1: PortE, PortB, PortC, PortD
//APB2: Tim1

//SysClk 16MHz (interner Oszillator) - Takt für APB1 auf 2MHz setzen

RCC->CFGR |= RCC_CFGR_HPRE_DIV1; //AHB-Prescaler /1 => 16NHz
RCC->CFGR |= RCC_CFGR_PPRE1_DIV1; //Takt für APB1 = AHB/1
}

//Nested Vector Interrupt Controller konfigurieren
void NVICInit(){
//NVIC_EnableIRQ(SPI2_IRQn);
}

//Pins, die als einfache Standard-EAs (Taster/LEDs) verwendet werden,konfigurieren
void EAInit(){
//Pins für LEDs initialisieren
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; //Takt für PortE aktivieren
//PE0
GPIOE->MODER |= GPIO_MODER_MODER0_0;
GPIOE->MODER &= ~GPIO_MODER_MODER0_1; //PE0 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_0; //PE0 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR0; //PE0 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR0_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR0_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_0; //LED abschalten
//PE1
GPIOE->MODER |= GPIO_MODER_MODER1_0;
GPIOE->MODER &= ~GPIO_MODER_MODER1_1; //PE1 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_1; //PE1 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR1; //PE1 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR1_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR1_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_1; //LED abschalten
//PE2
GPIOE->MODER |= GPIO_MODER_MODER2_0;
GPIOE->MODER &= ~GPIO_MODER_MODER2_1; //PE2 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_2; //PE2 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2; //PE2 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR2_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR2_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_2; //LED abschalten
//PE3
GPIOE->MODER |= GPIO_MODER_MODER3_0;
GPIOE->MODER &= ~GPIO_MODER_MODER3_1; //PE3 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_3; //PE3 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3; //PE3 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR3_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR3_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_3; //LED abschalten
//PE4
GPIOE->MODER |= GPIO_MODER_MODER4_0;
GPIOE->MODER &= ~GPIO_MODER_MODER4_1; //PE4 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_4; //PE4 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR4; //PE4 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR4_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR4_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_4; //LED abschalten
//PE5
GPIOE->MODER |= GPIO_MODER_MODER5_0;
GPIOE->MODER &= ~GPIO_MODER_MODER5_1; //PE5 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_5; //PE5 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR5; //PE5 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR5_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR5_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_5; //LED abschalten
//PE6
GPIOE->MODER |= GPIO_MODER_MODER6_0;
GPIOE->MODER &= ~GPIO_MODER_MODER6_1; //PE6 -> Ausgang MODER ->01
GPIOE->OTYPER &= ~GPIO_OTYPER_OT_6; //PE6 als Push-Pull-Stufe
GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6; //PE6 High-Speed
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR6_0;
GPIOE->PUPDR &= ~GPIO_PUPDR_PUPDR6_1; //Pull-up/-down-Widerstände abschalten
GPIOE->BSRR|= GPIO_BSRR_BS_6; //LED abschalten

//Pins für Taster initialisieren
//PB5
GPIOB->MODER &= ~GPIO_MODER_MODER5_0;
GPIOB->MODER &= ~GPIO_MODER_MODER5_1; //PB5 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_5;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR5_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR5_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR5_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR5_1; //Pull-up-Widerstände einschalten PUPDR ->0
//PB6
GPIOB->MODER &= ~GPIO_MODER_MODER6_0;
GPIOB->MODER &= ~GPIO_MODER_MODER6_1; //PB6 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_6;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR6_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR6_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR6_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR6_1; //Pull-up-Widerstände einschalten PUPDR ->01
//PB7
GPIOB->MODER &= ~GPIO_MODER_MODER7_0;
GPIOB->MODER &= ~GPIO_MODER_MODER7_1; //PB7 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_7;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR7_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR7_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR7_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR7_1; //Pull-up-Widerstände einschalten PUPDR ->01
//PB12
GPIOB->MODER &= ~GPIO_MODER_MODER12_0;
GPIOB->MODER &= ~GPIO_MODER_MODER12_1; //PB12 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_12;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR12_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR12_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR12_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR12_1; //Pull-up-Widerstände einschalten PUPDR ->01
//PB13
GPIOB->MODER &= ~GPIO_MODER_MODER13_0;
GPIOB->MODER &= ~GPIO_MODER_MODER13_1; //PB13 -> Eingang MODER ->00
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_13;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13_0;
GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR13_1;
GPIOB->PUPDR |= GPIO_PUPDR_PUPDR13_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR13_1; //Pull-up-Widerstände einschalten PUPDR ->01
}

//SPI2 konfigurieren, es wird nur ein LCD damit bedient.
void SPIInit(){
//Taktversorgung aktivieren
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; //Takt für SPI2 aktivieren
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; //Takt für PortB aktivieren
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Takt für PortC aktivieren
RCC->APB1ENR |= RCC_APB1ENR_PWREN;

//Pins konfigurieren
//PB10 -> SPI2-Clock
GPIOB->MODER &= ~GPIO_MODER_MODER10_0;
GPIOB->MODER |= GPIO_MODER_MODER10_1; //PB10 -> Alternate Function MODER ->10
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_10; //PB10 als Push-Pull-Stufe
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR10_0;
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR10_1; //PB10 High-Speed
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR10_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR10_1; //Pull-up/-down-Widerstände abschalten
GPIOB->AFR[1] |= (0b0110<<8); //SPI2 AF6

//PB15 -> SPI2-MoSi
GPIOB->MODER &= ~GPIO_MODER_MODER15_0;
GPIOB->MODER |= GPIO_MODER_MODER15_1; //PB15 -> Alternate Function MODER ->10
GPIOB->OTYPER &= ~GPIO_OTYPER_OT_15; //PB15 als Push-Pull-Stufe
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15_0;
GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15_1; //PB15 High-Speed
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR15_0;
GPIOB->PUPDR &= ~GPIO_PUPDR_PUPDR15_1; //Pull-up/-down-Widerstände abschalten
GPIOB->AFR[1] |= (0b0110<<28); //SPI2 AF6

//PC3 -> SPI2-Select LCD
GPIOC->MODER |= GPIO_MODER_MODER3_0;
GPIOC->MODER &= ~GPIO_MODER_MODER3_1; //PC3 als Ausgang MODER ->01
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_3; //PC3 als Push-Pull-Stufe
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3_0;
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3_1; //PC3 High-Speed
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR3_0;
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR3_1; //Pull-up/-down-Widerstände abschalten

//PC2 -> SPI2-R/S
GPIOC->MODER |= GPIO_MODER_MODER2_0;
GPIOC->MODER |= GPIO_MODER_MODER2_1; //PC2 als Ausgang MODER ->01
GPIOC->OTYPER &= ~GPIO_OTYPER_OT_3; //PC2 als Push-Pull-Stufe
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2_0;
GPIOC->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2_1; //PC2 als High-Speed
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR2_0;
GPIOC->PUPDR &= ~GPIO_PUPDR_PUPDR2_1; //Pull-up/-down-Widerstände abschalten

//SPI konfigurieren
SPI2->CR1 |= SPI_CR1_BIDIMODE; //Bidirektionaler Modus
SPI2->CR1 |= SPI_CR1_BIDIOE; //Nur senden
SPI2->CR1 &= ~SPI_CR1_CRCEN; //CRC-Berechnung abschalten
SPI2->CR1 &= ~SPI_CR1_CRCNEXT; //Keine CRC-Phase
SPI2->CR1 &= ~SPI_CR1_DFF; //8-Bit Frameformat
SPI2->CR1 &= ~SPI_CR1_RXONLY; //Receive-Only-Modus abschalten
SPI2->CR1 &= ~SPI_CR1_SSM; //Software Slave Management abschalten
SPI2->CR1 &= ~SPI_CR1_SSI; //NSS-Pin
SPI2->CR1 &= ~SPI_CR1_LSBFIRST; //MSB zuerst übertragen
SPI2->CR1 &= ~SPI_CR1_BR_2 & ~SPI_CR1_BR_1 | SPI_CR1_BR_0; //Taktteiler 4 -> 16MHz/4 -> 4MHz
SPI2->CR1 |= SPI_CR1_MSTR; //Master configuration
SPI2->CR1 |= SPI_CR1_CPOL; //Takt=1 im Leerlauf
SPI2->CR1 &= ~SPI_CR1_CPHA; //Clockphase, first clock transistion is first data capture edge
SPI2->CR1 |= SPI_CR1_SPE; //SPI aktivieren
/*
SPI2->CR2 |= SPI_CR2_TXEIE; //Tx-buffer empty interupt aktivieren
SPI2->CR2 &= ~SPI_CR2_RXNEIE; //Rx-buffer not empty interrupt deaktivieren
SPI2->CR2 &= ~SPI_CR2_ERRIE; //Error Interrupt deaktivieren
SPI2->CR2 &= ~SPI_CR2_FRF; //Motorola-Modus (Sandard)
SPI2->CR2 &= ~SPI_CR2_SSOE; //SS Output deaktivieren
SPI2->CR2 &= ~SPI_CR2_TXDMAEN; //TXE DMA-Request deaktivieren
SPI2->CR2 &= ~SPI_CR2_RXDMAEN; //RXNE DMA Request deaktivieren*/
}

//UART initialisieren
void UARTInit(){
//Taktversorgung aktiviern
RCC->APB1ENR |= RCC_APB1ENR_USART3EN; //UART3 mit Takt versorgen
RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; //PortD mit Takt versorgen

//Pins konfigurieren
//PD9 -> RX
GPIOD->MODER &= ~GPIO_MODER_MODER9_0;
GPIOD->MODER |= GPIO_MODER_MODER9_1; //PD9 -> Alternate Function MODER ->10
GPIOD->OTYPER &= ~GPIO_OTYPER_OT_9; //PD9 als Push-Pull-Stufe
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_0;
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9_1; //PD9 High-Speed
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR9_0;
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR9_1; //Pull-up/-down-Widerstände abschalten
GPIOD->AFR[1] |= (0b0111<<4); //UART
//PD8 -> TX
GPIOD->MODER &= ~GPIO_MODER_MODER8_0;
GPIOD->MODER |= GPIO_MODER_MODER8_1; //PD8 -> Alternate Function MODER ->10
GPIOD->OTYPER &= ~GPIO_OTYPER_OT_8; //PD8 als Push-Pull-Stufe
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_0;
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_1; //PD8 High-Speed
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR8_0;
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR8_1; //Pull-up/-down-Widerstände abschalten
GPIOD->AFR[1] |= (0b0111<<0); //UART
//PD11 -> CTS
GPIOD->MODER &= ~GPIO_MODER_MODER11_0;
GPIOD->MODER |= GPIO_MODER_MODER11_1; //PD11 -> Alternate Function MODER ->10
GPIOD->OTYPER &= ~GPIO_OTYPER_OT_11; //PD11 als Push-Pull-Stufe
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR11_0;
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR11_1; //PD11 High-Speed
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR11_0;
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR11_1; //Pull-up/-down-Widerstände abschalten
GPIOD->AFR[1] |= (0b0111<<16); //UART
//PD12 -> RTS
GPIOD->MODER &= ~GPIO_MODER_MODER12_0;
GPIOD->MODER |= GPIO_MODER_MODER12_1; //PD12 -> Alternate Function MODER ->10
GPIOD->OTYPER &= ~GPIO_OTYPER_OT_12; //PD12 als Push-Pull-Stufe
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR12_0;
GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR12_1; //PD12 High-Speed
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR12_0;
GPIOD->PUPDR &= ~GPIO_PUPDR_PUPDR12_1; //Pull-up/-down-Widerstände abschalten
GPIOD->AFR[1] |= (0b0111<<20); //UART

//UART konfigurieren
USART3->CR3 |= USART_CR3_CTSE; //CTS hardware flow control
USART3->CR3 |= USART_CR3_RTSE; //RTS hardware flow control

USART3->BRR |= 0b111110100000001; //Baudrate 16MHz/2000 => 8kHz
USART3->CR1 &= ~ USART_CR1_M; //8-Bit-Modus
USART3->CR1 &= ~USART_CR1_PCE; //Paritätskontrolle deaktivieren
//USART3->CR1 &= ~USART_CR1_PS; //Paritätsmodus
USART3->CR1 &= ~USART_CR1_PEIE; //PE-Interrupt
USART3->CR1 |= USART_CR1_RE; //Receiver Enable
USART3->CR1 |= USART_CR1_TE; //Transmitter Enable

}

void LCDInit(){
SndBfhlLCD(0b00111001);
SndBfhlLCD(0b00010101);
SndBfhlLCD(0b01010101);
SndBfhlLCD(0b01101110);

SndBfhlLCD(0b01110010);
SndBfhlLCD(0b00111000);
SndBfhlLCD(0b00001111);
SndBfhlLCD(0b00000001);
SndBfhlLCD(0b00000110);
printf("Init-Befehle an LCD gesendet\n");
}

//Sendet ein Befehlbyte ans LCD
void SndBfhlLCD(char byte){
SPI2->CR1 |= SPI_CR1_SPE; //SPI aktivieren
GPIOC->BSRR |= GPIO_BSRR_BR_2; //RS-Pin zurücksetzen
GPIOC->BSRR |= GPIO_BSRR_BR_3; //CS runterziehen

while (!(SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
SPI2->DR = byte; //Byte senden
while (!(SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
while (SPI2->SR & SPI_SR_BSY){ //Warten bis laufende Übertragung abgeschlossen ist
}
GPIOC->BSRR |= GPIO_BSRR_BS_3; //CS high
GPIOC->BSRR |= GPIO_BSRR_BS_2; //RS-Pin high
SPI2->CR1 &= ~SPI_CR1_SPE; //SPI deaktivieren
//RS NICHT toggeln
}

//Sendet ein Datenbyte ans LCD
void SndDtnlLCD(char byte){
SPI2->CR1 |= SPI_CR1_SPE; //SPI aktivieren
GPIOC->BSRR |= GPIO_BSRR_BS_2; //RS-Pin setzen
GPIOC->BSRR |= GPIO_BSRR_BR_3; //CS runterziehen

while (!(SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
SPI2->DR = byte; //Byte senden
while (!(SPI2->SR & SPI_SR_TXE)){ //Warten bis Transmit-Register frei ist
}
while (SPI2->SR & SPI_SR_BSY){ //Warten bis laufende Übertragung abgeschlossen ist
}
GPIOC->BSRR |= GPIO_BSRR_BS_3; //CS high
SPI2->CR1 &= ~SPI_CR1_SPE; //SPI deaktivieren
GPIOC->BSRR |= GPIO_BSRR_BS_2; //RS toggeln
GPIOC->BSRR |= GPIO_BSRR_BR_2;
}

//SPI2 Interrupt
void SPI2_IRQHandler (void){
GPIOE->BSRR |= GPIO_BSRR_BR_4; //LED an E4 einschalten
printf("SPI2 Interrupt Event\n");
}

void main(void){
SystemInit();
TaktInit();
NVICInit();
EAInit();

GPIOE->BSRR |= GPIO_BSRR_BR_5; //LED an E5 einschalten

UARTInit();


SPIInit();
LCDInit();

while(1){
SndDtnlLCD('T');
SndDtnlLCD('e');
SndDtnlLCD('s');
SndDtnlLCD('t');
SndDtnlLCD(' ');
SndDtnlLCD('O');
SndDtnlLCD('K');

GPIOE->BSRR |= GPIO_BSRR_BR_1; //LED an E1 einschalten
GPIOE->BSRR |= GPIO_BSRR_BS_1; //LED an E1 ausschalten
}
}