Пользователь
0,0
рейтинг
4 марта 2012 в 16:36

Разработка → STM32F1хх — продолжаем лечение от ардуинозависимости при помощи LCD

Добрый день, уважаемые хабровчане. В своей прошлой статье я рассмотрел применение замечательных микроконтроллеров STM32F1xx на примере управления сервоприводами. В этой статье мы обратимся к более интересному вопросу – управлению цветным графическим LCD-дисплеем. Помимо стандартной работы с дисплеем я постараюсь осветить вопросы использования особенностей микроконтроллеров STM32F для эффективного решения данного вопроса. Итак, начнем.

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

Железо


Прежде всего, о железе. Ядром системы, как и в прошлый раз, будет отладочная плата STM32VLDISCOVERY, которую можно приобрести за 300р. Собирать схему будем на макетной плате, чтобы поменьше паять. Однако, в отличие от прошлого раза, пайка нам таки предстоит, причем довольно специфическая – паять придется к гибко-жесткой печатной плате сзади дисплея (попросту говоря, к пленочке с контактами). Но об этом позже. Источник питания на 3.3В есть на отладочной плате, поэтому все что нам потребуется дополнительно – это собственно дисплей и источник питания на 10-20 вольт для его подсветки.
Пришла пора рассмотреть ту часть, ради которой все, собственно, и затевалось – дисплей.
На самом деле, вариантов, доступных разработчику в домашних условиях, довольно много.
На момент написания статьи, самым доступным вариантом я считал дисплеи от мобильных телефонов, так как их относительно легко можно приобрести, многие управляются по SPI и стоят довольно дешево. Собственно, одному из таких дисплеев и посвящена эта статья.
Однако, после того как я провел некоторое время на e-bay, я пришел к выводу, что самый оптимальный вариант, это, все-таки, покупка отдельного дисплея под свои нужды.
Во-первых, можно найти довольно дешевые модели, такие, как, например, вот этот дисплей:
image
1-8-TFT-Color-LCD-Display-Module-SPI-interface

Во-вторых, если заплатить чуть больше, можно получить законченный дисплейный модуль и не тратить время на пайку неудобных разъемов:
image
1-8-TFT-LCD-module-TF-Card-socket-break-out-arduino

В-третьих, некоторые дисплейные модули и вовсе выше всяческих похвал –
image
2-4inch-TFT-LCD-module-touch-panel-SD-card-cage

За 18 баксов мы получаем полностью законченный дисплейный модуль, включающий в себя дисплей 320х240 с контроллером, на который есть полная документация (обратите внимание, на контроллеры дисплея от многих мобильных телефонов документацию найти очень проблематично), резистивный тачскрин с контроллером (то есть он уже подключен, можно сразу запрашивать координаты по SPI), удобный разъем, и – как бонусный довесок – слот под микро-SD.
Также на борту стоит повышающий преобразователь для питания подсветки.
Поэтому в данный момент, я, конечно, считаю дисплеи с е-бей наиболее удобным и правильным вариантом.

Наконец, последний вариант, который мне бы хотелось отметить, это вот такие занятные девайсы:
image
1-5-lcd-rechargeable-digital-usb-photo-frame-keychain
Это чудо китайских технологий представляет собой миниатюрную цифровую фото-рамку. Как выяснилось, несколько зарубежных энтузиастов уже копались в ее недрах, и выяснили, что строятся они на 6502-совместимом микроконтроллере ST2203U, прошивка коего фиксируется при производстве, поэтому нам он не интересен. Зато, менее чем за 200р мы получим 128х128 STN ЖК-дисплей (большинство из них построены на контроллере PCF8833), микросхему флеш-памяти, литий-ионный аккумулятор со схемой контроллера заряда и удобный корпус для своего девайса.

И все же, остановимся пока на первом варианте, то есть — дисплее от мобильного телефона.
Согласно информации, взятой с просторов интернета, из небольших цветных ЖК-дисплеев популярностью пользуются дисплеи от Nokia ХЗ и Siemens S(K)65. По первому дисплею довольно много информации, но сам он уступает дисплеям от сименса в качестве изображения, разрешении, а главное – во времени отклика, которое делает его почти непригодным для воспроизведения анимации. А нам с вами не интересно выводить статические картинки, так ведь? Второй дисплей, судя по данным из интернета, предоставляет нам разрешение в 132 х 176 пикселя и время отклика, достаточное, для вывода анимации. Впрочем, возможности сравнить эти два дисплея у меня, к сожалению, не было – т.к. у меня завалялся старый Siemens SK65, выбор был однозначно сделан в пользу него.
Первые грабли подстерегают нас при покупке самого дисплея. Дело в том, что сименс устанавливает в свои S65 дисплеи на контроллерах трех разных производителей, LS020xxx, LPH88xxxx, L2F50xxx.

image

image

image

Про третий я не слышал вообще ничего, на второй есть официальный даташит, но, похоже, этот контроллер намного менее распространен.
А вот первый, LS020xxx — это тот контроллер, который чаще всего стоит в мобильниках, и мой SK65 не оказался исключением.
Казалось бы – все замечательно, но тут кроется один неприятный факт: на контроллер даташита нет. Таким образом, разрабатывать придется без официального документа, в котором бы были описаны команды и регистры. Но не все так плохо – умельцы провели сеанс реверс-инжениринга и определили основы взаимодействия с дисплеем, и информации о нем в интернете очень немало.
Рассмотрим дисплей подробнее.

Дисплей LS020xxx



image

Первое, что бросается в глаза – весьма удобные контактные площадки, подпаяться к которым куда проще, чем к мелким разъемам на большинстве дисплеев. Правда, расположены они на гибко-жесткой печатной плате, которую, при неосторожном обращении, можно поплавить паяльником, так что при пайке ставим температуру паяльника поменьше.
Всего дисплей имеет 10 контактов:

  1. RS – сигнал переключения команда / данные
  2. RESET – сигнал сброса дисплея
  3. CS – сигнал выбора устройства SPI
  4. CLK – тактовый сигнал SPI
  5. DAT – сигнал MOSI SPI
  6. 2.9V и 1.8V – питание дисплея
  7. LED+, LED_GND – питание и земля цепи подсветки
  8. GND – земля дисплея.


На контакты LED+ и LED_GND подается питание подсветки дисплея, которое может составлять от 10 до 20 вольт. Это питание подается на 3 светодиода, ток через них регулируется схематикой контроллера, так что к ним безбоязненно можно подключить одну-две 9В батарейки без токоограничительных резисторов. Я подключил две последовательно включенные, слегка подсевшие, кроны, получив напряжение около 17В.
Контакты 1V8, 2V9 и GND обеспечивают питание схем дисплея. Что интересно, во многих схемах подключения можно увидеть пин 1V8 вообще никуда не подключенным. Во всех остальных схемах пины 1V8 и 2V9 соединены вместе и подключены к источнику 3.3В. Это подключение оказалось вполне работоспособным, поэтому так и поступим.
Пины CS, DAT, CLK являют нам тривиальный SPI интерфейс без ответного провода. Этот факт не может не радовать, т.к. даже на AVRках имеется в наличии аппаратный SPI-контроллер, а STMки и вовсе богаты на периферию подобного рода – в младших моделях этих контроллеров два, в старших – аж 4 штуки, поэтому один из SPI можно выделить всецело для общения с дисплеем.
Пины RS и RESET – два дополнительных управляющих сигнала для дисплея. Первый, находясь в активном уровне (лог 0) индицирует о том, что мы передаем дисплею команды, а в неактивном (лог 1) – данные. RESET, как следует из названия, предназначен для сброса контроллера.
Теперь нам требуется подключить дисплей к нашей отладочной плате при помощи макетки. Для этого мной был выбран шлейф с 10-контактным IDC-разъемом.



Свой тактический промах я понял когда уже подпаял шлейф – дело в том, что на макетке горизонтальные гнезда соединены между собой, поэтому ряды разъема оказываются закороченными. Как бы то ни было, другого разъема у меня все равно не было, так что пришлось вносить поправки в виде «ножек» из проволоки — т.к. макетка представляет собой три независимые секции, подключив разъем на границе секций удалось избежать замыкания.

