Введение


В прошлой публикации STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1 я остановился на том, как уехал на озеро как были релизованы требования SR7, SR4 и SR6. Напомню, какие требования вообще есть для проекта:
SR0: Устройство должно измерять три параметра (иметь три переменных): Температуру микропроцессора, Напряжение VDDA, Напряжение с переменного резистора
SR1: Устройство должно выводить значение этих переменных на индикатор.
SR2: Единицы измерения для Температуры микропроцессора — градусы Цельсия, для остальных параметров — вольты.
SR3: При нажатии на кнопку 1, на индикаторе должен показываться экран со следующей измеряемой переменной,
SR4: При нажатии на кнопку 1 Светодиод 1 должен изменять свое состояние
SR5: При нажатии на кнопку 2, на индикаторе должен поменяться режим отображения переменных с постоянного показывания переменной на последовательное (менять экраны раз в 1.5 секунды) при следующем нажатии с последовательного на постоянное,
SR6: При нажатии на кнопку 2 светодиод 2 должен менять свое состояние.
SR7: Светодиод 3 должен моргать раз в 1 секунду.

Разработка: АЦП


Решив что я постиг все примудрости новых микроконтроллеров, я решил взять самое амбициозное требование SR0 — собственно это и есть основной функционал устройства — измерять 3 величины.
Для начала нужно было разобраться с АЦП. Решив взять этот блок с лету, особо не читая документацию на микроконтроллер, воооружившись специальным тулом Crt-C и Ctr-V, я нарисовал копию архитектур управления светодиодами и кнопок.

image

Но начав реализовывать сей чудный рисунок, который в общем-то очень даже рабочий, увлекся чтением документации и понял, что сделать можно вообще без активного объекта, используя канал DMA. Конечно такая архитектура уже будет процессоро-зависимой, по той простой причине, что не все микроконтроллеры имеют такой блок, но я подумал, что будет интересно и полезно показать, то, как можно немного все упростить, используя особенности микроконтроллера.
И переделал все вот так:

image

