Разделы презентаций


Программирование на Си в среде CCS

Содержание

Язык СиЯзык высокого уровняНизкоуровневые механизмы обращения с даннымиОптимально преобразуется в объектный исполняемый кодШирокое распространениеОчень большая поддержка

Слайды и текст этой презентации

Слайд 1Программирование на Си в среде CCS

Программирование на Си в среде CCS

Слайд 2Язык Си
Язык высокого уровня
Низкоуровневые механизмы обращения с данными
Оптимально преобразуется в

объектный исполняемый код
Широкое распространение
Очень большая поддержка

Язык СиЯзык высокого уровняНизкоуровневые механизмы обращения с даннымиОптимально преобразуется в объектный исполняемый кодШирокое распространениеОчень большая поддержка

Слайд 3Программа на Си
/* объявление переменных */
int a; // описание первого

слагаемого
int b; // описание второго слагаемого
int c; // описание результата

(суммы)
/* основная программа */
void main() /* главная функция программы */
{
// текст программы – подсчёт суммы двух чисел
a = 10;
b = 5;
c = a + b; // подсчёт суммы двух чисел
}
Программа на Си/* объявление переменных */int a; // описание первого слагаемогоint b; // описание второго слагаемогоint c;

Слайд 4Программа на Си
Состоит из функции main(), которая является главной функцией

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

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

Слайд 5Главная функция
/* основная программа */
void main() /* главная функция программы

*/
{
/* текст программы – подсчёт суммы двух чисел */

a = 10;
b = 5;
c = a + b; // подсчёт суммы двух чисел
}

Функция «main» является главной функцией программы. Она обязательно должна присутствовать в проекте. После того,
как будет выполнен код инициализации микроконтроллера и глобальных переменных программы, управление будет передано этой функции. Можно сказать, что выполнение пользовательской программы начинается с этой функции.

Главная функция/* основная программа */void main() /* главная функция программы */{ /* текст программы – подсчёт суммы

Слайд 6Оператор присваивания
Оператор присваивания – «=». Записывает в переменную значение. Кроме

того существует понятие «составного присваивания»: выполнить действие с переменной и

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

x = x + 5 можно записать как x+=5
y = y – 2 можно записать как y-=2

Оператор присваиванияОператор присваивания – «=». Записывает в переменную значение. Кроме того существует понятие «составного присваивания»: выполнить действие

Слайд 7Объявление переменных
/* объявление переменных */
int a; // описание первого слагаемого
int

b; // описание второго слагаемого
int c; // описание результата (суммы)
/*

объявление переменных */
int a, b, c;

Сначала указывается тип переменной, затем её имя.

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

Объявление переменных/* объявление переменных */int a; // описание первого слагаемогоint b; // описание второго слагаемогоint c; //

Слайд 8Объявление переменных
При объявлении переменной можно сразу её проинициализировать. В таком

случае, к началу выполнения программы (функции «main») её значение будет

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

Слайд 9Имена переменных
Состоят из букв и цифр
Первый символ – всегда буква
Символ

«_» считается буквой
Строчные буквы для имен переменных
Заглавные для символических констант
Нельзя

использовать в качестве имен зарезервированные слова (if, else, for...)
Имена переменныхСостоят из букв и цифрПервый символ – всегда букваСимвол «_» считается буквойСтрочные буквы для имен переменныхЗаглавные

Слайд 10Типы данных языка Си для TMS320x28xx

Типы данных языка Си для TMS320x28xx

Слайд 11Объявление массивов
Массив – последовательность элементов одного типа в памяти. Синтаксис

объявления массива: сначала указывается типа элементов массива, затем имя, а

после имени в квадратных скобках – количество элементов.
Нумерация элементов начинается с «0»

// Объявление массива «a» из 100 элементов типа "int"
int a[100];

void fillBuf (void){
a[0] = 5; // Записать число "5" в первый элемент массива
a[1] = 6; // Записать число "6" во второй элемент массива
}

Объявление массивовМассив – последовательность элементов одного типа в памяти. Синтаксис объявления массива: сначала указывается типа элементов массива,

Слайд 12Структуры
Структуры позволяют сгруппировать несколько переменных разных типов. Также среди членов

структуры могут быть указатели на переменные и указатели на функции.

Пример описания структуры:

// Пример описания структуры с элементами разных типов
struct myStruct {
int mem1;
unsigned long mem2;
long *pmem3;

void (*someFunc)();
};

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

Слайд 13Структуры
Использование структур удобно, когда нужно задать несколько объектов, имеющий одинаковые

свойства. Например – каналы АЦП, которые имеют коэффициент усиления, смещение

сигнала, код АЦП, результат преобразования и т.д. Один контроллер может поддерживать 10 – 15 каналов АЦП, что может дать 45 переменных.

// Структура-описатель канала АЦП
struct adcChannel {
// Коэфф. усиления
int gain;
// Смещение сигнала
int offset;
// Результат оцифровки
int result;

// Функция очистки данных
void (*clear)();
};

void main (void) {
// Создать структуры для обработки АЦП
struct adcChannel Udc; // Напр. ЗПТ
struct adcChannel Ia, Ib, Ic; // Фазные токи

// Настройка канала АЦП
Udc.gain = 540;
Udc.offset = 0;
}

СтруктурыИспользование структур удобно, когда нужно задать несколько объектов, имеющий одинаковые свойства. Например – каналы АЦП, которые имеют

Слайд 14Инициализация структур
Структуру можно инициализировать двумя способами:
Присвоить разные значения каждому элементу
Присвоить

всем элементам одно и то же значение
Значения для инициализации приводятся

после знака « = » в фигурных скобках:

// Определение структуры даты
struct Sdate {
int year, month, day, dayOfWeek;
int hour, minute, second;
};

// Создание экземпляра. Каждый элемент инициализирован своим значением
struct Sdate dateOfBirth = {1980, 11, 21, 4, 13, 24, 11};
// Все элементы инициализированы значением "0"
struct Sdate dateOfDeath = {0};

Инициализация структурСтруктуру можно инициализировать двумя способами:Присвоить разные значения каждому элементуПрисвоить всем элементам одно и то же значениеЗначения

Слайд 15Операции языка Си
Арифметические
Сравнения
Логические
Поразрядные логические
Присваивания

Операции языка СиАрифметическиеСравненияЛогическиеПоразрядные логическиеПрисваивания

Слайд 16Арифметические операции

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

Слайд 17Инкрементирование и декрементирование
Инкремент – увеличение значения переменной на 1
Декремент – уменьшение

значения переменной на 1
Различают пред-инкремент и пост-инкремент:
x++; //

x увеличивается на 1 после использования
x--; // x уменьшается на 1 после использования
++x; // x увеличивается на 1 перед использованием
--x; // x уменьшается на 1 перед использованием
Инкрементирование и декрементированиеИнкремент – увеличение значения переменной на 1Декремент – уменьшение значения переменной на 1Различают пред-инкремент и

Слайд 18Пост- и пред- инкремент
void main(void) {
a = 10;

b = a++;
// Сначала в b будет

записано значение переменной а,
// а затем "а" будет увеличена на 1.
// В результате получится b = 10, a = 11
}

Постинкремент: сначала с переменной производятся все действия, описанные в выражении, после чего её значение увеличивается на 1.

void main(void) {
a = 10;
b = ++a;
// Сначала "а" будет увеличена на 1,
// а затем в "b" будет записано значение переменной "а".
// В результате получится b = 11, a = 11
}

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

Пост- и пред- инкрементvoid main(void) {  a = 10;  b = a++;  // Сначала

Слайд 19Операция деления
При делении одного целого числа на другое результат получается

ЦЕЛЫЙ, дробная часть отсекается:
void main(void) {
int a =

5;
int b = 2;
int c;

// Так как переменные "a", "b", "c" - целые (имеют тип int),
// результат их деления также будет целым, без учёта остатка
// Таким образом с = 2
c = a / b;
}
Операция деленияПри делении одного целого числа на другое результат получается ЦЕЛЫЙ, дробная часть отсекается:void main(void) {

Слайд 20Управление ходом вычислений
if else
switch
while
do while
for
break
continue

Управление ходом вычисленийif elseswitchwhiledo whileforbreakcontinue

Слайд 21if else
if (выражение)
инструкция1
else
инструкция2
Иногда надо выбрать тот или иной вариант действий

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

способом, а если неверно — другим. Для этого применяют условные операторы.
Один из них – «if … else» - имеет следующий вид:
if elseif (выражение)	инструкция1else	инструкция2Иногда надо выбрать тот или иной вариант действий в зависимости от некоторых условий: если условие

Слайд 22if else
void checkCommands(void) {
// Если пришла команда на

выключение,
// остановить привод, иначе пересчитать напряжение фаз

if (commandStop == 1) {
stopDrive();
} else {
updateVoltages();
}
}

Пример использования:

В общем случае часть «else {…}» может отсутствовать.

В теле оператора «if» или «else» может выполняться одно действие (как в примере выше), а может несколько. Если выполняется только одно действие, то фигурные скобки ставить не нужно, но всё равно рекомендуется.

if elsevoid checkCommands(void) {  // Если пришла команда на выключение,  // остановить привод, иначе пересчитать

Слайд 23else if
void main(void) {
// Если пришла команда на

включение, подготовить и
// запустить привод; иначе, если пришла

команда на
// остановку, остановить привод. Иначе повторно
// запросить команды
if (commandStart == 1) {
clearAllFaults();
startDrive();
updateVoltages();
} else if (commandStop == 1) {
stopDrive();
setZeroVoltage();
} else {
requestCommands();
}
}

Также можно проверять несколько разных условий при помощи дополнительных блоков «else if (…)». Можно использовать сколько угодно таких проверок. В это случае будут выполнены только те действия, которые находятся внутри блока, условие которого истинно. Если истинны условия сразу в нескольких блоках, то будет выполнен только первый.

else ifvoid main(void) {  // Если пришла команда на включение, подготовить и  // запустить привод;

Слайд 24Критерий истинности условия оператора «if»
Истинным считается любое значение, не равное нулю:

if (4)
doCase1();
else if (100 / 8)

doCase2();

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

// Если скорость двигателя меньше 100 об/мин,
// увеличить скорость; а если больше - уменьшить
if (driveSpeed < 100)
driveSpeed += 5;
else if (driveSpeed > 100)
driveSpeed -= 5;

Критерий истинности условия оператора «if»Истинным считается любое значение, не равное нулю: if (4)   doCase1(); else

Слайд 25Операции сравнения

Операции сравнения

Слайд 26Логические операции

Логические операции

Слайд 27Поразрядные логические операции

Поразрядные логические операции

Слайд 28Поразрядные логические операции
Следует различать операции «&&» и «&».
Логическое И («&&»)

даёт результат «истина», когда оба операнда не равны 0, иначе

даёт результат «ложь». Результат такой операции, как правило, используется только для условного ветвления программы («если (А && В), то выполнить действие_1»).

Побитовое И («&») даёт результат операции «И» между соответствующими битами переменных. Используется для различных целей, например для наложения маски:

Поразрядные логические операцииСледует различать операции «&&» и «&».Логическое И («&&») даёт результат «истина», когда оба операнда не

Слайд 29Пример использования логических операторов
Операторы «логическое И» и «логическое ИЛИ» используются,

когда какое-то действие нужно выполнить в том случае, когда выполняется

сразу несколько условий (в случае оператора И) или, наоборот, хотя бы одно из условий (в случае оператора ИЛИ):

// Если нет аварий, задание скорости не нулевое
// и есть команда запуска - запустить привод
// А если есть авария или команда "стоп" - остановить
if (faultCounter == 0 && speedReference != 0 && commandStart == 1) {
startDrive();
} else if (faultCounter != 0 || commandStop == 1) {
stopDrive();
}

Пример использования логических операторовОператоры «логическое И» и «логическое ИЛИ» используются, когда какое-то действие нужно выполнить в том

Слайд 30Приоритеты логических операций
Приоритет у «&&» выше чем у «||» и

обе они младше операций отношения и равенства.
Выражение вычисляется в порядке

приоритетов. Если результат выражения оказывается известен до окончания вычислений, то вычисления прекращаются. Это следует учитывать при программировании.

Операции отношения имеют приоритет перед операциями проверки равенства.
Арифметические операции старше операций отношения:
a < b – 1 эквивалентно a < (b – 1)

Приоритеты логических операцийПриоритет у «&&» выше чем у «||» и обе они младше операций отношения и равенства.Выражение

Слайд 31Цикл while
Цикл «while» имеет следующий формат вызова:
while (выражение){
инструкции
}
Вычисляется выражение. Если

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

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

// Вычитывание данных из массива до тех пор,
// пока он не опустеет
while (bufferEmptyFlag == 0) {
readDataFromBuffer();
bufferEmptyFlag = checkBuffer();
}

Цикл whileЦикл «while» имеет следующий формат вызова:while (выражение){	инструкции}Вычисляется выражение. Если его значение равно «истине», то выполняется инструкция.

Слайд 32Цикл do while
Цикл «do while» имеет следующий формат вызова:
do
инструкция
while (выражение)
Выполняется

инструкция. Вычисляется выражение. Если его значение равно «истине», то цикл

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

// Пока не будет заполнен весь массив,
// заполнять его случайными числами
do {
// Записать в элемент с номером "counter" случайное число
buffer[counter] = randomNumber();
// Увеличить счётчик элементов
counter++;
} while (counter < bufferSize);

Цикл do whileЦикл «do while» имеет следующий формат вызова:do	инструкцияwhile (выражение)Выполняется инструкция. Вычисляется выражение. Если его значение равно

Слайд 33Цикл for
Цикл «for» имеет следующий формат вызова:
for (выражение1; выражение2; выражение3){
инструкция1;
}

Выражение1

выполняется только один раз, затем проверяется выражение2, и если оно

истинно, выполняются инструкции. После выполнения всех инструкций выполняется выражение3. Затем проверяется выражение2, и если оно истинно выполняются инструкции и так далее.
Что эквивалентно конструкции:
выражение1;
while (выражение2) {
инструкция1;
выражение3; }
Цикл forЦикл «for» имеет следующий формат вызова:for (выражение1; выражение2; выражение3){	инструкция1;}Выражение1 выполняется только один раз, затем проверяется выражение2,

Слайд 34Цикл for
Цикл «for» обычно используется в тех случаях, когда заранее

известно количество итераций. Наиболее часто для работы с массивами.
В данном

примере объявляется вспомогательная переменная «i», которая используется для перебора элементов массива. В цикле «for» эта переменная вначале обнуляется (выражение1: I = 0), затем проверяется условие, что конец массива не достигнут (выражение2: i < bufferSize), затем обнуляется элемент массива с номером i. После этого переменная i инкрементиуется (выражение3: i++). Таким образом все элементы массива от 0 до 99 будут обнулены.

// Очистить массив элементов
int i;
for (i = 0; i < bufferSize; i++) {
buffer[i] = 0;
}

Цикл forЦикл «for» обычно используется в тех случаях, когда заранее известно количество итераций. Наиболее часто для работы

Слайд 35switch и break
Оператор «switch» позволяет выполнять те или иные действия,

в зависимости от значения переменной. Синтаксис оператора:

switch (переменная){
case значение1:
инструкция1;
break;

case значение2:
инструкция2;
break;

case

значение3:
инструкция3;
break;

}
switch и breakОператор «switch» позволяет выполнять те или иные действия, в зависимости от значения переменной. Синтаксис оператора:switch

Слайд 36Оператор switch и break
// В зависимости от значения переменной

driveMode,
// запустить тот или иной алгоритм управления

switch (driveMode){
case 0:
scalarMode();
break;

case 1:
vectorMode();
break;

case 2:
slowDown();
break;

default:
doNoting();
break;
}
Оператор switch и break 	// В зависимости от значения переменной driveMode,  // запустить тот или иной

Слайд 37Наша первая программа на Си
/* Наша первая программа на языке

СИ */
/* Определение и инициализация глобальных переменных */
int x =

2;
int y = 7;
/* Основная программа */
void main (void){
// Определение локальной переменной
long z;
// Секция выполнения – бесконечный цикл
for( ; ; ){
z = x;
z = z + y; //Вычисление переменной z
}
}
Наша первая программа на Си/* Наша первая программа на языке СИ *//* Определение и инициализация глобальных переменных

Слайд 38Функции
Служат для описания законченного алгоритма. С помощью функций задача разбивается

на подзадачи, с целью упрощения отладки и возможности распараллелить процесс

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

Слайд 39Формат функции
тип_результата имя_функции (список_аргументов) {
тело функции
}
// Функция возведения переменной в

степень
// Аргументы: x - число, которое нужно возвести
//

pow - требуемая степень
long power (int x, unsigned int pow) {
unsigned int i; // Вспомогательная переменная
long result = x; // Результат

// Умножаем результат на х столько раз,
// сколько требуется
for (i = 1; i < pow; i++){
result = result * x;
}

// Возвращаем результат
return result;
}
Формат функциитип_результата имя_функции (список_аргументов) {	тело функции}// Функция возведения переменной в степень// Аргументы: x - число, которое нужно

Слайд 40Вызов функции
void main(void) {
int varA = 20;

unsigned int powerA = 8;
long res;

//

Записать в res число, которое вернёт вернёт функция power
// В результате получится res = varA в степени powerA
res = power(varA, powerA);
}

Чтобы вызвать функцию, необходимо написать её имя и передать соответствующие аргументы. В примере выше функция называлась «power» и принимала два аргумента. Типы переменных которые передаются в функцию должны соответствовать типам аргументов. Это также касается и результата.

Вызов функцииvoid main(void) {  int varA = 20;  unsigned int powerA = 8;  long

Слайд 41Аргументы функции
Переменные, которые передаются в функцию в качестве аргументов не

меняются внутри функции, как бы она не была устроена внутри.

Когда в функцию передаются переменные, то внутри неё временно создаются их копии, и вся работа внутри функции происходит с копиями.

// Функция умножения числа на 100
int multiple100 (int x) {
// Чтобы не занимать стек локальными переменными,
// не будем создавать переменную "result", а изменим
// само значение аргумента:
x = x * 100;
return x;
}

void main(void) {
int varA = 20;
int varB;

// Умножим переменную varA на 100 и запишем результат в varB
varB = multiple100(varA);
// В результате этой операции varB = 20000,
// А значение varA не изменится: varA = 20
}

Аргументы функцииПеременные, которые передаются в функцию в качестве аргументов не меняются внутри функции, как бы она не

Слайд 42Указатели
Си поддерживает низкоуровневое неконтролируемое обращение к памяти с помощью указателей.
int

*px;
Данная строка кода создаст переменную типа указатель.
То есть px хранит

в себе адрес, а *px – содержимое, размещенное по этому адресу.

// Объявить переменную varA и указатель, который пока что
// не указывает ни на какую переменную
int varA = 1;
int *pointerVarA;

void main(void) {
// Записать в указатель "pointerVarA" адрес переменно varA
// Символ "&" означает "получить адрес"
pointerVarA = &varA;

}

УказателиСи поддерживает низкоуровневое неконтролируемое обращение к памяти с помощью указателей.int *px;Данная строка кода создаст переменную типа указатель.То

Слайд 43Операции с указателями
Основные операции над указателями: увеличение (инкремент), уменьшение (декремент)

и присваивание. Увеличить или уменьшить указатель можно не только операциями

инкремента/декремента, но и сложением с константой.
Важно понимать, что инкрементирование/декрементирование указателя меняет его значение так, чтобы он указывал на следующий/предыдущий элемент. Таким образом, если указатель px указывает на переменную x типа int, которая занимает 1 ячейку памяти, то операция «px++» увеличит значение указателя на 1. А если бы переменная x имела тип long, который требует 2 ячейки памяти, то та же операция увеличила бы указатель px на 2.
Поэтому тип указателя должен совпадать с типом переменной, на которую он указывает. То есть указатель, который объявлен как «int *p», не может указывать на переменную, которая объявлена как «long x».

Операции с указателямиОсновные операции над указателями: увеличение (инкремент), уменьшение (декремент) и присваивание. Увеличить или уменьшить указатель можно

Слайд 44Указатели
Также через указатель можно получить значение, которое хранится в той

ячейке, на которую он указывает, при помощи оператора « *

»:

// Объявить переменные varA, varB и указатель, который пока что
// не указывает ни на какую переменную
int varA = 1, varB;
int *pointer;

void main(void) {
// Записать в указатель "pointerVarA" адрес переменно varA
// Символ "&" означает "получить адрес"
pointer = &varA;

// Теперь запишем в переменную varB значение, которое хранится
// в ячейке памяти, на которую указывает pointer:
varB = *pointer;

// Так как pointer указывал на переменную varA, то теперь
// varB имеет такое же значение: varB = 1
}

УказателиТакже через указатель можно получить значение, которое хранится в той ячейке, на которую он указывает, при помощи

Слайд 45Указатели - пример
Выполнение программы из предыдущего слайда.
Шаг первый: переменная «varA»

находится по адресу 0x8807,
Указатель «pointer» пока что указывает на какую-то

случайную ячейку
Указатели - примерВыполнение программы из предыдущего слайда.Шаг первый: переменная «varA» находится по адресу 0x8807,Указатель «pointer» пока что

Слайд 46Указатели - пример
Шаг второй: в указатель «pointer» записан адрес переменной

«varA»

Указатели - примерШаг второй: в указатель «pointer» записан адрес переменной «varA»

Слайд 47Указатели - пример
Шаг третий: в переменную «varB» записано значение ячейки

памяти, на которую указывал «pointer». Т.к. он указывал на переменную

«varA», то значение «varB» теперь равно значению «varA».
Указатели - примерШаг третий: в переменную «varB» записано значение ячейки памяти, на которую указывал «pointer». Т.к. он

Слайд 48Указатели и массивы
int inputBuf[100];
int *input;
int x, y;

void main(){
//

Имя массива является указателем на его первый элемент
//

Следующие две строки идентичны
input = inputBuf;
input = &inputBuf[0];

// В переменную x запишется первый элемент массива,
// а в y - второй
x = *input;
y = *(input + 1);

x = inputBuf[0];
y = inputBuf[1];
}

Указатели часто используются для перебора массивов

Указатели и массивыint inputBuf[100];int *input;int x, y;void main(){  // Имя массива является указателем на его первый

Слайд 49Указатели и структуры
struct my_time systime; // Структура
struct my_time *t;

// Указатель на структуру
void main (void) {
//

Доступ к элементу структуры через имя экземпляра
systime.hours = 1;

// Доступ к элементу структуры через указатель
t->hours = 1;
}

Также может быть создан указатель на структуру. В таком случае указатель хранит адрес первого элемента структуры. При обращении к элементам структуры через указатель, используется оператор « -> » вместо « . »

Указатели и структурыstruct my_time systime; // Структураstruct my_time *t;   // Указатель на структуруvoid main (void)

Слайд 50Указатели и функции
Иногда удобно передавать данные в функцию через указатели.

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

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

// Функция чтения значения из регистра
int readValue (long* x){
// Если буфер пуст, вернуть -1
// Иначе записать значение и вернуть 0
if (bufferEmpty == 1) {
return -1;
} else {
*x = ethernetData;
return 0;
}
}

Указатели и функцииИногда удобно передавать данные в функцию через указатели. Это удобно, когда необходимо возвращать значения сразу

Слайд 51Указатели и функции
void main (void) {
int result, data;


// Вызываем функция, передавая в неё адрес

переменной
// Если функция была выполнена успешно - обрабатываем данные
result = readValue(&data);
if (result == 0) {
processData();
}
}

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

Указатели и функцииvoid main (void) {  int result, data;    // Вызываем функция, передавая

Слайд 52Определение функций в отдельном файле
При создании проекта разными программистами или

фирмами удобно создавать различные функции в разных исходных файлах. Такой

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

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

Определение функций в отдельном файлеПри создании проекта разными программистами или фирмами удобно создавать различные функции в разных

Слайд 53Квалификатор extern
Используется при модульном программировании
extern volatile int x;
………………………………………………………
volatile int x=7;

Этот квалификатор

означает, что в одном из файлов определена переменная « x

».
Если при объявлении выделяется память под переменную, то процесс называется определением. Использование extern приводит к объявлению, но не к определению. Оно просто говорит компилятору, что определение происходит где-то в другом месте программы.
При описании переменной с этим квалификатором, под неё не выделяется места в памяти, но компилятор знает тип этой переменной и как правильно совершать с ней различные действия.
Квалификатор externИспользуется при модульном программированииextern volatile int	x;………………………………………………………volatile int 		x=7;	Этот квалификатор означает, что в одном из файлов определена

Слайд 54Заголовочные файлы .h
Файл.h должен содержать полную информацию по используемому модулю.
Хотя

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

программист, пользующийся модулем знал названия включенных в него функций и формат вызова каждой из них.
Будучи включенным в программу, заголовочный файл должен описывать все, что может потребоваться программисту для обращения к функциям модуля и переменным как ко внутренним функциям проекта.
Заголовочные файлы .hФайл.h должен содержать полную информацию по используемому модулю.Хотя это и необязательно, но принято описывать прототипы

Слайд 55Подключаемые файлы
Директива #include включает указанный файл в текущую позицию компилируемого

файла. Имя файла заключается либо в «” ”», либо в

«< >».
Имя файла может содержать полный или частичный путь к файлу или не содержать пути.
#include
#include “main.h”
#include “C:\...\main.h”

Фактически эта директива заменяется на содержимое включаемого файла.
Подключаемые файлыДиректива #include включает указанный файл в текущую позицию компилируемого файла. Имя файла заключается либо в «”

Слайд 56Пример создания внешнего модуля
Модуль должен производить суммирование двух целых чисел.

Входными и выходными переменными являются переменные модуля.
// Файл “functions.c”
// Функция

умножения
// В качестве аргументов принимает два числа типа int
// возвращает число типа long
long mult (int a, int b) {
return a * b;
}

// Функция деления
// В качестве аргументов принимает два числа типа int
// возвращает число типа long
long div (int a, int b) {
return a / b;
}
Пример создания внешнего модуляМодуль должен производить суммирование двух целых чисел. Входными и выходными переменными являются переменные модуля.//

Слайд 57Пример создания внешнего модуля
Заголовочный файл:
/* Заголовочный файл, в котором
*

описан интерфейс функций
*/

#ifndef FUNCTIONS_H_
#define FUNCTIONS_H_

long mult (int, int);
long div

(int, int);

#endif /* FUNCTIONS_H_ */
Пример создания внешнего модуляЗаголовочный файл:/* Заголовочный файл, в котором * описан интерфейс функций */#ifndef FUNCTIONS_H_#define FUNCTIONS_H_long mult

Слайд 58Пример создания внешнего модуля
Основная программа, использующая модуль:
/* Пример модульного подхода

к программированию */
/* Подключение заголовочного файла, в котором описаны интерфейсы

функций */
#include "functions.h"

/* Объявление переменных */
int x = 1; // Переменная типа "int"
int y; // Переменная типа "int"
long z; // Переменная типа "long"

/* Главная функция. Содержит вызов двух других функций,
* описанных в заголовочном файле "headers.h" */
void main(void) {
long q;
y = 3;
z = mult(x, y);
q = div(x, y);
}
Пример создания внешнего модуляОсновная программа, использующая модуль:/* Пример модульного подхода к программированию *//* Подключение заголовочного файла, в

Слайд 59Пример создания внешнего модуля

Пример создания внешнего модуля

Слайд 60Пример создания внешнего модуля

Пример создания внешнего модуля

Слайд 61Пример создания внешнего модуля

Пример создания внешнего модуля

Слайд 62Пример создания внешнего модуля

Пример создания внешнего модуля

Слайд 63Пример создания внешнего модуля

Пример создания внешнего модуля

Слайд 64Пример создания внешнего модуля

Пример создания внешнего модуля

Слайд 65Пример заголовочного файла
/* ========================================================================
File name: CLARKE.H (IQ

version)
Originator:

Digital Control Systems Group
Texas Instruments
Description:
Header file containing constants, data type definitions, and function prototypes for the CLARKE
History:
05-15-2002 Release Rev 1.0 */
typedef struct { _iq as; /* Input: phase-a stator variable */
_iq bs; /* Input: phase-b stator variable */
_iq ds; /* Output: stationary d-axis stator variable */
_iq qs; /* Output: stationary q-axis stator variable */
void (*calc)(); /* Pointer to calculation function */
} CLARKE;
typedef CLARKE *CLARKE_handle;
/*------------ Default initalizer for the CLARKE object. ---*/
#define CLARKE_DEFAULTS { 0, 0, 0, 0, (void (*)(long))clarke_calc }
/*----------- Prototypes for the functions in CLARKE.C ---*/
void clarke_calc(CLARKE_handle);
Пример заголовочного файла/* ========================================================================File name:    CLARKE.H (IQ version)

Слайд 66Включаемые функции
Когда вызывается включаемая функция, то ее код вставляется непосредственно

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

времени на операциях вызова и возврата из функции
Включенная функция может оптимизироваться компилятором совместно с окружающим ее кодом

Типы включаемых функций:
Встроенные операторы (встроенные операторы всегда включаемые (+, -, *, /...))
Автоматическое включение
Управляемое включение при определении функции

Включаемые функцииКогда вызывается включаемая функция, то ее код вставляется непосредственно в то место программы, где она вызывается.Преимущества

Слайд 67Включаемые функции
Включение функций осуществляется ключевым словом inline

Включаемые функцииВключение функций осуществляется ключевым словом inline

Слайд 68Директива #define
Директива #define является одной из директив препроцессора. Она позволяет

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

файла выполняется препроцессинг, который обрабатывает директивы (#define, #include, #pragma и другие). Во время препроцессинга все встречающиеся макроопределения в коде будут заменены на соответствующие значения.

// Определить число Пи
#define PI 3.1416

void main (void) {
// Задать радиус и рассчитать площадь круга
float radius = 4;
float square = radius * radius * PI;
}

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

Слайд 69Директива #define
Важно понимать, что макроопределение это не переменная. С помощью

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

Потому что фактически препроцессор просто заменит имена макроопределений на их значения. В примере ниже слово «condition» будет заменено на набор условий:

// Макроопределение сложного условия
#define condition (forceRun == 1) || \
((startCommand > 0) && (enableRun == 1) \
&& (faults == 0))

void main (void) {
// Проверить условие запуска двигателя:
// либо есть команда принудительного запуска (forceRun),
// Либо есть команда обычного запуска, при этом нет аварий
// и работа разрешена
if (condition)
startDrive();
}

Директива #defineВажно понимать, что макроопределение это не переменная. С помощью этой директивы можно определять не только константы,

Слайд 70Директива #define
#define condition (startCommand == 1)

void main (void) {

if (condition)
startDrive();
}
void main (void) {

if ((startCommand == 1))
startDrive();
}

Исходный файл:

После препроцессинга:

Директива #define#define condition (startCommand == 1)void main (void) {  if (condition)    startDrive();}void main

Слайд 71Методы условной компиляции. #ifdef и #ifndef
Метод условной компиляции состоит в использовании

директив #ifdef и #ifndef, что соответ­ственно означает «если определено» и

«если не определено». Стандартный вид #ifdef следующий:
#ifdef имя_макроса последовательность операторов #endif

Если имя макроса определено ранее в операторе #define, то последовательность операторов, сто­ящих между #ifdef и #endif, будет компилироваться.

Стандартный вид #ifndef следующий:
#ifndef имя_макроса последовательность операторов #endif

Если имя макроса не определено ранее в операторе #define, то последовательность операторов, стоящих между #ifndef и #endif, будет компилироваться.
Методы условной компиляции. #ifdef и #ifndefМетод условной компиляции состоит в использовании директив #ifdef и #ifndef, что соответ­ственно

Слайд 72Методы условной компиляции.

Методы условной компиляции.

Слайд 73Методы условной компиляции.

Методы условной компиляции.

Слайд 74Использование оптимизатора
Оптимизация «–o0». Оптимизация уровня регистров.
Располагает переменные непосредственно

в регистры
Осуществляет оптимизацию циклов программы
Удаляет неиспользованный программой код

Упрощает арифметические выражения и выходную отчетность
Вставляет в программу напрямую текст функций объявленных «inline»

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

Использование оптимизатора Оптимизация «–o0». Оптимизация уровня регистров. Располагает переменные непосредственно в регистры Осуществляет оптимизацию циклов программы Удаляет

Слайд 75Использование оптимизатора
Оптимизация «–o1». Выполняет те же действия, что при

оптимизации «-о0», и дополнительно:
осуществляет прямое присвоение локальных констант;
удаляет

неиспользованные присвоения переменных;
исключает локальные повторяющиеся выражения;

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

Использование оптимизатора Оптимизация «–o1». Выполняет те же действия, что при оптимизации «-о0», и дополнительно: осуществляет прямое присвоение

Слайд 76Использование оптимизатора
Оптимизация «–o2». Выполняет те же действия, что и

при оптимизации «-о1», и дополнительно:
осуществляет улучшенную оптимизацию циклов;
удаляет

глобальные повторяющиеся подвыражения;
исключает глобальные повторяющиеся присвоения;

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

Использование оптимизатора Оптимизация «–o2». Выполняет те же действия, что и при оптимизации «-о1», и дополнительно: осуществляет улучшенную

Слайд 77Использование оптимизатора
Оптимизация «–o3». Выполняет те же действия, что и

при оптимизации «-о2», и дополнительно:
удаляет все функции, которые не

вызываются;
упрощает функции результат вычислений (присваивается return) которой не используется программой;
напрямую без вызова вставляет небольшие функции указанные директивой «inline»
перегруппирует описание функции, так чтобы атрибуты, вызываемой функции,

Использование оптимизатора Оптимизация «–o3». Выполняет те же действия, что и при оптимизации «-о2», и дополнительно: удаляет все

Слайд 78Использование оптимизатора
Исходная программа:

Использование оптимизатораИсходная программа:

Слайд 79Использование оптимизатора
Без оптимизации

Использование оптимизатораБез оптимизации

Слайд 80Использование оптимизатора
Первый уровень оптимизации (-o0)

Использование оптимизатораПервый уровень оптимизации (-o0)

Слайд 81Использование оптимизатора
Второй уровень оптимизации (-о1)
Компилятору очевидно, что &x != 0x00FF

всегда

Использование оптимизатораВторой уровень оптимизации (-о1)Компилятору очевидно, что &x != 0x00FF всегда

Слайд 82Опасность оптимизации
Многие периферийные регистры изменяются не программным путём. Их состояние

зависит от состояния дискретных входов и выходов микроконтроллера. Компилятор видит,

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

Слайд 83Использование оптимизатора
Использование ключевого слова “volatile” защищает чтение и запись переменной

от оптимизации

Использование оптимизатораИспользование ключевого слова “volatile” защищает чтение и запись переменной от оптимизации

Слайд 84Использование оптимизатора
Второй уровень оптимизации с использованием “volatile”

Использование оптимизатораВторой уровень оптимизации с использованием “volatile”

Слайд 85Оптимизация переменных
Использование статических переменных.

Оптимизация переменныхИспользование статических переменных.

Слайд 86Оптимизация переменных
Использование статических переменных.

Оптимизация переменныхИспользование статических переменных.

Слайд 87Оптимизация переменных
Статические переменные:
не создаются при входе и не удаляются

при выходе из функции (экономия процессорного времени)
не расходуют пространство

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

Слайд 88Спасибо за внимание

Спасибо за внимание

Обратная связь

Если не удалось найти и скачать доклад-презентацию, Вы можете заказать его на нашем сайте. Мы постараемся найти нужный Вам материал и отправим по электронной почте. Не стесняйтесь обращаться к нам, если у вас возникли вопросы или пожелания:

Email: Нажмите что бы посмотреть 

Что такое TheSlide.ru?

Это сайт презентации, докладов, проектов в PowerPoint. Здесь удобно  хранить и делиться своими презентациями с другими пользователями.


Для правообладателей

Яндекс.Метрика