STM32 и дисплей. Использование FSMC.

Пришло время обсудить замечательную плюшку микроконтроллеров STM32 – а именно модуль FSMC. Это практически незаменимая вещь при работе с внешней памятью, либо, например, с графическим дисплеем. Собственно, с дисплеем то мы и будем играться, разбираясь с FSMC.

Но для начала, как обычно, немного теории. Итак, FSMC реализует параллельный интерфейс обмена данными между различными устройствами. Короче говоря — просто параллельная шина 😉 Используя FSMC при работе с внешней памятью, мы получаем возможность включить внешнюю память в адресное пространство микроконтроллера. Что это дает? А то, что обращение к внешней памяти значительно упрощается — необходимо просто обращаться к ОЗУ микроконтроллера по определенным заданным адресам. То есть все ритуальные танцы с временными диаграммами, таймингами и прочим модуль FSMC берет на себя. Мы просто пишем данные по адресу — а FSMC дергает линии данных, полностью осуществляя непосредственную работу с подключаемым устройством.

Схожим образом работает все это дело и при подключении дисплеев. Но все-таки тут все несколько иначе. Пусть у нас есть дисплей, у которого есть следующие выводы:

DB[17:0] – 18 линий для передачи данных (не забываем, что тут у нас параллельная передача данных, а не последовательная)

Также есть выводы для разрешения записи/чтения — туда мы должны выдавать строб-импульсы в определенной последовательности

Кроме того, у дисплея есть выводы chip select’а, reset, полно всего короче )

И всем этим хозяйством нужно рулить ) Вот тут то нам и поможет FSMC. И не просто поможет, а всю работу возьмет на себя! Итак, пусть мы уже подключили дисплей как надо, написали программу для FSMC STM32. Как же нам обращаться с дисплеем то? А опять все очень просто. Точно также, как с внешней памятью, мы будем всего лишь пихать байты по определенному адресу. А FSMC будет в это время лихорадочно дергать линиями данных, следить за временными интервалами, передавать все остальные нужные дисплею сигналы. А чтобы разделить передачу данных и команд мы должны записывать байты по разным адресам. И тут же встает вопрос — а какие именно адреса, и чем они определяются.

У дисплея есть вывод для выбора — данные/команда. То есть по состоянию этого вывода дисплей решает, что именно сейчас к нему прилетит. А у FSMC есть шина адреса и шина данных. Так вот этот вывод дисплея подключается к какому-нибудь пину шины адреса. И если мы подключили его, например, к 16 выводу шины адреса, то записав какой-нибудь байт по любому(!) адресу с нулевым 16 битом мы подадим дисплею команду. Вот, смотрите, пример небольшой.

Пусть как уже решили подключен 16 бит шины адреса. Берем адрес из доступных FSMC – 0x60000000. Видим, что в этом значении 16 бит равен нулю, а значит, когда мы запишем значение по этому адресу, FSMC подаст дисплею сигнал на запись, также сообщит дисплею, что сейчас будет команда, ну и, конечно же, выдаст на шину сами данные. А если мы запишем значение по адресу 0х60010000 (16 бит — единица!) то FSMC все разрулит и передаст дисплею данные. Вот так все просто )

Кстати, очень важный момент. В 16-битном режиме работы FSMC 16 бит шины адреса соответствует 17-му биту адреса (то есть 0х60020000). Ну естественно, все остальные адреса также смещены на 1 бит в этом режиме.

По идее работа с дисплеем и с внешней памятью с точки зрения программиста выглядит одинаково, но на деле все не так. При работе с дисплеем никакая память никуда не «проецируется», мы условно пишем данные по адресам, но это всего лишь дает FSMC сигнал о том, что пора начинать действовать 😉

Давайте потихоньку переходить к делу.

Для работы с FSMC будем по традиции использовать Standard Peripheral Library. Там все аналогично любой другой периферии, разве что настроек побольше ) Так что об этом не будем особо разговаривать — лучше на практике при написании программы посмотрим как и что там настраивается.

Что у нас в плане железа…

Испытывать FSMC я буду при помощи платы MiniSTM32 (про нее была уже статейка). Там уже установлен дисплей, так что никаких лишних телодвижений не потребуется. Вот как дисплей подключен:

