Введение
Очень важный выбор — с какого же языка начинать изучение?.. Языков не так что бы много, но варианты существуют. Си, си++, паскаль, C# и т.д. И от этого зависят перспективы -- насколько полезным будет это знание.
Ассемблер
Самый низкоуровневый язык программирования для контроллеров. Для каждого типа контроллеров он свой. Практически, это программирование на уровне инструкций ядра. Писать на нём лучше не надо, он для специфических задач.
Но! Хотя бы для какой-то одной платформы очень желательно с ним поиграться и посоздавать программки, пусть и простые. Это позволит гораздо полнее понять устройство контроллера, и в случае чего понять, когда без него не обойтись.
Выглядит как-то так:
; Сохранение контекста
; Аргументы: нет
; Результаты: PSP, R4-R11 восстановлены
ContextLoad PROC
EXPORT ContextLoad
PUSH {LR}
; Получить номер текущего слота
BL _GetCurrentProcess
; Получить указатель на запись в таблице процессов
BL _GetProcess
; Сохранить её в R1
MOV R1, R0
; Прочитать указатель стека в R0
LDR R0, [R1, #SP_OFFSET]
; Записать указатель стека процесса
MSR PSP, R0
; Передвинем указатель на список регистров
ADDS R1, #4
; Прочитать старшие регистры из таблицы
LDR R4, [R1, #0]
LDR R5, [R1,#4]
LDR R6, [R1, #8]
LDR R7, [R1, #12]
LDR R3, [R1, #16]
MOV R8, R3
LDR R3, [R1, #20]
MOV R9, R3
LDR R3, [R1, #24]
MOV R10, R3
LDR R3, [R1,#28]
MOV R11, R3
POP {PC}
ENDP
В общем-то, ничего сложного, разве что непривычно.
С
Самый классический и широкоупотребимый вариант. Практически для любого контроллера найдётся компилятор Си. Возможно, он будет платный, возможно, бесплатный, но он почти наверняка будет. Не стоит рассчитывать на современные стандарты языка, в лучшем случае там будет C99. Хотя, для тех контроллеров, к которым код умеет компилировать GCC, со стандартами можно разгуляться, правда, потеряв при этом частично переносимость. Код же на классическом старом С, если он грамотно написан, будет хорошо переносим и прекрасно соберётся даже под другой платформой. Конечно, с частичной заменой платформозависимых частей.
Для Си существует кучи готовых библиотек под разные задачи, драйвера для разных устройств, алгоритмы и всякое такое. Вероятно, их придётся адаптировать, но переделывать по готовому иногда проще, чем писать совсем не зная что.
Пример на Си:
// Остановить таймер
void timer_Stop(TEventHandler Handler) {
if(TimerFrequency) {
int i;
for(i = 0; i < TIMER_HANDLERS; i++) {
if(Handlers[i].Handler == Handler) {
Handlers[i].Run = false;
Handlers[i].Fired = false;
break;
}
}
}
}
С++
Плюсы, тоже очень хороший вариант, но он уже распространён поменьше. Не для всех контроллеров могут найтись плюсовые компиляторы. А если и найдутся, то стандарт там будет древний, да ещё и, весьма вероятно, покоцанный. Производители контроллеров любят делать самопальные компиляторы, и с поддержкой стандартов там так себе дела обстоят.
Но опять же, если компилятор GCC, то можно чуть ли не 17 стандарт использовать со всеми лямбдами, умными указателями и прочим.
Но там где есть компилятор плюсовой, можно уверенно использовать классы, наследование... ООП во всей красе. В Ардуино, вроде бы, используются классические плюсы.
Удобно оборачивать разные элементы программы в классы, удобно повторно использовать код — в С++ это является естественным поведением, тогда как в Си для этого надо писать код соответствующим образом, закладывая в него эту возможность искусственно:
1. Пример функции включения светодиода на С с повторным использованием:
void led_On(uint32_t Index) {
// Таблица светодиодв где-то задана выше, индекс передаётся в функцию
if(Index < sizeof(Pins) / sizeof(Pins[0]) {
gp_Low(&Pins[Index]); // Если номер допустимый - включаем.
}
}
И использование:
led_On(0);
led_On(1);
Хотя, конечно, можно хранить и всё это в соответствующих структурах и передавать указатель на них, но для простых задач это очень излишне.
2. Пример аналогичной функции включения светодиода на С++:
TLed::On() {
gp_Low(&this->Pin);
}
Всё упростилось — проверки не нужны, аргументы с выбором тоже, ведь класс содержит уже всё, что надо. И использование:
TLed LedWork(PA, 1); // Создаём объект для управления светодиодом, отображающим статус "Работа", на ножке РА1
TLed LedError(PA, 2); // Создаём объект для управления светодиодом, отображающим статус "Ошибка", на ножке РА2
LedWork.On();
LedError.On();
Для плюсов библиотек тоже довольно много.
Пример кода:
// Задать уровень на ножке (режим вывода)
void Pin::Set(bool Value) {
if(Value)
Ports[Port]->BSRR = (1UL << (Index & 0x0F));
else
Ports[Port]->BSRR = (1UL << ((Index & 0x0F) + 16));
}
// Прочитать уровень на ножке (режим входа)
bool Pin::Get() {
return ((Ports[Port]->IDR & (1 << Index)) != 0);
}
Прочие языки
Все остальные языки существуют эпизодически, часто в каких-то обрезках, не реализуя все возможности языка. Для одного контроллера паскаль найдётся, а для другого уже нет. И перенести наработки так просто не выйдет. Хотя, если этот язык хорошо известен и цель всего лишь понять логику и получить опыт, то почему бы и нет.
Библиотеки готовые найти сложно. Либо подбирать железо под готовые библиотеки, либо писать самостоятельно. То есть все вайфаи, блютусы, gps, датчики, и т.д. придётся подключать совсем своими силами.
Но раз эти языки есть и используются, значит, это кому-то нужно и полезно.
Заключение
Выбор языка — личное дело каждого, и всё зависит от цели. Главное, понимать их ограничения и возможности.