Урок 6 – работа с дисплеем st7783

STM32F4. Урок 6 – работа с дисплеем ST7783

Эта библиотека предназначена для управления графическим дисплеем с контроллером ST7783 (240х320 пикселей и 16-бит). Дисплей управляется внешней шиной контроллера FSMC, и следовательно должен быть подключен к CPU (смотрите назначение выводов в библиотеке).

 Библиотека выполняет основные функции, такие как иициализация, ориентаци я экрана, подсветка, установка курсора и очистка экрана. Для графических функций (круги, изображения и тд) существуют отдельные библиотеки. Для вывода текста есть библиотеки шрифтов.

 Скорость: очистка экрана занимает около 9мс.

Подсветка:

Сигнал управления подсветкой “LCD_Backlight” не может (и не должен) быть подключен непосредственно к светодиодной подсветке. Там должен быть установлен PNP-транзистор для коммутации (смотреть рисунок). [PB0 = LED_EN]

16-ти битный 8080-параллельный режим:

IM0 = Lo
IM1 = Hi
IM2 = Lo
IM3 = Lo

Пример изображения (используется графическая библиотека):

Используемые выводы:

PB0 -> LCD_Backlight PE3 -> LCD_RS
PD0 -> LCD_D2 PE7 -> LCD_D4
PD1 -> LCD_D3 PE8 -> LCD_D5
PD4 -> LCD_RD PE9 -> LCD_D6
PD5 -> LCD_WR PE10 -> LCD_D7
PD7 -> LCD_CS PE11 -> LCD_D8
PD8 -> LCD_D13 PE12 -> LCD_D9
PD9 -> LCD_D14 PE13 -> LCD_D10
PD10 -> LCD_D15 PE14 -> LCD_D11
PD14 -> LCD_D0 PE15 -> LCD_D12
PD15 -> LCD_D1

Требования:

   Подключаемые модули CooCox-IDE : GPIO, FSMC.
   Поддерживаемые библиотеки: отсутствуют.

Стандартные цвета:

#define RGB_COL_BLACK 0x0000
#define RGB_COL_BLUE 0x001F
#define RGB_COL_GREEN 0x07E0
#define RGB_COL_RED 0xF800
#define RGB_COL_WHITE 0xFFFF #define RGB_COL_CYAN 0x07FF
#define RGB_COL_MAGENTA 0xF81F
#define RGB_COL_YELLOW 0xFFE0 #define RGB_COL_GREY 0xF7DE

Функции:

ErrorStatus UB_LCD_Init(void); // Инициализация LCD
void UB_LCD_SetCursor2Draw(uint16_t xpos, uint16_t ypos); // Установить курсор в указанную позицию
void UB_LCD_FillScreen(uint16_t color); // Заполнить экран указанным цветом
void UB_LCD_Backlight_On(void); // Включить подсветку
void UB_LCD_Backlight_Off(void); // Выключить подсветку
void UB_LCD_SetMode(LCD_MODE_t mode); // Режим: Портретный,альбомный
void UB_LCD_SetWindow(uint16_t xstart, uint16_t ystart, uint16_t xend, uint16_t yend); // Установить размер окна для рисования

Пример использования:

//————————————————————–
// File : main.c
// Datum : 18.02.2013
// Version : 1.1
// Autor : UB
// EMail : mc-4u(@)t-online.de
// Web : www.mikrocontroller-4u.de
// CPU : STM32F4
// IDE : CooCox CoIDE 1.7.0
// Module : CMSIS_BOOT, M4_CMSIS_CORE
// Funktion : Demo der LCD-Library (ST7783)
// Hinweis : Diese zwei Files muessen auf 8MHz stehen
// “cmsis_boot/stm32f4xx.h”
// “cmsis_boot/system_stm32f4xx.c”
//————————————————————– #include “main.h”
#include “stm32_ub_lcd_st7783.h” int main(void)
{ uint32_t n; SystemInit(); // Инициализация настроек кварца UB_LCD_Init(); // Инициализация LCD // Удалить цвета с дисплея UB_LCD_FillScreen(RGB_COL_BLUE); // Перемещение курсора UB_LCD_SetCursor2Draw(10,50); // Нарисовать красную линию for(n=0;n

Источник: http://cxem.net/mc/mc306.php

Записки программиста

Если зайти на eBay и ввести в поиске «ST7735», можно найти немало модулей с дисплеем на базе данного контроллера. Модули обычно бывают двух типов — с TFT-дисплеем диагональю 1.44″ и разрешением 128×128 пикселей, а также с диагональю 1.8″ и разрешением 128×160 пикселей.

Последние в большинстве случаев также имеют и разъем для подключения SD-карт (но не все). Дисплеи позволят отображать 65536 цветов в палитре R5G6B5. Интересны данные модули тем, что будучи чуть-чуть дороже популярных 0.

96-дюймовых OLED-экранчиков на базе SSD1306 предлагают существенно большие разрешение и диагональ, а также в ~30 тысяч раз больше цветов.

Fun fact! Еще есть шилд для Arduino от Duinopeak с 1.8-дюймовым дисплеем, джойстиком и разъемом для SD-карт, а также шапка для Raspberry Pi от WaveShare с 1.

44-дюймовым дисплеем, джойстиком и тремя кнопками . Однако цена этих модулей с учетом доставки относительно высока. Кроме того, на AliExpress доступны укороченные 0.96-дюймовые модули с разрешением 80×160.

Модули питаются от 3.3 В или 5 В, имеют подсветку (которая питается только от 3.3 В, благодаря чему ее легко случайно спалить!) и используют SPI-подобный протокол.

Желающие посмотреть на конкретные единички и нолики протокола, могут воспользоваться Sigrok и соответствующим .sr файлом из репозитория sigrok-dumps.

Также мной был написан простенький декодер протокола ST7735 для Sigrok, но на момент написания этих строк патч еще не был вмержен в основную ветку (UPD: как оказалось, на самом деле он уже вмержен, см раз и два).

В PulseView протокол выглядит как-то так:

Команды и данные передаются с помощью пинов SCLK и MOSI, с порядком бит msb-first, как в обычном SPI. Здесь байт B1 является кодом команды FRMCTR1, а байты 01, 2C и 2D — аргументами этой команды.

Отличить аргументы от команды можно по пину DC (data or command), который имеет низкое напряжение для команд и высокое для данных. Также можно заметить, что CS (chip select) можно смело менять посреди передачи фрейма. Как и в традиционном SPI, чип выбран, когда напряжение на CS низкое.

Наконец, пин RES позволяет сбросить состояние контроллера, подав на этот пин низкое напряжение.

https://www.youtube.com/watch?v=i8H8Uiarz-U

Для получения приведенной выше картинки я использовал Arduino Uno и библиотеку для ST7735 от Adafruit. В версиях этой библиотеки старше 1.0.8 также вкорячили поддержку ST7789, с иерархиями классов, обмазкой всего макросами, и всяким таким. Из-за этого код библиотеки стал намного труднее для восприятия.

Любопытно, что ST7789 не является слишком уж распространненым контроллером. Он похож на ST7735, но в виде модуля продается только в магазине Adafruit за сравнительно большие деньги. Модуль имеет разрешение 240×240 при диагонали 1.54″. В своих проектах я бы не стал использовать этот модуль из-за небольшой диагонали, высокой стоимости и завязки на одного производителя.

А для изучения работы интересного мне ST7735 я использовал тэг 1.0.8 библиотеки.

Было решено спортировать библиотеку от Adafruit на STM32. Конечно, под STM32 нашлись и готовые библиотеки. Но, во-первых, использование готового кода — это скучно 🙂 Во-вторых, оно не приводит к появлению нормального понимания работы устройства.

Наконец, в-третьих, библиотеки, которые мне удалось найти, были написаны хардкорными бородатыми эмбеддерами, привыкшими делать все напрямую через регистры и бинарные сдвиги.

Не будучи хардкорным бородатым эмбеддером, я предпочитаю библиотеки без преждевременных оптимизаций, основанные на HAL, которые легче в поддержке и расширении.

Интерфейс библиотеки вышел следующим:

void init() {
    ST7735_Init();
}

void loop() {

    // Check border
    ST7735_FillScreen(ST7735_BLACK);

    for(int x = 0; x

Источник: https://eax.me/stm32-st7735/

Подключение oled дисплея с контроллером SSD1306 к STM32 по I2C

Многие, наверное, знают о таких маленьких дешёвых (меньше $3) OLED дисплеях, которые можно найти в огромном ассортименте на ebay или aliexpress.

В интернете существует множество различных статей о том, как подключать эти дисплеи к Arduino и другим МК, но для STM32f10x затруднительно найти даже библиотеку. Поэтому я решил написать эту статью.

Данный дисплей имеет разрешение 128х64 пиксела и контроллер SSD1306 и подклчается к микроконтроллеру по интерфейсу I2C.

Для STM32 была найдена библиотека для этого дисплея, но она была для серии f4xx — необходимо было модифицировать для f10x.

Исходные файлы модифицированной мной библиотеки можно взять тут.

ssd1306_i2c.cssd1306_i2c.h Интерфейс для работы с I2C
ssd1306.cssd1306.h Библиотека для работы с дисплеем. Представляет методы для рисования на дисплее, вывода текста, и вывода всего на oled.
fonts.cfonts.h Шрифты для вывода текста на экран. Там есть три шрифта, но можно создать любой свой при помощи этой программы или аналогов

Схема подключения предельно проста:

Vcc +3.3V. Допустимое напряжение — от 3.3В до 5В
GND GND
SCL PB6
SDA PB7

Для работы с библиотекой нужно подключить заголовочный файл:#include “ssd1306.h”
И перед использованием инициализировать:SSD1306_Init();
Теперь можно что-нибудь нарисовать:SSD1306_GotoXY(0, 44); //Устанавливаем курсор в позицию 0;44. Сначала по горизонтали, потом вертикали.
SSD1306_Puts(“Hello, habrahabr!!”, &Font_7x10, SSD1306_COLOR_WHITE); //пишем надпись в выставленной позиции шрифтом “Font_7x10” белым цветом. SSD1306_DrawCircle(10, 33, 7, SSD1306_COLOR_WHITE); //рисуем белую окружность в позиции 10;33 и радиусом 7 пикселей
Всё, что мы нарисовали сейчас находится в буффере в оперативной памяти МК, чтобы вывести всё на дисплей необходимо вызвать:SSD1306_UpdateScreen();
После этого наш дисплей обновится и будет выводить надпись и кружок. После вызова SSD1306_UpdateScreen() буффер в МК не сбрасывается сам, поэтому новые рисунки будут поверх предыдущих, для сброса можно заполнить всё чёрным цветом:SSD1306_Fill(SSD1306_COLOR_BLACK);
Все функции библиотеки:uint8_t SSD1306_Init(); //Инициализация SSD1306_UpdateScreen(); //Посылаем данные из буффера в памяти дисплею SSD1306_ToggleInvert(); //инвертирует цвета изображения в оперативной памяти SSD1306_Fill(SSD1306_COLOR_t Color); //заполняем дисплей желаемым цветом SSD1306_DrawPixel(uint16_t x, uint16_t y, SSD1306_COLOR_t color); //нарисовать один пиксел SSD1306_GotoXY(uint16_t x, uint16_t y); //установить позицию текстового курсора SSD1306_Putc(char ch, FontDef_t* Font, SSD1306_COLOR_t color); //вывести символ сh в позиции курсора SSD1306_Puts(char* str, FontDef_t* Font, SSD1306_COLOR_t color); //вывести строку str в позиции курсора SSD1306_DrawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, SSD1306_COLOR_t c); //нарисовать линию SSD1306_DrawRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, SSD1306_COLOR_t c); //наррисовать прямоугольник SSD1306_DrawFilledRectangle(uint16_t x, uint16_t y, uint16_t w, uint16_t h, SSD1306_COLOR_t c); //заполненный прямоугольник SSD1306_DrawTriangle(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3, SSD1306_COLOR_t color); //треугольник SSD1306_DrawCircle(int16_t x0, int16_t y0, int16_t r, SSD1306_COLOR_t c); //круг радиуса r SSD1306_DrawFilledCircle(int16_t x0, int16_t y0, int16_t r, SSD1306_COLOR_t c); //заполненный круг
Доступные шрифты, но вы так же можете добавить свои, в том числе и русские:

  • Font_7x10
  • Font_11x18
  • Font_16x26

Дисплей работает довольно быстро(FPS около 14-18) на скорости I2C 400кГц(450 тоже без проблем, но рисковать не стал, а на 500 подтормаживает) и без проблем.

Использовал CooCox IDE. Готовый проект можно скачать тут: Яндекс.Диск.

P.S. с момента написания статьи и до её публикации из песочницы прошло довольно много времени(6 месяцев), за которое я успел несколько раз изменить библиотеку.

Более новую версию библиотеки с поддержкой DMA и тестовый проект для Keil и cubeMx можно взять здесь. Самую последнюю версию библиотеки вы найдёте тут.

Пример работы библиотеки:

С удовольствием отвечу на ваши вопросы! Удачи!

Источник: https://habr.com/post/313490/

Вывод изображения на цветной дисплей ST7735

avrki@avrki.ru

Дата: 17 Августа 2015. Автор: Алексей

Здравствуйте. Сегодня я хочу с Вами поделиться своими наработками с TFT дисплеем на базе контроллера ST7735. Данный дисплей можно купить практически где угодно. Для его управления необходима шина SPI и три вывода CS, A0 и RES. Управляющий МК я взял ST32F407VGT. Данный МК установлен на плату Discovery4. Программа написана под IAR 7.

40, а для генерации проекта был использован ST32CubeMX. Железо.

МК TFT
PA1 CS
PA2 A0
PA3 RES
PA5 SCK
PA7 SDA

Программная часть.
Вот тут начинается самое веселье.

Библиотеку для работы с этим дисплеем я не стал сочинять, а решил взять из сети. Самая, на мой взгляд, интересная оказалась здесь. Написана она была правда под CooCox. По началу я ее запустил из под CooCox и наигравшись решил перенести на IAR. Больше всего меня порадовала данная библиотека тем, что она может выводить буковки как латинские так и русские без каких-либо конверторов.

Кто работал со знакосинтезирующими ЖК типа 16х2 меня поймут. Ну сказано, сделано. На деле оказалось все совсем не комильфо.
Засада первая.
ST32CubeMX – эта зараза при генерации проекта создает функции инициализации периферии не в отдельных файлах, а прям в основном main..

Вроде бы оно ничего, но при подключении внешних заголовочных файлов с функциями для которых нужна периферия просто не работают. Это связано с тем, что заголовочные файлы подключаются к проекту до вызова инициализационных функций периферии. Что делать? А делать остается только одно. Выдираем необходимые данные из этих функций и переносим в библиотеку для TFT дисплея. Вот что должно получиться.

SPI_HandleTypeDef hspi1; // Инициализация void lcd_st7735_init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_1LINE; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.

BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLED; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; hspi1.Init.

CRCPolynomial = 10; HAL_SPI_Init(&hspi1); // CS=0 – начали сеанс работы с дисплеем LCD_ST7735_CS_0; // аппаратный сброс дисплея LCD_ST7735_RES_0; // RST=0 HAL_Delay(LCD_DELAY); // пауза LCD_ST7735_RES_1; // RST=1 HAL_Delay(LCD_DELAY); // пауза // инициализация дисплея lcd_st7735_send_cmd(0x11); // после сброса дисплей спит – даем команду проснуться HAL_Delay(LCD_DELAY); // пауза lcd_st7735_send_cmd(0x3A); // режим цвета: lcd_st7735_send_data(0x05); //16 бит lcd_st7735_send_cmd(0x36); // направление вывода изображения: lcd_st7735_send_data(0x14); // снизу вверх, справа на лево, порядок цветов RGB lcd_st7735_send_cmd(0x29); // включаем изображение LCD_ST7735_CS_1; } Теперь после вызова данной функции, будет проинициализирован SPI MASTER полудуплекс и проинициализирован сам TFT дисплей. Функции передачи команды и данных я не стал переделывать, а перенес их как есть.
Засада вторая.
// Функция заполнения прямоугольной области экрана заданным цветом void lcd_st7735_fillrect(uint8_t startX, uint8_t startY, uint8_t stopX, uint8_t stopY, uint16_t color); Изначально я предполагаю что люди писавшие эту библу были адекватными и нулевую точку оси координат выбрали левый нижний угол. Я этому следовал, так как функция отрисовки пикселя по координатам и текст выводились как часы. Но что-то видимо пошло не так и в данной функции, которая должна отрисовывать прямоугольник, почему-то координата диагонали указана шиворот на выворот. То есть X перепутан с Y. На данный момент я на это забил, но чуть позже переделаю. Та же участь постигла функцию.
// Рисование прямоугольника (не заполненного) void lcd_st7735_rect(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint16_t color); Это происходит из-за того что последняя функция является производной от предыдущей.
Засада третья.
Функция отрисовки прямой. Вот тут я намучался по самый не балуй. Мало того что данная функция имела ту же проблему с координатной сеткой, так она еще себя вела немного не адекватно. Линии нарисованные до 150 пикселя по горизонту отражались нормально, но стоило перейти за черту 150-го пикселя, начинался полный хаос. Линия рисовалось произвольной длинны, произвольного направления. Так же на поле появлялись различные артефакты в виде хаотичных пикселей. По началу я думал, что это слишком большая скорость SPI (о чем предупреждал автор), но менял скорость было все тщетно. В итоге я не стал разбираться с функцией, а просто ее удалил. В замен я написал свою с использованием классического алгоритма Брезенхема и все пошло как по маслу. Функции вывода символа и текста работали хорошо, от чего я их менять не стал, а лишь перенес как есть. После чего немного подумав, я добавил еще пару функций для текста. Первая функция // Вывод строки в цвете по строке void lcd_st7735_putstr_xy(uint8_t x, uint8_t y, const uint8_t str[], uint16_t charColor, uint16_t bkgColor); Данная функция выводит переданную ей строку по координатам X номер адреса символа от 0 до 19, а Y номер адреса по сроке от 0 до 9 (снизу вверх). То есть данная функция чем-то напоминает функции вывода строки по координатам в знакосинтезирующих ЖК типа 16х2. Вторая функция // Форматирование строки // uint8_t *str массив для строки // int dig число для вывода // uint8_t rd количество разрядов в числе. // (массив должен иметь на 2 элемента больше чем количество разрядов в числе) void lcd_format_int(uint8_t *str, int dig, uint8_t rd); Данная функция должна переводить число из int в char массив. То есть из числа сделать строку. Заморочился я этим из-за того что библиотека stdio.h отжирает очень много памяти, но как оказалось моя функция сократила места не совсем много. Хотя если форматировать float , то я думаю что выйграл больше памяти чем stdio.h. Короче это философский вопрос и каждый должен сам решить что ему выгоднее.
А теперь самое сладкое. К данной библиотеке я добавил функцию вывода картинок. // Вывод картинки из массива void lcd_st7735_img(uint8_t x, uint8_t y, uint16_t w, uint16_t h, const uint16_t *pix); Функция получает координату левого нижнего угла выводимой картинки, высоту, ширину и массив с данными. А теперь вся история создания данной функции. Изначально я думал пихать JPG картинки на SD карту, а потом выводить на экран. В процессе изучения кодирования изображения я понял что даже самое низкое качество JPG изображения больше по памяти чем растровый BMP. JPG хорош для дисплеев мелкозернистых и с большим разрешением, а для таких клопиков как ST7735 с разрешением 160х128 достаточно BMP. Далее было принято решение написать функцию которая бы декодировала BMP изображения из 24 битного формата в 16 битный и выводила на дисплей. Изучив внутренности формата BMP я столкнулся с такой идиллией. Изображение в формате BMP с разрешением 160х128 весит 60 Кб, а если 160 умножить на 128 да на 2 байта на пиксель, то получается уже 40 Кб. 20 Кб отнимать у МК это кощунство. От сюда было принято решение, быть 16 битному массиву. Ага… Ну картинка 5х5 это 25 элементов массива. Набить ручками можно. 10х10 это 100 элементов. Если с пивом, то и это можно преодолеть. А если 160х128, то это уже 20480 элементов. Тут даже коньяк не поможет. Поразмыслив и решив что когда-то я доберусь до дисплеев с разрешением в 320х240 пикселей. Я перемножил и упал в осадок. Не… Уж пусть этим занимается ПК. Где такую взять? Ну конечно же в ентернете. Ага, ща. Перекопал всю сеть. Ничего подобного нет. Все наоборот из массива хотят BMP собрать. Ну да и фик с ним. Что я не джедай что ли. Берем VisualStudio 2010 и… Программа называется ConvertBMP. Конвертирует 24 битное BMP изображения с максимальным разрешением 320х240 пикселей в одномерный 16 битный массив. Данные цвета в массиве уже переконвертированны из 24 битного в 16 битный. На самом верху, как можно догадаться, выводится текущее изображение, которое нужно перебрать в массив. Ниже выводятся параметры изображения. Слева разрешение, а справа размер в байтах самого фала BMP. По нему можно определить на сколько отличается размер картинки от размера массива. Например, картинка 320х240 весит 230 Кб, а массив с той же картинкой 150 Кб. Я думаю, 80 Кб на дороге не валяются. Ниже выводится адрес размещения файла изображения на диске. Это нужно чисто для эстетики, дабы знать где лежит картинка. Ниже этажом выбор направления массива. Здесь давайте по подробнее. При написании программы я использовал библиотеки Microsoft это не секрет, а вот то что выборку пикселей по координатам идет от верхнего левого угла меня немного удивило. Я-то привык, как в меня в школе учили, что оси координат начинаются с левого нижнего угла, а тут. Короче я плюнул на это и начал отлаживать программу по микрософтовской координатной сетке. Когда программа была закончена, мне нужно было изменит циклы сборки массива. Но посмотрев уже на проделанный труд я решил не ломать старое, а добавить второй вариант. Теперь если выбрать прямой вариант, то массив будет собираться от верхнего левого угла и направо до конца строки. Затем переходит на следующую строку и снова слева направо. Ну как мы пишем на листе. А вот обратное направление, это когда массив собирается начиная от нижнего левого угла и направо до конца строки, затем переходит на строку выше и снова слева на право. Такой массив идеально подходит для вывода картинки на дисплей ST7735 160х128. Ну и наконец внизу расположены две кнопки. Одна для выбора картинки с диска, а вторая запускает конвертацию. После конвертации в папке img, расположенной рядом с программой, создастся заголовочный файл img.h. Что в нем внутри
// Файл img.h создан с помощью программы конвертатор ConvertBMP // Массив для вывода изображения на дисплей. Передача цвета RGB 16 бит 5-6-5 // Разрешение изображения 10×10, размер массива 200 байт // Адрес изображения C:UsersAlexDesktopКонвертер картинокpicture10x10.bmp // Конвертация обратная #ifndef IMG_H #define IMG_H uint16_t width = 10; // Ширина изображения uint16_t height = 10; // Высота изображения const uint16_t img[] = { 0xFFFF, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0xFFFF, 0xFFFF, 0x8CA1, 0x8CA1, 0x1686, 0x8CA1, 0x8CA1, 0x8CA1, 0xFFFF, 0xFFFF, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x1310, 0x1310, 0xA0B4, 0xFFFF, 0xFFFF, 0xA0B4, 0x1686, 0x8CA1, 0x8CA1, 0x8CA1, 0x1686, 0x1310, 0xA0B4, 0x1310, 0xFFFF, 0x1686, 0x1686, 0x1686, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0xA0B4, 0xA0B4, 0x1310, 0xA0B4, 0x1686, 0x1686, 0xA0B4, 0x1686, 0x8CA1, 0x8CA1, 0x8CA1, 0xA0B4, 0x1310, 0x1310, 0x1686, 0x1686, 0x1686, 0x1686, 0xFFFF, 0x1686, 0x8CA1, 0x8CA1, 0x1310, 0x1686, 0x1686, 0x1686, 0x1686, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x1310, 0x8CA1, 0xA0B4, 0x8CA1, 0x8CA1, 0x8CA1, 0x1686, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x8CA1, 0x1310, 0x1310, 0x8CA1, 0x8CA1, 0xFFFF, 0x1310, 0x1310, 0x1310, 0x1310, 0x1310 }; #endif /* IMG_H */ Для теста я создал картинку 10х10 пикселей и сконвертировал в обратный массив. Что записалось в файл. Первая строка это вода для хохмы. Вторая строка повествует о типе созданного массива. В ней говорится что массив содержит данные для каждого пикселя в 16 битном формате RGB 5 бит для красного цвета, 6 бит для зеленого и 5 бит для синего. Следующая строка содержит техническую информацию. Разрешение изображения, в данный момент 10х10, и вес массива. Заметьте именно массива. То есть значение указывает на количество байт или килобайт в массиве. Как раз этот размер можно сравнить с размером файла BMP и убедиться в целесообразности хранения именно массива, а не файла. (Если конечно Вы не используете SD карту с файловой системой). Следующая строка, говорит где лежит BMP файл который конвертировали. Это нужно чтобы не вспоминать что за картинка в массиве, а достаточно пройти по адресу и увидеть в живую изображение. Я рекомендую сохранять картинки в папке рядом с программой и тогда их можно будет всегда найти. Следующая строка указывает на сборку массива. Прямая или обратная. Далее идут две переменные ширины и высоты. Вот именно эти переменные и нужно передавать функции при выводе картинки на дисплей. А за переменными уже идет сам массив. Теперь от слов к делу. Запускаем STM32CubeMX и создаем новый проект с МК STM32F407VGT. Далее выбираем SPI1 в режиме мастер полудуплекс и RCC работа от внешнего кварца, так как на дискавери4 стоит на 8 МГц. Также настраиваем ножки PA1, PA2, PA3 на выход.
Далее переходим во вкладку настройки частоты. Здесь нам нужно записать 8 в частоту кварца, подключить его к HSE, подключить PLLCLK, а в HCLK записать 168. Будем гонять камень на максимуме. )))
Далее нажимаем на шестеренку сверху и обозвав проект, запускаем генератор.
Все, проект собран. Теперь нам нужно прикрутить нашу картинку. Для этого топаем в корень нашего проекта и создаем в нем две папки st7735 и img.
Судя из названия в первую кладем файлы библы для дисплея, а во вторую заголовочный файл с массивом картинки. Я для теста выбрал вот такую картинку. Теперь нужно подключить эти файлы к проекту. Для этого нажимаем правой кнопкой на заголовке проекта и выбираем Options…
В открывшемся окне переходим в пункт C/C++ Compiler, а там во вкладку Preprocessor и жмем на кнопочку добавления файлов к проекту. (Я в IAR не силен, так что я могу что-то не так называть)
И добавляем наши папочки. Так как у нас файлы лежат в корне проекта, то правим адресацию как на картинке ниже. Все, жмем Ок. Теперь прикручиваем наши файлы к проекту. А вот здесь начинается жопа. Я подробно расскажу как эту жопу обойти. Если Вы не хотите заморачиваться и взяли готовый проект, то эту часть можно пропустить. Если же у Вас появится желание переделать данный пример под другой МК с использованием SPI2 например, то я дальше пошагово объясню что куда переносить для нормальной работы. И так поехали. Вот мы и дошли до первой засады. Инициализация периферии. Если сейчас попытаться собрать проект и запихнуть в МК, то вывалится куча ошибок по SPI. А вот почему. // Отправка данных/команд на дисплей void lcd_st7735_send(uint8_t data) ; // Отправка команды на дисплей с ожиданием конца передачи void lcd_st7735_send_cmd(uint8_t cmd); Вот эти две функции отправляют данные и команды дисплею по шине SPI. А засада кроется в том что эти функции инициализируются до инициализации функции SPI. Вообще-то это дико, так как сначала в теле программы вызывается функция void MX_SPI1_Init(void); а за ней уже дисплей, но все равно пока я не перенес настройку SPI, функция инициализации не заработала. Поэтому переносим строку SPI_HandleTypeDef hspi1; в файл st7735.c в самый верх после инклюдов, а пачку команд инициализации SPI из функции void MX_SPI1_Init(void); переносим в функцию void lcd_st7735_init(void); Должно получится так. // Инициализация void lcd_st7735_init(void) { hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_1LINE; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLED; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLED; hspi1.Init.

CRCPolynomial = 10; HAL_SPI_Init(&hspi1); Теперь все что относится к инициализации SPI в основном файле программы удаляем. Далее все что нам нужно написать, это две строки. Все. Теперь заливаем проект в МК и видим выведенную картинку. Загрузки. Программа ConvertBMP
Проект для IAR 7.40

Спасибо за рабочий пример!

Спасибо , а как боротся с полосой пикселей ? Проверил экран через arduino ide , там в 2-х строчках выбирается дисплей , если стоит black , то есть полоса , если green , то нет(цвет лычки плёнки на экране).

Я понятия не имею как работать с этим дисплеем в среде arduino ide. Лучше спросить у автора этих функций. Да, не хотел пачкать дисплей, пока не установил в устройство. Вот пленка и висит.

Спасибо за инициализацию дисплея и за программу конвертации

Здравствуйте, подскажите как убрать так называемое звёздное небо на дисплее, при инициализации?

Никак. Это связано с тем, что после инициализации, дисплей выводит мусор из видеопамяти. Побороть это можно только залитием всего экрана одним цветом после инициализации.

Здравствуйте, какое энергопотребление дисплея?

Не измерял.

Источник: http://www.avrki.ru/articles/content/st7735_stm32/

Урок 9. Русский язык на OLED дисплее 128X64

ВИДЕО редактируется.

https://www.youtube.com/watch?v=fAo9LOZdTWc

Вы могли наблюдать, что в предыдущих уроках №7 и №8 уже использовался Русский шрифт. В этом уроке мы разберём некоторые проблемные моменты с которыми можно столкнуться при выводе Русских букв на OLED дисплей.

Нам понадобится:

Для реализации проекта нам необходимо установить библиотеки:

  • Библиотека iarduino_OLED_txt (текстовая) – для вывода текста и цифр на OLED дисплеи.
  • Дополнительно можно установить библиотеку iarduino_OLED (графическая), но в данном уроке она использоваться не будет.Графическая библиотека поддерживает все функции текстовой и имеет дополнительные функции для работы с изображениями и графикой, но при этом она занимает больше памяти, как оперативной, так и памяти программ.

О том как устанавливать библиотеки, Вы можете ознакомиться на странице Wiki – Установка библиотек в Arduino IDE.

Схема подключения:

OLED дисплей подключается к аппаратной или программной шине I2C Arduino.

ВыводНазначениеOLED дисплейArduino UNO
SCL Линия тактирования шины I2C SCL A5
SDA Линия данных шины I2C SDA A4
Vcc Питание (3,3 или 5 В) Vcc 5V
GND Общий GND GND

Схема установки дисплея при его подключении через Trema Set Shield.

Код программы:

#include // Подключаем библиотеку iarduino_OLED_txt.
iarduino_OLED_txt myOLED(0x78); // Объявляем объект myOLED, указывая адрес дисплея на шине I2C: 0x78 (если учитывать бит RW=0). //
extern uint8_t SmallFontRus[]; // Подключаем шрифт SmallFontRus. //
void setup(){ // myOLED.begin(); // Инициируем работу с дисплеем. myOLED.

setFont(SmallFontRus); // Указываем шрифт который требуется использовать для вывода цифр и текста.
// myOLED.setCoding(TXT_UTF8); // Указываем кодировку текста в скетче. Если на дисплее не отображается Русский алфавит, то …
} // раскомментируйте функцию setCoding и замените параметр TXT_UTF8, на TXT_CP866 или TXT_WIN1251.

//
void loop(){ // myOLED.clrScr(); // Чистим экран. myOLED.print( “Большие буквы:” , 0, 0); // Выводим текст начиная с 0 столбца 0 строки. myOLED.print( “ABCDEFGHIJKLM” , OLED_C, 2); // Выводим текст по центру 2 строки. myOLED.print( “NOPQRSTUVWXYZ” , OLED_C, 3); // Выводим текст по центру 3 строки. myOLED.

print( “АБВГДЕЁЖЗИЙКЛМНОП” , OLED_C, 5); // Выводим текст по центру 5 строки. myOLED.print( “РСТУФХЦЧШЩЪЫЬЭЮЯ” , OLED_C, 6); // Выводим текст по центру 6 строки. delay(3000); // Ждём 3 секунды. // myOLED.clrScr(); // Чистим экран. myOLED.print( “Маленькие буквы:” , 0, 0); // Выводим текст начиная с 0 столбца 0 строки. myOLED.

print( “abcdefghijklm” , OLED_C, 2); // Выводим текст по центру 2 строки. myOLED.print( “nopqrstuvwxyz” , OLED_C, 3); // Выводим текст по центру 3 строки. myOLED.print( “абвгдеёжзийклмноп” , OLED_C, 5); // Выводим текст по центру 5 строки. myOLED.print( “рстуфхцчшщъыьэюя” , OLED_C, 6); // Выводим текст по центру 6 строки.

delay(3000); // Ждём 3 секунды. // myOLED.clrScr(); // Чистим экран. myOLED.print( “Символы:” , 0, 0); // Выводим текст начиная с 0 столбца 0 строки. myOLED.print( “{}[]()?!#$%&*” , OLED_C, 3); // Выводим текст по центру 3 строки. myOLED.print( “~`'”^_-+=,.:;|/” , OLED_C, 5); // Выводим текст по центру 5 строки.

delay(3000); // Ждём 3 секунды. // myOLED.clrScr(); // Чистим экран. myOLED.print( “Цифры:” , 0, 0); // Выводим текст начиная с 0 столбца 0 строки. myOLED.print( “1234567890” , 6, 2); // Выводим текст начиная с 6 столбца 2 строки. myOLED.print( 1234567890 , 6, 3); // Выводим число начиная с 6 столбца 3 строки. myOLED.

print(-1234567890 , 0, 4); // Выводим число начиная с 0 столбца 4 строки. myOLED.print( 12345.7890 , 6, 5); // Выводим число начиная с 6 столбца 5 строки. delay(3000); // Ждём 3 секунды.
} //

Алгоритм работы программы:

В коде setup() происходит инициализация дисплея (подготовка дисплея к работе) и подключение шрифта «SmallFontRus» (в библиотеке имеется несколько предустановленных шрифтов, которые подключаются перед использованием). Со списком шрифтов и описанием всех функций библиотек iarduino_OLED и iarduino_OLED_txt, можно ознакомиться в разделе Wiki – OLED экран 128×64 / 0,96”.

Код loop() разбит на 4 части. Каждая часть начинается с очистки экрана функцией clrScr(), после чего следуют несколько функций print() для вывода текста или чисел на экран дисплея. Все части выполняются друг за другом с 3 секундной задержкой выполняемой функциями delay().

В результате на дисплее поочерёдно будут появляться: большие буквы (включая Русские), маленькие буквы (включая Русские), символы и цифры.

Проблемы при выводе Русских букв:

Кодировка:

Первая и основная проблема это кодировка в которой скетч передаётся компилятору. Разные версии Arduino IDE хранят скетч в различных кодировках. Даже последняя версия Arduino IDE 1.8.

5 (на момент написания данного урока) для ОС Windows, передаёт компилятору скетч в кодировке UTF-8 (если скетч был сохранён в файл *.ino) или в кодировке Windows-1251 (если скетч не был сохранён).

Но ни скетч, ни библиотеки используемые в нём, не знают в какой кодировке они будут переданы компилятору. Вот и получается что один и тот же скетч, оперируя Русскими буквами, может работать по разному.

Примечание:

Что такое кодировка?
Компьютер, как и контроллер (в т.ч. Arduino) хранит, получает и передаёт данные в виде 1 и 0. Из набора 1 и 0 можно точно составить числа, но нельзя однозначно составить буквы.

Кодировка это представление букв числами (по их порядковому номеру), которыми уже может оперировать компьютер или контроллер. Например, «А» – 1, «Б» – 2, «В» – 3 и т.д., тогда слово «ПРИВЕТ» можно передать, принять или сохранить, как набор чисел: 17,18,10,3,6,20.

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

Исторически сложилось что Латинские буквы имеют одинаковые порядковые номера в большинстве кодировок: «A»…«Z» = 65…90, «a»…«z» = 97…122, а Кириллические буквы не только имеют различные номера, но могут быть разбросаны, или вообще отсутствуют в кодировках.

Решение:

В библиотеках iarduino_OLED и iarduino_OLED_txt, мы предусмотрели функцию setCoding(), которая может принимать в качестве единственного аргумента, одно из значений: TXT_UTF8, TXT_CP866, TXT_WIN1251, определяющее текущую кодировку скетча.

Эта функция закомментирована в 3 строке кода setup программы данного урока. Если Русский текст на дисплее отображается некорректно, то раскомментируйте строку с функцией setCoding и замените параметр TXT_UTF8, на TXT_CP866 или TXT_WIN1251.

В большинстве случаев это решит проблему кодировок.

myOLED.setCoding(TXT_WIN1251); // Указываем что текст скетча представлен в кодировке Windows-1251.

Если функция setCoding() Вам не помогла, тогда вызовите функцию setCoding() с параметром false, а Русские буквы указывайте их кодом, как это показано в следующем разделе данного урока: «Недостаточно памяти».

При желании укажите в комментариях к уроку свою версию ОС, версию Arduino IDE и какую кодировку использует Ваша Arduino IDE (если Вы не знаете какую кодировку использует Arduino IDE, то напишите какие символы отображаются на дисплее вместо строчных и прописных Русских букв). Мы постараемся добавить Вашу кодировку в библиотеки.

Недостаточно оперативной памяти:

Вторая, но не менее важная проблема заключается в том, что строки занимают много оперативной памяти. Каждая буква строки занимает минимум 1 байт. Особенно актуальна эта проблема при создании различных пунктов меню.

Решение:

Наиболее эффективным решением данной проблемы является хранение строк не в области оперативной памяти, а в области памяти программ, так как объем памяти программ гораздо больше. Для этого указывайте строки в качестве аргумента функции F(). Строки указанные таким образом будут храниться в области памяти программ:

myOLED.print( F(“Строка для дисплея”) ); // Вывод строки на дисплей.
Serial.print( F(“Строка для монитора”) ); // Вывод строки в монитор последовательного порта.

Недостаточно оперативной памяти или памяти программ:

Если Вы работаете со строками на Русском языке в Arduino IDE, которая хранит скетч в кодировке UTF-8. Вы уже храните строки в области памяти программ (или оставили строки в области оперативной памяти). Вы все равно можете освободить до половины памяти занимаемой строками!

