Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interfacing TFT screen with STM32F446 using display bus interface

I'm trying to understand how to interface a TFT screen module with an STM32F4 chip on a custom PCB. Here is the module and its basic info.

To write commands and data to the screen, the ILI9481 driver on the screen module uses the Display Bus Interface (DBI), where data is sent over 8 or 16 bits through data wires.

Looking at library examples, I understand (and please correct me, if I am wrong), that in order to send a command of one byte, it simply sets the digital pins of the chip high or low, depending on the command. For example, command 0x2 in 8bit communication would be 00000010, where 0 would be the digital low on the chips GPIO pin and 1 would be digital high, meaning 1 of 8 wires are active (logical high). I Hope, I understand this correctly.

Now as I looked over examples, usually these digital pins are on the same GPIO port. And if I understand correctly, GPIO ports have a register, called BSRR, where you can manipulate the logical levels of the pins of the GPIO port. If the data pins are all on the same GPIO port, I assume this would work (from the example, where c is the command byte):

void STM32_TFT_8bit::write8(uint8_t c) {

  // BRR or BSRR avoid read, mask write cycle time
  // BSRR is 32 bits wide. 1's in the most significant 16 bits signify pins to reset (clear)
  // 1's in least significant 16 bits signify pins to set high. 0's mean 'do nothing'
  TFT_DATA->regs->BSRR = ((~c)<<16) | (c); //Set pins to the 8 bit number

  WR_STROBE;
}

However, on my PCB board, the data pins of the screen module are separated on different ports. So, my question is, how would I do the same thing, send a command while manipulating the logical levels? I assume, that I could write set/reset my pins one by one, depending on the command, but how would it look with the BSRR registers?

If my data pins are as follows:

  • D0 -> PC12
  • D1 -> PC11
  • D2 -> PC10
  • D4 -> PA12
  • D5 -> PA11
  • D6 -> PA10
  • D7 -> PA9

Would a command of 0x9D (0b10011101) through the registers would look something like this? :

   GPIOA->regs->BSRR = 0b0001101000000000; // A port: turn on PA9, PA11, PA12
   GPIOC->regs->BSRR = 0b0001010000000000; // C port: turn on PC10 and PC12
like image 911
kemdeveloper Avatar asked Nov 06 '22 00:11

kemdeveloper


1 Answers

how would it look with the BSRR registers?

A bitmask can be applied to the value that is written to the BSRR, for example like this:

/* set/reset selected GPIO output pins, ignore the rest */
static inline void _gpio_write(GPIO_TypeDef* GPIOx, uint16_t state, uint16_t mask)
{
    GPIOx->BSRR = ((uint32_t)(~state & mask) << 16) | (state & mask);
}

The data bits need to be rearranged before writing them to the GPIO output registers, for example like this:

#define BITS(w,b) (((w) & (1 << (b))) >> (b))

/* write a data/command byte to the data bus DB[7:0] of custom ILI9481 board
   used pin assignment: D0 -> PC12, D1 -> PC11, D2 -> PC10, (D3 -> PC1) (?)
                        D4 -> PA12, D5 -> PA11, D6 -> PA10, D7 -> PA9 */
static void _write_data_to_pins(uint8_t data)
{
    const uint16_t mask_c = 1<<12 | 1<<11 | 1<<10 | 1<<1; /* 0x1c02 */
    const uint16_t mask_a = 1<<12 | 1<<11 | 1<<10 | 1<<9; /* 0x1e00 */
    _gpio_write(GPIOC, (uint16_t)(BITS(data, 0) << 12 | BITS(data, 1) << 11 |
                                  BITS(data, 2) << 10 | BITS(data, 3) <<  1), mask_c);
    _gpio_write(GPIOA, (uint16_t)(BITS(data, 4) << 12 | BITS(data, 5) << 11 |
                                  BITS(data, 6) << 10 | BITS(data, 7) <<  9), mask_a);
}

Test:

/* just for testing: read the written data bits back and arrange them in a byte */
static uint8_t _read_data_from_pins(void)
{
    const uint32_t reg_c = GPIOC->ODR;
    const uint32_t reg_a = GPIOA->ODR;
    return (uint8_t)(BITS(reg_c, 12) << 0 | BITS(reg_c, 11) << 1 |
                     BITS(reg_c, 10) << 2 | BITS(reg_c,  1) << 3 |
                     BITS(reg_a, 12) << 4 | BITS(reg_a, 11) << 5 |
                     BITS(reg_a, 10) << 6 | BITS(reg_a,  9) << 7);
}

/* somewhere in main loop of test project */
{
    uint8_t d = 0xff;
    do {
        _write_data_to_pins(d);
        if (d != _read_data_from_pins()) {
            Error_Handler();
        }
    } while (d--);
}

(Note: Only 7 of the 8 data pins DB[7:0] were listed in the question, PC1 was assigned to data pin D3 here.)

(Note: Most of these bit-shifts can be easily optimized out by the compiler, use at least -O1 to get somewhat compact results with GCC.)


GPIOA->regs->BSRR = 0b0001101000000000; // A port: turn on PA9, PA11, PA12
GPIOC->regs->BSRR = 0b0001010000000000; // C port: turn on PC10 and PC12

These two code lines do what is stated in the comments. But they will leave all the other pins unchanged.

The resulting output will depend on the previous state of the output data register. - For the LOW data pins, the corresponding GPIO port bits in BSRR[31:16] need to be set to 1 in order to update all the 8-bit data bus lines at once.

To answer the actual question:
No, the output on the data bus will not be 0x9D (0b1001'1101) after writing the two quoted bit patterns to the two BSRR registers. - In my case, it would look like this (please correct me if I'm wrong):

/* write 0x9D (0b1001'1101) to the data bus
   used pin assignment: D0 -> PC12, D1 -> PC11, D2 -> PC10, (D3 -> PC1) (?)
                        D4 -> PA12, D5 -> PA11, D6 -> PA10, D7 -> PA9 */
GPIOC->BSRR = 0x8001402; /* = 0b00001000'00000000'00010100'00000010 */
GPIOA->BSRR = 0xc001200; /* = 0b00001100'00000000'00010010'00000000 */
like image 77
rel Avatar answered Dec 15 '22 19:12

rel