Top.Mail.Ru

Подключение дисплея на базе ST7735 к микроконтроллеру STM32.

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

Так, задачей нашей будет сделать базовый проект, который позволит с минимальным порогом вхождения начать работу с любым дисплеем на базе ST7735. В дебри настроек погружаться не будем, пересказыванием даташита заниматься тоже, во главе угла – реальная польза от практической реализации. Вот кстати ссылка на подробнейшее pdf описание контроллера – ссылка.

Подключение дисплея на базе ST7735.

Начинаем традиционно – с аппаратной части и подключения дисплея. У меня есть в наличии несколько десятков дисплеев, но преимущественно разрешением 128*160 пикселей. Такой и возьму:

Дисплей на базе ST7735

Дисплей имеет 14-ти контактный шлейф для коммутации, его распиновка выглядит следующим образом:

ST7735 распиновка
  1. NC – Not Connected
  2. GND
  3. LED K – катод светодиодов подсветки
  4. LED A – анод светодиодов подсветки
  5. GND
  6. RST – сигнал сброса
  7. D/C – сигнал выбора передачи данных или команд
  8. SDA – SPI MOSI
  9. SCK – SPI SCK
  10. VCC – напряжение питания
  11. IOVCC – источник питания внутренних интерфейсов
  12. CS – Chip Select
  13. GND
  14. NC – Not Connected

К слову, в разных схемах/статьях/алиэкспрессах обозначение может незначительно, но отличаться. Например, сигнал D/C часто обозначают как RS.

Собрав по-быстрому, “на коленке”, плату для подключения дисплея, получаем следующий вариант коммутации:

Подключение ST7735 к STM32

У меня выбранные ножки фиксированы разводкой платы, а так можно использовать, естественно, любые другие. На подсветку (LED K и LED A) я подключил 3.3 В напрямую (благо источник питания позволяет, потребление светодиодов подсветки на моем дисплее – в районе 40 мА на 3.3 В), то есть подсветка будет работать перманентно. А в обычных проектах я подключаю примерно так:

Схема управления подсветкой

Соответственно, так мы имеем возможность, во-первых, отключить подсветку, когда требуется, а, во-вторых, управлять яркостью, подавая, например, ШИМ-сигнал разной скважности с микроконтроллера.

В качестве управляющего контроллера у меня STM32F103RE. Пока не забыл, немаловажный нюанс – у меня стоит на этой плате кварц на 12 МГц, можно сказать, что величина нестандартная, так как в подавляющем большинстве случаев используется 8 МГц, ну или реже – 16 МГц. Поэтому обратите на это внимание в настройках тактирования при портировании проекта на свое железо.

Для выдачи данных на дисплей используется интерфейс SPI, причем дисплей только принимает информацию, не передавая ничего в обратном направлении. Это мы учтем при инициализации периферии чуть ниже. Кроме того, потребуются три порта ввода-вывода для подключения линий Chip Select, Reset, D/C. Последняя нужна для информирования дисплея о том, являются ли передаваемые байты данными или командой. Но и с этим мы разберемся чуть позже – при написании библиотеки для работы с ST7735.

Формирование цветов.

На чем хочется немного подробнее остановиться, так это на схеме передачи цветов. Дисплей предлагает 3 варианта кодирования цвета:

  • 12 битов на пиксель = 4 бита красный (R) + 4 бита зеленый (G) + 4 бита синий (B)
  • 16 битов на пиксель = 5 битов красный (R) + 6 битов зеленый (G) + 5 битов синий (B)
  • 18 битов на пиксель = 6 битов красный (R) + 6 битов зеленый (G) + 6 битов синий (B)

В этом базовом проекте я использую второй из этих вариантов. Это настраивается в регистре ST7735 COLMOD, для 16-ти битного формата значение 0x05.

При таком режиме основные цвета будут закодированы так:

Формирование цветов

То есть получаем 16-ти битные значения:

  • Красный – 0xF800
  • Зеленый – 0x07E0
  • Синий – 0x001F
  • Черный – 0x0000
  • Белый – 0xFFFF

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

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

STM32CubeMx ST7735 pinout

Тактовые частоты с учетом моего резонатора на 12 МГц такие:

STM32CubeMx ST7735 RCC

А настройки SPI:

SPI settings

DMA в базовом проекте использовать не будем, но я обычно рекомендую не пренебрегать возможностями контроллера. И, соответственно, для перебрасывания больших объемов данных задействовать DMA.

Итак, меньше слов, больше дела, генерируем проект и сразу добавляем в него файлы для будущей библиотеки ST7735:

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

Начинаем с необходимых определений в файле st7735.h. Первым делом, несколько цветов, чтобы наши тесты были более наглядными:

#define ST7735_COLOR_RED                                                0xF800
#define ST7735_COLOR_GREEN                                              0x07E0
#define ST7735_COLOR_BLUE                                               0x001F
#define ST7735_COLOR_YELLOW                                             0xFFE0
#define ST7735_COLOR_WHITE                                              0xFFFF
#define ST7735_COLOR_BLACK                                              0x0000
#define ST7735_COLOR_ORANGE                                             0xFA20

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

#define ST7735_RESET_PORT                                               (GPIOB)
#define ST7735_RESET_PIN                                                (GPIO_PIN_7)

#define ST7735_CS_PORT                                                  (GPIOC)
#define ST7735_CS_PIN                                                   (GPIO_PIN_11)

#define ST7735_DC_PORT                                                  (GPIOB)
#define ST7735_DC_PIN                                                   (GPIO_PIN_6)

Зафиксируем также размеры используемого дисплея. Для моего 128*160:

#define ST7735_X_SIZE                                                   128
#define ST7735_Y_SIZE                                                   160

Далее в файле идут всяческие определения команд/регистров, полный код файлов и ссылку на готовый проект можно будет найти в конце статьи. А пока продолжаем с библиотекой… И далее наши конструктивные действия будут сосредоточены в файле st7735.c. Чтобы использовать hspi1 добавляем:

extern SPI_HandleTypeDef hspi1;

Начинаем последовательно от самого низкого уровня и выше реализовывать необходимые функции. И первая функция – это отправка байта по интерфейсу SPI:

/*----------------------------------------------------------------------------*/
void ST7735_SendByte(uint8_t data)
{
   HAL_SPI_Transmit(&hspi1, &data, 1, ST7735_SPI_TIMEOUT);
}



/*----------------------------------------------------------------------------*/

Контроллер ST7735 может ожидать от нас либо байт команды, либо байт данных. С точки зрения SPI в этих случаях все одинаково, отличается лишь уровень сигнала на D/C:

/*----------------------------------------------------------------------------*/
void ST7735_SendCommand(uint8_t data)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_RESET);
  ST7735_SendByte(data);
}



/*----------------------------------------------------------------------------*/
void ST7735_SendData(uint8_t data)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);  
  ST7735_SendByte(data);
}



/*----------------------------------------------------------------------------*/
void ST7735_SendDataMultiple(uint8_t *data, uint32_t num)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);
  
  for (uint32_t i = 0; i < num; i++)
  {
    ST7735_SendByte(*data);
    data++;
  }
}



/*----------------------------------------------------------------------------*/

Инициализация дисплея.

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

