#include "wiring_private.h" // utili funzioni sbi e cbi
#include <TinyDebug.h> // Per debug
// 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 TIMER
// Base dei tempi impostata su TIMER:
// 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 PORTB0
#define PORT1W PORTB
#define PIN1W PINB
#define DDR1W DDRB
#define BIT1W 0
#define BITPOW1W 1
// Paramentri x Interrupt su PINChange PORTB0
#define PCINT1W PCINT0
#define PCMSK1W PCMSK
#define PCMSKG GIMSK
#define PCICR1W PCIE
#define INTVECT1W PCINT0_vect
// Paramentri x Interrupt su TIMER
#define TIMVECT1W TIMER0_COMPA_vect
#define TIMER1W_OFF {cbi(TIMSK, OCIE0A); TCCR0A = TCCR0B = 0;}
// Base dei tempi impostata su TIMER2:
// tick = 0,5 us (prescaler/8 CLK=16MHz)
// Modo 0: Normal TOP=0xFF = 128 us
#define TIMER1W_ON {TCCR0B = _BV(CS01);TCNT0= 0; OCR0A=0xFF; sbi(TIMSK, OCIE0A);}
#define TCNT1W OCR0A
// 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_BEGIN 1
#define ST_GETNBIT 2
#define ST_SEND 3
#define ST_GET 4
#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, next1w, resto1w,
counter1w=0, nbit1w=0, nby1w, error1w=0, buf1w,
LastDiscrepancy, LastFamilyDiscrepancy, Lastzero,
Rom_NO[8], MaskROM, BitID, nDev1w;
volatile uint16_t crc8; // 16 Per fare un calcolo semplice senza "ROR"
volatile uint64_t vadd[5];
volatile uint8_t FlagPWmode;
volatile uint8_t data[10];
volatile uint8_t *data1w = data;
volatile union {
uint8_t w8[10];
struct {
uint8_t RCmd; // Comando ROM
uint64_t ad; // Indirizza
uint8_t FCmd; // COmando Funzione
}cmd1w;
}cmd1w;
volatile union {
uint8_t w8[8];
uint8_t w32[2];
uint64_t w64;
} ad1w;
//
volatile union {
struct {
int16_t temp;
int8_t th;
int8_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() {
cbi(DDR1W, BIT1W); // Stato normale = Input Opendrain
sbi(DDR1W, BITPOW1W); // Output servizio
cbi(PORT1W, BITPOW1W);
noInterrupts();
// Reset Imposatazioni TIMER standard Arduino
TIMER1W_OFF;
cbi(PCMSK1W, PCINT1W); // Disable Int su PIN CHANGE
sbi(PCMSKG, PCICR1W); // Ma abilito Maschera generale
interrupts();
Debug.begin();
}
void STimer1w(uint8_t st0, uint8_t st1){ // Avvia Interrupt TIMER
stato1w=st0; // dallo stato iniziale st0
next1w=st1; // Processo successivo
stato2w=sta1wtx=0;
sbi(RFLAG, HIGHLW); // Enable Main Function select
noInterrupts();
TIMER1W_ON;
interrupts();
}
char buffer[40];
void loop() {
while(true){
uint16_t sc=sc_menu();
Debug.println(sc);
uint8_t ss=sc%10;
uint8_t pp=sc%100;
if(sc > 99){ss=ss/10; sc=sc/10;};
switch(sc/10){
case 0:
tdPrintln(F("Ricerca ROM [0xF0] ..."));
data[0]=0xF0; // Search ROM
// nbit1w=8; // numero di bit cmd - non serve perché fisso
LastDiscrepancy=0;
STimer1w(ST_BEGIN,ST_GETNBIT);
while(stato1w);
if(error1w) break;
printDevices();
break;
case 1: // Read Power Supply mode device n
tdPrintln(F("Read PW"));
data[0]=0x55; // Match ROM
ad1w.w64 = vadd[ss];
for(uint8_t i=0; i<8; i++)data[i+1]=ad1w.w8[i];
data[9]=0xB4; // Read Power Supply
nbit1w=80; // numero di bit
STimer1w(ST_BEGIN,ST_SEND);
while(stato1w); // Attende completamento
if(error1w) break;
nbit1w=1;
STimer1w(ST_GET,0);
while(stato1w);
if(error1w) break;
print64(vadd[ss], 0);
tdPrint(F(" -> "));
Debug.println(data[0], BIN);
FlagPWmode &= ~(data[0] << ss);
FlagPWmode |= (data[0] << ss);
tdPrint(F("FlagPWmode: "));
Debug.println(FlagPWmode, BIN);
break;
case 2: // Broadcast Convert T"
data[0]=0xCC; // Skip ROM
data[1]=0x44; // Convert T
nbit1w=16; // numero di bit
// Debug.println((uint16_t) data1w, HEX);
STimer1w(ST_BEGIN,ST_SEND);
while(stato1w); // Attende completamento
// Debug.println((uint16_t) data1w, HEX);
if(error1w) break;
nbit1w=1;
STimer1w(ST_GET,0);
while(stato1w);
if(error1w) break;
Debug.print("FlagConverted: ");
Debug.println(data[0], BIN);
break;
case 3: // Read ScratchPas device n
data[0]=0x55; // Match ROM
ad1w.w64 = vadd[ss];
for(uint8_t i=0; i<8; i++)data[i+1]=ad1w.w8[i];
data[9]=0xBE; // Read ScratchPad
nbit1w=80; // numero di bit
STimer1w(ST_BEGIN,ST_SEND);
while(stato1w); // Attende completamento
if(error1w) break;
nbit1w=72;
STimer1w(ST_GET,0);
while(stato1w);
printSPad();
break;
case 4: // Write ScratchPas device n w/ conf
sp1w.stru.conf = (pp <<5) | 0x1F; // Update conf
data[0]=0x55; // Match ROM
ad1w.w64 = vadd[ss];
for(uint8_t i=0; i<8; i++)data[i+1]=ad1w.w8[i];
data[9]=0x4E; // Write ScratchPad
nbit1w=80; // numero di bit
STimer1w(ST_BEGIN,ST_SEND);
while(stato1w);
delay(10);
data[0]=data[1]=0; // Temperature di alarme
data[2]=sp1w.stru.conf; // Aggiorno precisione
nbit1w=24; // numero di bit
STimer1w(ST_SEND, 0);
break;
}
if(error1w){
Debug.print("Error: ");
Debug.println(error1w);
error1w=0;
printStatus();
}
}
}
void printSPad(){ // carica sp1w e stampa
for(uint8_t i=0; i<9; i++) sp1w.w8[i]=data[i];
uint8_t pt=sp1w.stru.conf >> 5;
pt = 1 << ++pt;
Debug.print("temp: ");
Debug.println((float ) (sp1w.stru.temp/pt));
// Debug.print(sp1w.stru.temp/pt);
// Debug.print(",");
// Debug.println(sp1w.stru.temp%pt);
Debug.print("TH: ");
Debug.println(sp1w.stru.th, HEX);
Debug.print("TL: ");
Debug.println(sp1w.stru.tl, HEX);
Debug.print("conf: ");
Debug.println(sp1w.stru.conf, HEX);
Debug.print("crc: ");
Debug.print(sp1w.stru.crc, HEX);
Debug.print("/");
Debug.println(crc8, HEX);
}
uint16_t sc_menu(){
tdPrintln(F("Scegli:"));
tdPrintln(F("00: Cerca ed elenca Devices nella rete"));
tdPrintln(F("1n: Read Power Supply mode device n"));
tdPrintln(F("20: Broadcast Convert T"));
tdPrintln(F("3n: Read ScratchPad device n"));
tdPrintln(F("4np: Imposta precisione p su device n"));
return(get_int3());
}
uint16_t get_int3(){ // Legge valore itnero da console da max 3
uint16_t sc=0;
uint8_t ib=0;
while(Debug.available() > 0) Debug.read();
do {
if(Debug.available() > 0) {
ib=Debug.read();
if(ib>='0' && ib <='9'){
ib-='0';
if(sc == 0) sc = ib;
else sc=sc*10+ib;
if(sc > 99) ib='\n';
}
}
} while(ib != '\n');
return(sc);
}
void printDevices(){
Debug.print("Trovati: ");
Debug.print(nDev1w);
Debug.println(" devices:");
for(uint8_t i=0; i<nDev1w; i++){
Debug.print(i);
Debug.print(": ");
print64(vadd[i], 1);
}
}
void print64(uint64_t v, uint8_t cr){
ad1w.w64=v;
for(int8_t i=7; i>=0; i--){
sprintf(buffer, "%02X", ad1w.w8[i]);
Debug.print(buffer);
}
if(cr) Debug.println("");
}
void printStatus(void){
Debug.print("Stato1w: ");
Debug.print(stato1w);
Debug.print(" Stato2w: ");
Debug.print(stato2w);
Debug.print(" Sta1wtx: ");
Debug.println(sta1wtx);
}
void printError(void){
Debug.print("Error: ");
Debug.println(error1w);
error1w=0;
}
/* 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(TIMVECT1W) { // 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
////////////////////////// Sezione LOW-Level STATUS ////////////////////////////
if(sta1wtx) switch (sta1wtx) { // Prima controllo low-level
case STRESET: // Impulso di reset è lungo
if(--counter1w == 0) {
sbi(PORT1W, BITPOW1W);
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)TCNT1W=-TUNO; else TCNT1W=-TZERO;
resto1w=-LENBIT-TCNT1W;
sbi(DDR1W, BIT1W); // Stato = Output L
cbi(PORT1W, BIT1W); sta1wtx++; // Next release bus
if(buf1w & 0x01) sbi(PORT1W, BITPOW1W); else cbi(PORT1W, BITPOW1W); // Debug
break;
case STWRITE+1: // Send Impulso Hign (Release bus)
cbi(DDR1W, BIT1W); // rilascio BUS
TCNT1W += resto1w; // Tempo mancante a LBIT
if(--nbit1w){if((nbit1w & 7) == 0)buf1w=*data1w++; else buf1w >>=1;sta1wtx--;
} else {sbi(RFLAG, HIGHLW); sta1wtx=0;}
break;
case STWGET1: // Lettura bit from SLAVE
TCNT1W=-TUNO;
resto1w=-LENBIT-TCNT1W;
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
TCNT1W += 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_BEGIN: // Funzione primaria RESET PULSE
switch(stato2w) { // E' una sub-FSA
case 0: // Send Reset Pulse
sta1wtx=STRESET;stato2w++;
sbi(DDR1W, BIT1W); cbi(PORT1W, BIT1W); // Stato = Output L
counter1w=RESETH1W;
TCNT1W= -RESETL1W;
cbi(RFLAG, HIGHLW);
break;
case 1: // Fase iniziale: sono qui se sono presenti dei Devices
// Mi assicuro di spettare almeno 256 tick
counter1w = 2; sta1wtx=STWAIT; stato2w=0; stato1w=next1w; break;
}; break;
case ST_GETNBIT: // Funzione primaria SEARCH ROM Send CMD contenuto in data
switch(stato2w) { // E' una sub-FSA
case 0: // finita attesa iniziale
data1w=data; buf1w=*data1w++; nbit1w=8;
stato2w++; sta1wtx=STWRITE; cbi(RFLAG, HIGHLW);
break;
case 1: // finita trasmissione, attendo che finisca la finestra di rilascio
stato2w++; break;
case 2: // finita trasmissione cmd, Inizio1-WGetNbit
if(LastDiscrepancy == 0) { // ciclo iniziale
LastFamilyDiscrepancy=Rom_NO[0]=nDev1w=0;};
MaskROM = BitID = 1;
Lastzero = ad1w.w8[0]= crc8 = nby1w= 0;
stato2w++; sta1wtx=STWGET1; cbi(RFLAG, HIGHLW);
TCNT1W=-2; // per partire subito
break;
case 3: // Determinato primo bit vero, get bit negato -- ciclo interno
stato2w++; sta1wtx=STWGET1; cbi(RFLAG, HIGHLW); buf1w <<=1;
TCNT1W += resto1w; // Tempo mancante a LBIT
break;
case 4: // 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); TCNT1W += resto1w; // Tempo mancante a LBIT
break;
case 5: // 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=3; 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; stato1w=ST_BEGIN;
TCNT1W=0;
} else { // Fine lavoro
sbi(PORT1W, BITPOW1W);KTimer1w(0);
}
} else {
KTimer1w(BAD_CRC8);
};
}
break;
}; break;
case ST_SEND: // Invia stringa di nbit1w presente su data[]
switch(stato2w) { // E' una sub-FSA
case 0: // finita trasmissione Reset ora trasmetto data
data1w=data; buf1w=*data1w++;
stato2w++; sta1wtx=STWRITE; cbi(RFLAG, HIGHLW);
break;
case 1: // Fine lavoro. Chi vuol leggere la risposta, legga!
sbi(PORT1W, BITPOW1W);KTimer1w(0);
break;
}; break;
case ST_GET: // Legge risposta di nbit1w su buf1w/data
switch(stato2w) { // E' una sub-FSA
case 0: // Stato iniziale di chiamata
stato2w++; sta1wtx=STWGET1; cbi(RFLAG, HIGHLW);
nby1w=buf1w=data[0]=crc8=0; MaskROM=1;
TCNT1W=-2; // per partire subito
break;
case 1: // Accumulo nbit1w
if(nbit1w > 8) { // Zona calcolo CRC8
if(buf1w ^ (crc8 & 1)) crc8 = (crc8 ^ CRCMASK) | 0x100;
crc8 >>=1; // shift right
}
if(buf1w & 1) data[nby1w] |= MaskROM;
if(--nbit1w){
if(MaskROM & 0x80) {
MaskROM=1; nby1w++; data[nby1w] = 0;
} else MaskROM <<=1;
sta1wtx=STWGET1; cbi(RFLAG, HIGHLW);
} else {sbi(PORT1W, BITPOW1W);KTimer1w(0)};
break;
} break;
case ST_EXIT: // Fine Funzione check errorw1
TIMER1W_OFF;
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
TCNT1W=0; sta1wtx++; break; // Reset Timeout wait for H
case STRESET+2: // Se son qui Slave changed to high before timeput
if(TCNT1W >= 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((TCNT1W - resto1w) < TUNO) buf1w |=1; // Coreggo lo zero attribuito
cbi(PCMSK1W, PCINT1W); // Disable Int su PIN CHANGE
sta1wtx=0; sbi(RFLAG, HIGHLW);
resto1w=TCNT1W+2; TCNT1W=-2; // processo subito il bit tramite MAIN INT
break; // Abilito itnervento MAIN Status
}
}