King John Posted June 7, 2018 Report Share Posted June 7, 2018 Hello friends, I'm trying to use multiple displays (one SSD1963 and two ILIILI9341 based screens). I followed the steps in the wiki and am running into the following error: Linking build/ch.elf build/obj/gdisp_lld_ILI9341.o (symbol from plugin): In function `GDISPVMT_OnlyOne': (.text+0x0): multiple definition of `GDISPVMT_OnlyOne' build/obj/gdisp_lld_SSD1963.o (symbol from plugin):(.text+0x0): first defined here collect2.exe: error: ld returned 1 exit status make: *** [build/ch.elf] Error 1 I looked through the source and found that this is because GDISP_DRIVER_LIST isn't defined. I define that, but a rule prevents me from compiling: GDISP Multiple Drivers: You can't specify both GDISP_TOTAL_DISPLAYS and GDISP_DRIVER_LIST. Am I setting multiple displays up wrong? Link to comment Share on other sites More sharing options...
inmarket Posted June 7, 2018 Report Share Posted June 7, 2018 Simply remove the GDISP_TOTAL_DISPLAYS setting from your gfxconf.h. That setting is designed for when you have multiple displays all using the one controller type. Link to comment Share on other sites More sharing options...
King John Posted June 7, 2018 Author Report Share Posted June 7, 2018 (edited) I was able to get this working. Here's some info for those who need it. You only specify GDISP_DRIVER_LIST, contrary to what the Wiki suggests. It must be outdated. Example: #define GDISP_DRIVER_LIST GDISPVMT_SSD1963, GDISPVMT_ILI9341, GDISPVMT_ILI9341 You can then write your board file accordingly. In my example, I have SSD1963 as 0, ILI9341 as 1 and 2. You can find a hacked up (but working) example for an STM32F4 below. #ifndef _GDISP_LLD_BOARD_H #define _GDISP_LLD_BOARD_H //**** ILI9341 on SPI1. TESTED on STM32L1, STM32F1 AND STM32F4. // Pin & SPI setup #define SPI_METHOD_IRQ 1 #define SPI_METHOD_POLLING 2 #define SPI_METHOD SPI_METHOD_POLLING // SPI1 #define SPI_DRIVER_1 (&SPID1) #define SPI_PORT_1 GPIOA #define SCK_PAD_1 5 #define MISO_PAD_1 6 #define MOSI_PAD_1 7 #define CS_PORT_1 GPIOA #define RESET_PORT_1 GPIOA #define DNC_PORT_1 GPIOA #define CS_PAD_1 2 #define DNC_PAD_1 4 #define RESET_PAD_1 3 #define SPI_DRIVER_2 (&SPID2) #define SPI_PORT_2 GPIOB #define SCK_PAD_2 13 #define MISO_PAD_2 14 #define MOSI_PAD_2 15 #define CS_PORT_2 GPIOB #define RESET_PORT_2 GPIOB #define DNC_PORT_2 GPIOB #define CS_PAD_2 5 #define DNC_PAD_2 6 #define RESET_PAD_2 7 // SPI setup ajust " SPI_BaudRatePrescaler_X" to set SPI speed. // Peripherial Clock 42MHz SPI2 SPI3 // Peripherial Clock 84MHz SPI1 SPI1 SPI2/3 #define SPI_BaudRatePrescaler_2 ((uint16_t)0x0000) // 42 MHz 21 MHZ #define SPI_BaudRatePrescaler_4 ((uint16_t)0x0008) // 21 MHz 10.5 MHz #define SPI_BaudRatePrescaler_8 ((uint16_t)0x0010) // 10.5 MHz 5.25 MHz #define SPI_BaudRatePrescaler_16 ((uint16_t)0x0018) // 5.25 MHz 2.626 MHz #define SPI_BaudRatePrescaler_32 ((uint16_t)0x0020) // 2.626 MHz 1.3125 MHz #define SPI_BaudRatePrescaler_64 ((uint16_t)0x0028) // 1.3125 MHz 656.25 KHz #define SPI_BaudRatePrescaler_128 ((uint16_t)0x0030) // 656.25 KHz 328.125 KHz #define SPI_BaudRatePrescaler_256 ((uint16_t)0x0038) // 328.125 KHz 164.06 KHz static SPIConfig spi_cfg1 = {NULL, CS_PORT_1, CS_PAD_1, SPI_BaudRatePrescaler_4 //AJUST SPEED HERE.. }; static SPIConfig spi_cfg2 = {NULL, CS_PORT_2, CS_PAD_2, SPI_BaudRatePrescaler_2 //AJUST SPEED HERE.. }; static inline void init_board(GDisplay *g) { (void)g; g->board = 0; switch (g->systemdisplay) { case 1: //Set up the pins.. palSetPadMode(CS_PORT_1, CS_PAD_1, PAL_MODE_OUTPUT_PUSHPULL); palSetPadMode(SPI_PORT_1, SCK_PAD_1, PAL_MODE_ALTERNATE(5)); palSetPadMode(SPI_PORT_1, MISO_PAD_1, PAL_MODE_ALTERNATE(5)); palSetPadMode(SPI_PORT_1, MOSI_PAD_1, PAL_MODE_ALTERNATE(5)); palSetPadMode(RESET_PORT_1, RESET_PAD_1, PAL_MODE_OUTPUT_PUSHPULL); palSetPadMode(DNC_PORT_1, DNC_PAD_1, PAL_MODE_OUTPUT_PUSHPULL); //Set pins. palSetPad(CS_PORT_1, CS_PAD_1); palSetPad(RESET_PORT_1, RESET_PAD_1); palClearPad(DNC_PORT_1, DNC_PAD_1); //Start SPI1 with our config. spiStart(SPI_DRIVER_1, &spi_cfg1); spiSelectI(SPI_DRIVER_1); /* Slave Select assertion. */ break; case 2: //Set up the pins.. palSetPadMode(CS_PORT_2, CS_PAD_2, PAL_MODE_OUTPUT_PUSHPULL); palSetPadMode(SPI_PORT_2, SCK_PAD_2, PAL_MODE_ALTERNATE(5)); palSetPadMode(SPI_PORT_2, MISO_PAD_2, PAL_MODE_ALTERNATE(5)); palSetPadMode(SPI_PORT_2, MOSI_PAD_2, PAL_MODE_ALTERNATE(5)); palSetPadMode(RESET_PORT_2, RESET_PAD_2, PAL_MODE_OUTPUT_PUSHPULL); palSetPadMode(DNC_PORT_2, DNC_PAD_2, PAL_MODE_OUTPUT_PUSHPULL); //Set pins. palSetPad(CS_PORT_2, CS_PAD_2); palSetPad(RESET_PORT_2, RESET_PAD_2); palClearPad(DNC_PORT_2, DNC_PAD_2); //Start SPI2 with our config. spiStart(SPI_DRIVER_2, &spi_cfg2); spiSelectI(SPI_DRIVER_2); /* Slave Select assertion. */ break; } } static inline void post_init_board(GDisplay *g) { (void)g; } static inline void setpin_reset(GDisplay *g, bool_t state) { (void)g; switch (g->systemdisplay) { case 1: palWritePad(RESET_PORT_1, RESET_PAD_1, !state); break; case 2: palWritePad(RESET_PORT_2, RESET_PAD_2, !state); break; } } static inline void set_backlight(GDisplay *g, uint8_t percent) { (void)g; (void)percent; } static inline void acquire_bus(GDisplay *g) { (void)g; switch (g->systemdisplay) { case 1: spiSelectI(SPI_DRIVER_1); break; case 2: spiSelectI(SPI_DRIVER_2); break; } } static inline void release_bus(GDisplay *g) { (void)g; switch (g->systemdisplay) { case 1: spiUnselectI(SPI_DRIVER_1); break; case 2: spiUnselectI(SPI_DRIVER_2); break; } } static inline void write_index(GDisplay *g, uint8_t index) { (void)g; switch (g->systemdisplay) { case 1: palClearPad(DNC_PORT_1, DNC_PAD_1); #if SPI_METHOD == SPI_METHOD_IRQ spiSend(SPI_DRIVER_1, 1, &index); #elif SPI_METHOD == SPI_METHOD_POLLING spiPolledExchange(SPI_DRIVER_1, index); #endif palSetPad(DNC_PORT_1, DNC_PAD_1); break; case 2: palClearPad(DNC_PORT_2, DNC_PAD_2); #if SPI_METHOD == SPI_METHOD_IRQ spiSend(SPI_DRIVER_2, 1, &index); #elif SPI_METHOD == SPI_METHOD_POLLING spiPolledExchange(SPI_DRIVER_2, index); #endif palSetPad(DNC_PORT_2, DNC_PAD_2); break; } } static inline void write_data(GDisplay *g, uint8_t data) { (void)g; switch (g->systemdisplay) { case 1: #if SPI_METHOD == SPI_METHOD_IRQ spiSend(SPI_DRIVER_1, 1, &data); #elif SPI_METHOD == SPI_METHOD_POLLING spiPolledExchange(SPI_DRIVER_1, data); #endif break; case 2: #if SPI_METHOD == SPI_METHOD_IRQ spiSend(SPI_DRIVER_2, 1, &data); #elif SPI_METHOD == SPI_METHOD_POLLING spiPolledExchange(SPI_DRIVER_2, data); #endif break; } } static inline void setreadmode(GDisplay *g) { (void)g; } static inline void setwritemode(GDisplay *g) { (void)g; } static inline uint16_t read_data(GDisplay *g) { (void)g; return 0; } #endif /* _GDISP_LLD_BOARD_H */ Edited June 7, 2018 by King John Link to comment Share on other sites More sharing options...
inmarket Posted June 8, 2018 Report Share Posted June 8, 2018 Another trick you can use is in the init_board routine set the g->board field to a structure that contains register pointers and pin definitions for that display. Each display has its own g->board field. In any other routine you can use that field to access the structure and thus not have to switch on every operation. Link to comment Share on other sites More sharing options...
King John Posted June 8, 2018 Author Report Share Posted June 8, 2018 That's awesome! I'll have to change it over to this scheme as it's likely faster. Thank you very much for your help! Link to comment Share on other sites More sharing options...
Joel Bodenmann Posted June 9, 2018 Report Share Posted June 9, 2018 Glad to hear that you got everything working Link to comment Share on other sites More sharing options...
King John Posted June 15, 2018 Author Report Share Posted June 15, 2018 I finally had some time to clean up that board file. Hopefully this helps someone out there in the future. #ifndef _GDISP_LLD_BOARD_H #define _GDISP_LLD_BOARD_H //**** ILI9341 on SPI1. TESTED on STM32L1, STM32F1 AND STM32F4. // Pin & SPI setup #define SPI_METHOD_IRQ 1 #define SPI_METHOD_POLLING 2 #define SPI_METHOD SPI_METHOD_POLLING // SPI setup ajust " SPI_BaudRatePrescaler_X" to set SPI speed. // Peripherial Clock 42MHz SPI2 SPI3 // Peripherial Clock 84MHz SPI1 SPI1 SPI2/3 #define SPI_BaudRatePrescaler_2 ((uint16_t)0x0000) // 42 MHz 21 MHZ #define SPI_BaudRatePrescaler_4 ((uint16_t)0x0008) // 21 MHz 10.5 MHz #define SPI_BaudRatePrescaler_8 ((uint16_t)0x0010) // 10.5 MHz 5.25 MHz #define SPI_BaudRatePrescaler_16 ((uint16_t)0x0018) // 5.25 MHz 2.626 MHz #define SPI_BaudRatePrescaler_32 ((uint16_t)0x0020) // 2.626 MHz 1.3125 MHz #define SPI_BaudRatePrescaler_64 ((uint16_t)0x0028) // 1.3125 MHz 656.25 KHz #define SPI_BaudRatePrescaler_128 ((uint16_t)0x0030) // 656.25 KHz 328.125 KHz #define SPI_BaudRatePrescaler_256 ((uint16_t)0x0038) // 328.125 KHz 164.06 KHz //macro for functions #define iliboard ((ILI9341Config*)g->board) typedef struct ILI9341Config { SPIDriver* spid; ioportid_t spiport; short sck; short miso; short mosi; ioportid_t csport; short cs; ioportid_t resetport; int reset; ioportid_t dncport; short dnc; SPIConfig spicfg; } ILI9341Config; static ILI9341Config s1conf = { .spid = &SPID1, .spiport = GPIOA, .sck = 5, .miso = 6, .mosi = 7, .csport = GPIOA, .cs = 2, .dncport = GPIOA, .dnc = 3, .resetport = GPIOA, .reset = 4, .spicfg = {NULL, GPIOA, 2, SPI_BaudRatePrescaler_4} }; static ILI9341Config s2conf = { .spid = &SPID2, .spiport = GPIOB, .sck = 13, .miso = 14, .mosi = 15, .csport = GPIOE, .cs = 0, .dncport = GPIOB, .dnc = 6, .resetport = GPIOB, .reset = 7, //same as GPIO and port of above CS .spicfg = {NULL, GPIOE, 0, SPI_BaudRatePrescaler_2} }; static inline void init_board(GDisplay *g) { (void)g; switch (g->systemdisplay) { case 1: g->board = &s1conf; break; case 2: g->board = &s2conf; break; } ILI9341Config* conf = g->board; //Set up the pins.. //Might want to setup a conditional here for if it's SPID3, as that's supposed to be alternate mode 6 palSetPadMode(conf->spiport, conf->sck, PAL_MODE_ALTERNATE(5)); palSetPadMode(conf->spiport, conf->miso, PAL_MODE_ALTERNATE(5)); palSetPadMode(conf->spiport, conf->mosi, PAL_MODE_ALTERNATE(5)); palSetPadMode(conf->csport, conf->cs, PAL_MODE_OUTPUT_PUSHPULL); palSetPadMode(conf->resetport, conf->reset, PAL_MODE_OUTPUT_PUSHPULL); palSetPadMode(conf->dncport, conf->dnc, PAL_MODE_OUTPUT_PUSHPULL); //Set pins. palSetPad(conf->csport, conf->cs); palSetPad(conf->resetport, conf->reset); palClearPad(conf->dncport, conf->dnc); //Start SPI driver with our config. spiStart(conf->spid, &(conf->spicfg)); spiSelectI(conf->spid); /* Slave Select assertion. */ } static inline void post_init_board(GDisplay *g) { (void)g; } static inline void setpin_reset(GDisplay *g, bool_t state) { (void)g; palWritePad(iliboard->resetport, iliboard->reset, !state); } static inline void set_backlight(GDisplay *g, uint8_t percent) { (void)g; (void)percent; } static inline void acquire_bus(GDisplay *g) { (void)g; spiSelectI(iliboard->spid); } static inline void release_bus(GDisplay *g) { (void)g; spiUnselectI(iliboard->spid); } static inline void write_index(GDisplay *g, uint8_t index) { (void)g; palClearPad(iliboard->dncport, iliboard->dnc); #if SPI_METHOD == SPI_METHOD_IRQ spiSend(iliboard->spid, 1, &index); #elif SPI_METHOD == SPI_METHOD_POLLING spiPolledExchange(iliboard->spid, index); #endif palSetPad(iliboard->dncport, iliboard->dnc); } static inline void write_data(GDisplay *g, uint8_t data) { (void)g; #if SPI_METHOD == SPI_METHOD_IRQ spiSend(iliboard->spid, 1, &data); #elif SPI_METHOD == SPI_METHOD_POLLING spiPolledExchange(iliboard->spid, data); #endif } static inline void setreadmode(GDisplay *g) { (void)g; } static inline void setwritemode(GDisplay *g) { (void)g; } static inline uint16_t read_data(GDisplay *g) { (void)g; return 0; } #endif /* _GDISP_LLD_BOARD_H */ Link to comment Share on other sites More sharing options...
inmarket Posted June 18, 2018 Report Share Posted June 18, 2018 Very nice! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now