bądź unikalny i niepowtarzalny
W moje ręce trafiło kilka ciekawych scalaków. Są to układy zawierające 48-bitowe unikalne numery seryjne. Do komunikacji ze światem zewnętrznym wykorzystany jest interfejs 1 wire. Wszyscy znamy popularne układy do pomiaru temperatury DS1820 również posługujące się tym interfejsem. Jednak ten różni się tym, że wykorzystuje dwa piny (w mierniku temperatury można wykorzystać pin do niezależnego zasilania). Układ DS2401 nie posiada wydzielonego pinu do zasilania. By działać potrzebuje energii i do tego wykorzystuje linię danych DQ, mówiąc inaczej pasożytuje na zasilaniu z mikrokontrolera.
Zrobiłem układ badawczy w oparciu o procek ATMEGA32 (kiedyś skonstruowałem moduł do różnych eksperymentów). Schemat pokazuje rysunek:
Układ jest w obudowie SOT-223. Ponieważ różne faktorie numerują piny po swojemu, dodałem rysunek obudowy (z arkusza danych Maxim).
Środowisko testowe:
Testowy program:
Kod: Zaznacz cały
#define F_CPU 7372800UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include "ds2401.h"
#define USART_Speed 47
static const uint8_t HelloText [ ] PROGMEM = "Prezentacja obslugi DS2401\r\n" ;
static const uint8_t NotPresentText [ ] PROGMEM = "\r\nBrak ukladu DS2401\r\n" ;
static const uint8_t ReadyText [ ] PROGMEM = "Gotowe, numer seryjny: " ;
uint8_t Rom [ 8 ] ;
#define nop() __asm__ __volatile__ ("nop")
#define IdleState 0
#define BusyState 1
#define TXDBuffSize 64
#define RXDBuffSize 16
typedef struct { uint8_t CycFr ;
uint8_t CycTo ;
uint8_t State ;
uint8_t Buffer [ TXDBuffSize ] ;
} TXDRecT ;
typedef struct { uint8_t CycFr ;
uint8_t CycTo ;
uint8_t Buffer [ RXDBuffSize ] ;
} RXDRecT ;
static volatile TXDRecT TXDRec ;
static volatile RXDRecT RXDRec ;
SIGNAL ( USART_RXC_vect )
{
uint8_t RecVCh ;
/*---------------------------------------------------*/
RecVCh = UDR ;
RXDRec . Buffer [ RXDRec . CycTo ] = RecVCh ;
RXDRec . CycTo ++ ;
if ( RXDRec . CycTo >= RXDBuffSize )
RXDRec . CycTo = 0 ;
} /* USART_RXC_vect */
SIGNAL ( USART_TXC_vect )
{
/*---------------------------------------------------*/
if ( TXDRec . CycFr == TXDRec . CycTo )
{
TXDRec . State = IdleState ;
} /* if ... */
else
{
UDR = TXDRec . Buffer [ TXDRec . CycFr ] ;
TXDRec . CycFr ++ ;
if ( TXDRec . CycFr >= TXDBuffSize )
TXDRec . CycFr = 0 ;
} /* if ... else */ ;
} /* USART_TXC_vect */
static void USARTSoftwareInit ( void )
{
/*---------------------------------------------------*/
RXDRec . CycFr = 0 ;
RXDRec . CycTo = 0 ;
TXDRec . CycFr = 0 ;
TXDRec . CycTo = 0 ;
TXDRec . State = IdleState ;
} /* USARTSoftwareInit */
static void SendSerial ( uint8_t SerialCh )
{
uint8_t NextInx ;
/*---------------------------------------------------*/
cli ( ) ;
if ( TXDRec . State == IdleState )
{
TXDRec . State = BusyState ;
UDR = SerialCh ;
sei ( ) ;
} /* if ... */
else
{
for ( ; ; )
{
NextInx = TXDRec . CycTo + 1 ;
if ( NextInx >= TXDBuffSize )
NextInx = 0 ;
if ( NextInx != TXDRec . CycFr )
break ;
sei ( ) ;
nop ( ) ;
nop ( ) ;
cli ( ) ;
} /* for */ ;
TXDRec . Buffer [ TXDRec . CycTo ] = SerialCh ;
TXDRec . CycTo ++ ;
if ( TXDRec . CycTo >= TXDBuffSize )
TXDRec . CycTo = 0 ;
sei ( ) ;
} /* if ... else */ ;
} /* SendSerial */
static void SendSerialFlashTxt ( uint16_t String )
{
uint8_t Data ;
/*---------------------------------------------------*/
for ( ; ; )
{
Data = pgm_read_byte_near ( String ) ;
if ( Data )
SendSerial ( Data ) ;
else
break ;
String ++ ;
} /* for */ ;
} /* SendSerialFlashTxt */
static uint8_t SerialDataPresent ( void )
{
/*---------------------------------------------------*/
if ( RXDRec . CycFr == RXDRec . CycTo )
return ( 0 ) ;
else
return ( 1 ) ;
} /* SerialDataPresent */
static uint8_t GetSerial ( void )
{
uint8_t Ch ;
/*---------------------------------------------------*/
Ch = RXDRec . Buffer [ RXDRec . CycFr ] ;
RXDRec . CycFr ++ ;
if ( RXDRec . CycFr >= RXDBuffSize )
RXDRec . CycFr = 0 ;
return Ch ;
} /* GetSerial */
static void HardwareInit ( void )
{
/*---------------------------------------------------*/
UCSRA = 0 ;
UCSRB = ( 1 << RXCIE ) | ( 1 << TXCIE ) | ( 1 << RXEN ) | ( 1 << TXEN ) ;
UCSRC = ( 1 << URSEL ) | ( 1 << UCSZ1 ) | ( 1 << UCSZ0 ) ;
UBRRH = ( uint8_t ) ( ( USART_Speed >> 8 ) & 0xFF ) ;
UBRRL = ( uint8_t ) ( USART_Speed & 0xFF ) ;
} /* HardwareInit */
static void InitEnvir ( void )
{
/*---------------------------------------------------*/
OneWirePort &= ~ ( 1 << OneWireBit ) ;
} /* InitEnvir */
static uint8_t HexCh ( uint8_t Value )
{
/*---------------------------------------------------*/
Value &= 0x0F ;
switch ( Value )
{
case 0x00 :
return ( '0' ) ;
case 0x01 :
return ( '1' ) ;
case 0x02 :
return ( '2' ) ;
case 0x03 :
return ( '3' ) ;
case 0x04 :
return ( '4' ) ;
case 0x05 :
return ( '5' ) ;
case 0x06 :
return ( '6' ) ;
case 0x07 :
return ( '7' ) ;
case 0x08 :
return ( '8' ) ;
case 0x09 :
return ( '9' ) ;
case 0x0A :
return ( 'A' ) ;
case 0x0B :
return ( 'B' ) ;
case 0x0C :
return ( 'C' ) ;
case 0x0D :
return ( 'D' ) ;
case 0x0E :
return ( 'E' ) ;
case 0x0F :
return ( 'F' ) ;
} /* switch */ ;
return ( '?' ) ;
} /* HexCh */
static void DispByte ( uint8_t DataByte )
{
/*---------------------------------------------------*/
SendSerial ( HexCh ( DataByte >> 4 ) ) ;
SendSerial ( HexCh ( DataByte & 0x0F ) ) ;
SendSerial ( ' ' ) ;
} /* DispByte */
int main ( void )
{
/*---------------------------------------------------*/
HardwareInit ( ) ;
USARTSoftwareInit ( ) ;
InitEnvir ( ) ;
sei ( ) ;
SendSerialFlashTxt ( ( uint16_t ) HelloText ) ;
if ( DS2401ReadRom ( Rom ) )
{
// poprawny odczyt
// rom[0] = family code
// rom[1..6] = serial
// rom[7] = CRC
SendSerialFlashTxt ( ( uint16_t ) ReadyText ) ;
DispByte ( Rom [ 0 ] ) ;
DispByte ( Rom [ 1 ] ) ;
DispByte ( Rom [ 2 ] ) ;
DispByte ( Rom [ 3 ] ) ;
DispByte ( Rom [ 4 ] ) ;
DispByte ( Rom [ 5 ] ) ;
DispByte ( Rom [ 6 ] ) ;
DispByte ( Rom [ 7 ] ) ;
SendSerial ( '\r' ) ;
SendSerial ( '\n' ) ;
} /* if ... */
else
{
SendSerialFlashTxt ( ( uint16_t ) NotPresentText ) ;
} /* if ... else */ ;
for ( ; ; ) { } ;
}Do obsługi układu DS2401 napisałem dedykowany moduł, plik nagłówkowy:
Kod: Zaznacz cały
#ifndef _DS2401_
#define _DS2401_
#include <avr/io.h>
#define OneWirePort PORTD
#define OneWirePin PIND
#define OneWireConfigPort DDRD
#define OneWireBit PD7
extern uint8_t OW_Reset ( void ) ;
extern void OW_WriteBit ( uint8_t Bit ) ;
extern uint8_t OW_ReadBit ( void ) ;
extern void OW_WriteByte ( uint8_t Data ) ;
extern uint8_t OW_ReadByte ( void ) ;
extern uint8_t OW_Crc8 ( uint8_t * Data ,
uint8_t Lgt ) ;
extern uint8_t DS2401ReadRom ( uint8_t * Rom ) ;
#endiforaz plik implementacji:
Kod: Zaznacz cały
#define F_CPU 7372800UL
#include "DS2401.h"
#include <util/delay.h>
#include <avr/interrupt.h>
static inline void ow_low ( void )
{
/*---------------------------------------------------*/
OneWireConfigPort |= ( 1 << OneWireBit ) ;
OneWirePort &= ~ ( 1 << OneWireBit ) ;
} /* ow_low */
static inline void ow_release ( void )
{
/*---------------------------------------------------*/
OneWireConfigPort &= ~ ( 1 << OneWireBit ) ;
} /* ow_release */
static inline uint8_t ow_read_pin ( void )
{
/*---------------------------------------------------*/
return ( OneWirePin & ( 1 << OneWireBit ) ) ;
} /* ow_read_pin */
uint8_t OW_Reset ( void )
{
uint8_t Presence ;
/*---------------------------------------------------*/
cli ( ) ;
ow_low ( ) ;
_delay_us ( 500 ) ;
ow_release ( ) ;
_delay_us ( 70 ) ;
Presence = ! ow_read_pin ( ) ;
_delay_us ( 410 ) ;
sei ( ) ;
return Presence ;
} /* OW_Reset */
void OW_WriteBit ( uint8_t Bit )
{
/*---------------------------------------------------*/
cli ( ) ;
ow_low ( ) ;
if ( Bit )
{
_delay_us ( 6 ) ;
ow_release ( ) ;
_delay_us (64 ) ;
} /* if ... */
else
{
_delay_us ( 60 ) ;
ow_release ( ) ;
_delay_us ( 10 ) ;
} /* if ... else */ ;
sei ( ) ;
} /* OW_WriteBit */
uint8_t OW_ReadBit ( void )
{
uint8_t Bit ;
/*---------------------------------------------------*/
cli ( ) ;
ow_low ( ) ;
_delay_us ( 6 ) ;
ow_release ( ) ;
_delay_us ( 9 ) ;
Bit = ow_read_pin ( ) ? 1 : 0 ;
_delay_us ( 55 ) ;
sei ( ) ;
return Bit ;
} /* OW_ReadBit */
void OW_WriteByte ( uint8_t Data )
{
uint8_t Loop ;
/*---------------------------------------------------*/
for ( Loop = 0 ; Loop < 8 ; Loop ++ )
{
OW_WriteBit ( Data & 0x01 ) ;
Data >>= 1 ;
} /* for */ ;
} /* OW_WriteByte */
uint8_t OW_ReadByte ( void )
{
uint8_t Loop ;
uint8_t Data ;
/*---------------------------------------------------*/
Data = 0 ;
for ( Loop = 0 ; Loop < 8 ; Loop ++ )
{
if ( OW_ReadBit ( ) )
{
Data |= ( 1 << Loop ) ;
} /* if */ ;
} /* for */ ;
return Data ;
} /* OW_ReadByte */
uint8_t OW_Crc8 ( uint8_t * Data ,
uint8_t Lgt )
{
uint8_t Loop ;
uint8_t IntLoop ;
uint8_t Crc ;
uint8_t Inbyte ;
uint8_t Mix ;
/*---------------------------------------------------*/
Crc = 0 ;
for ( Loop = 0 ; Loop < Lgt ; Loop ++ )
{
Inbyte = Data [ Loop ] ;
for ( IntLoop = 0 ; IntLoop < 8 ; IntLoop ++ )
{
Mix = ( Crc ^ Inbyte ) & 0x01 ;
Crc >>= 1 ;
if ( Mix )
{
Crc ^= 0x8C ;
} /* if */ ;
Inbyte >>= 1 ;
} /* for */ ;
} /* for */ ;
return Crc ;
} /* OW_Crc8 */
uint8_t DS2401ReadRom ( uint8_t * Rom )
{
uint8_t Loop ;
/*---------------------------------------------------*/
if ( ! OW_Reset ( ) )
{
return 0 ;
} /* if */ ;
OW_WriteByte ( 0x33 ) ;
for ( Loop = 0 ; Loop < 8 ; Loop ++ )
{
Rom [ Loop ] = OW_ReadByte ( ) ;
} /* for */ ;
if ( OW_Crc8 ( Rom , 7 ) != Rom [ 7 ] )
{
return 0 ;
} /* if */ ;
return 1 ;
} /* DS2401ReadRom */Odczytany numer seryjny wysłany jest via serial (w PC użyłem popularny program Tera-term).
Teraz jak zbuduję jakieś urządzenie z wykorzystaniem tego numeru, to będę miał unikalny niepowtarzalny numer (przykładowo można użyć jako adres MAC w interfejsie ethernetowym).
Kompletny projekt w Atmet Studio 7:
