Здравствуйте! Сегодня хочу рассказать о том, как сделать устройство, которое позволяет копировать 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) Макетная плата
С таким набором можно двигаться дальше. Для начала приготовьте плату, на которой будете размещать компоненты.
Я это сделал так:
Теперь давайте определимся с параметрами USART на atmega8. У меня они такие:
1) Стоповый бит — 1
2) 9600boud
3) Бит четности — нет
Я буду использовать внутренний генератор на 4Mhz, так что регистр UBRR я буду настраивать под эту частоту. Вот кусок кода, где идет настройка USART:
Пришло время разобрать протокол записи на болванки 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:
Кусок кода, где происходит запись:
Надеюсь, понятно. Нам требуется еще написать приложение, которое будет общаться с микроконтроллером. То есть мы будем посылать запросы на чтение и на запись ключа микроконтроллеру с компьютера. Все исходники я выложу под топиком. Приложение довольно простое.
Впрочем, на видео все показано.
Конечно, можно было сделать все в ООП стиле, но почему-то я решил так.
Код, конечно, не самый лучший, т.к. я пытался быстрее написать, но все же он прекрасно работает.
Вот что получилось у меня в конце:
А вот видео:
Итак, что нам понадобится для изготовления данного устройства:
1) Atmega8
2) FTDI RL232, преобразующий USB в USART
3) Драйвер для FTDI RL232
4) Программатор(Я использовал USBasp)
5) Visual Studio
6) Atmel Studio
7) Макетная плата
С таким набором можно двигаться дальше. Для начала приготовьте плату, на которой будете размещать компоненты.
Я это сделал так:
Теперь давайте определимся с параметрами 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:
Кусок кода, где происходит запись:
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);
};
Код, конечно, не самый лучший, т.к. я пытался быстрее написать, но все же он прекрасно работает.
Вот что получилось у меня в конце:
А вот видео: