Motor Driver Evaluation Kit NEVB-MTR1-t01-1.1.0
Firmware for NEVB-MTR1-KIT1 for trapezoidal control of BLDC motors using Hall-effect sensors
Loading...
Searching...
No Matches
main.ino File Reference

Motor control implementation. More...

#include "config.h"
#include "tables.h"
#include "fault.h"
#include "filter.h"
#include "scpi.h"
Include dependency graph for main.ino:

Go to the source code of this file.

Functions

void setup (void)
 Main initialization function.
 
void loop ()
 Main Loop Function.
 
static void FlagsInit (void)
 Initializes motorFlags and faultFlags.
 
static void ConfigsInit (void)
 Initializes motorConfigs.
 
static void PLLInit (void)
 Initialize PLL (Phase-Locked Loop)
 
static void PortsInit (void)
 Initialize I/O port directions and pull-up resistors.
 
void TimersInit (void)
 Initializes and synchronizes Timers.
 
static void PinChangeIntInit (void)
 Initialize pin change interrupts.
 
static void ADCInit (void)
 Initializes the ADC.
 
static uint16_t ADCSingleConversion (void)
 Perform a single ADC conversion.
 
static void EnableUpdate (void)
 Check whether the enable pin is set and update flags accordingly.
 
static void DisableMotor (void)
 Disable motor.
 
static void RemoteUpdate (void)
 Check whether the remote pin is set and update flags accordingly.
 
static void SpeedController (void)
 Speed regulator loop.
 
static void FatalError ()
 Handle a fatal error and enter a fault state.
 
static void SetDuty (const uint16_t duty)
 Set duty cycle for TIM4.
 
static void TimersSetModeBlockCommutation (void)
 Configures timers for block commutation.
 
static void TimersWaitForNextPWMCycle (void)
 Wait for the start of the next PWM cycle.
 
static void BlockCommutate (const uint8_t direction, uint8_t hall)
 Perform block commutation based on direction and hall sensor input.
 
static uint8_t GetHall (void)
 Read the hall sensor inputs and decode the hall state.
 
static void DesiredDirectionUpdate (void)
 Updates global desired direction flag.
 
static void ActualDirectionUpdate (uint8_t lastHall, const uint8_t newHall)
 Update the global actual direction flag based on the two latest hall values.
 
static void ReverseRotationSignalUpdate (void)
 Update the reverse rotation flag.
 
static void EnablePWMOutputs (void)
 Enable PWM output pins.
 
static void DisablePWMOutputs (void)
 Disable PWM output pins.
 
static void ClearPWMPorts (void)
 Clear PWM output ports.
 
static void CommutationTicksUpdate (void)
 Update the 'tick' counter and check for a stopped motor.
 
 ISR (INT0_vect)
 Enable interrupt service routine.
 
 ISR (PCINT0_vect)
 Hall sensor change interrupt service routine.
 
 ISR (INT2_vect)
 Direction input change interrupt service routine.
 
 ISR (TIMER4_OVF_vect)
 Timer4 Overflow Event Interrupt Service Routine.
 
 ISR (TIMER1_OVF_vect)
 Timer1 Overflow Interrupt Service Routine.
 
 ISR (ADC_vect)
 ADC Conversion Complete Interrupt Service Routine.
 
static void SetFaultFlag (fault_flag_t flag, uint8_t value)
 Sets a fault flag value.
 

Variables

volatile motorflags_t motorFlags
 Motor control flags placed in I/O space for fast access.
 
volatile faultflags_t faultFlags
 Fault flags placed in I/O space for fast access.
 
volatile motorconfigs_t motorConfigs
 Motor Configs.
 
volatile uint16_t commutationTicks = 0
 The number of 'ticks' between two hall sensor changes (counter).
 
volatile uint16_t lastCommutationTicks = 0xffff
 The number of 'ticks' between two hall sensor changes (store).
 
volatile uint8_t speedInput = 0
 The most recent "speed" input measurement.
 
volatile uint8_t speedOutput = 0
 The most recent "speed" output from the speed controller.
 
volatile uint16_t ibus = 0
 Hi-side Current (IBUS) measurement (Register Value).
 
volatile int16_t iphaseU = 0
 In-line Phase U current current measurement (Register Value).
 
volatile int16_t iphaseV = 0
 In-line Phase V current current measurement (Register Value).
 
volatile int16_t iphaseW = 0
 In-line Phase W current current measurement (Register Value).
 
volatile uint16_t vbusVref = 0
 VBUS voltage measurement (Register Value)
 

Detailed Description

Motor control implementation.

This file contains the full implementation of the motor control, except the PID-controller, filter, fault, SCPI and table implementations and definitions.

User Manual:
ANxxx: Trapezoidal Control of BLDC Motors Using Hall Effect Sensors
Documentation
For comprehensive code documentation, supported compilers, compiler settings and supported devices see readme.html
Author
Nexperia: http://www.nexperia.com
Support Page
For additional support, visit: https://www.nexperia.com/support
Author
Aanas Sayed
Date
2024/03/08


Definition in file main.ino.

Function Documentation

◆ ActualDirectionUpdate()

static void ActualDirectionUpdate ( uint8_t lastHall,
const uint8_t newHall )
static

Update the global actual direction flag based on the two latest hall values.

Calling this function with the last two hall sensor values as parameters triggers an update of the global actualDirection flag.

Parameters
lastHallThe previous hall sensor value.
newHallThe current hall sensor value.

Definition at line 943 of file main.ino.

944{
945 // Ensure that lastHall is within the bounds of the table. If not, set it to
946 // 0, which is also an illegal hall value but a legal table index.
947 if (lastHall > 6)
948 {
949 lastHall = 0;
950 }
951
952 if (pgm_read_byte_near(&expectedHallSequenceForward[lastHall]) == newHall)
953 {
954 motorFlags.actualDirection = DIRECTION_FORWARD;
955 }
956 else if (pgm_read_byte_near(&expectedHallSequenceReverse[lastHall]) == newHall)
957 {
958 motorFlags.actualDirection = DIRECTION_REVERSE;
959 }
960 else
961 {
962 motorFlags.actualDirection = DIRECTION_UNKNOWN;
963 }
964}
#define DIRECTION_FORWARD
Forward direction flag value.
Definition config.h:699
#define DIRECTION_REVERSE
Reverse direction flag value.
Definition config.h:701
#define DIRECTION_UNKNOWN
Unknown direction flag value.
Definition config.h:703
volatile motorflags_t motorFlags
Motor control flags placed in I/O space for fast access.
Definition main.ino:50
const uint8_t expectedHallSequenceReverse[7]
Table of Expected Hall Sensor Values in Reverse Direction.
Definition tables.h:163
const uint8_t expectedHallSequenceForward[7]
Table of Expected Hall Sensor Values in Forward Direction.
Definition tables.h:149
Here is the caller graph for this function:

◆ ADCInit()

static void ADCInit ( void )
static

Initializes the ADC.

This function initializes the ADC for speed reference, current and gate voltage measurements.

The function performs self-tests to ensure the ADC's accuracy:

  • It measures 0V to verify the ADC's ability to read close to zero.
  • It measures 1.1V to ensure the ADC's accuracy within a specified range.
  • It measures BREF and waits for any board to be connected.

If any of the self-tests fail, a fatal error is indicated.

After self-testing, the ADC is configured for speed reference measurements and set to trigger on ADC_TRIGGER.

Note
ADC_TRIGGER is set to ADC_TRIGGER_TIMER0_OVF. This triggers the ADC at a rate of ~977 samples per second. The ADC prescaler is set to ADC_PRESCALER_DIV_128 as this gives a 125kHz ADC clock. It is recommended to have the ADC clock between 50kHz and 200kHz to get maximum ADC resolution.
See also
ADC_TRIGGER

Definition at line 536 of file main.ino.

537{
538#if (WAIT_FOR_BOARD == TRUE)
539 // Select initial AD conversion channel [IBUS] to check if board is connected.
540 ADMUX = (ADC_REFERENCE_VOLTAGE | (1 << ADLAR) | ADC_MUX_L_IBUS);
541 ADCSRB = ADC_MUX_H_IBUS;
542 _delay_ms(1);
543
544 // Enable ADC
545 ADCSRA = (1 << ADEN);
546
547 // Start ADC single conversion and discard first measurement.
548 uint16_t adc_reading = ADCSingleConversion();
549
550 // Enable pull up resistor
551 PORTF |= (1 << IBUS_PIN);
552
553 _delay_ms(10);
555
556 // Start ADC single conversion to measure BREF.
557 adc_reading = ADCSingleConversion();
558
559 // Wait to check if any board is connected. Should be less than
560 // 0x3C0 if the inverter board is connected
561 while (adc_reading > 0x3C0)
562 {
564
565 // Start ADC single conversion to measure BREF.
566 adc_reading = ADCSingleConversion();
567
568 _delay_ms(10);
569 }
570
571 // Disable pull up resistor
572 PORTF &= ~(1 << IBUS_PIN);
573
574 _delay_ms(10);
575
576#else
577 // Select AD reference voltage and left adjust result.
578 ADMUX = (ADC_REFERENCE_VOLTAGE | (1 << ADLAR));
579
581#endif
582
583 // Re-initialize ADC mux channel select.
584 ADMUX &= ~ADC_MUX_L_BITS;
585 ADMUX |= ADC_MUX_L_SPEED;
586 // Set trigger source to ADC_TRIGGER.
587 ADCSRB = ADC_MUX_H_SPEED | ADC_TRIGGER;
588
589 // Re-initialize ADC to work with interrupts.
590 ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | (1 << ADIF) | (1 << ADIE) | ADC_PRESCALER;
591}
void SweepLEDsBlocking(void)
Sweeps through all LEDs individually with a delay.
Definition fault.cpp:144
#define ADC_MUX_L_BITS
Lower ADC channel selection bits (MUX4:0) mask.
Definition config.h:971
#define ADC_MUX_L_IBUS
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:723
#define ADC_MUX_L_SPEED
Definition config.h:718
#define ADC_TRIGGER
ADC trigger used in this application.
Definition config.h:749
#define IBUS_PIN
IBUS ADC input pin (used to check if board is attached).
Definition config.h:759
#define ADC_MUX_H_IBUS
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:725
#define ADC_PRESCALER
ADC clock pre-scaler used in this application.
Definition config.h:745
#define ADC_MUX_H_SPEED
Definition config.h:721
#define ADC_REFERENCE_VOLTAGE
ADC voltage reference used in this application.
Definition config.h:747
static uint16_t ADCSingleConversion(void)
Perform a single ADC conversion.
Definition main.ino:600
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ADCSingleConversion()

static uint16_t ADCSingleConversion ( void )
static

Perform a single ADC conversion.

This function initiates a single ADC conversion to measure a voltage. It waits for the conversion to complete and then reads the ADC result.

Returns
The 16-bit ADC result representing the measured voltage.

Definition at line 600 of file main.ino.

601{
602 // Initialize ADC for one-time conversion.
603 ADCSRA |= (1 << ADSC);
604
605 // Wait for the conversion to complete.
606 while (ADCSRA & (1 << ADSC))
607 {
608 // Wait until the conversion is finished.
609 }
610
611 // Read the ADC result and combine ADCH and ADCL into a 16-bit value.
612 uint16_t value = (ADCL >> 6);
613 value |= 0x3ff & (ADCH << 2);
614
615 return value;
616}
Here is the caller graph for this function:

◆ BlockCommutate()

static void BlockCommutate ( const uint8_t direction,
uint8_t hall )
static

Perform block commutation based on direction and hall sensor input.

This function performs block commutation according to the specified direction of rotation and the hall sensor input. Block commutation is used to control motor phases during operation.

Parameters
directionDirection of rotation (DIRECTION_FORWARD or DIRECTION_REVERSE).
hallHall sensor input value corresponding to the rotor position.

Definition at line 865 of file main.ino.

866{
867 const uint8_t *tableAddress;
868
869 if (direction == DIRECTION_FORWARD)
870 {
871 tableAddress = blockCommutationTableForward;
872 }
873 else
874 {
875 tableAddress = blockCommutationTableReverse;
876 }
877 tableAddress += (hall * 4);
878
880 TCCR4E &= ~0b00111111;
881
883
884 PORTB |= (uint8_t)pgm_read_byte_near(tableAddress++);
885 PORTC |= (uint8_t)pgm_read_byte_near(tableAddress++);
886 PORTD |= (uint8_t)pgm_read_byte_near(tableAddress++);
887 TCCR4E |= (uint8_t)pgm_read_byte_near(tableAddress);
888
890}
static void DisablePWMOutputs(void)
Disable PWM output pins.
Definition main.ino:1005
static void EnablePWMOutputs(void)
Enable PWM output pins.
Definition main.ino:992
static void ClearPWMPorts(void)
Clear PWM output ports.
Definition main.ino:1018
const uint8_t blockCommutationTableReverse[32]
Block Commutation Port Direction Masks for Reverse Driving.
Definition tables.h:128
const uint8_t blockCommutationTableForward[32]
Block Commutation Port Direction Masks for Forward Driving.
Definition tables.h:80
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ClearPWMPorts()

static void ClearPWMPorts ( void )
static

Clear PWM output ports.

This function clears the PWM output ports by setting the corresponding bits of the port registers to low. It effectively turns off the PWM signals on the specified ports while leaving the port directions unchanged.

Definition at line 1018 of file main.ino.

1019{
1020 PORTB &= ~PWM_PATTERN_PORTB;
1021 PORTC &= ~PWM_PATTERN_PORTC;
1022 PORTD &= ~PWM_PATTERN_PORTD;
1023}
#define PWM_PATTERN_PORTC
Bit pattern of PWM pins placed on PORTC (Phase B).
Definition config.h:684
#define PWM_PATTERN_PORTB
Bit pattern of PWM pins placed on PORTB (Phase A).
Definition config.h:682
#define PWM_PATTERN_PORTD
Bit pattern of PWM pins placed on PORTD (Phase C).
Definition config.h:686
Here is the caller graph for this function:

◆ CommutationTicksUpdate()

static void CommutationTicksUpdate ( void )
static

Update the 'tick' counter and check for a stopped motor.

This function should be called at every PWM timer overflow to update the 'tick' counter. It increments the 'tick' counter until it reaches the maximum tick limit, indicating a potentially stopped or stalled motor. If the limit is reached, the global motor stopped flag is set.

Note
This function can be used to detect a stalled motor and take appropriate actions.
If the motor is determined to be stopped, it may change the motor drive waveform and disable PWM outputs as necessary.
See also
COMMUTATION_TICKS_STOPPED, TURN_OFF_MODE

Definition at line 1040 of file main.ino.

1041{
1042 // If the motor is not stopped, increment the tick counter.
1044 {
1046 }
1047 // If motor is stopped, set the stopped flag and clear the tick counter.
1048 else
1049 {
1050 // Set flags to notify that the motor is stopped.
1052 lastCommutationTicks = 0xffff;
1053
1054 // Get the current hall value.
1055 uint8_t hall = GetHall();
1056 if ((hall == 0) || (hall == 0b111))
1057 {
1059 }
1060
1061 // If the motor is in a fatal fault, motor is now stopped so loop forever.
1062 if (motorFlags.fatalFault == TRUE)
1063 {
1064 while (1)
1065 {
1067 _delay_ms(2);
1068 }
1069 }
1070 // If the motor is supposed to be enabled, and the drive method is not block commutation,
1071 // reset the speed output and set the drive waveform.
1072 else if (motorFlags.driveWaveform != WAVEFORM_BLOCK_COMMUTATION && motorFlags.enable == TRUE)
1073 {
1074 speedOutput = 0;
1075#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
1076 PIDResetIntegrator(&pidParameters);
1077#endif
1079 BlockCommutate(motorFlags.desiredDirection, GetHall());
1080 }
1081 // If the motor is supposed to be stopped, (and it has stopped now) ...
1082 else if (motorFlags.enable == FALSE)
1083 {
1084#if (TURN_OFF_MODE == TURN_OFF_MODE_RAMP)
1085 // ... unset the drive waveform and disable PWM outputs.
1086 motorFlags.driveWaveform = WAVEFORM_UNDEFINED;
1088 ClearPWMPorts();
1089#endif
1090 // ... update the desired direction flag as this would not have been
1091 // updated if the direction was changed while the motor was running.
1093 }
1094 }
1095}
void faultSequentialStateMachine(volatile faultflags_t *faultFlags, volatile motorflags_t *motorFlags)
Sequential State Machine for Handling Fault Flags.
Definition fault.cpp:182
#define WAVEFORM_BLOCK_COMMUTATION
Waveform constant for block commutation.
Definition config.h:777
#define WAVEFORM_UNDEFINED
Waveform status flag used for coasting.
Definition config.h:779
#define TRUE
TRUE constant value, defined to be compatible with comparisons.
Definition config.h:661
#define FALSE
FALSE constant value.
Definition config.h:659
@ FAULT_NO_HALL_CONNECTIONS
Is there no hall connections?
Definition config.h:1166
@ FAULT_MOTOR_STOPPED
Is motor stopped?
Definition config.h:1162
#define COMMUTATION_TICKS_STOPPED
Commutation Stopped Limit.
Definition config.h:246
static void BlockCommutate(const uint8_t direction, uint8_t hall)
Perform block commutation based on direction and hall sensor input.
Definition main.ino:865
static uint8_t GetHall(void)
Read the hall sensor inputs and decode the hall state.
Definition main.ino:903
static void TimersSetModeBlockCommutation(void)
Configures timers for block commutation.
Definition main.ino:817
static void SetFaultFlag(fault_flag_t flag, uint8_t value)
Sets a fault flag value.
Definition main.ino:1382
volatile uint16_t commutationTicks
The number of 'ticks' between two hall sensor changes (counter).
Definition main.ino:81
static void DesiredDirectionUpdate(void)
Updates global desired direction flag.
Definition main.ino:918
volatile uint8_t speedOutput
The most recent "speed" output from the speed controller.
Definition main.ino:108
volatile faultflags_t faultFlags
Fault flags placed in I/O space for fast access.
Definition main.ino:60
volatile uint16_t lastCommutationTicks
The number of 'ticks' between two hall sensor changes (store).
Definition main.ino:94
void PIDResetIntegrator(pidData_t *pid_st)
Resets the integrator in the PID regulator.
Definition pid.cpp:130
Here is the call graph for this function:
Here is the caller graph for this function:

◆ ConfigsInit()

static void ConfigsInit ( void )
static

Initializes motorConfigs.

This function initializes motorConfigs to their default values.

Definition at line 366 of file main.ino.

367{
368 motorConfigs.tim4Freq = (uint32_t)F_MOSFET;
369 motorConfigs.tim4Top = (uint16_t)TIM4_TOP(motorConfigs.tim4Freq);
370 motorConfigs.tim4DeadTime = (uint16_t)DEAD_TIME;
371 motorConfigs.speedInputSource = (uint8_t)SPEED_INPUT_SOURCE_LOCAL;
372}
#define TIM4_TOP(tim4Freq)
Definition config.h:1216
#define SPEED_INPUT_SOURCE_LOCAL
Speed input source - Local or speed input pin.
Definition config.h:763
#define F_MOSFET
Desired Switching Frequency for MOSFET Gate Signals.
Definition config.h:157
#define DEAD_TIME
Dead Time Specification.
Definition config.h:186
volatile motorconfigs_t motorConfigs
Motor Configs.
Definition main.ino:66
Here is the caller graph for this function:

◆ DesiredDirectionUpdate()

static void DesiredDirectionUpdate ( void )
static

Updates global desired direction flag.

Running this function triggers a reading of the direction input pin. The desiredDirection flag is set accordingly.

Definition at line 918 of file main.ino.

919{
920 if (motorFlags.enable == TRUE)
921 {
922 return;
923 }
924 else if ((PIND & (1 << DIRECTION_COMMAND_PIN)) != 0)
925 {
926 motorFlags.desiredDirection = DIRECTION_REVERSE;
927 }
928 else
929 {
930 motorFlags.desiredDirection = DIRECTION_FORWARD;
931 }
932}
#define DIRECTION_COMMAND_PIN
Pin where direction command input is located.
Definition config.h:753
Here is the caller graph for this function:

◆ DisableMotor()

static void DisableMotor ( void )
static

Disable motor.

This function disables the motor by clearing the enable flag and performing specific actions based on the TURN_OFF_MODE configuration:

Note
The behavior of this function depends on the TURN_OFF_MODE configuration.
See also
TURN_OFF_MODE

Definition at line 659 of file main.ino.

660{
661 motorFlags.enable = FALSE;
663
664#if (TURN_OFF_MODE == TURN_OFF_MODE_COAST)
665 // Disable driver signals to let the motor coast.
666 motorFlags.driveWaveform = WAVEFORM_UNDEFINED;
669#endif
670}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ DisablePWMOutputs()

static void DisablePWMOutputs ( void )
static

Disable PWM output pins.

This function disables PWM outputs by setting the port direction for all PWM pins as inputs. This action overrides the PWM signals, effectively stopping the generation of PWM. The PWM configuration itself remains unchanged.

Definition at line 1005 of file main.ino.

1006{
1007 DDRB &= ~PWM_PATTERN_PORTB;
1008 DDRC &= ~PWM_PATTERN_PORTC;
1009 DDRD &= ~PWM_PATTERN_PORTD;
1010}
Here is the caller graph for this function:

◆ EnablePWMOutputs()

static void EnablePWMOutputs ( void )
static

Enable PWM output pins.

This function sets the port direction for all PWM pins as output, allowing PWM signals to be generated on these pins. It does not modify the PWM configuration itself.

Definition at line 992 of file main.ino.

993{
994 DDRB |= PWM_PATTERN_PORTB;
995 DDRC |= PWM_PATTERN_PORTC;
996 DDRD |= PWM_PATTERN_PORTD;
997}
Here is the caller graph for this function:

◆ EnableUpdate()

static void EnableUpdate ( void )
static

Check whether the enable pin is set and update flags accordingly.

This function checks the state of the enable pin and updates the motorFlags and faultFlags accordingly. If the enable pin is not set, it can perform specific actions based on the TURN_OFF_MODE configuration:

Note
The behavior of this function depends on the TURN_OFF_MODE configuration.
See also
TURN_OFF_MODE

Definition at line 633 of file main.ino.

634{
635 if ((PIND & (1 << ENABLE_PIN)) != 0)
636 {
637 motorFlags.enable = TRUE;
638 }
639 else
640 {
641 DisableMotor();
642 }
643}
#define ENABLE_PIN
Enable input pin.
Definition config.h:755
static void DisableMotor(void)
Disable motor.
Definition main.ino:659
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FatalError()

static void FatalError ( )
static

Handle a fatal error and enter a fault state.

This function is called in response to a fatal error condition. It performs the following actions:

  • Sets the motor to stop rotation.
  • Once the motor is stopped, it will loop indefinitely inside CommutationTicksUpdate().

Definition at line 781 of file main.ino.

782{
783 // Stop the motor.
784 DisableMotor();
785
786 // Once this is set no more faults will be registered creating a snapshot
787 // of the failure point.
788 motorFlags.fatalFault = TRUE;
789}
Here is the call graph for this function:
Here is the caller graph for this function:

◆ FlagsInit()

static void FlagsInit ( void )
static

Initializes motorFlags and faultFlags.

This function initializes both motorFlags and faultFlags to their default values.

Definition at line 339 of file main.ino.

340{
341 // Initialize motorFlags with default values.
342 motorFlags.speedControllerRun = FALSE;
343 motorFlags.remote = FALSE;
344 motorFlags.enable = FALSE;
345 motorFlags.actualDirection = DIRECTION_UNKNOWN;
346 motorFlags.desiredDirection = DIRECTION_FORWARD;
347 motorFlags.driveWaveform = WAVEFORM_UNDEFINED;
348 motorFlags.fatalFault = FALSE;
349
350 // Initialize faultFlags with default values. Set motorStopped to FALSE at
351 // startup. This will make sure that the motor is not started if it is not
352 // really stopped. If it is stopped, this variable will quickly be updated.
353 faultFlags.motorStopped = FALSE;
354 faultFlags.reverseDirection = FALSE;
355 faultFlags.overCurrent = FALSE;
356 faultFlags.noHallConnections = FALSE;
357 faultFlags.userFlag1 = FALSE;
358 faultFlags.userFlag2 = FALSE;
359 faultFlags.userFlag3 = FALSE;
360}
Here is the caller graph for this function:

◆ GetHall()

static uint8_t GetHall ( void )
static

Read the hall sensor inputs and decode the hall state.

This function reads the hall sensor inputs and converts them into a number ranging from 1 to 6, where the hall sensors' states are represented as bits: H3|H2|H1.

Returns
The decoded hall sensor value.
Return values
0Illegal hall state. Possible hardware error.
1-6Valid hall sensor values.
7Illegal hall state. Possible hardware error.

Definition at line 903 of file main.ino.

904{
905 uint8_t hall;
906
907 hall = HALL_PIN & ((1 << H3_PIN) | (1 << H2_PIN) | (1 << H1_PIN));
908 hall >>= H1_PIN;
909
910 return hall;
911}
#define H2_PIN
Pin where H2 is connected.
Definition config.h:711
#define HALL_PIN
PIN register for Hall sensor input.
Definition config.h:707
#define H3_PIN
Pin where H3 is connected.
Definition config.h:713
#define H1_PIN
Pin where H1 is connected.
Definition config.h:709
Here is the caller graph for this function:

◆ ISR() [1/6]

ISR ( ADC_vect )

ADC Conversion Complete Interrupt Service Routine.

This interrupt service routine is automatically executed every time an AD conversion is finished, and the converted result is available in the ADC data register.

The switch/case construct ensures that the converted value is stored in the variable corresponding to the selected channel and changes the channel for the next ADC measurement.

Additional ADC measurements can be added to the cycle by extending the switch/case construct.

Definition at line 1263 of file main.ino.

1264{
1265 switch ((ADMUX & ADC_MUX_L_BITS) | (ADCSRB & ADC_MUX_H_BITS))
1266 {
1268 // Handle ADC conversion result for speed measurement.
1269 if (motorConfigs.speedInputSource == SPEED_INPUT_SOURCE_LOCAL)
1270 {
1271 speedInput = ADCH;
1272 }
1273 ADMUX &= ~ADC_MUX_L_BITS;
1274 ADMUX |= ADC_MUX_L_IBUS;
1275 ADCSRB &= ~ADC_MUX_H_BITS;
1276 ADCSRB |= ADC_MUX_H_IBUS;
1277 break;
1279 // Handle ADC conversion result for current measurement.
1280 ibus = ADCL >> 6;
1281 ibus |= (ADCH << 2);
1282 ADMUX &= ~ADC_MUX_L_BITS;
1283 ADMUX |= ADC_MUX_L_IPHASE_U;
1284 ADCSRB &= ~ADC_MUX_H_BITS;
1285 ADCSRB |= ADC_MUX_H_IPHASE_U;
1286
1287#if (IBUS_FAULT_ENABLE == TRUE)
1288 // Debounce current error flags.
1289 static uint8_t currentErrorCount = 0;
1291 {
1292 if (currentErrorCount < 3)
1293 {
1294 currentErrorCount++;
1295 }
1296 else
1297 {
1302 FatalError();
1303 }
1304 }
1305 else
1306#endif
1308 {
1310#if (IBUS_FAULT_ENABLE == TRUE)
1311 currentErrorCount = 0;
1312#endif
1313 }
1314 else
1315 {
1317#if (IBUS_FAULT_ENABLE == TRUE)
1318 currentErrorCount = 0;
1319#endif
1320 }
1321 break;
1323 // Handle ADC conversion result for phase current measurement.
1324 iphaseU = ADCL >> 6;
1325 iphaseU |= (ADCH << 2);
1326 ADMUX &= ~ADC_MUX_L_BITS;
1327 ADMUX |= ADC_MUX_L_IPHASE_V;
1328 ADCSRB &= ~ADC_MUX_H_BITS;
1329 ADCSRB |= ADC_MUX_H_IPHASE_V;
1330 break;
1332 // Handle ADC conversion result for phase current measurement.
1333 iphaseV = ADCL >> 6;
1334 iphaseV |= (ADCH << 2);
1335 ADMUX &= ~ADC_MUX_L_BITS;
1336 ADMUX |= ADC_MUX_L_IPHASE_W;
1337 ADCSRB &= ~ADC_MUX_H_BITS;
1338 ADCSRB |= ADC_MUX_H_IPHASE_W;
1339 break;
1341 // Handle ADC conversion result for phase current measurement.
1342 iphaseW = ADCL >> 6;
1343 iphaseW |= (ADCH << 2);
1344 ADMUX &= ~ADC_MUX_L_BITS;
1345 ADMUX |= ADC_MUX_L_VBUSVREF;
1346 ADCSRB &= ~ADC_MUX_H_BITS;
1347 ADCSRB |= ADC_MUX_H_VBUSVREF;
1348 break;
1350 // Handle ADC conversion result for gate voltage reference measurement.
1351 vbusVref = ADCL >> 6;
1352 vbusVref |= (ADCH << 2);
1353 ADMUX &= ~ADC_MUX_L_BITS;
1354 ADMUX |= ADC_MUX_L_SPEED;
1355 ADCSRB &= ~ADC_MUX_H_BITS;
1356 ADCSRB |= ADC_MUX_H_SPEED;
1357 break;
1358 default:
1359 // This is probably an error and should be handled.
1363 FatalError();
1364 break;
1365 }
1366
1367 // Clear Timer/Counter0 overflow flag.
1368 TIFR0 = (1 << TOV0);
1369}
#define ADC_MUX_H_BITS
High ADC channel selection bit (MUX5) mask.
Definition config.h:973
#define ADC_MUX_H_IPHASE_V
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:733
#define ADC_MUX_H_VBUSVREF
High analog channel selection bit (MUX5) for for motor vbusVref measurement.
Definition config.h:741
#define ADC_MUX_L_IPHASE_W
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:735
#define ADC_MUX_L_VBUSVREF
Lower analog channel selection bits (MUX4:0) for motor vbusVref measurement.
Definition config.h:739
#define ADC_MUX_H_IPHASE_W
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:737
#define ADC_MUX_L_IPHASE_V
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:731
#define ADC_MUX_L_IPHASE_U
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:727
#define ADC_MUX_H_IPHASE_U
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:729
@ FAULT_OVER_CURRENT
Has it tripped the over current limit?
Definition config.h:1164
@ FAULT_USER_FLAG1
Is user flag 1 set?
Definition config.h:1168
@ FAULT_USER_FLAG2
Is user flag 2 set?
Definition config.h:1170
@ FAULT_USER_FLAG3
Is user flag 3 set?
Definition config.h:1172
#define IBUS_WARNING_THRESHOLD
Hi-side Current (IBUS) Warning Threshold (Register Value)
Definition config.h:367
#define IBUS_ERROR_THRESHOLD
Hi-side Current (IBUS) Error Threshold (Register Value)
Definition config.h:406
volatile uint16_t vbusVref
VBUS voltage measurement (Register Value)
Definition main.ino:255
volatile uint8_t speedInput
The most recent "speed" input measurement.
Definition main.ino:101
volatile uint16_t ibus
Hi-side Current (IBUS) measurement (Register Value).
Definition main.ino:137
volatile int16_t iphaseW
In-line Phase W current current measurement (Register Value).
Definition main.ino:226
volatile int16_t iphaseU
In-line Phase U current current measurement (Register Value).
Definition main.ino:167
static void FatalError()
Handle a fatal error and enter a fault state.
Definition main.ino:781
volatile int16_t iphaseV
In-line Phase V current current measurement (Register Value).
Definition main.ino:196
Here is the call graph for this function:

◆ ISR() [2/6]

ISR ( INT0_vect )

Enable interrupt service routine.

This interrupt service routine is called if the enable pin changes state.

Definition at line 1101 of file main.ino.

1102{
1103 EnableUpdate();
1104}
static void EnableUpdate(void)
Check whether the enable pin is set and update flags accordingly.
Definition main.ino:633
Here is the call graph for this function:

◆ ISR() [3/6]

ISR ( INT2_vect )

Direction input change interrupt service routine.

This interrupt service routine is called every time the direction input pin changes state. The motor is stopped and the desired direction flag is updated accordingly. The motor will not start again until the enable pin is turned on again.

Note
Depending on the TURN_OFF_MODE configuration, it will either coast or ramp down the motor.
See also
TURN_OFF_MODE

Definition at line 1156 of file main.ino.

1157{
1158 // Update desired direction flag.
1160
1161 // Stop the motor.
1162 DisableMotor();
1163}
Here is the call graph for this function:

◆ ISR() [4/6]

ISR ( PCINT0_vect )

Hall sensor change interrupt service routine.

This interrupt service routine is called every time any of the hall sensors change. The actual direction, the reverse rotation and no hall connections flags are updated.

The motor stopped flag is also set to FALSE, since the motor is obviously not stopped when there is a hall change.

Definition at line 1115 of file main.ino.

1116{
1117 static uint8_t lastHall = 0xff;
1118 uint8_t hall;
1119
1120 hall = GetHall();
1121
1122 if (motorFlags.driveWaveform == WAVEFORM_BLOCK_COMMUTATION)
1123 {
1124 BlockCommutate(motorFlags.desiredDirection, hall);
1125 }
1126
1127 // Update flags that depend on hall sensor value.
1128 ActualDirectionUpdate(lastHall, hall);
1130
1131 lastHall = hall;
1132
1133 // Reset commutation timer.
1135 commutationTicks = 0;
1136
1137 // Since the hall sensors are changing, the motor can not be stopped.
1138 // For fast access, SetFaultFlag() is not used to update this flag.
1139 // Instead, the flag is updated directly.
1140 faultFlags.motorStopped = FALSE;
1141 faultFlags.noHallConnections = FALSE;
1142}
int16_t calculateEMA(uint16_t currentSample, uint16_t previousEMA, uint8_t alphaExponent)
Exponential Moving Average (EMA) calculation algorithm.
Definition filter.cpp:34
static void ActualDirectionUpdate(uint8_t lastHall, const uint8_t newHall)
Update the global actual direction flag based on the two latest hall values.
Definition main.ino:943
static void ReverseRotationSignalUpdate(void)
Update the reverse rotation flag.
Definition main.ino:974
Here is the call graph for this function:

◆ ISR() [5/6]

ISR ( TIMER1_OVF_vect )

Timer1 Overflow Interrupt Service Routine.

This interrupt service routine (ISR) is triggered on Timer1 overflow. It calls the faultSequentialStateMachine() function to handle motor fault reporting.

See also
faultSequentialStateMachine()

Definition at line 1244 of file main.ino.

Here is the call graph for this function:

◆ ISR() [6/6]

ISR ( TIMER4_OVF_vect )

Timer4 Overflow Event Interrupt Service Routine.

This interrupt service routine is trigger on Timer4 overflow. It manages the commutation ticks, which determines motor status. It also controls the execution of the speed regulation loop at constant intervals.

See also
TimersInit(), F_MOSFET

Definition at line 1173 of file main.ino.

1174{
1175 if (motorFlags.driveWaveform == WAVEFORM_BLOCK_COMMUTATION)
1176 {
1177 uint16_t dutyCycle = ((uint32_t)speedOutput * motorConfigs.tim4Top) >> 7;
1178
1179 if (dutyCycle > (uint16_t)(motorConfigs.tim4Top << 1))
1180 {
1181 dutyCycle = (uint16_t)(motorConfigs.tim4Top << 1);
1182 }
1183
1184 SetDuty(dutyCycle);
1185 }
1186
1188
1189 {
1190 // Run the speed regulation loop with constant intervals.
1191 static uint8_t speedRegTicks = 0;
1192 speedRegTicks++;
1193 if (speedRegTicks >= SPEED_CONTROLLER_TIME_BASE)
1194 {
1195 motorFlags.speedControllerRun = TRUE;
1196 speedRegTicks -= SPEED_CONTROLLER_TIME_BASE;
1197 }
1198 }
1199}
#define SPEED_CONTROLLER_TIME_BASE
Speed Controller Time Base.
Definition config.h:448
static void CommutationTicksUpdate(void)
Update the 'tick' counter and check for a stopped motor.
Definition main.ino:1040
static void SetDuty(const uint16_t duty)
Set duty cycle for TIM4.
Definition main.ino:799
Here is the call graph for this function:

◆ loop()

void loop ( )

Main Loop Function.

The main loop function is executed continuously in normal operation after the program has configured everything. It continually checks if the speed controller needs to be run.

Definition at line 321 of file main.ino.

322{
323 if (motorFlags.remote == TRUE)
324 {
325 ScpiInput(Serial);
326 }
327 if (motorFlags.speedControllerRun)
328 {
330 motorFlags.speedControllerRun = FALSE;
331 }
332}
static void SpeedController(void)
Speed regulator loop.
Definition main.ino:716
void ScpiInput(Stream &interface)
Processes incoming data from a serial interface for SCPI commands.
Definition scpi.cpp:122
Here is the call graph for this function:

◆ PinChangeIntInit()

static void PinChangeIntInit ( void )
static

Initialize pin change interrupts.

This function initializes pin change interrupt on hall sensor input pins input, shutdown input and motor direction control input.

Definition at line 500 of file main.ino.

501{
502 // Initialize external interrupt on shutdown pin (INT0) and direction input
503 // (INT2) pin.
504 EICRA = (0 << ISC21) | (1 << ISC20) | (0 << ISC01) | (1 << ISC00);
505 EIMSK = (1 << INT2) | (1 << INT0);
506
507 // Initialize pin change interrupt on hall sensor inputs (PCINT1..3).
508 PCMSK0 = (1 << PCINT3) | (1 << PCINT2) | (1 << PCINT1);
509
510 // Enable pin change interrupt on ports with pin change signals
511 PCICR = (1 << PCIE0);
512}
Here is the caller graph for this function:

◆ PLLInit()

static void PLLInit ( void )
static

Initialize PLL (Phase-Locked Loop)

This function configures and initializes the PLL for clock generation. It sets the PLL frequency control register and waits until the PLL is locked and stable before returning.

Definition at line 381 of file main.ino.

382{
383 // Configure PLL Frequency Control Register to output 48MHz for USB Module and
384 // 64MHz for the High-Speed Clock Timer with a base speed of 96MHz for PLL
385 // Output Frequency.
386
387 PLLFRQ = (0 << PINMUX) | (1 << PLLUSB) | PLL_POSTSCALER_DIV_1_5 | (1 << PDIV3) | (0 << PDIV2) | (1 << PDIV1) | (0 << PDIV0);
388
389 // Enable PLL.
390 PLLCSR = (1 << PINDIV) | (1 << PLLE);
391
392 // Wait until PLOCK bit is set, indicating PLL is locked and stable.
393 while ((PLLCSR & (1 << PLOCK)) == 0)
394 {
395 // Wait for PLL lock
396 ;
397 }
398}
#define PLL_POSTSCALER_DIV_1_5
PLL Post-scaler - division factor 1.5.
Definition config.h:901
Here is the caller graph for this function:

◆ PortsInit()

static void PortsInit ( void )
static

Initialize I/O port directions and pull-up resistors.

This function initializes all I/O ports with the correct directions and enables pull-up resistors, if needed, for various pins and signals used in the motor control.

Definition at line 407 of file main.ino.

408{
409#if (EMULATE_HALL == TRUE)
410 // Configure and set hall sensor pins for motor emulation
411 PORTB &= ~((1 << H1_PIN) | (1 << H2_PIN) | (1 << H3_PIN));
412 PORTB |= (0x07 & pgm_read_byte_near(&expectedHallSequenceForward[1]));
413 // Set hall sensor pins as outputs.
414 DDRB |= (1 << H1_PIN) | (1 << H2_PIN) | (1 << H3_PIN);
415#endif
416
417#if ((HALL_PULLUP_ENABLE == TRUE) && (EMULATE_HALL != TRUE))
418 // Configure and set hall sensor pins as input with pull-ups enabled
419 PORTB |= (1 << H1_PIN) | (1 << H2_PIN) | (1 << H3_PIN);
420#endif
421
422 // Configure and set pins FAULT_PIN_3, FAULT_PIN_2, and FAULT_PIN_1 as outputs
423 PORTB &= ~((1 << FAULT_PIN_3) | (1 << FAULT_PIN_2));
424 DDRB |= (1 << FAULT_PIN_3) | (1 << FAULT_PIN_2);
425 PORTD &= ~(1 << FAULT_PIN_1);
426 DDRD |= (1 << FAULT_PIN_1);
427
428 // If remote mode, set enable and direction pin as output to allow software
429 // triggered interrupts
430 if (motorFlags.remote == TRUE)
431 {
432 PORTD &= ~((1 << ENABLE_PIN) | (1 << DIRECTION_COMMAND_PIN));
433 DDRD |= (1 << ENABLE_PIN) | (1 << DIRECTION_COMMAND_PIN);
434 }
435}
#define FAULT_PIN_2
Fault Pin 2.
Definition config.h:771
#define FAULT_PIN_3
Fault Pin 3.
Definition config.h:773
#define FAULT_PIN_1
Fault Pin 1.
Definition config.h:769
Here is the caller graph for this function:

◆ RemoteUpdate()

static void RemoteUpdate ( void )
static

Check whether the remote pin is set and update flags accordingly.

This function checks the state of the remote pin and updates the motorFlags.

Note
This is only called during setup so any further pin changes are not detected.

Definition at line 679 of file main.ino.

680{
681 if ((PIND & (1 << REMOTE_PIN)) != 0)
682 {
683 motorFlags.remote = TRUE;
684 }
685 else
686 {
687 motorFlags.remote = FALSE;
688 }
689}
#define REMOTE_PIN
Remote input pin.
Definition config.h:757
Here is the caller graph for this function:

◆ ReverseRotationSignalUpdate()

static void ReverseRotationSignalUpdate ( void )
static

Update the reverse rotation flag.

This function compares the actual and desired direction flags to determine if the motor is running in the opposite direction of what is requested.

Note
For fast access, SetFaultFlag() is not used to update this flag. Instead, the flag is updated directly.

Definition at line 974 of file main.ino.

975{
976 if (motorFlags.actualDirection == motorFlags.desiredDirection)
977 {
978 faultFlags.reverseDirection = FALSE;
979 }
980 else
981 {
982 faultFlags.reverseDirection = TRUE;
983 }
984}
Here is the caller graph for this function:

◆ SetDuty()

static void SetDuty ( const uint16_t duty)
static

Set duty cycle for TIM4.

This function sets the duty cycle for block commutation, where the duty range is 0-1023 (not in percentage). To convert to percentage, use: duty * 100 / 1023.

Parameters
dutyNew duty cycle, range 0-1023.

Definition at line 799 of file main.ino.

800{
801 TC4H = duty >> 8;
802 OCR4A = 0xFF & duty;
803}
Here is the caller graph for this function:

◆ SetFaultFlag()

static void SetFaultFlag ( fault_flag_t flag,
uint8_t value )
static

Sets a fault flag value.

This function sets the specified fault flag to the value given.

Parameters
[in]flagsPointer to a faultflags_t structure.
[in]flagThe fault flag to set as defined in the fault_flag_t enumeration.
[in]valueThe boolean value to set the fault flag to.
See also
faultflags_t, fault_flag_t

Definition at line 1382 of file main.ino.

1383{
1384 if (motorFlags.fatalFault)
1385 {
1386 // Fatal fault active, cannot set any more flags.
1387 return;
1388 }
1389
1390 switch (flag)
1391 {
1392 case FAULT_RESERVED:
1393 faultFlags.reserved = value;
1394 break;
1396 faultFlags.reverseDirection = value;
1397 break;
1399 faultFlags.motorStopped = value;
1400 break;
1401 case FAULT_OVER_CURRENT:
1402 faultFlags.overCurrent = value;
1403 break;
1405 faultFlags.noHallConnections = value;
1406 break;
1407 case FAULT_USER_FLAG1:
1408 faultFlags.userFlag1 = value;
1409 break;
1410 case FAULT_USER_FLAG2:
1411 faultFlags.userFlag2 = value;
1412 break;
1413 case FAULT_USER_FLAG3:
1414 faultFlags.userFlag3 = value;
1415 break;
1416 default:
1417 return; // Invalid flag
1418 }
1419
1420 return; // Success
1421}
@ FAULT_RESERVED
Reserved flag, always false.
Definition config.h:1158
@ FAULT_REVERSE_DIRECTION
Is motor spinning in an unexpected direction?
Definition config.h:1160
Here is the caller graph for this function:

◆ setup()

void setup ( void )

Main initialization function.

The main initialization function initializes all subsystems needed for motor control.

Definition at line 267 of file main.ino.

268{
269 // Load motor configs
270 ConfigsInit();
271
272 // Initialize flags.
273 FlagsInit();
274
275 // Check if remote mode requested.
276 RemoteUpdate();
277
278 // Initialize peripherals.
279 PortsInit(); // depends on motorFlags.remote
280 ADCInit(); // include self-test + loop until board detected must be before TimersInit
281 PLLInit();
282 TimersInit();
283
284#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
285 PIDInit(PID_K_P, PID_K_I, PID_K_D, &pidParameters);
286#endif
287
288 if (motorFlags.remote == TRUE)
289 {
290 // Start serial interface with 115200 bauds.
291 Serial.begin(115200);
292 // while (!Serial); // wait for serial to finish initializing
293
294 // Initialise SCPI subsystem if remote mode.
295 ScpiInit();
296 }
297 else
298 {
299 // Update direction before enable flag.
301 // Do not update enable flag until everything is ready.
302 EnableUpdate();
303 }
304
305 // Set up pin change interrupts.
307
308 // Enable Timer4 overflow event interrupt.
309 TIMSK4 |= (1 << TOIE4);
310
311 // Enable interrupts globally and let motor driver take over.
312 sei();
313}
#define PID_K_P
PID Controller Proportional Gain Constant (Only for Closed Loop)
Definition config.h:509
#define PID_K_D
PID Controller Derivative Gain Constant (Only for Closed Loop)
Definition config.h:566
#define PID_K_I
PID Controller Integral Gain Constant (Only for Closed Loop)
Definition config.h:528
static void PortsInit(void)
Initialize I/O port directions and pull-up resistors.
Definition main.ino:407
static void ADCInit(void)
Initializes the ADC.
Definition main.ino:536
static void FlagsInit(void)
Initializes motorFlags and faultFlags.
Definition main.ino:339
static void PinChangeIntInit(void)
Initialize pin change interrupts.
Definition main.ino:500
static void PLLInit(void)
Initialize PLL (Phase-Locked Loop)
Definition main.ino:381
void TimersInit(void)
Initializes and synchronizes Timers.
Definition main.ino:455
static void ConfigsInit(void)
Initializes motorConfigs.
Definition main.ino:366
static void RemoteUpdate(void)
Check whether the remote pin is set and update flags accordingly.
Definition main.ino:679
void PIDInit(int16_t p_factor, int16_t i_factor, int16_t d_factor, pidData_t *pid)
Initialisation of PID controller parameters.
Definition pid.cpp:31
void ScpiInit(void)
Initializes the SCPI command parser and registers all supported commands.
Definition scpi.cpp:71
Here is the call graph for this function:

◆ SpeedController()

static void SpeedController ( void )
static

Speed regulator loop.

This function is called periodically every SPEED_CONTROLLER_TIME_BASE ticks. In this implementation, a simple PID controller loop is called, but this function could be replaced by any speed or other regulator.

If the SPEED_CONTROL_METHOD is set to SPEED_CONTROL_CLOSED_LOOP, a PID controller is used to regulate the speed. The speed input is converted into an increment set point, and a PID controller computes the output value. The output is limited to a maximum value of 255.

If the SPEED_CONTROL_METHOD is not set to SPEED_CONTROL_CLOSED_LOOP, a simple speed control mechanism is applied. If the motor is enabled, the function calculates the delta between the speed input and the current speed output. If the delta exceeds the maximum allowed change, it limits the change to SPEED_CONTROLLER_MAX_DELTA. If the motor is disabled, the speed output is set to 0.

Note
The behavior of this function depends on the SPEED_CONTROL_METHOD configuration.
See also
SPEED_CONTROL_METHOD, SPEED_CONTROLLER_TIME_BASE, SPEED_CONTROLLER_MAX_DELTA, SPEED_CONTROLLER_MAX_SPEED, PID_K_P, PID_K_I, PID_K_D_ENABLE, PID_K_D

Definition at line 716 of file main.ino.

717{
718 if (motorFlags.enable == TRUE)
719 {
720#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
721 // Calculate an increment set point from the analog speed input.
722 int16_t incrementSetpoint = ((int32_t)speedInput * SPEED_CONTROLLER_MAX_SPEED) / SPEED_CONTROLLER_MAX_INPUT;
723
724 // PID regulator with feed forward from speed input.
725 uint16_t outputValue;
726
727 outputValue = PIDController(incrementSetpoint, (motorConfigs.tim4Freq / (lastCommutationTicks * 3)) >> 1, &pidParameters);
728
729 if (outputValue > 255)
730 {
731 outputValue = 255;
732 }
733
734 speedOutput = outputValue;
735
736 // Without the delay PID does not reset when needed
737 _delay_us(1);
738#else
739 // Calculate the delta in speedInput
740 int16_t delta = speedInput - speedOutput;
741 // If delta exceeds the maximum allowed change, limit it and update
742 // speedOutput
743 if (delta > SPEED_CONTROLLER_MAX_DELTA)
744 {
746 }
747 else if (delta < -SPEED_CONTROLLER_MAX_DELTA)
748 {
750 }
751 else
752 {
754 }
755#endif
756 }
757 else
758 {
759 if (speedOutput > 0)
760 {
762 {
764 }
765 else
766 {
767 speedOutput = 0;
768 }
769 }
770 }
771}
#define SPEED_CONTROLLER_MAX_INPUT
Maximum Speed Reference Input.
Definition config.h:805
#define SPEED_CONTROLLER_MAX_DELTA
Speed Controller Maximum Delta (Applicable for Open Loop Control)
Definition config.h:469
#define SPEED_CONTROLLER_MAX_SPEED
Speed Controller Maximum Speed.
Definition config.h:490
uint16_t PIDController(int16_t setPoint, int16_t processValue, pidData_t *pid_st)
PID control algorithm.
Definition pid.cpp:54
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TimersInit()

void TimersInit ( void )

Initializes and synchronizes Timers.

This function sets the correct pre-scaler and starts all required timers.

Timer 1 is used to trigger the fault multiplexing on overflow. Timer 3 is used, if EMULATE_HALL is set, to trigger the change in hall output on overflow. Timer 4 is used to generate PWM outputs for the gates and trigger the commutation tick counter and check if the motor is spinning on overflow. The overflow event interrupt for Timer 4 is set outside this function at the end of the main initialization function.

Timer 0 is used by the Ardiono core to generate interrupts. For reference, it is set up in mode 3 (Fast PWM, TOP=0xFF) with a prescaler of 64. This means ovwerflow occurs ~977 times per second. The overflow interrupt is used to trigger the ADC conversion unless changed by the user.

See also
EMULATE_HALL, TIM3_FREQ, TimersSetModeBlockCommutation()

Definition at line 455 of file main.ino.

456{
457 // Set Timer1 accordingly.
458 TCCR1A = (1 << WGM11) | (0 << WGM10);
459 TCCR1B = (0 << WGM13) | (1 << WGM12);
460 TIMSK1 = (1 << TOIE1);
461
462 // Start Timer1.
463 TCCR1B |= TIM1_CLOCK_DIV_64;
464
465#if (EMULATE_HALL == TRUE)
466 // Set Timer3 accordingly.
467 TCCR3A = (1 << WGM31) | (1 << WGM30);
468 TCCR3B = (1 << WGM33) | (1 << WGM32);
469 TIMSK3 = (1 << TOIE3);
470
471 // Set top value of Timer/counter3.
472 OCR3AH = (uint8_t)(TIM3_TOP >> 8);
473 OCR3AL = (uint8_t)(0xff & TIM3_TOP);
474
475 // Start Timer3.
476 TCCR3B |= TIM1_CLOCK_DIV_8;
477#endif
478 // Set Timer4 in "PWM6 / Dual-slope" mode. Does not enable outputs yet.
479 TCCR4A = (0 << COM4A1) | (1 << COM4A0) | (0 << COM4B1) | (1 << COM4B0) | (1 << PWM4A) | (1 << PWM4B);
481 TCCR4C |= (0 << COM4D1) | (1 << COM4D0) | (1 << PWM4D);
482 TCCR4E = (1 << ENHC4);
483
484 // Set top value of Timer/counter4.
485 TC4H = (uint8_t)(motorConfigs.tim4Top >> 8);
486 OCR4C = (uint8_t)(0xff & motorConfigs.tim4Top);
487
488 // Set the dead time.
489 DT4 = (DEAD_TIME_HALF(motorConfigs.tim4DeadTime) << 4) | DEAD_TIME_HALF(motorConfigs.tim4DeadTime);
490
491 // Start Timer4.
493}
#define DEAD_TIME_HALF(deadTime)
This value specifies half the dead time in number of clock cycles. Divide by frequency to get duratio...
Definition config.h:1237
#define DT_PRESCALER_DIV_PATTERN(dtPrescaler)
Deadtime generator pre-scaler selection bits based on pre-scaler value.
Definition config.h:1227
#define TIM3_TOP
Calculated top value for Timer 3.
Definition config.h:1245
#define TIM4_PRESCALER_DIV_PATTERN(tim4Prescaler)
Timer 4 clock select bits based on pre-scaler value.
Definition config.h:1209
#define CHOOSE_DT_PRESCALER(deadTime)
Macro to choose Timer4 dead time pre-scaler based on the dead time.
Definition config.h:826
#define CHOOSE_TIM4_PRESCALER(tim4Freq)
Macro to choose Timer4 pre-scaler.
Definition config.h:808
#define TIM1_CLOCK_DIV_64
Timer1 clock - i/o clk with division factor 64.
Definition config.h:936
#define TIM1_CLOCK_DIV_8
Timer1 clock - i/o clk with division factor 8.
Definition config.h:934
Here is the caller graph for this function:

◆ TimersSetModeBlockCommutation()

static void TimersSetModeBlockCommutation ( void )
static

Configures timers for block commutation.

This function is called every time the drive waveform is changed and block commutation is needed. PWM outputs are safely disabled while configuration registers are changed to avoid unintended driving or shoot-through.

The function sets the PWM pins to input (High-Z) while changing modes and configures the timers. The output duty cycle is set to zero initially. It waits for the next PWM cycle to ensure that all outputs are updated, and then the motor drive waveform is set to block commutation. Finally, the PWM pins are changed back to output mode to allow PWM control.

Definition at line 817 of file main.ino.

818{
819 // Set PWM pins to input (High-Z) while changing modes.
821
822 // Sets up timers.
823 TCCR4A = (0 << COM4A1) | (1 << COM4A0) | (0 << COM4B1) | (1 << COM4B0) | (1 << PWM4A) | (1 << PWM4B);
824 TCCR4C |= (0 << COM4D1) | (1 << COM4D0) | (1 << PWM4D);
825 TCCR4D = (1 << WGM41) | (1 << WGM40);
826
827 // Set output duty cycle to zero for now.
828 SetDuty(0);
829
830 // Wait for the next PWM cycle to ensure that all outputs are updated.
832
834
835 // Change PWM pins to output again to allow PWM control.
837}
static void TimersWaitForNextPWMCycle(void)
Wait for the start of the next PWM cycle.
Definition main.ino:844
Here is the call graph for this function:
Here is the caller graph for this function:

◆ TimersWaitForNextPWMCycle()

static void TimersWaitForNextPWMCycle ( void )
static

Wait for the start of the next PWM cycle.

This function waits for the beginning of the next PWM cycle to ensure smooth transitions between different PWM modes and avoid shoot-through conditions.

Definition at line 844 of file main.ino.

845{
846 // Clear Timer1 Capture event flag.
847 TIFR4 = (1 << TOV4);
848
849 // Wait for new Timer1 Capture event flag.
850 while (!(TIFR4 & (1 << TOV4)))
851 {
852 }
853}
Here is the caller graph for this function:

Variable Documentation

◆ commutationTicks

volatile uint16_t commutationTicks = 0

The number of 'ticks' between two hall sensor changes (counter).

This variable is used to count the number of 'ticks' between each hall sensor change. It is cleared when the hall sensor change occurs. One 'tick' is one PWM period.

Note
The speed of the motor is inversely proportional to this value, but this variable is also reset to 0 when a hall sensor change is detected, so cannot be used to infer speed of the motor.
See also
lastCommutationTicks

Definition at line 81 of file main.ino.

◆ faultFlags

volatile faultflags_t faultFlags

Fault flags placed in I/O space for fast access.

This variable contains all the flags used for faults. It is placed in GPIOR0 register, which allows usage of several fast bit manipulation/branch instructions.

Warning
This variable can only have a maximum size of 1 byte.

Definition at line 60 of file main.ino.

◆ ibus

volatile uint16_t ibus = 0

Hi-side Current (IBUS) measurement (Register Value).

The most recent current measurement is stored in this variable.

The range is 0-1023.

This value is not scaled and represents the raw ADC register value. To obtain the scaled current value in amperes, you can use the formula:

\[ \text{Current (A)} = \frac{\text{REGISTER_VALUE} \times 0.004888 \times 1000000}{\text{IBUS_GAIN} \times \text{IBUS_SENSE_RESISTOR}} \]

Where:

  • REGISTER_VALUE : The raw ADC register value stored in this variable.
  • 0.004888 : The conversion factor for a 10-bit ADC with a Vref of 5V.
  • IBUS_GAIN : The gain of the current sense operational amplifier.
  • IBUS_SENSE_RESISTOR : The value of the shunt resistor in micro-ohms (μΩ).

The NEVB-MTR1-I56-1 comes with a current op-amp with a gain factor of 50 and a current sense resistor of value 4 mΩ. This corresponds to approximately 0.0244 amperes (A) per register value.

See also
IBUS_WARNING_THRESHOLD, IBUS_ERROR_THRESHOLD

Definition at line 137 of file main.ino.

◆ iphaseU

volatile int16_t iphaseU = 0

In-line Phase U current current measurement (Register Value).

The most recent current measurement is stored in this variable.

The range is 0-1023.

This value is not scaled and represents the raw ADC register value. To obtain the scaled current value in amperes, you can use the formula:

\[ \text{Current (A)} = \frac{\text{REGISTER_VALUE} \times 0.004888 \times 1000000}{\text{IBUS_GAIN} \times \text{IBUS_SENSE_RESISTOR}} \]

Where:

  • REGISTER_VALUE : The raw ADC register value stored in this variable.
  • 0.004888 : The conversion factor for a 10-bit ADC with a Vref of 5V.
  • IPHASE_GAIN : The gain of the current sense operational amplifier.
  • IPHASE_SENSE_RESISTOR : The value of the shunt resistor in micro-ohms (μΩ).

The NEVB-MTR1-I56-1 comes with a current op-amp with a gain factor of 20 and a current sense resistor of value 2.5 mΩ. This corresponds to approximately TODO: amperes (A) per register value.

Note
It is not used for any significant purpose in this implementation, but the measurement is updated.

Definition at line 167 of file main.ino.

◆ iphaseV

volatile int16_t iphaseV = 0

In-line Phase V current current measurement (Register Value).

The most recent current measurement is stored in this variable.

The range is 0-1023.

This value is not scaled and represents the raw ADC register value. To obtain the scaled current value in amperes, you can use the formula:

\[ \text{Current (A)} = \frac{\text{REGISTER_VALUE} \times 0.004888 \times 1000000}{\text{IBUS_GAIN} \times \text{IBUS_SENSE_RESISTOR}} \]

Where:

  • REGISTER_VALUE : The raw ADC register value stored in this variable.
  • 0.004888 : The conversion factor for a 10-bit ADC with a Vref of 5V.
  • IPHASE_GAIN : The gain of the current sense operational amplifier.
  • IPHASE_SENSE_RESISTOR : The value of the shunt resistor in micro-ohms (μΩ).

The NEVB-MTR1-I56-1 comes with a current op-amp with a gain factor of 20 and a current sense resistor of value 2.5 mΩ. This corresponds to approximately TODO: amperes (A) per register value.

Note
It is not used for any significant purpose in this implementation, but the measurement is updated.

Definition at line 196 of file main.ino.

◆ iphaseW

volatile int16_t iphaseW = 0

In-line Phase W current current measurement (Register Value).

The most recent current measurement is stored in this variable.

The range is 0-1023.

This value is not scaled and represents the raw ADC register value. To obtain the scaled current value in amperes, you can use the formula:

\[ \text{Current (A)} = \frac{\text{REGISTER_VALUE} \times 0.004888 \times 1000000}{\text{IBUS_GAIN} \times \text{IBUS_SENSE_RESISTOR}} \]

Where:

  • REGISTER_VALUE : The raw ADC register value stored in this variable.
  • 0.004888 : The conversion factor for a 10-bit ADC with a Vref of 5V.
  • IPHASE_GAIN : The gain of the current sense operational amplifier.
  • IPHASE_SENSE_RESISTOR : The value of the shunt resistor in micro-ohms (μΩ).

The NEVB-MTR1-I56-1 comes with a current op-amp with a gain factor of 20 and a current sense resistor of value 2.5 mΩ. This corresponds to approximately TODO: amperes (A) per register value.

Note
It is not used for any significant purpose in this implementation, but the measurement is updated.

Definition at line 226 of file main.ino.

◆ lastCommutationTicks

volatile uint16_t lastCommutationTicks = 0xffff

The number of 'ticks' between two hall sensor changes (store).

This variable is used to store the number of 'ticks' between each hall sensor change. It is set to the value in commutationTicks before it is cleared when the hall sensor change occurs.

One 'tick' is one PWM period. The speed of the motor is inversely proportional to this value.

See also
commutationTicks

Definition at line 94 of file main.ino.

◆ motorConfigs

volatile motorconfigs_t motorConfigs

Motor Configs.

This variable contains some of the motor configurations.

Definition at line 66 of file main.ino.

◆ motorFlags

volatile motorflags_t motorFlags

Motor control flags placed in I/O space for fast access.

This variable contains all the flags used for motor control. It is placed in GPIOR1 and GPIOR2 registers, which allows usage of several fast bit manipulation/branch instructions.

Warning
This variable can only have a maximum size of 2 bytes.

Definition at line 50 of file main.ino.

◆ speedInput

volatile uint8_t speedInput = 0

The most recent "speed" input measurement.

This variable is set by the ADC from the speed input reference pin. The range is 0-255.

Definition at line 101 of file main.ino.

◆ speedOutput

volatile uint8_t speedOutput = 0

The most recent "speed" output from the speed controller.

This variable controls the duty cycle of the generated PWM signals. The range is 0-255.

Definition at line 108 of file main.ino.

◆ vbusVref

volatile uint16_t vbusVref = 0

VBUS voltage measurement (Register Value)

The most recent VBUS voltage measurement is stored in this variable.

The range is 0-1023.

This value is not scaled and represents the raw ADC register value. To obtain the scaled VBUS voltage in volts, you can use the formula:

\[ \text{Voltage (V)} = \frac{\text{REGISTER_VALUE} \times 0.004888 \times (\text{VBUS_RTOP} + \text{VBUS_RBOTTOM})}{\text{VBUS_RBOTTOM}} \]

Where:

  • REGISTER_VALUE : The raw ADC register value stored in this variable.
  • 0.004888 : The conversion factor for a 10-bit ADC with a Vref of 5V.
  • VBUS_RTOP : The top resistor value in ohms (Ω) in the potential divider.
  • VBUS_RBOTTOM : The bottom resistor value in ohms (Ω) in the potential divider.

The NEVB-MTR1-C-1 has a resistor divider with RTOP of 100 kΩ and RBOTTOM of 6.2 kΩ, so it corresponds to approximately 0.0837 volts (V) per register value.

Note
It is not used for any significant purpose in this implementation, but the measurement is updated.

Definition at line 255 of file main.ino.