giovedì 24 gennaio 2019
SyQuest EzFlyer
Continuano le puntate del Piccolo Museo dell'informatica Innocenti con SyQuest EzFlyer, un sistema molto simile ad IOMega Zip ma con meno fortuna
mercoledì 23 gennaio 2019
Wakeup Arduino Uno con interrupt da MPU6050
L'accelerometro MPU6050 (altrimenti conosciuto come Gy-521) ha un pin interrupt che puo' essere programmato per risvegliare una Arduino Uno
Ho semplificato un po' il codice ritrovato a questo link
si possono personalizzare le soglie di attivazione dell'interrupt modificando il valore di MOT_THR e MOT_DUR
-----------------------------------------------------------------------
#include <avr/sleep.h>
#include <Wire.h>
#define SIGNAL_PATH_RESET 0x68
#define I2C_SLV0_ADDR 0x37
#define ACCEL_CONFIG 0x1C
#define MOT_THR 0x1F // Motion detection threshold bits [7:0]
#define MOT_DUR 0x20 // Duration counter threshold for motion interrupt generation, 1 kHz rate, LSB = 1 ms
#define MOT_DETECT_CTRL 0x69
#define INT_ENABLE 0x38
#define WHO_AM_I_MPU6050 0x75 // Should return 0x68
#define INT_STATUS 0x3A
//when nothing connected to AD0 than address is 0x68
#define ADO 0
#if ADO
#define MPU6050_ADDRESS 0x69 // Device address when ADO = 1
#else
#define MPU6050_ADDRESS 0x68 // Device address when ADO = 0
#endif
int wakePin = 2; // pin used for waking up
void wakeUpNow()
{
}
void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
{
Wire.begin();
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.write(data); // Put data in Tx buffer
Wire.endTransmission(); // Send the Tx buffer
}
uint8_t readByte(uint8_t address, uint8_t subAddress)
{
uint8_t data; // `data` will store the register data
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive
Wire.requestFrom(address, (uint8_t) 1); // Read one byte from slave register address
data = Wire.read(); // Fill Rx buffer with result
return data; // Return data read from slave register
}
void setup()
{
Serial.begin(9600);
writeByte( MPU6050_ADDRESS, 0x6B, 0x00);
writeByte( MPU6050_ADDRESS, SIGNAL_PATH_RESET, 0x07);//Reset all internal signal paths in the MPU-6050 by writing 0x07 to register 0x68;
writeByte( MPU6050_ADDRESS, I2C_SLV0_ADDR, 0x20);//write register 0x37 to select how to use the interrupt pin. For an active high, push-pull signal that stays until register (decimal) 58 is read, write 0x20.
writeByte( MPU6050_ADDRESS, ACCEL_CONFIG, 0x01);//Write register 28 (==0x1C) to set the Digital High Pass Filter, bits 3:0. For example set it to 0x01 for 5Hz. (These 3 bits are grey in the data sheet, but they are used! Leaving them 0 means the filter always outputs 0.)
writeByte( MPU6050_ADDRESS, MOT_THR, 20); //Write the desired Motion threshold to register 0x1F (For example, write decimal 20).
writeByte( MPU6050_ADDRESS, MOT_DUR, 40 ); //Set motion detect duration to 1 ms; LSB is 1 ms @ 1 kHz rate
writeByte( MPU6050_ADDRESS, MOT_DETECT_CTRL, 0x15); //to register 0x69, write the motion detection decrement and a few other settings (for example write 0x15 to set both free-fall and motion decrements to 1 and accelerometer start-up delay to 5ms total by adding 1ms. )
writeByte( MPU6050_ADDRESS, INT_ENABLE, 0x40 ); //write register 0x38, bit 6 (0x40), to enable motion detection interrupt.
writeByte( MPU6050_ADDRESS, 0x37, 160 ); // now INT pin is active low
pinMode(2, INPUT); // sets the digital pin 7 as input
pinMode(wakePin, INPUT_PULLUP); // wakePin is pin no. 2
}
void sleepNow()
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
delay(500);
attachInterrupt(0,wakeUpNow, LOW);
delay(500);
sleep_mode();
//ritorno da interrupt
delay(500);
sleep_disable();
delay(500);
detachInterrupt(0);
}
uint16_t readdata;
void loop()
{
sleepNow();
/*readdata = readByte(MPU6050_ADDRESS,0x3A);
Serial.print(readdata);Serial.print("-");
readdata = readByte(MPU6050_ADDRESS,0x37);
Serial.println(readdata);*/
Serial.println("Movimento registrato");
}
Ho semplificato un po' il codice ritrovato a questo link
si possono personalizzare le soglie di attivazione dell'interrupt modificando il valore di MOT_THR e MOT_DUR
-----------------------------------------------------------------------
#include <avr/sleep.h>
#include <Wire.h>
#define SIGNAL_PATH_RESET 0x68
#define I2C_SLV0_ADDR 0x37
#define ACCEL_CONFIG 0x1C
#define MOT_THR 0x1F // Motion detection threshold bits [7:0]
#define MOT_DUR 0x20 // Duration counter threshold for motion interrupt generation, 1 kHz rate, LSB = 1 ms
#define MOT_DETECT_CTRL 0x69
#define INT_ENABLE 0x38
#define WHO_AM_I_MPU6050 0x75 // Should return 0x68
#define INT_STATUS 0x3A
//when nothing connected to AD0 than address is 0x68
#define ADO 0
#if ADO
#define MPU6050_ADDRESS 0x69 // Device address when ADO = 1
#else
#define MPU6050_ADDRESS 0x68 // Device address when ADO = 0
#endif
int wakePin = 2; // pin used for waking up
void wakeUpNow()
{
}
void writeByte(uint8_t address, uint8_t subAddress, uint8_t data)
{
Wire.begin();
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.write(data); // Put data in Tx buffer
Wire.endTransmission(); // Send the Tx buffer
}
uint8_t readByte(uint8_t address, uint8_t subAddress)
{
uint8_t data; // `data` will store the register data
Wire.beginTransmission(address); // Initialize the Tx buffer
Wire.write(subAddress); // Put slave register address in Tx buffer
Wire.endTransmission(false); // Send the Tx buffer, but send a restart to keep connection alive
Wire.requestFrom(address, (uint8_t) 1); // Read one byte from slave register address
data = Wire.read(); // Fill Rx buffer with result
return data; // Return data read from slave register
}
void setup()
{
Serial.begin(9600);
writeByte( MPU6050_ADDRESS, 0x6B, 0x00);
writeByte( MPU6050_ADDRESS, SIGNAL_PATH_RESET, 0x07);//Reset all internal signal paths in the MPU-6050 by writing 0x07 to register 0x68;
writeByte( MPU6050_ADDRESS, I2C_SLV0_ADDR, 0x20);//write register 0x37 to select how to use the interrupt pin. For an active high, push-pull signal that stays until register (decimal) 58 is read, write 0x20.
writeByte( MPU6050_ADDRESS, ACCEL_CONFIG, 0x01);//Write register 28 (==0x1C) to set the Digital High Pass Filter, bits 3:0. For example set it to 0x01 for 5Hz. (These 3 bits are grey in the data sheet, but they are used! Leaving them 0 means the filter always outputs 0.)
writeByte( MPU6050_ADDRESS, MOT_THR, 20); //Write the desired Motion threshold to register 0x1F (For example, write decimal 20).
writeByte( MPU6050_ADDRESS, MOT_DUR, 40 ); //Set motion detect duration to 1 ms; LSB is 1 ms @ 1 kHz rate
writeByte( MPU6050_ADDRESS, MOT_DETECT_CTRL, 0x15); //to register 0x69, write the motion detection decrement and a few other settings (for example write 0x15 to set both free-fall and motion decrements to 1 and accelerometer start-up delay to 5ms total by adding 1ms. )
writeByte( MPU6050_ADDRESS, INT_ENABLE, 0x40 ); //write register 0x38, bit 6 (0x40), to enable motion detection interrupt.
writeByte( MPU6050_ADDRESS, 0x37, 160 ); // now INT pin is active low
pinMode(2, INPUT); // sets the digital pin 7 as input
pinMode(wakePin, INPUT_PULLUP); // wakePin is pin no. 2
}
void sleepNow()
{
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
delay(500);
attachInterrupt(0,wakeUpNow, LOW);
delay(500);
sleep_mode();
//ritorno da interrupt
delay(500);
sleep_disable();
delay(500);
detachInterrupt(0);
}
uint16_t readdata;
void loop()
{
sleepNow();
/*readdata = readByte(MPU6050_ADDRESS,0x3A);
Serial.print(readdata);Serial.print("-");
readdata = readByte(MPU6050_ADDRESS,0x37);
Serial.println(readdata);*/
Serial.println("Movimento registrato");
}
Livello Automatico Stanley AL24
Questo livello viene venduto in kit con il treppiede e la stadia da 5 m. E' uno strumento economico e di prestazioni adeguate al prezzo....l'unica cosa veramente strana e' che la stadia non presenta la bolla circolare
La distanza massima tra due punti e' di circa 50-60 m (considerando la posizione del cannochiale al centro tra i due punti)
Per misurare le distanze si usano i due riferimenti al di sopra ed al di sotto del crocefilo. Per esempio nell'immagine sotto riportate le misure sono circa 1,395 e 1,265. La distanza della stadia (dato il rapporto 1:100) puo' essere calcolata come 1,395-1,265=0,13*100 = 13 m
La distanza massima tra due punti e' di circa 50-60 m (considerando la posizione del cannochiale al centro tra i due punti)
La stadia non e' verticale ma ero da solo ;> |
Persistenza delle variabili in Alexa Skills
La persistenza delle variabili in Alexa Skills con NodeJS si ottiene tramite PersistantAttributes che si appoggia su DynamoDB in modo trasparente
In questa prima fase viene settata una variabile (per semplicita' statica) in modo persistente. Se non e' settata crea una nuova stringa. Importante impostare la chiamata come asincrona
Da notare che le variabili sono relative ad un determinato account per cui ogni utente ritrovera' le proprie impostazioni
---------------------------------------------------------------------------
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
async handle(handlerInput) {
const attributesManager = handlerInput.attributesManager;
var speechText = "Benvenuto nella skill ";
const attributes = await attributesManager.getPersistentAttributes() || {};
if (Object.keys(attributes).length === 0) {
attributes.nameValue = "Luca";
attributesManager.setPersistentAttributes(attributes);
await attributesManager.savePersistentAttributes();
}else {
speechText = "Benvenuto ${attributes.nameValue.toString()}";
}
return handlerInput.responseBuilder
.speak(speechText)
.withShouldEndSession(false)
.getResponse();
},
};
---------------------------------------------------------------------------
Con questo codice si puo' richiamare il valore della variabile da altra intent
---------------------------------------------------------------------------
const attributesManager = handlerInput.attributesManager;
const attributes = await attributesManager.getPersistentAttributes()
var speechOutput = attributes.nameValue.toString();
---------------------------------------------------------------------------
Per salvare i dati e' necessario impostare il nome della tabella in cui sono salvate le variabili e la possibilita' di creare in automatico la tabella ove non esista
---------------------------------------------------------------------------
const skillBuilder = Alexa.SkillBuilders.standard();
exports.handler = skillBuilder
.addRequestHandlers(
LaunchRequestHandler,
YesHandler,
geo,
HelpHandler,
ExitHandler,
SessionEndedRequestHandler
)
.addErrorHandlers(ErrorHandler)
.withTableName('alexa-data')
.withAutoCreateTable(true)
.lambda();
---------------------------------------------------------------------------
In questa prima fase viene settata una variabile (per semplicita' statica) in modo persistente. Se non e' settata crea una nuova stringa. Importante impostare la chiamata come asincrona
Da notare che le variabili sono relative ad un determinato account per cui ogni utente ritrovera' le proprie impostazioni
---------------------------------------------------------------------------
const LaunchRequestHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
},
async handle(handlerInput) {
const attributesManager = handlerInput.attributesManager;
var speechText = "Benvenuto nella skill ";
const attributes = await attributesManager.getPersistentAttributes() || {};
if (Object.keys(attributes).length === 0) {
attributes.nameValue = "Luca";
attributesManager.setPersistentAttributes(attributes);
await attributesManager.savePersistentAttributes();
}else {
speechText = "Benvenuto ${attributes.nameValue.toString()}";
}
return handlerInput.responseBuilder
.speak(speechText)
.withShouldEndSession(false)
.getResponse();
},
};
---------------------------------------------------------------------------
Con questo codice si puo' richiamare il valore della variabile da altra intent
---------------------------------------------------------------------------
const attributesManager = handlerInput.attributesManager;
const attributes = await attributesManager.getPersistentAttributes()
var speechOutput = attributes.nameValue.toString();
---------------------------------------------------------------------------
Per salvare i dati e' necessario impostare il nome della tabella in cui sono salvate le variabili e la possibilita' di creare in automatico la tabella ove non esista
---------------------------------------------------------------------------
const skillBuilder = Alexa.SkillBuilders.standard();
exports.handler = skillBuilder
.addRequestHandlers(
LaunchRequestHandler,
YesHandler,
geo,
HelpHandler,
ExitHandler,
SessionEndedRequestHandler
)
.addErrorHandlers(ErrorHandler)
.withTableName('alexa-data')
.withAutoCreateTable(true)
.lambda();
---------------------------------------------------------------------------
martedì 22 gennaio 2019
Emulatore in Pascal per Commodore 64
Mi sono imbattuto per caso in questo progetto ospitato su GitHub (che rimanda pero' ad un progetto del 2008) in cui viene programmato un semplice emulatore per Commodore 64 e lo ho trovato molto istruttivo perche' il codice (forse perche' in Pascal) e' molto semplice da seguire ed offre uno spaccato della programmazione di una macchina di emulazione
Si parte semplicemente creando un array di 64K byte che rappresentano la memoria di un C64 (si vedra' in seguito che parte di questi 64Kb saranno occupati da ROM)
Ognuna delle celle dell'array rappresenta una locazione di memoria che puo' essere letta o scritta con un semplice accesso all'array mediante puntatore
Si definiscono quindi le variabili byte A (accumulatore),X,Y,S (stack pointer varia da 0x01FF a 0x0100) ,P (processor status usato per contenere delle flags) ed IR (instruction register, contenuto di memoria che si utilizza) che rappresentano i registri della cpu 6510 con il registro PC (Program counter) che e' di lunghezza word 16 bit (dato che deve contenere un numero 0.65535 )
Il ciclo base dell'emulatore semplicemente legge il contenuto della memoria puntato da PC, interpreta l'istruzione macchine ed modifica il Program Counter per andare alla successiva istruzione
=================================
while true do
begin
IR := peek(PC);
inc(PC);
case IR of
$00 : interpret instruction $00
$01 : interpret instruction $01
else : unknown instruction encountered
end;
end.
=================================
Emulatore in esecuzione in shell su Linux |
Si parte semplicemente creando un array di 64K byte che rappresentano la memoria di un C64 (si vedra' in seguito che parte di questi 64Kb saranno occupati da ROM)
Ognuna delle celle dell'array rappresenta una locazione di memoria che puo' essere letta o scritta con un semplice accesso all'array mediante puntatore
Si definiscono quindi le variabili byte A (accumulatore),X,Y,S (stack pointer varia da 0x01FF a 0x0100) ,P (processor status usato per contenere delle flags) ed IR (instruction register, contenuto di memoria che si utilizza) che rappresentano i registri della cpu 6510 con il registro PC (Program counter) che e' di lunghezza word 16 bit (dato che deve contenere un numero 0.65535 )
Il ciclo base dell'emulatore semplicemente legge il contenuto della memoria puntato da PC, interpreta l'istruzione macchine ed modifica il Program Counter per andare alla successiva istruzione
=================================
while true do
begin
IR := peek(PC);
inc(PC);
case IR of
$00 : interpret instruction $00
$01 : interpret instruction $01
else : unknown instruction encountered
end;
end.
=================================
Si devono quindi implementare tutti i codici macchina e come questi modificano lo stato dei registri e le locazioni di memoria ed il flusso di programma
Il 6510 ha circa una ottantina di Opcode da implementare per realizzare un emulatore
Il 6510 usa come base un sistema little endian per cui il byte meno significativo di una word e' registrato nella prima locazione di memoria (o locazione di memoria piu' bassa)
Impostata l'emulazione del processore il passo successivo e' quello di inizializzare le ROM del C64 che, al minimo, devono contenere il Kernal ma puo' essere utile anche avere il Basic. Si prendono i due file del Kernal e del Basic, si legge il contenuto e lo si copia rispettivamente alle locazioni di memoria $A000...$BFFF e $E000...$FFFF. (un altro settore di memoria destinato alla ROM e' compreso tra $D000 e $DFFF....tutto il resto della memoria..anche se frammentato e' considerata RAM)
Il boot della macchina inizia leggendo il valore della locazione $FFFC-$FFFD ed impostando il program counter (una word) a questa locazione (in pratica viene lanciato un Cold Reset della macchina. Di default il valore letto e' $FCE2 (ovvero il mitico SYS 64738)
Il codice ROM si aspetta di dover ricevere informazioni dal VIC e dalla tastiera ma dato che questi non sono ancora emulati al capitolo 3 dell'emulatore viene inviato un segnale
Nel capitolo 4 viene introdotta la lettura da tastiera e l'output in modalita' testo
Per la tastiera, ogni volta che viene premuto un tasto (if keypressed) viene letta la lunghezza del buffer di tastiera alla locazione $C6 (il buffer di tastiera puo' contenere al massimo 10 caratteri), se il numero e' inferiore a 10 inserisce il nuovo carattere nel buffer che si trova tra $0277 e $0280 ed incrementa la locazione $C6
Per l'output a video viene letta la memoria video testuale che e' compresa tra $400 e $7E7 (1Kb). Nell'emulatore la schermata viene ricreata ogni volta che c'e' un accesso in lettura nell'area di memoria $400-$7E7
Per la posizione del cursore vengono lette le locazioni di memoria $00D3 e $00D6
Dal capitolo 5 viene gestita la creazione degli interrupt. In pratica un interrupt e' un evento che interrompe momentaneamente il ciclo di base del processore While True Do. Gli interrupt possono essere mascherabili e non mascherabili: di fatto viene messo nello stack il valore del program counter ed il contenuto del registro delle flags (in modo da recuperarlo quando si esce dall'interrupt) e poi si passa il controllo dell'esecuzione al codice dell'interrupt puntato dalle locazioni di memoria $FFFE-$FFFF (essendo un indirizzo di memoria e' una word) se e' mascherabile oppure $FFFA-$FFFB se non e' mascherabile
GLi interrupt sono generati da un dispositivo hardware ma esiste la possibilita' di avere interrupt software via BRK
Su C64 viene generato un IRQ con un clock di 50 Hz
Il codice Pascal si compila tranquillamente, almeno fino al capitolo 5, su Linux usando FreePascal
Dal capitolo 5 viene gestita la creazione degli interrupt. In pratica un interrupt e' un evento che interrompe momentaneamente il ciclo di base del processore While True Do. Gli interrupt possono essere mascherabili e non mascherabili: di fatto viene messo nello stack il valore del program counter ed il contenuto del registro delle flags (in modo da recuperarlo quando si esce dall'interrupt) e poi si passa il controllo dell'esecuzione al codice dell'interrupt puntato dalle locazioni di memoria $FFFE-$FFFF (essendo un indirizzo di memoria e' una word) se e' mascherabile oppure $FFFA-$FFFB se non e' mascherabile
GLi interrupt sono generati da un dispositivo hardware ma esiste la possibilita' di avere interrupt software via BRK
Su C64 viene generato un IRQ con un clock di 50 Hz
Il codice Pascal si compila tranquillamente, almeno fino al capitolo 5, su Linux usando FreePascal
lunedì 21 gennaio 2019
Iscriviti a:
Post (Atom)
Debugger integrato ESP32S3
Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede nell'ID USB della porta Jtag. Nel modulo...
-
In questo post viene indicato come creare uno scatterplot dinamico basato da dati ripresi da un file csv (nel dettaglio il file csv e' c...
-
Questo post e' a seguito di quanto gia' visto nella precedente prova Lo scopo e' sempre il solito: creare un sistema che permet...
-
La scheda ESP32-2432S028R monta un Esp Dev Module con uno schermo TFT a driver ILI9341 di 320x240 pixels 16 bit colore.Il sito di riferiment...