Подключение дисплея

Но вообще есть серьезные опасения, что китайцы нарисовали схему коряво, поскольку тут можно найти явно абсурдные вещи )

Вообще, если посмотреть на распиновку конкретного дисплея и на выводы FSMC микроконтроллера, то там очень хорошо видно, что они довольно точно соответствуют друг другу.

Итак, что же будем делать то в качестве примера…Давайте разберемся как окрашивать дисплей в определенный цвет. Сначала зальем экран красным, потом зеленым и потом синим (RGB). Короче получить мы должны мигающий дисплей, на котором сменяют друг друга три цвета 😉

Данные будем передавать в 16-битном режиме.

Передача данных дисплею

Красному цвету соответствует — 111111 000000 000000

Зеленому — 000000 111111 000000

Синему — 000000 000000 111111

Как мы тут видим на 18 бит цвета приходятся 16 бит данных, в итоге получаем следующее:

Для красного — 0xF800

Для зеленого — 0x07E0

Для синего — 0x001F

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

/*******************************************************************/
#include "stm32f10x_gpio.h"
#include "stm32f10x_fsmc.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x.h"
 
 
 
/*******************************************************************/
// Определяем адреса, по которым будем записывать данные
// Для записи данных
#define LCD_DATA    			    ((uint32_t)0x60020000)    
// Для записи команд
#define LCD_REG   		  	    ((uint32_t)0x60000000)	  
 
 
 
/*******************************************************************/
// Простенькая функция задержки
void delay(uint32_t delayTime)
{	
    uint32_t i;
    for(i = 0; i < delayTime; i++); 
} 
 
 
/*******************************************************************/
 // Так мы будем писать команды в регистры LCD 
void writeLCDCommand(unsigned int reg,unsigned int value) 
{ 
    *(uint16_t *) (LCD_REG) = reg; 
    *(uint16_t *) (LCD_DATA) = value; 
} 
 
 
 
/*******************************************************************/ 
// А так данные.. 
void writeLCDData(unsigned int data) 
{ 
    *(uint16_t *) (LCD_DATA)= data;
 } 
 
 
 
/*******************************************************************/ 
void initAll() 
{ 
    FSMC_NORSRAMInitTypeDef fsmc;
    FSMC_NORSRAMTimingInitTypeDef fsmcTiming; 
    GPIO_InitTypeDef gpio; 
 
    // Включаем тактирование портов 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB |RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOE , ENABLE); 
    // И тактирование FSMC 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_FSMC, ENABLE); 
 
    // Инициализация пинов, задейстованных в общении по FSMC 
    gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 | GPIO_Pin_15; 
    gpio.GPIO_Mode = GPIO_Mode_AF_PP;
    gpio.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_Init(GPIOD, &gpio); 
 
    gpio.GPIO_Pin = GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; 
    gpio.GPIO_Mode = GPIO_Mode_AF_PP; 
    gpio.GPIO_Speed = GPIO_Speed_50MHz; 
    GPIO_Init(GPIOE, &gpio); 
 
    gpio.GPIO_Mode = GPIO_Mode_Out_PP; 
    gpio.GPIO_Pin = GPIO_Pin_6; 
    GPIO_Init(GPIOD, &gpio);
 
    // Здесь у нас Reset 
    gpio.GPIO_Pin = GPIO_Pin_1 ;
    GPIO_Init(GPIOE, &gpio); 
 
    // CS 
    gpio.GPIO_Mode = GPIO_Mode_AF_PP; 
    gpio.GPIO_Pin = GPIO_Pin_7;
    GPIO_Init(GPIOD, &gpio); 
 
    // RS 
    gpio.GPIO_Pin = GPIO_Pin_11 ;
    GPIO_Init(GPIOD, &gpio);
 
    // CS -> 1
    // Reset -> 0
    // RD -> 1
    // RW -> 1
 
    GPIO_SetBits(GPIOD, GPIO_Pin_7);			
    GPIO_ResetBits(GPIOE, GPIO_Pin_1);		
    GPIO_SetBits(GPIOD, GPIO_Pin_4);		    
    GPIO_SetBits(GPIOD, GPIO_Pin_5);			
 
    // Настройка FSMC
    fsmcTiming.FSMC_AddressSetupTime = 0x02;
    fsmcTiming.FSMC_AddressHoldTime = 0x00;
    fsmcTiming.FSMC_DataSetupTime = 0x05;
    fsmcTiming.FSMC_BusTurnAroundDuration = 0x00;
    fsmcTiming.FSMC_CLKDivision = 0x00;
    fsmcTiming.FSMC_DataLatency = 0x00;
    fsmcTiming.FSMC_AccessMode = FSMC_AccessMode_B;
 
    fsmc.FSMC_Bank = FSMC_Bank1_NORSRAM1;
    fsmc.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable;
    fsmc.FSMC_MemoryType = FSMC_MemoryType_NOR;
    fsmc.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;
    fsmc.FSMC_BurstAccessMode = FSMC_BurstAccessMode_Disable;
    fsmc.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
    fsmc.FSMC_WrapMode = FSMC_WrapMode_Disable;
    fsmc.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
    fsmc.FSMC_WriteOperation = FSMC_WriteOperation_Enable;
    fsmc.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
    fsmc.FSMC_ExtendedMode = FSMC_ExtendedMode_Disable;
    fsmc.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
    fsmc.FSMC_ReadWriteTimingStruct = &amp;fsmcTiming;
    fsmc.FSMC_WriteTimingStruct = &fsmcTiming;	   
 
    FSMC_NORSRAMInit(&fsmc); 
    FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM1, ENABLE);	
}
 
 
 
/*******************************************************************/
void initLCD()
{
    // Глобальный Reset дисплея
    GPIO_ResetBits(GPIOE, GPIO_Pin_1);
    delay(0x0FFFFF);					   
    GPIO_SetBits(GPIOE, GPIO_Pin_1 );		 	 
    delay(0x0FFFFF);
 
    // Пляски с  бубном от китайских товарищей	
    writeLCDCommand(0x0000,0x0001);
    delay(10);
 
    writeLCDCommand(0x0015,0x0030);
    writeLCDCommand(0x0011,0x0040);
    writeLCDCommand(0x0010,0x1628);
    writeLCDCommand(0x0012,0x0000);
    writeLCDCommand(0x0013,0x104d);
    delay(10);
    writeLCDCommand(0x0012,0x0010);
    delay(10);
    writeLCDCommand(0x0010,0x2620);
    writeLCDCommand(0x0013,0x344d);
    delay(10);
 
    writeLCDCommand(0x0001,0x0100);
    writeLCDCommand(0x0002,0x0300);
    writeLCDCommand(0x0003,0x1030);
    writeLCDCommand(0x0008,0x0604);
    writeLCDCommand(0x0009,0x0000);
    writeLCDCommand(0x000A,0x0008);
 
    writeLCDCommand(0x0041,0x0002);
    writeLCDCommand(0x0060,0x2700);
    writeLCDCommand(0x0061,0x0001);
    writeLCDCommand(0x0090,0x0182);
    writeLCDCommand(0x0093,0x0001);
    writeLCDCommand(0x00a3,0x0010);
    delay(10);
 
    // Настройки гаммы
    writeLCDCommand(0x30,0x0000);		
    writeLCDCommand(0x31,0x0502);		
    writeLCDCommand(0x32,0x0307);		
    writeLCDCommand(0x33,0x0305);		
    writeLCDCommand(0x34,0x0004);		
    writeLCDCommand(0x35,0x0402);		
    writeLCDCommand(0x36,0x0707);		
    writeLCDCommand(0x37,0x0503);		
    writeLCDCommand(0x38,0x1505);		
    writeLCDCommand(0x39,0x1505);
    delay(10);
 
    // Включение дисплея
    writeLCDCommand(0x0007,0x0001);
    delay(10);
    writeLCDCommand(0x0007,0x0021);
    writeLCDCommand(0x0007,0x0023);
    delay(10);
    writeLCDCommand(0x0007,0x0033);
    delay(10);
    writeLCDCommand(0x0007,0x0133);
}
 
 
 
/*******************************************************************/
int main()
{
    initAll();
    initLCD();	
 
    while(1)
    {
       int i;		
 
        // Начальный и конечный адреса по горизонтали
        writeLCDCommand(0x0050, 0); 
        writeLCDCommand(0x0051, 239); 
        // Начальный и конечный адреса по вертикали
        writeLCDCommand(0x0052, 0); 
        writeLCDCommand(0x0053, 319);  
 
        writeLCDCommand(32, 0);
        writeLCDCommand(33, 0);
        *(uint16_t *) (LCD_REG) = 34;
 
        // Красный
        for (i = 0; i < 76800; i++)
	{
	    writeLCDData(0xF800);
	}
	delay(0x0FFFFF);
	// Зеленый
	for (i = 0; i < 76800; i++)
	{
	    writeLCDData(0x07E0);
	}
	delay(0x0FFFFF);
	//Синий
	for (i = 0; i < 76800; i++)
	{
	    writeLCDData(0x001F);
	}
	delay(0x0FFFFF);
    }
}
 
 
 
/*******************************************************************/

Магическое число 76800 — количество точек дисплея. Все остальное вроде бы понятно, с настройками FSMC тоже ясно, как всегда в SPL все поля структуры названы логично и адекватно их функции.

После прошивки программы в контроллер дисплей начинает исправно подмигивать. Чего, собственно, и добивались, так что на этом все, скоро попробуем залить в дисплей какое-нибудь изображение )

Понравилась статья? Поделись с друзьями!

STM32 и дисплей. Использование FSMC.: 70 комментариев
  1. Имеется похожая плата с таким же процессором. При сборке примера из статьи Keil выдает ошибки:
    .\Obj\lcd_01.axf: Error: L6218E: Undefined symbol FSMC_NORSRAMCmd (referred from main.o).
    .\Obj\lcd_01.axf: Error: L6218E: Undefined symbol FSMC_NORSRAMInit (referred from main.o).
    В чем может быть дело?

      • Пример скомпилился, но дисплей к сожалению молчит. Сверю повнимательнее соответствие подключения. Хотя на Вашей схеме странности имеются…

        • Схема — это да, китайцы знатно поугорали когда ее рисовали ) Там даже сходу на глаз видно несколько абсолютно бредовых моментов.

  2. А дисплей молчит скорее всего по простой причине — дисплей скорее всего другой, а значит все тайминги в настройках FSMC надо менять на соответствующие.

  3. А какой контроллер у вас стоит на дисплее? у меня вот такая плата http://emproj.com/Alientek
    Никак не могу понять какой там контроллер и где мне смотреть настройки для FSMC.

  4. А можно файлы проэкта получить?
    Не могу понять, что за ошибки компиляции выдает компилятор
    linking…
    sdio.axf: Error: L6200E: Symbol __ARM_use_no_argv multiply defined (by sdio.o and main.o).
    sdio.axf: Error: L6200E: Symbol __ARM_use_no_argv multiply defined (by sdcard.o and main.o).
    sdio.axf: Error: L6200E: Symbol main multiply defined (by sdio.o and main.o).
    sdio.axf: Error: L6200E: Symbol main multiply defined (by sdcard.o and main.o).
    sdio.axf: Error: L6200E: Symbol writeBuffer multiply defined (by sdcard.o and sdio.o).
    sdio.axf: Error: L6200E: Symbol readBuffer multiply defined (by sdcard.o and sdio.o).
    sdio.axf: Error: L6200E: Symbol SDCardInfo multiply defined (by sdcard.o and sdio.o).
    Target not created

  5. Здравствуйте!
    на строку
    FSMC_NORSRAMInitTypeDef fsmc;
    Ругается…
    test.c(24): error: #268: declaration may not appear after executable statement in block

  6. Хочу выразить огромную благодарность автору статьи. Для обучения — супер материал! У меня дисплейчик на контроллере ili9325, достаточно было поменять конфиг и таки заморгало разными цветами)

  7. Что-то я не понял. Как работает функции writeLCDCommand и writeLCDData ? Как данные из LCD_REG и LCD_DATA уходят в FSMC контроллер? Где связка?

  8. Попробовал запустить.
    Зависает на строчке
    *(uint16_t *) (LCD_DATA) = value;
    на самой первой строчке инициализации. без нее проходит. При пошаговой отладке проходит по функци инициализации FSMC но регистры остаются пустыми (

  9. Любобытно, но заработало послн того, как добавил строчку
    fsmc.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
    В инициализацию fsmc

  10. Код работает до отключения питания. Если выключить-включить, то загорается просто белый дисплей, а программатор перестает видеть чип.
    Error: Flash driver function execute error
    Пока не замкнешь BOOT0 на VCC, программатор не хочет стирать чип. Это проблема именно моего контроллера или она встречается у всех?
    Плата такая же точно.

  11. Так это только в случае именно этого кода. Все остальные примеры и коды работают замечательно. Может ли быть битый флэш у микроконтроллера?

    • Ну тоже вполне возможно, точно помню, что на моей плате работало и после перезагрузки

  12. Хм. Как ни странно, но заработало все после добавления этой строчки
    fsmc.FSMC_AsynchronousWait = FSMC_AsynchronousWait_Disable;
    Волшебная строка какая-то)

  13. Интересно, а можно ли заменить этот контроллер на аналогичный только из 4й серии? Перепаять в плате например на stm32f429/439

  14. А вы не подскажете, как работать со шрифтами с данным дисплеем? Например с hd44780 можно было просто выводить текст командой lcd_out. а тут я не пойму порядок обращения к массиву шрифтов и вывода его на экран. Шрифты взяты от ST

  15. А можно подключить 2 устройства (скажем, память и дисплей, как с I2C) к одному контроллеру или тут 1 контроллер — одно устройство?

  16. Работаю в Keil с nor памятью в асинхронном мультиплексном режиме, настройки выставлял по мануалу, чтение проходит, но при записи уходит в прерывание HardFault_Handler, с чем это может быть связано?

  17. Добрый день, что означает данная строчка?
    Это указатель типа, а что означает первая звездочка?
    *(uint16_t *) (LCD_REG) = reg;

  18. То что LCD_REG — адрес понятно.
    Почему две звездочки — вопрос?
    Так было бы понятно
    uint16_t * (LCD_REG) = reg;

  19. Добрый день!!! Подскажи пожалуйста что означает строчка
    writeLCDCommand(32, 0);
    writeLCDCommand(33, 0);
    *(uint16_t *) (LCD_REG) = 34;
    Интересует магическое число 34. В других местах тоже видел но чуть по другому LCD_REG_34. И еще в функции writeLCDCommand(32, 0); что за числа 32, 33. Как я понял эта первое число в функции должно быть код определенной команды и второе значение (например при инициализации writeLCDCommand(0x0012,0x0010);) а здесь получается другие числа?

  20. У меня все сразу скомпилировалось, но отображается некорректно: картинка еле-еле смахивает на оригинальную и все строки разлетелись по сторонам., т.е. показывает, но хрен знает что. Второй день думаю что там может быть не так? У меня контроллер STM32f103ZE, дисплей TFT LCD Ilitec 320х240. Причем я пробовал при разных цветовых палитрах, и поворачивал картинку перед преобразованием в массив — все равно показывает одну и ту же муть.

  21. У меня на дисплее шрифты работают нормально: и позиционирует, и окрашивает в любой цвет, правда алфавит только английский (с цифрами и знаками препинания), могу скинуть файлы

  22. У меня тоже подозрение что сам массив получается какой-то левый от той программы(Segger Bitmap converter for emWin, качнул с их сайта последнюю версию), причем у меня это мутное изображение еще и повторяется на дисплее, я так понял там 1000 причин может быть

  23. Попробовал, у меня со всеми изображениями так, но я зашел на сайт Segger-а и там было написано что есть формат DIB (device independed format) и он как раз для того чтобы картинка отображалась корректно на всех дисплеях. Еще может быть (но маловероятно), что дело в компиляторе, например у меня GCC кокосовский. Обычно я делал так: открывал сгенерированный файл, брал из него только сам массив и у себя в коде пробегал в цикле этот массив функцией записи в GRAM (естественно после инициализации lcd) . Отпишу если все таки получится, у меня от этого только интерес вырос)

  24. Aveal, я разобрался с дисплеем, хочу поделиться мыслями: в моем случае изображение было неправильным по 2 причинам:
    Во-первых, у меня LCD работает на других настройках RGB палитры нежели в примере, т.е. при прорисовке изображения у меня даже цвета были не те (у меня палитра «565 red and blue swaped»).
    Во-вторых, я пытался отобразить изображения произвольного размера (как правило меньшего) и из-за этого у меня разъезжались строки в разные стороны и было похоже буд-то у меня изображение плодится по оси Х (по оси Y все рисуется нормально) . Как я понял надо перед отрисовкой задать адресный диапазон для GRAM, в рамках которого я должен пробегать циклом, чего я не делал, а сразу пошел бегать в цикле. И получалось что по оси X (0..240) у меня растягивалось изображение во всю ширь экрана — вот откуда разъехавшиеся полосы. Но как задавать область прорисовки я пока думаю….

  25. Добрый день почему здесь не указывается режим и частота
    // Здесь у нас Reset
    gpio.GPIO_Pin = GPIO_Pin_1 ;
    GPIO_Init(GPIOE, &gpio);

  26. Здравствуйте, Aveal! Скажите, возможно ли без особых сложностей подключить к STM32 ЖК панель с разрешением 1366×768 с интерфейсом LVDS?

    • Без особых сложностей точно нет ) Вообще наверно лучше это на ПЛИС реализовывать как мне кажется.

  27. Из кода видно, что для отправки данных в параллельный порт дисплея используются: биты 0, 1, 4, 5, 6, 8, 9, 10, 14, 15 порта D и биты 7, 8, 9, 10, 11, 12, 13, 14, 15 порта E.

    Каким волшебным образом эти биты выстраиваются в правильный порядок для отправки в параллельный порт дисплея? Куда, например, мапится на порт дисплея бит 14 порта D, а куда — бит 14 порта E?

    Если мне, например, нужно использовать другие биты портов — где это перенастраивается?

  28. Волшебства тут нет )

    Это не просто какие-то выводы какого-то порта. Для подключения используется модуль FSMC. Например, PD14 — это сигнал D0 интерфейса FSMC, соответственно, подключаться этот сигнал должен к соответствующему выводу дисплея.

    • То есть, как я понял, это жёстко задано.
      И если у меня плата с STM32 и плата с экраном стыкуются так, что (16 бит интерфейс) D0-D7 дисплея идут на PORTC(0..7), а D8..D15 — на PORTB(0..7) — то мне воспользоваться FSMC не судьба?

      http://www.ebay.com/itm/-/400594146303 — вот такое чудо у меня из Китая.

      • Ну надо проверить на всякий случай по документации на контроллер, где именно у него FSMC, и есть ли возможность ремапинга.

  29. Добрый день!
    Как всё это дело заставить работать на 8-битной шине?
    Заранее спасибо!

    • В настройках FSMC надо указать нужную ширину шины, по идее все остальное модуль уже сделает сам.

  30. А как данные слать? Так же по 16бит, а FSMC будет сама их на пачки по 8 бит делить?

  31. кто подскажет как работает выбор чип селекта в FSMC, настраиваю на первый банк в режиме NOR

  32. Добрый день.
    Непонятна работа функции, которая отправляет команду..
    void writeLCDCommand(unsigned int reg,unsigned int value)
    {
    *(uint16_t *) (LCD_REG) = reg;
    *(uint16_t *) (LCD_DATA) = value;
    }

    Что пишется в LCD_REG, а что в LCD_DATA?

    • По адресу LCD_REG пишется адрес регистра дисплея, а в LCD_DATA — значение, которое необходимо в этот регистр записать.

  33. Спасибо.
    У меня к STM подключен двухстрочный ЖК МЭЛТ, чтобы его инициализировать, надо отправить несколько команд.
    Правильно понимаю, что команды можно отправить так:

    *(uint16_t *) (LCD_REG) = 0x30;

    LCD_REG определен как ((uint32_t)0x60000000)).

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *