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

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

Так как предполагается, что функции и данные будут объединены в структуру с одним именем, то такая структура будет носить название «объект».

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

Заголовочный файл состоит из 4-х частей:

1. объявление структуры как типа данных; в структуру входят все переменные функции, а также указатели на адреса процедур, применяющихся при исполнении функций, обычно это процедуры инициализации (init) и расчета (update или calc).

2. Определение типа данных для указателя на структуру п.1

3. Объявление прототипов процедур, применяемых при обслуживании и расчете функции.

4. Задание начальных констант в структуру.

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

Сохраните следующий файл как заголовочный (с расширением *.h) в директорию «include»:

#ifndef __RAMP_H__

#define __RAMP_H__

typedef struct

{

int step;

int ramp_out;

int max_out;

int (*init)();

int (*update)();

} RAMP;

typedef RAMP *RAMP_handle;

void RAMP_init(RAMP_handle);

void RAMP_update(RAMP_handle);

#define RAMP_DEFAULTS { 1,\

0,\

100,\

(int (*)(int))RAMP_init,\

(int (*)(int))RAMP_update}

#endif

Примечание. При задании параметров по умолчанию следует четко соблюдать чередование символов «переброс каретки» и «\», в противном случае возможны ошибки компиляции. См.пример.

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

#include "..\include\ramp.h"

/* процедура инициализации */

void RAMP_init(RAMP *v)

{

v->step=3;

}

/* процедура расчета */

void RAMP_update(RAMP *v)

{

v->ramp_out=v->ramp_out+v->step;

if (v->ramp_out>v->max_out) v->ramp_out=0;

}

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

RAMP ramp1=RAMP_DEFAULTS;

RAMP ramp2=RAMP_DEFAULTS;

В основной файл исходного текста проекта необходимо добавить вызовы функций объектов, например:

int a=0;

void main()

{

RAMP_init(&ramp1);

RAMP_init(&ramp2);

while (1)

{

a++;

RAMP_update(&ramp1);

RAMP_update(&ramp2);

}

}

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

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

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

#ifndef __LES1_H__

#define __LES1_H__

Для определения константы используем директиву #define. Допустим, необходимо, чтобы при компиляции происходило генерирование кода на сложение или вычитание. Для этого вводим

#define ADD 0

#define SUB 1

В дальнейшем ход компиляции будет определяться в зависимости от значений констант ADD или SUB.

#if ADD

my1.c = my1.a + my1.b;

#endif

#if SUB

my1.c = my1.a - my1.b;

#endif

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

#define quad(x) ((x)*(x))

Вызов макроса возможен следующим образом:

float temp=0;

temp = quad(3.5);

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

Директива #pragma позволяет определить область памяти, куда будут размещены данные или код (например, в командном файле должна быть задана секция sect):

#pragma CODE_SECTION(www, "sect");

void www(void);

В данном случае при объявлении прототипа функции указывается ее размещение в памяти.

Полный пример заголовочного файла les1.h:

#ifndef __LES1_H__

#define __LES1_H__

#define ADD 0

#define SUB 1

#define Uint16 unsigned int

#define quad(x) ((x)*(x))

typedef struct

{

float a;

float b;

float c;

} MY_STRUCT;

#define MY_STRUCT_DEFAULTS {2,\

3,\

0}

struct ABCD_bits{

Uint16 a:4;

Uint16 b:4;

Uint16 c:4;

Uint16 d:4;

};

typedef union ABCD_union {

Uint16 all;

struct ABCD_bits bit;

} ABCD_union;

#endif

Полный пример файла программы les1.c:

#include "les1.h"

#include <float.h>

MY_STRUCT my1=MY_STRUCT_DEFAULTS;

#pragma CODE_SECTION(www, "sect");

void www(void);

ABCD_union ax;

float temp=0;

void main()

{

while(1)

{

temp = quad(3.5);

#if ADD

my1.c = my1.a + my1.b;

#endif

#if SUB

my1.c = my1.a - my1.b;

#endif

www();

}

}

void www()

{

ax.bit.c=1;

}

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

Собственно разработанные процедуры полезно объединять в библиотеки, объединяющие процедуры пользователя по функциональному принципу (библиотека регуляторов, драйверов, сигналов и т.д.). Библиотека будет содержать объектный код, который будет подключен в основной проект при выполнении компоновки (выполняется при подаче команды project-build).

Для этого необходимо создать проект библиотеки Project-New, в появившемся окне указать имя библиотеки (автоматически будет создан каталог библиотеки, куда необходимо сохранять все ее исходные файлы), в поле типа проекта (Project type) выбрать Library (.lib). В окне настройки опции компиляции изменятся названия закладок (появится Archiver), см.рис.1.

Занятие 5. Технология создания собственных функций для приложений - student2.ru

Рис.1

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

Наши рекомендации