Тем, кто будет повторять мои эксперименты все же советую взять однорядный 10-контактный разъем, проблем будет меньше. После того как шлейф обжат, его следует подпаять к контактам дисплея – напоминаю о том, что пленка, на которой расположены контакты, не очень любит длительное воздействие высокой температуры, так что стараемся паять быстро и четко.
После пайки размещаем разъем на макетной плате и начинаем соединять перемычками с контроллером. Пины питания подсветки сразу же отводим куда подальше, втыкаем туда разъем от кроны и забываем о них. У кого есть источник питания на 10-20В – еще лучше, не придется постоянно отключать батарейки в целях экономия энергии.

Далее соединям SPI-интерфейс. Отладочную плату в макетку удалось установить только свесив горизонтальные пины (по уже описанной причине перемыкания), которых, к счастью было немного. Однако среди них оказался интерфейс SPI2, так что в нашем распоряжении остался только первый. Впрочем, нас это не должно сильно расстроить, т.к. больше мы пока и не собираемся ничего к нему подключать. Поэтому соединяем CLK с пином PA7, DAT – с пином PA5. CS будем дергать ручками в соответствии с логикой работы, как и RS с RESETом – поэтому эти три сигнала выводим туда, куда нам удобно. Я выбрал пины PA2, PA3, PA4. В итоге у вас получится что-то вроде этого:



Кстати, небольшой оффтоп: после того, как я все подключил, я выдал тестовый 10КГц меандр на один из пинов. И вот что показал осциллограф:

image

Канал 2, обозначенный синим цветом, подключен к самому пину. А вот канал 1 (желтый), подключен к соседнему с ним выводу. Видите эти красивые экспоненты с амплитудой почти 100мВ? Все эти перемычки на макетке дают неслабую емкостную связь между пинами. Что характерно, если отладочную плату вытащить из макетки, влияние пракически отсутствует:

image

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

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

Код


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

Начнем мы с простого вывода на экран картинки. Так как наша задача сейчас – разобраться с дисплеем, то не будем мудрствовать лукаво и прикручивать загрузку с карточек, чтение файловых систем и т.д. – просто разместим картинку в виде сырых 16-битных RGB-значений во флеш-памяти контроллера. Для этого рекомендую набросать на удобном вам языке программирования программку, которая переведет стандартный bmp-файл в запись вида
const uint16_t Picture[] = {0x0000, 0x0000, …, 0x0000};

Так как разрешение нашего дисплея 132 х 176 пикселей, каждый из которых занимает 2 байта, полная картинка будет занимать в памяти 46464 байт, то есть чуть больше 45 Кб.
К счастью, 500 КБ флеша СТМки позволяют нам сохранить картинку в сыром виде, и даже не одну.

Теперь переходим к программированию дисплея. Больше всего это похоже на черную магию, потому что даташита на контроллер у нас нет, зато есть инструкции от тех, кто реверсил, в которых сразу дается «заклинание» в виде последовательности байт, без объяснения, почему она именно такая. Если вас не устраивает такой подход, то наилучшим выходом будет приобрести какой-нибудь из упомянутых мной дисплеев с е-бея, к которым идет полный комплект документации.

Итак, заклинания.
Дисплей инициализируется следующей последовательностью (очень чувствительной к паузам!):

  1. Подаем уровень лог 0 на линию RESET и удерживаем его около 5 мс
  2. Выдаем первое заклинание дисплею, представляющее собой вот такой блок байт:
    	uint16_t Init1[12]={0xFDFD,0xFDFD,0xEF00,0xEE04,
    0x1B04,0xFEFE,0xFEFE,0xEF90,
    0x4A04,0x7F3F,0xEE04,0x4306}

    Заменив Init1[9]=0x7F3F на 0x7F1F, мы можем инициализировать дисплей в режиме 256 цветов – но т.к. 16-битный цвет все таки поинтереснее, остановимся на исходном варианте.
    После этого блока байт необходимо выждать 7 мс.
  3. Выдаем блок байт
    	uint16_t Init2[20]={0xEF90,0x0983,0x0800,0x0BAF,0x0A00,
    0x0500,0x0600,0x0700,0xEF00,0xEE0C,
    0xEF90,0x0080,0xEFB0,0x4902,0xEF00,
    0x7F01,0xE181,0xE202,0xE276,0xE183} 

    И выжидаем 50 мс
  4. Кастуем последний спелл в виде двух байт
     uint16_t Init3[2]={0x8001}
    и выжидаем 5 мс.


После этого на дисплее должен возникнуть разноцветный мусор, означающий что инициализация прошла успешно.
Итак, попробуем это все реализовать, попутно вспомнив, как работать с периферией в СТМке.
Я решил заодно испробовать бит-бандинг, для чего сразу объявил три указателя:

uint32_t *Reset	= (uint32_t*)0x42210184,
			*CS	= (uint32_t*)0x42210188,
			*RS	= (uint32_t*)0x4221018C;

Для тех кто не помнит об этой замечательной технике – бит-бандинг это очень полезная фича, представляющая собой мапирование каждого бита ИО-регистров и RAM в дворд где-то в адресном пространстве СТМки. Так как адресное пространство у нас 32-битное, то бишь 4 гигабайта, а реально задействовано намного меньше, разработчики решили потратить несколько метров, на то чтобы связать биты из ИО или RAM (bit-band alias region) с двордами в bit-band region.
Работает это просто – допустим, мы хотим управлять пином CS, который у нас соответствует PA2. Уровнем сигнала на нем управляет бит номер 2 регистра GPIOA->ODR.
Воспользуемся формулой из даташита:

bit_word_addr = bit_band_base + (byte_offset x 32) + (bit_number × 4)

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

#define PERIPH_BB_BASE        ((uint32_t)0x42000000)

byte_offset – смещение до искомого регистра, в данном случае, до GPIOA->ODR, от начала bit-band alias region.
Так как адрес GPIOA->ODR равен 0x4001080С, а начало периферийного bit-band alias региона объявлено как

#define PERIPH_BASE           ((uint32_t)0x40000000)

То смещение будет равно 0x4001080С-0x40000000 = 0x1080C
И наконец, номер интересующего нас бита равен 2. Собирая все это вместе получаем
*CS = 0x42000000 + (0x1080C)*32+2*4 = 0x42000000+210180+8=0x42210188

Теперь, записав отличное от нуля значение по этому адресу, мы установим связанный с этой ячейкой памяти бит в 1, записав нулевое – в 0.

Три блока байт у нас уже объявлены, проведем инициализацию периферии:

RCC->APB2ENR	|= RCC_APB2ENR_IOPAEN;	
RCC->APB2ENR	|= RCC_APB2ENR_SPI1EN;	

GPIOA->CRL  |=GPIO_CRL_MODE1;
GPIOA->CRL	&=~GPIO_CRL_CNF1;

GPIOA->CRL  |=GPIO_CRL_MODE2;
GPIOA->CRL	&=~GPIO_CRL_CNF2;

GPIOA->CRL  |=GPIO_CRL_MODE3;
GPIOA->CRL	&=~GPIO_CRL_CNF3;

GPIOA->CRL  |=GPIO_CRL_MODE5;
GPIOA->CRL	&=~GPIO_CRL_CNF5;
GPIOA->CRL	|=GPIO_CRL_CNF5_1;

*Reset=1;
*CS=1;
*RS=1;

GPIOA->CRL  |=GPIO_CRL_MODE7;
GPIOA->CRL	&=~GPIO_CRL_CNF7_0;
GPIOA->CRL	|=GPIO_CRL_CNF7_1;
	
SPI1->CR1 &= ~SPI_CR1_BR_2;
SPI1->CR1 |= SPI_CR1_DFF;
SPI1->CR1 |= SPI_CR1_MSTR|SPI_CR1_SPE;	

Тут все просто – Reset, CS, RS ставим на выход под управлением нашей программы,
CLK и MOSI – выход под управлением периферии, то есть, в данном случае, контроллера SPI.
SPI настраиваем на частоту Fclk/8 и посылку данных 16-битными вордами вместо байт.
Также следует описать функции задержки и синхронной посылки по SPI, которые, в силу их тривиальности, приводить тут не буду, за исключением прототипов:


__INLINE void Delay_ms(uint32_t us)
__INLINE void SendSPI(uint16_t Data)

Реализация их предельно проста – задержка реализована обычным циклом, синхронная посылка по SPI – выводом посылаемого байта в регистр данных SPI (SPI1->DR=Data;) и ожиданием в цикле до тех пор, пока не пропадет флаг SPI_SR_BSY из статуса SPI1->SR.

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


*Reset=0;
*CS=0;
 Delay_ms(5);
*Reset=1;
 Delay_ms(50);
 for(i=0;i<12;i++)
	SendSPI(Init1[i]);
Delay_ms(7);
 for(i=0;i<20;i++)
 	SendSPI(Init2[i]);
 Delay_ms(50);
 for(i=0;i<2;i++)
 	SendSPI(Init3[i]);
 Delay_ms(5);

Теперь пришла пора выучить новые заклинания. Итак:
Команда {0xEF90, 0x05OR, 0x06XX, 0x07YY} – вывод графической информации на экран, где байт OR обозначает ориентацию экрана при выводе и может быть равен 0х04 в случае горизонтальной и 0х00 в случае вертикальной – именно в этом направлении будут заполняться пиксели. Байты ХХ и YY обозначают позицию нижнего левого угла прямоугольника. Верхний правый угол всегда будет равен пикселю (175, 131).
После следует подавать байты графической информации в том формате, на который был инициализирован дисплей. В данном случае – 16-битные ворды вида 5-6-5 (R-G-B)

Для вывода информации в ограниченную область, команда используется в несколько модифицированном виде:
{0xEF90, 0x05OR,0x08X1,0x09X2,0x0AY1,0x0BY2}, где X1, Y1 – координаты нижнего левого угла области вывода, X2, Y2 – координаты верхнего правого угла.

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


SendSPI(0xEF90);
SendSPI(0x0504);
SendSPI(0x0600);
SendSPI(0x0700);
*RS=0;
for(i=0;i<132*176;i++)
	SendSPI(0x0000);
*RS=1;
*CS=1;

Ну а теперь выведем, наконец, нашу картинку!


SendSPI(0xEF90);
SendSPI(0x0504);
SendSPI(0x0600);
SendSPI(0x0700);
*RS=0;
for(i=0;i<132*176;i++)
	SendSPI(Picture[i]);
*RS=1;
*CS=1;

Если все сделано правильно, то мы должны получить вот такой результат:

Зеркально отраженный морпех как бы говорит нам: помни о порядке вывода пикселей!

Заканчивать работу с дисплеем рекомендуют не простым отключением питания, а следующей последовательностью команд:


uint16_t Power[26]={0xEF00,0x7E04,0xEFB0,0x5A48,0xEF00,0x7F01,0xEFB0,0x64FF,0x6500,0xEF00,0x7F01,0xE262,0xE202,0xEFB0,0xBC02,0xEF00,0x7F01,0xE200,0x8000,0xE204,0xE200,0xE100,0xEFB0,0xBC00,0xEF00,0x7F01};

*RS=1;
 for(i=0;i<26;i++)
	SendSPI(Power[i]);		
*CS=1;


На этом, в принципе, можно было бы и закончить, но на самом деле, впереди нас ждет самое интересное!

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


Итак, мы вывели на экран картинку из памяти. Это уже неплохое достижение и широкое поле для деятельности, но… То же самое мы могли легко проделать на пресловутой Ардуине. А наша цель – вылечиться от ардуинозависимости и понять, какие задачи лучше решать другими методами. Давайте подумаем: чем нас ограничивает текущая реализация?
Ответ очевиден – если мы захотим выводить анимацию, то большую часть времени мы будем крутиться в цикле вывода графической информации. Так как разрешение экрана 132х176 пикселей, каждый из которых описывается двумя байтами, нам нужно вывести 371 712 бит по шине SPI. Каждый бит выводится с частотой в восемь раз меньше тактовой, то есть нам нужно потратить 2 973 696 тактов на вывод одного кадра. Имея под рукой 75 МГц СТМки это, может быть, и не столь критично – хотя и неприятно, а для 16 МГц АВРки и вовсе беда.
Но к счастью, в архитектуре STM есть блок, который в подобных ситуациях делает нашу жизнь похожей на сказку. Это блок прямого доступа к памяти, то есть DMA.
Он позволяет осуществлять передачу данных без использования ядра контроллера между следующими точками:
  • Память-память
  • Память-периферия
  • Периферия-Память.

Довольно слов, давайте на деле посмотрим, что это нам дает!
Для начала добавим к инициализации периферии необходимый код:

	
DMA1_Channel3->CPAR = (uint32_t)(&SPI1->DR);	
DMA1_Channel3->CNDTR = 1560;
DMA1_Channel3->CCR |= DMA_CCR3_PL_0;
DMA1_Channel3->CCR |= DMA_CCR3_PSIZE_0;
DMA1_Channel3->CCR |= DMA_CCR3_MSIZE_0;
DMA1_Channel3->CCR |= DMA_CCR3_DIR;
DMA1_Channel3->CCR |= DMA_CCR3_MINC;
DMA1_Channel3->CCR |= DMA_CCR3_EN;

Здесь мы настраиваем адрес, по которому будут записаны данные (он равен адресу регистра данных SPI), устанавливаем количество 16-битных слов для передачи, равное 1560 (мои кадры по размеру составляют 30х52 пикселя), средний приоритет передачи,
размер приемника и источника – в данном случае, и тот и другой – 16 бит, направление передачи (из памяти в периферию), устанавливаем автоинкремент указателя на память, и включаем DMA.
После нам следует подготовить несколько кадров в таком же формате, что и описанный раньше const uint16_t Picture[].
Для этого я подготовил 12 хедеров frame01.h … frame12.h, и объединил данные из них в


const uint16_t* Video[]={frame1,frame2,frame3,frame4,frame5,frame6,frame7,frame8,frame9,frame10,frame11,frame12};

Также введем переменную-счетчик кадров


uint8_t FrameCnt=0;

Осталось совсем немного – в инициализации настраиваем системный таймер на нужную нам частоту смены кадров,


SysTick_Config(SystemCoreClock/50000);

Осталось описать обработчик прерывания таймера:


void SysTick_Handler()
{
	*CS=0;
	FrameCnt++;
	if(FrameCnt>11)
	FrameCnt=0;
	SPI1->CR2 &= ~SPI_CR2_TXDMAEN;
	DMA1_Channel3->CCR &=~DMA_CCR3_EN;
	*RS=1;
	*CS=0; 
	SendSPI(0xEF90);
	SendSPI(0x0504);
	SendSPI(0x0A00+70); 
	SendSPI(0x0800+50);
	SendSPI(0x0B1D+70); 
	SendSPI(0x0933+50); 
	*RS=0;
	DMA1_Channel3->CMAR = (uint32_t)Video[FrameCnt];
	DMA1_Channel3->CNDTR = 1560;
	DMA1_Channel3->CCR |= DMA_CCR3_EN;
	SPI1->CR2 |= SPI_CR2_TXDMAEN;
}

Здесь мы вычисляем номер нашего следующего кадра, после чего отключаем DMA-передачу на время. Это необходимо, чтобы передать дисплею команды на вывод графики.
В данном случае выводятся небольшие картинки, не во весь экран, т.к. попросту не хватает памяти контроллера. Если же прикрутить сюда чтение с какой-нибудь SD-карточки, то вполне можно крутить полноэкранное видео.

После того, как команды переданы, мы настраиваем адрес источника графики – один из фреймов, указатели на который хранятся в Video[], устанавливаем размер передаваемого блока и включаем DMA.

