Обсуждаем контроллеры компании Atmel.
Ответить

Re: EEPROM запись в цикле.

Пт окт 20, 2017 18:20:09

но даже не знаю какой стандарт поддерживает avr-gcc в atmelstudio ведь не так давно вышел очередной стандарт с++17


Про это можете забыть сразу. :)))

Вообще про плюсы на контроллере, да еще и Гарвардской архитектуры, вспоминать грешно.

Чтобы использовать C++ с комфортом, необходимо как минимум динамическое выделение памяти, а для микроконтроллера это непозволительная роскошь - здесь нормальный менеджер памяти сам почти всю доступную память и скушает. :))) Отдельный поворот делу придает то, что AVR, например, не может выполнять код из RAM (Гарвардская архитектура, да).

Конечно, отдельные эстеты умудряются писать на плюсах и под мелкие МК, но к этому нельзя относиться всерьез.

Так что только ANSI C, причем, желательно, с учетом правил MISRA. :)

Re: EEPROM запись в цикле.

Пт окт 20, 2017 18:33:18

:facepalm: какой там С++17, когда с перебором массива структур проблемы...

Re: EEPROM запись в цикле.

Пт окт 20, 2017 20:09:01

Вообще про плюсы на контроллере, да еще и Гарвардской архитектуры, вспоминать грешно.


Ну, это вы слегка погорячились. Практический пример преимущества использования плюсов под мк.

Так что при хорошем владении плас ничем не уступает Си, а местами даже дает преимущества.

Re: EEPROM запись в цикле.

Сб окт 21, 2017 08:50:26

Переделал немного код, добавил плавное включение. (осталось в момент плавного включения добавить обработку индикации).

Код:
Спойлер
Код:
#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

