You’d do a functional driver port. The resulting port into the framework results in its ability to utilize it within projects under MPLAB and EasyC/WPILIB - one set of code from that point forward.
The new framework utilizes a run-time binding of interrupt to isr/data structures vs the compile time binding in Kevin’s code. This permits a library to be utilized. You can tweak the “standard” driver if you want or just call it and use it as is. The tweaking could get you all the way back to the point that the driver you are using for your robot is essentially equivalent to compile-time binding… or you could have/utilize or choose an available driver package that is more generic such as the example to follow.
For example. The quad encoder is bound to the interrupt (phase_a) and phase_b pins via the driver writer supplied “package” routine. The user calls this “package” to declare/setup the system. Typically this is done only once so the Bind/unBind will reflash program memory jump tables for the appropriate physical and logical service routines. Typically only Binds will be done to hook or register the driver we want invoked by the respective interrupt as this physical configuration usually doesn’t change while the robot is running. The user of the driver doesn’t need to know how it works, just that pin ‘X’ is the phase a encoder pin and pin ‘Y’ is the B-phase encoder pin. The driver supplier in this case only supports 2 channels, but the driver programmer could easily expand to include 6 channel support.
The user of the driver then calls start/read/set/stop routines. Start enables the interrupt and stop disables the interrupt to control loading on the processor. As part of start, it specifies whether to invert the counting or not. Read returns the current count value for the channel and set changes the value to the number provided. These routines are provided by the driver writer. The driver writer could have decided to only invert the data at Read time, but in this case does it within the logical ISR handler.
The two handlers provided include one that is run at interrupt level (physical driver), while interrupts are disabled and a 2nd half of the driver (logical driver) to be run while interrupts are enabled. The user could have bound a third user driver to the same interrupt to do other processing (like changing the motor speed to accelerate or stop the robot after so many counts).
Anyway, the physical driver grabs the phase ‘a’ and phase ‘b’ values from the pins and saves them away. You wouldn’t need phase ‘a’ if this was a programmable edge interrupt but we don’t know which interrupt line the user will bind the driver to so its generic.
The logical driver then processes this information while interrupts are enabled. This logical driver inverts the count here, but that code could easily be moved to the Read user API routine to reduce the logical driver cpu run-time footprint.
/*
* User provided routines... bind a user driver to encoder
*
void QuadEncoder0_MyCode( void )
{
// Tie my function to encoder0 which is my lft wheel
UsrISR_Bind( &MyLeftWheel_LogISR, XYZ_interrupt );
}
/*
* Driver writer supplied routines...
* bind or configure driver to desired robot-specific interrupts/pins...
*/
void QuadEncoder_Package( channel, interrupt, phase_b_pin )
{
if (channel == 0) {
PhyISR_Bind( &QuadEncoder0_PhyISR, interrupt, RISING_EDGE );
LogISR_Bind( &QuadEncoder0_LogISR, interrupt );
QuadEncoder_Initialize( channel, interrupt, phase_b_pin );
return;
}
if (channel == 1) {
PhyISR_Bind( &QuadEncoder1_PhyISR, interrupt, RISING_EDGE );
LogISR_Bind( &QuadEncoder1_LogISR, interrupt );
QuadEncoder_Initialize( channel, interrupt, phase_b_pin );
return;
}
}
void QuadEncoder_unPackage( channel, interrupt, phase_b_pin )
{
if (channel == 0) {
PhyISR_unBind( &QuadEncoder0_PhyISR, interrupt );
LogISR_unBind( &QuadEncoder0_LogISR, interrupt );
QuadEncoder_UnInitialize( channel, interrupt, phase_b_pin );
return;
}
if (channel == 1)
{
PhyISR_unBind( &QuadEncoder1_PhyISR, interrupt );
LogISR_unBind( &QuadEncoder1_LogISR, interrupt );
QuadEncoder_UnInitialize( channel, interrupt, phase_b_pin );
return;
}
}
/*
* Driver writer supplied API (user callable) routines...
*/
unsigned char QuadEncoder_Start( channel, invert )
{
if (channel > QUADENCODER_CHANNEL_MAX) return(1);
switch(channel)
{
case 0: quadencoder0.invert = invert;
InterruptEnable( quadencoder0.interrupt );
return(0);
case 1: quadencoder1.invert = invert;
InterruptEnable( quadencoder1.interrupt );
return(0);
default: return(1);
}
return(0);
}
unsigned char QuadEncoder_Stop( channel )
{
if (channel > QUADENCODER_CHANNEL_MAX) return(1);
switch(channel)
{
case 0: DisableInterrupt( quadencoder0.interrupt ); return(0);
case 1: DisableInterrupt( quadencoder1.interrupt ); return(0);
default: return(1);
}
return(0);
}
long QuadEncoder_Read( channel )
{
long tmp;
if (channel > QUADENCODER_CHANNEL_MAX) return(0);
switch(channel)
{
case 0: CRITICAL_REGION_QUADENCODER0_BGN;
tmp = quadencoder0.count;
CRITICAL_REGION_QUADENCODER0_END;
break;
case 1: CRITICAL_REGION_QUADENCODER1_BGN;
tmp = quadencoder1.count;
CRITICAL_REGION_QUADENCODER1_END;
break;
default: tmp = 0;
}
return(tmp);
}
unsigned char QuadEncoder_Set( channel, value )
{
if (channel > QUADENCODER_CHANNEL_MAX) return(0);
switch(channel)
{
case 0: CRITICAL_REGION_QUADENCODER0_BGN;
quadencoder0.count = value;
CRITICAL_REGION_QUADENCODER0_END;
break;
case 1: CRITICAL_REGION_QUADENCODER1_BGN;
quadencoder1.count = value;
CRITICAL_REGION_QUADENCODER1_END;
break;
default: return(1);
}
return(0);
}
/*
* Driver write supplied handler routines...
*/
void QuadEncoder0_PhyISR( void )
{
quadencoder0.a_phase = ReadInterruptState( quadencoder0.interrupt );
quadencoder0.b_phase = ReadPin ( quadencoder0.phase_b_pin );
return;
}
void QuadEncoder0_LogISR( void )
{
long delta;
if (quadencoder0.lastphase == quadencoder0.a_phase) return;
quadencoder0.lastphase = quadencoder0.a_phase;
if (quadencoder0.a_phase == 0) return; // 1->0 transition, don't care
delta = 1;
if (quadencoder0.invert == 0)
{
if (quadencoder0.b_phase == 0)
delta = -1;
}
else
{
if (quadencoder0.b_phase != 0)
delta = -1;
}
CRITICAL_REGION_QUADENCODER0_BGN;
quadencoder0.count += delta;
CRITICAL_REGION_QUADENCODER0_END;
return;
}
:
.
Just an example of what a driver writer could choose to do within the framework. They could also decide to make channel==phase a pin, i.e. channel 0 was always portb<2> or something similar.