Макросы

Основная информация

Макросы – это программы на языке Си, используемые для создания алгоритмов управления и обработки данных. Термин «макрос» для обозначения подобных программ в панелях оператора используется по исторически сложившимся причинам и следует отделять от макросов препроцессора, описанных в стандарте языка Си.

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

  • обрабатывать полученные данные – активировать на экране индикатор тревоги в случае превышения контролируемым параметром заданного значения и т. п.;

  • иметь полный контроль (на уровне отдельных запросов) над обменом данными с другими устройствами;

  • реализовывать нестандартные протоколы обмена для специфических приборов;

  • сохранять данные в файл в произвольном формате или вычитывать данные из таких файлов;

  • отображать на экране панели геометрические объекты динамической формы и цвета;

  • и т.д.

Именно для этого нужны макросы.

Следует учесть, что выполнение макросов является дополнительным функционалом панели, но не основным. Код макросов должен быть как можно более компактным и решать лишь те связанные с работой панели задачи, выполнение которых не удаётся реализовать в ПЛК, ПР или другом подключенном устройстве. В случае необходимости выполнения больших и сложных программ рекомендуется использовать вместо панели оператора линейку панельных контролеров СПК210.

Работа с макросами в интерфейсе конфигуратора

Для работы с макросами используется одноименный узел дерева проекта. Он содержит две вкладки – Заголовочные файлы и Файлы исходного кода. Для создания нового файла следует нажать на соответствующую вкладку ПКМ, использовать команду Создать и указать:

  • имя файла (должно быть уникальным в рамках каждой из папок);

  • автора;

  • описание;

  • дату создания.

Graphic
Атрибут функции

С помощью команды Вставить можно вставить во вкладку файл, ранее скопированный из дерева проекта в буфер обмена.

При нажатии ПКМ на файл доступны следующие команды контекстного меню:

  • Вырезать (копировать файл в буфер обмена и удалить его из дерева проекта);

  • Копировать в буфер обмена;

  • Удалить;

  • Зашифровать;

  • Расшифровать;

  • Свойства (переход в редактор кода).

Graphic
Контекстное меню файла макроса

Функционал шифрования позволяет установить пароль, который потребуется ввести для открытия файла. Расшифровка позволяет отключить требование ввода пароля.

Двойное нажатие ЛКМ на файл приводит к открытию этого файла в редакторе кода. Редактор состоит из следующих областей:

  • панель команд (сверху);

  • панель навигации (слева);

  • кнопки переключения содержимого панели навигации (слева снизу);

  • текстовый редактор (по центру);

  • панель сообщений компиляции (снизу).

Graphic
Макросы

В панели навигации в зависимости от выбранной вкладки отображается дерево макросов проекта или список встроенных функций и макросов.

Ярлыки панели команд позволяют:

  • создать новый файл;

  • сохранить файл;

  • вырезать файл;

  • копировать файл;

  • вставить файл;

  • удалить файл;

  • отменить последнее изменение в выбранном файле (список изменений очищается при открытии другого файла);

  • восстановить последнее изменение в выбранном файле (список изменений очищается при открытии другого файла);

  • компилировать все файлы;

  • осуществить поиск или поиск с заменой по выбранному файлу;

  • закомментировать в выбранном файле выделенные строки;

  • раскомментировать в выбранном файле выделенные строки.

Компиляция – это процесс трансляции кода, написанном на языке высокого уровня, в объектный код. Объектный код обрабатывается линкером (компоновщиком), который превращает его в машинный код, исполняемый процессором панели.

Для компиляции макросов используется описанная выше команда. В результате компиляции в панели сообщений выводится информация об успешном завершении компиляции или описания возникших в её процессе ошибок.

Кроме того, компиляция макросов происходит в процессе компиляции проекта. В этом случае информация выводится в панель сообщений.

Вызов макросов

Вызов макросов возможен с помощью следующих элементов:

Функциональная область позволяет выполнять макросы циклически или однократно при наступлении определённых событий (например, при переходе на экран, на котором расположена функциональная область). Остальные элементы позволяют однократно выполнить макрос при нажатии и/или отпускании.

В настройках вызова указывается имя файла исходного кода данного макроса и режим выполнения (синхронно или асинхронно).

Особенности поддержки языка Си в панелях СП210

Особенности поддержки языка Си в панелях СП210

Параметр

Значение

Версия стандарта языка Си

C99

Компилятор, используемый в панели

arm-linux-gnueabihf-gcc version 4.9.4 (32-bit)

Компилятор, используемый в режиме эмуляции

MinGW (32-bit) with gcc version 4.5.2

Флаги компиляции

O0 -std=c99

Поддержка стандартной библиотеки языка Си

Полная

Директория монтирования USB-накопителя

/mnt/udisk/

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

В этом случае пути к файлам должны экранироваться:

C:\\test_folder\\test_file.txt

Базовая информация о языке C99

Цель пункта – познакомить читателя с основными элементами языка и привести примеры их использования; но он не является полноценным справочным руководством по языку. В данном пункте приведена базовая информация о языке C99 применительно к его реализации в панелях оператора СП210. В пункте не рассмотрены некоторые возможности языка, которые в большинстве случаев не потребуется применять при создании макросов для панелей оператора:

  • рекурсивный вызов функций;

  • работа с комплексными числами;

  • функционал препроцессора (кроме самых базовых команд);

  • создание функций с переменным количеством параметров;

  • динамическое выделение памяти;

  • и т. д.

Если вы не знакомы с языком Си, то рекомендуется использовать дополнительные справочные материалы – книги (например, Стивен Прата. Язык программирования C. Лекции и упражнения), интернет-ресурсы и т. п.

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

Список специфических функций и макросов, доступных для панелей СП210, приведен в пункте.

Файлы исходного кода

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

Пример

/**************************************************

    Имя     : Func0.c

    Автор  :

    Дата    : 11/17/2025

    Описание:

**************************************************/

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   

}

Файл включает в себя:

  • заголовок, сформированный на основе информации, указанной при создании файла;

  • директиву #include, подключающую системный заголовочный файл macro.h;

  • директиву #define, определяющую макрос препроцессора __SYS_DEFINE__ (не представляет интереса для пользователя);

  • функцию __MAIN__, по умолчанию не имеющую реализации (аналог функции main, описанной в стандарте языка Си). Её код создается пользователем.

Примечание
Шаблон создаваемого файла можно изменить путём редактирования файла \Tool\Macro\Template\UFunHeader.txt в директории установки конфигуратора.

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

  • объявления и определения других функций;

  • объявления и определения глобальных переменных;

  • определения структур, объединений, перечислений и псевдонимов.

Таким образом, в рамках функции __MAIN__ могут вызываться другие функции, созданные пользователем, или размещённые в подключаемых через директиву #include библиотеках.

Заголовочные файлы

Объявление и определение – это две важные концепции языка Си. Объявление – это способ сообщить компилятору о существовании объекта, а определение – способ создать объект и выделить память под его хранение.

Заголовочные файлы в основном содержат объявления (интерфейс), а файлы исходного кода – определения (реализацию).

Расширение заголовочных файлов - .h.

При создании языка Си введение концепции заголовочных файлов позволило обеспечить возможность раздельной компиляции файлов исходного кода; кроме того, их использование позволяет повысить степень модульности проекта.

Заголовочные файлы могут включать:

  • директивы препроцессора;

  • объявления внешних (extern) переменных;

  • объявление и определение структур, объединений, перечислений и псевдонимов;

  • объявление (прототипы) функций.

При создании макросов вы можете обойтись без использования заголовочных файлов. В случае их использования – предпримите усилия, чтобы в рамках файла исходного кода один и тот же заголовочный файл подключался не более одного раза (c помощью директив #ifndef или #pragma once).

Типы данных

Типы данных языка Си можно разделить на следующие категории:

  • тип bool;

  • целочисленные;

  • значения с плавающей точкой;

  • указатели;

  • массивы (и их частный вид – строки);

  • структуры;

  • объединения;

  • перечисления;

  • псевдонимы;

  • тип void.

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

1. Тип bool

Логический тип данных используется для представления значения, которое имеет только два возможных состояния – true (1) или false (0). Этот тип применяется для описания бинарных событий и команд (например, отсутствие/наличие тревоги, отключение/включение насоса и т. д.). Значения данного типа поддерживают логические операции.

Переменная типа bool занимает 1 байт памяти.

Примечание
Для использования макросов true и false требуется подключить заголовочный файл stdbool.h.
Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdbool.h"

 

__MAIN__()

{

    bool BoolVar;

   

    BoolVar = GetPSB(0) && GetPSB(1);

    SetPSB(2, BoolVar);

    // Эквивалентный псевдокод: PSB2 = PSB0 AND PSB1

   

    SetPSB(3, true);

    // Эквивалентный псевдокод: PSB3 = true

}

2. Целочисленные типы

Целочисленные типы отличаются друг от друга наличием/отсутствием знака и диапазоном значений. Значения этих типов поддерживают арифметические и побитовые операции, а также операции сравнения.

В таблице указаны размеры типов применительно к панели и её эмулятору; на других платформах размер может отличаться. Наличие нескольких вариантов именования для каждого типа связано с некоторыми историческими особенностями языка Си и наличием в панели псевдонимов для этих типов.

При выполнении арифметических операций возможен эффект переполнения – например, если переменная типа uint16_t имеет значение 65535 (это верхний предел ее диапазона) и мы прибавим к ней 1, то переменная получит значение 0 (т. е. ее значение станет равным нижней границе диапазона ее значений). Для панелей оператора СП210 переполнение и беззнаковых, и знаковых типов обрабатывается одним и тем же способом.

Деление на 0 приведет к исключению, в результате чего выполнение всех макросов прекращается до перезагрузки панели или перезапуска эмулятора.

Примечание

Типы с постфиксом _t является типами фиксированного размера, для которых гарантируется совпадение размера на всех платформах. Для их использования требуется подключить заголовочный файл stdint.h.

Целочисленные типы данных

Тип

Диапазон

Размер в байтах

char

INT8

int8_t

-128…127

1

unsigned char

BYTE

UINT8

uint8_t

0…255

short

INT16

int16_t

-32768… 32767

2

unsigned short

WORD

UINT16

uint16_t

0…65535

int

long

INT32

int32_t

-231…231-1

4

unsigned int

unsigned long

DWORD

UINT32

uint32_t

0…232

long long

INT64

int64_t

-263…263-1

8

unsigned long long

DDWORD

UINT64

uint64_t

0…264

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

__MAIN__()

{

    uint16_t u16_var1;

    uint16_t u16_var2 = 65535;

    int16_t  i16_var;

   

    uint32_t u32_Var1;

    uint32_t u32_Var2 = 123456;

   

    

    u16_var1 = PSW[0] + PSW[1];

    PSW[2] = u16_var1;

    // Можно сократить до PSW[2] = PSW[0] + PSW[1];

   

    PSW[3] = u16_var2 + 1;

    // Беззнаковое переполнение: в PSW[3] будет записано значение 0

   

    i16_var = 32767 + 1;

    PSW[4] = i16_var;

    // Знаковое переполнение: в PSW[3] будет записано значение -32768

    // (нижняя граница типа int16_t)

   

    u32_Var1 = *(uint32_t*)(PSW + 10);

    *(uint32_t*)(PSW + 12) = u32_Var2;

    // Чтение и запись параметра типа DWord

}

3. Типы данных с плавающей точкой

Значения этих типов поддерживают арифметические операции и операции сравнения. В реализации панелей оператора тип long double по диапазону и точности соответствует типу double, так что его использование не имеет смысла.

Значение с плавающей точкой является лишь приближением к реальному значению с ограниченной точностью. Поэтому такие значения нет смысла проверять на строгое равенство – вместо этого следует вычислять их разность, и если в рамках конкретной задачи она пренебрежимо мала, то считать их «равными».

Значения с плавающей точкой могут иметь особые состояния – такие, как NaN и бесконечность. Для проверки, является ли значение особым, используются макросы заголовочного файла math.h

Целочисленные типы данных

Тип

Диапазон

Точность

Размер в байтах

float

10-38…1038

7-8 знаков после точки

4

double

10-308…10308

15-17 знаков после точки

8

long double

12

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   float FloatVar1;

   float FloatVar2;

 

   // Чтение и запись параметра формата Float

   FloatVar1 = *(float*)(PSW + 20);

   FloatVar2 = FloatVar1 + 0.0001;

   *(float*)(PSW + 22) = FloatVar2;

 

   // Начиная с FloatVar1 = 2048 приращение 0.0001...

   // ... не будет происходить из-за ограничения точности.

   // Это называется «эффект расходимости мантиссы».

   // Существуют различные алгоритмы для его компенсации.

   // См., например, алгоритм Кэхэна

 

}

4. Указатели

Переменная типа «указатель» содержит адрес другой переменной (или функции – но об этом лучше прочитать в учебной литературе). Указатели обеспечивают гибкую и эффективную работу с памятью, и поэтому активно используются в коде стандартной библиотеки Си (и других библиотек). Они позволяют создавать функции, которые способны обрабатывать и модифицировать наборы данных произвольного типа и размера.

Для панелей оператора СП210 размер указателей составляет 4 байта, так как аппаратная платформа является 32-битной.

Указатели поддерживают арифметические операции и специфические операции получения адреса (&) и разыменования – доступа к значению, расположенному по адресу (*). Частой ошибкой является разыменование неинициализированного указателя («нулевого» или имеющего некорректное значение, не совпадающее с доступным адресом памяти). Результатом подобной операции является возникновение исключения, которое делает невозможным продолжение нормальной работы панели до её перезагрузки.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

__MAIN__()

{

   // Синтетический пример использования указателя

 

   uint16_t value = 123;

   // Указатель на тип uint16_t

   uint16_t *pointer;

 

   // Получение адреса переменной value  

   pointer = &value;

   // Доступ к значению, размещенному под указателем

   // В PSW[30] будет записано значение переменной value

   PSW[30] = *pointer;

 

   // Можно сделать и наоборот – записать в переменную value значение регистра PSW[30]

   *pointer = PSW[30];

}

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

__MAIN__()

{

   uint16_t ui16_var;

   // Функция Read читает значение из памяти панели или slave-устройства...

   // ...и размещает его в переменной

   // Так как функция должна поддерживать чтение значения произвольного типа и размера...

   // ...то на её последний вход следует передавать адрес переменной...

   // ...в которую это значение будет записано

   Read(_T("Внутренние регистры"), 0, TP_PFW, DT_Word, 30, 0, &ui16_var);

}

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   float FloatVar1;

 

   // Для доступа к параметру типа Float, размещенного в регистрах панели...

   // ...используется каламбур типизации

 

   FloatVar1 = *(float*)(PSW + 20);

 

   // PSW + 20 – это адрес регистра панели PSW20 (фактически – указатель)

   // (float*) заставляет компилятор считать, что это указатель на значение типа Float

   // (тот же синтаксис, что при объявлении переменной типа указатель)

   //

   // *(float*) – разыменовывает это указатель, чтобы получить лежащее в регистрах панели...

   // ... значение типа Float

}

5. Массивы

Массив объединяет набор объектов одного и того же типа. Данные массива размещаются в памяти последовательно. Для доступа к элементу массива используется оператор []. Индексация массивов ведется с 0.

Массивы часто обрабатываются в цикле for:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

__MAIN__()

{

   uint16_t Data[4];

 

   // Размещаем в массиве значения регистров панели PSW100, PSW200, PSW300 и PSW400...

   //  ...для какой-то последующей обработки

   for (int i = 0; i < 4; i++)

   {

      Data[i] = PSW[100 * (i + 1)];

   }

}

Размеры массивов удобно задавать через макроопределения или константы – иначе в случае их изменения легко забыть внести исправление в какой-нибудь фрагмент кода, где они используются.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Макроопределение

#define ARR_SIZE 4

 

__MAIN__()

{

   uint16_t Data[ARR_SIZE];

 

   // Размещаем в массиве значения регистров панели PSW100, PSW200, PSW300 и PSW400...

   //  ...для какой-то последующей обработки

   for (int i = 0; i < ARR_SIZEi++)

   {

      Data[i] = PSW[100 * (i + 1)];

   }

}

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Константа

const int16_t ARR_SIZE = 4;

 

__MAIN__()

{

   uint16_t Data[ArrSize];

 

   // Размещаем в массиве значения регистров панели PSW100, PSW200, PSW300 и PSW400...

   //  ...для какой-то последующей обработки

   for (int i = 0; i < ARR_SIZE; i++)

   {

      Data[i] = PSW[100 * (i + 1)];

   }

}

Если при инициализации массива не указать его размер – то компилятор определит его автоматически на основании числа инициализаторов.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

__MAIN__()

{

   // Будет выделена память под 3 значения

   uint16_t Data[] = {1, 2, 3};

}

Можно инициализировать лишь нужные элементы массива – остальные автоматически будут инициализированы нулями.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Макроопределение

#define ARR_SIZE 4

 

__MAIN__()

{

   // После инициализации массив примет вид {1, 0, 0, 4}

   uint16_t Data[ARR_SIZE] = {[0] = 1, [3] = 4};

}

Доступны массивы переменной длины (variable-length array; VLA) – их размер может меняться в процессе работы программы. В этом случае при доступе к элементу массива следует контролировать, что индекс соответствует текущему размеру массива. Для VLA не поддерживается инициализация и использование модификатора static.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

__MAIN__()

{

   uint16_t ArrSize;

   uint16_t VLA_Data[ArrSize];

   uint16_t CurrentIndex;

 

   ArrSize = PSW[51];

   CurrentIndex = PSW[52];

 

   if (ArrSize != 0 && CurrentIndex <= ArrSize)

   {

      VLA_Data[CurrentIndex] = 42;

   }

        

}

Массивы могут быть многомерными:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

__MAIN__()

{

   // массив 2x3x4

   uint16_t arr3d[2][3][4] =

   {

      {   // Первый «блок»

         {1, 2, 3, 4},      // 3 «строки» по 4 значения

         {5, 6, 7, 8},      

         {9, 10, 11, 12}   

      },

      {   // Второй «блок»           

         {13, 14, 15, 16},

         {17, 18, 19, 20},

         {21, 22, 23, 24}

      }

   };

       

   // В регистр PSW53 будет записано значение 7

   // (напомним, нумерация массивов ведется с 0)

   PSW[53] = arr3d[0][1][2];

}

Имя массива является указателем на его начальный элемент. Поэтому при использовании функций Reads/Writes (и других функций, принимающих на вход указатель) необязательно использовать оператор получения адреса (&).

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

#define ARR_SIZE 4

 

__MAIN__()

{

   uint16_t ui16_var;

   uint16_t Data[ARR_SIZE];

 

   // Для обычной переменной оператор & необходим

   Read(_T("Local Device"), 0, TP_PFW, DT_Word, 30, 0, &ui16_var);

 

   // А вот для массива подойдет любой из вариантов:

   Reads(_T("Local Device"), 0, TP_PSW, DT_Word, 0, 0, ARR_SIZE, &Data);

   Reads(_T("Local Device"), 0, TP_PSW, DT_Word, 0, 0, ARR_SIZE, Data);

}

Зная о родстве массивов и указателей – можно оценить, как в языке Си реализованы арифметические операции над последними.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

#define ARR_SIZE 4

 

__MAIN__()

{

   uint16_t arr[ARR_SIZE] = {10, 20, 30, 40};

   uint16_t *ptr = arr;

 

   // ptr указывает на arr[0]

   PSW[54] = *ptr;

 

   ptr = ptr + 1;

 

   // ptr указывает на arr[1]

   PSW[55] = *ptr;

 

}

То есть увеличение значения указателя на 1 «смещает» его на количество байт, равное типу указателя: «ptr + n» эквивалентно «ptr + n * sizeof(*ptr)».

Кроме того, указатели, как и массивы, поддерживают индексный доступ.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

#define ARR_SIZE 4

 

__MAIN__()

{

   uint16_t arr[ARR_SIZE] = {10, 20, 30, 40};

   uint16_t *ptr = arr;

 

   // ptr[0] соответствует arr[0]

   PSW[54] = ptr[0];

 

   // ptr[1] соответствует arr[1]

   PSW[55] = ptr[1];

 

}

6. Строки

В языке Си нет отдельного типа данных для строк. Вместо этого строки реализованы как массивы символов. В стандарте C99 доступны два вида символов:

Виды символов в C99

Тип

Источник

Кодировка

Размер символа

сhar

Встроенный тип

ASCII

1 байт

wchar_t

Заголовочный файл wchar.h

UTF-16

2 байта

Строка в кодировке ASCII представляет собой массив char, а строка в кодировке UTF-16 – массив wchar_t. Строки в языке Си являются нуль-терминированными – каждая строка должна заканчиваться символом, имеющим код «0» (NULL-терминатором). При инициализации строки этот символ добавляется автоматически. Строковые литералы обрамляются двойными кавычками (в отличие от отдельных символов, обрамляемых одинарными кавычками). Для UTF-16 значение литерала предваряется префиксом L.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "wchar.h"

 

__MAIN__()

{

   // Выделенный размер – 1 байт

   char AsciiChar = 'v';

   // Выделенный размер – 5 байт (4 байтовых символа + NULL-терминатор)

   char AsciiStr[] = "test";

   // Выделенный размер – 2 байта

   wchar_t UnicodeChar = L'я';

   // Выделенный размер – 10 байт (четыре 2-байтовых символа + 2-байтовый NULL-терминатор)

   wchar_t UnicodeStr[] = L"тест";

}

С помощью символа «\» можно экранировать управляющие последовательности и HEX-коды символов. Это, в частности, позволяет инициализировать строки c использованием кодировки Win-1251.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Строка «тест» (кодировка Win-1251)

   char AsciiStr[] = "\xF2\xE5\xF1\xF2";

   // Перенос строки

   char AsciiStr[] = "line 1\nline 2";

}

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

Заголовочные файлы для работы с символами и строками

Категория

Тип

Заголовочные файлы

Символ

char

ctype.h

char[]

string.h

stdlib.h

Строка

wchar_t

wctype.h

wchar_t[]

wchar.h

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

#include "string.h"

 

__MAIN__()

{

   char FullStr[20] = "";

   char str1[10] = "Hello, ";

   char str2[10] = "World!";

   uint16_t FullStrLen;

 

   // Записываем в FullStr результат объединения FullStr и str1

   // Из str1 используется не более 10 символов

   strncat(FullStr, str1, 10);

   // Записываем в FullStr результат объединения FullStr и str2

   // Из str2 используется не более 10 символов

   strncat(FullStr, str2, 10);

   // Вычисляем количество символов строки FullStr

   FullStrLen = strlen(FullStr);

 

   // Записывем строку FullStr в регистры панели начиная с PSW60

   Writes(_T("Внутренние регистры"), 0, TP_PSW, DT_Word, 60, 0, 1 + FullStrLen / 2, FullStr);

 

}

В заголовочном файле string.h также объявлены функции для работы с памятью, которые часто используются применительно к массивам – memcpy, memmove, memcmp, memset и др.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "string.h"

 

__MAIN__()

{

   // Записываем в регистры PSW56-59 значение 123

   // Регистр занимает 2 байта, поэтому 2 * 4

   memset(&PSW[56], 123, 2 * 4);

}

7. Структуры

Структура объединяет набор объектов разных типов. Каждый объект размещается в отдельных ячейках памяти.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Объявление структуры

struct MessageBox

{

   char title[30];

   char message[200];

   uint8_t IconCode;

   uint16_t x;

   uint16_t y;

};

 

 

__MAIN__()

{

   // Определение структуры c полной инициализацией полей

   struct MessageBox ErrorMessageBox1 = {"error", "Some error text", 0, 100, 100};

 

   // Определение структуры c частичной инициализацией полей

   // Остальные поля будут инициализированы нулями

   struct MessageBox ErrorMessageBox2 = {.title = "Error",

      .message = "Some error text"};

 

   // Доступ к полям структуры в коде

   ErrorMessageBox2.x = 100;

   ErrorMessageBox2.y = 100;

 

}

Можно совместить объявление структуры с её определением:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Объявление структуры

struct MessageBox

{

   char title[30];

   char message[200];

   uint8_t IconCode;

   uint16_t x;

   uint16_t y;

} ErrorMessageBox2;

 

 

__MAIN__()

{

   // Доступ к полям структуры в коде

   ErrorMessageBox2.title = "Error";

   ErrorMessageBox2.message = "Some error text";

   ErrorMessageBox2.x = 100;

   ErrorMessageBox2.y = 100;

}

Для доступа к полю структуры через указатель на структуру используется специфический оператор «->»:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Объявление структуры

struct MessageBox

{

   char title[30];

   char message[200];

   uint8_t IconCode;

   uint16_t x;

   uint16_t y;

} ErrorMessageBox2;

 

 

__MAIN__()

{

   struct MessageBox *PtrToMessageBox = &ErrorMessageBox2;

 

   // Доступ к полям структуры через указатель

  // Для полей-массивов использование оператора «->» не требуется...

  // ...потому что имя массива - указатель на его начальный элемент

   PtrToMessageBox.title = "Error";

   PtrToMessageBox.message = "Some error text";

   PtrToMessageBox->x = 100;

   PtrToMessageBox->y = 100;

}

В языке Си присутствует поддержка битовых полей (bitfield) – разновидностей структур, содержащих только целочисленные поля, для каждого из которых указано количество занимаемых бит.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Объявление структуры с битовыми полями

struct MERCURY23x_MODEL_PARAMS_2

{

   uint8_t CounterConstant : 4;

   uint8_t PhaseCount : 1;

   uint8_t IsAveragePowerProfile : 1;

   uint8_t TempRange : 1;

   uint8_t DirectionCount : 1;

};

Graphic
Фрагмент спецификации протокола Меркурий

При использовании структур можно столкнуться с выравниванием памяти (memory aligment). Рассмотрим влияние выравнивания на простом примере. Пусть у нас определена структура с двумя полями, одно из которых имеет тип uint8_t, а второе – uint32_t.

Пример

struct TestStuct

{

   uint8_t  var1;

   uint32_t var2;

} ts

Первое поле занимает 1 байт, второе 4 – и логично предположить, что вся структура будет занимать 5 байт. Но с помощью оператора sizeof можно убедиться, что в реальности в панели структура занимает 8 байт (для других платформ результат может быть иным):

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

struct TestStuct

{

   uint8_t  var1;

   uint32_t var2;

} ts

 

 

__MAIN__()

{

 

   // В PSW80 будет записано значение 8

   PSW[80] = sizeof(ts);

  

}

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

«Центральные процессоры в качестве основной единицы при работе с памятью используют машинное слово, размер которого может быть различным. Однако размер слова всегда равен нескольким байтам (байт является наименьшей единицей, в которой отсчитываются адреса). Как правило, машинное слово равно 2k байтам, то есть состоит из одного, двух, четырёх, восьми и т. д. байтов.

При сохранении какого-то объекта в памяти может случиться, что некое поле, состоящее из нескольких байтов, пересечёт “естественную границу” слов в памяти. Некоторые модели процессоров не могут обращаться к данным в памяти, нарушающим границы машинных слов. Некоторые могут обращаться к невыровненным данным дольше, нежели к данным, находящимся внутри целого “машинного слова” в памяти».

В некоторых ситуациях требуется управлять выравниванием памяти (например – отключить его); это, например, может быть удобным при реализации протоколов, когда структуру требуется «наложить» на байтовый буфер запроса или ответа.

Для этого существуют различные средства, но проще всего воспользоваться директивой pack:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Сохраняем текущее выравнивание и устанавливаем 1-байтовое

#pragma pack(push, 1)

struct TestStuct

{

   uint8_t  var1;

   uint32_t var2;

} ts

// Восстанавливаем сохраненное ранее выравнивание

#pragma pack(pop)

 

// На эту структуру директива pack уже не действует

struct TestStuct2

{

   uint8_t  var1;

   uint32_t var2;

} ts2;

 

__MAIN__()

{

 

   // В PSW80 будет записано значение 5

   PSW[80] = sizeof(ts);

   // В PSW81 будет записано значение 8

   PSW[81] = sizeof(ts2);

}

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

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

struct TestStuct

{

   uint8_t  var1;

   uint32_t var2;

};

 

__MAIN__()

{

   struct TestStuct ArrTs[4];

   ArrTs[0].var1 = 42;

}

8. Объединения

Объединения очень похожи на структуры, за одним существенным отличием – все их объекты размещаются в одних и тех же ячейках памяти. Это обеспечивает разные варианты представления одних и тех же данных.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

union IPAddress {

   uint32_t HexIp;          

   uint8_t octets[4];      

};

 

__MAIN__()

{

   union IPAddress ip;

   // Соответствует 192.168.0.10

   ip.HexIp = 0xC0A8000A;

 

   // Старший октет фактически хранится в младшем байте

 

   // В регистр PSW90 будет записано значение 192

   PSW[90] = ip.octets[3];

   // В регистр PSW91 будет записано значение 168

   PSW[91] = ip.octets[2];

   // В регистр PSW92 будет записано значение 0

   PSW[92] = ip.octets[1];

   // В регистр PSW93 будет записано значение 10

   PSW[93] = ip.octets[0];

}

9. Перечисления

Перечисление позволяет указать для конкретных значений целочисленной переменной символьные названия, что повышает читабельность кода. В языке Си все перечисления трактуются как значения типа int и имеют соответствующий диапазон доступных значений. Часто перечисления используются в качестве меток оператора switch.

Пример

#include "macro.h"

#define __SYS_DEFINE__

enum Days

{

   NONE, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY

};

 

__MAIN__()

{

  enum Days WeekDay;

  // Чтение системного регистра с номером дня недели

  WeekDay = SPSW[22];

 

  switch (WeekDay)

  {

     case MONDAY:

     case TUESDAY:

     case WEDNESDAY:

     case THURSDAY:

     case FRIDAY:

         // разместите здесь код управления системой в будние дни

         break;

     case SATURDAY:

     case SUNDAY:

        // разместите здесь код управления системой в выходные дни

        break;

     default:

        // разместите здесь код вывода сообщения об ошибке

   }  

}

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

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

enum Days

{

   NONE = 0,

   MONDAY = 1,   

   TUESDAY = 2,  

   WEDNESDAY = 3,

   THURSDAY = 4, 

   FRIDAY = 5,   

   SATURDAY = 6, 

   SUNDAY = 7    

};

При этом для некоторых элементов значения могут быть опущены:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

enum Colors

{

    RED = 1,

    GREEN,    // 2

    BLUE,     // 3

    WHITE = 7,

    BLACK     // 8

};

Значения элементов перечисления могут быть «значимыми»:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

// Использутся цветовая модель BGR, применяемая в функциях работы с элементом Холст

enum Colors

{

    RED = 0x0000FF,

    GREEN = 0x00FF00,   

    BLUE = 0xFF0000    

};

10. Псевдонимы (typedef)

Ключевое слово typedef создает псевдоним для типа данных, что повышает читабельность кода.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

typedef struct

{

   uint16_t x;

   uint16_t y;

} Point;

 

 

__MAIN__()

{

  // Использование typedef позволило обойтись без ключевого слова struct

  Point Line[2] = {10, 10, 100, 100};

}

11. Тип void

Void – это специфический тип данных. Он является неполным – создать переменную данного типа нельзя. Но void можно использовать:

  • в качестве типа, возвращаемого функцией – это будет означать, что данная функция не возвращает никакого значения (см., например, Beep);

  • при создании функций, которые не принимают никаких параметров (см., например, Beep);

  • при указании параметров функции, которые могут иметь произвольный тип (см., например, функцию WriteF);

  • указатель на void соответствует указателю на произвольный тип данных. Это может использоваться при создании функций, которые должны работать с данными произвольного типа (см., например, функции семейства Read/Write).

Переменные

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

Пример

#include "macro.h"

#define __SYS_DEFINE__

#include "stdint.h"

 

// Глобальная переменная

// Сохраняет свое значение между вызовами программы

// Может использоваться в пределах файла исходного кода

uint16_t global_var1;

 

// Пример одновременного определения нескольких переменных общего типа

uint16_t global_var2, global_var3;

 

__MAIN__()

{

   // Локальная переменная

   // Не сохраняет свое значение между вызовами программы

   // (переинициализурется при каждом вызове)

   // Может использоваться только в пределах блока

   // (в данном случае – в пределах функции __MAIN__)

   uint16_t local_function_var;

 

   // какой-то код

   {

      // Локальная переменная

       // Не сохраняет свое значение между вызовами программы

       // (переинициализурется при каждом вызове)

       // Может использоваться только в пределах блока

       // (в данном случае – до символа закрывающей фигурной скобки)

       uint16_t local_scope_var;

 

       // какой-то код

   }

 

   // использовать здесь local_scope_var не получится

}

Определение переменной должно быть сделано перед её использованием (т. е. в расположенных выше строках файла).

Переменные могут быть глобальными и локальными.

Глобальные переменные определяются вне функций. Они доступны для использования в рамках всего файла исходного кода. Кроме того, их можно использовать в других файлах (см. пункт). Если для глобальной переменной не указано значение инициализации – то она инициализируется значением 0. Значение глобальной переменной сохраняется между вызовами программы.

Локальные переменные объявляются внутри функций. Они доступны для использования только в рамках блочного оператора, в котором определены (блочный оператор представлен парой фигурных скобок). Если для локальной переменной не указано значение инициализации – то при старте программы она будет содержать произвольное значение («мусор»). Значение локальной переменной не сохраняется между вызовами программы. Частным случаем локальных переменных являются параметры функции.

Инициализация – это процесс однократной записи в переменную предустановленного значения при запуске программы.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdbool.h"

#include "stdint.h"

__MAIN__()

{

    bool BoolVar = true;

    uint16_t u16_var = 123;

    char str[] = "test";

}

Способы инициализации для других типов данных приведены в примерах пункт.

Спецификаторы и квалификаторы

При объявлении и определении переменных и функций могут использоваться спецификаторы. Основными из них являются static и extern. Спецификатор размещается перед типом переменной или перед типом значения, возвращаемого функций.

Локальные переменные со спецификатором static сохраняют свои значения между вызовами программы. Кроме того, если для них не указаны значения инициализации – то они инициализируются значением 0.

Для глобальных переменных и функций спецификатор static ограничивает область видимости файлом исходного кода, в котором они определены.

Пример

// Func0.c

 

#include "macro.h"

#define __SYS_DEFINE__

#include "stdint.h"

 

// Эта глобальная переменная доступна в других файлах

uint16_t global_var;

// Эта глобальная переменная недоступна в других файлах

static uint16_t static_global_var;

 

__MAIN__()

{

   // Эта локальная переменная не сохранит свое значение между вызовами программы...

   // ...и будет инициализироваться начальным значением при каждом вызове

   uint16_t local_var = 0;

   // Эта локальная переменная сохранит свое значение между вызовами программы

   static uint16_t static_local_var = 0;

}

 

// Func1.c

 

#include "macro.h"

#define __SYS_DEFINE__

#include "stdint.h"

 

// Импорт глобальной переменной из файла Func0.c

// Импортировать static_global_var не получится...

// ...так как она помечена модификатором static

extern uint16_t global_var;

 

__MAIN__()

{

   global_var = 42;

}

Как показано в примере выше, спецификатор extern используется для объявления (импорта) переменных, которые определены в другом файле.

Переменные с квалификатором const расцениваются как константы, значения которых не должны изменяться в процессе работы программы. Попытки каким-либо образом изменить эти значения (например, с помощью использования указателей) приводятся к неопределённому поведению (undefined behavior) программы.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

__MAIN__()

{

   // Переменная (может быть массивом, структурой, полем структуры и т. д.)

   float val = 11.22;

   // Константное значение, не может быть изменено

   const float readonly_val = 22.33;       

   // Указатель на константу. Значение адреса может быть изменено...

   // ...а вот расположенные по адресу данные - нет

   const float *ptr1 = &val

   // Эквивалент варианта выше

   float const *ptr2 = &val

   // Константный указатель. Значение адреса не может быть изменено...

   // ...а вот расположенные по адресу данные - могут

   float *const ptr3 = &val

   // Константый указатель на константу. Ни адрес, ни расположенные по нему данные...

   // ...не могут быть изменены

   const float *const ptr4 = &val

   // Эквивалент варианта выше

   float const *const ptr5 = &val

}

Литералы

Литерал – это элемент исходного кода, который соответствует фиксированному значению. Например, в строке

Пример
float val = 11.22;

значение 11.22 является литералом.

Для литералов определённых категорий доступны префиксы и постфиксы, влияющие на их формат и тип.

Префиксы для целочисленных типов:

  • 0x (шестнадцатеричная система счисления; HEX);

  • 0b (двоичная система счисления).

Префиксы для строк:

  • префикс L указывает на кодировку UTF-16.

Постфиксы для целочисленных типов:

  • u (U) – признак «беззнаковости» литерала. Может сочетаться с другими постфиксами (например, ul – unsigned long);

  • l (L) – литерал является значением типа long;

  • ll (LL) – литерал является значением типа long long.

По умолчанию (без постфикса) целочисленный литерал интерпретируется компилятором как значение типа int.

Постфиксы для типов с плавающей точкой:

  • f (f) – литерал является значением типа float;

  • L (L) – литерал является значением типа long double.

По умолчанию (без постфикса) литерал с плавающей точкой интерпретируется компилятором как значение типа double.

Постфиксы позволяют:

  • добиться нужной интерпретации литерала, что в некоторых случаях может быть очень важно (см. пример с делением ниже);

  • сэкономить память;

  • повысить читабельность кода.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

#include "wchar.h"

 

__MAIN__()

{

   // DEC

   uint16_t val1 = 16;

   // HEX

   uint16_t val2 = 0x10;

   // Двоичная систем, разделитель ' является опциональным

   uint16_t val3 = 0b0001'0000;  

 

   // Результат: 3 (так как выполняется целочисленное деление)

   float val4 = 10 / 3;

   // Результат: 3.333333

   float val5 = 10 / 3f; // или 3.0        

 

   wchar_t UnicodeStr[] = L"тест";

 

}

Идентификаторы

Идентификатор – это имя, указываемое при объявлении/определении переменных, функций, псевдонимов типов данных, макросов и других сущностей.

В языке Си идентификаторы являются регистрозависимыми, т. е. var и Var – это два разных идентификатора.

На идентификатор накладываются следующие ограничения:

  • может включать в себя буквы английского языка, цифры и символы нижнего подчеркивания (_);

  • первый символ не может быть цифрой;

  • не может совпадать с ключевым словом языка Си или идентификатором, используемым в стандартной библиотеке языка Си;

  • длина идентификатора не может превышать 256 символов.

Комментарии

В языке Си поддержаны однострочные и многострочные комментарии:

Пример

// однострочный комментарий

 

/* многострочный

комментарий */

Кнопки редактора макросов позволяют закомментировать/раскомментировать выделенные строки с помощью однострочных комментариев.

Операторы и выражения

Выражение — это упорядоченный набор операций над переменными, литералами и функциями.

Таблица основных операторов языка Си

Оператор

Описание

Оператор присваивания

=

 

 

Арифметические операторы

 

+

-

*

/

%

Cложение

Вычитание

Умножение

Деление

Остаток от деления

Применимы к арифметическим типам (оператор % применим только к целочисленным типам)

Логические операторы

&&

||

!

Логическое И (AND)

Логическое ИЛИ (OR)

Логическое отрицание (NOT)

Применимы к логическому типу. Заголовочный файл iso646.h содержит макроопределения-псевдонимы (например, and для &&)

Побитовые операторы

&

|

^

~

Побитовое И

Побитовое ИЛИ

Побитовое исключающее ИЛИ (XOR)

Побитовая инверсия

Применимы к целочисленным типам

Операторы сравнения

< 

> 

<=

>=

==

!=

«Меньше»

«Больше»

«Меньше или равно»

«Больше или равно»

Применимы к арифметическими логическим типам  

Проверка на равенство

Проверка на неравенство

Применимы к арифметическим (включая перечисления) и логическим типам

Операторы битового сдвига

<< 

>> 

Битовый сдвиг влево

Битовый сдвиг вправо

Применимы к арифметическим типам. Первый аргумент оператора – это значение, в котором выполняется сдвиг, второй – количество сдвигаемых бит

Остальные операторы

[]

.

->

&

*

,

sizeof

Доступ к объекту массива или указателя

Доступ к полю структуры

Доступ к полю структуры через указатель на структуру

Получение адреса объекта

Разыменование указателя

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

Получение адреса объекта или типа (возвращаемое значение имеет тип size_t, который определен в заголовочном файле stddef.h)

Для преобразования типов используется специфический оператор:

Пример

int ivar;

float fvar = 11.22;

 

// float to int

// ivar получит значение 11

ivar = (int)fvar;

В выражении могут одновременно использоваться несколько операторов:

Пример

uint16_t var;

// Что выполнится сначала – сложение 5 и 1, или умножение 10 на 5?

var = 10 * 5 + 1;

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

Пример

uint16_t var;

// Теперь мы точно знаем, что сначала выполнится сложение

var = 10 * (5 + 1);

В языке Си поддерживается множественное присваивание:

Пример

uint16_t var1, var2, var3;

var1 = var2 = var3 = 42;

Арифметические, логические и «сдвиговые» операторы могут комбинироваться с оператором присваивания:

Пример
var1 += 5; // эквивалентно var = var + 5;

Характерными для языка Си являются операторы инкремента и декремента, которые могут быть префиксными и постфиксными:

Пример

var2 = ++var1; // увеличивает var1 на +1, после чего записывает результат в var2

var3 = var2++; // записывает var2 в var3, после чего увеличивает var2 на +1

Некоторые характерные для других языков операторы в Си представлены в виде функций заголовочного файла math.h:

  • abs (получение абсолютной величины числа);

  • pow (возведение в степень);

  • sin, cos;

  • и др.

Функции

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

Ниже приведен пример определения и вызова функции, которая имеет 2 параметра:

  • T типа float;

  • IsAirSaturation типа bool.

Эти параметры принимают значения аргументов функции, переданные ей при вызове.

В результате выполнения функция возвращает значение типа float.

Пример

#include "macro.h"

#define __SYS_DEFINE__

#include "math.h"

#include "stdbool.h"

 

#define a0 999.83952

#define a1 16.952577

#define a2 -7.9905127E-3

#define a3 -4.6241757E-5

#define a4 1.0584601E-7

#define a5 -2.8103006E-10

#define b  0.0168872

 

// Уравнение Келла для определения плотности воды при температуре T

// Параметр IsAirSaturation определяет необходимость учета влияния растворенного...

// ...в воде воздуха

// http://www.bioconsult.ch/Inovatech/W-Lehre/J%20Che%20Eng%20Dat20,%2097.pdf

// Размерность: г/мл

// Точность: 4 знака после запятой

float WaterDensity(float T, bool IsAirSaturation)

{

   float wd;

   wd = (a0 + a1*T + a2*pow(T,2) + a3*pow(T,3) + a4*pow(T,4) + a5*pow(T,5) ) /

      (1.0 + b*T);

   // http://www.bioconsult.ch/Inovatech/W-Lehre/J%20Che%20Eng%20Dat20,%2097.pdf

   // Учет влияния растворенного воздуха

   if (IsAirSaturation)

   {

      wd = wd - 0.004612 + 0.000106 * T

   }

   

   // Перевод из кг/м3 в г/мл

   wd = 0.001 * wd;

   

   return wd;  

}

 

 

__MAIN__()

{

   *(float*)(PSW + 20) = WaterDensity(*(float*)(PSW + 10), false);

   *(float*)(PSW + 22) = WaterDensity(*(float*)(PSW + 12), false);

   // и т. д.

   // чтобы избежать копипасты – можно разместить вызов функции в цикле for

}

Оператор return традиционно размещается в конце функции, но может располагаться в любых ее фрагментах (в т. ч. таких операторов в функции может быть несколько, чтобы организовать выход из функции при различных условиях).

В некоторых ситуациях в качестве аргументов функции используются указатели – см., в частности, примеры использования функций Reads/Writes.

Если требуется указать, что функция не имеет явного возвращаемого значения (например, она как раз манипулирует данными, полученными по указателю), то в качестве типа функции следует использовать void.

Если функция не имеет параметров, то вместо них указывается void.

Характерный пример – функция Beep, которая используется для активации зуммера панели. Она не принимает и не возвращает никаких значений, и объявлена следующим образом:

Пример
void Beep(void);

Препроцессор

Препроцессор – это программа, подготавливающая код на языке Си к компиляции. К основным задачам препроцессора относятся:

  • вставка в обрабатываемый файл содержимого других заголовочных файлов с помощью директивы #include;

  • выполнение макроподстановок для директив #define;

  • выполнение директив условной компиляции (не рассматриваются в рамках данного документа);

  • выполнение других директив.

Директива #include используется для подключения заголовочных файлов – в частности, заголовочных файлов стандартной библиотеки языка Си. Это требуется для возможности вызова функций этой библиотеки. Например, для использования функций заголовочного файла math.h требуется добавить такую строку:

Пример
#include "math.h"

или такую:

Пример
#include <math.h>

Разница между этими двумя формами заключается только в принципе поиска файлов, что не имеет значения при работе с панелями оператора.

Директива #define используется для определения констант и макросов. Препроцессор заменяет первый аргумент директивы вторым, выполняя макроподстановку.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Макроопределение

#define ARR_SIZE 4

 

__MAIN__()

{

   uint16_t Data[ARR_SIZE];

 

   // Размещаем в массиве значения регистров панели PSW100, PSW200, PSW300 и PSW400...

   //  ...для какой-то последующей обработки

   for (int i = 0; i < ARR_SIZE; i++)

   {

      Data[i] = PSW[100 * (i + 1)];

   }

}

См. также список предопределённых констант #define, автоматически создаваемых препроцессором.

В качестве констант также можно использовать переменные с квалификатором const – по сравнению с макроконстантами этот подход уменьшает вероятность ошибки, так как для таких переменных компилятор сможет проверить соответствие типов.

Пример макроса c параметрами:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Ограничиваем числовое значение в диапазоне [min, max]

#define LIMIT(min, x, max) (((x) < (min)) ? (min) : (((x) > (max)) ? (max) : (x)))

 

__MAIN__()

{

   uint16_t var1 = 20;

   uint16_t var2 = 40;

   uint16_t var3 = 60;

        

   // В регистр PSW100 будет записано значение 30

   PSW[100] = LIMIT(30, var1, 50);

   // В регистр PSW101 будет записано значение 40

   PSW[101] = LIMIT(30, var2, 50);

   // В регистр PSW102 будет записано значение 50

   PSW[102] = LIMIT(30, var3, 50);

}

Такой макрос можно заменить функцией. Но подобные макросы полезны тем, что могут быть добавлены на вкладку Встроенные функции редактора макросов.

Если требуется, чтобы макрос выполнял несколько отдельных выражений, то используется идиома do {…} while(0):

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "stdint.h"

 

// Макрос меняет между собой значения аргументов типа int

// Символ «\» используется для переноса строк внутри кода макроса

#define SWAP(a, b) \

    do { \

        int temp = (a); \

        (a) = (b); \

        (b) = temp; \

    } while(0)

 

__MAIN__()

{

   int x = 10, y = 20;

   SWAP(x, y);

   // Теперь x = 20, y = 10

}

В макросах с параметрами могут использоваться специфические операторы # (обрамление параметра макроса двойными кавычками) и ## (объединение двух лексем). См. примеры использования этих операторов в статье на Википедии.

Примечание

Макросы с параметрами требуют тщательной расстановки скобок, поэтому использовать их следует с осторожностью.

Директивы #warning и #error позволяют вывести в лог компилятора предупреждение или сообщение об ошибке. Это, например, позволяет расставлять в исходном коде различного рода напоминания.

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   #warning Добавить проверку деления на 0

   PSW[106] = PSW[105] / PSW[104];

 

}

Директива #pragma позволяет выполнить указанное после имени директивы действие. Список поддерживаемых действий зависит от компилятора. Например, в пункте 12.5.4 использовалась #pragma pack для управления выравниванием в структуре.

Стандартная библиотека языка Си

Стандартная библиотека языка Си позволяет использовать в программе широкий набор функций, определённых в стандарте языка. Для использования конкретной функции требуется с помощью директивы #include подключить к программе заголовочный файл, в котором объявлена данная функция.

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

Список некоторых заголовочных файлов стандартной библиотеки языка Си

Заголовочный файл

Что содержит

ctype.h

Функции для обработки символов кодировки ASCII

errno.h

Коды ошибок, возвращаемых функциями стандартной библиотеки, и глобальную переменную errno, в которую они записываются

inttypes.h

Функции для работы с целочисленными переменными фиксированного размера

iso646.h

Макроопределения-псевдонимы для логических операторов (например, and для &&)

math.h

Арифметические и тригонометрические функции

stdbool.h

Макросы true и false

stdint.h

Целочисленные типы данных фиксированного размера (uint16_t и т. д.)

stdio.h

Функции для работы с файлами

stdlib.h

Функции общего назначения (выделение и освобождение памяти, генерация псевдослучайных чисел, сортировка и поиск, преобразование чисел в строки и строк в числа и т. д.)

string.h

Функции для работы с ASCII-строками и памятью (memcpy, memmove, memcmp, memset)

time.h

Функции конвертации различных форматов даты и времени

wchar.h

Функции для работы со строками в кодировке UTF-16 (UCS-2)

wctype.h

Функции для обработки символов кодировки UTF-16 (UCS-2)

Встроенные функции и макросы

В редакторе кода на вкладке Встроенные функции расположен список встроенных функций и макросов.

Graphic
Встроенные функции

Добавление пользовательских макросов на эту вкладку рассмотрено в пункте.

Работа с внутренними битами и регистрами панели

Регистры области памяти PSW и SPSW представлены как массивы данных:

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Запись значения в регистр PSW0

   PSW[0] = 123;

 

   // Чтение значения из регистра PSW1 в переменную

   WORD wVar;

   wVar = PSW[1];

 

   // Для работы с различными типами данных нужно использовать операторы конверсии:

   float rValue1;

   rValue1 = *(float*)(PSW + 10);

 

   float rValue2 = 11.22;

   *(float*)(PSW + 12) = rValue2;

 

   DWORD dwValue1;

   dwValue1 = *(DWORD*)(PSW + 14);

 

   DWORD dwValue2 = 123456;

   *(DWORD*)(PSW + 16) = dwValue2;

 

}

Для работы с другими областями памяти используются специальные функции. Некоторые из этих функций могут использоваться и для работы с PSW и SPSW:

Функции для работы с областями памяти панели

Функция

Области памяти, к которым она применима

GetPSB SetPSB

PSB

GetSPSBSetSPSB

SPSB

ReadReadsWriteWrites

PSB, SBSB, PSW, SPSW, PFW, SPFW

WriteF

PSW, SPSW, PFW, SPFW

ReadPFWWritePFW

PFW

ReadSPFWWriteSPFW

SPFW

Функции для работы с памятью панели и slave-устройств

В функциях Read/Reads/Write/WriteF/Writes в случае использования протокола Modbus для параметра DataArea доступны следующие значения:

Значения параметра DataArea для slave-устройств с протоколом Modbus

Modbus RTU

Modbus ASCII

Modbus TCP

Описание

TP7_0x

TP8_0x

TP9_0x

Область coils (биты, доступные для чтения и записи)

TP7_1x

TP8_1x

TP9_1x

Область discrete inputs (биты, доступные только для чтения)

TP7_2x

TP8_2x

TP9_2x

Область input registers (регистры, доступные только для чтения)

TP7_3x

TP8_3x

TP9_3x

Область holding registers (регистры, доступные для чтения и записи)

Эти наборы эквивалентны, т. е. можно, например, использовать TP7_4x для протоколов Modbus ASCII и Modbus TCP.

В случае использования других протоколов введите в редакторе кода имя функции (например, Reads) и поставьте открывающую скобку для вызова окна настройки аргументов функции. Выберите в нём нужное устройство и его область памяти, чтобы автоматически сформировать значение аргумента.

Graphic
Окно настройки аргументов функции

В функциях Read/Reads/Write/WriteF/Writes в случае доступа к внутренней памяти панели для параметра DataArea доступны следующие значения:

  • TP_PSB;

  • TP_SPSB;

  • TP_PSW;

  • TP_PFW;

  • TP_SPSW;

  • TP_SPFW.

В функциях Read/Reads/Write/WriteF/Writes для параметра DataType доступны следующие значения:

  • DT_Bit;

  • DT_Byte (доступен только для некоторых протоколов обмена);

  • DT_WORD;

  • DT_DWORD;

  • DT_DDWORD;

  • DT_Word_String;

  • DT_DWord_String;

  • DT_DDWord_String.

1. Функции GetPSB и GetSPSB

Функции GetPSB и GetSPSB используются для получения значения бита области PSB (GetPSB) или SPSB (GetSPSB).

Описание функции GetPSB (GetSPSB)

Определение

BOOL GetPSB(DWORD Addr);

Addr

Адрес бита

Возвращаемое значение

Значение бита – 1 (true) или 0 (false)

Примечание

Для использования макросов true и false следует подключить заголовочный файл stdbool.h

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Приведенный ниже фрагмент кода выполнится только в том случае, если PSB10 имеет... 

   // ...значение true

   if (GetPSB(10) )

   {

      // ваш код

   }

 

}

2. Функции SetPSB и SetSPSB

Функции SetPSB и SetSPSB используются для установки значения бита области PSB (SetPSB) или SPSB (SetSPSB).

Описание функции SetPSB (SetSPSB)

Определение

void SetPSB(DWORD Addr, BOOL Value);

Addr

Адрес бита

Value

Устанавливаемое значение бита – 1 (true) или 0 (false)

Возвращаемое значение

отсутствует

Примечание

Для использования макросов true и false следует подключить заголовочный файл stdbool.h

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Установка в PSB10 значения true

   SetPSB(10, 1);

}

3. Функция Read

Функция Read используется для чтения одного параметра из памяти панели или slave-устройства в переменную макроса. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода Read и поставьте открывающую скобку.

Описание функции Read

Определение

BOOL Read (int Device, int SlaveId, int DataArea, int DataType, int RegAddr, int BitAddr, void* pValue);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

SlaveId

Адрес устройства (в случае чтения из памяти панели следует использовать адрес 0)

DataArea

Область памяти

DataType

Тип данных

RegAddr

Адрес регистра

BitAddr

Номер бита (для чтения бита регистра, в остальных случаях равен 0)

pValue

Указатель на буфер, в котором будет размещено считанное значение

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Чтение holding-регистра 20 из Modbus RTU Slave-устройства с адресом 5

   WORD wValue1;

   Read(_T("Modbus RTU Master"), 5, TP7_4x, DT_WORD, 20, 0, &wValue1);      

 

   // Чтение внутреннего регистра PFW300

   WORD wValue2;

   Read(_T("Внутренние регистры"), 0, TP_PFW, DT_Word, 300, 0, &wValue2);

 

   // Чтение бита PSB100

   bool xValue1;

   Read(_T("Внутренние регистры"), 0, TP_PSB, DT_Bit, 100, 0, &xValue1);

 

   // Чтение бита PSW200.8

   bool xValue2;

   Read(_T("Внутренние регистры"), 0, TP_PSW, DT_Bit, 200, 8, &xValue2);

 

}

4. Функция Write

Функция Write используется для записи одного значения макроса в память панели или slave-устройство. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода Write и поставьте открывающую скобку.

Описание функции Write

Определение

BOOL Write(int Device, int SlaveId, int DataArea, int DataType, int RegAddr, int BitAddr, void* pValue);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

SlaveId

Адрес устройства (в случае записи в память панели следует использовать адрес 0)

DataArea

Область памяти

DataType

Тип данных

RegAddr

Адрес регистра

BitAddr

Номер бита (для записи бита регистра, в остальных случаях равен 0)

pValue

Указатель на буфер, в котором размещается записываемое значение

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Запись holding-регистра 20 в Modbus RTU Slave-устройстве с адресом 5

   WORD wValue1 = 123;

   Write(_T("Modbus RTU Master"), 5, TP7_4x, DT_WORD, 20, 0, wValue1);      

 

   // Запись во внутренний регистр PFW300

   WORD wValue2 = 123;

   Write(_T("Внутренние регистры"), 0, TP_PFW, DT_Word, 300, 0, wValue2);

 

   // Запись бита PSB100

   bool xValue1 = 1;

   Write(_T("Внутренние регистры"), 0, TP_PSB, DT_Bit, 100, 0, xValue1);

 

   // Запись бита PSW200.8

   bool xValue2 = 1;

   Write(_T("Внутренние регистры"), 0, TP_PSW, DT_Bit, 200, 8, xValue2);  

 

}

5. Функция Reads

Функция Reads используется для чтения одного или нескольких параметров из памяти панели или slave-устройства в переменные макроса. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода Reads и поставьте открывающую скобку.

Описание функции Reads

Определение

BOOL Reads(int Device, int SlaveId, int DataArea, int DataType, int RegAddr, int BitAddr, int Count, void* pValue);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

SlaveId

Адрес устройства (в случае чтения из памяти панели следует использовать адрес 0)

DataArea

Область памяти

DataType

Тип данных

RegAddr

Адрес начального регистра

BitAddr

Номер бита (для чтения битов регистров, в остальных случаях равен 0)

Count

Количество объектов

pValue

Указатель на буфер, в котором будут размещены считанные значения

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Чтение holding-регистров 20...29 из Modbus RTU Slave-устройства с адресом 5

   WORD awData1[10];

   Reads(_T("Modbus RTU Master"), 5, TP7_4x, DT_WORD, 20, 0, 10, awData1);      

 

   // Чтение внутренних регистров PFW300...329

   WORD awData2[30];

   Reads(_T("Внутренние регистры"), 0, TP_PFW, DT_Word, 300, 0, 30, awData2);

 

   // Чтение битов PSB100...PSB115

   WORD wBitMask;

   Reads(_T("Внутренние регистры"), 0, TP_PSB, DT_Bit, 100, 0, 16, &wBitMask);

 

   // Чтение битов PSW200.8...PSW200.15

   BYTE byBitMask;

   Reads(_T("Внутренние регистры"), 0, TP_PSW, DT_Bit, 200, 8, 8, &byBitMask);

 

}

6. Функция Writes

Функция Writes используется для записи одного или нескольких значений макроса в память панели или slave-устройство. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода Writes и поставьте открывающую скобку.

Описание функции Writes

Определение

BOOL Writes(int Device, int SlaveId, int DataArea, int DataType, int RegAddr, int BitAddr, int Count, void* pValue);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

SlaveId

Адрес устройства (в случае чтения из памяти панели следует использовать адрес 0)

DataArea

Область памяти

DataType

Тип данных

RegAddr

Адрес начального регистра

BitAddr

Номер бита (для записи битов регистров, в остальных случаях равен 0)

Count

Количество объектов

pValue

Указатель на буфер, в котором размещаются записываемые значения

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Запись holding-регистров 20...29 в Modbus RTU Slave-устройстве с адресом 5

   WORD awData1[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

   Writes(_T("Modbus RTU Master"), 5, TP7_4x, DT_WORD, 20, 0, 10, awData1);      

 

   // Запись во внутренние регистры PFW300...329

   // PWF300 получит значение 1, PFW310 получит значение 2, PFW320 получит значение 3,

   // PFW329 получит значение 4

   // Остальные регистры получат значение 0

   WORD awData2[30] = { [0] = 1, [10] = 2, [20] = 3, [29] = 4 };

   Writes(_T("Внутренние регистры"), 0, TP_PFW, DT_Word, 300, 0, 30, awData2);

 

   // Запись битов PSB100...PSB115

   WORD wBitMask = 39321;

   Writes(_T("Внутренние регистры"), 0, TP_PSB, DT_Bit, 100, 0, 16, &wBitMask);

 

}

7. Функция WriteF

Функция WriteF используется для записи одного значения с плавающей точкой в память панели или slave-устройство. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода WriteF и поставьте открывающую скобку.

Описание функции WriteF

Определение

BOOL WriteF(int Device, int SlaveId, int DataArea, int DataType, int RegAddr, int BitAddr, void Value);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

SlaveId

Адрес устройства (в случае чтения из памяти панели следует использовать адрес 0)

DataArea

Область памяти

DataType

Тип данных

RegAddr

Адрес начального регистра

BitAddr

Номер бита (не используется, равно 0)

Count

Количество объектов

Value

Записываемое значение типа Float или Double (в зависимости от DataType)

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Запись float-значения в holding-регистры 20-21 в Modbus RTU Slave-устройстве с

   // адресом 5

   float rValue = 11.22;

   WriteF(_T("Modbus RTU Master"), 5, TP7_4x, DT_DWord, 300, 0, rValue);      

 

   // Запись float-значения во внутренние регистры PFW300-PFW301

   float rValue = 11.22;

   WriteF(_T("Внутренние регистры"), 0, TP_PFW, DT_DWord, 300, 0, rValue);

}

8. Функции ReadPFW и ReadSPFW

Функции ReadPFW и ReadSPFW используются для чтения одного или нескольких регистров области PFW (ReadPFW) или SPFW (ReadSPFW) в переменные макроса.

Описание функции ReadPFW (ReadSPFW)

Определение

void ReadPFW(int RegAddr, int ByteCount, void* pData);

RegAddr

Адрес регистра

ByteCount

Количество байт (1 регистр = 2 байта)

pData

Указатель на буфер, в котором будут размещены считанные значения

Возвращаемое значение

Отсутствует

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

 

   // Чтение внутренних регистров PFW300...305

   WORD awValue[6];

   ReadPFW(300, 6 * sizeof(WORD), awValue);   

 

}

9. Функции WritePFW и WriteSPFW

Функции WritePFW и WriteSPFW используются для записи значений макроса в один или несколько регистров области PFW (WritePFW) или SPFW (WriteSPFW).

Описание функции WritePFW (WriteSPFW)

Определение

void WritePFW(int RegAddr, int ByteCount, void* pData);

RegAddr

Адрес регистра

ByteCount

Количество байт (1 регистр = 2 байта)

pData

Указатель на буфер, в котором размещаются записываемые значения

Возвращаемое значение

Отсутствует

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

  // Запись внутренних регистров PFW300...305

   WORD awValue[6] = {1, 2, 3, 4, 5, 6};

  WritePFW(300, 6 * sizeof(WORD), awValue);

}

Функции для обмена данными с ПЛК CODESYS V3.5

Данные функции используются для работы с переменными вкладки С устройства, импортированными из ПЛК с системой исполнения CODESYS V3.5 и опрашиваемыми по протоколу CODESYS V3 (PLC Handler) или OPC UA Client.;

1. Функция CodeSysRead

Функция CodeSysRead используется для чтения значения переменной из вкладки Библиотека переменных – С устройства в переменную макроса. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода CodeSysRead и поставьте открывающую скобку.

Graphic
Чтение символьной переменной
Описание функции CodeSysRead

Определение

BOOL CodeSysRead(int Device, char* LabelName, int Сount, int LabelType, void* pData);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

LabelName

Путь к переменной, разделитель – «/»

Сount

Количество элементов массива (для переменных элементарного типа устанавливается значение 1)

LabelType

Тип переменной (BOOL = 0, BYTE = 1, WORD = 2, DWORD = 3, LWORD = 4, SINT = 5, USINT = 6, INT = 7, UINT = 8, DINT = 9, UDINT = 10, LINT = 11, REAL = 12, LREAL = 13, STRING = 14, TIME = 15, TIME_OF_DAY = 16, DATE = 17, DATE_AND_TIME = 18, WSTRING = 21)

pValue

Указатель на буфер, в котором будет размещено считанное значение (размер буфера должен соответствовать типу данных)

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   WORD wVar;

   CodeSysRead(_T("OPC UA Client"), "Application/PLC_PRG/wTestVar", 1, 2, &wVar);

   PSW[10] = wVar;  

}

2. Функция CodeSysWrite

Функция CodeSysWrite используется для записи значения переменной макроса в переменную вкладки Библиотека переменных – С устройства. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода CodeSysWrite и поставьте открывающую скобку.

Описание функции CodeSysWrite

Определение

BOOL CodeSysWrite (int Device, char* LabelName, int Сount, int LabelType, void* pData);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

LabelName

Путь к переменной, разделитель – «/»

Сount

Количество элементов массива (для переменных элементарного типа устанавливается значение 1)

LabelType

Тип переменной (BOOL = 0, BYTE = 1, WORD = 2, DWORD = 3, LWORD = 4, SINT = 5, USINT = 6, INT = 7, UINT = 8, DINT = 9, UDINT = 10, LINT = 11, REAL = 12, LREAL = 13, STRING = 14, TIME = 15, TIME_OF_DAY = 16, DATE = 17, DATE_AND_TIME = 18, WSTRING = 21)

pValue

Указатель на буфер, в котором размещается записываемое значение (размер буфера должен соответствовать типу данных)

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   WORD wVar = 123;

   CodeSysWrite(_T("OPC UA Client"), "Application/PLC_PRG/wTestVar", 1, 2, &wVar);

}

3. Функция CodeSysReadString

Функция CodeSysReadString используется для чтения значения строковой переменной из вкладки Библиотека переменных – С устройства в переменную макроса. Для строк типа WSTRING выполняется автоматическая конвертация в ASCII. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода CodeSysReadString и поставьте открывающую скобку.

Описание функции CodeSysReadString

Определение

CodeSysReadString(int Device, char* LabelName, int Сount, int Len, void* pData);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

LabelName

Путь к переменной, разделитель – «/»

Len

Количество считываемых символов строки

pData

Указатель на буфер, в котором будет размещено считанное значение (размер буфера должен соответствовать Len)

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   char sVar[81] = {0};

   // sTestVar имеет тип STRING(80)

   CodeSysReadString(_T("OPC UA Client"), "Application/PLC_PRG/sTestVar", 1, 80, sVar);

   // Значение переменной будет записано в регистры панели PSW100...PSW139

   // Каждый символ строки типа STRING занимает 1 байт, размер регистра - 2 байта

   // Writes(_T("Внутренние регистры"), 0, TP_PSW, DT_Word, 100, 0, 40, sVar);

}

4. Функция CodeSysWriteString

Функция CodeSysWriteString используется для записи строковой переменной макроса в переменную вкладки Библиотека переменных – С устройства. Для строк типа WSTRING должна использоваться кодировка ASCII (конвертация будет выполнена автоматически). Строка не должна содержать символы кириллицы. Для настройки параметров функции с помощью вспомогательного меню введите в редакторе кода CodeSysWriteString и поставьте открывающую скобку.

Описание функции CodeSysWriteString

Определение

CodeSysWriteString(int Device, char* LabelName, int Сount, int Len,void* pData);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

LabelName

Путь к переменной, разделитель – «/»

Len

Количество символов записываемой строки

pData

Указатель на буфер, в котором размещается записываемое значение (размер буфера должен соответствовать Len)

Возвращаемое значение

TRUE – успешное выполнение операции, FALSE – ошибка (например, не был получен ответ от slave-устройства)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   char sVar[12] = "hello, world";

   CodeSysWriteString(_T("OPC UA Client"), "Application/PLC_PRG/sTestVar", 1, 12, sVar);

}

Функции для реализации пользовательского протокола

Данные функции используются совместно с протоколом Пользовательский и позволяют реализовать отправку и получение через заданный COM-порт произвольного набора байт.

1. Функция COMSend

Функция COMSend отправляет в COM-порт заданный набор байт.

Описание функции COMSend

Определение

int COMSend(int Device, char* pData, int BufSize);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

pData

Указатель на буфер отправляемых данных

BufSize

Размер буфера в байтах

Возвращаемое значение

Количество отправленных байт (-1 – ошибка выполнения функции)

Примечание

1. В меню Настройки проекта - Устройства должен быть выбран Пользовательский протокол

2. Функция блокирует поток выполнения до завершения своей работы

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "string.h"

 

__MAIN__()

{

   int result = -1;

   char abyData[20] = "Hello, world";

   // Для использования функции strlen требуется подключить заголовочный файл string.h

   result = COMSend(_T("Пользовательский протокол"), abyData, strlen(abyData) );

}

2. Функция COMReceive

Функция COMReceive используется для чтения данных из буфера COM-порта.

Описание функции COMReceive

Определение

int COMReceive(int Device, char* pData, int BufSize, unsigned short TimeOut, unsigned short EndFrameBytes);

Device

ID устройства. Используйте оператор _T для конвертации символьного имени устройства в его ID

pData

Указатель на буфер, в котором будут размещены считанные данные

BufSize

Размер буфера в байтах

TimeOut

Время ожидания (в мс) получения BufSize байт. По истечению времени происходит завершение работы функции

0 – время ожидания не ограничено

EndFrameBytes

Если в течение времени, требуемого для передачи указанного количества байт, не поступило новых данных, то происходит завершение работы функции. 

0 – функция не завершает свою работу до получения BufSize байт

Возвращаемое значение

Количество полученных байт (0 – в буфере отсутствуют данные, -1 – ошибка выполнения функции)

Примечание

1. В меню Настройки проекта - Устройства должен быть выбран Пользовательский протокол

2. Функция блокирует поток выполнения до завершения своей работы

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "string.h"

 

__MAIN__()

{

   int result = -1;

   char abyData[10] = {0};

 

   // Функция завершает свою работу после получения 10 байт данных

   result = COMReceive(_T("Пользовательский протокол"), abyData, sizeof(abyData), 0, 0);

}

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "string.h"

 

__MAIN__()

{

   int result = -1;

   char abyData[10] = {0};

   // Функция завершает свою работу после получения 10 байт данных

   // Если в течение 1000 мс не получено 10 байт - то функция завершает свою работу

   result = COMReceive(_T("Пользовательский протокол"), abyData, sizeof(abyData), 1000, 0);

}

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

#include "string.h"

 

__MAIN__()

{

   int result = -1;

   char abyData[10] = {0};

 

   // Функция завершает свою работу после получения 10 байт данных

   // Если в течение 1000 мс не получено 10 байт - то функция завершает свою работу

   // Если в процессе получения данных возникает пауза, длительность которой равна...

   // ...времени передачи 10 байт на установленной для COM-порта скорости...

   // ...то функция завершает свою работу

   result = COMReceive(_T("Пользовательский протокол"), abyData, sizeof(abyData), 1000, 10);

}

Функции для работы с элементом Холст

Данные функции используются для работы с элементом Холст.

1. Функция DCMapClear

Функция DCMapClear используется для очистки холста.

Описание функции DCMapClear

Определение

BOOL DCMapClear(DWORD dwDCMapID);

dwDCMapID

Индекс холста (см. настройки элемента Холст)

Возвращаемое значение

TRUE – операция уcпешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapClear(0);

}

2. Функция DCMapDrawCircle

Функция DCMapDrawCircle используется для отрисовки на холсте окружности.

Описание функции DCMapDrawCircle

Определение

BOOL DCMapDrawCircle(DWORD dwDCMapID, int x, int y, int Radius,

int LineWidth, DWORD LineColor, BOOL DoFill, DWORD FillColor);

dwDCMapID

Индекс холста (см. настройки элемента Холст)

x

Координата центра окружности по оси X (относительно верхнего левого края холста)

y

Координата центра окружности по оси Y (относительно верхнего левого края холста)

Radius

Радиус окружности

LineWidth

Толщина контура

LineColor

Цвет контура окружности в BGR

DoFill

Флаг заливки окружности цветом (1 – заливка, 0 – отсутствие заливки)

FillColor

Цвет заливки окружности в BGR

Возвращаемое значение

TRUE – операция уcпешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapDrawCircle(0, 200, 200, 100, 2, 0x0000000, 1, 0xFF0000);

}

3. Функция DCMapDrawCircleArc 

Функция DCMapDrawCircleArc используется для отрисовки на холсте дуги.

Описание функции DCMapDrawCircleArc

Определение

BOOL DCMapDrawCircleArc(DWORD dwDCMapID, int x, int y, int Radius, int LineWidth, DWORD LineColor, DWORD StartAngle, DWORD EndAngle); 

dwDCMapID

Индекс холста (см. настройки элемента Холст)

x

Координата центра дуги по оси X (относительно верхнего левого края холста)

y

Координата центра дуги по оси Y (относительно верхнего левого края холста)

Radius

Радиус дуги

LineWidth

Толщина контура

LineColor

Цвет контура окружности в BGR

StartAngle

Угол начала дуги

EndAngle

Угол конца дуги

Возвращаемое значение

TRUE – операция уcпешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapDrawCircleArc(0, 200, 200, 100, 2, 0x0000000, 70, 190);

}

4. Функция DCMapDrawEllipse

Функция DCMapDrawEllipse используется для отрисовки на холсте эллипса.

Описание функции DCMapDrawEllipse

Определение

BOOL DCMapDrawCircleArc(DWORD dwDCMapID, int x, int y, int xRadius, int yRadius, int LineWidth, DWORD LineColor, DWORD StartAngle, DWORD EndAngle); 

dwDCMapID

Индекс холста (см. настройки элемента Холст)

x

Координата центра эллипса по оси X (относительно верхнего левого края холста)

y

Координата центра эллипса по оси Y (относительно верхнего левого края холста)

xRadius Радиус по X
yRadius Радиус по Y

LineWidth

Толщина контура

LineColor

Цвет контура в BGR

DoFill

Флаг заливки эллипса цветом (1 – заливка, 0 – отсутствие заливки)

FillColor

Цвет заливки эллипса в BGR

Возвращаемое значение

TRUE – операция уcпешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapDrawEllipse(0, 200, 200, 100, 50, 2, 0x0000000, 1, 0xFF0000);

}

5. Функция DCMapDrawEllipseArc

Функция DCMapDrawEllipseArc используется для отрисовки на холсте эллиптической дуги.

Описание функции DCMapDrawEllipseArc

Определение

BOOL DCMapDrawCircleArc(DWORD dwDCMapID, int x, int y, int Radius, int LineWidth, DWORD LineColor, DWORD StartAngle, DWORD EndAngle);

dwDCMapID

Индекс холста (см. настройки элемента Холст)

x

Координата центра эллиптической дуги по оси X (относительно верхнего левого края холста)

y

Координата центра эллиптической дуги по оси Y (относительно верхнего левого края холста)

Radius

Радиус эллиптической дуги

LineWidth

Толщина контура

LineColor

Цвет контура в BGR

StartAngle

Угол начала эллиптической дуги

EndAngle

Угол конца эллиптической дуги

Возвращаемое значение

TRUE – операция уcпешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapDrawEllipseArc(0, 200, 200, 100, 50, 2, 0x0000000, 70, 190);

}

6. Функция DCMapDrawLine

Функция DCMapDrawLine используется для отрисовки на холсте линии.

Описание функции DCMapDrawLine

Определение

BOOL DCMapDrawLine(DWORD dwDCMapID, int xStart, int yStart, int xEnd, int yEnd, int LineWidth, DWORD LineColor);

dwDCMapID

Индекс холста (см. настройки элемента Холст)

xStart

Координата начальной точки по оси X (относительно верхнего левого края холста)

yStart

Координата начальной точки по оси Y (относительно верхнего левого края холста)

xEnd

Координата конечной точки по оси X (относительно верхнего левого края холста)

yEnd

Координата конечной точки по оси Y (относительно верхнего левого края холста)

LineWidth

Толщина линии

LineColor

Цвет линии в BGR

Возвращаемое значение

TRUE – операция уcпешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapDrawLine(0, 200, 200, 400, 500, 2, 0x0000000);

}

7. Функция DCMapDrawRect

Функция DCMapDrawRect используется для отрисовки на холсте прямоугольника.

Описание функции DCMapDrawRect

Определение

BOOL DCMapDrawRect(DWORD dwDCMapID, int x, int y, int Width, int Height, int LineWidth, DWORD LineColor, BOOL DoFill, DWORD FillColor);

dwDCMapID

Индекс холста (см. настройки элемента Холст)

x

Координата начальной точки прямоугольника по оси X (относительно верхнего левого края холста)

y

Координата начальной точки прямоугольника по оси Y (относительно верхнего левого края холста)

Width

Ширина

Height

Высота

LineWidth

Толщина контура

LineColor

Цвет контура в BGR

DoFill

Флаг заливки прямоугольника цветом (1 – заливка, 0 – отсутствие заливки)

FillColor

Цвет заливки прямоугольника в BGR

Возвращаемое значение

TRUE – операция успешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapDrawRect(0, 200, 200, 100, 150, 2, 0x0000000, 1, 0xFF0000);

}

8. Функция DCMapSetBackColor

Функция DCMapSetBackColor используется для установки фона холста.

Описание функции DCMapSetBackColor

Определение

BOOL DCMapSetBackColor(DWORD dwDCMapID, DWORD Color);

dwDCMapID

Индекс холста (см. настройки элемента Холст)

Color

Цвет фона холста в BGR

Возвращаемое значение

TRUE – операция успешно выполнена, FALSE – операция не выполнена (холст с таким индексом не существует)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DCMapSetBackColor(0, 0xFF0000);

}

Функции для работы с экранами и окнами

1. Функция ScreenJump

Функция ScreenJump используется для переключения экрана.

Описание функции ScreenJump

Определение

WORD ScreenJump(WORD ScreenNo);

ScreenNo

Номер экрана

Возвращаемое значение

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

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Переход на экран 2

   // В случае успешного перехода переменная wCurrentNo получит значение 2

   WORD wCurrentNo;

   wCurrentNo = ScreenJump(2);            

}

2. Функция OpenWindow

Функция OpenWindow используется для открытия окна.

Описании функции OpenWindow

Определение

void OpenWindow(int winNo, int x, int y);

winNo

Номер окна

x

Координата открытия по X (для верхней левой точки окна)

y

Координата открытия по Y (для верхней левой точки окна)

Возвращаемое значение

Отсутствует

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{       

   // Открыть окно с номером 2

   // Верхней левой точке окна будут соответствовать координаты (10, 10)            

   OpenWindow(2, 10, 10);        

}

3. Функция CloseWindow

Функция CloseWindow используется для закрытия окна.

Описание функции CloseWindow

Определение

void CloseWindow(WORD winNo);

winNo

Номер окна

Возвращаемое значение

Отсутствует

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{       

   // Закрыть окно с номером 2

   CloseWindow(2);       

}

Функции для обработки данных

1. Макрос GET_BIT

Макрос GET_BIT используется для извлечения указанного бита целочисленной переменной.

Описание макроса GET_BIT

Определение

bool GET_BIT(Value, BitNumber);

Value

Целочисленная переменная

BitNumber

Номер бита

Возвращаемое значение

Значение бита – 1 (true) или 0 (false)

Примечание

Для использования макросов true и false следует подключить заголовочный файл stdbool.h

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Приведенный ниже фрагмент кода выполнится только в том случае…

   // ...если PSW300.15 имеет значение true

   if (GET_BIT(PSW[300], 15) )

   {

      // ваш код

   }

                 

}

2. Макрос SET_BIT

Макрос SET_BIT используется для установки указанного бита целочисленной переменной в значение true.

Определение

SET_BIT(Value, BitNumber);

Value

Целочисленная переменная

BitNumber

Номер бита

Возвращаемое значение

Целочисленное значение с установленным битом

Примечание

Для использования макросов true и false следует подключить заголовочный файл stdbool.h

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Установка бита PSW300.15 в значение true

   PSW[300] = SET_BIT(PSW[300], 15);              

}

3. Макрос RESET_BIT

Макрос RESET_BIT используется для установки указанного бита целочисленной переменной в значение false.

Описание макроса RESET_BIT

Определение

RESET_BIT(Value, BitNumber);

Value

Целочисленная переменная

BitNumber

Номер бита

Возвращаемое значение

Целочисленное значение со сброшенным битом

Примечание

Для использования макросов true и false следует подключить заголовочный файл stdbool.h

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Установка бита PSW300.15 в значение false

   PSW[300] = RESET_BIT(PSW[300], 15);            

}

4. Макрос HIWORD

Макрос HIWORD используется для получения старшего слова значения типа DWORD.

Описание макроса HIWORD

Определение

WORD HIWORD(DWORD dw);

dw

Значение типа DWORD

Возвращаемое значение

Cтаршее слово dw

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DWORD dwVar = 0xFFFFAAAA;

   // PSW0 получит значение 65535 (0xFFFF)

   PSW[0] = HIWORD(dwVar);                         

}

5. Макрос LOWORD

Макрос LOWORD используется для получения младшего слова значения типа DWORD.

Описание макроса LOWORD

Определение

WORD LOWORD(DWORD dw);

dw

Значение типа DWORD

Возвращаемое значение

Младшее слово dw

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   DWORD dwVar = 0xAAAAFFFF;

   // PSW0 получит значение 65535 (0xFFFF)

   PSW[0] = LOWORD(dwVar);                         

}

6. Макрос HIBYTE

Макрос HIBYTE используется для получения старшего байта значения типа WORD.

Определение

BYTE HIBYTE(WORD w);

w

Значение типа WORD

Возвращаемое значение

Cтарший байт w

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   WORD wVar = 0xFFAA;

   // PSW0 получит значение 255 (0xFF)

   PSW[0] = HIBYTE(wVar);                 

}

7. Макрос LOBYTE

Макрос LOBYTE используется для получения младшего байта значения типа WORD.

Описание макроса LOBYTE

Определение

BYTE LOBYTE(WORD w);

w

Значение типа WORD

Возвращаемое значение

Младший байт w

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   WORD wVar = 0xAAFF;

   // PSW0 получит значение 255 (0xFF)

   PSW[0] = LOBYTE(wVar);         

}

8. Макрос MAKEDWORD

Макрос MAKEDWORD используется для «сборки» значения типа DWORD из двух значений типа WORD.

Определение

DWORD MAKEDWORD(WORD wl, WORD wh);

wl

Младшее слово значения

wh

Старшее слово значения

Возвращаемое значение

Значение типа DWORD

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{       

   WORD wLow = 0xAAAA;

   WORD wHigh = 0xFFFF;

   DWORD dwVar;

   // dwVar получит значение 0xFFFFAAAA

   *(DWORD*)(PSW + 0) = MAKEDWORD(wLow, wHigh);                     

}

9. Макрос MAKEWORD

Макрос MAKEWORD используется для «сборки» значения типа WORD из двух значений типа BYTE.

Описание макроса MAKEWORD

Определение

DWORD MAKEWORD(BYTE byl, BYTE byh);

byl

Младший байт значения

byh

Старший байт значения

Возвращаемое значение

Значение типа WORD

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{       

   BYTE byLow = 0xAA;

   BYTE byHigh = 0xFF;

   // PSW0 получит значение 65450 (0xFFAA)

  PSW[0] = MAKEWORD(byLow, byHigh);                       

}

10. Макрос MAX

Макрос MAX возвращает наибольшее из двух числовых значений.

Описание макроса MAX

Определение

MAX(a, b);

a

Числовое значение

b

Числовое значение

Возвращаемое значение

Наибольшее из a и b

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{       

   float rVar1 = 11.22;

   float rVar2 = 22.33;

   float rMax;

   // rMax получит значение 22.33

   rMax = MAX(rVar1, rVar2);

  *(float*)(PSW + 0) = rMax;                     

}

11. Макрос MIN

Макрос MIN возвращает наименьшее из двух числовых значений.

Описание макроса MIN

Определение

MIN(a, b);

a

Числовое значение

b

Числовое значение

Возвращаемое значение

Наименьшее из a и b

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{       

   float rVar1 = 11.22;

   float rVar2 = 22.33;

   float rMin;

   // rMin получит значение 11.22

   rMin = MIN(rVar1, rVar2)

  *(float*)(PSW + 0) = rMin;                     

}

Остальные функции

1. Функция Beep

Функция Beep используется для включения зуммера.

Описание функции Beep

Определение

void Beep(void);

Возвращаемое значение

Отсутствует

Примечание

Не имеет эффекта, если зуммер отключен (Настройки проекта – Общие – Отключить зуммер)

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Пока бит PSB300 имеет значение TRUE - зуммер будет активен

   if (GetPSB(300) )

   {

      Beep();   

   }             

}

2. Функция Delay

Функция Delay организует синхронную (блокирующую) задержку выполнения кода функции.

Описание функции Delay

Определение

void Delay(UINT ms);

ms

Интервал задержки в миллисекундах

Возвращаемое значение

Отсутствует

Примечание

Если вызывается в функции с типом вызова Синхронная, то блокирует работу всех остальных функций в пределах элемента, к которому привязана

Пример

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

   // Задержка выполнения следующих строк кода на 1 секунду

   Delay(1000);                   

}

3. Функции Lock и UnLock

Функция Lock организует блокировку доступа к секции кода для исключения несогласованного доступа к одним и тем же данным из разных функций. Функция UnLock снимает блокировку.

Описание функций Lock и UnLock

Определение

void Lock(int id);

void UnLock(int id);

id

Идентификатор секции (0...9)

Возвращаемое значение

Отсутствует

Примечание

1. Каждому вызову функции Lock должен соответствовать вызов функции UnLock

2. Рекомендуется по возможности избегать доступа к одним и тем же данным из разных функций

Пример

// Функции func0 и func1 вызываются независимо друг от друга...

// ...и осуществляют запись в один и тот же регистр

// (по возможности такого поведения стоит избегать)

// Для осуществления согласованного доступа (чтобы функции не начали перезаписывать...

// ...регистр одновременно, «испортив» его значение) используется блокировка

 

void func0()

{

   Lock(0);

   PSW[300] = 55;

   UnLock(0);

}

 

void func1()

{

   Lock(0);

   PSW[300] = 66;

   UnLock(0);

}

Добавление пользовательских макросов на вкладку Встроенные функции

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

  • открыть файл \Tool\Macro\sys\macro_def.h;

  • в конец файла с помощью директивы #define добавить новый макрос;

  • в папке \Tool\Macro\Definition добавить .html файл, имя которого должно совпадать с именем макроса. В качестве примера структуры файла смотрите уже существующие файлы данной директории.

Синхронное и асинхронное выполнение макросов

При настройке в графическом элементе действия Вызов макроса следует указать режим исполнения: синхронный или асинхронный. Эта настройка определяет, блокирует ли выполнение макроса работу остальных макросов проекта: синхронный — блокирует, асинхронный — не блокирует.

Graphic
Вызов макроса

Рассмотрим пример. Пусть в проекте создано два макроса:

Пример

/**************************************************

    Name    : Func0.c

**************************************************/

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

    PSW[0] = 1;

    Delay(5000); // 5000 мс = 5 секунд

}

Пример

/**************************************************

    Name    : Func1.c

**************************************************/

#include "macro.h"

#define __SYS_DEFINE__

 

__MAIN__()

{

    PSW[1] = 2;

}

Значения регистров PSW0 и PSW1 привязаны к цифровым дисплеям.

Создана функциональная область с режимом запуска бит управления (PSB0), в которой настроен вызов макросов Func0 и Func1. На экране также размещен переключатель, устанавливающий бит PSB0 в значение TRUE и таким образом запускающий исполнение обоих макросов.

Если для макроса Func0 настроен режим исполнения Синхронный, то сразу после нажатия на переключатель в регистр PSW0 будет записано значение 1, а спустя 5 секунд в регистр PSW1 будет записано значение 2. То есть фактически макрос Func1 будет запущен на исполнение только после завершения работы макроса Func0, в котором с помощью функции Delay организована блокирующая задержка.

Если для макроса Func0 настроен режим исполнения Асинхронный, то сразу после нажатия на переключатель в регистр PSW0 будет записано значение 1, а в регистр PSW1 – значение 2. То есть фактически макрос Func1 будет запущен на исполнение одновременно с Func0.

При этом режим исполнения макроса Func1 не повлияет на описанное выше поведение; режим исполнения определяет, будет ли настраиваемый макрос блокировать исполнение других макросов проекта.

Примечание
Синхронные макросы, настроенные в разных элементах, не блокируют работу друг друга.
Примечание
Синхронные макросы не блокируют обработку визуализации панели и обмен, настроенный через элементы.

Особенности работы с файлами

При работе с файлами следует учитывать следующие особенности:

  • для работы с файлами используются функции заголовочного файла stdio.h;

  • для работы с директориями используются функции заголовочного файла unistd.h (mkdir, rmdir);

  • директория монтирования USB-накопителя: /mnt/udisk;

  • требования к USB-накопителю: файловая система FAT32, объём – не более 32 Гб;

  • перед работой с USB-накопителем следует проверить факт его монтирования с помощью системного бита SPSB15. Попытка доступа к непримонтированному накопителю может привести к исключению в приложении панели;

  • для размонтирования USB-накопителя перед его извлечением следует установить системный бит SPSB201. Извлечение накопителя «на горячую» (без размонтирования) может привести к повреждению файлов или выходу из строя самого накопителя;

  • в режиме эмуляции можно работать с файлами на ПК (при условии, что эти файлы доступны для пользователя, от имени которого запускается конфигуратор). В этом случае пути к файлам должны экранироваться: C:\\test_folder\\test_file.txt

    .

    В качестве рабочей директории используется директория установки конфигуратора;

  • закрытие файла (fclose) не гарантирует, что сразу после этого буферизированные данные будут записаны в файл. Используйте функцию fflush для принудительной записи. При работе с директориями для аналогичной цели можно использовать функцию system("sync") из заголовочного файла stdlib.h.