Июл 172019
 

Язык Си (без ++) — один из основных языков для программирования микроконтроллеров, поскольку здесь требуется высокая скорость, а оперативной памяти не бывает много.

Пример простой программы на Си для микроконтроллера AVR

Это текст программы компиляторов типа AvrStudio, CodeVisionAVR и т.п.

 #include // заголовочный файл для ввода-вывода #include #define BV(x) (1 << x) int main(void) { DDRC=0xFF; // порт PORTC настроен на выход PORTC=0xFF; // установка уровней на порте PORTC while(1) // начало цикла { PORTC=~(BV(5)); // переключение 5-го бита порта PORTC _delay_ms(1000); // задержка 1 секунда PORTC=BV(5); // переключение 5-го бита порта PORTC обратно _delay_ms(1000); } return 0; }  

Функция main — это точка входа в программу, с которой компьютер начинает выполнение программы.

Допускается из main возвращать void, хотя это не по стандарту, так что лучше int.

В функцию main можно передавать аргументы командной строки:

int main(int argc, char* argv[]) { }  

Вообще говоря, мы можем писать программу для MK AVR также на языке Processing/Wiring. Это тот же Си, но упрощенный. Но компилироваться это будет только в Arduino IDE или т.п., а потом можно загружать полученный hex в наш микроконтроллер. При этом не обязательно, чтобы МК стоял на плате Arduino. Разницы то нет.

Вот так выглядит аналогичная программа на Processing/Wiring:

int ledPin = 13; void setup() { pinMode(ledPin, OUTPUT); } void loop() { digitalWrite(ledPin, HIGH); delay(1000); digitalWrite(ledPin, LOW); delay(1000); }  

Здесь не надо подключать хеддеры для МК, т.к. они подключатся автоматом. Но для внешних модулей могут понадобится. Короче, про Ардуино подробнее читайте здесь

http://ar.com/basic/uno

а пока мы вернемся к языку Си.

Общая структура памяти программы на Си

— куча — для динамического выделения памяти

— стек — локальные переменные класса памяти auto (включая аргументы функций)

— DATA — константы

— CODE — исполняемый код, инструкции процессора

Типы данных в Си

-Базовые типы данных: char, int, float, double.

-Модификаторы знака: signed, unsigned.

-Модификаторы знака: long, short.

void — тип без значения

При этом следущие типы равны:

int = signed int = signed // 16 или 32 бит (зависит от платформы ) unsigned = unsigned int char = signed char	// 8 бит (от -128 до 127) (ASCII) unsigned char // 8 бит (от 0 до 255) wchar_t // UNICODE  

В Си логический тип реализован неявно (с помощью int): false = нуль, true = не нуль.

Введение псевдонимов для ранее описанных типов данных:

typedef тип имя

где тип — любой существующий тип данных, имя — новое имя для этого типа.

Пример: typedef unsigned char byte;

Преобразование типов:

Если операнды операции имеют разные типы, то происходит неявное приведение типов:

double a = 1.222; int i = a; // дробная часть отсекается! double x = 2/5;	// результат будет 0 !  

(чтобы здесь получить 0.4 нужно было бы написать x=2.0/5 или 2/5.0)

Явное приведение типов:

int a=2, b=5; double b = (double)a / b;	// результат будет b=0.4  

Принудительное преобразование типов:

(тип) выражение;

(желательно вообще избегать преобразования типов)

Переменные и константы

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

Декларация переменных (вместе с инициализацией):

[класс памяти] [квалификаторы] [модификаторы] тип идентификатор = инициатор;

Например,

static const unsigned char x = 100;  

Здесь «;» — составляющая часть конструкции, завершающая часть.

Допустима (хотя и редко используется) запись: const x = 100; (по умолчанию int).

Квалификаторы (или «модификаторы доступа»): const, volatile.

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

volatile — содержимое переменной может измениться само собой (используется в многопоточных программах при взаимодействии процессов)

Возможен вариант const volatile, когда писать могут только снаружи.

Спецификторы хранения (описатель класса памяти): auto, register, extern, static.

auto — локальные переменных (по умолчанию) — программный стек.

register — просьба компилятору положить переменную в регистр ЦПУ (но он эту просьбу редко выполняет);

extern — объявление (declaration) переменных, но не определение (definition) (определение где-то в другом месте); определение может идти ниже по файлу (но как глобальная) или в другом файле.

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

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

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

Описание области действия идентификаторов (имен):

— внутреннее (локальное) — внутри блока {…}

— внешнее (глобальное) — вне всех блоков

Идентификатор, описанный внутри блока, известен только в этом блоке (локальный идентификатор).

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

Стоит избегать использования глобальных имен.

Переменные с классом памяти static видны только в пределах текущего блока (для локальных) или в пределах файла (для объявленных глобально).

Статические переменные хранятся в сегменте данных (data) и по умолчанию инициализируются нулем. Т.е. память под static-переменные выделяется при старте программы и существует до конца программы.

Замечание: Инициализация выполняется одни раз при выделении памяти!

void f(void) { static int a = 1; /* - это инициализация (но не присваивание!), т. е. переменная инициализурется единицей только один раз при старте программы! (или 0 по умолчанию, т.е. если бы было просто static int a;) */ a++; }  

Статическими могут быть также функции. Такая ф-ция может исп-ся только внутри данного файла.

Следует различать присваивание и инициализацию:

— Присваивание: имя_переменной = выражение;

— Многочисленное присваивание: x = y = z = 0;

— Инициализация переменных: тип имя_переменной = константа;

Константы

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

Константы бывают:

— целые:

10-я система: 127; -127; +127;

8-я система: 0127; (начинается с нуля — значит 8-ричная!)

16-я система: 0x7F; (x или X, f или F — регистр не влияет)

— вещественные: 3.14; 2. ; .25 (0 можно опускать); 2E3; 2e3; 2E-3; 2.0E+3;

— символьные: 8-битные ASCII: ‘A’, ‘=’, ‘\n’, ‘\t’, ‘\370’, ‘\xF8’ (символ градуса);

— строковые литералы (в двойных кавычках): «Hello, world!\n». Строки заканчиваются нулевым байтом — ‘\0’.

Макроопределения:

 #define WIDTH 80	//(подробнее ниже)  

Операции и операторы

Оператор (инструкция, англ. statement) — это единица выполнения программы.

В языке Си любое выражение, заканчивающееся символом «точка с запятой» (;), является оператором.

Фигурные скобки { } — это составной оператор.

Например,

{y = x; x++;}  

Кроме того { } является отдельным блоком и в нем можно определять локальные переменные.

; — пустой оператор.

Операции:

— Арифметические операторы: — + * / %

— Инкрименты и декрименты: ++a, —a, a++, a— (могут выполняться быстрее)

— Операторы сравнения (отн-ний): > >= < <= == != (возвращают 1 или 0)

— Логические операторы: && || ! (возвращают 1 или 0)

— Битовые операторы: & | ^ ~ >> <<

— Оператор ?: x ? y : z, напр.: r = 10>9 ? 100 : 200

sizeof — унарный оператор для вычисления размера переменной или типа

, — оператор запятая (последовательное вычисление): a = (b=3, b+2);

Порядок выполнения операторов:

— Унарные операторы выполняются справа-налево.

— Бинарные выполняются слева-направо.

— Присваивание выполняется справа-налево.

Порядок можно менять с помощью скобок!

Примеры:

a=10; r=!!a==a; // результат будет 0, т. к. !!a вернет 1;  

Выражение а + b + c интерпретируется как (а + b) + с.

r = (2==2==2); // результат будет 0, т. к. 2==2 вернет 1 и сравнит с 2; - и вычисляется слева направо. 5 < 3 < 2;	// результат будет 1, т. к. 5<3 вернет 0; a = b = c = 2;	// сначала c=2, потом b=c, потом a = b;  

sizeof() — возвращает длину в байтах переменной или типа; sizeof(int); sizeof(a);

sizeof выражение; — само выражение не вычисляется.

Оператор запятая:

x = (y = 3, y+1);  

левая сторона оператора вычисляется как void и не выдаёт значения, переменной x присвается значение выражения в правой стороне, т.е. y+1.

Указатели и ссылки в Си

& — оператор «получение адреса» объекта;

* — доступ к значению объекта по указанному адресу;

p = &number; // - получение адреса переменной number; q = *p; // - получение значения переменной по указанному адресу: st.a; // - обращение к полю структуры st; pst->a;	// - обращение к полю структуры по её указателю;  

Указатели:

Указатель

Понравилось? Поделитесь:

:

Sorry, the comment form is closed at this time.