Hallo zusammen,
ich habe eine 10EUR-China-Schieblehre an meiner Drehbank zur Anzeige des Durchmessers fest montiert. Bisher lese ich die Schieblehre mit Unterstützung von Hardware aus. Ich lese die Daten zuerst in ein Schieberegister ein. Wenn dann Zeit ist, lese ich das Schieberegister wieder aus. Das fand ich nicht so elegant und will nun mit dem Mega32 allein auskommen.
program DrehbankDRO;
{$NOSHADOW}
{ $WG} {global Warnings off}
Device = mega32, VCC = 5;
{ $BOOTRST $01E00} {Reset Jump to $01E00}
Define_Fuses
// Override_Fuses;
NoteBook = A;
COMport = USB;
LockBits0 = [];
FuseBits0 = [];
FuseBits1 = [SPIEN];
FuseBits2 = [];
Autorelease = true;
Import SysTick, SysLEDblink, Serport;
From System Import LongInt;
Define
ProcClock = 16000000; {Hertz}
SysTick = 0.5; {msec}
StackSize = 200, iData;
FrameSize = 200, iData;
SysLEDblink = 255; {SysTicks}
SysLedBlink0 = PortB, 7, low;
SysLedBlink1 = PortB, 6, low;
// SysLedBlink2 = PortB, 5, low;
Serport = 19200, Stop1;
Implementation
{$IDATA}
{--------------------------------------------------------------}
{ Type Declarations }
{--------------------------------------------------------------}
{ Const Declarations }
{--------------------------------------------------------------}
{ Var Declarations }
{$IDATA}
var
DataPin[@PIND, 3] : bit;
ClockPin[@PIND, 2] : bit;
LED[@PortB, 5] : bit;
RelPos : LongInt;
Flaggen : byte;
SteigenderINT[@Flaggen, 0] : bit;
NeueDaten[@Flaggen, 1] : bit;
{--------------------------------------------------------------}
{ functions }
{--------------------------------------------------------------}
procedure SendEsc;
begin
Write(serout, #27 );
Write(serout, '[' );
end;
{--------------------------------------------------------------}
procedure Term_Send(c : char);
begin
Write(serout, c);
end;
{--------------------------------------------------------------}
procedure Term_Erase_Screen;
begin
SENDESC; // Send escape sequence start
Term_Send( '2' );
Term_Send( 'J' );
end;
//***************************
// Set cursor position
//
// Top-left is (1,1)
//***************************
procedure Term_Set_Cursor_Position( row, column : byte);
begin
SENDESC; // Send escape sequence start
Write(serout, bytetostr(row) ); // Convert row byte
Term_Send( ';' );
Write(serout, bytetostr(column) ); // Convert column byte
Term_Send( 'H' );
end;
{--------------------------------------------------------------}
//***************************
// Send 'clear end of line' sequence
//***************************
procedure Term_Erase_to_End_of_Line;
begin
SENDESC; // Send escape sequence start
Term_Send( 'K' );
end;
{--------------------------------------------------------------}
procedure InitPorts;
begin
PortC:= %11000000;
DDRC:= %00000011;
DDRD:= %00000000;
DDRB:= %11100000;
PortB:= %11100000;
end InitPorts;
{--------------------------------------------------------------}
Interrupt INT0;
//wird angesprungen bei der steigenden Flanke eines neuen 2x24 Datenpaketes
//*** Interrupt wird erst nach ~800uSec wieder verlassen! ***
var
bitval : LongInt;
b : byte;
BitZaehler : byte;
begin
NeueDaten:= false;
GICR:= GICR and %10111111; //Int0 aus
BitZaehler:= 0;
RelPos:= 0;
while BitZaehler < 24 do //das erste 24er Paket ist nutzlos, da ABS-Position
repeat
until ClockPin = false; //auf fallende Flanke warten
inc (BitZaehler);
repeat
until ClockPin = true;
endwhile;
bitval:= 1;
BitZaehler:= 0;
while BitZaehler < 24 do
repeat
until ClockPin = false; //auf fallende Flanke warten
inc (BitZaehler);
if DataPin = true then
b:= 1;
RelPos:= RelPos or bitval;
else
b:= 0;
endif;
repeat
until ClockPin = true; //auf steigende Flanke warten
bitval:= bitval shl 1;
endwhile;
//Jetzt höchstwertiges Bit prüfen, wenn 1, dann 2er-Komplement
//bilden.
if b = 1 then
RelPos:= (RelPos or %11111111100000000000000000000000);
endif;
NeueDaten:= true;
Toggle(LED);
end;
{--------------------------------------------------------------}
{ Main Program }
{$IDATA}
begin
InitPorts;
EnableInts;
SysLEDflashOn(0);
Term_Erase_Screen;
Term_Set_Cursor_Position(2, 5);
Write(serout, 'Digitalanzeige die Zweite...');
loop
TCNT1H:= 0;
TCNT1L:= 0;
TCCR1B:= %00000001; //Timer1 mit Vortieler 1 starten. Ein Incremenr = 0,0625usec
while TCNT1L < 200 do //auf Clock Low für min. 12,5uSec us warten, dann sind wir vor Beginn eines Datagramm
if ClockPin = true then // Flankenwechsel auf Clock, wir sind mitten im Datagram
TCNT1H:= 0; // also Timer zurücksetzen und weiterwarten
TCNT1L:= 0; // das Warten kann im schlimmsten Fall ~1mSec dauern
endif;
endwhile;
TCCR1B:= %00000000; //Timer1 stopp
//Hier sind wir vor einem neuem Datagramm.
//Im FastMode dauert es jetzt etwa 20mSec,
//im NormalMode etwa 300mSec, bis das neue Datagramm kommt
//Pollen würde das System dichtmachen, daher setzten wir einen INT
//INT0 auf steigende Flanke Clockleitung triggern
MCUCR := MCUCR or %00000011; //MCU Control, Bit1=1, Bit0=1 --> INT0 auf steigende Flanke
incl(SteigenderINT);
GICR:= GICR or %01000000; //Bit6 --> INT0 enable
//Das Einelsen passiert komplett im Interrupt
if NeueDaten then
Term_Set_Cursor_Position(4, 5);
Term_Erase_to_End_of_Line;
RelPos:=RelPos*127; //Schieblehre hat ein internes Format,
RelPos:=RelPos div 2048; //hier umrechnen auf Millimeter
relpos:=relpos* (-1); //und Anzeige invertieren
Write(serout, longtostr(RelPos :6:2));
NeueDaten:= false;
endif;
endloop;
end DrehbankDRO.
Es funktioniert auch soweit. Aber ich glaube, das ich durch mein Pollen auf Beginn des Paketes und das Einlesen innerhalb des Interrupts das System schon soweit "dicht" gemacht habe, dass ich nicht viel anderes mehr machen kann.
Eigentlich sollte noch ein Drehzahlmesser, eine I2C-Realtimeclock und ein grafisches Display mit dran. Mit einem INT0, der 800uSec belegt ist, wird das nichts, denke ich.
Also endlich meine Frage: Kann ich es besser machen? Oder bringt alle Optimierung nur marginal was? Dann sollte ich einfach einen 2. µC für Diplay usw. nehmen, gekoopelt mit dem "Mess-µC" per SPI?
Vielen Dank für Anregungen und Grüße aus Petershagen!
Rolf
ich habe eine 10EUR-China-Schieblehre an meiner Drehbank zur Anzeige des Durchmessers fest montiert. Bisher lese ich die Schieblehre mit Unterstützung von Hardware aus. Ich lese die Daten zuerst in ein Schieberegister ein. Wenn dann Zeit ist, lese ich das Schieberegister wieder aus. Das fand ich nicht so elegant und will nun mit dem Mega32 allein auskommen.
Code
program DrehbankDRO;
{$NOSHADOW}
{ $WG} {global Warnings off}
Device = mega32, VCC = 5;
{ $BOOTRST $01E00} {Reset Jump to $01E00}
Define_Fuses
// Override_Fuses;
NoteBook = A;
COMport = USB;
LockBits0 = [];
FuseBits0 = [];
FuseBits1 = [SPIEN];
FuseBits2 = [];
Autorelease = true;
Import SysTick, SysLEDblink, Serport;
From System Import LongInt;
Define
ProcClock = 16000000; {Hertz}
SysTick = 0.5; {msec}
StackSize = 200, iData;
FrameSize = 200, iData;
SysLEDblink = 255; {SysTicks}
SysLedBlink0 = PortB, 7, low;
SysLedBlink1 = PortB, 6, low;
// SysLedBlink2 = PortB, 5, low;
Serport = 19200, Stop1;
Implementation
{$IDATA}
{--------------------------------------------------------------}
{ Type Declarations }
{--------------------------------------------------------------}
{ Const Declarations }
{--------------------------------------------------------------}
{ Var Declarations }
{$IDATA}
var
DataPin[@PIND, 3] : bit;
ClockPin[@PIND, 2] : bit;
LED[@PortB, 5] : bit;
RelPos : LongInt;
Flaggen : byte;
SteigenderINT[@Flaggen, 0] : bit;
NeueDaten[@Flaggen, 1] : bit;
{--------------------------------------------------------------}
{ functions }
{--------------------------------------------------------------}
procedure SendEsc;
begin
Write(serout, #27 );
Write(serout, '[' );
end;
{--------------------------------------------------------------}
procedure Term_Send(c : char);
begin
Write(serout, c);
end;
{--------------------------------------------------------------}
procedure Term_Erase_Screen;
begin
SENDESC; // Send escape sequence start
Term_Send( '2' );
Term_Send( 'J' );
end;
//***************************
// Set cursor position
//
// Top-left is (1,1)
//***************************
procedure Term_Set_Cursor_Position( row, column : byte);
begin
SENDESC; // Send escape sequence start
Write(serout, bytetostr(row) ); // Convert row byte
Term_Send( ';' );
Write(serout, bytetostr(column) ); // Convert column byte
Term_Send( 'H' );
end;
{--------------------------------------------------------------}
//***************************
// Send 'clear end of line' sequence
//***************************
procedure Term_Erase_to_End_of_Line;
begin
SENDESC; // Send escape sequence start
Term_Send( 'K' );
end;
{--------------------------------------------------------------}
procedure InitPorts;
begin
PortC:= %11000000;
DDRC:= %00000011;
DDRD:= %00000000;
DDRB:= %11100000;
PortB:= %11100000;
end InitPorts;
{--------------------------------------------------------------}
Interrupt INT0;
//wird angesprungen bei der steigenden Flanke eines neuen 2x24 Datenpaketes
//*** Interrupt wird erst nach ~800uSec wieder verlassen! ***
var
bitval : LongInt;
b : byte;
BitZaehler : byte;
begin
NeueDaten:= false;
GICR:= GICR and %10111111; //Int0 aus
BitZaehler:= 0;
RelPos:= 0;
while BitZaehler < 24 do //das erste 24er Paket ist nutzlos, da ABS-Position
repeat
until ClockPin = false; //auf fallende Flanke warten
inc (BitZaehler);
repeat
until ClockPin = true;
endwhile;
bitval:= 1;
BitZaehler:= 0;
while BitZaehler < 24 do
repeat
until ClockPin = false; //auf fallende Flanke warten
inc (BitZaehler);
if DataPin = true then
b:= 1;
RelPos:= RelPos or bitval;
else
b:= 0;
endif;
repeat
until ClockPin = true; //auf steigende Flanke warten
bitval:= bitval shl 1;
endwhile;
//Jetzt höchstwertiges Bit prüfen, wenn 1, dann 2er-Komplement
//bilden.
if b = 1 then
RelPos:= (RelPos or %11111111100000000000000000000000);
endif;
NeueDaten:= true;
Toggle(LED);
end;
{--------------------------------------------------------------}
{ Main Program }
{$IDATA}
begin
InitPorts;
EnableInts;
SysLEDflashOn(0);
Term_Erase_Screen;
Term_Set_Cursor_Position(2, 5);
Write(serout, 'Digitalanzeige die Zweite...');
loop
TCNT1H:= 0;
TCNT1L:= 0;
TCCR1B:= %00000001; //Timer1 mit Vortieler 1 starten. Ein Incremenr = 0,0625usec
while TCNT1L < 200 do //auf Clock Low für min. 12,5uSec us warten, dann sind wir vor Beginn eines Datagramm
if ClockPin = true then // Flankenwechsel auf Clock, wir sind mitten im Datagram
TCNT1H:= 0; // also Timer zurücksetzen und weiterwarten
TCNT1L:= 0; // das Warten kann im schlimmsten Fall ~1mSec dauern
endif;
endwhile;
TCCR1B:= %00000000; //Timer1 stopp
//Hier sind wir vor einem neuem Datagramm.
//Im FastMode dauert es jetzt etwa 20mSec,
//im NormalMode etwa 300mSec, bis das neue Datagramm kommt
//Pollen würde das System dichtmachen, daher setzten wir einen INT
//INT0 auf steigende Flanke Clockleitung triggern
MCUCR := MCUCR or %00000011; //MCU Control, Bit1=1, Bit0=1 --> INT0 auf steigende Flanke
incl(SteigenderINT);
GICR:= GICR or %01000000; //Bit6 --> INT0 enable
//Das Einelsen passiert komplett im Interrupt
if NeueDaten then
Term_Set_Cursor_Position(4, 5);
Term_Erase_to_End_of_Line;
RelPos:=RelPos*127; //Schieblehre hat ein internes Format,
RelPos:=RelPos div 2048; //hier umrechnen auf Millimeter
relpos:=relpos* (-1); //und Anzeige invertieren
Write(serout, longtostr(RelPos :6:2));
NeueDaten:= false;
endif;
endloop;
end DrehbankDRO.
Es funktioniert auch soweit. Aber ich glaube, das ich durch mein Pollen auf Beginn des Paketes und das Einlesen innerhalb des Interrupts das System schon soweit "dicht" gemacht habe, dass ich nicht viel anderes mehr machen kann.
Eigentlich sollte noch ein Drehzahlmesser, eine I2C-Realtimeclock und ein grafisches Display mit dran. Mit einem INT0, der 800uSec belegt ist, wird das nichts, denke ich.
Also endlich meine Frage: Kann ich es besser machen? Oder bringt alle Optimierung nur marginal was? Dann sollte ich einfach einen 2. µC für Diplay usw. nehmen, gekoopelt mit dem "Mess-µC" per SPI?
Vielen Dank für Anregungen und Grüße aus Petershagen!
Rolf
Attachments
Filename: | schieb.png |
Filesize: | 7.95 KB |
Title: | Datagramm |
Information: | Daten- und Clock eines kompletten Paketes |
Download counter: | 144 |