Достался простейший взбиватель сливок (жидкости) из поднебесной без регулировки скорости. Нажал - вертит, отпустил - остановился.

undefined

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

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

Можно сделать на КМОП таймере 555 ШИМ но поскольку желательно сохранить штатную кнопку и надо иметь хотя-б 3 скорости то

потребуется хоть и маленький но микроконтроллер. в роли него выступит ATTINY10.

Функционал простой: держим несколько секунд - включается на максимальные обороты , далее длинными нажатиями обороты, ступенчато, 4 раза снижаются. после чего последнее длинное нажатие выключает мотор и погружает МК в сон. Решение о том что после включения крутится он начинает с максимальных оборотов не спроста - вначале двигатель управляемый ШИМом надо "сдернуть" для чего и используется полная подача питания через полевой транзистор.

undefined

Первый прототип печатной платы с началом подгона под стесненные размеры корпуса.

Конденсаторы всегда имеют утечки. В данном устройстве конденсатор нужен для балансировки питания МК в моменты старта мотора. Минимальный практический обьем емкости 4.7мкф. Меньше емкости этой - нестабильность питания приводит к переколбасу МК, больше - излишние утечки и поедания впустую батарейного питания. 

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

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

Итоговое изделие.

 undefined

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

Исходный код прошивки написан в Atmel Studio и довольно прост.

/*
 * firmware for primitive chinese hand 3V DC motor shaker. support OFF/MAX/MEDIUM/LOW/VERY LOW speed setting via single button
 *
 * Author : dukk
 */ 
#define F_CPU 2000000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

uint8_t mode=5;
//Variable to tell main that the button is pressed (and debounced). Main will clear it after a detected button press
volatile uint8_t button_down = 1;

void powerdown()
{
	//disconnect timer from OUTPUT on pin
	TCCR0A &= ~(1<<COM0B1);
	PORTB &= ~ (1<<PORTB1);

	while(1)
	{
		cli();
		button_down=0;
		set_sleep_mode(SLEEP_MODE_PWR_DOWN);
		sleep_enable();
		sei();
		sleep_cpu();
		sleep_disable();
		TCCR0B |= (1 << CS01);
		
		//wait 32 timer overflows so our overflow interrupt can do debounce stuff
		for(uint8_t i=0;i<32;i++) {
			while ((TIFR0 & (1 << TOV0)) == 0);
		}
		//check button state after -> if it is surely detected as pressed - get out of sleep
		if(button_down) break;
	}
	
	//connect timer to OUTPUT pin
	TCCR0A |= (1<<COM0B1);
}

EMPTY_INTERRUPT(INT0_vect)

// Check button state on PB2 and set the button_down variable if a debounced button down press is detected.
ISR(TIM0_OVF_vect)
{
	// Counter for number of equal states
	static uint8_t count = 0;
	// Keeps track of current (debounced) state
	static uint8_t button_state = 0;

	// Check if button is high or low for the moment
	uint8_t current_state = (~PINB & (1<<PINB2)) != 0;
	
	if (current_state != button_state) {
		// Button is about to be pressed or released, increase counter
		count++;
		//at every 24ms *32 = 738ms debounce time
		if (count >= 32) {
			// The button have not bounced, change state
			button_state = current_state;
			// If the button was pressed (not released), tell main so
			if (current_state != 0) {
				button_down = 1;
				count = 0;
			}
		}
	} else {
		// Reset counter
		count = 0;
	}	
}

int main(void)
{
	cli();
    // Set clock to 2 MHz
    CCP = 0xD8;
    CLKPSR = 2; //Fosc/4
	
    WDTCSR |= (0<<WDE);
	PRR = (1<<PRADC);
	ACSR = (1<<ACD);
	
	//out PWM to PB1, all other is input
	DDRB = (1<<DDB1); //PB1
	//enable pull-up internal on PB2 (power and speed control button)
	PUEB = (1<<PUEB2);
	PORTB |= (1<<PUEB2);
	
	//Configure interrupt
    EICRA = (1<<ISC00); //any logic change INT0 generate interrupt
	SREG |= 1<<7;//I bit from SREG has to be enabled
	EIMSK |= (1 << INT0);//enable INT0 (PB2)
		
    //Timer0 in mode 14, fast PWM with ICR0 as top, Enable OC0B, low mode bits
    TCCR0A = (1<<COM0B1) | (1<<WGM01);
    //top value for counter
    ICR0 = 3072;
	// Enable overflow interrupt (at div8 it will overflow every 24ms)
	TIMSK0 = 1<<TOIE0;
    // Start timer with prescaler 1:8, high mode bits
    TCCR0B = (1<<CS01) | (1<<WGM03) | (1<<WGM02);
		
	sei();
	
	while(1)
	{
		if (button_down) {
			// Clear flag
			button_down = 0;
			
			if(++mode > 4) mode=0;
			switch(mode) {
				case 0:
					powerdown();
				break;
				case 1:
					OCR0B = 3072;
				break;
				case 2:
					OCR0B = 2048;
				break;
				case 3:
					OCR0B = 1024;
				break;
				case 4:
					OCR0B = 512;
				break;
			}			
		}
	}
}