Рисуем изображение на экране осциллографа при помощи STM32.

Всем доброго времени суток! Как уже понятно из названия, сегодня мы разберемся как вывести произвольное изображение на экран осциллографа 🙂 И поможет нам в этом наш верный друг – микроконтроллер STM32.

Итак, мой комплект для экспериментов на сегодня:

Рисуем на экране осциллографа

У осциллографа есть режим XY, который мы и будем использовать. Идея этого режима максимально проста – сигнал одного из каналов осциллографа отклоняет луч по горизонтальной оси, а сигнал второго – по вертикальной. Таким образом, подавая разные сигналы на 2 канала осциллографа мы можем перемещать луч в 2D-плоскости экрана, что нам, собственно, и требуется.

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

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

То есть, например, мы действуем так:

  • выдаем на каналы ЦАП значения напряжений, соответствующие координатам одной точки на экране
  • выдаем значения для координат второй точки
  • повторяем эти операции в цикле

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

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

Произвольное изображение на осциллографе

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

Для первого примера выведем какую-нибудь простую фигуру из линий, пусть будет квадрат. Итак, создаем проект в STM32CubeMx и включаем два канала DAC. Я использую STM32F103VET6, так что это выводы PA4 и PA5.

В качестве изображений для вывода на экран я буду использовать картинки 512*512 пикселей, чтобы они примерно занимали весь экран осциллографа. Вообще это все можно настраивать/перемещать настройками осциллографа, меняя положение и цену деления шкалы прибора. У меня на ось X приходится 12 “клеток”, на ось Y – 8:

STM32 может выдать напряжение от 0 до 3.3 В. Ставим на осциллографе 200 мВ на деление, получаем следующее…

Весь диапазон 1-го канала (оси X) равен 12 клеток * 200 мВ = 2.4 В. То есть выдавая на первый канал ЦАП напряжения от 0 до 2.4 В мы охватим весь простор экрана осциллографа. DAC в STM32 12-битный, то есть напряжению 3.3 В на выходе соответствует цифровое значение 4095. Тогда 2.4 В это:

\frac{4095}{3.3} * 2.4 = 2978

Получаем, что выдавая напряжение на выход ЦАП мы должны оперировать цифровыми значениями от 0 до 2978.

Аналогично, для второго канала, только с учетом, что там 8 клеток * 200 мВ – диапазон значений ЦАП составляет (0 … 1985). На практике, будем выдавать чуть поменьше, чтобы было расстояние до краев экрана, плюс добавим сдвиг от 0, опять же, чтобы изображение не находилось в самом углу. На самом деле, сверх меры нет смысла заморачиваться с этим, поскольку на практике все будет видно, куда, что подвинуть и где увеличить/уменьшить амплитуду 🙂

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

Рисуем квадрат на экране осциллографа

Поскольку изображение квадратное, то на обе оси (на оба канала) будем выдавать значения в диапазоне от 0 до 1985. И добавим запас от краев экрана – в итоге выдаем на ЦАП от 300 до 1685:

#define MIN_DAC_VALUE                                            300
#define MAX_DAC_VALUE                                            1685
#define IMAGE_SIZE                                               512

Создаем массив с нашими токами:

uint16_t square[8] = {100, 100, 100, 400, 400, 400, 400, 100};

А в цикле while(1) работаем с DAC:

  for (uint16_t i = 0; i < 8; i += 2)
  {
    HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, MIN_DAC_VALUE + (square[i] * (MAX_DAC_VALUE - MIN_DAC_VALUE) / IMAGE_SIZE));
    HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_R, MIN_DAC_VALUE + (square[i + 1] * (MAX_DAC_VALUE - MIN_DAC_VALUE) / IMAGE_SIZE));
  
    usDelay(2);
  }

Для того, чтобы получить значения для DAC мы преобразуем и сдвигаем координаты точек из массива:

Out = Min + координата * \frac{размер \medspace изображения}{Max – Min}

Кроме того, мы добавили функцию задержки, для этого включаем таймер (TIM6) и настраиваем для него прерывания по переполнению. Предделитель частоты и период таймера такие, чтобы прерывание генерировалось каждые 10 мкс. Настройки в Cube приводить не буду, все делаем так же как тут – ссылка.

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

void usDelay(uint32_t time)
{
  usCnt = time;
  while(usCnt > 0);
}
void TIM6_IRQHandler(void)
{
  /* USER CODE BEGIN TIM6_IRQn 0 */

  /* USER CODE END TIM6_IRQn 0 */
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_IRQn 1 */
  if (usCnt > 0)
  {
    usCnt--;
  }
  
  /* USER CODE END TIM6_IRQn 1 */
}

Вот и все! Запускаем и смотрим на экран:

Все отработало четко по плану 🙂

Собственно, вот таким образом можно вывести произвольный контур, задав массив с координатами точек по аналогичной схеме!

Но, поскольку вручную считать и копировать координаты довольно утомительное и времязатратное занятие, я написал небольшую утилиту на Qt для генерации массива нужного вида. Работает это так: открываем нужное изображение-контур в программе, далее обводим его линией, либо расставляем точки по периметру и генерируем массив. Все максимально просто! Можно и просто рисовать произвольную форму и создавать массив, все работает точно по такому же принципу 🙂

Код утилиты разберем в отдельной статье, а сегодня я просто выкладываю ссылку на проект. В папку “build-…” вместе с ехешником я положил нужные для запуска dll, так что программу можно просто запускать по .exe файлу, без установки Qt и сборки проекта из исходников.

После генерации получаем .c файл с массивом и добавляем его в наш проект для IAR’а. Примеры массивов я разместил в папке Images (ссылка на проект в конце статьи). Указатель на массив и его размер упакованы в структуру PAINT_Image. Кроме того для отрисовки добавлена специальная функция, которая как раз и принимает в качестве аргумента указатель на эту структуру.

  • void PAINT_Draw(PAINT_Image* image);

Так что просто вызываем эту функцию, например:

PAINT_Draw(&catImage);

И получаем:

Занятие довольно забавное и интересное, так что я поигрался с разными контурами, вот такие получились результаты 🙂

Изображение на экране осциллографа

В общем-то вот все и готово! Следуя этим шагам можно нарисовать на экране осциллографа любое изображение (само собой учитывая физические ограничения, которые мы обсудили).

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

На этом все! Спасибо за внимание и до скорых встреч, до новых статей!

Поделиться!

Подписаться
Уведомление о
guest
4 Комментарий
старее
новее большинство голосов
Inline Feedbacks
View all comments
Павел
Павел
1 месяц назад

На выходных попробуем.

Никита
Никита
1 месяц назад

Попробую в Keil сделать. Есть подводные камни при переносе из IAR?

Присоединяйтесь!

Profile Profile Profile Profile Profile
Vkontakte
Twitter

Язык сайта

Август 2020
Пн Вт Ср Чт Пт Сб Вс
 12
3456789
10111213141516
17181920212223
24252627282930
31  

© 2013-2020 MicroTechnics.ru