Русский
Русский
English
Статистика
Реклама

Atmega

Как я делал себе АВР для генератора

22.06.2020 22:22:51 | Автор: admin


Несколько лет назад делал себе АВР (автоматический ввод резерва) для работы на даче от генератора. Сейчас многие ИТ-шники переходят на удалёнку, работают с дач, где качество электропитания может оставлять лучшего. Поэтому решил написать о своем опыте самодельного АВР на микроконтроллере ATmega8A. Если тема интересна, добро пожаловать под кат, будет много букв и кода.

О заземлении


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

Стоит помнить, что в сети не всегда 220В. Коммутация на линиях, грозовые разряды вдалеке, статические разряды дают такие наводки, что в сети нередки короткие импульсы в несколько киловольт. С этим борются установкой разрядников и УЗИП на вводе в дом, но это очень редкая практика в РФ. Так что пусть искра в землю уходит, и не через вас сделайте по всему дому хорошее заземление. Без этого делать что-либо дальше просто нельзя!

О генераторах


К слову, у многих бытовых бензиновых генераторов обмотки никак не соединены с землёй. И это вполне нормально, когда вы питаете от генератора один электроинструмент. Но когда вам надо подключить генератор к дому, нужно сделать нулевой провод (N) и провод фазы (L). Для этого один из выводов генератора заземляется и из этой точки заземления уже независимо нужно вести в дом два провода один будет нейтралью N, а второй защитным заземлением (PE). При выборе генератора нужно обратить внимание, можно ли заземлять его выход, порой это запрещено в инструкции к генератору, тогда такой генератор вам не подойдёт.

Часто в Сети можно увидеть схемы подключения генератора без заземления и разделения линий N и PE. Не делайте так, дольше проживёте. Такие схемы хорошо работают до первого неудачного стечения обстоятельств. В типичных блоках питания современных электронных приборов стоят конденсаторы с линий L, N на землю. Если N не заземлить у генератора, то за счёт этих конденсаторов на линии N будет, если повезёт, 110 вольт относительно земли. Кстати, многие газовые котлы в таком режиме вообще перестают работать. Про влияние статики без присутствия заземления я уже писал выше.

О схемах АВР


Есть несколько разных схем реализации АВР. Дальше я буду писать о наиболее безопасной с моей точки зрения схеме однофазного АВР. Я не советую экономно делать АВР на одном контакторе или же с коммутацией только одного фазного провода. Только вместе с нейтралью.



На приведенной схеме питание от сети и от генератора подаётся через вводы 1 и 2. Они защищены спаренными автоматами. Через дополнительные автоматы питаются схемы коммутации и индикации. Видно, что катушки реле взаимно блокируются электрически. За включение того или иного ввода отвечает для упрощения не показанный на схеме микроконтроллер, который замыкает цепи в точке коммутации ТК1 или ТК2.

Принципиальным моментом является наличие в АВР 2х схем блокировок взаимной механической блокировки коммутирующих вводы контакторов и взаимной электрической блокировки контакторов. Самодельщики ради экономии, бывает, в своих конструкциях пренебрегают этими блокировками, а зря. Схема без блокировок может проработать некоторое время, но в какой-то момент контакты пригорят, возвратные пружины ослабнут и случится КЗ между вводами. Во-первых, это грозит большим бабахом, если обе линии окажутся под напряжением, но это не самая большая проблема. Гораздо важнее, что ваш генератор неожиданно для ремонтирующих проводку электриков может выдать в общую сеть напряжение при неблагоприятном стечении обстоятельств ремонтирующие линию электрики могут погибнуть. Для вас это уже уголовная статья.

О контакторах


Таким образом, использование обычных реле для нас отпадает, подойдут только специализированные контакторы. Для больших мощностей есть ещё вариант с моторизованными приводами, но это дорого и для типичного домашнего применения избыточно.

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

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

Ведущие производители контакторов имеют в своих линейках такое оборудование. Так что найти и купить комплект не представляет особого труда. Правда цены на брендовые контакторы на порядок выше наших/китайских. Поскольку количество циклов коммутации не ожидается большим, то выбор китайских контакторов вполне оправдан. К недостаткам можно отнести только то, что катушки контактора во время работы довольно сильно гудят.

Еще по поводу коммутируемой мощности. Контакты контактора должны выдерживать максимальную мощность, которую вам разрешено потреблять в доме. У меня это 10 кВт, поэтому контакторы я выбирал на допустимый ток через один контакт примерно в 50 ампер. Стоит отметить, что по какой-то причине коммутируемая мощность для типичного трехфазного контактора указывается в паспорте суммарная для всех трёх фаз, поэтому надо внимательно смотреть, какой допустимый ток именно через один контакт.

О схеме управления


Когда я занимался созданием АВР у меня было несколько особых требований к его работе:

  • У меня не так часто отключают электричество, поэтому я решил, что мне не нужен автозапуск генератора, а вот от автоматической остановки генератора я решил не отказываться: когда сеть восстанавливается, генератор сам затихает и сразу понятно, что теперь с питанием всё хорошо, да и бензин экономится
  • После старта генератора ему надо дать время прогреться и только после прогрева давать ему нагрузку. Т.е. мне нужен был таймер включения АВР после подачи напряжения от генератора
  • После восстановления напряжения в сети часто происходили повторные отключения через короткий промежуток времени, поэтому мне нужен был таймер, который бы выждал перед переходом с генератора на сеть некоторое время и не глушил сразу генератор
  • Генератору, говорят, полезно перед выключением немного поработать без нагрузки. И для этого мне тоже нужен был таймер

Таким образом вырисовывалась картина, что мне нужен контроллер с несколькими таймерами. В те времена я увлекался кодингом на AVR, поэтому решил сделать такой контроллер на Atmega 8a.

Хорошо бы, чтоб контроллер работал долго и надёжно. Кроме того, чтобы сделать полную гальваническую развязку и снабдить контроллер сторожевым таймером я ничего более не придумал. Ну и сделать схему и программу максимально простыми. Поскольку делалось всё для себя, то все настройки и калибровки решил оставить в коде весь UI свелся к одному светодиоду )

Основная задача контроллера мониторить напряжение на вводах и, при необходимости, переключать вводы. При этом приоритетным является ввод от деревенской сети.

Тут стоит отметить, что качество сети таково, что колебания от 150 в до 250 в вполне обычное явление. Поэтому понятие что есть хорошее питание от сети очень размыто. Через какое-то время я решил эту проблему, когда поставил на весь дом один мощный тиристорный стаблизатор напряжения на 11 кВт. Но, важно, стабилизатор можно ставить только до АВР, а не после! Включать стабилизатор для генератора категорически не рекомендуется. Есть опасность, что при определенной комбинации нагрузок, особенно всяких мощных насосов, система из генератора и стабилизатора станет неустойчивой и войдет в автоколебания.

После некоторых раздумий нарисовал такую схему в Eagle.



В схеме есть два идентичных трансформаторных источника питания, при наличии напряжения на любом из вводов схема обеспечена питанием. Между вводами возможно напряжение в 600в, поэтому изоляция трансформаторов должна быть хорошей. Питание берется после пакетников QF3 и QF4 соответственно.

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

Для коммутации катушек контакторов применяется стандартная схема из даташита для управления семисторами. 2 штуки ). Катушки это индуктивная нагрузка, поэтому цепи снаббера на выходе из резистора и конденсатора обязательны.

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

Из особенностей еще в качестве генератора опорного напряжения использован TL431. В остальном всё включено стандартно для Atmega 8. Есть светодиоды для индикации наличия напряжения питания на вводах и один светодиод статуса устройства. Тактируется схема с помощью внешнего кварца на 16 МГц.

Eagle мне породил вот такую печатную плату. Никаких SMD, симисторы и стабилизатор с легкими радиаторами.



Два тороидальных трансформатора установлены прямо на плате. Плату изготовил традиционным радиолюбительским способом с помощью фоторезиста. После монтажа покрыл тремя слоями акрилового лака. Надеюсь не пробьет его высокое напряжение.

О программе управления


Код программы довольно длинный, извините.

Код программы
/* * ABP - программа управления блоком "Автоматического ввода резерва" * В блоке управления есть два ввода напряжения от сети и генератора и три выхода- * один выход для управления контактором включения сети, второй - контактором * включения генератора и третий - реле запуска стартера генератора. * В блоке управления есть выход RS232 для отладочной информации, порт SPI для  * программирования микроконтроллера и 3 светодиода. Два зеленых светодиода  * показывают наличие напряжения питания на входах сети и генератора. Красный  * светодиод показывает состояние контроллера количеством вспышек. * * для нормальной печати напряжений нужно линковать большие библиотеки printf * дополнительные опции в линкере -Wl,-u,vfprintf -lprintf_flt */ #ifndef F_CPU #  define F_CPU 16000000UL #endif#define BAUD 9600#include <stdio.h>#include <stdlib.h>#include <avr/io.h>#include <util/delay.h>#include <avr/interrupt.h>#include <avr/wdt.h>#include <avr/sleep.h>// переменные для сохранения состояния контроллера после запуска// используются только для отладкиuint8_t mcusr_mirror __attribute__ ((section (".noinit")));void get_mcusr(void) \__attribute__((naked)) \__attribute__((section(".init3")));void get_mcusr(void){   mcusr_mirror = MCUSR;   MCUSR = 0;   wdt_disable();}//настройка UART для отладочной печати в порт RS232void uart_init( void ){/* //настройка скорости обмена   UBRRH = 0;   UBRRL = 103; //9600 при кварце 16 МГц */    #include <util/setbaud.h>    UBRRH = UBRRH_VALUE;    UBRRL = UBRRL_VALUE;       #if USE_2X      UCSRA |= (1 << U2X);    #else      UCSRA &= ~(1 << U2X);   #endif   //8 бит данных, 1 стоп бит, без контроля четности   UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 );   //разрешить прием и передачу данных   UCSRB = ( 1 << TXEN ) | ( 1 <<RXEN );}int uart_putc(  char c, FILE *file ){   //ждем окончания передачи предыдущего байта   while( ( UCSRA & ( 1 << UDRE ) ) == 0 );   UDR = c;   wdt_reset();   return 0;}FILE uart_stream = FDEV_SETUP_STREAM( uart_putc, NULL, _FDEV_SETUP_WRITE );// настройка счетчика 1 для счета секунд - главный таймер в программеvoid timer1_init( void ){   TCCR1A = 0; // регистр настройки таймера 1 - ничего интересного   /* 16000000 / 1024 = 15625 Гц, режим СТС со сбросом 15625 должен давать прерывания раз в 1 сек */   // режим CTC, ICP1 interrupt sense (falling)(not used) + prescale /1024 + без подавления шума (not used)    TCCR1B = (0 << WGM13) | (1 << WGM12) | (0 << ICES1) | ((1 << CS12) | (0 << CS11) | (1 << CS10)) | (0 << ICNC1);    OCR1A = 15625;        // прерывание    TIMSK |= (1 << OCIE1A);}// описание состояния контакторовtypedef enum _ABP_RLY_STATES {   RLY_OFF = 0, // контактор выключен   RLY_ON // контактор включен} ABP_RLY_STATES;// перечень используемых в блоке релеtypedef enum _ABP_RLY {   RLY_220N = 0,   RLY_220G,   RLY_GEN} ABP_RLY;volatile ABP_RLY_STATES contactors[RLY_GEN+1]; // расчетные состояния контакторов// описание состяния софтовых таймеровtypedef enum _ABP_TMR_STATES {   TMR_OFF = 0, // таймер выключен   TMR_ON // таймер включен} ABP_TMR_STATES;// структура описывающая софтовый таймерtypedef struct {   ABP_TMR_STATES state; // состояние включения таймера   unsigned char passed_secs; // сколько секунд прошло (ограничение до 255!!!)   unsigned char set_secs; // установка срабатывания таймера в секундах} TMR_INSTANCE;// перечень используемых софтовых таймеровtypedef enum _ABP_TMRS {   TMR_220N_ON = 0,  // задержка включения контактора после появления 220 от сети   TMR_220G_ON, // задержка включения контактора после появления 220 от генератора   TMR_220N_OFF, // задержка выключения контактора после пропадания 220 в сети   TMR_220G_OFF, // задержка выключения контактора после пропадания 220 от генератора    TMR_PRINT, // задержка отладочной печати в последовательный порт   TMR_GEN_OFF // задержка выключения реле стартера генератора после появления 220 от сети} ABP_TMRS;volatile TMR_INSTANCE abp_timers[TMR_GEN_OFF+1]; // таймеры по перечню ABP_TMRSvoid abp_timers_init( void ) {   // время срабатывания таймеров в секундах   abp_timers[TMR_220N_ON].set_secs = 10; // ожидание после включения сетевого напряжения   abp_timers[TMR_220G_ON].set_secs = 60; // ожидание для переключения на генератор для прогрева генератора   abp_timers[TMR_220N_OFF].set_secs = 5; // ожидание после пропадания сетевого напряжения   abp_timers[TMR_220G_OFF].set_secs = 5; // ожидание после пропадания напряжения генератора   abp_timers[TMR_GEN_OFF].set_secs = 60; // ожидание для охлаждения генератора перед остановом   abp_timers[TMR_PRINT].set_secs = 2; // задержка печати      unsigned char i;   for(i=TMR_220N_ON; i<=TMR_GEN_OFF; i++ ) {      abp_timers[i].state = TMR_OFF;      abp_timers[i].passed_secs = 0;   }   for(i=RLY_220N; i<=RLY_GEN; i++ ) {      contactors[i] = RLY_OFF;   }}// запуск таймера void abp_timer_start( ABP_TMRS tmr ) {   abp_timers[tmr].passed_secs = 0;   abp_timers[tmr].state = TMR_ON;    }// остановка таймера void abp_timer_stop( ABP_TMRS tmr ) {   abp_timers[tmr].state = TMR_OFF;   abp_timers[tmr].passed_secs = 0;}// проверка срабатывания таймераunsigned char abp_timer_check( ABP_TMRS tmr ) {   if (abp_timers[tmr].passed_secs >= abp_timers[tmr].set_secs) {      return 1;    } else {      return 0;   }}// прерывание для подсчета секунд в таймерахISR(TIMER1_COMPA_vect){   // сюда надо добавлять переменные счетчиков таймеров включения/выключения   unsigned char i;   for(i=TMR_220N_ON; i<=TMR_GEN_OFF; i++ ) {      if (abp_timers[i].state) {         abp_timers[i].passed_secs++;         }   }}//настройка COUNTER2 для управления светодиодом через переменную led_Statevoid counter2_init( void ){   ASSR = 0;  /* AS0 = 0 */  /* disable asynchronous mode */   while (ASSR); /*EMPTY*/   OCR2 = 223;             /* 70 Гц на выходе */   TCCR2 |= (1 << CS22) | (1 << CS21) | (1 << CS20);  /* prescale /1024 */      TCCR2 |= (1 << WGM21);                /* mode CTC */   TCCR2 &= ~(1 << WGM20);      TCCR2 &= ~(1 << COM21);              /* не выводить на OC2 */   TCCR2 &= ~(1 << COM20);      TIMSK |= (1 << OCIE2);          /* enable compare interrupt */}typedef enum _ABP_LED_STATES {   ABP_UNDEF = 0, // режим неопределен   ABP_1RELAY, // включено 1 реле   ABP_2RELAY  // включено 2 реле} ABP_LED_STATES;ABP_LED_STATES led_State = ABP_UNDEF;const unsigned char led_pattern[3][10] ={ { 1,0,1,0,1,0,1,0,1,0 }, // статус не определен  { 1,0,0,0,0,0,0,0,0,0 }, // включено 1 реле  { 1,0,1,0,0,0,0,0,0,0 } }; // включено 2 релеvolatile unsigned char timer2_count = 0;         volatile unsigned char led_cycle = 0; // от 0 до 9ISR(TIMER2_COMP_vect)  // должно вызываться примерно 70 раз в секунду{   if (++timer2_count > 6)       // типа примерно через 0.1 сек. нужно сменить режим светодиода   {      timer2_count = 0;           // сбрасываем счетчик      if (led_pattern[led_State][led_cycle]) {         PORTB &= ~(1 << PB0); // включаем              } else {         PORTB |=  (1 << PB0); // выключаем      }      if (++led_cycle >= 10)         led_cycle = 0;   }  }// количество семплов для усреднения значения датчиков напряжения#define SAMPLES 2500// используемое опорное напряжение TL431#define REFERENCEV 2.479// экспериментальные коэффициенты пересчета для делителей напряжения#define DIVIDER1 (12.3/2.13)#define DIVIDER2 (12.4/2.03)double realV1 = 0; // здесь итоговое зхначение измерения V0double realV2 = 0; // здесь итоговое зхначение измерения V1volatile int sampleCount = 0;volatile unsigned long tempVoltage1 = 0; // переменные для накопления суммыvolatile unsigned long tempVoltage2 = 0;volatile unsigned long sumVoltage1 = 0; // переменные для передачи суммы семплов в основной цикл volatile unsigned long sumVoltage2 = 0;void ADC_init() // ADC1,0{   // внешний ИОН 2,5В, 10 bit преобразование   ADMUX = (0 << REFS0) | (0 << REFS1) | (0 << ADLAR) |   (0 << MUX3) | (0 << MUX2) | (0 << MUX1) | (0 << MUX0); // ADC0   // включить, free running, с прерываниями   ADCSRA = (1 << ADEN) | (1 << ADFR) | (1 << ADIE) |   (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); // делитель 128      ADCSRA |= (1 << ADSC);             // Start ADC Conversion}ISR(ADC_vect) // должен накапливать измерения по 2500 семплам по каждому каналу{   if ((ADMUX & (1 << MUX0))) { // если работаем с ADC1      if (sampleCount++) // пропускаем первое измерение         tempVoltage1 += ADC;      if (sampleCount >= SAMPLES) {         sampleCount = 0;         sumVoltage1 = tempVoltage1;         tempVoltage2 = 0;         tempVoltage1 = 0;         ADMUX &= ~(1 << MUX0); // переключаем на ADC0      }           } else { // если работаем с ADC0      if (sampleCount++) // пропускаем первое измерение         tempVoltage2 += ADC;      if (sampleCount >= SAMPLES) {         sampleCount = 0;         sumVoltage2 = tempVoltage2;         tempVoltage2 = 0;         tempVoltage1 = 0;         ADMUX |= (1 << MUX0); // переключаем на ADC1      }     }   ADCSRA |=(1 << ADIF);              // Acknowledge the ADC Interrupt Flag}// валидность напряжения на входах блока АВРtypedef enum _ABP_U_STATES {   U_INVALID = 0, // напряжение не в норме   U_VALID // напряжение в норме} ABP_U_STATES;ABP_U_STATES u220n, u220g; // расчетная валидность напряжения на входах// допустимый диапазон напряжений питания в В на выходе выпрямителей// напряжения меняются не только от изменения сетевого напряжения, но и // плавают под нагрузкой (реле стартера), поэтому диапазон широкий#define MAX_V 14.0#define MIN_V 7.5void ports_init() {   // настройка порта светодиода индикации   PORTB &= ~(1 << PB0);   DDRB  |=  (1 << PB0); // output   PORTB &= ~(1 << PB0); // включаем      // настройка порта Контактора 1   PORTD |=  (1 << PD4);   DDRD  |=  (1 << PD4); // output   PORTD |=  (1 << PD4); // высокий уровень - выключаем   // настройка порта Контактора 2   PORTD |=  (1 << PD3);   DDRD  |=  (1 << PD3); // output   PORTD |=  (1 << PD3); // высокий уровень - выключаем   // настройка порта Реле запуска генератора   PORTD &= ~(1 << PD2);   DDRD  |=  (1 << PD2); // output   PORTD &= ~(1 << PD2); // низкий уровень - выключаем}void validate220() {   // логика валидации напряжения питания сети и генератора   realV1 = DIVIDER1 * ((sumVoltage1 * REFERENCEV) / 1024) / SAMPLES;   realV2 = DIVIDER2 * ((sumVoltage2 * REFERENCEV) / 1024) / SAMPLES;      if( realV1 > MAX_V || realV1 < MIN_V ) { // проверка напряжения от генератора      u220g = U_INVALID;      } else {      u220g = U_VALID;   }   if( realV2 > MAX_V || realV2 < MIN_V ) { // проверка напряжения от сети      u220n = U_INVALID;      } else {      u220n = U_VALID;   }}void validate_contactors() {      // проверка валидности включения контактора сети 220 в      // на выходе RLY_220N = RLY_ON или RLY_OFF      if( u220n == U_VALID && contactors[RLY_220N] == RLY_OFF      && abp_timers[TMR_220N_ON].state == TMR_OFF) { // есть напряжение, реле пока выкл, и таймер не вкл         abp_timer_stop( TMR_220N_OFF ); // остановка таймера выключения 220         abp_timer_start( TMR_220N_ON ); // запуск таймера включения 220      }      if( u220n == U_VALID && contactors[RLY_220N] == RLY_OFF      && abp_timers[TMR_220N_ON].state == TMR_ON && abp_timer_check(TMR_220N_ON)) { // есть напряжение, реле пока выкл, таймер сработал         abp_timer_stop( TMR_220N_OFF ); // остановка таймера выключения 220         abp_timer_stop( TMR_220N_ON ); // остановка таймера включения 220         contactors[RLY_220N] = RLY_ON; // ставим флаг включения 220      }      if( u220n == U_VALID && contactors[RLY_220N] == RLY_ON      && abp_timers[TMR_220N_OFF].state == TMR_ON) { // есть напряжение, реле вкл, и таймер выкл включен         abp_timer_stop( TMR_220N_OFF ); // остановка таймера выключения 220      }      if( u220n == U_INVALID && contactors[RLY_220N] == RLY_ON      && abp_timers[TMR_220N_OFF].state == TMR_OFF) { // нет напряжение, реле пока выкл, и таймер не вкл         abp_timer_stop( TMR_220N_ON ); // остановка таймера включения 220         abp_timer_start( TMR_220N_OFF ); // запуск таймера выключения 220      }      if( u220n == U_INVALID && contactors[RLY_220N] == RLY_ON      && abp_timers[TMR_220N_OFF].state == TMR_ON && abp_timer_check(TMR_220N_OFF)) { // нет напряжения, реле пока вкл, таймер сработал         abp_timer_stop( TMR_220N_OFF ); // остановка таймера выключения 220         abp_timer_stop( TMR_220N_ON ); // остановка таймера включения 220         contactors[RLY_220N] = RLY_OFF; // ставим флаг выключения 220      }      if( u220n == U_INVALID && contactors[RLY_220N] == RLY_OFF      && abp_timers[TMR_220N_ON].state == TMR_ON) { // нет напряжения, реле выкл, и таймер вкл включен         abp_timer_stop( TMR_220N_ON ); // остановка таймера включения 220      }            // проверка валидности включения контактора генератора      // на выходе RLY_220G = RLY_ON или RLY_OFF      if( u220g == U_VALID && contactors[RLY_220G] == RLY_OFF      && abp_timers[TMR_220G_ON].state == TMR_OFF) { // есть напряжение, реле пока выкл, и таймер не вкл         abp_timer_stop( TMR_220G_OFF ); // остановка таймера выключения ген         abp_timer_start( TMR_220G_ON ); // запуск таймера включения ген      }      if( u220g == U_VALID && contactors[RLY_220G] == RLY_OFF      && abp_timers[TMR_220G_ON].state == TMR_ON && abp_timer_check(TMR_220G_ON)) { // есть напряжение, реле пока выкл, таймер сработал         abp_timer_stop( TMR_220G_OFF ); // остановка таймера выключения ген         abp_timer_stop( TMR_220G_ON ); // остановка таймера включения ген         contactors[RLY_220G] = RLY_ON; // ставим флаг включения ген      }      if( u220g == U_VALID && contactors[RLY_220G] == RLY_ON      && abp_timers[TMR_220G_OFF].state == TMR_ON) { // есть напряжение, реле вкл, и таймер выкл включен         abp_timer_stop( TMR_220G_OFF ); // остановка таймера выключения ген      }      if( u220g == U_INVALID && contactors[RLY_220G] == RLY_ON      && abp_timers[TMR_220G_OFF].state == TMR_OFF) { // нет напряжение, реле пока выкл, и таймер не вкл         abp_timer_stop( TMR_220G_ON ); // остановка таймера включения ген         abp_timer_start( TMR_220G_OFF ); // запуск таймера выключения ген      }      if( u220g == U_INVALID && contactors[RLY_220G] == RLY_ON      && abp_timers[TMR_220G_OFF].state == TMR_ON && abp_timer_check(TMR_220G_OFF)) { // нет напряжения, реле пока вкл, таймер сработал         abp_timer_stop( TMR_220G_OFF ); // остановка таймера выключения ген         abp_timer_stop( TMR_220G_ON ); // остановка таймера включения ген         contactors[RLY_220G] = RLY_OFF; // ставим флаг выключения ген      }      if( u220g == U_INVALID && contactors[RLY_220G] == RLY_OFF      && abp_timers[TMR_220G_ON].state == TMR_ON) { // нет напряжения, реле выкл, и таймер вкл включен         abp_timer_stop( TMR_220G_ON ); // остановка таймера включения ген      }            // запуск и останов генератора с таймером      if( contactors[RLY_220N] == RLY_OFF && contactors[RLY_GEN] == RLY_OFF) { // нет сети, стартуем ген         abp_timer_stop( TMR_GEN_OFF ); // остановка таймера останова генератора         contactors[RLY_GEN] = RLY_ON; // ставим флаг запуска генератора      }      if( contactors[RLY_220N] == RLY_ON && abp_timers[TMR_GEN_OFF].state == TMR_OFF       && u220g == U_VALID) { // есть 220 в сети и есть от генератора         abp_timer_start( TMR_GEN_OFF ); // запуск таймера останова генератора      }      if( contactors[RLY_220N] == RLY_ON && abp_timers[TMR_GEN_OFF].state == TMR_OFF      && u220g == U_INVALID) { // есть 220 в сети и нет от генератора         abp_timer_stop( TMR_GEN_OFF ); // остановка таймера останова генератора         contactors[RLY_GEN] = RLY_OFF; // ставим флаг останова генератора      }      if( contactors[RLY_220N] == RLY_ON && abp_timers[TMR_GEN_OFF].state == TMR_ON      &&  abp_timer_check(TMR_GEN_OFF) ) { // есть 220 в сети и истекло время таймера         abp_timer_stop( TMR_GEN_OFF ); // остановка таймера останова генератора         contactors[RLY_GEN] = RLY_OFF; // ставим флаг останова генератора      }}void switch_contactors() {   // логика переключения контакторов   if( contactors[RLY_220N] == RLY_ON ) {      PORTD |=  (1 << PD4); // высокий уровень - выключаем контактор 1 ген       _delay_ms(50); //даем возможность отключиться контактору генератора        PORTD &= ~(1 << PD3); // низкий уровень - включаем контактор 2 220      led_State = ABP_2RELAY;   } else {      if( contactors[RLY_220G] == RLY_ON ) {         PORTD |=  (1 << PD3); // высокий уровень - выключаем контактор 2 220         PORTD &= ~(1 << PD4); // низкий уровень - включаем контактор 1 ген         led_State = ABP_1RELAY;      } else {         PORTD |=  (1 << PD3); // высокий уровень - выключаем контактор 2 220         PORTD |=  (1 << PD4); // высокий уровень - выключаем контактор 1 ген         led_State = ABP_UNDEF;      }   }   if( contactors[RLY_GEN] != RLY_ON ) { // v2 реле работает на время работы генератора - не дает напряжения на стоппер      PORTD &= ~(1 << PD2); // низкий уровень - выключаем реле - возврат напряги на стоппер - стоп гены   } else {      PORTD |= (1 << PD2); // высокий уровень - включаем реле - держим режим работы гены   }}int main(void){   ports_init(); // настройка портов светодиода, реле и контакторов      uart_init(); //настройка uart   stdout =  &uart_stream;      counter2_init(); // настройка таймера мигания светодиода   ADC_init(); // настройка АЦП измерения 220   abp_timers_init(); // настройка таймеров задержек включения и выключения   timer1_init(); // настройка секундного таймера на аппаратном счетчике 1    set_sleep_mode(SLEEP_MODE_IDLE);  // разрешаем сон в режиме IDLE    sleep_enable();    wdt_enable(WDTO_2S); // Сторожевой таймер настроен на таймаут в 2 секунды   sei(); // запускаем работу прерываний      _delay_ms(1000); // ждем первых результатов ЦАП, отличных от 0   wdt_reset();   printf( "Start flag after reset = %u\r\n", mcusr_mirror );   abp_timer_start(TMR_PRINT);       while(1)    {      wdt_reset(); // сбрасываем сторожевой таймер            validate220(); // проверка качества 220 от сети и генератора         validate_contactors(); // валидация возможности включения контакторов с таймерами            switch_contactors(); // переключение контакторов по схеме            // отладочная печать раз в 2 секунды      if (abp_timer_check(TMR_PRINT)) {         printf( "V220 = %4.2f VG = %4.2f\r\n", realV2, realV1 );         printf( "valid = %u %u \r\n", u220n, u220g );         printf( "rly = %u %u %u\r\n", contactors[RLY_220N], contactors[RLY_220G], contactors[RLY_GEN] );         abp_timer_start(TMR_PRINT);      }              sleep_cpu(); // заснуть до следующего пррывания по таймерам    }}



Программа разработана с помощью бесплатного AVR Studio и использует стандартные библиотеки AVR.

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

Для контроля зависаний предусмотрен сторожевой таймер.

Все циклы измерений сделаны на прерываниях и с использованием аппаратных таймеров. Счетчик секунд сделан на таймере 1. По прерыванию таймера 1 обновляются программные таймеры, отвечающие за задержки включения и отключения контакторов и реле генератора.
Второй таймер используется для создания эффекта мигания светодиода статуса. Предусмотрено три паттерна мигания. Значения из паттерна мигания берутся в прерывании таймера 2. По миганию можно судить о состоянии контроллера.

Два АЦП также работают по таймерам и усредняют по 2500 сэмплов измерений напряжения. Для перевода измерений в реальные вольты предусмотрены калибровочные константы. Их значения надо исправить в ходе настройки АВР.

Кроме того, есть еще ряд констант, которые нужно определить в ходе наладки.
abp_timers[TMR_220N_ON].set_secs = 10; // ожидание после включения сетевого напряженияabp_timers[TMR_220G_ON].set_secs = 60; // ожидание для переключения на генератор для прогрева генератораabp_timers[TMR_220N_OFF].set_secs = 5; // ожидание после пропадания сетевого напряженияabp_timers[TMR_220G_OFF].set_secs = 5; // ожидание после пропадания напряжения генератораabp_timers[TMR_GEN_OFF].set_secs = 60; // ожидание для охлаждения генератора перед остановом


Реле останова генератора при работе от генератора держится включенным, блокируя поступление напряжения на цепь останова генератора. После завершения работы таймера работы генератора на холостом ходу, реле выключается и на цепь останова генератора через это реле начинает поступать ток. На самом генераторе стоит специальный блок, который после появления напряжения с некоторой задержкой замыкает цепь зажигания на массу, что приводит к останову генератора. Этот же блок содержит цепь подзаряда аккумулятора генератора. Если кому интересны детали, напишите в комментах, я сделаю отдельный пост об этом блоке. В нём нет кода, всё аппаратно.

Если кто-то надумает повторить АВР, то стоит подкорректировать значения настроек. Готовую прошивку не публикую, так как программу всё равно надо править в ходе настройки АВР.

Надо сказать, что мой АВР работает уже 4 года без проблем, так что схема можно считать проверенная как и код.
Подробнее..

Ещё одна фитолампа на Arduino

14.12.2020 08:18:37 | Автор: admin

Пришла зима, короткий день, домашней пальме мало света. Нужно организовать подсветку. Готовую лампу покупать как-то неловко, да и надо ж чем-то заняться долгими зимними вечерами. Поехали ;)

  1. Купил на Али мощные светодиоды с питанием в 220В. Вот такие:

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

2. Регулятор оборотов кулера.

Пусть это будет step-down конвертор. Хороший КПД, меньше помех в цепях питания.

Ключевой транзистор и диод выпаял из покойной материнской платы, дроссель оттуда же. Оригинальную обмотку из толстого провода срезал, намотал свою проводом 0.3мм - сколько поместилось. Транзистор Q1 - какой-то маломощный npn из закромов. Подаем на вход импульсы разной скважности - на выходе получаем напряжение от 0 до 12В. Удобно, но нужен генератор импульсов с достаточно высокой частотой (десятки кГц и выше).

3 . Как сделать генератор импульсов со скважностью от 0 до 100%? Причем с возможностью автоматической регулировки? Проще всего взять микроконтроллер, который умеет в PWM. Я склоняюсь к AtMega8 - простой, отлично документированный и дешевый. Но использовать микроконтроллер только для вращения кулера - это из пушки по воробьям. Можно на этом же железе решить и дополнительные задачи.

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

  • подсветка работает 8 часов и отключается по таймеру или кнопкой;

  • включается подсветка по кнопке или с наступлением рассвета;

  • рассвет детектируется светодиодом. Для этого функционала достаточно фоторезистора и АЦП.

Кулер желательно крутить помедленнее, но не допуская ни полной остановки, ни перегрева светодиода. Для детектирования скорости вращения у кулера есть встроенный датчик, для измерения температуры возьмем терморезистор. Мне попался NTC MF52A1, с сопротивлением при 25С 10кОм. В даташите есть таблица с зависимостью сопротивления от температуры; так что можно сделать простой и очень дешевый измеритель температуры с приемлемой точностью. Итого, от контроллера нужно:

  • АЦП - 2 входа

  • PWM - 1 выход

  • прерывание датчика вращения кулера - 1 вход

  • кнопка вкл/выкл - 1 вход

  • управление подсветкой - 1 выход

  • UART - удобно для диагностики в железе

4. Конфигурирование микроконтроллера

Fuse bits.

Чтобы не получить окирпиченный МК, проще всего воспользоваться готовым калькулятором fuse битов. Например, таким. Главное, что нужно - выбрать синхронизацию от внешнего кварца. Остальные биты можно не трогать ;) У меня получилось

lfuse

0xFF

hfuse

0xD9

Таймеры.

Во первых, PWM. Для этого пригодны 2 таймера: 16-битный Timer1 и 8-битный Timer2. Timer1 будет считать системное время (об этом позже), Timer2 (с 256 уровнями PWM) - отлично подходит для регулятора напряжения. При тактовой частоте 16 МГц максимальная частота PWM будет

16000000/256=62500 Гц

Длительность самого короткого импулься

1/16МГц=62 нС

Думаю, это приемлемо. В даташите на ключевой транзистор есть Rise Time 33 nS. В окончательном варианте устройства можно использовать встроенный генератор на 8МГц - это тоже сработает.

Настройка Timer2

См https://sites.google.com/site/qeewiki/books/avr-guide/pwm-atmega8

Нам нужны следующие функции:

  • No Prescaling - чтобы получить частоту повыше

  • Fast PWM

  • Inverted mode (LOW at bottom, HIGH on Match) - преобразователь напряжения с инвертором; когда напряжение на выходе прямо зависит от значения счетчика - это удобно.

// PWM// Выход жестко приколочен к пину PB3#define PWM_FAN_PIN _BV(PB3)DDRB |= PWM_FAN_PIN;OCR2 = 255;    // set PWM for 100% duty cycle    TCCR2 |= (1 << COM21);    TCCR2 |= (1 << COM20);    // set inverting mode    TCCR2 |= (1 << WGM21) | (1 << WGM20);    // set fast PWM Mode    TCCR2 |= (1 << CS20);    // set prescaler to 0 and starts PWM

Измерение времени.

В Arduino принято измерять время в миллисекундах - с такой частотой тикает таймер. Такая точность для наших целей не нужна, да и миллисекундный счетчик переполняется через 49 суток. Мы будем считать интервалы по 100 мС, этот счетчик переполнится очень не скоро.

16000000/256/6250 = 10 Гц

Чтобы получить такие временные интервалы с кварцем 16 МГц, нужен предделитель 256 и счетчик до 6250. Прекрасно.

Настройка Timer1

См https://sites.google.com/site/qeewiki/books/avr-guide/timer-on-the-atmega8

// 16000000/256/6250 = 10 hz// timer 1, CTC, 256 prescalerTCCR1B |= (1 << CS12) | (1 << WGM12);TIMSK |= (1 << OCF1A); // enable interrupt OCR1A = 6250;

Код счетчика времени очень простой:

uint32_t uptime_x_0_1s = 0;ISR (TIMER1_COMPA_vect){    // action to be done every 0.1 sec    uptime_x_0_1s++;}

Переполнится счетчик через

2^{32}/3600/24/365/10 = 13 лет

Больше чем достаточно ;)

Измерение частоты вращения кулера

В кулере есть встроенный датчик, который замыкается на землю 1 раз (или 2 раза - разные источники пишут разное) за оборот. Для измерения частоты вращения подключим датчик к пину, умеющему генерировать прерывания, сконфигурируем его как вход с внутренним pull-up резистором.

Прерывания от кулера

См http://www.atmega8.ru/wiki/view/doc.9.html

// FAN interrupt  #define TAHO_FAN_PIN _BV(PD3)SREG |= (1<<7);PORTD |= TAHO_FAN_PIN; // pull-upGIMSK |= _BV(INT1);MCUCR |= _BV(ISC11); // Прерывание вызывается по возрастающему фронту сигнала на входе INT

Счетчик прерываний совсем простой:

uint16_t ts_fan_interrupt;ISR (INT1_vect){    /* interrupt code here */    // убираем дребезг - на совсем малых оборотах     // он почему-то наблюдался    if (TCNT1 - ts_fan_interrupt <10) return;    ts_fan_interrupt = TCNT1;    fan_cnt ++;}

Функция do_fan_rps срабатывает не чаще чем 1 раз в секунду и подсчитывает число прерываний за 1 секунду.

unsigned int rps = 0;void do_fan_rps(){static uint32_t rps_ts;unsigned int fan_cnt_;int dt = uptime_x_0_1s - rps_ts;if ( dt < 10) return;cli();fan_cnt_ = fan_cnt;fan_cnt = 0;sei();rps = fan_cnt_ * 10 /dt ;rps_ts = uptime_x_0_1s;}

АЦП будем использовать для измерения температуры и освещенности. АЦП в AtMega 10-разрядный, и он может измерять 1024 уровня от 0 до опорного напряжения Vref. 2 младших разряда можно отбросить, и работать с результатом в uint8_t - это экономит немного памяти. Имеется также встроенный источник опорного напряжения 2.56 В.

Обработка АЦП

См https://narodstream.ru/avr-urok-22-izuchaem-acp-chast-1/

 ADCSRA |= (1<<ADEN);  // Разрешение использования АЦПADCSRA |=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//Делитель 128 = 125 кГц ADMUX  |= (1<<REFS1)|(1<<REFS0)|(1<<ADLAR); //Внутренний Источник ОН 2,56в, adc0, use high 8b (ADCH)

Чтение из АЦП процесс медленный, лучше им не злоупотреблять

uint8_t ADC_read(uint8_t channel){ADMUX &= ~7;ADMUX |= (channel & 7);ADCSRA |= (1<<ADSC); //Начинаем преобразованиеwhile((ADCSRA & (1<<ADSC))); //проверим закончилось ли аналого-цифровое преобразованиеreturn ADCH; }

Измеритель температуры представляет собой резисторный делитель напряжения. Зависимость напряжения от температуры, скорее всего, можно представить в виде формулы. Но проще составить таблицу соответствия температуры и напряжения с точностью 1 С - размер ее не будет слишком велик. Я взял datasheet на терморезистор, librecalc, закон Ома и получил что-то такое:

uint8_t adc_measures_base24[] = { 255, 250, 245, 239, 234, 228, 223, 218, 213, 207, 202, 197, 192, 188, 183, 178, 173, 169, 164, 160, 156, 151, 147, 143, 139, 135, 132, 128, 124, 121, 117, 114, 111, 108, 105, 102, 99, 96, 93, 90, 88, 85, 83, 80, 78, 76, 73, 71, 69, 67, 65, 63, 61, 60, 58, 56, 55, 53, 52, 50, 49, 47, 46, 45, 43, 42, 41, 40, 39, 38, 36, 35, 34, 33, 33, 32, 31, 30, 29, 28, 28, 27, 26, 25, 25, 24, 23, 23, 22, 22, 21, 20, 20, 19, 19, 18, 18, 18, 17, 17, 16, 16};

Температуру ниже 24С и выше 120С измерять не имеет смысла.

UART. Как оказалость, здесь тоже имеются свои тонкости. Передача данных через UART требует высокой точности временных интервалов; поэтому использовать UART без внешнего кварца не стоит. Также, не любую частоту передачи можно получить с помощью встроенных делителей частоты. Очень удобно, что есть готовая таблица, из которой можно найти скорость с минимальной погрешностью. У меня увереннно заработала передача на 38400bps.

Настройка UART
#define BAUD 38400#define MY_UBRR (F_CPU/16/BAUD - 1)// UARTUBRRH = (unsigned char) ((MY_UBRR) >> 8);UBRRL = (unsigned char) (MY_UBRR);UCSRB = (1<<TXEN);UCSRC = (1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);  

PID-регулятор

Что ж за самоделка без PID-регулятора? ;) Здесь из 2. Нужно поддерживать температуру около 40С, и обороты около 30 rps. 2 датчика (цифровой и аналоговый), и одно управляющее воздействие - скважность PWM.

Код регулятора
#define GOAL_T 40#define GOAL_RPS 30// K_T = 1/2 #define K_T_A 1#define K_T_B 2// K_RPS = 1/5#define K_RPS_A 1#define K_RPS_B 5 void do_pid(){static uint32_t pid_ts;if (uptime_x_0_1s-pid_ts < 10) return; // не чаще 1 раз/секpid_ts = uptime_x_0_1s;int pwm_val = OCR2;int dt = t1 - GOAL_T;int d_rps = GOAL_RPS - rps;int d_pwm_t = (dt * K_T_A ) / K_T_B;if (d_pwm_t < -5) {d_pwm_t = -5;};int d_pwm_rps = (d_rps * K_RPS_A) / K_RPS_B;int pwm_val_t = pwm_val + d_pwm_t;int pwm_val_rps = pwm_val + d_pwm_rps;int d_pwm;if (pwm_val_t > pwm_val_rps){d_pwm = d_pwm_t;} else {d_pwm = d_pwm_rps;};pwm_val += d_pwm;if (pwm_val > 255) pwm_val = 255;if (pwm_val <0 ) pwm_val = 0;OCR2 = pwm_val;}

Коеффициенты для обоих регуляторов заданы в виде натуральной дроби, чтобы не использовать float вычисления.

Про обработку кнопки с подавлением дребезга и прочее мигание светодиодом, пожалуй, писать не нужно ;)

В результате получилась вот такая конструкция на макетной плате.

Шакальное фото

Выводы.

Поставленная задача решается: светодиод светится, кулер крутится, температура 40С держится с достаточно низкими оборотами кулера; можно брать и более мощные светодиоды.

Код прошивки и схема устройства выложены на github.

TODO:

  • Научиться разводить платы в KiCad и сделать более изящную конструкцию.

  • Избавиться от кварца; точности встроенного генератора должно хватить для измерения интервала 8ч.

  • Вместо step-down конвертора использовать источник питания 5В и step-up конвертор.

З: Пожалуй, нужно упомянуть о использованном софте. При работе над устройством ни один правообладатель не пострадал.

ОС - Ubuntu 18

Компилятор - avr-gcc

IDE - geany

Программатор - Arduino Uno с прошивкой ArduinoISP

Схема электрическая принципиальная рисовалась в KiCad

Прототип паялся на макетной плате.

Подробнее..

Колодец с мёдом

09.02.2021 14:04:30 | Автор: admin
КДПВКДПВ

Захотелось мне сделать кухонную безделушку / украшение. Конечно же, с электроникой и интерактивностью.

Идея - дозатор мёда такой, чтобы процесс подачи выглядел как подъём ведёрка из колодца девушкой в русском национальном оформлении.

Устройство

Прямоугольный фанерный корпус, внутри которого расположена банка с мёдом, привод и управляющая электроника.

Сверху на корпусе находится венец колодца, столбы с воротом, фигурка девушки в русском народном костюме и с лампой.

Всё делалось с минимальными затратами.

Реализация

1. Корпус нарезанная на лазере фанера, верх зелёный фетр. Деревяшки, образующие столбы и венец колодца - из ближайшего леса.

2. Крышка. Чтобы мёд не стоял открытым и не заветривался, надо сделать крышку. Крышка должна незаметно открываться, позволяя достать мёд. В металлической крышке банки сделал квадратное отверстие, закрываемое откидными крышечками из пластика с уплотнителем по краю, чтобы не было щелей. Тяги от крышечек прикрепил с сервомашинке. Полный ход крышек 90. Венцом (брёвнами) колодца маскируются открытые крышки.

Сервомашинка с тягами открытия крышекСервомашинка с тягами открытия крышек

3. Механизм вращения ворота колодца. Так как кукла чисто декоративная, вращать ворот она не может. После проработки нескольких вариантов кинематики сделал так: деревянный столбик ровно расколот пополам, в нём выбрана сердцевина, на оси ворота шкив. Тонкий ПВХ ремешок внутри столбика от верхнего шкива до редуктора с двигателем под полом. Непросто было уместить шкив диаметром 11 мм в 14-мм столбик.

4. Контроль вращения. Так как ремень однозначно будет проскальзывать (не нашёл такого мелкого зубчатого, вряд ли есть шириной до 1,5 мм) нужен ещё и контроль оборотов ворота, чтобы ведро не намоталось. В тот же столбик уместил и оптопару ИК светодиод + фототранзистор. Пришлось делать на отражении, для метода пересечения луча - просто нет места.

Схема датчика оборотов внутри столбаСхема датчика оборотов внутри столба

5. Ведёрко. Тут засада нет ничего, что само зачёрпывает мёд быстро и надёжно. Мини-ведёрко не переворачивается, перфорированный диск из нержавейки погружается слишком медленно, традиционная булава не тонет. Относительно нормально заработала ловушка-венчик из нержавеющей проволоки 1мм (двойная плоская спираль), она быстро погружается и позволяет вытащить пол-чайной ложки без риска проскальзывания ремешка.

Вот такое "ведро"Вот такое "ведро"

6. Дополнительно сделал функцию ночника, в свободную руку фигурки добавил светильник. Для имитации матового светильника-лампады обрезал кусок стержня для клеевого пистолета, создав объёмный тетраэдр, вставил в рамку из проволочки и вплавил внутрь оранжевый светодиод smd0603.

7. Кнопка запуска процесса. Любые дистанционные способы (на поднесение ложки или др.) портили бы внешний вид, или не подходили для пластиковой ложки / простой демонстрации. Реализовал микрокнопкой, замаскировав её под половинкой бревна. Получилось незаметно и гармонично.

КнопкаКнопка

8. Женская фигурка вырезана из пенопласта (покупная кукла смотрелась бы лучше, но нужен полностью свободный ход рук), нижняя рубаха, платок и сарафансшиты из кусочков льняных тканей, и немного стоит колом, ну не водится синтетики у реконструктора)). Если буду делать ещё раз, куплю куклу, минискульптор из меня не очень.

Янедкс по запросу "девушка пропорции фигуры" что только не выдаёт))Янедкс по запросу "девушка пропорции фигуры" что только не выдаёт))

Электронная часть

Микроконтроллер Atmega8A. Плата разведена, ЛУТом изготовлена без проблем, деталей мало.

ЛУТ
Плата до отмывки флюсаПлата до отмывки флюса

Драйвер двигателя микросхема TA7291P, мостовой драйвер с питанием силовой части от 5 В и невысокими потерями.

Двигатель от какого-то CD-плеера, бесшумный.

Датчик оборотов из самодельной оптопары - наверху, внутри столба.

Сервомашинка. Пришлось добавлять реостат по питанию - жрёт много тока.

Схема для измерения тока двигателя, ток измеряется через шунт и усилитель на транзисторе. Используется для оценки пустой/полной банки и для контроля заклинивания двигателя.

Белая подсветка под банкой и оранжевый светильник в руке.

На корпус выведен фоторезистор для контроля внешней освещённости.

Питание от USB сзади корпуса. Эта версия без батарей, но можно их поставить.

Вид снизу в процессе пусконаладкиВид снизу в процессе пусконаладки

Алгоритмы

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

свет от сокровищ))
Мория Винни-пухаМория Винни-пуха

Сразу после полного открытия крышек вал делает 10 оборотов, поднимая венчик, затем пауза для слива мёда в ложку. Затем отпускание на 0,5 оборота это такой сигнал, чтобы убрать ложку, затем полное опускание венчика в банку (9,5 оборотов). Крышки закрываются.

Если в процессе работы нажать ещё раз на кнопку, то после опускания венчика крышки не закроются, а после небольшой паузы колодец снова поднимет порцию мёда.

Светильник работает при процессе и в темноте.

Защита по току двигателя не даёт испортить механику при заклинивании, отключая двигатель.

Выводы

Окончательный видОкончательный вид

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

Дополнительный декоративный свет мне нравится, создаёт мини-подсветку в кухне.

Маскировка электромеханики как и задумывалось, ничего лишнего не видно.

Из замеченных проблем: редкие проскальзывания ремешка при больших нагрузках, шумный редуктор, значительное трение оси ворота в столбах - там надо бы ставить подшипники, но места нет. Можно попробовать фторопластовые втулки.

Моделька подарена, новых фоток сделать не могу, есть видео.

Спасибо за внимание!

Подробнее..

Ненормативная схемотехника ATmega8 кто сказал, что выше головы не прыгнешь?

30.04.2021 12:16:20 | Автор: admin
Вот уж несколько лет, как я увлёкся микроконтроллерами, а именно семейством AVR. Ещё на этапе освоения Ардуино (в этот момент часть аудитории поплевались и ушли читать другие статьи) я пытался выдавить из неё больше, чем задумано. Меня всегда больше интересовали нестандартные решения обычных задач. Сейчас я знаю об AVR намного больше, чем ещё пару лет назад, и всё больше убеждаюсь, что знаю очень мало.

С чего начинает среднестатистический начинающий электронщик? Правильно, с часов! Как только научился пользоваться голыми контроллерами, а не платами Ардуино, захотел сделать часы на Атмеге, голой Атмеге без кучи ключей и буферных микросхем. И обязательно со статической индикацией, а не с динамической (ну не люблю я её). Собственно, статическая индикация программно гораздо проще, а ведь мы простых путей не ищем. Но зато возникают другие проблемы, которые несколько раз меня останавливали в самом начале пути. Понятно, что если взять Атмегу пожирней, задействовать в ней половину ножек и полпроцента памяти, задача упрощается донельзя, собственно, такие настольные часы на ATmega128 работают у меня на столе пару лет.

Но это не наш метод. Я с самого начала хотел именно ATmega8, как самую доступную и самую дешёвую (в том числе в дип-корпусе). Всё то же самое можно и на ATmega48, но её попробуй ещё найди, разве что у Вас в ящике стола валяется их много с незапамятных времён.
Посмотрим на картинку, известную всем, кто недавно интересуется AVR.

Глядя на неё, легко посчитать, что мы можем задействовать на часовой индикатор 20 ножек, ещё на двух у нас будет кварц (куда от него денешься, внутреннее тактирование не прокатит, нам ведь от часов нужна точность какая-никакая). Ну и сброс. На четыре семисегментных индикатора нужно 28 ног, ну даже 27, ведь десятки часов можно отображать цифрами 1 и 2, а ноль не отображать. Ещё когда идея только зарождалась, я это количество сократил до 22 ножек, ведь для отображения десятков часов можно обойтись цифрой 1, и оба её сегмента зажигать от одной ноги контроллера. Но как ни крути, пары ног мне всё же не хватало, даже при том что я давно знал о возможности использования как ввода-вывода ножки Reset, но так ещё ни разу и не попробовал (если не считать ATtiny13, её не так жалко было), ведь параллельного программатора у меня нет, а любая отладка это минимум несколько итераций прошивки, вряд ли всё идеально выйдет с первого раза. Так и лежал этот замысел в закромах мозга, и ждал своего времени, пока как-то мне не пришла в голову ещё одна интересная идея.
Посмотрел я однажды на светодиодную ленту (120 светодиодов на метр) и понял, что можно легко и просто сделать электронные часы любого размера с её использованием. Просто нарезаем сегменты желаемой длины и наклеиваем их на подходящее основание, для больших размеров сегмент может складываться из двух и более лент по ширине. В моём случае отрезаем сегменты по шесть светиков, таким образом длина сегмента составляет 50 мм, высота цифры 100, для моей задумки офисных часов вполне достаточно.

Но вот незадача, напряжение питания ленты 12 вольт, а AVR хочет не больше 5 вольт (люди утверждают, что и 8 вольт выдерживают, но я проверять пока не буду, да и не поможет). То есть нужно ставить 22 ключа для включения всех сегментов. Если применить драйвер ULN2003, хватит (почти) трёх штук, это копейки, но, как я писал выше, это не наш метод, хотя этот вариант можно приберечь для часов побольше. Ну никак это не вписывается в концепцию голая Атмега.
И вот тут-то начинается самое интересное. Напряжение питания ленты 12 вольт, первые признаки жизни белые светодиоды (три штуки последовательно) начинают подавать при более чем 7.5 вольт, и, что очень важно, до этого напряжения ток через ленту практически равен нулю (можете проверить). На ножке контроллера, работающей в режиме выхода, может быть уровень 0 вольт или 5 вольт (третий вариант рассмотрим позже), и если подключить сегмент анодом к +12 вольт, а катодом к выходу контроллера, напряжение на сегменте будет равняться соответственно 12 вольт (светит) или 7 вольт (не светит). Даже если бы через погасший сегмент протекал мизерный ток, он ушёл бы к плюсу питания контроллера через защитный диод, которые в AVR гораздо более выносливые, чем нас пугали в обучающих статьях. Третий вариант ножка в режиме входа, напряжение на сегменте 7 вольт благодаря защитному диоду, стало быть сегмент не светит. Конечно, как только я обдумал всё это в теории, сразу же проверил на практике, залив в контроллер простой блинк для одной из ног.

А так как сейчас в силу некоторых обстоятельств мои возможности для экспериментов слегка ограничены, напряжение питания для сегмента случайно оказалось более 18 вольт, и сегмент гас не полностью, так что я успел разочаровано подумать, что подпалил выход. Когда уменьшил напряжение (на тот момент даже нечем было померить), всё стало на свои места, так что, благодаря нелепой случайности, теперь я точно знаю, что для схемы не смертельно небольшое повышение напряжения выше 12 вольт. С напряжением определились, а что с током? На моей ленте (и скорей всего на вашей тоже) стоят резисторы по 150 ом, ток через сегмент не более 20 мА, только сегмент в данном случае это три светика, а каждый сегмент моих часов состоят из двух сегментов ленты, так что 40 мА. Вроде даже вписываемся в даташит, но на 20 выходов это уже 800 мА, что намного выше дозволенных 200 мА на корпус. Ток через ленту очень зависит от напряжения на ней, и нелинейно падает даже при небольшом снижении, что является большим минусом при обычном использовании ленты, и плюсом в данном случае, ведь реальное напряжение на сегментах равно 12 вольт минус падение на ключе (около 0.6 вольт), а ещё при желании можно снизить напряжение питания, понизив тем самым яркость часов. Так что страшные 800 мА несложно снизить раза в два. В любом случае, я был уверен, что это не станет проблемой, да и приобретённый опыт ценнее, чем возможность потери одной Атмеги. Вот так просто ATmega8 коммутирует индикатор с напряжением в два с лишним раза выше, чем её собственное напряжение питания. Именно это я имел ввиду в заголовке статьи. Хотя способ совсем не нов, по такому же принципу работает советская микросхема К155ИД1 высоковольтный дешифратор управления газоразрядными индикаторами, где сравнительно низковольтные выходы (до 60 вольт) коммутировали индикаторы с напряжением зажигания 150 вольт и выше.

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

Аноды к +5 вольт через один на всех резистор, для отладки прошивки этого за глаза. Первоначально я остановился на варианте использования в качестве двух недостающих ног пинов подключения кварца (итого 22 выхода), а тактировать контроллер от внешнего генератора минутных импульсов через (внимание!)

вход сброса. Это ещё один занимательный лайфхак, который описан здесь Как сохранять переменные arduino, при reset
Можете попробовать залить в Ардуино простой код и посмотреть результат работы в Мониторе порта, периодически сбрасывая её кнопкой или с клавиатуры:
пробник
#define NO_INIT __attribute__((section(".noinit")))void setup() {    static unsigned NO_INIT nonInitCounter;    nonInitCounter=nonInitCounter+2048;    Serial.begin(9600);    Serial.print("Setup counter: ");    Serial.println(nonInitCounter);}void loop(){}

Не буду приводить свой код, дабы не подвергаться критике лишний раз (я не самый лучший
программист), но если в двух словах, можно создать глобальную переменную, которая сохраняется при перезагрузке контроллера. Раз в минуту под воздействием внешнего импульса сброса Атмега перезагружается, и начинает заново отрабатывать прошивку: считывает переменную, преобразует её в показания часов и минут, выводит на индикатор, увеличивает переменную на одну минуту. Дальше опять сброс, и всё по-новой. Пришлось порыться в интернете и опробовать два вида программного сброса, ведь внешнего генератора у меня пока не было, а жмакать сброс всё время вручную не сильно удобно.
пример функции
void softReset() {  //программный сброс //http://kazus.ru/forums/showthread.php?t=13540&page=3#  asm volatile ("rjmp 0x0000");//Sketch uses 2412 bytes  //программный сброс //https://www.cyberforum.ru/post11307314.html  //((void(*)(void))0)();//Sketch uses 2416 bytes}

Что интересно, эта переменная может сохраняться даже после отключения питания (не всегда). И это не EEPROM, если кто подумал. По мере отладки я пришёл к использованию нескольких таких переменных.
Кто-то скажет: какой внешний генератор, а как же концепция голой Атмеги? На момент опытов я был уверен, что в качестве генератора выступит некая козявка размером с рисинку и ценой в пару копеек. Но когда всё заработало в теории и пришло время с козявкой определиться, меня ждал облом. Единственное, что меня могло бы устроить, это старая добрая К176ИЕ12, кто бы мог подумать! На дворе двадцать первый век, а в природе нет простого доступного аппаратного генератора минутных импульсов.
Не беда, подумал я. Можно и раз в секунду. Немного переписал код, проверил работу, отлично. Но опять же, для внешнего генератора я нашёл только DS1307 (и её аналоги). Да, стоит копейки, для обвязки достаточно кварца (и ещё несколько необязательных элементов). Вот только чтобы она генерировала секундные импульсы, ей нужно подать команду по шине I2C. Чем подать? И какую? (Теперь я уже знаю, но на тот момент не было с чем пробовать). Короче, не подходит. Ладно, последняя надежда. Некоторые западные часы 90-х тактировались частотой сети 50 гц. Возможно, сейчас у нас частота стабильней, чем была 30 лет назад. Попробовал сымитировать работает. Если бы воплотил в жизнь, было бы оригинально контроллер, перезагружающийся 100 раз в секунду. Но я пока отмёл этот вариант и стал искать другие.
Итак, я мог использовать 22 выхода (что мне достаточно) и пин сброса в качестве входа. Но не срослось Вернёмся к варианту с кварцем. Минус две ноги на кварц получаем 20, даже если ножку сброса сделать портом ввода-вывода, получим 21, всё равно не хватает одного, хоть ты тресни! Но недавно я нашёл способ, о котором задумывался ещё два года назад, что-то не получилось тогда. У нас есть пин AREF для опорного напряжения, относительно которого происходят аналоговые измерения. И на этот пин мы можем программно подать напряжение питания в качестве опорного или встроенное опорное напряжение 2.56 (для ATmega8). Проверил, измерил, действительно можно получить на ножке AREF напряжение 0 вольт, 2.56 вольт, или 5 вольт.
превращаем AREF в ещё один выход
// превращаем AREF в ещё один выходvoid setup() {}void loop() {  ADMUX = 7 + 64; // уровень 1 (5 вольт) // 7-номер аналогового входа//почему 7 аналоговый вход? А потому что физически у Атмеги входы от 0 до 5,//и назначение аналоговым входом входа 7 не помешает работе остальных в качестве выходов  delay(3000);  ADMUX = 7 + 64 + 128; // уровень 2.56 вольт // 7-номер аналогового входа  delay(3000);  ADMUX = 7; // уровень 0  delay(3000);}

Нюанс: это если замерять относительно общего провода. Если мерить относительно +5 вольт, во всех трёх случаях получаем ноль. То есть нагрузку можно подключить между AREF и общим. Экспериментально установил, что можно получить ток до 20 мА при 2.56 вольт на ноге и до 40 мА при 5 вольт на ноге, этого вполне достаточно, чтоб зажечь светодиод, к примеру. Вот он, заветный двадцать второй выход! Конечно, напрямую управлять двенадцативольтовым сегментом не получится, нужен транзистор. Под руку попался легендарный КТ315, сколько лет я к нему не прикасался, как по мне, это одно из лучших произведений советской электроники, наряду с микрухой К155ЛА3. Когда-то я КТ315 и КТ361 даже в качестве ключей импульсного трансформатора питания применял, при напряжении порядка 60 вольт, и неоднократно. Здесь же нагрузка для него плёвая, и я принципиально даже резистор в базу не поставлю (20 мА вполне себе допустимый ток базы).
Ну что ж, думал я, 22 выхода есть, отлажу код с 21 выходом, а 22-й на ножке сброса оставлю напоследок, когда всё остальное уже заработало. Ещё ведь и кнопки установки времени нужно куда-то приткнуть, а ног не осталось вовсе. Если б Атмега была в SMD-корпусе, можно было бы воспользоваться входами А6 и А7, выходами они всё равно не умеют. Но у меня корпус DIP, так что такой роскоши позволить себе не могу. Зато ведь можно выходы сделать входами, правда, в это время придётся погасить индикацию, но для опроса кнопок достаточно нескольких миллисекунд, никто и не заметит (как я ошибался!). Значит, перевожу ножки кнопок в режим входов, подтягиваем программно к питанию, и ждём пару миллисекунд. Если в это время какая-то из кнопок замкнута на минус, ждём ещё 20 миллисекунд, убеждаемся, что кнопка всё ещё нажата, и производим соответствующее действие в программе. Кнопки: минуты плюс, минуты минус, часы плюс, коррекция плюс. Коррекция подстройка значения секунд раз в сутки в зависимости от суточного ухода показаний. Всё заработало почти с первого раза. Нюанс: при замкнутой кнопке сегмент, подключенный к той же ножке, оказывается включён, такой вот побочный эффект, что поделать. Поэтому кнопки изменения минут подключаю к выводам контроллера, которые управляют сегментами единиц часов, а кнопки изменения часов подключаю к выводам контроллера, которые управляют сегментами единиц минут. Выставляя минуты, совсем не обращаешь внимания, что с показаниями часов что-то не так, и наоборот.
Ну вот всё и получилось в макете с семисегментными индикаторами. Но параллельно я пытался накопать информации по поводу применения ноги сброса в качестве порта ввода-вывода. А там всё довольно туманно и неопределённо. Основное, что об этом пишут: 1 выход с открытым коллектором (не точно), 2 выход чрезвычайно слаботочный (тоже не наверняка). Даже если использовать внешний транзистор, нужно точно знать, открытый коллектор (сток) или нет, ведь тогда состояние выхода нужно инвертировать. А перепрошить я уже не смогу. В общем, опять двадцать пять! Вернее, двадцать один. Двадцать один гарантированный выход вместо 22. Опять одного не хватает. Опять я мечусь в поисках решения.
Возвращаюсь к AREF. Я могу получить три разных уровня на нём. Значит можно зажечь один сегмент, или два одновременно, или ни одного. Я обратился к своей же прошлой публикации (как давно это было!):Ненормативная схемотехника: семисегментный индикатор на ATtiny13
Чтобы попробовать использовать решение с совместным включением двух сегментов. Перебирая возможные пары сегментов, и осознавая, что подобное усложнение портит всю картину, я внезапно додумался, что для цифры десятков минут поле поиска значительно сужается цифра ведь может быть только от 0 до 5. И тут пришло озарение:

во всех этих цифрах сегменты А и D или вместе светятся, или вместе погашены! Не нужно никаких трёх состояний, достаточно просто соединить сегменты А и D вместе и подключить к одному выходу. И теперь 21 выхода хватит всем. Довожу код, проверяю на макетке с семисегментным индикатором, бинго!
Всё, пора делать финальный вариант. Нарезаю светодиодную ленту, наклеиваю сегменты на подходящее основание, которым оказался кусок пластиковой вагонки (примерно 150х350 для цифр высотой 100). К каждому сегменту нужно подвести +12 вольт и проводник от соответствующего выхода контроллера.

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

Сегменты цифр разведены по контроллеру именно в таком порядке для упрощения кода.
Первая цифра (единица, напоминаю) состоит из цельного куска ленты длиной 10 см. Подключаю 5 вольт питания контроллера и 12 вольт питания индикации, включаю. Вот он, торжественный момент, всё красиво светится, часики работают. Кстати, на КДПВ в начале статьи в качестве светофильтра на индикаторах лежит лист обычной бумаги для принтера, яркости хватает с избытком.
Мне не очень нравится американская система отображения времени, когда часы дважды в сутки считают до двенадцати. Я применил свою: с нуля часов до 19, и затем 8, 9, 10, 11. А с учётом того, что часы офисные, в 8 вечера их редко кто увидит.

В такие моменты ощущаешь некоторое разочарование, что прям вот так сразу заработало, даже как-то неинтересно. Поначалу я упорно не хотел замечать некое мерцание индикаторов, пока мне на него не указали коллеги. В макете этого мерцания не было видно совсем, а тут прям бросается в глаза. Выше я писал, что при опросе кнопок после перевода ножек в режим ввода сделана пауза в пару миллисекунд, без этой паузы остаточный потенциал на ножке воспринимался как нажатие. Так вот тих двух миллисекунд, в течение которых индикаторы потушены, оказалось достаточно для мерцания индикации. И это при том, что опрос происходит два раза в секунду. То есть глаз замечает двухмиллисекундную паузу дважды в секунду, чего я никак не ожидал. Было подозрение, что вследствие переходных процессов контроллер каждый раз перепроверяет, действительно ли нажаты кнопки (а это по 20 мсек на каждую). Я внёс некоторые изменения в код, временно отключив все лишние функции, но подозрения не подтвердились. В результате помогли следующие изменения: для опроса кнопок перевожу те выходы, которые задействованы под кнопки, в высокий уровень, тем самым погасив соответствующие сегменты, только после этого перевожу те же ножки в режим входа. Таким образом от паузы в 2 мсек можно избавиться совсем, но я оставил на всякий случай 300 микросекунд, проблема исчезла полностью.
Что мы имеем в итоге? Ножка сброса контроллера работает по прямому назначению и используется только при заливке прошивки, ножки кварца подключены к кварцу, как и положено. Двадцать ног работают как порты ввода-вывода, и нога AREF управляет ключевым транзистором, как раз на него и навешена цифра десятков часов (1). Ещё четыре ножки питания, никто не отлынивает, все ноги задействованы. По поводу кварца: я всерьёз рассматривал вариант применения часового кварцевого резонатора на 32768 Гц (считал его более точным), но отказался от идеи, побороздив интернет. Оказывается, запустить Атмегу с часовым кварцем не так-то просто и нет никаких гарантий работоспособности, а плюсов от применения не особо. Экономичность нас не интересует в данном случае, основное потребление индикация. С точностью тоже всё неопределённо. А суточный уход вполне компенсируется программно. Зато большим плюсом является простота подключения, кварц на 8 или 16 МГц без проблем работает даже без конденсаторов. В результате вся схема состоит (если не считать индикаторы и питание) из Атмеги, кварцевого резонатора, и транзистора, припаянных прямо к панельке. Питание контроллера обеспечивается малогабаритным стабилизатором из серии 7805, ток через него мизерный, но на всякий случай я припаял его теплоотводом к кусочку оцинковки примерно 30х30 мм. В целом же часы питаются от внешнего блока питания на 12 вольт, который, по сути, дороже всех комплектующих. Фактический ток потребления часов 560 мА при показаниях часов 18-08 (это максимальное количество сегментов, которые можно засветить одновременно), получается около 28 мА на сегмент. Это при напряжении питания часов 11.7 вольт, ещё 0.3 вольта падает на диоде, включенном для защиты от неправильного подключения и чтоб немного снизить напряжение и ток соответственно. Падение на выходных ключах Атмеги около 0.56 вольт. Все токовые режимы превышены, но Атмега справляется, честь и хвала творцам! Запаса яркости избыточно, напряжение питания можно ещё снижать. Если тактировать Атмегу от внутреннего генератора, то можно ножки кварца отдать индикатору, а время считывать по I2C с DS1307. Опять же будут заняты все ноги, но зато питание часов можно будет отключать хоть на неделю, а время продолжит тикать. Хотя точность DS1307 совсем не радует, и по моему опыту, и по отзывам в интернете. Зато на ней есть дополнительный выход с открытым коллектором, которому можно дать команду мигать с частотой 1 Гц, и навесить на него разделительную точку. В моих часах разделительных точек пока нет, можно разрезать ту же ленту на отдельные светодиоды и подключить постоянно к напряжению питания. Мигать не будет, но я думаю над этим. Может, кто подскажет, как выжать ещё каплю из Атмеги?
И, напоследок, код для тех, кто захочет повторить. Компилировал и прошивал в ArduinoIDE. Я не программер, так что примите как есть, если кто предложит лучше, с удовольствием выложу.
Особо чувствительным не смотреть
Я предупреждал
bool Flag;uint8_t TimS;uint16_t TimH = 12; // время при включении 12-34uint16_t TimH_;uint16_t TimM = 34;uint16_t TimH0;uint16_t TimH1;uint16_t TimM0;uint16_t TimM1;uint16_t TimKorr = 7; // коррекия по умолчанию 7 - это 0 секунд, если 0 - это -28 сек, если 14 - это +28 сек// массив для цифрint semisegm_[10] = {B01011111, B00000110, B01101011, B01100111, B00110110, B01110101, B01111101, B00000111, B01111111, B01110111};void setup() {                                                         //  PORTB = 0;  PORTC = 0;  PORTD = 0;  // инициализация Timer1  cli();  // отключить глобальные прерывания  TCCR1A = 0;   // установить регистр в 0  TCCR1B = 0;   // установить регистр в 0  // Таймер переполняeтся каждые 65535 отсчетов при коэффициенте деления 1024 или за 4,194с  OCR1A = 62499; // установка регистра совпадения (4 секунд)  TCCR1B |= (1 << WGM12);  // включить CTC режим > сброс таймера по совпадению  TCCR1B |= (1 << CS10);   // Установить биты CS10 CS12 на коэффициент деления 1024  TCCR1B |= (1 << CS12);  TIMSK |= (1 << OCIE1A);  // для ATMEGA8  sei(); // включить глобальные прерывания}   void loop() {  if (TimM > 59) {    TimM = 0;    TimH++;    Flag = 0;  }  if (TimH > 23)TimH = 0;  if (TimM < 0) {    TimM = 59;  }  if (TimH == 0) {    if (TimM == 0) {      if (TimS == 7) {        if (Flag == 0) {          TimS = TimKorr;          Flag = 1;        }      }    }  }  TimH_ = TimH;  if (TimH > 19)TimH_ = TimH - 12;  TimH0 = TimH_ / 10;  TimH1 = TimH_ % 10;  TimM0 = TimM / 10;  TimM1 = TimM % 10;  Led(TimH0, TimH1, TimM0, TimM1);  Key();}// функция индикацииvoid Led(uint16_t TimH0, uint16_t TimH1, uint16_t TimM0, uint16_t TimM1) {  DDRB = semisegm_[TimM1];  DDRC = semisegm_[TimM0];  DDRD = semisegm_[TimH1];  bitWrite(DDRD, 7, bitRead(semisegm_[TimM1], 6));  ADMUX = 199 * TimH0; // уровень 2.56 на AREF  PORTB = 0;  PORTC = 0;  PORTD = 0;  delay(500); // полсекунды просто отображаем время}// функция опроса кнопокvoid Key() {  bitWrite(PORTB, 2, 1);  bitWrite(PORTD, 2, 1);  bitWrite(PORTD, 1, 1);  bitWrite(PORTD, 0, 1);  bitWrite(DDRB, 2, 0);  bitWrite(DDRD, 2, 0);  bitWrite(DDRD, 1, 0);  bitWrite(DDRD, 0, 0);  delayMicroseconds(300);   if (bit_is_clear(PINB, 2)) {      delay(20);    if (bit_is_clear(PINB, 2)) {      TimH++;    }  }  if (bit_is_clear(PIND, 1)) {    delay(20);    if (bit_is_clear(PIND, 1)) {      TimM++;    }  }  if (bit_is_clear(PIND, 0)) {    delay(20);    if (bit_is_clear(PIND, 0)) {      TimM--;    }  }  if (bit_is_clear(PIND, 2)) {    delay(20);    if (bit_is_clear(PIND, 2)) {      TimKorr++;      if (TimKorr > 14)TimKorr = 0;      Led(0, 8, TimKorr / 10, TimKorr % 10);      delay(500);    }  }}ISR(TIMER1_COMPA_vect) // Выполняем 1 раз в 4 секунды.{  TimS++;  if (TimS > 14) {    TimM++;    TimS = 0;  }}


Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru