На данном уроке мы попробуем к контроллеру STM32 подключить дисплей TFT разрешением 240×320.
С подобным дисплеем мы уже работали в далёком уроке 37, только это был модуль, в котором подключение дисплея к контроллеру происходило по 8-битной шине. Тогда мы использовали для этого FSMC контроллера STM32. Это такой удобный контроллер памяти.
А теперь мы попытаемся подключить модуль с подобным дисплеем, который подключается к контроллеру уже по шине SPI. Данный дисплей также выполнен на контроллере ILI9341. Такой дисплей у меня наконец-то появился. Подключим мы его также к плате STM32F4-Discovery, с которой мы когда-то очень долго работали, причём с дисплеем, подключенным по 8-битной шине, мы также работали, подключая его к такой же отладочной плате. Не каждому удалось заиметь такой дисплей, многие по ошибке, а многие и не по ошибке приобрели дисплей, именно подключенный по шине SPI и просили меня сделать такой урок. Конечно же на такие массовые просьбы я просто не мог не откликнуться, мне прислали именно такой дисплей и мы теперь с ним поработаем, начнём с библиотеки HAL, причём сначала без DMA, потом применим DMA для того, чтобы сравнить, что нам даст эта периферия.
Дисплей выглядит следующим образом
У меня дисплей размером диагонали 2,8 дюймов, могут быть другие размеры, главное, чтобы разрешение было 320×240 и контроллер чтобы был такой же.
Верхние 5 ножек относятся к работе с сенсорной панелью (TouchScreen), поэтому мы их использовать не будем, следующая 6 ножка — MISO тоже не будет нами использована, так как нам нет смысла ничего читать из дисплея, мы знаем, что у нас именно ILI9341, да и не совсем корректно контроллер работает с MISO.
Подключим к нашей отладочной плате только нижние следующим образом
Вот соответствие ножек на плате и на дисплее
Питание мы подаём 5 вольт, так как на плате модуля есть стабилизатор, а подсветку питаем 3-мя вольтами. С шиной SPI мы работаем постоянно, причём на контроллере STM32F1 мы её прошли вдоль и поперёк, применяя различные библиотеки. Думаю, F4 также не станет исключением и мы с ним также будем работать не только с применением библиотеки HAL, но так как с данной библиотекой мы уже работали с шиной SPI на контроллере STM32F4, то, думаю, нам следует пока её и использовать, чтобы не наделать ошибок.
Запустим проектогенератор Cube MX и создадим новый проект, выбрав соответствующий контроллер
Включим кварцевый резонатор
Настроим тактирование в Closk Configuration
Включим SPI1 и настроим пока небольшую скорость, чтобы не думать, что ошибки из-за слишком высокой скорости, потом прибавим
У нас включились 3 основные ножки нашей шины. Включим на выход остальные ножки, которые присутствуют в нашей таблице
Добавим нашим ножкам скорости
Включим RNG для лучшего формирования случайных чисел
Немного перестроим частоты в Clock Configuration, чтобы получить вот тут 48, только так, чтобы основная частота осталась максимальной
Присвоим имя проекту, выберем среду разработки и удвоим размер стека и кучи
Сгенерируем проект и откроем его в Keil.
Настроим программатор на автоперезагрузку, отключим оптимизацию и создадим два файла — spi_ili9341.c и spi_ili9341.h для работы с нашим дисплеем следующего содержания
На данном уроке мы попробуем к контроллеру STM32 подключить дисплей TFT разрешением 240×320.
С подобным дисплеем мы уже работали в далёком уроке 37, только это был модуль, в котором подключение дисплея к контроллеру происходило по 8-битной шине. Тогда мы использовали для этого FSMC контроллера STM32. Это такой удобный контроллер памяти.
А теперь мы попытаемся подключить модуль с подобным дисплеем, который подключается к контроллеру уже по шине SPI. Данный дисплей также выполнен на контроллере ILI9341. Такой дисплей у меня наконец-то появился. Подключим мы его также к плате STM32F4-Discovery, с которой мы когда-то очень долго работали, причём с дисплеем, подключенным по 8-битной шине, мы также работали, подключая его к такой же отладочной плате. Не каждому удалось заиметь такой дисплей, многие по ошибке, а многие и не по ошибке приобрели дисплей, именно подключенный по шине SPI и просили меня сделать такой урок. Конечно же на такие массовые просьбы я просто не мог не откликнуться, мне прислали именно такой дисплей и мы теперь с ним поработаем, начнём с библиотеки HAL, причём сначала без DMA, потом применим DMA для того, чтобы сравнить, что нам даст эта периферия.
Дисплей выглядит следующим образом
У меня дисплей размером диагонали 2,8 дюймов, могут быть другие размеры, главное, чтобы разрешение было 320×240 и контроллер чтобы был такой же.
Верхние 5 ножек относятся к работе с сенсорной панелью (TouchScreen), поэтому мы их использовать не будем, следующая 6 ножка — MISO тоже не будет нами использована, так как нам нет смысла ничего читать из дисплея, мы знаем, что у нас именно ILI9341, да и не совсем корректно контроллер работает с MISO.
Подключим к нашей отладочной плате только нижние следующим образом
Вот соответствие ножек на плате и на дисплее
Питание мы подаём 5 вольт, так как на плате модуля есть стабилизатор, а подсветку питаем 3-мя вольтами. С шиной SPI мы работаем постоянно, причём на контроллере STM32F1 мы её прошли вдоль и поперёк, применяя различные библиотеки. Думаю, F4 также не станет исключением и мы с ним также будем работать не только с применением библиотеки HAL, но так как с данной библиотекой мы уже работали с шиной SPI на контроллере STM32F4, то, думаю, нам следует пока её и использовать, чтобы не наделать ошибок.
Запустим проектогенератор Cube MX и создадим новый проект, выбрав соответствующий контроллер
Включим кварцевый резонатор
Настроим тактирование в Closk Configuration
Включим SPI1 и настроим пока небольшую скорость, чтобы не думать, что ошибки из-за слишком высокой скорости, потом прибавим
У нас включились 3 основные ножки нашей шины. Включим на выход остальные ножки, которые присутствуют в нашей таблице
Добавим нашим ножкам скорости
Включим RNG для лучшего формирования случайных чисел
Немного перестроим частоты в Clock Configuration, чтобы получить вот тут 48, только так, чтобы основная частота осталась максимальной
Присвоим имя проекту, выберем среду разработки и удвоим размер стека и кучи
Сгенерируем проект и откроем его в Keil.
Настроим программатор на автоперезагрузку, отключим оптимизацию и создадим два файла — spi_ili9341.c и spi_ili9341.h для работы с нашим дисплеем следующего содержания
Консоль отладчика это конечно хорошо, но подключать же к нашему устройству компьютер для отображения скажем температуры. Знакосинтезирующие индикаторы на базе HD44780 вы уже и сами сможете подключить, вывод и чтение портов вы уже знаете, а больше ничего и не нужно. Гораздо интереснее будет подключить дисплей от мобильного телефона.
Поэтому в рамках курса для новичков ознакомимся с SPI на примере работы с дисплеем от телефона Nokia 3310.
Пожалуй, начиная с этого урока, я начну пропускать довольно большие куски кода и откажусь от детального описания кода. Базовые сведения для его понимания у вас уже есть. Описание как работает та или иная вспомогательная функция отнимет очень много времени и отвлечет от непосредственно рассматриваемой задачи. Весь материал, не рассмотренный в статье, предлагается для самостоятельного изучения с целью повышения навыком отладки и программирования.
Схема
Как обычно продолжаем предыдущий урок. Дисплей к плате подключаем по следующей схеме:
Проводки можно подпаять к пружинным контактам дисплея, а в некоторых моделях и непосредственно к дисплею. Электролитический конденсатор можно ставить на 1.0мк — 10.0мк. У меня, во всяком случае, работает.
Дорабатываем библиотеку
Добавляем в библиотеку LPC13xx_Lib файл ssp.h, содержащий описания функций и определения констант (файл в архиве). Отмечу только одну константу:
Это размер внутреннего буфера контроллера SPI. Он одинаковый и для буфера приёма и для буфера передачи. Вещь крайне полезная для фонового обмена данными. Пока мы его не задействовали, т.к. писали чуть более универсальный код.
Добавляем в библиотеку LPC13xx_Lib файл ssp.с и после подключения необходимых заголовочных файлов пишем функцию инициализации:
- Для начала записываем единицу в регистр PRESETCTRL, тем самым мы снимаем сигнал сброса с контроллера SSP (можно рассматривать SSP как микросхему в микросхеме, и указанный бит является как бы выводом RESET вложенной микросхемы).
- Через регистр SYSAHBCLKCTRL подаем тактирование на периферию (подключаем кварц к этой вложенной микросхеме);
- В регистр SSPCLKDIV заносим предделитель опорной частоты, для тактирования периферии. Если записать в него 0 (по умолчанию), то контроллер SSP отключится. Мы установили 1, хотя спокойно можно было и большее значение (что по идеи должно снизить потребление);
- Для вывода PIO0.8 выбираем функцию MISO вывода блока SSP;
- Для вывода PIO0.9 выбираем функцию MISO вывода блока SSP;
- Так как SCK вывод может быть назначен на разные выводы, то через регистр SCKLOC указываем, что нас интересует PIO2.10. Для самого вывода PIO2.10 выбираем функцию SCK вывода блока SSP. Кому-то это покажется излишеством, но так оно есть;
- Для вывода PIO0.2 выбираем функцию SSEL вывода блока SSP, тем самым разрешив аппаратный контроль за выбором устройства. В итоге, когда по SPI надо будет передать данные, контроллер SSP самостоятельно посадит этот вывод на 0, выбрав ведомое устройство;
- Через регистр CR0 настраивается основной режим работы (подробно в таблице 235 UM10375). Тут задаем размер «байта» любой желаемый от 4 до 16, формат пакета, полярность сигналов и паузы, делитель опорной частоты для бита (см. ниже);
- В регистр CPSR заносим делитель для получения опорной частоты (см. ниже). Он должен быть в диапазоне от 2 до 254 и обязательно чётным числом;
- Очищаем буфер приёма, во избежание чтения мусора;
- Через регистр CR1 указываем опции работы SSP. В частности что работаем и работаем в режиме мастера.
На первый взгляд может показаться сложной система задания частоты шины SPI. По этому попробую описать последовательно. Системная частота делиться на делитель в регистре SSPCLKDIV и эта частота задаётся основной для работы контроллера SSP. Затем она делиться на делитель в регистре CPSR для формирования опорной частоты шины SPI (это ещё не есть частота шины). И, наконец, опорная частота делиться на коэффициент, заданные в поле регистра CR0 (+1) для получения частоты тактового сигнала шины.
Прерывания нам здесь не нужны, мы владелец шины и сами определяем, когда осуществлять обмен и обмен у нас будет блокируемым. А обмен у нас происходит посредством функции:
За счёт использования именованных констант код стал легко читаемым (плюс комментарии). Логика простая, мы вызываем функцию с одним параметром – данные которые надо передать. Функция блокируется до появления в буфере приёма хотя бы одного байта, после чего принятый байт и возвращается.
Дорабатываем код
В проект добавлено 2 файла lcd.h и lcd.c. Код не вызывает особого интереса, потому в статье приводится не будет. По работе с дисплеем 3310 написано уже много, не вижу смысла повторяться. Вся работа с ним сводится к вызову рассмотренных функций SSPInit и SSPTransfer плюс пара дерганий выводов через GPIOSetValue. Отмечу только, что в функции инициализации дисплея мы вывод SSEL использовали как GPIO вывод, а потом отдали во владении SSP контроллеру. Такое поведение вполне допустимо.
Функция main так же претерпела некоторые изменения. В ней добавилась инициализация дисплея и вывод приветствия. В основном цикле вместо чтения одного канала мы поочерёдно проходим все. Важным изменением является то, что мы больше не используем функцию printf. Взамен неё мы используем следующую конструкцию:
Функция sprintf так же как и printf осуществляет формирование строки. Но в отличие от последней «выводит» строку в буфер, а не на отладочную консоль. Эту строку из буфера мы выводим на дисплей вызовом LCD_writeString, предварительно установив позицию вывода на дисплее.
Запуск
Компилируем, запускаем и видим в результате залапанный дисплей:
Логика работы проста. После отображения приветствия на экран выводятся данные с 6 каналов АЦП в формате:
Обновление производится несколько раз в секунду. На время измерения и вывода зажигается светодиод. Но если вы нажмете кнопку, то задержка снимается, и контроллер как бешенный начинает гонять данные.
Это позволит вам несколько визуально оценить скорость работы.
Статистика
18кБ (плачьте и негодуйте любители оптимального кода). всё наше место скушала при этом библиотека Redlib (semihost) которую мы использовали ради одной лишь функции sprintf. А функцию мы использовали для преобразования числа в строку символов (ну ещё и форматирование, но это мелочи).
Естественно можно обойтись и библиотекой Redlib (none) написав свою функцию перевода числа в строку (sprintf мы не можем использовать для этой библиотеки, проект просто не соберётся). Это я предлагаю сделать вам самостоятельно. Ту же функцию itoa можно найти практически в любой книге по С.