Все! Теперь наша программа тратит на вывод графики не более нескольких десятков тактов каждые 50 мс, те самые, что идут на обработку прерывания по таймеру – после того как прерывание будет обработано, DMA начинает передачу данных в регистр SPI без нашего участия! Таким образом мы можем совершенно спокойно заниматься своими делами в основном цикле, готовить следующий кадр, или обсчитывать какие-нибудь объекты, не заботясь о выводе графики.
Если все сделано правильно, получаем следующее:


Заключение


Чего же мы достигли?
Прежде всего, мы научились работать с такой интересной периферией, как цветной графический дисплей. Это уже само по себе неплохо и может пригодиться в проектах.
Но главное – мы научились использовать блок DMA, который открывает широчайшие возможности, по сравнению с софтварной передачей данных на контроллерах, не имеющих такого блока.

Что касается дисплеев, то я хотел бы добавить следующее: на e-bay я нашел отладочную плату, которую без вопросов назову отличным и необходимым инструментом разработчика на STM32. Продается она тут:Mini-STM32
image

За 45 баксов мы получаем невероятно удобный и мощный инструмент, в который входит микроконтроллер STM32F103VET, с аппаратным USB, контроллером статической памяти, в разы облегчающем работу с дисплеем и аппаратным контроллером SD-карт. Собственно дисплей, с разрешением 320х240 и установленным на нем резистивным тач-скрином, контроллером тач-скрина, буст-конвертером для питания подсветки. Вся необходимая обвязка для USB. RS-232 с конвертером. И батарейка, питающая бек-ап регистры контроллера.
Также, по дополнительному запросу, за 28 долларов в заказ добавят удобный программатор, клон J-LINKа, который, по моим ощущениям, намного стабильнее чем тот, что встроен в отладочную за 300р.

В общем, настоятельно рекомендую всем, кто собирается заниматься STMками приобрести комплект из этой отладочной платы и программатора. Последующие статьи я буду писать уже с использованием Mini-STM32 как основного инструмента.
Так как в данный момент по работе я разбираюсь с весьма занятными радио-модулями, то следующая статья, вероятнее всего будет про них.

Ссылки


Разбираясь с дисплеем, я пользовался следующими материалами:
http://www.juras-projects.org/eng/lcd.php
http://avrhobby.ru/index.php?option=com_content&view=article&id=71:-rgb-1&catid=42:rgb-&Itemid=71
@Ariman
карма
352,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (16)

  • +1
    Кстати о птичках — я дисплеи обычно заказываю тут.
    Они почти все как ваш номер 3. Это не реклама, я с этим магазином никак не связан, но может быть кому-то пригодится.
    Месяц назад заказывал себе такой, поиграл с ним и сломал перепаивая его на 16 бит, позавчера заказал размером побольше.
    • 0
      Это он и есть, похоже.
      Хм, а зачем вы перепаивали, если это можно регулировать программно? Возможно, я ошибаюсь, но вроде бы пинами там задается только начальный режим. Обратившись по 8-битному интерфейсу к регистру 0x0C, RGB Display Interface Control, можно поменять значение нескольких бит, и выставить 16-бит шину.
      • 0
        На том который достался мне 8 или 16 бит устанавливаются (если верить документации) очень очень очень мелким smd резистором. Вроде нормально перепаял, но дисплей всё равно сдох. Вечерком обратно перепаяю, может я где то накосячил.
        • 0
          Вам достался точно такой же, они не просто на одном и том же контроллере, но и, похоже, вообще не различаются.
          Резистором задается начальный режим, насколько я понимаю. То есть интерфейс, по которому вы должны начать свое общение с контроллером дисплея. После, обратившись к регистру настройки интерфейса, вы можете его сменить программно.

          Во всяком случае, так я понял даташит.
          • 0
            Спасибо! Попробую реанимировать.
  • 0
    Спасибо, коллега, статья очень полезная — у меня как раз завалялся такой дисплей, а STM32 для меня сейчас — основное направление деятельности. Но у меня есть пара вопросов:

    1) Я правильно понимаю, что вы не используете прерывания по завершению передачи DMA и даже не проверяете флаг готовности? Если так, то код на малых частотах может работать некорректно.

    2) Вот этот вопрос не только к вам, но также ко многим STM32-падаванам: почему вы отказываетесь использовать STM32F* Standard Peripheral Library, а вместо этого работаете напрямую с регистрами периферии, при этом даже не приблизившись к пределу производительности камня? Читабельность кода падает, а выигрыш в скорости мизерный и не критичный для такого кода. Я замечал подобное уже не раз, но, согласно моим экспериментам, SPL «тормозит» только в отладочной сборке, при этом проверяя аргументы функций на корректность и упрощая отладку. В релизной сборке с оптимизациями SPL обычно максимум в 3 раза медленнее прямого доступа к периферии, но при этом код читать и править куда проще.

    Прошу всех STM32шников поучаствовать в обсуждении, но рассуждать объективно, без эмоций (некоторые принимают подобные вопросы как обвинение или вызов).
    • 0
      1) Да, не проверяю, и, разумеется, в случае «боевого» кода так делать нельзя, но этот код демонстрационный и частоты смены кадров заведомо позволяет DMA успеть завершить передачу)

      2) Этот вопрос я рассматривал в первой статье, чтобы понимать как работает SPL неплохо бы знать, как внутри устроены контроллеры периферии, поэтому я фокусирую внимание на работе непосредственно с регистрами в своих статьях.
    • 0
      Полностью поддерживаю позицию по SPL, использую её везде и работать с кодом намного легче. Не нужно всё время вспоминать регистры. + В исходниках библиотеки в заголовках расписана последовательность работы с библиотекой и собственно переферией — как инициализировать, как настраивать, и т. д. Не говоря уж о вложенных примерах.
    • 0
      А у меня обычно получается так, что процессор обрастает собственной библиотекой периферии, с одинаковым интерфейсом для разных платформ. При этом я активно использую средства С++.
  • 0
    Ожидается наплыв спроса на брелки-фоторамки )
    • 0
      Брелок я сам не прочь потестить. Даже не столько из-за дисплея (честно, после дисплеев с е-бей вообще не хочется возиться с выковырянными неизвестно откуда дисплейчиками без док на контроллеры и без нормальных разъемов), сколько из-за того что довеском будет Li-Ion, флешка и классный корпус)
      Есть большое желание развести плату под СТМку, по размерам соответствующую плате из брелока и сделать мобильный девайс.
      • 0
        В брелках слишком уж мелко всё внутри, как в мобильниках, не подключиться. Можно плату с аккумулятором засунуть вот в такой относительно карманный корпус: iron.snop.ru/ (этот толстый за счет Ethernet'ного magjack'а, но такие же корпуса бывают более тонкие).
  • 0
    Поддерживаю Mini-STM32 — заказывал такой, работает сразу из коробки :-)

    Насчет высоких частот и макетки — подозреваю ошибку монтажа.
    Схемы на 1-10Мгц на макетке у людей работают, а тут частоты существенно скромнее.
    Паразитные емкости в 10pF, пусть даже 100pF — это не то, что может так меандр расколбасить.
    • 0
      Монтажа там, как такового, не было. Просто отладочная воткнутая в макетку, осцил — на соседних пинах.
      Это, конечно, было давно — надо бы перепроверить как-нибудь. Но тогда было именно такое впечатление.
      К тому же — это не меандр расколбасило, графики в разных масштабах. Это с соседнего пина, который настроен на инпут (высокий импеданс то бишь, без подтяжки) снимался меандр в ~100 мВ, как при довольно неплохой емкостной связи.
      Сам-то меандр вполне чистенький.
      • 0
        Ааа… Это болталась дорожка в воздухе… Так оно и на печатке так бы болталась )
        Вот выдай на соседнюю линию 0, и тогда будем смотреть какие там помехи :-)
  • 0
    Ну так если это будет какой-нибудь аналоговый вход, то разработчика ждет неприятный сюрприз.
    Поэтому я и говорю — с этим нужно считаться. На печатке можно отвести дорожку куда следует)
    Но вообще — да, вся эта помехозащищенность — та еще морока, при разводке можно затра замучаться весьма и весьма, пока помехи уберешь.

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

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