Skip to content
This repository has been archived by the owner on Oct 2, 2023. It is now read-only.

Port Speed Controller Firmware to PlatformIO, Clean up, and Correct Driver Implementations #23

Draft
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

a2k-hanlon
Copy link

@a2k-hanlon a2k-hanlon commented Feb 25, 2022

Towards the effort to debug the faulty accelerator pedal behaviour, this PR introduces several improvements to the Speed Controller (SCN) firmware that runs on one of the STM32 Nucleo boards on Daybreak's dashboard.

The following improvements were made:

  • Transitioned the Speed Controller firmware from Kiel uVision to PlatformIO for ease of build and debug
  • Refactored of all of the hardware register accesses to use the register bit and bitfield definitions provided by the CMSIS device header for readability
  • Corrected several mistakes and oversights in the hardware drivers, including one to the encoder timer initialization (see line comments pointing out these corrections)
  • Added a baud rate parameter to the virtual COM driver (serial communication using a UART) with higher baud rates
  • Added missing include guards in some of the headers
  • Removed duplicate function documentation comments from the header files and refactored all these comments to a consistent doxygen format
  • Cleaned up some of the preprocessor flags
  • Changed all helper functions and global variables to static type
  • Added a CI build workflow for the Speed Controller Firmware

/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* Enable HSE in BYPASS mode (no crystal, oscillator bypassed by an external clock signal) */
/* On a Nucleo board, this external clock signal comes from the built-in ST Link */
RCC->CR |= (uint32_t)(RCC_CR_HSEBYP | RCC_CR_HSEON);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RCC_CR_HSEBYP bit was not being set in the old default clock configuration generated by Kiel

TIM2->CR1 &= ~TIM_CR1_CKD; // Set the timer to use default clock division
TIM2->CR1 |= TIM_CR1_OPM; // Set the timer to use one pulse mode

TIM2->ARR = 10 * period - 1;// Set autoreload value to set the period
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adjusted this to assume and use a reset value of 0 instead of 1 for the timer counter. The hardware always uses a 0 reset.


/** Interrupt handler for Timer 2.
static volatile int8_t timeoutFlag = FALSE;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be volatile since it is set from an interrupt service routine. It has also been made static, and a getter and setter pair have been added for this flag to be used by main.c.

Comment on lines +52 to +67
/**
* Get the timer timeout flag to see if the timer period has elapsed
*
* @returns The state of the timeout flag
*/
uint8_t GetTimeoutFlag(void)
{
return timeoutFlag;
}

/**
* Stops the counter on the timer
* Clear the timer timeout flag
*/
void StopTimer(void){
TIM2->CR1 &= ~(0x1UL); //disable TIM2

void ClearTimeoutFlag(void)
{
timeoutFlag = FALSE;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New getter and setter functions for timeoutFlag. Now it can only be read and cleared by external code. Only the TIM2 ISR sets the flag.


TIM2->ARR = 10*period; //set autoreload to reset every period

TIM2->CR1 &= (0x11UL << 8); //set the timer to use default clock division
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing ~ here, this has been corrected

SendInt(ADC_reading);
#endif /* ADC_DEBUG */

#if ADC_REVERSE_READING
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New preprocessor flags!

// Enable capture/compare channels 1 and 2, other channels disabled and all polarities default
TIM1->CCER = (TIM_CCER_CC2E | TIM_CCER_CC1E);

TIM1->SMCR |= 0x3 << TIM_SMCR_SMS_Pos; // Set timer to encoder mode 3 (clocked by both TI1FP1 and TI2FP2)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was an easy and rather significant mistake here - the STM32F1 reference manual specifies a value of '011' for the SMCR register SMS bitfield for encoder mode 3 (where edges of both encoder signals increment/decrement the timer counter). This is the mode we want. The '011' is binary (ie. = 0x3); however, it was entered in the code as hex 0x011 (= 0b0001_0001) instead.

Not only did this put the timer in encoder mode 1 instead of 3, it also changed the TS bitfield unintentionally. The effect of doing this for TIM1 is to enable the trigger output (TRGO signal) of TIM2 as a trigger source. By default it triggers a reset of the TIM1 counter (which tracks the encoder position). This is not good, because TIM2 is used to provide a countdown for the maximum time interval between speed control CAN messages. However, I think by default the TIM2 TRGO is configured to only assert upon software setting the UG bit in TIM2's register, which the software definitely isn't doing. Needs a bit more investigation.

GPIOC->CRL |= (0x4 << 24); //C6

//CAN receive setup
VirtualComInit(BAUD_115200);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New baud 115200 instead of 9600

Comment on lines +215 to +225
if (GetTimeoutFlag() == TRUE)
{
#if PRINT_DEBUG
SendString(", timout ");
#endif /* PRINT_DEBUG */

#if SEND_CAN_MSG
CANSend(&CAN_drive);
#endif /* SEND_CAN_MSG */

ClearTimeoutFlag();
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's where the new timeout flag getter and setter are used

Comment on lines +43 to +44
static void sendMotorCommand(float curr, float vel);
static void sendReverseToggle(void);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved to bottom of file and made static


DBGMCU->CR |= DBGMCU_CR_DBG_TIM2_STOP; // Halt TIM2 upon debug halt

TIM2->CR1 |= TIM_CR1_CEN; // Enable TIM2
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling the timer should be the very last step of the initialization. This was corrected.

Comment on lines -36 to -37
CAN1->FM1R |= 0x1C << 8; // Assign all filters to CAN1
CAN1->FMR |= 0x1UL; // Set to filter initialization mode
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CAN1->FM1R |= 0x1C << 8 is supposed to be a write to CAN1->FMR. Although it was done wrong, fortunately the FM1R register is locked until the next operation CAN1->FMR |= 0x1UL; (enter filter initialization mode). So, it just did nothing rather than what it was supposed to do. This has been corrected.

Comment on lines +54 to +55
int CANSetFilter(uint16_t id);
int CANSetFilters(uint16_t *ids, uint8_t num);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added completion status return values

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant