typedef struct
{
//char name[ 16 ]; // Name of the CAN controller hardware
//uint32_t ref_clk; // CAN system clock frequency in Hz
//uint32_t sjw_max; // Synchronisation jump width
uint32_t brp_min; // Bit-rate prescaler
uint32_t brp_max;
uint32_t brp_inc;
uint32_t tseg1_min; // Time segement 1 = prop_seg + phase_seg1
uint32_t tseg1_max;
uint32_t tseg2_min; // Time segement 2 = phase_seg2
uint32_t tseg2_max;
} CAN_BitTimingConst_TypeDef;
typedef struct
{
uint32_t ref_clk; // CAN system clock frequency in Hz
uint32_t bitrate; // Bit-rate in bits/second
uint32_t sample_point; // Sample point in one-tenth of a percent
uint32_t brp; // Bit-rate prescaler
uint32_t tq; // Time quanta ( TQ ) in nanoseconds
uint32_t tseg1; // Time segement 1 = prop_seg + phase_seg1
uint32_t tseg2; // Time segement 2 = phase_seg2
uint32_t sjw; // Synchronisation jump width in TQs
//uint32_t prop_seg; // Propagation segment in TQs
//uint32_t phase_seg1; // Phase buffer segment 1 in TQs
//uint32_t phase_seg2; // Phase buffer segment 2 in TQs
} CAN_BitTiming_TypeDef;
int32_t CAN_CalcBitTiming( CAN_BitTimingConst_TypeDef *btc,
CAN_BitTiming_TypeDef *bt );
#define CAN_CALC_MAX_ERROR 50 // in one-tenth of a percent
int32_t CAN_UpdateSamplePoint( CAN_BitTimingConst_TypeDef *btc,
int32_t sampl_pt, int32_t tseg, int32_t *tseg1, int32_t *tseg2 )
{
*tseg2 = tseg + 1 - ( sampl_pt * ( tseg + 1 ) ) / 1000;
if ( *tseg2 < btc->tseg2_min )
*tseg2 = btc->tseg2_min;
if ( *tseg2 > btc->tseg2_max )
*tseg2 = btc->tseg2_max;
*tseg1 = tseg - *tseg2;
if ( *tseg1 > btc->tseg1_max )
{
*tseg1 = btc->tseg1_max;
*tseg2 = tseg - *tseg1;
}
return 1000 * ( tseg + 1 - *tseg2 ) / ( tseg + 1 );
}
// CIA Sample Point : 75.0% : Speed > 800000
// CIA Sample Point : 80.0% : Speed > 500000
// CIA Sample Point : 87.5% : Speed <= 500000
uint32_t CAN_CIA_SamplePoint( uint32_t bitrate )
{
uint32_t sampl_pt;
if ( bitrate > 800000 )
sampl_pt = 750;
else if ( bitrate > 500000 )
sampl_pt = 800;
else
sampl_pt = 875;
return sampl_pt;
}
int32_t CAN_CalcBitTiming( CAN_BitTimingConst_TypeDef *btc,
CAN_BitTiming_TypeDef *bt )
{
uint64_t v64;
int32_t rate = 0;
int32_t best_error = 1000000000, error = 0;
int32_t best_tseg = 0, best_brp = 0, brp = 0;
int32_t tsegall, tseg = 0, tseg1 = 0, tseg2 = 0;
int32_t spt_error = 1000, spt = 0, sampl_pt;
// Use gived sample points
if ( bt->sample_point )
sampl_pt = bt->sample_point;
else
// Use CIA recommended sample points
sampl_pt = CAN_CIA_SamplePoint( bt->bitrate );
// tseg even = round down, odd = round up
for ( tseg = ( btc->tseg1_max + btc->tseg2_max ) * 2 + 1;
tseg >= ( btc->tseg1_min + btc->tseg2_min ) * 2; tseg-- )
{
tsegall = 1 + tseg / 2;
// Compute all possible tseg choices (tseg=tseg1+tseg2)
brp = bt->ref_clk / ( tsegall * bt->bitrate ) + tseg % 2;
// chose brp step which is possible in system
brp = ( brp / btc->brp_inc ) * btc->brp_inc;
if ( ( brp < btc->brp_min ) || ( brp > btc->brp_max ) )
continue;
rate = bt->ref_clk / ( brp * tsegall );
error = bt->bitrate - rate;
// tseg brp biterror
if ( error < 0 )
error = -error;
if ( error > best_error )
continue;
best_error = error;
if ( error == 0 )
{
spt = CAN_UpdateSamplePoint( btc, sampl_pt, tseg / 2, &tseg1, &tseg2 );
error = sampl_pt - spt;
if ( error < 0 )
error = -error;
if ( error > spt_error )
continue;
spt_error = error;
}
best_tseg = tseg / 2;
best_brp = brp;
if ( error == 0 )
break;
}
if ( best_error )
{
/* Error in one-tenth of a percent */
error = ( best_error * 1000 ) / bt->bitrate;
if ( error > CAN_CALC_MAX_ERROR )
{
// error ( "bitrate error %ld.%ld%% too high\n", error / 10, error % 10 );
return DRIVER_ERROR_PARAMETER;
}
else
{
// warn( "bitrate error %ld.%ld%%\n", error / 10, error % 10 );
}
}
v64 = ( (uint64_t) best_brp * 1000000000UL ) / bt->ref_clk;
bt->tq = (uint32_t) v64;
bt->brp = best_brp;
bt->tseg2 = tseg2;
bt->tseg1 = tseg1;
bt->sjw = 1;
// bt->prop_seg = tseg1 / 2;
// bt->phase_seg1 = tseg1 - bt->prop_seg;
// bt->phase_seg2 = tseg2;
// real bit-rate
bt->bitrate = bt->ref_clk / ( bt->brp * ( tseg1 + tseg2 + 1 ) );
// real sample point
bt->sample_point = CAN_UpdateSamplePoint( btc, sampl_pt, best_tseg, &tseg1,
&tseg2 );
return DRIVER_OK;
}