Копіювання ключів dallas. Запис на rw1990(rw1990.1)

Доброго дня! Сьогодні хочу розповісти про те, як зробити пристрій, який дозволяє копіювати 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;
}


Звичайно, можна було зробити все в ООП стилі, але чомусь я вирішив так.

Ісходник так званого мною терміналу iButtonmain.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

А ось відео:


Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.