I am using keil drivers to use CAN bus on an STM22F107. The method below is used to set CAN baud rate.
status = ptrCAN->SetBitrate (ARM_CAN_BITRATE_NOMINAL, // Set nominal bitrate
125000U, // Set bitrate to 125 kbit/s ||||| 150k in SW -> 50k in oscilloscope | 200k->1.25meg | 125k->71.4k;
ARM_CAN_BIT_PROP_SEG(5U) | // Set propagation segment to 5 time quanta
ARM_CAN_BIT_PHASE_SEG1(1U) | // Set phase segment 1 to 1 time quantum (sample point at 87.5% of bit time)
ARM_CAN_BIT_PHASE_SEG2(1U) | // Set phase segment 2 to 1 time quantum (total bit is 8 time quanta long)
ARM_CAN_BIT_SJW(1U)); // Resynchronization jump width is same as phase segment 2
The function definition is as below:
static int32_t CANx_SetBitrate (ARM_CAN_BITRATE_SELECT select, uint32_t bitrate, uint32_t bit_segments, uint8_t x) {
CAN_TypeDef *ptr_CAN;
uint32_t mcr, sjw, prop_seg, phase_seg1, phase_seg2, pclk, brp, tq_num;
if (x >= CAN_CTRL_NUM) { return ARM_DRIVER_ERROR; }
if (select != ARM_CAN_BITRATE_NOMINAL) { return ARM_CAN_INVALID_BITRATE_SELECT; }
if (can_driver_powered[x] == 0U) { return ARM_DRIVER_ERROR; }
prop_seg = (bit_segments & ARM_CAN_BIT_PROP_SEG_Msk ) >> ARM_CAN_BIT_PROP_SEG_Pos;
phase_seg1 = (bit_segments & ARM_CAN_BIT_PHASE_SEG1_Msk) >> ARM_CAN_BIT_PHASE_SEG1_Pos;
phase_seg2 = (bit_segments & ARM_CAN_BIT_PHASE_SEG2_Msk) >> ARM_CAN_BIT_PHASE_SEG2_Pos;
sjw = (bit_segments & ARM_CAN_BIT_SJW_Msk ) >> ARM_CAN_BIT_SJW_Pos;
if (((prop_seg + phase_seg1) < 1U) || ((prop_seg + phase_seg1) > 16U)) { return ARM_CAN_INVALID_BIT_PROP_SEG; }
if (( phase_seg2 < 1U) || ( phase_seg2 > 8U)) { return ARM_CAN_INVALID_BIT_PHASE_SEG2; }
if (( sjw < 1U) || ( sjw > 4U)) { return ARM_CAN_INVALID_BIT_SJW; }
ptr_CAN = ptr_CANx[x];
tq_num = 1U + prop_seg + phase_seg1 + phase_seg2;
pclk = CAN_GetClock (); if (pclk == 0U) { return
ARM_DRIVER_ERROR; }
brp = pclk / (tq_num * bitrate); if (brp > 1024U) { return ARM_CAN_INVALID_BITRATE; }
if (pclk > (brp * tq_num * bitrate)) {
if ((((pclk - (brp * tq_num * bitrate)) * 1024U) / pclk) > CAN_CLOCK_TOLERANCE) { return ARM_CAN_INVALID_BITRATE; }
} else if (pclk < (brp * tq_num * bitrate)) {
if (((((brp * tq_num * bitrate) - pclk) * 1024U) / pclk) > CAN_CLOCK_TOLERANCE) { return ARM_CAN_INVALID_BITRATE; }
}
mcr = ptr_CAN->MCR;
ptr_CAN->MCR = CAN_MCR_INRQ; // Activate initialization mode
while ((ptr_CAN->MSR&CAN_MSR_INAK) == 0U); // Wait to enter initialization mode
ptr_CAN->BTR = (ptr_CAN->BTR & (CAN_BTR_LBKM | CAN_BTR_SILM)) | ((brp - 1U) & CAN_BTR_BRP) | ((sjw - 1U) << 24) | ((phase_seg2 - 1U) << 20) | ((prop_seg + phase_seg1 - 1U) << 16);
ptr_CAN->MCR = mcr; // Return to previous mode
return ARM_DRIVER_OK;
Unfortunately, when I use this method, I get wrong baud rate in CAN (I checked with oscilloscope). For example, when I set baud rate as 125kb/s, the actual value on the pin is 71.4kb/s for example.
Could you please help me to find the problem. Many thanks.
1/(125*10^3)
= 8us bit length and1+(5+1)+(1+1)+(1+1)=11 tq
, you need an 8us / 11 = 1.375MHz clock. But in reality you'll have something like 2MHz or 4MHz probably? As previously pointed out in another question, I don't think your tq settings make sense to begin with. \$\endgroup\$