/*
* Netzteilsteuerung mit seriellem Dialog, MODBUS, Digitalpoti und LCD-Anzeige.
* In dieser Steuerung laufen die tshar-Prozesse
*
- fun_mess - Hole jeden ADC-Kanal und bilde einen Mittelwert
* - fun_LCD -
* - fun_DIA -
* //ul>
* @version 2020-10-26 : serdia.cpp: entstanden aus der Schmierautomatsteuerung
* @version 2020-11-19 : serfloat.cpp: umgestellt auf float. Das vereinfacht das Berechnen von Leistung und Ladung
* @version 2021-01-23 : serfloat.cpp: Dialog & LCD-Anzeige laeuft im ATmega644p. Probleme mit MCP3208 Messung und MCP4922 Ausgaben
* @version 2021-02-23 : auch die Temperaturanzeige geht
* @version 2021-03-01 : Kalibriermenue 'k' Eingabe 'm' Max Werte beachten Kalibriernummer
* Anzeige min mit grenz fuer alle Kanaele ROM: 29892 (74C4H), RAM: 1479 (5C7H)
* @version 2021-03-05 : MODBUS hinzugefügt
* @version 2021-03-13 : MODBUS repariert, jetzt mit ONEWIRE neu versucht ROM: 32580 (7F44H), RAM: 1874 (752H)
* @version 2021-03-13 : MODBUS repariert, ohne ONEWIRE ROM: 32416 (7EA0H), RAM: 1858 (742H)
* @version 2021-03-14 : umbenannt nach Mispav
* @version 2021-03-20 :
* @version 2021-04-06 : Endlich geht MODBUS mit hemst und mit Java ModbusMaster zuverlaessig.
* @version 2021-04-06 : Korrektur Andlich geht MODBUS mit hemst und mit Java ModbusMaster zuverlaessig.
* @version 2023-11-13 : 32-Bit-Werte mit FC=16 empfangen
* @version 2024-04-29 : 32-Bit-Werte mit FC=16 empfangen
* weil 8 x 16 = 128 Worte
*
4.8.1 C:/WinAVR/bin/avr-g++ -c -I D://h/home/cc/include -I C:/WinAVR/avr/include -gdwarf-2 -mmcu=atmega644p -Wall -Os
-DAVR -DNPRGPATH=5 -DF_CPU=16008972UL -DLCD=2 -DLCD_YMAX=4 -DLCD_XMAX=20
-DTSONEWI_PORTA=0x80 -DSSDDR=DDRB -DSSPORT=PORTB -DSSMCP3208=0x08
-DADC_KANAELE_PMOD_MCP3208=8 -DADC_KANAELE_AVR=0 -DADCFILTER_NTOTAL=16
-DSSMCP4922=0x10 -DDAC_KANAELE_PMOD_MCP4922=2 -DSSLT2400=0x00
-DADC_KANAELE_LT2400=0 -DGETTICK_TIMER=1 -DAVR_TIMER_DIVIDER_CODE=2
-DBAUD=19200 -DPARITY=110 -DNIN_CHARBUFFER=22 -DNSPRINTF_FLOAT=1 -DOPT_tseeprom_CRC=1
*/
#ifndef AVR
#define DB_ENABLED 1
#define DB_INFO 0
#define OPT_TEST_DIGIPOTI 0 // Teste die Dynamik
#endif
#define VERSION __DATE__ " " __TIME__ //
#define VERSION_JAHR 2024 // jjjj
#define VERSION_MMTT 504 // mtt Darf nicht mit fuehrender 0 geschrieben werden. nicht Oktal!
#define OPT_SER_DIAL 2 // 0=ohne seriellen Dialog
// 1=Mit seriellem Dialog durch Schnittstelle 1 tsser Standard
// 2=Mit seriellem Dialog durch Schnittstelle 2 des ATmega644 / Durch COM2 im PC
// Geplant fuer atmega328: 3=Soft-UART, geht nur mit eingeschraenkter BAUD_Rate
#define OPT_ser0avri 1 // Ser0 mit RX-Interrupt
#define OPT_SIMULATOR 0 // Nicht wirklich fuer einen Atmel AVR, sondern fuer den Simulator SimAVR
// Also ohne Stackcheck
#ifndef LCD_REFRESH_ZEIT
#define LCD_REFRESH_ZEIT 0.5
#define LCD_REFRESH_ZEIT 0.5
#endif
#define DEBUG_INDEX 0
#define DEBUG_MODBUS 0
#define DEBUG_HatEmpfangen 0
#define OPT_MES 1 // TODO: muss 1 sein
#ifdef AVR
#define OPT_LCD 1 // TODO: muss 1 sein
#else
#if LPT > 0
#define OPT_LCD 1
#else
#define OPT_LCD 1
#endif
#endif
#ifndef MODBUSADDRS // Ist 20 oder 27 Siehe
#define MODBUSADDRS 20 // Sollte von Aufruf / makefile definiert sein
#define MODBUSADDRS 20 // Sollte von Aufruf / makefile definiert sein
#endif
#include
#include
#include
#include
#include // Auch Prototypen fuer ser0... , wie in ser0avri.cpp ser0avr.cpp
#include
#include
#include
#include
#include
#include
// #include
#include
#ifndef AVR // Nur wenn mispav im PC laeuft
#include
#include // Dialog durch eine DOS-BOX
#endif
#ifdef MODBUSADDRS
#if OPT_SER_DIAL == 1
#error kann Schnittstelle nicht fuer Dialog UND MODBUS gleichzeitig verwenden
#endif
#include
#include // MODBUS Telegramme
#if OPT_ser0avri && AVR // Teste den Interrupt-Treiber lq\ser0avri.cpp
extern u8 ser_0_AVR_ifill ; // mit jedem gelesenen Zeichen wird der dieser Fuell-Zeiger um 1 erhoeht
extern u8 ser_0_AVR_iempty ; // mit jedem verarbeitetem Zeichen wird der dieser Entnahme-Zeiger um 1 erhoeht
#endif
#endif // #ifdef MODBUSADDRS
u32 startlcdtick ;
#ifdef AVR
#include
#include
#endif
#define VORTEST 0 // 0 = fuer die Schaltung 1 = Vortest im s/atm32b
#define MENU(im,d) if ( (s8)d > 0 ) ++im ; if ( (s8)d < 0 ) --im ; // MENU(iMenu, diff) ;
#if WIN95
#define GEBERTEILER 3 // 1 reagiert auf jede Geberphase, 4 auf einen kompletten Phasenwechsel
#else
#define GEBERTEILER 3 // 1 reagiert auf jede Geberphase, 4 auf einen kompletten Phasenwechsel
#endif
u32 startpotitick ;
extern u8 sfmodbus_fehler ; // Fehler bei Zwischenspeichern des Telegramms - braucht nicht mehr senden
bool gestartet ;
bool Tasteraktion ; // Flankenerkennung,Taster wurde niedergedrueckt
bool eepromfehler ;
bool bLaufendeAnzeige ;
// Diese Kalibtierwerte werden dauerhaft gespeichert
// AVR im EEPROM
// Sonstige: in Datei
struct kaldat { // Kalibrierwerte fuer 6 Kanaele
// u8 nks ; // Zugriff asp.vk[XUM].nks
s16 null ; // Nullpunktverschiebung // Zugriff asp.vk[XUM].null
s16 grenz ;
float fkkal ; // Faktor // Zugriff asp.vk[XUM].fkkal
} ;
/// Daten eines Kalibrierpunktes
struct kalpunkt {
float Ref ;
s16 Roh ;
} ;
/// Daten eines Kanals
struct kanal {
float dar ; ///< Daten eines Kanals: aktueller Darstellungswert
s16 w ; ///< Daten eines Kanals: aktueller ADC-Wert
kalpunkt a ;
kalpunkt b ;
} ;
// Index-Nummern der Kanaele
#define XUS 0 // Sollwert der Ausgangsspannung
#define XIS 1 // Sollwert der Ausgangsstromes
#define XUM 2 // Messwert der Ausgangsspannung
#define XIM 3 // Messwert des Ausgangsstromes
#define XTM 4 // Kalibrierdaten-Index von Temperatur-istwert
#define XUKE 5 // Kalibrierdaten-Index fuer Versorgungsspannung UKE = UKEp
#define NKALKAN 6 // Anzahl der kalibrierbaren Kanaele
struct kanal vkanal[NKALKAN] ;
// 2-Buchstaben-Kanalbezeichnungen
static const char tx_Kanal[] PROGMEM = "US" "IS" "UM" "IM" "TM" "UK" ;
// Aufteilung des EEPROM
#define EEPROM_offset 0 // Wo im EEPROM sind die EEPROM-Daten
#define KENNUNG 0x39FD
struct EEPROM { // War fuer Daten sind im EEPROM
struct kaldat vk[NKALKAN] ; // Zugriff asp.vk
// s16 Usoll ; // Das ist der unnormierte Wert
#define USOLLMIN 0
#define USOLLMAX (s16)(16380*1.0) // Bei VREF=5V *0.80 Bei VREF=4V *1.0
// s16 Isoll ;
#define ISOLLMIN 0
#define ISOLLMAX (s16)(16380*1.0) // Bei VREF=5V *0.80 Bei VREF=4V *1.0
u16 kennung ;
u16 crc ;
} ;
struct EEPROM vsp ; // Vergleichspeicher
struct EEPROM asp ; // Arbeitsspeicher
s16 DigPotWertR ; // Rohwert, wird aus PhaseA / PhaseB gewonnen
s16 DigPotWertA ; // alle 0.3 s fuer Auswertung
s16 DigPotWert_alt ;
u32 acttick ;
s16 diff ; // Digitalpoti Differenz der letzten 200 ms
s16 ddiff ; // Digitalpoti dynamische Differenz der letzten 200 ms
#define POTIFAKTOR 4 // Das POTI ist ansonsten zu langsam
u32 startsectick ;
u32 max_difftick ;
u32 max_difftickLCD ;
u16 anzeigesec ;
u16 restsec ;
// u16 soll ;
PGM_P letzteMeldung ;
PGM_P ausmeldung ;
u32 ulSekunden ;
float Ladung_as ;
char format_7_2_f[] = "%7.2f" ; // fuer out_printf
char format_6_d_b[] = "%6d " ; // fuer out_printf
char format_6_f_b[] = "%7f " ; // fuer out_printf
static const char tx_B[] PROGMEM = " B:";
static const char tx_C[] PROGMEM = " C:";
static const char tx_D[] PROGMEM = " D:";
#ifdef AVR
#define POTIPORT PORTB // Portnummer Poti 0
// #define POTIDDR DDRB // Portnummer Poti 0
#define POTIPIN PINB // Portnummer Poti 0
#define POTIBITA 0x02 // Bitmaske Phase A
#define POTIBITB 0x04 // Bitmaske Phase B
#define STPORT PORTB
// #define STDDR DDRB
#define STPIN PINB
#define STBIT 0x01 // Schalter am Poti
#else // nicht AVR
#if P8255_D1 > 0
u8 p8255[12] ;
#endif
#if LPT1 > 0
u8 lpt1_ctrl ;
u8 lpt1_data ;
u8 lpt1_status ; // Wird da hineingeholt
#define POTIPIN lpt1_status
// Anschluss 25Polig PC S/PCPAR IN379
#define POTIBITA 0x08 // Pin 15/25 Pin 4/HD10
#define POTIBITB 0x10 // Pin 13/25 Pin 5/HD10
#define STPIN lpt1_status
#define STBIT 0x20 // Pin 12/25 Pin 6/HD10
#else // Nur damit es kompilierbar wird
u8 POTIPORT ;
u8 POTIDDR ;
u8 POTIPIN ;
u8 POTIBITA = 0x01;
u8 POTIBITB = 0x02 ;
u8 PORTB ;
u8 PORTD ;
u8 DDRB ;
u8 PIND = 0x60 ;
#define STARTWERTE
u8 RMDDR ;
u8 STDDR ;
u8 STBIT = 0x04 ;
u8 STPIN ;
#endif
#endif // nicht AVR
#define STIST ( (STPIN & STBIT) == 0 ) // true, wenn Starttaster ist gedrueckt
#ifdef AVR
/*
Absturz im Kalibriermenu
DIA:Stapel frei =47 verwendet =18 von =65
LCD:Stapel frei =8 verwendet =57 von =65
ONE:Stapel frei =8 verwendet =22 von =30
MES:Stapel frei =10 verwendet =20 von =30
2021-03-18 Dialog geht
mispav:z z: Stackverwendung
DIA:Stapel frei =176 verwendet =80 von =256
LCD:Stapel frei =199 verwendet =57 von =256
MES:Stapel frei =237 verwendet =19 von =256
1s =2001122gettick() =1684322020T =841.69s
Schleife=0.003990s LCD=0.000000s N_ADC_CHAN:8
*/
#if OPT_SER_DIAL
#define NSTKDIA 100 // TODO: Engere Grenze herausfinden
#endif
#if OPT_LCD
#define NSTKLCD 100 // TODO: Engere Grenze herausfinden
#endif
#define NSTKMES 50 // TODO: Engere Grenze herausfinden
#else // #ifdef AVR
#if OPT_SER_DIAL
#define NSTKDIA 2000
#endif
#if OPT_LCD
#define NSTKLCD 2000
#endif
#define NSTKMES 2000
#endif // Ende #ifdef AVR
#if OPT_SER_DIAL
void *stkDIA[NSTKDIA] ;
#endif
#if OPT_LCD
void *stkLCD[NSTKLCD] ;
#endif
void *stkMES[NSTKMES] ;
#ifdef MODBUSADDRS
u8 modbusaddr = MODBUSADDRS ;
struct smodbus {
s16 ModbusGRegister[SFMODBUS_FIXED_REGISTERS]; // Grundregister
s16 sfm_dummy ; // Kompatibel zu 32-Bit-Rechner MSVC50
float sfm_Usoll ;
float sfm_Isoll ;
float sfm_Usollgrenz ;
float sfm_Isollgrenz ;
// Normierung (Kalibrierung) der Sollwerte
s16 sfm_USnull ;
s16 sfm_ISnull ;
float sfm_USfak ;
float sfm_ISfak ;
// Normierung (Kalibrierung) der Istwerte
s16 sfm_UInull ;
s16 sfm_IInull ;
float sfm_UIfak ;
float sfm_IIfak ;
// Normierung (Kalibrierung) der Istwerte
s16 sfm_UKEnull ;
s16 sfm_Tnull ;
float sfm_UKEfak ;
float sfm_Tfak ;
#define REGN_WRITE_MAX ((u16 *)&(pmb->sfm_Umess)-(u16 *)pmb) // Ab hier darf nicht mehr empfangen werden
// Registers, die nur gesendet werden koennen
float sfm_Umess ;
float sfm_Imess ;
float sfm_Takt ; // Aktuelle Temperatur
float sfm_UKE ;
s16 sfm_adc[8] ; // Rohwerte
} ;
#define REGISTERCOUNT (sizeof(struct smodbus)/sizeof(s16))
#define wortnr(x) (((s16 *)(&pmb->x))-((s16 *)pmb))
s16 ModbusRegister[REGISTERCOUNT]; // struct smodbus
#if DEBUG_HatEmpfangen
u8 HatEmpfangen[REGISTERCOUNT] ;
#endif
struct smodbus * const pmb = (struct smodbus *)&ModbusRegister[0];
#endif // Ende #ifdef MODBUSADDRS
// -------------------------------------------------------------
// Die Zustandsfunktionen fuer das Poti 0
void ZFPoti_start(void) ;
void ZFPoti_steht(void) ;
void ZFPoti_dreht(void) ;
void (*zfgeber0)(void) = ZFPoti_start ; // Der aktuelle (naechste) Zustand dieses Zustandsautomaten
// im Uhrzeigesinn (-1): 0 0 -> 0 1 -> 1 1 -> 1 0 -> 0 0
// gegen den Uhrzeigersinn ( 1): 0 0 -> 1 0 -> 1 1 -> 0 1 -> 0 0
// Alle anderen BitAENDERUNGEN vom Poti sind ungueltig
char modif[16] = {
// neue alt Anzahl der Aenderungen
0, // 0 0 0 0 0
1, // 0 0 0 1 1
-1, // 0 0 1 0 1
0, // 0 0 1 1 2 = uebersprungen
-1, // 0 1 0 0 1
0, // 0 1 0 1 0
0, // 0 1 1 0 2
1, // 0 1 1 1 1
1, // 1 0 0 0 1
0, // 1 0 0 1 2
0, // 1 0 1 0 0
-1, // 1 0 1 1 1
0, // 1 1 0 0 2
-1, // 1 1 0 1 1
1, // 1 1 1 0 1
0 // 1 1 1 1 0
} ;
u8 poti_Cd ; // Alter Kod
/*!
* Vorbereiten des Auswerteprogrtamms fuer den Drehgeber.
*/
FUNCT void ZFPoti_start(void)
{
#ifdef AVR
POTIPORT |= POTIBITA ;
POTIPORT |= POTIBITB ; // Pull-Up-Widerstaende aktivieren
#endif
#if LPT1 > 0
POTIPIN = inportb( LPT1+1 ); // Zustand 0x379 hereinholen
#endif
poti_Cd = 0 ;
if( POTIPIN & POTIBITA ) poti_Cd = 2 ;
if( POTIPIN & POTIBITB ) ++poti_Cd ;
zfgeber0 = ZFPoti_steht ;
startpotitick = acttick ;
}
/*!
* Bewegungen des Drehgebers auswerten.
*/
FUNCT void ZFPoti_steht(void)
{
u8 os ; // alter Kod 0...3
u8 s ; // neuer Kod in Bits 3:2 und alter Kod in bits 1:0 kombiniert 0...15
os = poti_Cd ;
s = os ;
poti_Cd = 0 ;
#if LPT1 > 0
POTIPIN = inportb( LPT1+1 ); // Zustand 0x379 hereinholen
#endif
if( POTIPIN & POTIBITA ) { poti_Cd = 2 ; s += 8 ; }
if( POTIPIN & POTIBITB ) { ++poti_Cd ; s += 4 ; }
// poti_Cd ist der aktuelle Kod, 0...3
// s & 0x0C ist auch der aktuelle Kod (neue), aber verschoben nach links um 2 Bit,
// s & 0x03 ist der vorherige Kod (alt),
// poti_Cd ist jetzt der neue Kod
// s ist kombiniert: neuer Kod in Bits 3:2 und alter Kod in bits 1:0
if ( modif[s] )
{
startpotitick = acttick ;
zfgeber0 = ZFPoti_dreht ;
DBprintf(( "POTIPIN=0x%X modif[%u]=%d", POTIPIN, s, modif[s] ));
}
DigPotWertR += modif[s] ;
}
/*!
* Bewegungen des Drehgebers auswerten.
*/
FUNCT void ZFPoti_dreht(void)
{
u8 os ; // alter Kod 0...3
u8 s ; // neuer Kod in Bits 3:2 und alter Kod in bits 1:0 kombiniert 0...15
os = poti_Cd ;
s = os ;
poti_Cd = 0 ;
#if LPT1 > 0
POTIPIN = inportb( LPT1+1 ); // Zustand 0x379 hereinholen
#endif
if( POTIPIN & POTIBITA ) { poti_Cd = 2 ; s += 8 ; }
if( POTIPIN & POTIBITB ) { ++poti_Cd ; s += 4 ; }
// poti_Cd ist der aktuelle Kod, 0...3
// s & 0x0C ist auch der aktuelle Kod (neue), aber verschoben nach links um 2 Bit,
// s & 0x03 ist der vorherige Kod (alt),
// poti_Cd ist jetzt der neue Kod
// s ist kombiniert: neuer Kod in Bits 3:2 und alter Kod in bits 1:0
DigPotWertR += modif[s] ;
if ( difftick( startpotitick, acttick ) > sectotick(0.2) )
{
DigPotWertA = DigPotWertR ; // Fuer Auswertung
zfgeber0 = ZFPoti_steht ;
}
}
// ----------------------------------------------------------------------
// ----------------- Die Zustandsfunktionen fuer den Taster ---------
// ----------------- Entprellen und Flankenerkennung -------------------
// ----------------------------------------------------------------------
void ZFTA_Warteaus(void) ;
void ZFTA_Warteaus_verzoegerung(void) ;
void ZFTA_Istaus(void) ;
void ZFTA_Warteein_verzoegerung(void) ;
void (*zfta)(void) = ZFTA_Warteaus ; // Der aktuelle (naechste) Zustand dieses Zustandsautomaten
u32 zfta_tick ;
/*!
* Warte bis der Taster losgelassen wird
*/
FUNCT void ZFTA_Warteaus(void)
{
#ifdef AVR
STPORT |= STBIT ; // Start-Taster: Pull up
#endif
#if LPT1 > 0
STPIN = inportb( LPT1+1 ); // Zustand 0x379 hereinholen
#endif
if ( STIST ) // STIST: true, wenn Starttaster ist gedrueckt
{ // Warten bis der Taster losgelassen ist
return ; // Ist nicht, weiterwarten
}
// Der Taster ist losgelassen.
zfta_tick = acttick ;
zfta = ZFTA_Warteaus_verzoegerung ;
}
/*!
* Warte bis der Taster lange genug losgelassen bleibt
*/
FUNCT void ZFTA_Warteaus_verzoegerung(void)
{
#if LPT1 > 0
STPIN = inportb( LPT1+1 ); // Zustand 0x379 hereinholen
#endif
if ( STIST ) // STIST: true, wenn Starttaster ist gedrueckt
{ // Der Taster wurde nur gaaanz kurz losgelassen
zfta = ZFTA_Warteaus ;
}
if ( difftick( zfta_tick, acttick ) > sectotick( 0.030 ) )
{ // schon 30 ms
zfta = ZFTA_Istaus ;
}
}
/*!
* Der Taster is losgelassen.
* Warte bis der Taster wieder gedrueckt wird
*/
FUNCT void ZFTA_Istaus(void)
{
#if LPT1 > 0
STPIN = inportb( LPT1+1 ); // Zustand 0x379 hereinholen
#endif
if ( STIST ) // STIST: true, wenn Starttaster ist gedrueckt
{ // Will Einschalten oder ausschalten
zfta_tick = acttick ;
zfta = ZFTA_Warteein_verzoegerung ;
return ; // Ist nicht, weiterwarten
}
}
/*!
* Warte bis der Taster lange genug gedrueckt wird
*/
FUNCT void ZFTA_Warteein_verzoegerung(void)
{
#if LPT1 > 0
STPIN = inportb( LPT1+1 ); // Zustand 0x379 hereinholen
#endif
if ( STIST ) // STIST: true, wenn Starttaster ist gedrueckt
{
if ( difftick( zfta_tick, acttick ) > sectotick( 0.030 ) )
{ // schon 30 ms
Tasteraktion = true ;
// Der Taster ist gedruect, die Aktion ist ausgefuehrt, warte bis Taster losgelassen
zfta = ZFTA_Warteaus ;
}
}
else // war zu kurz gedrueckt:
{ // Der Taster wurde nur gaaanz kurz gedrueckt
zfta = ZFTA_Istaus ;
}
}
// ----------------------------------------------------------------------
// --------------------------------- Unterprogramme fuer Dialog ---------
// ----------------------------------------------------------------------
/*!
* Zeige den Namen der Software und die Version (=Datum) an
* Die Ausgabe kann auf die LCD oder die serielle Schnittstelle gehen.
*/
FUNCT void out_version(void)
{
// 01234567890123456789
out_puts_P( PSTR("\nMISPAV " VERSION " SN " ) );
#ifdef MISPAV_SN
out_uint32( MISPAV_SN );
#endif
}
FUNCT void eeprom_schreiben(void)
{
#if OPT_SIMULATOR
return ;
#else
u8 *pv ;
u8 *pa ;
uint iz ;
out_puts_P( PSTR( "EEPROM schreiben" ) );
// Vom EEPROM in den Vergleichsspeicher holen.
if ( tseeprom_read_crc( EEPROM_offset, (u8 *)&vsp, sizeof(vsp) ) != 0 )
{ // Das EEPROM konnte gar nicht gelesen werden
// 01234567890123456789
out_puts_P( PSTR( "\nFEHLER EEPROM ls1" ) );
tsdelay( msectotick(1000) ); // fuer einige Zeit angezeigt lassen.
// Konnte nicht lesen: Den gesamten Arbeitsspeicher schreiben
if ( tseeprom_write_crc( EEPROM_offset, (u8 *)&asp, sizeof(asp) ) != 0 )
{
out_puts_P( PSTR( "\nFEHLER EEPROM schr" ) );
eepromfehler = true ;
return;
}
if ( tseeprom_read_crc( EEPROM_offset, (u8 *)&vsp, sizeof(vsp) ) != 0 )
{
// 01234567890123456789
out_puts_P( PSTR( "\nFEHLER EEPROM ls2" ) );
eepromfehler = true ;
return;
}
}
pv = (u8 *)&vsp ; // Das ist gerade im EEPROM
pa = (u8 *)&asp ; // Das sollte im EEPROM sein
for ( iz = 0 ; iz < sizeof(vsp) ; ++iz )
{
if ( pv[iz] != pa[iz] ) // Es gibt geaenderte Daten. Alles schreiben
{ // vom Arbeitsspeicher in das EEPROM schreiben
tseeprom_write_crc( EEPROM_offset, (u8 *)&asp, sizeof(asp) ) ;
break ;
}
}
tseeprom_read_crc( EEPROM_offset, (u8 *)&vsp, sizeof(vsp) );
pv = (u8 *)&vsp ;
pa = (u8 *)&asp ;
eepromfehler = false ;
for ( iz = 0 ; iz < sizeof(vsp) ; ++iz )
{
if ( pv[iz] != pa[iz] )
{
tslcd_pos(0,1);
out_puts_P( PSTR( "\nFEHLER EEPROM vgl" ) );
eepromfehler = true ;
}
}
if ( !eepromfehler )
{
tslcd_pos(0,1);
out_puts_P( PSTR( "\nEEPROM Inhalt vgl OK" ) );
}
#endif
}
FUNCT float p_zu_d( u8 k, s16 w )
{
return (w+asp.vk[k].null)*asp.vk[k].fkkal/16384.0F;
}
/*
* Berechne den Darstellungswert aus dem aktuellen Wert im Kanal k
*/
/*
FUNCT float dWert( u8 k)
{
#if DEBUG_INDEX
if ( k >= NKALKAN )
{
k = k ;
Eprintf(( "dWert( u8 k) k=%u", k ));
}
#endif
return p_zu_d( k, vkanal[k].w );
}
*/
FUNCT void out_kanal( u8 k)
{
// out_printf( "%5f", dWert(k) );
#if LCD_XMAX <= 16
out_printf( "%5f", vkanal[k].dar );
#elif LCD_XMAX <= 18
out_printf( "%6f", vkanal[k].dar );
#else // 20 und mehr
out_printf( "%7f", vkanal[k].dar );
#endif
}
FUNCT void out_Isoll( void )
{
// vkanal[XIS].d = (vkanal[XIS].w+asp.vk[XIS].null)*asp.vk[XIS].fkkal/16384.0F;
// out_printf( "%6fA", vkanal[XIS].d );
out_kanal( XIS );
out_char( 'A' );
}
FUNCT void out_Usoll( void )
{
// vkanal[XUS].d = (vkanal[XUS].w+asp.vk[XUS].null)*asp.vk[XUS].fkkal/16384.0F ;
// out_printf( "%6fV", vkanal[XUS].d );
out_kanal( XUS );
out_char( 'V' );
}
FUNCT void out_Imess( void )
{
// vkanal[XIM].d = (vkanal[XIM].w+asp.vk[XIM].null)*asp.vk[XIM].fkkal/16384.0F ;
// out_printf( "%6fA", vkanal[XIM].d );
out_kanal( XIM );
out_char( 'A' );
}
FUNCT void out_Umess( void )
{
// vkanal[XUM].d = (vkanal[XUM].w+asp.vk[XUM].null)*asp.vk[XUM].fkkal/16384.0F ;
// out_printf( "%6fV", vkanal[XUM].d );
out_kanal( XUM );
out_char( 'V' );
}
FUNCT void out_Pist( void )
{
float y ;
// y = dWert(XUM) * dWert( XIM );
y = vkanal[XUM].dar * vkanal[XIM].dar ;
out_printf( "%6kW", y );
}
FUNCT void out_hhmmss( void )
{
u32 r ;
u8 s ;
u8 m ;
u8 h ;
r = ulSekunden ;
s = (u8)(r % 60UL) ;
r /= 60UL;
m = (u8)(r % 60UL) ;
r /= 60UL;
h = (u8)r ;
out_printf( "%02u:%02u:%02u", h,m,s );
}
FUNCT void out_temperatur(void)
{
out_printf( "%5.1fC", p_zu_d( XTM, vkanal[XTM].w ) );
}
FUNCT void out_UKE(void)
{
out_printf( "%5.2fV", p_zu_d( XUKE, vkanal[XUKE].w ) );
}
FUNCT void Ausgabe_w_Usoll(s16 x)
{
if ( x > USOLLMAX ) x = USOLLMAX ;
if ( x < USOLLMIN ) x = USOLLMIN ;
if ( x > asp.vk[XUS].grenz ) x = asp.vk[XUS].grenz ;
vkanal[XUS].w = x ;
vkanal[XUS].dar = p_zu_d( XUS, vkanal[XUS].w );
dac(0,vkanal[XUS].w); // an DAC ausgeben
}
FUNCT void Ausgabe_w_Isoll(s16 x)
{
if ( x > ISOLLMAX ) x = ISOLLMAX ;
if ( x < ISOLLMIN ) x = ISOLLMIN ;
if ( x > asp.vk[XIS].grenz ) x = asp.vk[XIS].grenz ;
vkanal[XIS].w = x ;
vkanal[XIS].dar = p_zu_d( XIS, vkanal[XIS].w );
dac(1,vkanal[XIS].w); // an DAC ausgeben
}
FUNCT void rechne_dac_Usoll(float d)
{
float fw ;
fw = (d *16384.0F / asp.vk[XUS].fkkal) - asp.vk[XUS].null;
if ( fw > 32767.0 ) fw = 32767.0 ;
if ( fw < -32767.0 ) fw = -32767.0 ;
Ausgabe_w_Usoll((s16)fw); // und Ausgabe an dac(0
}
FUNCT void rechne_dac_Isoll(float d)
{
float fw ;
fw = (d *16384.0F / asp.vk[XIS].fkkal) - asp.vk[XIS].null;
if ( fw > 32767.0 ) fw = 32767.0 ;
if ( fw < -32767.0 ) fw = -32767.0 ;
Ausgabe_w_Isoll((s16)fw); // und Ausgabe an dac(1
}
#if OPT_SER_DIAL
FUNCT void out_txt_wertbegrenzt(void)
{
out_puts_P( PSTR( " Wert wird begrenzt\n" ));
}
FUNCT void out_txt_vwertbegrenzt(void)
{
out_puts_P( PSTR( " Variable Grenze\n" ));
}
FUNCT void out_Fehler_Eingabe(void)
{
out_puts_P( PSTR("*: Eingabefehler") );
}
FUNCT void dialog_Spannung(void)
{
float d ;
out_Usoll(); // Anzeigen des alten Sollwertes
out_cursor_deleol();
if ( in_float_P( PSTR( "Usoll" ), &d ) != 0 ) // Hole neuen Sollwert
{
out_Fehler_Eingabe();
return ;
}
rechne_dac_Usoll(d); // und Ausgeben an dac
out_char(':');
out_Usoll(); // Anzeigen
}
FUNCT void dialog_Strom(void)
{
float d ;
out_Isoll();
out_cursor_deleol();
if ( in_float_P( PSTR( "Isoll" ), &d ) != 0 )
{
out_Fehler_Eingabe();
return ;
}
rechne_dac_Isoll(d); // und Ausgeben an dac
out_char(':');
out_Isoll(); // Anzeigen
}
#ifndef AVR
bool bLogon ;
FUNCT void out_log_char( const u8 c )
{
if ( bLogon ) logoutocha(c);
vtocha(c);
}
#define BLOGON bLogon = true
#define BLOGOFF bLogon = false
#else // nicht #ifndef AVR
#define BLOGON // Koennte hier auch ein Steuersignal fuer hetrm senden, z.B: [Ctrl+R] 022 0x12
#define BLOGOFF // Koennte hier auch ein Steuersignal fuer hetrm senden, z.B: [Ctrl+T] 024 0x14
// Siehe file:///D|/h/home/html/hjh/pc/ascii.htm
#endif // Ende #ifndef AVR
FUNCT void kal_Fenster(void) { // Erreicht durch case 'k'
static u8 mkal = XUM ;
u8 m ; // Anzeigekanal
u8 c ;
u32 kastart ;
float ff ;
float fGrenzwert ;
float fnull ;
out_cursor_cls();
if ( mkal < 0 || mkal >= NKALKAN ) mkal = 0 ;
if( NKALKAN == 0 )
{ // Das erzeugt keinen Code (AVR) weil NKALKAN #defined eine Zahl ist
out_puts_P( PSTR( "Es gibt keinen ADC-Kanal\n") );
return ;
}
M_k_weiter:
BLOGOFF ;
/*
for ( i = 0 ; i < 11 ; ++i )
{
out_cursor_curpos( 0, i );
out_cursor_deleol();
tsnext("k1");
}
*/
/*
0 1 2 3 4 5 6 7
01234567890123456789012345678901234567890123456789012345678901234567890123456789
1 :K# WW Roh 'n'Nul 'f'Fak Darst 'a'Roh 'v'Ref 'b'Roh 'w'Ref 'm'Max
2 : 0 US 0 -169 30.000 -0.309V 0 0.0000 0 0.0000 23.690
3 : 1 IS 0 0 2.0000 0.0000A 0 0.0000 0 0.0000 1.6000
4 :*2 UI 6164 0 52.224 19.648V 0 0.0000 0 0.0000
5 : 3 IM 2665 5 2.0000 0.3259A 0 0.0000 0 0.0000
6 :Kanal adc0[%] adc1[%] adc2[%] adc3[%] adc4[%] adc5[%] adc6[%] adc7[%]
7 :min 33.12 14.33 12.79 27.96 55.35 78.92 81.91 61.12
8 :mit 37.62 16.27 13.98 32.11 60.02 81.38 83.66 65.53
9 :grenz 42.30 18.73 15.74 36.53 64.53 83.32 84.86 69.69
*/
// Kopfzeile und nicht veraenderliche Daten nur ein Mal anzeigen
out_cursor_curpos( 0, 0 );
out_puts_P( PSTR(
"Kanal Roh 'n'Nul 'f'Fak Darst 'a'Roh 'v'Ref 'b'Roh 'w'Ref 'g'Grenze" ));
out_cursor_deleol();
// erster Block
for ( m = 0 ; m < NKALKAN; ++m )
{
out_cursor_curpos( 0, 1+m );
out_cursor_deleol();
out_uint8( m );
if ( mkal == m )
{
out_char('*'); // 1.
}
else
{
out_char(':');
}
// out_char(' ');
tsnext("k2");
out_char( pgm_read_byte( &tx_Kanal[2*m] ) );
out_char( pgm_read_byte( &tx_Kanal[2*m+1] ) );
/* switch( m )
{
case XUS:
out_puts_P( PSTR("US") );
break ;
case XIS:
out_puts_P( PSTR("IS") );
break ;
case XUM:
out_puts_P( PSTR("UM") );
break ;
case XIM:
out_puts_P( PSTR("IM") );
break ;
case XTM:
out_puts_P( PSTR("TM") );
break ;
case XUKE:
out_puts_P( PSTR("UK") );
break ;
}
*/ tsnext("k3");
// out_cursor_curpos( 13, 1+m );
// out_printf(format_6_d_b, asp.vk[m].null ); // %5d : von -9999 bis 32767
// out_printf(format_6_f_b, asp.vk[m].fkkal );
out_cursor_curpos( 37, 1+m );
out_printf(format_6_d_b, vkanal[m].a.Roh);
out_printf(format_6_f_b, vkanal[m].a.Ref); // %5k
out_printf(format_6_d_b, vkanal[m].b.Roh);
out_printf(format_6_f_b, vkanal[m].b.Ref);
// Die Einstellbegrenzung
if ( m < 2 ) out_printf( "%6f", p_zu_d( m, asp.vk[m].grenz ) );
out_cursor_deleol();
if ( in_check() ) goto MBefehl ;
}
// Immer noch die Festwerte
out_cursor_curpos( 0, 1+NKALKAN ); // Roh
out_puts_P( PSTR("Kanal " ));
for ( m = 0 ; m < N_ADC_CHAN ; ++m ) // Tabellenueberschrift
{ // " adc0[%] adc1[%] adc2[%] adc3[%] adc4[%] adc5[%] adc6[%] adc7[%]"
out_printf( " adc%1u[%%]", m );
tsnext("mink");
}
out_puts_P( PSTR("\nmin \nmit \nmax " ));
#if ( WIN95 && (VT_MDOS==1) ) || DOS622 || MSVC40 || MSVCXX
vtrefresh();
#endif
if ( in_check() ) goto MBefehl ;
out_cursor_curpos( 0, 13 );
out_puts_P( PSTR( "Befehle im Kalibriermenue:" )); out_cursor_deleol();
out_puts_P( PSTR( "\n u i - Spannung / Strom neu vorgeben" )); out_cursor_deleol();
out_puts_P( PSTR( "\n a b - Rohwert a b von ADC-Wert uebernehmen" )); out_cursor_deleol();
out_puts_P( PSTR( "\n c d - Rohwert a b vorgeben" )); out_cursor_deleol();
out_puts_P( PSTR( "\n v w - Referenzwert v w vorgeben" )); out_cursor_deleol();
out_puts_P( PSTR( "\n l - Lineare Gerade berechnen" )); out_cursor_deleol();
if ( mkal < 2 ) out_puts_P( PSTR( "\n g - Grenzwert einstellen" ));
else out_nl();
out_cursor_deleol();
out_puts_P( PSTR( "\n n f - Nullpukt, Faktor neu eingeben" )); out_cursor_deleol();
out_puts_P( PSTR( "\n 0..5 - Auswahl der Zeile K# zur Bearbeitung" )); out_cursor_deleol();
out_puts_P( PSTR( "\n s - EEPROM schreiben" )); out_cursor_deleol();
out_puts_P( PSTR( "\n x - Kalibrierdialog verlassen" )); out_cursor_deleol();
if ( in_check() ) goto MBefehl ;
kastart = gettick();
for(;;) // laufende Anzeige bis eine Taste betaetigt in_check()
{
if ( difftick( kastart, gettick() ) < sectotick( 0.3 ) )
{
tsnext("kla");
if ( in_check() ) break ;
continue ;
}
kastart = gettick();
// Oberer Anzeigeblock
for ( m = 0 ; m < NKALKAN; ++m )
{
if ( in_check() ) break ;
out_cursor_curpos( 5, 1+m ); // Roh
out_printf( " %6d", vkanal[m].w );
out_cursor_curpos( 28, 1+m ); // Darst
switch( m )
{
case XUS:
out_Usoll();
break ;
case XIS:
out_Isoll();
break ;
case XUM:
out_Umess();
break ;
case XIM:
out_Imess();
break ;
case XTM:
out_temperatur();
break ;
case XUKE:
out_UKE();
break ;
}
// Auch die Kalibrierwerte hier anzeigen, falls jemand beobachten
// will wie diese vom DigiPoti veraendert werden.
out_cursor_curpos( 12, 1+m );
out_printf(format_6_d_b, asp.vk[m].null ); // %5d : von -9999 bis 32767
out_printf(format_6_f_b, asp.vk[m].fkkal );
tsnext("k3");
}
if ( in_check() ) break ;
// Anzeigeblock "adc"
// out_cursor_curpos( 7, 1+1+NKALKAN ); // min
for ( m = 0 ; m < N_ADC_CHAN ; ++m )
{
out_cursor_curpos( 7+m*8, 1+1+NKALKAN ); // min
out_printf( format_7_2_f, (float)adcfk[m] * (100.0/16384.0) );
tsnext("mink");
}
if ( in_check() ) break ;
for ( m = 0 ; m < N_ADC_CHAN ; ++m )
{
out_cursor_curpos( 7+m*8, 1+2+NKALKAN ); // mittel
out_printf( format_7_2_f, (float)adcf[m] * (100.0/16384.0) );
tsnext("mitk");
}
if ( in_check() ) break ;
for ( m = 0 ; m < N_ADC_CHAN ; ++m )
{
out_cursor_curpos( 7+m*8, 1+3+NKALKAN ); // grenz
out_printf( format_7_2_f, (float)adcfg[m] * (100.0/16384.0) );
tsnext("maxk");
}
out_nl();
if ( in_check() ) break ;
}
MBefehl:;
for ( u8 y = 13 ; y < 24 ; ++y )
{
out_cursor_curpos( 0, y );
out_cursor_deleol();
}
// Steht in Zeile 23
BLOGON ;
out_cursor_curpos( 0, 13 );
out_puts_P( PSTR("Kal:") ); out_cursor_deleol();
c = in_char();
out_char(c);
switch(c) {
case '0': mkal = 0 ; // Kanal 0 "US" Spannung Sollwert
break ;
case '1': mkal = 1 ; // Kanal 1 "IS" Strom Sollwert
break ;
case '2': mkal = 2 ;
break ;
case '3': mkal = 3 ;
break ;
case '4': mkal = 4 ;
break ;
case '5': mkal = 5 ;
break ;
case 'a': // 'k' Kalib - Punkt 'a'
vkanal[mkal].a.Roh = vkanal[mkal].w ;
break ;
case 'b':
vkanal[mkal].b.Roh = vkanal[mkal].w ;
break ;
case 'c': // 'k' Kalib - Punkt 'a' manuell eingeben
in_int16_P( PSTR("ADC-Punkte a "), &(vkanal[mkal].a.Roh) );
break ;
case 'd':
in_int16_P( PSTR("ADC-Punkte b "), &(vkanal[mkal].b.Roh) );
break ;
case 'v': // 'k' Kalib - Punkt 'a'
in_float_P( PSTR("Referenzwert v "), &(vkanal[mkal].a.Ref) );
break ;
case 'w':
in_float_P( PSTR("Referenzwert w "), &(vkanal[mkal].b.Ref) );
break ;
case 'l':
out_puts_P( PSTR( "\nJustage: ") );
ff = ( vkanal[mkal].b.Ref - vkanal[mkal].a.Ref ) / ( vkanal[mkal].b.Roh - vkanal[mkal].a.Roh );
fnull =vkanal[mkal].a.Ref / ff - vkanal[mkal].a.Roh ;
ff *= 16384.0 ;
out_puts_P( PSTR( "\nVorschlag: ") );
out_printf( "fnull=%9k ", fnull );
out_printf( "ff=%9k ", ff );
if ( fnull > 16380.0 )
{
out_puts_P( PSTR( "null zu gross\n" ) );
break ;
}
if ( fnull < -16380.0 )
{
out_puts_P( PSTR( "null zu negativ" ) );
break ;
}
out_puts_P( PSTR( "\n Uebernehmen [j/n]?") );
c = in_char();
out_char(c);
if ( c == 'j' )
{
asp.vk[mkal].fkkal = ff ;
asp.vk[mkal].null = (s16)fnull ;
out_puts_P( PSTR( "\n Uebernommen") );
}
else
{
out_puts_P( PSTR( "\n Verworfen") );
}
if ( mkal < 2 ) goto Mneusoll ;
break ;
case 'n': // 'k' Kalibriermenue Neueingabe der Nullpunktverschiebung
out_cursor_deleol(); in_int16_P( PSTR("Null"), &asp.vk[mkal].null );
if ( mkal < 2 ) goto Mneusoll ;
break ;
case 'f': // 'k' Neueingabe des Kalibrier - Faktor
out_cursor_deleol(); in_float_P( PSTR("Faktor"), &asp.vk[mkal].fkkal );
if ( mkal < 2 ) goto Mneusoll ;
break ;
case 'g': // 'k' Kalibriermenue Grenzwert Aendern, fuer Sollwertbegrenzung
out_cursor_deleol();
if ( in_float_P( PSTR("Grenze"), &fGrenzwert ) == 0 )
{
// Umrechnen in ADC-Punkte
fGrenzwert = (fGrenzwert *16384.0F / asp.vk[mkal].fkkal) - asp.vk[mkal].null;
if ( fGrenzwert > 16000 )
{
out_txt_wertbegrenzt();
fGrenzwert = 16000 ;
}
if ( fGrenzwert < 1 )
{
out_txt_wertbegrenzt();
fGrenzwert = 1 ;
}
asp.vk[mkal].grenz = (s16)fGrenzwert ;
}
break ;
case 'u': // 'k' Kalibriermenue Spannung vorgben
dialog_Spannung();
break ;
case 'i': // 'k' Kalibriermenue Strom vorgeben
dialog_Strom();
break ;
case 's': // 'k' Kalibriermenue - Daten dauerhaft sichern
out_cursor_deleol();
out_puts_P(PSTR("\nEEPROM schreiben?"));
out_cursor_deleol();
c = in_char();
out_char( c );
if ( c == 'j' || c == 'y' )
{
eeprom_schreiben();
}
break ;
case 'x': // 'k' Kalibrieren Abbrechen / Beenden
return ;
} // end switch
goto M_k_weiter ;
Mneusoll:
if ( mkal == XUS ) Ausgabe_w_Usoll(vkanal[XUS].w);
if ( mkal == XIS ) Ausgabe_w_Isoll(vkanal[XIS].w);
goto M_k_weiter ;
}
/*
* Laufende Anzeige fuer den seriellen Dialog.
* Wird ein- ausgeschaltet durch 'a'
*/
FUNCT void lfdanz(void)
{
out_puts_P( PSTR(" S=")); tsnext("LA");
out_Usoll(); tsnext("LA");
out_puts_P( PSTR(" ")); tsnext("LA");
out_Isoll(); tsnext("LA");
out_puts_P( PSTR(" M=")); tsnext("LA");
out_Umess(); tsnext("LA");
out_puts_P( PSTR(" ")); tsnext("LA");
out_Imess(); tsnext("LA");
out_puts_P( PSTR(" T=")); tsnext("LA");
out_temperatur(); tsnext("LA");
out_puts_P( PSTR(" P=")); tsnext("LA");
out_Pist(); tsnext("LA");
out_puts_P( PSTR(" Z=")); tsnext("LA");
out_hhmmss(); tsnext("LA"); // Zeigt ulSekunden an, als hh:mm:ss
out_puts_P( PSTR(" Q=")); tsnext("LA");
out_printf("%6kAh", Ladung_as/3600.0 ); tsnext("LA"); // Zeigt Ladung seit Reset an, in uAh, mAh, Ah kAh
out_puts_P( PSTR(" UKE=")); tsnext("LA");
out_UKE(); tsnext("LA");
}
#if OPT_LCD
/*!
* Befehl 'l' : Das, was die LCD_Anzeige gerade anzeigt, jetzt an
* die serielle Schnittstelle ausgeben.
*/
FUNCT void out_lcd( void )
{
extern char lcd_Sollanzeige[LCD_YMAX][LCD_XMAX] ;
u8 y , x ;
u8 c ;
for ( y = 0 ; y < LCD_YMAX ; ++y )
{
out_nl();
for ( x = 0 ; x < LCD_XMAX ; ++x )
{
c = lcd_Sollanzeige[y][x] ;
if ( c < 0x20 || c >= 0x7f ) c = 0x20 ;
out_char( c );
}
}
out_puts_P( PSTR("\nLCD: Kommentar eingeben, Abbruch ^Z\n" ));
for(;;)
{
c = in_char();
if ( c == 0x1A ) break ;
out_char(c);
}
out_nl();
}
#endif
#ifdef AVR // 2024-04-30
#define ser_0_AVR_NFIFO 20
extern u8 ser_0_AVR_ifill;
extern u8 ser_0_AVR_iempty;
extern u8 ser_0_AVR_ififo[ser_0_AVR_NFIFO];
#endif
// ----------------------------------------------------------------------
// --------------------------------------- Dialog (seriell) -------------
// ----------------------------------------------------------------------
FUNCT void fun_DIA(void)
{
char c ;
u8 i ;
u8 sfmodbus_fehler_alt = 0 ;
u8 u8Altsec ;
// tsseropen(); // Das macht den Interrupt aus in m644B 0x00C1, UCSR0B
BLOGON ;
#ifndef AVR
vout_char[tshar_task_no] = out_log_char ;
#endif
out_version();
out_nl();
for(;;)
{
// Maximal 497 Ticks: bei 16MHz, 2000001 Ticks/sek : 497/2000001= 0,0002485s=0,2485ms
// Bei EEPROM-Schreiben 37 ms
out_nl();
while( in_check() == false )
{ // Keine Taste - laufende Anzeige
if ( sfmodbus_fehler_alt != sfmodbus_fehler )
{
out_uint8( sfmodbus_fehler );
sfmodbus_fehler_alt = sfmodbus_fehler ;
out_puts_P( PSTR( "=sfmodbus_fehler\n" ));
}
if ( bLaufendeAnzeige && (u8)ulSekunden != u8Altsec )
{
u8Altsec = (u8)ulSekunden ;
BLOGOFF ; // Nur im PC: Log-Datei abschalten
lfdanz();
out_cursor_deleol();
out_char(13); // CR
}
tsnext("nochar");
}
BLOGON ; // Nur im PC: Log-Datei abschalten
out_puts_P( PSTR("\nmispav:") );
// Es wurde ein Zeichen eingetippt
c = in_char() ;
out_char(c); // Den eingegebenen Kommandobuchstaben noch einmal anzeigen
out_char(' ');
switch ( c ) // Welcher Befehl ?
{
case '?' :
out_puts_P( PSTR( "Hilfe\n" ) ) ;
out_version();
out_puts_P( PSTR( "\nBedienung durch: \n"
"? - Hilfe anzeigen\n"
"u - Spannungsollwert vorgeben\n"
"i - Stromsollwert vorgeben\n"
"m - Messwerte anzeigen\n"
"a - laufend anzeigen\n"
"l - LCD-Anzeige hier ausgeben\n"
"k - Kalibrierung\n"
"r - Rueckstellen der Zaehler\n"
"z - Rechnerauslastung\n"
) );
#ifdef MODBUSADDRS
out_puts_P( PSTR( "b - Protokoll des MODBUS\n" ) );
#endif
#ifdef AVR
out_puts_P( PSTR( "p - Zustand aller Ports\n" ) );
#endif
#ifndef AVR
#ifndef __TURBOC__
out_puts_P( PSTR( "l - LCD-Anzeigen\n") ) ;
#endif
out_puts_P( PSTR( "q - Programm beenden\n") ) ;
#endif
break ;
case 'l' : // LCD-Anzeige auf Konsole anzeigen
out_lcd();
break ;
case 'z' :
out_puts_P( PSTR("z: Stackverwendung\nDIA:") );
ts_stack_usage( stkDIA, NSTKDIA);
#if OPT_LCD
out_puts_P( PSTR("\nLCD:") );
ts_stack_usage( stkLCD, NSTKLCD );
#endif
out_puts_P( PSTR("\nMES:") );
ts_stack_usage( stkMES, NSTKMES );
out_nl();
out_puts_P( PSTR(" 1s =") );
out_uint32( sectotick(1.000000) );
out_puts_P( PSTR("gettick() =") );
out_tick_sec( gettick() , 2 );
out_puts_P( PSTR("\nSchleife=") );
out_sec( max_difftick, 6 );
max_difftick = 0 ;
out_puts_P( PSTR(" LCD=") );
out_sec( max_difftickLCD, 6 );
max_difftickLCD = 0 ;
out_puts_P( PSTR(" N_ADC_CHAN:") );
out_uint32( N_ADC_CHAN );
if ( eepromfehler ) out_puts_P( PSTR( "\n EEPROM-Fehler" ));
else out_puts_P( PSTR( "\n EEPROM OK" ));
#ifdef AVR // 2024-04-30
// #define ser_0_AVR_NFIFO 20
// extern u8 ser_0_AVR_ifill;
// extern u8 ser_0_AVR_iempty;
// extern u8 ser_0_AVR_ififo[ser_0_AVR_NFIFO];
out_puts_P( PSTR("\nMODBUS_ifill=") );
out_uint8( ser_0_AVR_ifill );
out_puts_P( PSTR(" MODBUS_iempty=") );
out_uint8( ser_0_AVR_iempty );
out_nl();
for ( i = 0 ; i < ser_0_AVR_NFIFO ; ++i ) {
out_char( ' ' );
out_hex2( ser_0_AVR_ififo[i] );
}
out_puts_P( PSTR(" MODBUS_telegramm=\n") );
for ( i = 0 ; i < ser_0_AVR_NFIFO ; ++i ) {
out_hex2( sfmodbus_telegramm[i] );
if ( ++i >= 20 ) break ;
out_char( ' ' );
}
out_nl();
#endif
break ;
case 'u':
dialog_Spannung();
break ;
case 'i':
dialog_Strom();
break ;
case 'm':
lfdanz();
break;
#ifdef MODBUSADDRS
case 'b': // MODBUS, Statistik und empfangenes Telegramm
for ( i = 0 ; i < REGISTERCOUNT ; ++i )
{
if ( ( i % 8 ) == 0 ) out_nl(); else out_char(' ');
#if DEBUG_HatEmpfangen
if ( HatEmpfangen[i] ) out_char('E'); else out_char(' ');
#endif
out_printf( "R%u=%04X" , i , ModbusRegister[i] );
}
for ( i = 0 ; i < NMODBUS_IN ; ++i )
{
if ( i % 8 == 0 )
{
out_printf( "\nin%u", i );
}
out_printf( " %02X", sfmodbus_telegramm[i] );
}
out_nl();
#if OPT_ser0avri && defined(AVR)
out_puts_P( PSTR( "\n ser_0_AVR_ifill=") );
out_uint8( ser_0_AVR_ifill );
out_puts_P( PSTR( " ser_0_AVR_iempty=") );
out_uint8( ser_0_AVR_iempty );
out_nl();
#endif
break ;
#endif
case 'a': // Laufende Anzeige ein- / ausschalten
out_puts_P( PSTR( "lfd Anzeige ") );
if ( bLaufendeAnzeige )
{
bLaufendeAnzeige = false ;
out_puts_P( PSTR( "AUS" ) );
}
else
{
bLaufendeAnzeige = true ;
out_puts_P( PSTR( "EIN" ) );
}
break ;
case 'r': // RS - Rueckstellen der Zaehler
Ladung_as = 0 ;
ulSekunden = 0 ;
break ;
case 'k': // Kalibrierdialog mit laufender Anzeige
kal_Fenster();
break ;
#ifdef AVR
case 'p': // Ports anzeigen
/* out_puts_P( PSTR( " adc(0):" ) );
out_int16( adc(0) );
out_puts_P( PSTR( " adc(1):" ) );
out_int16( adc(1) );
out_puts_P( PSTR( " adcf[0]:" ) );
out_int16( adcf[0] );
out_puts_P( PSTR( " adcf[1]:" ) );
out_int16( adcf[1] );
*/ out_nl();
out_puts_P( PSTR( " PINA:" ) ) ; out_hex2(PINA);
out_puts_P( tx_B ) ; out_hex2(PINB);
out_puts_P( tx_C ) ; out_hex2(PINC);
out_puts_P( tx_D ) ; out_hex2(PIND);
out_puts_P( PSTR( " DDRA:" ) ) ; out_hex2(DDRA);
out_puts_P( tx_B ) ; out_hex2(DDRB);
out_puts_P( tx_C ) ; out_hex2(DDRC);
out_puts_P( tx_D ) ; out_hex2(DDRD);
out_puts_P( PSTR( " PORTA:" ) ) ; out_hex2(PORTA);
out_puts_P( tx_B ) ; out_hex2(PORTB);
out_puts_P( tx_C ) ; out_hex2(PORTC);
out_puts_P( tx_D ) ; out_hex2(PORTD);
out_nl();
/* out_puts_P( PSTR( " SREG:" ) ) ; out_hex2(SREG);
out_puts_P( PSTR( " UCSR0A:" ) ) ; out_hex2(UCSR0A);
out_puts_P( tx_B ) ) ; out_hex2(UCSR0B);
out_puts_P( tx_C ) ) ; out_hex2(UCSR0C);
*/
/* for( u8 m = 0 ; m < 4 ; ++m )
{
out_puts_P( PSTR( "vkanal[" ) ) ; out_uint8(m);
out_puts_P( PSTR( "].w=" ) ) ;
out_int16( vkanal[m].w) ;
out_puts_P( PSTR( " .a.Roh=" ) ) ;
out_int16( vkanal[m].a.Roh) ;
out_puts_P( PSTR( " .b.Roh=" ) ) ;
out_int16( vkanal[m].b.Roh) ;
out_nl();
}
*/
/* out_puts_P( PSTR( " DigPotWertA:" ) ) ; out_hex4( DigPotWertA );
out_puts_P( PSTR( " _alt:" ) ) ; out_hex4( DigPotWert_alt );
*/
break ;
#endif
// Folgende Funktionen nicht in der Elektronik, sondern nur im PC
#ifndef AVR // DOS, Windows, Linux, OS9
case 'q' : // Befehl 'q' Beenden
out_puts_P( PSTR( "Beenden" ) );
tseeprom_close(); // EEPROM Datei abschliessen
tsserclose();
exit(0);
#endif // #ifdef AVR
case 0x0d: // CR
case 0x0a: // LF
case 0x09: // Tab
case 0x20: // Leerzeichen
break ; // ignorieren
default:
out_puts_P( PSTR("*: Falsche Taste! Hilfe=?") );
break ;
}
#ifndef AVR
logoutflush();
#endif
tsnext("fun_dialog");
}
} // Ende FUNCT void fun_DIA(void)
#endif // #if OPT_SER_DIAL
FUNCT void lcd_anzeige_std(void)
{
tslcd_pos( 0, 0); // erste Zeile
#if LCD_XMAX >= 18
out_puts_P( PSTR("S") );
#endif
out_Usoll(); // Anzeige USoll 5stellig
out_char(' ');
out_Isoll();
out_char(' ');
tslcd_pos( 0, 1); // zweite Zeile
#if LCD_XMAX >= 18
out_puts_P( PSTR("M") ); // Messwert
#endif
out_Umess();
out_char(' ');
out_Imess();
out_char(' ');
}
FUNCT void lcd_menu( u8 iWahl, PGM_P ptxMenu )
{
u8 y0 = 0 ; // Nummer des ersten Menuepunktes
u8 yp ; // Zeilennummer auf der LCD-Anzeige
u8 ym ;
u8 c ;
PGM_P p ;
if ( ( iWahl & 0x0F ) >= LCD_YMAX )
{ // Menuepunkt 0 darf nicht oben sein
y0 = ( iWahl & 0x0f ) - ( LCD_YMAX - 1 ) ;
}
else
{ // Menuepunkt 0 darf oben sein
}
p = ptxMenu + 2*y0 ;
for(yp = 0 ; yp < LCD_YMAX ; ++yp ) // Zeile 0...3
{
ym = y0 + yp ; // Menupunkt 0..2 oder 0..3 oder 0..5
tslcd_pos( LCD_XMAX-3, yp);
if ( ym == iWahl )
{
out_char( '>' );
}
else if ( ym == (iWahl - 0x10) )
{
out_char( '*' );
}
else
{
out_char( ' ' );
}
c = pgm_read_byte( p );
if ( c == 0 ) // Ende des Textes im TEXT-Segment
{
out_char(' ');
}
else
{
++p ;
out_char( c );
}
c = pgm_read_byte( p );
if ( c == 0 )
{
out_char(' ');
}
else
{
++p ;
out_char( c );
}
}
}
/*!
* Beschleunigung beim schnelleren Drehen
*/
FUNCT void dynamik(void)
{ // Beschleunigungstyp 1
// static u8 bfak[10] = { 1, 1, 2, 3, 5, 8, 12, 18, 27, 40 } ;
static u8 bfak[10] = { 4, 6, 10, 14, 16, 16, 16, 16, 16, 16 } ;
u16 udiff ; // Absolutwert
ddiff = 0 ;
udiff = 0 ;
diff = ( DigPotWertA - DigPotWert_alt ) / GEBERTEILER ;
if ( diff == 0 )
{
startpotitick = gettick();
return ;
}
// Sind schon wieder einige zehntel Sekunde vergangen ?
if ( difftick( startpotitick, gettick() ) < sectotick(0.2) )
{
diff = 0 ;
return ;
}
startpotitick = gettick();
DigPotWert_alt = DigPotWertA ;
// DigPotWert_alt += diff * GEBERTEILER ;
ddiff = diff ;
DBprintf(( "diff = %d", diff ));
if ( ddiff < 0 ) udiff = -ddiff ;
else udiff = ddiff ;
// Berechnen der dynamisierten Differenz
if ( udiff >= 10 ) ddiff *= bfak[9] ;
else ddiff *= bfak[udiff] ;
#if OPT_TEST_DIGIPOTI
if ( diff != 0 ) vtprintf("dynamik(): digiPoti: %9.3fs DigPotWertA=%d diff=%d ddiff=%d \n", ticktosec(startpotitick), DigPotWertA, diff, ddiff );
#endif
}
/*!
* Speichern der Kalibrier- und Einstelldaten im EEPROM mit LCD-Anzeige,
* dass das Speichern erfolgreich war.
*/
FUNCT void lcd_speichern(void)
{
tslcd_cls();
tslcd_pos(0,0);
eeprom_schreiben();
tslcd_refresh();
tsdelay( msectotick(2000) ); // Meldung fuer einige Zeit angezeigt lassen.
tslcd_cls();
}
/*!
* Kalibrieren eines Kanals
* param iKanal - 0 = US kalibriere Usoll
- iMenu=1 = IS kalibriere Isoll
- iMenu=2 = UI kalibriere Uist
- iMenu=3 = II kalibriere Iist
*/
FUNCT void lcd_kalibk(u8 iKanal)
{
u8 iMenu ;
tslcd_cls();
startpotitick = acttick ;
iMenu = 0 ;
for(;;) // Wir bleiben im Kalibriermenue
{
#if LCD_YMAX >=4
lcd_anzeige_std();
#endif
// 2024-04-30
tslcd_refresh();
while ( difftick( startlcdtick, acttick ) < sectotick( LCD_REFRESH_ZEIT) ) tsnext("wlcd");
startlcdtick = acttick ;
tslcd_pos( 0, LCD_YMAX >=4 ? 2 : 0 ); // Zeile 2 : "Kalib N -30"
out_char( pgm_read_byte( &tx_Kanal[2*iKanal] ) );
out_char( pgm_read_byte( &tx_Kanal[2*iKanal+1] ) );
/* switch( iKanal )
{
case XUS : out_puts_P( PSTR( "US" ) ); break ;
case XIS : out_puts_P( PSTR( "IS" ) ); break ;
case XUM : out_puts_P( PSTR( "UM" ) ); break ;
case XIM : out_puts_P( PSTR( "IM" ) ); break ;
case XTM : out_puts_P( PSTR( "TM" ) ); break ;
case XUKE : out_puts_P( PSTR( "UK" ) ); break ;
}
*/ out_puts_P( PSTR( " Kalib " ) );
out_kanal(iKanal);
/*switch( iKanal )
{
case XUS : out_Usoll(); break ;
case XIS : out_Isoll(); break ;
case XUM : out_Umess(); break ;
case XIM : out_Imess(); break ;
case XTM : out_kanal(XTM); break ;
case XUKE : out_kanal(XUKE); break ;
}
*/ out_char(' ');
tslcd_pos( 0, LCD_YMAX >=4 ? 3 : 1 ); // Zeile 3 : "Usoll F 30.3"
out_char('N');
out_printf( "%5d", asp.vk[iKanal].null );
out_char('F');
out_printf( "%6f", asp.vk[iKanal].fkkal );
out_char(' ');
// iMenu = 0 1 2
lcd_menu( iMenu, PSTR("NU" "FA" "NG" "X" ) ) ;
if ( Tasteraktion ) // Der Taster wurde niedergedrueckt.
{
Tasteraktion = 0 ;
switch( iMenu )
{
case 0: iMenu = 0x10 ; // Null jetzt ausgewaehlt '*' fuer Aenderung des Wertes
break ;
case 0x10: iMenu = 0x0 ; // Null jetzt nur noch vorgewaehlt '>'
break ;
case 1: iMenu = 0x11 ; // Faktor jetzt ausgewaehlt '*' fuer Aenderung des Wertes
break ;
case 0x11: iMenu = 1 ; // Faktor jetzt nur noch vorgewaehlt '>'
break ;
case 2: // Negiere den Faktor
if ( iKanal == XTM ) asp.vk[iKanal].fkkal = -asp.vk[iKanal].fkkal ;
return ;
case 3: // Verlassen
return ;
}
continue ;
}
dynamik();
if ( diff != 0 ) // Wurde das Poti verdreht ?
{
switch( iMenu )
{
case 0x10: // "*N" Null
asp.vk[iKanal].null += ddiff*POTIFAKTOR ;
if ( asp.vk[iKanal].null < -16000 ) asp.vk[iKanal].null = -16000 ;
if ( asp.vk[iKanal].null > 16000 ) asp.vk[iKanal].null = 16000 ;
break ;
case 0x11 : // "*F" Faktor veraendern
if ( diff < 0 )
{ // Vermindern
if ( asp.vk[iKanal].fkkal <= -0.1 || asp.vk[iKanal].fkkal >= 0.1 )
asp.vk[iKanal].fkkal /= 1.0F-ddiff/1000.0F ;
}
else
{ // Erhoehen
if ( asp.vk[iKanal].fkkal > -8000.0 && asp.vk[iKanal].fkkal < 8000.0 )
asp.vk[iKanal].fkkal *= 1.0F+ddiff/1000.0F ;
}
break ;
default: // Anderen Menuepunkt vorwaehlen
MENU( iMenu, diff) ;
if ( iMenu > 128 ) iMenu = 0 ;
if ( iMenu >= 4 ) iMenu = 3 ;
break ;
} // Ende switch( iMenu )
} // Ende if ( diff != 0 ) // Wurde das Poti verdreht ?
tsnext("lcd_kalibk");
}
return ;
}
FUNCT void lcd_kalib(void)
{
u8 iMenu ;
// iMenu=0 = XUS "US" kalibriere Usoll
// iMenu=1 = XIS "IS" kalibriere Isoll
// iMenu=2 = XUM "UM" kalibriere Umess
// iMenu=3 = XIM "IM" kalibriere Imess
// iMenu=4 = XTM "TM" kalibriere Temperaturmessung NTC
// iMenu=5 = XUKE "UK" kalibriere Messung der Versorgungsspannung
// iMenu=6 = "SP" Speichern in EEPROM
// iMenu=7 = "X " Exit, zurueck in das Hauptmenue
tslcd_cls();
startpotitick = acttick ;
iMenu = 0 ;
for(;;)
{
// 2024-04-30
tslcd_refresh();
while ( difftick( startlcdtick, acttick ) < sectotick( LCD_REFRESH_ZEIT) ) tsnext("wlcd");
startlcdtick = acttick ;
DBprintf(( "lcd_kalib ruft lcd_anzeige_std()" ));
lcd_anzeige_std();
DBprintf(( "lcd_kalib zurueck von lcd_anzeige_std()" ));
// Wir bleiben im Kalibriermenue
lcd_menu( iMenu, PSTR("US" "IS" "UM" "IM" "TM" "UK" "SP" "X" ) ) ; // Das ist EIN String
tslcd_refresh();
if ( Tasteraktion )
{
Tasteraktion = 0 ;
switch( iMenu )
{
case 0: // Menupunkt 0: Kalibriere XUS
case 1: // Menupunkt 1: Kalibriere XIS
case 2: // Menupunkt 2: Kalibriere XUM
case 3: // Menupunkt 3: Kalibriere XIM
case 4: // Menupunkt 4: Kalibriere XTM
case 5: // Menupunkt 5: Kalibriere XUKE
lcd_kalibk(iMenu); // Jetzt 0..5 einstellen
break ;
case 6: // Menupunkt 6: Speichern in EEPROM
lcd_speichern();
break ;
case 7:
return ;
default:
break ;
}
DigPotWert_alt = DigPotWertA ;
continue ; // Tasteraktion verhindert Dreherkennung
} // Ende if ( Tasteraktion )
dynamik();
if ( diff != 0 ) // Wurde das Poti verdreht ?
{
MENU( iMenu, diff) ; // Anderen Menuepunkt
if ( iMenu >= 128 ) iMenu = 0 ; // u8 kam unter 0, also nach 255,254...
if ( iMenu >= 8 ) iMenu = 7 ; // Es geht nicht weiter
} // Ende if ( diff != 0 ) // Wurde das Poti verdreht ?
tsnext("lcdd");
}
}
// ----------------------------------------------------------------------
// --------------------------------------- LCD-Anzeige ------------------
// ----------------------------------------------------------------------
FUNCT void lcd_max(void)
{
u8 iMenu ;
tslcd_cls();
startpotitick = acttick ;
iMenu = 0 ;
for(;;)
{
// Laufende Anzeige
#if LCD_YMAX >=4
lcd_anzeige_std(); // US IS UI II
#endif
tslcd_pos( 0, LCD_YMAX >=4 ? 2 : 0 );
out_puts_P( PSTR("Umax:") );
out_printf( "%6fV", p_zu_d(XUS, asp.vk[XUS].grenz) );
tslcd_pos( 0, LCD_YMAX >=4 ? 3 : 1 );
out_puts_P( PSTR("Imax:") );
out_printf( "%6fA", p_zu_d(XIS, asp.vk[XIS].grenz) );
// tslcd_pos( 0,3 );
// 0 1 2 3
lcd_menu( iMenu, PSTR( "UG" "IG" "SP" "X") ) ;
tslcd_refresh();
if ( Tasteraktion )
{
Tasteraktion = 0 ;
switch( iMenu )
{
case 0: iMenu = 0x10 ; // Jetzt UG U Grenz einstellen
break ;
case 0x10: iMenu = 0 ;
break ;
case 1: iMenu = 0x11 ; // Jetzt IG I Grenz einstellen
break ;
case 0x11: iMenu = 1 ;
break ;
case 2: // Menupunkt 2: Speichern in EEPROM
lcd_speichern();
break ;
case 3: // X - Maximalwertmenue verlassen
return ;
}
DigPotWert_alt = DigPotWertA ;
continue ; // Tasteraktion verhindert Dreherkennung
} // Ende if ( Tasteraktion )
dynamik();
if ( diff != 0 ) // Wurde das Poti verdreht ?
{
switch( iMenu )
{
case 0x10: // USoll Maximum einstellen
asp.vk[XUS].grenz += ddiff*POTIFAKTOR ;
if ( asp.vk[XUS].grenz > USOLLMAX )
{
asp.vk[XUS].grenz = USOLLMAX ;
}
if ( asp.vk[XUS].grenz < USOLLMIN )
{
asp.vk[XUS].grenz = USOLLMIN ;
}
if ( vkanal[XUS].w > asp.vk[XUS].grenz )
{
vkanal[XUS].w = asp.vk[XUS].grenz ;
Ausgabe_w_Usoll( vkanal[XUS].w ); // und Ausgabe an dac(0
}
break ;
case 0x11 : // ISoll Maximum einstellen
asp.vk[XIS].grenz += ddiff*POTIFAKTOR ;
if ( asp.vk[XIS].grenz > ISOLLMAX )
{
asp.vk[XIS].grenz = ISOLLMAX ;
}
if ( asp.vk[XIS].grenz < ISOLLMIN )
{
asp.vk[XIS].grenz = ISOLLMIN ;
}
if ( vkanal[XIS].w > asp.vk[XIS].grenz )
{
vkanal[XIS].w = asp.vk[XIS].grenz ;
Ausgabe_w_Isoll( vkanal[XIS].w ); // und Ausgabe an dac(1,
}
break ;
default: // Anderen Menuepunkt vorwaehlen
MENU( iMenu, diff) ;
if ( iMenu > 128 ) iMenu = 0 ;
if ( iMenu >= 4 ) iMenu = 3 ;
break ;
} // Ende switch( iMenu )
} // Ende if ( diff != 0 ) // Wurde das Poti verdreht ?
tsnext("lcdd");
}
}
// ----------------------------------------------------------------------
// --------------------------------------- LCD-Anzeige ------------------
// ----------------------------------------------------------------------
FUNCT void fun_LCD(void)
{
u8 iMenu ;
tslcd_init();
tslcd_Schreibzeiger_aus();
bNoAutoRefreshLCD = true ; // Nicht automatisch bei jedem Zeichen auffrischen
//out_puts_P( PSTR( "U_I_Steller \n" ) );
//out_puts_P( PSTR( "VERSION \n" ) );
tslcd_pos( 0, 0);
#if LCD_YMAX >=4
tslcd_puts_P( PSTR( "mispav\n" __DATE__ "\n" __TIME__ ) );
#else // 2-zeilige Anzeige
tslcd_puts_P( PSTR( "mispav " __TIME__ "\n" __DATE__ ) );
#endif
tslcd_refresh();
// version();
tsdelay( sectotick(2.4) ); // Die Version fuer einige Zeit angezeigt lassen.
tslcd_cls();
// Initialisieren fuer dynamik();
// startpotitick = acttick ;
startlcdtick = acttick ;
iMenu = 0 ;
for(;;)
{
// 2024-04-30
tslcd_refresh();
while ( difftick( startlcdtick, acttick ) < sectotick( LCD_REFRESH_ZEIT) ) tsnext("wlcd");
startlcdtick = acttick ;
// Wir sind zunaechst im Hauptmenu
// Laufende Anzeige
DBprintf(( "fun_LCD() ruft lcd_anzeige_std()" ));
lcd_anzeige_std();
DBprintf(( "fun_LCD() zurueck von lcd_anzeige_std()" ));
#if LCD_YMAX >=4
tslcd_pos( 0,2 );
out_Pist();
out_char(' ');
out_hhmmss(); // Zeigt ulSekunden an, als hh:mm:ss
tslcd_pos( 0,3 );
out_printf("%6kAh", Ladung_as/3600.0 ); // Zeigt Ladung seit Reset an, in uAh, mAh, Ah kAh
out_temperatur();
#endif
// 0 1 2 3 4
lcd_menu( iMenu, PSTR("US" "IS" "KA" "RS" "GR") ) ; // Die Menuekuerzel anzeigen
tslcd_refresh();
dynamik();
if ( Tasteraktion )
{
Tasteraktion = 0 ;
switch( iMenu )
{
case 0: iMenu = 0x10 ; // Jetzt US Spannungssollwert einstellen
break ;
case 0x10: iMenu = 0 ;
break ;
case 1: iMenu = 0x11 ; // Jetzt IS Stromsollwert einstellen
break ;
case 0x11: iMenu = 1 ;
break ;
case 2: // KA - Kalibriermenu
lcd_kalib();
tslcd_cls();
break ;
case 3: // RS - Rueckstellen der Zaehler
Ladung_as = 0 ;
ulSekunden = 0 ;
break ;
case 4: // GR - Grenzwerte einstellen
lcd_max();
tslcd_cls();
break ;
}
// DigPotWert_alt = DigPotWertA ;
continue ; // Tasteraktion verhindert Dreherkennung
} // Ende if ( Tasteraktion )
DBprintf(( "fun_LCD() teste diff != 0 " ));
if ( diff != 0 )
{
#if OPT_TEST_DIGIPOTI
if ( diff != 0 ) vtprintf("fun_LCD(): digiPoti: %9.3fs DigPotWertA=%d diff=%d iMenu=%d\n", ticktosec(startpotitick), DigPotWertA, diff, iMenu );
#endif
switch( iMenu )
{
case 0x10: // USoll einstellen
vkanal[XUS].w += ddiff*POTIFAKTOR ;
Ausgabe_w_Usoll( vkanal[XUS].w ); // Und Darstellung berechnen und Ausgeben auf dac(0
break ;
case 0x11 : // ISoll einstellen
vkanal[XIS].w += ddiff*POTIFAKTOR ;
Ausgabe_w_Isoll( vkanal[XIS].w ); // Und Darstellung berechnen und Ausgeben auf dac(1
break ;
default: // Anderen Menuepunkt vorwaehlen
MENU( iMenu, diff) ;
if ( iMenu > 128 ) iMenu = 0 ;
if ( iMenu >= 5 ) iMenu = 4 ;
break ;
} // Ende switch( iMenu )
} // Ende if ( diff != 0 ) // Wurde das Poti verdreht ?
tsnext("lcdd");
}
}
FUNCT void fun_mess( void )
{
#ifndef ADCFILTER_DELAY_FEST
adcfilter_delay = sectotick(0.01);
#endif
for(;;)
{
adcfilter();
// iKanal 2
vkanal[XUM].w = adcf[0]; // MCP3208 ch0 = UM,
vkanal[XUM].dar = p_zu_d( XUM, vkanal[XUM].w );
tsnext("fun_mess");
// iKanal 3
vkanal[XIM].w = adcf[1];
vkanal[XIM].dar = p_zu_d( XIM, vkanal[XIM].w );
tsnext("fun_mess");
// iKanal 4
vkanal[XTM].w = adcf[6]; // MCP3208 ch6 = KTY81, Temperatur
vkanal[XTM].dar = p_zu_d( XTM, vkanal[XTM].w );
tsnext("fun_mess");
// iKanal 5
vkanal[XUKE].w = adcf[7]; // MCP3208 ch7 = UKE
vkanal[XUKE].dar = p_zu_d( XUKE, vkanal[XUKE].w );
tsnext("fun_mess");
}
}
#if OPT_SER_DIAL
#if NPRGPATH < 4
#error NPRGPATH ist nicht 4
#endif
#else
#if NPRGPATH < 3
#error NPRGPATH ist nicht 3
#endif
#endif
#ifdef MODBUSADDRS
/*
PC=0x158c *2=0x2b18:92CF :_Z13setzeRegistertt push r12
PC=0x158d *2=0x2b1a:92DF : push r13
PC=0x158e *2=0x2b1c:92EF : push r14
PC=0x158f *2=0x2b1e:92FF : push r15
PC=0x1590 *2=0x2b20:01FC : movw r30,r24 WAdr
PC=0x1591 *2=0x2b22:0FEE : add r30,r30
PC=0x1592 *2=0x2b24:1FFF : adc r31,r31
PC=0x1593 *2=0x2b26:58EB : subi r30,-117 # =0x8b ~ addi 117 75H
PC=0x1594 *2=0x2b28:4FFD : sbci r31,-3 # =0xfd s32: =-629 =0x-275 ~ adci 2 02H
PC=0x1595 *2=0x2b2a:8371 : std Z+1,r23
PC=0x1596 *2=0x2b2c:8360 : std Z+0,r22 ModbusRegister[WAdr] = Value
PC=0x1597 *2=0x2b2e:308F : cpi r24,15 # =0xf
PC=0x1598 *2=0x2b30:0591 : cpc r25,r1
PC=0x1599 *2=0x2b32:F471 : brne 0x15a8 # =5544 =0x15a8
PC=0x159a *2=0x2b34:9160 : lds r22,0291 ;
PC=0x159c *2=0x2b38:9170 : lds r23,0292 ;
PC=0x159e *2=0x2b3c:9180 : lds r24,0293 ;
PC=0x15a0 *2=0x2b40:9190 : lds r25,0294 ;
PC=0x15a2 *2=0x2b44:90FF : pop r15
PC=0x15a3 *2=0x2b46:90EF : pop r14
PC=0x15a4 *2=0x2b48:90DF : pop r13
PC=0x15a5 *2=0x2b4a:90CF : pop r12
Hier ist 0x-275 = FD8B = ModbusRegister
OK; weil in sfmodbus-cpp, SFmodbus_Receiving(void) ist ModbusRegister = 275
*/
FUNCT void setzeRegister(u16 WAdr,u16 Value)
{
float fw ;
s16 x ;
ModbusRegister[WAdr] = Value ; // Speichert 16 Bits ab
if ( WAdr-1 < wortnr(sfm_USfak) )
{
if ( WAdr-1 == wortnr(sfm_Usoll) )
{ // Schreibt das 2. Wort des Sollwertes USoll
rechne_dac_Usoll( pmb->sfm_Usoll );
return ;
}
if ( WAdr-1 == wortnr(sfm_Isoll) )
{ // Schreibt das 2. Wort des Sollwertes ISoll
rechne_dac_Isoll( pmb->sfm_Isoll );
return ;
}
if ( WAdr-1 == wortnr(sfm_Usollgrenz) )
{ // Schreibt das 2. Wort des Sollwertes USollmax
fw = (pmb->sfm_Usollgrenz *16384.0F / asp.vk[XUS].fkkal) - asp.vk[XUS].null;
if ( fw > (float)USOLLMAX )
{
x = USOLLMAX ;
}
else if ( fw < USOLLMIN )
{
x = USOLLMIN ;
}
else
{
x = (s16)fw; // Fuer Ausgabe
}
asp.vk[XUS].grenz = x ; // Neuer Grenzwert Usoll
if ( vkanal[XUS].w > x )
{
vkanal[XUS].w = x ;
}
Ausgabe_w_Usoll( vkanal[XUS].w ); // Und Darstellung berechnen und Ausgeben auf dac(0
return ;
}
if ( WAdr-1 == wortnr(sfm_Isollgrenz) )
{
fw = (pmb->sfm_Isollgrenz *16384.0F / asp.vk[XIS].fkkal) - asp.vk[XIS].null;
if ( fw > (float)ISOLLMAX )
{
x = ISOLLMAX ;
}
else if ( fw < ISOLLMIN )
{
x = ISOLLMIN ;
}
else
{
x = (s16)fw; // Fuer Ausgabe
}
asp.vk[XIS].grenz = x ; // Neuer Grenzwert Isoll
if ( vkanal[XIS].w > x )
{
vkanal[XIS].w = x ;
}
Ausgabe_w_Isoll( vkanal[XIS].w ); // Und Darstellung berechnen und Ausgeben auf dac(1
return ;
}
if ( WAdr-1 == wortnr(sfm_USfak) )
{
asp.vk[XUS].fkkal = pmb->sfm_USfak ;
return ;
}
if ( WAdr-1 == wortnr(sfm_ISfak) )
{
asp.vk[XIS].fkkal = pmb->sfm_ISfak ;
return ;
}
if ( WAdr == wortnr(sfm_USnull) )
{
asp.vk[XUS].null = pmb->sfm_USnull ;
return ;
}
if ( WAdr == wortnr(sfm_ISnull) )
{
asp.vk[XIS].null = pmb->sfm_ISnull ;
return ;
}
}
else // nicht if ( WAdr-1 < wortnr(sfm_USfak)
{
if ( WAdr == wortnr(sfm_UInull) )
{
asp.vk[XUM].null = pmb->sfm_UInull ;
return ;
}
if ( WAdr == wortnr(sfm_IInull) )
{
asp.vk[XIM].null = pmb->sfm_IInull ;
return ;
}
if ( WAdr-1 == wortnr(sfm_UIfak) )
{
asp.vk[XUM].fkkal = pmb->sfm_UIfak ;
return ;
}
if ( WAdr-1 == wortnr(sfm_IIfak) )
{
asp.vk[XIM].fkkal = pmb->sfm_IIfak ;
return ;
}
if ( WAdr == wortnr(sfm_UKEnull) )
{
asp.vk[XUKE].null = pmb->sfm_UKEnull ;
return ;
}
if ( WAdr == wortnr(sfm_Tnull) )
{
asp.vk[XTM].null = pmb->sfm_Tnull ;
return ;
}
if ( WAdr-1 == wortnr(sfm_UKEfak) )
{
asp.vk[XUKE].fkkal = pmb->sfm_UKEfak ;
return ;
}
if ( WAdr-1 == wortnr(sfm_Tfak) )
{
asp.vk[XTM].fkkal = pmb->sfm_Tfak ;
return ;
}
}
return ;
}
#endif // Ende #ifdef MODBUSADDRS
FUNCT bool return_false( void )
{
return false ;
}
// ----------------------------------------------------------------------
// --------------------------------------- Das Hauptprogramm ------------
// ----------------------------------------------------------------------
FUNCT int main( int argc, const char **argv )
{
// bool bl ;
u32 sectick; // static: ROM+62 RAM+4
u16 WAdr ;
u16 ri ;
u16 WAnz ;
u16 Value ;
u8 rv ;
u8 i ;
u16 nBytes ;
#ifdef AVR
SREG = 0x80 ; // SREG &= 0x80 ; Enable Interrupt
// Alle unbenutzten Ports Pull-Up einschalten , aber nicht Ausgang schalten
PORTA |= 0xFF ;
PORTB |= 0xFF ;
PORTC |= 0xFF ;
PORTD |= 0xFF ;
// Treiber, die das anders brauchen sollen sich das schalten wie sie wollen
#endif
tickopen(1000000);
tsopen(); // Aktiviert die Hauptschnittstelle
tsseropen(); // AVR: Das macht den Interrupt aus in m644B 0x00C1, UCSR0B
// PC: Das oeffnet die Standard-Konsole
spimas_open();
adcopen();
dacopen();
#ifndef AVR
DBmain("mispav");
logoutopen("mispav");
#endif
// Initialisierung der Ausgabe: Sollwerte 0 ausgeben, damit nichts kaputtgeht.
vkanal[XUS].w = 0;
vkanal[XIS].w = 0;
dac(0,vkanal[XUS].w);
dac(1,vkanal[XIS].w);
#if !defined(AVR)
vtopen();
tseeprom_open( "mispav", sizeof(asp) ); // EEPROM Datei fuer den Arbeitsspeicher
#endif
// starttick = gettick(); // Timer 32 Bit holen
#if OPT_SIMULATOR == 0
#if OPT_LCD
ts_stack_fill(stkLCD, NSTKLCD );
#endif
#if OPT_SER_DIAL
ts_stack_fill(stkDIA, NSTKDIA );
#endif
#if OPT_MES
ts_stack_fill(stkMES, NSTKMES );
#endif
#endif
#ifndef AVR
{ // Nur im PC
void (*ofkt)( const u8 o );
ofkt = vout_char[0];
vout_char[0] = logoutocha;
// In die LOG-Datei die MODBUS-Registernummern fuer das JAVA-Programm
// D:\h\home\netbeans\OpenJDK\util\ModbusMaster\src\modbusmaster\spvers.java
out_putsv( "public static final int sfm_Usoll = " ); out_uint32( wortnr(sfm_Usoll) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Isoll = " ); out_uint32( wortnr(sfm_Isoll) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Usollgrenz = " ); out_uint32( wortnr(sfm_Usollgrenz) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Isollgrenz = " ); out_uint32( wortnr(sfm_Isollgrenz) ); out_putsv(";\n");
out_putsv( "public static final int sfm_USnull = " ); out_uint32( wortnr(sfm_USnull) ); out_putsv(";\n");
out_putsv( "public static final int sfm_ISnull = " ); out_uint32( wortnr(sfm_ISnull) ); out_putsv(";\n");
out_putsv( "public static final int sfm_USfak = " ); out_uint32( wortnr(sfm_USfak) ); out_putsv(";\n");
out_putsv( "public static final int sfm_ISfak = " ); out_uint32( wortnr(sfm_ISfak) ); out_putsv(";\n");
out_putsv( "public static final int sfm_UInull = " ); out_uint32( wortnr(sfm_UInull) ); out_putsv(";\n");
out_putsv( "public static final int sfm_IInull = " ); out_uint32( wortnr(sfm_IInull) ); out_putsv(";\n");
out_putsv( "public static final int sfm_UIfak = " ); out_uint32( wortnr(sfm_UIfak) ); out_putsv(";\n");
out_putsv( "public static final int sfm_IIfak = " ); out_uint32( wortnr(sfm_IIfak) ); out_putsv(";\n");
out_putsv( "public static final int sfm_UKEnull = " ); out_uint32( wortnr(sfm_UKEnull) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Tnull = " ); out_uint32( wortnr(sfm_Tnull) ); out_putsv(";\n");
out_putsv( "public static final int sfm_UKEfak = " ); out_uint32( wortnr(sfm_UKEfak) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Tfak = " ); out_uint32( wortnr(sfm_Tfak) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Umess = " ); out_uint32( wortnr(sfm_Umess) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Imess = " ); out_uint32( wortnr(sfm_Imess) ); out_putsv(";\n");
out_putsv( "public static final int sfm_Takt = " ); out_uint32( wortnr(sfm_Takt) ); out_putsv(";\n");
out_putsv( "public static final int sfm_UKE = " ); out_uint32( wortnr(sfm_UKE) ); out_putsv(";\n");
out_putsv( "public static final int sfm_adc = " ); out_uint32( wortnr(sfm_adc[0]) ); out_putsv(";\n");
logoutflush();
out_putsv( " ADC_KANALSTART_LPRBS = " ); out_uint32( ADC_KANALSTART_LPRBS );
out_putsv( " ADC_KANAELE_LPRBS = " ); out_uint32( ADC_KANAELE_LPRBS );
out_putsv( " ADC_KANALENDE_LPRBS = " ); out_uint32( ADC_KANALENDE_LPRBS );
out_putsv(";\n");
out_putsv( " ADC_KANALSTART_DIALOG = " ); out_uint32( ADC_KANALSTART_DIALOG );
out_putsv( " ADC_KANAELE_DIALOG = " ); out_uint32( ADC_KANAELE_DIALOG );
out_putsv( " ADC_KANALENDE_DIALOG = " ); out_uint32( ADC_KANALENDE_DIALOG );
out_putsv(";\n");
out_putsv( " ADC_KANALSTART_SIN = " ); out_uint32( ADC_KANALSTART_SIN );
out_putsv( " ADC_KANAELE_SIN = " ); out_uint32( ADC_KANAELE_SIN );
out_putsv( " ADC_KANALENDE_SIN = " ); out_uint32( ADC_KANALENDE_SIN );
out_putsv(";\n");
out_putsv( " ADC_KANALSTART_SIN = " ); out_uint32( ADC_KANALSTART_SIN );
out_putsv( " ADC_KANAELE_SIN = " ); out_uint32( ADC_KANAELE_SIN );
out_putsv( " N_ADC_CHAN = " ); out_uint32( N_ADC_CHAN );
out_putsv(";\n");
logoutflush();
vout_char[0] = ofkt;
}
#endif
// Vom EEPROM in den Arbeitsspeicher holen.
// tseeprom_read( EEPROM_offset, (u8 *)&asp, sizeof(asp) );
#if OPT_SIMULATOR
rv = 21 ; // Initwerte nicht aus EEPROM
#else // #if OPT_SIMULATOR
rv = tseeprom_read_crc( EEPROM_offset, (u8 *)&asp, sizeof(asp) );
#endif // #if OPT_SIMULATOR
if ( rv != 0 || asp.kennung != KENNUNG )
{
// Konnte das EEPROM nicht lesen,
eepromfehler = true ;
asp.kennung = KENNUNG ;
#ifdef AVR
#if MISPAV_SN == 3 // sn:3 2023-11-13
// Siehe hierzu sn3/kal.xls
// Shunt 0.1 Ohm Parallel 0.1 Ohm also 0.05 Ohm
asp.vk[XUS].null = -4 ; // Offset fuer U Sollwert
asp.vk[XUS].fkkal = 40.822 ; // Faktor fuer U Sollwert 100% DAC
asp.vk[XUS].grenz= 12.5/40.822*16384.0+4 ;
asp.vk[XIS].null = -59 ; // Offset fuer I Sollwert
asp.vk[XIS].fkkal = 1.7034 ; // Faktor fuer I Sollwert 100% DAC
asp.vk[XIS].grenz= 0.6/1.7034*16384.0+59 ;
asp.vk[XUM].null = 11 ; // Offset fuer U Istwert
asp.vk[XUM].fkkal = 40.864 ; // Faktor fuer U Istwert 100% ADC
asp.vk[XIM].null = -43 ; // Offset fuer I Istwert
asp.vk[XIM].fkkal = 1.6924 ; // Faktor fuer I Istwert 100% ADC
asp.vk[XTM].null = -3736 ; // Normierung fuer Messwert Temperatur 2021-04-07 kty81
asp.vk[XTM].fkkal = 16384.0*0.042118318297 ; // Siehe Arbeitsblatt KTY81 in ../sn3/kal.xls
asp.vk[XUKE].null = 0 ; // Normierung fuer Messwert UKE
asp.vk[XUKE].fkkal = 412.0 ; // Nach Spannungsteiler.
// file:///D:/HJH_TZ/Projekte/Mispav/sn3.htm#2023-11-13k
#elif MISPAV_SN == 2
// Siehe d:\HJH_TZ\Projekte\Mispav\sn2\kal_sn2.xls
asp.vk[XUS].null = -4 ; // Offset fuer U Sollwert
asp.vk[XUS].fkkal = 40.39 ; // Faktor fuer U Sollwert 100% DAC
asp.vk[XUS].grenz= 12.5/40.39*16384.0+4 ;
asp.vk[XIS].null = -59 ; // Offset fuer I Sollwert
asp.vk[XIS].fkkal = 4.07 ; // Faktor fuer I Sollwert 100% DAC
asp.vk[XIS].grenz= 4.00/4.07*16384.0+59 ;
// 2024-04-26 20:00:00
// asp.vk[XUM].null = 12 ; // Offset fuer U Istwert
// asp.vk[XUM].fkkal = 24.859 ; // Faktor fuer U Istwert 100% ADC
// 2024-04-28 19:51
asp.vk[XUM].null = -8 ; // Offset fuer U Istwert
asp.vk[XUM].fkkal = 40.39 ; // Faktor fuer U Istwert 100% ADC
// 2024-04-27 09:00:00
// asp.vk[XIM].null = -18 ; // Offset fuer I Istwert
// asp.vk[XIM].fkkal = 6.250 ; // Faktor fuer I Istwert 100% ADC
// 2024-04-28 18:41:00
asp.vk[XIM].null = -28 ; // Offset fuer I Istwert
asp.vk[XIM].fkkal = 4.07 ; // Faktor fuer I Istwert 100% ADC
// Normierung fuer Messwert Temperatur 2021-04-07 kty81
// Siehe Arbeitsblatt KTY81 in ../sn3/kal.xls
// asp.vk[XTM].null = -3736 ;
// asp.vk[XTM].fkkal = 16384.0*0.042118318297 ;
// 2024-04-28 19:05:00
asp.vk[XTM].null = -3711 ;
asp.vk[XTM].fkkal = 714.8 ;
// asp.vk[XUKE].null = 0 ; // Normierung fuer Messwert UKE
// asp.vk[XUKE].fkkal = 412.0 ; // Nach Spannungsteiler.
// 2024-04-28 19:12
asp.vk[XUKE].null = -216 ; // Normierung fuer Messwert UKE
asp.vk[XUKE].fkkal = 42.890 ; // Nach Spannungsteiler.
#else // #if MISPAV_SN ==
asp.vk[XUS].null = 0 ; // Normierung fuer Sollwert U
asp.vk[XUS].fkkal = 30.0 ; // 100% DAC / ADC = 30 V
asp.vk[XUS].grenz= (s16)(12.5/30*16384.0+0) ;
asp.vk[XIS].null = 0 ; // Normierung fuer Sollwert I
asp.vk[XIS].fkkal = 2.0 ; // 100% DAC / ADC = 1 A
asp.vk[XIS].grenz= (s16)(0.6/2.0*16384.0+0) ;
asp.vk[XUM].null = 0 ; // Normierung fuer Messwert U
asp.vk[XUM].fkkal = 30.0 ; // 100% DAC / ADC = 30 V
asp.vk[XIM].null = 0 ; // Normierung fuer Messwert I
asp.vk[XIM].fkkal = 2.0 ; // 100% DAC / ADC = 1 A
asp.vk[XTM].null = 0 ; // Normierung fuer Messwert Temperatur
asp.vk[XTM].fkkal = 150.0 ; // 0% DAC / ADC = 150 grd C
asp.vk[XUKE].null = 0 ; // Normierung fuer Messwert UKE
asp.vk[XUKE].fkkal = 30.0 ; // 100% DAC / ADC = 30 V
#endif
#else // nicht AVR
asp.vk[XUS].null = 0 ; // Normierung fuer Sollwert U
asp.vk[XUS].fkkal = 30.0 ; // 100% DAC / ADC = 30 V
asp.vk[XUS].grenz= (s16)(12.5/30*16384.0+0) ;
asp.vk[XIS].null = 0 ; // Normierung fuer Sollwert I
asp.vk[XIS].fkkal = 2.0 ; // 100% DAC / ADC = 1 A
asp.vk[XIS].grenz= (s16)(0.6/2.0*16384.0+0) ;
asp.vk[XUM].null = 0 ; // Normierung fuer Messwert U
asp.vk[XUM].fkkal = 30.0 ; // 100% DAC / ADC = 30 V
asp.vk[XIM].null = 0 ; // Normierung fuer Messwert I
asp.vk[XIM].fkkal = 2.0 ; // 100% DAC / ADC = 1 A
asp.vk[XTM].null = 0 ; // Normierung fuer Messwert Temperatur
asp.vk[XTM].fkkal = 150.0 ; // 0% DAC / ADC = 150 grd C
asp.vk[XUKE].null = 0 ; // Normierung fuer Messwert UKE
asp.vk[XUKE].fkkal = 30.0 ; // 100% DAC / ADC = 30 V
#endif // #ifdef AVR
}
// Die 4 Hauptprogramme sind:
// 0 - Dieses, main()
#if OPT_LCD
tseinri( 1, fun_LCD, "LCD", stkLCD, sizeof(stkLCD) );
vout_char[1] = tslcd_ocha ; // An das LCD anstatt and die serielle Ausgabe
vout_aktiv[1] = return_false ;
#endif
#if OPT_MES
tseinri( 2, fun_mess, "MES", stkMES, sizeof(stkMES) );
#endif
#if OPT_SER_DIAL
tseinri( 3 , fun_DIA, "DIA", stkDIA, sizeof(stkDIA) );
#if defined (AVR) && OPT_SER_DIAL == 2 // Verwende (im Atmega644P) die zweite serielle Schnittstelle.
ser1_open();
vout_char[3] = ser1_out_char ;
vout_aktiv[3] = ser1_out_aktiv ;
vin_char[3] = ser1_in_char ; // Seriell
vin_check[3] = ser1_in_check ;
#endif // Ende #if defined (AVR) && OPT_SER_DIAL == 2
#endif // Ende #if OPT_SER_DIAL
#if OPT_SER_DIAL != 1 && MODBUSADDRS != 0
SFMODBUS_version_jahr = VERSION_JAHR ;
SFMODBUS_version_mmtt = VERSION_MMTT ;
strcpy( (char *)&SFMODBUS_Namer, "mispav" ) ;
#endif
acttick = gettick(); // Timer 32 Bit holen
sectick = acttick ; // Sekundentakt fuer Berechnung der Ladung As
for(;;)
{
#if OPT_SER_DIAL != 1 && MODBUSADDRS != 0
(*sfmodbus)(); // Die Zustandsmaschine fuer MODBUS-Empfang am Laufen halten.
// Check if need to generate an answer
// Pruefen, ob eine Antwort erzeugt werden muss.
if ( sfmodbus == SFmodbus_WaitAnswer )
{ // Ein MODBUS-Master-Telegramm wurde empfangen.
#ifndef AVR
// logvtmsec(); TODO: Umstellen auf logout
// logvtprintf("Telegramm ist gekommen");
// logvt_modbus_master( sfmodbus_telegramm, sfmodbus_iidx, registeroption_uword );
#endif
if ( sfmodbus_telegramm[0] == MODBUSADDRS )
{ // ... Fuer dieses Geraet
// Antwort erzeugen
sfmodbus_sendreset(); // Starte ein Antworttelegramm
// Reset sfmodbus_otline[sfmodbus_outfill=0]
switch( sfmodbus_telegramm[1] ) // Function Code FC=
{
/*!
* case 3 : MODBUS-Register abfragen
*/
case 3 : // FC=03 Get Holding Registers
case 4 : // FC=04 Get Registers
WAdr = ( sfmodbus_telegramm[2] << 8 ) | sfmodbus_telegramm[3] ; // 10
if ( WAdr == 42 )
WAdr = WAdr ;
WAnz = ( sfmodbus_telegramm[4] << 8 ) | sfmodbus_telegramm[5] ; // 1
if ( WAdr < REGISTERCOUNT // < 11
&& WAnz <= REGISTERCOUNT
&& WAnz < NMODBUS_OUT-7
&& WAnz > 0
&& WAdr + WAnz <= REGISTERCOUNT
)
{ // add in sfmodbus_otline[sfmodbus_outfill++]
sfmodbus_sendchar( MODBUSADDRS ); // Slave Address
sfmodbus_sendchar( sfmodbus_telegramm[1] ); // FC=03 / FC=04 : Read Holding Register / Read Register
sfmodbus_sendchar( 2 * WAnz ); // = Byte count
#if DEBUG_MODBUS
if ( WAnz == 4 )
WAnz = WAnz ;
#endif
for ( i = 0 ; i < WAnz ; ++i )
{
ri = WAdr+i ; // Register-Index
if ( ri == wortnr(sfm_Usoll) )
{
pmb->sfm_Usoll = vkanal[XUS].dar ; // dWert(XUS); // (vkanal[XUS].w+asp.vk[XUS].null)*asp.vk[XUS].fkkal/16384.0F ;
}
if ( ri == wortnr(sfm_Isoll) )
{
pmb->sfm_Isoll = vkanal[XIS].dar ; // dWert(XIS); // (vkanal[XIS].w+asp.vk[XIS].null)*asp.vk[XIS].fkkal/16384.0F ;
}
if ( ri == wortnr(sfm_Usollgrenz) )
{
pmb->sfm_Usollgrenz = (asp.vk[XUS].grenz+asp.vk[XUS].null)*asp.vk[XUS].fkkal/16384.0F ;
}
if ( ri == wortnr(sfm_Isollgrenz) )
{
pmb->sfm_Isollgrenz = (asp.vk[XIS].grenz+asp.vk[XIS].null)*asp.vk[XIS].fkkal/16384.0F ;
}
if ( ri == wortnr(sfm_USnull) )
{
pmb->sfm_USnull = asp.vk[XUS].null; // s16
}
if ( ri == wortnr(sfm_ISnull) )
{
pmb->sfm_ISnull = asp.vk[XIS].null; // s16
}
if ( ri == wortnr(sfm_USfak) )
{
pmb->sfm_USfak = asp.vk[XUS].fkkal;
}
if ( ri == wortnr(sfm_ISfak) )
{
pmb->sfm_ISfak = asp.vk[XIS].fkkal;
}
if ( ri == wortnr(sfm_UInull) )
{
pmb->sfm_UInull = asp.vk[XUM].null;
}
if ( ri == wortnr(sfm_IInull) )
{
pmb->sfm_IInull = asp.vk[XIM].null;
}
if ( ri == wortnr(sfm_UIfak) )
{
pmb->sfm_UIfak = asp.vk[XUM].fkkal;
}
if ( ri == wortnr(sfm_IIfak) )
{
pmb->sfm_IIfak = asp.vk[XIM].fkkal;
}
if ( ri == wortnr(sfm_Umess) )
{
pmb->sfm_Umess = vkanal[XUM].dar ; // dWert(XUM) ;
}
if ( ri == wortnr(sfm_Imess) )
{
pmb->sfm_Imess = vkanal[XIM].dar ; // dWert(XIM) ;
}
// Temparatur umrechnen nach float
#if OPT_ONE_WIRE
oh ueberraschung das wollte ich nicht
if ( ri == wortnr(sfm_Takt) )
{
pmb->sfm_Takt = (float)vCTSOneWireMehr[0].Temperaturwert/100.0F ;
}
#else
if ( ri == wortnr(sfm_Takt) )
{
pmb->sfm_Takt = vkanal[XTM].dar ; // dWert(XTM) ;
}
#endif
if ( ri == wortnr(sfm_UKE) )
{
pmb->sfm_UKE = p_zu_d( XUKE, vkanal[XUKE].w );
}
if ( ri == wortnr(sfm_adc[0]) )
{
pmb->sfm_adc[0] = adcf[0] ;
}
if ( ri == wortnr(sfm_adc[1]) )
{
pmb->sfm_adc[1] = adcf[1] ;
}
if ( ri == wortnr(sfm_adc[2]) )
{
pmb->sfm_adc[2] = adcf[2] ;
}
if ( ri == wortnr(sfm_adc[3]) )
{
pmb->sfm_adc[3] = adcf[3] ;
}
if ( ri == wortnr(sfm_adc[4]) )
{
pmb->sfm_adc[4] = adcf[4] ;
}
if ( ri == wortnr(sfm_adc[5]) )
{
pmb->sfm_adc[5] = adcf[5] ;
}
if ( ri == wortnr(sfm_adc[6]) )
{
pmb->sfm_adc[6] = adcf[6] ;
}
if ( ri == wortnr(sfm_adc[7]) )
{
pmb->sfm_adc[7] = adcf[7] ;
}
sfmodbus_sendword( ModbusRegister[ri] ); // Ein Wort hinzufuegen
}
}
else
{
// Wenn ein Fehler erkannt ist, dann sende ein Fehlertelegramm
sfmodbus_sendchar( MODBUSADDRS );
sfmodbus_sendchar( sfmodbus_telegramm[1] + 128 ); // Read Holding Register / Read Register + Fehleranzeige
sfmodbus_sendchar( MODBUS_ERR_REGADDR ); // Exception Code
}
// Zu End switch( sfmodbus_telegramm[1] )
break ; // end case 3 : case 4:
case 6 : // FC=06 Set Single Holding Register
WAdr = ( sfmodbus_telegramm[2] << 8 ) | sfmodbus_telegramm[3] ;
Value = ( sfmodbus_telegramm[4] << 8 ) | sfmodbus_telegramm[5] ;
// Welch ein Register wurde empfangen ?
#if DEBUG_HatEmpfangen
if ( WAdr >= 0 && WAdr < REGISTERCOUNT )
{
if ( HatEmpfangen[WAdr] != 255 ) ++HatEmpfangen[WAdr];
}
#endif
if ( WAdr >= SFMODBUS_FIXED_REGISTERS && WAdr < REGN_WRITE_MAX )
{ // Dieses Register darf gesetzt werden
setzeRegister(WAdr,Value); // Speichere den 16-Bit-Wert ab
// und wenn eine 32-Bit Float vollstaendig wurde, dann gib auch aus.
// Die Antwort bilden, Das Slave-Telegramm mit FC=6
sfmodbus_sendchar( MODBUSADDRS ); // Slave Address
sfmodbus_sendchar( 6 ); // Set Single Holding Register
sfmodbus_sendword( WAdr ); // The Register Address
sfmodbus_sendword( Value ); // The Value which is set
break; // Zu End switch( sfmodbus_telegramm[1] )
}
// Andere Register koennen nicht veraendert werden !
sfmodbus_sendchar( MODBUSADDRS );
sfmodbus_sendchar( 6 | 128 ); // Set Single Holding Register + Error indicator
sfmodbus_sendchar( MODBUS_ERR_REGADDR ); // Fehler Code
break; // Zu End switch( sfmodbus_telegramm[1] )
case 16: // FC=16 Set Multiple Holding Register (Seit 2023-11-13)
WAdr = ( sfmodbus_telegramm[2] << 8 ) | sfmodbus_telegramm[3] ;
WAnz = ( sfmodbus_telegramm[4] << 8 ) | sfmodbus_telegramm[5] ; // =2
nBytes = sfmodbus_telegramm[6] ; // =4
#if OPT_LOG
logvtprintf("FC=%d Setze mehrere Register WAdr=%d, WAnz=%d, nBytes=%d ...",
sfmodbus_telegramm[1], WAdr, WAnz, nBytes );
#endif
if ( WAdr < SFMODBUS_FIXED_REGISTERS ) goto MErr16; // Korrektur 2023-12-27
if ( WAdr+WAnz > REGN_WRITE_MAX ) goto MErr16; // Hier war >=
if ( nBytes != WAnz*2 ) goto MErr16; // Hier war != WAnz/2
// nur diese 32-Bit-Registers if ( ! ( WAdr == 13 || WAdr == 15 )) goto MErr16;
// Which Value to set ?
for ( i = 0 ; i < WAnz ; ++i ) {
if ( WAdr+i >= SFMODBUS_FIXED_REGISTERS // 2023-12-27 Hier war REGN_WRITE_MAX
&& WAdr+i < REGN_WRITE_MAX )
{
Value = ( sfmodbus_telegramm[7+2*i] << 8 ) | sfmodbus_telegramm[8+2*i] ;
// ModbusRegister[WAdr+i] = Value ;
setzeRegister(WAdr+i,Value); // Speichere den 16-Bit-Wert ab
// und wenn eine 32-Bit Float vollstaendig wurde, dann gib auch aus.
}
}
// Bestaetigungstelegramm an Master zuruecksenden
sfmodbus_sendchar( MODBUSADDRS ); // Slave Address
sfmodbus_sendchar( 16 ); // Set Multiple Holding Register
sfmodbus_sendword( WAdr ); // The Register Address
sfmodbus_sendword( WAnz ); // The Register Count
sfmodbus_sendend();
// Behandlung speyieller Register
/*
if ( WAdr == SFMODBUS_RMES ) {
#if OPT_LOG
logvtprintf( "RMES=0x%08lX=%lu ", SFMODBUS_rmes, SFMODBUS_rmes );
#endif
AusgRMes(); // Code hinaus
}
if ( WAdr == SFMODBUS_RSIM ) {
#if OPT_LOG
logvtprintf( "RSIM=0x%08lX=%lu ", SFMODBUS_rsim, SFMODBUS_rsim );
#endif
AusgRSim(); // Code hinaus
}
*/
#if OPT_LOG
logvtprintf( "OK\n" );
#endif
break;
// Andere Register koennen nicht veraendert werden !
MErr16:
#if OPT_LOG
logvtprintf( "FEHLER\n" );
#endif
sfmodbus_sendchar( MODBUSADDRS );
sfmodbus_sendchar( 16 | 128 ); // Set Single Holding Register + Error indicator
sfmodbus_sendchar( MODBUS_ERR_REGADDR ); // Exception Code
sfmodbus_sendend();
break;
default: // Nicht 3, 4, 6
// Erzeuge eine Antwort mit Fehlerkennung
sfmodbus_sendchar( MODBUSADDRS );
sfmodbus_sendchar( sfmodbus_telegramm[1] + 128 ); // FC + Error indicator
sfmodbus_sendchar( MODBUS_ERR_FUNCODE ); // Exception Code
} // End switch( sfmodbus_telegramm[1] )
sfmodbus_sendend();
// Das Telegramm ist erzeugt und wird gesendet.
#ifndef AVR
// logvt_modbus_slave( sfmodbus_otline, sfmodbus_outfill, registeroption_uword );
#endif
// sfmodbus_telegramm[0] = 0 ;
} // Ende if ( sfmodbus_telegramm[0] == MODBUSADDRS )
else
{
// Ich erzeuge kein Telegramm, weil ich nicht angesprochen war
sfmodbus_sendeabbruch();
}
}
#endif // Ende: #if OPT_SER_DIAL != 1 && MODBUSADDRS != 0
// Die Zustandsautomaten
(*zfgeber0)(); // Drehgeber
(*zfta)(); // Taster
u32 atick = gettick(); // Timer 32 Bit holen
u32 dtick = difftick(acttick, atick);
if ( dtick > max_difftick ) max_difftick = dtick ;
acttick = atick;
dtick = difftick(sectick, atick);
if ( dtick > sectotick(1.0) )
{
// Und wieder ist eine Sekunde vergangen
if ( dtick > sectotick(3.0) )
{
sectick = atick ;
}
sectick += sectotick(1.0) ;
++ulSekunden ;
Ladung_as += vkanal[XIM].dar ; // dWert(XIM) ;
}
tsnext("Main"); // fun_LCD, fun_DIA,
#ifndef AVR
hesleep(5);
#endif
}
return 0 ; // return - Niemals !
}
//