//----------///init_pwm///----------//
void init_pwm()
{
   TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
   TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (0<<CS00);
   OCR0A=0x00;
   OCR0B=0x00;
   
   TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (1<<WGM10);
   TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
   OCR1AL=0x00;
   OCR1BL=0x00;

   TCCR2A=(1<<COM2A1) | (0<<COM2A0) | (1<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (1<<WGM20);
   TCCR2B=(0<<WGM22) | (0<<CS22) | (1<<CS21) | (0<<CS20);
   OCR2A=0x00;
   OCR2B=0x00;
}
//----------//
void init_int0()
{
   //настраиваем на срабатывание INT0 по переднему фронту
   EICRA |= (1<<ISC01)|(0<<ISC00);
   //разрешаем внешнее прерывание INT0
   EIMSK |= (1<<INT0);
}
//----------//
void init_io()
{
   DDRB=(0<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(0<<PB5)|(0<<PB6)|(0<<PB7);
   PORTB=0x00;
   DDRC=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4)|(1<<PC5)|(1<<PC6);
   PORTC=0x00;
   DDRD=(1<<PD0)|(1<<PD1)|(1<<PD2)|(1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
   PORTD=0x00;
}
//----------//
//настройка параметров работы функций
#define BTN_LOCK_TIME      30               /*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME      1000            /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
//настройки портов
/*порт чтения кнопок*/
#define BTN_PORT         PORTB
#define BTN_DDR            DDRB
#define BTN_PIN            PINB
/*пины чтения кнопок*/
#define BTN_LINE_UP      (1<<7)
#define BTN_LINE_DN      (1<<6)
#define BTN_LINE_POWER   (1<<5)
#define BTN_LINE_SW      (1<<0)
//глобальные переменные
volatile uint8_t BtnFlags;               //байт флагов нажатия кнопки
#define BTN_SHRT_UP         (1<<0)         /*бит короткого нажатия кнопки up*/
#define BTN_SHRT_DN         (1<<1)         /*бит короткого нажатия кнопки dn*/
#define BTN_SHRT_POWER      (1<<2)         /*бит короткого нажатия кнопки POWER */
#define BTN_SHRT_SW         (1<<3)         /*бит короткого нажатия кнопки SW*/
#define BTN_LONG_UP         (1<<4)         /*бит длинного нажатия кнопки up*/
#define BTN_LONG_DN         (1<<5)         /*бит длинного нажатия кнопки dn*/
#define BTN_LONG_SW         (1<<6)         /*бит короткого нажатия кнопки SW*/
//----------
//Функция настройки библиотеки работы с кнопками
void BtnInit (void)
{
   BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//на ввод
   BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//подтяжка вкл
}
//----------
//Функция чтения данных о нажатии кнопок
char BtnGet (void)
{
   cli();
   char temp = BtnFlags;
   BtnFlags = 0;
   sei();
   return temp;
}
//----------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ (вызывать в прерывании с частотой 100 Гц)
//короткое нажатие устанавливает бит BTN_SHRT_X глобальной переменной BtnFlags
//длинное нажатие устанавливает бит BTN_LONG_X глобальной переменной BtnFlags
void BtnExe (void)
{
   static unsigned char BtnLockBit;            //защелка (защита от дребезга)
   static unsigned char BtnLockCoun;         //счетчик защелки (защита от дребезга)
   static unsigned char BtnLongCoun;         //счетчик длинного нажатия
   static unsigned char BtnLastState;         //последнее состояние кнопок перед отпусканием

   char mask = 0;
   if (! (BTN_PIN & BTN_LINE_UP))      mask = BTN_SHRT_UP;
   if (! (BTN_PIN & BTN_LINE_DN))      mask = BTN_SHRT_DN;
   if (! (BTN_PIN & BTN_LINE_POWER))   mask = BTN_SHRT_POWER;
   if (! (BTN_PIN & BTN_LINE_SW))      mask = BTN_SHRT_SW;

   if (mask){                           //опрос состояния кнопки
      if (BtnLockCoun < (BTN_LOCK_TIME/10)){   //клавиша нажата
         BtnLockCoun++;
         return;                        //защелка еще не дощитала - возврат
      }
      BtnLastState = mask;
      BtnLockBit =1;                     //нажатие зафиксировано
      if (BtnLongCoun >= (BTN_LONG_TIME/10))
      return;                        //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше
      if (++BtnLongCoun >= (BTN_LONG_TIME/10))
      BtnFlags |= (BtnLastState<<4);         //счетчик досчитал до максимума - устанавливаем биты длинного нажатия
   }
   else{                              //клавиша отжата
      if (BtnLockCoun){
         BtnLockCoun --;
         return;                        //защелка еще не обнулилась - возврат
      }
      if (! BtnLockBit)                  //СТАТИЧЕСКИЙ ВОЗВРАТ
      return;
      BtnLockBit =0;                     //отжатие зафиксировано
      if (BtnLongCoun < (BTN_LONG_TIME/10))
      BtnFlags |= BtnLastState;         //установка бита короткого нажатия
      BtnLongCoun = 0;               //сброс счетчика длительности нажатия
   }
}
//----------****7SEG****----------
#define SEGA 6
#define SEGB 5
#define SEGC 1
#define SEGD 2
#define SEGE 3
#define SEGF 4
#define SEGG 0

#define ANOD1 4
#define ANOD2 7
#define ANOD3 4
//----------
void segchar (unsigned char seg)
{
   switch (seg)
   {
      case 0:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(1<<SEGG);break;
      case 1:
      PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
      case 2:
      PORTC=(0<<SEGA)|(0<<SEGB)|(1<<SEGC)|(0<<SEGD)|(0<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
      case 3:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
      case 4:
      PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 5:
      PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 6:
      PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 7:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
      case 8:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 9:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 99: //OFF Все сегменты
      PORTC=(1<<SEGA)|(1<<SEGB)|(1<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
   }
}

#define CONFIG_AMOUNT 6
typedef struct
{
   char FlagPower;
   char ValuePWM;
}ConfigurationLamp;

EEMEM ConfigurationLamp E_ConfigLamp[CONFIG_AMOUNT];
ConfigurationLamp ConfigLamp[CONFIG_AMOUNT];
//----------

void LoadingEEPROM()
{
   eeprom_read_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}

void SaveEEPROM()
{
   eeprom_update_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}
void WriteValue(unsigned char Zona,unsigned char ValuePWM)
{
   switch (Zona)
   {
      case 0:OCR0A=ValuePWM*2.56;break;
      case 1:OCR0B=ValuePWM*2.56;break;
      case 2:OCR1AL=ValuePWM*2.56;break;
      case 3:OCR1BL=ValuePWM*2.56;break;
      case 4:OCR2A=ValuePWM*2.56;break;
      case 5:OCR2B=ValuePWM*2.56;break;
   }
}
char ReadValue(unsigned char Zona)
{
   static unsigned char ValuePWM=0;
   switch (Zona)
   {
      case 0:ValuePWM=OCR0A;break;
      case 1:ValuePWM=OCR0B;break;
      case 2:ValuePWM=OCR1AL;break;
      case 3:ValuePWM=OCR1BL;break;
      case 4:ValuePWM=OCR2A;break;
      case 5:ValuePWM=OCR2B;break;
   }
   return(ValuePWM/2.56);
}
unsigned char TempValuePWM=0;
void UpdateValue()
{
   for (unsigned char ZonaCount = 0; ZonaCount < CONFIG_AMOUNT; ZonaCount++)
   {
      TempValuePWM=ReadValue(ZonaCount);
      if (ConfigLamp[ZonaCount].FlagPower==1)
      {
         if (TempValuePWM==0)
         {
            while(TempValuePWM<ConfigLamp[ZonaCount].ValuePWM)
            {
               TempValuePWM++;
               WriteValue(ZonaCount,TempValuePWM);
               _delay_ms(50);
            }
         }
         WriteValue(ZonaCount,ConfigLamp[ZonaCount].ValuePWM);
      }
      else
      {
         WriteValue(ZonaCount,0);
      }
      
   }
}
unsigned char ZoneNumber=0;
unsigned char count = 0;
void WriteSeg(unsigned char Number)
{
   unsigned char data1=ConfigLamp[Number].ValuePWM%10;
   unsigned char data2=ConfigLamp[Number].ValuePWM/10;
   PORTB |=(1<<ANOD1);
   PORTD |=(1<<ANOD2);
   PORTD |=(1<<ANOD3);
   segchar(99);
   if(ConfigLamp[Number].FlagPower==1)
   {
      count++;
      if (count==1){
         PORTB &= ~(1<<ANOD1);
         segchar(data1);
         PORTD |=(1<<ANOD2);
         PORTD |=(1<<ANOD3);
      }
      if (count==2){
         PORTD &= ~(1<<ANOD2);
         segchar(data2);
         PORTB |=(1<<ANOD1);
         PORTD |=(1<<ANOD3);
      }
      if (count==3){
         PORTD &= ~(1<<ANOD3);
         segchar(Number+1);
         PORTB |=(1<<ANOD1);
         PORTD|=(1<<ANOD2);
      }
      if (count==3){count=0;}
   }
}
char Stop=1;
ISR(INT0_vect)
{
   WriteSeg(ZoneNumber);
   UpdateValue();
   BtnExe();
}
void BtnUpdate(void)
{
   char BtnMask = BtnGet ();
   if (BtnMask == BTN_SHRT_POWER)
   {
      ConfigLamp[ZoneNumber].FlagPower++;
      if (ConfigLamp[ZoneNumber].FlagPower>1)
      {
         ConfigLamp[ZoneNumber].FlagPower=0;
      }
      SaveEEPROM();
   }
   if ((BtnMask == BTN_SHRT_SW))
   {
      ZoneNumber++;
      if (ZoneNumber==6)
      {
         ZoneNumber=0;
      }
   }
   //одиночное нажатие +
   if ((BtnMask == BTN_SHRT_UP)& (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      ConfigLamp[ZoneNumber].ValuePWM++;
      SaveEEPROM();
   }
   //одиночное нажатие -
   if ((BtnMask == BTN_SHRT_DN)& (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      ConfigLamp[ZoneNumber].ValuePWM--;
      SaveEEPROM();
   }
   //Удержание +
   if ((BtnMask == BTN_LONG_UP) & (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      while ((!(PINB&0b10000000))& (ConfigLamp[ZoneNumber].ValuePWM < 99))
      {
         ConfigLamp[ZoneNumber].ValuePWM++;
         _delay_ms(50);
      }
      SaveEEPROM();
   }
   //Удержание -
   if ((BtnMask == BTN_LONG_DN) & (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      while ((!(PINB&0b01000000))& (ConfigLamp[ZoneNumber].ValuePWM > 0))
      {
         ConfigLamp[ZoneNumber].ValuePWM--;
         _delay_ms(50);
      }
      SaveEEPROM();
   }
}

int main(void)
{
   cli();
   LoadingEEPROM();
   init_io();
   BtnInit();
   init_int0();
   init_pwm();
   sei();
    while(1)
    {
      BtnUpdate();
   }
   return 0;
}

Re: EEPROM запись в цикле.

Сб окт 21, 2017 09:45:41

philosoraptor, я читал и эту статью, и ей подобные. И тут есть несколько моментов.

1. Автор рассматривает исключительно задачу работы с периферией, причем исключительно с портами. Выше же шла речь о более широком применении С++, в частности, и в основной логике - иначе зачем вообще заморачиваться?

2. Автор использует исключительно статические классы и ни словом не заикнулся (логично) про создание их экземпляров. И все равно, несмотря на все изгибание C++ для применимости в эмбеде, не обошлось без ограничений:

- "От динамической конфигурации линий ввода-вывода сразу отказываемся из-за необходимости доступа к портам через указатель со всеми вытекающими последствиями."

- "Мы не можем прочитать состояние выходных линий регистра – он всегда работает на выход, поэтому функцию чтения состояния не реализуем и не объявляем. Попытка прочитать состояние такого пора вызовет ошибку компиляции."

А динамическая конфигурация бывает нужна. Например, чтобы имитировать открытый коллектор на AVR.

Неизвестно, на какие компромиссы придется пойти при разработке API к другим специфичным блокам, которым периодически бывают нужны, например, особые последовательности доступа.

3. И несмотря на все ограничения код получился тяжеловесным и сложным для понимания. Даже сам автор в некоторый момент устал: "Обобщая доступ к отдельным битам в списке линий приходим к концепции среза. Не буду вдаваться в подробности их реализации".

4. В синтетических примерах автора результат дизассемблирования выглядит неплохо, но где гарантия, что в каких-то реальных условиях что-то не пойдет не так, и компилятор не сгенерирует код для того, что по задумке должен вычислять во время компиляции?

Родственный пример (для AVR-libc) - функции задержки, например, _delay_ms(). Если подставить в нее не константу, а переменную, будет сюрприз. Здесь может произойти нечто подобное.

Кстати, рекурсия, используемая автором, запрещена стандартом MISRA.

Код:
struct MakePinList
{
private:
      // рекурсивно проходим все параметры
      // на следующей итерации Position увеличится на 1,
      // а T2 превратится в T1 и так далее
typedef typename MakePinList
<
      Position + 1,

...


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

Код:
#define MAKE_PORT(portName, className, ID) \
   class className{\
   public:\
      typedef uint8_t DataT;\
   public:\
...


Это вообще в некоторой степени противоречит идеологии C++.

Re: EEPROM запись в цикле.

Сб окт 21, 2017 12:20:15

Так в программирование неуместно употреблять один подход, и скорее даже не возможно написать код следуя всего лишь одной парадигме программирования т.к
даже на языке Си, который не является объектно-ориентированным, можно работать в соответствии с принципами объектно-ориентированного программирования, хотя это и сопряжено с определёнными сложностями, а функциональное программирование можно применять при работе на любом императивном языке, в котором имеются функции,
Я решил закончить данную прошивку в стиле функционального программирования, а уже следующие не мение интересную попробовать написать (не потеряв при этом быстродействие) сделав уклон на ООП.

Добавлено after 8 minutes 23 seconds:
А насчет MISRA C который являеться как бы ''стандартом разработки программного обеспечения на Си'" . предполгаю что на данный момент времени он не являеться актуальным т.к насколько мне известно они не удосужились внести С99 в описание своего стандарта, не говоря о уже о том что процесса сертификации MISRA не существует в принцепе.

Добавлено after 1 hour 7 minutes 32 seconds:
Я вот думаю как все таки более лаконично написать легко переносимую функцию? для работы с динамической индикацией семи сегментного индикатора.
А то segchar()+WriteSeg() выглядят как то не комильфо.

Re: EEPROM запись в цикле.

Сб окт 21, 2017 12:30:14

предполгаю что на данный момент времени он не являеться актуальным


Это всего лишь ваше предположение. :) Впрочем, некоторые говорят, что и Си не является актуальным. :)))

Процесса сертификации MISRA не существует по простой причине: MISRA - это, так сказать, набор хороших советов, которые применимы для создания кода, используемого в разных применениях в разных отраслях с разными требованиями и подходами к сертификации. Например, устройство в целом может быть сертифицировано на некоторый уровень SIL, при этом для достижения этой цели логично использовать правила MISRA при написании прошивки - это уберет много граблей. Можно, конечно, не заморачиваться и идти путем проб и ошибок, самостоятельно переоткрывая рекомендации MISRA. :wink:

Я вот думаю как все таки более лаконично написать легко переносимую функцию?


Я для динамической индикации использую что-то вроде фреймбуфера, в который складываю паттерны для символов на индикаторе. Этот буфер выводится в порт по прерыванию. Заполняет его отдельная функция, которая заодно преобразует числа/символы в паттерны.

Re: EEPROM запись в цикле.

Сб окт 21, 2017 13:29:01

YS писал(а):_delay_ms(). Если подставить в нее не константу, а переменную, будет сюрприз.

На эти грабли, пожалуй, все новички наступают.
YS писал(а): В синтетических примерах автора результат дизассемблирования выглядит неплохо, но где гарантия, что в каких-то реальных условиях что-то не пойдет не так, и компилятор не сгенерирует код для того, что по задумке должен вычислять во время компиляции?

Гарантии нет в любом случае, даже если строго следовать всем стандартам. Остается уповать лишь на здравый смысл кодера и на способность препроцессора отловить совсем уж явные несуразности.
YS писал(а):речь о более широком применении С++, в частности, и в основной логике - иначе зачем вообще заморачиваться?

Например, если мы неплохо владеем С++, то легко сможем составить на нем и полностью обратно-совместимый с чистым Си код. В чем профит, спросите вы? Ну, к примеру, есть множество ардуинообразных библиотек на С++, которые можно использовать и без ардуины. Да, их можно легко портировать и на Си, но это лишние телодвижения.

Re: EEPROM запись в цикле.

Сб окт 21, 2017 13:52:51

Гарантии нет в любом случае, даже если строго следовать всем стандартам.


Просто не надо полагаться на оптимизации вроде вычисления выражений на этапе компиляции (если только они не состоят исключительно из констант-дефайнов) и, тем более, замену компилятором участка кода на результат его выполнения. Это очень рискованно само по себе, а заодно усложняет портирование кода.

Я считаю, что на оптимизацию вообще полагаться не надо. Надо стараться, чтобы написанный код был как можно более прозрачен.

Например, если мы неплохо владеем С++, то легко сможем составить на нем и полностью обратно-совместимый с чистым Си код.


В этом случае обычно пишут библиотеку на чистом C, а на C++ делают только обертку для желающих. Такой подход гораздо лучше потому, что обеспечивает совместимость не только на уровне исходников, но и на бинарном уровне - скажем, DLL на чистом Си гораздо универсальнее. То же самое справедливо и для скомпилированных статических библиотек.

Re: EEPROM запись в цикле.

Пн окт 23, 2017 03:28:10

Ну вот и готов финишный код.
Конечно реализация плавного запуска желает желать лучшего, но она все же справляется со своими задачами.

Код:
Спойлер
Код:
#define F_CPU 8000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

//----------///init_pwm///----------//
void init_pwm()
{
   TCCR0A=(1<<COM0A1) | (0<<COM0A0) | (1<<COM0B1) | (0<<COM0B0) | (1<<WGM01) | (1<<WGM00);
   TCCR0B=(0<<WGM02) | (0<<CS02) | (1<<CS01) | (0<<CS00);
   OCR0A=0x00;
   OCR0B=0x00;
   
   TCCR1A=(1<<COM1A1) | (0<<COM1A0) | (1<<COM1B1) | (0<<COM1B0) | (0<<WGM11) | (1<<WGM10);
   TCCR1B=(0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12) | (0<<CS12) | (1<<CS11) | (0<<CS10);
   OCR1AL=0x00;
   OCR1BL=0x00;

   TCCR2A=(1<<COM2A1) | (0<<COM2A0) | (1<<COM2B1) | (0<<COM2B0) | (1<<WGM21) | (1<<WGM20);
   TCCR2B=(0<<WGM22) | (0<<CS22) | (1<<CS21) | (0<<CS20);
   OCR2A=0x00;
   OCR2B=0x00;
}
//----------//
void init_int0()
{
   //настраиваем на срабатывание INT0 по переднему фронту
   EICRA |= (1<<ISC01)|(0<<ISC00);
   //разрешаем внешнее прерывание INT0
   EIMSK |= (1<<INT0);
}
//----------//
void init_io()
{
   DDRB=(0<<PB0)|(1<<PB1)|(1<<PB2)|(1<<PB3)|(1<<PB4)|(0<<PB5)|(0<<PB6)|(0<<PB7);
   PORTB=0x00;
   DDRC=(1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4)|(1<<PC5)|(1<<PC6);
   PORTC=0x00;
   DDRD=(1<<PD0)|(1<<PD1)|(1<<PD2)|(1<<PD3)|(1<<PD4)|(1<<PD5)|(1<<PD6)|(1<<PD7);
   PORTD=0x00;
}
//----------//
//настройка параметров работы функций
#define BTN_LOCK_TIME      30               /*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME      1000            /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
//настройки портов
/*порт чтения кнопок*/
#define BTN_PORT         PORTB
#define BTN_DDR            DDRB
#define BTN_PIN            PINB
/*пины чтения кнопок*/
#define BTN_LINE_UP      (1<<7)
#define BTN_LINE_DN      (1<<6)
#define BTN_LINE_POWER   (1<<5)
#define BTN_LINE_SW      (1<<0)
//глобальные переменные
volatile uint8_t BtnFlags;               //байт флагов нажатия кнопки
#define BTN_SHRT_UP         (1<<0)         /*бит короткого нажатия кнопки up*/
#define BTN_SHRT_DN         (1<<1)         /*бит короткого нажатия кнопки dn*/
#define BTN_SHRT_POWER      (1<<2)         /*бит короткого нажатия кнопки POWER */
#define BTN_SHRT_SW         (1<<3)         /*бит короткого нажатия кнопки SW*/
#define BTN_LONG_UP         (1<<4)         /*бит длинного нажатия кнопки up*/
#define BTN_LONG_DN         (1<<5)         /*бит длинного нажатия кнопки dn*/
#define BTN_LONG_SW         (1<<6)         /*бит короткого нажатия кнопки SW*/
//----------
//Функция настройки библиотеки работы с кнопками
void BtnInit (void)
{
   BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//на ввод
   BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_POWER|BTN_LINE_SW);//подтяжка вкл
}
//----------
//Функция чтения данных о нажатии кнопок
char BtnGet (void)
{
   cli();
   char temp = BtnFlags;
   BtnFlags = 0;
   sei();
   return temp;
}
//----------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ (вызывать в прерывании с частотой 100 Гц)
//короткое нажатие устанавливает бит BTN_SHRT_X глобальной переменной BtnFlags
//длинное нажатие устанавливает бит BTN_LONG_X глобальной переменной BtnFlags
void BtnExe (void)
{
   static unsigned char BtnLockBit;            //защелка (защита от дребезга)
   static unsigned char BtnLockCoun;         //счетчик защелки (защита от дребезга)
   static unsigned char BtnLongCoun;         //счетчик длинного нажатия
   static unsigned char BtnLastState;         //последнее состояние кнопок перед отпусканием

   char mask = 0;
   if (! (BTN_PIN & BTN_LINE_UP))      mask = BTN_SHRT_UP;
   if (! (BTN_PIN & BTN_LINE_DN))      mask = BTN_SHRT_DN;
   if (! (BTN_PIN & BTN_LINE_POWER))   mask = BTN_SHRT_POWER;
   if (! (BTN_PIN & BTN_LINE_SW))      mask = BTN_SHRT_SW;

   if (mask){                           //опрос состояния кнопки
      if (BtnLockCoun < (BTN_LOCK_TIME/10)){   //клавиша нажата
         BtnLockCoun++;
         return;                        //защелка еще не дощитала - возврат
      }
      BtnLastState = mask;
      BtnLockBit =1;                     //нажатие зафиксировано
      if (BtnLongCoun >= (BTN_LONG_TIME/10))
      return;                        //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше
      if (++BtnLongCoun >= (BTN_LONG_TIME/10))
      BtnFlags |= (BtnLastState<<4);         //счетчик досчитал до максимума - устанавливаем биты длинного нажатия
   }
   else{                              //клавиша отжата
      if (BtnLockCoun){
         BtnLockCoun --;
         return;                        //защелка еще не обнулилась - возврат
      }
      if (! BtnLockBit)                  //СТАТИЧЕСКИЙ ВОЗВРАТ
      return;
      BtnLockBit =0;                     //отжатие зафиксировано
      if (BtnLongCoun < (BTN_LONG_TIME/10))
      BtnFlags |= BtnLastState;         //установка бита короткого нажатия
      BtnLongCoun = 0;               //сброс счетчика длительности нажатия
   }
}
//----------****7SEG****----------
#define SEGA 6
#define SEGB 5
#define SEGC 1
#define SEGD 2
#define SEGE 3
#define SEGF 4
#define SEGG 0

#define ANOD1 4
#define ANOD2 7
#define ANOD3 4
//----------
void segchar (unsigned char seg)
{
   switch (seg)
   {
      case 0:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(1<<SEGG);break;
      case 1:
      PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
      case 2:
      PORTC=(0<<SEGA)|(0<<SEGB)|(1<<SEGC)|(0<<SEGD)|(0<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
      case 3:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(1<<SEGF)|(0<<SEGG);break;
      case 4:
      PORTC=(1<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 5:
      PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 6:
      PORTC=(0<<SEGA)|(1<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 7:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
      case 8:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(0<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 9:
      PORTC=(0<<SEGA)|(0<<SEGB)|(0<<SEGC)|(0<<SEGD)|(1<<SEGE)|(0<<SEGF)|(0<<SEGG);break;
      case 99: //OFF Все сегменты
      PORTC=(1<<SEGA)|(1<<SEGB)|(1<<SEGC)|(1<<SEGD)|(1<<SEGE)|(1<<SEGF)|(1<<SEGG);break;
   }
}

#define CONFIG_AMOUNT 6
typedef struct
{
   char FlagPower;
   char ValuePWM;
}ConfigurationLamp;

EEMEM ConfigurationLamp E_ConfigLamp[CONFIG_AMOUNT];
ConfigurationLamp ConfigLamp[CONFIG_AMOUNT];
//----------

void LoadingEEPROM()
{
   eeprom_read_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}

void SaveEEPROM()
{
   eeprom_update_block((void*)ConfigLamp, (void*)E_ConfigLamp, sizeof(ConfigLamp));
}
//Функция записи значения PWM
void WriteValue(unsigned char Zona,unsigned char ValuePWM)
{
   switch (Zona)
   {
      case 0:OCR0A=ValuePWM*2.56;break;
      case 1:OCR0B=ValuePWM*2.56;break;
      case 2:OCR1AL=ValuePWM*2.56;break;
      case 3:OCR1BL=ValuePWM*2.56;break;
      case 4:OCR2A=ValuePWM*2.56;break;
      case 5:OCR2B=ValuePWM*2.56;break;
   }
}
//Функция считывания значения PWM
char ReadValue(unsigned char Zona)
{
   static unsigned char ValuePWM=0;
   switch (Zona)
   {
      case 0:ValuePWM=OCR0A;break;
      case 1:ValuePWM=OCR0B;break;
      case 2:ValuePWM=OCR1AL;break;
      case 3:ValuePWM=OCR1BL;break;
      case 4:ValuePWM=OCR2A;break;
      case 5:ValuePWM=OCR2B;break;
   }
   return(ValuePWM/2.56);
}
//Функция вывода значений на 7SEG индикатор.
unsigned char ZoneNumber=0;
unsigned char count = 0;
void WriteSeg(unsigned char NumberZon,unsigned char ValuePWM)
{
   
   unsigned char data1=ValuePWM%10;
   unsigned char data2=ValuePWM/10;
   PORTB |=(1<<ANOD1);
   PORTD |=(1<<ANOD2);
   PORTD |=(1<<ANOD3);
   segchar(99);
   if(ConfigLamp[NumberZon].FlagPower==1)
   {
      count++;
      if (count==1){
         PORTB &= ~(1<<ANOD1);
         segchar(data1);
         PORTD |=(1<<ANOD2);
         PORTD |=(1<<ANOD3);
      }
      if (count==2){
         PORTD &= ~(1<<ANOD2);
         segchar(data2);
         PORTB |=(1<<ANOD1);
         PORTD |=(1<<ANOD3);
      }
      if (count==3){
         PORTD &= ~(1<<ANOD3);
         segchar(NumberZon+1);
         PORTB |=(1<<ANOD1);
         PORTD|=(1<<ANOD2);
      }
      if (count==3){count=0;}
   }
}

/*****************************************Обновление Значений PWM********************************************************/
float TempValuePWM=0;
void UpdateValue()
{
   for (unsigned char ZonaCount = 0; ZonaCount < CONFIG_AMOUNT; ZonaCount++)
   {
      TempValuePWM=ReadValue(ZonaCount);
      if (ConfigLamp[ZonaCount].FlagPower==1)
      {
         if (TempValuePWM==0)
         {
            //Плавный Запуск
            TempValuePWM=1;
            while(TempValuePWM<ConfigLamp[ZonaCount].ValuePWM)
            {
               TempValuePWM=TempValuePWM+TempValuePWM/40;
               WriteValue(ZonaCount,TempValuePWM);
               WriteSeg(ZonaCount,TempValuePWM);
               _delay_ms(13);
            }
         }
         WriteValue(ZonaCount,ConfigLamp[ZonaCount].ValuePWM);
      }
      else
      {
         WriteValue(ZonaCount,0);
      }
      
   }
}

char Stop=1;
ISR(INT0_vect)
{
   WriteSeg(ZoneNumber,ConfigLamp[ZoneNumber].ValuePWM);
   UpdateValue();
   BtnExe();
}
void BtnUpdate(void)
{
   char BtnMask = BtnGet ();
   if (BtnMask == BTN_SHRT_POWER)
   {
      ConfigLamp[ZoneNumber].FlagPower++;
      if (ConfigLamp[ZoneNumber].FlagPower>1)
      {
         ConfigLamp[ZoneNumber].FlagPower=0;
      }
      SaveEEPROM();
   }
   if ((BtnMask == BTN_SHRT_SW))
   {
      ZoneNumber++;
      if (ZoneNumber==6)
      {
         ZoneNumber=0;
      }
   }
   //одиночное нажатие +
   if ((BtnMask == BTN_SHRT_UP)& (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      ConfigLamp[ZoneNumber].ValuePWM++;
      SaveEEPROM();
   }
   //одиночное нажатие -
   if ((BtnMask == BTN_SHRT_DN)& (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      ConfigLamp[ZoneNumber].ValuePWM--;
      SaveEEPROM();
   }
   //Удержание +
   if ((BtnMask == BTN_LONG_UP) & (ConfigLamp[ZoneNumber].ValuePWM < 99)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      while ((!(PINB&0b10000000))& (ConfigLamp[ZoneNumber].ValuePWM < 99))
      {
         ConfigLamp[ZoneNumber].ValuePWM++;
         _delay_ms(50);
      }
      SaveEEPROM();
   }
   //Удержание -
   if ((BtnMask == BTN_LONG_DN) & (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))
   {
      while ((!(PINB&0b01000000))& (ConfigLamp[ZoneNumber].ValuePWM > 0))
      {
         ConfigLamp[ZoneNumber].ValuePWM--;
         _delay_ms(50);
      }
      SaveEEPROM();
   }
}

int main(void)
{
   cli();
   LoadingEEPROM();
   init_io();
   BtnInit();
   init_int0();
   init_pwm();
   sei();
    while(1)
    {
      BtnUpdate();
   }
   return 0;
}


Если кому интересно для чего писался этот код , то ловите ссылочку.
viewtopic.php?f=11&t=148259&p=3213287#p3213287

Re: EEPROM запись в цикле.

Пн окт 23, 2017 14:25:27

Код:
void BtnUpdate(void)
{
................................
................................
      ZoneNumber++;
// !!!! здесь может вызваться обработчик INT0_vect??? Если да, то внимательно посмотрите, что произойдет!!!!
      if (ZoneNumber==6)
      {
         ZoneNumber=0;
      }
..............................................
..............................................
}
Последний раз редактировалось viiv Пн окт 23, 2017 17:07:05, всего редактировалось 3 раз(а).

Re: EEPROM запись в цикле.

Пн окт 23, 2017 16:32:38

Уважаемый viiv, я конечно еще не знаю всех тонкостей программирование мк. Но может все таки дадите более развернутый комментарий.

Re: EEPROM запись в цикле.

Пн окт 23, 2017 17:02:48

Обработчик прерываний асинхронный? т.е. может прервать программу в любом месте? Тогда посмотрите внимательно, что будет, если обработчик вызовится там, где у меня комментарий (при вызове обработчика ZoneNumber равняется 6).

2)
unsigned char count = 0; // используется только в WriteSeg() - нет причин делать данную переменную глобальной; Ограничте видимость функцией WriteSeg().
Это не единственное, "видимость" чего желательно ограничить.

3)
Конечно реализация плавного запуска желает желать лучшего, но она все же справляется со своими задачами.


Сколько времени Вы не выходите из обработчика? Вызов _delay_ms () в обработчике мне "глаза режет". :-)

4) float - применен неоправдано. Зачем нужен float?

5)
if ((BtnMask == BTN_SHRT_DN)& (ConfigLamp[ZoneNumber].ValuePWM > 0)&(ConfigLamp[ZoneNumber].FlagPower==1))

Warning-ов нет??? '&' и '&&' - это разные вещи.

Re: EEPROM запись в цикле.

Пн окт 23, 2017 17:14:18

Насчет _delay_ms () знаю что не комильфо, но тот цикл используется всего лишь один раз при включении а более элегантного решения в голову не пришло (.
Насчет всего остального спасибо еще немного покопаюсь в этом коде.
А насчет 5того пункта компилятор не выдавал Warningов , хотя согласен логическое И правильней писать &&, странно что эти условия еще и работали.

Re: EEPROM запись в цикле.

Пн окт 23, 2017 17:52:39

Код:
if (ZoneNumber==6)


У Вас же есть специальная константа для этого (CONFIG_AMOUNT). Нет причин ее здесь не использовать - потенциальная причина ошибок.

Таких мест не одно.
Код:
while ((!(PINB&0b10000000))& (ConfigLamp[ZoneNumber].ValuePWM < 99))

BTN_PIN и BTN_LINE_UP - вроде для того и определили, чтобы в данном случае не PINB и 0b10000000 написАть, а что-то более понятное.

Ну и я бы, написал
Код:
if (ZoneNumber >= CONFIG_AMOUNT)
Т.е. по тексту сразу видно, что проверяется выход за переделы диапазона. А сравнение с константой на равенство, на мой взгляд, менее информативно :-)

ЗЫ. Логику не смотрел вообще. Но например, логика опроса кнопок станная.
Код:
#define BTN_SHRT_UP         (1<<0)         /*бит короткого нажатия кнопки up*/
#define BTN_SHRT_DN         (1<<1)         /*бит короткого нажатия кнопки dn*/
#define BTN_SHRT_POWER      (1<<2)         /*бит короткого нажатия кнопки POWER */
#define BTN_SHRT_SW         (1<<3)         /*бит короткого нажатия кнопки SW*/
#define BTN_LONG_UP         (1<<4)         /*бит длинного нажатия кнопки up*/
#define BTN_LONG_DN         (1<<5)         /*бит длинного нажатия кнопки dn*/
#define BTN_LONG_SW         (1<<6)         /*бит короткого нажатия кнопки SW*/


Могут одновревенно стоять носколько бит? Если да, то (BtnMask == BTN_SHRT_UP) - неверно. Если нет, то и незачем определять, как маску - это сильно запутывет.

Re: EEPROM запись в цикле.

Вт окт 24, 2017 02:07:53

Спасибо за более конструктивную критику у себя внес некоторые изменения.
Ответить