Pull to refresh

Comments 21

Некоторое время назад подключил к значительно более медленному кристаллу более маленький LCD, имея разведённой на стороне LCD лишь 8-ми битную шину.
В итоге родилась маленькая конструкция-монстрик, которая работает аналогично Вашему примеру в конце, но… в общем, смотрите сами:

Пара функций
<p>void LCD_FillRect(short left, short top, short right, short bottom, int Color) {
int iterations = 0;
char R,G,B;
int Max;
LCD_SetBounds(left, top, right, bottom);
Max = (right+1-left) <em> (bottom+1-top);
R = Color & 0xFF;
G = Color >> 8 & 0xFF;
B = Color >> 16 & 0xFF;
if ((R == G) & (G == B)) {
WriteOneByteManyTimes(R, Max</em>3);
} else {
while (iterations < Max) {
WriteData(B);
WriteData(G);
WriteData(R);
iterations = iterations + 1;
};
};
};
void WriteOneByteManyTimes(uint8_t byte, int iterations) {
int i;
GPIOC->BSRR = (GPIO_BSRR_BR_9 | GPIO_BSRR_BS_7);
GPIOA->ODR = (byte);
for (i=0;i<iterations;i++){
GPIOC->BSRR = GPIO_BSRR_BR_8;
GPIOC->BSRR = GPIO_BSRR_BS_8;
};
GPIOC->BSRR = (GPIO_BSRR_BS_9| GPIO_BSRR_BS_8 | GPIO_BSRR_BS_7 | GPIO_BSRR_BS_6);
};



PS: Что за ужас стал с тэгом "source"?
Исправил:

Пара функций
void LCD_FillRect(short left, short top, short right, short bottom, int Color) {
    int iterations = 0;
    char R,G,B;
    int Max;
    LCD_SetBounds(left, top, right, bottom);
    Max = (right+1-left) * (bottom+1-top);
    R = Color & 0xFF;
    G = Color >> 8 & 0xFF;
    B = Color >> 16 & 0xFF;
    if ((R == G) & (G == B)) {
        WriteOneByteManyTimes(R, Max*3);
    } else {
        while (iterations < Max) {
            WriteData(B);
            WriteData(G);
            WriteData(R);
            iterations = iterations + 1;
        };
    };
};
void WriteOneByteManyTimes(uint8_t byte, int iterations) {
    int i;
    GPIOC->BSRR = (GPIO_BSRR_BR_9 | GPIO_BSRR_BS_7);
    GPIOA->ODR = (byte);
    for (i=0;i<iterations;i++){
        GPIOC->BSRR = GPIO_BSRR_BR_8;
        GPIOC->BSRR = GPIO_BSRR_BS_8;
    };
    GPIOC->BSRR = (GPIO_BSRR_BS_9| GPIO_BSRR_BS_8 | GPIO_BSRR_BS_7 | GPIO_BSRR_BS_6);
};

как у нее со скоростью в итоге?
Грубоватая оценка будет, т.к. не замерял, но в целом менее 0.1 секунды (на глазок). Кристалл — STM32L152RBT6, тактирование, если мне не изменяет память, 16МГц. Дома попробую уточнить скорость заливки.
Проверял отличия между "обычным" методом (глаз видит, порядка 0.3 секунд заливается чёрным), и "быстрым", где только дёргается пин #WR. Разница отменная.
К сожалению, в моём случае, можно залить только чёрным/серым/белым.
Итак. Сделал замер скорости.
Сразу оговорюсь — лично мне приходится использовать 8-ми битную шину, а цвет для простоты передаю в 18 битном формате (3 байта / пиксел).
Для простой функции скорость получилась:
При 16MHz ядра:

  • Обычная (Байт, такт, байт, такт, байт, такт) — 0.3782s
  • Ускоренная (Байт, такт, такт, такт… много-много раз) — 0.1005s
  • Альтернативный вариант (Изменил цикл) — 0.00526s

Впечатляет. Но полный кадр, полноцветно, заливается долго.
Варианты кода:

Обычный
// Выводим каждый байт, симулируя работу шины 8080
    WriteData(B);
    WriteData(G);
    WriteData(R);


Ускоренный
<...>
Max = (right+1-left) * (bottom+1-top);
WriteOneByteManyTimes(R, Max*3);
<...>

void WriteOneByteManyTimes(uint8_t byte, int iterations) {
    int i;
    GPIOC->BSRR = (GPIO_BSRR_BR_9 | GPIO_BSRR_BS_7);
    GPIOA->BSRR = (GPIO_BSRR_BR_0 | GPIO_BSRR_BR_1 | GPIO_BSRR_BR_2 | GPIO_BSRR_BR_3 | GPIO_BSRR_BR_4 | GPIO_BSRR_BR_5 | GPIO_BSRR_BR_6 | GPIO_BSRR_BR_7);
    GPIOA->BSRR = byte;
    for (i=0;i<iterations;i++){
        GPIOC->BSRR = GPIO_BSRR_BR_8;
        GPIOC->BSRR = GPIO_BSRR_BS_8;
    };
    GPIOC->BSRR = (GPIO_BSRR_BS_9| GPIO_BSRR_BS_8 | GPIO_BSRR_BS_7 | GPIO_BSRR_BS_6);
};


Альтернативный
<...>
Max = (right+1-left) * (bottom+1-top);
WriteOneByteManyTimes(R, Max);
<...>
    for (i=0;i<iterations;i++){
        GPIOC->BSRR = GPIO_BSRR_BR_8;
        GPIOC->BSRR = GPIO_BSRR_BS_8;
        GPIOC->BSRR = GPIO_BSRR_BR_8;
        GPIOC->BSRR = GPIO_BSRR_BS_8;
        GPIOC->BSRR = GPIO_BSRR_BR_8;
        GPIOC->BSRR = GPIO_BSRR_BS_8;
    };


В последнем варианте — пин дёргается трижды за цикл и конвеер приходится перезагружать реже — в итоге получаем ускорение работы подпрограммы за счёт уменьшения количества инструкций перехода.

В сухом остатке:
Изначальная скорость заполнения экрана (320*240) px при глубине цвета в 24 бита (фактически, 18, т.к. два последних бита игнорируются) на тактовой в 16MHz (внутренний высокочастотный генератор) — те же 378.2ms (результат чуть хуже, чем у автора).
При заполнении серым цветом, результат уже лучше — можно добиться 52.6ms, без использования аппаратных средств, на голом "ногодрыге" одним пином.
Не самый лучший результат, согласен. Но на другом кристалле (STM32F107V) на частоте 50MHz обновление экрана вообще едва заметно глазу. Особенно, заливка чёрным в самом начале (грубо могу оценить в 32ms без оптимизации цикла и в 17 при оптимизации).

ЗЫ: Придираться к форматированию кода в блоке не буду, подбирать параметры — тоже.
Хороший результат. Точек конечно намного меньше. Меня на подобном экране (2.2") процессор STM32F030 на частоте 12 МГц вполне устраивал.
Да, близко, но вы формируете импульсы записи программно, а не аппаратно, и в случае раздельных трех байт, каждый со своим цветом, быстрая запись у вас возможна только белого, черного и серых цветов.
В случае ТС, можно залить любым из 16 битного цвета, т.к. фактически он дёргает пином записи и передаёт за такт те самые 16 бит.
Согласен, однако у данного микроконтроллера не много портов. Занимая все порты группы GPIOB, терялась возможность использовать шину I2C, она разведена только в группе B. При использование же 9 бит, она доступна. Таким образом получается универсальное решение для дальнейшей разработки и использования SPI, I2C, UART.

Как-то так

Выводить на экран через порты, можно лишь на платах, которые криво разведены (спроектированы).

У STM32F103 есть же FSMC контроллер!

В этом режиме работа с экраном идет, мягко говоря, значительно быстрее, чем через порты..

"Очистка" экрана при этом выглядит как то вот так:

#define Black           0x0000      /*   0,   0,   0 */

#define Bank1_LCD_D    ((uint32_t)0x60020000)    //disp Data ADDR
#define Bank1_LCD_C    ((uint32_t)0x60000000)    //disp Reg ADDR

void clearScr(uint16_t color)  {
 *(__IO uint16_t *) (Bank1_LCD_C)= 0x50;
 *(__IO uint16_t *) (Bank1_LCD_D)= 0;
 *(__IO uint16_t *) (Bank1_LCD_C)= 0x51;
 *(__IO uint16_t *) (Bank1_LCD_D)= 239;
 *(__IO uint16_t *) (Bank1_LCD_C)= 0x52;
 *(__IO uint16_t *) (Bank1_LCD_D)= 0;
 *(__IO uint16_t *) (Bank1_LCD_C)= 0x53;
 *(__IO uint16_t *) (Bank1_LCD_D)= 319;
 *(__IO uint16_t *) (Bank1_LCD_C)= 32;
 *(__IO uint16_t *) (Bank1_LCD_D)= 0;
 *(__IO uint16_t *) (Bank1_LCD_C)= 33;
 *(__IO uint16_t *) (Bank1_LCD_D)= 0;
 *(__IO uint16_t *) (Bank1_LCD_C)= 34;
 for(i=0;i<76800;i++)  *(__IO uint16_t *) (Bank1_LCD_D)= color;
}

Время обновления экрана не замерял, но на глаз — мгновенно.
Нормальный экран с динамически отображаемым текстом и графикой можно сделать только с FSMC.
Попытка использовать плату c управлением LCD через порты ничего кроме раздражения заметными на глаз задержками не вызывает.
Судя по данным STMicroelectronics, FSMC есть только у серии STM32F4 и STM32L162QD. В данном случае планировалось бюджетное решение, а STM32F103 и STM32F4 находятся в разных ценовых категориях.
В серии STM32F103 есть контроллеры с FSMC, но все они большие — от 100 выводов, например STM32F103VET6.
upd: тоже не успел.
Да, тоже уже посмотрел. Странно что в сводной таблице на сайте STM эта информация отсутствует.
21 Flexible static memory controller (FSMC)
Low-density devices are STM32F101xx, STM32F102xx and STM32F103xx
microcontrollers where the Flash memory density ranges between 16 and 32 Kbytes.
Medium-density devices are STM32F101xx and STM32F103xx microcontrollers where
the Flash memory density ranges between 32 and 128 Kbytes.
High-density devices are STM32F101xx and STM32F103xx microcontrollers where the
Flash memory density ranges between 256 and 512 Kbytes.
XL-density devices are STM32F101xx and STM32F103xx microcontrollers where the
Flash memory density ranges between 768 Kbytes and 1 Mbyte.
Connectivity line devices are STM32F105xx and STM32F107xx microcontrollers.
This section applies to high-density and XL-density devices only.

Но описанный автором контроллер — F103C8T6, НЕ относится к "high-density and XL-density devices", так что там НЕТ FSMC контроллера. Потому — велосипеды и костыли.

upd: Не успел.
Да, верно, FSMC в 103-й серии появляется у STM32F103RC, первого чипа в этой серии с 256М флеша.
Вот из его Key Features:

Memories

  • 256 to 512 Kbytes of Flash memory
  • up to 64 Kbytes of SRAM
  • Flexible static memory controller with 4 Chip Select. Supports Compact Flash, SRAM, PSRAM, NOR and NAND memories
  • LCD parallel interface, 8080/6800 modes
Сорри, ошибся. В документации есть примечание "For devices delivered in LQFP64 packages, the FSMC function is not available."
Так что FSMC начинается с STM32F103VC, у него 100-ногий корпус.
Опс… не обратил внимания на фотку платы контроллера и его тип (что не High-density).

Видимо подсознание отбросило этот факт… поскольку, лично я бы, к такому не стал бы пытаться LCD дисплей подключить. Особенно, если требуется от него (экран) вывод динамических данных.

в конце концов, STM32F103ZET6 core board не сильно дороже стоит. Для "поиграться", как минимум.
Ну смотря сколько не жалко потратить.
Плата с контроллером из данной статьи стоит сейчас около 160 р.
Минимальная с FSMC (STM32F103VCT6) уже от 830. А плата с STM32F103ZET6 от 1000 р.
Ну если для кого то 700р это большие деньги… тогда опс..
Эх. Я бы тоже поставил дисплей на контроллер, но… в малых корпусах просто нет его. Так что, костыли и велосипеды — вот наш девиз XD
Согласен. Да и это наверное не совсем костыли. Это скорее нестандартное использование, выявление скрытых возможностей.
Костыли — это когда например что-то глючит, и наугад ставим задержки чтобы "устаканились" переходные процессы. Или вводим какую-нибудь глобальную переменную, в одном месте в нее заносим что-то, а далеко в другом — проверяем.
А у нас просто оптимизация в рамках заданных аппаратных средств.
Sign up to leave a comment.

Articles