#include "wiring_private.h" // utili funzioni sbi e cbi
// After running the simulator, click on the DS18B20 chip to change the temperature
// Chip by bonnyr, source code: https://github.com/bonnyr/wokwi-ds1820-custom-chip/
// Gestione protocollo tramtie Interrupt e TIMER2
// Base dei tempi impostata su TIMER2:
// tick = 0,5 us (prescaler/8) tau = 128 us
// RESET_1W = 485 us = 3*128 + 101
// PRES1W > 50 us
// LENBIT = 60+10 us
// TZERO = 20 us (>15 us)
// TUNO = 6 us
#define RESETH1W 4
#define RESETL1W 200
#define PRES1W 100
#define LENBIT 140
#define TZERO 40
#define TUNO 6
// Gestione flag binarie
#define RFLAG GPIOR0
#define MOREBIT 0
#define SEARCHR 1
#define HIGHLW 2
// Uscita su PORTD2
#define PORT1W PORTD
#define PIN1W PIND
#define DDR1W DDRD
#define BIT1W 2
#define PINPOW1W 3
// Paramentri x Interrupt su PINChange PORTD2
#define PCINT1W PCINT18
#define PCMSK1W PCMSK2
#define PCICR1W PCIE2
#define INTVECT1W PCINT2_vect
// Errori di ritorno
#define NO_DEVICE_PRESENT 1
#define SHORT_PRESENCE 2
#define LONG_PRESENCE 3
#define NO_CMD_FEED 4
#define BAD_NETWORK 5
#define BAD_CRC8 6
// STATI FSA
// Main function
#define ST_SEARCHROM 1
#define ST_EXIT 9
// Low-level function
#define STRESET 1
#define STWAIT 4
#define STWRITE 5
#define STWGET1 7
// maschera per implementare 2 de 3 XOR per calcolo CRC = X^8 + X^5 + X^4 + 1
#define CRCMASK 0x18
//
// Macro per Kill Timer
// NOTE DIDATTICA: provare a definirla come funzione e sostituirla al case ST_EXIT
#define KTimer1w(ERR) {sbi(RFLAG, HIGHLW); sta1wtx=0; stato1w=ST_EXIT; error1w=ERR;}
// Variabili di stato 1-Wire
volatile uint8_t stato1w=0, stato2w=0, sta1wtx, resto1w,
counter1w=0, nbit1w=0, nby1w, error1w=0, *data1w, buf1w,
LastDiscrepancy, LastFamilyDiscrepancy, Lastzero,
Rom_NO[8], MaskROM, BitID, nDev1w;
volatile uint16_t crc8; // Per copret fare un calcolo semplice senza ROR
volatile uint64_t vadd[5];
uint8_t data[10];
volatile union {
uint8_t w8[10];
struct {
uint8_t RCmd; // Comando ROM
uint64_t ad; // Indirizza
uint8_t FCmd; // COmando Funzione
}cmd1w;
}
volatile union {
uint8_t w8[8];
uint8_t w32[2];
uint64_t w64;
} ad1w;
//
volatile union {
struct {
uint16_t temp;
uint8_t th;
uint8_t tl;
uint8_t conf;
uint8_t res[3]; //0xFF, xx. 0x10
uint8_t crc; // CRC
} stru; // Struttura ScratchPad
uint8_t w8[9];
uint64_t w64;
} sp1w;
void setup(){
noInterrupts();
// Reset Imposatzioni standard Arduino
TCCR2A = TCCR2B = 0;
cbi(DDR1W, BIT1W); // Stato normale = Input Opendrain
sbi(DDR1W, PINPOW1W); // Output servizio
cbi(PORT1W, PINPOW1W);
cbi(TIMSK2, TOIE2); // TImer da attivare quando necessario
cbi(PCMSK1W, PCINT1W); // Disable Int su PIN CHANGE
sbi(PCICR, PCICR1W); // Ma abilito Maschera generale
interrupts();
Serial.begin(9600);
}
void STimer1w(uint8_t st0, uint8_t st1){ // Avvia Interrupt TIMER
stato1w=st0; // dallo stato iniziale st0
stato2w=st1;
sta1wtx=0;
sbi(RFLAG, HIGHLW); // Enable Main Function select
// Base dei tempi impostata su TIMER2:
// tick = 0,5 us (prescaler/8)
// Modo 0: Normal TOP=0xFF = 128 us
noInterrupts();
TCCR2B = _BV(CS21);
TCNT2= 0; // così parte subito o quasi
sbi(TIMSK2, TOIE2); // scatta interrupt on overflow
interrupts();
}
void SendCmd1w(uint8_t cmd) {
data[0]=cmd;
LastDiscrepancy=0;
STimer1w(ST_SEARCHROM,0);
}
char buffer[40];
void loop() {
uint8_t os=stato1w;
if(stato1w == 0){
Serial.println("Sending Ricerca ROM [0xF0] ...");
SendCmd1w(0xF0);
}
while(stato1w){
if(os != stato1w) {
os=stato1w;
Serial.print("Stato1w: ");
Serial.print(stato1w);
Serial.print(" Stato2w: ");
Serial.print(stato2w);
Serial.print(" Sta1wtx: ");
Serial.println(sta1wtx);
}
};
if(error1w) {
Serial.print("Error: ");
Serial.print(error1w);
error1w=0;
} else {
Serial.print("Completed: ");
Serial.print("Stato1w: ");
Serial.print(stato1w);
Serial.print(" Stato2w: ");
Serial.print(stato2w);
Serial.print(" Sta1wtx: ");
Serial.println(sta1wtx);
Serial.println(buf1w, BIN);
}
/* Serial.print(" crc8: ");
Serial.print(crc8, HEX);
*/
Serial.print(" nDev: ");
Serial.println(nDev1w);
for(uint8_t i=0; i<nDev1w; i++){
Serial.print("\n");
Serial.print(i);
Serial.print(": ");
print64(vadd[i]);
}
while(true);
// put your main code here, to run repeatedly:
}
void print64(uint64_t v){
Serial.print(" addr: ");
ad1w.w64=v;
for(int8_t i=7; i>=0; i--){
sprintf(buffer, "%02X", ad1w.w8[i]);
Serial.print(buffer);
}
}
/* FSA per la gestione della trasmissizone 1-Wire
* sta1wtx = Stato per read/write low-level
* stato1w = Stato Funzioni di alto livello x dirottare low-level
*/
ISR(TIMER2_OVF_vect) { // Implementazione a doppio livello di stato
// static void *vcase[] = {&&statox, &&stato1, &&stato2, &&stato3, &&stato4, &&stato5, &&stato6, &&stato7, &&stato8, &&stato9};
// goto *vcase[stato1w];
if(stato1w){ // Master condition
if(sta1wtx) switch (sta1wtx) { // Prima controllo low-level
case STRESET: // Impulso di reset è lungo
if(--counter1w == 0) {
sbi(PORT1W, PINPOW1W);
cbi(DDR1W, BIT1W); // rilascio BUS
sta1wtx++; // Per getire il timeout
sbi(PCMSK1W, PCINT1W); // Enable Int su PIN CHANGE
}; cbi(RFLAG, HIGHLW); break;
case STRESET+1: // Se arrivo qua, wuol dire che non ci sono device che rispondono
KTimer1w(NO_DEVICE_PRESENT);break;
case STRESET+2: // Se arrivo qua, wuol dire che INTPC rileva overflow conteggio Presence Low
KTimer1w(LONG_PRESENCE); break;
case STWAIT: // Generica attesa lunga
if(--counter1w) cbi(RFLAG, HIGHLW); else sbi(RFLAG, HIGHLW);
break;
case STWRITE: // Write Stream, ciclico until 0
if(buf1w & 0x01)TCNT2=-TUNO; else TCNT2=-TZERO;
resto1w=-LENBIT-TCNT2;
sbi(DDR1W, BIT1W); // Stato = Output L
cbi(PORT1W, BIT1W); sta1wtx++; // Next release bus
break;
case STWRITE+1: // Send Impulso Hign (Release bus)
cbi(DDR1W, BIT1W); // rilascio BUS
TCNT2 += resto1w; // Tempo mancante a LBIT
if(--nbit1w){if(nbit1w & 0x7 == 0)buf1w=*data1w++; else buf1w >>=1;sta1wtx--;
} else {sbi(RFLAG, HIGHLW); sta1wtx=0;}
break;
case STWGET1: // Lettura bit from SLAVE
TCNT2=-TUNO;
resto1w=-LENBIT-TCNT2;
sbi(DDR1W, BIT1W); // Stato = Output L
cbi(PORT1W, BIT1W); sta1wtx++; // Next release bus
break;
case STWGET1+1: // Release bus, read SLAVE
cbi(DDR1W, BIT1W); // rilascio BUS
if(bit_is_set(PIN1W, BIT1W)){
buf1w |=1; // imasto alto? allora è UNO sicuro
sbi(RFLAG, HIGHLW); // e processo subito il resto
} else { // è probabilmente ZERO ma verifico con PCINT
buf1w &=0xFE; // Intanto provo a mettere zero
TCNT2 += resto1w; // Comunico a Int ilTempo mancante a LBIT
sbi(PCMSK1W, PCINT1W); // Enable Int su PIN CHANGE
}
break;
};
///////////////////// Sezione MAIN STATUS ///////////////////////////////////
if(bit_is_set(RFLAG, HIGHLW))switch (stato1w) { // Funzioni di alto livello
case ST_SEARCHROM: // Funzione primaria SEARCH ROM
switch(stato2w) { // E' una sub-FSA
case 0: // Send Reset Pulse and CMD contenuto in data
data1w=data; nbit1w=8; sta1wtx=STRESET;stato2w++;
sbi(DDR1W, BIT1W); cbi(PORT1W, BIT1W); // Stato = Output L
counter1w=RESETH1W;
TCNT2= -RESETL1W;
cbi(RFLAG, HIGHLW);
break;
case 1: // Fase iniziale: sono qui se sono presenti dei Devices
// Invio uno Stream con attesa prima di inviare
counter1w = 2; sta1wtx=STWAIT; stato2w++; break;
case 2: // finita attesa iniziale
buf1w=*data1w++;
stato2w++; sta1wtx=STWRITE; cbi(RFLAG, HIGHLW);
break;
case 3: // finita trasmissione, attendo che finisca la finestra di rilascio
stato2w++; break;
case 4: // finita trasmissione, Inizio1-WGetNbit
if(LastDiscrepancy == 0) { // ciclo iniziale
LastFamilyDiscrepancy=Rom_NO[0]=nDev1w=0;
// uint64_t *v=(uint64_t *)Rom_NO; // Rom_No è un vettore di 8 uint8_t
// *v=0; //
}
MaskROM = BitID = 1;
Lastzero = ad1w.w8[0]= crc8 = nby1w= 0;
stato2w++; sta1wtx=STWGET1; cbi(RFLAG, HIGHLW);
TCNT2=-2; // per partire subito
break;
case 5: // Determinato primo bit vero, get bit negato
stato2w++; sta1wtx=STWGET1; cbi(RFLAG, HIGHLW); buf1w <<=1;
TCNT2 += resto1w; // Tempo mancante a LBIT
break;
case 6: // Arrvato bit negato - elaboro risutato WREAD2C/WGetNbit
if(buf1w == 0){ // Discrepancy
if(BitID == LastDiscrepancy) buf1w=1;
else {
if(BitID > LastDiscrepancy) buf1w=0;
else {
if(Rom_NO[nby1w] & MaskROM)buf1w=1; else buf1w=0;
}
}
if(buf1w == 0){
Lastzero=BitID;
if(BitID < 9)LastFamilyDiscrepancy=BitID;
}
} else {
if(buf1w == 3) KTimer1w(BAD_NETWORK) else buf1w >>=1;
}
nbit1w=1; stato2w++; sta1wtx=STWRITE; // Trasmetto bit recepito
cbi(RFLAG, HIGHLW); TCNT2 += resto1w; // Tempo mancante a LBIT
break;
case 7: // Trasmesso 1 bit, accumulo come WGetNbit
if(BitID <= 56) { // Zona Address, calcolo CRC8
if(buf1w ^ (crc8 & 1)) crc8 = (crc8 ^ CRCMASK) | 0x100;
crc8 >>=1; // shift right
}
if(buf1w & 1) ad1w.w8[nby1w] |= MaskROM;
if(BitID < 64){
if(MaskROM & 0x80) {
MaskROM=1; Rom_NO[nby1w] |= ad1w.w8[nby1w]; nby1w++;
Rom_NO[nby1w] = ad1w.w8[nby1w] = 0;
} else MaskROM <<=1;
BitID++; stato2w=5; sta1wtx=STWGET1; cbi(RFLAG, HIGHLW);
} else { // Fine lavoro Controlli WSEARCHROM1
if(ad1w.w8[7] == crc8) { // CRC corretto
vadd[nDev1w++]= ad1w.w64;
Rom_NO[7] |= ad1w.w8[7];
if(Lastzero) { // WSEARCHROM: trova next device
LastDiscrepancy=Lastzero;
stato2w = 0;
TCNT2=0;
} else { // Fine lavoro
sbi(PORT1W, PINPOW1W);KTimer1w(0);
}
} else {
KTimer1w(BAD_CRC8);
};
}
break;
};
break;
case ST_EXIT: // Fine Funzione check errorw1
cbi(TIMSK2, TOIE2); // disabilito INT Timer2
TCCR2A = TCCR2B = 0; // Reset all
stato1w=sta1wtx=0; //
cbi(DDR1W, BIT1W); // /Per sicurezza rilascio BUS
break;
};
}
}
// Gesdtone eventi pinchange
ISR(INTVECT1W){ // Service routine per input 1-wire
if(stato1w) switch (sta1wtx){ // evito falsi allarmi
case STRESET+1: // Wait for SLAVE change to low
TCNT2=0; sta1wtx++; break; // Reset Timeout wait for H
case STRESET+2: // Se son qui Slave changed to high before timeput
if(TCNT2 >= PRES1W) sta1wtx=0; else KTimer1w(SHORT_PRESENCE);
cbi(PCMSK1W, PCINT1W); // Disable Int su PIN CHANGE
sbi(RFLAG, HIGHLW); break; // Abilito itnervento MAIN Status
case STWGET1+1: // Read bit misuro impulso risposta Low
if((TCNT2 - resto1w) < TUNO) buf1w |=1; // Coreggo lo zero attribuito
cbi(PCMSK1W, PCINT1W); // Disable Int su PIN CHANGE
sta1wtx=0; sbi(RFLAG, HIGHLW);
resto1w=TCNT2+2; TCNT2=-2; // processo subito il bit tramite MAIN INT
break; // Abilito itnervento MAIN Status
}
}