Дело в том, что в кодировке UTF-8 каждая Русская буква занимает целых 2 байта. Если указывать Русские символы кодом в той кодировке, где они занимают 1 байт, можно освободить половину памяти занимаемой строками, вне зависимости от того, в каком типе памяти они хранятся.

Символы в шрифтах для библиотек iarduino_OLED и iarduino_OLED_txt располагаются в соответствии с кодировкой CP866, значит хранить и выводить Ваши строки на экран дисплея можно в этой кодировке:

myOLED.setCoding(false); // Отменяем текущую кодировку, так как Русские буквы будем указывать кодом.
myOLED.print(“200340244343250255256 iArduino”); // Выводим текст “Ардуино iArduino”. Вместо Русских букв используем их код в кодировке CP866.

Примечание:

Как указывать символ или букву по его коду в строках скетчей?
Для начала нужно узнать коды всех символов той кодировки, в которой Вы желаете их указывать, таблицы символов различных кодировок можно найти в интернете. Для кодировки CP866, Русские символы имеют следующие коды:

А 128200 И 136210 Р 144220 Ш 152230 а 160240 и 168250 р 224340 ш 232350 Ё 240360
Б 129201 Й 137211 С 145221 Щ 153231 б 161241 й 169251 с 225341 щ 233351 ё 241361
В 130202 К 138212 Т 146222 Ъ 154232 в 162242 к 170252 т 226342 ъ 234352 242362
Г 131203 Л 139213 У 147223 Ы 155233 г 163243 л 171253 у 227343 ы 235353 243363
Д 132204 П 140214 Ф 148224 Ь 156234 д 164244 м 172254 ф 228344 ь 236354 244364
Е 133205 Н 141215 Х 149225 Э 157235 е 165245 н 173255 х 229345 э 237355 245365
Ж 134206 О 142216 Ц 150226 Ю 158236 ж 166246 о 174256 ц 230346 ю 238356 246366
З 135207 П 143217 Ч 151227 Я 159237 з 167247 п 175257 ч 231347 я 239357 247367

Для вывода любого символа нужно указать его код в 8-ричной системе счисления, которому должен предшествовать обратный слеш «». Данное правило действует для любых строк в Arduino IDE.

В строке «Ардуино iArduino» из примера выше, первая буква – «A», имеет код 128. Если перевести 128 в 8-ричную систему счисления, получится (200)8.

Значит букву «А» можно записать как «200», букву «р» как «340», букву «д» как «244» и т.д.

Для перевода чисел из 10-тичной в 8-ричную систему предлагаем воспользоваться стандартным калькулятором Windows. Откройте калькулятор, выберите вид калькулятора – «Программист» и введите число, Вы увидите его представление в разных системах счисления: HEX(16), DEC(10), OCT(8) и BIN(2).

Но если Вы желаете научиться быстро переводить числа между системами счисления 2, 4, 8, 10, 16, без калькулятора, то посмотрите Урок 32 – перевод чисел между системами счисления.

Ссылки:

Источник: https://lesson.iarduino.ru/page/urok-8-russkiy-yazyk-na-oled-displee-128×64/

Программирование дисплея на контроллере ST7920

Хочу рассказать о программировании дисплея на контроллере ST7920 с использованием ATtiny2313 контроллера.

Этот дисплей имеет 2 режима работы:

И имеет 3 режима подключения:

  1. Подключение по 8 битной шине
  2. Подключение по 4 битной шине
  3. Подключения по SPI (3 или 2 битной шине)

В данном посте я расскажу о:

  • Работе в текстовом режиме
  • Подключения и программирование по 8 битной шине
  • Подключения и программирование по SPI

Для того что бы подключить дисплей к контроллеру нам понадобится:

  1. Дисплей на контроллере ST7920
  2. 2 подстрочных резистора на 320 Ом.
  3. Для 8 битного режима резистор на 4.7 кОм (или больше)
  4. Контроллер ATtiny2313
  5. Источник питание на 5В.

Схема подключения

Подключение по 8 битной шине данных

Распиновка контактов: GND — Земля VCC — +5В V0 — Настройка контрастности RS — Определяет режим передачи данных (1 — это данные, 0 — это команда) RW — Запись или чтения (1 — чтения, 0 — запись) E — Строб D0-D7 — Шина данных PSB — Определяет какой протокол передачи данных будет использоваться ( 1 — 8/4 бит шина, 0 — SPI) BLA — Анод подсветки (+)

BLK — Катод подсветки (-)

Подключение по SPI

Распиновка контактов: GND — Земля VCC — +5В V0 — Настройка контрастности RS — (CS) Разрешает и запрещает дисплею принимать данные (1 — Запрещает, 0 — Разрешает) RW — (SID) Шина данных E — (SCLK) Строб PSB — Определяет какой протокол передачи данных будет использоваться ( 1 — 8/4 бит шина, 0 — SPI) BLA — Анод подсветки (+)

BLK — Катод подсветки (-)

Подстроечные резисторы

RP1 — Регулятор контрастности
RP2 — Регулятор яркости

Описание протоколов программирования дисплея

8 битный режим

И так, с начало я расскажу о том как в общих чертах происходит работа с дисплеем. Для того что бы работать с дисплеем нам нужно отправлять команды и данные на дисплей.

К командам относится: Включения/выключение дисплея, отображение курсора, перемещение курсора и т.д. К данным относятся например символы которые вы хотите видеть на дисплее.

Давайте рассмотрим пример того как производится инициализация для 8 битного режима.
Давайте рассмотрим пример того как производится инициализация:

  • Задержка в 50 мкс.
  • Отправляем команду установки 8 битного режима.
  • Задержка 120 мкс.
  • Отправляем команду включения дисплея ( в ней же указывается, включить ли курсор, и мигать ли курсором)
  • Задержка в 50 мкс.
  • Повторно отправляем функцию установки 8 битного режима
  • Задержка 120 мкс.
  • Отправляем команду отчистить экран
  • Задержка 20 мкс.
  • Устанавливаем ENTRY MODE (эта команда говорит о том в какую сторону сдвигать курсор после написания символа, нам соответственно нужно вправо)

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

Рассмотрим как отправить одну команду на дисплей в 8 битном режиме:

  • Устанавливаем низкий уровень E
  • Устанавливаем низкий уровень RS
  • Устанавливаем низкий уровень RW
  • Задержка 1 мкс.
  • Устанавливаем высокий уровень E
  • Отправляем в порт данных байт команды
  • Задержка 1 мкс.
  • Устанавливаем низкий уровень E
  • Задержка 50 мкс.

Для отправки одного байта данных выполняется абсолютно то же самое, только в начале устанавливается высокий уровень RS. RS = 0 Команда

RS = 1 Данные

Вот как отправляется один байт данных:

  • Устанавливаем низкий уровень E
  • Устанавливаем высокий уровень RS
  • Устанавливаем низкий уровень RW
  • Задержка 1 мкс.
  • Устанавливаем высокий уровень E
  • Отправляем в порт данных байт команды
  • Задержка 1 мкс.
  • Устанавливаем низкий уровень E
  • Задержка 50 мкс.

Давайте рассмотрим код отправки команды
Для начала установим константы что бы было удобнее:

.equ PCom = PORTD ; Управляющий порт к которому подключены RS, RW, E
.equ PW = PORTB ; Порт данных .equ RS = 2 ; Контакт порта PCom к которому подключен RS
.equ E = 0 ; Контакт порта PCom к которому подключен E
.equ RW = 1 ; Контакт порта PCom к которому подключен RW
.def Data = R18 ; Регистр используется для записи данных в порт

Функция отправки команды:

;Перед вызовом функции в регистр Data нужно установить необходимую команду
LCD12864_CommandOut: ;Вывод команды на дисплей. cbi PCom, E ; Устанавливаем низкий уровень E cbi PCom, RS ; Устанавливаем низкий уровень RS cbi PCom, RW ; Устанавливаем низкий уровень RW LCD8_MACRO_DELAY 1, 1 ; Задержка 1 мкс. sbi PCom, E ; Устанавливаем высокий уровень E out PW, Data ; Отправляем в порт данных байт команды LCD8_MACRO_DELAY 1, 1 ; Задержка 1 мкс. cbi PCom, E ; Устанавливаем низкий уровень E LCD8_MACRO_DELAY 1, 50 ; Задержка 50 мкс. Ret

Функция отправки данных:

;Перед вызовом функции в регистр Data нужно установить необходимую команду
LCD12864_DataOut: ;Вывод данных на дисплей. sbi PCom, E ; Устанавливаем высокий уровень E cbi PCom, RS ; Устанавливаем низкий уровень RS cbi PCom, RW ; Устанавливаем низкий уровень RW LCD8_MACRO_DELAY 1, 1 ; Задержка 1 мкс. sbi PCom, E ; Устанавливаем высокий уровень E out PW, Data ; Отправляем в порт данных байт команды LCD8_MACRO_DELAY 1, 1 ; Задержка 1 мкс. cbi PCom, E ; Устанавливаем низкий уровень E LCD8_MACRO_DELAY 1, 50 ; Задержка 50 мкс. Ret

В коде был использован макрос LCD8_MACRO_DELAY, вот его код

; Пример макроса функции задержки в микросекундах
.MACRO LCD8_MACRO_DELAY ; 1 параметр количество задержек, 2 параметр, количество микросекунд в задержки ldi Temp, @0 ldi Temp1, @1 rcall LCD12864_Delay
.ENDM ;Пример функции задержки для контроллера на 4 МГц.
;Функция имеет 2 параметра:
;R16 – Количество микросекунд
;R17 – Количество циклов по R16 микросекунд.
LCD12864_Delay: push R16 ;Сохраняем младшую задержку в ОЗУ.
ES0: dec R16 ;- задержка. cpi R16, 0 ;Закончилась? brne ES0 ;Нет – еще раз. pop R16 ;Да? Восстановить задержку. dec R17 ;Отнять от “количества задержек” разряда. cpi R17, 0 ;Количество задержек = 0? brne LCD12864_Delay ret

Теперь рассмотрим команды инициализации дисплея в текстовом, 8 битном режиме:

На основе этих четырех функций можно написать функцию инициализации дисплея:

LCD12864_Init: ;Инициализация дисплея. LCD8_MACRO_DELAY 1, 50 ; Задержка в 50 мкс. Ldi Data, 0b00110000 rcall LCD12864_CommandOut ; Отправляем команду установки 8 битного режима. LCD8_MACRO_DELAY 1, 120 ; Задержка в 120 мкс. Ldi Data, 0b00001111 rcall LCD12864_CommandOut ; Отправляем команду включения дисплея, включить курсор, мигать курсором LCD8_MACRO_DELAY 1, 50 ; Задержка в 50 мкс. Ldi Data, 0b00110000 rcall LCD12864_CommandOut ; Отправляем команду установки 8 битного режима. LCD8_MACRO_DELAY 1, 120 ; Задержка в 120 мкс. Ldi Data, 0b00000001 rcall LCD12864_CommandOut ; Отправляем команду отчистить экран LCD8_MACRO_DELAY 1, 20 ; Задержка в 20 мкс. Ldi Data, 0b00000110 rcall LCD12864_CommandOut ; установка направления движения курсора вправо LCD8_MACRO_DELAY 1, 50 ; Задержка в 50 мкс. ret

После выполнения инициализации на экране вы должны увидеть мигающий курсор.

Режим SPI

Теперь о функции приема передачи команды/данных по SPI.
В этом режиме участвуют 2 линии:

  • SID это контакт передачи данных, на дисплее он же RW
  • SCLK – это линия строб, на дисплее он же E

В SPI режиме передача одной команды или 1 байта данных происходит при передачи 24 бит
Протокол передачи данных таков:

  • Передаем 4 единицы подряд
  • Передаем 1 бит RW – чтения или запись
  • Передаем 1 бит RS – Команда или данные
  • Передаем 0
  • Передаем 4 бита старшей половины байта данных
  • Передаем 4 нуля
  • Передаем 4 бита младшей половины байта данных
  • Передаем 4 нуля подряд

На этом передача одного байта завершена.

После каждого переданного бита делается строб, то есть:

  • Задержка 1 мкс.
  • Устанавливаем высокий уровень SCLK
  • Задержка 1 мкс.
  • Устанавливаем низкий уровень SCLK
  • Задержка 1 мкс.

Рассмотрим функцию передачи команды/данных в режиме SPI, но сперва объявим константы:

.equ PCom = PORTD ; Управляющий порт к которому подключены SID и SCLK .equ SID = 1 ; RW Шина данных
.equ SCLK = 0; E Строб
.def Data = R18 ; Регистр используется для записи данных в порт

А теперь сама функция:

/*************************************
Функции отправки команды и данных по последовательному порту LCD12864_CommandOut – Отправляет команду LCD12864_DataOut – Отправляет данные Команда или данные должны находится в регистре Data
**************************************/
LCD12864_CommandOut: ldi r20, 0 rjmp command
LCD12864_DataOut: ldi r20, 1 command: LCD8_MACRO_DELAY 1, 1 sbi PCom, SID ; Устанавливаем SID В 1 ; Шлем 4 единицы rcall strob ; 1 rcall strob ; 1 rcall strob ; 1 rcall strob ; 1 rcall strob ; 1 ; Устанавливаем rw на запись cbi PCom, SID ; rw = 0 rcall strob ; Выберем, команда или данные и отправим ее. cbi PCom, SID ; rs = 0 cpi r20, 0 breq command1 sbi PCom, SID ; rs = 1
command1: rcall strob ; Отправляем 0 cbi PCom, SID ; 0 rcall strob ; Началась отправка байта ldi r20, 8 ; Счетчик бит
for_send_data: cpi r20, 0 ; Смотрим не закончились ли биты? breq stop_send_data ; Если закончились то переходим к отправки последних нулей cpi r20, 4 ; Смотрим, если было отправлено 4 бита то выполняем отправку 4 нулей. brne no_strob ; Иначе переходим к отправки следующего бита ; Отправка 4 нуля cbi PCom, SID rcall strob rcall strob rcall strob rcall strob ;Отправка следующего бита
no_strob: dec r20 ; Уменьшаем счетчик бит rol Data ; Сдвигаем регистр с данными на 1 влево brcs send_bit_1 ; Если сдвинутый регистр был 1, то флаг C был поднят, а значит переходим на отправку бита 1 ; Если флаг С не был поднят, отправляем 0 cbi PCom, SID ; Данные 0 бит rcall strob rjmp for_send_data ;Отправляем бит 1
send_bit_1: sbi PCom, SID ; Данные 1 бит rcall strob rjmp for_send_data
stop_send_data: ; Отправка байта закончилась, отправляем 4 нуля cbi PCom, SID rcall strob rcall strob rcall strob rcall strob cbi PCom, SID ret

Функция строб:

strob: LCD8_MACRO_DELAY 1, 1 ; Задержка в 1 мкс sbi PCom, SCLK ; Устанавливаем высокий уровень SCLK LCD8_MACRO_DELAY 1, 1 ; Задержка в 1 мкс cbi PCom, SCLK ; Устанавливаем низкий уровень SCLK LCD8_MACRO_DELAY 1, 1 ; Задержка в 1 мкс ret
.endif
;******************************************************

Текстовый режим

Теперь после того как вы научились инициализировать дисплей вы можете выводить любые символы на экран, например вывести букву A:

ldi Data, 'A' rcall LCD12864_DataOut

И на дисплее вы увидите букву A.

И так, теперь о том как устроенно адресное пространство в текстовом режиме:
Экран делится на 8 столбцов и 4 строки, в каждый столбец вы можете записать по 2 обычных символа или 1 иероглиф.
Адресное пространство находится от 0 до 31.

1 2 3 4 5 6 7
16 17 18 19 20 21 22 23
8 9 10 11 12 13 14 15
24 25 26 27 28 29 30 31

Как видите первая строчка это адреса от 0 до 7 Вторая же строчка от 16 до 23 Третья строчка от 8 до 15 То есть если вы напишете 16 букв подряд с адреса 0, то они будут в первой строчке,

но если вы напишите 17 символов, то последний символ будет не на второй строчке, а на третей!

И напоследок, для тех кто хочет использовать графический режим, есть такая статья: LCD 12864 на контроллере ST7920. Параллельный режим (8 бит)

Библиотека для работы с ST7920

Ссылка на файл библиотеки

Источник: http://www.pvsm.ru/assembler/55601

Ссылка на основную публикацию
Adblock
detector
",css:{backgroundColor:"#000",opacity:.6}},container:{block:void 0,tpl:"
"},wrap:void 0,body:void 0,errors:{tpl:"
",autoclose_delay:2e3,ajax_unsuccessful_load:"Error"},openEffect:{type:"fade",speed:400},closeEffect:{type:"fade",speed:400},beforeOpen:n.noop,afterOpen:n.noop,beforeClose:n.noop,afterClose:n.noop,afterLoading:n.noop,afterLoadingOnShow:n.noop,errorLoading:n.noop},o=0,p=n([]),h={isEventOut:function(a,b){var c=!0;return n(a).each(function(){n(b.target).get(0)==n(this).get(0)&&(c=!1),0==n(b.target).closest("HTML",n(this).get(0)).length&&(c=!1)}),c}},q={getParentEl:function(a){var b=n(a);return b.data("arcticmodal")?b:(b=n(a).closest(".arcticmodal-container").data("arcticmodalParentEl"),!!b&&b)},transition:function(a,b,c,d){switch(d=null==d?n.noop:d,c.type){case"fade":"show"==b?a.fadeIn(c.speed,d):a.fadeOut(c.speed,d);break;case"none":"show"==b?a.show():a.hide(),d();}},prepare_body:function(a,b){n(".arcticmodal-close",a.body).unbind("click.arcticmodal").bind("click.arcticmodal",function(){return b.arcticmodal("close"),!1})},init_el:function(d,a){var b=d.data("arcticmodal");if(!b){if(b=a,o++,b.modalID=o,b.overlay.block=n(b.overlay.tpl),b.overlay.block.css(b.overlay.css),b.container.block=n(b.container.tpl),b.body=n(".arcticmodal-container_i2",b.container.block),a.clone?b.body.html(d.clone(!0)):(d.before("
"),b.body.html(d)),q.prepare_body(b,d),b.closeOnOverlayClick&&b.overlay.block.add(b.container.block).click(function(a){h.isEventOut(n(">*",b.body),a)&&d.arcticmodal("close")}),b.container.block.data("arcticmodalParentEl",d),d.data("arcticmodal",b),p=n.merge(p,d),n.proxy(e.show,d)(),"html"==b.type)return d;if(null!=b.ajax.beforeSend){var c=b.ajax.beforeSend;delete b.ajax.beforeSend}if(null!=b.ajax.success){var f=b.ajax.success;delete b.ajax.success}if(null!=b.ajax.error){var g=b.ajax.error;delete b.ajax.error}var j=n.extend(!0,{url:b.url,beforeSend:function(){null==c?b.body.html("
"):c(b,d)},success:function(c){d.trigger("afterLoading"),b.afterLoading(b,d,c),null==f?b.body.html(c):f(b,d,c),q.prepare_body(b,d),d.trigger("afterLoadingOnShow"),b.afterLoadingOnShow(b,d,c)},error:function(){d.trigger("errorLoading"),b.errorLoading(b,d),null==g?(b.body.html(b.errors.tpl),n(".arcticmodal-error",b.body).html(b.errors.ajax_unsuccessful_load),n(".arcticmodal-close",b.body).click(function(){return d.arcticmodal("close"),!1}),b.errors.autoclose_delay&&setTimeout(function(){d.arcticmodal("close")},b.errors.autoclose_delay)):g(b,d)}},b.ajax);b.ajax_request=n.ajax(j),d.data("arcticmodal",b)}},init:function(b){if(b=n.extend(!0,{},a,b),!n.isFunction(this))return this.each(function(){q.init_el(n(this),n.extend(!0,{},b))});if(null==b)return void n.error("jquery.arcticmodal: Uncorrect parameters");if(""==b.type)return void n.error("jquery.arcticmodal: Don't set parameter \"type\"");switch(b.type){case"html":if(""==b.content)return void n.error("jquery.arcticmodal: Don't set parameter \"content\"");var e=b.content;return b.content="",q.init_el(n(e),b);case"ajax":return""==b.url?void n.error("jquery.arcticmodal: Don't set parameter \"url\""):q.init_el(n("
"),b);}}},e={show:function(){var a=q.getParentEl(this);if(!1===a)return void n.error("jquery.arcticmodal: Uncorrect call");var b=a.data("arcticmodal");if(b.overlay.block.hide(),b.container.block.hide(),n("BODY").append(b.overlay.block),n("BODY").append(b.container.block),b.beforeOpen(b,a),a.trigger("beforeOpen"),"hidden"!=b.wrap.css("overflow")){b.wrap.data("arcticmodalOverflow",b.wrap.css("overflow"));var c=b.wrap.outerWidth(!0);b.wrap.css("overflow","hidden");var d=b.wrap.outerWidth(!0);d!=c&&b.wrap.css("marginRight",d-c+"px")}return p.not(a).each(function(){var a=n(this).data("arcticmodal");a.overlay.block.hide()}),q.transition(b.overlay.block,"show",1*")),b.overlay.block.remove(),b.container.block.remove(),a.data("arcticmodal",null),n(".arcticmodal-container").length||(b.wrap.data("arcticmodalOverflow")&&b.wrap.css("overflow",b.wrap.data("arcticmodalOverflow")),b.wrap.css("marginRight",0))}),"ajax"==b.type&&b.ajax_request.abort(),p=p.not(a))})},setDefault:function(b){n.extend(!0,a,b)}};n(function(){a.wrap=n(document.all&&!document.querySelector?"html":"body")}),n(document).bind("keyup.arcticmodal",function(d){var a=p.last();if(a.length){var b=a.data("arcticmodal");b.closeOnEsc&&27===d.keyCode&&a.arcticmodal("close")}}),n.arcticmodal=n.fn.arcticmodal=function(a){return e[a]?e[a].apply(this,Array.prototype.slice.call(arguments,1)):"object"!=typeof a&&a?void n.error("jquery.arcticmodal: Method "+a+" does not exist"):q.init.apply(this,arguments)}}(jQuery)}var debugMode="undefined"!=typeof debugFlatPM&&debugFlatPM,duplicateMode="undefined"!=typeof duplicateFlatPM&&duplicateFlatPM,countMode="undefined"!=typeof countFlatPM&&countFlatPM;document["wri"+"te"]=function(a){let b=document.createElement("div");jQuery(document.currentScript).after(b),flatPM_setHTML(b,a),jQuery(b).contents().unwrap()};function flatPM_sticky(c,d,e=0){function f(){if(null==a){let b=getComputedStyle(g,""),c="";for(let a=0;a=b.top-h?b.top-h{const d=c.split("=");return d[0]===a?decodeURIComponent(d[1]):b},""),c=""==b?void 0:b;return c}function flatPM_testCookie(){let a="test_56445";try{return localStorage.setItem(a,a),localStorage.removeItem(a),!0}catch(a){return!1}}function flatPM_grep(a,b,c){return jQuery.grep(a,(a,d)=>c?d==b:0==(d+1)%b)}function flatPM_random(a,b){return Math.floor(Math.random()*(b-a+1))+a}