Самодельный ИК-пульт для iRobot Roomba

от автора

Опрос в моём предыдущем посте «Управляем роботом-пылесосом iRobot Roomba через ИК» показал, что сообществу интересно узнать, как изготовить самому ИК-пульт для Roomb-ы. Итак, встречайте! =)

Схема пульта:

Я использовал:

  • МК ATTINY2313A-SU,
  • кнопки DTSM-20,
  • транзистор BC846,
  • SMD светодиод для индикации,
  • ИК-диод L-34F3C,
  • кварц на 7,3728МГц.
  • несколько резисторов и конденсаторов.

Изначально я планировал сделать «спартанский» вариант — плата с кнопками в термоусадке, но потом всё-таки решил собрать всё в корпусе. Корпус выбирал из того, что есть в продаже, мне понравился GAINTA G401. Под него и делал плату:

Функции кнопок:
SW1 — Dock
SW2 — Pause/Power
SW3 — Forward Left
SW4 — Clean
SW5 — Left
SW6 — Forward
SW7 — Spot
SW8 — Right
SW9 — Forward Right

Как в последствии выяснилось, кнопка SW2, которая, по задумке, при нажатии передаёт команду «Pause», а при долгом нажатии «Power» — не нужна, т.к. эти функции выполняет кнопка «Clean». Она работает так же, как и кнопка на самом роботе-пылесосе: короткое нажатии — чистить, во время работы — остановка, а долгое нажатие — выключение. Т.ч. если кто будет повторять пульт — просто не устанавливайте SW2.

Листинг программы

/* Пульт управление роботом-пылесосом Irobot Roomba. Программа для контроллера Atmel ATTINY2313A. FUSE-биты:  Fuse Low Byte: CKDIV|CKOUT|SUT|SKSEL|   0  |  1  |10 |0100 | 0x64 Default   1  |  1  |10 |1100 | 0b11101100=0xEC  для кварца 3-8 МГц */  //#define F_CPU 7.3728E6 //#define __AVR_ATtiny2313A__  #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h>  /*------<Макросы>-----*/ //Частота 39477 Гц //Период 25,3 мкс: #define P_GEN {TCNT0=0; PORTB |= 0x8; while (TCNT0 < 93); PORTB &= ~0x8; while (TCNT0 < 186);}  //Roomba коды: #define LEFT 			129 #define FORWARD 		130 #define RIGHT 			131 #define SPOT 			132 #define DOCK 			133 #define CLEAN 			136 #define PAUSE 			137 #define POWER 			138 #define FORWARD_LEFT  	139 #define FORWARD_RIGHT 	140 #define TEST 			170   /*----------------<Функции:>----------------*/ void init(void) { //Установка делителя частоты на /1: 	CLKPR = (1<<CLKPCE); 	CLKPR = (0<<CLKPS0)|(0<<CLKPS1)|(0<<CLKPS2)|(0<<CLKPS3); //Настройка портов: 	PORTD = 0xFF; 	DDRD  = 0x0; 	PORTB = (1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2); 	DDRB  = (1<<DDB3)|(1<<DDB4); //Настройка прерываний: 	GIMSK = 0x30; //(1<<PCIE)|(1<<4); 	PCMSK = 0x3; //(1<<PCINT0)|(1<<PCINT1); 	PCMSK2 = 0x7F; }  void ir_tx(unsigned char data_ir) { 	PORTB |= (1<<PB4); //Включим светодиод /* Кодирование данных: 1: __________ 2862uS    |988uS           |_____ 0: _____ 887uS|  2862uS      |__________ В конце пауза ~16870 uS */ 	unsigned char cnt; //Настраиваем таймеры: 	TCCR0B = (0<<CS12)|(0<<CS11)|(1<<CS10); // Для несущей f/1 	TCCR1B = (0<<CS12)|(1<<CS11)|(1<<CS10); // Для данных f/64	         for (cnt=0; cnt<8; cnt++)         {             TCNT1=0;             if (data_ir & (0x80 >> cnt))             {	//Если 1                 while (TCNT1 < 330) P_GEN;                 while (TCNT1 < (330+114)) ;             }             else             {	//Если 0                 while (TCNT1 < 113) P_GEN;                 while (TCNT1 < (330+114)) ;             }         }         TCNT1=0;         while (TCNT1 < 1950)  ;    	PORTB &= ~(1<<PB4);	//Выключим светодиод }  void send_command(void) { 	volatile unsigned char button_b; 	volatile unsigned char button_d; 	volatile unsigned char button_b_new; 	volatile unsigned char button_d_new; 	unsigned char pause_cnt = 0; 	TCCR1B = (0<<CS12)|(1<<CS11)|(1<<CS10); // f/64 	TCNT1=0; 	while (TCNT1 < 346); //346 - Задержка 3 ms  	button_b = ~PINB & ((1<<PINB0)|(1<<PINB1)); 	button_d = ~PIND & 0x7F; 	button_b_new = button_b; 	button_d_new = button_d; 	     while ((button_b == button_b_new)&&(button_d == button_d_new))     {         if ((button_b_new == 0) && (button_d_new == 0)) break;		 		if (button_b == (1<<PINB0)) {ir_tx(DOCK);}         if ((pause_cnt < 20) && (button_b == (1<<PINB1))) {ir_tx(PAUSE); pause_cnt++;} 		if (pause_cnt >= 20) {ir_tx(POWER);}         if (button_d == (1<<PIND0)) {ir_tx(FORWARD_LEFT);}         if (button_d == (1<<PIND1)) {ir_tx(CLEAN);}         if (button_d == (1<<PIND2)) {ir_tx(LEFT);}         if (button_d == (1<<PIND3)) {ir_tx(FORWARD);}         if (button_d == (1<<PIND4)) {ir_tx(SPOT);}         if (button_d == (1<<PIND5)) {ir_tx(RIGHT);}         if (button_d == (1<<PIND6)) {ir_tx(FORWARD_RIGHT);} 		button_b_new = ~PINB & ((1<<PINB0)|(1<<PINB1));         button_d_new = ~PIND & 0x7F; 		     } }  /*----------------------<Обработка прерываний:>------------------------*/ ISR(PCINT_B_vect) { 	cli(); 	send_command(); 	sei(); }  ISR(PCINT_D_vect) { 	cli(); 	send_command(); 	sei(); }  /*-----------------<основной цикл программы>-----------------------*/ int main(void) { 	init(); 	sei(); 	for(;;) 	{ 		set_sleep_mode(SLEEP_MODE_PWR_DOWN); 		sleep_mode(); 	} }  

Фото в сборе:

Подписи кнопок, конечно, выглядят коряво — теперь я понимаю, что гравировка — дело не простое! 🙂

Сама плата:

Проводники, которые предполагалось выполнять на обратной стороне сделал проводами.

Видео, демонстрирующие работу:


Файлы KiCAD-проекта: RemoteControlPCB.zip.
Прошивка: RoombaRemoteControl.elf.hex.

Для прошивки через программатор USBasp, программой avrdude:

avrdude -p t2313 -c usbasp -U flash:w:./RoombaRemoteControl.elf.hex 

Прошивка fuse-битов для работы с кварцем:

avrdude -p t2313 -c usbasp -U lfuse:w:0xEC:m 

ссылка на оригинал статьи http://habrahabr.ru/post/169967/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *