Генератор программного UART
Большинство контроллеров PIC младшего и среднего семейства имеют на борту всего один аппаратный USART-модуль, но часто бывает нужно управлять двумя и более устройствами посредством этого интерфейса. При отладке программы хорошо бы убедиться в том, что прораммный UART-приемник работает корректно. Вот пример скрипта, который генерирует на вход ПИКу сигнал UART. Скорость выбирается пользователем (по умолчанию 9600), данные берутся из файла.
Пример исходного файла data_uart.txt:
baudrate 19200 wait 1 ms 10 20 30 40 50 60 wait 3 ms 55 66 77 88
Здесь baudrate — команда на изменение скорости, wait — команда на выдержку паузы. Числа даны в десятичной системе счисления (как их задавать в шестнадцатеричной или в символьном виде, — не знаю).
В добавок, данный пример является некоей иллюстрацией синхронизации двух процессов через внутреннюю переменную. Скрипт разбит на два процесса: один читает файлы и формирует данные для генерации сигнала, а второй формирует сам сигнал. Процессы выполняются параллельно. В конкрентом случае можно было обойтись и без разделения программы на два процесса, т.к. у нас всего один процесс (и только в одном месте) запускает второй. Но в общем случае это может оказаться полезным, когда:
- байт может отправляется из разных мест процесса;
- байт может отправляться из разных процессов;
- во время отправки данных головной процесс должен заниматься своими делами.
Для синхронизации служит общая переменная UART_Work. Когда она = 0, процесс отправки байта свободен. Для отправки байта головной процесс должен подготовить данные для отправки (записать нужное значение в UART_Data), а потом установить UART_Work в «1», тем самым сообщив процессу отправки, что можно начинать работать. Процесс отправки сам обнулит эту переменную, когда байт будет полностью передан, сообщем тем самым основному процессу, что передатчик вновь свободен.
-------------------------------------------------------------------------------
--
-- Данный скрипт демонстрирует формирование программного UART сигнала на входе
-- контроллера PORTC, 5. Данные читаются из файла "data_uart.txt"
--
-------------------------------------------------------------------------------
-- Пример файла с данными:
-------------------------------------------------------------------------------
-- baudrate 19200
-- wait 1 ms
-- 10 20 30 40 50 60
-- wait 3 ms
-- 55 66 77 88
-------------------------------------------------------------------------------
testbench for "pic16f877a" is
begin
process is
begin
report "======================== SCL loaded OK! ==========================";
wait;
end process;
---------------------------------------------------
uart_send_byte: process is -- Подпрограмма вывода одного байта по последовательному
-- интерфейсу UART через вход RC5
variable UART_Delay : time; -- Длительность одного бита
variable UART_Data : byte; -- Передаваемый байт
variable UART_Bit : byte; -- Для выделения бита при передаче
variable UART_Counter : integer; -- Счетчик битов
variable UART_Work : integer; -- Установкой этой переменной в 1 из другого
-- процесса мы инициируем начало передачи.
begin
RC5 <= '1';
UART_Work := 0; -- Сообщаем головной программе, что процесс свободен,
-- моно передавать новый байт
while UART_Work == 0 loop -- Ожидаем команду на старт от основного процесса
wait for 1 ic;
end loop;
RC5 <= '0'; -- Формируем стартовый бит
wait for UART_Delay;
UART_Counter := 8; -- цикл для 8 бит данных
while UART_Counter > 0 loop
UART_Counter := UART_Counter - 1;
UART_Bit := UART_Data / 2; -- Выделяем младший бит
UART_Bit := UART_Data - UART_Bit * 2;
UART_Data := UART_Data / 2;
if UART_Bit == 0 then -- Отправляем его
RC5 <= '0';
else
RC5 <= '1';
end if;
wait for UART_Delay;
end loop;
RC5 <= '1'; -- Формируем стоповый бит
wait for UART_Delay;
end process uart_send_byte;
---------------------------------------------------
uart: process is -- Читаем данные из файла и формируем
-- из них сигнал для контроллера
file InFile : text; -- Рабочий файл
variable Status : file_open_status;
variable InLine : line; -- Строка, прочитанная из файла
variable i : integer; -- Рабочая переменная
variable Delay : time; -- Рабочая задержка
begin
UART_Delay := 1 sec / 9600; -- скорость по умолчанию 9600
-- Открываем файл с данными
file_open(Status, InFile, "data_uart.txt", read_mode);
if Status != open_ok then
report "Ошибка открытия файла data_uart.txt" severity failure;
end if;
while endfile(InFile) == false loop -- Обрабатываем, пока файл не пуст
readline(InFile, InLine);
print("Прочитана строка: ", InLine);
while match(InLine, "") == false loop
-- Проверяем строку на наличие команды
if match(InLine, "baudrate") == true then -- Изменение скорости
read(InLine, i); -- Удаляем из строки слово "baudrate"
read(InLine, i); -- Читаем значение скорости
if i != 0 then
UART_Delay := 1 sec / i; -- Устанавливаем новую скорость
print("Новая скорость UART = ", i);
else
print("ОШИБКА в файле: Скорость не может быть = 0!");
end if;
elsif match(InLine, "wait") == true then -- Задержка
read(InLine, i); -- Удаляем из строки слово "wait"
read(InLine, Delay); -- Читаем значение задержки
print("Ждем ", Delay);
wait for Delay; -- формируем задержку
else -- Передача байта
read(InLine, UART_Data); -- Читаем байт
print("Отправка байта ", UART_Data);
UART_Work := 1; -- Даем команду на начало передачи
while UART_Work == 1 loop -- Ожидаем завершения передачи
wait for 1 ic;
end loop;
end if;
end loop; -- match InLine
end loop; -- endfile
file_close(InFile);
end process uart;
end testbench;Ниже приведен график генерируемого сигнала. На нем отчетливо видно, что данные передаются двумя пакетами с интервалом 3 мс.
Генератор аналогового сигнала
Симулятор в интегрированной среде MPLAB IDE не позволяет имитировать разные уровни напряжения на аналоговых входах. Однако он позволяет симулировать регистры ADRESH и ADRESL напрямую. Этим мы можем воспользоваться для имитации аналоговых сигналов.
Ниже приведен пример, который будет заполнять регистры ADRESH:ADRESL значениями из файла. Это будет происходить в тот момент, когда программа будет обращаться к АЦП. Это пример является только демонстрацией необходимых действия для формирования регитсров ADRES, он не подходит для практического применения, поскольку входные данные синхронизированы только с обращениями программы к АЦП. Т.е. если обращений не будет, то и сигналы формироваться не будут (или другими словами: когда бы мы не обратились к АЦП, мы всегда прочитаем один и тот же сигнал). На практике такое не встречается.
testbench for "pic16f877a" is
begin
process is
begin
report "======================== SCL loaded OK! ==========================";
wait;
end process;
---------------------------------------------------
ADC: process is
file InFile : text; -- Рабочий файл
variable Status : file_open_status;
variable InLine : line; -- Строка, прочитанная из файла
variable Value : integer; -- Рабочая переменная
constant FileName : string := "data_adc.txt";
begin
file_open(Status, InFile, FileName, read_mode);
if Status != open_ok then
report "Ошибка открытия файла!" severity failure;
end if;
while endfile(InFile) == false loop -- Обрабатываем, пока файл не пуст
readline(InFile, InLine);
print("Прочитана строка: ", InLine);
while match(InLine, "") == false loop -- Обрабатываем строку, пока она не пуста
wait until ADCON0.GO_nDONE == '1'; -- Ждем, когда программа начнет чтение ADC
read(InLine, Value);
if ADCON0.ADON == '1' then
print("Формируем значение ", Value);
while ADCON0.GO_nDONE == '1' loop -- Ждем завершения измерения (для 877a
wait for 1 ic; -- этот бит сбрасывается MPLAB SIM'ом
end loop; -- автоматически. Но, мо-моему, для неко-
-- торых контроллеров эту нужно делать
-- вручную)
if ADCON1.ADFM == '0' then -- Левое выравнивание
ADRESH <= Value / 4;
ADRESL <= Value * 64;
else
-- Правое выравнивание
ADRESH <= Value / 256;
ADRESL <= Value;
end if;
end if; -- ADON
end loop; -- match InLine
end loop; -- endfile
file_close(InFile);
report "Конец скрипта";
wait;
end process ADC;
end testbench;Генератор DTMF
Итак, как уже было сказано, для практического применения предыдущий пример не очень подходит. Представьте, что нам нужно оцифровать и распознать DTMF сигнал. Для этого нам нужно точно вымерять моменты обращения к АЦП, а если скрипт синхронизирован только с самим обращением, то он будет работать при любых условиях. В новом примере мы создадим реальную модель аналогового сигнала, по которой уже можно будет отлаживать программу.
Напомню: DTMF сигнал — это сигнал, получаемый суммированием двух синусоидальных сигналов из двух наборов частот:
- 697 Hz, 770 Hz, 852 Hz, 941 Hz
- 1209 Hz, 1336 Hz, 1477 Hz, 1633 Hz
Скрипт будет состоять из 4-х процессов:
- FREQ1 — генератор синуса заданной частоты
- FREQ2 — генератор синуса заданной частоты
- WORK — Управляющий — будет читать файл с данными и формировать целеуказания для FREQ1 и FREQ2
- ADC — независимый процесс, который при обращении пользовательской программы к АЦП будет формировать значения регистров ADRESH:ADRESL по мгновенным значениям синусов, генерируемых процессами FREQ1 и FREQ2.
(Примечание: еще добавлены пятый процесс, в который для удобства вынесены объявления глобальных переменных, и шестой, который содержит строку «SCL loaded OK!»)
Формат файла «data_dtmf.txt»
Этот файл содержит список команд для управления процессом. В самом простом случае он может состоять из чисел от 1 до 16 (0 — будет воспринят как 16), из которых будет генерироваться последовательность тонов. Однако, с помощью доолнительных команд можно менять параметры сигналов:
freq <c> <f>
Генерировать синус частоты f на канале c (c = 1 или 2)
stop
остановить генерацию (отключает оба канала)
duration <d>
задать длительность тонов (по умолчанию 100 мс)
pause <p>
задать паузу между тонами (по умолчанию 100 мс)
level
задать уровень сигнала от 0% до 100% (по умолчанию 100%)
Также допускается использование комментариев
Пример рабочего файла:
// Задаем временные параметры тонов duration 30 ms pause 30 ms // Генерируем все тоны по очереди 1 2 3 4 5 6 7 8 // Уменьшаем уровень сигнала втрое level 33 9 10 11 12 13 14 15 16 // Генерируем опорную частоту 1 КГц на первом канале freq 1 1000 wait 100 ms stop // Генерируем опорную частоту 2 КГц на втором канале freq 2 2000 wait 100 ms stop
Вот отчет его обработки:
(0) SIM-N0001 Note: ======================== SCL loaded OK! ========================== (0) SIM-N0001 Note: Line: // Задаем временные параметры тонов (0) SIM-N0001 Note: Line: duration 30 ms (0) SIM-N0001 Note: Установлено время гудка 30000000 ns (0) SIM-N0001 Note: Line: pause 30 ms (0) SIM-N0001 Note: Установлено время паузы 30000000 ns (0) SIM-N0001 Note: Line: (0) SIM-N0001 Note: Line: // Генерируем все тоны по очереди (0) SIM-N0001 Note: Line: 1 2 3 4 5 6 7 8 (0) SIM-N0001 Note: Генерируется тон: 1 ADC-W0001: Tad time is less than 1.60us ADC-W0008: No stimulus file attached to ADRESL for A/D. (60000) SIM-N0001 Note: Генерируется тон: 2 (120000) SIM-N0001 Note: Генерируется тон: 3 (180000) SIM-N0001 Note: Генерируется тон: 4 (240000) SIM-N0001 Note: Генерируется тон: 5 (300000) SIM-N0001 Note: Генерируется тон: 6 (360000) SIM-N0001 Note: Генерируется тон: 7 (420000) SIM-N0001 Note: Генерируется тон: 8 (480000) SIM-N0001 Note: Line: (480000) SIM-N0001 Note: Line: // Уменьшаем уровень сигнала втрое (480000) SIM-N0001 Note: Line: level 33 (480000) SIM-N0001 Note: Установлен уровень сигнала = 33 % (480000) SIM-N0001 Note: Line: (480000) SIM-N0001 Note: Line: 9 10 11 12 13 14 15 16 (480000) SIM-N0001 Note: Генерируется тон: 9 (540000) SIM-N0001 Note: Генерируется тон: 10 (600000) SIM-N0001 Note: Генерируется тон: 11 (660000) SIM-N0001 Note: Генерируется тон: 12 (720000) SIM-N0001 Note: Генерируется тон: 13 (780000) SIM-N0001 Note: Генерируется тон: 14 (840000) SIM-N0001 Note: Генерируется тон: 15 (900000) SIM-N0001 Note: Генерируется тон: 16 (900000) SIM-N0001 Note: Line: (900000) SIM-N0001 Note: Line: // Генерируем опорную частоту 1 КГц на первом канале (900000) SIM-N0001 Note: Line: freq 1 1000 (900000) SIM-N0001 Note: Частота канала 1 = 1000 (900000) SIM-N0001 Note: Sample rate = 976 ns (900000) SIM-N0001 Note: OK (900000) SIM-N0001 Note: Line: wait 100 ms (900000) SIM-N0001 Note: Ждем 100000000 ns (1000000) SIM-N0001 Note: Line: stop (1000000) SIM-N0001 Note: Line: (1000000) SIM-N0001 Note: Line: // Генерируем опорную частоту 2 КГц на втором канале (1000000) SIM-N0001 Note: Line: freq 2 2000 (1000000) SIM-N0001 Note: Частота канала 2 = 2000 (1000000) SIM-N0001 Note: OK (1000000) SIM-N0001 Note: Line: wait 100 ms (1000000) SIM-N0001 Note: Ждем 100000000 ns (1100000) SIM-N0001 Note: Line: stop (1100000) SIM-N0001 Note: Line: (1100000) SIM-F0001 Failure: Работа скрипта успешно завершена
Текст скрипта
Наконец, сам скрипт:
(файл с таблицей синуса прилагается sinus.rar)
Скрипт смотреть в источнике.
Он, хоть и сравнительно большой, но понятный. Не составит труда дабавить еще один процесс, который будет подмешивать к сигналу шум. Для этого нужно будет воспользоваться функцией random_time, а в процессе ADC вычислять сумму не двух сигналов, а трех. Уровень шума удобно будет регулировать через входной файл.
Ниже приведен фрагмент графика сигнала, сформированного данным скриптом для команды «1» (697 Гц + 1209 Гц):
Заключение
Итак, в статье были описаны известные мне конструкции языка SCL, а также приведены несколько небольших примеров использования этого языка для имитации цифровых и аналоговых сигналов в симуляторе. Я надеюсь, что описанное здесь поможет упростить процесс отладки, а главное — поможет вам утвердиться во мнении, что симулятор в MPLAB IDE — это действительно мощное средство, и не такое оно неудобное, как может показаться, если пользоваться только диалоговым окном Stimulus, пренебрегая самим языком SCL. Так что теперь, я думаю, такие задачи, как имитация 128-битного манчестерского кода, трудностей больше не вызовут.
Тем, кто раньше пользовался диалоговым окном Stimulus для задания входных сигналов, а после прочтения статьи задумался: «А как бы мне то же самое сделать на SCL?» — напомню, что в MPLAB IDE есть возможность сформировать файл SCL для заданных в диалоговом окне сигналов. Для этого нужно в диалоговом окне «Stimulus» нажать кнопку «Advanced…» и в открывшемся окне нажать кнопку «Generate SCL File». Все заданные вами воздействия будут автоматически переведены в скрипт на языке SCL.
Источник: http://pic24.ru/doku.php/osa/articles/scl
Больше интересных и полезных статей на нашем форуме по АСУ ТПhttps://p4b.su/forums
Тендера фриланс по АСУ ТП https://p4b.su/projects
Библиотека с технической литературой по промышленной автоматизации https://p4b.su/library
Группа ВК https://vk.com/p4b_su