Здравствуйте! Сегодня хочу рассказать о том, как сделать устройство, которое позволяет копировать Touch Memory фирмы Dallas. Статья предназначена для тех, кто имеет опыт в программирование на языках C/C++. Рассказывать, как устроен протокол OneWire я не собираюсь, т.к. в интернете куча информации на эту тему.

Итак, что нам понадобится для изготовления данного устройства:

1) Atmega8
2) FTDI RL232, преобразующий USB в USART
3) Драйвер для FTDI RL232
4) Программатор(Я использовал USBasp)
5) Visual Studio
6) Atmel Studio
7) Макетная плата

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

image

image

image

image

Теперь давайте определимся с параметрами USART на atmega8. У меня они такие:
1) Стоповый бит — 1
2) 9600boud
3) Бит четности — нет

Я буду использовать внутренний генератор на 4Mhz, так что регистр UBRR я буду настраивать под эту частоту. Вот кусок кода, где идет настройка USART:

	DDRD|=(1<<1);
	DDRD&=~(1<<0);
	UBRRH=0;
	UBRRL=25;
	UCSRB|=(1<<RXEN)|(1<<TXEN);
	UCSRC|=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);

Пришло время разобрать протокол записи на болванки rw1990. Команда считывания 8-байтного кода у них идентична с ключами dallas.

Запись на rw1990 происходит так:
1) Посылаем импульс reset, и ожидаем presence импульс;
2) Отправляем команду 0xD1, тем самым разрешаем запись;
3) Тайм-слот, посылаем логический «0» (смотреть рис.1);
4) Посылаем импульс reset и ожидаем presence импульс;
5) Отправляем команду записи, 0xD5;
6) Посылаем 8-байтный код(все биты инвертированы), передача отличается от протокола oneWire (смотреть рис.1);
7) Посылаем импульс reset и ожидаем presence импульс;
8) Отправляем команду 0xD1, тем самым запрещаем запись;
9) Тайм-слот, посылаем логический «1» (смотреть рис.1).

Рис.1:

image

Кусок кода, где происходит запись:

bool onewire_init(){
	onewire_low();
	_delay_us(480);
	onewire_high();
	_delay_us(2);
	for(uint8_t i= 60;i;i++){
		if(!onewire_level()){
			while(!onewire_level());
			return true;
		}
		_delay_us(1);
	}
	return false;
}

void time_slot(uint8_t data){
	onewire_low();
	if(data)
		_delay_us(6);
	else
		_delay_us(60);
	onewire_high();
	_delay_ms(10);
}

void rw1990_write_bit(uint8_t data){
	onewire_low();
	if (data)
		_delay_us(6);
	else
		_delay_us(60);
	onewire_high();
	_delay_ms(2);
}

void rw1990_write(uint8_t data){
	for(uint8_t i=0;i<8;i++){
		rw1990_write_bit(data & 0x01);
		data>>=1;
	}
}

bool rw1990_newcode(uint8_t* buf){
	if (onewire_init()){
		onewire_send(0xD1);
		time_slot(0);
	}else return false;
	if(onewire_init()){
		onewire_send(0xD5);
		for(uint8_t i=0;i<8;i++){
			rw1990_write(~buf[i]);
		}
	}else return false;
	if (onewire_init()){
		onewire_send(0xD1);
		time_slot(1);
	}else return false;
	return true;
}

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

Впрочем, на видео все показано.

Исходный код для atmega8
#define F_CPU 4000000 

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

#define ONEWIRE_PORT      PORTB
#define ONEWIRE_DDR       DDRB
#define ONEWIRE_PIN       PINB
#define ONEWIRE_PIN_NUM   0


void     usart_init();
void     onewire_high();
void     onewire_low();
uint8_t  onewire_level();
bool     onewire_init();
void     onewire_write_bit(uint8_t);
void     onewire_send(uint8_t);
uint8_t  onewire_read_bit();
uint8_t  onewire_read();
bool     onewire_readrom(uint8_t*);
void     time_slot(uint8_t);
void     rw1990_write_bit(uint8_t);
void     rw1990_write(uint8_t);
bool     rw1990_newcode(uint8_t*);
uint8_t  usart_read();


uint8_t rom[8];
uint8_t new_rom[8];
uint8_t t=0;


ISR (USART_UDRE_vect){
	UDR=t;
	UCSRB &=~(1<<UDRIE);
	t=0;
}


int main(void)
{
	usart_init();
	asm("sei");
    while(1)
    {
		
		uint8_t r=usart_read();
		if (r==0x40){
			for(uint8_t i=0;i<8;i++){
				new_rom[i]=usart_read();
			}
			if(rw1990_newcode(new_rom)){
				t=0x45;
				UCSRB |=(1<<UDRIE);
			}else{
				t=0x46;
				UCSRB |=(1<<UDRIE);
			}
		}else if(r==0x30){
			if(onewire_readrom(rom)){
				t=0x35;
				UCSRB |= (1<<UDRIE);
				for (uint8_t i=0;i<8;i++){
					t=rom[i];
					UCSRB |= (1<<UDRIE);
					_delay_ms(1);
				}
			}else{
				t=0x36;
				UCSRB |= (1<<UDRIE);
			}
		}

	}
}


void onewire_high(){
	ONEWIRE_PORT &=~ (1<<ONEWIRE_PIN_NUM);
	ONEWIRE_DDR  &=~ (1<<ONEWIRE_PIN_NUM);
}

void onewire_low(){
	ONEWIRE_PORT &=~ (1<<ONEWIRE_PIN_NUM);
	ONEWIRE_DDR  |= (1<<ONEWIRE_PIN_NUM);
}

uint8_t onewire_level(){
	return ONEWIRE_PIN & (1<<ONEWIRE_PIN_NUM);
}

bool onewire_init(){
	onewire_low();
	_delay_us(480);
	onewire_high();
	_delay_us(2);
	for(uint8_t i= 60;i;i++){
		if(!onewire_level()){
			while(!onewire_level());
			return true;
		}
		_delay_us(1);
	}
	return false;
}

void onewire_write_bit(uint8_t bit){
	onewire_low();
	if(bit){
		_delay_us(5);
		onewire_high();
		_delay_us(90);
	}else{
		_delay_us(90);
		onewire_high();
		_delay_us(5);
	}
}

void onewire_send(uint8_t data){
	for(uint8_t i=0;i<8;i++){
		onewire_write_bit(data&0x01);
		data>>= 1;
	}
}

void usart_init(){
	DDRD|=(1<<1);
	DDRD&=~(1<<0);
	UBRRH=0;
	UBRRL=25;
	UCSRB|=(1<<RXEN)|(1<<TXEN);
	UCSRC|=(1<<URSEL)|(1<<UCSZ0)|(1<<UCSZ1);
}

uint8_t onewire_read_bit(){
	onewire_low();
	_delay_us(2);
	onewire_high();
	_delay_us(8);
	uint8_t r = onewire_level();
	_delay_us(80);
	return r;
}

uint8_t onewire_read(){
	  uint8_t r = 0;
	  for (uint8_t p = 8; p; p--) {
		  r >>= 1;
	 if (onewire_read_bit())
		  r |= 0x80;
	  }
	  return r;
}

void time_slot(uint8_t data){
	onewire_low();
	if(data)
		_delay_us(6);
	else
		_delay_us(60);
	onewire_high();
	_delay_ms(10);
}

void rw1990_write_bit(uint8_t data){
	onewire_low();
	if (data)
		_delay_us(6);
	else
		_delay_us(60);
	onewire_high();
	_delay_ms(2);
}

void rw1990_write(uint8_t data){
	for(uint8_t i=0;i<8;i++){
		rw1990_write_bit(data & 0x01);
		data>>=1;
	}
}

bool rw1990_newcode(uint8_t* buf){
	if (onewire_init()){
		onewire_send(0xD1);
		time_slot(0);
	}else return false;
	if(onewire_init()){
		onewire_send(0xD5);
		for(uint8_t i=0;i<8;i++){
			rw1990_write(~buf[i]);
		}
	}else return false;
	if (onewire_init()){
		onewire_send(0xD1);
		time_slot(1);
	}else return false;
	return true;
}

bool onewire_readrom(uint8_t* buf){
		if (onewire_init()){
			onewire_send(0x33);
			for(uint8_t i=0;i<8;i++){
				buf[i]=onewire_read();
			}
		}else return false;
		return true;
}

uint8_t  usart_read(){
while(!(UCSRA & (1 << RXC)));
	return UDR;
}


Конечно, можно было сделать все в ООП стиле, но почему-то я решил так.

Исходник так называемого мной терминала iButton
main.cpp
#include <Windows.h>
#include <iostream>
#include "ComPort.h"


using namespace std;

int main(){
	char buf_file[10];
	wchar_t file[10];
	char name[20];
	char command[10];
	unsigned char rom[8];

	cout<<"Enter COM port: ";
	cin>>buf_file;

	cout<<"Enter your name: ";
	cin>>name;

	mbstowcs(file,buf_file,10);
	ComPort port((LPCWSTR)file,CBR_9600);

	cout<<"Welcome "<<name<<"! "<<"If you need help, you can write command \"help\"."<<endl;
	while(strcmp(command,"exit")){
		cout<<name<<"> ";
		cin>>command;
		if(!strcmp(command,"write_rom")){
			scanf("%x %x %x %x %x %x %x %x",&rom[0],&rom[1],&rom[2],&rom[3],&rom[4],&rom[5],&rom[6],&rom[7]);
			for(int i=0;i<50;i++){
				port.ComWrite(0x40);
				port.ComWrite((char*)rom,sizeof(rom));
				char recv=port.ComRead();
				if(recv==0x45){ 
					cout<<"Device> write successfull!"<<endl;
					break;
				}else if(recv==0x46){ 
					cout<<"Device> write fail!"<<endl;
				}
				Sleep(100);
			}
		}else if(!strcmp(command,"read_rom")){
			for(int i=0;i<50;i++){
				port.ComWrite(0x30);
				char recv=port.ComRead();
				if(recv==0x35){ 
					for(int i=0;i<8;i++){
						rom[i]=port.ComRead();
					}
					cout<<"Device> read successfull!    ";
					printf("%02X %02X %02X %02X %02X %02X %02X %0X\n",rom[0],rom[1],rom[2],rom[3],rom[4],rom[5],rom[6],rom[7]);
					break;
				}else if(recv==0x36){ 
					cout<<"Device> read fail!"<<endl;
				}
				Sleep(100);
			}
		}
	}
}


ComPort.cpp
#include <Windows.h>
#include "ComPort.h"

ComPort::ComPort(LPCWSTR str,DWORD baud)
{
	hSerial=CreateFile(str,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
	ComPort::baud=baud;
	SerialParams.DCBlength=sizeof(SerialParams);
	if(!GetCommState(hSerial,&SerialParams))
		MessageBox(NULL,L"Getting state error!",L"Error!",MB_OK|MB_ICONERROR);
	SerialParams.BaudRate=baud;
	SerialParams.ByteSize=8;
	SerialParams.StopBits=ONESTOPBIT;
	SerialParams.Parity=NOPARITY;
	if(!SetCommState(hSerial,&SerialParams))
		MessageBox(NULL,L"Error setting serial port state!",L"Error!",MB_OK|MB_ICONERROR);

}

void ComPort::ComWrite(unsigned char buf)
{
	DWORD send;
	WriteFile(hSerial,&buf,1,&send,NULL);
}

void ComPort::ComWrite(char* buf,int size)
{
	DWORD send;
	WriteFile(hSerial,buf,size,&send,NULL);
}

bool ComPort::ComRead(char* buf,int size)
{
	DWORD recv;
	char recvchar;
	ZeroMemory(buf,size);
	for(int i=0;i<size;i++){
	ReadFile(hSerial,&recvchar,1,&recv,0);
	if(recvchar=='~')
		break;
	buf[i]=recvchar;
	}
	return true;
}

char ComPort::ComRead()
{
	DWORD recv;
	char recvchar;
	ReadFile(hSerial,&recvchar,1,&recv,0);
	return recvchar;
}

ComPort::~ComPort(void)
{
	CloseHandle(hSerial);
}



ComPort.h
class ComPort
{
private:
	HANDLE hSerial;
	DCB SerialParams;
	LPCWSTR sPort;
	int baud;
public:
	ComPort(LPCWSTR,DWORD);
	void ComWrite(unsigned char);
	void ComWrite(char*,int);
	bool ComRead(char*,int);
	char ComRead();
	~ComPort(void);
};






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

Вот что получилось у меня в конце:

image

image

image

image

А вот видео:

Комментарии (6)