pourquoi pas? Первый, даже нулевой и вынесенный за скобки вопрос – зачем лентяйке лентяйка? Поясню. Во- первых, это даже не «прибор выходного дня», а «прибор свободного часа», а во вторых – вы уехали все отдыхать – лентяйка лентяек (или благородно RemoteofRemotes) вместе с просто лентяйкой могут включать и выключать что-либо (тв, радио, двд, люстру), имеющее пульт. Ну и сугубо личное – у меня в год уходит больше двух килобаксов на отопление – и солярка ныне дороже 95го, и у газпрома на нас, живущих в 30 км от МКАДа газа не хватает, но есть специфика – ночной тариф, в три раза дешевле дневного. Поскольку я, как типичный эмбеддер[1] / свободный агент, работаю до 3 ночи, а сплю потом до 10 утра, график «ночного тарифа» (с 23 до 7) меня совершенно не устраивает, но победить наследников Чуйбаса[2] я не могу. Ну тут как с горой- не идет постчубайская вертикаль навстречу – пойду я. Вот я включаю все электронагреватели в 11 вечера, а лентяйка лентяек (далее Л2) выключате в 7 утра. Выключаю кнопкой «выключить все». Модули эти уже давно стоят, они просто втыкаются в розетку, держат достаточно ампер и прошиваются в пульт персонально. К Л2 можно добавить второй палец, удвоив код (но с другим портом) – и тогда можно будет не только выключать все, но и включать, а затем выключать. С вопросом «Кто виноват» у нас давно ясно, теперь к вопросу «что делать?». Конструкция Я взял ардуину (совместимую) нано v.3 на контроллере Atmega328p(они вроде все на этом чипе, но укажем для ясности), и самую дешевую сервомашинку с не самым простым названием «TowerPro SG90 Mini Gear Micro Servo 9g», запитал все аккумулятором 6 В 12 А*ч[3]. Вот оно как, без аккумулятора, что его фотографировать[4] : Рисунок 1 Л2. Пояснения в тексте. Питание на Vin, выход стабилизатора платы стабилизирован элеткролитом 3300 мкФ. Теперь по деталям – выход сервы подключен к порту B1. Cервомашинки работают так- на управляющий вход нужно послать импульст шириной от 1 до 2 (зависит от конкретной машинки) мс, и она соответсвенно встает в -90° или +90°. Сервомашинку можно взять самую простую и дешевую, но обязательно рабочую. Сервомашинка смонтироана пластиковым креплением швеллеру из детского конструктора. Между швеллером и сервомашинкой впихивается пульт таким образом, чтобы рычажек сервомашинки (идет в комплекте) нажимал нужную кнопку. Чтобы моточик сервомашинки не вызывал ресет микроконтроллера из-за падения напряжения на стабилизаторе (он дохловат, если честно), к выходу +5 В, он же Vccподключен электролит 3300 на 16 В. Больше не рекомендуем, посадит платный (в смесле установленный на плате) стабилизатор +5 В, ну или тогда надо его включать через цепь диод/резистор, чтобы он умел быстро разряжаться, но заряжался умеренным током, без садизма. Из деталек еще отмечу 6 канальный dip выключатель для выбора времени задержки выключения. Смонтирована плата микроконтроллера и вспомогательные детальки на макетке сколько-то –там – не помню – сколько дырок, взял маленькую. Печатку делать не стал, все равно буду думать более глобально – электричество обещают поднять в цене до уровня 7 руб/кВт*ч, чтобы население платило как юрлица. Засим к мягкому, т.е. к софту Как нам запрограммировать мк Исторически сложилось, что я пишу код для мк на CodeVision. Ну просто много много лет назад другое (а другое тоже достойно) не пошло, а CodeVisionпошел. Весь код давать не буду (мой реальный код – дауншифт кода для системы управления 8 сервомашинками с отчетами, обратными связями и т.д., я не стал его щипать, так и оставил,только подправив инициализацю). Для начала объявим, что порты D2-D7 будут входами, подтянутыми к +5 через внутренние резисторы, порты D0-D1 по традиции не трогаем – это USART: PORTD=0b11111100; DDRD=0; К портам D2-D7 подключен 6 – канальный дип свич для выбора времени задержки, и поскольку нулевая задержка мне не нужна (может кому нужна –замените 64 на 63), то определение времени задержки в секундах выглядит так (это делается один раз при инициации) OffTime= 3600* (64-(PIND>>2)); Соответсвенно выбор от 1 до 64 часов задержки. Энтузиасты могут подключить свичи еще и к другим свободным портам и сделать задержку любого диапазона. Порт B0 (он собственно и генерит импульс управления сервомашинкой) – выход с начальным состоянием 0: DDRB|=0x01; PORTB&=~0x01; Теперь о собственно времени импульса. Тут есть тонкост (отмеченная ниже как !!!), без знания которой сервомашинка будет дрожать и пищать. У нас задержка определяется таймером 2, выставляем его при инициации: ASSR=0x00; // синхронизация - кварц TCCR2A=0x02; //прерывание по совпадению OCR2A TCCR2B=0x06; // Коэффициент предделителя =256 TCNT2=0x00; // Сброс счетчика таймера OCR2A=0xAA;//это все равно поставим как надо в коде OCR2B=0x00;//это все равно поставим как надо в коде При наступлении нужного времени мы запускаем сервомашинку, начиная импульс: { TCNT2=0; // Cбрасываемсчетчиктаймера OCR2A= Angle; // выставляемвремяимпульса PORTB.0 = 0x01;// Импульспошел GTCCR|=0x02;// сбрасываем предделитель таймера. ОЧЕНЬ ВАЖНО!!! TIFR2|=0x02; // установка прерывания по совпадению OCR2A TIMSK2=0x02; // разрешить прерывание по совпадению OCR2A } Народ, или, как говорят пухлолицые топ-менеджеры федеральных каналов, пипл часто жалуется на дрожание сервомашинок, борется , борется и сдается. Так вот сдаваться не надо, а надо сбрасывать предделитель командой CTCCR|=0x02. Поясню – при запуске таймера в предделителе может быть любое число от 0 до k-1 (k- коэффициент деления предделителя), cсоответственно сервомашинка и будет дрожать с амплитудой единицы младшего разряда.Т.е. если у вас OCR2A=10, то дрожание будет в пределах от 9 до 10, что составляет 10%. Закончить импульс проще, чем начать interrupt [TIM2_COMPA] void timer2_compa_isr(void) { PORTB.0 = 0x00; TIMSK2=0x00; // Disabel Timer2 Interruptions } После команды TIMSK2=0x00; // DisabelTimer2 Interruptionsфункция обработки прерывания по таймеру 2 не будет вызываться, пока мы снова не разрешим это прерывание командой TIMSK2 = 0x02;// Go!. Ну и желательно посматривать, чтобы кто- то не влез с прерыванием по OCR2A, в свободное (а это 99.99%) от нажатия кнопок время можно использовать таймер 2, запретив прерывание по OCR2A.И следить, чтобы разные функции не пытались одновременно использовать прерывание по таймеру 2. И тогда у вас сервомашинка будет работать как часы!Но эти советы – в общем-то, для начинающих. Если вы умеете использовать одно прерывание для управления парой дюжин сервомашинок, сеткой 1wire, да еще и софтовый uart на нем гонять – то вам мои заметки и читать не надо, будет только вред. [1] An embedded system is a special-purpose system in which the computer is completely encapsulated by the device it controls. Однако если вы встроили болт в унитаз – вы еще не эмбеддер. [2] Чубайс – скорее уже жупел, нежели реальный исторический персонаж. Поскольку отношения читателей к нему могут быть диаметрально противоположны, ничего я вам про него не скажу. [3] Реально напряжение аккумулятора 6.9 – 7.2 В. [4] Согласно современной тенденции в любой книге по микроконтроллерам более половины рисунков служат лишь цели заполнения объема, приведу характерные примеры – фотографии пассатиж, паяльника, банки с хлорным железом (на фото не видно, что это хлорное железо, не говоря уж о том, что непонятно – это шестиводное или обезвоженное хлорное железо, а это две большие разницы – всегда берите шестиводное и ни в коем случае не обезвоженное) и как апофеоз – фотография ножниц (да-да , обычных офисных ножниц) на полстраницы. Мы не будем придерживаться современных тенденций, поскольку гнаться за модой – не наше.
пы сы не успел обнародовать (20150105)- вчера (20150107) по "Эхо Москвы"- у Баблаян "Корейские старт-аперы запустили кик-старт по автономному нажимателю кнопок", отличается от моего немногим- у него еще bluеtooth, но нет переключателя - таймера. Кто меня сдал корейцам?
нет, радио. У меня по дому расставлены нагреватели через управляемые по радио розетки, этой системе уже лет 15, все руки не доходили протянуть нормальные провода до них и поставить управляемые от системы. Ну собсвенно я уже писал - нагреватели выключаю в 7 утра, когда тариф вырастает в 3 раза. Но, поскольку наследники чуйбаса обещают выровнять тарифы чл и фл (сейчас в области 7 руб с копейками за квт*ч), конечно систему более четкого реагирования сделаю, чтобы и нагреватели управлялись, и клапана на батареи поставлю (сейчас на батареях регуляторы с биметаллом), под это дело были куплены радиомодули sim20 a, модули уже включены в розетки и показывают температуру по комнатам, осталось поставить по мосфиту для управления клапаном (который тоже нужно еще купить и врезать, ну врезать я и сам врежу) и по симистору для управления (только вкл/выкл) электронагревателем. Поднимут цену на эл-во - буду на солярке, ну и т.д.
Радио, тоже хорошо. Видимо, 433 МГц? Планирую в ближайшее время освоить модуль RFM69. А за одно поразвлекаться реверс инжинирингом протоколов. В первую очередь повторить разборки noolite: http://m.habrahabr.ru/company/contactless/blog/216023/. Понятно, задействовать малину. Времени катастрофически не хватает.
ноолайт ребята с contactless.ru реверснули. У них олимексино на линуксе им управляет, можете посмотреть.
Я на их отчёт ссылку и запостил. Но для повторения надо ещё хорошо покопаться в установке RFM69 на Малину и ещё кое в чём. Но есть и простые протоколы. Я rc-switch https://github.com/sui77/rc-switch подменил управление одним своим устройством с пульта. Немножко удивлён, что Вы с вашим уровнем компетенций пошли по пути нажимания кнопок. А по ссылке отчёт контактлесс о том как они ломали протокол, и, главное, показали методику такой ломки. В НооЛайт много команд, а повторить команду одной кнопки полагаю гораздо проще - не надо вникать в протокол, записал и повторил. Посмотрите: https://github.com/sui77/rc-switch/wiki
так вопрос еще того , что есть. радиомодули у меня только simcom, они только друг с другом (манчестер там и все такое) говорят, а у меня был модуль на 8 сервомашинок. Вот я на нем и слепил, до весны пусть поработает. По радио я возможно впрягусь, но я хочу реверснуть протокол x10, у меня полно пультов этого производителя (и протокола), и какая - то шантрапа в Рязани или Туле делает и даже продает, хотя копирайт и все такое, я бы стреманулся. Но у них модуль рф вроде самопал, посмотрю- подумаю, может кто делает. Хотя вроде бы протокол и открытый х10, да это неважно во времена интернетов и трекеров. А про contactless я знаю, я сам их знаю хорошо, из одной альмы матери.
Привет! Вроде не замечал какого-то дрожжания при использовании библиотеки серво, но все равно хотелось бы понять. Я не понял - на каком именно этапе надо сбрасывать предделитель? то есть хотел бы понять применительно к библиотеке серво, может ее можно улучшить? вот например я делал на основе библиотеки серво ее упрощенный и немного урезанный вариант. в прерывании циклически перемежаются сервы - на предыдущей сигнал обнуляется, на следующей поднимается и т.д. Код (C++): ISR(TIMER1_COMPA_vect){ if (s<0){ TCNT1 = 0; s++; OCR1A = TCNT1+ServoTicks[s]; if (ServoEnable[s]>0){WriteServo_on(s);}} else { WriteServo_off(s); s++; if (s<allservos){ OCR1A = TCNT1+ServoTicks[s]; if (ServoEnable[s]>0){WriteServo_on(s);}} else { OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL)-ticks; s=-1;}} } вот код целиком (че он там делает точно уже не помню, как-то крутит одну из 4х серв - всего их может быть до 8ми) - делитель там настраивается один раз только в самом начале (в "сетапе") TCCR1B = 0b00000010; // set prescaler of 8 то есть его надо устанавливать еще где-то в процессе? не могу понять где и зачем. Спойлер Код (C++): #include <avr/io.h> #define F_CPU 8000000UL #include <avr/interrupt.h> #include <util/delay.h> #define setH(port,bit) do{port |= (1 << bit);}while(0) #define setL(port,bit) do{port &= ~(1 << bit);}while(0) #define readP(reg,bit) (reg & (1 << bit)) #define Led_port PORTB #define Led_reg PINB #define Led_bit 7 #define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) //millis #define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) #define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) #define FRACT_MAX (1000 >> 3) volatile unsigned long timer0_overflow_count = 0; volatile unsigned long timer0_millis = 0; static unsigned char timer0_fract = 0; //servos #define usToTicks(_us) (( clockCyclesPerMicrosecond()* _us) / 8) #define ticksToUs(_ticks) (( (unsigned)_ticks * 8)/ clockCyclesPerMicrosecond() ) #define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo #define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo #define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached #define REFRESH_INTERVAL 20000 // minumim time to refresh servos in microseconds typedef struct pin_t { volatile uint8_t *port; uint8_t bit; }Pin; Pin Servo0 = {&PORTB, 1<<0}; Pin Servo1 = {&PORTB, 1<<1}; Pin Servo2 = {&PORTB, 1<<2}; Pin Servo3 = {&PORTB, 1<<3}; Pin Led = {&PORTB, 1<<7}; Pin* ServoArray[] = { &Servo0, &Servo1, &Servo2, &Servo3 }; #define allservos 4 // 8 max void WriteServo_on(uint8_t num) { Pin* pin = ServoArray[num]; *pin->port |= pin->bit; } void WriteServo_off(uint8_t num) { Pin* pin = ServoArray[num]; *pin->port &= ~pin->bit; } uint8_t ServoEnable[allservos]={1,1,1,1}; uint16_t ServoTicks[allservos]={ usToTicks(DEFAULT_PULSE_WIDTH), usToTicks(DEFAULT_PULSE_WIDTH), usToTicks(DEFAULT_PULSE_WIDTH), usToTicks(DEFAULT_PULSE_WIDTH)}; uint16_t ticks = usToTicks(DEFAULT_PULSE_WIDTH)*allservos; uint16_t sum; int8_t s = -1; void WriteServo_us(uint8_t num, uint16_t val){ uint8_t se; if (val>=MIN_PULSE_WIDTH && val<=MAX_PULSE_WIDTH) { val=usToTicks(val); se=1;} else if (val>MAX_PULSE_WIDTH) { val=usToTicks(MAX_PULSE_WIDTH); se=1;} else if (val!=0) { val=usToTicks(MIN_PULSE_WIDTH); se=1;} else { val=usToTicks(DEFAULT_PULSE_WIDTH); se=0;} uint8_t oldSREG = SREG; cli(); ServoTicks[num]=val; ServoEnable[num]=se; SREG = oldSREG; for(uint8_t i=0; i<allservos; i++){ sum = sum + ServoTicks[i];} ticks = sum; sum = 0;} uint16_t ReadServo_us(uint8_t num){ uint16_t ms; uint8_t oldSREG = SREG; cli(); ms=ServoTicks[num]; SREG = oldSREG; return ms;} unsigned long millis(){ unsigned long m; uint8_t oldSREG = SREG; SREG &= ~(0b10000000); m = timer0_millis; SREG = oldSREG; return m;} ISR(TIMER0_OVF_vect){ unsigned long m = timer0_millis; unsigned char f = timer0_fract; m += MILLIS_INC; f += FRACT_INC; if (f >= FRACT_MAX) { f -= FRACT_MAX; m += 1;} timer0_fract = f; timer0_millis = m; timer0_overflow_count++;} ISR(TIMER1_COMPA_vect){ if (s<0){ TCNT1 = 0; s++; OCR1A = TCNT1+ServoTicks[s]; if (ServoEnable[s]>0){WriteServo_on(s);}} else { WriteServo_off(s); s++; if (s<allservos){ OCR1A = TCNT1+ServoTicks[s]; if (ServoEnable[s]>0){WriteServo_on(s);}} else { OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL)-ticks; s=-1;}} } unsigned long time = 0; static inline uint16_t AdcRead(uint8_t analog_pin){ //ADC ADMUX = 0b01000000 | analog_pin; //VCC ADCSRA = 0b11000110; // СК/64 (125KHZ) while ((ADCSRA & (1 << ADIF)) == 0); uint16_t adcw; adcw= ADCL|(ADCH<<8); ADCSRA |= (1 << ADIF); return adcw; } int main(void) { TCCR0 = 0b00000011; TIMSK |= (1<<0); TCCR1A = 0; // normal counting mode TCCR1B = 0b00000010; // set prescaler of 8 TCNT1 = 0; // clear the timer count TIFR |= (1<<4); // Atmega8 clear any pending interrupts; TIMSK |= (1<<4); // Atmega8 enable the output compare interrupt // TIFR1 |= _BV(OCF1A); // clear any pending interrupts; // TIMSK1 |= _BV(OCIE1A) ; // enable the output compare interrupt DDRB=0b10001111; OCR1A = TCNT1 + (uint16_t)usToTicks(REFRESH_INTERVAL); TIFR |= (1<<4); SREG |= (1<<7); uint8_t but1 = 0; uint8_t but2 = 0; uint16_t h = 2100; uint8_t d = 0; uint16_t adc_w; while (1==1) { adc_w = AdcRead(1); _delay_ms(1); if (h>2000) {d = 0;} if (h<800) {d = 1;} if(readP(PINB,6) && !but1){ but1 = 1; if (d==0) {h=h-100;} else {h=h+100;} WriteServo_us(1,h); } if(!readP(PINB,6) && but1) {but1 = 0;} if(readP(PINB,5) && !but2){ but2 = 1; WriteServo_us(1,0); } if(!readP(PINB,5) && but2) {but2 = 0;} uint8_t led = readP(Led_reg, Led_bit); if (millis()-time > 10) { time = millis(); if (led==0){ setH(Led_port,Led_bit);} else{ setL(Led_port,Led_bit);}} } return 0;} в симуляторе это выглядит вот так примерно, живьем этоеще не пробовал, но как уже упоминал - с самой библиотекой серво не замечал дрожания (или просто не знаю что конкретно надо замечатьи в каких условиях оно возникает)
Сбрасывать предделитель нужно перед включением таймера на отсчет импульса управления сервомашинкой. Дрожание импульса, обусловленное остатком в предделителе, видно на осциллографе. Команда для таймера 2, если у Вас другой таймер то см. в даташите. Команда должна стоять до начала отсчета (разрешения прерывания ) перед каждым импульсом. GTCCR|=0x02;// сбрасываем предделитель таймера. ОЧЕНЬ ВАЖНО!!!
а вот нашел - в CPU / SFIOR лежит нулевой бит PSR10 - Но написано что он сбрасывает предделитель и первого таймера и таймера 0. но на таймере 0 сидит миллис - это же должно нарушить его работу? видимо поэтому в библиотеке серво это не делается. выходит либо пересаживаться на таймер-2 либо мириться с дребезжанием? хотя оного я на живых сервах не особо замечал.
А для чего Вам нужен millis() и важна ли его точность, или точность millis() важнее чем точность импульсов на сервомашинки?
миллис очень удобен. надо будет посмотреть что с ним станет при постоянном сбросе предделителя. если систематическая ошибка не будет накапливаться, то и пофиг, +- пара миллисекунд не принципиально.