Top.Mail.Ru

Рисуем изображение на экране осциллографа при помощи 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 мкс. Настройки в CubeMx приводить не буду, все делаем так же как тут - ссылка.

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

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 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Павел
Павел
3 лет назад

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

Никита
Никита
3 лет назад

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

4
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x