Все архитектура готова, и тут я завис. Оказалось что настроить АЦП немного сложнее чем порты, да и у меня упорно не измерялось напряжение с переменного резистора. Температура есть, Vdda есть, а с переменника никак. Настроить АЦП помог опять тот же ресурс, что помог мне сделать проект STM32L. ADC — Аналого-цифровой преобразователь и STM32L. Контроллер DMA. А разобраться с переменником демо-проект, скачанный с документацией для платы Olimex. Оказалось, что его просто надо было подключить отдельной ножкой PortD.Pin1 процессора. Как обычно, всю настройку железа я выкинул в __low_level_init()
Настройк АЦП и DMA
  //включаем потенциометр(Триммер) PORTD_PIN1 
   GPIOD->MODER |= GPIO_MODER_MODER1_0;
   GPIOD->PUPDR |= GPIO_PUPDR_PUPDR1_0;
   GPIOD->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR1;
   //настраиваем АЦП, 12 бит, канал 16- температурный сенсор, 17 - VDDA,
   // 22 - триммер в континиус режиме, 
   //регулярные каналы, скан каналов, ожидание следующего измерения, пока не скинут
   // EOC, установка EOC после после серии измерений, см http://chipspace.ru/stm32l-discovery-adc/
   // В итоге мы будем мерить последовательно каналы 16(температуру) и 17(vdda) и   
   // 22(триммер)первое преобразование будет температура, 2- vdda, 3- триммер
   ADC1->CR2 |= (ADC_CR2_DELS_2 | ADC_CR2_CONT);
   ADC1->CR1 |= ADC_CR1_SCAN;   
   //Порт GPIOE.7 как аналоговый вход - триммер 
   GPIOE->MODER |= GPIO_MODER_MODER7;
   //3 измерения   
   ADC1->SQR1 |= ADC_SQR1_L_1;
   //Выбираем ADC_IN 16 для 1 преобразования, стр 305
   //Выбираем ADC_IN 17 для 2 преобразования, стр 305
   //Выбираем ADC_IN 22 для 3 преобразования, стр 305
   ADC1->SQR5 |= ADC_SQR5_SQ1_4 | ADC_SQR5_SQ2_0 | ADC_SQR5_SQ2_4 | ADC_SQR5_SQ3_1 | ADC_SQR5_SQ3_2 | ADC_SQR5_SQ3_4;
   //Выбираем время преобразование для 16 и 17  и 22 канала стр 301 и 279
   ADC1->SMPR2 |= ADC_SMPR2_SMP16 | ADC_SMPR2_SMP17_2;
   ADC1->SMPR1 |= ADC_SMPR1_SMP22_2;
   // Включаем внутренние входы каналов измреения температурного сенсора и VDDA   
   ADC->CCR |= ADC_CCR_TSVREFE;  
   // DMA
   ADC1->CR2 |= (ADC_CR2_DMA | ADC_CR2_DDS);
   //Настройка DMA
   //Направление передачи данных - чтение из периферии, запись в память.
   DMA1_Channel1->CCR &= ~DMA_CCR1_DIR;  
   //Адрес периферии не инкрементируется после каждой пересылки. 
   DMA1_Channel1->CCR &= ~DMA_CCR1_PINC;
   //Адрес памяти инкрементируется после каждой пересылки. 
   DMA1_Channel1->CCR |= DMA_CCR1_MINC; 
   //Размерность данных периферии - 16 бит.
   DMA1_Channel1->CCR |= DMA_CCR1_PSIZE_0; 
   //Размерность данных памяти - 16 бит
   DMA1_Channel1->CCR |= DMA_CCR1_MSIZE_0;
   //Приоритет - очень высокий (Very High)
   DMA1_Channel1->CCR |= DMA_CCR1_PL; 
   DMA1_Channel1->CCR |= DMA_CCR1_CIRC;    


Сами файлы реализации классов:
adc.h
#include "types.h"            //Стандартные типы проекта
#define SENSORTEMPERATURE_CHANNEL       0
#define VDDA_CHANNEL                    1 
#define TRIMMER_CHANNEL                 2
class cAdc
{
  public:
    explicit  cAdc(const tU32 memoryBaseAddr, const tU8 measureCount);
    tBoolean switchOn(void);
    tBoolean startConversion(void);
    tBoolean isConversionReady(void);
    tF32 getValue(void) const;
  private:
    void initDma(const tU32 memoryBaseAddr, const tU8 measureCount);
};


adc.cpp
#include <stm32l1xx.h>      // Регистры STM2
#include "adc.h"                  // Описание класса
#include "susuassert.h"      //for ASSERT
#include "bitutil.h"               //для макросов работы с битами SETBIT, CLRBIT
#define ADC1_DR_ADDRESS    ((tU32)0x40012458)
/*******************************************************************************
* Function:  constructor
* Description: инициализиурет канал DMA адресом в RAM, куда складывать данные
*              измерений и количеством измерений
******************************************************************************/
cAdc::cAdc(const tU32 memoryBaseAddr, const tU8 measureCount)
{
  ASSERT(measureCount != 0); 
  this->initDma(memoryBaseAddr, measureCount);
}
/*******************************************************************************
* Function:  switchOn
* Description: Включает АЦП
******************************************************************************/
tBoolean cAdc::switchOn(void)
{
  tBoolean  result = FALSE;
  //Включаем АЦП, стр 299 CD00240194.pdf
  SETBIT(ADC1->CR2, ADC_CR2_ADON);
  result =  tBoolean(CHECK_BIT_SET(ADC1->SR, ADC_SR_ADONS));
  return result;     
}
/*******************************************************************************
* Function:  startConversion()
* Description: Запускает преобразование
******************************************************************************/
tBoolean cAdc::startConversion(void)
{
  tBoolean  result = FALSE;
  //Запускаем преобразование АЦП, стр 299 CD00240194.pdf
  SETBIT(ADC1->CR2, ADC_CR2_SWSTART);
  result = tBoolean(CHECK_BIT_SET(ADC1->SR, ADC_SR_STRT));
  return result;
}
/*******************************************************************************
* Function:  getValue()
* Description: читаем результат преобразования
******************************************************************************/
tF32 cAdc::getValue(void) const
{
  tF32  result = ADC1->DR;
  return result; 
}
/*******************************************************************************
* Function:  isConversionReady()
* Description: готово ли преобразование?
******************************************************************************/
tBoolean cAdc::isConversionReady(void)
{
  tBoolean result = tBoolean(CHECK_BIT_SET(ADC1->SR, ADC_SR_EOC));
  return result;
}
/*******************************************************************************
* Function:  initDma()
* Description: инициализирует канал DMA
******************************************************************************/
void cAdc::initDma(const tU32 memoryBaseAddr, const tU8 measureCount)
{
  //Задаем адрес периферии - регистр результата преобразования АЦП для регулярных каналов. 
  DMA1_Channel1->CPAR = ADC1_DR_ADDRESS;
  //Задаем адрес памяти - базовый адрес массива в RAM.
  DMA1_Channel1->CMAR = memoryBaseAddr;
  DMA1_Channel1->CNDTR = measureCount;
  //Включаем DMA
  SETBIT(DMA1_Channel1->CCR, DMA_CCR1_EN);  
}


adcdirector.h
#include "adc.h"              //для класса cAdc
#define MEASUR_NUMBER       (tU8) 3
class cAdcDirector 
{
  public:
    explicit  cAdcDirector(void);
    void startConversion(void);
    __IO uint16_t channelValue[MEASUR_NUMBER];   // для хранения преобразований
  private:
    cAdc *pAdc;    
};


adcdirector.cpp
#include "adcdirector.h"  //Описание класса 
/*******************************************************************************
* Function:  constructor
* Description: создает экземпляр АЦП, и передает ему адреса в RAM, куда АЦП с 
*              помощью DMA будет скалдывать результат преобразований. 
******************************************************************************/
cAdcDirector::cAdcDirector(void)
{
  this->pAdc = new cAdc((tU32)&channelValue[0], MEASUR_NUMBER);
  this->pAdc->switchOn();   
}
/*******************************************************************************
* Function:  startConversion
* Description: Запускаем АЦП на измерение, все данные сыплятся по DMA в массив
*              channelValue
******************************************************************************/
void cAdcDirector::startConversion(void)
{
  this->pAdc->startConversion();     
}


Проверить работу можно было только под отладчиком, потому что вывода на индиктор у меня пока нет. Но перед этим нужно добавить создание нового экзмпляра класса в main()
main()
void main( void )
{  
  //задача ButtonControllera должна оповещать другие задачи о нажатии
  //на кнопку, и передавать её значение. Для этого заводим массив указателей на 
  //задачи, которые надо оповещать
  static tTaskHandle tasksToNotifyFromButton[BUTTON_TASKS_NOTYFIED_NUM];
  cAdcDirector *pAdcDirector = new cAdcDirector();
  pAdcDirector->startConversion();
  cLedsDirector *pLedsDirector = new cLedsDirector();
  oRTOS.taskCreate(pLedsDirector, LEDSDIRECTOR_STACK_SIZE, LEDSDIRECTOR_PRIORITY, "Leds"); 
  tasksToNotifyFromButton[LEDS_TASK_HANDLE_INDEX] = pLedsDirector->taskHandle;
  cButtonsController *pButtonsController =  new cButtonsController(tasksToNotifyFromButton, BUTTON_TASKS_NOTYFIED_NUM);
  oRTOS.taskCreate(pButtonsController, BUTTONSCONTROLLER_STACK_SIZE, BUTTONSCONTROLLER_PRIORITY, "Buttons");   
  oRTOS.startScheduler();
}


Запустил на отладку и получил следующую картинку: Как раз видно, что все 3 значения в массиве channelValue[] поменялись и выделены красным. Проверять значения не стал, но на вскидку — что-то примерно похожее.

image

По обыкновению проект был сохранен тут: АЦП, кнопки и светодиоды в IAR 6.50

Разработка: Переменные


И так АЦП вроде бы работает, настало время превратить груду этих единиц и ноликов в что-то понятное людям, а имеено в температуру и напряжение:
Для начала я продумал единый интерфейс для всех переменных. Здесь всего один виртуальный метод — собтсвенно расчет и один метод получения рассчитанного значения.
image

А далее нарисовал как могла бы выглядеть температура:

image

А после этого подумалось, что хорошо бы еще иметь фильтр для сглаживания результатов измрений. А поскольку фильтр нужен всем переменным, то можно его сделать в виде интерфейса. В итоге был создан такой концепт:

image

Класс температуры рализует метод расчета интерфейса. Но тут следует сделать ремарку, для расчета температуры используются заводские коэффициенты зашитые в микроконтроллере, и по идее, чтобы можно было портировать данный код на другую платформу надо бы сделать класс для доступа к энергонезависимым параметрам и передавать ссылку на этот класс классам, которым эти коэффициенты нужны, в данном случае температуре. Но времени у меня в обрез и делать городульку из-за трех коэффциентов очень не хотелось, поэтому спишем такой косяк на нехватку времени и оставим галочку в уме, что тут портация не выйдет(ну и ладно :)). Рализация всего этого дела выглядит так:
ivariable.h
#include "types.h"            //Стандартные типы проекта
#include "adcdirector.h"      //для класса cAdcdirector
class iVariable 
{
  public:
    explicit  iVariable(const cAdcDirector *pAdcDirector);
    virtual tF32 calculate(void) = 0;
    tF32 getValue(void) const {return value;};  
  protected:
    const cAdcDirector *pAdcDirector;
    tF32 value;   
};


ivariable.cpp
#include "ivariable.h"      //Описание класса 
#include "susuassert.h"     // for ASSERT
/*******************************************************************************
* Function:  constructor
* Description: 
******************************************************************************/
iVariable::iVariable(const cAdcDirector *pAdcDirector)
{
  ASSERT(pAdcDirector != NULL);
  this->pAdcDirector = pAdcDirector;
  this->value = 0.0F;
}


ifilter.h
#include "types.h"            //Стандартные типы проекта
class iFilter 
{
  public:
    explicit iFilter(void);
    virtual tF32 filter(const tF32 previousValue, 
                        const tF32 currentValue, tF32 filterConst);
};


ifilter.cpp
#include "susuassert.h"       // for ASSERT
#include "types.h"            // для типов проекта
#include "ifilter.h"          // описание класса
/*******************************************************************************
* Function:  constructor
* Description: 
******************************************************************************/
iFilter::iFilter(void)  
{
}
/*******************************************************************************
* Function:  filter
* Description: Функция фильтрации
******************************************************************************/
tF32 iFilter::filter(const tF32 previousValue, const tF32 currentValue, tF32 filterConst)
{
  ASSERT(filterConst != 0);
  tF32 filteredValue = previousValue;
  filteredValue = filteredValue + (currentValue - filteredValue) / filterConst;
  return filteredValue;
}


temperature.h
#include "types.h"            //Стандартные типы проекта
#include "adcdirector.h"      //для класса cAdcdirector
#include "ifilter.h"          //для интрефейса iFilter
#include "iVariable.h"        //для интрефейса iVariable
class cTemperature : public iVariable, private iFilter 
{
  public:
    explicit cTemperature(cAdcDirector *pAdcDirector);  
    tF32 calculate(void);
};


temperature.cpp
#include "temperature.h"  //Описание класса 
//Разница 110С - 30С (температура в точках калибровки), см стр 289
#define DELTA_110_30  80.0F 
//процессор нагревается сам немного, поэтому коррекция на 28 градусов, а не на 30 :)
#define DEGREE_30     28.0F  
//Адрес коэффицента калибровки 2 стр 102 CD00277537.pdf
#define TS_CAL2_ADDR   0x1FF8007C  
//Адрес коэффицента калибровки 1 стр 102 CD00277537.pdf
#define TS_CAL1_ADDR   0x1FF8007A  
//Адрес кода VDDA при 3.0 В
#define VDDA_CAL_ADDR  0x1FF80076  
#define FILTER_CONST   20.0F 
/*******************************************************************************
* Function:  constructor
* Description: 
******************************************************************************/
cTemperature::cTemperature(cAdcDirector *pAdcDirector) : iVariable(pAdcDirector)  
{
}
/*******************************************************************************
* Function:  calculate
* Description: Расчет температуры
******************************************************************************/
tF32 cTemperature::calculate(void)
{
  tF32 temperature = 0.0F; //измеренная температура по одному отсчету АЦП 
  tF32 vdda = 0.0F;   //значение кода vdda
  //коэффициенты калибровки температурного сенсора, см стр 289 CD00240193.pdf и
  //стр 102 CD00277537.pdf
  tF32 tsCal2 = (tF32)(*((tU32 *)(TS_CAL2_ADDR)) >> 16); 
  tF32 tsCal1 = (tF32) (*((tU32 *)(TS_CAL1_ADDR )));
  tF32 vddaCal = (tF32)(*((tU32 *)(VDDA_CAL_ADDR)) >> 16);
  temperature = (tF32)this->pAdcDirector->channelValue[SENSORTEMPERATURE_CHANNEL];
  vdda = (tF32)this->pAdcDirector->channelValue[VDDA_CHANNEL];
  //поскольку все коэффициенты были получены на производсве при 3.0 В VDDA, 
  //нам необходимо сделать коррекцию на наше значение vdda, остальное
  //формула со см стр 289 CD00240193.pdf 
  temperature = DELTA_110_30 * ((temperature * vddaCal)/vdda -  tsCal1) / 
                                (tsCal2 - tsCal1) + DEGREE_30;
  this->value = this->filter(this->value, temperature, FILTER_CONST); 
  return  this->value;       
}


Теперь нужно сделать активный объект который будет переодически вызывать расчет температуры. Да ивообще в последствии будет контейнером для перменных, через него мы будем иметь доступ к переменным. Холст-кисть и вуаля:

image

Реализация проста до безобразия:
variablesdirector.h
#include "iActiveObject.h"    //Для интерфейса iActiveObject
#include "temperature.h"      //для класса cTemperature
class cVariablesDirector : public iActiveObject
{
  public:
    explicit cVariablesDirector(cAdcDirector* pAdcDirector);
    void run(void);
    cTemperature *pTemperature;    
};


variablesdirector.cpp
#include "variablesdirector.h"  // описание класса 
#include "frtoswrapper.h"       // для oRTOS
#include "susuassert.h"         // для ASSERT
#define VARIABLESDIRECTOR_DELAY (tU32)40/portTICK_PERIOD_MS
/*******************************************************************************
* Function:  constructor
* Description: включает АЦП
******************************************************************************/
cVariablesDirector::cVariablesDirector(cAdcDirector* pAdcDirector)
{
  ASSERT(pAdcDirector != NULL);
  this->pTemperature = new cTemperature(pAdcDirector);
}
/*******************************************************************************
* Function:  run
* Description: Задача  расчета температуры
******************************************************************************/
void cVariablesDirector::run(void)
{
  for(;;)
  {
    this->pTemperature->calculate();
    oRTOS.taskDelay(VARIABLESDIRECTOR_DELAY);
  }
}


Осталась самое малое — запустить и проверить. Перед этим конечно же надо создать еще одну задачу в main()
main()
#include <stm32l1xx.h>          // Регистры STM2
#include "ledsdirector.h"       // Для класса cLedsDirector
#include "buttonscontroller.h"  // Для класса cButtonsController
#include "types.h"              // Для типов проекта
#include "frtoswrapper.h"       // для cRtos
#include "variablesdirector.h"  // Для cVariablesDirector
#define LEDS_TASK_HANDLE_INDEX          0
#define BUTTON_TASKS_NOTYFIED_NUM       1
#define LEDSDIRECTOR_STACK_SIZE configMINIMAL_STACK_SIZE
#define LEDSDIRECTOR_PRIORITY (tU32)2
#define BUTTONSCONTROLLER_STACK_SIZE 256//configMINIMAL_STACK_SIZE
#define BUTTONSCONTROLLER_PRIORITY (tU32)3
#define VARIABLESDIRECTOR_STACK_SIZE (tU16) configMINIMAL_STACK_SIZE
#define VARIABLESDIRECTOR_PRIORITY (tU32)2
// Не охота было заморачиваться с синглтоном, сделал oRTOS глобальным объектом
// можно было конечно сделать сRTOS статическим, но че-то тоже заморочек много
// зато просто, все равно всем нужен :)
cRTOS oRTOS;
....
void main( void )
{  
  //задача ButtonControllera должна оповещать другие задачи о нажатии
  //на кнопку, и передавать её значение. Для этого заводим массив указателей на 
  //задачи, которые надо оповещать
  static tTaskHandle tasksToNotifyFromButton[BUTTON_TASKS_NOTYFIED_NUM];
  cAdcDirector *pAdcDirector = new cAdcDirector();
  pAdcDirector->startConversion();
  cVariablesDirector *pVariablesDirector = new cVariablesDirector(pAdcDirector); 
  oRTOS.taskCreate(pVariablesDirector, VARIABLESDIRECTOR_STACK_SIZE, VARIABLESDIRECTOR_PRIORITY, "Var");
  cLedsDirector *pLedsDirector = new cLedsDirector();
  oRTOS.taskCreate(pLedsDirector, LEDSDIRECTOR_STACK_SIZE, LEDSDIRECTOR_PRIORITY, "Leds"); 
  tasksToNotifyFromButton[LEDS_TASK_HANDLE_INDEX] = pLedsDirector->taskHandle;
  cButtonsController *pButtonsController =  new cButtonsController(tasksToNotifyFromButton, BUTTON_TASKS_NOTYFIED_NUM);
  oRTOS.taskCreate(pButtonsController, BUTTONSCONTROLLER_STACK_SIZE, BUTTONSCONTROLLER_PRIORITY, "Buttons");   
  oRTOS.startScheduler();
}


И опять проверка возможна только под отладчиком, поскольку выводить информацию пока что некуда. И вот загружаем ставим точку остановки на задаче переодического расчета температуры, жмем раз 40 F5(Run), чтобы фильтр устаканился и смотрим на значение температуры — выделно красным 23.68 С, ну по ощущениям так есть, ну возможно 23.62 С :)

image

Сохраняем проект, чтобы не забыть: Кнопки, Светодиоды, Температура на IAR 6.50
Тот же самый фокус повторяем с переменными Vdda и Trimmer (переменный резистор). Архитектура идентичная архитектуре класса cTemeperature.

image

А сам контейнер перменных и по совместительству активный объект — cVariableDirector стал выглядеть вот так:

image

Добавляем вызов расчета напряжений в cVariableDirector
VariableDirector.cpp
#include "variablesdirector.h"  // описание класса 
#include "frtoswrapper.h"       // для oRTOS
#include "susuassert.h"         // для ASSERT
#define VARIABLESDIRECTOR_DELAY (tU32)40/portTICK_PERIOD_MS
/*******************************************************************************
* Function:  constructor
* Description: включает АЦП
******************************************************************************/
cVariablesDirector::cVariablesDirector(cAdcDirector* pAdcDirector)
{
  ASSERT(pAdcDirector != NULL);
  this->pTemperature = new cTemperature(pAdcDirector);
  this->pVdda =  new cVdda(pAdcDirector);
  this->pTrimmer =  new cTrimmer(pAdcDirector);
}
/*******************************************************************************
* Function:  run
* Description: Задача  расчета температуры
******************************************************************************/
void cVariablesDirector::run(void)
{
  for(;;)
  {
    this->pTemperature->calculate();
    this->pVdda->calculate();
    this->pTrimmer->calculate();
    oRTOS.taskDelay(VARIABLESDIRECTOR_DELAY);
  }
}


Запускаем на отладку и получаем следующий результат(красные циферки атрибутов value): Как видно температура 23.5С, Vdda как и написано в документации 2.72, а напряжение на потенциометре 2.52 (но его можно менять, повервнут ролик резистора)

image

Ну вот и реализовано основное требование проекта, надо признаться провозился я с ним дольше запланированного — почти 7 дней. Но это больше из-за непоняток с напряжением переменного резистора, очень долго тупил, почему не измеряется ничего. Хорошо додумался подсмотреть у разработчиков платы Olimex :) Осталась одна небольшая задача — вывод на индикатор. Я подумал, что ничего сложного не будет, поскольку тот мой последний проект 8-летней давности как раз был на PIC16 со встроенным драйвером индикатора, и уж индикатор то мне дастся очень просто. А как это получилось, я расскажу в заключительной части.
Да забыл совсем — сохранил же проект тут:
Кнопки, Cветодиоды, и все переменные на IAR 6.50

Комментарии (12)


  1. monah_tuk
    06.07.2015 04:33

    void cVariablesDetector::run(void)
    


    Зачем второй void? В смысле, для чего он в Си — я знаю, но в C++ он для этого не нужен. Особенности IAR?


    1. lamerok Автор
      06.07.2015 11:09
      -2

      Да, вы правы, в С++ в нем смысла нет: (void) и () — это одно и то же. Но так написано в стандарте кодирования. А в стандарте написано, потому что видимо раньше писали на Си, и перетекло оттуда.
      А еще lint ругается — но сам же пишет, что в С++ это означает тоже самое что с void, что без void. В общем, чтобы удовлетворить lint и пройти без замечаний.
      1717 empty prototype for function declaration, assumed '(void)' — An empty prototype,
      as in:
      void f();
      has a different meaning in C than in C++. In C it says nothing about the arguments of the function;
      in C++, it says there are no arguments. This message is not given for member function
      declarations or for function definitions. Rather, weaker Elective Notes (1917 and 1918) are
      given. This is because the chance of ambiguity does not exist in these cases. [11 section 8.2.5]


    1. lamerok Автор
      06.07.2015 11:18
      +2

      Да точно, еще раз вглянул стандарт С++
      The parameter-declaration-clause determines the arguments that can be specified, and their processing,
      when the function is called. [Note: the parameter-declaration-clause is used to convert the arguments
      specified on the function call; see 5.2.2. ] If the parameter-declaration-clause is empty, the function takes
      no arguments. The parameter list (void) is equivalent to the empty parameter list.


      1. monah_tuk
        06.07.2015 15:54

        Я не отрицал выделенного. Вопрос про избыточность был. Но вообще:

        Но так написано в стандарте кодирования.

        хватило бы: от них иногда плеваться хочется, но никуда не попрёшь. Я, правда, думал, что вы для себя пишите. А откуда взялись tU32, tF32, tBoolean?


        1. lamerok Автор
          06.07.2015 20:04
          -2

          Ну это для себя было, точнее в свободное время для курса в университете(на волонтерских началах пока что), но чисто дома. К стандарту привык, потому что когда ревью кода делаю(сам писал давно очень, а ревью делаю переодически), моя задача проверить код на стандарт первым делом. Типы тоже указаны в стандарте.Скорее всего я его отдельно очень упрощенный для университета сделаю.


  1. ProstoTyoma
    07.07.2015 16:18
    +1

    Наследование от iFilter выглядит плохо. Да и не интерфейс это, раз вы по iFilter* ни к кому не обращаетесь. Лучше создайте фильтр отдельно и заагрегируйте как-нибудь.


    1. lamerok Автор
      07.07.2015 19:26

      Да — согласен.


  1. sam_satan
    08.07.2015 16:26

    Не понимаю, зачем вы выкидываете всю настройку железа в __low_level_init(). Получается недо ооп. Если поменять ацп с ADC1 на ADC2, нужно и менять и функцию инициализации и сам класс.
    Логично инициализировать периферию в конструкторе класса ацп, ведь он и предназначался для связи логики с железом.

    При этом отдельно хочется заметить, что динамическое выделение памяти под классы, относящиеся к железу, во всраиваемых системах это моветон. Мало где увидишь плату, где например припаян разъем usb, а через секунду эти же ножки заняты внешней припаянной flash памятью или lcd дисплеем.
    Как уже где-то писалось, крайне не хочется во время нажатия на педаль тормоза в автомобиле получить exception о том, что недостаточно памяти для выделения памяти под класс cBrakePushHandler.


    1. lamerok Автор
      08.07.2015 16:37

      Да по поводу динамического выделения памяти — согласен, в 3 части как раз фильтр реализовал, без него.
      По поводу создания объектов с main(), тут есть особенность freeRTOS, если объявлять в main() вот так:


    1. lamerok Автор
      08.07.2015 16:50

      Извиняюсь нажал на Enter случайно…
      1. Да по поводу динамического выделения памяти — согласен, в 3 части как раз фильтр реализовал, без него.
      По поводу создания объектов с main(), тут есть особенность freeRTOS, если объявлять в main() вот так:

      void main (void)
      {
        cLedsDirector oLedsDirector;
        ...
        oRTOS.creatTask(&oLedsDirector...) ;
        oRTOS.startScheduler();
       
      }
      

      То вообще из-за особенностей работы freeRTOS, ничего работать не будет, посколько oLedsDirector создан на стеке, а при запуске планировщика и первой задачи, весь стек идет к чертям собачим, так как freeRTOS указатель стека перезаписывает на начало.
      Поэтому тут надо будет либо немного «патчить» операционку, либо создавать объект oLedsDirector глобально. А глобальные объекты, тоже как то не по понятиям.
      Поэтому нью тут решение подходящее, можно его переопределить, если уж совсем надо.
      Из-за этой особенности операционки, нельзя создавать локальные переменные в мейне, ну точнее можно, но надо знать, что их потом нельзя использовать.
      Ну и в данном проекте, все объекты создаются один раз в одном месте. и живут пока ресет не нажмут. Поэтому тут ничего плохого в этом нет. Никакого переполнения памяти не будет.

      2. В лоу левел инит, скинул, только, то что не надо для конкретного проекта перенастраивать никогда. Но конечно, же это просто так мне захотелось, можно сделать и настройку отдельно, но я не хотел вообще железо(которое не перенастраивается) нигде трогать — настроил и забыл, а в классы вынес только, то что по ходу меняется. Там в регистр записать что-то, проверить и так далее…


      1. sam_satan
        10.07.2015 11:25

        Про переопределение стека фриртосом спасибо, не знал.
        По поводу C++ в embedded советую книгу Real time c++


        1. lamerok Автор
          10.07.2015 13:51

          Спасибо за книгу, обязательно почитаю.