/*----------------------------------------------------------------------------*/
void ST7735_Init()
{
  HAL_GPIO_WritePin(ST7735_CS_PORT, ST7735_CS_PIN, GPIO_PIN_RESET);
  
  HAL_GPIO_WritePin(ST7735_RESET_PORT, ST7735_RESET_PIN, GPIO_PIN_SET);
  HAL_Delay(5);
  HAL_GPIO_WritePin(ST7735_RESET_PORT, ST7735_RESET_PIN, GPIO_PIN_RESET);
  HAL_Delay(5);
  HAL_GPIO_WritePin(ST7735_RESET_PORT, ST7735_RESET_PIN, GPIO_PIN_SET);
  HAL_Delay(5);
    
  ST7735_SendCommand(ST7735_SWRESET);  
  HAL_Delay(150);

  ST7735_SendCommand(ST7735_SLPOUT);  
  HAL_Delay(500);

  ST7735_SendCommand(ST7735_FRMCTR1);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);

  ST7735_SendCommand(ST7735_FRMCTR2);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);

  ST7735_SendCommand(ST7735_FRMCTR3);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);

  ST7735_SendCommand(ST7735_INVCTR);
  ST7735_SendData(0x07);

  ST7735_SendCommand(ST7735_PWCTR1);
  ST7735_SendData(0xA2);
  ST7735_SendData(0x02);
  ST7735_SendData(0x84);

  ST7735_SendCommand(ST7735_PWCTR2);
  ST7735_SendData(0xC5);

  ST7735_SendCommand(ST7735_PWCTR3);
  ST7735_SendData(0x0A);
  ST7735_SendData(0x00);

  ST7735_SendCommand(ST7735_PWCTR4);
  ST7735_SendData(0x8A);
  ST7735_SendData(0x2A);

  ST7735_SendCommand(ST7735_PWCTR5);
  ST7735_SendData(0x8A);
  ST7735_SendData(0xEE);

  ST7735_SendCommand(ST7735_VMCTR1);
  ST7735_SendData(0x0E);

  ST7735_SendCommand(ST7735_INVOFF);

  ST7735_SendCommand(ST7735_MADCTL);
  ST7735_SendData(0xC0);

  ST7735_SendCommand(ST7735_COLMOD);
  ST7735_SendData(0x05);

  ST7735_SendCommand(ST7735_GMCTRP1);
  ST7735_SendData(0x02);
  ST7735_SendData(0x1c);
  ST7735_SendData(0x07);
  ST7735_SendData(0x12);
  ST7735_SendData(0x37);
  ST7735_SendData(0x32);
  ST7735_SendData(0x29);
  ST7735_SendData(0x2d);
  ST7735_SendData(0x29);
  ST7735_SendData(0x25);
  ST7735_SendData(0x2B);
  ST7735_SendData(0x39);	
  ST7735_SendData(0x00);
  ST7735_SendData(0x01);
  ST7735_SendData(0x03);
  ST7735_SendData(0x10);

  ST7735_SendCommand(ST7735_GMCTRN1);
  ST7735_SendData(0x03);
  ST7735_SendData(0x1d);
  ST7735_SendData(0x07);
  ST7735_SendData(0x06);
  ST7735_SendData(0x2E);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x29);
  ST7735_SendData(0x2D);
  ST7735_SendData(0x2E);
  ST7735_SendData(0x2E);
  ST7735_SendData(0x37);
  ST7735_SendData(0x3F);	
  ST7735_SendData(0x00);
  ST7735_SendData(0x00);
  ST7735_SendData(0x02);
  ST7735_SendData(0x10);

  ST7735_SendCommand(ST7735_NORON);
  HAL_Delay(10);

  ST7735_SendCommand(ST7735_DISPON);
  HAL_Delay(100);
}



/*----------------------------------------------------------------------------*/

Установка рабочей области.

Для того, чтобы выводить на дисплей графическую информацию, нужно задать область, с которой мы хотим работать. Для этого используются команды CASET и RASET, то есть установка начальных и конечных значений колонок (x) и рядов (y) пикселей дисплея. Разбираем подробно и наглядно:

Координаты дисплея

Допустим мы хотим работать с областью размером 25 * 50 пикселей, расположенной в левом верхнем углу. Суммарно дисплей представляет из себя полотно 128*160 пикселей. Так что для достижения наших целей мы должны установить необходимые пределы командами CASET и RASET (псевдокод):

  • CASET(0, 25)
  • RASET(0, 50)
  • Изменения после этих команд вступают в силу после выполнения команды RAMWR
  • Далее можно отправлять данные для задания цвета пикселей.

Формат команд такой, рассмотрим на примере CASET:

Команда CASET

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

/*----------------------------------------------------------------------------*/
void ST7735_SetColAddr(uint16_t cStart, uint16_t cStop)
{
  uint8_t data[4];
  
  data[0] = (cStart & 0xFF00) >> 8;
  data[1] = cStart & 0x00FF;
  data[2] = (cStop & 0xFF00) >> 8;
  data[3] = cStop & 0x00FF;
  
  ST7735_SendCommand(ST7735_CASET);
  ST7735_SendDataMultiple(data, 4);
}



/*----------------------------------------------------------------------------*/
void ST7735_SetRowAddr(uint16_t rStart, uint16_t rStop)
{
  uint8_t data[4];
  
  data[0] = (rStart & 0xFF00) >> 8;
  data[1] = rStart & 0x00FF;
  data[2] = (rStop & 0xFF00) >> 8;
  data[3] = rStop & 0x00FF;

  ST7735_SendCommand(ST7735_RASET);
  ST7735_SendDataMultiple(data, 4);
}



/*----------------------------------------------------------------------------*/

Отрисовка графических примитивов.

В качестве наглядного эксперимента будем выводить на дисплей основополагающую фигуру – прямоугольник. Задавать аргументами будем:

  • cStart, rStart – координата левого верхнего угла
  • cStop, rStop – координата правого нижнего угла
  • color – цвет заливки

Реализация:

/*----------------------------------------------------------------------------*/
void ST7735_DrawRect(uint16_t cStart, uint16_t rStart, uint16_t cStop, uint16_t rStop, uint16_t color)
{
  ST7735_SetColAddr(cStart, cStop - 1);
  ST7735_SetRowAddr(rStart, rStop - 1);
  
  ST7735_SendCommand(ST7735_RAMWR);
  
  uint32_t size = (cStop - cStart) * (rStop - rStart);
  uint8_t colorBytes[2];
  colorBytes[0] = (color & 0xFF00) >> 8;
  colorBytes[1] = color & 0x00FF;
  
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);
  
  for (uint32_t i = 0; i < size; i++)
  {
    ST7735_SendByte(colorBytes[0]);
    ST7735_SendByte(colorBytes[1]);
  }
}



/*----------------------------------------------------------------------------*/

В целом, тут ничего нового:

  • Устанавливаем рабочую область при помощи ST7735_SetColAddr() и ST7735_SetRowAddr()
  • Далее та команда, о которой я упоминал – ST7735_SendCommand(ST7735_RAMWR)
  • Рассчитываем размер области в пикселях
  • Разбиваем 16 бит цветовой информации на байты и выдаем на SPI для каждого пикселя. Соответственно, цвета всех пикселей у нас будут одинаковыми, заливка сплошная и однородная.

Ну и все, запускаем тестирование. Для этого будем рисовать прямоугольники размером на весь дисплей разных цветов. В main.c в цикле while(1) добавляем (напоминаю, что полный код под спойлерами в конце статьи):

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    // Demo 1
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_RED);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_GREEN);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_BLUE);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_YELLOW);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_WHITE);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_BLACK);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_ORANGE);
    // End of demo 1
  }
  /* USER CODE END 3 */

Само собой не забываем про инициализацию ST7735:

/* USER CODE BEGIN 2 */
  ST7735_Init();

  /* USER CODE END 2 */

Компилируем и прошиваем. Результат выглядит следующим образом:

Все вроде бы работает. Но ключевое слово тут “вроде бы”. Хотя при первом использовании дисплея проблема может быть неочевидна. А проблема заключается в скорости отрисовки, которая может и должна быть значительно выше. Это как раз один из случаев, когда быстродействия библиотеки HAL не хватает. Поэтому отправку данных мы осуществим “вручную”. На практике это означает ряд программных изменений.

Ускорение библиотеки для ST7735.

Изменяем функцию отправки данных:

/* ---------------------------------------------------------------------------*/
void ST7735_SendByte(uint8_t data)
{
  while((SPI1->SR & SPI_SR_TXE) == RESET);  
  SPI1->DR = data;
}



/* ---------------------------------------------------------------------------*/

А также добавляем функцию ожидания завершения передачи:

/* ---------------------------------------------------------------------------*/
void ST7735_WaitLastData()
{
  while((SPI1->SR & SPI_SR_TXE) == RESET);
  while((SPI1->SR & SPI_SR_BSY) != RESET);
}



/* ---------------------------------------------------------------------------*/

В чем разница ожидания флага окончания передачи в первой функции и зачем нужна вторая? Разница в том, что флаг SPI_SR_TXE сигнализирует о том, что данные из регистра SPI1->DR ушли в сдвиговый регистр STM32, откуда они будут выданы уже наружу. А флаг SPI_SR_BSY – это уже сигнал о полном окончании процесса передачи. Иногда разницы существенной нет, но не в этом случае.

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

  • Выставляем низкий уровень на выводе D/C
  • Помещаем в регистр SPI1->DR байт команды
  • Проверяем флаг SPI_SR_TXE перед передачей байта данных
  • Выставляем высокий уровень на D/C
  • Отправляем байт данных

Все, здесь возникает проблема – между 3-м и 4-м шагами. Поскольку мы проверили только флаг SPI_SR_TXE перед следующей передачей, значит данные ушли в сдвиговый регистр, НО(!) не факт, что были переданы дисплею. А мы уже меняем уровень на D/C.

Резюме здесь такое – в процессе отправки, например, большого кол-ва байт данных друг за другом достаточно проверять лишь флаг SPI_SR_TXE. Что оптимально с точки зрения производительности. Но по завершению отправки всех данных или после отправки команды нужно учесть и флаг SPI_SR_BSY. Что получаем в коде:

/* ---------------------------------------------------------------------------*/
void ST7735_WaitLastData()
{
  while((SPI1->SR & SPI_SR_TXE) == RESET);
  while((SPI1->SR & SPI_SR_BSY) != RESET);
}



/* ---------------------------------------------------------------------------*/
void ST7735_SendCommand(uint8_t data)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_RESET);
  ST7735_SendByte(data);
  ST7735_WaitLastData();
}



/* ---------------------------------------------------------------------------*/
void ST7735_SendData(uint8_t data)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);  
  ST7735_SendByte(data);
  ST7735_WaitLastData();
}



/* ---------------------------------------------------------------------------*/
void ST7735_SendDataMultiple(uint8_t *data, uint32_t num)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);
  
  for (uint32_t i = 0; i < num; i++)
  {
    ST7735_SendByte(*data);
    data++;
  }
  
  ST7735_WaitLastData();
}



/* ---------------------------------------------------------------------------*/

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

SPI1->CR1 |= SPI_CR1_SPE;

Это легко упустить из виду и сложно отловить, когда ничего не работает.

Остается модифицировать функцию рисования прямоугольника:

/* ---------------------------------------------------------------------------*/
void ST7735_DrawRect(uint16_t cStart, uint16_t rStart, uint16_t cStop, uint16_t rStop, uint16_t color)
{
  ST7735_SetColAddr(cStart, cStop - 1);
  ST7735_SetRowAddr(rStart, rStop - 1);
  
  ST7735_SendCommand(ST7735_RAMWR);
  
  uint32_t size = (cStop - cStart) * (rStop - rStart);
  uint8_t colorBytes[2];
  colorBytes[0] = (color & 0xFF00) >> 8;
  colorBytes[1] = color & 0x00FF;
  
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);
  
  for (uint32_t i = 0; i < size; i++)
  {
    ST7735_SendByte(colorBytes[0]);
    ST7735_SendByte(colorBytes[1]);
  }
  
  ST7735_WaitLastData();
}



/* ---------------------------------------------------------------------------*/

Запускаем тот же самый тест:

Результат налицо – не прибавить, не убавить.

Проведем еще один демо-тест для проверки работоспособности нашей координатной системы. Расположим фигуры таким образом:

Пример программы для ST7735 и STM32

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

  /* USER CODE BEGIN 2 */
  ST7735_Init();
  
  // Demo 2
  ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_BLACK);
  
  ST7735_DrawRect(0, 0, 35, 35, ST7735_COLOR_RED);
  ST7735_DrawRect(35, 35, 85, 100, ST7735_COLOR_GREEN);
  ST7735_DrawRect(85, 100, 128, 160, ST7735_COLOR_BLUE);
  // End of demo 2

  /* USER CODE END 2 */

И полученный результат соответствует нашим планам:

Тест библиотеки для ST7735

Я изначально собирался описать еще процесс вывода изображения на этот дисплей, но и эта статья получилась гораздо длиннее, чем я планировал, и с выводом изображения есть свои нюансы, поэтому будет в ближайшие дни вторая часть, а на сегодня на этом заканчиваем!

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "st7735.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
SPI_HandleTypeDef hspi1;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_SPI1_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern unsigned short testImage[];

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_SPI1_Init();
  /* USER CODE BEGIN 2 */
  ST7735_Init();
  
  // Demo 2
 /* ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_BLACK);
  
  ST7735_DrawRect(0, 0, 35, 35, ST7735_COLOR_RED);
  ST7735_DrawRect(35, 35, 85, 100, ST7735_COLOR_GREEN);
  ST7735_DrawRect(85, 100, 128, 160, ST7735_COLOR_BLUE); */
  // End of demo 2

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    // Demo 1
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_RED);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_GREEN);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_BLUE);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_YELLOW);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_WHITE);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_BLACK);
    ST7735_DrawRect(0, 0, ST7735_X_SIZE, ST7735_Y_SIZE, ST7735_COLOR_ORANGE);
    // End of demo 1
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief SPI1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  /* SPI1 parameter configuration*/
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  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_4;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC11 */
  GPIO_InitStruct.Pin = GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pins : PB6 PB7 */
  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/**
  ******************************************************************************
  * @file           : st7735.c
  * @brief          : ST7735 driver
  * @author         : MicroTechnics (microtechnics.ru)
  ******************************************************************************
  */



/* Includes ------------------------------------------------------------------*/

#include "st7735.h"



/* Declarations and definitions ----------------------------------------------*/

extern SPI_HandleTypeDef hspi1;



/* Functions -----------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
void ST7735_SendByte(uint8_t data)
{
  // HAL_SPI_Transmit(&hspi1, &data, 1, ST7735_SPI_TIMEOUT);
  while((SPI1->SR & SPI_SR_TXE) == RESET);  
  SPI1->DR = data;
}



/*----------------------------------------------------------------------------*/
void ST7735_WaitLastData()
{
  while((SPI1->SR & SPI_SR_TXE) == RESET);
  while((SPI1->SR & SPI_SR_BSY) != RESET);
}



/*----------------------------------------------------------------------------*/
void ST7735_SendCommand(uint8_t data)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_RESET);
  ST7735_SendByte(data);
  ST7735_WaitLastData();
}



/*----------------------------------------------------------------------------*/
void ST7735_SendData(uint8_t data)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);  
  ST7735_SendByte(data);
  ST7735_WaitLastData();
}



/*----------------------------------------------------------------------------*/
void ST7735_SendDataMultiple(uint8_t *data, uint32_t num)
{
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);
  
  for (uint32_t i = 0; i < num; i++)
  {
    ST7735_SendByte(*data);
    data++;
  }
  
  ST7735_WaitLastData();
}



/*----------------------------------------------------------------------------*/
void ST7735_Init()
{ 
  SPI1->CR1 |= SPI_CR1_SPE;
  HAL_GPIO_WritePin(ST7735_CS_PORT, ST7735_CS_PIN, GPIO_PIN_RESET);
  
  HAL_GPIO_WritePin(ST7735_RESET_PORT, ST7735_RESET_PIN, GPIO_PIN_SET);
  HAL_Delay(5);
  HAL_GPIO_WritePin(ST7735_RESET_PORT, ST7735_RESET_PIN, GPIO_PIN_RESET);
  HAL_Delay(5);
  HAL_GPIO_WritePin(ST7735_RESET_PORT, ST7735_RESET_PIN, GPIO_PIN_SET);
  HAL_Delay(5);
    
  ST7735_SendCommand(ST7735_SWRESET);  
  HAL_Delay(150);

  ST7735_SendCommand(ST7735_SLPOUT);  
  HAL_Delay(500);

  ST7735_SendCommand(ST7735_FRMCTR1);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);

  ST7735_SendCommand(ST7735_FRMCTR2);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);

  ST7735_SendCommand(ST7735_FRMCTR3);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);
  ST7735_SendData(0x01);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x2D);

  ST7735_SendCommand(ST7735_INVCTR);
  ST7735_SendData(0x07);

  ST7735_SendCommand(ST7735_PWCTR1);
  ST7735_SendData(0xA2);
  ST7735_SendData(0x02);
  ST7735_SendData(0x84);

  ST7735_SendCommand(ST7735_PWCTR2);
  ST7735_SendData(0xC5);

  ST7735_SendCommand(ST7735_PWCTR3);
  ST7735_SendData(0x0A);
  ST7735_SendData(0x00);

  ST7735_SendCommand(ST7735_PWCTR4);
  ST7735_SendData(0x8A);
  ST7735_SendData(0x2A);

  ST7735_SendCommand(ST7735_PWCTR5);
  ST7735_SendData(0x8A);
  ST7735_SendData(0xEE);

  ST7735_SendCommand(ST7735_VMCTR1);
  ST7735_SendData(0x0E);

  ST7735_SendCommand(ST7735_INVOFF);

  ST7735_SendCommand(ST7735_MADCTL);
  ST7735_SendData(0xC0);

  ST7735_SendCommand(ST7735_COLMOD);
  ST7735_SendData(0x05);

  ST7735_SendCommand(ST7735_GMCTRP1);
  ST7735_SendData(0x02);
  ST7735_SendData(0x1c);
  ST7735_SendData(0x07);
  ST7735_SendData(0x12);
  ST7735_SendData(0x37);
  ST7735_SendData(0x32);
  ST7735_SendData(0x29);
  ST7735_SendData(0x2d);
  ST7735_SendData(0x29);
  ST7735_SendData(0x25);
  ST7735_SendData(0x2B);
  ST7735_SendData(0x39);	
  ST7735_SendData(0x00);
  ST7735_SendData(0x01);
  ST7735_SendData(0x03);
  ST7735_SendData(0x10);

  ST7735_SendCommand(ST7735_GMCTRN1);
  ST7735_SendData(0x03);
  ST7735_SendData(0x1d);
  ST7735_SendData(0x07);
  ST7735_SendData(0x06);
  ST7735_SendData(0x2E);
  ST7735_SendData(0x2C);
  ST7735_SendData(0x29);
  ST7735_SendData(0x2D);
  ST7735_SendData(0x2E);
  ST7735_SendData(0x2E);
  ST7735_SendData(0x37);
  ST7735_SendData(0x3F);	
  ST7735_SendData(0x00);
  ST7735_SendData(0x00);
  ST7735_SendData(0x02);
  ST7735_SendData(0x10);

  ST7735_SendCommand(ST7735_NORON);
  HAL_Delay(10);

  ST7735_SendCommand(ST7735_DISPON);
  HAL_Delay(100);

}



/*----------------------------------------------------------------------------*/
void ST7735_SetColAddr(uint16_t cStart, uint16_t cStop)
{
  uint8_t data[4];
  
  data[0] = (cStart & 0xFF00) >> 8;
  data[1] = cStart & 0x00FF;
  data[2] = (cStop & 0xFF00) >> 8;
  data[3] = cStop & 0x00FF;
  
  ST7735_SendCommand(ST7735_CASET);
  ST7735_SendDataMultiple(data, 4);
}



/*----------------------------------------------------------------------------*/
void ST7735_SetRowAddr(uint16_t rStart, uint16_t rStop)
{
  uint8_t data[4];
  
  data[0] = (rStart & 0xFF00) >> 8;
  data[1] = rStart & 0x00FF;
  data[2] = (rStop & 0xFF00) >> 8;
  data[3] = rStop & 0x00FF;

  ST7735_SendCommand(ST7735_RASET);
  ST7735_SendDataMultiple(data, 4);
}



/*----------------------------------------------------------------------------*/
void ST7735_DrawRect(uint16_t cStart, uint16_t rStart, uint16_t cStop, uint16_t rStop, uint16_t color)
{
  ST7735_SetColAddr(cStart, cStop - 1);
  ST7735_SetRowAddr(rStart, rStop - 1);
  
  ST7735_SendCommand(ST7735_RAMWR);
  
  uint32_t size = (cStop - cStart) * (rStop - rStart);
  uint8_t colorBytes[2];
  colorBytes[0] = (color & 0xFF00) >> 8;
  colorBytes[1] = color & 0x00FF;
  
  HAL_GPIO_WritePin(ST7735_DC_PORT, ST7735_DC_PIN, GPIO_PIN_SET);
  
  for (uint32_t i = 0; i < size; i++)
  {
    ST7735_SendByte(colorBytes[0]);
    ST7735_SendByte(colorBytes[1]);
  }
  
  ST7735_WaitLastData();
}



/*----------------------------------------------------------------------------*/
/**
  ******************************************************************************
  * @file           : st7735.c
  * @brief          : ST7735 driver header
  * @author         : MicroTechnics (microtechnics.ru)
  ******************************************************************************
  */

#ifndef ST7735_H
#define ST7735_H



/* Includes ------------------------------------------------------------------*/

#include "stm32f1xx_hal.h"
#include "stdbool.h"



/* Declarations and definitions ----------------------------------------------*/

#define ST7735_COLOR_RED                                                0xF800
#define ST7735_COLOR_GREEN                                              0x07E0
#define ST7735_COLOR_BLUE                                               0x001F
#define ST7735_COLOR_YELLOW                                             0xFFE0
#define ST7735_COLOR_WHITE                                              0xFFFF
#define ST7735_COLOR_BLACK                                              0x0000
#define ST7735_COLOR_ORANGE                                             0xFA20

#define ST7735_RESET_PORT                                               (GPIOB)
#define ST7735_RESET_PIN                                                (GPIO_PIN_7)

#define ST7735_CS_PORT                                                  (GPIOC)
#define ST7735_CS_PIN                                                   (GPIO_PIN_11)

#define ST7735_DC_PORT                                                  (GPIOB)
#define ST7735_DC_PIN                                                   (GPIO_PIN_6)

#define ST7735_X_SIZE                                                   128
#define ST7735_Y_SIZE                                                   160

#define ST7735_NOP                                                      0x00
#define ST7735_SWRESET                                                  0x01
#define ST7735_RDDID                                                    0x04
#define ST7735_RDDST                                                    0x09

#define ST7735_SLPIN                                                    0x10
#define ST7735_SLPOUT                                                   0x11
#define ST7735_PTLON                                                    0x12
#define ST7735_NORON                                                    0x13

#define ST7735_INVOFF                                                   0x20
#define ST7735_INVON                                                    0x21
#define ST7735_DISPOFF                                                  0x28
#define ST7735_DISPON                                                   0x29
#define ST7735_CASET                                                    0x2A
#define ST7735_RASET                                                    0x2B
#define ST7735_RAMWR                                                    0x2C
#define ST7735_RAMRD                                                    0x2E

#define ST7735_PTLAR                                                    0x30
#define ST7735_COLMOD                                                   0x3A
#define ST7735_MADCTL                                                   0x36

#define ST7735_FRMCTR1                                                  0xB1
#define ST7735_FRMCTR2                                                  0xB2
#define ST7735_FRMCTR3                                                  0xB3
#define ST7735_INVCTR                                                   0xB4
#define ST7735_DISSET5                                                  0xB6

#define ST7735_PWCTR1                                                   0xC0
#define ST7735_PWCTR2                                                   0xC1
#define ST7735_PWCTR3                                                   0xC2
#define ST7735_PWCTR4                                                   0xC3
#define ST7735_PWCTR5                                                   0xC4
#define ST7735_VMCTR1                                                   0xC5

#define ST7735_RDID1                                                    0xDA
#define ST7735_RDID2                                                    0xDB
#define ST7735_RDID3                                                    0xDC
#define ST7735_RDID4                                                    0xDD

#define ST7735_PWCTR6                                                   0xFC

#define ST7735_GMCTRP1                                                  0xE0
#define ST7735_GMCTRN1                                                  0xE1

#define ST7735_SPI_TIMEOUT                                              100



/* Functions -----------------------------------------------------------------*/

extern void ST7735_Init();
extern void ST7735_DrawRect(uint16_t cStart, uint16_t rStart, uint16_t cStop, uint16_t rStop, uint16_t color);



#endif // #ifndef ST7735_H

Ссылка на проект – MT_ST7735_Example_1

Подписаться
Уведомление о
guest
0 комментариев
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x