Motor Driver Evaluation Kit NEVB-MTR1-t01-1.3.1
Firmware for NEVB-MTR1-KIT1 for trapezoidal control of BLDC motors using Hall-effect sensors
Loading...
Searching...
No Matches
main.ino
Go to the documentation of this file.
1/* This file has been prepared for Doxygen automatic documentation generation.*/
29
30// Include motor control related headers
31#include "config.h"
32#include "tables.h"
33#include "fault.h"
34#include "filter.h"
35#include "scpi.h"
36
37// Include PID control algorithm if closed-loop speed control is enabled
38#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
39#include "pid.h"
40#endif
41
50volatile motorflags_t motorFlags FAST_ACCESS(0x4A);
51
60volatile faultflags_t faultFlags FAST_ACCESS(0x3E);
61
67
81volatile uint16_t commutationTicks = 0;
94volatile uint16_t lastCommutationTicks = 0xffff;
95
101volatile uint8_t speedInput = 0;
102
108volatile uint8_t speedOutput = 0;
109
137volatile uint16_t ibus = 0;
138
167volatile int16_t iphaseU = 0;
196volatile int16_t iphaseV = 0;
197
226volatile int16_t iphaseW = 0;
227
255volatile uint16_t vbusVref = 0;
256
257#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
259pidData_t pidParameters;
260#endif
261
267void setup(void)
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.
300 DesiredDirectionUpdate();
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}
314
321void loop()
322{
323 if (motorFlags.remote == TRUE)
324 {
325 ScpiInput(Serial);
326 }
327 if (motorFlags.speedControllerRun)
328 {
330 motorFlags.speedControllerRun = FALSE;
331 }
332}
333
339static void FlagsInit(void)
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}
361
366static void ConfigsInit(void)
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}
373
381static void PLLInit(void)
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}
399
407static void PortsInit(void)
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}
436
455void TimersInit(void)
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}
494
500static void PinChangeIntInit(void)
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}
513
536static void ADCInit(void)
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}
592
600static uint16_t ADCSingleConversion(void)
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}
617
633static void EnableUpdate(void)
634{
635 if ((PIND & (1 << ENABLE_PIN)) != 0)
636 {
637 motorFlags.enable = TRUE;
638 }
639 else
640 {
641 DisableMotor();
642 }
643}
644
659static void DisableMotor(void)
660{
661 motorFlags.enable = FALSE;
662 SetFaultFlag(FAULT_MOTOR_STOPPED, 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;
667 DisablePWMOutputs();
668 ClearPWMPorts();
669#endif
670}
671
679static void RemoteUpdate(void)
680{
681 if ((PIND & (1 << REMOTE_PIN)) != 0)
682 {
683 motorFlags.remote = TRUE;
684 }
685 else
686 {
687 motorFlags.remote = FALSE;
688 }
689}
690
722static void SpeedController(void)
723{
724 if (motorFlags.enable == TRUE)
725 {
726 // Inhibit drive output if VBUS is not sufficiently powered. This prevents
727 // PID integrator wind-up and unintended PWM when the motor power rail is
728 // absent at startup.
730 {
731#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
732 PIDResetIntegrator(&pidParameters);
733#endif
734 speedOutput = 0;
735 return;
736 }
737
738#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
739 // Calculate an increment set point from the analog speed input.
740 int16_t incrementSetpoint = ((int32_t)speedInput * SPEED_CONTROLLER_MAX_SPEED) / SPEED_CONTROLLER_MAX_INPUT;
741
742 // PID regulator with feed forward from speed input.
743 uint16_t outputValue;
744
745 outputValue = PIDController(incrementSetpoint, (motorConfigs.tim4Freq / (lastCommutationTicks * 3)) >> 1, &pidParameters);
746
747 if (outputValue > PID_OUTPUT_MAX)
748 {
749 outputValue = PID_OUTPUT_MAX;
750 }
751
752 speedOutput = outputValue;
753
754 // Without the delay PID does not reset when needed
755 _delay_us(1);
756#else
757 // Calculate the delta in speedInput
758 int16_t delta = speedInput - speedOutput;
759 // If delta exceeds the maximum allowed change, limit it and update
760 // speedOutput
761 if (delta > SPEED_CONTROLLER_MAX_DELTA)
762 {
764 }
765 else if (delta < -SPEED_CONTROLLER_MAX_DELTA)
766 {
768 }
769 else
770 {
772 }
773#endif
774 }
775 else
776 {
777 if (speedOutput > 0)
778 {
780 {
782 }
783 else
784 {
785 speedOutput = 0;
786 }
787 }
788 }
789}
790
799static void FatalError()
800{
801 // Stop the motor.
802 DisableMotor();
803
804 // Once this is set no more faults will be registered creating a snapshot
805 // of the failure point.
806 motorFlags.fatalFault = TRUE;
807}
808
817static FORCE_INLINE void SetDuty(const uint16_t duty)
818{
819 TC4H = duty >> 8;
820 OCR4A = 0xFF & duty;
821}
822
835static FORCE_INLINE void TimersSetModeBlockCommutation(void)
836{
837 // Set PWM pins to input (High-Z) while changing modes.
838 DisablePWMOutputs();
839
840 // Sets up timers.
841 TCCR4A = (0 << COM4A1) | (1 << COM4A0) | (0 << COM4B1) | (1 << COM4B0) | (1 << PWM4A) | (1 << PWM4B);
842 TCCR4C |= (0 << COM4D1) | (1 << COM4D0) | (1 << PWM4D);
843 TCCR4D = (1 << WGM41) | (1 << WGM40);
844
845 // Set output duty cycle to zero for now.
846 SetDuty(0);
847
848 // Wait for the next PWM cycle to ensure that all outputs are updated.
849 TimersWaitForNextPWMCycle();
850
851 motorFlags.driveWaveform = WAVEFORM_BLOCK_COMMUTATION;
852
853 // Change PWM pins to output again to allow PWM control.
854 EnablePWMOutputs();
855}
856
862static FORCE_INLINE void TimersWaitForNextPWMCycle(void)
863{
864 // Clear Timer1 Capture event flag.
865 TIFR4 = (1 << TOV4);
866
867 // Wait for new Timer1 Capture event flag.
868 while (!(TIFR4 & (1 << TOV4)))
869 {
870 }
871}
872
883static FORCE_INLINE void BlockCommutate(const uint8_t direction, uint8_t hall)
884{
885 const uint8_t *tableAddress;
886
887 if (direction == DIRECTION_FORWARD)
888 {
889 tableAddress = blockCommutationTableForward;
890 }
891 else
892 {
893 tableAddress = blockCommutationTableReverse;
894 }
895 tableAddress += (hall * 4);
896
897 ClearPWMPorts();
898 TCCR4E &= ~0b00111111;
899
900 DisablePWMOutputs();
901
902 PORTB |= (uint8_t)pgm_read_byte_near(tableAddress++);
903 PORTC |= (uint8_t)pgm_read_byte_near(tableAddress++);
904 PORTD |= (uint8_t)pgm_read_byte_near(tableAddress++);
905 TCCR4E |= (uint8_t)pgm_read_byte_near(tableAddress);
906
907 EnablePWMOutputs();
908}
909
921static FORCE_INLINE uint8_t GetHall(void)
922{
923 uint8_t hall;
924
925 hall = HALL_PIN & ((1 << H3_PIN) | (1 << H2_PIN) | (1 << H1_PIN));
926 hall >>= H1_PIN;
927
928 return hall;
929}
930
936static FORCE_INLINE void DesiredDirectionUpdate(void)
937{
938 if (motorFlags.enable == TRUE)
939 {
940 return;
941 }
942 else if ((PIND & (1 << DIRECTION_COMMAND_PIN)) != 0)
943 {
944 motorFlags.desiredDirection = DIRECTION_REVERSE;
945 }
946 else
947 {
948 motorFlags.desiredDirection = DIRECTION_FORWARD;
949 }
950}
951
961static FORCE_INLINE void ActualDirectionUpdate(uint8_t lastHall, const uint8_t newHall)
962{
963 // Ensure that lastHall is within the bounds of the table. If not, set it to
964 // 0, which is also an illegal hall value but a legal table index.
965 if (lastHall > 6)
966 {
967 lastHall = 0;
968 }
969
970 if (pgm_read_byte_near(&expectedHallSequenceForward[lastHall]) == newHall)
971 {
972 motorFlags.actualDirection = DIRECTION_FORWARD;
973 }
974 else if (pgm_read_byte_near(&expectedHallSequenceReverse[lastHall]) == newHall)
975 {
976 motorFlags.actualDirection = DIRECTION_REVERSE;
977 }
978 else
979 {
980 motorFlags.actualDirection = DIRECTION_UNKNOWN;
981 }
982}
983
992static FORCE_INLINE void ReverseRotationSignalUpdate(void)
993{
994 if (motorFlags.actualDirection == motorFlags.desiredDirection)
995 {
996 faultFlags.reverseDirection = FALSE;
997 }
998 else
999 {
1000 faultFlags.reverseDirection = TRUE;
1001 }
1002}
1003
1010static FORCE_INLINE void EnablePWMOutputs(void)
1011{
1012 DDRB |= PWM_PATTERN_PORTB;
1013 DDRC |= PWM_PATTERN_PORTC;
1014 DDRD |= PWM_PATTERN_PORTD;
1015}
1016
1023static FORCE_INLINE void DisablePWMOutputs(void)
1024{
1025 DDRB &= ~PWM_PATTERN_PORTB;
1026 DDRC &= ~PWM_PATTERN_PORTC;
1027 DDRD &= ~PWM_PATTERN_PORTD;
1028}
1029
1036static FORCE_INLINE void ClearPWMPorts(void)
1037{
1038 PORTB &= ~PWM_PATTERN_PORTB;
1039 PORTC &= ~PWM_PATTERN_PORTC;
1040 PORTD &= ~PWM_PATTERN_PORTD;
1041}
1042
1058static FORCE_INLINE void CommutationTicksUpdate(void)
1059{
1060 // If the motor is not stopped, increment the tick counter.
1062 {
1064 }
1065 // If motor is stopped, set the stopped flag and clear the tick counter.
1066 else
1067 {
1068 // Set flags to notify that the motor is stopped.
1069 SetFaultFlag(FAULT_MOTOR_STOPPED, TRUE);
1070 lastCommutationTicks = 0xffff;
1071
1072 // Get the current hall value.
1073 uint8_t hall = GetHall();
1074 if ((hall == 0) || (hall == 0b111))
1075 {
1076 SetFaultFlag(FAULT_NO_HALL_CONNECTIONS, TRUE);
1077 }
1078
1079 // If the motor is in a fatal fault, motor is now stopped so loop forever.
1080 if (motorFlags.fatalFault == TRUE)
1081 {
1082 while (1)
1083 {
1084 faultSequentialStateMachine(&faultFlags, &motorFlags);
1085 _delay_ms(2);
1086 }
1087 }
1088 // If the motor is supposed to be enabled, and the drive method is not block commutation,
1089 // reset the speed output and set the drive waveform.
1090 else if (motorFlags.driveWaveform != WAVEFORM_BLOCK_COMMUTATION && motorFlags.enable == TRUE)
1091 {
1092 speedOutput = 0;
1093#if (SPEED_CONTROL_METHOD == SPEED_CONTROL_CLOSED_LOOP)
1094 PIDResetIntegrator(&pidParameters);
1095#endif
1096 TimersSetModeBlockCommutation();
1097 BlockCommutate(motorFlags.desiredDirection, GetHall());
1098 }
1099 // If the motor is supposed to be stopped, (and it has stopped now) ...
1100 else if (motorFlags.enable == FALSE)
1101 {
1102#if (TURN_OFF_MODE == TURN_OFF_MODE_RAMP)
1103 // ... unset the drive waveform and disable PWM outputs.
1104 motorFlags.driveWaveform = WAVEFORM_UNDEFINED;
1105 DisablePWMOutputs();
1106 ClearPWMPorts();
1107#endif
1108 // ... update the desired direction flag as this would not have been
1109 // updated if the direction was changed while the motor was running.
1110 DesiredDirectionUpdate();
1111 }
1112 }
1113}
1114
1119ISR(INT0_vect)
1120{
1121 EnableUpdate();
1122}
1123
1133ISR(PCINT0_vect)
1134{
1135 static uint8_t lastHall = 0xff;
1136 uint8_t hall;
1137
1138 hall = GetHall();
1139
1140 if (motorFlags.driveWaveform == WAVEFORM_BLOCK_COMMUTATION)
1141 {
1142 BlockCommutate(motorFlags.desiredDirection, hall);
1143 }
1144
1145 // Update flags that depend on hall sensor value.
1146 ActualDirectionUpdate(lastHall, hall);
1147 ReverseRotationSignalUpdate();
1148
1149 lastHall = hall;
1150
1151 // Reset commutation timer.
1153 commutationTicks = 0;
1154
1155 // Since the hall sensors are changing, the motor can not be stopped.
1156 // For fast access, SetFaultFlag() is not used to update this flag.
1157 // Instead, the flag is updated directly.
1158 faultFlags.motorStopped = FALSE;
1159 faultFlags.noHallConnections = FALSE;
1160}
1161
1174ISR(INT2_vect)
1175{
1176 // Update desired direction flag.
1177 DesiredDirectionUpdate();
1178
1179 // Stop the motor.
1180 DisableMotor();
1181}
1182
1191ISR(TIMER4_OVF_vect)
1192{
1193 if (motorFlags.driveWaveform == WAVEFORM_BLOCK_COMMUTATION)
1194 {
1195 uint16_t dutyCycle = ((uint32_t)speedOutput * motorConfigs.tim4Top) >> 7;
1196
1197 if (dutyCycle > (uint16_t)(motorConfigs.tim4Top << 1))
1198 {
1199 dutyCycle = (uint16_t)(motorConfigs.tim4Top << 1);
1200 }
1201
1202 SetDuty(dutyCycle);
1203 }
1204
1205 CommutationTicksUpdate();
1206
1207 {
1208 // Run the speed regulation loop with constant intervals.
1209 static uint8_t speedRegTicks = 0;
1210 speedRegTicks++;
1211 if (speedRegTicks >= SPEED_CONTROLLER_TIME_BASE)
1212 {
1213 motorFlags.speedControllerRun = TRUE;
1214 speedRegTicks -= SPEED_CONTROLLER_TIME_BASE;
1215 }
1216 }
1217}
1218
1219#if (EMULATE_HALL == TRUE)
1235ISR(TIMER3_OVF_vect)
1236{
1237 if (motorFlags.enable == TRUE)
1238 {
1239 uint8_t hall = GetHall();
1240
1241 if (motorFlags.desiredDirection == DIRECTION_FORWARD)
1242 {
1243 PORTB = (PORTB & ~((1 << H1_PIN) | (1 << H2_PIN) | (1 << H3_PIN))) | ((0x07 & pgm_read_byte_near(&expectedHallSequenceForward[hall])) << H1_PIN);
1244 }
1245 else
1246 {
1247 PORTB = (PORTB & ~((1 << H1_PIN) | (1 << H2_PIN) | (1 << H3_PIN))) | ((0x07 & pgm_read_byte_near(&expectedHallSequenceReverse[hall])) << H1_PIN);
1248 }
1249 }
1250}
1251#endif
1252
1262ISR(TIMER1_OVF_vect)
1263{
1264 faultSequentialStateMachine(&faultFlags, &motorFlags);
1265}
1266
1281ISR(ADC_vect)
1282{
1283 switch ((ADMUX & ADC_MUX_L_BITS) | (ADCSRB & ADC_MUX_H_BITS))
1284 {
1286 // Handle ADC conversion result for speed measurement.
1287 if (motorConfigs.speedInputSource == SPEED_INPUT_SOURCE_LOCAL)
1288 {
1289 speedInput = ADCH;
1290 }
1291 ADMUX &= ~ADC_MUX_L_BITS;
1292 ADMUX |= ADC_MUX_L_IBUS;
1293 ADCSRB &= ~ADC_MUX_H_BITS;
1294 ADCSRB |= ADC_MUX_H_IBUS;
1295 break;
1297 // Handle ADC conversion result for current measurement.
1298 ibus = ADCL >> 6;
1299 ibus |= (ADCH << 2);
1300 ADMUX &= ~ADC_MUX_L_BITS;
1301 ADMUX |= ADC_MUX_L_IPHASE_U;
1302 ADCSRB &= ~ADC_MUX_H_BITS;
1303 ADCSRB |= ADC_MUX_H_IPHASE_U;
1304
1305#if (IBUS_FAULT_ENABLE == TRUE)
1306 // Debounce current error flags.
1307 static uint8_t currentErrorCount = 0;
1309 {
1310 if (currentErrorCount < 3)
1311 {
1312 currentErrorCount++;
1313 }
1314 else
1315 {
1316 SetFaultFlag(FAULT_OVER_CURRENT, TRUE);
1317 SetFaultFlag(FAULT_USER_FLAG1, TRUE);
1318 SetFaultFlag(FAULT_USER_FLAG2, TRUE);
1319 SetFaultFlag(FAULT_USER_FLAG3, TRUE);
1320 FatalError();
1321 }
1322 }
1323 else
1324#endif
1326 {
1327 SetFaultFlag(FAULT_OVER_CURRENT, TRUE);
1328#if (IBUS_FAULT_ENABLE == TRUE)
1329 currentErrorCount = 0;
1330#endif
1331 }
1332 else
1333 {
1334 SetFaultFlag(FAULT_OVER_CURRENT, FALSE);
1335#if (IBUS_FAULT_ENABLE == TRUE)
1336 currentErrorCount = 0;
1337#endif
1338 }
1339 break;
1341 // Handle ADC conversion result for phase current measurement.
1342 iphaseU = ADCL >> 6;
1343 iphaseU |= (ADCH << 2);
1344 ADMUX &= ~ADC_MUX_L_BITS;
1345 ADMUX |= ADC_MUX_L_IPHASE_V;
1346 ADCSRB &= ~ADC_MUX_H_BITS;
1347 ADCSRB |= ADC_MUX_H_IPHASE_V;
1348 break;
1350 // Handle ADC conversion result for phase current measurement.
1351 iphaseV = ADCL >> 6;
1352 iphaseV |= (ADCH << 2);
1353 ADMUX &= ~ADC_MUX_L_BITS;
1354 ADMUX |= ADC_MUX_L_IPHASE_W;
1355 ADCSRB &= ~ADC_MUX_H_BITS;
1356 ADCSRB |= ADC_MUX_H_IPHASE_W;
1357 break;
1359 // Handle ADC conversion result for phase current measurement.
1360 iphaseW = ADCL >> 6;
1361 iphaseW |= (ADCH << 2);
1362 ADMUX &= ~ADC_MUX_L_BITS;
1363 ADMUX |= ADC_MUX_L_VBUSVREF;
1364 ADCSRB &= ~ADC_MUX_H_BITS;
1365 ADCSRB |= ADC_MUX_H_VBUSVREF;
1366 break;
1368 // Handle ADC conversion result for gate voltage reference measurement.
1369 vbusVref = ADCL >> 6;
1370 vbusVref |= (ADCH << 2);
1371 ADMUX &= ~ADC_MUX_L_BITS;
1372 ADMUX |= ADC_MUX_L_SPEED;
1373 ADCSRB &= ~ADC_MUX_H_BITS;
1374 ADCSRB |= ADC_MUX_H_SPEED;
1375 break;
1376 default:
1377 // This is probably an error and should be handled.
1378 SetFaultFlag(FAULT_USER_FLAG1, TRUE);
1379 SetFaultFlag(FAULT_USER_FLAG2, TRUE);
1380 SetFaultFlag(FAULT_USER_FLAG3, TRUE);
1381 FatalError();
1382 break;
1383 }
1384
1385 // Clear Timer/Counter0 overflow flag.
1386 TIFR0 = (1 << TOV0);
1387}
1388
1400static void SetFaultFlag(fault_flag_t flag, uint8_t value)
1401{
1402 if (motorFlags.fatalFault)
1403 {
1404 // Fatal fault active, cannot set any more flags.
1405 return;
1406 }
1407
1408 switch (flag)
1409 {
1410 case FAULT_RESERVED:
1411 faultFlags.reserved = value;
1412 break;
1414 faultFlags.reverseDirection = value;
1415 break;
1417 faultFlags.motorStopped = value;
1418 break;
1419 case FAULT_OVER_CURRENT:
1420 faultFlags.overCurrent = value;
1421 break;
1423 faultFlags.noHallConnections = value;
1424 break;
1425 case FAULT_USER_FLAG1:
1426 faultFlags.userFlag1 = value;
1427 break;
1428 case FAULT_USER_FLAG2:
1429 faultFlags.userFlag2 = value;
1430 break;
1431 case FAULT_USER_FLAG3:
1432 faultFlags.userFlag3 = value;
1433 break;
1434 default:
1435 return; // Invalid flag
1436 }
1437
1438 return; // Success
1439}
Motor config header file.
void SweepLEDsBlocking(void)
Sweeps through all LEDs individually with a delay.
Definition fault.cpp:144
void faultSequentialStateMachine(volatile faultflags_t *faultFlags, volatile motorflags_t *motorFlags)
Sequential State Machine for Handling Fault Flags.
Definition fault.cpp:182
Fault LED header file.
int16_t calculateEMA(uint16_t currentSample, uint16_t previousEMA, uint8_t alphaExponent)
Exponential Moving Average (EMA) calculation algorithm.
Definition filter.cpp:34
Filter header file.
#define ADC_MUX_H_BITS
High ADC channel selection bit (MUX5) mask.
Definition config.h:1059
#define ADC_MUX_L_BITS
Lower ADC channel selection bits (MUX4:0) mask.
Definition config.h:1057
#define TIM4_TOP(tim4Freq)
Definition config.h:1302
#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:1323
#define DT_PRESCALER_DIV_PATTERN(dtPrescaler)
Deadtime generator pre-scaler selection bits based on pre-scaler value.
Definition config.h:1313
#define TIM3_TOP
Calculated top value for Timer 3.
Definition config.h:1331
#define TIM4_PRESCALER_DIV_PATTERN(tim4Prescaler)
Timer 4 clock select bits based on pre-scaler value.
Definition config.h:1295
#define PLL_POSTSCALER_DIV_1_5
PLL Post-scaler - division factor 1.5.
Definition config.h:987
#define H2_PIN
Pin where H2 is connected.
Definition config.h:797
#define WAVEFORM_BLOCK_COMMUTATION
Waveform constant for block commutation.
Definition config.h:863
#define ADC_MUX_L_IBUS
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:809
#define DIRECTION_FORWARD
Forward direction flag value.
Definition config.h:785
#define ADC_MUX_H_IPHASE_V
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:819
#define ADC_MUX_H_VBUSVREF
High analog channel selection bit (MUX5) for for motor vbusVref measurement.
Definition config.h:827
#define WAVEFORM_UNDEFINED
Waveform status flag used for coasting.
Definition config.h:865
#define FAULT_PIN_2
Fault Pin 2.
Definition config.h:857
#define ADC_MUX_L_IPHASE_W
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:821
#define DIRECTION_COMMAND_PIN
Pin where direction command input is located.
Definition config.h:839
#define SPEED_INPUT_SOURCE_LOCAL
Speed input source - Local or speed input pin.
Definition config.h:849
#define FAST_ACCESS(register_address)
Assign a specific memory address to a variable for fast access.
Definition config.h:948
#define CHOOSE_DT_PRESCALER(deadTime)
Macro to choose Timer4 dead time pre-scaler based on the dead time.
Definition config.h:912
#define ADC_MUX_L_VBUSVREF
Lower analog channel selection bits (MUX4:0) for motor vbusVref measurement.
Definition config.h:825
#define SPEED_CONTROLLER_MAX_INPUT
Maximum Speed Reference Input.
Definition config.h:891
#define DIRECTION_REVERSE
Reverse direction flag value.
Definition config.h:787
#define ADC_MUX_L_SPEED
Definition config.h:804
struct motorflags motorflags_t
Collection of all motor control flags.
#define ENABLE_PIN
Enable input pin.
Definition config.h:841
#define ADC_MUX_H_IPHASE_W
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:823
#define PWM_PATTERN_PORTC
Bit pattern of PWM pins placed on PORTC (Phase B).
Definition config.h:770
fault_flag_t
Enumeration of fault flags.
Definition config.h:1242
#define ADC_TRIGGER
ADC trigger used in this application.
Definition config.h:835
#define HALL_PIN
PIN register for Hall sensor input.
Definition config.h:793
struct motorconfigs motorconfigs_t
Collection of motor configurations.
#define ADC_MUX_L_IPHASE_V
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:817
#define IBUS_PIN
IBUS ADC input pin (used to check if board is attached).
Definition config.h:845
#define ADC_MUX_H_IBUS
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:811
#define TRUE
TRUE constant value, defined to be compatible with comparisons.
Definition config.h:747
#define FALSE
FALSE constant value.
Definition config.h:745
#define CHOOSE_TIM4_PRESCALER(tim4Freq)
Macro to choose Timer4 pre-scaler.
Definition config.h:894
#define ADC_PRESCALER
ADC clock pre-scaler used in this application.
Definition config.h:831
#define REMOTE_PIN
Remote input pin.
Definition config.h:843
#define ADC_MUX_L_IPHASE_U
Lower analog channel selection bits (MUX4:0) for motor current measurement.
Definition config.h:813
#define FORCE_INLINE
Macro for forcing inline expansion of functions.
Definition config.h:932
#define PWM_PATTERN_PORTB
Bit pattern of PWM pins placed on PORTB (Phase A).
Definition config.h:768
#define ADC_MUX_H_SPEED
Definition config.h:807
#define PWM_PATTERN_PORTD
Bit pattern of PWM pins placed on PORTD (Phase C).
Definition config.h:772
#define FAULT_PIN_3
Fault Pin 3.
Definition config.h:859
#define DIRECTION_UNKNOWN
Unknown direction flag value.
Definition config.h:789
struct faultflags faultflags_t
Collection of all fault flags.
#define ADC_MUX_H_IPHASE_U
High analog channel selection bit (MUX5) for for motor current measurement.
Definition config.h:815
#define H3_PIN
Pin where H3 is connected.
Definition config.h:799
#define H1_PIN
Pin where H1 is connected.
Definition config.h:795
#define ADC_REFERENCE_VOLTAGE
ADC voltage reference used in this application.
Definition config.h:833
#define FAULT_PIN_1
Fault Pin 1.
Definition config.h:855
@ FAULT_OVER_CURRENT
Has it tripped the over current limit?
Definition config.h:1250
@ FAULT_USER_FLAG1
Is user flag 1 set?
Definition config.h:1254
@ FAULT_NO_HALL_CONNECTIONS
Is there no hall connections?
Definition config.h:1252
@ FAULT_USER_FLAG2
Is user flag 2 set?
Definition config.h:1256
@ FAULT_RESERVED
Reserved flag, always false.
Definition config.h:1244
@ FAULT_USER_FLAG3
Is user flag 3 set?
Definition config.h:1258
@ FAULT_REVERSE_DIRECTION
Is motor spinning in an unexpected direction?
Definition config.h:1246
@ FAULT_MOTOR_STOPPED
Is motor stopped?
Definition config.h:1248
#define TIM1_CLOCK_DIV_64
Timer1 clock - i/o clk with division factor 64.
Definition config.h:1022
#define TIM1_CLOCK_DIV_8
Timer1 clock - i/o clk with division factor 8.
Definition config.h:1020
#define IBUS_WARNING_THRESHOLD
Hi-side Current (IBUS) Warning Threshold (Register Value)
Definition config.h:367
#define PID_K_P
PID Controller Proportional Gain Constant (Only for Closed Loop)
Definition config.h:509
#define F_MOSFET
Desired Switching Frequency for MOSFET Gate Signals.
Definition config.h:157
#define IBUS_ERROR_THRESHOLD
Hi-side Current (IBUS) Error Threshold (Register Value)
Definition config.h:406
#define VBUS_MIN_THRESHOLD
Minimum VBUS Threshold for Motor Operation (Register Value)
Definition config.h:692
#define SPEED_CONTROLLER_MAX_DELTA
Speed Controller Maximum Delta (Applicable for Open Loop Control)
Definition config.h:469
#define PID_OUTPUT_MAX
Maximum PID Controller Output (Only for Closed Loop)
Definition config.h:615
#define PID_K_D
PID Controller Derivative Gain Constant (Only for Closed Loop)
Definition config.h:566
#define SPEED_CONTROLLER_MAX_SPEED
Speed Controller Maximum Speed.
Definition config.h:490
#define SPEED_CONTROLLER_TIME_BASE
Speed Controller Time Base.
Definition config.h:448
#define COMMUTATION_TICKS_STOPPED
Commutation Stopped Limit.
Definition config.h:246
#define PID_K_I
PID Controller Integral Gain Constant (Only for Closed Loop)
Definition config.h:528
#define DEAD_TIME
Dead Time Specification.
Definition config.h:186
static void PortsInit(void)
Initialize I/O port directions and pull-up resistors.
Definition main.ino:407
volatile uint16_t vbusVref
VBUS voltage measurement (Register Value)
Definition main.ino:255
static void ADCInit(void)
Initializes the ADC.
Definition main.ino:536
static void SpeedController(void)
Speed regulator loop.
Definition main.ino:722
volatile uint8_t speedInput
The most recent "speed" input measurement.
Definition main.ino:101
static void FlagsInit(void)
Initializes motorFlags and faultFlags.
Definition main.ino:339
volatile uint16_t ibus
Hi-side Current (IBUS) measurement (Register Value).
Definition main.ino:137
volatile motorconfigs_t motorConfigs
Motor Configs.
Definition main.ino:66
static void PinChangeIntInit(void)
Initialize pin change interrupts.
Definition main.ino:500
void setup(void)
Main initialization function.
Definition main.ino:267
static void PLLInit(void)
Initialize PLL (Phase-Locked Loop)
Definition main.ino:381
volatile uint16_t commutationTicks
The number of 'ticks' between two hall sensor changes (counter).
Definition main.ino:81
volatile int16_t iphaseW
In-line Phase W current current measurement (Register Value).
Definition main.ino:226
volatile uint8_t speedOutput
The most recent "speed" output from the speed controller.
Definition main.ino:108
volatile uint16_t lastCommutationTicks
The number of 'ticks' between two hall sensor changes (store).
Definition main.ino:94
void TimersInit(void)
Initializes and synchronizes Timers.
Definition main.ino:455
static void EnableUpdate(void)
Check whether the enable pin is set and update flags accordingly.
Definition main.ino:633
static uint16_t ADCSingleConversion(void)
Perform a single ADC conversion.
Definition main.ino:600
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:799
static void ConfigsInit(void)
Initializes motorConfigs.
Definition main.ino:366
static void DisableMotor(void)
Disable motor.
Definition main.ino:659
volatile int16_t iphaseV
In-line Phase V current current measurement (Register Value).
Definition main.ino:196
static void RemoteUpdate(void)
Check whether the remote pin is set and update flags accordingly.
Definition main.ino:679
void loop()
Main Loop Function.
Definition main.ino:321
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:33
uint16_t PIDController(int16_t setPoint, int16_t processValue, pidData_t *pid_st)
PID control algorithm.
Definition pid.cpp:56
void PIDResetIntegrator(pidData_t *pid_st)
Resets the integrator in the PID regulator.
Definition pid.cpp:131
PID controller header file.
struct pidData pidData_t
PID Status.
void ScpiInput(Stream &interface)
Processes incoming data from a serial interface for SCPI commands.
Definition scpi.cpp:122
void ScpiInit(void)
Initializes the SCPI command parser and registers all supported commands.
Definition scpi.cpp:71
SCPI implementation header file.
Motor Control Tables.
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
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