You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constboolIDENTIFY_HALLS_ON_BOOT= true; // If true, controller will initialize the hall table by slowly spinning the motor
14
+
constboolIDENTIFY_HALLS_REVERSE= false; // If true, will initialize the hall table to spin the motor backwards
28
15
29
-
// End user config section -----------------------------
16
+
uint8_thallToMotor[8] = {255, 255, 255, 255, 255, 255, 255, 255}; // Default hall table. Overwrite this with the output of the hall auto-identification
17
+
18
+
constintTHROTTLE_LOW=600; // ADC value corresponding to minimum throttle, 0-4095
19
+
constintTHROTTLE_HIGH=2650; // ADC value corresponding to maximum throttle, 0-4095
30
20
31
-
#defineMEMORY_LEN 10000
32
-
intmemory_current[MEMORY_LEN];
33
-
intmemory_current_target[MEMORY_LEN];
34
-
intmemory_throttle[MEMORY_LEN];
35
-
intmemory_hall[MEMORY_LEN];
36
-
intmemory_voltage[MEMORY_LEN];
37
-
intmemory_duty[MEMORY_LEN];
38
-
intmemory_pos=0;
39
-
boolmemory_writing= false;
21
+
constboolCURRENT_CONTROL= true; // Use current control or duty cycle control
22
+
constintPHASE_MAX_CURRENT_MA=6000; // If using current control, the maximum phase current allowed
23
+
constintBATTERY_MAX_CURRENT_MA=3000; // If using current control, the maximum battery current allowed
24
+
constintCURRENT_CONTROL_LOOP_GAIN=200; // Adjusts the speed of the current control loop
25
+
26
+
// End user config section -----------------------------
40
27
41
28
constuintLED_PIN=25;
42
29
constuintAH_PIN=16;
@@ -87,99 +74,115 @@ uint8_t read_throttle();
87
74
88
75
89
76
voidon_adc_fifo() {
90
-
uint32_tflags=save_and_disable_interrupts(); // Disable interrupts for the time-critical reading ADC section
77
+
// This interrupt is where the magic happens. This is fired once the ADC conversions have finished (roughly 6us for 3 conversions)
78
+
// This reads the hall sensors, determines the motor state to switch to, and reads the current sensors and throttle to
79
+
// determine the desired duty cycle. This takes ~7us to complete.
80
+
81
+
uint32_tflags=save_and_disable_interrupts(); // Disable interrupts for the time-critical reading ADC section. USB interrupts may interfere
91
82
92
-
adc_run(false);
93
-
gpio_put(FLAG_PIN, 1);
83
+
adc_run(false);// Stop the ADC from free running
84
+
gpio_put(FLAG_PIN, 1);// For debugging, toggle the flag pin
94
85
95
-
fifo_level=adc_fifo_get_level();
96
-
adc_isense=adc_fifo_get();
86
+
fifo_level=adc_fifo_get_level();
87
+
adc_isense=adc_fifo_get();// Read the ADC values into the registers
97
88
adc_vsense=adc_fifo_get();
98
89
adc_throttle=adc_fifo_get();
99
90
100
-
restore_interrupts(flags);
91
+
restore_interrupts(flags);// Re-enable interrupts
101
92
102
93
if(fifo_level!=3) {
103
94
// The RP2040 is a shitty microcontroller. The ADC is unpredictable, and 1% of times
104
95
// will return more or less than the 3 samples it should. If we don't get the expected number, abort
105
96
return;
106
97
}
107
98
108
-
hall=get_halls(); // Takes ~200 nanoseconds
109
-
motorState=hallToMotor[hall];
99
+
hall=get_halls(); // Read the hall sensors
100
+
motorState=hallToMotor[hall];// Convert the current hall reading to the desired motor state
intuser_current_target_ma=throttle*PHASE_MAX_CURRENT_MA / 256; // Calculate the user-demanded phase current
110
+
intbattery_current_limit_ma=BATTERY_MAX_CURRENT_MA*DUTY_CYCLE_MAX / duty_cycle; // Calculate the maximum phase current allowed while respecting the battery current limit
duty_cycle=0; // If zero throttle, ignore the current control loop and turn all transistors off
116
+
ticks_since_init=0; // Reset the timer since the transistors were turned on
117
+
}
118
+
else
119
+
ticks_since_init++;
120
+
121
+
duty_cycle+= (current_target_ma-current_ma) / CURRENT_CONTROL_LOOP_GAIN; // Perform a simple integral controller to adjust the duty cycle
122
+
duty_cycle=MAX(0, MIN(DUTY_CYCLE_MAX, duty_cycle)); // Clamp the duty cycle
130
123
131
124
booldo_synchronous=ticks_since_init>16000; // Only enable synchronous switching some time after beginning control loop. This allows control loop to stabilize
pwm_config_set_wrap(&config, 255-1);// Set the PWM to wrap at 254. This allows a PWM value of 255 to equal 100% duty cycle
256
+
pwm_config_set_phase_correct(&config, true);// Set phase correct (counts up then down). This is allows firing the interrupt in the middle of the PWM cycle
257
+
pwm_config_set_output_polarity(&config, false, true);// Invert the lowside PWM such that 0 corresponds to lowside transistors on
259
258
260
-
writePhases(0, 0, 0, 0, 0, 0);
259
+
writePhases(0, 0, 0, 0, 0, 0);// Initialize all the PWMs to be off
261
260
262
261
pwm_init(A_PWM_SLICE, &config, false);
263
262
pwm_init(B_PWM_SLICE, &config, false);
264
263
pwm_init(C_PWM_SLICE, &config, false);
265
264
266
-
pwm_set_mask_enabled(0x07);
265
+
pwm_set_mask_enabled(0x07);// Enable our three PWM timers
267
266
}
268
267
269
268
uintget_halls() {
269
+
// Read the hall sensors with oversampling. Hall sensor readings can be noisy, which produces commutation "glitches".
270
+
// This reads the hall sensors multiple times, then takes the majority reading to reduce noise. Takes ~200 nanoseconds
271
+
270
272
uinthallCounts[] = {0, 0, 0};
271
-
for(uinti=0; i<HALL_OVERSAMPLE; i++) // Read all the hall pins repeatedly, tally results
273
+
for(uinti=0; i<HALL_OVERSAMPLE; i++) // Read all the hall pins repeatedly, count votes
272
274
{
273
275
hallCounts[0] +=gpio_get(HALL_1_PIN);
274
276
hallCounts[1] +=gpio_get(HALL_2_PIN);
275
277
hallCounts[2] +=gpio_get(HALL_3_PIN);
276
278
}
277
279
278
280
uinthall_raw=0;
281
+
for(uinti=0; i<3; i++)
282
+
if (hallCounts[i] >HALL_OVERSAMPLE / 2) // If more than half the readings are positive,
283
+
hall_raw |= 1<<i; // set the i-th bit high
279
284
280
-
if (hallCounts[0] >HALL_OVERSAMPLE / 2) // If votes >= threshold, call that a 1
281
-
hall_raw |= (1<<0); // Store a 1 in the 0th bit
282
-
if (hallCounts[1] >HALL_OVERSAMPLE / 2)
283
-
hall_raw |= (1<<1); // Store a 1 in the 1st bit
284
-
if (hallCounts[2] >HALL_OVERSAMPLE / 2)
285
-
hall_raw |= (1<<2); // Store a 1 in the 2nd bit
286
-
287
-
returnhall_raw&0x7; // Just to make sure we didn't do anything stupid, set the maximum output value to 7
285
+
returnhall_raw; // Range is 0-7. However, 0 and 7 are invalid hall states
288
286
}
289
287
290
288
voididentify_halls()
291
289
{
290
+
// This is the magic function which sets the hallToMotor commutation table. This allows
291
+
// you to plug in the motor and hall wires in any order, and the controller figures out the order.
292
+
// This works by PWM-ing to all of the "half states", such as 0.5, by switching rapidly between state 0 and 1.
293
+
// This commutates to all half-states and reads the corresponding hall value. Then, since electrical position
294
+
// should lead rotor position by 90 degrees, for this hall state, save a motor state 1.5 steps ahead.
295
+
292
296
sleep_ms(2000);
293
297
for(uinti=0; i<6; i++)
294
298
{
295
-
uint8_tnextState= (i+1) % 6; // Calculate what the next state should be. This is for switching into half-states
296
-
printf("Going to %d\n", i);
297
-
for(uintj=0; j<1000; j++) // For a while, repeatedly switch between states
299
+
for(uintj=0; j<1000; j++) // Commutate to a half-state long enough to allow the rotor to stop moving
0 commit comments