Ein kontaktloser Umdrehungszähler U/Messzeit, bzw. Drehzahlmesser U/Minute soll auf Basis eines Hall-Effekt Sensors erstellt werden.
Hintergrund des kleinen Projekts war die Zielsetzung an einer stufenlos einstellbaren Drehmaschine möglichste genaue Informationen über die vorhandene Spindeldrehzahl zu bekommen.
Hierfür bat sich eine Lösung über einen TLE 4905 Sensor an. Der digitale Hall Sensor wird über einen kleinen Neodym Magneten getriggert, welcher an der Spindel der Drehmaschine montiert wird. Über einen Taster sollen wahlweise Drehzahl gemessen oder Umdrehungen gezählt werden. Die Werte sollen über ein vierstelliges 7- Segment Display ausgegeben werden
Lösungsansatz für die Arduino Umgebung
//Drehzahlmesser-2014-Dezember-getestet und funktioniert.
//Lösungsansätze durch Zerspannungsbude.net und der GNU Bibliotheken
//Multiplex7seg umggeschrieben für 4 Segmente mit gemeinsamer Kathode
#include <Multiplex7Seg.h> //Multiplex4segment Bibl einbinden, umgeschrieben
int pin= 2; // pin 2 für Interrupt 1
byte digitPins[] = {9, 10, 11, 12}; // Segmente von rechts nach Links
byte segmentPins[] = {13, 3, 4, 5, 6, 7, 8}; // segmente a to g
volatile unsigned long dauer; // microsekunden seit dem letzten Interrupt
volatile unsigned long lastm; // Zählerwert beim letzten Interrup
int drehzahl; // selbstredend
uint8_t count; // 1-Byte Wert braucht kein volatile (atomic)
void setup() //Setup-Routine
{
pinMode(pin,INPUT); //Interrupt 0 ist Pin 2 interrupt 1 ist pin 3
Multiplex7Seg::set(1, 4, digitPins, segmentPins); // initialisierung der Multiplexbibliothek- Variablen (Arrays)
digitalWrite(pin, HIGH); // und Pullup-Widerstand einschalten /Intern,
//HIGH-Sensor is ON: There are 20K pullup resistors built into the Atmega chip that can be accessed from software. These built-in pullup resistors are accessed by setting the pinMode() as INPUT_PULLUP. This effectively inverts the behavior of the INPUT mode, where HIGH means the sensor is off, and LOW means the sensor is on.
attachInterrupt(0, readmicros, RISING ); // Interrupt 0 auf Routine readmicros setzen
/*
LOW to trigger the interrupt whenever the pin is LOW,
CHANGE to trigger the interrupt whenever the pin changes value
RISING to trigger when the pin goes from low to high,
FALLING for when the pin goes from high to low.
The Due board allows also:
HIGH to trigger the interrupt whenever the pin is high.
*/
}
void loop() { // Hauptprogramm
unsigned long d;
cli();
if (count++ > 2) // Inaktivitätszähler
{
dauer = 0;
d = 0; //Magnet da?
}
else
{
d = dauer;
}
sei();
drehzahl = d ? 60000000L / (3*d) : 0; // Drehzahl ausrechnen. Wenn d existiert (Bedingungsoperator ?) dann 60000000L durch d
// 1 mn = 60 sec = 60 * 1000 * 1000 us
/*if (drehzahl<=30){
Multiplex7Seg::loadValue(drehzahl);
delay(500);
}
else if (drehzahl<=100){
Multiplex7Seg::loadValue(drehzahl);
delay(300);
}
else
{
Multiplex7Seg::loadValue(drehzahl);
delay(100);
}*/
Multiplex7Seg::loadValue(drehzahl);
if (drehzahl>=1&&drehzahl<=100){
int a= 10000/drehzahl;
int b= a<=1000 ? a: 1000; //timing verbesserung fürs display bei niedrigen drehzahlen
delay(b);
}
else
delay(100);
}
void readmicros() { // Interrupt-Routine
unsigned long m = micros(); // Microsekundenzähler auslesen
if (m > lastm) // counter-wrap ignorieren
dauer = m - lastm; // Differenz zum letzten Durchlauf berechnen
lastm = m; // Letzten Wert merken
count=0; // Inaktivitätszähler
}
Die Multiplex7seg.h musste ich anpassen, beim Display waren die gemeinsame Kathode/Anode vertauscht.
Script für einen ATtiny85 geschrieben:
/* Setup for Arduino Nano
Notice: Interrupt Control only on INT0 Pin!"
*/
/*forAttiny85
* Daisy Chained Shift Registers
* 4 74HC595 shift registers connected to
* 4 x 7 Segment
*
* SETUP:
* ATtiny Pin 1 = PB5 #RST (PCINT5/RESET/ADC0/dW)
* ATtiny Pin 2 = PB3 (PCINT3/XTAL1/CLKI/OC1B/ADC3)
* ATtiny Pin 3 = PB4 (PCINT4/XTAL2/CLKO/OC1B/ADC2)
* ATtiny Pin 4 = GND
* ATtiny Pin 5 = PB0 (MOSI/DI/SDA/AIN0/OC0A/OC1A/AREF/PCINT0)
* ATtiny Pin 6 = PB1 (MISO/DO/AIN1/OC0B/OC1A/PCINT1)
* ATtiny Pin 7 = PB2 (SCK/USCK/SCL/ADC1/T0/INT0/PCINT2)
* ATtiny Pin 8 = VCC (2.7-5.5V)
*/
int dataPin = 5; //Pins for Daisy Chain shifted Segments
int latchPin = 6;
int clockPin =7;
int pin = 2; //INT0
int countpin =4; //Pin zum wechseln auf Umdrehenungen zählen
volatile unsigned long dauer; // microsekunden seit dem letzten Interrupt
volatile unsigned long lastm; // Zählerwert beim letzten Interrup
int value; // selbstredend
uint8_t count; // 1-Byte Wert braucht kein volatile (atomic)
int cou =0;
int ifcou=0;
int cstate=0;
byte dec_digits[11] = {0b00111111,0b00000110,0b01011011,0b01001111,0b01100110,0b01101101,0b01111101,0b00000111,0b01111111,0b01101111,0b00000000 };
// 0 1 2 3 4 5 6 7 8 9 empty=10
void setup() {
//set pins to output so you can control the shift register
pinMode(pin, INPUT);
pinMode(countpin, INPUT);
digitalWrite(pin, HIGH); //geändert
//digitalWrite(countpin, LOW); //geändert
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
attachInterrupt(0, readmicros, RISING ); // Interrupt 0 auf Routine readmicros setzen
}
void loop() {
cou= digitalRead(countpin);
unsigned long d;
cli(); // disable global interrupts
if (count++ > 2) // Inaktivitätszähler
{
dauer = 0;
d = 0; //Magnet da?
}
else
{
d = dauer;
}
sei(); // enable interrupts
//value = d ? 60000000L / (3*d) : 0; Drehzahl ausrechnen. Wenn d existiert (Bedingungsoperator ?) dann 60000000L durch d
//Extend with counting rounds:
//readmicros called over attachedinterrupt: Timer = 600000000L
if (cstate==1){
value=ifcou;
if(cou>>0){cstate=0;delay(200);}
anzeigen();
}
else if (cstate==0){
ifcou=0; ///Zaehler 0
if(cou>>0){cstate=1;delay(200);} //für nächsten Wechsel
value = d ? 60000000L / (1*d) : 0;
anzeigen();
}
}
void anzeigen()
{
int thousandsColumn = (value/1000); //bei 2305 z.B: 2,305
value %=1000; //Rest der Division durch 1000 gleich neues Value (305)
int hundredsColumn = (value/100); //305 durch 100 = 3,05
value %=100; //Rest der Division durch 100 gleich neues Value (05)
int tensColumn=(value/10);
value %=10;
int onesColumn=(value/1);
if (thousandsColumn ==0){
thousandsColumn=10;
}
if (thousandsColumn==10 && hundredsColumn==0){
hundredsColumn=10 ;
}
if (thousandsColumn==10 && hundredsColumn==10 && tensColumn==0){
tensColumn=10 ;
}
// take the latchPin low so
// the LEDs don't change while you're sending in bits:
digitalWrite(latchPin, LOW);
// shift out the bits:
if (cstate==1){shiftOut(dataPin, clockPin, MSBFIRST, (dec_digits[onesColumn]+0b10000000));}
else {shiftOut(dataPin, clockPin, MSBFIRST, dec_digits[onesColumn]);}
shiftOut(dataPin, clockPin, MSBFIRST, dec_digits[tensColumn]);
shiftOut(dataPin, clockPin, MSBFIRST, dec_digits[hundredsColumn]);
shiftOut(dataPin, clockPin, MSBFIRST, dec_digits[thousandsColumn]);
//take the latch pin high so the LEDs will light up:
digitalWrite(latchPin, HIGH);
// pause before next value:
if (value>=1&&value<=100){
int a= 10000/value;
int b= a<=1000 ? a: 1000; //timing verbesserung fürs display bei niedrigen drehzahlen
delay(b);
}
else
delay(100);
}
void readmicros()
{ // Interrupt-Routine
unsigned long m = micros(); // Microsekundenzähler auslesen
zaehlen();
if (m > lastm)
{ // counter-wrap ignorieren
dauer = m - lastm; // Differenz zum letzten Durchlauf berechnen
lastm = m; // Letzten Wert merken*/
count= 0; // Inaktivitätszähler
}
}
void zaehlen(){
ifcou+=1;
}