Полная версия этой страницы:
Программирование на C++
KingGeorg
Oct 23 2004, 12:51
По идее, IAR должны были бы вместе с EC++ и стантдартными библиотеками предоставлять и библиотеки классов, описывающие микроконтроллеры, хотя бы тот же MSP430. Но я у них ничего подобного не нашёл, нет даже упоминаний. Интересно, кто-нибудь вообще в этом деле использует С++ как С++. Если да, давайте одсудим детали. Я (скромный такой

) за месяц с небольшим знакомства с предметной областью кое-что своё накарябал. Готов предъявить и получить заимечания.
IgorKossak
Oct 23 2004, 13:20
У IAR вообще всё идёт довольно медленно, но не будем отзываться плохо об отсутствующих.
И тем не менее, даже у них был когда-то туториал (под AVR), где они реализовали таймер в виде базового класса. Странно, что начиная с версии 3.10, они подменили его совершенно никчемным примером с числами Фибоначчи.
Кстати, KingGeorg, давайте обсудим что у Вас есть.
KingGeorg
Oct 24 2004, 09:31
У меня есть классы:
clocking -- инкапсулирует систему тактирования
DAC12 -- инкапсулирует цифро-аналоговый преобразователь
flash -- инкапсулирует работу с флеш-памятью
abstract_port -- инкапсулирует работу с портами ввода-вывода
|
+-- read_only_port -- реализация порта только для ввода
|......|
|......+-- read_interrupt_port -- то же с обработкой прерываний
|
+---write_only_port -- реализация порта только для вывода
SVS -- инкапсулирует работу с супервайзером питания
timerA -- инкапсулирует таймер А
timerB -- инкапсулирует таймер В
WDT -- инкапсулирует сторожевой таймер
IgorKossak
Oct 24 2004, 10:29
Неплохо, а где можно на реализацию взглянуть?
KingGeorg
Oct 27 2004, 14:54
Ну, напимер, сторожевой таймер:
Код
#ifndef __WDT_HPP__
#define __WDT_HPP__
#include "430.hpp"
// здесь typedef bit unsigned char
// typedef byte unsigned char, и описания регистров связанных с прерываниями
// от разных устройств (комментарий для сообщения в форум)
struct WDTCTL{
bit WDTIS : 2; /* Выбор интервала сторожевого таймера. Эти биты определяют интервал времени
* сторожевого таймера, по истечении которого устанавливаетс
* флаг WDTIFG и/или генерируется сигнал PUC.
* 00 Частота источника тактирования сторожевого таймера / 32768
* 01 Частота источника тактирования сторожевого таймера / 8192
* 10 Частота источника тактирования сторожевого таймера / 512
* 11 Частота источника тактирования сторожевого таймера / 64 */
bit WDTSSEL : 1; /* Выбор источника тактирования сторожевого таймера
* 0 SMCLK 1 ACLK */
bit WDTCNTCL : 1; /* Очистка счетчика сторожевого таймера.
* Установкой WDTCNTCL=1 производится очистка счетчика до значения 0000h.
* Бит WDTCNTCL автоматически сбрасывается.
* 0 Действие не производитс
* 1 WDTCNT = 0000h */
bit WDTTMSEL : 1; /*a Выбор режима сторожевого таймера
* 0 Сторожевой режим
* 1 Режим интервального таймера */
bit WDTNMI : 1; /* Выбор NMI сторожевого таймера.
* Этот бит позволяет установить режим функционирования вывода nonRST/NMI.
* 0 Функция сброса
* 1 Функция NMI */
bit WDTNMIES : 1; /* Выбор фронта NMI сторожевого таймера. Этот бит позволяет выбрать
* фронт прерывания для NMI прерывания при WDTNMI=1.
* Изменение этого бита может вызвать NMI. Чтобы избежать
* случайного запуска NMI следует изменять этот бит при WDTNMI=0.
* 0 NMI прерывание происходит по переднему фронту
* 1 NMI прерывание происходит по спаду */
bit WDTHOLD : 1; /* Останов сторожевого таймера. Этот бит останавливает сторожевой таймер.
* Установка WDTHOLD=1, когда WDT не используется, позволяет снизить
* энергопотребление.
* 0 Сторожевой таймер не остановлен
* 1 Сторожевой таймер остановлен */
byte WDTPW; /* Пароль сторожевого таймера. Всегда читается как 069h.
* Должен записываться как 05Ah, в противном случае будет сгенерирован PUC. */
static const WDTCTL *address; // адрес настоящего регистра управления WDT
WDTCTL() // конструктор читает данные из настоящего регистра
{ *(int*)this = *(int*)address; }
void set(void) // установить данные в настоящий регистр
{
WDTPW = 0x5A;
*(int*)address = *(int*)this;
}
};
class WDT
{
public:
static void reset(void) // сброс и рестарт сторожевого таймера
{
WDTCTL wdtctl;
wdtctl.WDTCNTCL = 1;
wdtctl.set();
}
static void turnOff(void) // выключение сторожевого таймера
{
WDTCTL wdtctl;
wdtctl.WDTHOLD = 1;
wdtctl.set();
}
static void turnOn(void) // включение сторожевого таймера
{
WDTCTL wdtctl;
wdtctl.WDTHOLD = 0;
wdtctl.set();
}
enum dividers{ d32768, d8192, d512, d64}; // значения делителя частоты ст. тайм.
static void set_divider( const dividers divider) // установека значения делителя частоты
{
WDTCTL wdtctl;
wdtctl.WDTIS = divider;
wdtctl.set();
}
enum source{ SMCLK, ACLK}; // источники тактовой частоты ст. тайм.
static void set_source( source sour) // установека источника частоты
{
WDTCTL wdtctl;
wdtctl.WDTSSEL = sour;
wdtctl.set();
}
enum modes{ watchdog, interval}; // режимы сторожевой-интервальный
static void set_mode(modes mode)
{
WDTCTL wdtctl;
wdtctl.WDTTMSEL = mode;
wdtctl.set();
}
enum NMIs{ rst, NMI};
static void setNMI( NMIs nmi)
{
WDTCTL wdtctl;
wdtctl.WDTNMI = nmi;
wdtctl.set();
}
enum fronts{ front, back};
static void setNMI( fronts fr)
{
WDTCTL wdtctl;
wdtctl.WDTNMIES = fr;
wdtctl.set();
}
static int getWDTIFG(void)
{
return WDTIFG;
}
};
#endif
KingGeorg
Nov 1 2004, 16:11
А вот код инкапсулирующий ЦАП
Код
#ifndef __DAC12_HPP__
#define __DAC12_HPP__
#include "430.hpp"
struct DAC12_CTL
{
bit DAC12GRP : 1; /* Группировка ЦАП12. Группируется DAC12_x с DAC12_х, имеющий следующий
* более высокий порядковый номер. 0 Нет группировки 1 ЦАП`ы сгруппированы */
bit DAC12ENC : 1; /* Включение преобразования ЦАП12. Этот бит включает модуль ЦАП12,
* когда DAC12LSELx>0. Когда DAC12LSELx=0, бит DAC12ENC игнорируется.
* 0 ЦАП12 выключен 1 ЦАП12 включен */
bit DAC12IFG : 1; /* Флаг прерывания ЦАП12 0 Прерывание не ожидается 1 Прерывание ожидается */
bit DAC12IE : 1; /* Разрешение прерывания от ЦАП12 0 Запрещено 1 Разрешено */
bit DAC12DF : 1; /* Формат данных ЦАП12 0 Натуральный двоичный 1 Формат с дополнением до двух */
bit DAC12AMP : 3; /* Настройка усилителя ЦАП12. Эти биты выбирают время установки в зависимости
* от потребляемого тока для входного и выходного усилителей ЦАП12
* DAC12AMPx Входной буфер Выходной буфер
* 000 Выключен ЦАП12 выключен, выход в высокоимпедансном состоянии
* 001 Выключен ЦАП12 выключен, на выходе 0В
* 010 Низкая скорость/ток Низкая скорость/ток
* 011 Низкая скорость/ток Средняя скорость/ток
* 100 Низкая скорость/ток Высокая скорость/ток
* 101 Средняя скорость/ток Средняя скорость/ток
* 110 Средняя скорость/ток Высокая скорость/ток
* 111 Высокая скорость/ток Высокая скорость/ток */
bit DAC12IR : 1; /* Входной диапазон ЦАП12.
* Этот бит устанавливает диапазон входного опорного напряжения и выходного напряжения.
* 0 Полный диапазон выходного напряжения ЦАП12 равен 3-х кратному опорному напряжению
* 1 Полный диапазон выходного напряжения ЦАП12 равен 1-но кратному опорному напряжению */
bit DAC12CALON : 1; /* Включение калибровки ЦАП12. Этот бит инициирует последовательность калибровки
* смещения ЦАП12 и сбрасывается автоматически после завершения калибровки.
* 0 Калибровка не выполняетс
* 1 Инициирование калибровки / выполняется калибровка */
bit DAC12LSEL : 2; /* Выбор загрузки ЦАП12. Выбирается сигнал запуска загрузки защелки ЦАП12. Для обновлени
* ЦАП должен быть установлен DAC12ENC, за исключением случая, когда DAC12LSELx=0.
* 00 Загрузка в защелку ЦАП12 выполняется при записи в DAC12_xDAT
* (DAC12ENC игнорируется)
* 01 Загрузка в защелку ЦАП12 выполняется при записи в DAC12_xDAT, или,
* когда используется группировка, при записи во все регистры DAC12_xDAT группы
* 10 Фронт сигнала c Таймера_А3.Выход1 (TA1)
* 11 Фронт сигнала c Таймера_B7.Выход2 (TB2) */
bit DAC12RES : 1; /* Выбор разрешения ЦАП12 0 12-разрядное разрешение 1 8-разрядное разрешение */
bit DAC12SREF : 2; /* Выбор опорного напряжения ЦАП12 00 VREF+ 01 VREF+ 10 VeREF+ 11 VeREF+ */
bit reserved : 1; /* Не используется */
};
__no_init volatile DAC12_CTL dac12_ctl[2] @ 0x01C0;
struct DAC12_DAT
{
unsigned int data :12; /* данные ЦАП */
unsigned int reserved : 4; /* Не используется */
};
__no_init volatile DAC12_DAT dac12_dat[2] @ 0x01C8;
class DAC12
{
byte n;
public:
DAC12(byte no) : n(no-1)
{}
enum grouping{ nogrp, grp};
void group(grouping g){ dac12_ctl[n].DAC12GRP = grp; } // группировка ЦАПов
void on( void) { dac12_ctl[n].DAC12ENC = 1; } // включение и
void off(void) { dac12_ctl[n].DAC12ENC = 0; } // выключение ЦАП
bit is_interrupt(void){ return dac12_ctl[n].DAC12IFG; } // Флаг прерывания ЦАП12
void enable_interrupt(){ dac12_ctl[n].DAC12IE = 1; } // Разрешение прерывания от ЦАП12
void disable_interrupt(){dac12_ctl[n].DAC12IE = 0; } // Запрет прерывания от ЦАП12
enum formats{ natural, up_to_two};
void data_format( formats fmt){ dac12_ctl[n].DAC12DF = fmt; } // Формат данных ЦАП12
/* не стал делать enum */
void amp_cust( byte a){ dac12_ctl[n].DAC12AMP = a; } // Настройка усилителя ЦАП12.
enum ranges{ x3, x1};
void range( ranges r){ dac12_ctl[n].DAC12IR = r; } // Входной диапазон ЦАП12.
void calibration(void){ dac12_ctl[n].DAC12CALON = 1; } // Включение калибровки ЦАП12.
enum run_signals{ DAC12_DAT, DAC12_DAT_or_group, A3_1, B7_2};
void sel_loading(run_signals s) { dac12_ctl[n].DAC12LSEL = s; } // Выбор загрузки ЦАП12.
enum permissions{ x12, x8};
void permission( permissions p){ dac12_ctl[n].DAC12RES = p; } // Выбор разрешения ЦАП12
enum reference_voltages{ VREF, VREF_, VeREF, VeREF_ };
void ref_volt( reference_voltages v){ dac12_ctl[n].DAC12SREF = v; } // Выбор опорного напряжения ЦАП12
int operator =( int value)
{ return dac12_dat[n].data = value & 0x0FFF; }
};
#endif
Обеспокоен Вашим молчанием, господа

. Хотелось бы узнать мнение компетентных специалистов.
lamerok
Nov 2 2004, 04:50
Уважаемый KingGeorg
Я все таки так и не пойму, зачем все это делать через С++?????
Можете вы мне объяснить преимущества???? А то до сих пор пока не вижу никаких преимуществ. И никто толком объяснить не можем в чем фишка.
Все это можно, точно также лаконично, только без объявления класса описать на обысчном С и оформить в виде библиотеки....
KingGeorg
Nov 4 2004, 11:38
Фишка вот в чём. Всё это нет никакого резона писать не только на С++, но и на С. Можно на ассемблере. Если нужно только сбрасывать в цикле сторожевой таймер, или только передать одно значение на ЦАП.
А если программа достаточно сложная, должна принимать решения в зависимости не от 3 -10, а от 500 условий? Причём условий имеющих разную природу?
С++ нужен не для того, чтобы делать что-то через него. А для того, чтобы описать на нём предметную область, выделить в ней объекты-субъекты, определить что каждый из них может делать, и забыть об их внутреннем устройстве. Каждый из объектов теперь можно считать "чёрным ящиком", о котором мы знаем как он себя ведёт и собираем систему из них. Это и есть ООП.
Конечно, любую предметную область можно описать и на процедурно-ориентированном языке. При этом хочешь -- не хочешь существует суперобъект "ПРОГРАМА", который всё и делает. Но предметные области не состоят из единственного объекта. Не состоят они и из "действий", непонятно кем производимых, над "данными". Предметные области состоят из сущностей которые взаимодейтствуют, влияют друг на друга, изменяют состояния друг друга, и, тем самым изменяют состояние всей системы. Так что объектно-ориентированное описание получается более адекватным.
По поводу лаконичности:
Лаконичным должен быть исполняемый код. Как этого добиться -- проблемы компилятора. Справляется. А исходный код должен быть понятен хотябы автору, даже через неделю. Ну устроены мы так, что подавай нам избыточность.
-Tумблер-
Nov 5 2004, 13:32
Цитата(KingGeorg @ Nov 4 2004, 03:38 PM)
А если программа достаточно сложная, должна принимать решения в зависимости не от 3 -10, а от 500 условий? Причём условий имеющих разную природу?
Да - если это так, то на мой взгляд, выгоднее описывать
обьекты конечными автоматами. И программа будет структурно
выглядеть как совокупность автоматов (в том числе подчиненных).
А С++ тут ни при чем. По крайней мере - прямым образом.
Он дает выигрыш если нужно динамически создавать-уничтожать
большое кол-во однотипных обьектов. И вообше при создании
гигантских программных продуктов. (как MS Word)
O-кстати! Например, писать программаторы конечно лучше
всего на ++ - большое количество похожих но не точно
одинаковых обьектов.
IgorKossak
Nov 5 2004, 15:03
Уже давно пишу на С++ для МК.
Плевать на снижение на 0.5% производительности и увеличение на 1.5% объёма кода.
Проекты разные: от 0.5К до 1.5М.
Пишу на С++ не из религиозных соображений, а просто потому, что УДОБНО мыслить категориями объектов, а не алгоритмов.
Пытался даже пропагандировать среди коллег, но недолго, т. к. быстро осознал тщетность этих попыток. Со временем они и сами перешли на С++, когда увидели преимущества (но не раньше) даже те, кто был ярым сторонником ассемблера и АлгоритмБилдера (последний похоронили как страшный сон).
Мой совет начинающим - не слушайте ничьих советов, пробуйте ВСЁ (пока ещё не выработалась интуиция) и только на основании собственного опыта делайте выводы.
По крайней мере ответственность за принятые решения будет только Ваша.
lamerok
Nov 10 2004, 09:42
KingGeorg
Попробывал маленький проектик, аля мигание...Вобщем то работает-). Тока кода получилось многовато.
Ок. Понятно. Т.е я так понимаю, что резонно применять С++ только в здоровых приложениях, в которых есть нормальный запас по памяти.
Для мелких проектов С++ избыточен.
froakch
Nov 15 2004, 09:50
Цитата
У меня есть классы:
clocking -- инкапсулирует систему тактирования
DAC12 -- инкапсулирует цифро-аналоговый преобразователь
flash -- инкапсулирует работу с флеш-памятью
abstract_port -- инкапсулирует работу с портами ввода-вывода
|
+-- read_only_port -- реализация порта только для ввода
|......|
|......+-- read_interrupt_port -- то же с обработкой прерываний
|
+---write_only_port -- реализация порта только для вывода
SVS -- инкапсулирует работу с супервайзером питания
timerA -- инкапсулирует таймер А
timerB -- инкапсулирует таймер В
WDT -- инкапсулирует сторожевой таймер
KingGeorg, могу я получить flash, timerA, timerB и WDT. Если да, то заранее благодарю (froakch@mail.ru).
Slavik
Nov 29 2004, 10:11
C++ удобно применять где мегабайты текста.
А для встраиваемых приложений, где к примеру килобайт до пятисот памяти или процессор мегагерц до ста, смысла никакого. Для таких вещей оптимально C/Asm.
Инженеру должно быть удобно мыслить в любых здравых категориях, лишь бы задача была решена, и решена оптимально по возможности.
На мой субъективный взгляд использовать С++ темболее для микроконтроллеров неразумно.
Цитата(Slavik @ Nov 29 2004, 13:11)
На мой субъективный взгляд использовать С++ темболее для микроконтроллеров неразумно.
Почему бы и нет? Если Мы от использования Си++ только выигрываем?
Из плюсов - 3 кита ООП.
из минусов:
вижу только "разбухание" исходников, и то что надо немножко "повернуть" голову

Но наша цель не компактность исходников а скомпилированного кода.

P.S.
Код при компиляциии не увеличивается, если не использовать динамическое объявлении переменных.
Slavik
Nov 30 2004, 09:44
Опять же, на мой субъективный взгляд, для встраиваемых приложений больше подходит именно структурное программирование. А проблема с C++ не в разбухании исходников, а в разбухании скомпилированного кода даже без динамически выделяемой памяти, и, как следствие уменьшение производительности.
Но, придвидя, что разговор может скатится к теме типа "что лучьше, паскаль или си", умолкаю.
Сам я ярый приверженец Asm, С, С++.

:D
А наша цель - рабочее устройство, отвечающее заданными требованиям.
IgorKossak
Nov 30 2004, 09:49
Цитата(COMA @ Nov 30 2004, 09:10)
Код при компиляциии не увеличивается, если не использовать динамическое объявлении переменных.
Удивительно. У меня код те только не разбухает от этого, но иногда и уменьшается, в сравнении с теми случаями, когда я этого не применяю.
Цитата(IgorKossak @ Nov 30 2004, 12:49)
Удивительно. У меня код те только не разбухает от этого, но иногда и уменьшается, в сравнении с теми случаями, когда я этого не применяю.
Не совсем правильно сформулировал мысль.

В простом случае:
class Comlex
{...}
Complex a; - скомпилируется в меншьший код, чем
Comlex* a = new Complex();
Вот это хотел сказать

Вместо
Цитата
Код при компиляциии не увеличивается, если не использовать динамическое объявлении переменных.
IgorKossak
Dec 1 2004, 08:19
Цитата(COMA @ Nov 30 2004, 17:41)
Цитата(IgorKossak @ Nov 30 2004, 12:49)
Удивительно. У меня код те только не разбухает от этого, но иногда и уменьшается, в сравнении с теми случаями, когда я этого не применяю.
Не совсем правильно сформулировал мысль.

В простом случае:
class Comlex
{...}
Complex a; - скомпилируется в меншьший код, чем
Comlex* a = new Complex();
Вот это хотел сказать

Вместо
Цитата
Код при компиляциии не увеличивается, если не использовать динамическое объявлении переменных.
Что касается операций new и delete, то я их вообще переписал под себя слегка упростив. Правда пока обкатал только на платформе AVR применительно к OS scmRTOS.
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.