Визуальная электроника

Одному из наших заказчиков понадобилось измерить частоту сигнала в полевых условиях. Весь процесс происходил на Крайнем Севере, поэтому кроме мультиметра и индикаторной отвертки мы ничего не нашли. Под рукой была только наша плата VE-EP4CE10E и VGA монитор. Пришла идея сделать простенький частотомер с VGA выходом. Напомню, что частотоме́р это радиоизмерительный прибор для определения частоты периодического процесса или частот гармонических составляющих спектра сигнала.

Первая проблема с которой мы столкнулись, это способ записи изображения цифр в память FPGA. Блоки памяти можно инициализировать либо *.HEX либо *.MIF файлом. Изображение наших цифр мы решили хранить в 24 битном *.BMP файле. Естественно нам понадобилась программа конвертер из *.BMP в *.MIF. Поискав по просторам интернет ничего стоящего мы не нашли, и решили по быстрому соорудить свою на Qt5. Вот ее код:

 

C++ Code:
  1. #include "mainwindow.h"
  2. #include "ui_mainwindow.h"
  3.  
  4. #include <QFileDialog>
  5. #include <QString>
  6. #include <QDebug>
  7.  
  8. QByteArray picture;
  9. unsigned int pic_width=0, pic_height=0;
  10.  
  11. MainWindow::MainWindow(QWidget *parent) :
  12. QMainWindow(parent),
  13. ui(new Ui::MainWindow)
  14. {
  15. ui->setupUi(this);
  16.  
  17. }
  18.  
  19. MainWindow::~MainWindow()
  20. {
  21. delete ui;
  22. }
  23.  
  24. //Открываем BMP файл
  25. void MainWindow::on_pushButton_clicked()
  26. {
  27. QGraphicsScene *scene = new QGraphicsScene;
  28. QString width,height;
  29. QString path;
  30. path = QFileDialog::getOpenFileName(0,"Open BMP", QApplication::applicationDirPath()+"/files", "*.bmp");
  31. QFile fileIn(path);
  32.  
  33. if(fileIn.open(QIODevice::ReadOnly))
  34. {
  35. picture = fileIn.readAll(); //читаем файл
  36. fileIn.close(); //закрываем файл
  37. scene->addPixmap(QPixmap(path)); //добавляем картинку
  38. pic_height=scene->height(); //определяем размеры
  39. pic_width=scene->width();
  40. height="Heigth="+QString::number(scene->height());
  41. width="Width="+QString::number(scene->width());
  42. ui->label->setText(height);
  43. ui->label_2->setText(width);
  44. ui->graphicsView->setGeometry(10,40,scene->width(),scene->height());
  45. ui->graphicsView->setScene(scene); //выводим картинку на форму
  46. }
  47. }
  48. //Формируем MIF файл
  49. void MainWindow::on_pushButton_2_clicked()
  50. {
  51. QString mif,adr_str,rgb_str;
  52. unsigned long adr1,adr2,rgb;
  53. unsigned char R,G,B;
  54.  
  55. if(pic_height*pic_width!=0)
  56. {
  57. mif.append("-- Copyright (C) 1991-2015 Altera Corporation. All rights reserved\n");
  58. mif.append("-- Your use of Altera Corporation's design tools, logic functions\n");
  59. mif.append("-- and other software and tools, and its AMPP partner logic\n");
  60. mif.append("-- functions, and any output files from any of the foregoing\n");
  61. mif.append("-- (including device programming or simulation files), and any\n");
  62. mif.append("-- associated documentation or information are expressly subject\n");
  63. mif.append("-- to the terms and conditions of the Altera Program License\n");
  64. mif.append("-- Subscription Agreement, the Altera Quartus II License Agreement,\n");
  65. mif.append("-- the Altera MegaCore Function License Agreement, or other\n");
  66. mif.append("-- applicable license agreement, including, without limitation,\n");
  67. mif.append("-- that your use is for the sole purpose of programming logic\n");
  68. mif.append("-- devices manufactured by Altera and sold by Altera or its\n");
  69. mif.append("-- authorized distributors. Please refer to the applicable\n");
  70. mif.append("-- agreement for further details.\n");
  71. mif.append(" \n");
  72. mif.append("-- Quartus II generated Memory Initialization File (.mif)\n");
  73. mif.append(" \n");
  74. mif.append("WIDTH=24;\n");
  75. mif.append("DEPTH="+QString::number(pic_height*pic_width)+";\n");
  76. mif.append(" \n");
  77. mif.append("ADDRESS_RADIX=UNS;\n");
  78. mif.append("DATA_RADIX=UNS;\n");
  79. mif.append(" \n");
  80. mif.append("CONTENT BEGIN\n");
  81.  
  82. //Перебираем пиксели bmp файла
  83. for(unsigned int dy=0;dy<pic_height;dy++)
  84. {
  85. for(unsigned int dx=0;dx<pic_width;dx++)
  86. {
  87. adr1=pic_width*dy+dx;
  88. adr2=pic_width*(pic_height-dy-1)+dx;
  89. R=picture.data()[54+adr2*3];
  90. G=picture.data()[54+adr2*3+1];
  91. B=picture.data()[54+adr2*3+2];
  92. rgb=R*65536 + G*256 + B;
  93. adr_str=QString::number(adr1);
  94. rgb_str=QString::number(rgb);
  95. mif.append(adr_str);
  96. mif.append(":");
  97. mif.append(rgb_str);
  98. mif.append(";\n");
  99. }
  100. }
  101.  
  102. mif.append("END;\n");
  103.  
  104. //Сораняем файл
  105. QString path;
  106. path = QFileDialog::getSaveFileName(0,"Save MIF", QApplication::applicationDirPath()+"/files", "*.mif");
  107. QFile fileOut(path);
  108. if(fileOut.open(QIODevice::WriteOnly))
  109. {
  110. QTextStream writeStream(&fileOut); // Создаем объект класса QTextStream
  111. // и передаем ему адрес объекта fileOut
  112. writeStream << mif; // Посылаем строку в поток для записи
  113. fileOut.close(); // Закрываем файл
  114. }
  115. }
  116. }

В принципе тут все просто. Открываем файл *.BMP пробегаем по пикселям, преобразуем 3 байта RGB в 24 битное число, и формируем *.MIF файл. В этой версии преобразуется изображение с глубиной цвета 24 бита. В принципе не сложно доработать для любого изображения. Вот так выглядит наша программа:

bmp to mif

Далее создадим проект в Quartus II:

pic2

Генератор развертки VGA сигнала мы уже использовали в своих проектах. Модуль контроля формирует один раз в секунду импульсы окончания счета, и записи полученного значения в регистр вывода на экран. Код BCD счетчика достаточно банальный. Поэтому рассмотрим код преобразование десятичного счетчика в изображение.

Verilog Code:
  1. module pic_gen(
  2. //vga
  3. input [11:0]char_count,
  4. input [11:0]line_count,
  5. input wire blank,
  6. input wire char_clock,
  7.  
  8. output reg [3:0]red_out,
  9. output reg [3:0]green_out,
  10. output reg [3:0]blue_out,
  11.  
  12. //built-in memory
  13. input [23:0]bmp_data,
  14. output reg[13:0]bmp_adress,
  15.  
  16. //counter input
  17. input [31:0]bcd_cnt,
  18. input wire strobe
  19. );
  20.  
  21. reg [31:0]loc_cnt;
  22. reg [11:0]dx;
  23. reg [11:0]dy;
  24.  
  25. //по стробу защелкиваем счетчик
  26. always @(posedge strobe)
  27. begin
  28. loc_cnt = bcd_cnt;
  29. end
  30.  
  31. //Вывод изображения
  32. always @(posedge char_clock)
  33. begin
  34. //Ширина изображения одной цифры не превышает 32 пикселей
  35. dx<=char_count & 5'b11111;
  36.  
  37. //Выбор чифры в знакоместе, в зависимости от значения счетчика
  38. if(char_count<32)
  39. begin
  40. bmp_adress <= dx+line_count*320+32*loc_cnt[31:28];
  41. end
  42. else if(char_count>=32 && char_count<64)
  43. begin
  44. bmp_adress <= dx+line_count*320+32*loc_cnt[27:24];
  45. end
  46. else if(char_count>=64 && char_count<96)
  47. begin
  48. bmp_adress <= dx+line_count*320+32*loc_cnt[23:20];
  49. end
  50. else if(char_count>=96 && char_count<128)
  51. begin
  52. bmp_adress <= dx+line_count*320+32*loc_cnt[19:16];
  53. end
  54. else if(char_count>=128 && char_count<160)
  55. begin
  56. bmp_adress <= dx+line_count*320+32*loc_cnt[15:12];
  57. end
  58. else if(char_count>=160 && char_count<192)
  59. begin
  60. bmp_adress <= dx+line_count*320+32*loc_cnt[11:8];
  61. end
  62. else if(char_count>=192 && char_count<224)
  63. begin
  64. bmp_adress <= dx+line_count*320+32*loc_cnt[7:4];
  65. end
  66. else if(char_count>=224 && char_count<256)
  67. begin
  68. bmp_adress <= dx+line_count*320+32*loc_cnt[3:0];
  69. end
  70.  
  71. //Формирование изображения
  72. //Изображение за кадром, выводим черный цвет
  73. if(blank==0)
  74. begin
  75. blue_out <= 0;
  76. green_out <= 0;
  77. red_out <= 0;
  78. end
  79. //В окне 0,256,0,32 выводим нашу частоту
  80. else
  81. begin
  82. if(char_count<256 && line_count<32)<br /> begin<br /> blue_out <= (bmp_data >> 20) & 8'b00001111;<br /> green_out <= (bmp_data >> 12) & 8'b00001111;
  83. red_out <= (bmp_data >> 4) & 8'b00001111;
  84. end
  85. //Остальное закрашиваем белым
  86. else
  87. begin
  88. blue_out <= 4'b1111;
  89. green_out <= 4'b1111;
  90. red_out <= 4'b1111;
  91. end
  92. end
  93. end
  94.  
  95. endmodule

Логика программы в принципе понятна из комментариев. Напомню модуль встроенной памяти хранит изображение цифр от 0 до 9. Ну и в конце видео работы и исходные файлы:

Для начала мы измерили тактовую частоты ПЛИС (50 МГц) затем частоту кадровой развертки VGA (60 Гц) и наконец частоту строчной развертки (48.3 КГц). Как видно из видео, измерения довольно точные.

Исходные файлы проекта BMP to MIF: bmptomif_qt.zip

Файл проекта частотомера: freq_count.zip

Добавить комментарий