2020-05-24 17:07:16 +02:00
# include "DCC.h"
# include "DCCWaveform.h"
# include "DIAG.h"
// This module is responsible for converting API calls into
// messages to be sent to the waveform generator.
// It has no visibility of the hardware, timers, interrupts
// nor of the waveform issues such as preambles, start bits checksums or cutouts.
//
// Nor should it have to deal with JMRI responsess other than the OK/FAIL
// or cv value returned. I will move that back to the JMRI interface later
//
// The interface to the waveform generator is narrowed down to merely:
// Scheduling a message on the prog or main track using a function
// Obtaining ACKs from the prog track using a function
// There are no volatiles here.
void DCC : : begin ( ) {
DCCWaveform : : begin ( ) ;
}
void DCC : : setThrottle ( uint16_t cab , uint8_t tSpeed , bool tDirection ) {
setThrottle2 ( cab , tSpeed , tDirection ) ;
// retain speed for loco reminders
updateLocoReminder ( cab , tSpeed , tDirection ) ;
}
void DCC : : setThrottle2 ( uint16_t cab , uint8_t tSpeed , bool tDirection ) {
uint8_t b [ 5 ] ;
uint8_t nB = 0 ;
if ( cab > 127 )
b [ nB + + ] = highByte ( cab ) | 0xC0 ; // convert train number into a two-byte address
b [ nB + + ] = lowByte ( cab ) ;
b [ nB + + ] = 0x3F ; // 128-step speed control byte
if ( tSpeed > 0 )
b [ nB + + ] = tSpeed + ( tSpeed > 0 ) + tDirection * 128 ; // max speed is 126, but speed codes range from 2-127 (0=stop, 1=emergency stop)
else {
b [ nB + + ] = 1 ;
tSpeed = 0 ;
}
DCCWaveform : : mainTrack . schedulePacket ( b , nB , 0 ) ;
}
void DCC : : setFunction ( int cab , byte byte1 ) {
uint8_t b [ 4 ] ;
uint8_t nB = 0 ;
if ( cab > 127 )
b [ nB + + ] = highByte ( cab ) | 0xC0 ; // convert train number into a two-byte address
b [ nB + + ] = lowByte ( cab ) ;
b [ nB + + ] = ( byte1 | 0x80 ) & 0xBF ;
DCCWaveform : : mainTrack . schedulePacket ( b , nB , 4 ) ; // Repeat the packet four times
}
void DCC : : setFunction ( int cab , byte byte1 , byte byte2 ) {
byte b [ 5 ] ;
byte nB = 0 ;
if ( cab > 127 )
b [ nB + + ] = highByte ( cab ) | 0xC0 ; // convert train number into a two-byte address
b [ nB + + ] = lowByte ( cab ) ;
b [ nB + + ] = ( byte1 | 0xDE ) & 0xDF ; // for safety this guarantees that first byte will either be 0xDE (for F13-F20) or 0xDF (for F21-F28)
b [ nB + + ] = byte2 ;
DCCWaveform : : mainTrack . schedulePacket ( b , nB , 4 ) ; // Repeat the packet four times
}
void DCC : : setAccessory ( int address , byte number , bool activate ) {
byte b [ 3 ] ; // save space for checksum byte
b [ 0 ] = address % 64 + 128 ; // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address
b [ 1 ] = ( ( ( ( address / 64 ) % 8 ) < < 4 ) + ( number % 4 < < 1 ) + activate % 2 ) ^ 0xF8 ; // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate
DCCWaveform : : mainTrack . schedulePacket ( b , 2 , 4 ) ; // Repeat the packet four times
}
void DCC : : writeCVByteMain ( int cab , int cv , byte bValue ) {
byte b [ 6 ] ; // save space for checksum byte
byte nB = 0 ;
if ( cab > 127 )
b [ nB + + ] = highByte ( cab ) | 0xC0 ; // convert train number into a two-byte address
b [ nB + + ] = lowByte ( cab ) ;
b [ nB + + ] = cv1 ( 0xEC , cv ) ; // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
b [ nB + + ] = cv2 ( cv ) ;
b [ nB + + ] = bValue ;
DCCWaveform : : mainTrack . schedulePacket ( b , nB , 4 ) ;
}
void DCC : : writeCVBitMain ( int cab , int cv , byte bNum , bool bValue ) {
byte b [ 6 ] ; // save space for checksum byte
byte nB = 0 ;
bValue = bValue % 2 ;
bNum = bNum % 8 ;
if ( cab > 127 )
b [ nB + + ] = highByte ( cab ) | 0xC0 ; // convert train number into a two-byte address
b [ nB + + ] = lowByte ( cab ) ;
b [ nB + + ] = cv1 ( 0xE8 , cv ) ; // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
b [ nB + + ] = cv2 ( cv ) ;
b [ nB + + ] = 0xF0 + bValue * 8 + bNum ;
DCCWaveform : : mainTrack . schedulePacket ( b , nB , 4 ) ;
}
bool DCC : : writeCVByte ( int cv , byte bValue ) {
uint8_t message [ ] = { cv1 ( 0x7C , cv ) , cv2 ( cv ) , bValue } ;
DCCWaveform : : progTrack . schedulePacket ( message , sizeof ( message ) , 6 ) ; // NMRA recommends 6 write or reset packets for decoder recovery time
return verifyCV ( cv , bValue ) ;
}
bool DCC : : writeCVBit ( int cv , byte bNum , bool bValue ) {
bValue = bValue % 2 ;
bNum = bNum % 8 ;
uint8_t message [ ] = { cv1 ( 0x78 , cv ) , cv2 ( cv ) , 0xF0 + bValue * 8 + bNum } ;
DCCWaveform : : progTrack . schedulePacket ( message , sizeof ( message ) , 6 ) ; // NMRA recommends 6 write or reset packets for decoder recovery time
/* TODO... what is the verify opcode here?
bitWrite ( message [ 2 ] , 4 , 1 ) ; // change instruction code from Write Bit to Verify Bit
DCCWaveform : : progTrack . schedulePacket ( message , sizeof ( message ) , 6 ) ; // NMRA recommends 6 write or reset packets for decoder recovery time
*/
return DCCWaveform : : progTrack . getAck ( ) ;
}
int DCC : : readCV ( int cv ) {
byte message [ ] = { cv1 ( 0x78 , cv ) , // any CV>1023 will become modulus(1024) due to bit-mask of 0x03
cv2 ( cv ) ,
0
} ; // trailing zero will be updated in loop below
byte value = 0 ;
// get each bit individually
for ( int i = 0 ; i < 8 ; i + + ) {
message [ 2 ] = 0xE8 + i ;
DCCWaveform : : progTrack . schedulePacket ( message , sizeof ( message ) , 4 ) ; // NMRA recommends 5 read packets
value + = ( DCCWaveform : : progTrack . getAck ( ) < < i ) ;
}
return verifyCV ( cv , value ) ? value : - 1 ;
}
void DCC : : loop ( ) {
DCCWaveform : : loop ( ) ; // powwer overload checks
// if the main track transmitter still has a pending packet, skip this loop.
if ( DCCWaveform : : mainTrack . packetPending ) return ;
// each time around the Arduino loop, we resend a loco speed packet reminder
for ( ; nextLoco < MAX_LOCOS ; nextLoco + + ) {
if ( speedTable [ nextLoco ] . loco > 0 ) {
setThrottle2 ( speedTable [ nextLoco ] . loco , speedTable [ nextLoco ] . speed , speedTable [ nextLoco ] . forward ) ;
nextLoco + + ;
return ;
}
}
for ( nextLoco = 0 ; nextLoco < MAX_LOCOS ; nextLoco + + ) {
if ( speedTable [ nextLoco ] . loco > 0 ) {
setThrottle2 ( speedTable [ nextLoco ] . loco , speedTable [ nextLoco ] . speed , speedTable [ nextLoco ] . forward ) ;
nextLoco + + ;
return ;
}
}
}
///// Private helper functions below here /////////////////////
byte DCC : : cv1 ( byte opcode , int cv ) {
cv - - ;
return ( highByte ( cv ) & ( byte ) 0x03 ) | opcode ;
}
byte DCC : : cv2 ( int cv ) {
cv - - ;
return lowByte ( cv ) ;
}
bool DCC : : verifyCV ( int cv , byte value ) {
byte message [ ] = { cv1 ( 0x74 , cv ) , cv2 ( cv ) , value } ;
2020-05-26 10:44:20 +02:00
DIAG ( F ( " \n \n Verifying cv %d = %d " ) , cv , value ) ;
2020-05-24 17:07:16 +02:00
DCCWaveform : : progTrack . schedulePacket ( message , sizeof ( message ) , 5 ) ;
return DCCWaveform : : progTrack . getAck ( ) ;
}
void DCC : : updateLocoReminder ( int loco , byte tSpeed , bool forward ) {
// determine speed reg for this loco
int reg ;
int firstEmpty = MAX_LOCOS ;
for ( reg = 0 ; reg < MAX_LOCOS ; reg + + ) {
if ( speedTable [ reg ] . loco = = loco ) break ;
if ( speedTable [ reg ] . loco = = 0 & & firstEmpty = = MAX_LOCOS ) firstEmpty = reg ;
}
if ( reg = = MAX_LOCOS ) reg = firstEmpty ;
if ( reg > = MAX_LOCOS ) {
DIAG ( F ( " \n Too many locos \n " ) ) ;
return ;
}
speedTable [ reg ] . loco = loco ;
speedTable [ reg ] . speed = tSpeed ;
speedTable [ reg ] . forward = forward ;
}
DCC : : LOCO DCC : : speedTable [ MAX_LOCOS ] ;
int DCC : : nextLoco = 0 ;