-
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.
-
@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:
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);
Code:
/* 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.
Code:
//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
}
-
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:
Code:
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
-
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.)
Code:
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 :)
-
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:
Code:
/* 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();
-
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).
-
SPI1 mit SPL für einen 411RE, sollte aber Pin-Kompatibel mit dem 446 sein. Für CS muss noch ein Pin konfiguriert werden.
Code:
#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
-
Hi again,
die Zeile in der Initialisierung stimmt nicht
Code:
RCC->AHB1ENR |= RCC_APB1ENR_SPI2EN; //Takt für SPI2 aktivieren
sondern muss wohl so lauten
Code:
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN; //Takt für SPI2 aktivieren
Gruss botty
-
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.
-
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,
Code:
//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