Инициализация PWM конструктором класса ( C++ )
Долгое время писал код под МК на С. Друг пишет под МК на С++, посоветовал мне, вот я решил попробовать. Микроконтроллер у меня ATmega88А, IDE Atmel Studio 6.2. Буду использовать 6 апаратных PWM для управления сервоприводами, так как нужно писать 6 одинаковых кусков кода, решил сделать класс Servo.
через конструктор передаем куда будем писать наше значение, тоесть регистр таймера для управления PWM
Используем:
Вроде код получился читабельней.
Но вот как быть с инициализацией. Писать просто функцию где будут инициализироваться 3 апаратных таймера как то не по ООП. Добавить в конструктор весь код, будет инициализировать всё 6 раз, тоесть делать то же самое. Как можно сделать чтобы получить независимую инициализацию для каждого обьекта (канала) PWM — если регистры конфигурации разные для каждого таймера?
class Servo
{
private:
int* pulse_width;
public:
Servo(int* PWM_Channel);
void write(int data);
}; //Servo
через конструктор передаем куда будем писать наше значение, тоесть регистр таймера для управления PWM
// default constructor
Servo::Servo(int* PWM_Channel)
{
pulse_width = PWM_Channel;
} //Servo
void Servo:: write(int data)
{
*pulse_width = data;
}
Используем:
int main(void)
{
Servo servo1(&OCR0A);
Servo servo2(&OCR0B);
servo1.write(10);
servo2.write(50);
while(1)
{
}
}
Вроде код получился читабельней.
Но вот как быть с инициализацией. Писать просто функцию где будут инициализироваться 3 апаратных таймера как то не по ООП. Добавить в конструктор весь код, будет инициализировать всё 6 раз, тоесть делать то же самое. Как можно сделать чтобы получить независимую инициализацию для каждого обьекта (канала) PWM — если регистры конфигурации разные для каждого таймера?
14 комментариев
А потом уже можно делать клас серво-машинки
Ну и все. Как использвоать, думаю, очевидно.
Более высокоуровневый подход — проценты заполнения ШИМ и из процентов пересчитывать в заполнение.
Использование С++ для 8-битных контроллеров не надо, нельзя, нихт, кайн и т.д. и т.п.
Был опыт правда на ATmega128 (камень более жирный и по ОЗУ и по ПЗУ). Пытался делать некий пользовательский интерфейс на ЖК, с возможностью переконфигурирования (т.е. имелось несколько классов: Window, Label и т.д.). Больше 2-х окон было создать невозможно: тупо заканчивалась память. Вообще у меня сложилось мнение, что для меги надо использовать только С.
C++ это не только абстракция и наследование, но еще и как минимум(краткий список полезных для эмбеддера фич):
0. Пространства имен! Нет, правда, создавая большой проект на С я затрахиваюсь перед каждой функцией добавлять префикс. В С++ это решается с помощью namespace и классов. И когда ты ставишь точку, автодополнение дает тебе список ТОЛЬКО того, что тебе можно.
1. Разделение уровней доступа — если у меня и есть какая-то внутренняя переменная, то в С иногда сложно избежать соблазна не поставить костыль. В C++ внутри класса я просто делаю ее private. Отсюда — приходится продумывать архитектуру приложения — на поддержке кода это очень пригодится.
2. Перегрузка функций и шаблоны. Правда, просто обожаю в С продумывать функции для всех возможных типов данных. И для uint8_t и для int8_t и 16_t и 32_t и float и т.д. и т.п.
3. Безопасное кастование! Сравните static_cast и reinterpret_cast(аналог С-шных скобочек (type_t)var). Мне иногда попадаются просто фееричные преобразования типов в разгребаемом коде, когда они неявно перегоняются из знакового в беззнаковый, из short в long да по нескольку раз… static_cast в отличие от reinterpret_cast в неподобающих случаях вызовет ошибку компиляции и заставит хорошенько подумать. Причем в С ошибка может быть обнаружена только с помощью статического анализа кода(PVS Studio справляется, Clang и cppcheck — как правило нет). Ну может быть он ругнется на сравнение знаковой и беззнаковой переменной в if или в переходе от double к float… или вы скормили const в не cosnt…
4.Перечисляемые типы. Дефайны — зло. Есть тысяча и один кейсов, когда они могут привести к некоторым проблемам, ОСОБЕННО!!! при отсутствии пространства имен. Вы уверены, что своим дефайном вы ничего не переопределили? Или, например, не включили какую-нибудь опцию вида #if defined(FOO)? В том или ином виде enum есть со времен ANSI C, Classname.enum таки гораздо функциональнее.
Список можно продолжать до бесконечности. Самое главное — все что я описал не требует мегабайтов памяти ни в коде ни в оперативке, реализуется абсолютно всеми компиляторами C++ (в отличие от специфичных функций современных стандартов) и реально экономит время и силы.
Стек и память вообще очень просто контролируются.
Для Embedded, ИМХО, достаточно не использовать дорогие операции. STL немножко можно, но аккуратно.
Если ARM и в запасе ОЗУ больше 64к — ни в чем себе не отказывайте :)
Сейчас пилю проект на 16МГц ARM Cortex-M3 и 8кбайт ОЗУ (из которых 6 съедают буферы для UART) — вот где боль :)
Для матерых С-шников вроде меня рекомендую книгу Ira Pohl C++ for C programmers. Все перечисленное выше — там подробно разбирается. Но конкретно перечисленное выше — то что реально облегчает мой эмбед-кодинг.
До сих пор эту книгу никак не закажу, но я проходил курс на Coursera в начале года.
А так, наиболее полный источник — это практика :) Практика разгребания чужого кода конечно же :)