Язык Си (без ++) — один из основных языков для программирования микроконтроллеров, поскольку здесь требуется высокая скорость, а оперативной памяти не бывает много.
Пример простой программы на Си для микроконтроллера 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.