SPI Chipselect won't work
Posted: Wed Jun 19, 2019 11:23 am
Hi
I'm currently working on a little project that uses 4 LCDs (st7735 controller). They are connected as followed:
Mosi=4 , SCK=14 , A0 = 5 , CS1=2 , CS2= 26, CS3= 25, CS4= 32
Problem: Only CS1 (GPIO2) will work. All other CS stay low. I can't think of any reason why
This is my code. I also attached the code in a .zip below. Please help, this is driving me crazy
Code for Main.c . Only 3 Displays are used at the moment, so the second SPI is commented out.
Code of the LX177B.c Display Library
LX177B.h
I'm currently working on a little project that uses 4 LCDs (st7735 controller). They are connected as followed:
Mosi=4 , SCK=14 , A0 = 5 , CS1=2 , CS2= 26, CS3= 25, CS4= 32
Problem: Only CS1 (GPIO2) will work. All other CS stay low. I can't think of any reason why
This is my code. I also attached the code in a .zip below. Please help, this is driving me crazy
Code for Main.c . Only 3 Displays are used at the moment, so the second SPI is commented out.
Code: Select all
// TFT Test for LX177B Library
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_attr.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "LX177B.h"
#include "freertos/queue.h"
//LCD Grundvariablen
TickType_t xDelay30ms = pdMS_TO_TICKS(30);
//Struct für LCD
typedef struct
{
uint8_t Life[4];
uint8_t Mode;
uint8_t Modeval;
}LCD_Trans_t;
//Queue für LCD (4 Einträge a 6 bytes)
QueueHandle_t LCD_Queue_handler;
void lcd_task(void *pvParameter)
{
//SPI Handler
spi_device_handle_t Display_Handler[4];
const uint16_t Col_a = 0b1111101000110010;
const uint16_t Col_b = 0x291F;
const uint16_t Col_white = 0xFFFF;
const uint16_t Col_red = 0b1111100000000000;
const uint16_t Col_orange = 0b1111100110000000;
const uint16_t Col_blue = 0b0000001111100000;
//Display Data
LCD_Trans_t LCD_Data_REC;
//Display Pos
const uint8_t Life_y_Pos = 80;
const uint8_t Boost_y_Pos = 100;
//################ INIT #################/
// Hardware kann nur 3 CS bedienen, daher 2 SPI Busse für 4 Geräte :(
// Mosi=4 , SCK=14 , A0 = 5 , CS1=2 , CS2= 26, CS3= 25, CS4= 32
// Mosi=25 , SCK=27, CS4= 18
LX_Init_SPI(VSPI_HOST, 4, 14, 1);
//LX_Init_SPI(VSPI_HOST, 23, 24, 2);
LX_Create_Device(VSPI_HOST, &Display_Handler[0], 2, 5); //pink
LX_Create_Device(VSPI_HOST, &Display_Handler[1], 26, 5); //gelb
LX_Create_Device(VSPI_HOST, &Display_Handler[2], 25, 5); //grün
//LX_Create_Device(VSPI_HOST, &Display_Handler[3], 32, 5); //rot
LX_Init_Chip(&Display_Handler[0], BLACKTAB_128_160);
LX_Init_Chip(&Display_Handler[1], BLACKTAB_128_160);
LX_Init_Chip(&Display_Handler[2], BLACKTAB_128_160);
//LX_Init_Chip(&Display_Handler[3], BLACKTAB_128_160);
LX_set_orientation(&Display_Handler[0], ST7735_LANDSCAPE, BLACKTAB_128_160);
LX_set_orientation(&Display_Handler[1], ST7735_LANDSCAPE, BLACKTAB_128_160);
LX_set_orientation(&Display_Handler[2], ST7735_LANDSCAPE, BLACKTAB_128_160);
//LX_set_orientation(&Display_Handler[3], ST7735_LANDSCAPE, BLACKTAB_128_160);
while (1)
{
//Neue Daten abfragen
xQueueReceive(LCD_Queue_handler, &LCD_Data_REC, NULL);
for (uint8_t z = 0; z < 3; z++)
{
//LCD SRAM löschen
LX_delete_SRAM();
switch (LCD_Data_REC.Mode)
{
//Mode=1 -> Balkenanzeige
case 1:
LX_draw_rect(0, 0, 159, 30, Col_b, 2);
LX_draw_text(30, 20, "Boostmode", &FreeSans, 1, Col_a);
LX_draw_text(2, (Life_y_Pos + 12), "Leben", &TomThumb, 2, Col_white);
LX_draw_rect(51, Life_y_Pos, 159, (Life_y_Pos + 15), Col_white, 2);
for (int i = 1; i <= LCD_Data_REC.Life[z]; i++)
{
LX_fill_rect((56 + (i - 1)*(30 + 4)), (Life_y_Pos + 3), (56 + 30*i + 4*(i - 1)), (Life_y_Pos + 12), Col_red);
}
LX_draw_text(2, (Boost_y_Pos + 12), "Boost", &TomThumb, 2, Col_white);
LX_draw_rect(51, Boost_y_Pos, 159, (Boost_y_Pos + 15), Col_white, 2);
LX_fill_rect(56, (Boost_y_Pos + 3), (56 + LCD_Data_REC.Modeval), (Boost_y_Pos + 12), Col_blue);
break;
case 2:
LX_draw_rect(0, 0, 159, 30, Col_b, 2);
LX_draw_text(30, 20, "Deathmatch", &FreeSans, 1, Col_a);
LX_draw_text(2, (Life_y_Pos + 12), "Leben", &TomThumb, 2, Col_white);
LX_draw_rect(51, Life_y_Pos, 159, (Life_y_Pos + 15), Col_white, 2);
for (int i = 1; i <= LCD_Data_REC.Life[z]; i++)
{
LX_fill_rect((56 + (i - 1)*(30 + 4)), (Life_y_Pos + 3), (56 + 30*i + 4*(i - 1)), (Life_y_Pos + 12), Col_red);
}
LX_draw_text(2, (Boost_y_Pos + 12), "Speed", &TomThumb, 2, Col_white);
LX_draw_rect(51, Boost_y_Pos, 159, (Boost_y_Pos + 15), Col_white, 2);
LX_fill_rect(56, (Boost_y_Pos + 3), (56 + LCD_Data_REC.Modeval), (Boost_y_Pos + 12), (Col_orange + (LCD_Data_REC.Modeval*5)));
break;
default:
break;
}
//Aktualisieren der 4 Endgeräte
LX_start_full_refresh(&Display_Handler[z]);
vTaskDelay(xDelay30ms);
LX_stop_full_refresh(&Display_Handler[z]);
}
}
}
void Queue_task(void*pvParam)
{
LCD_Trans_t Test_Data =
{
.Mode = 0x01,
.Modeval = 50,
.Life[0] = 1,
.Life[1] = 2,
.Life[2] = 3,
.Life[3] = 0
};
while (1)
{
if (Test_Data.Modeval < 100)
{
Test_Data.Modeval++;
}
else
{
Test_Data.Modeval = 0;
}
xQueueSend(LCD_Queue_handler, (void*)&Test_Data, NULL);
vTaskDelay(xDelay30ms);
}
}
void app_main()
{
//Queue
LCD_Queue_handler = xQueueCreate(6, sizeof(LCD_Trans_t));
//Task erstellen
xTaskCreate(lcd_task, "lcd_task", 2048, NULL, 5, NULL);
xTaskCreate(Queue_task, "Queue_task", 2048, NULL, 5, NULL);
}
Code: Select all
#include "LX177B.h"
//Globale Variablen
LCD_Data_t *LX_SRAM;
spi_transaction_t LX_DATA;
gpio_num_t A0;
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++ Hardware Defines ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//Device auf dem SPI Bus erstellen
void LX_Create_Device(spi_host_device_t SPI, spi_device_handle_t *SPI_Handler, gpio_num_t NSEL, gpio_num_t A0_Pin)
{
//Pin einstellen
A0 = A0_Pin;
gpio_pad_select_gpio(A0_Pin);
gpio_set_direction(A0_Pin, GPIO_MODE_OUTPUT);
//Config des LCD in den Struct eintragen
//CPHA=0 CPOL=0
//MSb first
//CLK mit 50% Dutycycle
//CS geht 3 Cycle vor und nach Transaktion auf High
spi_device_interface_config_t LX_Config;
memset(&LX_Config, 0, sizeof(LX_Config));
LX_Config.address_bits = 0;
LX_Config.clock_speed_hz = SPI_MASTER_FREQ_16M; //16,6Mhz theoretisch ok
LX_Config.spics_io_num = NSEL;
LX_Config.queue_size = 4;
LX_Config.mode = 0;
LX_Config.cs_ena_pretrans = 2;
LX_Config.flags = SPI_DEVICE_HALFDUPLEX;
//Dem zuvor initialisierten Bus ein Gerät hinzufügen
ESP_ERROR_CHECK(spi_bus_add_device(SPI, &LX_Config, SPI_Handler));
ESP_LOGI(LX_TAG,"GLCD: SPI Device created, CS= %d,A0=%d",NSEL,A0_Pin);
}
//SPI Init für das LX177B LCD
void LX_Init_SPI(spi_host_device_t SPI_Channel, gpio_num_t Mosi, gpio_num_t Clk, int DMA_Channel)
{
//SPI Pins definieren
spi_bus_config_t LX_bus_cfg =
{
.miso_io_num = -1,
.mosi_io_num = Mosi,
.sclk_io_num = Clk,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 128*160*2
};
//Init des SPI BUS selbst
ESP_ERROR_CHECK(spi_bus_initialize(SPI_Channel, &LX_bus_cfg, DMA_Channel));
ESP_LOGI(LX_TAG, "GLCD: SPI initialized");
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++ Interne Routinen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//LX Command schreiben
void inline LX_Write_Command(spi_device_handle_t *SPI_Handler, uint8_t *Data,uint8_t Bytes_to_send)
{
//Benutzt den Buffer und macht keinen Read (wichtig für HalfDuplex)
gpio_set_level(A0, 0);
spi_transaction_t LX_dout;
memset(&LX_dout, 0, sizeof(LX_dout));
LX_dout.tx_buffer = Data;
LX_dout.length = 8*Bytes_to_send;
ESP_ERROR_CHECK(spi_device_polling_start(*SPI_Handler, &LX_dout, portMAX_DELAY));
ESP_ERROR_CHECK(spi_device_polling_end(*SPI_Handler, portMAX_DELAY));
}
//LX Parameter schreiben
void inline LX_Write_Parameter(spi_device_handle_t *SPI_Handler, uint8_t *Data, uint8_t Bytes_to_send)
{
//Benutzt den Buffer und macht keinen Read (wichtig für HalfDuplex)
gpio_set_level(A0, 1);
spi_transaction_t LX_dout;
memset(&LX_dout, 0, sizeof(LX_dout));
LX_dout.tx_buffer = Data;
LX_dout.length = 8*Bytes_to_send;
ESP_ERROR_CHECK(spi_device_polling_start(*SPI_Handler, &LX_dout, portMAX_DELAY));
ESP_ERROR_CHECK(spi_device_polling_end(*SPI_Handler, portMAX_DELAY));
}
//°°°°°° Write Commandset °°°°°°°
void ST7735_write_Commandset(spi_device_handle_t *SPI_Handler, const uint8_t *Adress)
{
uint8_t i = 1;
uint8_t Pause = 0;
uint8_t Pausetime = 0;
CMD_t Dout;
uint8_t Commands_to_send;
uint8_t Bytes_to_send;
//Anzahl der zu sendenden Befehle extrahieren
Commands_to_send = *Adress;
//Befehle nacheinander abarbeiten
for(uint8_t ct = 0 ; ct < Commands_to_send ; ct++)
{
//Startwerte
Pause = 0;
Bytes_to_send = 0;
//CMD extrahieren
Dout.reg = *(Adress + i);
i++;
//Anzahl Bytes extrahieren
Bytes_to_send = *(Adress + i);
i++;
//Pause vorhanden, Pausenindikator entfernen
if(Bytes_to_send >= DELAY_FLAG)
{
Bytes_to_send = Bytes_to_send - DELAY_FLAG;
Pause = 1;
}
//Nutzdaten auslesen
for(uint8_t z = 0 ; z < Bytes_to_send ; z++)
{
Dout.data[z] = *(Adress + i);
i++;
}
//Senden
LX_Write_Command(SPI_Handler, &Dout.reg, (1));
if (Bytes_to_send > 0)
{
LX_Write_Parameter(SPI_Handler, &(Dout.data[0]), Bytes_to_send);
}
//Pause auslesen
if(Pause > 0)
{
Pausetime = *(Adress + i);
i++;
vTaskDelay(Pausetime / portTICK_PERIOD_MS);
}
}
}
//°°°°°° RAM Bereich festlegen °°°°°°°
void ST7735_window_set(spi_device_handle_t *SPI_Handler ,uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
uint8_t Command[5];
Command[0] = ST7735_CASET;
LX_Write_Command(SPI_Handler, &Command[0], 1);
Command[1] = 0x00;
Command[2] = x1;
Command[3] = 0;
Command[4] = x2;
LX_Write_Parameter(SPI_Handler, &Command[1], 4);
Command[0] = ST7735_RASET;
LX_Write_Command(SPI_Handler, &Command[0], 1);
Command[1] = 0x00;
Command[2] = y1;
Command[3] = 0x00;
Command[4] = y2;
LX_Write_Parameter(SPI_Handler, &Command[1], 4);
Command[0] = ST7735_RAMWR;
LX_Write_Command(SPI_Handler, &Command[0], 1);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++ Benutzerroutinen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//°°°°°°° LCD Init °°°°°°°
void LX_Init_Chip(spi_device_handle_t *SPI_Handler, enum ST7735_DISPLAY_TYPE Colour_of_tab)
{
uint8_t Val;
//Init des Displaycontrollers
switch (Colour_of_tab)
{
case BLUETAB_128_160:
ST7735_write_Commandset(SPI_Handler,st7735_blue_init);
break;
case GREENTAB_128_160:
ST7735_write_Commandset(SPI_Handler,st7735_red_init1);
ST7735_write_Commandset(SPI_Handler,st7735_red_init_green2);
ST7735_write_Commandset(SPI_Handler,st7735_red_init3);
break;
case REDTAB_128_160:
ST7735_write_Commandset(SPI_Handler,st7735_red_init1);
ST7735_write_Commandset(SPI_Handler,st7735_red_init_red2);
ST7735_write_Commandset(SPI_Handler,st7735_red_init3);
break;
case BLACKTAB_128_160:
ST7735_write_Commandset(SPI_Handler,st7735_red_init1);
ST7735_write_Commandset(SPI_Handler,st7735_red_init_red2);
ST7735_write_Commandset(SPI_Handler,st7735_red_init3);
Val = ST7735_MADCTL;
LX_Write_Command(SPI_Handler, &Val, 1);
Val = 0xC0;
LX_Write_Parameter(SPI_Handler, &Val, 1);
break;
}
//Speicherplatz für LCD reservieren
LX_SRAM = heap_caps_malloc(sizeof(LCD_Data_t), MALLOC_CAP_DMA);
assert(LX_SRAM != NULL);
}
//°°°°°°° LCD Ausrichtung °°°°°°°
void LX_set_orientation(spi_device_handle_t *SPI_Handler, enum ST7735_ORIENTATION orientation, enum ST7735_DISPLAY_TYPE Colour_of_tab)
{
uint8_t Val;
Val = ST7735_MADCTL;
LX_Write_Command(SPI_Handler,&Val,1);
switch (orientation)
{
case ST7735_PORTRAIT:
if (Colour_of_tab == BLACKTAB_128_160)
{
Val = MADCTL_MX | MADCTL_MY | MADCTL_RGB;
}
else
{
Val =MADCTL_MX | MADCTL_MY | MADCTL_BGR;
}
break;
case ST7735_LANDSCAPE:
if (Colour_of_tab == BLACKTAB_128_160)
{
Val =MADCTL_MY | MADCTL_MV | MADCTL_RGB;
}
else
{
Val =MADCTL_MY | MADCTL_MV | MADCTL_BGR;
}
break;
case ST7735_PORTRAIT_INV:
if (Colour_of_tab == BLACKTAB_128_160)
{
Val =MADCTL_RGB;
}
else
{
Val =MADCTL_BGR;
}
break;
case ST7735_LANDSCAPE_INV:
if (Colour_of_tab == BLACKTAB_128_160)
{
Val = MADCTL_MX | MADCTL_MV | MADCTL_RGB;
}
else
{
Val = MADCTL_MX | MADCTL_MV | MADCTL_BGR;
}
break;
}
LX_Write_Parameter(SPI_Handler,&Val, 1);
}
//°°°°°°° Rechteck zeichnen °°°°°°°
void LX_fill_rect(uint8_t x1, uint8_t y1,uint8_t x2,uint8_t y2,uint16_t Colour)
{
if ((x1 > x2) | (x2 > 159) | (x1 > 159))
{
ESP_LOGI(LX_TAG, "x-Coordinates corrupt");
return;
}
if ((y1 > y2) | (y2 > 127) | (y1 > 127))
{
ESP_LOGI(LX_TAG, "y-Coordinates corrupt");
return;
}
//Dots setzen
for (uint32_t x = x1; x <= x2; x++)
{
for (uint32_t y = y1; y <= y2; y++)
{
(LX_SRAM->Dot[y][x])=Colour;
}
}
}
//°°°°°°° Kasten zeichnen °°°°°°°
void LX_draw_rect( uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t Colour,uint8_t Line_width)
{
if ((x1 > x2) | (x2 > 159) | (x1 > 159))
{
ESP_LOGI(LX_TAG, "x-Coordinates corrupt");
return;
}
if ((y1 > y2) | (y2 > 127) | (y1 > 127))
{
ESP_LOGI(LX_TAG, "y-Coordinates corrupt");
return;
}
//Dots setzen linie oben
for(uint32_t x = x1 ; x <= x2 ; x++)
{
for (uint8_t L = 0; L < Line_width; L++)
{
(LX_SRAM->Dot[y1+L][x]) = Colour;
}
}
//Dots setzen linie unten
for (uint32_t x = x1; x <= x2; x++)
{
for (uint8_t L = 0; L < Line_width; L++)
{
(LX_SRAM->Dot[y2 - L][x])= Colour;
}
}
//Dots setzen linie senkrecht
for (uint32_t y = y1; y <= y2; y++)
{
for (uint8_t L = 0; L < Line_width; L++)
{
(LX_SRAM->Dot[y][x1+L]) = Colour;
}
}
//Dots setzen linie senkrecht
for(uint32_t y = y1 ; y <= y2 ; y++)
{
for (uint8_t L = 0; L < Line_width; L++)
{
(LX_SRAM->Dot[y][x2 - L]) = Colour;
}
}
}
//°°°°°°° VLINE zeichnen °°°°°°°
void LX_draw_vline( uint8_t x1, uint8_t x2, uint8_t y, uint16_t Colour, uint8_t Line_width)
{
if ((x1 > x2) | (x2 > 159) | (x1 > 159))
{
ESP_LOGI(LX_TAG, "x-Coordinates corrupt");
return;
}
if (y > 127)
{
ESP_LOGI(LX_TAG, "y-Coordinate corrupt");
return;
}
//Dots setzen linie oben
for(uint32_t x = x1 ; x <= x2 ; x++)
{
for (uint8_t L = 0; L < Line_width; L++)
{
(LX_SRAM->Dot[y + L][x]) = Colour;
}
}
}
//°°°°°°° HLINE zeichnen °°°°°°°
void LX_draw_hline( uint8_t y1, uint8_t y2, uint8_t x, uint16_t Colour, uint8_t Line_width)
{
if ((y1 > y2) | (y2 > 127) | (y1 > 127))
{
ESP_LOGI(LX_TAG, "y-Coordinates corrupt");
return;
}
if (x > 159)
{
ESP_LOGI(LX_TAG, "x-Coordinate corrupt");
return;
}
//Dots setzen linie oben
for(uint32_t y = y1 ; y <= y2 ; y++)
{
for (uint8_t L = 0; L < Line_width; L++)
{
(LX_SRAM->Dot[y][x+L]) = Colour;
}
}
}
//°°°°°°° CHAR zeichnen °°°°°°°
void LX_draw_char(int16_t x,int16_t y,const GFXglyph *glyph,const GFXfont *font,uint8_t size,uint16_t color)
{
if(size < 1)
{
ESP_LOGI(LX_TAG, "Char Size <1");
return;
}
uint8_t *bitmap = font->bitmap;
uint16_t bo = glyph->bitmapOffset;
uint8_t bits = 0, bit = 0;
uint16_t set_pixels = 0;
uint8_t cur_x, cur_y;
uint8_t xs, ys;
for(cur_y = 0; cur_y < glyph->height; cur_y++)
{
for(cur_x = 0; cur_x < glyph->width; cur_x++)
{
//Neues Byte für das zeichen lesen
if(bit == 0)
{
bits =*(bitmap+bo);
bo++;
bit = 0x80;
}
if(bits & bit)
{
set_pixels++;
}
else if (set_pixels > 0)
{
xs = (x + (glyph->xOffset + cur_x - set_pixels) * size);
ys = (y + (glyph->yOffset + cur_y) * size);
LX_fill_rect(xs, ys,xs+(size * set_pixels), (ys +size), color);
set_pixels=0;
}
bit >>= 1;
}
// Draw rest of line
if (set_pixels > 0)
{
xs = (x + (glyph->xOffset + cur_x - set_pixels) * size);
ys = (y + (glyph->yOffset + cur_y) * size);
LX_fill_rect(xs,ys,(xs +size * set_pixels),(ys +size),color);
set_pixels=0;
}
}
}
//°°°°°°° TEXT zeichnen °°°°°°°
void LX_draw_text(int8_t x, int8_t y,char *text,const GFXfont *p_font,uint8_t size,uint16_t color)
{
int32_t cursor_x = x;
int32_t cursor_y = y;
GFXfont font;
memcpy(&font, p_font, sizeof(GFXfont));
for (uint32_t text_pos = 0; text_pos < strlen(text); text_pos++) {
char c = text[text_pos];
if (c == '\n') {
cursor_x = x;
cursor_y += font.yAdvance * size;
}
else if (c >= font.first && c <= font.last && c != '\r') {
GFXglyph glyph;
memcpy(&glyph, &font.glyph[c - font.first], sizeof(GFXglyph));
LX_draw_char(cursor_x, cursor_y, &glyph, &font, size, color);
cursor_x += glyph.xAdvance * size;
}
}
}
//°°°°°°° SRAM löschen °°°°°°°
void LX_delete_SRAM()
{
for (int i = 0; i < (128 * 160); i++)
{
LX_SRAM->Data[i] = 0x00;
}
}
//°°°°°°° LCD aktualisieren °°°°°°°
void LX_start_full_refresh(spi_device_handle_t *SPI_Handler)
{
//Kompletten Speicher beschreiben
ST7735_window_set(SPI_Handler, 0, 0, 159, 127);
//Spi Struct
memset(&LX_DATA, 0, sizeof(LX_DATA));
LX_DATA.length = 128*160*16;
LX_DATA.rxlength = 0;
LX_DATA.tx_buffer = LX_SRAM;
gpio_set_level(A0, 1);
ESP_ERROR_CHECK(spi_device_queue_trans(*SPI_Handler, &LX_DATA, portMAX_DELAY));
}
void LX_stop_full_refresh(spi_device_handle_t *SPI_Handler)
{
spi_transaction_t *LX_DATA_RES;
ESP_ERROR_CHECK(spi_device_get_trans_result(*SPI_Handler, &LX_DATA_RES, portMAX_DELAY));
assert(LX_DATA_RES == &LX_DATA);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++ Datensätze ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
const uint8_t st7735_blue_init[] =
{ // Initialization commands for 7735B screens
18, // 18 commands in list:
ST7735_SWRESET, DELAY_FLAG, // 1: Software reset, no args, w/delay
50, // 50 ms delay
ST7735_SLPOUT, DELAY_FLAG, // 2: Out of sleep mode, no args, w/delay
255, // 255 = 500 ms delay
ST7735_COLMOD, 1 | DELAY_FLAG, // 3: Set color mode, 1 arg + delay:
0x05, // 16-bit color
10, // 10 ms delay
ST7735_FRMCTR1, 3 | DELAY_FLAG, // 4: Frame rate control, 3 args + delay:
0x00, // fastest refresh
0x06, // 6 lines front porch
0x03, // 3 lines back porch
10, // 10 ms delay
ST7735_MADCTL, 1, // 5: Memory access ctrl (directions), 1 arg:
0x08, // Row addr/col addr, bottom to top refresh
ST7735_DISSET5, 2, // 6: Display settings #5, 2 args, no delay:
0x15, // 1 clk cycle nonoverlap, 2 cycle gate
// rise, 3 cycle osc equalize
0x02, // Fix on VTL
ST7735_INVCTR, 1, // 7: Display inversion control, 1 arg:
0x0, // Line inversion
ST7735_PWCTR1, 2 | DELAY_FLAG, // 8: Power control, 2 args + delay:
0x02, // GVDD = 4.7V
0x70, // 1.0uA
10, // 10 ms delay
ST7735_PWCTR2, 1, // 9: Power control, 1 arg, no delay:
0x05, // VGH = 14.7V, VGL = -7.35V
ST7735_PWCTR3, 2, // 10: Power control, 2 args, no delay:
0x01, // Opamp current small
0x02, // Boost frequency
ST7735_VMCTR1, 2 | DELAY_FLAG, // 11: Power control, 2 args + delay:
0x3C, // VCOMH = 4V
0x38, // VCOML = -1.1V
10, // 10 ms delay
ST7735_PWCTR6, 2, // 12: Power control, 2 args, no delay:
0x11, 0x15,
ST7735_GMCTRP1, 16, // 13: Magical unicorn dust, 16 args, no delay:
0x09, 0x16, 0x09, 0x20, // (seriously though, not sure what
0x21, 0x1B, 0x13, 0x19, // these config values represent)
0x17, 0x15, 0x1E, 0x2B,
0x04, 0x05, 0x02, 0x0E,
ST7735_GMCTRN1, 16 | DELAY_FLAG,// 14: Sparkles and rainbows, 16 args + delay:
0x0B, 0x14, 0x08, 0x1E, // (ditto)
0x22, 0x1D, 0x18, 0x1E,
0x1B, 0x1A, 0x24, 0x2B,
0x06, 0x06, 0x02, 0x0F,
10, // 10 ms delay
ST7735_CASET, 4, // 15: Column addr set, 4 args, no delay:
0x00, 0x02, // XSTART = 2
0x00, 0x81, // XEND = 129
ST7735_RASET, 4, // 16: Row addr set, 4 args, no delay:
0x00, 0x02, // XSTART = 1
0x00, 0x81, // XEND = 160
ST7735_NORON, DELAY_FLAG, // 17: Normal display on, no args, w/delay
10, // 10 ms delay
ST7735_DISPON, DELAY_FLAG, // 18: Main screen turn on, no args, w/delay
255 // 255 = 500 ms delay
};
const uint8_t st7735_red_init1[] = { // Init for 7735R, part 1 (red or green tab)
15, // 15 commands in list:
ST7735_SWRESET, DELAY_FLAG, // 1: Software reset, 0 args, w/delay
150, // 150 ms delay
ST7735_SLPOUT, DELAY_FLAG, // 2: Out of sleep mode, 0 args, w/delay
255, // 500 ms delay
ST7735_FRMCTR1, 3, // 3: Frame rate ctrl - normal mode, 3 args:
0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
ST7735_FRMCTR2, 3, // 4: Frame rate control - idle mode, 3 args:
0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
ST7735_FRMCTR3, 6, // 5: Frame rate ctrl - partial mode, 6 args:
0x01, 0x2C, 0x2D, // Dot inversion mode
0x01, 0x2C, 0x2D, // Line inversion mode
ST7735_INVCTR, 1, // 6: Display inversion ctrl, 1 arg, no delay:
0x07, // No inversion
ST7735_PWCTR1, 3, // 7: Power control, 3 args, no delay:
0xA2,
0x02, // -4.6V
0x84, // AUTO mode
ST7735_PWCTR2, 1, // 8: Power control, 1 arg, no delay:
0xC5, // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
ST7735_PWCTR3, 2, // 9: Power control, 2 args, no delay:
0x0A, // Opamp current small
0x00, // Boost frequency
ST7735_PWCTR4, 2, // 10: Power control, 2 args, no delay:
0x8A, // BCLK/2, Opamp current small & Medium low
0x2A,
ST7735_PWCTR5, 2, // 11: Power control, 2 args, no delay:
0x8A, 0xEE,
ST7735_VMCTR1, 1, // 12: Power control, 1 arg, no delay:
0x0E,
ST7735_INVOFF, 0, // 13: Don't invert display, no args, no delay
ST7735_MADCTL, 1, // 14: Memory access control (directions), 1 arg:
0xC8, // row addr/col addr, bottom to top refresh
ST7735_COLMOD, 1, // 15: set color mode, 1 arg, no delay:
0x05 // 16-bit color
};
const uint8_t st7735_red_init_green2[] = { // Init for 7735R, part 2 (green tab only)
2, // 2 commands in list:
ST7735_CASET, 4, // 1: Column addr set, 4 args, no delay:
0x00, 0x02, // XSTART = 0
0x00, 0x7F+0x02, // XEND = 127
ST7735_RASET, 4, // 2: Row addr set, 4 args, no delay:
0x00, 0x01, // XSTART = 0
0x00, 0x9F+0x01 // XEND = 159
};
const uint8_t st7735_red_init_red2[] = { // Init for 7735R, part 2 (red tab only)
2, // 2 commands in list:
ST7735_CASET, 4, // 1: Column addr set, 4 args, no delay:
0x00, 0x00, // XSTART = 0
0x00, 0x7F, // XEND = 127
ST7735_RASET, 4, // 2: Row addr set, 4 args, no delay:
0x00, 0x00, // XSTART = 0
0x00, 0x9F, // XEND = 159
};
const uint8_t st7735_red_init3[] = { // Init for 7735R, part 3 (red or green tab)
4, // 4 commands in list:
ST7735_GMCTRP1, 16, // 1: Magical unicorn dust, 16 args, no delay:
0x02, 0x1c, 0x07, 0x12,
0x37, 0x32, 0x29, 0x2d,
0x29, 0x25, 0x2B, 0x39,
0x00, 0x01, 0x03, 0x10,
ST7735_GMCTRN1, 16, // 2: Sparkles and rainbows, 16 args, no delay:
0x03, 0x1d, 0x07, 0x06,
0x2E, 0x2C, 0x29, 0x2D,
0x2E, 0x2E, 0x37, 0x3F,
0x00, 0x00, 0x02, 0x10,
ST7735_NORON, DELAY_FLAG, // 3: Normal display on, no args, w/delay
10, // 10 ms delay
ST7735_DISPON, DELAY_FLAG, // 4: Main screen turn on, no args w/delay
100 // 100 ms delay
};
Code: Select all
#ifndef LX177_H
#define LX177_H
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "LX_FONT.h"
//------------------------------------------------------------------------------------------------------
// Registerwerte und Definitionen
//------------------------------------------------------------------------------------------------------
#define ST7735_NOP 0x00
#define ST7735_SWRESET 0x01
#define ST7735_RDDID 0x04
#define ST7735_RDDST 0x09
#define ST7735_SLPIN 0x10
#define ST7735_SLPOUT 0x11
#define ST7735_PTLON 0x12
#define ST7735_NORON 0x13
#define ST7735_INVOFF 0x20
#define ST7735_INVON 0x21
#define ST7735_DISPOFF 0x28
#define ST7735_DISPON 0x29
#define ST7735_CASET 0x2A
#define ST7735_RASET 0x2B
#define ST7735_RAMWR 0x2C
#define ST7735_RAMRD 0x2E
#define ST7735_PTLAR 0x30
#define ST7735_COLMOD 0x3A
#define ST7735_MADCTL 0x36
#define ST7735_FRMCTR1 0xB1
#define ST7735_FRMCTR2 0xB2
#define ST7735_FRMCTR3 0xB3
#define ST7735_INVCTR 0xB4
#define ST7735_DISSET5 0xB6
#define ST7735_PWCTR1 0xC0
#define ST7735_PWCTR2 0xC1
#define ST7735_PWCTR3 0xC2
#define ST7735_PWCTR4 0xC3
#define ST7735_PWCTR5 0xC4
#define ST7735_VMCTR1 0xC5
#define ST7735_RDID1 0xDA
#define ST7735_RDID2 0xDB
#define ST7735_RDID3 0xDC
#define ST7735_RDID4 0xDD
#define ST7735_PWCTR6 0xFC
#define ST7735_GMCTRP1 0xE0
#define ST7735_GMCTRN1 0xE1
#define DELAY_FLAG 0x80
//Colour of Tab
enum ST7735_DISPLAY_TYPE {
BLUETAB_128_160,
GREENTAB_128_160,
REDTAB_128_160,
BLACKTAB_128_160,
};
//Orientation
enum ST7735_ORIENTATION {
ST7735_LANDSCAPE,
ST7735_PORTRAIT,
ST7735_LANDSCAPE_INV,
ST7735_PORTRAIT_INV
};
//Display Modes
enum ST7735_MADCTL_ARGS {
MADCTL_MY = 0x80, // Mirror Y
MADCTL_MX = 0x40, // Mirrror x
MADCTL_MV = 0x20, // Swap XY
MADCTL_ML = 0x10, // Scan address order
MADCTL_RGB = 0x00,
MADCTL_BGR = 0x08,
MADCTL_MH = 0x04 // Horizontal scan oder
};
//------------------------------------------------------------------------------------------------------
// Typendefinitionen
//------------------------------------------------------------------------------------------------------
//Typedefs
typedef struct __attribute__((__packed__))
{
uint8_t reg;
uint8_t data[16];
} CMD_t;
typedef union
{
uint16_t Dot[128][160];
uint16_t Data[20480];
}LCD_Data_t;
//------------------------------------------------------------------------------------------------------
// Globale Variablen
//------------------------------------------------------------------------------------------------------
static const char *LX_TAG = "GLCD:";
extern const uint8_t st7735_blue_init[];
extern const uint8_t st7735_red_init1[];
extern const uint8_t st7735_red_init_green2[];
extern const uint8_t st7735_red_init_red2[];
extern const uint8_t st7735_red_init3[];
extern spi_transaction_t LX_DATA;
extern LCD_Data_t *LX_SRAM;
//------------------------------------------------------------------------------------------------------
// InitFunktionen mit Polling
//------------------------------------------------------------------------------------------------------
/* SPI Init
* (*)Handler-> Pointer auf Handler
* SPI_Channel -> SPI Channel (VSPI oder HSPI)
* DMA_Channel -> (0=kein DMA, 1/2=DMA Channel 1/2)*/
void LX_Init_SPI(spi_host_device_t SPI_Channel, gpio_num_t Mosi, gpio_num_t Clk, int DMA_Channel);
/* Chip Init (internes Polling)*/
void LX_Init_Chip(spi_device_handle_t *SPI_Handler, enum ST7735_DISPLAY_TYPE Colour_of_tab);
/* Einzelnen Befehl mithilfe von Polling setzen*/
void LX_Write_Command(spi_device_handle_t *SPI_Handler, uint8_t *Data, uint8_t Bytes_to_send);
/* Device auf dem SPI Bus erstellen*/
void LX_Create_Device(spi_host_device_t SPI, spi_device_handle_t *SPI_Handler, gpio_num_t NSEL, gpio_num_t A0_Pin);
void ST7735_write_Commandset(spi_device_handle_t *SPI_Handler, const uint8_t *Adress);
void LX_set_orientation(spi_device_handle_t *SPI_Handler, enum ST7735_ORIENTATION orientation, enum ST7735_DISPLAY_TYPE Colour_of_tab);
void ST7735_window_set(spi_device_handle_t *SPI_Handler, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
//------------------------------------------------------------------------------------------------------
// SRAM Manipulationen
//------------------------------------------------------------------------------------------------------
void LX_delete_SRAM();
void LX_draw_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t Colour, uint8_t Line_width);
void LX_draw_hline(uint8_t y1, uint8_t y2, uint8_t x, uint16_t Colour, uint8_t Line_width);
void LX_draw_vline(uint8_t x1, uint8_t x2, uint8_t y, uint16_t Colour, uint8_t Line_width);
void LX_draw_char(int16_t x, int16_t y, const GFXglyph *glyph, const GFXfont *font, uint8_t size, uint16_t color);
void LX_draw_text(int8_t x, int8_t y, char *text, const GFXfont *p_font, uint8_t size, uint16_t color);
void LX_fill_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t Colour);
//------------------------------------------------------------------------------------------------------
// Sendefunktionen mit Interrupt
//------------------------------------------------------------------------------------------------------
/*Daten aus der Arbeitsvariable an das Display senden. Die Daten werden in die Queue gelegt
*und müssen später unbedingt mit der stop_refresh-Funktion abgeholt werden*/
void LX_start_full_refresh(spi_device_handle_t *SPI_Handler);
/*Die Queue abfragen. Sollte der falsche Queue-Eintrag erwischt werden gibt es einen Assert-Fehler
*Folglich stürzt der Controller ab bei einem solchen Fehler*/
void LX_stop_full_refresh(spi_device_handle_t *SPI_Handler);
#endif