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?AC...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/resour...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:
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
}
Lesezeichen